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