OpenTally/src/election.rs

383 lines
11 KiB
Rust
Raw Normal View History

2021-05-28 19:58:40 +10:00
/* OpenTally: Open-source election vote counting
* Copyright © 2021 Lee Yingtong Li (RunasSudo)
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::constraints::{Constraints, ConstraintMatrix};
use crate::logger::Logger;
2021-05-28 19:58:40 +10:00
use crate::numbers::Number;
2021-06-13 03:15:15 +10:00
use crate::sharandom::SHARandom;
2021-05-28 19:58:40 +10:00
use std::collections::HashMap;
2021-06-14 20:43:36 +10:00
/// An election to be counted
2021-05-28 19:58:40 +10:00
pub struct Election<N> {
2021-06-16 17:20:29 +10:00
/// Name of the election
pub name: String,
2021-06-16 17:20:29 +10:00
/// Number of candidates to be elected
2021-05-28 19:58:40 +10:00
pub seats: usize,
2021-06-16 17:20:29 +10:00
/// [Vec] of [Candidate]s in the election
2021-05-28 19:58:40 +10:00
pub candidates: Vec<Candidate>,
2021-06-16 17:20:29 +10:00
/// Indexes of withdrawn candidates
2021-06-12 00:50:01 +10:00
pub withdrawn_candidates: Vec<usize>,
2021-06-16 17:20:29 +10:00
/// [Vec] of [Ballot]s cast in the election
2021-05-28 19:58:40 +10:00
pub ballots: Vec<Ballot<N>>,
/// Constraints on candidates
pub constraints: Option<Constraints>,
2021-05-28 19:58:40 +10:00
}
impl<N: Number> Election<N> {
2021-06-14 20:43:36 +10:00
/// Parse the given BLT file and return an [Election]
2021-05-30 18:28:39 +10:00
pub fn from_blt<I: Iterator<Item=String>>(mut lines: I) -> Self {
2021-05-28 19:58:40 +10:00
// Read first line
2021-05-30 18:28:39 +10:00
let line = lines.next().expect("Unexpected EOF");
2021-05-28 19:58:40 +10:00
let mut bits = line.split(" ");
let num_candidates = bits.next().expect("Syntax Error").parse().expect("Syntax Error");
let seats: usize = bits.next().expect("Syntax Error").parse().expect("Syntax Error");
// Initialise the Election object
let mut election = Election {
name: String::new(),
2021-05-28 19:58:40 +10:00
seats: seats,
candidates: Vec::with_capacity(num_candidates),
2021-06-12 00:50:01 +10:00
withdrawn_candidates: Vec::new(),
2021-05-28 19:58:40 +10:00
ballots: Vec::new(),
constraints: None,
2021-05-28 19:58:40 +10:00
};
// Read ballots
for line in &mut lines {
if line == "0" {
break;
}
let mut bits = line.split(" ");
2021-06-12 00:50:01 +10:00
if line.starts_with("-") {
// Withdrawn candidates
for bit in bits.into_iter() {
let val = bit[1..bit.len()].parse::<usize>().expect("Syntax Error");
election.withdrawn_candidates.push(val - 1);
}
continue;
}
2021-05-28 19:58:40 +10:00
let value = N::parse(bits.next().expect("Syntax Error"));
let mut ballot = Ballot {
orig_value: value,
preferences: Vec::new(),
};
for preference in bits {
if preference != "0" {
let preference = preference.parse::<usize>().expect("Syntax Error");
ballot.preferences.push(preference - 1);
}
}
election.ballots.push(ballot);
}
// Read candidates
for line in lines.by_ref().take(num_candidates) {
2021-05-30 18:28:39 +10:00
let mut line = &line[..];
2021-05-28 19:58:40 +10:00
if line.starts_with("\"") && line.ends_with("\"") {
line = &line[1..line.len()-1];
}
election.candidates.push(Candidate { name: line.to_string() });
}
// Read name
let line = lines.next().expect("Syntax Error");
let mut line = &line[..];
if line.starts_with("\"") && line.ends_with("\"") {
line = &line[1..line.len()-1];
}
election.name.push_str(line);
2021-05-28 19:58:40 +10:00
return election;
}
2021-06-14 20:43:36 +10:00
/// Convert ballots with weight >1 to multiple ballots of weight 1
pub fn normalise_ballots(&mut self) {
let mut normalised_ballots = Vec::new();
for ballot in self.ballots.iter() {
let mut n = N::new();
let one = N::one();
while n < ballot.orig_value {
let new_ballot = Ballot {
orig_value: N::one(),
preferences: ballot.preferences.clone(),
};
normalised_ballots.push(new_ballot);
n += &one;
}
}
self.ballots = normalised_ballots;
}
2021-05-28 19:58:40 +10:00
}
2021-06-14 20:43:36 +10:00
/// A candidate in an [Election]
2021-05-28 19:58:40 +10:00
#[derive(PartialEq, Eq, Hash)]
pub struct Candidate {
2021-06-16 17:20:29 +10:00
/// Name of the candidate
2021-05-28 19:58:40 +10:00
pub name: String,
}
2021-06-14 20:43:36 +10:00
/// The current state of counting an [Election]
2021-06-16 13:00:54 +10:00
//#[derive(Clone)]
pub struct CountState<'a, N: Number> {
2021-06-16 17:20:29 +10:00
/// Pointer to the [Election] being counted
2021-05-28 19:58:40 +10:00
pub election: &'a Election<N>,
2021-06-16 17:20:29 +10:00
/// [HashMap] of [CountCard]s for each [Candidate] in the election
2021-05-28 19:58:40 +10:00
pub candidates: HashMap<&'a Candidate, CountCard<'a, N>>,
2021-06-16 17:20:29 +10:00
/// [CountCard] representing the exhausted pile
2021-05-28 19:58:40 +10:00
pub exhausted: CountCard<'a, N>,
2021-06-16 17:20:29 +10:00
/// [CountCard] representing loss by fraction
pub loss_fraction: CountCard<'a, N>,
2021-05-28 19:58:40 +10:00
2021-06-16 17:20:29 +10:00
/// [crate::stv::meek::BallotTree] for Meek STV
2021-06-16 13:00:54 +10:00
pub ballot_tree: Option<crate::stv::meek::BallotTree<'a, N>>,
2021-06-16 17:20:29 +10:00
/// Values used to break ties, based on forwards tie-breaking
pub forwards_tiebreak: Option<HashMap<&'a Candidate, usize>>,
2021-06-16 17:20:29 +10:00
/// Values used to break ties, based on backwards tie-breaking
pub backwards_tiebreak: Option<HashMap<&'a Candidate, usize>>,
2021-06-16 17:20:29 +10:00
/// [SHARandom] for random tie-breaking
2021-06-13 03:15:15 +10:00
pub random: Option<SHARandom<'a>>,
2021-06-16 17:20:29 +10:00
/// Quota for election
2021-06-07 20:52:18 +10:00
pub quota: Option<N>,
2021-06-16 17:20:29 +10:00
/// Vote required for election
///
/// With a static quota, this is equal to the quota. With ERS97 rules, this may vary from the quota.
2021-06-07 20:52:18 +10:00
pub vote_required_election: Option<N>,
2021-05-28 19:58:40 +10:00
2021-06-16 17:20:29 +10:00
/// Number of candidates who have been declared elected
2021-05-28 19:58:40 +10:00
pub num_elected: usize,
2021-06-16 17:20:29 +10:00
/// Number of candidates who have been declared excluded
2021-05-28 19:58:40 +10:00
pub num_excluded: usize,
2021-05-29 01:22:46 +10:00
/// [ConstraintMatrix] for constrained elections
pub constraint_matrix: Option<ConstraintMatrix>,
2021-06-16 17:20:29 +10:00
/// The type of stage being counted
///
/// For example, "Surplus of", "Exclusion of"
2021-05-29 01:22:46 +10:00
pub kind: Option<&'a str>,
2021-06-16 17:20:29 +10:00
/// The description of the stage being counted, excluding [CountState::kind]
2021-05-29 01:22:46 +10:00
pub title: String,
2021-06-16 17:20:29 +10:00
/// [Logger] for this stage of the count
pub logger: Logger<'a>,
2021-05-28 19:58:40 +10:00
}
impl<'a, N: Number> CountState<'a, N> {
2021-06-14 20:43:36 +10:00
/// Construct a new blank [CountState] for the given [Election]
2021-05-28 19:58:40 +10:00
pub fn new(election: &'a Election<N>) -> Self {
let mut state = CountState {
election: &election,
candidates: HashMap::new(),
exhausted: CountCard::new(),
loss_fraction: CountCard::new(),
2021-06-16 13:00:54 +10:00
ballot_tree: None,
forwards_tiebreak: None,
backwards_tiebreak: None,
2021-06-13 03:15:15 +10:00
random: None,
2021-06-07 20:52:18 +10:00
quota: None,
vote_required_election: None,
2021-05-28 19:58:40 +10:00
num_elected: 0,
num_excluded: 0,
constraint_matrix: None,
2021-05-29 01:22:46 +10:00
kind: None,
title: String::new(),
logger: Logger { entries: Vec::new() },
2021-05-28 19:58:40 +10:00
};
for candidate in election.candidates.iter() {
state.candidates.insert(candidate, CountCard::new());
}
2021-06-12 00:50:01 +10:00
for withdrawn_idx in election.withdrawn_candidates.iter() {
state.candidates.get_mut(&election.candidates[*withdrawn_idx]).unwrap().state = CandidateState::Withdrawn;
}
if let Some(constraints) = &election.constraints {
let mut num_groups: Vec<usize> = constraints.0.iter().map(|c| c.groups.len()).collect();
let mut cm = ConstraintMatrix::new(&mut num_groups[..]);
// Init constraint matrix total cells min/max
for (i, constraint) in constraints.0.iter().enumerate() {
for (j, group) in constraint.groups.iter().enumerate() {
let mut idx = vec![0; constraints.0.len()];
idx[i] = j + 1;
let mut cell = &mut cm[&idx];
cell.min = group.min;
cell.max = group.max;
}
}
// Fill in grand total, etc.
cm.update_from_state(&state.election, &state.candidates);
cm.init();
//println!("{}", cm);
state.constraint_matrix = Some(cm);
}
2021-05-28 19:58:40 +10:00
return state;
}
2021-06-14 20:43:36 +10:00
/// [Step](CountCard::step) every [CountCard] to prepare for the next stage
2021-05-28 19:58:40 +10:00
pub fn step_all(&mut self) {
for (_, count_card) in self.candidates.iter_mut() {
count_card.step();
}
self.exhausted.step();
self.loss_fraction.step();
2021-05-28 19:58:40 +10:00
}
}
2021-06-14 20:43:36 +10:00
/// Represents either a reference to a [CountState] or a clone
2021-05-29 01:22:46 +10:00
#[allow(dead_code)]
2021-06-16 13:00:54 +10:00
pub enum CountStateOrRef<'a, N: Number> {
2021-06-16 17:20:29 +10:00
/// Cloned [CountState]
///
/// Currently unused/unimplemented, but may be used in future for rollback-based constraints
State(CountState<'a, N>),
/// Reference to a [CountState]
2021-05-28 19:58:40 +10:00
Ref(&'a CountState<'a, N>),
}
2021-06-16 13:00:54 +10:00
impl<'a, N: Number> CountStateOrRef<'a, N> {
2021-06-14 20:43:36 +10:00
/// Construct a [CountStateOrRef] as a reference to a [CountState]
2021-05-29 01:22:46 +10:00
pub fn from(state: &'a CountState<N>) -> Self {
return Self::Ref(state);
}
2021-06-14 20:43:36 +10:00
/// Return a reference to the underlying [CountState]
2021-05-28 19:58:40 +10:00
pub fn as_ref(&self) -> &CountState<N> {
match self {
CountStateOrRef::State(state) => &state,
CountStateOrRef::Ref(state) => state,
}
}
}
2021-06-14 20:43:36 +10:00
/// Result of a stage of counting
2021-06-16 13:00:54 +10:00
pub struct StageResult<'a, N: Number> {
2021-06-16 17:20:29 +10:00
/// See [CountState::kind]
2021-05-29 01:22:46 +10:00
pub kind: Option<&'a str>,
2021-06-16 17:20:29 +10:00
/// See [CountState::title]
2021-05-29 01:22:46 +10:00
pub title: &'a String,
2021-06-16 17:20:29 +10:00
/// Detailed logs of this stage, rendered from [CountState::logger]
pub logs: Vec<String>,
2021-06-16 17:20:29 +10:00
/// Reference to the [CountState] or cloned [CountState] of this stage
2021-05-28 19:58:40 +10:00
pub state: CountStateOrRef<'a, N>,
}
2021-06-14 20:43:36 +10:00
/// Current state of a [Candidate] during an election count
2021-05-28 19:58:40 +10:00
#[derive(Clone)]
pub struct CountCard<'a, N> {
2021-06-16 17:20:29 +10:00
/// State of the candidate
2021-05-28 19:58:40 +10:00
pub state: CandidateState,
2021-06-16 17:20:29 +10:00
/// Order of election or exclusion
///
/// Positive integers represent order of election; negative integers represent order of exclusion
2021-05-28 19:58:40 +10:00
pub order_elected: isize,
2021-06-16 13:00:54 +10:00
//pub orig_votes: N,
2021-06-16 17:20:29 +10:00
/// Net votes transferred to this candidate in this stage
2021-05-28 19:58:40 +10:00
pub transfers: N,
2021-06-16 17:20:29 +10:00
/// Votes of the candidate at the end of this stage
2021-05-28 19:58:40 +10:00
pub votes: N,
2021-06-16 17:20:29 +10:00
/// Parcels of ballots assigned to this candidate
2021-05-28 19:58:40 +10:00
pub parcels: Vec<Parcel<'a, N>>,
2021-06-16 13:00:54 +10:00
/// Candidate's keep value (Meek STV)
pub keep_value: Option<N>,
2021-05-28 19:58:40 +10:00
}
impl<'a, N: Number> CountCard<'a, N> {
2021-06-14 20:43:36 +10:00
/// Returns a new blank [CountCard]
2021-05-28 19:58:40 +10:00
pub fn new() -> Self {
return CountCard {
2021-06-12 00:50:01 +10:00
state: CandidateState::Hopeful,
2021-05-28 19:58:40 +10:00
order_elected: 0,
2021-06-16 13:00:54 +10:00
//orig_votes: N::new(),
2021-05-28 19:58:40 +10:00
transfers: N::new(),
votes: N::new(),
parcels: Vec::new(),
2021-06-16 13:00:54 +10:00
keep_value: None,
2021-05-28 19:58:40 +10:00
};
}
2021-06-14 20:43:36 +10:00
/// Transfer the given number of votes to this [CountCard], incrementing [transfers](CountCard::transfers) and [votes](CountCard::votes)
2021-05-28 19:58:40 +10:00
pub fn transfer(&mut self, transfer: &'_ N) {
self.transfers += transfer;
self.votes += transfer;
}
2021-06-16 13:00:54 +10:00
/// Set [transfers](CountCard::transfers) to 0
2021-05-28 19:58:40 +10:00
pub fn step(&mut self) {
2021-06-16 13:00:54 +10:00
//self.orig_votes = self.votes.clone();
2021-05-28 19:58:40 +10:00
self.transfers = N::new();
}
}
2021-06-14 20:43:36 +10:00
/// Parcel of [Vote]s during a count
2021-05-28 19:58:40 +10:00
pub type Parcel<'a, N> = Vec<Vote<'a, N>>;
2021-06-14 20:43:36 +10:00
/// Represents a [Ballot] with an associated value
2021-05-28 19:58:40 +10:00
#[derive(Clone)]
pub struct Vote<'a, N> {
2021-06-16 17:20:29 +10:00
/// Ballot from which the vote is derived
2021-05-28 19:58:40 +10:00
pub ballot: &'a Ballot<N>,
2021-06-16 17:20:29 +10:00
/// Current value of the ballot
2021-05-28 19:58:40 +10:00
pub value: N,
2021-06-16 13:00:54 +10:00
/// Index of the next preference to examine
2021-05-28 19:58:40 +10:00
pub up_to_pref: usize,
}
2021-06-14 20:43:36 +10:00
/// A record of a voter's preferences
2021-05-28 19:58:40 +10:00
pub struct Ballot<N> {
2021-06-16 17:20:29 +10:00
/// Original value/weight of the ballot
2021-05-28 19:58:40 +10:00
pub orig_value: N,
2021-06-16 17:20:29 +10:00
/// Indexes of candidates preferenced on the ballot
2021-05-28 19:58:40 +10:00
pub preferences: Vec<usize>,
}
2021-06-14 20:43:36 +10:00
/// State of a [Candidate] during a count
2021-05-28 19:58:40 +10:00
#[allow(dead_code)]
#[derive(PartialEq)]
#[derive(Clone)]
pub enum CandidateState {
2021-06-16 17:20:29 +10:00
/// Hopeful (continuing candidate)
2021-06-12 00:50:01 +10:00
Hopeful,
2021-06-16 17:20:29 +10:00
/// Required by constraints to be guarded from exclusion
2021-06-12 00:50:01 +10:00
Guarded,
2021-06-16 17:20:29 +10:00
/// Declared elected
2021-06-12 00:50:01 +10:00
Elected,
2021-06-16 17:20:29 +10:00
/// Required by constraints to be doomed to be excluded
2021-06-12 00:50:01 +10:00
Doomed,
2021-06-16 17:20:29 +10:00
/// Withdrawn candidate
2021-06-12 00:50:01 +10:00
Withdrawn,
2021-06-16 17:20:29 +10:00
/// Declared excluded
2021-06-12 00:50:01 +10:00
Excluded,
2021-05-28 19:58:40 +10:00
}