From 3e59672ccfd8f055494dedf4f2b83e6fe3a2292b Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Sun, 4 Dec 2022 13:11:11 +0200 Subject: [PATCH 01/22] =?UTF-8?q?=F0=9F=A5=B3init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 py3votecore/consensus_under_deadline.py diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/py3votecore/consensus_under_deadline.py @@ -0,0 +1 @@ + From 72b8de6c0ab7d2834e7057ca4e0986af8fe55a0c Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Sun, 4 Dec 2022 15:21:02 +0200 Subject: [PATCH 02/22] =?UTF-8?q?=F0=9F=93=9Dheaders=20for=20algorithm=20c?= =?UTF-8?q?lass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 70 +++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 8b13789..9a89572 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -1 +1,71 @@ + +class ConsensusUnderDeadline(): + ''' + A class representing a model for group decision making under strict deadline, based on iterative process. The model was + presented by Marina Bannikova, Lihi Dery, Svetlana Obraztsova, Zinovi Rabinovich, Jeffrey S. Rosenschein (2019), + https://arxiv.org/abs/1905.07173 + ''' + + def __init__(self, voters: tuple, alternatives: tuple, voters_preferences: list, default_alternative: int, alternative_scores: dict, remaining_rounds: int) -> int: + ''' + Constructor for Consensus-Under-Deadline algorithm. + + Arguments: + voters - all of the enlisted voters + alternatives - all of the optional choices to vote for + voters_preferences - the voters preferences of the votes + default_alternative - an alternative that will be chosen upon disagreement + alternative_scores - the number of votes for each alternative, updated in each round + remaining_rounds - a threshold for the amount if rounds left until decision should be taken + + Returns: + The winner alternative + ''' + pass + + def votes_calculate(self, ballots: dict) -> dict: + ''' + Taken the given votes, return the total score for each ballot. + + Arguments: + ballots - the voter's preferences for current round + + Returns: + A dictionary of scores for each alternative + ''' + pass + + def possible_winners(self) -> list: + ''' + Returns the alternatives who are possibly win consider their scores and remaining time. + + Returns: + List of alternatives with a chance to win + ''' + pass + + def choose_random_voter(voters: tuple) -> int: + ''' + Random voter selection. + + Arguments: + voters - all the voters who whish to change their ballot + + Returns: + A voter who shall change is vote + ''' + pass + + def change_vote(self, voter: int, vote: str): + ''' + Change voters ballot. + + Arguments: + voter - the voter who shall change his vote + vote - the voter's new preferred alternative + ''' + pass + +if __name__ == '__main__': + pass \ No newline at end of file From e2f2cbd99ad7b748f357cef2c6c0fed1ed66659f Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 6 Dec 2022 18:44:15 +0200 Subject: [PATCH 03/22] =?UTF-8?q?=F0=9F=93=9Dheaders=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 9a89572..cd679be 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -38,7 +38,7 @@ def votes_calculate(self, ballots: dict) -> dict: def possible_winners(self) -> list: ''' - Returns the alternatives who are possibly win consider their scores and remaining time. + Returns the alternatives who are possibly to win, consider their scores and the remaining time. Returns: List of alternatives with a chance to win From 923a8fbbfdd89343819ac7e7f4e95f9df84413ef Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 6 Dec 2022 23:56:38 +0200 Subject: [PATCH 04/22] =?UTF-8?q?=E2=99=BB=EF=B8=8Fstructure=20+=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 201 +++++++++++++++++++++--- 1 file changed, 175 insertions(+), 26 deletions(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index cd679be..6cc3869 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -1,30 +1,168 @@ - +import doctest class ConsensusUnderDeadline(): ''' - A class representing a model for group decision making under strict deadline, based on iterative process. The model was - presented by Marina Bannikova, Lihi Dery, Svetlana Obraztsova, Zinovi Rabinovich, Jeffrey S. Rosenschein (2019), + A class representing a model for group decision making under strict deadline, based on iterative process. Each alternative must be accept + by unanimously. + The model was presented by Marina Bannikova, Lihi Dery, Svetlana Obraztsova, Zinovi Rabinovich, Jeffrey S. Rosenschein (2019), https://arxiv.org/abs/1905.07173 + + Author: Raphael Suliman ''' - def __init__(self, voters: tuple, alternatives: tuple, voters_preferences: list, default_alternative: int, alternative_scores: dict, remaining_rounds: int) -> int: + def __init__(self, voters: tuple, voters_type: tuple, alternatives: tuple, voters_preferences: list, + default_alternative: str, voters_current_ballot: dict, remaining_rounds: int) -> int: ''' Constructor for Consensus-Under-Deadline algorithm. Arguments: voters - all of the enlisted voters + voters_type - whether the voter is an 'active' one, or 'lazy'. 1 - active, 0 - lazy alternatives - all of the optional choices to vote for - voters_preferences - the voters preferences of the votes + voters_preferences - the voters preferences of the alternatives in decreasing order default_alternative - an alternative that will be chosen upon disagreement - alternative_scores - the number of votes for each alternative, updated in each round + voters_current_ballot - voter's ballot in current round remaining_rounds - a threshold for the amount if rounds left until decision should be taken - + ''' + self.voters = voters + self.voters_type = voters_type + self.alternatives = alternatives + self.voters_preferences = voters_preferences + self.default_alternative = default_alternative + self.voters_current_ballot = voters_current_ballot + self.remaining_rounds = remaining_rounds + + def deploy_algorithm(self): + ''' + Runs the algorithm 'Consensus Under Deadline' to determine the winning result. + Returns: The winner alternative + + ---------------------------------TESTS--------------------------------- + Classic example: + Since 3 different votes were chosen, we'll assume that all 3 voters will want to change their ballot (to prevent default option) + using seed in random, voter 1 will be chosen as an example, as all voters active + >>> v = (1, 2, 3) + >>> v_type = (1, 1, 1) + >>> alters = ('a', 'b', 'c') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + >>> vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + >>> t = 2 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.deploy_algorithm()) + b + + Lazy voter example: + >>> v = (1, 2, 3, 4, 5) + >>> v_type = (1, 1, 1, 1, 0) + >>> alters = ('a', 'b', 'c', 'd') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> t = 4 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.deploy_algorithm()) + b + + No consensus reached example: + >>> v = (1, 2, 3, 4, 5) + >>> v_type = (0, 0, 0, 0, 0) + >>> alters = ('a', 'b', 'c', 'd') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> t = 3 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.deploy_algorithm()) + null + ''' + pass + + def possible_winners(self) -> list: + ''' + Returns the alternatives who are possibly to win, consider their scores and the remaining time. + + Returns: + List of alternatives with a chance to win + + ---------------------------------TESTS--------------------------------- + >>> v = {1, 2, 3} + >>> v_type = {1, 1, 1} + >>> alters = {'a', 'b', 'c'} + >>> df_alter = 'null' + >>> vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + >>> v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + >>> t = 2 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.possible_winners()) + [a, b, c] + + >>> t = 1 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.possible_winners()) + [] + + >>> v = (1, 2, 3, 4, 5) + >>> v_type = (0, 0, 0, 0, 0) + >>> alters = ('a', 'b', 'c', 'd') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> t = 4 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.possible_winners()) + [a, b, c] + + >>> t = 3 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.possible_winners()) + [a, b] + + >>> t = 2 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.possible_winners()) + [b] + ''' + pass + + def change_vote(self, voter: int, vote: str): + ''' + Change voters ballot. + + Arguments: + voter - the voter who shall change his vote + vote - the voter's new preferred alternative + + ---------------------------------TESTS--------------------------------- + >>> v = (1, 2, 3, 4, 5) + >>> v_type = (0, 0, 0, 0, 0) + >>> alters = ('a', 'b', 'c', 'd') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> t = 4 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> print(cud.voters_current_ballot) + {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + + >>> cud.change_vote(1, 'b') + >>> print(cud.voters_current_ballot) + {1: 'b', 2:'a', 3:'b', 4:'b', 5:'c' } + + >>> cud.change_vote(2, 'b') + >>> print(cud.voters_current_ballot) + {1: 'b', 2:'b', 3:'b', 4:'b', 5:'c' } + + >>> cud.change_vote(4, 'd') + >>> print(cud.voters_current_ballot) + {1: 'b', 2:'a', 3:'b', 4:'d', 5:'c' } ''' pass - def votes_calculate(self, ballots: dict) -> dict: + @staticmethod + def votes_calculate(ballots: dict) -> dict: ''' Taken the given votes, return the total score for each ballot. @@ -33,19 +171,24 @@ def votes_calculate(self, ballots: dict) -> dict: Returns: A dictionary of scores for each alternative - ''' - pass - def possible_winners(self) -> list: - ''' - Returns the alternatives who are possibly to win, consider their scores and the remaining time. + ---------------------------------TESTS--------------------------------- + >>> ballots = {1:'a', 2:'b', 3:'c'} + >>> print(ConsensusUnderDeadline.votes_calculate(ballots)) + {'a': 1, 'b': 1, 'c': 1 } - Returns: - List of alternatives with a chance to win + >>> ballots = {1:'a', 2:'a', 3:'b', 4:'b', 5:'c'} + >>> print(ConsensusUnderDeadline.votes_calculate(ballots)) + {'a': 2, 'b': 2, 'c': 1, 'd': 0 } + + >>> ballots = {1:'a', 2:'a', 3:'b', 4:'a', 5:'d'} + >>> print(ConsensusUnderDeadline.votes_calculate(ballots)) + {'a': 3, 'b': 1, 'c': 0, 'd': 1 } ''' pass - - def choose_random_voter(voters: tuple) -> int: + + @staticmethod + def choose_random_voter(voters: list) -> int: ''' Random voter selection. @@ -54,18 +197,24 @@ def choose_random_voter(voters: tuple) -> int: Returns: A voter who shall change is vote - ''' - pass - def change_vote(self, voter: int, vote: str): - ''' - Change voters ballot. + ---------------------------------TESTS--------------------------------- + Seed will be used for testing + >>> v = [1, 2, 3] + >>> print(ConsensusUnderDeadline.choose_random_voter(v)) + [1] + + >>> v = [1, 2, 3] + >>> print(ConsensusUnderDeadline.choose_random_voter(v)) + [2] + + >>> v = [1, 2, 3] + >>> print(ConsensusUnderDeadline.choose_random_voter(v)) + [3] - Arguments: - voter - the voter who shall change his vote - vote - the voter's new preferred alternative ''' pass + if __name__ == '__main__': - pass \ No newline at end of file + doctest.testmod() \ No newline at end of file From 74b2a222628b4f47fe8f4ec5916a838500abd0c1 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 6 Dec 2022 23:59:28 +0200 Subject: [PATCH 05/22] =?UTF-8?q?=E2=99=BB=EF=B8=8Fempty=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 6cc3869..1f6eabd 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -78,7 +78,7 @@ def deploy_algorithm(self): >>> print(cud.deploy_algorithm()) null ''' - pass + return 0 def possible_winners(self) -> list: ''' @@ -125,7 +125,7 @@ def possible_winners(self) -> list: >>> print(cud.possible_winners()) [b] ''' - pass + return 0 def change_vote(self, voter: int, vote: str): ''' @@ -159,7 +159,7 @@ def change_vote(self, voter: int, vote: str): >>> print(cud.voters_current_ballot) {1: 'b', 2:'a', 3:'b', 4:'d', 5:'c' } ''' - pass + return 0 @staticmethod def votes_calculate(ballots: dict) -> dict: @@ -185,7 +185,7 @@ def votes_calculate(ballots: dict) -> dict: >>> print(ConsensusUnderDeadline.votes_calculate(ballots)) {'a': 3, 'b': 1, 'c': 0, 'd': 1 } ''' - pass + return 0 @staticmethod def choose_random_voter(voters: list) -> int: @@ -202,18 +202,18 @@ def choose_random_voter(voters: list) -> int: Seed will be used for testing >>> v = [1, 2, 3] >>> print(ConsensusUnderDeadline.choose_random_voter(v)) - [1] + 1 >>> v = [1, 2, 3] >>> print(ConsensusUnderDeadline.choose_random_voter(v)) - [2] + 2 >>> v = [1, 2, 3] >>> print(ConsensusUnderDeadline.choose_random_voter(v)) - [3] + 3 ''' - pass + return 0 if __name__ == '__main__': From 4b4c4535cb6ca29126d4dd1e453b3246d652ff4e Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 7 Dec 2022 01:12:31 +0200 Subject: [PATCH 06/22] =?UTF-8?q?=E2=9C=85unitests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consensus_under_deadline.py | 0 .../test_consensus_under_deadline.py | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+) rename {py3votecore => test_performance}/consensus_under_deadline.py (100%) create mode 100644 test_performance/test_consensus_under_deadline.py diff --git a/py3votecore/consensus_under_deadline.py b/test_performance/consensus_under_deadline.py similarity index 100% rename from py3votecore/consensus_under_deadline.py rename to test_performance/consensus_under_deadline.py diff --git a/test_performance/test_consensus_under_deadline.py b/test_performance/test_consensus_under_deadline.py new file mode 100644 index 0000000..1950bc9 --- /dev/null +++ b/test_performance/test_consensus_under_deadline.py @@ -0,0 +1,19 @@ +from consensus_under_deadline import ConsensusUnderDeadline +import unittest + +class TestConsensusUnderDeadline(unittest.TestCase): + def setUp(self) -> None: + self.v = (1, 2, 3) + self.v_type = (1, 1, 1) + self.alters = ('a', 'b', 'c') + self.df_alter = 'null' + self.v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + self.vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + self.t = 2 + self.cud = ConsensusUnderDeadline(voters=self.v, voters_type = self.v_type, alternatives=self.alters, default_alternative=self.df_alter,voters_preferences=self.vp, voters_current_ballot=self.v_cur_ballot, remaining_rounds=self.t) + + def test_prop(self) -> None: + self.assertEqual(self.cud.voters, (1, 2, 3)) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From be780f8ba6eb3f7d2242ab2cc7a6fb2b53b05492 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 7 Dec 2022 09:36:40 +0200 Subject: [PATCH 07/22] =?UTF-8?q?=E2=9C=85unittest=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_consensus_under_deadline.py | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/test_performance/test_consensus_under_deadline.py b/test_performance/test_consensus_under_deadline.py index 1950bc9..95196ee 100644 --- a/test_performance/test_consensus_under_deadline.py +++ b/test_performance/test_consensus_under_deadline.py @@ -3,17 +3,57 @@ class TestConsensusUnderDeadline(unittest.TestCase): def setUp(self) -> None: - self.v = (1, 2, 3) - self.v_type = (1, 1, 1) - self.alters = ('a', 'b', 'c') - self.df_alter = 'null' - self.v_cur_ballot = {1: 'a', 2:'b', 3:'c'} - self.vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - self.t = 2 - self.cud = ConsensusUnderDeadline(voters=self.v, voters_type = self.v_type, alternatives=self.alters, default_alternative=self.df_alter,voters_preferences=self.vp, voters_current_ballot=self.v_cur_ballot, remaining_rounds=self.t) + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 2 + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - def test_prop(self) -> None: - self.assertEqual(self.cud.voters, (1, 2, 3)) - + def test_deploy_algorithm_results(self) -> None: + self.assertEqual(self.cud.deploy_algorithm(), 'b') # classic example + self.cud.remaining_rounds = 0 + self.assertEqual(self.cud.deploy_algorithm(), 'null') # test case with no time to change the vote + self.cud.voters_current_ballot = {1: 'a', 2:'a', 3:'a'} + self.assertEqual(self.cud.deploy_algorithm(), 'a') # test case with unanimously on the start + self.cud.remaining_rounds = 3 + self.cud.voters_preferences =[['a', 'b', 'c'], ['b', 'a', 'c'],['c', 'a', 'c']] + self.assertEqual(self.cud.deploy_algorithm(), 'a') # voters have same pretty close preference + + def test_init_args(self) -> None: + v = (1, 2, 3) + v_type = (1, 2, 3) + alters = ('a', 'b', 'c') + df_alter = 'null' + v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 2 + # voters character is binary + with self.assertRaises(TypeError): + self.cud = ConsensusUnderDeadline(voters=v, voters_type = (1, 2, 3), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + # each voter key must bey unique + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(voters=(1, 1, 1), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + self.cud = ConsensusUnderDeadline(voters=(1, 1, 2), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + # each alternative key must bey unique + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'a', 'c'), default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'c', 'c'), default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + # number of voters and voter's type must be aligned + with self.assertRaises(TypeError): + self.cud = ConsensusUnderDeadline(voters=(1, 2, 3), voters_type = (1, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + self.cud = ConsensusUnderDeadline(voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + # voters ballot must match the existing voters and alternative + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={0: 'a', 2:'b', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={1: 'a', 2:'t', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={1: 'a', 2:'%', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={7: '&', 4:'^', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) + # given time must be >= 0 + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(remaining_rounds=-1, voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + self.cud = ConsensusUnderDeadline(remaining_rounds=-3, voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) if __name__ == '__main__': unittest.main() \ No newline at end of file From d173c64a19ecb75427474dcf7795a52f9b3794a7 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 7 Dec 2022 09:46:39 +0200 Subject: [PATCH 08/22] =?UTF-8?q?=E2=9C=85possib;e=5Fwinners?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_consensus_under_deadline.py | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/test_performance/test_consensus_under_deadline.py b/test_performance/test_consensus_under_deadline.py index 95196ee..104852a 100644 --- a/test_performance/test_consensus_under_deadline.py +++ b/test_performance/test_consensus_under_deadline.py @@ -12,16 +12,6 @@ def setUp(self) -> None: t = 2 self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - def test_deploy_algorithm_results(self) -> None: - self.assertEqual(self.cud.deploy_algorithm(), 'b') # classic example - self.cud.remaining_rounds = 0 - self.assertEqual(self.cud.deploy_algorithm(), 'null') # test case with no time to change the vote - self.cud.voters_current_ballot = {1: 'a', 2:'a', 3:'a'} - self.assertEqual(self.cud.deploy_algorithm(), 'a') # test case with unanimously on the start - self.cud.remaining_rounds = 3 - self.cud.voters_preferences =[['a', 'b', 'c'], ['b', 'a', 'c'],['c', 'a', 'c']] - self.assertEqual(self.cud.deploy_algorithm(), 'a') # voters have same pretty close preference - def test_init_args(self) -> None: v = (1, 2, 3) v_type = (1, 2, 3) @@ -55,5 +45,40 @@ def test_init_args(self) -> None: with self.assertRaises(ValueError): self.cud = ConsensusUnderDeadline(remaining_rounds=-1, voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) self.cud = ConsensusUnderDeadline(remaining_rounds=-3, voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + + def test_deploy_algorithm_results(self) -> None: + self.assertEqual(self.cud.deploy_algorithm(), 'b') # classic example + self.cud.remaining_rounds = 0 + self.assertEqual(self.cud.deploy_algorithm(), 'null') # test case with no time to change the vote + self.cud.voters_current_ballot = {1: 'a', 2:'a', 3:'a'} + self.assertEqual(self.cud.deploy_algorithm(), 'a') # test case with unanimously on the start + self.cud.remaining_rounds = 3 + self.cud.voters_preferences =[['a', 'b', 'c'], ['b', 'a', 'c'],['c', 'a', 'c']] + self.assertEqual(self.cud.deploy_algorithm(), 'a') # voters have same pretty close preference + + def test_possible_winners(self) -> None: + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + self.assertEqual(self.cud.possible_winners() ,[]) + self.cud = ConsensusUnderDeadline(remaining_rounds=4,voters=(1, 2, 3, 4, 5), voters_type = (0, 0, 0, 0, 0), alternatives=('a', 'b', 'c', 'd'), default_alternative=df_alter,voters_preferences={1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' }, voters_current_ballot=v_cur_ballot) + v = (1, 2, 3, 4, 5) + v_type = (0, 0, 0, 0, 0) + alters = ('a', 'b', 'c', 'd') + v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + t = 4 + self.cud = ConsensusUnderDeadline(remaining_rounds=1,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) + t = 3 + self.cud = ConsensusUnderDeadline(remaining_rounds=1,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + self.assertEqual(self.cud.possible_winners() ,['a', 'b']) + if __name__ == '__main__': unittest.main() \ No newline at end of file From e74eb73eb40986cc95ac2c6b3d7508b5eb3cebaa Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 7 Dec 2022 09:54:05 +0200 Subject: [PATCH 09/22] =?UTF-8?q?=E2=9C=85change=20vote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_consensus_under_deadline.py | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/test_performance/test_consensus_under_deadline.py b/test_performance/test_consensus_under_deadline.py index 104852a..b8c4ce8 100644 --- a/test_performance/test_consensus_under_deadline.py +++ b/test_performance/test_consensus_under_deadline.py @@ -79,6 +79,44 @@ def test_possible_winners(self) -> None: t = 3 self.cud = ConsensusUnderDeadline(remaining_rounds=1,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) self.assertEqual(self.cud.possible_winners() ,['a', 'b']) - + + def test_change_vote_args(self): + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + # given voter isn't exist + with self.assertRaises(ValueError): + self.cud.change_vote(5, 'a') + self.cud.change_vote(4, 'a') + # given alternative isn't exist + with self.assertRaises(ValueError): + self.cud.change_vote(1, 'd') + self.cud.change_vote(1, 'e') + # user hasn't changed his ballot + with self.assertRaises(ValueError): + self.cud.change_vote(1, 'a') + self.cud.change_vote(2, 'b') + + def test_change_vote_results(self): + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + self.cud.change_vote(1, 'c') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'b', 3:'c'} ) + self.cud.change_vote(2, 'c') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'c'} ) + self.cud.change_vote(3, 'a') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'a'} ) + if __name__ == '__main__': unittest.main() \ No newline at end of file From 50d48702eacc23f9dc6e53f2b499ac3fefb58cdf Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 7 Dec 2022 09:58:39 +0200 Subject: [PATCH 10/22] =?UTF-8?q?=E2=9C=85votes=5Fcalculate=5Fresults?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test_performance/test_consensus_under_deadline.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test_performance/test_consensus_under_deadline.py b/test_performance/test_consensus_under_deadline.py index b8c4ce8..f3ecb3d 100644 --- a/test_performance/test_consensus_under_deadline.py +++ b/test_performance/test_consensus_under_deadline.py @@ -117,6 +117,21 @@ def test_change_vote_results(self): self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'c'} ) self.cud.change_vote(3, 'a') self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'a'} ) + + def test_votes_calculate_results(self): + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + v_cur_ballot = {1: 'a', 2:'a', 3:'c'} + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 2, 'b': 0, 'c': 1 }) + v_cur_ballot = {1: 'a', 2:'a', 3:'a'} + self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 3, 'b': 0, 'c': 0 }) if __name__ == '__main__': unittest.main() \ No newline at end of file From 18be337286bd3b9c5fd54fcf531696d03f4899e0 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 7 Dec 2022 09:59:47 +0200 Subject: [PATCH 11/22] s --- {test_performance => py3votecore}/consensus_under_deadline.py | 0 test_performance/test_consensus_under_deadline.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename {test_performance => py3votecore}/consensus_under_deadline.py (100%) diff --git a/test_performance/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py similarity index 100% rename from test_performance/consensus_under_deadline.py rename to py3votecore/consensus_under_deadline.py diff --git a/test_performance/test_consensus_under_deadline.py b/test_performance/test_consensus_under_deadline.py index f3ecb3d..8903c44 100644 --- a/test_performance/test_consensus_under_deadline.py +++ b/test_performance/test_consensus_under_deadline.py @@ -1,4 +1,4 @@ -from consensus_under_deadline import ConsensusUnderDeadline +from py3votecore.consensus_under_deadline import ConsensusUnderDeadline import unittest class TestConsensusUnderDeadline(unittest.TestCase): From 8b9d937174cc5c3901536f79f02c4e1d8fec0e23 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 21 Dec 2022 19:19:09 +0200 Subject: [PATCH 12/22] =?UTF-8?q?=E2=9C=85subfunctions=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 174 ++++++++++++++---------- 1 file changed, 105 insertions(+), 69 deletions(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 1f6eabd..07d2778 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -1,37 +1,46 @@ import doctest +import random +import logging +from collections import Counter + +logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG) +logger = logging.getLogger() class ConsensusUnderDeadline(): ''' A class representing a model for group decision making under strict deadline, based on iterative process. Each alternative must be accept by unanimously. - The model was presented by Marina Bannikova, Lihi Dery, Svetlana Obraztsova, Zinovi Rabinovich, Jeffrey S. Rosenschein (2019), + The model was presented by Marina Bannikova, Lihi Dery, Svetlana Obraztsova, Zinovi Rabinovich, Jeffrey S. Rosenschein (2019), https://arxiv.org/abs/1905.07173 Author: Raphael Suliman ''' def __init__(self, voters: tuple, voters_type: tuple, alternatives: tuple, voters_preferences: list, - default_alternative: str, voters_current_ballot: dict, remaining_rounds: int) -> int: + default_alternative: str, remaining_rounds: int, random_selection: bool) -> int: ''' Constructor for Consensus-Under-Deadline algorithm. Arguments: voters - all of the enlisted voters voters_type - whether the voter is an 'active' one, or 'lazy'. 1 - active, 0 - lazy - alternatives - all of the optional choices to vote for + alternatives - all of the optional choices to vote for voters_preferences - the voters preferences of the alternatives in decreasing order - default_alternative - an alternative that will be chosen upon disagreement - voters_current_ballot - voter's ballot in current round - remaining_rounds - a threshold for the amount if rounds left until decision should be taken + default_alternative - an alternative that will be chosen upon disagreement + remaining_rounds - a threshold for the amount if rounds left until decision should be taken + random_selection - whether the selection of voter for changing their ballot. If False - the smallest voter's number will be selected ''' self.voters = voters self.voters_type = voters_type self.alternatives = alternatives self.voters_preferences = voters_preferences self.default_alternative = default_alternative - self.voters_current_ballot = voters_current_ballot + # initiate first ballot for each voter by their top preference + self.voters_current_ballot = {i + 1: voters_preferences[i][0] + for i in range(len(voters_preferences))} self.remaining_rounds = remaining_rounds - + self.random_selection = random_selection + def deploy_algorithm(self): ''' Runs the algorithm 'Consensus Under Deadline' to determine the winning result. @@ -50,7 +59,7 @@ def deploy_algorithm(self): >>> v_cur_ballot = {1: 'a', 2:'b', 3:'c'} >>> vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] >>> t = 2 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t, random_selection=False) >>> print(cud.deploy_algorithm()) b @@ -62,7 +71,7 @@ def deploy_algorithm(self): >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] >>> t = 4 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t, random_selection=False) >>> print(cud.deploy_algorithm()) b @@ -79,53 +88,74 @@ def deploy_algorithm(self): null ''' return 0 - + def possible_winners(self) -> list: ''' - Returns the alternatives who are possibly to win, consider their scores and the remaining time. + Returns the alternatives who are possibly to win, consider their scores and the remaining time. Returns: List of alternatives with a chance to win - ---------------------------------TESTS--------------------------------- + ---------------------------------TESTS--------------------------------- >>> v = {1, 2, 3} >>> v_type = {1, 1, 1} >>> alters = {'a', 'b', 'c'} >>> df_alter = 'null' >>> vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - >>> v_cur_ballot = {1: 'a', 2:'b', 3:'c'} >>> t = 2 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter, voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - [a, b, c] + ['a', 'b', 'c'] >>> t = 1 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - [] + ['null'] >>> v = (1, 2, 3, 4, 5) >>> v_type = (0, 0, 0, 0, 0) >>> alters = ('a', 'b', 'c', 'd') >>> df_alter = 'null' - >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] >>> t = 4 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - [a, b, c] + ['a', 'b', 'c'] >>> t = 3 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter, voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - [a, b] + ['a', 'b'] >>> t = 2 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> vp =[['b', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter, voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - [b] + ['b'] ''' - return 0 + logger.info('calculating possible winners alternatives') + unanimously = len( + self.voters) # the amount of votes needed to reach consensus + current_votes_score = ConsensusUnderDeadline.votes_calculate( + self.voters_current_ballot) + # add all alternatives who haven't been voted for - cover the case where alternative with 0 votes can still be a winner + for alter in self.alternatives: + if alter not in current_votes_score: + current_votes_score[alter] = 0 + possible_winners_alters = [] + logger.debug('total votes: %g',unanimously) + logger.debug('current vote scores: %s',current_votes_score) + logger.debug('remaining rounds: %g',self.remaining_rounds) + for alt, score in current_votes_score.items(): + # if an alternative has a chance to get the remaining votes in the remaining time + if (score + self.remaining_rounds) >= unanimously: + possible_winners_alters.append(alt) + logger.debug('alternative %s nominate as a winner candidate',alt) + # if none of the alternatives has a chance to be chosen - return default alternative + if len(possible_winners_alters) == 0: + possible_winners_alters.append(self.default_alternative) + logger.debug('possible winners: %s',possible_winners_alters) + return possible_winners_alters def change_vote(self, voter: int, vote: str): ''' @@ -133,9 +163,9 @@ def change_vote(self, voter: int, vote: str): Arguments: voter - the voter who shall change his vote - vote - the voter's new preferred alternative + vote - the voter's new preferred alternative - ---------------------------------TESTS--------------------------------- + ---------------------------------TESTS--------------------------------- >>> v = (1, 2, 3, 4, 5) >>> v_type = (0, 0, 0, 0, 0) >>> alters = ('a', 'b', 'c', 'd') @@ -143,29 +173,63 @@ def change_vote(self, voter: int, vote: str): >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] >>> t = 4 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.voters_current_ballot) - {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } - + {1: 'a', 2: 'a', 3: 'b', 4: 'b', 5: 'c'} + >>> cud.change_vote(1, 'b') >>> print(cud.voters_current_ballot) - {1: 'b', 2:'a', 3:'b', 4:'b', 5:'c' } + {1: 'b', 2: 'a', 3: 'b', 4: 'b', 5: 'c'} >>> cud.change_vote(2, 'b') >>> print(cud.voters_current_ballot) - {1: 'b', 2:'b', 3:'b', 4:'b', 5:'c' } + {1: 'b', 2: 'b', 3: 'b', 4: 'b', 5: 'c'} >>> cud.change_vote(4, 'd') >>> print(cud.voters_current_ballot) - {1: 'b', 2:'a', 3:'b', 4:'d', 5:'c' } + {1: 'b', 2: 'b', 3: 'b', 4: 'd', 5: 'c'} ''' - return 0 + self.voters_current_ballot[voter] = vote + + def choose_random_voter(self, voters: list) -> int: + ''' + Random voter selection. + + Arguments: + voters - all the voters who whish to change their ballot + + Returns: + A voter who shall change is vote + + ---------------------------------TESTS--------------------------------- + random_selection true for testing + >>> v = (1, 2, 3, 4, 5) + >>> v_type = (0, 0, 0, 0, 0) + >>> alters = ('a', 'b', 'c', 'd') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> t = 4 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + >>> v = [1, 2, 3] + >>> print(cud.choose_random_voter(v)) + 1 + + >>> v = [2, 3, 4] + >>> print(cud.choose_random_voter(v)) + 2 + + >>> v = [3, 4, 5, 6] + >>> print(cud.choose_random_voter(v)) + 3 + ''' + return random.choice(voters) if self.random_selection else min(voters) @staticmethod def votes_calculate(ballots: dict) -> dict: ''' Taken the given votes, return the total score for each ballot. - + Arguments: ballots - the voter's preferences for current round @@ -175,46 +239,18 @@ def votes_calculate(ballots: dict) -> dict: ---------------------------------TESTS--------------------------------- >>> ballots = {1:'a', 2:'b', 3:'c'} >>> print(ConsensusUnderDeadline.votes_calculate(ballots)) - {'a': 1, 'b': 1, 'c': 1 } + {'a': 1, 'b': 1, 'c': 1} >>> ballots = {1:'a', 2:'a', 3:'b', 4:'b', 5:'c'} >>> print(ConsensusUnderDeadline.votes_calculate(ballots)) - {'a': 2, 'b': 2, 'c': 1, 'd': 0 } + {'a': 2, 'b': 2, 'c': 1} >>> ballots = {1:'a', 2:'a', 3:'b', 4:'a', 5:'d'} >>> print(ConsensusUnderDeadline.votes_calculate(ballots)) - {'a': 3, 'b': 1, 'c': 0, 'd': 1 } - ''' - return 0 - - @staticmethod - def choose_random_voter(voters: list) -> int: - ''' - Random voter selection. - - Arguments: - voters - all the voters who whish to change their ballot - - Returns: - A voter who shall change is vote - - ---------------------------------TESTS--------------------------------- - Seed will be used for testing - >>> v = [1, 2, 3] - >>> print(ConsensusUnderDeadline.choose_random_voter(v)) - 1 - - >>> v = [1, 2, 3] - >>> print(ConsensusUnderDeadline.choose_random_voter(v)) - 2 - - >>> v = [1, 2, 3] - >>> print(ConsensusUnderDeadline.choose_random_voter(v)) - 3 - + {'a': 3, 'b': 1, 'd': 1} ''' - return 0 + return dict(Counter(ballots.values())) if __name__ == '__main__': - doctest.testmod() \ No newline at end of file + doctest.testmod() From c0de3acae6c7850692be5cf601dd096f77744145 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Wed, 21 Dec 2022 21:58:45 +0200 Subject: [PATCH 13/22] =?UTF-8?q?=F0=9F=94=8Alogs=20added=20for=20subfunct?= =?UTF-8?q?ions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 07d2778..fb7711b 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -30,6 +30,7 @@ def __init__(self, voters: tuple, voters_type: tuple, alternatives: tuple, voter remaining_rounds - a threshold for the amount if rounds left until decision should be taken random_selection - whether the selection of voter for changing their ballot. If False - the smallest voter's number will be selected ''' + logger.info('ConsensusUnderDeadline object created') self.voters = voters self.voters_type = voters_type self.alternatives = alternatives @@ -190,6 +191,8 @@ def change_vote(self, voter: int, vote: str): {1: 'b', 2: 'b', 3: 'b', 4: 'd', 5: 'c'} ''' self.voters_current_ballot[voter] = vote + logger.info('voter %s changed his vote for %s', voter, vote) + def choose_random_voter(self, voters: list) -> int: ''' @@ -223,7 +226,9 @@ def choose_random_voter(self, voters: list) -> int: >>> print(cud.choose_random_voter(v)) 3 ''' - return random.choice(voters) if self.random_selection else min(voters) + ans = random.choice(voters) if self.random_selection else min(voters) + logger.info('voter %s has been chosen for changing ballot', ans) + return ans @staticmethod def votes_calculate(ballots: dict) -> dict: From c34b6b6296cce310b72b0c507a6c8b3482c0818a Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 27 Dec 2022 14:05:15 +0200 Subject: [PATCH 14/22] =?UTF-8?q?=E2=9C=85full=20algorithm=20implemented?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 93 ++++++++++++++++++++----- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index fb7711b..542f1fb 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -3,7 +3,7 @@ import logging from collections import Counter -logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.DEBUG) +logging.basicConfig(filename='running_algorithm.log', encoding='utf-8', level=logging.DEBUG) logger = logging.getLogger() class ConsensusUnderDeadline(): @@ -60,7 +60,7 @@ def deploy_algorithm(self): >>> v_cur_ballot = {1: 'a', 2:'b', 3:'c'} >>> vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] >>> t = 2 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t, random_selection=False) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.deploy_algorithm()) b @@ -72,7 +72,7 @@ def deploy_algorithm(self): >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] >>> t = 4 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t, random_selection=False) + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.deploy_algorithm()) b @@ -83,12 +83,70 @@ def deploy_algorithm(self): >>> df_alter = 'null' >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] - >>> t = 3 - >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) + >>> t = 2 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.deploy_algorithm()) null ''' - return 0 + logger.info('deploying algorithm') + unanimously = len(self.voters) # the required score for an alternative to win + logger.debug('required votes for unanimously: %g', unanimously) + logger.debug('round number: %g', self.remaining_rounds) + while self.remaining_rounds >=0 : + logger.debug('voters have cast their ballots') + self.round_passed() #mark this round as passed + current_votes_score = ConsensusUnderDeadline.votes_calculate(self.voters_current_ballot) + for key, value in current_votes_score.items(): + if value == unanimously: + return key + possible_winners = self.possible_winners() # all the alternative who's possible to be elected + logger.debug('round number: %g', self.remaining_rounds) + # if no alternative is eligible to win - no need to keep iterating + if possible_winners == self.default_alternative: + logger.debug('possible winners: %s. Algorithm is finished with no winner',self.default_alternative) + break; + voters_candidate = [] # candidate voters to change their ballot + # select voters to change their ballot base on their type and selected alternative + for voter_index in range(len(self.voters)): + voter = self.voters[voter_index] + voter_type = self.voters_type[voter_index] + # if voter's current vote isn't eligible to win - mark him as wishes to change ballot + if self.voters_current_ballot.get(voter_index + 1) not in possible_winners: + voters_candidate.append(voter) + logger.debug('voter %g wishes to change his ballot', voter) + # if voter is active and he has more winners candidate alternatives to vote for + elif voter_type == 1 and len(possible_winners) != 1: + voters_candidate.append(voter) + logger.debug('voter %g wishes to change his ballot', voter) + if len(voters_candidate) != 0: + ballot_change_voter = self.choose_random_voter(voters_candidate) + ballot_change_voter_preference = self.voters_preferences[ballot_change_voter - 1] + ballot_change_voter_current_ballot = self.voters_current_ballot[ballot_change_voter] + # voter chooses to change his ballot to the top possible alternative (besides his current) + for preference in ballot_change_voter_preference: + if preference in possible_winners and preference != ballot_change_voter_current_ballot: + self.change_vote(ballot_change_voter, preference, ballot_change_voter_current_ballot) + break + # if unanimously hasn't reached - return default alternative + return self.default_alternative + + + + def round_passed(self): + ''' + Lower round by one - symbolize a passing iteration. + >>> v = {1, 2, 3} + >>> v_type = {1, 1, 1} + >>> alters = {'a', 'b', 'c'} + >>> df_alter = 'null' + >>> vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + >>> t = 2 + >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter, voters_preferences=vp, remaining_rounds=t, random_selection=False) + >>> print(cud.remaining_rounds) + 2 + ''' + self.remaining_rounds -= 1 + def possible_winners(self) -> list: ''' @@ -111,7 +169,7 @@ def possible_winners(self) -> list: >>> t = 1 >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - ['null'] + ['a', 'b', 'c'] >>> v = (1, 2, 3, 4, 5) >>> v_type = (0, 0, 0, 0, 0) @@ -121,12 +179,12 @@ def possible_winners(self) -> list: >>> t = 4 >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - ['a', 'b', 'c'] + ['a', 'b', 'c', 'd'] >>> t = 3 >>> cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter, voters_preferences=vp, remaining_rounds=t, random_selection=False) >>> print(cud.possible_winners()) - ['a', 'b'] + ['a', 'b', 'c'] >>> t = 2 >>> vp =[['b', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] @@ -146,19 +204,18 @@ def possible_winners(self) -> list: possible_winners_alters = [] logger.debug('total votes: %g',unanimously) logger.debug('current vote scores: %s',current_votes_score) - logger.debug('remaining rounds: %g',self.remaining_rounds) for alt, score in current_votes_score.items(): # if an alternative has a chance to get the remaining votes in the remaining time - if (score + self.remaining_rounds) >= unanimously: + if (score + self.remaining_rounds + 1) >= unanimously: possible_winners_alters.append(alt) logger.debug('alternative %s nominate as a winner candidate',alt) # if none of the alternatives has a chance to be chosen - return default alternative if len(possible_winners_alters) == 0: - possible_winners_alters.append(self.default_alternative) + return self.default_alternative logger.debug('possible winners: %s',possible_winners_alters) return possible_winners_alters - def change_vote(self, voter: int, vote: str): + def change_vote(self, voter: int, new_vote: str, current_vote: str): ''' Change voters ballot. @@ -178,20 +235,20 @@ def change_vote(self, voter: int, vote: str): >>> print(cud.voters_current_ballot) {1: 'a', 2: 'a', 3: 'b', 4: 'b', 5: 'c'} - >>> cud.change_vote(1, 'b') + >>> cud.change_vote(1, 'b', 'a') >>> print(cud.voters_current_ballot) {1: 'b', 2: 'a', 3: 'b', 4: 'b', 5: 'c'} - >>> cud.change_vote(2, 'b') + >>> cud.change_vote(2, 'b', 'a') >>> print(cud.voters_current_ballot) {1: 'b', 2: 'b', 3: 'b', 4: 'b', 5: 'c'} - >>> cud.change_vote(4, 'd') + >>> cud.change_vote(4, 'd', 'b') >>> print(cud.voters_current_ballot) {1: 'b', 2: 'b', 3: 'b', 4: 'd', 5: 'c'} ''' - self.voters_current_ballot[voter] = vote - logger.info('voter %s changed his vote for %s', voter, vote) + self.voters_current_ballot[voter] = new_vote + logger.info('voter %s changed his vote from %s to %s', voter, current_vote, new_vote) def choose_random_voter(self, voters: list) -> int: From 54064735c2fd61ad3130852ea7bbf8cb2c737be3 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 27 Dec 2022 16:26:09 +0200 Subject: [PATCH 15/22] =?UTF-8?q?=E2=99=BB=EF=B8=8Fcreate=20outer=20functi?= =?UTF-8?q?on=20for=20CUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 97 +++++++++++++++++++------ 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 542f1fb..fb6de2c 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -3,9 +3,60 @@ import logging from collections import Counter -logging.basicConfig(filename='running_algorithm.log', encoding='utf-8', level=logging.DEBUG) +logging.basicConfig(filename='running_algorithm.log', + encoding='utf-8', level=logging.DEBUG) logger = logging.getLogger() + +def mdvr(voters: tuple, voters_type: tuple, alternatives: tuple, voters_preferences: list, + default_alternative: str, remaining_rounds: int, random_selection: bool): + ''' + Runs the algorithm 'Consensus Under Deadline' to determine the winning result. + + Returns: + The winner alternative + + ---------------------------------TESTS--------------------------------- + Classic example: + Since 3 different votes were chosen, we'll assume that all 3 voters will want to change their ballot (to prevent default option) + using seed in random, voter 1 will be chosen as an example, as all voters active + >>> v = (1, 2, 3) + >>> v_type = (1, 1, 1) + >>> alters = ('a', 'b', 'c') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'b', 3:'c'} + >>> vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + >>> t = 2 + >>> print(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False)) + b + + Lazy voter example: + >>> v = (1, 2, 3, 4, 5) + >>> v_type = (1, 1, 1, 1, 0) + >>> alters = ('a', 'b', 'c', 'd') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> t = 4 + >>> print(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False)) + b + + No consensus reached example: + >>> v = (1, 2, 3, 4, 5) + >>> v_type = (0, 0, 0, 0, 0) + >>> alters = ('a', 'b', 'c', 'd') + >>> df_alter = 'null' + >>> v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } + >>> vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + >>> t = 2 + >>> print(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False)) + null + ''' + cud = ConsensusUnderDeadline(voters, voters_type, alternatives, voters_preferences, + default_alternative, remaining_rounds, random_selection) + return cud.deploy_algorithm() + + class ConsensusUnderDeadline(): ''' A class representing a model for group decision making under strict deadline, based on iterative process. Each alternative must be accept @@ -89,23 +140,27 @@ def deploy_algorithm(self): null ''' logger.info('deploying algorithm') - unanimously = len(self.voters) # the required score for an alternative to win + # the required score for an alternative to win + unanimously = len(self.voters) logger.debug('required votes for unanimously: %g', unanimously) logger.debug('round number: %g', self.remaining_rounds) - while self.remaining_rounds >=0 : + while self.remaining_rounds >= 0: logger.debug('voters have cast their ballots') - self.round_passed() #mark this round as passed - current_votes_score = ConsensusUnderDeadline.votes_calculate(self.voters_current_ballot) + self.round_passed() # mark this round as passed + current_votes_score = ConsensusUnderDeadline.votes_calculate( + self.voters_current_ballot) for key, value in current_votes_score.items(): if value == unanimously: return key - possible_winners = self.possible_winners() # all the alternative who's possible to be elected + # all the alternative who's possible to be elected + possible_winners = self.possible_winners() logger.debug('round number: %g', self.remaining_rounds) # if no alternative is eligible to win - no need to keep iterating if possible_winners == self.default_alternative: - logger.debug('possible winners: %s. Algorithm is finished with no winner',self.default_alternative) - break; - voters_candidate = [] # candidate voters to change their ballot + logger.debug( + 'possible winners: %s. Algorithm is finished with no winner', self.default_alternative) + break + voters_candidate = [] # candidate voters to change their ballot # select voters to change their ballot base on their type and selected alternative for voter_index in range(len(self.voters)): voter = self.voters[voter_index] @@ -119,19 +174,19 @@ def deploy_algorithm(self): voters_candidate.append(voter) logger.debug('voter %g wishes to change his ballot', voter) if len(voters_candidate) != 0: - ballot_change_voter = self.choose_random_voter(voters_candidate) + ballot_change_voter = self.choose_random_voter( + voters_candidate) ballot_change_voter_preference = self.voters_preferences[ballot_change_voter - 1] ballot_change_voter_current_ballot = self.voters_current_ballot[ballot_change_voter] # voter chooses to change his ballot to the top possible alternative (besides his current) for preference in ballot_change_voter_preference: if preference in possible_winners and preference != ballot_change_voter_current_ballot: - self.change_vote(ballot_change_voter, preference, ballot_change_voter_current_ballot) + self.change_vote( + ballot_change_voter, preference, ballot_change_voter_current_ballot) break # if unanimously hasn't reached - return default alternative return self.default_alternative - - def round_passed(self): ''' Lower round by one - symbolize a passing iteration. @@ -145,8 +200,7 @@ def round_passed(self): >>> print(cud.remaining_rounds) 2 ''' - self.remaining_rounds -= 1 - + self.remaining_rounds -= 1 def possible_winners(self) -> list: ''' @@ -202,17 +256,18 @@ def possible_winners(self) -> list: if alter not in current_votes_score: current_votes_score[alter] = 0 possible_winners_alters = [] - logger.debug('total votes: %g',unanimously) - logger.debug('current vote scores: %s',current_votes_score) + logger.debug('total votes: %g', unanimously) + logger.debug('current vote scores: %s', current_votes_score) for alt, score in current_votes_score.items(): # if an alternative has a chance to get the remaining votes in the remaining time if (score + self.remaining_rounds + 1) >= unanimously: possible_winners_alters.append(alt) - logger.debug('alternative %s nominate as a winner candidate',alt) + logger.debug( + 'alternative %s nominate as a winner candidate', alt) # if none of the alternatives has a chance to be chosen - return default alternative if len(possible_winners_alters) == 0: return self.default_alternative - logger.debug('possible winners: %s',possible_winners_alters) + logger.debug('possible winners: %s', possible_winners_alters) return possible_winners_alters def change_vote(self, voter: int, new_vote: str, current_vote: str): @@ -248,8 +303,8 @@ def change_vote(self, voter: int, new_vote: str, current_vote: str): {1: 'b', 2: 'b', 3: 'b', 4: 'd', 5: 'c'} ''' self.voters_current_ballot[voter] = new_vote - logger.info('voter %s changed his vote from %s to %s', voter, current_vote, new_vote) - + logger.info('voter %s changed his vote from %s to %s', + voter, current_vote, new_vote) def choose_random_voter(self, voters: list) -> int: ''' From 9b8515b66a00483e648984e4250e45fdfd65cc52 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 27 Dec 2022 17:40:07 +0200 Subject: [PATCH 16/22] =?UTF-8?q?=F0=9F=A5=85error=20handles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index fb6de2c..2f9b508 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -82,14 +82,26 @@ def __init__(self, voters: tuple, voters_type: tuple, alternatives: tuple, voter random_selection - whether the selection of voter for changing their ballot. If False - the smallest voter's number will be selected ''' logger.info('ConsensusUnderDeadline object created') + if len(tuple(set(voters))) != len(voters): + raise ValueError('each voter must have unique id') self.voters = voters + flag = False + flag = [True for type in voters_type if (1 < type or type < 0)] + if flag: + raise TypeError('voter type can only be presented with 1 or 0') self.voters_type = voters_type + if len(tuple(set(alternatives))) != len(alternatives): + raise ValueError('each alternative must have unique id') + if len(voters) != len(voters_preferences): + raise TypeError('length of voters and preference must be the same') self.alternatives = alternatives self.voters_preferences = voters_preferences self.default_alternative = default_alternative # initiate first ballot for each voter by their top preference self.voters_current_ballot = {i + 1: voters_preferences[i][0] for i in range(len(voters_preferences))} + if remaining_rounds < 0: + raise ValueError(f'''time can't be negative''') self.remaining_rounds = remaining_rounds self.random_selection = random_selection @@ -302,6 +314,12 @@ def change_vote(self, voter: int, new_vote: str, current_vote: str): >>> print(cud.voters_current_ballot) {1: 'b', 2: 'b', 3: 'b', 4: 'd', 5: 'c'} ''' + if voter not in self.voters: + raise ValueError(f'''voter doesn't exist''') + if new_vote not in self.alternatives or current_vote not in self.alternatives: + raise ValueError(f'''given alternative doesn't exist''') + if new_vote == current_vote: + raise ValueError(f'''voter can't change his ballot to current one''') self.voters_current_ballot[voter] = new_vote logger.info('voter %s changed his vote from %s to %s', voter, current_vote, new_vote) From 0c60e723eb5c3e7c88d494bba623e6282b138305 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 27 Dec 2022 17:54:16 +0200 Subject: [PATCH 17/22] =?UTF-8?q?=E2=99=BB=EF=B8=8Frestructure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_consensus_under_deadline.py | 127 ++++++++++++++++ .../test_consensus_under_deadline.py | 137 ------------------ 2 files changed, 127 insertions(+), 137 deletions(-) create mode 100644 test_functionality/test_consensus_under_deadline.py delete mode 100644 test_performance/test_consensus_under_deadline.py diff --git a/test_functionality/test_consensus_under_deadline.py b/test_functionality/test_consensus_under_deadline.py new file mode 100644 index 0000000..7cadcf7 --- /dev/null +++ b/test_functionality/test_consensus_under_deadline.py @@ -0,0 +1,127 @@ +from ..py3votecore.consensus_under_deadline import ConsensusUnderDeadline +import unittest + +class TestConsensusUnderDeadline(unittest.TestCase): + def setUp(self) -> None: + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 2 + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + + def test_init_args(self) -> None: + v = (1, 2, 3) + v_type = (0, 0, 0) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 2 + # voters character is binary + with self.assertRaises(TypeError): + self.cud = ConsensusUnderDeadline(voters=v, voters_type = (1, 2, 3), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # each voter key must bey unique + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(voters=(1, 1, 1), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.cud = ConsensusUnderDeadline(voters=(1, 1, 2), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # each alternative key must bey unique + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'a', 'c'), default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'c', 'c'), default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # number of voters and voter's type must be aligned + with self.assertRaises(TypeError): + self.cud = ConsensusUnderDeadline(voters=(1, 2, 3), voters_type = (1, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.cud = ConsensusUnderDeadline(voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # given time must be >= 0 + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(remaining_rounds=-1, voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + self.cud = ConsensusUnderDeadline(remaining_rounds=-3, voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + + def test_possible_winners(self) -> None: + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 0 + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.assertEqual(self.cud.possible_winners() ,'null') + self.cud = ConsensusUnderDeadline(voters=(1, 2, 3, 4, 5), voters_type = (0, 0, 0, 0, 0), alternatives=('a', 'b', 'c', 'd'), voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']], default_alternative=df_alter,remaining_rounds=4, random_selection=False) + v = (1, 2, 3, 4, 5) + v_type = (0, 0, 0, 0, 0) + alters = ('a', 'b', 'c', 'd') + vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + self.cud = ConsensusUnderDeadline(remaining_rounds=4,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c', 'd']) + self.cud = ConsensusUnderDeadline(remaining_rounds=3,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) + + def test_change_vote_args(self): + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + # given voter isn't exist + with self.assertRaises(ValueError): + self.cud.change_vote(5, 'a', 'a') + self.cud.change_vote(4, 'a', 'a') + # given alternative isn't exist + with self.assertRaises(ValueError): + self.cud.change_vote(1, 'd', 'b') + self.cud.change_vote(1, 'e', 'd') + # user hasn't changed his ballot + with self.assertRaises(ValueError): + self.cud.change_vote(1, 'a', 'a') + self.cud.change_vote(2, 'b', 'b') + + def test_change_vote_results(self): + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.cud.change_vote(1, 'c', 'a') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'b', 3:'c'} ) + self.cud.change_vote(2, 'c', 'b') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'c'} ) + self.cud.change_vote(3, 'a', 'c') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'a'} ) + + def test_votes_calculate_results(self): + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) + vp =[['a', 'b', 'c'], ['a', 'b', 'a'],['c', 'a', 'b']] + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 2, 'c': 1 }) + vp =[['a', 'b', 'c'], ['a', 'b', 'a'],['a', 'c', 'b']] + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 3 }) + + def test_mdvr(self) -> None: + v = (1, 2, 3, 4, 5) + v_type = (0, 0, 0, 0, 0) + alters = ('a', 'b', 'c', 'd') + df_alter = 'null' + vp = [['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + t = 4 + self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False), 'b') # classic example + self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=0, random_selection=False), 'null') # test case with no time to change the vote + self.cud.voters_current_ballot = {1: 'a', 2:'a', 3:'a'} + self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['a', 'c', 'b', 'd'], ['a', 'b', 'c', 'd'], ['a', 'b', 'd', 'c']], remaining_rounds=t, random_selection=False), 'a') # test case with unanimously on the start + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/test_performance/test_consensus_under_deadline.py b/test_performance/test_consensus_under_deadline.py deleted file mode 100644 index 8903c44..0000000 --- a/test_performance/test_consensus_under_deadline.py +++ /dev/null @@ -1,137 +0,0 @@ -from py3votecore.consensus_under_deadline import ConsensusUnderDeadline -import unittest - -class TestConsensusUnderDeadline(unittest.TestCase): - def setUp(self) -> None: - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - v_cur_ballot = {1: 'a', 2:'b', 3:'c'} - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 2 - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - - def test_init_args(self) -> None: - v = (1, 2, 3) - v_type = (1, 2, 3) - alters = ('a', 'b', 'c') - df_alter = 'null' - v_cur_ballot = {1: 'a', 2:'b', 3:'c'} - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 2 - # voters character is binary - with self.assertRaises(TypeError): - self.cud = ConsensusUnderDeadline(voters=v, voters_type = (1, 2, 3), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - # each voter key must bey unique - with self.assertRaises(ValueError): - self.cud = ConsensusUnderDeadline(voters=(1, 1, 1), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - self.cud = ConsensusUnderDeadline(voters=(1, 1, 2), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - # each alternative key must bey unique - with self.assertRaises(ValueError): - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'a', 'c'), default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'c', 'c'), default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - # number of voters and voter's type must be aligned - with self.assertRaises(TypeError): - self.cud = ConsensusUnderDeadline(voters=(1, 2, 3), voters_type = (1, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - self.cud = ConsensusUnderDeadline(voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot, remaining_rounds=t) - # voters ballot must match the existing voters and alternative - with self.assertRaises(ValueError): - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={0: 'a', 2:'b', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={1: 'a', 2:'t', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={1: 'a', 2:'%', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, voters_current_ballot={7: '&', 4:'^', 3:'c'}, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t) - # given time must be >= 0 - with self.assertRaises(ValueError): - self.cud = ConsensusUnderDeadline(remaining_rounds=-1, voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - self.cud = ConsensusUnderDeadline(remaining_rounds=-3, voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - - def test_deploy_algorithm_results(self) -> None: - self.assertEqual(self.cud.deploy_algorithm(), 'b') # classic example - self.cud.remaining_rounds = 0 - self.assertEqual(self.cud.deploy_algorithm(), 'null') # test case with no time to change the vote - self.cud.voters_current_ballot = {1: 'a', 2:'a', 3:'a'} - self.assertEqual(self.cud.deploy_algorithm(), 'a') # test case with unanimously on the start - self.cud.remaining_rounds = 3 - self.cud.voters_preferences =[['a', 'b', 'c'], ['b', 'a', 'c'],['c', 'a', 'c']] - self.assertEqual(self.cud.deploy_algorithm(), 'a') # voters have same pretty close preference - - def test_possible_winners(self) -> None: - self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - v_cur_ballot = {1: 'a', 2:'b', 3:'c'} - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 1 - self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - self.assertEqual(self.cud.possible_winners() ,[]) - self.cud = ConsensusUnderDeadline(remaining_rounds=4,voters=(1, 2, 3, 4, 5), voters_type = (0, 0, 0, 0, 0), alternatives=('a', 'b', 'c', 'd'), default_alternative=df_alter,voters_preferences={1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' }, voters_current_ballot=v_cur_ballot) - v = (1, 2, 3, 4, 5) - v_type = (0, 0, 0, 0, 0) - alters = ('a', 'b', 'c', 'd') - v_cur_ballot = {1: 'a', 2:'a', 3:'b', 4:'b', 5:'c' } - vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] - t = 4 - self.cud = ConsensusUnderDeadline(remaining_rounds=1,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) - t = 3 - self.cud = ConsensusUnderDeadline(remaining_rounds=1,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - self.assertEqual(self.cud.possible_winners() ,['a', 'b']) - - def test_change_vote_args(self): - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - v_cur_ballot = {1: 'a', 2:'b', 3:'c'} - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 1 - self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - # given voter isn't exist - with self.assertRaises(ValueError): - self.cud.change_vote(5, 'a') - self.cud.change_vote(4, 'a') - # given alternative isn't exist - with self.assertRaises(ValueError): - self.cud.change_vote(1, 'd') - self.cud.change_vote(1, 'e') - # user hasn't changed his ballot - with self.assertRaises(ValueError): - self.cud.change_vote(1, 'a') - self.cud.change_vote(2, 'b') - - def test_change_vote_results(self): - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - v_cur_ballot = {1: 'a', 2:'b', 3:'c'} - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 1 - self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - self.cud.change_vote(1, 'c') - self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'b', 3:'c'} ) - self.cud.change_vote(2, 'c') - self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'c'} ) - self.cud.change_vote(3, 'a') - self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'a'} ) - - def test_votes_calculate_results(self): - self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - v_cur_ballot = {1: 'a', 2:'a', 3:'c'} - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 1 - self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 2, 'b': 0, 'c': 1 }) - v_cur_ballot = {1: 'a', 2:'a', 3:'a'} - self.cud = ConsensusUnderDeadline(remaining_rounds=t,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, voters_current_ballot=v_cur_ballot) - self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 3, 'b': 0, 'c': 0 }) - -if __name__ == '__main__': - unittest.main() \ No newline at end of file From 1674dcc62c981da17af6e7daff41b25ed96b3232 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Tue, 27 Dec 2022 18:14:38 +0200 Subject: [PATCH 18/22] =?UTF-8?q?=E2=99=BB=EF=B8=8Frestructure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/test_consensus_under_deadline.py | 127 +++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 py3votecore/test_consensus_under_deadline.py diff --git a/py3votecore/test_consensus_under_deadline.py b/py3votecore/test_consensus_under_deadline.py new file mode 100644 index 0000000..06e1170 --- /dev/null +++ b/py3votecore/test_consensus_under_deadline.py @@ -0,0 +1,127 @@ +from consensus_under_deadline import ConsensusUnderDeadline, mdvr +import unittest + +class TestConsensusUnderDeadline(unittest.TestCase): + def setUp(self) -> None: + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 2 + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + + def test_init_args(self) -> None: + v = (1, 2, 3) + v_type = (0, 0, 0) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 2 + # voters character is binary + with self.assertRaises(TypeError): + self.cud = ConsensusUnderDeadline(voters=v, voters_type = (1, 2, 3), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # each voter key must bey unique + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(voters=(1, 1, 1), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.cud = ConsensusUnderDeadline(voters=(1, 1, 2), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # each alternative key must bey unique + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'a', 'c'), default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'c', 'c'), default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # number of voters and voter's type must be aligned + with self.assertRaises(TypeError): + self.cud = ConsensusUnderDeadline(voters=(1, 2, 3), voters_type = (1, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.cud = ConsensusUnderDeadline(voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + # given time must be >= 0 + with self.assertRaises(ValueError): + self.cud = ConsensusUnderDeadline(remaining_rounds=-1, voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + self.cud = ConsensusUnderDeadline(remaining_rounds=-3, voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + + def test_possible_winners(self) -> None: + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 0 + self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) + self.assertEqual(self.cud.possible_winners() ,'null') + self.cud = ConsensusUnderDeadline(voters=(1, 2, 3, 4, 5), voters_type = (0, 0, 0, 0, 0), alternatives=('a', 'b', 'c', 'd'), voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']], default_alternative=df_alter,remaining_rounds=4, random_selection=False) + v = (1, 2, 3, 4, 5) + v_type = (0, 0, 0, 0, 0) + alters = ('a', 'b', 'c', 'd') + vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + self.cud = ConsensusUnderDeadline(remaining_rounds=4,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c', 'd']) + self.cud = ConsensusUnderDeadline(remaining_rounds=3,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) + self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) + + def test_change_vote_args(self): + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + # given voter isn't exist + with self.assertRaises(ValueError): + self.cud.change_vote(5, 'a', 'a') + self.cud.change_vote(4, 'a', 'a') + # given alternative isn't exist + with self.assertRaises(ValueError): + self.cud.change_vote(1, 'd', 'b') + self.cud.change_vote(1, 'e', 'd') + # user hasn't changed his ballot + with self.assertRaises(ValueError): + self.cud.change_vote(1, 'a', 'a') + self.cud.change_vote(2, 'b', 'b') + + def test_change_vote_results(self): + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.cud.change_vote(1, 'c', 'a') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'b', 3:'c'} ) + self.cud.change_vote(2, 'c', 'b') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'c'} ) + self.cud.change_vote(3, 'a', 'c') + self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'a'} ) + + def test_votes_calculate_results(self): + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) + v = (1, 2, 3) + v_type = (1, 1, 1) + alters = ('a', 'b', 'c') + df_alter = 'null' + vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] + t = 1 + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) + vp =[['a', 'b', 'c'], ['a', 'b', 'a'],['c', 'a', 'b']] + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 2, 'c': 1 }) + vp =[['a', 'b', 'c'], ['a', 'b', 'a'],['a', 'c', 'b']] + self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) + self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 3 }) + + def test_mdvr(self) -> None: + v = (1, 2, 3, 4, 5) + v_type = (0, 0, 0, 0, 0) + alters = ('a', 'b', 'c', 'd') + df_alter = 'null' + vp = [['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] + t = 4 + self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False), 'b') # classic example + self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=0, random_selection=False), 'null') # test case with no time to change the vote + self.cud.voters_current_ballot = {1: 'a', 2:'a', 3:'a'} + self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['a', 'c', 'b', 'd'], ['a', 'b', 'c', 'd'], ['a', 'b', 'd', 'c']], remaining_rounds=t, random_selection=False), 'a') # test case with unanimously on the start + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 5188eba4e752b90e361723c56dfbb23ced3f7085 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Sun, 1 Jan 2023 12:46:05 +0200 Subject: [PATCH 19/22] =?UTF-8?q?=E2=9C=85running=20time=20for=20algorithm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 6 +- .../consensus_under_deadline_performance.py | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 py3votecore/consensus_under_deadline_performance.py diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 2f9b508..de99b3f 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -3,10 +3,9 @@ import logging from collections import Counter -logging.basicConfig(filename='running_algorithm.log', - encoding='utf-8', level=logging.DEBUG) -logger = logging.getLogger() +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger() def mdvr(voters: tuple, voters_type: tuple, alternatives: tuple, voters_preferences: list, default_alternative: str, remaining_rounds: int, random_selection: bool): @@ -386,6 +385,5 @@ def votes_calculate(ballots: dict) -> dict: ''' return dict(Counter(ballots.values())) - if __name__ == '__main__': doctest.testmod() diff --git a/py3votecore/consensus_under_deadline_performance.py b/py3votecore/consensus_under_deadline_performance.py new file mode 100644 index 0000000..868f18f --- /dev/null +++ b/py3votecore/consensus_under_deadline_performance.py @@ -0,0 +1,55 @@ +import random +import time +import matplotlib.pyplot as plt +from consensus_under_deadline import mdvr + +TOTAL_VOTERS = 250 +TOTAL_ALTERNATIVES = 26 +CHARS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] + +def measure_running_time(func, *args): + start_time = time.perf_counter() + func(*args) + end_time = time.perf_counter() + return end_time - start_time + +def create_random_alternatives_preference(size): + # Create a list of characters to choose from + + arr = [] + + for i in range(size): + arr.append(CHARS[i]) + + # Choose n random characters without replacement + preference = random.sample(arr, size) + + return preference +#voters: tuple, voters_type: tuple, alternatives: tuple, voters_preferences: list, + #default_alternative: str, remaining_rounds: int, random_selection: bool + +running_times = [] +input_sizes = [] +for i in range (1, TOTAL_VOTERS): + input_sizes.append(i) + voters = tuple([x + 1 for x in range(i)]) + voters_type = [random.randint(0, 1) for k in range(i)] + alternatives_preferences = [] + default_alternative = 'null' + remaining_rounds = int(TOTAL_VOTERS) + random_selection = False + alternative_size = len(CHARS) + alters = tuple(CHARS[:alternative_size]) + for j in range(i): + alternatives_preferences.append(create_random_alternatives_preference(alternative_size)) + elapsed_time = measure_running_time(mdvr, voters, voters_type, alters, alternatives_preferences, default_alternative, remaining_rounds, remaining_rounds ) + running_times.append(elapsed_time) + +print(len(input_sizes)) +print(running_times) + +plt.plot(input_sizes, running_times) +plt.xlabel("voters size") +plt.ylabel("Running time (seconds)") +plt.show() \ No newline at end of file From 7817d0a4e9c68e29dc86c7630ce8d63119062411 Mon Sep 17 00:00:00 2001 From: rafasu4 Date: Mon, 2 Jan 2023 20:40:15 +0200 Subject: [PATCH 20/22] =?UTF-8?q?=E2=9A=A1improvement=20for=20edge=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 15 ++++++++++----- py3votecore/test_consensus_under_deadline.py | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index de99b3f..4e27571 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -167,10 +167,13 @@ def deploy_algorithm(self): possible_winners = self.possible_winners() logger.debug('round number: %g', self.remaining_rounds) # if no alternative is eligible to win - no need to keep iterating - if possible_winners == self.default_alternative: + if possible_winners == [self.default_alternative]: logger.debug( 'possible winners: %s. Algorithm is finished with no winner', self.default_alternative) break + # if only one option is valid + elif len(possible_winners) == 1: + return possible_winners[0] voters_candidate = [] # candidate voters to change their ballot # select voters to change their ballot base on their type and selected alternative for voter_index in range(len(self.voters)): @@ -188,12 +191,12 @@ def deploy_algorithm(self): ballot_change_voter = self.choose_random_voter( voters_candidate) ballot_change_voter_preference = self.voters_preferences[ballot_change_voter - 1] - ballot_change_voter_current_ballot = self.voters_current_ballot[ballot_change_voter] + voter_current_ballot = self.voters_current_ballot[ballot_change_voter] # voter chooses to change his ballot to the top possible alternative (besides his current) for preference in ballot_change_voter_preference: - if preference in possible_winners and preference != ballot_change_voter_current_ballot: + if preference in possible_winners and preference != voter_current_ballot: self.change_vote( - ballot_change_voter, preference, ballot_change_voter_current_ballot) + ballot_change_voter, preference, voter_current_ballot) break # if unanimously hasn't reached - return default alternative return self.default_alternative @@ -201,6 +204,8 @@ def deploy_algorithm(self): def round_passed(self): ''' Lower round by one - symbolize a passing iteration. + + ---------------------------------TESTS--------------------------------- >>> v = {1, 2, 3} >>> v_type = {1, 1, 1} >>> alters = {'a', 'b', 'c'} @@ -277,7 +282,7 @@ def possible_winners(self) -> list: 'alternative %s nominate as a winner candidate', alt) # if none of the alternatives has a chance to be chosen - return default alternative if len(possible_winners_alters) == 0: - return self.default_alternative + return [self.default_alternative] logger.debug('possible winners: %s', possible_winners_alters) return possible_winners_alters diff --git a/py3votecore/test_consensus_under_deadline.py b/py3votecore/test_consensus_under_deadline.py index 06e1170..81de2a0 100644 --- a/py3votecore/test_consensus_under_deadline.py +++ b/py3votecore/test_consensus_under_deadline.py @@ -47,7 +47,7 @@ def test_possible_winners(self) -> None: vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] t = 0 self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - self.assertEqual(self.cud.possible_winners() ,'null') + self.assertEqual(self.cud.possible_winners() ,['null']) self.cud = ConsensusUnderDeadline(voters=(1, 2, 3, 4, 5), voters_type = (0, 0, 0, 0, 0), alternatives=('a', 'b', 'c', 'd'), voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']], default_alternative=df_alter,remaining_rounds=4, random_selection=False) v = (1, 2, 3, 4, 5) v_type = (0, 0, 0, 0, 0) From 89b79f531a06f0b9ff024ac5762a21b349495cda Mon Sep 17 00:00:00 2001 From: Stycks4 Date: Wed, 18 Jan 2023 22:01:53 +0200 Subject: [PATCH 21/22] =?UTF-8?q?=E2=99=BB=EF=B8=8Frestructure=20hierarchy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consensus_under_deadline_performance.py | 55 -------- py3votecore/test_consensus_under_deadline.py | 127 ------------------ .../test_consensus_under_deadline.py | 4 +- 3 files changed, 2 insertions(+), 184 deletions(-) delete mode 100644 py3votecore/consensus_under_deadline_performance.py delete mode 100644 py3votecore/test_consensus_under_deadline.py diff --git a/py3votecore/consensus_under_deadline_performance.py b/py3votecore/consensus_under_deadline_performance.py deleted file mode 100644 index 868f18f..0000000 --- a/py3votecore/consensus_under_deadline_performance.py +++ /dev/null @@ -1,55 +0,0 @@ -import random -import time -import matplotlib.pyplot as plt -from consensus_under_deadline import mdvr - -TOTAL_VOTERS = 250 -TOTAL_ALTERNATIVES = 26 -CHARS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] - -def measure_running_time(func, *args): - start_time = time.perf_counter() - func(*args) - end_time = time.perf_counter() - return end_time - start_time - -def create_random_alternatives_preference(size): - # Create a list of characters to choose from - - arr = [] - - for i in range(size): - arr.append(CHARS[i]) - - # Choose n random characters without replacement - preference = random.sample(arr, size) - - return preference -#voters: tuple, voters_type: tuple, alternatives: tuple, voters_preferences: list, - #default_alternative: str, remaining_rounds: int, random_selection: bool - -running_times = [] -input_sizes = [] -for i in range (1, TOTAL_VOTERS): - input_sizes.append(i) - voters = tuple([x + 1 for x in range(i)]) - voters_type = [random.randint(0, 1) for k in range(i)] - alternatives_preferences = [] - default_alternative = 'null' - remaining_rounds = int(TOTAL_VOTERS) - random_selection = False - alternative_size = len(CHARS) - alters = tuple(CHARS[:alternative_size]) - for j in range(i): - alternatives_preferences.append(create_random_alternatives_preference(alternative_size)) - elapsed_time = measure_running_time(mdvr, voters, voters_type, alters, alternatives_preferences, default_alternative, remaining_rounds, remaining_rounds ) - running_times.append(elapsed_time) - -print(len(input_sizes)) -print(running_times) - -plt.plot(input_sizes, running_times) -plt.xlabel("voters size") -plt.ylabel("Running time (seconds)") -plt.show() \ No newline at end of file diff --git a/py3votecore/test_consensus_under_deadline.py b/py3votecore/test_consensus_under_deadline.py deleted file mode 100644 index 81de2a0..0000000 --- a/py3votecore/test_consensus_under_deadline.py +++ /dev/null @@ -1,127 +0,0 @@ -from consensus_under_deadline import ConsensusUnderDeadline, mdvr -import unittest - -class TestConsensusUnderDeadline(unittest.TestCase): - def setUp(self) -> None: - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 2 - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - - def test_init_args(self) -> None: - v = (1, 2, 3) - v_type = (0, 0, 0) - alters = ('a', 'b', 'c') - df_alter = 'null' - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 2 - # voters character is binary - with self.assertRaises(TypeError): - self.cud = ConsensusUnderDeadline(voters=v, voters_type = (1, 2, 3), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - # each voter key must bey unique - with self.assertRaises(ValueError): - self.cud = ConsensusUnderDeadline(voters=(1, 1, 1), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - self.cud = ConsensusUnderDeadline(voters=(1, 1, 2), voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - # each alternative key must bey unique - with self.assertRaises(ValueError): - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'a', 'c'), default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=('a', 'c', 'c'), default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - # number of voters and voter's type must be aligned - with self.assertRaises(TypeError): - self.cud = ConsensusUnderDeadline(voters=(1, 2, 3), voters_type = (1, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - self.cud = ConsensusUnderDeadline(voters=(1), voters_type = (0, 1), alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - # given time must be >= 0 - with self.assertRaises(ValueError): - self.cud = ConsensusUnderDeadline(remaining_rounds=-1, voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) - self.cud = ConsensusUnderDeadline(remaining_rounds=-3, voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) - - def test_possible_winners(self) -> None: - self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 0 - self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - self.assertEqual(self.cud.possible_winners() ,['null']) - self.cud = ConsensusUnderDeadline(voters=(1, 2, 3, 4, 5), voters_type = (0, 0, 0, 0, 0), alternatives=('a', 'b', 'c', 'd'), voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']], default_alternative=df_alter,remaining_rounds=4, random_selection=False) - v = (1, 2, 3, 4, 5) - v_type = (0, 0, 0, 0, 0) - alters = ('a', 'b', 'c', 'd') - vp =[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] - self.cud = ConsensusUnderDeadline(remaining_rounds=4,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) - self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c', 'd']) - self.cud = ConsensusUnderDeadline(remaining_rounds=3,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, random_selection=False) - self.assertEqual(self.cud.possible_winners() ,['a', 'b', 'c']) - - def test_change_vote_args(self): - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 1 - self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) - # given voter isn't exist - with self.assertRaises(ValueError): - self.cud.change_vote(5, 'a', 'a') - self.cud.change_vote(4, 'a', 'a') - # given alternative isn't exist - with self.assertRaises(ValueError): - self.cud.change_vote(1, 'd', 'b') - self.cud.change_vote(1, 'e', 'd') - # user hasn't changed his ballot - with self.assertRaises(ValueError): - self.cud.change_vote(1, 'a', 'a') - self.cud.change_vote(2, 'b', 'b') - - def test_change_vote_results(self): - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 1 - self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) - self.cud.change_vote(1, 'c', 'a') - self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'b', 3:'c'} ) - self.cud.change_vote(2, 'c', 'b') - self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'c'} ) - self.cud.change_vote(3, 'a', 'c') - self.assertEqual(self.cud.voters_current_ballot, {1: 'c', 2:'c', 3:'a'} ) - - def test_votes_calculate_results(self): - self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) - v = (1, 2, 3) - v_type = (1, 1, 1) - alters = ('a', 'b', 'c') - df_alter = 'null' - vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] - t = 1 - self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) - self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 1, 'b': 1, 'c': 1 }) - vp =[['a', 'b', 'c'], ['a', 'b', 'a'],['c', 'a', 'b']] - self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) - self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 2, 'c': 1 }) - vp =[['a', 'b', 'c'], ['a', 'b', 'a'],['a', 'c', 'b']] - self.cud = ConsensusUnderDeadline(remaining_rounds=t, random_selection=False,voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp) - self.assertEqual(ConsensusUnderDeadline.votes_calculate(self.cud.voters_current_ballot), {'a': 3 }) - - def test_mdvr(self) -> None: - v = (1, 2, 3, 4, 5) - v_type = (0, 0, 0, 0, 0) - alters = ('a', 'b', 'c', 'd') - df_alter = 'null' - vp = [['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']] - t = 4 - self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False), 'b') # classic example - self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=0, random_selection=False), 'null') # test case with no time to change the vote - self.cud.voters_current_ballot = {1: 'a', 2:'a', 3:'a'} - self.assertEqual(mdvr(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['a', 'c', 'b', 'd'], ['a', 'b', 'c', 'd'], ['a', 'b', 'd', 'c']], remaining_rounds=t, random_selection=False), 'a') # test case with unanimously on the start - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/test_functionality/test_consensus_under_deadline.py b/test_functionality/test_consensus_under_deadline.py index 7cadcf7..329e08d 100644 --- a/test_functionality/test_consensus_under_deadline.py +++ b/test_functionality/test_consensus_under_deadline.py @@ -1,4 +1,4 @@ -from ..py3votecore.consensus_under_deadline import ConsensusUnderDeadline +from py3votecore.consensus_under_deadline import ConsensusUnderDeadline, mdvr import unittest class TestConsensusUnderDeadline(unittest.TestCase): @@ -47,7 +47,7 @@ def test_possible_winners(self) -> None: vp =[['a', 'b', 'c'], ['b', 'c', 'a'],['c', 'a', 'b']] t = 0 self.cud = ConsensusUnderDeadline(voters=v, voters_type = v_type, alternatives=alters, default_alternative=df_alter,voters_preferences=vp, remaining_rounds=t, random_selection=False) - self.assertEqual(self.cud.possible_winners() ,'null') + self.assertEqual(self.cud.possible_winners() ,['null']) self.cud = ConsensusUnderDeadline(voters=(1, 2, 3, 4, 5), voters_type = (0, 0, 0, 0, 0), alternatives=('a', 'b', 'c', 'd'), voters_preferences=[['a', 'b', 'c', 'd'], ['a', 'c', 'b', 'd'], ['b', 'c', 'a', 'd'], ['b', 'a', 'c', 'd'], ['c', 'b', 'd', 'a']], default_alternative=df_alter,remaining_rounds=4, random_selection=False) v = (1, 2, 3, 4, 5) v_type = (0, 0, 0, 0, 0) From 3a76f9cf12f51602cdb71a60f6b5b0ce6b553d74 Mon Sep 17 00:00:00 2001 From: Stycks4 Date: Thu, 2 Feb 2023 10:39:04 +0200 Subject: [PATCH 22/22] =?UTF-8?q?=E2=9C=8F=EF=B8=8Fintro=20for=20the=20alg?= =?UTF-8?q?orithm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- py3votecore/consensus_under_deadline.py | 15 +++++++++++---- .../test_consensus_under_deadline.py | 9 +++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/py3votecore/consensus_under_deadline.py b/py3votecore/consensus_under_deadline.py index 4e27571..88a0227 100644 --- a/py3votecore/consensus_under_deadline.py +++ b/py3votecore/consensus_under_deadline.py @@ -1,12 +1,17 @@ +''' + Consensus under deadlin (CUD) is a group decision algorithm based on a time-bounded iterative voting + process, based on preferences of multiple voters. In this implementation, The voter's task is to find an + alternative which will be agreed upon all of the voters, i.e unanimous. + This algorithm is based on the following article: https://arxiv.org/abs/1905.07173 + Authors: Marina Bannikova, Lihi Dery, Svetlana Obraztsova, Zinovi Rabinovich, Jeffrey S. Rosenschein + Programmer: Raphael Suliman + Date: January 2023. +''' import doctest import random import logging from collections import Counter - -logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger() - def mdvr(voters: tuple, voters_type: tuple, alternatives: tuple, voters_preferences: list, default_alternative: str, remaining_rounds: int, random_selection: bool): ''' @@ -391,4 +396,6 @@ def votes_calculate(ballots: dict) -> dict: return dict(Counter(ballots.values())) if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + logger = logging.getLogger() doctest.testmod() diff --git a/test_functionality/test_consensus_under_deadline.py b/test_functionality/test_consensus_under_deadline.py index 329e08d..3ed0df9 100644 --- a/test_functionality/test_consensus_under_deadline.py +++ b/test_functionality/test_consensus_under_deadline.py @@ -1,3 +1,12 @@ +''' + Consensus under deadlin (CUD) is a group decision algorithm based on a time-bounded iterative voting + process, based on preferences of multiple voters. In this implementation, The voter's task is to find an + alternative which will be agreed upon all of the voters, i.e unanimous. + This algorithm is based on the following article: https://arxiv.org/abs/1905.07173 + Authors: Marina Bannikova, Lihi Dery, Svetlana Obraztsova, Zinovi Rabinovich, Jeffrey S. Rosenschein + Programmer: Raphael Suliman + Date: January 2023. +''' from py3votecore.consensus_under_deadline import ConsensusUnderDeadline, mdvr import unittest