In this election, Carter would be eliminated in the first round, and Adams would be the winner with 66 votes to 34 for Brown.
Now suppose that the results were announced, but election officials accidentally destroyed the ballots before they could be certified, and the votes had to be recast. Wanting to “jump on the bandwagon”, 10 of the voters who had originally voted in the order Brown, Adams, Carter change their vote to favor the presumed winner, changing those votes to Adams, Brown, Carter.
# Math in Society 2.7 -- What's Wrong with IRV? (the monotonicity failure)
# 100 voters. In the re-vote, SWITCH of the 12 "Brown, Adams, Carter" voters
# jump on the bandwagon and move Adams up to first: "Adams, Brown, Carter".
# Nothing else changes, and not one vote is ever moved AWAY from Adams.
def election(switch):
return [(37 + switch, "ABC"), (22, "BCA"), (12 - switch, "BAC"), (29, "CAB")]
def irv(schedule):
alive = sorted({c for _, r in schedule for c in r})
alive = set(alive)
tied = False
while True:
tally = {c: 0 for c in alive}
for n, r in schedule:
for c in r:
if c in alive:
tally[c] += n
break
cont = sum(tally.values())
lead = max(tally, key=lambda c: (tally[c], c))
if tally[lead] * 2 > cont or len(alive) == 1:
return lead, tally[lead], cont, tied
low = min(tally.values())
out = sorted(c for c in alive if tally[c] == low)
tied = tied or len(out) > 1
alive.discard(out[0])
names = {"A": "Adams", "B": "Brown", "C": "Carter"}
print("switch first-place votes IRV winner")
prev = None
for switch in range(0, 13):
first = {}
for n, r in election(switch):
first[r[0]] = first.get(r[0], 0) + n
w, got, cont, tied = irv(election(switch))
flag = " <-- the winner just changed" if prev is not None and w != prev else ""
if tied:
flag += " (elimination tie: outcome depends on the tiebreak rule)"
print(f"{switch:>4} " + " ".join(f"{c} {first.get(c, 0):>3}" for c in "ABC")
+ f" {names[w]:<7}{got:>4}/{cont}{flag}")
prev = w
print("\nAdams' column only ever goes UP, and yet partway down this table Adams")
print("stops winning. Gaining support cost Adams the election -- exactly what the")
print("Monotonicity Criterion forbids.")
print("\nTry it: change the 22 'BCA' voters to (22, 'BAC') and re-run. Now Brown's")
print("votes transfer to Adams instead of Carter and Adams wins every row, so IRV")
print("does not ALWAYS violate monotonicity -- it depends on where the eliminated")
print("candidate's second choices go.")
In this re-vote, Brown will be eliminated in the first round, having the fewest first-place votes. After transferring votes, we find that Carter will win this election with 51 votes to Adams’ 49 votes! Even though the only vote changes made favored Adams, the change ended up costing Adams the election. This doesn’t seem right, and introduces our second fairness criterion: