Add test case for ERS97 BLT with Meek STV

This commit is contained in:
RunasSudo 2021-01-16 02:59:16 +11:00
parent 7807e36c8e
commit f6cc873d05
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
5 changed files with 102 additions and 2 deletions

View File

@ -0,0 +1,15 @@
Stage:,1,,2,,4,,6,,9,,11,
Comment:,First preferences,,Surpluses distributed,,Surpluses distributed,,Surpluses distributed,,Surpluses distributed,,Surpluses distributed,
Smith,134,EL,107.26,EL,106.96,EL,104.42,EL,101.58,EL,73,EL
Carpenter,81,,87.98,,88.47,,97.71,,101.58,EL,73,EL
Wright,27,,31.99,,32.34,,34.97,,0,EX,0,EX
Glazier,24,,30.19,,30.62,,0,EX,0,EX,0,EX
Duke,105,,106.6,,106.96,EL,104.42,EL,101.58,EL,73,EL
Prince,91,,91,,91,,92.45,,97.07,,73,EL
Baron,64,,64,,64,,64.03,,67.24,,0,EX
Abbot,59,,59.8,,64.85,,66,,68.92,,70.83,
Vicar,55,,55,,69.21,,70.99,,73.27,,75.16,EL
Monk,23,,23.4,,0,EX,0,EX,0,EX,0,EX
Freeman,90,,93.59,,94.27,,95.97,,99.81,,73,EL
Exhausted,0,,2.2,,4.31,,22.05,,41.95,,242.01,
Quota,107.57,,107.26,,106.96,,104.42,,101.58,,73.01,
1 Stage: 1 2 4 6 9 11
2 Comment: First preferences Surpluses distributed Surpluses distributed Surpluses distributed Surpluses distributed Surpluses distributed
3 Smith 134 EL 107.26 EL 106.96 EL 104.42 EL 101.58 EL 73 EL
4 Carpenter 81 87.98 88.47 97.71 101.58 EL 73 EL
5 Wright 27 31.99 32.34 34.97 0 EX 0 EX
6 Glazier 24 30.19 30.62 0 EX 0 EX 0 EX
7 Duke 105 106.6 106.96 EL 104.42 EL 101.58 EL 73 EL
8 Prince 91 91 91 92.45 97.07 73 EL
9 Baron 64 64 64 64.03 67.24 0 EX
10 Abbot 59 59.8 64.85 66 68.92 70.83
11 Vicar 55 55 69.21 70.99 73.27 75.16 EL
12 Monk 23 23.4 0 EX 0 EX 0 EX 0 EX
13 Freeman 90 93.59 94.27 95.97 99.81 73 EL
14 Exhausted 0 2.2 4.31 22.05 41.95 242.01
15 Quota 107.57 107.26 106.96 104.42 101.58 73.01

BIN
tests/data/ers97_meekm.ods Normal file

Binary file not shown.

View File

@ -19,7 +19,7 @@ from pytest_steps import test_steps
import pyRCV2.blt import pyRCV2.blt
import pyRCV2.numbers import pyRCV2.numbers
from pyRCV2.method.gregory import EGSTVCounter from pyRCV2.method.gregory import EGSTVCounter
from pyRCV2.model import CandidateState, CountCompleted from pyRCV2.model import CandidateState
import tests.util import tests.util
import csv import csv

85
tests/test_ers97_meekm.py Normal file
View File

@ -0,0 +1,85 @@
# pyRCV2: Preferential vote counting
# Copyright © 2020–2021 Lee Yingtong Li (RunasSudo)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from pytest_steps import test_steps
import pyRCV2.blt
import pyRCV2.numbers
from pyRCV2.method.meek import MeekSTVCounter
from pyRCV2.model import CandidateState
import tests.util
import csv
def isclose(a, b):
# Allow discrepancy of ~0.01
diff = a - b
if diff < pyRCV2.numbers.Num('0.014') and diff > pyRCV2.numbers.Num('-0.014'):
return True
return False
# Read model CSV
with open('tests/data/ers97_meekm.csv', 'r', newline='') as f:
reader = csv.reader(f)
data = [x for x in reader]
candidates = [data[i][0] for i in range(2, len(data) - 2)]
@test_steps(*['Stage {}'.format(data[0][i]) for i in range(1, len(data[0]), 2)])
def test_ers97_meekm_py():
"""Compare count of ers97.blt with model result produced by Hill et al. reference implementation"""
pyRCV2.numbers.set_numclass(pyRCV2.numbers.FixedGuarded)
pyRCV2.numbers.set_dps(4)
with open('tests/data/ers97.blt', 'r') as f:
election = pyRCV2.blt.readBLT(f.read())
assert len(election.candidates) == len(candidates)
counter = MeekSTVCounter(election, {
'quota': 'droop_exact',
'quota_criterion': 'gt',
'quota_mode': 'progressive',
'ties': []
})
result = counter.reset()
for i in range(1, len(data[0]), 2):
stage = int(data[0][i])
while len(counter.step_results) < stage:
result = counter.step()
comment = data[1][i]
assert result.comment == comment, 'Failed to verify comment'
for j, cand in enumerate(candidates):
votes = pyRCV2.numbers.Num(data[j + 2][i])
cc = next(cc for c, cc in result.candidates.items() if c.name == cand)
assert isclose(cc.votes, votes), 'Failed to verify candidate "{}" votes, got {} expected {}'.format(cand, cc.votes.pp(2), votes.pp(2))
state = data[j + 2][i + 1] if len(data[j + 2]) > (i + 1) else ''
accept = {'': CandidateState.HOPEFUL, 'EL': CandidateState.ELECTED, 'EX': CandidateState.EXCLUDED}
assert cc.state == accept[state], 'Failed to verify candidate "{}" state'.format(cand)
# NB: Ignore exhausted votes
quota = pyRCV2.numbers.Num(data[len(candidates) + 3][i])
assert isclose(result.quota, quota), 'Failed to verify quota, got {} expected {}'.format(result.quota.pp(2), quota.pp(2))
yield 'Stage {}'.format(stage)

View File

@ -23,7 +23,7 @@ from pyRCV2.method.meek import MeekSTVCounter
from pyRCV2.model import CandidateState, CountCompleted from pyRCV2.model import CandidateState, CountCompleted
from pyRCV2.ties import TiesBackwards from pyRCV2.ties import TiesBackwards
def test_meekm(): def test_meekm_py():
"""Compare count of meekm.blt with model result produced by Hill et al. reference implementation""" """Compare count of meekm.blt with model result produced by Hill et al. reference implementation"""
pyRCV2.numbers.set_numclass(pyRCV2.numbers.FixedGuarded) pyRCV2.numbers.set_numclass(pyRCV2.numbers.FixedGuarded)