Implement configurable --sample-per-ballot

This commit is contained in:
RunasSudo 2021-08-04 13:46:32 +10:00
parent 0efc1e6eab
commit 0800701960
No known key found for this signature in database
GPG Key ID: 7234E476BF21C61A
17 changed files with 94 additions and 43 deletions

View File

@ -15,7 +15,7 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV
| Australian Senate STV | Rules from the [*Commonwealth Electoral Act 1918*](https://www.legislation.gov.au/Details/C2020C00400/Html/Text#_Toc59107700), using the unweighted inclusive Gregory method. | [E2] [E3] [E4] | ✓ | | Australian Senate STV | Rules from the [*Commonwealth Electoral Act 1918*](https://www.legislation.gov.au/Details/C2020C00400/Html/Text#_Toc59107700), using the unweighted inclusive Gregory method. | [E2] [E3] [E4] | ✓ |
| Western Australia STV | Rules from the [*Electoral Act 1907* (WA)](https://www.legislation.wa.gov.au/legislation/prod/filestore.nsf/FileURL/mrdoc_29498.pdf/$FILE/Electoral%20Act%201907%20-%20[17-a0-06].pdf), using the weighted inclusive Gregory method. | [E2] [E3] | | | Western Australia STV | Rules from the [*Electoral Act 1907* (WA)](https://www.legislation.wa.gov.au/legislation/prod/filestore.nsf/FileURL/mrdoc_29498.pdf/$FILE/Electoral%20Act%201907%20-%20[17-a0-06].pdf), using the weighted inclusive Gregory method. | [E2] [E3] | |
| Australian Capital Territory STV | Rules from the [*Electoral Act 1992* (ACT)](https://www.legislation.act.gov.au/View/a/1992-71/current/PDF/1992-71.PDF), using the exclusive Gregory method. | | ✓ | | Australian Capital Territory STV | Rules from the [*Electoral Act 1992* (ACT)](https://www.legislation.act.gov.au/View/a/1992-71/current/PDF/1992-71.PDF), using the exclusive Gregory method. | | ✓ |
| Cambridge STV | Rules from the former [chapter 54A of the *Massachusetts General Laws*](https://www.cambridgema.gov/-/media/Files/electioncommission/massachusettsgenerallawschapter54a.pdf), as modified and in effect in Cambridge, Massachusetts. See also [here](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf). | | ✓ | | Cambridge STV | Rules in force in Cambridge, Massachusetts. These rules are derived from the [former chapter 54A of the Massachusetts General Laws](https://www.cambridgema.gov/-/media/Files/electioncommission/massachusettsgenerallawschapter54a.pdf), but have by regulation been modified to incorporate the procedures set out in Article IX of the former [1938 Charter of the City of Cincinnati](https://catalog.hathitrust.org/Record/001754258). See also [here](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf). | | ✓ |
| [Wright STV](https://www.aph.gov.au/Parliamentary_Business/Committees/House_of_Representatives_Committees?url=em/elect07/subs/sub051.1.pdf) | Rules proposed by Anthony van der Craats designed for computer counting, involving reset and re-iteration of the count after each candidate exclusion. | | ✓ | | [Wright STV](https://www.aph.gov.au/Parliamentary_Business/Committees/House_of_Representatives_Committees?url=em/elect07/subs/sub051.1.pdf) | Rules proposed by Anthony van der Craats designed for computer counting, involving reset and re-iteration of the count after each candidate exclusion. | | ✓ |
| [PRSA 1977](https://www.prsa.org.au/rule1977.htm) | Simple rules designed for hand counting, using the exclusive Gregory method, with counting performed in thousandths of a vote. | | ✓ | | [PRSA 1977](https://www.prsa.org.au/rule1977.htm) | Simple rules designed for hand counting, using the exclusive Gregory method, with counting performed in thousandths of a vote. | | ✓ |
| [ERS97](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/) | More complex rules designed for hand counting, using the exclusive Gregory method. | | ✓ | | [ERS97](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/) | More complex rules designed for hand counting, using the exclusive Gregory method. | | ✓ |
@ -88,21 +88,23 @@ Other Gregory methods are supported, but not recommended:
* *Unweighted inclusive Gregory*: During surplus transfers, all applicable ballot papers of the elected candidate are examined. Transfers are not weighted, and each ballot paper has equal value in the calculation. * *Unweighted inclusive Gregory*: During surplus transfers, all applicable ballot papers of the elected candidate are examined. Transfers are not weighted, and each ballot paper has equal value in the calculation.
* *Exclusive Gregory (last bundle)*: During surplus transfers, only the ballot papers received in the last transfer (all of one value) are examined. * *Exclusive Gregory (last bundle)*: During surplus transfers, only the ballot papers received in the last transfer (all of one value) are examined.
Random subset methods are also supported, but also not recommended: Random sample methods are also supported, but also not recommended:
* *Cincinnati (inclusive subset)*: During surplus transfers, a subset of the elected candidate's ballot papers, equal in size to the surplus, is examined. * *Cincinnati (inclusive sample)*: During surplus transfers, a subset of the elected candidate's ballot papers, equal in size to the surplus, is examined.
* *Hare (exclusive subset)*: During surplus transfers, a subset of the ballot papers received in the last transfer, equal in size to the surplus, is examined. * *Hare (exclusive sample)*: During surplus transfers, a subset of the ballot papers received in the last transfer, equal in size to the surplus, is examined.
The use of a random subset method requires *Normalise ballots* to be enabled, and requires the *Quota criterion* to be set to *>=*. When a random subset method is used, surplus transfers and exclusions are performed one ballot paper at a time, and candidates are declared elected immediately on reaching the quota. Consequential surpluses therefore do not arise, and surpluses only occur during the count of first preferences. The use of a random sample method requires *Normalise ballots* to be enabled, and requires the *Quota criterion* to be set to *>=*.
In both random subset methods, the subset is selected using the deterministic method used in [Cambridge, Massachusetts](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf) (derived from Article IX of the former 1938 Cincinnati *Code of Ordinances*). This depends on the order of ballot papers in the BLT file, and is independent of the *Random seed* option. In both random sample methods, the subset is selected using the deterministic method used in [Cambridge, Massachusetts](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf) (derived from Article IX of the former 1938 Cincinnati *Code of Ordinances*). This depends on the order of ballot papers in the BLT file, and is independent of the *Random seed* option.
### Papers to examine in surplus transfer (--transferable-only) ### Papers to examine in surplus transfer (--t ransferable-only)
* *Include non-transferable papers* (default): When this option is selected, all ballot papers of the transferring candidate are examined. Non-transferable papers are always exhausted at the relevant surplus fractions. * *Include non-transferable papers* (default): When this option is selected, all ballot papers of the transferring candidate are examined. Non-transferable papers are always exhausted at the relevant surplus fractions.
* *Use transferable papers only* (CLI: --transferable-only): When this option is selected, only transferable papers of the transferring candidate are examined. Non-transferable papers are exhausted only if the value of the transferable papers is less than the surplus. * *Use transferable papers only* (CLI: --transferable-only): When this option is selected, only transferable papers of the transferring candidate are examined. Non-transferable papers are exhausted only if the value of the transferable papers is less than the surplus.
### Exclusion method (--exclusion) ### (Gregory) Exclusion method (--exclusion)
When *Surplus method* is set to a Gregory method, this option controls how candidates are excluded:
* *Single stage* (default): When excluding candidate(s), transfer all their ballot papers in one stage. * *Single stage* (default): When excluding candidate(s), transfer all their ballot papers in one stage.
* *By value*: When excluding candidate(s), transfer their ballot papers in descending order of accumulated transfer value. Each transfer of all ballots of a certain transfer value forms a separate stage, i.e. if a transfer allows another candidate to meet the quota criterion, no further papers are transferred to that candidate. * *By value*: When excluding candidate(s), transfer their ballot papers in descending order of accumulated transfer value. Each transfer of all ballots of a certain transfer value forms a separate stage, i.e. if a transfer allows another candidate to meet the quota criterion, no further papers are transferred to that candidate.
@ -110,8 +112,6 @@ In both random subset methods, the subset is selected using the deterministic me
* *By parcel (by order)*: When excluding a candidate, transfer their ballot papers one parcel at a time, in the order each was received. Each parcel forms a separate stage. This option cannot be combined with bulk exclusion. * *By parcel (by order)*: When excluding a candidate, transfer their ballot papers one parcel at a time, in the order each was received. Each parcel forms a separate stage. This option cannot be combined with bulk exclusion.
* *Wright method (re-iterate)*: When excluding candidate(s), reset the count from the distribution of first preferences, disregarding the excluded candidates. * *Wright method (re-iterate)*: When excluding candidate(s), reset the count from the distribution of first preferences, disregarding the excluded candidates.
When *Surplus method* is set to *Meek method*, this setting is ignored, and the Meek method is instead applied.
### (Meek) NZ-style exclusion (--meek-nz-exclusion) ### (Meek) NZ-style exclusion (--meek-nz-exclusion)
When *Surplus method* is set to *Meek method*, this option controls how candidate keep values are updated when candidates are excluded: When *Surplus method* is set to *Meek method*, this option controls how candidate keep values are updated when candidates are excluded:
@ -119,6 +119,13 @@ When *Surplus method* is set to *Meek method*, this option controls how candidat
* When NZ-style exclusion is disabled (default), the excluded candidate's keep value is immediately reduced to 0. This is the method specified in the 1987 and 2006 Meek rules. * When NZ-style exclusion is disabled (default), the excluded candidate's keep value is immediately reduced to 0. This is the method specified in the 1987 and 2006 Meek rules.
* When NZ-style exclusion is enabled, all elected candidates' keep values are first updated by one further iteration; only then is the excluded candidate's keep value reduced to 0. This is the method specified in the New Zealand *Local Electoral Regulations 2001*. * When NZ-style exclusion is enabled, all elected candidates' keep values are first updated by one further iteration; only then is the excluded candidate's keep value reduced to 0. This is the method specified in the New Zealand *Local Electoral Regulations 2001*.
### (Sample) Transfer ballot-by-ballot (--sample-per-ballot)
When *Surplus method* is set to a random sample method, this option controls when candidates are declared elected:
* When ballot-by-ballot transfer is disabled (default), candidates are declared elected only at the end of a stage, as usual.
* When ballot-by-ballot transfer is enabled, candidates are declared elected immediately on meeting the quota after the transfer of any single ballot paper. Consequential surpluses therefore do not arise, and surpluses only occur during the count of first preferences.
### Ties (-t/--ties) ### Ties (-t/--ties)
This dropdown allows you to select how ties (in surplus transfer or exclusion) are broken. The options are: This dropdown allows you to select how ties (in surplus transfer or exclusion) are broken. The options are:

View File

@ -106,13 +106,13 @@
</label> </label>
<label> <label>
Method: Method:
<select id="selTransfers"> <select id="selMethod">
<option value="wig" selected>Weighted inclusive Gregory</option> <option value="wig" selected>Weighted inclusive Gregory</option>
<option value="uig">Unweighted inclusive Gregory</option> <option value="uig">Unweighted inclusive Gregory</option>
<option value="eg">Exclusive Gregory (last bundle)</option> <option value="eg">Exclusive Gregory (last bundle)</option>
<option value="meek">Meek method</option> <option value="meek">Meek method</option>
<option value="cincinnati">Cincinnati (inclusive subset)</option> <option value="cincinnati">Cincinnati (inclusive sample)</option>
<option value="hare">Hare (exclusive subset)</option> <option value="hare">Hare (exclusive sample)</option>
</select> </select>
</label> </label>
<label> <label>
@ -124,6 +124,7 @@
</div> </div>
<div> <div>
<label style="margin-right:1em;"> <label style="margin-right:1em;">
<span class="pill-grey" title="This option has effect only if “Method” is set to a Gregory method">Gregory</span>
Exclusion: Exclusion:
<select id="selExclusion"> <select id="selExclusion">
<option value="single_stage" selected>Single stage</option> <option value="single_stage" selected>Single stage</option>
@ -139,6 +140,13 @@
NZ-style exclusion NZ-style exclusion
</label> </label>
</div> </div>
<div>
<label>
<input type="checkbox" id="chkSamplePerBallot">
<span class="pill-grey" title="This option has effect only if “Method” is set to a sample-based method">Sample</span>
Per-ballot transfers
</label>
</div>
<div class="subheading"> <div class="subheading">
Tie-breaking: Tie-breaking:
</div> </div>

View File

@ -144,11 +144,12 @@ async function clickCount() {
document.getElementById('selQuotaMode').value, document.getElementById('selQuotaMode').value,
document.getElementById('selTies').value.split(','), document.getElementById('selTies').value.split(','),
document.getElementById('txtSeed').value, document.getElementById('txtSeed').value,
document.getElementById('selTransfers').value, document.getElementById('selMethod').value,
document.getElementById('selSurplus').value, document.getElementById('selSurplus').value,
document.getElementById('selPapers').value == 'transferable', document.getElementById('selPapers').value == 'transferable',
document.getElementById('selExclusion').value, document.getElementById('selExclusion').value,
document.getElementById('chkMeekNZExclusion').checked, document.getElementById('chkMeekNZExclusion').checked,
document.getElementById('chkSamplePerBallot').checked,
document.getElementById('chkBulkElection').checked, document.getElementById('chkBulkElection').checked,
document.getElementById('chkBulkExclusion').checked, document.getElementById('chkBulkExclusion').checked,
document.getElementById('chkDeferSurpluses').checked, document.getElementById('chkDeferSurpluses').checked,
@ -161,7 +162,9 @@ async function clickCount() {
// Reset UI // Reset UI
document.getElementById('printPane').style.display = 'none'; document.getElementById('printPane').style.display = 'none';
divLogs2.innerHTML = ''; // Might have error messages from previous execution document.getElementById('resultLogs1').innerHTML = '';
tblResult.innerHTML = '';
divLogs2.innerHTML = '';
// Dispatch to worker // Dispatch to worker
worker.postMessage({ worker.postMessage({
@ -376,7 +379,7 @@ function changePreset() {
document.getElementById('chkRoundValues').checked = false; document.getElementById('chkRoundValues').checked = false;
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_size'; document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'wig'; document.getElementById('selMethod').value = 'wig';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'single_stage'; document.getElementById('selExclusion').value = 'single_stage';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -400,7 +403,7 @@ function changePreset() {
document.getElementById('chkRoundValues').checked = false; document.getElementById('chkRoundValues').checked = false;
document.getElementById('selSumTransfers').value = 'per_ballot'; document.getElementById('selSumTransfers').value = 'per_ballot';
document.getElementById('selSurplus').value = 'by_size'; document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'wig'; document.getElementById('selMethod').value = 'wig';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'single_stage'; document.getElementById('selExclusion').value = 'single_stage';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -425,7 +428,7 @@ function changePreset() {
//document.getElementById('selSumTransfers').value = 'single_step'; //document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('txtMeekSurplusTolerance').value = '0.001%'; document.getElementById('txtMeekSurplusTolerance').value = '0.001%';
//document.getElementById('selSurplus').value = 'by_size'; //document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'meek'; document.getElementById('selMethod').value = 'meek';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'single_stage'; document.getElementById('selExclusion').value = 'single_stage';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -454,7 +457,7 @@ function changePreset() {
//document.getElementById('selSumTransfers').value = 'single_step'; //document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('txtMeekSurplusTolerance').value = '0.0001'; document.getElementById('txtMeekSurplusTolerance').value = '0.0001';
//document.getElementById('selSurplus').value = 'by_size'; //document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'meek'; document.getElementById('selMethod').value = 'meek';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'single_stage'; document.getElementById('selExclusion').value = 'single_stage';
document.getElementById('selTies').value = 'forwards,random'; document.getElementById('selTies').value = 'forwards,random';
@ -483,7 +486,7 @@ function changePreset() {
//document.getElementById('selSumTransfers').value = 'single_step'; //document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('txtMeekSurplusTolerance').value = '0.0001'; document.getElementById('txtMeekSurplusTolerance').value = '0.0001';
//document.getElementById('selSurplus').value = 'by_size'; //document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'meek'; document.getElementById('selMethod').value = 'meek';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'single_stage'; document.getElementById('selExclusion').value = 'single_stage';
document.getElementById('selTies').value = 'forwards,random'; document.getElementById('selTies').value = 'forwards,random';
@ -506,7 +509,7 @@ function changePreset() {
document.getElementById('chkRoundValues').checked = false; document.getElementById('chkRoundValues').checked = false;
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_order'; document.getElementById('selSurplus').value = 'by_order';
document.getElementById('selTransfers').value = 'uig'; document.getElementById('selMethod').value = 'uig';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'by_value'; document.getElementById('selExclusion').value = 'by_value';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -529,7 +532,7 @@ function changePreset() {
document.getElementById('chkRoundValues').checked = false; document.getElementById('chkRoundValues').checked = false;
document.getElementById('selSumTransfers').value = 'by_value'; document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_order'; document.getElementById('selSurplus').value = 'by_order';
document.getElementById('selTransfers').value = 'wig'; document.getElementById('selMethod').value = 'wig';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'by_source'; document.getElementById('selExclusion').value = 'by_source';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -552,7 +555,7 @@ function changePreset() {
document.getElementById('chkRoundValues').checked = false; document.getElementById('chkRoundValues').checked = false;
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_order'; document.getElementById('selSurplus').value = 'by_order';
document.getElementById('selTransfers').value = 'eg'; document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable'; document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'by_value'; document.getElementById('selExclusion').value = 'by_value';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -563,6 +566,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true; document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false; // TODO: Cambridge-style bulk exclusion document.getElementById('chkBulkExclusion').checked = false; // TODO: Cambridge-style bulk exclusion
document.getElementById('chkDeferSurpluses').checked = false; document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('chkSamplePerBallot').checked = true;
document.getElementById('txtMinThreshold').value = '49'; document.getElementById('txtMinThreshold').value = '49';
document.getElementById('selNumbers').value = 'rational'; document.getElementById('selNumbers').value = 'rational';
document.getElementById('txtPPDP').value = '0'; document.getElementById('txtPPDP').value = '0';
@ -570,7 +574,7 @@ function changePreset() {
document.getElementById('chkRoundQuota').checked = true; document.getElementById('chkRoundQuota').checked = true;
document.getElementById('txtRoundQuota').value = '0'; document.getElementById('txtRoundQuota').value = '0';
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selTransfers').value = 'cincinnati'; document.getElementById('selMethod').value = 'cincinnati';
document.getElementById('selPapers').value = 'transferable'; document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'single_stage'; document.getElementById('selExclusion').value = 'single_stage';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -593,7 +597,7 @@ function changePreset() {
document.getElementById('chkRoundValues').checked = false; document.getElementById('chkRoundValues').checked = false;
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_size'; document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'wig'; document.getElementById('selMethod').value = 'wig';
document.getElementById('selPapers').value = 'both'; document.getElementById('selPapers').value = 'both';
document.getElementById('selExclusion').value = 'wright'; document.getElementById('selExclusion').value = 'wright';
document.getElementById('selTies').value = 'random'; document.getElementById('selTies').value = 'random';
@ -619,7 +623,7 @@ function changePreset() {
document.getElementById('txtRoundValues').value = '3'; document.getElementById('txtRoundValues').value = '3';
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_order'; document.getElementById('selSurplus').value = 'by_order';
document.getElementById('selTransfers').value = 'eg'; document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable'; document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'parcels_by_order'; document.getElementById('selExclusion').value = 'parcels_by_order';
document.getElementById('selTies').value = 'backwards,random'; document.getElementById('selTies').value = 'backwards,random';
@ -645,7 +649,7 @@ function changePreset() {
document.getElementById('txtRoundValues').value = '2'; document.getElementById('txtRoundValues').value = '2';
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_size'; document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'eg'; document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable'; document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'by_value'; document.getElementById('selExclusion').value = 'by_value';
document.getElementById('selTies').value = 'forwards,random'; document.getElementById('selTies').value = 'forwards,random';
@ -671,7 +675,7 @@ function changePreset() {
document.getElementById('txtRoundValues').value = '2'; document.getElementById('txtRoundValues').value = '2';
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_size'; document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'eg'; document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable'; document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'by_value'; document.getElementById('selExclusion').value = 'by_value';
document.getElementById('selTies').value = 'forwards,random'; document.getElementById('selTies').value = 'forwards,random';
@ -697,7 +701,7 @@ function changePreset() {
document.getElementById('txtRoundValues').value = '2'; document.getElementById('txtRoundValues').value = '2';
document.getElementById('selSumTransfers').value = 'single_step'; document.getElementById('selSumTransfers').value = 'single_step';
document.getElementById('selSurplus').value = 'by_size'; document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'eg'; document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable'; document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'by_value'; document.getElementById('selExclusion').value = 'by_value';
document.getElementById('selTies').value = 'forwards,random'; document.getElementById('selTies').value = 'forwards,random';
@ -723,7 +727,7 @@ function changePreset() {
document.getElementById('txtRoundValues').value = '2'; document.getElementById('txtRoundValues').value = '2';
document.getElementById('selSumTransfers').value = 'per_ballot'; document.getElementById('selSumTransfers').value = 'per_ballot';
document.getElementById('selSurplus').value = 'by_size'; document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selTransfers').value = 'eg'; document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable'; document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'by_value'; document.getElementById('selExclusion').value = 'by_value';
document.getElementById('selTies').value = 'forwards,random'; document.getElementById('selTies').value = 'forwards,random';

View File

@ -138,6 +138,10 @@ struct STV {
#[clap(help_heading=Some("STV VARIANTS"), long)] #[clap(help_heading=Some("STV VARIANTS"), long)]
meek_nz_exclusion: bool, meek_nz_exclusion: bool,
/// (Cincinnati/Hare) Sample-based methods: Check for candidate election after each individual ballot paper transfer
#[clap(help_heading=Some("STV VARIANTS"), long)]
sample_per_ballot: bool,
// ------------------------- // -------------------------
// -- Count optimisations -- // -- Count optimisations --
@ -274,6 +278,7 @@ where
cmd_opts.transferable_only, cmd_opts.transferable_only,
&cmd_opts.exclusion, &cmd_opts.exclusion,
cmd_opts.meek_nz_exclusion, cmd_opts.meek_nz_exclusion,
cmd_opts.sample_per_ballot,
!cmd_opts.no_early_bulk_elect, !cmd_opts.no_early_bulk_elect,
cmd_opts.bulk_exclude, cmd_opts.bulk_exclude,
cmd_opts.defer_surpluses, cmd_opts.defer_surpluses,

View File

@ -16,7 +16,7 @@
*/ */
use super::{ExclusionMethod, NextPreferencesEntry, NextPreferencesResult, STVError, STVOptions, SumSurplusTransfersMode, SurplusMethod, SurplusOrder}; use super::{ExclusionMethod, NextPreferencesEntry, NextPreferencesResult, STVError, STVOptions, SumSurplusTransfersMode, SurplusMethod, SurplusOrder};
use super::subset; use super::sample;
use crate::constraints; use crate::constraints;
use crate::election::{Candidate, CandidateState, CountState, Parcel, Vote}; use crate::election::{Candidate, CandidateState, CountState, Parcel, Vote};
@ -106,7 +106,7 @@ where
match opts.surplus { match opts.surplus {
SurplusMethod::WIG | SurplusMethod::UIG | SurplusMethod::EG => { distribute_surplus(state, &opts, elected_candidate); } SurplusMethod::WIG | SurplusMethod::UIG | SurplusMethod::EG => { distribute_surplus(state, &opts, elected_candidate); }
SurplusMethod::Cincinnati | SurplusMethod::Hare => { subset::distribute_surplus(state, &opts, elected_candidate)?; } SurplusMethod::Cincinnati | SurplusMethod::Hare => { sample::distribute_surplus(state, &opts, elected_candidate)?; }
_ => unreachable!() _ => unreachable!()
} }

View File

@ -22,7 +22,7 @@ pub mod gregory;
/// Meek method of surplus distributions, etc. /// Meek method of surplus distributions, etc.
pub mod meek; pub mod meek;
/// Random subset methods of surplus distributions /// Random subset methods of surplus distributions
pub mod subset; pub mod sample;
/// WebAssembly wrappers /// WebAssembly wrappers
//#[cfg(target_arch = "wasm32")] //#[cfg(target_arch = "wasm32")]
@ -67,14 +67,16 @@ pub struct STVOptions {
pub ties: Vec<TieStrategy>, pub ties: Vec<TieStrategy>,
/// Method of surplus distributions /// Method of surplus distributions
pub surplus: SurplusMethod, pub surplus: SurplusMethod,
/// Order to distribute surpluses /// (Gregory STV) Order to distribute surpluses
pub surplus_order: SurplusOrder, pub surplus_order: SurplusOrder,
/// Examine only transferable papers during surplus distributions /// (Gregory STV) Examine only transferable papers during surplus distributions
pub transferable_only: bool, pub transferable_only: bool,
/// Method of exclusions /// (Gregory STV) Method of exclusions
pub exclusion: ExclusionMethod, pub exclusion: ExclusionMethod,
/// (Meek STV) NZ Meek STV behaviour: Iterate keep values one round before candidate exclusion /// (Meek STV) NZ Meek STV behaviour: Iterate keep values one round before candidate exclusion
pub meek_nz_exclusion: bool, pub meek_nz_exclusion: bool,
/// (Cincinnati/Hare) Sample-based methods: Check for candidate election after each individual ballot paper transfer
pub sample_per_ballot: bool,
/// Bulk elect as soon as continuing candidates fill all remaining vacancies /// Bulk elect as soon as continuing candidates fill all remaining vacancies
pub early_bulk_elect: bool, pub early_bulk_elect: bool,
/// Use bulk exclusion /// Use bulk exclusion
@ -117,6 +119,7 @@ impl STVOptions {
transferable_only: bool, transferable_only: bool,
exclusion: &str, exclusion: &str,
meek_nz_exclusion: bool, meek_nz_exclusion: bool,
sample_per_ballot: bool,
early_bulk_elect: bool, early_bulk_elect: bool,
bulk_exclude: bool, bulk_exclude: bool,
defer_surpluses: bool, defer_surpluses: bool,
@ -190,6 +193,7 @@ impl STVOptions {
_ => panic!("Invalid --exclusion"), _ => panic!("Invalid --exclusion"),
}, },
meek_nz_exclusion, meek_nz_exclusion,
sample_per_ballot,
early_bulk_elect, early_bulk_elect,
bulk_exclude, bulk_exclude,
defer_surpluses, defer_surpluses,
@ -230,10 +234,13 @@ impl STVOptions {
if ties_str != "prompt" { flags.push(format!("--ties {}", ties_str)); } if ties_str != "prompt" { flags.push(format!("--ties {}", ties_str)); }
for t in self.ties.iter() { if let TieStrategy::Random(seed) = t { flags.push(format!("--random-seed {}", seed)); } } for t in self.ties.iter() { if let TieStrategy::Random(seed) = t { flags.push(format!("--random-seed {}", seed)); } }
if self.surplus != SurplusMethod::WIG { flags.push(self.surplus.describe()); } if self.surplus != SurplusMethod::WIG { flags.push(self.surplus.describe()); }
if self.surplus != SurplusMethod::Meek && self.surplus_order != SurplusOrder::BySize { flags.push(self.surplus_order.describe()); } if self.surplus == SurplusMethod::WIG || self.surplus == SurplusMethod::UIG || self.surplus == SurplusMethod::EG {
if self.surplus != SurplusMethod::Meek && self.transferable_only { flags.push("--transferable-only".to_string()); } if self.surplus_order != SurplusOrder::BySize { flags.push(self.surplus_order.describe()); }
if self.surplus != SurplusMethod::Meek && self.exclusion != ExclusionMethod::SingleStage { flags.push(self.exclusion.describe()); } if self.transferable_only { flags.push("--transferable-only".to_string()); }
if self.exclusion != ExclusionMethod::SingleStage { flags.push(self.exclusion.describe()); }
}
if self.surplus == SurplusMethod::Meek && self.meek_nz_exclusion { flags.push("--meek-nz-exclusion".to_string()); } if self.surplus == SurplusMethod::Meek && self.meek_nz_exclusion { flags.push("--meek-nz-exclusion".to_string()); }
if (self.surplus == SurplusMethod::Cincinnati || self.surplus == SurplusMethod::Hare) && self.sample_per_ballot { flags.push("--sample-per-ballot".to_string()); }
if !self.early_bulk_elect { flags.push("--no-early-bulk-elect".to_string()); } if !self.early_bulk_elect { flags.push("--no-early-bulk-elect".to_string()); }
if self.bulk_exclude { flags.push("--bulk-exclude".to_string()); } if self.bulk_exclude { flags.push("--bulk-exclude".to_string()); }
if self.defer_surpluses { flags.push("--defer-surpluses".to_string()); } if self.defer_surpluses { flags.push("--defer-surpluses".to_string()); }
@ -256,7 +263,6 @@ impl STVOptions {
if self.exclusion != ExclusionMethod::SingleStage { return Err(STVError::InvalidOptions("--surplus meek requires --exclusion single_stage")); } if self.exclusion != ExclusionMethod::SingleStage { return Err(STVError::InvalidOptions("--surplus meek requires --exclusion single_stage")); }
} }
if self.surplus == SurplusMethod::Cincinnati || self.surplus == SurplusMethod::Hare { if self.surplus == SurplusMethod::Cincinnati || self.surplus == SurplusMethod::Hare {
if self.exclusion != ExclusionMethod::SingleStage { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --exclusion single_stage")); }
if self.quota_criterion != QuotaCriterion::GreaterOrEqual { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --quota-criterion geq")); } if self.quota_criterion != QuotaCriterion::GreaterOrEqual { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --quota-criterion geq")); }
if !self.normalise_ballots { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --normalise-ballots")); } if !self.normalise_ballots { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --normalise-ballots")); }
} }
@ -1327,7 +1333,7 @@ where
meek::exclude_candidates(state, opts, excluded_candidates); meek::exclude_candidates(state, opts, excluded_candidates);
} }
SurplusMethod::Cincinnati | SurplusMethod::Hare => { SurplusMethod::Cincinnati | SurplusMethod::Hare => {
subset::exclude_candidates(state, opts, excluded_candidates)?; sample::exclude_candidates(state, opts, excluded_candidates)?;
} }
} }
} }

View File

@ -116,7 +116,9 @@ where
} }
} }
super::elect_hopefuls(state, opts)?; if opts.sample_per_ballot {
super::elect_hopefuls(state, opts)?;
}
} else { } else {
// Exhausted // Exhausted
if opts.transferable_only { if opts.transferable_only {
@ -253,7 +255,9 @@ where
} }
} }
super::elect_hopefuls(state, opts)?; if opts.sample_per_ballot {
super::elect_hopefuls(state, opts)?;
}
} else { } else {
// Exhausted // Exhausted
state.exhausted.transfer(&vote.value); state.exhausted.transfer(&vote.value);

View File

@ -228,6 +228,7 @@ impl STVOptions {
transferable_only: bool, transferable_only: bool,
exclusion: &str, exclusion: &str,
meek_nz_exclusion: bool, meek_nz_exclusion: bool,
sample_per_ballot: bool,
early_bulk_elect: bool, early_bulk_elect: bool,
bulk_exclude: bool, bulk_exclude: bool,
defer_surpluses: bool, defer_surpluses: bool,
@ -255,6 +256,7 @@ impl STVOptions {
transferable_only, transferable_only,
exclusion, exclusion,
meek_nz_exclusion, meek_nz_exclusion,
sample_per_ballot,
early_bulk_elect, early_bulk_elect,
bulk_exclude, bulk_exclude,
defer_surpluses, defer_surpluses,

View File

@ -39,6 +39,7 @@ fn act_kurrajong20_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::ByValue, exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,

View File

@ -76,6 +76,7 @@ fn aec_tas19_rational() {
transferable_only: false, transferable_only: false,
exclusion: stv::ExclusionMethod::ByValue, exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: true, early_bulk_elect: true,
bulk_exclude: true, bulk_exclude: true,
defer_surpluses: false, defer_surpluses: false,

View File

@ -39,6 +39,7 @@ fn cambridge_cc03_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::SingleStage, exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: true,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,

View File

@ -55,6 +55,7 @@ fn prsa1_constr1_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder, exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,
@ -119,6 +120,7 @@ fn prsa1_constr2_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder, exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,
@ -183,6 +185,7 @@ fn prsa1_constr3_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder, exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,
@ -259,6 +262,7 @@ fn ers97_cantbulkexclude_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::ByValue, exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: true, bulk_exclude: true,
defer_surpluses: true, defer_surpluses: true,

View File

@ -39,6 +39,7 @@ fn csm15_float64() {
transferable_only: false, transferable_only: false,
exclusion: stv::ExclusionMethod::Wright, exclusion: stv::ExclusionMethod::Wright,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, // Required for validation early_bulk_elect: false, // Required for validation
bulk_exclude: true, bulk_exclude: true,
defer_surpluses: false, defer_surpluses: false,

View File

@ -39,6 +39,7 @@ fn ers97_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::ByValue, exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: true, bulk_exclude: true,
defer_surpluses: true, defer_surpluses: true,

View File

@ -41,6 +41,7 @@ fn meek87_ers97_float64() {
transferable_only: false, transferable_only: false,
exclusion: stv::ExclusionMethod::SingleStage, exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: true, early_bulk_elect: true,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,
@ -75,6 +76,7 @@ fn meek06_ers97_fixed12() {
transferable_only: false, transferable_only: false,
exclusion: stv::ExclusionMethod::SingleStage, exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: true, early_bulk_elect: true,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: true, defer_surpluses: true,
@ -150,6 +152,7 @@ fn meeknz_ers97_fixed12() {
transferable_only: false, transferable_only: false,
exclusion: stv::ExclusionMethod::SingleStage, exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: true, meek_nz_exclusion: true,
sample_per_ballot: false,
early_bulk_elect: true, early_bulk_elect: true,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: true, defer_surpluses: true,

View File

@ -39,6 +39,7 @@ fn prsa1_rational() {
transferable_only: true, transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder, exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, // Required for validation early_bulk_elect: false, // Required for validation
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,

View File

@ -43,6 +43,7 @@ fn scotland_linn07_fixed5() {
transferable_only: false, transferable_only: false,
exclusion: stv::ExclusionMethod::SingleStage, exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,
@ -77,6 +78,7 @@ fn scotland_linn07_gfixed5() {
transferable_only: false, transferable_only: false,
exclusion: stv::ExclusionMethod::SingleStage, exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false, meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, early_bulk_elect: false,
bulk_exclude: false, bulk_exclude: false,
defer_surpluses: false, defer_surpluses: false,