Skip to content

Commit

Permalink
Split undervote bucket into "0 rankings" and "less than max rankings"
Browse files Browse the repository at this point in the history
  • Loading branch information
artoonie committed Jun 25, 2024
1 parent 04a3835 commit f293caa
Show file tree
Hide file tree
Showing 166 changed files with 3,175 additions and 2,893 deletions.
3 changes: 2 additions & 1 deletion src/main/java/network/brightspots/rcv/CastVoteRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ Map<String, BigDecimal> getWinnerToFractionalValue() {
// it is active or not.
enum StatusForRound {
ACTIVE,
INACTIVE_BY_UNDERVOTE,
INACTIVE_BY_NO_RANKINGS,
INACTIVE_BY_UNUSED_RANKINGS,
INACTIVE_BY_OVERVOTE,
INACTIVE_BY_SKIPPED_RANKING,
INACTIVE_BY_REPEATED_RANKING,
Expand Down
47 changes: 28 additions & 19 deletions src/main/java/network/brightspots/rcv/ResultsWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,27 @@ private void generateSummarySpreadsheet(

Pair<String, StatusForRound>[] statusesToPrint =
new Pair[] {
new Pair<>("Overvotes", StatusForRound.INACTIVE_BY_OVERVOTE),
new Pair<>("Skipped Rankings", StatusForRound.INACTIVE_BY_SKIPPED_RANKING),
new Pair<>("Exhausted Choices", StatusForRound.INACTIVE_BY_EXHAUSTED_CHOICES),
new Pair<>("Repeated Rankings", StatusForRound.INACTIVE_BY_REPEATED_RANKING)
new Pair<>("by Overvotes",
StatusForRound.INACTIVE_BY_OVERVOTE),
new Pair<>("by Skipped Rankings",
StatusForRound.INACTIVE_BY_SKIPPED_RANKING),
new Pair<>("by Repeated Rankings",
StatusForRound.INACTIVE_BY_REPEATED_RANKING),
new Pair<>("Fully Ranked Ballots by Exhausted Choices",
StatusForRound.INACTIVE_BY_EXHAUSTED_CHOICES),
new Pair<>("Partially Ranked Ballots by Exhausted Choices",
StatusForRound.INACTIVE_BY_UNUSED_RANKINGS),
};

for (Pair<String, StatusForRound> statusToPrint : statusesToPrint) {
csvPrinter.print("Inactive Ballots by " + statusToPrint.getKey());
// Skip any "Inactive by X" if no ballots are associated with that bucket
RoundTally lastRound = roundTallies.get(numRounds);
BigDecimal numUndervotesLastRound = lastRound.getBallotStatusTally(statusToPrint.getValue());
if (numUndervotesLastRound.equals(BigDecimal.ZERO)) {
continue;
}

csvPrinter.print("Inactive Ballots " + statusToPrint.getKey());
for (int round = 1; round <= numRounds; round++) {
StatusForRound status = statusToPrint.getValue();
BigDecimal thisRoundInactive = roundTallies.get(round).getBallotStatusTally(status);
Expand All @@ -405,14 +418,9 @@ private void generateSummarySpreadsheet(
}

csvPrinter.print("Inactive Ballots Total");
// Undervotes should not be included in the Inactive Ballots count, even though we treat them
// as such internally. Subtract undervotes (which are static throughout a contest) from the
// inactive ballot totals.
BigDecimal numUndervotes =
roundTallies.get(1).getBallotStatusTally(StatusForRound.INACTIVE_BY_UNDERVOTE);
for (int round = 1; round <= numRounds; round++) {
BigDecimal thisRoundInactive = roundTallies.get(round).inactiveBallotSum();
csvPrinter.print(thisRoundInactive.subtract(numUndervotes));
csvPrinter.print(thisRoundInactive);

// Don't display percentage of inactive ballots
csvPrinter.print("");
Expand Down Expand Up @@ -469,15 +477,15 @@ private void addActionRows(CSVPrinter csvPrinter) throws IOException {

private void addContestSummaryRows(CSVPrinter csvPrinter, RoundTally round1Tally)
throws IOException {
BigDecimal numUndervotes =
round1Tally.getBallotStatusTally(StatusForRound.INACTIVE_BY_UNDERVOTE);
BigDecimal numNoRankings =
round1Tally.getBallotStatusTally(StatusForRound.INACTIVE_BY_NO_RANKINGS);
BigDecimal totalNumberBallots =
round1Tally.activeBallotSum().add(round1Tally.inactiveBallotSum());
csvPrinter.printRecord("Contest Summary");
csvPrinter.printRecord("Number to be Elected", config.getNumberOfWinners());
csvPrinter.printRecord("Number of Candidates", config.getNumCandidates());
csvPrinter.printRecord("Total Number of Ballots", totalNumberBallots);
csvPrinter.printRecord("Number of Undervotes", numUndervotes);
csvPrinter.printRecord("Number of Ballots with No Rankings", numNoRankings);
csvPrinter.println();
}

Expand Down Expand Up @@ -939,8 +947,8 @@ private void generateSummaryJson(
configData.put(slice.toLowerString(), sliceId);
}

BigDecimal firstRoundUndervotes =
roundTallies.get(1).getBallotStatusTally(StatusForRound.INACTIVE_BY_UNDERVOTE);
BigDecimal numNoRankings =
roundTallies.get(1).getBallotStatusTally(StatusForRound.INACTIVE_BY_NO_RANKINGS);
BigDecimal totalNumberBallots =
roundTallies.get(1).activeBallotSum().add(roundTallies.get(1).inactiveBallotSum());
BigDecimal lastRoundThreshold = roundTallies.get(numRounds).getWinningThreshold();
Expand All @@ -950,7 +958,7 @@ private void generateSummaryJson(
summaryData.put("numWinners", config.getNumberOfWinners());
summaryData.put("numCandidates", config.getCandidateNames().size());
summaryData.put("totalNumBallots", totalNumberBallots);
summaryData.put("undervotes", firstRoundUndervotes.toBigInteger());
summaryData.put("undervotes", numNoRankings.toBigInteger());

ArrayList<Object> results = new ArrayList<>();
for (int round = 1; round <= numRounds; round++) {
Expand Down Expand Up @@ -994,8 +1002,9 @@ private Map<String, BigDecimal> getInactiveJsonMap(RoundTally roundTally) {
new Pair[] {
new Pair<>("overvotes", StatusForRound.INACTIVE_BY_OVERVOTE),
new Pair<>("skippedRankings", StatusForRound.INACTIVE_BY_SKIPPED_RANKING),
new Pair<>("exhaustedChoices", StatusForRound.INACTIVE_BY_EXHAUSTED_CHOICES),
new Pair<>("repeatedRankings", StatusForRound.INACTIVE_BY_REPEATED_RANKING)
new Pair<>("repeatedRankings", StatusForRound.INACTIVE_BY_REPEATED_RANKING),
new Pair<>("exhaustedChoicesFullyRanked", StatusForRound.INACTIVE_BY_EXHAUSTED_CHOICES),
new Pair<>("exhaustedChoicesPartiallyRanked", StatusForRound.INACTIVE_BY_UNUSED_RANKINGS),
};
for (Pair<String, StatusForRound> statusToPrint : statusesToPrint) {
inactiveMap.put(
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/network/brightspots/rcv/Tabulator.java
Original file line number Diff line number Diff line change
Expand Up @@ -1002,8 +1002,10 @@ private void recordSelectionForCastVoteRecord(
String outcomeDescription;
switch (statusForRound) {
case ACTIVE -> outcomeDescription = selectedCandidate;
case INACTIVE_BY_UNDERVOTE -> outcomeDescription = "undervote" + additionalLogText;
case INACTIVE_BY_NO_RANKINGS -> outcomeDescription = "no rankings" + additionalLogText;
case INACTIVE_BY_OVERVOTE -> outcomeDescription = "overvote" + additionalLogText;
case INACTIVE_BY_UNUSED_RANKINGS -> outcomeDescription =
"fewer than max rankings" + additionalLogText;
case INACTIVE_BY_SKIPPED_RANKING -> outcomeDescription =
"exhausted by skipped ranking" + additionalLogText;
case INACTIVE_BY_REPEATED_RANKING -> outcomeDescription =
Expand Down Expand Up @@ -1068,7 +1070,7 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
// check for a CVR with no rankings at all
if (cvr.candidateRankings.numRankings() == 0) {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.INACTIVE_BY_UNDERVOTE, "");
cvr, roundTally, null, StatusForRound.INACTIVE_BY_NO_RANKINGS, "");
}

// iterate through the rankings in this cvr from most to least preferred.
Expand Down Expand Up @@ -1181,7 +1183,7 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
if (config.getMaxSkippedRanksAllowed() != Integer.MAX_VALUE
&& maxAllowedRanking - rank > config.getMaxSkippedRanksAllowed()) {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.INACTIVE_BY_UNDERVOTE, "");
cvr, roundTally, null, StatusForRound.INACTIVE_BY_UNUSED_RANKINGS, "");
} else {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.INACTIVE_BY_EXHAUSTED_CHOICES, "");
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/network/brightspots/rcv/TabulatorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,17 @@ private static boolean compareFiles(
Logger.info("Files are equal.");
} else {
Logger.info("Files are different.");
// If the files are different, overwrite the expected file with the actual file
// While this PR is in progress -- delete before merging
// try {
// Files.copy(
// Paths.get(actualOutputPath),
// Paths.get(expectedPath),
// java.nio.file.StandardCopyOption.REPLACE_EXISTING);
// } catch (IOException e) {
// Logger.severe("Failed to copy file: %s", e.getMessage());
// fail();
// }
fail();
}
return didCompare;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Contest Summary
Number to be Elected,1
Number of Candidates,36
Total Number of Ballots,985
Number of Undervotes,0
Number of Ballots with 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
BETSY HODGES,359,36.44%,0,359,36.44%,0,359,36.44%,0,359,36.48%,0,359,36.48%,0,359,36.48%,0,359,36.48%,0,359,36.48%,0,359,36.48%,0,359,36.52%,0,359,36.52%,0,359,36.59%,0,359,36.59%,0,359,36.59%,0,359,36.59%,0,359,36.59%,0,359,36.59%,0,359,36.59%,0,359,36.59%,0,359,36.63%,2,361,36.87%,0,361,36.87%,0,361,36.87%,0,361,36.87%,0,361,36.87%,0,361,36.87%,0,361,37.13%,0,361,37.13%,1,362,37.35%,2,364,37.6%,2,366,38.4%,23,389,41.33%,69,458,57.39%,0
Expand Down Expand Up @@ -55,8 +55,4 @@ 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 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 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
Loading

0 comments on commit f293caa

Please sign in to comment.