def tau_sincere(self): """Tau-vector associated to sincere voting. Returns ------- TauVector * In Approval, all voters approve of their top candidate, and voters approve of their middle candidate if and only if their utility for her is strictly greater than 0.5. * In Plurality, all voters vote for their top candidate. * In Anti-plurality, all voters vote against their bottom candidate (i.e. for the other two). Notes ----- In Plurality and Anti-plurality, sincere and fanatic voting are the same. They differ only in Approval. """ t = self.d_ballot_share_weak_voters_sincere.copy() for ranking in RANKINGS: if self.voting_rule == APPROVAL: share_vote_one_two = self.have_ranking_with_utility_above_u(ranking, Fraction(1, 2)) share_vote_one = self.d_ranking_share[ranking] - share_vote_one_two t[ballot_one(ranking)] += share_vote_one t[ballot_one_two(ranking)] += share_vote_one_two elif self.voting_rule == PLURALITY: t[ballot_one(ranking)] += self.d_ranking_share[ranking] elif self.voting_rule == ANTI_PLURALITY: t[ballot_one_two(ranking)] += self.d_ranking_share[ranking] else: raise NotImplementedError return TauVector(t, voting_rule=self.voting_rule, symbolic=self.symbolic)
def is_equilibrium(self, strategy): """Whether a strategy is an equilibrium. Parameters ---------- strategy : StrategyTwelve A strategy that specifies at least all the rankings that are present in the profile. Returns ------- EquilibriumStatus Whether `strategy` is an equilibrium in this profile. Examples -------- >>> from fractions import Fraction >>> profile = ProfileTwelve({'ab_c': Fraction(1, 10), 'b_ac': Fraction(6, 10), ... 'c_ab': Fraction(2, 10), 'ca_b': Fraction(1, 10)}) >>> strategy = StrategyTwelve({'abc': 'ab', 'bac': 'b', 'cab': 'utility-dependent'}) >>> profile.is_equilibrium(strategy) EquilibriumStatus.EQUILIBRIUM """ d_ranking_best_response = self.tau(strategy).d_ranking_best_response status = EquilibriumStatus.EQUILIBRIUM for ranking, share in self.d_ranking_share.items(): if share == 0: continue best_response = d_ranking_best_response[ranking] if best_response.ballot == INCONCLUSIVE: # pragma: no cover - Should never happen status = min(status, EquilibriumStatus.INCONCLUSIVE) else: type_1 = ranking[:1] + '_' + ranking[1:] # E.g. a_bc type_12 = ranking[:2] + '_' + ranking[2:] # E.g. ab_c if best_response.ballot == UTILITY_DEPENDENT: if self.voting_rule == APPROVAL: best_ballot_1, best_ballot_12 = ballot_one( ranking), ballot_one_two(ranking) elif self.voting_rule == PLURALITY: best_ballot_1, best_ballot_12 = ballot_one( ranking), ballot_two(ranking) elif self.voting_rule == ANTI_PLURALITY: best_ballot_1, best_ballot_12 = ballot_one_three( ranking), ballot_one_two(ranking) else: raise NotImplementedError else: best_ballot_1 = best_response.ballot best_ballot_12 = best_response.ballot if self.d_type_share[type_1] > 0 and getattr( strategy, type_1) != best_ballot_1: return EquilibriumStatus.NOT_EQUILIBRIUM if self.d_type_share[type_12] > 0 and getattr( strategy, type_12) != best_ballot_12: return EquilibriumStatus.NOT_EQUILIBRIUM return status
def share_sincere_among_strategic_voters(self, strategy): """Share of strategic voters that happen to cast a sincere ballot. Parameters ---------- strategy : StrategyTwelve A strategy that specifies at least all the rankings that are present in the profile. Returns ------- Number The ratio of sincere voters among strategic voters. Examples -------- >>> from fractions import Fraction >>> profile = ProfileTwelve({'ab_c': Fraction(1, 10), 'b_ac': Fraction(6, 10), ... 'c_ab': Fraction(2, 10), 'ca_b': Fraction(1, 10)}) >>> strategy = StrategyTwelve({'abc': 'ab', 'bac': 'b', 'cab': 'utility-dependent'}) >>> profile.share_sincere_among_strategic_voters(strategy) 1 """ assert self.voting_rule == strategy.voting_rule # Weak orders: these voters are always "sincere", even in Plurality or Anti-Plurality. For example, in # Plurality, a voter a~b>c will either vote for a or b, and this is considered "sincere". share_sincere = sum(self.d_weak_order_share.values()) # Rankings for ranking, ballot in strategy.d_ranking_ballot.items(): if self.d_ranking_share[ranking] == 0: continue # For a ranking abc, ballot can be real ballots (e.g. 'a', 'ab'), '' or 'utility-dependent'. if self.voting_rule == APPROVAL: if ballot == ballot_one(ranking): share_sincere += self.have_ranking_with_utility_below_u( ranking, u=.5) elif ballot == ballot_one_two(ranking): share_sincere += self.have_ranking_with_utility_above_u( ranking, u=.5) else: share_sincere += self.d_ranking_share[ranking] elif self.voting_rule == PLURALITY: if ballot == ballot_one(ranking): share_sincere += self.d_ranking_share[ranking] elif ballot == UTILITY_DEPENDENT: share_sincere += self.have_ranking_with_utility_below_u( ranking, u=.5) elif self.voting_rule == ANTI_PLURALITY: if ballot == ballot_one_two(ranking): share_sincere += self.d_ranking_share[ranking] elif ballot == UTILITY_DEPENDENT: share_sincere += self.have_ranking_with_utility_above_u( ranking, u=.5) else: raise NotImplementedError return self.ce.simplify(share_sincere)
def _f(self): if getattr(self, ranking) == UTILITY_DEPENDENT: if getattr(self, 'voting_rule') in {APPROVAL, ANTI_PLURALITY}: return ballot_one_two(ranking) elif getattr(self, 'voting_rule') == PLURALITY: return ballot_two(ranking) else: raise NotImplementedError return getattr(self, ranking)
def __init__(self, d_ranking_ballot, d_weak_order_ballot=None, profile=None, voting_rule=None): """ >>> strategy = StrategyOrdinal({'abc': 'non_existing_ballot'}) Traceback (most recent call last): ValueError: Unknown strategy: non_existing_ballot """ voting_rule = self._get_voting_rule_(profile, voting_rule) # Prepare the dictionary of thresholds d_ranking_threshold = DictPrintingInOrderIgnoringZeros() for ranking, ballot in d_ranking_ballot.items(): if ballot == '': d_ranking_threshold[ranking] = None elif ballot == ballot_one(ranking) and voting_rule in { APPROVAL, PLURALITY }: d_ranking_threshold[ranking] = 1 elif ballot == ballot_two(ranking) and voting_rule == PLURALITY: d_ranking_threshold[ranking] = 0 elif (ballot in {ballot_one_two(ranking), ballot_one_two(ranking)[::-1]} and voting_rule in {APPROVAL, ANTI_PLURALITY}): d_ranking_threshold[ranking] = 0 elif (ballot in { ballot_one_three(ranking), ballot_one_three(ranking)[::-1] } and voting_rule == ANTI_PLURALITY): d_ranking_threshold[ranking] = 1 else: raise ValueError('Unknown strategy: ' + ballot) # Call parent class super().__init__(d=d_ranking_threshold, d_weak_order_ballot=d_weak_order_ballot, profile=profile, voting_rule=voting_rule)
def __init__(self, d, d_weak_order_ballot=None, ratio_optimistic=None, profile=None, voting_rule=None): voting_rule = self._get_voting_rule_(profile, voting_rule) # Prepare the dictionaries of thresholds and ratios self.d_ranking_threshold = DictPrintingInOrderIgnoringNone( {ranking: None for ranking in RANKINGS}) self.d_ranking_ratio_optimistic = DictPrintingInOrderIgnoringNone( {ranking: None for ranking in RANKINGS}) for ranking, value in d.items(): if isinstance(value, tuple): self.d_ranking_threshold[ ranking], self.d_ranking_ratio_optimistic[ranking] = value else: self.d_ranking_threshold[ranking] = value self.d_ranking_ratio_optimistic[ranking] = ratio_optimistic # Prepare the dictionary of ballots d_ranking_ballot = DictPrintingInOrderIgnoringZeros() for ranking, threshold in self.d_ranking_threshold.items(): if threshold is None: d_ranking_ballot[ranking] = '' elif threshold == 1: if voting_rule in {APPROVAL, PLURALITY}: d_ranking_ballot[ranking] = ballot_one(ranking) elif voting_rule == ANTI_PLURALITY: d_ranking_ballot[ranking] = ballot_one_three(ranking) else: raise NotImplementedError elif threshold == 0: if voting_rule in {APPROVAL, ANTI_PLURALITY}: d_ranking_ballot[ranking] = ballot_one_two(ranking) elif voting_rule == PLURALITY: d_ranking_ballot[ranking] = ballot_two(ranking) else: raise NotImplementedError else: d_ranking_ballot[ranking] = UTILITY_DEPENDENT # Call parent class super().__init__(d_ranking_ballot=d_ranking_ballot, d_weak_order_ballot=d_weak_order_ballot, profile=profile, voting_rule=voting_rule)
def tau_fanatic(self): """Tau-vector associated to fanatic voting. Returns ------- TauVector In Approval or Plurality, all voters approve of their top candidate only. In Anti-Plurality, they all disapprove of their bottom candidate, i.e. they approve their two first candidates. """ t = self.d_ballot_share_weak_voters_fanatic.copy() if self.voting_rule in {APPROVAL, PLURALITY}: for ranking, share in self.d_ranking_share.items(): t[ballot_one(ranking)] += share elif self.voting_rule == ANTI_PLURALITY: for ranking, share in self.d_ranking_share.items(): t[ballot_one_two(ranking)] += share else: raise NotImplementedError return TauVector(t, voting_rule=self.voting_rule, symbolic=self.symbolic)
def tau_fanatic(self): """Tau-vector associated to fanatic voting. Returns ------- TauVector * In Approval or Plurality, all voters approve of their top candidate only, * In Anti-plurality, all voters vote against their bottom candidate (i.e. for the other two). Notes ----- In Plurality and Anti-plurality, sincere and fanatic voting are the same. They differ only in Approval. """ t = self.d_ballot_share_weak_voters_fanatic.copy() for ranking, share in self.d_ranking_share.items(): if self.voting_rule in {APPROVAL, PLURALITY}: t[ballot_one(ranking)] += share elif self.voting_rule == ANTI_PLURALITY: t[ballot_one_two(ranking)] += self.d_ranking_share[ranking] else: raise NotImplementedError return TauVector(t, voting_rule=self.voting_rule, symbolic=self.symbolic)
def __init__(self, d_ranking_ballot, d_weak_order_ballot=None, profile=None, voting_rule=None): """ >>> strategy = StrategyTwelve({'non_existing_ranking': 'utility-dependent'}) Traceback (most recent call last): ValueError: Unknown key: non_existing_ranking >>> strategy = StrategyTwelve({'abc': 'non_existing_ballot'}) Traceback (most recent call last): ValueError: Unknown strategy: non_existing_ballot """ voting_rule = self._get_voting_rule_(profile, voting_rule) # Populate the dictionary and check for typos in the input self.d_ranking_ballot = DictPrintingInOrderIgnoringZeros( {ranking: '' for ranking in RANKINGS}) for ranking, ballot in d_ranking_ballot.items(): # Sanity checks if ranking not in RANKINGS: raise ValueError('Unknown key: ' + ranking) if voting_rule == APPROVAL: authorized_ballots = { ballot_one(ranking), ballot_one_two(ranking), ballot_one_two(ranking)[::-1], '', UTILITY_DEPENDENT } elif voting_rule == PLURALITY: authorized_ballots = { ballot_one(ranking), ballot_two(ranking), '', UTILITY_DEPENDENT } elif voting_rule == ANTI_PLURALITY: authorized_ballots = { ballot_one_two(ranking), ballot_one_two(ranking)[::-1], ballot_one_three(ranking), ballot_one_three(ranking)[::-1], '', UTILITY_DEPENDENT } else: raise NotImplementedError if ballot not in authorized_ballots: raise ValueError('Unknown strategy: ' + ballot) # Record the ballot self.d_ranking_ballot[ ranking] = ballot if ballot == UTILITY_DEPENDENT else sort_ballot( ballot) # Weak orders self.d_weak_order_ballot = DictPrintingInOrderIgnoringZeros( {weak_order: '' for weak_order in WEAK_ORDERS_WITHOUT_INVERSIONS}) if d_weak_order_ballot: if voting_rule == APPROVAL: for weak_order, ballot in d_weak_order_ballot.items(): if ballot != '': raise ValueError( 'In Approval, you should not specify ballots for weak orders.' ) elif voting_rule == PLURALITY: for weak_order, ballot in d_weak_order_ballot.items(): if not is_weak_order(weak_order): raise ValueError('Unknown key: ' + weak_order) elif is_hater(weak_order): possible_ballots = { weak_order[0], weak_order[2], SPLIT } if ballot not in possible_ballots: raise ValueError('Unknown strategy: ' + ballot) self.d_weak_order_ballot[sort_weak_order( weak_order)] = ballot else: # is_lover(weak_order) if ballot != '': raise ValueError( 'In Plurality, you should not specify ballots for "lovers" (e.g. a>b~c).' ) elif voting_rule == ANTI_PLURALITY: for weak_order, ballot in d_weak_order_ballot.items(): if not is_weak_order(weak_order): raise ValueError('Unknown key: ' + weak_order) elif is_lover(weak_order): possible_ballots = { sort_ballot(weak_order[0] + weak_order[2]), sort_ballot(weak_order[0] + weak_order[4]), SPLIT } if ballot != SPLIT: ballot = sort_ballot(ballot) if ballot not in possible_ballots: raise ValueError('Unknown strategy: ' + ballot + ' for ' + weak_order) self.d_weak_order_ballot[sort_weak_order( weak_order)] = ballot else: # is_hater(weak_order) if ballot != '': raise ValueError( 'In Anti-Plurality, you should not specify ballots ' 'for "haters" (e.g. a~b>c).') else: raise NotImplementedError # Call parent class super().__init__(profile=profile, voting_rule=voting_rule)