Skip to content

Commit

Permalink
update tests and fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
epacuit committed Apr 15, 2024
1 parent 4756488 commit 0df461c
Show file tree
Hide file tree
Showing 25 changed files with 2,509 additions and 45 deletions.
Binary file modified .DS_Store
Binary file not shown.
24 changes: 24 additions & 0 deletions .github/workflows/draft-pdf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
on: [push]

jobs:
paper:
runs-on: ubuntu-latest
name: Paper Draft
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build draft PDF
uses: openjournals/openjournals-draft-action@master
with:
journal: joss
# This should be the path to the paper within your repo.
paper-path: paper.md
- name: Upload
uses: actions/upload-artifact@v1
with:
name: paper
# This is the output path where Pandoc will write the compiled
# PDF. Note, this should be the same directory as the input
# paper.md
path: paper.pdf

68 changes: 68 additions & 0 deletions paper/paper.bib
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@misc{Boehmer2024,
title={Guide to Numerical Experiments on Elections in Computational Social Choice},
author={Boehmer, N. and Faliszewski, P. and Janeczko, \L. and Kaczmarczyk, A. and Lisowski, G. and Pierczy\'{n}ski, G. and Rey, S. and Stolicki, D. and Szufa, S. and W\k{a}s, T.},
year={2024},
eprint={2402.11765},
archivePrefix={arXiv},
primaryClass={cs.GT}
}

@book{Brandt2016,
title = {Handbook of Computational Social Choice},
Publisher = {Cambridge University Press},
Editor = {Felix Brandt and Vincent Conitzer and Ulle Endriss and J\'{e}r\^{o}me Lang and Ariel D. Procaccia},
Year = 2016}
@inproceedings{HKP2024,
title={Learning to Manipulate under Limited Information},
author={Holliday, Wesley H. and Kristoffersen, Alexander and Pacuit, Eric},
booktitle={1st Workshop on Social Choice and Learning Algorithms (SCaLA 2024) at the 23rd International Conference on Autonomous Agents and Multiagent Systems},
editor={B. Armstrong and R. Fairstein and N. Mattei and Z. Terzopoulou},
pages={},
year={2024},
address={Auckland, New Zealand},
month={May 6-7}
}

@inproceedings{Mattei2013,
Author = {Nicholas Mattei and Toby Walsh},
Booktitle = {Proceedings of Third International Conference on Algorithmic Decision Theory (ADT 2013)},
Pages = {259-270},
Publisher = {Springer},
Title = {PrefLib: {A} Library of Preference Data},
Year = {2013},
doi = {10.1007/978-3-642-41575-3_20},
}
@misc{Simbera2021,
author = {Jan \v{S}imbera},
title = {Votelib: Evaluation of voting systems in Python},
year = {2021},
publisher = {GitHub},
journal = {GitHub repository},
url = {https://github.com/simberaj/votelib}
}

@misc{MGGG2024,
author = {MGGG Redistricting Lab},
title = {VoteKit},
year = {2024},
publisher = {GitHub},
journal = {GitHub repository},
url = {https://github.com/mggg/VoteKit}
}
@incollection{Zwicker2016,
Address = {New York},
Author = {William S. Zwicker},
Booktitle = {Handbook of Computational Social Choice},
Pages = {23-56},
Publisher = {Cambridge University Press},
Title = {Introduction to the Theory of Voting},
doi = {10.1017/cbo9781107446984.003},
Editor = {Felix Brandt and Vincent Conitzer and Ulle Endriss and J\'{e}r\^{o}me Lang and Ariel D. Procaccia},
Year = 2016}
82 changes: 82 additions & 0 deletions paper/paper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: 'pref_voting: The Preferential Voting Tools package for Python'
tags:
- Python
- voting methods
- voting paradoxes
- social choice theory
- utility functions
authors:
- name: Wesley H. Holliday
orcid: 0000-0001-6054-9052
equal-contrib: true
affiliation: 1 # (Multiple affiliations must be quoted)
- name: Eric Pacuit
orcid: 0000-0002-0751-9011
equal-contrib: true # (This is how you can denote equal contributions between multiple authors)
affiliation: 2
affiliations:
- name: Department of Philosophy, University of California, Berkeley, USA
index: 1
- name: Department of Philosophy, University of Maryland, USA
index: 2
date: 11 January 2024
bibliography: paper.bib

# Optional fields if submitting to a AAS journal too, see this blog post:
# https://blog.joss.theoj.org/2018/12/a-new-collaboration-with-aas-publishing
aas-doi:
aas-journal:
---

# Summary

Preferential Voting Tools (`pref_voting`) is a Python package designed for research in and applications of voting theory. The basic problem of voting theory [`@Zwicker2016`] concerns how to combine "inputs" from many individual voters into a single social "output". For example, a common type of input to elicit from each voter is a *ranking* of some set of candidates according to the voter's preferences, while a common type of social output is the selection of a *winning candidate* (or perhaps a set of candidates tied for winning). A *voting method* is then a function that takes in a ranking from each individual and outputs a winning candidate (or set of tied candidates). Other functions may instead output a social ranking of the candidates, or a probability distribution over the candidates, etc., and other input types are also possible, such as grades that voters assign to candidates, or voter utility functions, etc. Faced with any of these types of aggregation functions, voting theorists study a function from several perspectives, including the general principles or "axioms" it satisfies, its statistical behavior according to various probability models for generating voter inputs, its computational complexity, and more. These studies are greatly facilitated by the implementation of algorithms for computing aggregation functions and checking their properties, which are provided in `pref_voting`.

# Statement of need

Research in the burgeoning field of *computational social choice* [`@Brandt2016`] often applies computer-assisted methods to the study of voting methods and other aggregation functions. The aim of `pref_voting` is to contribute to a comprehensive set of tools for such research. Other packages in this area include `abcvoting` [`@Lackner2023`], which focuses on approval-based committee voting, `prefsampling` [`@Boehmer2024`], which implements probability models for generating voter rankings, and `prelibtools` [`@Mattei2013`], which provides tools for working with preference data from [PrefLib.org](https://PrefLib.org). The `pref_voting` package provides functionality not available in those previous packages, as desribed below, while also interfacing with other packages. Like `pref_voting`, the `VoteLib` [`@Simbera2021`] and `VoteKit` [`@MGGG2024`] packages provides implementations of a number of voting methods; and like `prefsampling`, `VoteKit` provides tools for generating elections. However, neither package includes all the voting methods and functionality in `pref_voting`, as described below. The `pref_voting` package has already been used in research in computational social choice [`@HKP2024`]. The package can also be used by election administrators to determine election outcomes, as it is used in the backend of the [Stable Voting website](https://stablevoting.org).

# Functionality

## Elections

The `pref_voting` package includes classes for the most important representations of elections, or types of `edata`, used in voting theory:

- `Profile`: each voter has a linear order of the candidates;
- `ProfileWithTies`: each voter has a ranking of the candidates that may contain ties and may omit some candidates;
- `GradeProfile`: voters assign to candidates grades from some finite list of grades;
- `UtilityProfile`: each voter has a cardinal utility function on the set of candidates;
- `SpatialProfile`: each voter and each candidate is placed in a multi-dimensional space;
- `MajorityGraph`: an edge from one candidate to another represents that a majority of voters prefer the first to the second;
- `MarginGraph`: a weighted version of a `MajorityGraph`, where the weight on an edge represents the margin of victory (or other measure of strength of majority preference).

The package also includes methods for transforming one type of representation into another, e.g., turning a `SpatialProfile` into a `UtilityProfile` given a choice of how spatial positions of voters and candidates determine voter utility functions, or turning a `MarginGraph` into a `ProfileWithTies` that induces that `MarginGraph` by solving an associated linear program, and so on. Other methods are included for standard voting-theoretic tests and operations, e.g., testing for the existence of Condorcet winners/losers, removing candidates, and so on. Methods are also included to import from and export to the PrefLib preference data format, the ABIF format, and other data formats.

## Generating elections

For sampling profiles according to standard probability models, `pref_voting` interfaces with the `prefsampling` package. In addition, `pref_voting` contains functions for sampling other types of `edata` listed above, as well as functions for enumerating such objects up to certain equivalence relations.

## Aggregation methods

Several classes of aggregation methods are built into `pref_voting`:

- `VotingMethod`: given `edata`, outputs a sorted list of candidates, representing tied winners;
- `ProbVotingMethod`: given `edata`, outputs a dictionary whose keys are candidates and whose values are probabilities;
- `SocialWelfareFunctions`: given `edata`, outputs a `Ranking` of the candidates.

Dozens of aggregation methods are implemented in `pref_voting` and organized into standard classes identified in voting theory, e.g., positional scoring rules, iterative methods, margin-based methods (weighted tournament methods), cardinal methods, etc.

## Axioms

The `pref_voting` package also contains an `Axiom` class for functions that check whether an aggregation method satisfies a given axiom with respect to some `edata`. Each axiom comes with a `has_violation` function that checks whether there is at least one violation of the axiom by the aggreation method for the given `edata`, as well as a `find_all_violations` function that enumerates all such violations with the relevant witnessing data. Axioms are divided into several well-known classes from voting theory, e.g., dominance axioms, monotonicity axioms, variable voter axioms, variable candidate axioms, etc.

## Analysis

Finally, `pref_voting` comes with functions that facilitate the analysis of aggregation methods, such as producing data on the frequency of axiom violations in elections generated using one of the available probability models.

# Acknowledgements

We thank Jobst Heitzig and Dominik Peters for helpful contributions and Zoi Terzopoulou for helpful feature requests.

# References
13 changes: 11 additions & 2 deletions pref_voting/io/readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def abif_to_profile_with_ties(filename, cand_type=None):
def preflib_to_profile(
instance_or_preflib_file,
include_cmap=False,
use_cand_names=False,
as_linear_profile=False):

"""
Expand All @@ -152,6 +153,8 @@ def preflib_to_profile(
Args:
preflib_file (str): the path to the file
include_cmap (bool): if True, then include the candidate map. Defaults to False.
use_cand_names (bool): if True, then use the candidate map as the candidate names. Defaults to False.
as_linear_profile (bool): if True, then return a Profile object. Defaults to False. If False, then return a ProfileWithTies object.
Returns:
Profile or ProfileWithTies: the profile read from the file
Expand Down Expand Up @@ -183,9 +186,15 @@ def preflib_to_profile(
rank = dict()
for r,cs in enumerate(order):
for c in cs:
rank[c] = r + 1
if not use_cand_names:
rank[c] = r + 1
else:
rank[instance.alternatives_name[c]] = r + 1
if include_cmap:
cmap[c] = instance.alternatives_name[c]
if use_cand_names:
cmap[instance.alternatives_name[c]] = instance.alternatives_name[c]
else:
cmap[c] = instance.alternatives_name[c]

rankings.append(rank)
rcounts.append(instance.multiplicity[order])
Expand Down
7 changes: 5 additions & 2 deletions pref_voting/io/writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ def to_preflib_instance(profile):

instance = OrdinalInstance()
vote_map = dict()
cand_to_cidx = {c: i for i, c in enumerate(profile.candidates)}
cmap = {i: profile.cmap[c] for c, i in cand_to_cidx.items()}
for r,c in zip(*profile.rankings_counts):
ranking = r.to_indiff_list() if type(r) == Ranking else tuple([(c,) for c in r])
ranking = tuple([tuple([cand_to_cidx[_c] for _c in indiff]) for indiff in r.to_indiff_list()]) if type(r) == Ranking else tuple([(c,) for c in r])
if ranking in vote_map.keys():
vote_map[ranking] += c
else:
vote_map[ranking] = c
instance.append_vote_map(vote_map)
instance.append_vote_map(vote_map)
instance.alternatives_name = cmap
return instance

def write_preflib(profile, filename):
Expand Down
3 changes: 2 additions & 1 deletion pref_voting/mappings.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ def _indifference_classes(self, items, use_extended=False):
for x in items:
if x not in processed_items:
indiff = [y for y in items if compare_fnc(x, y) == 0]
indiff_classes.append(indiff)
if len(indiff) > 0:
indiff_classes.append(indiff)
for y in indiff:
processed_items.add(y)
return indiff_classes
Expand Down
Loading

0 comments on commit 0df461c

Please sign in to comment.