diff --git a/docs/options.md b/docs/options.md
index 72b7659..2a43e7a 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -66,10 +66,9 @@ Some STV counting rules provide, for example, that ‘no surplus shall be transf
## Method (-m/--method)
-This dropdown allows you to select how ballots are transferred during surplus transfers or exclusions. The 2 recommended methods are:
+This dropdown allows you to select how ballots are transferred during surplus transfers or exclusions. The recommended method is:
* Weighted inclusive Gregory: During surplus transfers, all applicable ballot papers of the transferring candidate are examined. Transfers are weighted according to the weights of the ballot papers.
-* Wright STV: Same as weighted inclusive Gregory, but when a candidate is excluded, the count is reset from the beginning (minus the excluded candidate).
Other methods are supported, but not recommended:
@@ -88,6 +87,7 @@ Other surplus transfer methods, such as non-fractional transfers (e.g. random sa
* Exclude in one round (default): When excluding a candidate, transfer all their ballot papers in one round.
* Exclude by parcel (by order): When excluding a candidate, transfer their ballot papers one parcel at a time, in their order each was received. Each parcel forms a separate round, i.e. if a transfer allows another candidate to meet the quota criterion, no further papers are transferred to that candidate.
* Exclude by value: When excluding a candidate, transfer their ballot papers in descending order of accumulated transfer value. Each transfer of all ballots of a certain transfer value forms a separate round.
+* Wright method (re-iterate): When a candidate is excluded, the count is reset from the beginning (minus the excluded candidate).
## Ties (-t/--ties)
diff --git a/html/index.html b/html/index.html
index dd59484..d43f7c1 100644
--- a/html/index.html
+++ b/html/index.html
@@ -147,6 +147,7 @@
+
diff --git a/html/index.js b/html/index.js
index b03dbcf..7f404ad 100644
--- a/html/index.js
+++ b/html/index.js
@@ -83,9 +83,9 @@ function changePreset() {
document.getElementById('chkRoundVotes').checked = false;
document.getElementById('chkRoundWeights').checked = false;
document.getElementById('selSurplus').value = 'size';
- document.getElementById('selTransfers').value = 'wright';
+ document.getElementById('selTransfers').value = 'wig';
document.getElementById('selPapers').value = 'both';
- document.getElementById('selExclusion').value = 'one_round';
+ document.getElementById('selExclusion').value = 'wright';
document.getElementById('selTies').value = 'backwards_random';
} else if (document.getElementById('selPreset').value === 'prsa77') {
document.getElementById('selQuotaCriterion').value = 'geq';
diff --git a/html/worker.js b/html/worker.js
index 8fdcd7e..afd1b15 100644
--- a/html/worker.js
+++ b/html/worker.js
@@ -46,8 +46,6 @@ onmessage = function(evt) {
counter = py.pyRCV2.method.base_stv.UIGSTVCounter(election, evt.data.data.options);
} else if (evt.data.data.transfers === 'eg') {
counter = py.pyRCV2.method.base_stv.EGSTVCounter(election, evt.data.data.options);
- } else if (evt.data.data.transfers === 'wright') {
- counter = py.pyRCV2.method.wright.WrightSTVCounter(election, evt.data.data.options);
} else {
counter = py.pyRCV2.method.base_stv.WIGSTVCounter(election, evt.data.data.options);
}
diff --git a/pyRCV2/cli/stv.py b/pyRCV2/cli/stv.py
index f746653..ccc669f 100644
--- a/pyRCV2/cli/stv.py
+++ b/pyRCV2/cli/stv.py
@@ -1,5 +1,5 @@
# pyRCV2: Preferential vote counting
-# Copyright © 2020 Lee Yingtong Li (RunasSudo)
+# Copyright © 2020–2021 Lee Yingtong Li (RunasSudo)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -19,7 +19,6 @@ import pyRCV2.model
import pyRCV2.numbers
from pyRCV2.method.base_stv import UIGSTVCounter, WIGSTVCounter, EGSTVCounter
-from pyRCV2.method.wright import WrightSTVCounter
from pyRCV2.ties import TiesBackwards, TiesPrompt, TiesRandom
import sys
@@ -37,9 +36,9 @@ def add_parser(subparsers):
parser.add_argument('--round-votes', type=int, help='round votes to specified decimal places')
parser.add_argument('--round-weights', type=int, help='round ballot weights to specified decimal places')
parser.add_argument('--surplus-order', '-s', choices=['size', 'order'], default='size', help='whether to distribute surpluses by size or by order of election (default: size)')
- parser.add_argument('--method', '-m', choices=['wig', 'uig', 'eg', 'wright'], default='wig', help='method of surpluses and exclusions (default: wig - weighted inclusive Gregory)')
+ parser.add_argument('--method', '-m', choices=['wig', 'uig', 'eg'], default='wig', help='method of transferring surpluses (default: wig - weighted inclusive Gregory)')
parser.add_argument('--transferable-only', action='store_true', help='examine only transferable papers during surplus distributions')
- parser.add_argument('--exclusion', choices=['one_round', 'parcels_by_order', 'by_value'], default='one_round', help='whether to perform exclusions in one round or parcel by parcel (default: one_round)')
+ parser.add_argument('--exclusion', choices=['one_round', 'parcels_by_order', 'by_value', 'wright'], default='one_round', help='how to perform exclusions (default: one_round)')
parser.add_argument('--ties', '-t', action='append', choices=['backwards', 'prompt', 'random'], default=None, help='how to resolve ties (default: backwards then random)')
parser.add_argument('--random-seed', default=None, help='arbitrary string used to seed the RNG for random tie breaking')
@@ -85,8 +84,6 @@ def main(args):
counter = UIGSTVCounter(election, vars(args))
elif args.method == 'eg':
counter = EGSTVCounter(election, vars(args))
- elif args.method == 'wright':
- counter = WrightSTVCounter(election, vars(args))
else:
counter = WIGSTVCounter(election, vars(args))
diff --git a/pyRCV2/method/base_stv.py b/pyRCV2/method/base_stv.py
index 97a8eb9..2a6d57c 100644
--- a/pyRCV2/method/base_stv.py
+++ b/pyRCV2/method/base_stv.py
@@ -1,5 +1,5 @@
# pyRCV2: Preferential vote counting
-# Copyright © 2020 Lee Yingtong Li (RunasSudo)
+# Copyright © 2020–2021 Lee Yingtong Li (RunasSudo)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -67,7 +67,7 @@ class BaseSTVCounter:
'quota_criterion': 'geq', # 'geq' or 'gt'
'surplus_order': 'size', # 'size' or 'order'
'papers': 'both', # 'both' or 'transferable'
- 'exclusion': 'one_round', # 'one_round', 'parcels_by_order' or 'by_value'
+ 'exclusion': 'one_round', # 'one_round', 'parcels_by_order', 'by_value' or 'wright'
'ties': [], # List of tie strategies (e.g. TiesRandom)
'round_votes': None, # Number of decimal places or None
'round_weights': None, # Number of decimal places or None
@@ -315,6 +315,37 @@ class BaseSTVCounter:
candidate_excluded, count_card = self.candidate_to_exclude()
count_card.state = CandidateState.EXCLUDING
+ # Handle Wright STV
+ if self.options['exclusion'] == 'wright':
+ count_card.state = CandidateState.EXCLUDED
+
+ # Reset the count
+ # Carry over certain candidate states
+ new_candidates = SafeDict()
+ for candidate, count_card in self.candidates.items():
+ new_count_card = CountCard()
+
+ if count_card.state == CandidateState.WITHDRAWN:
+ new_count_card.state = CandidateState.WITHDRAWN
+ elif count_card.state == CandidateState.EXCLUDED:
+ new_count_card.state = CandidateState.EXCLUDED
+
+ __pragma__('opov')
+ new_candidates[candidate] = new_count_card
+ __pragma__('noopov')
+
+ self.candidates = new_candidates
+ self.exhausted = CountCard()
+ self.loss_fraction = CountCard()
+ self.num_elected = 0
+
+ step_results = self.step_results # Carry over step results
+ result = self.reset()
+ self.step_results = step_results
+ result.comment = 'Exclusion of ' + candidate_excluded.name
+
+ return result
+
# Exclude this candidate
self.do_exclusion(candidate_excluded, count_card)
diff --git a/pyRCV2/method/wright.py b/pyRCV2/method/wright.py
deleted file mode 100644
index bb0ccda..0000000
--- a/pyRCV2/method/wright.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# pyRCV2: Preferential vote counting
-# Copyright © 2020 Lee Yingtong Li (RunasSudo)
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-
-__pragma__ = lambda x: None
-
-from pyRCV2.method.base_stv import WIGSTVCounter
-from pyRCV2.model import CandidateState, CountCard
-from pyRCV2.safedict import SafeDict
-
-class WrightSTVCounter(WIGSTVCounter):
- """
- Wright STV implementation
- """
-
- def exclude_candidate(self):
- """
- Overrides BaseSTVCounter.exclude_candidate per Wright STV
- """
-
- candidate_excluded, count_card = self.candidate_to_exclude()
- count_card.state = CandidateState.EXCLUDED
-
- # Reset the count
- # Carry over certain candidate states
- new_candidates = SafeDict()
- for candidate, count_card in self.candidates.items():
- new_count_card = CountCard()
-
- if count_card.state == CandidateState.WITHDRAWN:
- new_count_card.state = CandidateState.WITHDRAWN
- elif count_card.state == CandidateState.EXCLUDED:
- new_count_card.state = CandidateState.EXCLUDED
-
- __pragma__('opov')
- new_candidates[candidate] = new_count_card
- __pragma__('noopov')
-
- self.candidates = new_candidates
- self.exhausted = CountCard()
- self.loss_fraction = CountCard()
- self.num_elected = 0
-
- step_results = self.step_results # Carry over step results
- result = self.reset()
- self.step_results = step_results
- result.comment = 'Exclusion of ' + candidate_excluded.name
-
- return result
diff --git a/pyRCV2/transcrypt.py b/pyRCV2/transcrypt.py
index 752be3a..af92cab 100644
--- a/pyRCV2/transcrypt.py
+++ b/pyRCV2/transcrypt.py
@@ -16,7 +16,7 @@
import pyRCV2.blt
import pyRCV2.model
-import pyRCV2.method, pyRCV2.method.base_stv, pyRCV2.method.wright
+import pyRCV2.method, pyRCV2.method.base_stv
import pyRCV2.numbers
import pyRCV2.random
import pyRCV2.ties
diff --git a/tests/test_combinations.py b/tests/test_combinations.py
index 7091001..04d533c 100644
--- a/tests/test_combinations.py
+++ b/tests/test_combinations.py
@@ -1,5 +1,5 @@
# pyRCV2: Preferential vote counting
-# Copyright © 2020 Lee Yingtong Li (RunasSudo)
+# Copyright © 2020–2021 Lee Yingtong Li (RunasSudo)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -17,7 +17,6 @@
import pyRCV2.blt
import pyRCV2.numbers
import pyRCV2.method.base_stv
-import pyRCV2.method.wright
from pyRCV2.model import CountCompleted
import json
@@ -63,4 +62,4 @@ def maketst(numbers, counter_cls, options):
test_prsa1_scottish_py, test_prsa1_scottish_js = maketst('Fixed', 'pyRCV2.method.base_stv.WIGSTVCounter', {})
test_prsa1_stvc_py, test_prsa1_stvc_js = maketst('Rational', 'pyRCV2.method.base_stv.WIGSTVCounter', {'quota': 'droop_exact', 'quota_criterion': 'gt', 'prog_quota': True})
test_prsa1_senate_py, test_prsa1_senate_js = maketst('Fixed', 'pyRCV2.method.base_stv.UIGSTVCounter', {'surplus_order': 'order', 'exclusion': 'by_value'})
-test_prsa1_wright_py, test_prsa1_wright_js = maketst('Fixed', 'pyRCV2.method.wright.WrightSTVCounter', {})
+test_prsa1_wright_py, test_prsa1_wright_js = maketst('Fixed', 'pyRCV2.method.base_stv.WIGSTVCounter', {'exclusion': 'wright'})
diff --git a/tests/test_csm.py b/tests/test_csm.py
index 5b49381..547c2b9 100644
--- a/tests/test_csm.py
+++ b/tests/test_csm.py
@@ -1,5 +1,5 @@
# pyRCV2: Preferential vote counting
-# Copyright © 2020 Lee Yingtong Li (RunasSudo)
+# Copyright © 2020–2021 Lee Yingtong Li (RunasSudo)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -19,7 +19,7 @@ from pytest_steps import test_steps
import pyRCV2.blt
import pyRCV2.numbers
-from pyRCV2.method.wright import WrightSTVCounter
+from pyRCV2.method.base_stv import WIGSTVCounter
from pyRCV2.model import CandidateState, CountCompleted
def count_until_exclude(counter):
@@ -43,7 +43,7 @@ def test_csm15():
cands = {c.name: c for c in election.candidates}
- counter = WrightSTVCounter(election)
+ counter = WIGSTVCounter(election, {'exclusion': 'wright'})
# Round 1
result = counter.reset()