# ProfileTwelve¶

class poisson_approval.ProfileTwelve(d_type_share, d_weak_order_share=None, normalization_warning=True, ratio_sincere=0, ratio_fanatic=0, voting_rule='Approval', symbolic=False)[source]

A profile of preference with twelve types.

Parameters: d_type_share (dict) – E.g. {'ab_c': 0.4, 'c_ab': 0.6}. d_type_share['ab_c'] is the probability that a voter prefers candidate a, then candidate b, then candidate c, with a utility for b that is infinitely close to 1. In contrast, d_type_share['a_bc'] is the probability that a voter prefers a then b then c, with a utility for b that is infinitely close to 0. d_weak_order_share (dict) – E.g. {'a~b>c': 0.2, 'a>b~c': 0.1}. d_weak_order_share['a~b>c'] is the probability that a voter likes candidates a and b equally and prefer them to candidate c. normalization_warning (bool) – Whether a warning should be issued if the input distribution is not normalized. ratio_sincere (Number) – The ratio of sincere voters, in the interval [0, 1]. This is used for tau(). ratio_fanatic (Number) – The ratio of fanatic voters, in the interval [0, 1]. This is used for tau(). The sum of ratio_sincere and ratio_fanatic must not exceed 1. voting_rule (str) – The voting rule. Possible values are APPROVAL, PLURALITY and ANTI_PLURALITY. symbolic (bool) – Whether the computations are symbolic or numeric.

Notes

If the input distribution d_type_share is not normalized, the profile will be normalized anyway and a warning is issued (unless normalization_warning is False).

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)})
>>> profile
ProfileTwelve({'ab_c': Fraction(1, 10), 'b_ac': Fraction(3, 5), 'c_ab': Fraction(1, 5), 'ca_b': Fraction(1, 10)})
>>> print(profile)
<ab_c: 1/10, b_ac: 3/5, c_ab: 1/5, ca_b: 1/10> (Condorcet winner: b)
>>> profile.c_ab
Fraction(1, 5)
>>> profile.d_type_share['c_ab']  # Alternate syntax for profile.c_ab
Fraction(1, 5)
>>> profile.cab
Fraction(3, 10)
>>> profile.d_ranking_share['cab']  # Alternate syntax for profile.cab
Fraction(3, 10)
>>> profile.weighted_maj_graph
array([[0, Fraction(-1, 5), Fraction(2, 5)],
[Fraction(1, 5), 0, Fraction(2, 5)],
[Fraction(-2, 5), Fraction(-2, 5), 0]], dtype=object)
>>> profile.condorcet_winners
Winners({'b'})
>>> profile.is_profile_condorcet
1.0
>>> profile.has_majority_favorite  # Is one candidate 'top' in a majority of ballots?
True
>>> profile.has_majority_ranking  # Does one ranking represent a majority of ballots?
True
>>> profile.is_single_peaked  # Is the profile single-peaked?
True
>>> profile.support_in_rankings
{'abc', 'bac', 'cab'}
>>> profile.is_generic_in_rankings  # Are all rankings there?
False
>>> profile.analyzed_strategies_pure
Equilibria:
<abc: a, bac: b, cab: ac> ==> b (FF)
<abc: a, bac: ab, cab: c> ==> a (D)
<abc: ab, bac: b, cab: utility-dependent> ==> b (FF)
<BLANKLINE>
Non-equilibria:
<abc: a, bac: b, cab: c> ==> b (FF)
<abc: a, bac: b, cab: utility-dependent> ==> b (FF)
<abc: a, bac: ab, cab: ac> ==> a (D)
<abc: a, bac: ab, cab: utility-dependent> ==> a (D)
<abc: ab, bac: b, cab: c> ==> b (FF)
<abc: ab, bac: b, cab: ac> ==> b (FF)
<abc: ab, bac: ab, cab: c> ==> a, b (FF)
<abc: ab, bac: ab, cab: ac> ==> a (D)
<abc: ab, bac: ab, cab: utility-dependent> ==> a (D)
>>> print(profile.analyzed_strategies_pure.winners_at_equilibrium)
a, b


In the following example, one third of the voters are sincere:

>>> 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)},
...                         ratio_sincere=Fraction(1, 3))
>>> tau_sincere = profile.tau_sincere
>>> print(tau_sincere)
<ab: 1/10, ac: 1/10, b: 3/5, c: 1/5> ==> b
>>> strategy = StrategyTwelve({'abc': 'a', 'bac': 'b', 'cab': 'utility-dependent'})
>>> tau_strategic = profile.tau_strategic(strategy)
>>> print(tau_strategic)
<a: 1/10, ac: 1/10, b: 3/5, c: 1/5> ==> b
>>> tau = profile.tau(strategy)
>>> print(tau)
<a: 1/15, ab: 1/30, ac: 1/10, b: 3/5, c: 1/5> ==> b


The profile can include weak orders:

>>> profile = ProfileTwelve({'ab_c': Fraction(1, 10), 'b_ac': Fraction(6, 10)},
...                         d_weak_order_share={'a~b>c': Fraction(3, 10)})
>>> profile
ProfileTwelve({'ab_c': Fraction(1, 10), 'b_ac': Fraction(3, 5)}, d_weak_order_share={'a~b>c': Fraction(3, 10)})
>>> print(profile)
<ab_c: 1/10, b_ac: 3/5, a~b>c: 3/10> (Condorcet winner: b)

a_bc

Share of voters with this type.

Type: Number
a_cb

Share of voters with this type.

Type: Number
ab_c

Share of voters with this type.

Type: Number
abc

Share of voters with this ranking.

Type: Number
ac_b

Share of voters with this type.

Type: Number
acb

Share of voters with this ranking.

Type: Number
analyzed_strategies(strategies)

Analyze a list of strategies for the profile.

Parameters: strategies (iterable) – An iterator of strategies, such as a list of strategies. The analyzed strategies of the profile. AnalyzedStrategies

Examples

analyzed_strategies_group

Analyzed group strategies.

Cf. analyzed_strategies() and strategies_group. This is implemented only for profiles where we consider that there is a natural notion of group, such as ProfileNoisyDiscrete.

Type: AnalyzedStrategies
analyzed_strategies_ordinal

Analyzed ordinal strategies.

Type: AnalyzedStrategies
analyzed_strategies_pure

Analyzed pure strategies.

Cf. analyzed_strategies() and strategies_pure. This is implemented only for discrete profiles such as ProfileTwelve or ProfileDiscrete.

Type: AnalyzedStrategies
b_ac

Share of voters with this type.

Type: Number
b_ca

Share of voters with this type.

Type: Number
ba_c

Share of voters with this type.

Type: Number
bac

Share of voters with this ranking.

Type: Number
bc_a

Share of voters with this type.

Type: Number
bca

Share of voters with this ranking.

Type: Number
best_responses_to_strategy(d_ranking_best_response)

Convert best responses to a StrategyThreshold.

Parameters: d_ranking_best_response (dict) – Key: ranking. Value: BestResponse. The conversion of the best responses into a strategy. Only the rankings present in this profile are mentioned in the strategy. StrategyThreshold
c_ab

Share of voters with this type.

Type: Number
c_ba

Share of voters with this type.

Type: Number
ca_b

Share of voters with this type.

Type: Number
cab

Share of voters with this ranking.

Type: Number
cb_a

Share of voters with this type.

Type: Number
cba

Share of voters with this ranking.

Type: Number
condorcet_winners

Condorcet winner(s).

Type: Winners
contains_rankings

Whether the profile contains some rankings.

Type: bool
contains_weak_orders

Whether the profile contains some weak orders.

Type: bool
d_ballot_share_weak_voters_fanatic

Ballot shares due to the weak orders if they vote fanatically

Voters of the type 'a>b~c':

• In Approval or Plurality, they vote for a.
• In Anti-plurality, half of them vote for ab (i.e. against c) and half of them vote for ac (i.e. against b).

Voters of the type 'a~b>c':

• In Approval or Plurality, half of them vote for a and half of them vote for b.
• In Anti-plurality, they vote for ab (i.e. against c).
Type: dict
d_ballot_share_weak_voters_sincere

Ballot shares due to the weak orders if they vote sincerely

Voters of the type 'a>b~c':

• In Approval or Plurality, they vote for a.
• In Anti-plurality, half of them vote for ab (i.e. against c) and half of them vote for ac (i.e. against b).

Voters of the type 'a~b>c':

• In Approval or Anti-plurality, they vote for ab (i.e. against c).
• In Plurality, half of them vote for a and half of them vote for b.
Type: dict
fictitious_play(init, n_max_episodes, perception_update_ratio=<function one_over_t>, ballot_update_ratio=1, winning_frequency_update_ratio=<function one_over_t>, verbose=False)

Seek for convergence by fictitious play.

Parameters: init (Strategy or TauVector or str) – The initialization. If it is a strategy, it must be an argument accepted by tau(), i.e. by tau_strategic(). If it is a tau-vector, it is used directly. If it is a string: 'sincere' or 'fanatic': tau_sincere or tau_fanatic is respectively used. 'random_tau': use RandTauVectorUniform to draw a tau-vector uniformly at random that is consistent with the voting rule. 'random_tau_undominated': use random_tau_undominated() to draw a tau-vector where all voters cast an undominated ballot at random. n_max_episodes (int) – Maximal number of iterations. perception_update_ratio (callable or Number) – The coefficient when updating the perceived tau: tau_perceived = (1 - perception_update_ratio(t)) * tau_perceived + perception_update_ratio(t) * tau_actual. For any t from 1 to n_max_episodes included, the update ratio must be in [0, 1]. The default function is one_over_t(), which leads to an arithmetic average. However, the recommended function is one_over_log_t_plus_one(), which accelerates the convergence. If perception_update_ratio is a Number, it is considered as a constant function. ballot_update_ratio (callable or Number) – The ratio of voters who update their ballot: tau_actual = (1 - ballot_update_ratio(t)) * tau_actual + ballot_update_ratio(t) * tau_response. For any t from 1 to n_max_episodes included, the update ratio must be in [0, 1]. The default function is the constant 1, which corresponds to a full update. If ballot_update_ratio is a Number, it is considered as a constant function. winning_frequency_update_ratio (callable or Number) – The coefficient when updating the winning frequency of each candidate: d_candidate_winning_frequency[c] = (1 - winning_frequency_update_ratio(t)) * d_candidate_winning_frequency[c] + winning_frequency_update_ratio(t) * winning_probability[c]. The default function is one_over_t(), which leads to an arithmetic average. Note that this parameters has an influence only in case of non-convergence. verbose (bool) – If True, print all intermediate steps. Key tau: TauVector or None. The limit tau-vector. If None, it means that the process did not converge. Key strategy: StrategyThreshold or None. The limit strategy. If None, it means that the process did not converge. Key n_episodes: the number of episodes until convergence. If the process did not converge, by convention, this value is n_max_episodes. Key d_candidate_winning_frequency: dict. Key: candidate. Value: winning frequency. If the process reached a limit, the winning frequencies are computed in the limit only. If the process did not converge, the frequency is computed on the whole history. dict

Notes

Comparison between iterated_voting() and fictitious_play():

In general, you should use iterated_voting() only if you care about cycles, with the constraint that it implies having constant update ratios.

has_majority_favorite

Whether there is a majority favorite (a candidate ranked first by strictly more than half of the voters).

Type: bool
has_majority_ranking

Whether there is a majority ranking (a ranking shared by strictly more than half of the voters).

Type: bool
has_majority_type

Whether there is a majority type (a type shared by strictly more than half of the 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)})
>>> profile.has_majority_type
True


This does NOT include weak orders:

>>> profile = ProfileTwelve({'ab_c': Fraction(1, 10)}, d_weak_order_share={'b>a~c': Fraction(9, 10)})
>>> profile.has_majority_type
False

Type: bool
have_ranking_with_utility_above_u(ranking, u)[source]

Share of voters who have a given ranking and strictly above a given utility for their middle candidate.

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)})
>>> profile.have_ranking_with_utility_above_u(ranking='cab', u=.5)
Fraction(1, 10)
>>> profile.have_ranking_with_utility_above_u(ranking='cab', u=0)
Fraction(3, 10)
>>> profile.have_ranking_with_utility_above_u(ranking='cab', u=1)
0

have_ranking_with_utility_below_u(ranking, u)[source]

Share of voters who have a given ranking and strictly below a given utility for their middle candidate.

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)})
>>> profile.have_ranking_with_utility_below_u(ranking='cab', u=.5)
Fraction(1, 5)
>>> profile.have_ranking_with_utility_below_u(ranking='cab', u=1)
Fraction(3, 10)
>>> profile.have_ranking_with_utility_below_u(ranking='cab', u=0)
0

have_ranking_with_utility_u(ranking, u)[source]

Share of voters who have a given ranking and a given utility for their middle candidate.

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)})
>>> profile.have_ranking_with_utility_u(ranking='cab', u=.5)
0

is_equilibrium(strategy)[source]

Whether a strategy is an equilibrium.

Parameters: strategy (StrategyTwelve) – A strategy that specifies at least all the rankings that are present in the profile. Whether strategy is an equilibrium in this profile. EquilibriumStatus

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

is_generic_in_rankings

Whether the profile is generic in rankings (contains all rankings).

Type: bool
is_generic_in_types

Whether the profile is generic in types (contains all types).

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)})
>>> profile.is_generic_in_types
False

Type: bool
is_profile_condorcet

Whether the profile is Condorcet. 1. means there is a strict Condorcet winner, 0.5 means there are one or more weak Condorcet winner(s), 0. means there is no Condorcet winner.

Type: float
is_single_peaked

Whether the profile is single-peaked.

Type: bool
is_standardized

Whether the profile is standardized. Cf. standardized_version().

Type: bool
iterated_voting(init, n_max_episodes, perception_update_ratio=1, ballot_update_ratio=1, winning_frequency_update_ratio=<function one_over_t>, verbose=False)

Seek for convergence by iterated voting.

Parameters: init (Strategy or TauVector or str) – The initialization. If it is a strategy, it must be an argument accepted by tau(), i.e. by tau_strategic(). If it is a tau-vector, it is used directly. If it is a string: 'sincere' or 'fanatic': tau_sincere or tau_fanatic is respectively used. 'random_tau': use RandTauVectorUniform to draw a tau-vector uniformly at random that is consistent with the voting rule. 'random_tau_undominated': use random_tau_undominated() to draw a tau-vector where all voters cast an undominated ballot at random. n_max_episodes (int) – Maximal number of iterations. perception_update_ratio (Number in [0, 1]) – The coefficient when updating the perceived tau: tau_perceived = (1 - perception_update_ratio) * tau_perceived + perception_update_ratio * tau_actual. ballot_update_ratio (Number in [0, 1]) – The ratio of voters who update their ballot: tau_actual = (1 - ballot_update_ratio) * tau_actual + ballot_update_ratio * tau_response. winning_frequency_update_ratio (callable or Number) – The coefficient when updating the winning frequency of each candidate: d_candidate_winning_frequency[c] = (1 - winning_frequency_update_ratio(t)) * d_candidate_winning_frequency[c] + winning_frequency_update_ratio(t) * winning_probability[c]. The default function is one_over_t(), which leads to an arithmetic average. Note that this parameters has an influence only in case of non-convergence. verbose (bool) – If True, print all intermediate steps. Key cycle_taus_perceived: list of TauVector. The limit cycle of perceived tau-vectors. cycle_taus_perceived[t] is a barycenter of cycle_taus_perceived[t - 1] with cycle_taus_actual[t - 1], parametrized by perception_update_ratio. Key cycle_strategies: list of StrategyThreshold. The limit cycle of strategies. cycle_strategies[t] is the best response to cycle_taus_perceived[t]. Key cycle_taus_actual: list of TauVector. The limit cycle of actual tau-vectors. cycle_taus_actual[t] is a barycenter of cycle_taus_actual[t - 1] and the tau-vector resulting from strategies[t], parametrized by ballot_update_ratio. Key n_episodes: the number of episodes until convergence. If the process did not converge, by convention, this value is n_max_episodes. Key d_candidate_winning_frequency: dict. Key: candidate. Value: winning frequency. If the process reached a limit or a periodical orbit, the winning frequencies are computed in the limit only. If the process did not converge, the frequency is computed on the whole history. cycle_taus_perceived, cycle_strategies and cycle_taus_actual have the same length. If it is 1, the process converges to this limit. If it is greater than 1, the process reaches a periodical orbit. If it is 0, by convention, it means that the process does not converge and does not reach a periodical orbit. dict

Notes

Comparison between iterated_voting() and fictitious_play():

In general, you should use iterated_voting() only if you care about cycles, with the constraint that it implies having constant update ratios.

classmethod order_and_label(t)[source]

Order and label of a discrete type.

Examples

>>> ProfileTwelve.order_and_label('ab_c')
('abc', '$r(ab\\_c)$')
>>> ProfileTwelve.order_and_label('a~b>c')
('a~b>c', '$r(a\\sim b>c)$')

classmethod order_and_label_weak(t)

Auxiliary function for order_and_label(), specialized for weak orders.

Parameters: t (object) – A weak order of the form 'a>b~c' or 'a~b>c'. order (str) – The weak order itself. label (str) – The label to be used for the corner of the triangle.

Examples

>>> Profile.order_and_label_weak('a~b>c')
('a~b>c', '$r(a\\sim b>c)$')

random_tau_undominated()

Random tau based on undominated ballots.

This is used, for example, in ProfileCardinal.iterated_voting().

Returns: A random tau-vector. Independently for each ranking, a proportion uniformly drawn in [0, 1] of voters use one undominated ballot, and the rest use the other undominated ballot. For example, in Approval voting, voters with ranking abc are randomly split between ballots a and ab. TauVector
standardized_version

Standardized version of the profile (makes it unique, up to permutations of the candidates).

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)})
>>> print(profile.standardized_version)
<a_bc: 3/5, ba_c: 1/10, c_ba: 1/5, cb_a: 1/10> (Condorcet winner: a)
>>> profile.is_standardized
False

Type: ProfileTwelve
strategies_group

group strategies of the profile.

Yields: Strategy – All possible group strategies of the profile. This is implemented only for profiles where we consider that there is a natural notion of group, such as ProfileNoisyDiscrete.

Examples

Type: Iterator
strategies_ordinal

ordinal strategies of the profile.

Yields: StrategyOrdinal – All possible ordinal strategies for this profile.

Examples

Type: Iterator
strategies_pure

pure strategies of the profile.

Yields: StrategyTwelve – All possible pure strategies of the profile. Iterator
support_in_rankings

Support of the profile (in terms of rankings).

Type: SetPrintingInOrder of str
support_in_types

Support of the profile (in terms of types).

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)})
>>> profile.support_in_types
{'ab_c', 'b_ac', 'c_ab', 'ca_b'}

Type: SetPrintingInOrder of str
support_in_weak_orders

Support of the profile (in terms of weak orders).

Type: SetPrintingInOrder of str
tau(strategy)

Tau-vector associated to a strategy, with partial sincere and fanatic voting.

Parameters: strategy (an argument accepted by tau_strategic()) – A share ratio_sincere of the voters vote sincerely (in the sense of tau_sincere), a share ratio_fanatic vote only for their top candidate, and the rest of the voters vote strategically (in the sense of tau_strategic()). In other words, this tau-vector is the barycenter of tau_sincere, tau_fanatic and tau_strategic(strategy), with respective weights self.ratio_sincere, self.ratio_fanatic and 1 - self.ratio_sincere - self.ratio_fanatic. TauVector
tau_fanatic

Tau-vector associated to fanatic voting.

Returns: 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). TauVector

Notes

In Plurality and Anti-plurality, sincere and fanatic voting are the same. They differ only in Approval.

tau_sincere

Tau-vector associated to sincere voting.

Returns: 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). TauVector

Notes

In Plurality and Anti-plurality, sincere and fanatic voting are the same. They differ only in Approval.

tau_strategic(strategy)[source]

Tau-vector associated to a strategy.

Parameters: strategy (StrategyTwelve) – A strategy that specifies at least all the rankings that are present in the profile. Tau-vector associated to this profile and strategy strategy. TauVector

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'})
>>> tau_strategic = profile.tau_strategic(strategy)
>>> print(tau_strategic)
<ab: 1/10, ac: 1/10, b: 3/5, c: 1/5> ==> b

weighted_maj_graph

Weighted majority graph.

Type: np.ndarray
τ(strategy)

Tau-vector (alternate notation).

Parameters: strategy (Strategy) – A strategy that specifies at least all the rankings that are present in the profile. Tau-vector associated to this profile and strategy strategy. TauVector