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,963 @@
1
+ '''
2
+ File: c1_methods.py
3
+ Author: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu)
4
+ Date: January 10, 2022
5
+ Update: July 31, 2022
6
+
7
+ Implementations of voting methods that work on both profiles and majority graphs.
8
+ '''
9
+
10
+ from pref_voting.voting_method import *
11
+ from pref_voting.helper import get_mg, get_weak_mg
12
+ from pref_voting.margin_based_methods import distance_to_margin_graph
13
+ from pref_voting.probabilistic_methods import c1_maximal_lottery
14
+ from pref_voting.rankings import Ranking, break_ties_alphabetically
15
+ from pref_voting.social_welfare_function import swf
16
+ import copy
17
+ import math
18
+ from itertools import product, permutations, combinations, chain
19
+ import networkx as nx
20
+ import matplotlib.pyplot as plt
21
+ from pref_voting.voting_method_properties import ElectionTypes
22
+
23
+ @vm(name = "Condorcet",
24
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
25
+ def condorcet(edata, curr_cands = None):
26
+ """
27
+ Return the Condorcet winner if one exists, otherwise return all the candidates. A Condorcet winner is a candidate :math:`c` that is majority preferred to every other candidate.
28
+
29
+ Args:
30
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `condorcet_winner` method.
31
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
32
+
33
+ Returns:
34
+ A sorted list of candidates
35
+
36
+ .. seealso::
37
+
38
+ :meth:`pref_voting.profiles.Profile.condorcet_winner`, :meth:`pref_voting.profiles_with_ties.ProfileWithTies.condorcet_winner`, :meth:`pref_voting.weighted_majority_graphs.MajorityGraph.condorcet_winner`
39
+
40
+ :Example:
41
+
42
+ .. exec_code::
43
+
44
+ from pref_voting.profiles import Profile
45
+ from pref_voting.c1_methods import condorcet
46
+
47
+ prof = Profile([[0, 1, 2], [1, 2, 0], [2, 0, 1]], [1, 1, 1])
48
+
49
+ prof.display()
50
+ print(prof.condorcet_winner())
51
+ condorcet.display(prof)
52
+ condorcet.display(prof.majority_graph())
53
+ condorcet.display(prof.margin_graph())
54
+
55
+ prof2 = Profile([[0, 1, 2], [2, 1, 0], [1, 0, 2]], [3, 1, 1])
56
+
57
+ prof2.display()
58
+ print(prof2.condorcet_winner())
59
+ condorcet.display(prof2)
60
+ condorcet.display(prof2.majority_graph())
61
+ condorcet.display(prof2.margin_graph())
62
+
63
+ """
64
+
65
+ candidates = edata.candidates if curr_cands is None else curr_cands
66
+ cond_winner = edata.condorcet_winner(curr_cands = curr_cands)
67
+
68
+ return [cond_winner] if cond_winner is not None else sorted(candidates)
69
+
70
+ @vm(name = "Weak Condorcet",
71
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
72
+ def weak_condorcet(edata, curr_cands = None):
73
+
74
+ """
75
+ Return all weak Condorcet winner if one exists, otherwise return all the candidates. A weak Condorcet winner is a candidate :math:`c` such that no other candidate is majority preferred to :math:`c`.
76
+
77
+ Args:
78
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `weak_condorcet_winner` method.
79
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
80
+
81
+ Returns:
82
+ A sorted list of candidates
83
+
84
+ .. seealso::
85
+
86
+ :meth:`pref_voting.profiles.Profile.weak_condorcet_winner`,
87
+ :meth:`pref_voting.profiles_with_ties.ProfileWithTies.weak_condorcet_winner`,
88
+ :meth:`pref_voting.weighted_majority_graphs.MajorityGraph.weak_condorcet_winner`
89
+
90
+ """
91
+
92
+ candidates = edata.candidates if curr_cands is None else curr_cands
93
+ weak_cond_winners = edata.weak_condorcet_winner(curr_cands = curr_cands)
94
+
95
+ return weak_cond_winners if weak_cond_winners is not None else sorted(candidates)
96
+
97
+ @vm(name = "Copeland",
98
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
99
+ def copeland(edata, curr_cands = None):
100
+ """
101
+ The Copeland score for c is the number of candidates that c is majority preferred to minus the number of candidates majority preferred to c. The Copeland winners are the candidates with the maximum Copeland score in the profile restricted to ``curr_cands``.
102
+
103
+ Equivalently, give each candidate 1 point for each head-to-head win, 1/2 point for each head-to-head tie, and 0 points for each head-to-head loss. Then the Copeland winners are the candidates with the maximum number of points.
104
+
105
+ Args:
106
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `copeland_scores` method.
107
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
108
+
109
+ Returns:
110
+ A sorted list of candidates
111
+
112
+ .. seealso::
113
+
114
+ :meth:`pref_voting.profiles.Profile.copeland_scores`, :meth:`pref_voting.profiles_with_ties.ProfileWithTies.copeland_scores`, :meth:`pref_voting.weighted_majority_graphs.MajorityGraph.copeland_scores`
115
+
116
+
117
+ :Example:
118
+
119
+ .. plot:: margin_graphs_examples/mg_ex_copeland_llull.py
120
+ :context: reset
121
+ :include-source: True
122
+
123
+
124
+ .. code-block::
125
+
126
+ from pref_voting.c1_methods import copeland
127
+ copeland.display(prof)
128
+
129
+
130
+ .. exec_code::
131
+ :hide_code:
132
+
133
+ from pref_voting.profiles import Profile
134
+ from pref_voting.c1_methods import copeland
135
+
136
+ prof = Profile([[1, 3, 0, 4, 2], [0, 1, 4, 2, 3], [2, 4, 0, 1, 3], [3, 0, 2, 4, 1], [4, 3, 1, 0, 2], [2, 3, 0, 1, 4]], [1, 1, 1, 1, 1, 1])
137
+
138
+ copeland.display(prof)
139
+ print(prof.copeland_scores())
140
+
141
+
142
+ """
143
+ c_scores = edata.copeland_scores(curr_cands = curr_cands)
144
+ max_score = max(c_scores.values())
145
+ return sorted([c for c in c_scores.keys() if c_scores[c] == max_score])
146
+
147
+ @swf(name = "Copeland ranking")
148
+ def copeland_ranking(edata, curr_cands=None, local=True, tie_breaking=None):
149
+ """The SWF that ranks candidates by their Copeland scores. If local is True, then the Copeland scores are computed with respect to the profile restricted to curr_cands. Otherwise, the Copeland scores are computed with respect to the entire profile.
150
+
151
+ Args:
152
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
153
+ curr_cands (List[int], optional): The candidates to rank. If None, then all candidates in profile are ranked
154
+ local (bool, optional): If True, then the Copeland scores are computed with respect to the profile restricted to curr_cands. Otherwise, the Copeland scores are computed with respect to the entire profile.
155
+ tie_breaking (str, optional): The tie-breaking method to use. If None, then no tie-breaking is used. If "alphabetic", then the tie-breaking is done alphabetically.
156
+
157
+ Returns:
158
+ A Ranking object
159
+ """
160
+
161
+ cands = edata.candidates if curr_cands is None else curr_cands
162
+
163
+ if local:
164
+ copeland_scores_dict = edata.copeland_scores(curr_cands=cands)
165
+
166
+ else:
167
+ c_scores = edata.copeland_scores(curr_cands=edata.candidates)
168
+ copeland_scores_dict = {c: c_scores[c] for c in cands}
169
+
170
+ for cand in cands:
171
+ copeland_scores_dict[cand] = -copeland_scores_dict[cand]
172
+
173
+ copeland_ranking = Ranking(copeland_scores_dict)
174
+ copeland_ranking.normalize_ranks()
175
+
176
+ if tie_breaking == "alphabetic":
177
+ copeland_ranking = break_ties_alphabetically(copeland_ranking)
178
+
179
+ return copeland_ranking
180
+
181
+
182
+ @vm(name = "Llull",
183
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
184
+ def llull(edata, curr_cands = None):
185
+ """The Llull score for a candidate :math:`c` is the number of candidates that :math:`c` is weakly majority preferred to. This is equivalent to calculating the Copeland scores for a candidate :math:`c` with 1 point for each candidate that :math:`c` is majority preferred to, 1 point for each candidate that :math:`c` is tied with (instead of 1/2 a point as for Copeland), and 0 points for each candidate that is majority preferred to :math:`c`. The Llull winners are the candidates with the maximum Llull score in the profile restricted to ``curr_cands``.
186
+
187
+ Args:
188
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `copeland_scores` method.
189
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
190
+
191
+ Returns:
192
+ A sorted list of candidates
193
+
194
+ .. seealso::
195
+
196
+ :meth:`pref_voting.profiles.Profile.copeland_scores`, :meth:`pref_voting.profiles_with_ties.ProfileWithTies.copeland_scores`, :meth:`pref_voting.weighted_majority_graphs.MajorityGraph.copeland_scores`
197
+
198
+ :Example:
199
+
200
+ .. plot:: margin_graphs_examples/mg_ex_copeland_llull.py
201
+ :context: reset
202
+ :include-source: True
203
+
204
+
205
+ .. code-block::
206
+
207
+ from pref_voting.c1_methods import llull
208
+ llull.display(prof)
209
+
210
+
211
+ .. exec_code::
212
+ :hide_code:
213
+
214
+ from pref_voting.profiles import Profile
215
+ from pref_voting.c1_methods import llull
216
+
217
+ prof = Profile([[1, 3, 0, 4, 2], [0, 1, 4, 2, 3], [2, 4, 0, 1, 3], [3, 0, 2, 4, 1], [4, 3, 1, 0, 2], [2, 3, 0, 1, 4]], [1, 1, 1, 1, 1, 1])
218
+
219
+ llull.display(prof)
220
+ print(prof.copeland_scores(scores=(1, 0.5, 0)))
221
+
222
+ """
223
+
224
+ l_scores = edata.copeland_scores(curr_cands = curr_cands, scores = (1,1,0))
225
+ max_score = max(l_scores.values())
226
+ return sorted([c for c in l_scores.keys() if l_scores[c] == max_score])
227
+
228
+ def left_covers(dom, c1, c2):
229
+ # left covers: c1 left covers c2 when all the candidates that are majority preferred to c1 are also majority preferred to c2.
230
+
231
+ # weakly left covers: c1 weakly left covers c2 when all the candidates that are majority preferred to or tied with c1
232
+ # are also majority preferred to or tied with c2.
233
+
234
+ return dom[c1].issubset(dom[c2])
235
+
236
+ def right_covers(dom, c1, c2):
237
+ # right covers: c1 right covers c2 when all the candidates that c2 majority preferrs are majority
238
+ # preferred by c1
239
+
240
+ return dom[c2].issubset(dom[c1])
241
+
242
+ @vm(name = "Uncovered Set",
243
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
244
+ def uc_gill(edata, curr_cands = None):
245
+ """Uncovered Set (Gillies version): Given candidates :math:`a` and :math:`b`, say that :math:`a` defeats :math:`b` in the election if :math:`a` is majority preferred to :math:`b` and :math:`a` left covers :math:`b`: i.e., for all :math:`c`, if :math:`c` is majority preferred to :math:`a`, then :math:`c` majority preferred to :math:`b`. The winners are the set of candidates who are undefeated in the election restricted to ``curr_cands``.
246
+
247
+ Args:
248
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `dominators` method.
249
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
250
+
251
+ Returns:
252
+ A sorted list of candidates
253
+
254
+ .. seealso::
255
+
256
+ :func:`pref_voting.c1_methods.uc_fish`, :func:`pref_voting.c1_methods.uc_bordes`, :func:`pref_voting.c1_methods.uc_mckelvey`
257
+
258
+ :Example:
259
+
260
+ .. plot:: margin_graphs_examples/mg_ex_uncovered_sets.py
261
+ :context: reset
262
+ :include-source: True
263
+
264
+ .. code-block::
265
+
266
+ from pref_voting.c1_methods import uc_gill
267
+ uc_gill.display(prof)
268
+
269
+
270
+ .. exec_code::
271
+ :hide_code:
272
+
273
+ from pref_voting.profiles import Profile
274
+ from pref_voting.c1_methods import uc_gill
275
+
276
+ prof = Profile([[2, 3, 0, 1], [0, 2, 1, 3], [3, 0, 1, 2], [1, 2, 0, 3], [1, 2, 3, 0]], [1, 1, 1, 2, 1])
277
+
278
+ uc_gill.display(prof)
279
+
280
+ """
281
+
282
+ candidates = edata.candidates if curr_cands is None else curr_cands
283
+ dom = {c: set(edata.dominators(c, curr_cands = curr_cands)) for c in candidates}
284
+ uc_set = list()
285
+ for c1 in candidates:
286
+ is_in_ucs = True
287
+ for c2 in edata.dominators(c1, curr_cands = curr_cands): # consider only c2 predecessors
288
+ if c1 != c2:
289
+ # check if c2 left covers c1
290
+ if left_covers(dom, c2, c1):
291
+ is_in_ucs = False
292
+ if is_in_ucs:
293
+ uc_set.append(c1)
294
+ return list(sorted(uc_set))
295
+
296
+ def uc_gill_defeat(edata, curr_cands = None):
297
+ """Returns the defeat relation used to find the Uncovered Set (Gillies version) winners.
298
+
299
+ Args:
300
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `dominators` method.
301
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
302
+
303
+ Returns:
304
+ A networkx object in which there is an edge from :math:`a` to :math:`b` when :math:`a` to :math:`b` according to Top Cycle.
305
+
306
+ .. seealso::
307
+
308
+ :func:`pref_voting.c1_methods.uc_gill`
309
+
310
+ :Example:
311
+
312
+
313
+ .. plot:: margin_graphs_examples/uc_gill_defeat_example.py
314
+ :include-source: True
315
+
316
+
317
+ """
318
+
319
+ defeat = nx.DiGraph()
320
+
321
+ candidates = edata.candidates if curr_cands is None else curr_cands
322
+
323
+ defeat.add_nodes_from(candidates)
324
+
325
+ dom = {c: set(edata.dominators(c, curr_cands = curr_cands)) for c in candidates}
326
+ for c1 in candidates:
327
+ for c2 in edata.dominators(c1, curr_cands = curr_cands): # consider only c2 predecessors
328
+ if c1 != c2:
329
+ # check if c2 left covers c1
330
+ if left_covers(dom, c2, c1):
331
+ defeat.add_edge(c2, c1)
332
+ return defeat
333
+
334
+ @vm(name = "Uncovered Set - Fishburn",
335
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
336
+ def uc_fish(edata, curr_cands = None):
337
+ """Uncovered Set (Fishburn version): Given candidates :math:`a` and :math:`b`, say that :math:`a` defeats :math:`b` in the election :math:`a` left covers :math:`b`: i.e., for all :math:`c`, if :math:`c` is majority preferred to :math:`a`, then :math:`c` majority preferred to :math:`b`. The winners are the set of candidates who are undefeated in the election restricted to ``curr_cands``.
338
+
339
+ Args:
340
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `dominators` method.
341
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
342
+
343
+ Returns:
344
+ A sorted list of candidates
345
+
346
+ .. seealso::
347
+
348
+ :func:`pref_voting.c1_methods.uc_gill`, :func:`pref_voting.c1_methods.uc_bordes`, :func:`pref_voting.c1_methods.uc_mckelvey`
349
+
350
+ :Example:
351
+
352
+
353
+ .. plot:: margin_graphs_examples/mg_ex_uncovered_sets.py
354
+ :include-source: True
355
+
356
+
357
+ .. code-block::
358
+
359
+ from pref_voting.c1_methods import uc_fish
360
+ uc_fish.display(prof)
361
+
362
+
363
+ .. exec_code::
364
+ :hide_code:
365
+
366
+ from pref_voting.profiles import Profile
367
+ from pref_voting.c1_methods import uc_fish
368
+
369
+ prof = Profile([[2, 3, 0, 1], [0, 2, 1, 3], [3, 0, 1, 2], [1, 2, 0, 3], [1, 2, 3, 0]], [1, 1, 1, 2, 1])
370
+
371
+ uc_fish.display(prof)
372
+
373
+ """
374
+ candidates = edata.candidates if curr_cands is None else curr_cands
375
+ dom = {c: set(edata.dominators(c, curr_cands = curr_cands)) for c in candidates}
376
+ uc_set = list()
377
+ for c1 in candidates:
378
+ is_in_ucs = True
379
+ for c2 in candidates:
380
+ if c1 != c2:
381
+ # check if c2 left covers c1 but c1 does not left cover c2
382
+ if left_covers(dom, c2, c1) and not left_covers(dom, c1, c2):
383
+ is_in_ucs = False
384
+ if is_in_ucs:
385
+ uc_set.append(c1)
386
+ return list(sorted(uc_set))
387
+
388
+ def uc_fish_defeat(edata, curr_cands = None):
389
+ """Returns the defeat relation used to find the Uncovered Set (Fishburn version) winners.
390
+
391
+ Args:
392
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `dominators` method.
393
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
394
+
395
+ Returns:
396
+ A networkx object in which there is an edge from :math:`a` to :math:`b` when :math:`a` to :math:`b` according to Top Cycle.
397
+
398
+ .. seealso::
399
+
400
+ :func:`pref_voting.c1_methods.uc_fish`
401
+
402
+
403
+ :Example:
404
+
405
+
406
+ .. plot:: margin_graphs_examples/uc_fish_defeat_example.py
407
+ :include-source: True
408
+
409
+ """
410
+ defeat = nx.DiGraph()
411
+
412
+ candidates = edata.candidates if curr_cands is None else curr_cands
413
+
414
+ defeat.add_nodes_from(candidates)
415
+ dom = {c: set(edata.dominators(c, curr_cands = curr_cands)) for c in candidates}
416
+ for c1 in candidates:
417
+ is_in_ucs = True
418
+ for c2 in candidates:
419
+ if c1 != c2:
420
+ # check if c2 left covers c1 but c1 does not left cover c2
421
+ if left_covers(dom, c2, c1) and not left_covers(dom, c1, c2):
422
+ defeat.add_edge(c2, c1)
423
+ return defeat
424
+
425
+ @vm(name = "Uncovered Set - Bordes",
426
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
427
+ def uc_bordes(edata, curr_cands = None):
428
+ """Uncovered Set (Bordes version): Given candidates :math:`a` and :math:`b`, say that :math:`a` Bordes covers :math:`b` if :math:`a` is majority preferred to :math:`b` and for all :math:`c`, if :math:`c` is majority preferred or tied with :math:`a`, then :math:`c` is majority preferred to or tied with :math:`b`. The winners are the set of candidates who are not Bordes covered in the election restricted to ``curr_cands``.
429
+
430
+ Args:
431
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has `dominators` and `majority_prefers` methods.
432
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
433
+
434
+ Returns:
435
+ A sorted list of candidates
436
+
437
+ .. seealso::
438
+
439
+ :func:`pref_voting.c1_methods.uc_gill`, :func:`pref_voting.c1_methods.uc_fish`, :func:`pref_voting.c1_methods.uc_mckelvey`
440
+
441
+ :Example:
442
+
443
+
444
+ .. plot:: margin_graphs_examples/mg_ex_uncovered_sets.py
445
+ :context: reset
446
+ :include-source: True
447
+
448
+
449
+ .. code-block::
450
+
451
+ from pref_voting.c1_methods import uc_bordes
452
+ uc_bordes.display(prof)
453
+
454
+
455
+ .. exec_code::
456
+ :hide_code:
457
+
458
+ from pref_voting.profiles import Profile
459
+ from pref_voting.c1_methods import uc_bordes
460
+
461
+ prof = Profile([[2, 3, 0, 1], [0, 2, 1, 3], [3, 0, 1, 2], [1, 2, 0, 3], [1, 2, 3, 0]], [1, 1, 1, 2, 1])
462
+
463
+ uc_bordes.display(prof)
464
+
465
+ """
466
+
467
+ candidates = edata.candidates if curr_cands is None else curr_cands
468
+
469
+ dom = {c: set(edata.dominators(c, curr_cands = curr_cands)).union([_c for _c in candidates if (not edata.majority_prefers(c, _c) and not edata.majority_prefers(_c, c))]) for c in candidates}
470
+
471
+ uc_set = list()
472
+ for c1 in candidates:
473
+ is_in_ucs = True
474
+ for c2 in edata.dominators(c1, curr_cands = curr_cands): # consider only c2 predecessors
475
+ if c1 != c2:
476
+ # check if c2 left covers c1
477
+ if left_covers(dom, c2, c1):
478
+ is_in_ucs = False
479
+ if is_in_ucs:
480
+ uc_set.append(c1)
481
+ return list(sorted(uc_set))
482
+
483
+ @vm(name = "Uncovered Set - McKelvey",
484
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
485
+ def uc_mckelvey(edata, curr_cands = None):
486
+ """Uncovered Set (McKelvey version): Given candidates :math:`a` and :math:`b`, say that :math:`a` McKelvey covers :math:`b` if a Gillies covers :math:`b` and :math:`a` Bordes covers :math:`b`. The winners are the set of candidates who are not McKelvey covered in the election restricted to ``curr_cands``.
487
+
488
+ Args:
489
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has `dominators` and `majority_prefers` methods.
490
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
491
+
492
+ Returns:
493
+ A sorted list of candidates
494
+
495
+ .. seealso::
496
+
497
+ :func:`pref_voting.c1_methods.uc_gill`, :func:`pref_voting.c1_methods.uc_fish`, :func:`pref_voting.c1_methods.uc_bordes`
498
+
499
+ :Example:
500
+
501
+ .. plot:: margin_graphs_examples/mg_ex_uncovered_sets.py
502
+ :context: reset
503
+ :include-source: True
504
+
505
+
506
+ .. code-block::
507
+
508
+ from pref_voting.c1_methods import uc_mckelvey
509
+ uc_bordes.display(prof)
510
+
511
+
512
+ .. exec_code::
513
+ :hide_code:
514
+
515
+ from pref_voting.profiles import Profile
516
+ from pref_voting.c1_methods import uc_mckelvey
517
+
518
+ prof = Profile([[2, 3, 0, 1], [0, 2, 1, 3], [3, 0, 1, 2], [1, 2, 0, 3], [1, 2, 3, 0]], [1, 1, 1, 2, 1])
519
+
520
+ uc_mckelvey.display(prof)
521
+
522
+ """
523
+ candidates = edata.candidates if curr_cands is None else curr_cands
524
+
525
+ strict_dom = {c: set(edata.dominators(c, curr_cands = curr_cands)) for c in candidates}
526
+ weak_dom = {c: strict_dom[c].union([_c for _c in candidates if (not edata.majority_prefers(c, _c) and not edata.majority_prefers(_c, c))]) for c in candidates}
527
+ uc_set = list()
528
+ for c1 in candidates:
529
+ is_in_ucs = True
530
+ for c2 in edata.dominators(c1, curr_cands = curr_cands): # consider only c2 predecessors
531
+ if c1 != c2:
532
+ # check if c2 left covers c1
533
+ if left_covers(strict_dom, c2, c1) and left_covers(weak_dom, c2, c1):
534
+ is_in_ucs = False
535
+ if is_in_ucs:
536
+ uc_set.append(c1)
537
+ return list(sorted(uc_set))
538
+
539
+ @vm(name = "Top Cycle",
540
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
541
+ def top_cycle(edata, curr_cands = None):
542
+ """The smallest set of candidates such that every candidate inside the set is majority preferred to every candidate outside the set.
543
+
544
+ Args:
545
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `majority_prefers` method.
546
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
547
+
548
+ Returns:
549
+ A sorted list of candidates
550
+
551
+ .. seealso::
552
+
553
+ Also known as ``getcha`` and ``smith_set``.
554
+
555
+ Related function includes :func:`pref_voting.c1_methods.gocha`
556
+
557
+ :Example:
558
+
559
+
560
+ .. plot:: margin_graphs_examples/mg_ex_top_cycle_gocha.py
561
+ :context: reset
562
+ :include-source: True
563
+
564
+
565
+ .. code-block::
566
+
567
+ from pref_voting.c1_methods import top_cycle, getcha, smith_set
568
+ top_cycle.display(prof)
569
+ getcha.display(prof)
570
+ smith_set.display(prof)
571
+
572
+
573
+ .. exec_code::
574
+ :hide_code:
575
+
576
+ from pref_voting.profiles import Profile
577
+ from pref_voting.c1_methods import top_cycle, getcha, smith_set
578
+
579
+ prof = Profile([[1, 2, 0, 3], [1, 3, 0, 2], [3, 1, 0, 2], [0, 3, 1, 2]], [1, 1, 1, 1])
580
+
581
+ top_cycle.display(prof)
582
+ getcha.display(prof)
583
+ smith_set.display(prof)
584
+
585
+
586
+ """
587
+ wmg = get_weak_mg(edata, curr_cands = curr_cands)
588
+ scc = list(nx.strongly_connected_components(wmg))
589
+ min_indegree = min([max([wmg.in_degree(n) for n in comp]) for comp in scc])
590
+ smith = [comp for comp in scc if max([wmg.in_degree(n) for n in comp]) == min_indegree][0]
591
+ return sorted(list(smith))
592
+
593
+ # Create some aliases for Top Cycle
594
+ top_cycle.set_name("GETCHA")
595
+ getcha = copy.deepcopy(top_cycle)
596
+ getcha.skip_registration = True
597
+
598
+ top_cycle.set_name("Smith Set")
599
+ smith_set = copy.deepcopy(top_cycle)
600
+ smith_set.skip_registration = True
601
+
602
+ # reset the name Top Cycle
603
+ top_cycle.set_name("Top Cycle")
604
+
605
+ def top_cycle_defeat(edata, curr_cands = None):
606
+ """Return the defeat relation associated with the Top Cycle voting method.
607
+
608
+ Args:
609
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `majority_prefers` method.
610
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
611
+
612
+ Returns:
613
+ A networkx object in which there is an edge from :math:`a` to :math:`b` when :math:`a` to :math:`b` according to Top Cycle.
614
+
615
+ .. seealso::
616
+
617
+ :func:`pref_voting.c1_methods.top_cycle`
618
+
619
+ :Example:
620
+
621
+ .. plot:: margin_graphs_examples/top_cycle_defeat.py
622
+ :context: reset
623
+ :include-source: True
624
+
625
+ """
626
+
627
+ defeat = nx.DiGraph()
628
+ candidates = edata.candidates if curr_cands is None else curr_cands
629
+ smith_set = top_cycle(edata, curr_cands = candidates)
630
+
631
+ defeat.add_nodes_from(candidates)
632
+ defeat.add_edges_from([(a, b) for a in candidates for b in candidates if a != b and a in smith_set and b not in smith_set])
633
+ return defeat
634
+
635
+ @vm(name = "GOCHA",
636
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
637
+ def gocha(edata, curr_cands = None):
638
+ """The GOCHA set (also known as the Schwartz set) is the set of all candidates x such that if y can reach x in the transitive closer of the majority relation, then x can reach y in the transitive closer of the majority relation.
639
+
640
+ Args:
641
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `majority_prefers` method.
642
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
643
+
644
+ Returns:
645
+ A sorted list of candidates
646
+
647
+ .. seealso::
648
+
649
+ Also known as ``schwartz_set``.
650
+
651
+ Related function includes :func:`pref_voting.c1_methods.top_cycle`
652
+
653
+ :Example:
654
+
655
+ .. plot:: margin_graphs_examples/mg_ex_top_cycle_gocha.py
656
+ :context: reset
657
+ :include-source: True
658
+
659
+ .. code-block::
660
+
661
+ from pref_voting.c1_methods import top_cycle, gocha, schwartz_set
662
+
663
+ gocha.display(prof)
664
+ schwartz_set.display(prof)
665
+
666
+ .. exec_code::
667
+ :hide_code:
668
+
669
+ from pref_voting.profiles import Profile
670
+ from pref_voting.c1_methods import gocha, schwartz_set
671
+
672
+ prof = Profile([[1, 2, 0, 3], [1, 3, 0, 2], [3, 1, 0, 2], [0, 3, 1, 2]], [1, 1, 1, 1])
673
+
674
+ gocha.display(prof)
675
+ schwartz_set.display(prof)
676
+
677
+ """
678
+
679
+ mg = get_mg(edata, curr_cands = curr_cands)
680
+ transitive_closure = nx.algorithms.dag.transitive_closure(mg)
681
+ schwartz = set()
682
+ for ssc in nx.strongly_connected_components(transitive_closure):
683
+ if not any([transitive_closure.has_edge(c2,c1)
684
+ for c1 in ssc for c2 in transitive_closure.nodes if c2 not in ssc]):
685
+ schwartz = schwartz.union(ssc)
686
+ return sorted(list(schwartz))
687
+
688
+ # Create some aliases for GOCHA
689
+ gocha.set_name("Schwartz Set")
690
+ schwartz_set = copy.deepcopy(gocha)
691
+ schwartz_set.skip_registration = True
692
+
693
+ # reset the name GETCHA
694
+ gocha.set_name("GOCHA")
695
+
696
+
697
+ ## Banks
698
+ #
699
+
700
+ def seqs(iterable):
701
+ s = list(iterable)
702
+ return chain.from_iterable(permutations(s, r) for r in range(len(s)+1))
703
+
704
+ def is_transitive(G, p):
705
+ for c1_idx, c1 in enumerate(p[:-1]):
706
+ for c2 in p[c1_idx+1::]:
707
+ if not G.has_edge(c1,c2):
708
+ return False
709
+ return True
710
+
711
+ def is_subsequence(x, y):
712
+ it = iter(y)
713
+ return all(any(c == ch for c in it) for ch in x)
714
+
715
+ @vm(name = "Banks",
716
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
717
+ def banks(edata, curr_cands = None):
718
+ """ Say that a *chain* in majority graph is a subset of candidates that is linearly ordered by the majority relation. Then a candidate :math:`a` if :math:`a` is the maximum element with respect to the majority relation of some maximal chain in the majority graph.
719
+
720
+ Args:
721
+ edata (Profile, ProfileWithTies, MarginGraph): Any election data that has a `margin` method.
722
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
723
+
724
+ Returns:
725
+ A sorted list of candidates
726
+
727
+
728
+ :Example:
729
+
730
+ .. plot:: margin_graphs_examples/mg_ex_banks.py
731
+ :context: reset
732
+ :include-source: True
733
+
734
+
735
+ .. code-block::
736
+
737
+ from pref_voting.c1_methods import banks
738
+
739
+ banks.display(prof)
740
+
741
+
742
+ .. exec_code::
743
+ :hide_code:
744
+
745
+ from pref_voting.weighted_majority_graphs import MarginGraph
746
+ from pref_voting.c1_methods import banks
747
+
748
+ mg = MarginGraph([0, 1, 2, 3], [(0, 2, 2), (0, 3, 6), (1, 0, 8), (2, 3, 4), (2, 1, 10), (3, 1, 12)])
749
+
750
+ banks.display(mg)
751
+
752
+ """
753
+
754
+ mg = get_mg(edata, curr_cands = curr_cands)
755
+ trans_paths = list()
756
+ for s in seqs(mg.nodes):
757
+ if nx.algorithms.simple_paths.is_simple_path(mg, s):
758
+ if is_transitive(mg, s):
759
+ trans_paths.append(s)
760
+
761
+ maximal_paths = list()
762
+ #print("max paths")
763
+ for s in trans_paths:
764
+ is_max = True
765
+ for other_s in trans_paths:
766
+ if s != other_s:
767
+ if is_subsequence(s, other_s):
768
+ is_max = False
769
+ break
770
+ if is_max:
771
+ maximal_paths.append(s)
772
+
773
+ return sorted(list(set([p[0] for p in maximal_paths])))
774
+
775
+ def banks_with_explanation(edata, curr_cands = None):
776
+ """Return the Banks winners and the list of maximal chains in the majority graph.
777
+
778
+ Args:
779
+ edata (Profile, ProfileWithTies, MarginGraph): Any election data that has a `margin` method.
780
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
781
+
782
+ Returns:
783
+ A sorted list of candidates
784
+
785
+ A list of lists of candidates each representing a maximal chain in the majority graph
786
+
787
+ :Example:
788
+
789
+ .. plot:: margin_graphs_examples/mg_ex_banks.py
790
+ :context: reset
791
+ :include-source: True
792
+
793
+
794
+ .. code-block::
795
+
796
+ from pref_voting.c1_methods import banks_with_explanation
797
+
798
+ bws, maximal_chains = banks_with_explanation(mg)
799
+
800
+ print(f"Winning set: {bws}")
801
+ for c in maximal_chains:
802
+ print(f"Maximal chain: {c}")
803
+
804
+
805
+ .. exec_code::
806
+ :hide_code:
807
+
808
+ from pref_voting.weighted_majority_graphs import MarginGraph
809
+ from pref_voting.c1_methods import banks_with_explanation
810
+
811
+ mg = MarginGraph([0, 1, 2, 3], [(0, 2, 2), (0, 3, 6), (1, 0, 8), (2, 3, 4), (2, 1, 10), (3, 1, 12)])
812
+
813
+ bws, maximal_chains = banks_with_explanation(mg)
814
+
815
+ print(f"Winning set: {bws}")
816
+ for c in maximal_chains:
817
+ print(f"Maximal chain: {c}")
818
+
819
+ """
820
+
821
+ mg = get_mg(edata, curr_cands = curr_cands)
822
+ trans_paths = list()
823
+ for s in seqs(mg.nodes):
824
+ if nx.algorithms.simple_paths.is_simple_path(mg, s):
825
+ if is_transitive(mg, s):
826
+ trans_paths.append(s)
827
+
828
+ maximal_paths = list()
829
+ #print("max paths")
830
+ for s in trans_paths:
831
+ is_max = True
832
+ for other_s in trans_paths:
833
+ if s != other_s:
834
+ if is_subsequence(s, other_s):
835
+ is_max = False
836
+ break
837
+ if is_max:
838
+ maximal_paths.append(s)
839
+
840
+ return sorted(list(set([p[0] for p in maximal_paths]))), maximal_paths
841
+
842
+
843
+ def lin_order_to_rel(lin_order):
844
+ """Convert a linear order (a list of items) into a set of ordered pairs"""
845
+ els = sorted(lin_order)
846
+ rel = []
847
+ for a,b in combinations(els, 2):
848
+ if lin_order.index(a) < lin_order.index(b):
849
+ rel.append((a,b))
850
+ elif lin_order.index(b) < lin_order.index(a):
851
+ rel.append((b,a))
852
+ return rel
853
+
854
+
855
+ def slater_rankings(edata, curr_cands = None):
856
+ """
857
+ A Slater ranking is a linear order :math:`R` of the candidates that minimizes the number of edges in the majority graph we have to turn around before we obtain :math:`R`.
858
+
859
+ Args:
860
+ edata (Profile, ProfileWithTies, MarginGraph): Any election data that has a `margin` method.
861
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
862
+
863
+ Returns:
864
+ rankings: A list of Slater rankings.
865
+
866
+ dist: The minimum distance of the Slater rankings.
867
+
868
+ :Example:
869
+
870
+ .. exec_code::
871
+
872
+ from pref_voting.weighted_majority_graphs import MarginGraph
873
+ from pref_voting.c1_methods import slater_rankings
874
+
875
+ mg = MarginGraph([0, 1, 2, 3], [(0, 2, 2), (0, 3, 6), (1, 0, 8), (2, 3, 4), (2, 1, 10), (3, 1, 12)])
876
+
877
+ srs, d = slater_rankings(mg)
878
+ print(f"minimum distance: {d}")
879
+ for sr in srs:
880
+ print(f"ranking: {sr}")
881
+ """
882
+ candidates = edata.candidates if curr_cands is None else curr_cands
883
+ min_dist = np.inf
884
+
885
+ rankings = list()
886
+ for lin_order in permutations(candidates):
887
+ lo_rel = lin_order_to_rel(lin_order)
888
+
889
+ dist = distance_to_margin_graph(edata, lo_rel, exp = 0, curr_cands = curr_cands)
890
+ if dist < min_dist:
891
+ min_dist = dist
892
+ rankings = [lin_order]
893
+ elif dist == min_dist:
894
+ rankings.append(lin_order)
895
+ return rankings, min_dist
896
+
897
+ @vm(name = "Slater",
898
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
899
+ def slater(edata, curr_cands = None):
900
+ """A Slater ranking is a linear order :math:`R` of the candidates that minimizes the number of edges in the majority graph we have to turn around before we obtain :math:`R`. A candidate is a Slater winner if the candidate is the top element of some Slater ranking.
901
+
902
+ Args:
903
+ edata (Profile, ProfileWithTies, MarginGraph): Any election data that has a `margin` method.
904
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
905
+
906
+ Returns:
907
+ A sorted list of candidates
908
+
909
+
910
+ :Example:
911
+
912
+ .. plot:: margin_graphs_examples/mg_ex_slater.py
913
+ :context: reset
914
+ :include-source: True
915
+
916
+
917
+ .. code-block::
918
+
919
+ from pref_voting.c1_methods import slater
920
+
921
+ slater.display(prof)
922
+
923
+
924
+ .. exec_code::
925
+ :hide_code:
926
+
927
+ from pref_voting.weighted_majority_graphs import MarginGraph
928
+ from pref_voting.c1_methods import slater
929
+
930
+ mg = MarginGraph([0, 1, 2, 3], [(0, 2, 2), (0, 3, 6), (1, 0, 8), (2, 3, 4), (2, 1, 10), (3, 1, 12)])
931
+
932
+ slater.display(mg)
933
+
934
+ """
935
+ rankings, dist = slater_rankings(edata, curr_cands = curr_cands)
936
+
937
+ return sorted(list(set([r[0] for r in rankings])))
938
+
939
+ @vm(name = "Bipartisan Set",
940
+ input_types = [ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MAJORITY_GRAPH, ElectionTypes.MARGIN_GRAPH])
941
+ def bipartisan(edata, curr_cands = None, threshold = 0.0000001):
942
+ """The Bipartisan Set is the support of the (chosen) C1 maximal lottery.
943
+
944
+ Args:
945
+ edata (Profile, ProfileWithTies, MajorityGraph, MarginGraph): Any election data that has a `margin_matrix` attribute.
946
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
947
+
948
+ Returns:
949
+ A sorted list of candidates.
950
+ """
951
+
952
+ ml = c1_maximal_lottery(edata, curr_cands=curr_cands)
953
+ return sorted([c for c in ml.keys() if ml[c] > threshold])
954
+
955
+ c1_swf = [
956
+ copeland_ranking
957
+ ]
958
+
959
+ defeat_methods = [
960
+ top_cycle_defeat,
961
+ uc_gill_defeat,
962
+ uc_fish_defeat
963
+ ]