Implement --subtract-nontransferable for NSW Local Government rules
This commit is contained in:
parent
4119a293b1
commit
26d45cac50
@ -104,10 +104,11 @@ Random sample methods are also supported, but also not recommended:
|
|||||||
|
|
||||||
The use of a random sample method requires *Normalise ballots* to be enabled, and will usually be used with a *Quota criterion* set to *>=*.
|
The use of a random sample method requires *Normalise ballots* to be enabled, and will usually be used with a *Quota criterion* set to *>=*.
|
||||||
|
|
||||||
### Papers to examine in surplus transfer (--transferable-only)
|
### Papers to examine in surplus transfer (--transferable-only/--subtract-nontransferable)
|
||||||
|
|
||||||
* *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.
|
* *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.
|
* *Use transferable papers only* (--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. This is the method typically used with other surplus distribution methods.
|
||||||
|
* *Subtract non-transferables* (--transferable-only --subtract-nontransferable): Same as *Use transferable papers only*, but the value of the transferable papers is calculated by subtracting the value of non-transferable papers from the progress total. This has effect only as far as concerns rounding.
|
||||||
|
|
||||||
### (Gregory) Exclusion method (--exclusion)
|
### (Gregory) Exclusion method (--exclusion)
|
||||||
|
|
||||||
|
@ -123,6 +123,7 @@
|
|||||||
<select id="selPapers">
|
<select id="selPapers">
|
||||||
<option value="both" selected>Include non-transferable papers</option>
|
<option value="both" selected>Include non-transferable papers</option>
|
||||||
<option value="transferable">Use transferable papers only</option>
|
<option value="transferable">Use transferable papers only</option>
|
||||||
|
<option value="subtract_nontransferable">Subtract non-transferables</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -154,7 +154,7 @@ async function clickCount() {
|
|||||||
document.getElementById('txtSeed').value,
|
document.getElementById('txtSeed').value,
|
||||||
document.getElementById('selMethod').value,
|
document.getElementById('selMethod').value,
|
||||||
document.getElementById('selSurplus').value,
|
document.getElementById('selSurplus').value,
|
||||||
document.getElementById('selPapers').value == 'transferable',
|
document.getElementById('selPapers').value,
|
||||||
document.getElementById('selExclusion').value,
|
document.getElementById('selExclusion').value,
|
||||||
document.getElementById('chkMeekNZExclusion').checked,
|
document.getElementById('chkMeekNZExclusion').checked,
|
||||||
document.getElementById('selSample').value,
|
document.getElementById('selSample').value,
|
||||||
|
@ -239,7 +239,7 @@ function changePreset() {
|
|||||||
document.getElementById('selSumTransfers').value = 'by_value_and_source';
|
document.getElementById('selSumTransfers').value = 'by_value_and_source';
|
||||||
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 = 'transferable';
|
document.getElementById('selPapers').value = 'subtract_nontransferable';
|
||||||
document.getElementById('selExclusion').value = 'single_stage';
|
document.getElementById('selExclusion').value = 'single_stage';
|
||||||
document.getElementById('selTies').value = 'backwards,random';
|
document.getElementById('selTies').value = 'backwards,random';
|
||||||
} else if (document.getElementById('selPreset').value === 'minneapolis') {
|
} else if (document.getElementById('selPreset').value === 'minneapolis') {
|
||||||
|
@ -125,6 +125,10 @@ 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
|
||||||
|
#[clap(help_heading=Some("STV VARIANTS"), long)]
|
||||||
|
subtract_nontransferable: bool,
|
||||||
|
|
||||||
/// (Gregory STV) Method of exclusions
|
/// (Gregory STV) Method of exclusions
|
||||||
#[clap(help_heading=Some("STV VARIANTS"), long, possible_values=&["single_stage", "by_value", "by_source", "parcels_by_order", "wright"], default_value="single_stage", value_name="method")]
|
#[clap(help_heading=Some("STV VARIANTS"), long, possible_values=&["single_stage", "by_value", "by_source", "parcels_by_order", "wright"], default_value="single_stage", value_name="method")]
|
||||||
exclusion: String,
|
exclusion: String,
|
||||||
@ -294,6 +298,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.exclusion.into(),
|
cmd_opts.exclusion.into(),
|
||||||
cmd_opts.meek_nz_exclusion,
|
cmd_opts.meek_nz_exclusion,
|
||||||
cmd_opts.sample.into(),
|
cmd_opts.sample.into(),
|
||||||
|
@ -265,6 +265,11 @@ where
|
|||||||
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 {
|
||||||
|
// Override transferable_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;
|
||||||
let mut surplus_fraction;
|
let mut surplus_fraction;
|
||||||
|
@ -104,6 +104,10 @@ 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
|
||||||
|
#[builder(default="false")]
|
||||||
|
pub subtract_nontransferable: bool,
|
||||||
|
|
||||||
/// (Gregory STV) Method of exclusions
|
/// (Gregory STV) Method of exclusions
|
||||||
#[builder(default="ExclusionMethod::SingleStage")]
|
#[builder(default="ExclusionMethod::SingleStage")]
|
||||||
pub exclusion: ExclusionMethod,
|
pub exclusion: ExclusionMethod,
|
||||||
@ -189,6 +193,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.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()); }
|
||||||
@ -224,6 +229,7 @@ impl STVOptions {
|
|||||||
//if self.sample == SampleMethod::StratifyFloor && self.sample_per_ballot { return Err(STVError::InvalidOptions("--sample stratify_floor is incompatible with --sample-per-ballot")); }
|
//if self.sample == SampleMethod::StratifyFloor && self.sample_per_ballot { return Err(STVError::InvalidOptions("--sample stratify_floor is incompatible with --sample-per-ballot")); }
|
||||||
if self.sample_per_ballot && !self.immediate_elect { return Err(STVError::InvalidOptions("--sample-per-ballot is incompatible with --no-immediate-elect")); }
|
if self.sample_per_ballot && !self.immediate_elect { return Err(STVError::InvalidOptions("--sample-per-ballot is incompatible with --no-immediate-elect")); }
|
||||||
}
|
}
|
||||||
|
if self.subtract_nontransferable && !self.transferable_only { return Err(STVError::InvalidOptions("--subtract-nontransferable requires --transferable-only")) }
|
||||||
if self.min_threshold != "0" && self.defer_surpluses { return Err(STVError::InvalidOptions("--min-threshold is incompatible with --defer-surpluses (not yet implemented)")); } // TODO: NYI
|
if self.min_threshold != "0" && self.defer_surpluses { return Err(STVError::InvalidOptions("--min-threshold is incompatible with --defer-surpluses (not yet implemented)")); } // TODO: NYI
|
||||||
if self.round_subtransfers == RoundSubtransfersMode::ByValueAndSource && self.bulk_exclude { return Err(STVError::InvalidOptions("--round-subtransfers by_value_and_source is incompatible with --bulk-exclude (not yet implemented)")); } // TODO: NYI
|
if self.round_subtransfers == RoundSubtransfersMode::ByValueAndSource && self.bulk_exclude { return Err(STVError::InvalidOptions("--round-subtransfers by_value_and_source is incompatible with --bulk-exclude (not yet implemented)")); } // TODO: NYI
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -249,7 +249,7 @@ impl STVOptions {
|
|||||||
random_seed: String,
|
random_seed: String,
|
||||||
surplus: &str,
|
surplus: &str,
|
||||||
surplus_order: &str,
|
surplus_order: &str,
|
||||||
transferable_only: bool,
|
papers: &str,
|
||||||
exclusion: &str,
|
exclusion: &str,
|
||||||
meek_nz_exclusion: bool,
|
meek_nz_exclusion: bool,
|
||||||
sample: &str,
|
sample: &str,
|
||||||
@ -277,7 +277,8 @@ impl STVOptions {
|
|||||||
ties::from_strs(ties.iter().map(|v| v.as_string().unwrap()).collect(), Some(random_seed)),
|
ties::from_strs(ties.iter().map(|v| v.as_string().unwrap()).collect(), Some(random_seed)),
|
||||||
surplus.into(),
|
surplus.into(),
|
||||||
surplus_order.into(),
|
surplus_order.into(),
|
||||||
transferable_only,
|
if papers == "transferable" || papers == "subtract_nontransferable" { true } else { false },
|
||||||
|
if papers == "subtract_nontransferable" { true } else { false },
|
||||||
exclusion.into(),
|
exclusion.into(),
|
||||||
meek_nz_exclusion,
|
meek_nz_exclusion,
|
||||||
sample.into(),
|
sample.into(),
|
||||||
|
@ -33,9 +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)
|
||||||
.build().unwrap();
|
.build().unwrap();
|
||||||
|
|
||||||
assert_eq!(stv_opts.describe::<Rational>(), "--round-votes 0 --round-quota 0 --round-subtransfers by_value_and_source --quota-criterion geq --ties backwards random --random-seed 20220322 --surplus-order by_order --transferable-only");
|
assert_eq!(stv_opts.describe::<Rational>(), "--round-votes 0 --round-quota 0 --round-subtransfers by_value_and_source --quota-criterion geq --ties backwards random --random-seed 20220322 --surplus-order by_order --transferable-only --subtract-nontransferable");
|
||||||
|
|
||||||
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