Skip to content

Commit

Permalink
add missing dois, fix bug in baldwin
Browse files Browse the repository at this point in the history
  • Loading branch information
epacuit committed Apr 28, 2024
1 parent 6c15557 commit 76354f5
Show file tree
Hide file tree
Showing 15 changed files with 243 additions and 149 deletions.
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Wes Holliday and Eric Pacuit'

# The full version, including alpha/beta/rc tags
release = '1.1.0'
release = '1.1.2'


# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/source/margin_based_methods.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Margin Methods
=======================================

Suppose that $\mathbf{P}$ is a profile. We write $\mathcal{M}(\mathbf{P})$ for the margin graph of $\mathbf{P}$.
Suppose that $\mathbf{P}$ is a profile. We write $\mathcal{M}(\mathbf{P})$ for the margin graph of $\mathbf{P}$.

A voting method $F$ is **margin-based** if it satisfies the following invariance property: For all $\mathbf{P}, \mathbf{P}'$, if $\mathcal{M}(\mathbf{P})= \mathcal{M}(\mathbf{P}')$, then $F(\mathbf{P}) = F(\mathbf{P}')$.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/profiles_with_ties.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Use the `ProfileWithTies` class to create a profile in which voters may submit s

```{eval-rst}
.. info::
.. note::
The ``Ranking`` class used to the represent the ballots in a ``ProfileWithTies`` is described on the [ballots](ballots) page.
```
Expand Down
12 changes: 8 additions & 4 deletions paper/paper.bib
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ @book{Arrow1963
Edition = {2nd},
Publisher = {John Wiley \& Sons, Inc.},
Title = {Social Choice and Individual Values},
Year = {1963}}
Year = {1963},
doi = {https://doi.org/10.12987/9780300186987}}
@incollection{Aspremont2002,
Address = {Amsterdam},
Expand Down Expand Up @@ -53,7 +54,7 @@ @incollection{Brams2002
Publisher = {North-Holland},
Title = {Voting Procedures},
Volume = {1},
doi = {10.1016/s1574-0110(02)80008-x},
doi = {https://doi.org/10.1016/S1574-0110(02)80008-X},
Year = {2002}}
@book{Brams2007,
Expand All @@ -62,7 +63,8 @@ @book{Brams2007
Publisher = {Springer},
Title = {Approval Voting},
address = {New York},
Year = {2007}}
Year = {2007},
doi = {https://doi.org/10.1007/978-0-387-49896-6}}

@book{Balinski2010,
Author = {Michel Balinski and Rida Laraki},
Expand Down Expand Up @@ -140,7 +142,8 @@ @article{Gibbard1977
Pages = {665-681},
Title = {Manipulation of Schemes that Mix Voting with Chance},
Volume = {45},
Year = {1977}}
Year = {1977},
doi = {https://doi.org/10.2307/1911681}}
@article{Green-Armytage2016,
title={Statistical evaluation of voting rules},
Expand Down Expand Up @@ -218,6 +221,7 @@ @book{Nurmi1987
Publisher = {D. Reidel},
Title = {Comparing Voting Systems},
Year = {1987},
doi = {https://doi.org/10.1007/978-94-009-3985-1}
}
@book{Nurmi1999,
Expand Down
2 changes: 1 addition & 1 deletion pref_voting/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.1.0'
__version__ = '1.1.2'
32 changes: 13 additions & 19 deletions pref_voting/iterative_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,25 +919,22 @@ def baldwin(profile, curr_cands = None):
prof.display()
baldwin.display(prof)
"""
all_num_cands = profile.num_cands
all_num_cands = profile.num_cands
candidates = profile.candidates if curr_cands is None else curr_cands

rcounts = profile._rcounts # get all the ranking data
rankings = profile._rankings if curr_cands is None else _find_updated_profile(profile._rankings, np.array([c for c in profile.candidates if c not in curr_cands]), all_num_cands)

cands_to_ignore = np.empty(0) if curr_cands is None else np.array([c for c in profile.candidates if c not in curr_cands])

rankings = profile._rankings if curr_cands is None else _find_updated_profile(profile._rankings, np.array([c for c in profile.candidates if c not in candidates]), all_num_cands)
num_cands = len(candidates)
cands_to_ignore = np.empty(0)
borda_scores = {c: _borda_score(rankings, rcounts, len(candidates), c) for c in candidates}

min_borda_score = min(list(borda_scores.values()))

last_place_borda_scores = [c for c in candidates
if c in borda_scores.keys() and borda_scores[c] == min_borda_score]

cands_to_ignore = np.concatenate((cands_to_ignore, last_place_borda_scores), axis=None)

winners = list()
if cands_to_ignore.shape[0] == all_num_cands: # all candidates have lowest Borda score
if cands_to_ignore.shape[0] == num_cands: # all candidates have lowest Borda score
winners = sorted(last_place_borda_scores)
else: # remove the candidates with lowest Borda score
num_cands = len(candidates)
Expand All @@ -951,7 +948,7 @@ def baldwin(profile, curr_cands = None):

cands_to_ignore = np.concatenate((cands_to_ignore, last_place_borda_scores), axis=None)

if cands_to_ignore.shape[0] == all_num_cands: # removed all remaining candidates
if cands_to_ignore.shape[0] == num_cands: # removed all remaining candidates
winners = sorted(last_place_borda_scores)
elif num_cands - cands_to_ignore.shape[0] == 1: # only one candidate remains
winners = sorted([c for c in candidates if c not in cands_to_ignore])
Expand Down Expand Up @@ -994,21 +991,18 @@ def baldwin_tb(profile, curr_cands = None, tie_breaker=None):
# the tie_breaker is any linear order (i.e., list) of the candidates
tb = tie_breaker if tie_breaker is not None else list(range(profile.num_cands))

all_num_cands = profile.num_cands
all_num_cands = profile.num_cands
candidates = profile.candidates if curr_cands is None else curr_cands

rcounts = profile._rcounts # get all the ranking data
rankings = profile._rankings if curr_cands is None else _find_updated_profile(profile._rankings, np.array([c for c in profile.candidates if c not in curr_cands]), all_num_cands)

cands_to_ignore = np.empty(0) if curr_cands is None else np.array([c for c in profile.candidates if c not in curr_cands])

borda_scores = {c: _borda_score(rankings, rcounts, len(candidates), c) for c in candidates}
rankings = profile._rankings if curr_cands is None else _find_updated_profile(profile._rankings, np.array([c for c in profile.candidates if c not in candidates]), all_num_cands)
num_cands = len(candidates)
cands_to_ignore = np.empty(0)
borda_scores = {c: _borda_score(rankings, rcounts, num_cands, c) for c in candidates}

min_borda_score = min(list(borda_scores.values()))

last_place_borda_scores = [c for c in candidates
if c in borda_scores.keys() and borda_scores[c] == min_borda_score]

cand_to_remove = last_place_borda_scores[0]
for c in last_place_borda_scores[1:]:
if tb.index(c) < tb.index(cand_to_remove):
Expand Down Expand Up @@ -1037,7 +1031,7 @@ def baldwin_tb(profile, curr_cands = None, tie_breaker=None):

cands_to_ignore = np.concatenate((cands_to_ignore, np.array([cand_to_remove])), axis=None)

if cands_to_ignore.shape[0] == all_num_cands: # removed all remaining candidates
if cands_to_ignore.shape[0] == num_cands: # removed all remaining candidates
winners = sorted(last_place_borda_scores)
elif num_cands - cands_to_ignore.shape[0] == 1: # only one candidate remains
winners = sorted([c for c in candidates if c not in cands_to_ignore])
Expand Down
56 changes: 19 additions & 37 deletions pref_voting/margin_based_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,34 +613,9 @@ def _ranked_pairs_from_stacks(edata, curr_cands = None):
A sorted list of candidates.
.. seealso::
:meth:`pref_voting.margin_based_methods.ranked_pairs`, :meth:`pref_voting.margin_based_methods.is_stack`
:Example:
.. plot:: margin_graphs_examples/mg_ex_bp_rp.py
:context: reset
:include-source: True
.. code-block::
from pref_voting.margin_based_methods import ranked_pairs, ranked_pairs_from_stacks
ranked_pairs.display(mg)
ranked_pairs_from_stacks.display(mg)
.. exec_code::
:hide_code:
from pref_voting.weighted_majority_graphs import MarginGraph
from pref_voting.margin_based_methods import ranked_pairs, ranked_pairs_from_stacks
mg = MarginGraph([0, 1, 2, 3], [(0, 2, 3), (1, 0, 5), (2, 1, 5), (2, 3, 1), (3, 0, 3), (3, 1, 1)])
:meth:`pref_voting.margin_based_methods.is_stack`
ranked_pairs.display(mg)
ranked_pairs_from_stacks.display(mg)
"""

Expand All @@ -658,6 +633,14 @@ def _ranked_pairs_basic(
curr_cands = None,
strength_function = None):
"""An implementation of Ranked Pairs that uses a basic algorithm.
Args:
edata (Profile, ProfileWithTies, MarginGraph): Any election data that has a `margin` method.
curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
Returns:
A sorted list of candidates.
"""
candidates = edata.candidates if curr_cands is None else curr_cands
cidx_to_cand = {cidx: c for cidx, c in enumerate(candidates)}
Expand Down Expand Up @@ -752,7 +735,10 @@ def ranked_pairs(


@vm(name="Ranked Pairs")
def ranked_pairs_with_test(edata, curr_cands = None, strength_function = None):
def ranked_pairs_with_test(
edata,
curr_cands=None,
strength_function=None):
"""Find the Ranked Pairs winners, but include a test to determined if it will take too long to compute the Ranked Pairs winners. If the calculation of the winners will take too long, return None.
.. important::
Expand Down Expand Up @@ -905,18 +891,17 @@ def ranked_pairs_tb(
.. seealso::
:meth:`pref_voting.margin_based_methods.ranked_pairs`, :meth:`pref_voting.margin_based_methods.ranked_pairs_with_test`, :meth:`pref_voting.margin_based_methods.ranked_pairs_from_stacks`
:meth:`pref_voting.margin_based_methods.ranked_pairs`, :meth:`pref_voting.margin_based_methods.ranked_pairs_with_test`
.. exec_code::
from pref_voting.profiles import Profile
from pref_voting.margin_based_methods import ranked_pairs_from_stacks, ranked_pairs_tb, ranked_pairs_zt
from pref_voting.margin_based_methods import ranked_pairs_tb, ranked_pairs_zt
prof = Profile([[2, 3, 1, 0], [0, 3, 1, 2], [1, 3, 2, 0], [2, 1, 3, 0]], [1, 1, 1, 1])
prof.display()
ranked_pairs_from_stacks.display(prof)
ranked_pairs_tb.display(prof)
ranked_pairs_tb.display(prof, tie_breaker = [3, 2, 1, 0])
ranked_pairs_zt.display(prof)
Expand Down Expand Up @@ -972,18 +957,17 @@ def ranked_pairs_zt(
.. seealso::
:meth:`pref_voting.margin_based_methods.ranked_pairs`, :meth:`pref_voting.margin_based_methods.ranked_pairs_with_test`, :meth:`pref_voting.margin_based_methods.ranked_pairs_from_stacks`
:meth:`pref_voting.margin_based_methods.ranked_pairs`, :meth:`pref_voting.margin_based_methods.ranked_pairs_with_test`
.. exec_code::
from pref_voting.profiles import Profile
from pref_voting.margin_based_methods import ranked_pairs_from_stacks, ranked_pairs_tb, ranked_pairs_zt
from pref_voting.margin_based_methods import ranked_pairs_tb, ranked_pairs_zt
prof = Profile([[2, 3, 1, 0], [0, 3, 1, 2], [1, 3, 2, 0], [2, 1, 3, 0]], [1, 1, 1, 1])
prof.display()
ranked_pairs_from_stacks.display(prof)
ranked_pairs_tb.display(prof)
ranked_pairs_tb.display(prof, tie_breaker = [3, 2, 1, 0])
ranked_pairs_zt.display(prof)
Expand Down Expand Up @@ -1327,7 +1311,7 @@ def simple_stable_voting(
.. seealso::
:meth:`pref_voting.margin_based_methods.simple_stable_voting_faster`, :meth:`pref_voting.margin_based_methods.stable_voting`
:meth:`pref_voting.margin_based_methods.stable_voting`
:Example:
Expand Down Expand Up @@ -1478,7 +1462,7 @@ def stable_voting(
.. seealso::
:meth:`pref_voting.margin_based_methods.simple_stable_faster`, :meth:`pref_voting.margin_based_methods.simple_stable_voting`
:meth:`pref_voting.margin_based_methods.simple_stable_voting`
:Example:
Expand Down Expand Up @@ -1634,9 +1618,7 @@ def distance_to_margin_graph(edata, rel, exp = 1, curr_cands = None):
#river,
#river_with_test,
simple_stable_voting,
#simple_stable_voting_faster,
stable_voting,
#stable_voting_faster,
essential,
weighted_covering,
loss_trimmer
Expand Down
4 changes: 4 additions & 0 deletions pref_voting/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ def profile_with_ties():
{0:1, 1:1, 2:2},
{0:2, 1:2, 2:1}],
rcounts=[2, 1])

@pytest.fixture
def profile_single_voter():
return Profile([[0, 1, 2, 3]])
Loading

0 comments on commit 76354f5

Please sign in to comment.