Iterative Methods

The voting methods defined below all follow a similar procedure: iteratively remove “poorly performing” candidates from the profile until there is a “clear winner” in the current stage. The voting methods differ in how they answer the following two questions:

  1. What does it mean for a candidate to “perform poorly”? Different definitions of a poorly performing candidate include the fewest number of first-place votes, the most last-place votes, the least Borda score, etc.

  2. How to break ties when there are more than one candidate that is identified as “poorly performing”? One approach is to remove all candidates that are poorly performing at each round (and if all remaining candidates would be removed, then the remaining candidates are tied for the win). A second approach is to use so-called parallel-universe tiebreaking. On this approach, a candidate A is a winner if there is some poorly perfoming candidate B such that in the profile without B, A is a winner.

  3. What counts as a “clear winner” in the current stage? Possible answers include a candidate with a strict majority of first-place votes, a Condorcet winner, etc.

To illustrate the difference with respect to the first question, consider the voting methods Instant Runoff and Coombs. According to Instant Runoff, a candidate is “poorly performing” if that candidate has the fewest number first-place votes, and according to Coombs, a candidate is “poorly performing” if that candidate has the most last-place votes.

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, coombs

prof = Profile([[2, 1, 0], [0, 2, 1], [1, 2, 0]], [1, 2, 2])

prof.display()
instant_runoff.display(prof)
coombs.display(prof)
+---+---+---+
| 1 | 2 | 2 |
+---+---+---+
| 2 | 0 | 1 |
| 1 | 2 | 2 |
| 0 | 1 | 0 |
+---+---+---+
Instant Runoff winner is {1}
Coombs winner is {2}

To illustrate the difference with respect to the second question, consider Instant Runoff and Instant Runoff PUT that uses parallel-universe tiebreaking.

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, instant_runoff_put

prof = Profile([[1, 2, 0], [2, 1, 0], [0, 1, 2]], [1, 1, 1])

prof.display()
instant_runoff.display(prof)
instant_runoff_put.display(prof)
+---+---+---+
| 1 | 1 | 1 |
+---+---+---+
| 1 | 2 | 0 |
| 2 | 1 | 1 |
| 0 | 0 | 2 |
+---+---+---+
Instant Runoff winners are {0, 1, 2}
Instant Runoff PUT winners are {1, 2}

Instant Runoff

pref_voting.iterative_methods.instant_runoff(profile, curr_cands=None, algorithm='basic')[source]

If there is a majority winner then that candidate is the winner. If there is no majority winner, then remove all candidates that are ranked first by the fewest number of voters. Continue removing candidates with the fewest number first-place votes until there is a candidate with a majority of first place votes.

Important

If there is more than one candidate with the fewest number of first-place votes, then all such candidates are removed from the profile.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

  • algorithm (str, optional) – The algorithm to use. Options are “basic” and “recursive”. The default is “basic”.

Returns:

A sorted list of candidates

See also

Instant Runoff is also known as “Ranked Choice”, “Hare”, and “Alternative Vote”.

Related functions: pref_voting.iterative_methods.instant_runoff_tb(), pref_voting.iterative_methods.instant_runoff_put(), pref_voting.iterative_methods.instant_runoff_with_explanation()

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, ranked_choice, alternative_vote, hare

prof = Profile([[2, 1, 0], [0, 2, 1], [1, 2, 0]], [1, 2, 2])

prof.display()
instant_runoff.display(prof)
ranked_choice.display(prof)
alternative_vote.display(prof)
hare.display(prof)
+---+---+---+
| 1 | 2 | 2 |
+---+---+---+
| 2 | 0 | 1 |
| 1 | 2 | 2 |
| 0 | 1 | 0 |
+---+---+---+
Instant Runoff winner is {1}
Ranked Choice winner is {1}
Alternative Vote winner is {1}
Hare winner is {1}

Instant Runoff TB

pref_voting.iterative_methods.instant_runoff_tb(profile, curr_cands=None, tie_breaker=None)[source]

Instant Runoff (instant_runoff) with tie breaking: If there is more than one candidate with the fewest number of first-place votes, then remove the candidate with lowest in the tie_breaker ranking from the profile.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

  • tie_breaker (List[int]) – A list of the candidates in the profile to be used as a tiebreaker.

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, instant_runoff_tb

prof = Profile([[1, 2, 0], [2, 1, 0], [0, 1, 2]], [1, 1, 1])

prof.display()
print("no tiebreaker")
instant_runoff.display(prof)
print("tie_breaker = [0, 1, 2]")
instant_runoff_tb.display(prof)
print("tie_breaker = [1, 2, 0]")
instant_runoff_tb.display(prof, tie_breaker=[1, 2, 0])
+---+---+---+
| 1 | 1 | 1 |
+---+---+---+
| 1 | 2 | 0 |
| 2 | 1 | 1 |
| 0 | 0 | 2 |
+---+---+---+
no tiebreaker
Instant Runoff winners are {0, 1, 2}
tie_breaker = [0, 1, 2]
Instant Runoff TB winner is {1}
tie_breaker = [1, 2, 0]
Instant Runoff TB winner is {2}

Instant Runoff PUT

pref_voting.iterative_methods.instant_runoff_put(profile, curr_cands=None)[source]

Instant Runoff (instant_runoff()) with parallel universe tie-breaking (PUT), defined recursively: if there is a candidate with a strict majority of first-place votes, that candidate is the IRV-PUT winner; otherwise a candidate x is an IRV-PUT winner if there is some candidate y with a minimal number of first-place votes such that after removing y from the profile, x is an IRV-PUT winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Warning

This will take a long time on profiles with many candidates having the same plurality scores

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, instant_runoff_tb, instant_runoff_put

prof = Profile([[1, 2, 0], [2, 1, 0], [0, 1, 2]], [1, 1, 1])

prof.display()
print("no tiebreaker")
instant_runoff.display(prof)
print("tie_breaker = [0, 1, 2]")
instant_runoff_tb.display(prof, tie_breaker=[0, 1, 2])
print("tie_breaker = [0, 2, 1]")
instant_runoff_tb.display(prof, tie_breaker=[0, 2, 1])
print("tie_breaker = [1, 0, 2]")
instant_runoff_tb.display(prof, tie_breaker=[1, 0, 2])
print("tie_breaker = [1, 2, 0]")
instant_runoff_tb.display(prof, tie_breaker=[1, 2, 0])
print("tie_breaker = [2, 0, 1]")
instant_runoff_tb.display(prof, tie_breaker=[2, 0, 1])
print("tie_breaker = [2, 1, 0]")
instant_runoff_tb.display(prof, tie_breaker=[2, 1, 0])
print()
instant_runoff_put.display(prof)
+---+---+---+
| 1 | 1 | 1 |
+---+---+---+
| 1 | 2 | 0 |
| 2 | 1 | 1 |
| 0 | 0 | 2 |
+---+---+---+
no tiebreaker
Instant Runoff winners are {0, 1, 2}
tie_breaker = [0, 1, 2]
Instant Runoff TB winner is {1}
tie_breaker = [0, 2, 1]
Instant Runoff TB winner is {1}
tie_breaker = [1, 0, 2]
Instant Runoff TB winner is {2}
tie_breaker = [1, 2, 0]
Instant Runoff TB winner is {2}
tie_breaker = [2, 0, 1]
Instant Runoff TB winner is {1}
tie_breaker = [2, 1, 0]
Instant Runoff TB winner is {1}

Instant Runoff PUT winners are {1, 2}

Instant Runoff with Explanation

pref_voting.iterative_methods.instant_runoff_with_explanation(profile, curr_cands=None)[source]

Instant Runoff with an explanation. In addition to the winner(s), return the order in which the candidates are eliminated as a list of lists.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

A list describing the order in which candidates are eliminated

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, instant_runoff_with_explanation


prof = Profile([[2, 1, 0], [0, 2, 1], [1, 2, 0]], [1, 2, 2])
prof.display()
instant_runoff.display(prof)
ws, exp = instant_runoff_with_explanation(prof)
print(f"winning set: {ws}")
print(f"order of elimination: {exp}")

prof = Profile([[1, 2, 0], [2, 1, 0], [0, 1, 2]], [1, 1, 1])
prof.display()
instant_runoff.display(prof)
ws, exp = instant_runoff_with_explanation(prof)
print(f"winning set: {ws}")
print(f"order of elimination: {exp}")

prof = Profile([[2, 0, 1, 3], [2, 0, 3, 1], [3, 0, 1, 2],  [3, 2, 1, 0], [0, 2, 1, 3]], [1, 1, 1, 1, 1])
prof.display()
instant_runoff.display(prof)
ws, exp = instant_runoff_with_explanation(prof)
print(f"winning set: {ws}")
print(f"order of elimination: {exp}")
+---+---+---+
| 1 | 2 | 2 |
+---+---+---+
| 2 | 0 | 1 |
| 1 | 2 | 2 |
| 0 | 1 | 0 |
+---+---+---+
Instant Runoff winner is {1}
winning set: [1]
order of elimination: [[2]]
+---+---+---+
| 1 | 1 | 1 |
+---+---+---+
| 1 | 2 | 0 |
| 2 | 1 | 1 |
| 0 | 0 | 2 |
+---+---+---+
Instant Runoff winners are {0, 1, 2}
winning set: [0, 1, 2]
order of elimination: [[0, 1, 2]]
+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 |
+---+---+---+---+---+
| 2 | 2 | 3 | 3 | 0 |
| 0 | 0 | 0 | 2 | 2 |
| 1 | 3 | 1 | 1 | 1 |
| 3 | 1 | 2 | 0 | 3 |
+---+---+---+---+---+
Instant Runoff winner is {2}
winning set: [2]
order of elimination: [[1], [0]]

Instant Runoff for Truncated Linear Orders

pref_voting.iterative_methods.instant_runoff_for_truncated_linear_orders(profile, curr_cands=None, threshold=None, hide_warnings=True)[source]

Instant Runoff for Truncated Linear Orders. Iteratively remove the candidates with the fewest number of first place votes, until there is a candidate with more than the threshold number of first-place votes. If a threshold is not set, then it is strictly more than half of the non-empty ballots.

Parameters:
  • profile (ProfileWithTies) – An anonymous profile with no ties in the ballots (note that ProfileWithTies allows for truncated linear orders).

  • threshold (int, float, optional) – The threshold needed to win the election. If it is not set, then it is strictly more than half of the remaining ballots.

  • hide_warnings (bool, optional) – Show or hide the warnings when more than one candidate is eliminated in a round.

Returns:

A sorted list of candidates

Note

This is the simultaneous version of instant runoff, not the parallel-universe tiebreaking version. It is intended to be run on profiles with large number of voters in which there is a very low probability of a tie in the fewest number of first place votes. A warning is displayed when more than one candidate is eliminated.

Example:

from pref_voting.profiles_with_ties import ProfileWithTies
from pref_voting.iterative_methods import instant_runoff_for_truncated_linear_orders

prof = ProfileWithTies([{0:1, 1:1},{0:1, 1:2, 2:3, 3:4}, {0:1, 1:3, 2:3}, {3:2}, {0:1}, {0:1}, {}, {}])
prof.display()

tprof, report = prof.truncate_overvotes()
for r, new_r, count in report:
    print(f"{r} --> {new_r}: {count}")
tprof.display()
instant_runoff_for_truncated_linear_orders.display(tprof)
+-----+---+-----+---+---+---+---+---+
|  1  | 1 |  1  | 1 | 1 | 1 | 1 | 1 |
+-----+---+-----+---+---+---+---+---+
| 0 1 | 0 |  0  | 3 | 0 | 0 |   |   |
|     | 1 | 1 2 |   |   |   |   |   |
|     | 2 |     |   |   |   |   |   |
|     | 3 |     |   |   |   |   |   |
+-----+---+-----+---+---+---+---+---+
( 0  1 )  --> : 1
0 ( 1  2 )  --> 0 : 1
+---+---+---+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+---+---+---+---+---+---+---+---+
|   | 0 | 0 | 3 | 0 | 0 |   |   |
|   | 1 |   |   |   |   |   |   |
|   | 2 |   |   |   |   |   |   |
|   | 3 |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+
Instant Runoff (Truncated Linear Orders) winner is {0}

Plurality With Runoff PUT

pref_voting.iterative_methods.plurality_with_runoff_put(profile, curr_cands=None)[source]

If there is a majority winner then that candidate is the Plurality with Runoff winner. Otherwise hold a runoff between the top two candidates: the candidate with the most first place votes and the candidate with the 2nd most first place votes (or perhaps tied for the most first place votes). In the case of multiple candidates tied for the most or 2nd most first place votes, use parallel-universe tiebreaking: a candidate is a Plurality with Runoff winner if it is a winner in some runoff as described. If the candidates are all tied for the most first place votes, then all candidates are winners.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Note

Plurality with Runoff is the same as Instant Runoff when there are 3 candidates, but they can give different answers with 4 or more candidates.

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, plurality_with_runoff_put

prof = Profile([[0, 1, 2, 3], [3, 1, 2, 0], [2, 0, 3, 1], [1, 2, 3, 0], [2, 3, 0, 1], [0, 3, 2, 1]], [2, 1, 2, 2, 1, 2])
prof.display()
instant_runoff.display(prof)
plurality_with_runoff_put.display(prof)
+---+---+---+---+---+---+
| 2 | 1 | 2 | 2 | 1 | 2 |
+---+---+---+---+---+---+
| 0 | 3 | 2 | 1 | 2 | 0 |
| 1 | 1 | 0 | 2 | 3 | 3 |
| 2 | 2 | 3 | 3 | 0 | 2 |
| 3 | 0 | 1 | 0 | 1 | 1 |
+---+---+---+---+---+---+
Instant Runoff winner is {0}
PluralityWRunoff PUT winner is {2}

Benham

pref_voting.iterative_methods.benham(profile, curr_cands=None)[source]

As long as the profile has no Condorcet winner, eliminate the candidate with the lowest plurality score.

Important

If there is more than one candidate with the fewest number of first-place votes, then all such candidates are removed from the profile.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

See also

Related functions: pref_voting.iterative_methods.benham_put()

Benham TB

pref_voting.iterative_methods.benham_tb(profile, curr_cands=None, tie_breaker=None)[source]

Benham (benham) with tie breaking: If there is more than one candidate with the fewest number of first-place votes, then remove the candidate with lowest in the tie_breaker ranking from the profile.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

  • tie_breaker (List[int]) – A list of the candidates in the profile to be used as a tiebreaker.

Returns:

A sorted list of candidates

Benham PUT

pref_voting.iterative_methods.benham_put(profile, curr_cands=None)[source]

Benham (benham()) with parallel universe tie-breaking (PUT), defined recursively: if there is a Condorcet winner, that candidate is the Benham-PUT winner; otherwise a candidate x is a Benham-PUT winner if there is some candidate y with minimal plurality score such that after removing y from the profile, x is a Benham-PUT winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Warning

This will take a long time on profiles with many candidates having the same plurality scores.

Bottom-Two-Runoff Instant Runoff

pref_voting.iterative_methods.bottom_two_runoff_instant_runoff(profile, curr_cands=None)[source]

Find the two candidates with the lowest two plurality scores, remove the one who loses head-to-head to the other, and repeat until a single candidate remains.

If there is a tie for lowest or second lowest plurality score, consider all head-to-head matches between a candidate with lowest and a candidate with second lowest plurality score, and remove all the losers of the head-to-head matches, unless this would remove all candidates.

Note

BTR-IRV is a Condorcet consistent voting method, i.e., if a Condorcet winner exists, then BTR-IRV will elect the Condorcet winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Bottom-Two-Runoff Instant Runoff PUT

pref_voting.iterative_methods.bottom_two_runoff_instant_runoff_put(profile, curr_cands=None)[source]

Find the two candidates with the lowest two plurality scores, remove the one who loses head-to-head to the other, and repeat until a single candidate remains. Parallel-universe tiebreaking is used to break ties for lowest or second lowest plurality scores.

Note

BTR-IRV is a Condorcet consistent voting method, i.e., if a Condorcet winner exists, then BTR-IRV will elect the Condorcet winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Tideman’s Alternative

pref_voting.iterative_methods.tideman_alternative(vm)[source]

Given a voting method vm, returns a voting method that restricts the profile to the set of vm winners, then eliminates all the candidate with the fewest first-place votes, and then repeats until there is only one vm winner. If at some stage all remaining candidates are tied for the fewest number of first-place votes, then all remaining candidates win.

Parameters:

vm (VotingMethod) – A voting method.

Returns:

The Tideman Alternative PUT version of vm.

Tideman’s Alternative PUT

pref_voting.iterative_methods.tideman_alternative_put(vm)[source]

Given a voting method vm, returns a voting method that restricts the profile to the set of vm winners, then eliminates the candidate with the fewest first-place votes, and then repeats until there is only one vm winner. Parallel-universe tiebreaking is used when there are multiple candidates with the fewest first-place votes.

Parameters:

vm (VotingMethod) – A voting method.

Returns:

The Tideman Alternative PUT version of vm.

Tideman’s Alterantive GOCHA

pref_voting.iterative_methods.tideman_alternative_gocha(profile, curr_cands=None)

Coombs

pref_voting.iterative_methods.coombs(profile, curr_cands=None)[source]

If there is a majority winner then that candidate is the Coombs winner. If there is no majority winner, then remove all candidates that are ranked last by the greatest number of voters. Continue removing candidates with the most last-place votes until there is a candidate with a majority of first place votes.

Important

If there is more than one candidate with the largest number of last-place votes, then all such candidates are removed from the profile.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

See also

pref_voting.iterative_methods.coombs_with_tb(), pref_voting.iterative_methods.coomb_put(), pref_voting.iterative_methods.coombs_with_explanation()

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import instant_runoff, coombs

prof = Profile([[2, 1, 0], [0, 2, 1], [1, 2, 0]], [1, 2, 2])

prof.display()
coombs.display(prof)
instant_runoff.display(prof)
+---+---+---+
| 1 | 2 | 2 |
+---+---+---+
| 2 | 0 | 1 |
| 1 | 2 | 2 |
| 0 | 1 | 0 |
+---+---+---+
Coombs winner is {2}
Instant Runoff winner is {1}

Coombs TB

pref_voting.iterative_methods.coombs_tb(profile, curr_cands=None, tie_breaker=None)[source]

Coombs with a fixed tie-breaking rule: The tie-breaking rule is any linear order (i.e., list) of the candidates. The default rule is to order the candidates as follows: 0,….,num_cands-1.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

  • tie_breaker (List[int]) – A list of the candidates in the profile to be used as a tiebreaker.

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import coombs, coombs_tb

prof = Profile([[2, 0, 1], [0, 2, 1], [1, 0, 2], [2, 1, 0], [0, 1, 2]], [1, 1, 1, 2, 1])
prof.display()
print("no tiebreaker")
coombs.display(prof)
print("tie_breaker = [0, 1, 2]")
coombs_tb.display(prof)
print("tie_breaker = [2, 1, 0]")
coombs_tb.display(prof, tie_breaker=[2, 1, 0])
+---+---+---+---+---+
| 1 | 1 | 1 | 2 | 1 |
+---+---+---+---+---+
| 2 | 0 | 1 | 2 | 0 |
| 0 | 2 | 0 | 1 | 1 |
| 1 | 1 | 2 | 0 | 2 |
+---+---+---+---+---+
no tiebreaker
Coombs winners are {0, 1, 2}
tie_breaker = [0, 1, 2]
Coombs TB winner is {2}
tie_breaker = [2, 1, 0]
Coombs TB winner is {0}

Coombs PUT

pref_voting.iterative_methods.coombs_put(profile, curr_cands=None)[source]

Coombs with parallel universe tie-breaking (PUT), defined recursively: if there is a candidate with a strict majority of first-place votes, that candidate is the Coombs-PUT winner; otherwise a candidate x is a Coombs-PUT winner if there is some candidate y with a maximal number of last-place votes such that after removing y from the profile, x is a Coombs-PUT winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Warning

This will take a long time on profiles with many candidates having the same number of last-place votes.

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import coombs, coombs_tb, coombs_put

prof = Profile([[2, 0, 1], [1, 0, 2], [0, 1, 2]], [2, 1, 1])

prof.display()
print("no tiebreaker")
coombs.display(prof)
print("tie_breaker = [0, 1, 2]")
coombs_tb.display(prof, tie_breaker=[0, 1, 2])
print("tie_breaker = [0, 2, 1]")
coombs_tb.display(prof, tie_breaker=[0, 2, 1])
print("tie_breaker = [1, 0, 2]")
coombs_tb.display(prof, tie_breaker=[1, 0, 2])
print("tie_breaker = [1, 2, 0]")
coombs_tb.display(prof, tie_breaker=[1, 2, 0])
print("tie_breaker = [2, 0, 1]")
coombs_tb.display(prof, tie_breaker=[2, 0, 1])
print("tie_breaker = [2, 1, 0]")
coombs_tb.display(prof, tie_breaker=[2, 1, 0])
print()
coombs_put.display(prof)
+---+---+---+
| 2 | 1 | 1 |
+---+---+---+
| 2 | 1 | 0 |
| 0 | 0 | 1 |
| 1 | 2 | 2 |
+---+---+---+
no tiebreaker
Coombs winner is {0}
tie_breaker = [0, 1, 2]
Coombs TB winner is {2}
tie_breaker = [0, 2, 1]
Coombs TB winner is {0}
tie_breaker = [1, 0, 2]
Coombs TB winner is {2}
tie_breaker = [1, 2, 0]
Coombs TB winner is {0}
tie_breaker = [2, 0, 1]
Coombs TB winner is {0}
tie_breaker = [2, 1, 0]
Coombs TB winner is {0}

Coombs PUT winners are {0, 2}

Coombs with Explanation

pref_voting.iterative_methods.coombs_with_explanation(profile, curr_cands=None)[source]

Coombs with an explanation. In addition to the winner(s), return the order in which the candidates are eliminated as a list of lists.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

A list describing the order in which candidates are eliminated

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import coombs, coombs_with_explanation


prof = Profile([[2, 1, 0], [0, 2, 1], [1, 2, 0]], [1, 2, 2])
prof.display()
coombs.display(prof)
ws, exp = coombs_with_explanation(prof)
print(f"winning set: {ws}")
print(f"order of elimination: {exp}")

prof = Profile([[1, 0, 3, 2], [2, 3, 1, 0], [2, 0, 3, 1], [1, 2, 3, 0]], [1, 1, 1, 1])
prof.display()
coombs.display(prof)
ws, exp = coombs_with_explanation(prof)
print(f"winning set: {ws}")
print(f"order of elimination: {exp}")
+---+---+---+
| 1 | 2 | 2 |
+---+---+---+
| 2 | 0 | 1 |
| 1 | 2 | 2 |
| 0 | 1 | 0 |
+---+---+---+
Coombs winner is {2}
winning set: [2]
order of elimination: [[0]]
+---+---+---+---+
| 1 | 1 | 1 | 1 |
+---+---+---+---+
| 1 | 2 | 2 | 1 |
| 0 | 3 | 0 | 2 |
| 3 | 1 | 3 | 3 |
| 2 | 0 | 1 | 0 |
+---+---+---+---+
Coombs winner is {2}
winning set: [2]
order of elimination: [[0], [1]]

Baldwin

pref_voting.iterative_methods.baldwin(profile, curr_cands=None)[source]

Iteratively remove all candidates with the lowest Borda score until a single candidate remains. If, at any stage, all candidates have the same Borda score, then all (remaining) candidates are winners.

Note

Baldwin is a Condorcet consistent voting method, i.e., if a Condorcet winner exists, then Baldwin will elect the Condorcet winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import baldwin

prof = Profile([[1, 0, 2, 3], [3, 1, 0, 2], [2, 0, 3, 1]], [2, 1, 1])

prof.display()
baldwin.display(prof)
+---+---+---+
| 2 | 1 | 1 |
+---+---+---+
| 1 | 3 | 2 |
| 0 | 1 | 0 |
| 2 | 0 | 3 |
| 3 | 2 | 1 |
+---+---+---+
Baldwin winner is {1}

Baldwin TB

pref_voting.iterative_methods.baldwin_tb(profile, curr_cands=None, tie_breaker=None)[source]

Baldwin with a fixed tie-breaking rule: The tie-breaking rule is any linear order (i.e., list) of the candidates. The default rule is to order the candidates as follows: 0,….,num_cands-1.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

  • tie_breaker (List[int]) – A list of the candidates in the profile to be used as a tiebreaker.

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import baldwin, baldwin_tb

prof = Profile([[0, 2, 1, 3], [1, 3, 0, 2], [2, 3, 0, 1]], [1, 1, 1])
prof.display()
print("no tiebreaker")
baldwin.display(prof)
print("tie_breaker = [0, 1, 2, 3]")
baldwin_tb.display(prof)
print("tie_breaker = [2, 1, 0, 3]")
baldwin_tb.display(prof, tie_breaker=[2, 1, 0, 3])
+---+---+---+
| 1 | 1 | 1 |
+---+---+---+
| 0 | 1 | 2 |
| 2 | 3 | 3 |
| 1 | 0 | 0 |
| 3 | 2 | 1 |
+---+---+---+
no tiebreaker
Baldwin winner is {0}
tie_breaker = [0, 1, 2, 3]
Baldwin TB winner is {2}
tie_breaker = [2, 1, 0, 3]
Baldwin TB winner is {3}

Baldwin PUT

pref_voting.iterative_methods.baldwin_put(profile, curr_cands=None)[source]

Baldwin with parallel universe tie-breaking (PUT), defined recursively: if there is a single candidate in the profile, that candidate wins; otherwise a candidate x is a Baldwin-PUT winner if there is some candidate y with a minimal Borda score such that after removing y from the profile, x is a Baldwin-PUT winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import baldwin, baldwin_tb, baldwin_put

prof = Profile([[1, 2, 0], [0, 1, 2], [2, 0, 1]], [1, 3, 2])

prof.display()
print("no tiebreaker")
baldwin.display(prof)
print("tie_breaker = [0, 1, 2]")
baldwin_tb.display(prof, tie_breaker=[0, 1, 2])
print("tie_breaker = [0, 2, 1]")
baldwin_tb.display(prof, tie_breaker=[0, 2, 1])
print("tie_breaker = [1, 0, 2]")
baldwin_tb.display(prof, tie_breaker=[1, 0, 2])
print("tie_breaker = [1, 2, 0]")
baldwin_tb.display(prof, tie_breaker=[1, 2, 0])
print("tie_breaker = [2, 0, 1]")
baldwin_tb.display(prof, tie_breaker=[2, 0, 1])
print("tie_breaker = [2, 1, 0]")
baldwin_tb.display(prof, tie_breaker=[2, 1, 0])
print()
baldwin_put.display(prof)
+---+---+---+
| 1 | 3 | 2 |
+---+---+---+
| 1 | 0 | 2 |
| 2 | 1 | 0 |
| 0 | 2 | 1 |
+---+---+---+
no tiebreaker
Baldwin winner is {0}
tie_breaker = [0, 1, 2]
Baldwin TB winner is {2}
tie_breaker = [0, 2, 1]
Baldwin TB winner is {0}
tie_breaker = [1, 0, 2]
Baldwin TB winner is {2}
tie_breaker = [1, 2, 0]
Baldwin TB winner is {0}
tie_breaker = [2, 0, 1]
Baldwin TB winner is {0}
tie_breaker = [2, 1, 0]
Baldwin TB winner is {0}

Baldwin PUT winners are {0, 2}

Baldwin with Explanation

pref_voting.iterative_methods.baldwin_with_explanation(profile, curr_cands=None)[source]

Baldwin with an explanation. In addition to the winner(s), return the order in which the candidates are eliminated as a list of dictionaries specifying the Borda scores in the profile restricted to the candidates that have not been eliminated.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

A list describing for each round, the candidates that are eliminated and the Borda scores of the remaining candidates (in the profile restricted to candidates that have not been eliminated)

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import baldwin, baldwin_with_explanation

prof = Profile([[2, 1, 0], [0, 2, 1], [1, 2, 0]], [1, 2, 2])
prof.display()
baldwin.display(prof)
ws, exp = baldwin_with_explanation(prof)
print(f"winning set: {ws}")
print(f"order of elimination: {exp}")

prof = Profile([[1, 0, 3, 2], [2, 3, 1, 0], [2, 0, 3, 1], [1, 2, 3, 0]], [1, 1, 1, 1])
prof.display()
baldwin.display(prof)
ws, exp = baldwin_with_explanation(prof)
print(f"winning set: {ws}")
print(f"order of elimination: {exp}")
+---+---+---+
| 1 | 2 | 2 |
+---+---+---+
| 2 | 0 | 1 |
| 1 | 2 | 2 |
| 0 | 1 | 0 |
+---+---+---+
Baldwin winner is {2}
winning set: [2]
order of elimination: [[[0], {0: 4, 1: 5, 2: 6}], [[1], {1: 2, 2: 3}]]
+---+---+---+---+
| 1 | 1 | 1 | 1 |
+---+---+---+---+
| 1 | 2 | 2 | 1 |
| 0 | 3 | 0 | 2 |
| 3 | 1 | 3 | 3 |
| 2 | 0 | 1 | 0 |
+---+---+---+---+
Baldwin winners are {1, 2}
winning set: [1, 2]
order of elimination: [[[0], {0: 4, 1: 7, 2: 8, 3: 5}], [[3], {1: 4, 2: 5, 3: 3}], [[1, 2], {1: 2, 2: 2}]]

Nanson

Strict Nanson

pref_voting.iterative_methods.strict_nanson(profile, curr_cands=None)[source]

Iteratively remove all candidates with the Borda score strictly below the average Borda score until one candidate remains. If, at any stage, all candidates have the same Borda score, then all (remaining) candidates are winners.

Note

Strict Nanson is a Condorcet consistent voting method, i.e., if a Condorcet winner exists, then Strict Nanson will elect the Condorcet winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import strict_nanson

prof = Profile([[2, 1, 0, 3], [0, 2, 1, 3], [1, 3, 0, 2], [0, 3, 2, 1]], [2, 1, 1, 1])

prof.display()
strict_nanson.display(prof)
+---+---+---+---+
| 2 | 1 | 1 | 1 |
+---+---+---+---+
| 2 | 0 | 1 | 0 |
| 1 | 2 | 3 | 3 |
| 0 | 1 | 0 | 2 |
| 3 | 3 | 2 | 1 |
+---+---+---+---+
Strict Nanson winner is {0}

Strict Nanson with Explanation

pref_voting.iterative_methods.strict_nanson_with_explanation(profile, curr_cands=None)[source]

Strict Nanson with an explanation. In addition to the winner(s), return the order in which the candidates are eliminated as a list of dictionaries specifying the Borda scores in the profile restricted to the candidates that have not been eliminated and the average Borda score.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

A list describing for each round, the candidates that are eliminated and the Borda scores of the remaining candidates (in the profile restricted to candidates that have not been eliminated)

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import strict_nanson_with_explanation

prof = Profile([[2, 1, 0, 3], [0, 2, 1, 3], [1, 3, 0, 2], [0, 3, 2, 1]], [2, 1, 1, 1])

prof.display()
print(strict_nanson_with_explanation(prof))
+---+---+---+---+
| 2 | 1 | 1 | 1 |
+---+---+---+---+
| 2 | 0 | 1 | 0 |
| 1 | 2 | 3 | 3 |
| 0 | 1 | 0 | 2 |
| 3 | 3 | 2 | 1 |
+---+---+---+---+
([0], [{'avg_borda_score': 7.5, 'elim_cands': [3], 'borda_scores': {0: 9, 1: 8, 2: 9, 3: 4}}, {'avg_borda_score': 5.0, 'elim_cands': [1], 'borda_scores': {0: 5, 1: 4, 2: 6}}, {'avg_borda_score': 2.5, 'elim_cands': [2], 'borda_scores': {0: 3, 2: 2}}])

Weak Nanson

pref_voting.iterative_methods.weak_nanson(profile, curr_cands=None)[source]

Iteratively remove all candidates with Borda score less than or equal the average Borda score until one candidate remains. If, at any stage, all candidates have the same Borda score, then all (remaining) candidates are winners.

Note

Weak Nanson is a Condorcet consistent voting method, i.e., if a Condorcet winner exists, then Weak Nanson will elect the Condorcet winner.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import weak_nanson

prof = Profile([[2, 1, 0, 3], [0, 2, 1, 3], [1, 3, 0, 2], [0, 3, 2, 1]], [2, 1, 1, 1])

prof.display()
weak_nanson.display(prof)
+---+---+---+---+
| 2 | 1 | 1 | 1 |
+---+---+---+---+
| 2 | 0 | 1 | 0 |
| 1 | 2 | 3 | 3 |
| 0 | 1 | 0 | 2 |
| 3 | 3 | 2 | 1 |
+---+---+---+---+
Weak Nanson winner is {2}

Weak Nanson with Explanation

pref_voting.iterative_methods.weak_nanson_with_explanation(profile, curr_cands=None)[source]

Weak Nanson with an explanation. In addition to the winner(s), return the order in which the candidates are eliminated as a list of dictionaries specifying the Borda scores in the profile restricted to the candidates that have not been eliminated and the average Borda score.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

A list describing for each round, the candidates that are eliminated and the Borda scores of the remaining candidates (in the profile restricted to candidates that have not been eliminated)

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import weak_nanson_with_explanation

prof = Profile([[2, 1, 0, 3], [0, 2, 1, 3], [1, 3, 0, 2], [0, 3, 2, 1]], [2, 1, 1, 1])

prof.display()
print(weak_nanson_with_explanation(prof))
+---+---+---+---+
| 2 | 1 | 1 | 1 |
+---+---+---+---+
| 2 | 0 | 1 | 0 |
| 1 | 2 | 3 | 3 |
| 0 | 1 | 0 | 2 |
| 3 | 3 | 2 | 1 |
+---+---+---+---+
([2], [{'avg_borda_score': 7.5, 'elim_cands': [3], 'borda_scores': {0: 9, 1: 8, 2: 9, 3: 4}}, {'avg_borda_score': 5.0, 'elim_cands': [0, 1], 'borda_scores': {0: 5, 1: 4, 2: 6}}])

Raynaud

pref_voting.iterative_methods.raynaud(edata, curr_cands=None, score_method='margins')[source]

Iteratively remove the candidate(s) whose worst loss is biggest, unless all candidates have the same worst loss. See https://electowiki.org/wiki/Raynaud.

Parameters:
  • edata (Profile, ProfileWithTies, MarginGraph) – Any election data that has a margin method.

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands.

  • score_method (str, optional) – Options include “margins” (the default), “winning” assigns to each candidate \(c\) the maximum support of a candidate majority preferred to \(c\), and “pairwise_opposition” assigns to each candidate \(c\) the maximum support of any candidate over \(c\). These scores only lead to different results on non-linear profiles.

Returns:

A sorted list of candidates.

Woodall

pref_voting.iterative_methods.woodall(profile, curr_cands=None)[source]

If there is a single member of the Smith Set (i.e., a Condorcet winner) then that candidate is the winner. If there the Smith Set contains more than one candidate, then remove all candidates that are ranked first by the fewest number of voters. Continue removing candidates with the fewest number first-place votes until there is a single member of the originally Smith Set remaining.

Important

If there is more than one candidate with the fewest number of first-place votes, then all such candidates are removed from the profile.

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Iterated Removal of Condorcet Loser

pref_voting.iterative_methods.iterated_removal_cl(edata, curr_cands=None)[source]

Iteratively remove candidates that are Condorcet losers until there are no Condorcet losers. A candidate \(c\) is a Condorcet loser when every other candidate is majority preferred to \(c\).

Parameters:
  • edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph) – Any election data that has a condorcet_loser method.

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.profiles_with_ties import ProfileWithTies
from pref_voting.iterative_methods import iterated_removal_cl

prof = Profile([[2, 1, 3, 0], [2, 1, 0, 3], [3, 1, 2, 0], [1, 2, 3, 0]], [1, 1, 1, 1])

prof.display()
iterated_removal_cl.display(prof)
iterated_removal_cl.display(prof.majority_graph())
iterated_removal_cl.display(prof.margin_graph())

prof2 = ProfileWithTies([{2:1, 1:1, 3:2, 0:3}, {2:1, 1:2, 0:3, 3:4}, {3:1, 1:2, 2:3, 0:4}, {1:1, 2:2, 3:3, 0:4}], [1, 1, 1, 1])

prof2.display()
iterated_removal_cl.display(prof2)
+---+---+---+---+
| 1 | 1 | 1 | 1 |
+---+---+---+---+
| 2 | 2 | 3 | 1 |
| 1 | 1 | 1 | 2 |
| 3 | 0 | 2 | 3 |
| 0 | 3 | 0 | 0 |
+---+---+---+---+
Iterated Removal Condorcet Loser winners are {1, 2}
Iterated Removal Condorcet Loser winners are {1, 2}
Iterated Removal Condorcet Loser winners are {1, 2}
+-----+---+---+---+
|  1  | 1 | 1 | 1 |
+-----+---+---+---+
| 2 1 | 2 | 3 | 1 |
|  3  | 1 | 1 | 2 |
|  0  | 0 | 2 | 3 |
|     | 3 | 0 | 0 |
+-----+---+---+---+
Iterated Removal Condorcet Loser winner is {1}

Iterated Removal of Condorcet Loser with Explanation

pref_voting.iterative_methods.iterated_removal_cl_with_explanation(edata, curr_cands=None)[source]

Iterated Removal Condorcet Loser with an explanation. In addition to the winner(s), return the order of elimination, where each candidate in the list is a Condorcet loser in the profile (restricted to the remaining candidates).

Parameters:
  • edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph) – Any election data that has a condorcet_loser method.

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates

Example:

from pref_voting.profiles import Profile
from pref_voting.iterative_methods import iterated_removal_cl_with_explanation

prof = Profile([[2, 1, 3, 0], [2, 1, 0, 3], [3, 1, 2, 0], [1, 2, 3, 0]], [1, 1, 1, 1])

prof.display()
ws, exp = iterated_removal_cl_with_explanation(prof)
print(f"The winning set is {ws}")
print(f"The order of elimination is {exp}")
+---+---+---+---+
| 1 | 1 | 1 | 1 |
+---+---+---+---+
| 2 | 2 | 3 | 1 |
| 1 | 1 | 1 | 2 |
| 3 | 0 | 2 | 3 |
| 0 | 3 | 0 | 0 |
+---+---+---+---+
The winning set is [1, 2]
The order of elimination is [0, 3]

Knockout Voting

pref_voting.iterative_methods.knockout(profile, curr_cands=None)[source]

Find the two candidates in curr_cands with the lowest and second lowest Borda scores among any candidates in curr_cands. Then remove from curr_cands whichever one loses to the other in a head-to-head majority comparison. Repeat this process, always using the original Borda score (i.e., the Borda scores calculated with respect to all candidates in the profile, not with respect to curr_cands as for Baldwin and Nanson) until only one candidate remains in curr_cands. Parallel universe tie-breaking (PUT) is used when there are ties in lowest or second lowest Borda scores.

Note

Proposed by Edward B. Foley (with unspecified handling of ties in Borda scores, so PUT is used here as an example).

Parameters:
  • profile (Profile) – An anonymous profile of linear orders on a set of candidates

  • curr_cands (List[int], optional) – If set, then find the winners for the profile restricted to the candidates in curr_cands

Returns:

A sorted list of candidates