Implement --surplus-assume-total
This replaces and extends the former --subtract-nontransferable option #4
This commit is contained in:
parent
c140ef0a90
commit
4cf9053681
@ -325,6 +325,8 @@
|
|||||||
|
|
||||||
\subsection\label{surplus-wig-sf}\paragraph If \textit{--papers} is \textit{both}, divide the elected candidate's surplus by their progress total.
|
\subsection\label{surplus-wig-sf}\paragraph If \textit{--papers} is \textit{both}, divide the elected candidate's surplus by their progress total.
|
||||||
|
|
||||||
|
% FIXME: Update this for new --transferable-only, --surplus-assume-total flags
|
||||||
|
|
||||||
\subsectionc\paragraph If \textit{--papers} is \textit{transferable}, for each parcel held by the elected candidate, multiply the number of transferable ballots in the parcel by the parcel's value, and sum the products. Divide the elected candidate's surplus by the sum.
|
\subsectionc\paragraph If \textit{--papers} is \textit{transferable}, for each parcel held by the elected candidate, multiply the number of transferable ballots in the parcel by the parcel's value, and sum the products. Divide the elected candidate's surplus by the sum.
|
||||||
|
|
||||||
\subsectionc\paragraph If \textit{--papers} is \textit{subtract_nontransferable}, for each parcel held by the elected candidate, multiply the number of non-transferable ballots in the parcel by the parcel's value, and sum the products. Subtract the sum from the elected candidate's progress total. Divide the elected candidate's surplus by the difference.
|
\subsectionc\paragraph If \textit{--papers} is \textit{subtract_nontransferable}, for each parcel held by the elected candidate, multiply the number of non-transferable ballots in the parcel by the parcel's value, and sum the products. Subtract the sum from the elected candidate's progress total. Divide the elected candidate's surplus by the difference.
|
||||||
|
@ -109,11 +109,12 @@ Random sample methods are also supported, but also not recommended:
|
|||||||
|
|
||||||
A random sample method will usually be used with a *Quota criterion* set to *>=*.
|
A random sample method will usually be used with a *Quota criterion* set to *>=*.
|
||||||
|
|
||||||
### Ballots to examine in surplus transfer (--transferable-only/--subtract-nontransferable)
|
### Ballots to examine in surplus transfer (--transferable-only/--surplus-assume-total)
|
||||||
|
|
||||||
* *Include non-transferable ballots* (default): When this option is selected, all ballots of the transferring candidate are examined. Non-transferable ballots are always exhausted at the relevant surplus fractions. This is the method typically used with the weighted inclusive Gregory or Meek methods.
|
* *Include non-transferable ballots* (default): When this option is selected, all ballots of the transferring candidate are examined. The denominator of the surplus fraction is the total value of the ballots. Non-transferable ballots are always exhausted at the relevant surplus fractions. This is the method typically used with the weighted inclusive Gregory or Meek methods.
|
||||||
* *Use transferable ballots only* (--transferable-only): When this option is selected, only transferable ballots of the transferring candidate are examined. Non-transferable ballots are exhausted only if the value of the transferable ballots is less than the surplus. This is the method typically used with other surplus distribution methods.
|
* *Assume progress total* (--surplus-assume-total): Same as *Include non-transferable ballots*, but the denominator of the surplus fraction is the candidate's recorded progress total. This has effect only as far as concerns rounding, and only in the weighted inclusive Gregory method.
|
||||||
* *Subtract non-transferables* (--transferable-only --subtract-nontransferable): Same as *Use transferable ballots only*, but the value of the transferable ballots is calculated by subtracting the value of non-transferable ballots from the progress total. This has effect only as far as concerns rounding.
|
* *Use transferable ballots only* (--transferable-only): When this option is selected, only transferable ballots of the transferring candidate are examined. The denominator of the surplus fraction is the total value of the transferable ballots. Non-transferable ballots are exhausted only if the value of the transferable ballots is less than the surplus. This is the method typically used with other surplus distribution methods.
|
||||||
|
* *Subtract non-transferables* (--transferable-only --surplus-assume-total): Same as *Use transferable ballots only*, but the value of the transferable ballots is calculated by subtracting the value of non-transferable ballots from the progress total. This has effect only as far as concerns rounding, and only in the weighted inclusive Gregory method.
|
||||||
|
|
||||||
### (Gregory) Exclusion method (--exclusion)
|
### (Gregory) Exclusion method (--exclusion)
|
||||||
|
|
||||||
|
@ -123,6 +123,7 @@
|
|||||||
<label>
|
<label>
|
||||||
<select id="selPapers">
|
<select id="selPapers">
|
||||||
<option value="both" selected>Include non-transferable ballots</option>
|
<option value="both" selected>Include non-transferable ballots</option>
|
||||||
|
<option value="assume_progress_total">Assume progress total</option>
|
||||||
<option value="transferable">Use transferable ballots only</option>
|
<option value="transferable">Use transferable ballots only</option>
|
||||||
<option value="subtract_nontransferable">Subtract non-transferables</option>
|
<option value="subtract_nontransferable">Subtract non-transferables</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -207,7 +207,7 @@ function changePreset() {
|
|||||||
document.getElementById('selSumTransfers').value = 'by_parcel';
|
document.getElementById('selSumTransfers').value = 'by_parcel';
|
||||||
document.getElementById('selSurplus').value = 'by_order';
|
document.getElementById('selSurplus').value = 'by_order';
|
||||||
document.getElementById('selMethod').value = 'wig';
|
document.getElementById('selMethod').value = 'wig';
|
||||||
document.getElementById('selPapers').value = 'both';
|
document.getElementById('selPapers').value = 'assume_progress_total';
|
||||||
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';
|
||||||
} else if (document.getElementById('selPreset').value === 'act') {
|
} else if (document.getElementById('selPreset').value === 'act') {
|
||||||
|
@ -121,9 +121,9 @@ pub struct SubcmdOptions {
|
|||||||
#[clap(help_heading=Some("STV VARIANTS"), long)]
|
#[clap(help_heading=Some("STV VARIANTS"), long)]
|
||||||
transferable_only: bool,
|
transferable_only: bool,
|
||||||
|
|
||||||
/// (Gregory STV) If --transferable-only, calculate value of transferable papers by subtracting value of non-transferable papers
|
/// (Gregory STV) When calculating surplus fractions, assume the progress total is the total value of all the candidate's papers
|
||||||
#[clap(help_heading=Some("STV VARIANTS"), long)]
|
#[clap(help_heading=Some("STV VARIANTS"), long)]
|
||||||
subtract_nontransferable: bool,
|
surplus_assume_total: bool,
|
||||||
|
|
||||||
/// (Gregory STV) Method of exclusions [default: single_stage] [possible values: single_stage, by_value, by_source, parcels_by_order, reset_and_reiterate]
|
/// (Gregory STV) Method of exclusions [default: single_stage] [possible values: single_stage, by_value, by_source, parcels_by_order, reset_and_reiterate]
|
||||||
#[clap(help_heading=Some("STV VARIANTS"), long, possible_values=&["single_stage", "by_value", "first_prefs_then_by_value", "by_source", "parcels_by_order", "wright", "reset_and_reiterate"], default_value="single_stage", value_name="method", hide_possible_values=true, hide_default_value=true)]
|
#[clap(help_heading=Some("STV VARIANTS"), long, possible_values=&["single_stage", "by_value", "first_prefs_then_by_value", "by_source", "parcels_by_order", "wright", "reset_and_reiterate"], default_value="single_stage", value_name="method", hide_possible_values=true, hide_default_value=true)]
|
||||||
@ -302,7 +302,7 @@ where
|
|||||||
cmd_opts.surplus.into(),
|
cmd_opts.surplus.into(),
|
||||||
cmd_opts.surplus_order.into(),
|
cmd_opts.surplus_order.into(),
|
||||||
cmd_opts.transferable_only,
|
cmd_opts.transferable_only,
|
||||||
cmd_opts.subtract_nontransferable,
|
cmd_opts.surplus_assume_total,
|
||||||
cmd_opts.exclusion.into(),
|
cmd_opts.exclusion.into(),
|
||||||
cmd_opts.meek_nz_exclusion,
|
cmd_opts.meek_nz_exclusion,
|
||||||
cmd_opts.sample.into(),
|
cmd_opts.sample.into(),
|
||||||
|
@ -266,15 +266,20 @@ where
|
|||||||
// Calculate and print surplus fraction
|
// Calculate and print surplus fraction
|
||||||
|
|
||||||
let total_ballots = &transferable_ballots + &exhausted_ballots;
|
let total_ballots = &transferable_ballots + &exhausted_ballots;
|
||||||
let total_votes = &transferable_votes + &exhausted_votes;
|
let mut total_votes = &transferable_votes + &exhausted_votes;
|
||||||
|
|
||||||
let count_card = state.candidates.get_mut(elected_candidate).unwrap();
|
let count_card = state.candidates.get_mut(elected_candidate).unwrap();
|
||||||
count_card.ballot_transfers = -&total_ballots;
|
count_card.ballot_transfers = -&total_ballots;
|
||||||
|
|
||||||
if opts.transferable_only && opts.subtract_nontransferable {
|
if opts.surplus_assume_total {
|
||||||
|
// Override total_votes
|
||||||
|
total_votes = count_card.votes.clone();
|
||||||
|
|
||||||
|
if opts.transferable_only {
|
||||||
// Override transferable_votes
|
// Override transferable_votes
|
||||||
transferable_votes = count_card.votes.clone() - exhausted_votes;
|
transferable_votes = count_card.votes.clone() - exhausted_votes;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut surplus_denom = calculate_surplus_denom(&surplus, &transferable_ballots, &transferable_votes, &total_ballots, &total_votes, opts);
|
let mut surplus_denom = calculate_surplus_denom(&surplus, &transferable_ballots, &transferable_votes, &total_ballots, &total_votes, opts);
|
||||||
let surplus_numer;
|
let surplus_numer;
|
||||||
|
@ -81,9 +81,9 @@ pub struct STVOptions {
|
|||||||
#[builder(default="false")]
|
#[builder(default="false")]
|
||||||
pub transferable_only: bool,
|
pub transferable_only: bool,
|
||||||
|
|
||||||
/// (Gregory STV) If --transferable-only, calculate value of transferable papers by subtracting value of non-transferable papers
|
/// (Gregory STV) When calculating surplus fractions, assume the progress total is the total value of all the candidate's papers
|
||||||
#[builder(default="false")]
|
#[builder(default="false")]
|
||||||
pub subtract_nontransferable: bool,
|
pub surplus_assume_total: bool,
|
||||||
|
|
||||||
/// (Gregory STV) Method of exclusions
|
/// (Gregory STV) Method of exclusions
|
||||||
#[builder(default="ExclusionMethod::SingleStage")]
|
#[builder(default="ExclusionMethod::SingleStage")]
|
||||||
@ -169,7 +169,7 @@ impl STVOptions {
|
|||||||
if self.surplus != SurplusMethod::Meek {
|
if self.surplus != SurplusMethod::Meek {
|
||||||
if self.surplus_order != SurplusOrder::BySize { flags.push(self.surplus_order.describe()); }
|
if self.surplus_order != SurplusOrder::BySize { flags.push(self.surplus_order.describe()); }
|
||||||
if self.transferable_only { flags.push("--transferable-only".to_string()); }
|
if self.transferable_only { flags.push("--transferable-only".to_string()); }
|
||||||
if self.subtract_nontransferable { flags.push("--subtract-nontransferable".to_string()); }
|
if self.surplus_assume_total { flags.push("--surplus-assume-total".to_string()); }
|
||||||
if self.exclusion != ExclusionMethod::SingleStage { flags.push(self.exclusion.describe()); }
|
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()); }
|
||||||
@ -238,14 +238,10 @@ impl STVOptions {
|
|||||||
return Err(STVError::InvalidOptions("--constraint-mode repeat_count requires a Gregory method for --surplus"));
|
return Err(STVError::InvalidOptions("--constraint-mode repeat_count requires a Gregory method for --surplus"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.subtract_nontransferable {
|
if self.surplus_assume_total {
|
||||||
if self.surplus != SurplusMethod::WIG {
|
if self.surplus != SurplusMethod::WIG {
|
||||||
// Invalid because other methods do not distinguish between ballots of different value during surplus transfer
|
// Invalid because other methods do not distinguish between ballots of different value during surplus transfer
|
||||||
return Err(STVError::InvalidOptions("--subtract-nontransferable requires --surplus wig"));
|
return Err(STVError::InvalidOptions("--surplus-assume-total requires --surplus wig"));
|
||||||
}
|
|
||||||
if !self.transferable_only {
|
|
||||||
// Invalid because nontransferables are only subtracted with --transferable-only
|
|
||||||
return Err(STVError::InvalidOptions("--subtract-nontransferable requires --transferable-only"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !self.immediate_elect {
|
if !self.immediate_elect {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* OpenTally: Open-source election vote counting
|
/* OpenTally: Open-source election vote counting
|
||||||
* Copyright © 2021–2022 Lee Yingtong Li (RunasSudo)
|
* Copyright © 2021–2023 Lee Yingtong Li (RunasSudo)
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -280,7 +280,7 @@ impl STVOptions {
|
|||||||
surplus.into(),
|
surplus.into(),
|
||||||
surplus_order.into(),
|
surplus_order.into(),
|
||||||
if papers == "transferable" || papers == "subtract_nontransferable" { true } else { false },
|
if papers == "transferable" || papers == "subtract_nontransferable" { true } else { false },
|
||||||
if papers == "subtract_nontransferable" { true } else { false },
|
if papers == "assume_progress_total" || papers == "subtract_nontransferable" { true } else { false },
|
||||||
exclusion.into(),
|
exclusion.into(),
|
||||||
meek_nz_exclusion,
|
meek_nz_exclusion,
|
||||||
sample.into(),
|
sample.into(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* OpenTally: Open-source election vote counting
|
/* OpenTally: Open-source election vote counting
|
||||||
* Copyright © 2021–2022 Lee Yingtong Li (RunasSudo)
|
* Copyright © 2021–2023 Lee Yingtong Li (RunasSudo)
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -33,10 +33,10 @@ fn nswlg_albury21_rational() {
|
|||||||
.ties(vec![TieStrategy::Backwards, TieStrategy::Random(String::from("20220322"))])
|
.ties(vec![TieStrategy::Backwards, TieStrategy::Random(String::from("20220322"))])
|
||||||
.surplus_order(stv::SurplusOrder::ByOrder)
|
.surplus_order(stv::SurplusOrder::ByOrder)
|
||||||
.transferable_only(true)
|
.transferable_only(true)
|
||||||
.subtract_nontransferable(true)
|
.surplus_assume_total(true)
|
||||||
.build().unwrap();
|
.build().unwrap();
|
||||||
|
|
||||||
assert_eq!(stv_opts.describe::<Rational>(), "--round-votes 0 --round-quota 0 --round-subtransfers by_parcel --quota-criterion geq --ties backwards random --random-seed 20220322 --surplus-order by_order --transferable-only --subtract-nontransferable");
|
assert_eq!(stv_opts.describe::<Rational>(), "--round-votes 0 --round-quota 0 --round-subtransfers by_parcel --quota-criterion geq --ties backwards random --random-seed 20220322 --surplus-order by_order --transferable-only --surplus-assume-total");
|
||||||
|
|
||||||
utils::read_validate_election::<Rational>("tests/data/City_of_Albury-finalpreferencedatafile.csv", "tests/data/City_of_Albury-finalpreferencedatafile.blt", stv_opts, None, &[]);
|
utils::read_validate_election::<Rational>("tests/data/City_of_Albury-finalpreferencedatafile.csv", "tests/data/City_of_Albury-finalpreferencedatafile.blt", stv_opts, None, &[]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user