Refactor/DRY Gregory method implementations
This commit is contained in:
parent
ad7efe3133
commit
d23eee79bb
@ -48,38 +48,42 @@ def groupby(iterable, keyfunc):
|
||||
groups.append(group)
|
||||
return groups
|
||||
|
||||
class WIGSTVCounter(BaseSTVCounter):
|
||||
"""
|
||||
Basic weighted inclusive Gregory STV counter
|
||||
"""
|
||||
class BaseGregorySTVCounter(BaseSTVCounter):
|
||||
"""Abstract Gregory STV counter"""
|
||||
|
||||
def describe_options(self):
|
||||
# WIG is the default
|
||||
return BaseSTVCounter.describe_options(self)
|
||||
def _do_gregory_surplus(self, is_weighted, candidate_surplus, count_card, surplus, parcels):
|
||||
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences(parcels)
|
||||
|
||||
def do_surplus(self, candidate_surplus, count_card, surplus):
|
||||
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences(count_card.parcels)
|
||||
if is_weighted:
|
||||
total_units = total_votes
|
||||
else:
|
||||
total_units = total_ballots
|
||||
|
||||
if self.options['papers'] == 'transferable':
|
||||
__pragma__('opov')
|
||||
transferable_votes = total_votes - exhausted_votes
|
||||
if is_weighted:
|
||||
transferable_units = total_votes - exhausted_votes
|
||||
transferable_votes = transferable_units
|
||||
else:
|
||||
transferable_units = total_ballots - exhausted_ballots
|
||||
transferable_votes = total_votes - exhausted_votes
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / transferable_votes).pp(2) + '.')
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / transferable_units).pp(2) + '.')
|
||||
else:
|
||||
tv = self.round_tv(surplus / transferable_votes)
|
||||
tv = self.round_tv(surplus / transferable_units)
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + tv.pp(2) + '.')
|
||||
else:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value received.')
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / total_votes).pp(2) + '.')
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / total_units).pp(2) + '.')
|
||||
else:
|
||||
tv = self.round_tv(surplus / total_votes)
|
||||
tv = self.round_tv(surplus / total_units)
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + tv.pp(2) + '.')
|
||||
__pragma__('noopov')
|
||||
|
||||
@ -88,6 +92,11 @@ class WIGSTVCounter(BaseSTVCounter):
|
||||
num_ballots = x[1]
|
||||
num_votes = x[2]
|
||||
|
||||
if is_weighted:
|
||||
num_units = num_votes
|
||||
else:
|
||||
num_units = num_ballots
|
||||
|
||||
new_parcel = []
|
||||
if len(cand_ballots) > 0:
|
||||
__pragma__('opov')
|
||||
@ -98,16 +107,16 @@ class WIGSTVCounter(BaseSTVCounter):
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.candidates[candidate].transfers += self.round_votes((num_votes * surplus) / transferable_votes)
|
||||
self.candidates[candidate].transfers += self.round_votes((num_units * surplus) / transferable_units)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_votes * tv)
|
||||
self.candidates[candidate].transfers += self.round_votes(num_units * tv)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_votes) # Do not allow weight to increase
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.candidates[candidate].transfers += self.round_votes((num_votes * surplus) / total_votes)
|
||||
self.candidates[candidate].transfers += self.round_votes((num_units * surplus) / total_units)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_votes * tv)
|
||||
self.candidates[candidate].transfers += self.round_votes(num_units * tv)
|
||||
__pragma__('noopov')
|
||||
|
||||
for ballot, ballot_value in cand_ballots:
|
||||
@ -115,18 +124,30 @@ class WIGSTVCounter(BaseSTVCounter):
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
new_value = (ballot_value * surplus) / transferable_votes
|
||||
if is_weighted:
|
||||
new_value = (ballot_value * surplus) / transferable_units
|
||||
else:
|
||||
new_value = (ballot.value * surplus) / transferable_units
|
||||
else:
|
||||
tv = self.round_tv(surplus / transferable_votes)
|
||||
new_value = ballot_value * tv
|
||||
tv = self.round_tv(surplus / transferable_units)
|
||||
if is_weighted:
|
||||
new_value = ballot_value * tv
|
||||
else:
|
||||
new_value = ballot.value * tv
|
||||
else:
|
||||
new_value = ballot_value
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
new_value = (ballot_value * surplus) / total_votes
|
||||
if is_weighted:
|
||||
new_value = (ballot_value * surplus) / total_units
|
||||
else:
|
||||
new_value = (ballot.value * surplus) / total_units
|
||||
else:
|
||||
tv = self.round_tv(surplus / total_votes)
|
||||
new_value = ballot_value * tv
|
||||
tv = self.round_tv(surplus / total_units)
|
||||
if is_weighted:
|
||||
new_value = ballot_value * tv
|
||||
else:
|
||||
new_value = ballot.value * tv
|
||||
|
||||
new_parcel.append((ballot, self.round_weight(new_value)))
|
||||
__pragma__('noopov')
|
||||
@ -138,7 +159,10 @@ class WIGSTVCounter(BaseSTVCounter):
|
||||
else:
|
||||
self.exhausted.transfers += self.round_votes((surplus - transferable_votes))
|
||||
else:
|
||||
self.exhausted.transfers += self.round_votes((exhausted_votes * surplus) / total_votes)
|
||||
if is_weighted:
|
||||
self.exhausted.transfers += self.round_votes((exhausted_votes * surplus) / total_votes)
|
||||
else:
|
||||
self.exhausted.transfers += self.round_votes((exhausted_ballots * surplus) / total_ballots)
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
@ -148,6 +172,7 @@ class WIGSTVCounter(BaseSTVCounter):
|
||||
count_card.state = CandidateState.ELECTED
|
||||
|
||||
def do_exclusion(self, candidates_excluded):
|
||||
"""Implements BaseSTVCounter.do_exclusion"""
|
||||
# Optimisation: Pre-sort exclusion ballots if applicable
|
||||
# self._exclusion[1] -> list of ballots-per-stage, ballots-per-stage = List[Tuple[Candidate,List[Ballot+Value]]]
|
||||
if self._exclusion is None:
|
||||
@ -233,7 +258,20 @@ class WIGSTVCounter(BaseSTVCounter):
|
||||
count_card.state = CandidateState.EXCLUDED
|
||||
self._exclusion = None
|
||||
|
||||
class UIGSTVCounter(WIGSTVCounter):
|
||||
class WIGSTVCounter(BaseGregorySTVCounter):
|
||||
"""
|
||||
Basic weighted inclusive Gregory STV counter
|
||||
"""
|
||||
|
||||
def describe_options(self):
|
||||
# WIG is the default
|
||||
return BaseSTVCounter.describe_options(self)
|
||||
|
||||
def do_surplus(self, candidate_surplus, count_card, surplus):
|
||||
"""Implements BaseSTVCounter.do_surplus"""
|
||||
self._do_gregory_surplus(True, candidate_surplus, count_card, surplus, count_card.parcels)
|
||||
|
||||
class UIGSTVCounter(BaseGregorySTVCounter):
|
||||
"""
|
||||
Basic unweighted inclusive Gregory STV counter
|
||||
"""
|
||||
@ -242,101 +280,11 @@ class UIGSTVCounter(WIGSTVCounter):
|
||||
"""Overrides BaseSTVCounter.describe_options"""
|
||||
return '--method uig ' + BaseSTVCounter.describe_options(self)
|
||||
|
||||
def __init__(self, *args):
|
||||
WIGSTVCounter.__init__(self, *args)
|
||||
|
||||
def do_surplus(self, candidate_surplus, count_card, surplus):
|
||||
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences(count_card.parcels)
|
||||
"""Implements BaseSTVCounter.do_surplus"""
|
||||
self._do_gregory_surplus(False, candidate_surplus, count_card, surplus, count_card.parcels)
|
||||
|
||||
if self.options['papers'] == 'transferable':
|
||||
__pragma__('opov')
|
||||
transferable_ballots = total_ballots - exhausted_ballots
|
||||
transferable_votes = total_votes - exhausted_votes
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / transferable_ballots).pp(2) + '.')
|
||||
else:
|
||||
tv = self.round_tv(surplus / transferable_ballots)
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + tv.pp(2) + '.')
|
||||
else:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value received.')
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / total_ballots).pp(2) + '.')
|
||||
else:
|
||||
tv = self.round_tv(surplus / total_ballots)
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + tv.pp(2) + '.')
|
||||
__pragma__('noopov')
|
||||
|
||||
for candidate, x in next_preferences.items():
|
||||
cand_ballots = x[0]
|
||||
num_ballots = x[1]
|
||||
num_votes = x[2]
|
||||
|
||||
new_parcel = []
|
||||
if len(cand_ballots) > 0:
|
||||
__pragma__('opov')
|
||||
self.candidates[candidate].parcels.append(new_parcel)
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.candidates[candidate].transfers += self.round_votes((num_ballots * surplus) / transferable_ballots)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_ballots * tv)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_votes)
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.candidates[candidate].transfers += self.round_votes((num_ballots * surplus) / total_ballots)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_ballots * tv)
|
||||
__pragma__('noopov')
|
||||
|
||||
for ballot, ballot_value in cand_ballots:
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
new_value = (ballot.value * surplus) / transferable_ballots
|
||||
else:
|
||||
tv = self.round_tv(surplus / transferable_ballots)
|
||||
new_value = ballot.value * tv
|
||||
else:
|
||||
new_value = ballot_value
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
new_value = (ballot.value * surplus) / total_ballots
|
||||
else:
|
||||
tv = self.round_tv(surplus / total_ballots)
|
||||
new_value = ballot.value * tv
|
||||
|
||||
new_parcel.append((ballot, self.round_weight(new_value)))
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
pass # No ballots exhaust
|
||||
else:
|
||||
self.exhausted.transfers += self.round_votes(surplus - transferable_votes)
|
||||
else:
|
||||
self.exhausted.transfers += self.round_votes((exhausted_ballots * surplus) / total_ballots)
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
count_card.transfers -= surplus
|
||||
__pragma__('noopov')
|
||||
|
||||
count_card.state = CandidateState.ELECTED
|
||||
|
||||
class EGSTVCounter(UIGSTVCounter):
|
||||
class EGSTVCounter(BaseGregorySTVCounter):
|
||||
"""
|
||||
Exclusive Gregory (last bundle) STV implementation
|
||||
"""
|
||||
@ -346,95 +294,6 @@ class EGSTVCounter(UIGSTVCounter):
|
||||
return '--method eg ' + BaseSTVCounter.describe_options(self)
|
||||
|
||||
def do_surplus(self, candidate_surplus, count_card, surplus):
|
||||
"""Overrides UIGSTVCounter.do_surplus"""
|
||||
|
||||
"""Implements BaseSTVCounter.do_surplus"""
|
||||
last_bundle = count_card.parcels[len(count_card.parcels)-1]
|
||||
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences([last_bundle])
|
||||
|
||||
if self.options['papers'] == 'transferable':
|
||||
__pragma__('opov')
|
||||
transferable_ballots = total_ballots - exhausted_ballots
|
||||
transferable_votes = total_votes - exhausted_votes
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / transferable_ballots).pp(2) + '.')
|
||||
else:
|
||||
tv = self.round_tv(surplus / transferable_ballots)
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + tv.pp(2) + '.')
|
||||
else:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value received.')
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + (surplus / total_ballots).pp(2) + '.')
|
||||
else:
|
||||
tv = self.round_tv(surplus / total_ballots)
|
||||
self.logs.append('Surplus of ' + candidate_surplus.name + ' distributed at value ' + tv.pp(2) + '.')
|
||||
__pragma__('noopov')
|
||||
|
||||
for candidate, x in next_preferences.items():
|
||||
cand_ballots = x[0]
|
||||
num_ballots = x[1]
|
||||
num_votes = x[2]
|
||||
|
||||
new_parcel = []
|
||||
if len(cand_ballots) > 0:
|
||||
__pragma__('opov')
|
||||
self.candidates[candidate].parcels.append(new_parcel)
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.candidates[candidate].transfers += self.round_votes((num_ballots * surplus) / transferable_ballots)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_ballots * tv)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_votes)
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
self.candidates[candidate].transfers += self.round_votes((num_ballots * surplus) / total_ballots)
|
||||
else:
|
||||
self.candidates[candidate].transfers += self.round_votes(num_ballots * tv)
|
||||
__pragma__('noopov')
|
||||
|
||||
for ballot, ballot_value in cand_ballots:
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
if self.options['round_tvs'] is None:
|
||||
new_value = (ballot.value * surplus) / transferable_ballots
|
||||
else:
|
||||
tv = self.round_tv(surplus / transferable_ballots)
|
||||
new_value = ballot.value * tv
|
||||
else:
|
||||
new_value = ballot_value
|
||||
else:
|
||||
if self.options['round_tvs'] is None:
|
||||
new_value = (ballot.value * surplus) / total_ballots
|
||||
else:
|
||||
tv = self.round_tv(surplus / total_ballots)
|
||||
new_value = ballot.value * tv
|
||||
|
||||
new_parcel.append((ballot, self.round_weight(new_value)))
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
if self.options['papers'] == 'transferable':
|
||||
if transferable_votes > surplus:
|
||||
pass # No ballots exhaust
|
||||
else:
|
||||
self.exhausted.transfers += self.round_votes((surplus - transferable_votes))
|
||||
else:
|
||||
self.exhausted.transfers += self.round_votes((exhausted_ballots * surplus) / total_ballots)
|
||||
__pragma__('noopov')
|
||||
|
||||
__pragma__('opov')
|
||||
count_card.transfers -= surplus
|
||||
__pragma__('noopov')
|
||||
|
||||
count_card.state = CandidateState.ELECTED
|
||||
self._do_gregory_surplus(False, candidate_surplus, count_card, surplus, [last_bundle])
|
||||
|
Reference in New Issue
Block a user