Implement backwards tie breaking
This commit is contained in:
parent
06ab133615
commit
503fed26d1
@ -105,7 +105,7 @@
|
||||
<label>
|
||||
Ties:
|
||||
<select id="selTies">
|
||||
<option value="backwards_random" selected>Backwards then random (NYI)</option>
|
||||
<option value="backwards_random" selected>Backwards then random</option>
|
||||
<option value="random">Random</option>
|
||||
</select>
|
||||
</label>
|
||||
|
@ -50,7 +50,7 @@ onmessage = function(evt) {
|
||||
}
|
||||
|
||||
if (evt.data.options['ties'] === 'backwards_random') {
|
||||
counter.options['ties'] = [py.pyRCV2.ties.TiesBackwards(), py.pyRCV2.ties.TiesRandom(evt.data.seed)];
|
||||
counter.options['ties'] = [py.pyRCV2.ties.TiesBackwards(counter), py.pyRCV2.ties.TiesRandom(evt.data.seed)];
|
||||
} else if (evt.data.options['ties'] === 'random') {
|
||||
counter.options['ties'] = [py.pyRCV2.ties.TiesRandom(evt.data.seed)];
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ def main(args):
|
||||
counter.options['ties'] = []
|
||||
for t in args.ties:
|
||||
if t == 'backwards':
|
||||
counter.options['ties'].append(TiesBackwards())
|
||||
counter.options['ties'].append(TiesBackwards(counter))
|
||||
elif t == 'prompt':
|
||||
counter.options['ties'].append(TiesPrompt())
|
||||
elif t == 'random':
|
||||
|
@ -62,7 +62,8 @@ class BaseSTVCounter:
|
||||
def reset(self):
|
||||
"""
|
||||
Public function:
|
||||
Reset the count and perform the first step
|
||||
Perform the first step (distribute first preferences)
|
||||
Does not reset the states of candidates, etc.
|
||||
"""
|
||||
|
||||
# Distribute first preferences
|
||||
@ -83,7 +84,7 @@ class BaseSTVCounter:
|
||||
self.elect_meeting_quota()
|
||||
|
||||
__pragma__('opov')
|
||||
return CountStepResult(
|
||||
result = CountStepResult(
|
||||
'First preferences',
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
@ -93,6 +94,9 @@ class BaseSTVCounter:
|
||||
)
|
||||
__pragma__('noopov')
|
||||
|
||||
self.step_results = [result]
|
||||
return result
|
||||
|
||||
def step(self):
|
||||
"""
|
||||
Public function:
|
||||
@ -151,7 +155,7 @@ class BaseSTVCounter:
|
||||
self.num_elected += 1
|
||||
|
||||
__pragma__('opov')
|
||||
return CountStepResult(
|
||||
result = CountStepResult(
|
||||
'Bulk election',
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
@ -161,6 +165,9 @@ class BaseSTVCounter:
|
||||
)
|
||||
__pragma__('noopov')
|
||||
|
||||
self.step_results.append(result)
|
||||
return result
|
||||
|
||||
def distribute_surpluses(self):
|
||||
"""
|
||||
Distribute surpluses, if any
|
||||
@ -200,7 +207,7 @@ class BaseSTVCounter:
|
||||
self.elect_meeting_quota()
|
||||
|
||||
__pragma__('opov')
|
||||
return CountStepResult(
|
||||
result = CountStepResult(
|
||||
'Surplus of ' + candidate_surplus.name,
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
@ -210,6 +217,9 @@ class BaseSTVCounter:
|
||||
)
|
||||
__pragma__('noopov')
|
||||
|
||||
self.step_results.append(result)
|
||||
return result
|
||||
|
||||
def do_surplus(self, candidate_surplus, count_card, surplus):
|
||||
"""
|
||||
Transfer the surplus of the given candidate
|
||||
@ -237,7 +247,7 @@ class BaseSTVCounter:
|
||||
self.elect_meeting_quota()
|
||||
|
||||
__pragma__('opov')
|
||||
return CountStepResult(
|
||||
result = CountStepResult(
|
||||
'Exclusion of ' + candidate_excluded.name,
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
@ -247,6 +257,9 @@ class BaseSTVCounter:
|
||||
)
|
||||
__pragma__('noopov')
|
||||
|
||||
self.step_results.append(result)
|
||||
return result
|
||||
|
||||
def candidate_to_exclude(self):
|
||||
"""
|
||||
Determine the candidate to exclude
|
||||
|
@ -40,6 +40,9 @@ class Ballot:
|
||||
self.value = value
|
||||
self.preferences = preferences
|
||||
|
||||
def clone(self):
|
||||
return Ballot(self.value, self.preferences)
|
||||
|
||||
class Election:
|
||||
"""
|
||||
Represents a BLT election
|
||||
@ -74,6 +77,16 @@ class CountCard:
|
||||
self.orig_votes = self.votes
|
||||
self.transfers = Num('0')
|
||||
|
||||
def clone(self):
|
||||
"""Return a clone of this count card (including cloning ballots) as a record of this stage"""
|
||||
result = CountCard()
|
||||
result.orig_votes = self.orig_votes
|
||||
result.transfers = self.transfers
|
||||
result.ballots = [b.clone() for b in self.ballots]
|
||||
result.state = self.state
|
||||
result.order_elected = self.order_elected
|
||||
return result
|
||||
|
||||
class CountCompleted:
|
||||
pass
|
||||
|
||||
@ -87,3 +100,14 @@ class CountStepResult:
|
||||
|
||||
self.total = total
|
||||
self.quota = quota
|
||||
|
||||
def clone(self):
|
||||
"""Return a clone of this result as a record of this stage"""
|
||||
|
||||
candidates = SafeDict()
|
||||
for c, cc in self.candidates.items():
|
||||
__pragma__('opov')
|
||||
candidates[c] = cc.clone()
|
||||
__pragma__('noopov')
|
||||
|
||||
return CountStepResult(self.comment, candidates, self.exhausted.clone(), self.loss_fraction.clone(), self.total, self.quota)
|
||||
|
@ -65,14 +65,40 @@ class TiesPrompt:
|
||||
def choose_highest(self, l):
|
||||
return self.choose_lowest(l)
|
||||
|
||||
# FIXME: This is untested!
|
||||
class TiesBackwards:
|
||||
"""
|
||||
Break ties based on the candidate who had the highest/lowest total at the end
|
||||
of the most recent stage where one candidate had a higher/lower total than
|
||||
all other tied candidates, if such a stage exists
|
||||
"""
|
||||
|
||||
def __init__(self, counter):
|
||||
self.counter = counter
|
||||
|
||||
def choose_lowest(self, l):
|
||||
raise Exception('Not yet implemented')
|
||||
for result in reversed(self.counter.step_results):
|
||||
__pragma__('opov')
|
||||
l2 = [(x, result.candidates[x[0]].votes) for x in l]
|
||||
l2.sort(key=lambda x: x[1])
|
||||
if l2[0][1] < l2[1][1]: # Did one candidate have fewer votes than the others?
|
||||
return l2[0][0]
|
||||
__pragma__('noopov')
|
||||
return None
|
||||
|
||||
def choose_highest(self, l):
|
||||
raise Exception('Not yet implemented')
|
||||
for result in reversed(self.counter.step_results):
|
||||
__pragma__('opov')
|
||||
l2 = [(x, result.candidates[x[0]].votes) for x in l]
|
||||
l2.sort(key=lambda x: x[1], reverse=True)
|
||||
if l2[0][1] > l2[1][1]: # Did one candidate have more votes than the others?
|
||||
return l2[0][0]
|
||||
__pragma__('noopov')
|
||||
return None
|
||||
|
||||
class TiesRandom:
|
||||
"""Break ties randomly, using the SHARandom deterministic RNG"""
|
||||
|
||||
def __init__(self, seed):
|
||||
self.random = SHARandom(seed)
|
||||
|
||||
|
Reference in New Issue
Block a user