class PushOver: """ Voting a Candidate that is easy to beat insincerely high in the first round so that the second round is easy to win """ def __init__(self, preferences, voting_scheme): self.preferences = preferences self.voting_scheme = voting_scheme self.vs = VotingScheme(preferences, len(preferences[0])) self.outcome = self.vs.execute_voting(voting_scheme) self.winner_list = self.vs.get_outcome() self.happiness = self.vs.calc_happiness() self.hate = self.calculate_hate() self.hate_list = self.calculate_hated_candidates() # self.calculate() self.change_voter_votes_borda(1) def calculate(self): print("Outcome", self.outcome) print("Winner list", self.winner_list) # for candidate in self.winner_list: # print("Candidate", candidate, "has", self.outcome[candidate], " votes") print("Hated list", self.hate_list) for voter, voter_preferences in self.preferences.items(): # If the preferred candidate of the voter would win without doing anything # if voter_preferences[0] is self.winner_list[0] or voter_preferences[0] is self.winner_list[1]: # Maybe it is interesting to vote for someone else that other voters # do not like so that the second round is easier # print() # print("Voter", voter, "is very HAPPY") # print(self.preferences[voter]) self.change_voter_votes_plurality(voter) """ In plurality voting only one candidate can be voted and it is therefore the only that should be changed. """ def change_voter_votes_plurality(self, voter): self.change_voter_vote(voter, 0) """ In voting for two, only the two first candidates in the preference list get a point. Since both of them get a point it is not important which one is the first or second in the manipulation. """ def change_voter_votes_for_two(self, voter): self.change_voter_vote(voter, 0) self.change_voter_vote(voter, 1) """ In anti-plurality voting, only the least preferred candidate in the preference list does not get a point. Therefore, only changing the least preferred candidate could affect the outcome of the voting """ def change_voter_votes_anti_plurality(self, voter): self.change_voter_vote(voter, len(self.preferences[voter])) """ In borda voting every preference gets a point and the higher it is in the preference list, the more points it gets. Therefore, all possible combinations of preferences have to be checked """ def change_voter_votes_borda(self, voter): # TODO i = 0 for possible in itertools.permutations(self.preferences[voter]): print("A new possibility is", possible) i += 1 print(i) # self.change_voter_vote(voter, len(self.preferences[voter])) return def change_voter_vote(self, voter, vote): new_preferences = self.preferences.copy() possible_strategy = [] true_preferred_cand = self.preferences[voter][0] opponent = self.vs.get_opponent(true_preferred_cand) for candidate in self.hate_list: voter_preferences = self.preferences[voter].copy() if candidate is voter_preferences[0]: # It does not make sense making win someone less hated than the person we would like that wins break # print() # Change the preferred voted c_index_v_pref = voter_preferences.index(candidate) help = voter_preferences[c_index_v_pref] voter_preferences[c_index_v_pref] = true_preferred_cand # print("The voter", voter, "was voting for", voter_preferences[0], "and is gonna try to vote for", help) voter_preferences[vote] = help new_preferences[voter] = voter_preferences # Create a new voting scheme with the new preferences new_vs = VotingScheme(new_preferences, len(new_preferences[0])) # Vote a first time with the new preferences new_vs.execute_voting(self.voting_scheme) first_outcome = new_vs.get_outcome() new_opponent = new_vs.get_opponent(true_preferred_cand) # If the outcome of applying the voting manipulation is the same one then it is not a good manipulation if collections.Counter(first_outcome[:2]) == collections.Counter( self.winner_list[:2]): continue elif self.hate_list.index(new_opponent) >= self.hate_list.index( opponent): continue # print("The result of the first round is", help_outcome) # Vote a first time new_vs.second_round(self.preferences) second_outcome = new_vs.get_outcome() # If after the second round the winner is the desired one then it is an acceptable way of strategic voting if new_vs.get_outcome()[0] is self.preferences[voter][0]: print("For the voter", voter, "that was voting for", self.preferences[voter][0], "a new possible strategies is voting for", help) print("The new outcome in first round would be", first_outcome, "and in the second", new_vs.get_outcome()) possible_strategy.append(help) # If the two first candidates are not the same as previously # and the preferred candidate of the voter is still in top 2 if len(possible_strategy) > 0: print("For the voter", voter, "the possible strategies are voting for", possible_strategy) def calculate_hate(self): hate = {} for voter, voter_preferences in self.preferences.items(): # Since the preferences are order being the most preferred in the position zero and the less liked # in the position n, it can also be understood as a measure of not liking or dislike for dislike, candidate in enumerate(voter_preferences): try: hate[candidate] += dislike except: hate[candidate] = dislike return hate def calculate_hated_candidates(self): return sorted(self.hate, key=self.hate.get, reverse=True)
class Model: COMPROMISE = "Compromised in favor of this candidate" BURYING = "This candidate was buried" BULLET_VOTING = "There was bulet voting in favor of this candidate" """ Voting a Candidate that is easy to beat insincerely high in the first round so that the second round is easy to win """ def __init__(self, preferences, voting_scheme_option): self.preferences = preferences self.voting_scheme_option = voting_scheme_option self.voting_scheme = VotingScheme(preferences, len(preferences[0])) self.points_outcome = self.voting_scheme.execute_voting( voting_scheme_option) self.outcome = self.voting_scheme.get_outcome() self.happiness = self.voting_scheme.calc_happiness(self.outcome) self.overall_happiness = self.voting_scheme.calc_overall_happiness( self.outcome) """ Calculates for every voter the possible strategic-voting result output possibly empty strategic-voting option """ def calculate(self, bullet_voting_allowed): strategic_voting_option = [] self.n_strategic_options = 0 for voter in range(len(self.preferences)): voter_max_hap = self.voting_scheme.calc_happiness( self.outcome)[voter] voter_original_hap = voter_max_hap voter_strategic_votes = {} candidates = self.preferences[voter] if self.voting_scheme_option != VotingSchemeOption.PLURALITY_VOTING and bullet_voting_allowed: self.calculate_bullet_voting(candidates, voter, voter_max_hap, voter_strategic_votes, voter_original_hap) elif self.voting_scheme_option == VotingSchemeOption.PLURALITY_VOTING: self.calculate_compromising_and_burying_plurality( voter, voter_max_hap, voter_strategic_votes, voter_original_hap) else: self.calculate_compromising_and_burying( voter, voter_max_hap, voter_strategic_votes, voter_original_hap) strategic_voting_option.append( self.evaluate_outcome(voter, voter_strategic_votes)) risk = self.n_strategic_options / len(self.preferences) return self.outcome, self.overall_happiness, strategic_voting_option, risk def calculate_compromising_and_burying_plurality(self, voter, voter_max_hap, voter_strategic_votes, voter_original_hap): for index, candidate in enumerate(self.preferences[voter]): new_voter = voter new_voter_preferences = self.preferences[voter].copy() # Change the preferred voted new_voter_preferences[index] = new_voter_preferences[0] new_voter_preferences[0] = candidate # print("The voter", voter, "was voting for", voter_preferences[0], "and is gonna try to vote for", help) new_outcome, new_voting_scheme = self.calculate_new_outcome( new_voter_preferences, voter) if new_outcome[0] is self.outcome[0]: continue new_happiness = self.voting_scheme.get_new_happiness_by_voter( voter, new_outcome) if new_happiness > voter_original_hap: self.n_strategic_options += 1 if new_happiness <= voter_max_hap: continue voter_max_hap = new_happiness voter_strategic_votes[new_voting_scheme] = new_outcome def calculate_compromising_and_burying(self, voter, voter_max_hap, voter_strategic_votes, voter_original_hap): for new_voter_preference in itertools.permutations( self.preferences[voter]): new_voter_preference = list(new_voter_preference) new_outcome, new_voting_scheme = self.calculate_new_outcome( new_voter_preference, voter) if new_outcome[0] is self.outcome[0]: continue new_happiness = self.voting_scheme.get_new_happiness_by_voter( voter, new_outcome) if new_happiness > voter_original_hap: self.n_strategic_options += 1 if new_happiness <= voter_max_hap: continue voter_max_hap = new_happiness voter_strategic_votes[new_voting_scheme] = new_outcome def calculate_bullet_voting(self, candidates, voter, voter_max_hap, voter_strategic_votes, voter_original_hap): # to perform bullet voting as strategic voting we have to generate the preference with only the first one for candidate in candidates: new_voter_preference = ["" for i in candidates] new_voter_preference[0] = candidate new_outcome, new_voting_scheme = self.calculate_new_outcome( new_voter_preference, voter) if new_outcome[0] is self.outcome[0]: continue new_happiness = self.voting_scheme.get_new_happiness_by_voter( voter, new_outcome) if new_happiness > voter_original_hap: self.n_strategic_options += 1 if new_happiness <= voter_max_hap: continue voter_strategic_votes[new_voting_scheme] = new_outcome """ Evaluate the input strategic vote for the input voter and return the kind of strategic vote applied and to which candidate. input @voter input @voter_strategic_votes output (dict/list/array?) changes """ def evaluate_outcome(self, voter, voter_strategic_votes): best_happiness = self.voting_scheme.get_new_happiness_by_voter( voter, self.outcome) changes = {} # for bullet prove change the preference in the array with "" # before checking if the position is greater/smaller than the original check if the position is "" new_preferences = [] new_final_outcome = [] new_overall_happiness = 0 for new_voting_scheme, new_outcome in voter_strategic_votes.items(): if self.voting_scheme.get_new_happiness_by_voter( voter, new_outcome) <= best_happiness: continue best_happiness = self.voting_scheme.get_new_happiness_by_voter( voter, new_outcome) changes = {} if new_voting_scheme.preferences[voter][1] is "": candidate = new_voting_scheme.preferences[voter][0] changes[candidate] = self.BULLET_VOTING new_preferences = new_voting_scheme.preferences[voter] new_final_outcome = new_outcome new_overall_happiness = self.voting_scheme.calc_overall_happiness( new_outcome) continue # Position is the index in the array and candidate the value for old_position, candidate in enumerate(self.outcome): if new_outcome.index(candidate) < old_position: # Compromising changes[candidate] = self.COMPROMISE elif new_outcome.index(candidate) > old_position: # Burying changes[candidate] = self.BURYING new_preferences = new_voting_scheme.preferences[voter] new_final_outcome = new_outcome new_overall_happiness = self.voting_scheme.calc_overall_happiness( new_outcome) return new_preferences, new_final_outcome, new_overall_happiness, changes def calculate_new_outcome(self, new_voter_preference, voter): new_preference = self.preferences.copy() new_preference[voter] = new_voter_preference new_voting_scheme = VotingScheme(new_preference, len(new_preference[0])) new_voting_scheme.execute_voting(self.voting_scheme_option) new_outcome = new_voting_scheme.get_outcome() return new_outcome, new_voting_scheme