forked from ron-rivest/audit-lab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
outcomes.py
98 lines (74 loc) · 2.38 KB
/
outcomes.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
# outcomes.py
# Ronald L. Rivest (with Karim Husayn Karimi)
# July 7, 2017
# python3
"""
Tally and outcome computations.
Code to compute an election outcome, given a sequence of votes and a contest type.
Also known as "social choice functions".
An outcome is always a *tuple* of ids, even if there is only one winner.
"""
# TBD: Tie-breaking, etc.
import ids
def compute_tally(vec):
"""
Here vec is an iterable of hashable elements.
Return dict giving tally of elements.
"""
tally = {}
for x in vec:
tally[x] = tally.get(x, 0) + 1
return tally
def plurality(e, cid, tally):
"""
Return, for input dict tally mapping votes to (int) counts,
vote with largest count. (Tie-breaking done arbitrarily here.)
Winning vote must be a valid winner
(e.g. not ("-Invalid",) or ("-NoSuchContest",) )
an Exception is raised if this is not possible.
An undervote or an overvote can't win.
"""
max_cnt = -1e90
max_vote = None
for vote in tally:
if tally[vote] > max_cnt and \
len(vote) == 1 and \
not ids.is_error_selid(vote[0]):
max_cnt = tally[vote]
max_vote = vote
if max_vote==None:
assert "No winner allowed in plurality contest.", tally
return max_vote
def compute_ro_c(e):
"""
Compute reported outcomes ro_c for each cid, from e.rn_cr.
"""
e.ro_c = dict()
for cid in e.rn_cr:
tally = e.rn_cr[cid]
e.ro_c[cid] = compute_outcome(e, cid, tally)
def compute_outcome(e, cid, tally):
"""
Return outcome for the given contest, given tally of votes.
"""
if e.contest_type_c[cid].lower()=="plurality":
return plurality(e, cid, tally)
else:
# TBD: IRV, etc...
multi.myerror(("Non-plurality outcome rule {} for contest {}"
"not yet implemented!")
.format(e.contest_type_c[cid], cid))
def compute_tally2(vec):
"""
Input vec is an iterable of (a, r) pairs.
(i.e., (actual vote, reported vote) pairs).
Return dict giving mapping from rv to dict
giving tally of av's that appear with that rv.
(Used for comparison audits.)
"""
tally2 = {}
for (av, rv) in vec:
if rv not in tally2:
tally2[rv] = compute_tally([aa for (aa, rr)
in vec if rv == rr])
return tally2