Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix some math on the per-slice inactive ballots #875

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ private <T> void watchGenericService(
originalCallback.handle(workerStateEvent);
onSuccessCallback.handle(workerStateEvent);
});
service.setOnFailed(
workerStateEvent -> {
stage.setOnCloseRequest(null);
});
}

/**
Expand Down
49 changes: 43 additions & 6 deletions src/main/java/network/brightspots/rcv/Tabulator.java
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,7 @@ private OvervoteDecision getOvervoteDecision(CandidatesAtRanking candidates)
private void recordSelectionForCastVoteRecord(
CastVoteRecord cvr,
RoundTally currentRoundTally,
BreakdownBySlice<RoundTally> roundTallyBySlice,
String selectedCandidate,
StatusForRound statusForRound,
String additionalLogText)
Expand Down Expand Up @@ -997,6 +998,11 @@ private void recordSelectionForCastVoteRecord(

if (statusForRound != StatusForRound.ACTIVE) {
currentRoundTally.addInactiveBallot(statusForRound, cvr.getFractionalTransferValue());
for (ContestConfig.TabulateBySlice slice : config.enabledSlices()) {
String sliceId = cvr.getSlice(slice);
RoundTally sliceRoundTally = roundTallyBySlice.get(slice).get(sliceId);
sliceRoundTally.addInactiveBallot(statusForRound, cvr.getFractionalTransferValue());
}
}

String outcomeDescription;
Expand Down Expand Up @@ -1052,6 +1058,14 @@ private RoundTally computeTalliesForRound(int currentRound) throws TabulationAbo
for (CastVoteRecord cvr : castVoteRecords) {
if (cvr.isExhausted()) {
roundTally.addInactiveBallot(cvr.getBallotStatus(), cvr.getFractionalTransferValue());

// Add inactive ballot to each slice too
for (ContestConfig.TabulateBySlice slice : config.enabledSlices()) {
String sliceId = cvr.getSlice(slice);
RoundTally sliceRoundTally = roundTallyBySlice.get(slice).get(sliceId);
sliceRoundTally.addInactiveBallot(
cvr.getBallotStatus(), cvr.getFractionalTransferValue());
}
continue;
}

Expand All @@ -1070,7 +1084,12 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
// check for a CVR with no rankings at all
if (cvr.candidateRankings.numRankings() == 0) {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.DID_NOT_RANK_ANY_CANDIDATES, "");
cvr,
roundTally,
roundTallyBySlice,
null,
StatusForRound.DID_NOT_RANK_ANY_CANDIDATES,
"");
}

// iterate through the rankings in this cvr from most to least preferred.
Expand Down Expand Up @@ -1099,7 +1118,12 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
if (config.getMaxSkippedRanksAllowed() != Integer.MAX_VALUE
&& (rank - lastRankSeen > config.getMaxSkippedRanksAllowed() + 1)) {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.INVALIDATED_BY_SKIPPED_RANKING, "");
cvr,
roundTally,
roundTallyBySlice,
null,
StatusForRound.INVALIDATED_BY_SKIPPED_RANKING,
"");
break;
}
lastRankSeen = rank;
Expand All @@ -1119,6 +1143,7 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
recordSelectionForCastVoteRecord(
cvr,
roundTally,
roundTallyBySlice,
null,
StatusForRound.INVALIDATED_BY_REPEATED_RANKING,
" " + duplicateCandidate);
Expand All @@ -1130,14 +1155,24 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
OvervoteDecision overvoteDecision = getOvervoteDecision(candidates);
if (overvoteDecision == OvervoteDecision.EXHAUST) {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.INVALIDATED_BY_OVERVOTE, "");
cvr,
roundTally,
roundTallyBySlice,
null,
StatusForRound.INVALIDATED_BY_OVERVOTE,
"");
break;
} else if (overvoteDecision == OvervoteDecision.SKIP_TO_NEXT_RANK) {
if (rank == cvr.candidateRankings.maxRankingNumber()) {
// If the final ranking is an overvote, even if we're trying to skip to the next rank,
// we consider this inactive by exhausted choices -- not an overvote.
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.EXHAUSTED_CHOICE, "");
cvr,
roundTally,
roundTallyBySlice,
null,
StatusForRound.EXHAUSTED_CHOICE,
"");
}
continue;
}
Expand All @@ -1156,7 +1191,7 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {

// transfer cvr to selected candidate
recordSelectionForCastVoteRecord(
cvr, roundTally, selectedCandidate, StatusForRound.ACTIVE, "");
cvr, roundTally, roundTallyBySlice, selectedCandidate, StatusForRound.ACTIVE, "");

// This will also update the roundTallyBySlice for each enabled slice
incrementTallies(roundTally, cvr, selectedCandidate, roundTallyBySlice);
Expand All @@ -1174,7 +1209,7 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
// if this is the last ranking we are out of rankings and must exhaust this cvr
if (rank == cvr.candidateRankings.maxRankingNumber()) {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.EXHAUSTED_CHOICE, "");
cvr, roundTally, roundTallyBySlice, null, StatusForRound.EXHAUSTED_CHOICE, "");
}
} // end looping over the rankings within one ballot
} // end looping over all ballots
Expand All @@ -1186,6 +1221,8 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
RoundTallies roundTalliesForSlice = roundTalliesBySlices.get(slice).get(entry.getKey());
roundTalliesForSlice.put(currentRound, entry.getValue());
roundTalliesForSlice.get(currentRound).lockInRound();
Logger.info("Round %d slice %s %s tallies: %s",
currentRound, slice, entry.getKey(), entry.getValue().inactiveBallotSum().toString());
}
}
roundTally.lockInRound();
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/network/brightspots/rcv/TabulatorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ void test2018MaineGovPrimaryDem() {
@Test
@DisplayName("testMinneapolisMultiSeatThreshold")
void testMinneapolisMultiSeatThreshold() {
runTabulationTest("minneapolis_multi_seat_threshold");
yezr marked this conversation as resolved.
Show resolved Hide resolved
runTabulationTest("minneapolis_multi_seat_threshold", 3);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Precinct,MINNEAPOLIS W-13 P-09
Contest Summary
Number to be Elected,1
Number of Candidates,36
Total Number of Ballots,985
Total Number of Ballots,987
Number of Undervotes (No Rankings),0

Rounds,Round 1 Votes,% of vote,transfer,Round 2 Votes,% of vote,transfer,Round 3 Votes,% of vote,transfer,Round 4 Votes,% of vote,transfer,Round 5 Votes,% of vote,transfer,Round 6 Votes,% of vote,transfer,Round 7 Votes,% of vote,transfer,Round 8 Votes,% of vote,transfer,Round 9 Votes,% of vote,transfer,Round 10 Votes,% of vote,transfer,Round 11 Votes,% of vote,transfer,Round 12 Votes,% of vote,transfer,Round 13 Votes,% of vote,transfer,Round 14 Votes,% of vote,transfer,Round 15 Votes,% of vote,transfer,Round 16 Votes,% of vote,transfer,Round 17 Votes,% of vote,transfer,Round 18 Votes,% of vote,transfer,Round 19 Votes,% of vote,transfer,Round 20 Votes,% of vote,transfer,Round 21 Votes,% of vote,transfer,Round 22 Votes,% of vote,transfer,Round 23 Votes,% of vote,transfer,Round 24 Votes,% of vote,transfer,Round 25 Votes,% of vote,transfer,Round 26 Votes,% of vote,transfer,Round 27 Votes,% of vote,transfer,Round 28 Votes,% of vote,transfer,Round 29 Votes,% of vote,transfer,Round 30 Votes,% of vote,transfer,Round 31 Votes,% of vote,transfer,Round 32 Votes,% of vote,transfer,Round 33 Votes,% of vote,transfer
Expand Down Expand Up @@ -55,8 +55,8 @@ JOSHUA REA,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%
Undeclared Write-ins,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Active Ballots,985,,,985,,,985,,,984,,,984,,,984,,,984,,,984,,,984,,,983,,,983,,,981,,,981,,,981,,,981,,,981,,,981,,,981,,,981,,,980,,,979,,,979,,,979,,,979,,,979,,,979,,,972,,,972,,,969,,,968,,,953,,,941,,,798,,
Current Round Threshold,39657,,,39638,,,39635,,,39633,,,39627,,,39622,,,39614,,,39608,,,39599,,,39583,,,39568,,,39552,,,39533,,,39522,,,39502,,,39477,,,39462,,,39450,,,39414,,,39387,,,39359,,,39309,,,39247,,,39112,,,39041,,,38981,,,38726,,,38541,,,38312,,,38162,,,37796,,,36810,,,31898,,
Inactive Ballots by Overvotes,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Overvotes,2,,0,2,,0,2,,0,2,,0,2,,0,2,,0,2,,0,2,,0,2,,0,2,,0,2,,1,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0
Inactive Ballots by Skipped Rankings,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices,0,,0,0,,0,0,,1,1,,0,1,,0,1,,0,1,,0,1,,0,1,,1,2,,0,2,,1,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,0,3,,1,4,,1,5,,0,5,,0,5,,0,5,,0,5,,0,5,,7,12,,0,12,,3,15,,1,16,,15,31,,12,43,,143,186,,0
Inactive Ballots by Repeated Rankings,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots Total,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots Total,2,,0,2,,0,2,,1,3,,0,3,,0,3,,0,3,,0,3,,0,3,,1,4,,0,4,,2,6,,0,6,,0,6,,0,6,,0,6,,0,6,,0,6,,0,6,,1,7,,1,8,,0,8,,0,8,,0,8,,0,8,,0,8,,7,15,,0,15,,3,18,,1,19,,15,34,,12,46,,143,189,,0
Loading
Loading