Implement exclusion of parcels by value + Senate STV

This commit is contained in:
RunasSudo 2020-12-30 02:33:09 +11:00
parent 803941a53a
commit 61506f0082
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
2 changed files with 33 additions and 11 deletions

View File

@ -40,7 +40,7 @@
<select id="selPreset" onchange="changePreset()"> <select id="selPreset" onchange="changePreset()">
<option value="scottish" selected>Scottish STV</option> <option value="scottish" selected>Scottish STV</option>
<option value="stvc">pyRCV STV-C</option> <option value="stvc">pyRCV STV-C</option>
<!--<option value="senate">Australian Senate STV</option>--> <option value="senate">Australian Senate STV</option>
<option value="wright">Wright STV</option> <option value="wright">Wright STV</option>
<option value="prsa77">PRSA 1977</option> <option value="prsa77">PRSA 1977</option>
</select> </select>
@ -119,7 +119,7 @@
<select id="selExclusion"> <select id="selExclusion">
<option value="one_round" selected>Exclude in one round</option> <option value="one_round" selected>Exclude in one round</option>
<option value="parcels_by_order">Exclude by parcel (by order)</option> <option value="parcels_by_order">Exclude by parcel (by order)</option>
<!--<option value="parcels_by_value">Exclude by parcel (by value)</option>--> <option value="parcels_by_value">Exclude by parcel (by value)</option>
</select> </select>
</label> </label>
<br> <br>

View File

@ -20,6 +20,8 @@ from pyRCV2.model import CandidateState, CountCard, CountCompleted, CountStepRes
from pyRCV2.numbers import Num, Rational from pyRCV2.numbers import Num, Rational
from pyRCV2.safedict import SafeDict from pyRCV2.safedict import SafeDict
import itertools
class STVException(Exception): class STVException(Exception):
pass pass
@ -535,11 +537,28 @@ class WIGSTVCounter(BaseSTVCounter):
def do_exclusion(self, candidate_excluded, count_card): def do_exclusion(self, candidate_excluded, count_card):
if self.options['exclusion'] == 'parcels_by_order': if self.options['exclusion'] == 'parcels_by_order':
parcel = count_card.parcels[0] if len(count_card.parcels) > 0:
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences([parcel]) parcel = count_card.parcels[0]
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences([parcel])
else:
# TODO: Skip this entirely if this is the case
parcel = []
count_card.parcels.remove(parcel) count_card.parcels.remove(parcel)
elif self.options['exclusion'] == 'parcels_by_value': elif self.options['exclusion'] == 'parcels_by_value':
raise Exception('Not implemented') # Sort the parcels by value
ballots = [(b, bv) for p in count_card.parcels for b, bv in p]
__pragma__('opov')
ballots.sort(key=lambda x: x[1] / x[0].value.to_rational(), reverse=True)
count_card.parcels = [list(g) for k, g in itertools.groupby(ballots, lambda x: x[1] / x[0].value.to_rational())]
__pragma__('noopov')
if len(count_card.parcels) > 0:
parcel = count_card.parcels[0]
count_card.parcels.remove(parcel)
else:
parcel = []
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences([parcel])
else: # one_round else: # one_round
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences(count_card.parcels) next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences(count_card.parcels)
count_card.parcels = [] count_card.parcels = []
@ -573,6 +592,9 @@ class WIGSTVCounter(BaseSTVCounter):
__pragma__('noopov') __pragma__('noopov')
if len(count_card.parcels) == 0: if len(count_card.parcels) == 0:
__pragma__('opov')
count_card.transfers -= count_card.votes
__pragma__('noopov')
count_card.state = CandidateState.EXCLUDED count_card.state = CandidateState.EXCLUDED
class UIGSTVCounter(WIGSTVCounter): class UIGSTVCounter(WIGSTVCounter):
@ -590,14 +612,14 @@ class UIGSTVCounter(WIGSTVCounter):
if self.options['papers'] == 'transferable': if self.options['papers'] == 'transferable':
__pragma__('opov') __pragma__('opov')
transferable_ballots = total_ballots - exhausted_ballots transferable_ballots = total_ballots - exhausted_ballots # Num
transferable_votes = total_votes.to_num() - exhausted_votes.to_num() transferable_votes = total_votes - exhausted_votes # Rational
__pragma__('noopov') __pragma__('noopov')
for candidate, x in next_preferences.items(): for candidate, x in next_preferences.items():
cand_ballots = x[0] cand_ballots = x[0]
num_ballots = x[1] num_ballots = x[1] # Num
num_votes = x[2] num_votes = x[2] # Rational
new_parcel = [] new_parcel = []
if len(cand_ballots) > 0: if len(cand_ballots) > 0:
@ -619,11 +641,11 @@ class UIGSTVCounter(WIGSTVCounter):
__pragma__('opov') __pragma__('opov')
if self.options['papers'] == 'transferable': if self.options['papers'] == 'transferable':
if transferable_votes > surplus: if transferable_votes > surplus:
new_value = (ballot.value * surplus) / transferable_ballots new_value = (ballot.value * surplus).to_rational() / transferable_ballots.to_rational()
else: else:
new_value = ballot_value new_value = ballot_value
else: else:
new_value = (ballot.value * surplus) / total_ballots new_value = (ballot.value * surplus).to_rational() / total_ballots.to_rational()
new_parcel.append((ballot, new_value)) new_parcel.append((ballot, new_value))
__pragma__('noopov') __pragma__('noopov')