Various tidyups

Use "Droop" as default quota (since same as "Droop (exact)" when quota not rounded)
Rename ers97.blt
Update documentation
This commit is contained in:
RunasSudo 2021-08-09 17:58:05 +10:00
parent 46e895ee5a
commit 764ebd98e6
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
23 changed files with 34 additions and 45 deletions

View File

@ -43,7 +43,7 @@ This functionality is not available on the command line.
The quota dropdowns allow you to define the quota used in the election, and the quota criterion used to elect candidates. The quota may be set to:
* *Droop* and *Droop (exact)*: *V*/(*S*+1)
* *Droop* (default) and *Droop (exact)*: *V*/(*S*+1)
* *Hare* and *Hare (exact)*: *V*/*S*
where *V* is the number of votes and *S* is the number of seats.
@ -54,15 +54,15 @@ When *Round quota to [n] d.p.* is not enabled, *Droop* (or *Droop (exact)*) is a
### Quota criterion (-c/--quota-criterion)
The quota criterion may be set to *>=* (candidates are elected if they meet or exceed the quota) or *>* (candidates are elected only if they strictly exceed the quota).
The quota criterion may be set to *>=* (candidates are elected if they meet or exceed the quota) or *>* (default; candidates are elected only if they strictly exceed the quota).
Note that the combination ‘*>= Droop (exact)*’ (with *Round quota to [n] d.p.* enabled) can result in more candidates meeting the quota than there are available vacancies, hence this particular combination is not recommended.
Note that the combination ‘*>= Droop (exact)*’ (or, when *Round quota to [n] d.p.* is disabled, ‘*>= Droop*’) can result in more candidates meeting the quota than there are available vacancies, hence this particular combination is not recommended.
### Quota mode (--quota-mode)
This option allows you to specify whether the votes required for election can change during the count. The options are:
* *Static quota*: The quota is calculated once after all first-preference votes are allocated, and remains constant throughout the count.
* *Static quota* (default): The quota is calculated once after all first-preference votes are allocated, and remains constant throughout the count.
* *Static with ERS97 rules*: The quota is static, but candidates may be elected if their vote exceeds (or equals, according to the *Quota criterion*) the active vote, divided by (*S* + 1).
* *Dynamic by total vote*: The quota is recalculated at the end of each stage, according to the *Quota* option.
* *Dynamic by active vote*: The quota is recalculated at the end of each stage, according to the *Quota* option, but where *V* is the active vote and *S* is the number of remaining vacancies.
@ -101,8 +101,8 @@ The use of a random sample method requires *Normalise ballots* to be enabled, an
### Papers to examine in surplus transfer (--transferable-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.
* *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.
* *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. This is the method typically used with the weighted inclusive Gregory or Meek methods.
* *Use transferable papers 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. This is the method typically used with other surplus distribution methods.
### (Gregory) Exclusion method (--exclusion)
@ -125,7 +125,7 @@ When *Surplus method* is set to *Meek method*, this option controls how candidat
When *Surplus method* is set to a random sample method, this option controls which subset of ballot papers is selected for transfer during surplus distributions:
* *Stratified (then by order)*: The candidate's ballot papers are first stratified into subparcels according to next available preference. From each subparcel, the subset transferred comprises the ballot papers most recently received by the candidate.
* *Stratified (then by order)* (default): The candidate's ballot papers are first stratified into subparcels according to next available preference. From each subparcel, the subset transferred comprises the ballot papers most recently received by the candidate.
* *By order*: The subset transferred comprises the ballot papers most recently received by the candidate.
* *Every n-th ballot*: 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*).
@ -173,7 +173,7 @@ This dropdown allows you to select how numbers (vote totals, etc.) are represent
* *Fixed*: Numbers are represented as fixed-precision decimals, up to a certain number of decimal places (default: 5).
* *Fixed (guarded)*: Numbers are represented as fixed-precision decimals with ‘guard digits’ – also known as [‘quasi-exact’ arithmetic](http://www.votingmatters.org.uk/ISSUE24/I24P2.pdf). If *n* decimal places are requested, numbers are represented up to 2*n* decimal places, and two values are considered equal if the absolute difference is less than (10<sup>−*n*</sup>)/2.
* *Rational*: Numbers are represented exactly as fractions, resulting in the elimination of rounding error, but increasing computational complexity when the number of surplus transfers is very large.
* *Rational* (default): Numbers are represented exactly as fractions, resulting in the elimination of rounding error, but increasing computational complexity when the number of surplus transfers is very large.
* *Float (64-bit)*: Numbers are represented as native 64-bit floating-point numbers. This is fast, but not recommended as unexpectedly large rounding errors may be introduced in some circumstances.
### Display up to [n] d.p. (--pp-decimals)

View File

@ -365,7 +365,7 @@ async function printResult() {
function changePreset() {
if (document.getElementById('selPreset').value === 'wigm') {
document.getElementById('selQuotaCriterion').value = 'gt';
document.getElementById('selQuota').value = 'droop_exact';
document.getElementById('selQuota').value = 'droop';
document.getElementById('selQuotaMode').value = 'static';
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false;

View File

@ -97,7 +97,7 @@ struct STV {
// -- Quota --
/// Quota type
#[clap(help_heading=Some("QUOTA"), short, long, possible_values=&["droop", "hare", "droop_exact", "hare_exact"], default_value="droop_exact")]
#[clap(help_heading=Some("QUOTA"), short, long, possible_values=&["droop", "hare", "droop_exact", "hare_exact"], default_value="droop")]
quota: String,
/// Whether to elect candidates on meeting (geq) or strictly exceeding (gt) the quota

View File

@ -75,7 +75,7 @@ pub struct STVOptions {
pub normalise_ballots: bool,
/// Quota type
#[builder(default="QuotaType::DroopExact")]
#[builder(default="QuotaType::Droop")]
pub quota: QuotaType,
/// Whether to elect candidates on meeting (geq) or strictly exceeding (gt) the quota
@ -173,7 +173,7 @@ impl STVOptions {
if self.surplus != SurplusMethod::Meek && self.sum_surplus_transfers != SumSurplusTransfersMode::SingleStep { flags.push(self.sum_surplus_transfers.describe()); }
if self.surplus == SurplusMethod::Meek && self.meek_surplus_tolerance != "0.001%" { flags.push(format!("--meek-surplus-tolerance {}", self.meek_surplus_tolerance)); }
if self.normalise_ballots { flags.push("--normalise-ballots".to_string()); }
if self.quota != QuotaType::DroopExact { flags.push(self.quota.describe()); }
if self.quota != QuotaType::Droop { flags.push(self.quota.describe()); }
if self.quota_criterion != QuotaCriterion::Greater { flags.push(self.quota_criterion.describe()); }
if self.surplus != SurplusMethod::Meek && self.quota_mode != QuotaMode::Static { flags.push(self.quota_mode.describe()); }
let ties_str = self.ties.iter().map(|t| t.describe()).join(" ");

View File

@ -25,7 +25,6 @@ fn act_kurrajong20_rational() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_votes(Some(6))
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)

View File

@ -62,7 +62,6 @@ fn aec_tas19_rational() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_votes(Some(0))
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::UIG)
.surplus_order(stv::SurplusOrder::ByOrder)

View File

@ -25,7 +25,6 @@ fn cambridge_cc03_rational() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_quota(Some(0))
.normalise_ballots(true)
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::Cincinnati)
.transferable_only(true)

View File

@ -19,9 +19,9 @@ use assert_cmd::Command;
use predicates::prelude::*;
#[test]
fn cli_ers97() {
fn cli_ers97old() {
Command::cargo_bin("opentally").expect("Cargo Error")
.args(&["stv", "tests/data/ers97.blt", "--numbers", "fixed", "--decimals", "5", "--round-tvs", "2", "--round-weights", "2", "--round-votes", "2", "--round-quota", "2", "--quota", "droop_exact", "--quota-mode", "ers97", "--surplus", "eg", "--transferable-only", "--exclusion", "by_value"])
.args(&["stv", "tests/data/ers97old.blt", "--numbers", "fixed", "--decimals", "5", "--round-tvs", "2", "--round-weights", "2", "--round-votes", "2", "--round-quota", "2", "--quota", "droop_exact", "--quota-mode", "ers97", "--surplus", "eg", "--transferable-only", "--exclusion", "by_value"])
.assert().success();
}

View File

@ -43,7 +43,6 @@ fn prsa1_constr1_rational() {
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
@ -93,7 +92,6 @@ fn prsa1_constr2_rational() {
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
@ -143,7 +141,6 @@ fn prsa1_constr3_rational() {
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
@ -177,11 +174,11 @@ fn prsa1_constr3_rational() {
/// Same election data as ers97_rational, but with a constraint that prevents the bulk exclusion of Glazier and Wright
#[test]
fn ers97_cantbulkexclude_rational() {
fn ers97old_cantbulkexclude_rational() {
// Read CSV file
let reader = csv::ReaderBuilder::new()
.has_headers(false)
.from_path("tests/data/ers97_cantbulkexclude.csv")
.from_path("tests/data/ers97old_cantbulkexclude.csv")
.expect("IO Error");
let records: Vec<csv::StringRecord> = reader.into_records().map(|r| r.expect("Syntax Error")).collect();
@ -192,10 +189,10 @@ fn ers97_cantbulkexclude_rational() {
let stages: Vec<usize> = records.first().unwrap().iter().skip(1).step_by(2).map(|s| s.parse().unwrap()).collect();
// Read BLT
let mut election: Election<Rational> = Election::from_file("tests/data/ers97.blt").expect("Syntax Error");
let mut election: Election<Rational> = Election::from_file("tests/data/ers97old.blt").expect("Syntax Error");
// Read CON
let file = File::open("tests/data/ers97_cantbulkexclude.con").expect("IO Error");
let file = File::open("tests/data/ers97old_cantbulkexclude.con").expect("IO Error");
let file_reader = io::BufReader::new(file);
let lines = file_reader.lines();
election.constraints = Some(Constraints::from_con(lines.map(|r| r.expect("IO Error").to_string()).into_iter()));
@ -205,6 +202,7 @@ fn ers97_cantbulkexclude_rational() {
.round_values(Some(2))
.round_votes(Some(2))
.round_quota(Some(2))
.quota(stv::QuotaType::DroopExact)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.quota_mode(stv::QuotaMode::ERS97)
.surplus(stv::SurplusMethod::EG)
@ -213,7 +211,7 @@ fn ers97_cantbulkexclude_rational() {
.early_bulk_elect(false)
.bulk_exclude(true)
.defer_surpluses(true)
.constraints_path(Some("tests/data/ers97_cantbulkexclude".to_string()))
.constraints_path(Some("tests/data/ers97old_cantbulkexclude".to_string()))
.build().unwrap();
utils::validate_election::<Rational>(stages, records, election, stv_opts, None, &["nt", "vre"]);

View File

@ -24,7 +24,6 @@ use opentally::stv;
fn csm15_float64() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.exclusion(stv::ExclusionMethod::Wright)
.early_bulk_elect(false)

View File

@ -21,12 +21,13 @@ use opentally::numbers::Rational;
use opentally::stv;
#[test]
fn ers97_rational() {
fn ers97old_rational() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(2))
.round_values(Some(2))
.round_votes(Some(2))
.round_quota(Some(2))
.quota(stv::QuotaType::DroopExact)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.quota_mode(stv::QuotaMode::ERS97)
.surplus(stv::SurplusMethod::EG)
@ -37,5 +38,5 @@ fn ers97_rational() {
.defer_surpluses(true)
.build().unwrap();
utils::read_validate_election::<Rational>("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, None, &["nt", "vre"]);
utils::read_validate_election::<Rational>("tests/data/ers97old.csv", "tests/data/ers97old.blt", stv_opts, None, &["nt", "vre"]);
}

View File

@ -21,30 +21,30 @@ use opentally::election::{CandidateState, CountState, Election};
use opentally::numbers::{Fixed, NativeFloat64, Number};
use opentally::stv;
// Compare ers97.blt count with result produced by 1987 Hill–Wichmann–Woodall reference implementation
// Compare ers97old.blt count with result produced by 1987 Hill–Wichmann–Woodall reference implementation
#[test]
fn meek87_ers97_float64() {
fn meek87_ers97old_float64() {
let stv_opts = stv::STVOptionsBuilder::default()
.meek_surplus_tolerance("0.001%".to_string())
.quota(stv::QuotaType::DroopExact)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.quota_mode(stv::QuotaMode::DynamicByTotal)
.surplus(stv::SurplusMethod::Meek)
.immediate_elect(false)
.build().unwrap();
utils::read_validate_election::<NativeFloat64>("tests/data/ers97_meek.csv", "tests/data/ers97.blt", stv_opts, Some(2), &["exhausted", "quota"]);
utils::read_validate_election::<NativeFloat64>("tests/data/ers97old_meek.csv", "tests/data/ers97old.blt", stv_opts, Some(2), &["exhausted", "quota"]);
}
// Compare ers97.blt count with result produced by OpenSTV 1.7 "Meek STV"
// Compare ers97old.blt count with result produced by OpenSTV 1.7 "Meek STV"
#[test]
fn meek06_ers97_fixed12() {
fn meek06_ers97old_fixed12() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(9))
.round_values(Some(9))
.round_votes(Some(9))
.round_quota(Some(9))
.meek_surplus_tolerance("0.0001".to_string())
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.quota_mode(stv::QuotaMode::DynamicByTotal)
.surplus(stv::SurplusMethod::Meek)
@ -54,7 +54,7 @@ fn meek06_ers97_fixed12() {
Fixed::set_dps(12);
// Read BLT
let election: Election<Fixed> = Election::from_file("tests/data/ers97.blt").expect("Syntax Error");
let election: Election<Fixed> = Election::from_file("tests/data/ers97old.blt").expect("Syntax Error");
// Initialise count state
let mut state = CountState::new(&election);
@ -95,16 +95,15 @@ fn meek06_ers97_fixed12() {
}
}
// Compare ers97.blt count with result produced by OpenSTV 1.7 "New Zealand Meek STV" (same result as Hill 2006 implementation)
// Compare ers97old.blt count with result produced by OpenSTV 1.7 "New Zealand Meek STV" (same result as Hill 2006 implementation)
#[test]
fn meeknz_ers97_fixed12() {
fn meeknz_ers97old_fixed12() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(9))
.round_values(Some(9))
.round_votes(Some(9))
.round_quota(Some(9))
.meek_surplus_tolerance("0.0001".to_string())
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.quota_mode(stv::QuotaMode::DynamicByTotal)
.surplus(stv::SurplusMethod::Meek)
@ -115,7 +114,7 @@ fn meeknz_ers97_fixed12() {
Fixed::set_dps(12);
// Read BLT
let election: Election<Fixed> = Election::from_file("tests/data/ers97.blt").expect("Syntax Error");
let election: Election<Fixed> = Election::from_file("tests/data/ers97old.blt").expect("Syntax Error");
// Initialise count state
let mut state = CountState::new(&election);

View File

@ -25,7 +25,6 @@ fn minneapolis_boe09_rational() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(4))
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.early_bulk_elect(true)
.bulk_exclude(true)
@ -41,7 +40,6 @@ fn minneapolis_pal13_rational() {
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(4))
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.early_bulk_elect(true)
.bulk_exclude(true)

View File

@ -27,7 +27,6 @@ fn prsa1_rational() {
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)

View File

@ -33,7 +33,6 @@ fn scotland_linn07_fixed5() {
.round_quota(Some(0))
.sum_surplus_transfers(stv::SumSurplusTransfersMode::PerBallot)
.normalise_ballots(true)
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.early_bulk_elect(false)
.pp_decimals(5)
@ -52,7 +51,6 @@ fn scotland_linn07_gfixed5() {
.round_quota(Some(0))
.sum_surplus_transfers(stv::SumSurplusTransfersMode::PerBallot)
.normalise_ballots(true)
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.early_bulk_elect(false)
.pp_decimals(5)