prefvote

PrefVote

PrefVote is a project to promote preference voting. It supports several ranked-choice voting methods and algorithms. It was originally derived from the Vote::STV software written by Ian Kluft in Perl in 1999, with periodic maintenance over the years.

Since the project’s original language Perl has strengths in prototyping, that’s the reference implementation in this project for multiple language implementations. With translations to multiple programming languages, the library is designed with a common test suite among the different implementations to verify proper functioning.

About preference voting algorithms

The original Vote::STV software implemented the single transferable vote algorithm, which is a subset of ranked-choice voting, also called preference voting. PrefVote has expanded into a library of multiple voting method implementations all based on ranked choice.

STV was the first implemented voting method in PrefVote since it was the original implementation as Vote::STV back to 1998. But STV has largely fallen out of favor because studies of voting methods found it lacking on some desirable characteristics. STV was retained while modernizing the code to develop testing infrastructure.

No voting method can be perfect, due to a long list of desirable properties, some of which turn out to be in conflict. Given that there is no perfect voting method, it’s important to have agreement among a community on which method it uses. Methods which meet Condorcet requirements make comparisons between each pair of candidates and, if one exists, always pick a winner who beats all other candidates in pairwise comparisons. Condorcet methods differ in how to handle cases where there isn’t a clear Condorcet winner.

The second voting method implemented in PrefVote was the Schulze algorithm (see full definition paper). The method was designed by Marcus Schulze in 1997 to compute a graph out of voter preferences among candidates and pick the ones preferred over all others. An ordering of all the candidates can be computed over multiple rounds after removing the previous round’s winner(s).

Next was Ranked Pairs. It was designed by Nicolaus Tideman in 1987. It tallies ranked choice ballots into pairwise comparisons among all choices/candidates. The pairs are sorted by margin of victory from largest margin down to ties at zero. Pairs are “locked” in for consideration of the final order of candidates if they do not conflict with locked pairs with larger margins. Candidates are then ordered starting who beats all other candidates, then each who beat all remaining candidates.

After the reference implementation in Perl, next up for language implementations will be Rust.

Input file format

The original and primary input file format of PrefVote is YAML with a predefined data structure. There is documentation for the data format. Also, when PrefVote is used as a library any data can be submitted by calling functions directly.

In support of a proposed Open Source standard for preference voting systems, PrefVote also supports the Condorcet Election Format (CEF), or CEF. There is documentation for PrefVote’s support for CEF input files.

Tie-breaking modifications to algorithms

PrefVote modifies all the algorithms in one way. It adds a tie-breaking factor using the average ballot-position ranking of a choice/candidate. It started as experimentation with intuition that the average ranking of a choice was indicative of the will of the voters. Though it wouldn’t be acceptable as a primary factor because averages don’t have quantitative data - and quantitative data is paramount to meeting the expectation that the candidate with the most votes must be the winner. Test runs show that it approximates Condorcet results fairly well and converges with Condorcet by around 100 random ballots. It didn’t even need to be that close to Condorcet to convey meaning about voter’s preferences. So this tie-breaking method restores ballot-position ordering information which Condorcet lacks.

PrefVote’s Core module from which all the voting methods inherit common code is not a voting method itself. But in performing the counting of average choice ranking (ACR) for other methods to use for tie-breaking, it contains results which can be displayed for testing purposes. Since it only uses average ranking, it really must not be used for actual voting. There is a principle in voting that every vote counts. That means quantitative factors must be the primary ordering for results. But ACR turns out to be surprisingly well-suited to be a second sorting factor for tie-breaking.

Example voting result from test suite

This is an example result from Single Transferable Vote (STV), Schulze and Ranked Pairs using the same file in the test suite with each algorithm. 250 ballots were randomly generated. So there’s no actual meaning to the result except testing the software.

Notes about all the following examples:

Single Transferable Vote (STV) results from the example data

Results: Test Vote 004

1 seat available ∣ 250 ballots

Abbreviation Name/description Result
FACTIOUS factious/divisive 1/selected
EVIL evil villain 2/placed
CHAOTIC chaotic unpredictable 3/eliminated
ABNORMAL abnormal and antisocial 4/eliminated
BORING boring as anything 5/eliminated
DYSFUNCTIONAL dysfunctional incompetent 6/eliminated
Round # Quota FACTIOUS EVIL CHAOTIC ABNORMAL BORING DYSFUNCTIONAL
1 125 68 54 36 31 33 28 ❌
2 124.5 75 58 45 36 35 ❌
3 122 78 69 51 46 ❌
4 118.5 90 81 66 ❌
5 114 118 ✅ 110
6 56.50847 113.01695 ✅

Notes about the STV example:

Schulze method results from the example data

Results: Test Vote 004

1 seat available ∣ 250 ballots

Abbreviation Name/description Result
FACTIOUS factious/divisive 1/selected
EVIL evil villain 2/placed
CHAOTIC chaotic unpredictable 3/placed
DYSFUNCTIONAL dysfunctional incompetent 4/placed
ABNORMAL abnormal and antisocial 5/placed
BORING boring as anything 6/placed

Margin-of-victory matrix

FACTIOUS EVIL CHAOTIC DYSFUNCTIONAL ABNORMAL BORING
FACTIOUS 🛇 7 ✅ 57 ✅ 67 ✅ 74 ✅ 79 ✅
EVIL -7 ❌ 🛇 48 ✅ 68 ✅ 68 ✅ 64 ✅
CHAOTIC -57 ❌ -48 ❌ 🛇 16 ✅ 5 ✅ 15 ✅
DYSFUNCTIONAL -67 ❌ -68 ❌ -16 ❌ 🛇 13 ✅ 5 ✅
ABNORMAL -74 ❌ -68 ❌ -5 ❌ -13 ❌ 🛇 11 ✅
BORING -79 ❌ -64 ❌ -15 ❌ -5 ❌ -11 ❌ 🛇

Notes about the Schulze example:

Ranked Pairs voting results from the example data

Results: Test Vote 004

1 seat available ∣ 250 ballots

Abbreviation Name/description Result
FACTIOUS factious/divisive 1/selected
EVIL evil villain 2/placed
CHAOTIC chaotic unpredictable 3/placed
DYSFUNCTIONAL dysfunctional incompetent 4/placed
ABNORMAL abnormal and antisocial 5/placed
BORING boring as anything 6/placed

Margin-of-victory matrix

FACTIOUS EVIL CHAOTIC DYSFUNCTIONAL ABNORMAL BORING
FACTIOUS 🛇 7 ✅🔒 57 ✅🔒 67 ✅🔒 74 ✅🔒 79 ✅🔒
EVIL -7 ❌ 🛇 48 ✅🔒 68 ✅🔒 68 ✅🔒 64 ✅🔒
CHAOTIC -57 ❌ -48 ❌ 🛇 16 ✅🔒 5 ✅🔒 15 ✅🔒
DYSFUNCTIONAL -67 ❌ -68 ❌ -16 ❌ 🛇 13 ✅🔒 5 ✅🔒
ABNORMAL -74 ❌ -68 ❌ -5 ❌ -13 ❌ 🛇 11 ✅🔒
BORING -79 ❌ -64 ❌ -15 ❌ -5 ❌ -11 ❌ 🛇

Notes about the Ranked Pairs example: