Election Simulator 3000¶
This is a library of functions for simulating thousands of elections held using different voting methods (Borda count, Approval voting, etc.) under different voter models (impartial culture, spatial model, etc.) and estimating various metrics from them (Social Utility Efficiency = Voter Satisfaction Efficiency = VSE, Condorcet Efficiency, likelihood of Condorcet cycles, etc.)
For example, it can be used to reproduce Figure 1 from Merrill 1984:
Or the table of Effectiveness from Weber 1977:
Standard |
Vote-for-half |
Borda |
|
---|---|---|---|
2 |
81.37 |
81.71 |
81.41 |
3 |
75.10 |
75.00 |
86.53 |
4 |
69.90 |
79.92 |
89.47 |
5 |
65.02 |
79.09 |
91.34 |
6 |
61.08 |
81.20 |
92.61 |
10 |
50.78 |
82.94 |
95.35 |
255 |
12.78 |
86.37 |
99.80 |
See /examples folder for more on what it can do, such as reproductions of previous research.
Goals¶
Fast (~25,000 elections per second on Core i7-9750H)
Flexible
Well-documented, easily-used and improved upon by other people
Well-tested and bug-free
Able to reproduce peer-reviewed research
Requirements¶
See requirements.txt
. As of this README, it includes numpy and scipy for the simulations, tabulate for printing example tables, joblib for parallelizing extreme examples, and pytest, hypothesis, and pytest-cov for running the tests. All should be installable through conda
.
Optionally, elsim
can use numba for speed. If not available, the code will still run, just more slowly.
Installation¶
One possibility is to install with pip:
pip install git+https://github.com/endolith/elsim.git
Documentation¶
Currently just the docstrings of the submodules and functions themselves, in numpydoc format. Now being rendered at https://endolith.github.io/elsim/
Usage¶
Specify an election with three candidates (0, 1, 2), where two voters rank candidates 0 > 2 > 1, two voters rank candidates 1 > 2 > 0, and one ranks candidates 2 > 0 > 1:
>>> election = [[0, 2, 1],
... [0, 2, 1],
... [1, 2, 0],
... [1, 2, 0],
... [2, 0, 1]]
Calculate the winner using Black’s method:
>>> from elsim.methods import black
>>> black(election)
2
Candidate 2 is the Condorcet winner, and wins under Black’s method.
Submodules and chained functions¶
Originally, the functions in submodules were meant to be chained together in a simple flow:
A function from
elsim.elections
takes parameters as input (number of candidates, number of voters, dispersion in spatial model, etc.) and produces an array of utilities (each voter’s appraisal of each candidate).Then a function from
elsim.strategies
converts each voter’s utilities into a ballot.Then a function from
elsim.methods
counts the collection of ballots and chooses a winner.
flowchart LR Parameters -- Election --> Utilities Utilities -- Strategy --> Ballots Ballots -- Method --> Winner
However, while implementing many different types of simulations, it has become more complicated. Some functions produce intermediate results, while others skip over multiple steps. I’m no longer sure the best way to organize these functions into submodules. Here is a diagram showing the flow of every function currently in the submodules:
%%{ init: { 'flowchart': { 'curve': 'monotoneX' } } }%% flowchart LR %% elections.py Parameters -- <code>normal_electorate</code> --> Positions[Spatial positions] Positions -- <code>normed_dist_utilities</code> --> Utilities Parameters -- <code>random_utilities</code> --> Utilities Parameters -- <code>impartial_culture</code> --> ranked_ballots %% strategies.py Utilities -- <code>approval_optimal</code> --> approval_ballots Utilities -- <code>vote_for_k</code> --> approval_ballots Utilities -- <code>honest_normed_scores</code> --> score_ballots Utilities -- <code>honest_rankings</code> --> ranked_ballots subgraph Ballots approval_ballots[Approval ballots] score_ballots[Score ballots] ranked_ballots[Ranked ballots] end %% approval.py approval_ballots -- <code>approval</code> --> Winner score_ballots -- <code>combined_approval</code> --> Winner %% condorcet.py (moved out of order so it renders with fewer line collisions) ranked_ballots -- <code>ranked_election_to_matrix</code> --> Matrix Matrix -- <code>condorcet_from_matrix</code> --> Winner ranked_ballots -- <code>condorcet</code> --> Winner %% black.py ranked_ballots -- <code>black</code> --> Winner %% borda.py ranked_ballots -- <code>borda</code> --> Winner %% coombs.py ranked_ballots -- <code>coombs</code> --> Winner %% fptp.py ranked_ballots -- <code>fptp</code> --> Winner ranked_ballots -- <code>sntv</code> --> Winner %% irv.py ranked_ballots -- <code>irv</code> --> Winner %% runoff.py ranked_ballots -- <code>runoff</code> --> Winner %% score.py score_ballots -- <code>score</code> --> Winner %% star.py score_ballots -- <code>star</code> --> Winner score_ballots -- <code>matrix_from_scores</code> --> Matrix %% utility_winner.py Utilities -- <code>utility_winner</code> --> Winner
Tests¶
Tests can be run by installing the testing dependencies and then running pytest
in the project folder.
Bugs / Requests¶
File issues on the GitHub issue tracker.
Similar projects¶
Election simulators¶
1D:
Election Methods in Pictures (“Voteline”) by Ka-Ping Yee - 5 candidates, normal/uniform/bimodal distribution
Comparing Voting Systems for a Normal Distribution of Voters - Wolfram demonstration clone
2D:
Voting Simulation Visualizations by Ka-Ping Yee - “Yee diagrams”
Elections On The Plane - Yee diagrams by Brian Olson
Yee Pictures illustrating voting method behavior by Warren D. Smith
ND:
Voter Satisfaction Efficiency by Jameson Quinn