Initial framework for equal rankings
This commit is contained in:
parent
27ead09960
commit
b0f869bf02
@ -14,6 +14,7 @@ llvm-profdata merge -sparse target/coverage/prof/*.profraw -o target/coverage/op
|
||||
eval llvm-cov show target/coverage/debug/opentally -instr-profile=target/coverage/opentally.profdata -Xdemangler="$HOME/.cargo/bin/rustfilt" \
|
||||
$(for file in $(cargo test --no-run --message-format=json 2>/dev/null | jq -r "select(.profile.test == true) | .filenames[]"); do echo -n --object '"'$file'" '; done) \
|
||||
-ignore-filename-regex="$HOME/." \
|
||||
-ignore-filename-regex=rustc \
|
||||
-ignore-filename-regex=numbers/rational_num.rs \
|
||||
-ignore-filename-regex=stv/wasm.rs \
|
||||
-ignore-filename-regex=tests \
|
||||
|
@ -44,10 +44,14 @@ onmessage = function(evt) {
|
||||
// Init election
|
||||
election = wasm['election_from_blt_' + numbers](evt.data.bltData);
|
||||
|
||||
// Normalise ballots if requested
|
||||
if (evt.data.normaliseBallots) {
|
||||
wasm['election_normalise_ballots_' + numbers](election);
|
||||
}
|
||||
|
||||
// Process equal rankings
|
||||
wasm['election_realise_equal_rankings_' + numbers](election);
|
||||
|
||||
// Init constraints if applicable
|
||||
if (evt.data.conData) {
|
||||
wasm['election_load_constraints_' + numbers](election, evt.data.conData);
|
||||
|
@ -309,6 +309,9 @@ where
|
||||
election.normalise_ballots();
|
||||
}
|
||||
|
||||
// Process equal rankings
|
||||
election.realise_equal_rankings();
|
||||
|
||||
// Initialise count state
|
||||
let mut state = CountState::new(&election);
|
||||
|
||||
|
@ -47,6 +47,8 @@ pub struct Election<N> {
|
||||
|
||||
impl<N: Number> Election<N> {
|
||||
/// Convert ballots with weight >1 to multiple ballots of weight 1
|
||||
///
|
||||
/// Assumes ballots have integer weight.
|
||||
pub fn normalise_ballots(&mut self) {
|
||||
let mut normalised_ballots = Vec::new();
|
||||
for ballot in self.ballots.iter() {
|
||||
@ -63,6 +65,16 @@ impl<N: Number> Election<N> {
|
||||
}
|
||||
self.ballots = normalised_ballots;
|
||||
}
|
||||
|
||||
/// Convert ballots with equal rankings to strict-preference "minivoters"
|
||||
pub fn realise_equal_rankings(&mut self) {
|
||||
let mut realised_ballots = Vec::new();
|
||||
for ballot in self.ballots.iter() {
|
||||
let mut b = ballot.realise_equal_rankings();
|
||||
realised_ballots.append(&mut b);
|
||||
}
|
||||
self.ballots = realised_ballots;
|
||||
}
|
||||
}
|
||||
|
||||
/// A candidate in an [Election]
|
||||
@ -74,7 +86,6 @@ pub struct Candidate {
|
||||
}
|
||||
|
||||
/// The current state of counting an [Election]
|
||||
//#[derive(Clone)]
|
||||
pub struct CountState<'a, N: Number> {
|
||||
/// Pointer to the [Election] being counted
|
||||
pub election: &'a Election<N>,
|
||||
@ -368,6 +379,22 @@ pub struct Vote<'a, N> {
|
||||
pub up_to_pref: usize,
|
||||
}
|
||||
|
||||
impl<'a, N> Vote<'a, N> {
|
||||
/// Get the next preference and increment `up_to_pref`
|
||||
///
|
||||
/// Assumes that each preference level contains only one preference.
|
||||
pub fn next_preference(&mut self) -> Option<usize> {
|
||||
if self.up_to_pref >= self.ballot.preferences.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let preference = &self.ballot.preferences[self.up_to_pref];
|
||||
self.up_to_pref += 1;
|
||||
|
||||
return Some(*preference.first().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
/// A record of a voter's preferences
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||
@ -375,8 +402,32 @@ pub struct Ballot<N> {
|
||||
/// Original value/weight of the ballot
|
||||
#[cfg_attr(not(target_arch = "wasm32"), with(SerializedNum))]
|
||||
pub orig_value: N,
|
||||
/// Indexes of candidates preferenced on the ballot
|
||||
pub preferences: Vec<usize>,
|
||||
/// Indexes of candidates preferenced at each level on the ballot
|
||||
pub preferences: Vec<Vec<usize>>,
|
||||
}
|
||||
|
||||
impl<N: Number> Ballot<N> {
|
||||
/// Convert ballot with equal rankings to strict-preference "minivoters"
|
||||
pub fn realise_equal_rankings(&self) -> Vec<Ballot<N>> {
|
||||
// Preferences for each minivoter
|
||||
let mut minivoters = vec![Vec::new()];
|
||||
|
||||
for preference in self.preferences.iter() {
|
||||
if preference.len() == 1 {
|
||||
for minivoter in minivoters.iter_mut() {
|
||||
minivoter.push(preference.clone());
|
||||
}
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
let weight_each = self.orig_value.clone() / N::from(minivoters.len());
|
||||
let ballots = minivoters.into_iter()
|
||||
.map(|p| Ballot { orig_value: weight_each.clone(), preferences: p })
|
||||
.collect();
|
||||
return ballots;
|
||||
}
|
||||
}
|
||||
|
||||
/// rkyv-serialized representation of [Number]
|
||||
|
@ -151,7 +151,7 @@ impl<N: Number, I: Iterator<Item=char>> BLTParser<N, I> {
|
||||
self.accept();
|
||||
break;
|
||||
} else {
|
||||
preferences.push(self.usize()? - 1);
|
||||
preferences.push(vec![self.usize()? - 1]);
|
||||
self.delimiter_not_nl();
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ pub fn parse_reader<R: Read, N: Number>(reader: R) -> Election<N> {
|
||||
|
||||
ballots.push(Ballot {
|
||||
orig_value: value,
|
||||
preferences: preferences.into_iter().map(|(_, i)| *i).collect(),
|
||||
preferences: preferences.into_iter().map(|(_, i)| vec![*i]).collect(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,13 @@ impl<'t, N: Number> BallotTree<'t, N> {
|
||||
|
||||
for bit in self.ballots.iter() {
|
||||
if bit.up_to_pref < bit.ballot.preferences.len() {
|
||||
let candidate = &candidates[bit.ballot.preferences[bit.up_to_pref]];
|
||||
let preference = &bit.ballot.preferences[bit.up_to_pref];
|
||||
|
||||
if preference.len() != 1 {
|
||||
todo!();
|
||||
}
|
||||
|
||||
let candidate = &candidates[*preference.first().unwrap()];
|
||||
|
||||
if next_preferences.contains_key(candidate) {
|
||||
let np_bt = next_preferences.get_mut(candidate).unwrap();
|
||||
|
@ -692,14 +692,18 @@ fn next_preferences<'a, N: Number>(state: &CountState<'a, N>, votes: Vec<Vote<'a
|
||||
|
||||
let mut next_candidate = None;
|
||||
|
||||
for (i, preference) in vote.ballot.preferences.iter().enumerate().skip(vote.up_to_pref) {
|
||||
let candidate = &state.election.candidates[*preference];
|
||||
let count_card = &state.candidates[candidate];
|
||||
|
||||
if let CandidateState::Hopeful | CandidateState::Guarded = count_card.state {
|
||||
next_candidate = Some(candidate);
|
||||
vote.up_to_pref = i + 1;
|
||||
break;
|
||||
loop {
|
||||
match vote.next_preference() {
|
||||
Some(preference) => {
|
||||
let candidate = &state.election.candidates[preference];
|
||||
let count_card = &state.candidates[candidate];
|
||||
|
||||
if let CandidateState::Hopeful | CandidateState::Guarded = count_card.state {
|
||||
next_candidate = Some(candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => { break; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,14 +275,19 @@ where
|
||||
fn transfer_ballot<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOptions, source_candidate: &Candidate, mut vote: Vote<'a, N>, ignore_nontransferable: bool) -> Result<(), STVError> {
|
||||
// Get next preference
|
||||
let mut next_candidate = None;
|
||||
for (i, preference) in vote.ballot.preferences.iter().enumerate().skip(vote.up_to_pref) {
|
||||
let candidate = &state.election.candidates[*preference];
|
||||
let count_card = &state.candidates[candidate];
|
||||
|
||||
if let CandidateState::Hopeful | CandidateState::Guarded = count_card.state {
|
||||
next_candidate = Some(candidate);
|
||||
vote.up_to_pref = i + 1;
|
||||
break;
|
||||
|
||||
loop {
|
||||
match vote.next_preference() {
|
||||
Some(preference) => {
|
||||
let candidate = &state.election.candidates[preference];
|
||||
let count_card = &state.candidates[candidate];
|
||||
|
||||
if let CandidateState::Hopeful | CandidateState::Guarded = count_card.state {
|
||||
next_candidate = Some(candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => { break; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,13 @@ macro_rules! impl_type {
|
||||
election.0.normalise_ballots();
|
||||
}
|
||||
|
||||
/// Wrapper for [Election::realise_equal_rankings]
|
||||
#[wasm_bindgen]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn [<election_realise_equal_rankings_$type>](election: &mut [<Election$type>]) {
|
||||
election.0.realise_equal_rankings();
|
||||
}
|
||||
|
||||
/// Call [Constraints::from_con] and set [Election::constraints]
|
||||
#[wasm_bindgen]
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -40,7 +40,7 @@ pub fn write<W: Write, N: Number>(election: Election<N>, output: W) {
|
||||
output.write_fmt(format_args!("{}", ballot.orig_value)).expect("IO Error");
|
||||
|
||||
for preference in ballot.preferences {
|
||||
output.write_fmt(format_args!(" {}", preference + 1)).expect("IO Error");
|
||||
output.write_fmt(format_args!(" {}", preference.into_iter().map(|p| p + 1).join("="))).expect("IO Error");
|
||||
}
|
||||
|
||||
output.write(b" 0\n").expect("IO Error");
|
||||
|
@ -40,7 +40,9 @@ pub fn write<W: Write, N: Number>(election: Election<N>, output: W) {
|
||||
// Code preferences to rankings
|
||||
let mut rankings = vec![0_usize; election.candidates.len()];
|
||||
for (i, preference) in ballot.preferences.into_iter().enumerate() {
|
||||
rankings[preference] = i + 1;
|
||||
for p in preference {
|
||||
rankings[p] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write rankings
|
||||
|
Loading…
Reference in New Issue
Block a user