-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmerrill_1984_fig_2c_2d_updated.py
135 lines (109 loc) · 5.57 KB
/
merrill_1984_fig_2c_2d_updated.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
Add modern voting methods to Figures 2.c and 2.d
Condorcet Efficiency under Spatial-Model Assumptions
(201 voters, two dimensions, correlation = .5, relative dispersion = .5 or 1.0)
from
S. Merrill III, "A Comparison of Efficiency of Multicandidate
Electoral Systems", American Journal of Political Science, vol. 28,
no. 1, pp. 23-48, 1984. :doi:`10.2307/2110786`
Results with 100_000 elections:
2.c
| Method | 2 | 3 | 4 | 5 | 6 | 7 |
|:----------------|------:|------:|------:|------:|------:|------:|
| Condorcet RCV | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 |
| Coombs | 100.0 | 99.4 | 98.6 | 97.8 | 96.9 | 96.0 |
| STAR | 100.0 | 97.8 | 94.7 | 92.1 | 89.9 | 88.0 |
| Borda | 100.0 | 91.4 | 89.2 | 87.1 | 85.8 | 84.8 |
| Score | 100.0 | 88.7 | 84.6 | 82.7 | 81.3 | 79.9 |
| Approval (opt.) | 100.0 | 86.0 | 79.7 | 73.9 | 70.5 | 67.0 |
| Hare RCV | 100.0 | 94.1 | 86.6 | 79.0 | 71.3 | 65.1 |
| Top-2 Runoff | 100.0 | 94.1 | 87.1 | 79.9 | 72.8 | 65.9 |
| Plurality | 100.0 | 80.6 | 67.8 | 57.3 | 49.1 | 42.7 |
2.d
| Method | 2 | 3 | 4 | 5 | 6 | 7 |
|:----------------|------:|------:|------:|------:|------:|------:|
| Condorcet RCV | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 | 100.0 |
| Coombs | 100.0 | 98.3 | 95.7 | 93.4 | 90.8 | 88.5 |
| STAR | 100.0 | 96.5 | 91.5 | 87.2 | 83.1 | 79.8 |
| Borda | 100.0 | 89.2 | 86.4 | 83.9 | 82.2 | 80.7 |
| Score | 100.0 | 86.2 | 80.5 | 77.4 | 74.5 | 72.1 |
| Approval (opt.) | 100.0 | 83.7 | 76.9 | 71.6 | 67.6 | 64.6 |
| Hare RCV | 100.0 | 72.4 | 50.4 | 35.6 | 26.2 | 19.8 |
| Top-2 Runoff | 100.0 | 72.4 | 50.5 | 35.3 | 24.5 | 17.1 |
| Plurality | 100.0 | 56.3 | 34.8 | 21.5 | 13.5 | 8.6 |
These look generally like Merrill's, but are smoother, and there are some
discrepancies, as great as 7%. This may just be random variation from not
running as many simulations, however the Coombs results are consistently
high.
"""
import time
from collections import Counter
import matplotlib.pyplot as plt
import numpy as np
from tabulate import tabulate
from elsim.elections import normal_electorate, normed_dist_utilities
from elsim.methods import (approval, black, borda, condorcet, coombs, fptp,
irv, runoff, score, star, utility_winner)
from elsim.strategies import (approval_optimal, honest_normed_scores,
honest_rankings)
n_elections = 5_000 # Roughly 30 seconds each on a 2019 6-core i7-9750H
n_voters = 201
n_cands_list = (2, 3, 4, 5, 6, 7)
corr = 0.5
D = 2
ranked_methods = {'Plurality': fptp, 'Top-2 Runoff': runoff, 'Hare RCV': irv,
'Borda': borda, 'Coombs': coombs, 'Condorcet RCV': black}
rated_methods = {'SU max': utility_winner,
'Approval (opt.)': lambda utilities, tiebreaker:
approval(approval_optimal(utilities), tiebreaker),
'Score': lambda utilities, tiebreaker:
score(honest_normed_scores(utilities, 5), tiebreaker),
'STAR': lambda utilities, tiebreaker:
star(honest_normed_scores(utilities, 5), tiebreaker),
}
for fig, disp, ymin in (('2.c', 1.0, 50),
('2.d', 0.5, 0)):
condorcet_winner_count = {key: Counter() for key in (
ranked_methods.keys() | rated_methods.keys() | {'CW'})}
start_time = time.monotonic()
for iteration in range(n_elections):
for n_cands in n_cands_list:
v, c = normal_electorate(n_voters, n_cands, dims=D, corr=corr,
disp=disp)
utilities = normed_dist_utilities(v, c)
rankings = honest_rankings(utilities)
# If there is a Condorcet winner, analyze election, otherwise skip
# it
CW = condorcet(rankings)
if CW is not None:
condorcet_winner_count['CW'][n_cands] += 1
for name, method in ranked_methods.items():
if method(rankings, tiebreaker='random') == CW:
condorcet_winner_count[name][n_cands] += 1
for name, method in rated_methods.items():
if method(utilities, tiebreaker='random') == CW:
condorcet_winner_count[name][n_cands] += 1
elapsed_time = time.monotonic() - start_time
print('Elapsed:', time.strftime("%H:%M:%S", time.gmtime(elapsed_time)),
'\n')
plt.figure(f'Figure {fig}. {n_voters} voters, {n_elections} elections',
figsize=(8, 6.5))
plt.title(f'Figure {fig}: Condorcet Efficiency under Spatial-Model '
f'Assumptions [Disp: {disp}]')
table = []
# Of those elections with CW, likelihood that method chooses CW
x_cw, y_cw = zip(*sorted(condorcet_winner_count['CW'].items()))
for method in ('Condorcet RCV', 'Coombs', 'STAR', 'Borda', 'Score',
'Approval (opt.)', 'Hare RCV', 'Top-2 Runoff', 'Plurality'):
x, y = zip(*sorted(condorcet_winner_count[method].items()))
CE = np.array(y)/y_cw
plt.plot(x, CE*100, '-', label=method)
table.append([method, *CE*100])
print(tabulate(table, ["Method", *x], tablefmt="pipe", floatfmt='.1f'))
print()
plt.legend()
plt.grid(True, color='0.7', linestyle='-', which='major', axis='both')
plt.grid(True, color='0.9', linestyle='-', which='minor', axis='both')
plt.ylim(ymin, 102)
plt.xlim(1.8, 7.2)
plt.show()