Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
{
"julia.environmentPath": "/Users/epacuit/Dropbox/code/voting-scripts/pref_voting"
}
16 changes: 15 additions & 1 deletion docs/source/generate_weighted_majority_graphs.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Generate Weighted Majority Graphs
Generate (Weighted) Majority Graphs
=======================================

## Generate Linearly Edge-Ordered Tournaments
Expand Down Expand Up @@ -32,13 +32,27 @@ Generate Weighted Majority Graphs

## Enumerate Canonical Objects

```{eval-rst}
.. autofunction:: pref_voting.generate_weighted_majority_graphs.enumerate_tournaments

```

```{eval-rst}
.. autofunction:: pref_voting.generate_weighted_majority_graphs.enumerate_canonical_edge_ordered_tournaments

```

```{eval-rst}
.. autofunction:: pref_voting.generate_weighted_majority_graphs.enumerate_canonical_weakly_edge_ordered_tournaments

```

```{eval-rst}
.. autofunction:: pref_voting.generate_weighted_majority_graphs.enumerate_uniquely_weighted_margin_graphs

```

```{eval-rst}
.. autofunction:: pref_voting.generate_weighted_majority_graphs.enumerate_margin_graphs

```
80 changes: 80 additions & 0 deletions docs/source/iterative_methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,78 @@ To illustrate the difference with respect to the second question, consider Insta

```

### Top-N Instant Runoff for Truncated Linear Orders

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.top_n_instant_runoff_for_truncated_linear_orders

```

## Approval IRV

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.approval_irv

```

### Approval IRV TB

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.approval_irv_tb

```

### Approval IRV PUT

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.approval_irv_put

```

### Approval IRV with Explanation

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.approval_irv_with_explanation

```

## Split IRV

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.split_irv

```

### Split IRV TB

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.split_irv_tb

```

### Split IRV PUT

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.split_irv_put

```

### Split IRV with Explanation

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.split_irv_with_explanation

```

## Plurality With Runoff PUT

```{eval-rst}
Expand All @@ -92,6 +164,14 @@ To illustrate the difference with respect to the second question, consider Insta

```

### Plurality With Runoff PUT with Explanation

```{eval-rst}

.. autofunction:: pref_voting.iterative_methods.plurality_with_runoff_put_with_explanation

```

## Benham

```{eval-rst}
Expand Down
22 changes: 21 additions & 1 deletion docs/source/variable_voter_axioms.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ Variable Voter Axioms

```

### Single-Voter Resolvability with Truncation

```{eval-rst}

.. autofunction:: pref_voting.variable_voter_axioms.has_single_voter_resolvability_violation_with_truncation

.. autofunction:: pref_voting.variable_voter_axioms.find_all_single_voter_resolvability_violations_with_truncation

```

### Single-Voter Resolvability with Ties

```{eval-rst}

.. autofunction:: pref_voting.variable_voter_axioms.has_single_voter_resolvability_violation_with_ties

.. autofunction:: pref_voting.variable_voter_axioms.find_all_single_voter_resolvability_violations_with_ties

```

## Weak Single-Voter Resolvability

```{eval-rst}
Expand Down Expand Up @@ -133,4 +153,4 @@ Variable Voter Axioms
.. autofunction:: pref_voting.variable_voter_axioms.has_nonlinear_neutral_reversal_violation

.. autofunction:: pref_voting.variable_voter_axioms.find_all_nonlinear_neutral_reversal_violations
```
```
3 changes: 3 additions & 0 deletions pref_voting/.claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"enableAllProjectMcpServers": false
}
87 changes: 83 additions & 4 deletions pref_voting/generate_weighted_majority_graphs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'''
File: generate_margin_graphs.py
File: generate_weighted_majority_graphs.py
Author: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu)
Date: July 14, 2022
Updated: December 19, 2022
Expand All @@ -8,7 +8,6 @@

'''


import networkx as nx
from itertools import combinations
from pref_voting.helper import sublists, compositions, enumerate_compositions, convex_lexicographic_sublists
Expand Down Expand Up @@ -221,7 +220,87 @@ def pair(p):

return MarginGraph(candidates, w_edges)

## Generating Canonical MarginGraphs without Tied Margins
## Enumerating Canonical Majority Graphs

def _canonical_tournament(num_verts, edges):
"""
Return (certificate, canonical_edges) for a tournament on 0,...,num_verts-1.

The certificate is the lex-smallest upper-triangle bitstring over all
relabelings of the vertices. canonical_edges is the corresponding
canonically labeled edge set.
"""
edge_set = set(edges)
pairs = list(combinations(range(num_verts), 2))

best_bits = None
for perm in permutations(range(num_verts)):
bits = tuple(
1 if (perm[i], perm[j]) in edge_set else 0
for i, j in pairs
)
if best_bits is None or bits < best_bits:
best_bits = bits

canonical_edges = [
(i, j) if bit else (j, i)
for bit, (i, j) in zip(best_bits, pairs)
]
return best_bits, canonical_edges


def enumerate_tournaments(num_cands, candidates=None, cmap=None):
"""
Enumerate one MajorityGraph representative from each isomorphism class
of tournaments on num_cands candidates.
"""
if not isinstance(num_cands, int) or num_cands < 0:
raise ValueError("num_cands must be a nonnegative integer.")

if candidates is None:
candidates = list(range(num_cands))
else:
candidates = list(candidates)
if len(candidates) != num_cands:
raise ValueError("candidates must have length num_cands.")

if num_cands <= 1:
return [MajorityGraph(candidates, [], cmap=cmap)]

# One canonically labeled representative on vertices 0,...,k-1
# for each isomorphism class at size k.
canonical_edge_sets = [[]]

for new_v in range(1, num_cands):
seen = {}

for old_edges in canonical_edge_sets:
# Bit i = 0 means i -> new_v, bit i = 1 means new_v -> i.
for mask in range(1 << new_v):
new_edges = list(old_edges)

for i in range(new_v):
if (mask >> i) & 1:
new_edges.append((new_v, i))
else:
new_edges.append((i, new_v))

cert, canon_edges = _canonical_tournament(new_v + 1, new_edges)
if cert not in seen:
seen[cert] = canon_edges

canonical_edge_sets = [seen[cert] for cert in sorted(seen)]

return [
MajorityGraph(
candidates,
[(candidates[i], candidates[j]) for i, j in edges],
cmap=cmap,
)
for edges in canonical_edge_sets
]

## Enumerating Canonical MarginGraphs without Tied Margins

def _enumerate_ceots(num_cands, num_edges, partial_ceot, used_nodes, next_node):

Expand Down Expand Up @@ -338,7 +417,7 @@ def enumerate_uniquely_weighted_margin_graphs(num_cands, weight_domain):
[(e[0], e[1], weight_list[eidx]) for eidx, e in enumerate(reversed(ceot))])


## Generating Canonical MarginGraphs with Tied Margins
## Enumerating Canonical MarginGraphs with Tied Margins

def _enumerate_cweots_as_edgelist(num_cands, include_weak_tournaments=True):

Expand Down
2 changes: 1 addition & 1 deletion pref_voting/grade_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def __init__(

self.use_grade_order = grade_order is not None

self.compare_function = lambda v1, v2: (v1 > v2) - (v2 > v1) if grade_order is None else lambda v1, v2: (grade_order.index(v1) < grade_order.index(v2)) - (grade_order.index(v2) < grade_order.index(v1))
self.compare_function = (lambda v1, v2: (v1 > v2) - (v2 > v1)) if grade_order is None else (lambda v1, v2: (grade_order.index(v1) < grade_order.index(v2)) - (grade_order.index(v2) < grade_order.index(v1)))

self.gmap = gmap if gmap is not None else {g: str(g) for g in self.grades}
"""The candidate map is a dictionary associating an alternative with the name used when displaying a alternative."""
Expand Down
Loading
Loading