Implement --no-early-bulk-elect
This commit is contained in:
parent
bd6b4b01c5
commit
ce8b252453
@ -137,6 +137,14 @@ When ballots are normalised, a set of preferences with weight *n* > 1 is instead
|
||||
|
||||
## Count optimisations
|
||||
|
||||
## Early bulk election (--no-early-bulk-elect)
|
||||
|
||||
When early bulk election is enabled (default), all remaining candidates are declared elected in a single stage as soon as the number of not-excluded candidates exactly equals the number of vacancies to fill. Further surplus distributions are not performed, and outstanding exclusions, if any, are not completed. This is typical of most STV rules.
|
||||
|
||||
When early bulk election is disabled, surpluses continue to be distributed, and outstanding exclusions continue to be completed, even once the number of not-excluded candidates exactly equals the number of vacancies to fill. Bulk election is performed only once there are no more surpluses to distribute, and no exclusions to complete.
|
||||
|
||||
In either case, candidates are declared elected in descending order of votes. This ensures that only one candidate is ever elected at a time and the order of election is well-defined, which is required e.g. for some affirmative action rules.
|
||||
|
||||
### Bulk exclusion (--bulk-exclude)
|
||||
|
||||
When bulk exclusion is disabled (default), only one candidate is ever excluded per stage.
|
||||
|
@ -179,7 +179,7 @@
|
||||
Count optimisations:
|
||||
</div>
|
||||
<label class="col-6">
|
||||
<input type="checkbox" id="chkBulkElection" disabled>
|
||||
<input type="checkbox" id="chkBulkElection" checked>
|
||||
Early bulk election
|
||||
</label>
|
||||
<label class="col-6">
|
||||
|
@ -115,6 +115,7 @@ async function clickCount() {
|
||||
document.getElementById('selPapers').value == 'transferable',
|
||||
document.getElementById('selExclusion').value,
|
||||
document.getElementById('chkMeekNZExclusion').checked,
|
||||
document.getElementById('chkBulkElection').checked,
|
||||
document.getElementById('chkBulkExclusion').checked,
|
||||
document.getElementById('chkDeferSurpluses').checked,
|
||||
document.getElementById('chkMeekImmediateElect').checked,
|
||||
@ -318,7 +319,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'gt';
|
||||
document.getElementById('selQuota').value = 'droop_exact';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('selNumbers').value = 'rational';
|
||||
@ -338,7 +339,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('selNumbers').value = 'fixed';
|
||||
@ -361,7 +362,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'gt';
|
||||
document.getElementById('selQuota').value = 'droop_exact';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('chkMeekImmediateElect').checked = false;
|
||||
@ -385,7 +386,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = true;
|
||||
document.getElementById('chkMeekImmediateElect').checked = true;
|
||||
@ -413,7 +414,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = true;
|
||||
document.getElementById('chkMeekImmediateElect').checked = true;
|
||||
@ -441,7 +442,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = true;
|
||||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('selNumbers').value = 'fixed';
|
||||
@ -464,7 +465,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = true;
|
||||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('selNumbers').value = 'fixed';
|
||||
@ -486,7 +487,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = true;
|
||||
document.getElementById('selNumbers').value = 'fixed';
|
||||
@ -511,7 +512,7 @@ function changePreset() {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop_exact';
|
||||
document.getElementById('selQuotaMode').value = 'ers97';
|
||||
//document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = true;
|
||||
document.getElementById('chkDeferSurpluses').checked = true;
|
||||
document.getElementById('selNumbers').value = 'fixed';
|
||||
|
@ -140,6 +140,10 @@ struct STV {
|
||||
// -------------------------
|
||||
// -- Count optimisations --
|
||||
|
||||
/// Continue count even if continuing candidates fill all remaining vacancies
|
||||
#[clap(help_heading=Some("COUNT OPTIMISATIONS"), long)]
|
||||
no_early_bulk_elect: bool,
|
||||
|
||||
/// Use bulk exclusion
|
||||
#[clap(help_heading=Some("COUNT OPTIMISATIONS"), long)]
|
||||
bulk_exclude: bool,
|
||||
@ -221,6 +225,7 @@ where
|
||||
cmd_opts.transferable_only,
|
||||
&cmd_opts.exclusion,
|
||||
cmd_opts.meek_nz_exclusion,
|
||||
!cmd_opts.no_early_bulk_elect,
|
||||
cmd_opts.bulk_exclude,
|
||||
cmd_opts.defer_surpluses,
|
||||
cmd_opts.meek_immediate_elect,
|
||||
|
@ -71,6 +71,8 @@ pub struct STVOptions {
|
||||
pub exclusion: ExclusionMethod,
|
||||
/// (Meek STV) NZ Meek STV behaviour: Iterate keep values one round before candidate exclusion
|
||||
pub meek_nz_exclusion: bool,
|
||||
/// Bulk elect as soon as continuing candidates fill all remaining vacancies
|
||||
pub early_bulk_elect: bool,
|
||||
/// Use bulk exclusion
|
||||
pub bulk_exclude: bool,
|
||||
/// Defer surplus distributions if possible
|
||||
@ -101,6 +103,7 @@ impl STVOptions {
|
||||
transferable_only: bool,
|
||||
exclusion: &str,
|
||||
meek_nz_exclusion: bool,
|
||||
early_bulk_elect: bool,
|
||||
bulk_exclude: bool,
|
||||
defer_surpluses: bool,
|
||||
meek_immediate_elect: bool,
|
||||
@ -164,6 +167,7 @@ impl STVOptions {
|
||||
_ => panic!("Invalid --exclusion"),
|
||||
},
|
||||
meek_nz_exclusion,
|
||||
early_bulk_elect,
|
||||
bulk_exclude,
|
||||
defer_surpluses,
|
||||
meek_immediate_elect,
|
||||
@ -193,6 +197,7 @@ impl STVOptions {
|
||||
if self.surplus != SurplusMethod::Meek && self.transferable_only { flags.push("--transferable-only".to_string()); }
|
||||
if self.surplus != SurplusMethod::Meek && 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.early_bulk_elect { flags.push("--no-early-bulk-elect".to_string()); }
|
||||
if self.bulk_exclude { flags.push("--bulk-exclude".to_string()); }
|
||||
if self.defer_surpluses { flags.push("--defer-surpluses".to_string()); }
|
||||
if self.surplus == SurplusMethod::Meek && self.meek_immediate_elect { flags.push("--meek-immediate-elect".to_string()); }
|
||||
@ -422,6 +427,13 @@ where
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Attempt early bulk election
|
||||
if opts.early_bulk_elect {
|
||||
if bulk_elect(&mut state, &opts)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Continue exclusions
|
||||
if continue_exclusion(&mut state, &opts) {
|
||||
calculate_quota(&mut state, opts);
|
||||
@ -438,7 +450,7 @@ where
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Attempt bulk election
|
||||
// Attempt late bulk election
|
||||
if bulk_elect(&mut state, &opts)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
@ -200,6 +200,7 @@ impl STVOptions {
|
||||
transferable_only: bool,
|
||||
exclusion: &str,
|
||||
meek_nz_exclusion: bool,
|
||||
early_bulk_elect: bool,
|
||||
bulk_exclude: bool,
|
||||
defer_surpluses: bool,
|
||||
meek_immediate_elect: bool,
|
||||
@ -223,6 +224,7 @@ impl STVOptions {
|
||||
transferable_only,
|
||||
exclusion,
|
||||
meek_nz_exclusion,
|
||||
early_bulk_elect,
|
||||
bulk_exclude,
|
||||
defer_surpluses,
|
||||
meek_immediate_elect,
|
||||
|
@ -71,6 +71,7 @@ fn aec_tas19_rational() {
|
||||
transferable_only: false,
|
||||
exclusion: stv::ExclusionMethod::ByValue,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: true,
|
||||
defer_surpluses: false,
|
||||
meek_immediate_elect: false,
|
||||
|
@ -39,6 +39,7 @@ fn csm15_float64() {
|
||||
transferable_only: false,
|
||||
exclusion: stv::ExclusionMethod::Wright,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: true,
|
||||
defer_surpluses: false,
|
||||
meek_immediate_elect: false,
|
||||
|
@ -39,6 +39,7 @@ fn ers97_rational() {
|
||||
transferable_only: true,
|
||||
exclusion: stv::ExclusionMethod::ByValue,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: true,
|
||||
defer_surpluses: true,
|
||||
meek_immediate_elect: false,
|
||||
|
@ -44,6 +44,7 @@ fn meek87_ers97_float64() {
|
||||
transferable_only: false,
|
||||
exclusion: stv::ExclusionMethod::SingleStage,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: false,
|
||||
defer_surpluses: false,
|
||||
meek_immediate_elect: false,
|
||||
@ -72,6 +73,7 @@ fn meek06_ers97_fixed12() {
|
||||
transferable_only: false,
|
||||
exclusion: stv::ExclusionMethod::SingleStage,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: false,
|
||||
defer_surpluses: true,
|
||||
meek_immediate_elect: true,
|
||||
@ -145,6 +147,7 @@ fn meeknz_ers97_fixed12() {
|
||||
transferable_only: false,
|
||||
exclusion: stv::ExclusionMethod::SingleStage,
|
||||
meek_nz_exclusion: true,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: false,
|
||||
defer_surpluses: true,
|
||||
meek_immediate_elect: true,
|
||||
|
@ -39,6 +39,7 @@ fn prsa1_rational() {
|
||||
transferable_only: true,
|
||||
exclusion: stv::ExclusionMethod::ParcelsByOrder,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: false,
|
||||
bulk_exclude: false,
|
||||
defer_surpluses: false,
|
||||
meek_immediate_elect: false,
|
||||
|
@ -46,6 +46,7 @@ fn scotland_linn07_fixed5() {
|
||||
transferable_only: false,
|
||||
exclusion: stv::ExclusionMethod::SingleStage,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: false,
|
||||
defer_surpluses: false,
|
||||
meek_immediate_elect: false,
|
||||
@ -74,6 +75,7 @@ fn scotland_linn07_gfixed5() {
|
||||
transferable_only: false,
|
||||
exclusion: stv::ExclusionMethod::SingleStage,
|
||||
meek_nz_exclusion: false,
|
||||
early_bulk_elect: true,
|
||||
bulk_exclude: false,
|
||||
defer_surpluses: false,
|
||||
meek_immediate_elect: false,
|
||||
|
Loading…
Reference in New Issue
Block a user