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,514 @@
1
+ """
2
+ File: iterative_methods.py
3
+ Author: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu)
4
+ Date: January 6, 2022
5
+ Revised: November 13, 2023
6
+
7
+ Implementations of voting methods that combine multiple methods
8
+ """
9
+
10
+ from pref_voting.voting_method import *
11
+ from pref_voting.scoring_methods import plurality, borda
12
+ from pref_voting.iterative_methods import iterated_removal_cl, instant_runoff, instant_runoff_put, instant_runoff_for_truncated_linear_orders
13
+ from pref_voting.profiles import _find_updated_profile, _num_rank
14
+
15
+ from pref_voting.c1_methods import condorcet, smith_set, copeland, top_cycle
16
+ from pref_voting.margin_based_methods import minimax, minimax_scores
17
+ from pref_voting.profiles import Profile
18
+ from pref_voting.profiles_with_ties import ProfileWithTies
19
+ from pref_voting.voting_method_properties import VotingMethodProperties, ElectionTypes
20
+
21
+ @vm(name = "Daunou",
22
+ input_types = [ElectionTypes.PROFILE])
23
+ def daunou(profile, curr_cands=None):
24
+ """Implementation of Daunou's voting method as described in the paper: https://link.springer.com/article/10.1007/s00355-020-01276-w
25
+
26
+ If there is a Condorcet winner, then that candidate is the winner. Otherwise, iteratively remove all Condorcet losers then select the plurality winner from among the remaining candidates.
27
+
28
+ Args:
29
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
30
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
31
+
32
+ Returns:
33
+ A sorted list of candidates
34
+
35
+ :Example:
36
+
37
+ .. exec_code::
38
+
39
+ from pref_voting.profiles import Profile
40
+ from pref_voting.combined_methods import daunou
41
+ from pref_voting.scoring_methods import plurality
42
+
43
+ prof = Profile([[1, 3, 2, 0], [0, 2, 3, 1], [1, 3, 0, 2], [3, 1, 0, 2]], [1, 1, 1, 1])
44
+
45
+ prof.display()
46
+
47
+ daunou.display(prof)
48
+ plurality.display(prof)
49
+
50
+ """
51
+
52
+ candidates = profile.candidates if curr_cands is None else curr_cands
53
+ cw = profile.condorcet_winner(curr_cands=candidates)
54
+ if cw is not None:
55
+ winners = [cw]
56
+ else:
57
+ cands_survive_it_rem_cl = iterated_removal_cl(profile, curr_cands=curr_cands)
58
+ winners = plurality(profile, curr_cands=cands_survive_it_rem_cl)
59
+
60
+ return sorted(winners)
61
+
62
+
63
+ @vm(name = "Blacks",
64
+ input_types = [ElectionTypes.PROFILE])
65
+ def blacks(profile, curr_cands=None):
66
+ """If a Condorcet winner exists return that winner. Otherwise, return the Borda winning set.
67
+
68
+ Args:
69
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
70
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
71
+
72
+ Returns:
73
+ A sorted list of candidates
74
+
75
+ :Example:
76
+
77
+ .. exec_code::
78
+
79
+ from pref_voting.profiles import Profile
80
+ from pref_voting.combined_methods import blacks
81
+ from pref_voting.scoring_methods import borda
82
+
83
+ prof = Profile([[2, 0, 1], [0, 1, 2], [2, 1, 0], [1, 2, 0]], [1, 1, 1, 1])
84
+
85
+ prof.display()
86
+
87
+ blacks.display(prof)
88
+ borda.display(prof)
89
+
90
+
91
+ """
92
+
93
+ cw = profile.condorcet_winner(curr_cands=curr_cands)
94
+
95
+ if cw is not None:
96
+ winners = [cw]
97
+ else:
98
+ winners = borda(profile, curr_cands=curr_cands)
99
+
100
+ return winners
101
+
102
+ @vm(name = "Smith IRV",
103
+ input_types = [ElectionTypes.PROFILE])
104
+ def smith_irv(profile, curr_cands=None):
105
+ """After restricting to the Smith Set, return the Instant Runoff winner.
106
+
107
+ Args:
108
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
109
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
110
+
111
+ Returns:
112
+ A sorted list of candidates
113
+
114
+ :Example:
115
+
116
+ .. exec_code::
117
+
118
+ from pref_voting.profiles import Profile
119
+ from pref_voting.combined_methods import smith_irv
120
+ from pref_voting.iterative_methods import instant_runoff, instant_runoff_put
121
+
122
+ prof = Profile([[0, 2, 1, 3], [1, 3, 0, 2], [2, 1, 3, 0], [2, 3, 0, 1]], [1, 1, 1, 1])
123
+
124
+ prof.display()
125
+
126
+ instant_runoff.display(prof)
127
+ instant_runoff_put.display(prof)
128
+ smith_irv.display(prof)
129
+
130
+ """
131
+
132
+ smith = smith_set(profile, curr_cands=curr_cands)
133
+
134
+ return instant_runoff(profile, curr_cands=smith)
135
+
136
+ @vm(name = "Smith IRV PUT",
137
+ input_types = [ElectionTypes.PROFILE])
138
+ def smith_irv_put(profile, curr_cands=None):
139
+ """After restricting to the Smith Set, return the Instant Runoff winner.
140
+
141
+ Args:
142
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
143
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
144
+
145
+ Returns:
146
+ A sorted list of candidates
147
+
148
+ :Example:
149
+
150
+ .. exec_code::
151
+
152
+ from pref_voting.profiles import Profile
153
+ from pref_voting.combined_methods import smith_irv_put
154
+ from pref_voting.iterative_methods import instant_runoff, instant_runoff_put
155
+
156
+ prof = Profile([[0, 2, 1, 3], [1, 3, 0, 2], [2, 1, 3, 0], [2, 3, 0, 1]], [1, 1, 1, 1])
157
+
158
+ prof.display()
159
+
160
+ instant_runoff.display(prof)
161
+ instant_runoff_put.display(prof)
162
+ smith_irv_put.display(prof)
163
+
164
+ """
165
+
166
+ smith = smith_set(profile, curr_cands=curr_cands)
167
+
168
+ return instant_runoff_put(profile, curr_cands=smith)
169
+
170
+ @vm(name = "Condorcet IRV",
171
+ input_types = [ElectionTypes.PROFILE])
172
+ def condorcet_irv(profile, curr_cands=None, algorithm = "basic", **kwargs):
173
+ """If a Condorcet winner exists, elect that candidate, otherwise return the instant runoff winners.
174
+
175
+ Args:
176
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
177
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
178
+ algorithm (str, optional): The algorithm to use. Options are "basic" and "recursive". The default is "basic".
179
+
180
+ Returns:
181
+ A sorted list of candidates
182
+
183
+ :Example:
184
+
185
+ .. exec_code::
186
+
187
+ from pref_voting.profiles import Profile
188
+ from pref_voting.combined_methods import condorcet_irv
189
+ from pref_voting.iterative_methods import instant_runoff, instant_runoff_put
190
+
191
+ prof = Profile([[0, 2, 1, 3], [1, 3, 0, 2], [2, 1, 3, 0], [2, 3, 0, 1]], [1, 1, 1, 1])
192
+
193
+ prof.display()
194
+
195
+ instant_runoff.display(prof)
196
+ instant_runoff_put.display(prof)
197
+ condorcet_irv.display(prof)
198
+
199
+ """
200
+
201
+ cw = profile.condorcet_winner(curr_cands=curr_cands)
202
+ if cw is not None:
203
+ return [cw]
204
+ else:
205
+
206
+ return instant_runoff(profile, curr_cands=curr_cands, algorithm=algorithm, **kwargs)
207
+
208
+ @vm(name = "Condorcet IRV PUT",
209
+ input_types = [ElectionTypes.PROFILE])
210
+ def condorcet_irv_put(profile, curr_cands=None):
211
+ """If a Condorcet winner exists, elect that candidate, otherwise return the instant runoff put winners.
212
+
213
+ Args:
214
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
215
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
216
+
217
+ Returns:
218
+ A sorted list of candidates
219
+
220
+ :Example:
221
+
222
+ .. exec_code::
223
+
224
+ from pref_voting.profiles import Profile
225
+ from pref_voting.combined_methods import condorcet_irv_put
226
+ from pref_voting.iterative_methods import instant_runoff, instant_runoff_put
227
+
228
+ prof = Profile([[0, 2, 1, 3], [1, 3, 0, 2], [2, 1, 3, 0], [2, 3, 0, 1]], [1, 1, 1, 1])
229
+
230
+ prof.display()
231
+
232
+ instant_runoff.display(prof)
233
+ instant_runoff_put.display(prof)
234
+ condorcet_irv_put.display(prof)
235
+
236
+ """
237
+
238
+ cw = profile.condorcet_winner(curr_cands=curr_cands)
239
+ if cw is not None:
240
+ return [cw]
241
+ else:
242
+ return instant_runoff_put(profile, curr_cands=curr_cands)
243
+
244
+ def compose(vm1, vm2):
245
+ """After restricting the profile to the set of vm1 winners, run vm2
246
+
247
+ Args:
248
+ vm1, vm2 (VotingMethod): The voting methods to be composed.
249
+
250
+ Returns:
251
+ A VotingMethod that composes vm1 and vm2.
252
+
253
+ :Example:
254
+
255
+ .. exec_code::
256
+
257
+ from pref_voting.profiles import Profile
258
+ from pref_voting.combined_methods import compose
259
+ from pref_voting.scoring_methods import borda
260
+ from pref_voting.c1_methods import copeland
261
+
262
+ prof = Profile([[1, 3, 0, 2], [2, 1, 3, 0], [3, 0, 2, 1]], [1, 2, 1])
263
+
264
+ prof.display()
265
+
266
+ copeland_borda = compose(copeland, borda)
267
+
268
+ copeland.display(prof)
269
+ borda.display(prof)
270
+ copeland_borda.display(prof)
271
+
272
+ """
273
+
274
+ def _vm(edata, curr_cands=None):
275
+
276
+ vm1_ws = vm1(edata, curr_cands=curr_cands)
277
+
278
+ return vm2(edata, curr_cands=vm1_ws)
279
+
280
+ return VotingMethod(_vm, name=f"{vm1.name}-{vm2.name}")
281
+
282
+
283
+ def _compose(vm1, vm2):
284
+ """
285
+ Same as compose, but used to make it easier to document composed voting methods.
286
+ """
287
+
288
+ def _vm(edata, curr_cands=None):
289
+
290
+ vm1_ws = vm1(edata, curr_cands=curr_cands)
291
+
292
+ return vm2(edata, curr_cands=vm1_ws)
293
+
294
+ return _vm
295
+
296
+ @vm(name = "Condorcet Plurality",
297
+ input_types = [ElectionTypes.PROFILE])
298
+ def condorcet_plurality(profile, curr_cands = None):
299
+ """Return the Condorcet winner if one exists, otherwise return the plurality winners.
300
+
301
+ Args:
302
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
303
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
304
+
305
+ Returns:
306
+ A sorted list of candidates
307
+
308
+ """
309
+
310
+ return _compose(condorcet, plurality)(profile, curr_cands=curr_cands)
311
+
312
+
313
+ @vm(name="Smith-Minimax",
314
+ input_types=[ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MARGIN_GRAPH])
315
+ def smith_minimax(edata, curr_cands = None):
316
+ """Return the Minimax winner after restricting to the Smith set.
317
+
318
+ Args:
319
+ profile (Profile, ProfileWithTies, MarginGraph): An anonymous profile of linear orders on a set of candidates
320
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
321
+
322
+ Returns:
323
+ A sorted list of candidates
324
+
325
+ """
326
+
327
+ return _compose(top_cycle, minimax)(edata, curr_cands=curr_cands)
328
+
329
+ @vm(name="Copeland-Local-Borda",
330
+ input_types=[ElectionTypes.PROFILE, ElectionTypes.MARGIN_GRAPH])
331
+ def copeland_local_borda(edata, curr_cands = None):
332
+ """Return the Borda winner after restricting to the Copeland winners.
333
+
334
+ Args:
335
+ profile (Profile, MarginGraph): An anonymous profile of linear orders on a set of candidates
336
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
337
+
338
+ Returns:
339
+ A sorted list of candidates
340
+
341
+ """
342
+ return _compose(copeland, borda)(edata, curr_cands=curr_cands)
343
+
344
+ @vm(name="Copeland-Local-Minimax",
345
+ input_types=[ElectionTypes.PROFILE, ElectionTypes.MARGIN_GRAPH])
346
+ def copeland_local_minimax(edata, curr_cands = None):
347
+ """Return the Minimax winner after restricting to the Copeland winners.
348
+
349
+ Args:
350
+ profile (Profile, MarginGraph): An anonymous profile of linear orders on a set of candidates
351
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
352
+
353
+ Returns:
354
+ A sorted list of candidates
355
+
356
+ """
357
+ return _compose(copeland, minimax)(edata, curr_cands=curr_cands)
358
+
359
+ def voting_method_with_scoring_tiebreaker(vm, score, name):
360
+
361
+ def _vm(profile, curr_cands=None):
362
+
363
+ vm_ws = vm(profile, curr_cands=curr_cands)
364
+
365
+ if len(vm_ws) == 1:
366
+ return vm_ws
367
+
368
+ # get (restricted) rankings
369
+ _rankings, rcounts = profile.rankings_counts
370
+
371
+ cands_to_ignore = np.array([c for c in profile.candidates if c not in curr_cands]) if curr_cands is not None else np.array([])
372
+
373
+ rankings = _rankings if curr_cands is None else _find_updated_profile(np.array(_rankings), cands_to_ignore, len(profile.candidates))
374
+
375
+ curr_cands = profile.candidates if curr_cands is None else curr_cands
376
+
377
+ # find the candidate scores using the score function
378
+ cand_scores = {c: sum(_num_rank(rankings, rcounts, c, level) * score(len(curr_cands), level) for level in range(1, len(curr_cands) + 1)) for c in curr_cands}
379
+
380
+ max_ws_score = max([cand_scores[w] for w in vm_ws])
381
+
382
+ return sorted([w for w in vm_ws if cand_scores[w] == max_ws_score])
383
+
384
+ return _vm
385
+
386
+ @vm(name="Copeland-Global-Borda",
387
+ input_types=[ElectionTypes.PROFILE])
388
+ def copeland_global_borda(profile, curr_cands=None):
389
+ """From the Copeland winners, return the candidate with the largest *global* Borda score.
390
+
391
+ Args:
392
+ profile (Profile): An anonymous profile of linear orders on a set of candidates
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 sorted list of candidates
397
+
398
+ """
399
+
400
+ return voting_method_with_scoring_tiebreaker(copeland, lambda num_cands, rank : num_cands - rank, "Copeland-Global-Borda")(profile, curr_cands=curr_cands)
401
+
402
+
403
+ @vm(name="Copeland-Global-Minimax",
404
+ input_types=[ElectionTypes.PROFILE, ElectionTypes.PROFILE_WITH_TIES, ElectionTypes.MARGIN_GRAPH])
405
+ def copeland_global_minimax(edata, curr_cands=None):
406
+ """From the Copeland winners, return the candidates with the best *global* Minimax score.
407
+
408
+ Args:
409
+ edata (Profile, ProfileWithTies, MarginGraph): Any edata with a Margin method.
410
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
411
+
412
+ Returns:
413
+ A sorted list of candidates
414
+
415
+ """
416
+
417
+ curr_cands = edata.candidates if curr_cands is None else curr_cands
418
+
419
+ copeland_ws = copeland(edata, curr_cands=curr_cands)
420
+
421
+ mm_scores = minimax_scores(edata, curr_cands=curr_cands)
422
+
423
+ best_score = max([mm_scores[c] for c in copeland_ws])
424
+
425
+ return sorted([c for c in copeland_ws if mm_scores[c] == best_score])
426
+
427
+ def faceoff(vm1, vm2):
428
+ """If the vm1 and vm2 winners are the same, return that set of winners. Otherwise, for each choice of a vm1 winner A and vm2 winner B, add to the ultimate winners whichever of A or B is majority preferred to the other (or both if they are tied).
429
+
430
+ Args:
431
+ vm1, vm2 (VotingMethod): The voting methods to faceoff.
432
+
433
+ Returns:
434
+ A VotingMethod that runs the faceoff of vm1 and vm2.
435
+
436
+ """
437
+
438
+ def _vm(edata, curr_cands=None):
439
+
440
+ curr_cands = edata.candidates if curr_cands is None else curr_cands
441
+
442
+ vm1_winners = vm1(edata, curr_cands)
443
+ vm2_winners = vm2(edata, curr_cands)
444
+
445
+ if vm1_winners == vm2_winners:
446
+ return vm1_winners
447
+
448
+ else:
449
+ winners = list()
450
+
451
+ for a in vm1_winners:
452
+ for b in vm2_winners:
453
+ if edata.margin(a,b) > 0:
454
+ winners.append(a)
455
+ elif edata.margin(b,a) > 0:
456
+ winners.append(b)
457
+ elif edata.margin(a,b) == 0:
458
+ winners.append(a)
459
+ winners.append(b)
460
+
461
+ return list(set(winners))
462
+
463
+ return VotingMethod(_vm, name=f"{vm1.name}-{vm2.name} Faceoff")
464
+
465
+ def _faceoff(vm1, vm2):
466
+ """
467
+ Same as faceoff, but used to make it easier to document faceoff voting methods.
468
+ """
469
+
470
+ def _vm(edata, curr_cands=None):
471
+
472
+ curr_cands = edata.candidates if curr_cands is None else curr_cands
473
+
474
+ vm1_winners = vm1(edata, curr_cands)
475
+ vm2_winners = vm2(edata, curr_cands)
476
+
477
+ if vm1_winners == vm2_winners:
478
+ return vm1_winners
479
+
480
+ else:
481
+ winners = list()
482
+
483
+ for a in vm1_winners:
484
+ for b in vm2_winners:
485
+ if edata.margin(a,b) > 0:
486
+ winners.append(a)
487
+ elif edata.margin(b,a) > 0:
488
+ winners.append(b)
489
+ elif edata.margin(a,b) == 0:
490
+ winners.append(a)
491
+ winners.append(b)
492
+
493
+ return list(set(winners))
494
+
495
+ return _vm
496
+
497
+ @vm(name="Borda-Minimax Faceoff",
498
+ input_types=[ElectionTypes.PROFILE])
499
+ def borda_minimax_faceoff(edata, curr_cands=None):
500
+ """If the Borda and Minimax winners are the same, return that set of winners. Otherwise, for each choice of a Borda winner A and Minimax winner B, add to the ultimate winners whichever of A or B is majority preferred to the other (or both if they are tied).
501
+
502
+ Args:
503
+ profile (Profile, MarginGraph): An anonymous profile of linear orders on a set of candidates
504
+ curr_cands (List[int], optional): If set, then find the winners for the profile restricted to the candidates in ``curr_cands``
505
+
506
+ Returns:
507
+ A sorted list of candidates
508
+
509
+ ..note:
510
+ Proposed by Edward B. Foley.
511
+
512
+ """
513
+
514
+ return _faceoff(borda, minimax)(edata, curr_cands=curr_cands)
@@ -0,0 +1,128 @@
1
+ '''
2
+ File: create_methods.py
3
+ Author: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu)
4
+ Date: August 8, 2024
5
+
6
+ '''
7
+
8
+ def compose(vm1, vm2):
9
+ """After restricting the profile to the set of vm1 winners, run vm2
10
+
11
+ Args:
12
+ vm1, vm2 (VotingMethod): The voting methods to be composed.
13
+
14
+ Returns:
15
+ A VotingMethod that composes vm1 and vm2.
16
+
17
+ :Example:
18
+
19
+ .. exec_code::
20
+
21
+ from pref_voting.profiles import Profile
22
+ from pref_voting.combined_methods import compose
23
+ from pref_voting.scoring_methods import borda
24
+ from pref_voting.c1_methods import copeland
25
+
26
+ prof = Profile([[1, 3, 0, 2], [2, 1, 3, 0], [3, 0, 2, 1]], [1, 2, 1])
27
+
28
+ prof.display()
29
+
30
+ copeland_borda = compose(copeland, borda)
31
+
32
+ copeland.display(prof)
33
+ borda.display(prof)
34
+ copeland_borda.display(prof)
35
+
36
+ """
37
+
38
+ def _vm(edata, curr_cands=None):
39
+
40
+ vm1_ws = vm1(edata, curr_cands=curr_cands)
41
+
42
+ return vm2(edata, curr_cands=vm1_ws)
43
+
44
+ return VotingMethod(_vm, name=f"{vm1.name}-{vm2.name}")
45
+
46
+
47
+ def _compose(vm1, vm2):
48
+ """
49
+ Same as compose, but used to make it easier to document composed voting methods.
50
+ """
51
+
52
+ def _vm(edata, curr_cands=None):
53
+
54
+ vm1_ws = vm1(edata, curr_cands=curr_cands)
55
+
56
+ return vm2(edata, curr_cands=vm1_ws)
57
+
58
+ return _vm
59
+
60
+ def faceoff(vm1, vm2):
61
+ """If the vm1 and vm2 winners are the same, return that set of winners. Otherwise, for each choice of a vm1 winner A and vm2 winner B, add to the ultimate winners whichever of A or B is majority preferred to the other (or both if they are tied).
62
+
63
+ Args:
64
+ vm1, vm2 (VotingMethod): The voting methods to faceoff.
65
+
66
+ Returns:
67
+ A VotingMethod that runs the faceoff of vm1 and vm2.
68
+
69
+ """
70
+
71
+ def _vm(edata, curr_cands=None):
72
+
73
+ curr_cands = edata.candidates if curr_cands is None else curr_cands
74
+
75
+ vm1_winners = vm1(edata, curr_cands)
76
+ vm2_winners = vm2(edata, curr_cands)
77
+
78
+ if vm1_winners == vm2_winners:
79
+ return vm1_winners
80
+
81
+ else:
82
+ winners = list()
83
+
84
+ for a in vm1_winners:
85
+ for b in vm2_winners:
86
+ if edata.margin(a,b) > 0:
87
+ winners.append(a)
88
+ elif edata.margin(b,a) > 0:
89
+ winners.append(b)
90
+ elif edata.margin(a,b) == 0:
91
+ winners.append(a)
92
+ winners.append(b)
93
+
94
+ return list(set(winners))
95
+
96
+ return VotingMethod(_vm, name=f"{vm1.name}-{vm2.name} Faceoff")
97
+
98
+ def _faceoff(vm1, vm2):
99
+ """
100
+ Same as faceoff, but used to make it easier to document faceoff voting methods.
101
+ """
102
+
103
+ def _vm(edata, curr_cands=None):
104
+
105
+ curr_cands = edata.candidates if curr_cands is None else curr_cands
106
+
107
+ vm1_winners = vm1(edata, curr_cands)
108
+ vm2_winners = vm2(edata, curr_cands)
109
+
110
+ if vm1_winners == vm2_winners:
111
+ return vm1_winners
112
+
113
+ else:
114
+ winners = list()
115
+
116
+ for a in vm1_winners:
117
+ for b in vm2_winners:
118
+ if edata.margin(a,b) > 0:
119
+ winners.append(a)
120
+ elif edata.margin(b,a) > 0:
121
+ winners.append(b)
122
+ elif edata.margin(a,b) == 0:
123
+ winners.append(a)
124
+ winners.append(b)
125
+
126
+ return list(set(winners))
127
+
128
+ return _vm
@@ -0,0 +1,16 @@
1
+ # FILE NAME: minimal_Anti-Plurality.soc
2
+ # TITLE:
3
+ # DESCRIPTION:
4
+ # DATA TYPE: soc
5
+ # MODIFICATION TYPE:
6
+ # RELATES TO:
7
+ # RELATED FILES:
8
+ # PUBLICATION DATE:
9
+ # MODIFICATION DATE:
10
+ # NUMBER ALTERNATIVES: 3
11
+ # NUMBER VOTERS: 2
12
+ # NUMBER UNIQUE ORDERS: 1
13
+ # ALTERNATIVE NAME 0: 0
14
+ # ALTERNATIVE NAME 1: 1
15
+ # ALTERNATIVE NAME 2: 2
16
+ 2: 2, 1, 0
@@ -0,0 +1,17 @@
1
+ # FILE NAME: minimal_Borda.soc
2
+ # TITLE:
3
+ # DESCRIPTION:
4
+ # DATA TYPE: soc
5
+ # MODIFICATION TYPE:
6
+ # RELATES TO:
7
+ # RELATED FILES:
8
+ # PUBLICATION DATE:
9
+ # MODIFICATION DATE:
10
+ # NUMBER ALTERNATIVES: 3
11
+ # NUMBER VOTERS: 3
12
+ # NUMBER UNIQUE ORDERS: 2
13
+ # ALTERNATIVE NAME 0: 0
14
+ # ALTERNATIVE NAME 1: 1
15
+ # ALTERNATIVE NAME 2: 2
16
+ 2: 1, 2, 0
17
+ 1: 2, 0, 1
@@ -0,0 +1,20 @@
1
+ # FILE NAME: minimal_Bracket_Voting.soc
2
+ # TITLE:
3
+ # DESCRIPTION:
4
+ # DATA TYPE: soc
5
+ # MODIFICATION TYPE:
6
+ # RELATES TO:
7
+ # RELATED FILES:
8
+ # PUBLICATION DATE:
9
+ # MODIFICATION DATE:
10
+ # NUMBER ALTERNATIVES: 5
11
+ # NUMBER VOTERS: 3
12
+ # NUMBER UNIQUE ORDERS: 3
13
+ # ALTERNATIVE NAME 0: 0
14
+ # ALTERNATIVE NAME 1: 1
15
+ # ALTERNATIVE NAME 2: 2
16
+ # ALTERNATIVE NAME 3: 3
17
+ # ALTERNATIVE NAME 4: 4
18
+ 1: 2, 1, 4, 3, 0
19
+ 1: 3, 1, 4, 2, 0
20
+ 1: 4, 1, 3, 2, 0