pref_voting 1.16.31__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. pref_voting/__init__.py +1 -0
  2. pref_voting/analysis.py +496 -0
  3. pref_voting/axiom.py +38 -0
  4. pref_voting/axiom_helpers.py +129 -0
  5. pref_voting/axioms.py +10 -0
  6. pref_voting/c1_methods.py +963 -0
  7. pref_voting/combined_methods.py +514 -0
  8. pref_voting/create_methods.py +128 -0
  9. pref_voting/data/examples/condorcet_winner/minimal_Anti-Plurality.soc +16 -0
  10. pref_voting/data/examples/condorcet_winner/minimal_Borda.soc +17 -0
  11. pref_voting/data/examples/condorcet_winner/minimal_Bracket_Voting.soc +20 -0
  12. pref_voting/data/examples/condorcet_winner/minimal_Bucklin.soc +19 -0
  13. pref_voting/data/examples/condorcet_winner/minimal_Coombs.soc +20 -0
  14. pref_voting/data/examples/condorcet_winner/minimal_Coombs_PUT.soc +20 -0
  15. pref_voting/data/examples/condorcet_winner/minimal_Coombs_TB.soc +20 -0
  16. pref_voting/data/examples/condorcet_winner/minimal_Dowdall.soc +19 -0
  17. pref_voting/data/examples/condorcet_winner/minimal_Instant_Runoff.soc +18 -0
  18. pref_voting/data/examples/condorcet_winner/minimal_Instant_Runoff_PUT.soc +18 -0
  19. pref_voting/data/examples/condorcet_winner/minimal_Instant_Runoff_TB.soc +18 -0
  20. pref_voting/data/examples/condorcet_winner/minimal_Iterated_Removal_Condorcet_Loser.soc +17 -0
  21. pref_voting/data/examples/condorcet_winner/minimal_Pareto.soc +17 -0
  22. pref_voting/data/examples/condorcet_winner/minimal_Plurality.soc +18 -0
  23. pref_voting/data/examples/condorcet_winner/minimal_PluralityWRunoff_PUT.soc +18 -0
  24. pref_voting/data/examples/condorcet_winner/minimal_Positive-Negative_Voting.soc +17 -0
  25. pref_voting/data/examples/condorcet_winner/minimal_Simplified_Bucklin.soc +18 -0
  26. pref_voting/data/examples/condorcet_winner/minimal_Superior_Voting.soc +19 -0
  27. pref_voting/data/examples/condorcet_winner/minimal_Weighted_Bucklin.soc +19 -0
  28. pref_voting/data/examples/condorcet_winner/minimal_resolute_Anti-Plurality.soc +17 -0
  29. pref_voting/data/examples/condorcet_winner/minimal_resolute_Borda.soc +17 -0
  30. pref_voting/data/examples/condorcet_winner/minimal_resolute_Bracket_Voting.soc +20 -0
  31. pref_voting/data/examples/condorcet_winner/minimal_resolute_Bucklin.soc +19 -0
  32. pref_voting/data/examples/condorcet_winner/minimal_resolute_Coombs.soc +21 -0
  33. pref_voting/data/examples/condorcet_winner/minimal_resolute_Coombs_PUT.soc +21 -0
  34. pref_voting/data/examples/condorcet_winner/minimal_resolute_Coombs_TB.soc +20 -0
  35. pref_voting/data/examples/condorcet_winner/minimal_resolute_Dowdall.soc +18 -0
  36. pref_voting/data/examples/condorcet_winner/minimal_resolute_Instant_Runoff.soc +18 -0
  37. pref_voting/data/examples/condorcet_winner/minimal_resolute_Instant_Runoff_PUT.soc +18 -0
  38. pref_voting/data/examples/condorcet_winner/minimal_resolute_Instant_Runoff_TB.soc +18 -0
  39. pref_voting/data/examples/condorcet_winner/minimal_resolute_Plurality.soc +18 -0
  40. pref_voting/data/examples/condorcet_winner/minimal_resolute_PluralityWRunoff_PUT.soc +18 -0
  41. pref_voting/data/examples/condorcet_winner/minimal_resolute_Positive-Negative_Voting.soc +17 -0
  42. pref_voting/data/examples/condorcet_winner/minimal_resolute_Simplified_Bucklin.soc +20 -0
  43. pref_voting/data/examples/condorcet_winner/minimal_resolute_Weighted_Bucklin.soc +19 -0
  44. pref_voting/data/voting_methods_properties.json +414 -0
  45. pref_voting/data/voting_methods_properties.json.lock +0 -0
  46. pref_voting/dominance_axioms.py +387 -0
  47. pref_voting/generate_profiles.py +801 -0
  48. pref_voting/generate_spatial_profiles.py +198 -0
  49. pref_voting/generate_utility_profiles.py +160 -0
  50. pref_voting/generate_weighted_majority_graphs.py +506 -0
  51. pref_voting/grade_methods.py +184 -0
  52. pref_voting/grade_profiles.py +357 -0
  53. pref_voting/helper.py +370 -0
  54. pref_voting/invariance_axioms.py +671 -0
  55. pref_voting/io/__init__.py +0 -0
  56. pref_voting/io/readers.py +432 -0
  57. pref_voting/io/writers.py +256 -0
  58. pref_voting/iterative_methods.py +2425 -0
  59. pref_voting/maj_graph_ex1.png +0 -0
  60. pref_voting/mappings.py +577 -0
  61. pref_voting/margin_based_methods.py +2345 -0
  62. pref_voting/monotonicity_axioms.py +872 -0
  63. pref_voting/num_evaluation_method.py +77 -0
  64. pref_voting/other_axioms.py +161 -0
  65. pref_voting/other_methods.py +939 -0
  66. pref_voting/pairwise_profiles.py +547 -0
  67. pref_voting/prob_voting_method.py +105 -0
  68. pref_voting/probabilistic_methods.py +287 -0
  69. pref_voting/profiles.py +856 -0
  70. pref_voting/profiles_with_ties.py +1069 -0
  71. pref_voting/rankings.py +466 -0
  72. pref_voting/scoring_methods.py +481 -0
  73. pref_voting/social_welfare_function.py +59 -0
  74. pref_voting/social_welfare_functions.py +7 -0
  75. pref_voting/spatial_profiles.py +448 -0
  76. pref_voting/stochastic_methods.py +99 -0
  77. pref_voting/strategic_axioms.py +1394 -0
  78. pref_voting/swf_axioms.py +173 -0
  79. pref_voting/utility_functions.py +102 -0
  80. pref_voting/utility_methods.py +178 -0
  81. pref_voting/utility_profiles.py +333 -0
  82. pref_voting/variable_candidate_axioms.py +640 -0
  83. pref_voting/variable_voter_axioms.py +3747 -0
  84. pref_voting/voting_method.py +355 -0
  85. pref_voting/voting_method_properties.py +92 -0
  86. pref_voting/voting_methods.py +8 -0
  87. pref_voting/voting_methods_registry.py +136 -0
  88. pref_voting/weighted_majority_graphs.py +1539 -0
  89. pref_voting-1.16.31.dist-info/METADATA +208 -0
  90. pref_voting-1.16.31.dist-info/RECORD +92 -0
  91. pref_voting-1.16.31.dist-info/WHEEL +4 -0
  92. pref_voting-1.16.31.dist-info/licenses/LICENSE.txt +21 -0
@@ -0,0 +1,77 @@
1
+ '''
2
+ File: num_evaluation_method.py
3
+ Author: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu)
4
+ Date: June 2, 2024
5
+
6
+ The NumEvaluationMethod class and helper functions for numerical evaluation methods.
7
+ '''
8
+
9
+ import functools
10
+ import numpy as np
11
+ from pref_voting.mappings import Utility
12
+ from pref_voting.rankings import Ranking
13
+
14
+ class NumEvaluationMethod(object):
15
+ """
16
+ A class to add functionality to numerical evaluation methods
17
+
18
+ Args:
19
+ nem (function): An implementation of a numerical evaluation method. The function should accept any type of profile, and a keyword parameter ``curr_cands`` to find the numerical evaluation after restricting to ``curr_cands``.
20
+ name (string): The human-readable name of the numerical evaluation function.
21
+
22
+ Returns:
23
+ A utility function (Utility) that represents the numerical evaluation of each candidate.
24
+
25
+ """
26
+ def __init__(self, nem, name = None):
27
+
28
+ self.nem = nem
29
+ self.name = name
30
+ functools.update_wrapper(self, nem)
31
+
32
+ def __call__(self, edata, curr_cands = None, **kwargs):
33
+
34
+ if (curr_cands is not None and len(curr_cands) == 0) or len(edata.candidates) == 0:
35
+ return Utility({})
36
+ return self.nem(edata, curr_cands = curr_cands, **kwargs)
37
+
38
+ def ranking(self, edata, curr_cands = None, **kwargs):
39
+ """
40
+ Return the ranking generated by the numerical evaluation.
41
+ """
42
+
43
+ if (curr_cands is not None and len(curr_cands) == 0) or len(edata.candidates) == 0:
44
+ return Ranking({})
45
+ ev = self.nem(edata, curr_cands = curr_cands, **kwargs)
46
+ return ev.ranking()
47
+
48
+ def display(self, edata, curr_cands = None, cmap = None, **kwargs):
49
+ """
50
+ Display the winning set of candidates.
51
+ """
52
+
53
+ cmap = cmap if cmap is not None else edata.cmap
54
+
55
+ ev = self.__call__(edata, curr_cands = curr_cands, **kwargs)
56
+
57
+ if ev is None: # some voting methods may return None if, for instance, it is taking long to compute the winner.
58
+ print(f"{self.name} numerical evaluation is not available")
59
+ else:
60
+ w_str = f"{self.name} evaluation is "
61
+ print(w_str + str(ev))
62
+
63
+ def set_name(self, new_name):
64
+ """Set the name of the social welfare function."""
65
+
66
+ self.name = new_name
67
+
68
+ def __str__(self):
69
+ return f"{self.name}"
70
+
71
+ def nem(name = None):
72
+ """
73
+ A decorator used when creating a numerical evaluation method.
74
+ """
75
+ def wrapper(f):
76
+ return NumEvaluationMethod(f, name=name)
77
+ return wrapper
@@ -0,0 +1,161 @@
1
+ """
2
+ File: other_axioms.py
3
+ Date: April 29 2025
4
+ Authors: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu)
5
+
6
+ Other axioms
7
+ ---------------------
8
+ """
9
+
10
+ from itertools import repeat
11
+ import numpy as np
12
+
13
+ from pref_voting.axiom import Axiom
14
+ from pref_voting.profiles import Profile
15
+ from pref_voting.profiles_with_ties import ProfileWithTies
16
+ from pref_voting.rankings import Ranking
17
+
18
+
19
+ def _reverse_ranking(ballot, all_cands):
20
+ """
21
+ Reverse a single ballot, treating *unranked* candidates as
22
+ tied for last before the reversal.
23
+
24
+ Parameters
25
+ ----------
26
+ ballot : tuple | Ranking
27
+ • tuple – a strict linear order
28
+ • Ranking – weak order, possibly incomplete
29
+ all_cands : list | set
30
+ The full candidate set of the profile.
31
+ """
32
+ if isinstance(ballot, tuple):
33
+ return tuple(reversed(ballot))
34
+
35
+ if isinstance(ballot, Ranking):
36
+ full_rmap = ballot.rmap.copy()
37
+ if full_rmap:
38
+ last_rank = max(full_rmap.values()) + 1
39
+ else:
40
+ last_rank = 1
41
+ for c in all_cands:
42
+ if c not in full_rmap:
43
+ full_rmap[c] = last_rank
44
+
45
+ max_rank = max(full_rmap.values())
46
+ rev_rmap = {c: max_rank + 1 - k for c, k in full_rmap.items()}
47
+ return Ranking(rev_rmap)
48
+
49
+ def _reverse_profile(P):
50
+ """
51
+ Build the reversed profile ``Pᵣ``.
52
+ """
53
+ all_cands = P.candidates
54
+ rev_ballots = [_reverse_ranking(b, all_cands) for b in P.rankings]
55
+
56
+ if isinstance(P, Profile):
57
+ return Profile(rev_ballots)
58
+
59
+ if isinstance(P, ProfileWithTies):
60
+ P_r = ProfileWithTies(rev_ballots, candidates=all_cands)
61
+ if P.using_extended_strict_preference:
62
+ P_r.use_extended_strict_preference()
63
+ return P_r
64
+
65
+ def _reverse_margin_graph(mg):
66
+ """
67
+ Return the *edge-reversed* margin graph.
68
+
69
+ All positive-margin edges (u, v, w) become (v, u, w); weights are preserved.
70
+ """
71
+ rev_edges = [(v, u, w) for (u, v, w) in mg.edges]
72
+ return MarginGraph(mg.candidates[:], rev_edges, cmap=mg.cmap)
73
+
74
+ def has_reversal_symmetry_violation(edata, vm, verbose=False):
75
+ """
76
+ Returns True iff ``vm`` violates reversal symmetry on *edata*.
77
+
78
+ Reversal Symmetry states that if x is a **unique** winner in ``edata``,
79
+ then x should not be among the winners in the reversal of ``edata``.
80
+ """
81
+
82
+ if len(edata.candidates) <= 1:
83
+ return False
84
+
85
+ if isinstance(edata, MarginGraph):
86
+ mg = edata
87
+ winners = vm(mg)
88
+ if len(winners) != 1:
89
+ return False
90
+
91
+ x = winners[0]
92
+ mg_r = _reverse_margin_graph(mg)
93
+ rev_winners = vm(mg_r)
94
+
95
+ if x in rev_winners:
96
+ if verbose:
97
+ print(f"Reversal-symmetry violation for {vm.name} on a MarginGraph")
98
+ print(f"Unique winner {x} also wins after edge reversal.")
99
+ print("\nOriginal margin graph:")
100
+ mg.display()
101
+ print(mg.description())
102
+ vm.display(mg)
103
+ print('\nReversed margin graph:')
104
+ mg_r.display()
105
+ print(mg_r.description())
106
+ vm.display(mg_r)
107
+
108
+ return True
109
+ return False
110
+
111
+ winners = vm(edata)
112
+ if isinstance(winners, np.ndarray):
113
+ winners = winners.tolist()
114
+ if len(winners) != 1:
115
+ return False
116
+
117
+ x = winners[0]
118
+ P_r = _reverse_profile(edata)
119
+ rev_winners = vm(P_r)
120
+ if isinstance(rev_winners, np.ndarray):
121
+ rev_winners = rev_winners.tolist()
122
+
123
+ if x in rev_winners:
124
+ if verbose:
125
+ print(f"Reversal-symmetry violation for {vm.name}")
126
+ print(f"Unique winner {x} also wins after reversal.")
127
+ print("\nOriginal profile:")
128
+ edata.display()
129
+ print(edata.description())
130
+ vm.display(edata)
131
+ print("\nReversed profile:")
132
+ P_r.display()
133
+ print(P_r.description())
134
+ vm.display(P_r)
135
+ return True
136
+ return False
137
+
138
+
139
+ def find_all_reversal_symmetry_violations(edata, vm, verbose=False):
140
+ """
141
+ Returns a one-item list [(unique_winner, winners_after_reversal)] describing the violation on *edata*, or [].
142
+ """
143
+ if not has_reversal_symmetry_violation(edata, vm, verbose):
144
+ return []
145
+
146
+ winners = vm(edata)
147
+ winners = winners.tolist() if isinstance(winners, np.ndarray) else winners
148
+ if isinstance(edata, MarginGraph):
149
+ rev_winners = vm(_reverse_margin_graph(edata))
150
+ else:
151
+ rev_winners = vm(_reverse_profile(edata))
152
+
153
+ rev_winners = rev_winners.tolist() if isinstance(rev_winners, np.ndarray) else rev_winners
154
+ return [(winners, rev_winners)]
155
+
156
+
157
+ reversal_symmetry = Axiom(
158
+ "Reversal Symmetry",
159
+ has_violation=has_reversal_symmetry_violation,
160
+ find_all_violations=find_all_reversal_symmetry_violations,
161
+ )