============
Preferential
============

Copeland's Method
-----------------

Below is an example of an election that took place between 5 candidates
competing for one seat::

    >>> from ballotbox.ballot import BallotBox
    >>> from ballotbox.singlewinner.plurality import FirstPastPostVoting
    >>> from ballotbox.singlewinner.preferential import CopelandVoting

    >>> rounds = []
    >>> round1 = BallotBox(method=FirstPastPostVoting)
    >>> round1.batch_votes([("alice", 41), ("bob", 59)])
    >>> rounds.append(round1)

    >>> round2 = BallotBox(method=FirstPastPostVoting)
    >>> round2.batch_votes([("alice", 71), ("carol", 29)])
    >>> rounds.append(round2)

    >>> round3 = BallotBox(method=FirstPastPostVoting)
    >>> round3.batch_votes([("alice", 61), ("dave", 39)])
    >>> rounds.append(round3)

    >>> round4 = BallotBox(method=FirstPastPostVoting)
    >>> round4.batch_votes([("alice", 71), ("eve", 0)])
    >>> rounds.append(round4)

    >>> round5 = BallotBox(method=FirstPastPostVoting)
    >>> round5.batch_votes([("bob", 30), ("carol", 60)])
    >>> rounds.append(round5)

    >>> round6 = BallotBox(method=FirstPastPostVoting)
    >>> round6.batch_votes([("bob", 30), ("dave", 70)])
    >>> rounds.append(round6)

    >>> round7 = BallotBox(method=FirstPastPostVoting)
    >>> round7.batch_votes([("bob", 59), ("eve", 41)])
    >>> rounds.append(round7)

    >>> round8 = BallotBox(method=FirstPastPostVoting)
    >>> round8.batch_votes([("carol", 60), ("dave", 10)])
    >>> rounds.append(round8)

    >>> round9 = BallotBox(method=FirstPastPostVoting)
    >>> round9.batch_votes([("carol", 71), ("eve", 29)])
    >>> rounds.append(round9)

    >>> round10 = BallotBox(method=FirstPastPostVoting)
    >>> round10.batch_votes([("dave", 39), ("eve", 61)])
    >>> rounds.append(round10)

    >>> bb = BallotBox(method=CopelandVoting)
    >>> bb.get_winner(rounds)
    [(2, 'alice')]


The Kemeny-Young Method
-----------------------

Here is a fictional vote to move the capital of Tennessee. The inhabitants of
each city want to have the capital as close to their city as possible, if their
city doesn't actually win. Each voter will list their preferences for the state
capital from most desired to least.

The candidates for the capital are:

 * Memphis, the state's largest city, with 42% of the voters, but located far
   from the other cities

 * Nashville, with 26% of the voters, near the center of Tennessee

 * Knoxville, with 17% of the voters

 * Chattanooga, with 15% of the voters

The three nearest cities to Memphis, closest first, are: Nashville,
Chattanooga, and Knoxville -- so Memphis voters would vote in that order (with
Memphis first, of course). For Nashville, the nearest are: Chattanooga,
Knoxville, and Memphis. For Chattanooga they are: Knoxville, Nashville, and
Memphis. For Knoxville they are: Chattanooga, Nashville, and Memphis.

So this means that 42% of the voters would make their list of city preferences
in the first order given. Similarly, 26%, 17%, and 15% would do the same for
the remaining, respectively.

Here the code that determines the winner (we'll just assume that there are 100
people living in Tennessee)::

    >>> from ballotbox.singlewinner.preferential import KemenyYoungVoting

    >>> bb = BallotBox(method=KemenyYoungVoting)
    >>> preference = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> bb.add_votes(preference, 42)
    >>> preference = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 26)
    >>> preference = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 17)
    >>> preference = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 15)

    >>> bb.get_winner()
    [(393, (u'Nashville', u'Chattanooga', u'Knoxville', u'Memphis'))]

We can also return runners' up, etc.::

    >>> bb.get_winner(position_count=2)
    [(393, (u'Nashville', u'Chattanooga', u'Knoxville', u'Memphis')), (377, (u'Nashville', u'Chattanooga', u'Memphis', u'Knoxville'))]
    >>> bb.get_winner(position_count=3)
    [(393, (u'Nashville', u'Chattanooga', u'Knoxville', u'Memphis')), (377, (u'Nashville', u'Chattanooga', u'Memphis', u'Knoxville')), (361, (u'Nashville', u'Memphis', u'Chattanooga', u'Knoxville'))]

Here's a listing of all possibilities and their ranks::

    >>> for rank, preference in bb.get_winner(position_count=24):
    ...   print " ".join(preference), rank
    Nashville Chattanooga Knoxville Memphis 393
    Nashville Chattanooga Memphis Knoxville 377
    Nashville Memphis Chattanooga Knoxville 361
    Chattanooga Nashville Knoxville Memphis 357
    Memphis Nashville Chattanooga Knoxville 345
    Chattanooga Nashville Memphis Knoxville 341
    Nashville Knoxville Chattanooga Memphis 327
    Chattanooga Memphis Nashville Knoxville 325
    Chattanooga Knoxville Nashville Memphis 321
    Nashville Knoxville Memphis Chattanooga 311
    Memphis Chattanooga Nashville Knoxville 309
    Chattanooga Knoxville Memphis Nashville 305
    Nashville Memphis Knoxville Chattanooga 295
    Knoxville Nashville Chattanooga Memphis 291
    Chattanooga Memphis Knoxville Nashville 289
    Memphis Nashville Knoxville Chattanooga 279
    Knoxville Nashville Memphis Chattanooga 275
    Memphis Chattanooga Knoxville Nashville 273
    Knoxville Memphis Nashville Chattanooga 259
    Knoxville Chattanooga Nashville Memphis 255
    Memphis Knoxville Nashville Chattanooga 243
    Knoxville Chattanooga Memphis Nashville 239
    Knoxville Memphis Chattanooga Nashville 223
    Memphis Knoxville Chattanooga Nashville 207

Now, we could have a situation where there was a tie or equal preference
between to choices. For instance, in the following example, Everyone in
Chattanooga equally preferred Knoxville and Nashville::

    >>> bb = BallotBox(method=KemenyYoungVoting)
    >>> preference1 = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> preference2 = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> preference3 = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> preference4 = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 2, "Memphis": 3}
    >>> bb.batch_votes([
    ... (preference1, 42), (preference2, 26), (preference3, 17), 
    ... (preference4, 15)])
    >>> bb.get_winner()
    [(393, (u'Nashville', u'Chattanooga', u'Knoxville', u'Memphis'))]

Here's a listing of all possibilities and their ranks::

    >>> for rank, preference in bb.get_winner(position_count=24):
    ...   print " ".join(preference), rank
    Nashville Chattanooga Knoxville Memphis 393
    Nashville Chattanooga Memphis Knoxville 377
    Nashville Memphis Chattanooga Knoxville 361
    Chattanooga Nashville Knoxville Memphis 357
    Memphis Nashville Chattanooga Knoxville 345
    Chattanooga Nashville Memphis Knoxville 341
    Nashville Knoxville Chattanooga Memphis 327
    Chattanooga Memphis Nashville Knoxville 325
    Nashville Knoxville Memphis Chattanooga 311
    Memphis Chattanooga Nashville Knoxville 309
    Chattanooga Knoxville Nashville Memphis 306
    Nashville Memphis Knoxville Chattanooga 295
    Chattanooga Knoxville Memphis Nashville 290
    Memphis Nashville Knoxville Chattanooga 279
    Knoxville Nashville Chattanooga Memphis 276
    Chattanooga Memphis Knoxville Nashville 274
    Knoxville Nashville Memphis Chattanooga 260
    Memphis Chattanooga Knoxville Nashville 258
    Knoxville Memphis Nashville Chattanooga 244
    Knoxville Chattanooga Nashville Memphis 240
    Memphis Knoxville Nashville Chattanooga 228
    Knoxville Chattanooga Memphis Nashville 224
    Knoxville Memphis Chattanooga Nashville 208
    Memphis Knoxville Chattanooga Nashville 192

Note that the rank amounts changed from the previous example as a result of the
tie in preference3.


Minmax Voting
-------------

The Minmax voting method has three varieties:

 * winning votes

 * margins

 * pairwise opposition

Let's take a look at these in order, starting with the "winning votes" method::

    >>> from ballotbox.singlewinner.preferential import MinimaxVoting

    >>> bb = BallotBox(method=MinimaxVoting, mode="winning votes")
    >>> preference = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> bb.add_votes(preference, 42)
    >>> preference = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 26)
    >>> preference = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 17)
    >>> preference = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 2, "Memphis": 3}
    >>> bb.add_votes(preference, 15)

    >>> bb.get_winner("Nashville", "Memphis")
    [(58, 'Nashville > Memphis')]
    >>> bb.get_winner("Memphis", "Nashville")
    [(0, 'Memphis > Nashville')]
    >>> bb.get_winner("Knoxville", "Chattanooga")
    [(0, 'Knoxville > Chattanooga')]
    >>> bb.get_winner("Chattanooga", "Knoxville")
    [(83, 'Chattanooga > Knoxville')]
    >>> bb.get_winner("Nashville", "Knoxville")
    [(68, 'Nashville > Knoxville')]

Next is the "margins" method::

    >>> bb = BallotBox(method=MinimaxVoting, mode="margins")
    >>> preference1 = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> preference2 = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> preference3 = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> preference4 = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 2, "Memphis": 3}
    >>> bb.batch_votes([
    ... (preference1, 42), (preference2, 26), (preference3, 17), 
    ... (preference4, 15)])

    >>> bb.get_winner("Nashville", "Memphis")
    [(16, 'Nashville > Memphis')]
    >>> bb.get_winner("Memphis", "Nashville")
    [(-16, 'Memphis > Nashville')]
    >>> bb.get_winner("Knoxville", "Chattanooga")
    [(-66, 'Knoxville > Chattanooga')]
    >>> bb.get_winner("Chattanooga", "Knoxville")
    [(66, 'Chattanooga > Knoxville')]
    >>> bb.get_winner("Nashville", "Knoxville")
    [(51, 'Nashville > Knoxville')]

Finally, we have the "pairwise opposition" method::

    >>> bb = BallotBox(method=MinimaxVoting, mode="pairwise opposition")
    >>> preference1 = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> preference2 = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> preference3 = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> preference4 = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 2, "Memphis": 3}
    >>> bb.batch_votes([
    ... (preference1, 42), (preference2, 26), (preference3, 17), 
    ... (preference4, 15)])

    >>> bb.get_winner("Nashville", "Memphis")
    [(58, 'Nashville > Memphis')]
    >>> bb.get_winner("Memphis", "Nashville")
    [(42, 'Memphis > Nashville')]
    >>> bb.get_winner("Knoxville", "Chattanooga")
    [(17, 'Knoxville > Chattanooga')]
    >>> bb.get_winner("Chattanooga", "Knoxville")
    [(83, 'Chattanooga > Knoxville')]
    >>> bb.get_winner("Nashville", "Knoxville")
    [(68, 'Nashville > Knoxville')]


Borda Voting
------------

The Borda count method has four major varieties:

 * standard

 * fractional

 * truncated

 * modified

These are covered below, starting with the standard Borda method::

    >>> from ballotbox.singlewinner.preferential import BordaVoting

    >>> bb = BallotBox(method=BordaVoting, mode="standard")
    >>> preference = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> bb.add_votes(preference, 42)
    >>> preference = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 26)
    >>> preference = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 17)
    >>> preference = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 15)

    >>> bb.get_winner()
    [(194, u'Nashville')]

Here are the results using the fractional Borda count::

    >>> bb = BallotBox(method=BordaVoting, mode="fractional")
    >>> preference = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> bb.add_votes(preference, 42)
    >>> preference = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 26)
    >>> preference = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 17)
    >>> preference = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 15)

    >>> bb.get_winner()
    [(57.666666666666664, u'Nashville')]

Here are the results using a truncated Borda count::

    >>> bb = BallotBox(method=BordaVoting, mode="truncated")
    >>> preference = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 0, "Knoxville": 0}
    >>> bb.add_votes(preference, 42)
    >>> preference = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 0, "Memphis": 0}
    >>> bb.add_votes(preference, 26)
    >>> preference = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 0}
    >>> bb.add_votes(preference, 17)
    >>> preference = {
    ...   "Chattanooga": 1, "Knoxville": 0, "Nashville": 0, "Memphis": 0}
    >>> bb.add_votes(preference, 15)

    >>> bb.get_winner()
    [(179, u'Nashville')]

Here are the results using modified Borda count::

    >>> bb = BallotBox(method=BordaVoting, mode="modified")
    >>> preference = {
    ...   "Memphis": 1, "Nashville": 2}
    >>> bb.add_votes(preference, 42)
    >>> preference = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3}
    >>> bb.add_votes(preference, 26)
    >>> preference = {
    ...   "Knoxville": 1, "Chattanooga": 2}
    >>> bb.add_votes(preference, 17)
    >>> preference = {
    ...   "Chattanooga": 1}
    >>> bb.add_votes(preference, 15)

    >>> bb.get_winner()
    [(52, u'Nashville')]

In the Borda count it is possible for a candidate who is the first preference
of an absolute majority of voters to fail to be elected; this is because the
Borda count affords greater importance to a voter's lower preferences than most
other systems. Here is an example of that in action::

    >>> bb = BallotBox(method=BordaVoting, mode="standard")
    >>> preference = {
    ...   "Alice": 1, "Carol": 2, "Bob": 3, "Dave": 4}
    >>> bb.add_votes(preference, 51)
    >>> preference = {
    ...   "Carol": 1, "Bob": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 5)
    >>> preference = {
    ...   "Bob": 1, "Carol": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 23)
    >>> preference = {
    ...   "Dave": 1, "Carol": 2, "Bob": 3, "Alice": 4}
    >>> bb.add_votes(preference, 21)

    >>> bb.get_winner()
    [(205, u'Carol')]

In most other systems, Alice would have been the winner. Take, for example, the
Kemeny-Young method::

    >>> bb = BallotBox(method=KemenyYoungVoting)
    >>> preference = {
    ...   "Alice": 1, "Carol": 2, "Bob": 3, "Dave": 4}
    >>> bb.add_votes(preference, 51)
    >>> preference = {
    ...   "Carol": 1, "Bob": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 5)
    >>> preference = {
    ...   "Bob": 1, "Carol": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 23)
    >>> preference = {
    ...   "Dave": 1, "Carol": 2, "Bob": 3, "Alice": 4}
    >>> bb.add_votes(preference, 21)

    >>> bb.get_winner()
    [(388, (u'Alice', u'Carol', u'Bob', u'Dave'))]


Nanson Voting
-------------

By using the Nanson method, we end up with a condorcet Borda count. It uses
the same mechanisms as Borda, but it retallies votes after eliminating
candidates who rank lower than the average Borda count in each round. This
results in exactly one winner::

    >>> from ballotbox.singlewinner.preferential import NansonVoting

    >>> bb = BallotBox(method=NansonVoting)
    >>> preference = {
    ...   "Alice": 1, "Carol": 2, "Bob": 3, "Dave": 4}
    >>> bb.add_votes(preference, 51)
    >>> preference = {
    ...   "Carol": 1, "Bob": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 5)
    >>> preference = {
    ...   "Bob": 1, "Carol": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 23)
    >>> preference = {
    ...   "Dave": 1, "Carol": 2, "Bob": 3, "Alice": 4}
    >>> bb.add_votes(preference, 21)

    >>> bb.get_winner()
    [(205, u'Carol')]


Baldwin Voting
--------------

While the Nanson method drops candidates below the average, the Baldwin method
simple drops the lowest ranking candidate (using Borda count scores) and
retallies until only one winner remains::

    >>> from ballotbox.singlewinner.preferential import BaldwinVoting

    >>> bb = BallotBox(method=BaldwinVoting)
    >>> preference = {
    ...   "Alice": 1, "Carol": 2, "Bob": 3, "Dave": 4}
    >>> bb.add_votes(preference, 51)
    >>> preference = {
    ...   "Carol": 1, "Bob": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 5)
    >>> preference = {
    ...   "Bob": 1, "Carol": 2, "Dave": 3, "Alice": 4}
    >>> bb.add_votes(preference, 23)
    >>> preference = {
    ...   "Dave": 1, "Carol": 2, "Bob": 3, "Alice": 4}
    >>> bb.add_votes(preference, 21)

    >>> bb.get_winner()
    [(205, u'Carol')]


------------------
Ranked Pair Voting
------------------

    >>> from ballotbox.singlewinner.preferential import RankedPairsVoting

    >>> bb = BallotBox(method=RankedPairsVoting)
    >>> preference = {
    ...   "Memphis": 1, "Nashville": 2, "Chattanooga": 3, "Knoxville": 4}
    >>> bb.add_votes(preference, 42)
    >>> preference = {
    ...   "Nashville": 1, "Chattanooga": 2, "Knoxville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 26)
    >>> preference = {
    ...   "Knoxville": 1, "Chattanooga": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 17)
    >>> preference = {
    ...   "Chattanooga": 1, "Knoxville": 2, "Nashville": 3, "Memphis": 4}
    >>> bb.add_votes(preference, 15)

    >>> bb.get_winner()
