Fix bug where withdrawn candidates would interfere with bulk exclusion, etc. logic
This commit is contained in:
parent
1369fb3cfc
commit
1e2f74e60b
@ -66,12 +66,14 @@ class BaseSTVCounter:
|
|||||||
self.step_results = []
|
self.step_results = []
|
||||||
self.num_elected = 0
|
self.num_elected = 0
|
||||||
self.num_excluded = 0
|
self.num_excluded = 0
|
||||||
|
self.num_withdrawn = 0
|
||||||
|
|
||||||
# Withdraw candidates
|
# Withdraw candidates
|
||||||
for candidate in self.election.withdrawn:
|
for candidate in self.election.withdrawn:
|
||||||
__pragma__('opov')
|
__pragma__('opov')
|
||||||
self.candidates[candidate].state = CandidateState.WITHDRAWN
|
self.candidates[candidate].state = CandidateState.WITHDRAWN
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
|
self.num_withdrawn += 1
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
@ -177,7 +179,7 @@ class BaseSTVCounter:
|
|||||||
# Are there just enough candidates to fill all the seats?
|
# Are there just enough candidates to fill all the seats?
|
||||||
if self.options['bulk_elect']:
|
if self.options['bulk_elect']:
|
||||||
# Include EXCLUDING to avoid interrupting an exclusion
|
# Include EXCLUDING to avoid interrupting an exclusion
|
||||||
if len(self.election.candidates) - self.num_excluded + sum(1 for c, cc in self.candidates.items() if cc.state == CandidateState.EXCLUDING) <= self.election.seats:
|
if len(self.election.candidates) - self.num_withdrawn - self.num_excluded + sum(1 for c, cc in self.candidates.items() if cc.state == CandidateState.EXCLUDING) <= self.election.seats:
|
||||||
# Declare elected all remaining candidates
|
# Declare elected all remaining candidates
|
||||||
candidates_elected = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
|
candidates_elected = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
|
||||||
if len(candidates_elected) == 1:
|
if len(candidates_elected) == 1:
|
||||||
@ -295,7 +297,7 @@ class BaseSTVCounter:
|
|||||||
|
|
||||||
# If we did not perform bulk election in before_surpluses: Are there just enough candidates to fill all the seats?
|
# If we did not perform bulk election in before_surpluses: Are there just enough candidates to fill all the seats?
|
||||||
if not self.options['bulk_elect']:
|
if not self.options['bulk_elect']:
|
||||||
if len(self.election.candidates) - self.num_excluded <= self.election.seats:
|
if len(self.election.candidates) - self.num_withdrawn - self.num_excluded <= self.election.seats:
|
||||||
# Declare elected one remaining candidate at a time
|
# Declare elected one remaining candidate at a time
|
||||||
hopefuls = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
|
hopefuls = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
|
||||||
hopefuls.sort(key=lambda x: x[1].votes, reverse=True)
|
hopefuls.sort(key=lambda x: x[1].votes, reverse=True)
|
||||||
@ -367,7 +369,7 @@ class BaseSTVCounter:
|
|||||||
Returns List[Tuple[Candidate, CountCard]]
|
Returns List[Tuple[Candidate, CountCard]]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
remaining_candidates = len(self.election.candidates) - self.num_excluded
|
remaining_candidates = len(self.election.candidates) - self.num_withdrawn - self.num_excluded
|
||||||
__pragma__('opov')
|
__pragma__('opov')
|
||||||
total_surpluses = sum((cc.votes - self.quota for c, cc in self.candidates.items() if cc.votes > self.quota), Num(0))
|
total_surpluses = sum((cc.votes - self.quota for c, cc in self.candidates.items() if cc.votes > self.quota), Num(0))
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
|
@ -47,6 +47,7 @@ def test_aec_tas19():
|
|||||||
counter = UIGSTVCounter(election, {
|
counter = UIGSTVCounter(election, {
|
||||||
'surplus_order': 'order',
|
'surplus_order': 'order',
|
||||||
'exclusion': 'by_value',
|
'exclusion': 'by_value',
|
||||||
|
'bulk_exclude': True,
|
||||||
'round_quota': 0,
|
'round_quota': 0,
|
||||||
'round_votes': 0,
|
'round_votes': 0,
|
||||||
})
|
})
|
||||||
|
@ -59,6 +59,9 @@ def maketst(numbers, counter_cls, options, options_description):
|
|||||||
return t_py, t_js
|
return t_py, t_js
|
||||||
|
|
||||||
test_prsa1_scottish_py, test_prsa1_scottish_js = maketst('Fixed', 'pyRCV2.method.gregory.WIGSTVCounter', {'round_quota': 0}, '--round-quota 0')
|
test_prsa1_scottish_py, test_prsa1_scottish_js = maketst('Fixed', 'pyRCV2.method.gregory.WIGSTVCounter', {'round_quota': 0}, '--round-quota 0')
|
||||||
test_prsa1_senate_py, test_prsa1_senate_js = maketst('Fixed', 'pyRCV2.method.gregory.UIGSTVCounter', {'surplus_order': 'order', 'exclusion': 'by_value', 'round_quota': 3, 'round_votes': 3, 'round_tvs': 3, 'round_weights': 3}, '--method uig --round-quota 3 --round-votes 3 --round-tvs 3 --round-weights 3 --surplus-order order --exclusion by_value')
|
test_prsa1_senate_py, test_prsa1_senate_js = maketst('Fixed', 'pyRCV2.method.gregory.UIGSTVCounter', {'surplus_order': 'order', 'exclusion': 'by_value', 'bulk_exclude': True, 'round_quota': 0, 'round_votes': 0}, '--method uig --bulk-exclude --round-quota 0 --round-votes 0 --surplus-order order --exclusion by_value')
|
||||||
test_prsa1_meek_py, test_prsa1_meek_js = maketst('Fixed', 'pyRCV2.method.meek.MeekSTVCounter', {'quota_criterion': 'gt', 'quota': 'droop_exact', 'quota_mode': 'progressive'}, '--method meek --quota droop_exact --quota-criterion gt --quota-mode progressive')
|
test_prsa1_meek_py, test_prsa1_meek_js = maketst('Fixed', 'pyRCV2.method.meek.MeekSTVCounter', {'quota_criterion': 'gt', 'quota': 'droop_exact', 'quota_mode': 'progressive'}, '--method meek --quota droop_exact --quota-criterion gt --quota-mode progressive')
|
||||||
test_prsa1_wright_py, test_prsa1_wright_js = maketst('Fixed', 'pyRCV2.method.gregory.WIGSTVCounter', {'exclusion': 'wright', 'round_quota': 0, 'bulk_exclude': True}, '--bulk-exclude --round-quota 0 --exclusion wright')
|
test_prsa1_wright_py, test_prsa1_wright_js = maketst('Fixed', 'pyRCV2.method.gregory.WIGSTVCounter', {'exclusion': 'wright', 'round_quota': 0, 'bulk_exclude': True}, '--bulk-exclude --round-quota 0 --exclusion wright')
|
||||||
|
|
||||||
|
# Regression test: Withdrawn candidates interfering with bulk exclusion
|
||||||
|
test_meekm_senate_py, test_meekm_senate_js = maketst('Fixed', 'pyRCV2.method.gregory.UIGSTVCounter', {'surplus_order': 'order', 'exclusion': 'by_value', 'bulk_exclude': True, 'round_quota': 0, 'round_votes': 0}, '--method uig --bulk-exclude --round-quota 0 --round-votes 0 --surplus-order order --exclusion by_value')
|
||||||
|
Reference in New Issue
Block a user