Skip to content

Commit

Permalink
Add SetChoices
Browse files Browse the repository at this point in the history
  • Loading branch information
janos committed Dec 8, 2022
1 parent 68c2772 commit 44f33e0
Show file tree
Hide file tree
Showing 5 changed files with 648 additions and 1 deletion.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ The Schulze method is a [Condorcet method](https://en.wikipedia.org/wiki/Condorc

White paper [Markus Schulze, "The Schulze Method of Voting"](https://arxiv.org/pdf/1804.02973.pdf).

## Vote and Compute
## Usage

`Vote` and `Compute` are the core functions in the library. They implement the Schulze method on the most compact required representation of votes, here called preferences that is properly initialized with the `NewPreferences` function. `Vote` writes the `Ballot` values to the provided preferences and `Compute` returns the ranked list of choices from the preferences, with the first one as the winner. In case that there are multiple choices with the same score, the returned `tie` boolean flag is true.

The act of voting represents calling the `Vote` function with a `Ballot` map where keys in the map are choices and values are their rankings. Lowest number represents the highest rank. Not all choices have to be ranked and multiple choices can have the same rank. Ranks do not have to be in consecutive order.

`Unvote` function allows to update the pairwise preferences in a way to cancel the previously added `Ballot` to preferences using `Vote` function. It is useful to change the vote without the need to re-vote all ballots.

`SetChoices` allows to update the pairwise preferences if the choices has to be changed during voting. New choices can be added, existing choices can be removed or rearranged. New choices will have no preferences against existing choices, neither existing choices will have preferences against the new choices.

## Voting

`Voting` holds number of votes for every pair of choices. It is a convenient construct to use when the preferences slice does not have to be exposed, and should be kept safe from accidental mutation. Methods on the Voting type are not safe for concurrent calls.
Expand Down
13 changes: 13 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2022, Janoš Guljaš <[email protected]>
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package schulze

// Preferences reruns a copy of preferences for testing purposes.
func (v *Voting[C]) Preferences() []int {
p := make([]int, len(v.preferences))
copy(p, v.preferences)
return p
}
24 changes: 24 additions & 0 deletions schulze.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,30 @@ func Unvote[C comparable](preferences []int, choices []C, b Ballot[C]) error {
return vote(preferences, choices, b, -1) // subtract one to decrement every pairwise preference
}

// SetChoices updates the preferences passed as the first argument by changing
// its values to accommodate the changes to the choices. It is required to
// pass the exact choices as the second parameter and complete updated choices
// as the third argument.
func SetChoices[C comparable](preferences []int, current, updated []C) []int {
currentLength := len(current)
updatedLength := len(updated)
updatedPreferences := NewPreferences(updatedLength)
for iUpdated := 0; iUpdated < updatedLength; iUpdated++ {
iCurrent := int(getChoiceIndex(current, updated[iUpdated]))
for j := 0; j < updatedLength; j++ {
if iUpdated < currentLength && updated[iUpdated] == current[iUpdated] && j < currentLength && updated[j] == current[j] {
updatedPreferences[iUpdated*updatedLength+j] = preferences[iUpdated*currentLength+j]
} else {
jCurrent := int(getChoiceIndex(current, updated[j]))
if iCurrent >= 0 && jCurrent >= 0 {
updatedPreferences[iUpdated*updatedLength+j] = preferences[iCurrent*currentLength+jCurrent]
}
}
}
}
return updatedPreferences
}

// vote updates the preferences with ballot values according to the passed
// choices. The weight is the value which is added to the preferences slice
// values for pairwise wins. If the weight is 1, the ballot is added, and if it
Expand Down
Loading

0 comments on commit 44f33e0

Please sign in to comment.