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.
- pref_voting/__init__.py +1 -0
- pref_voting/analysis.py +496 -0
- pref_voting/axiom.py +38 -0
- pref_voting/axiom_helpers.py +129 -0
- pref_voting/axioms.py +10 -0
- pref_voting/c1_methods.py +963 -0
- pref_voting/combined_methods.py +514 -0
- pref_voting/create_methods.py +128 -0
- pref_voting/data/examples/condorcet_winner/minimal_Anti-Plurality.soc +16 -0
- pref_voting/data/examples/condorcet_winner/minimal_Borda.soc +17 -0
- pref_voting/data/examples/condorcet_winner/minimal_Bracket_Voting.soc +20 -0
- pref_voting/data/examples/condorcet_winner/minimal_Bucklin.soc +19 -0
- pref_voting/data/examples/condorcet_winner/minimal_Coombs.soc +20 -0
- pref_voting/data/examples/condorcet_winner/minimal_Coombs_PUT.soc +20 -0
- pref_voting/data/examples/condorcet_winner/minimal_Coombs_TB.soc +20 -0
- pref_voting/data/examples/condorcet_winner/minimal_Dowdall.soc +19 -0
- pref_voting/data/examples/condorcet_winner/minimal_Instant_Runoff.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_Instant_Runoff_PUT.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_Instant_Runoff_TB.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_Iterated_Removal_Condorcet_Loser.soc +17 -0
- pref_voting/data/examples/condorcet_winner/minimal_Pareto.soc +17 -0
- pref_voting/data/examples/condorcet_winner/minimal_Plurality.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_PluralityWRunoff_PUT.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_Positive-Negative_Voting.soc +17 -0
- pref_voting/data/examples/condorcet_winner/minimal_Simplified_Bucklin.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_Superior_Voting.soc +19 -0
- pref_voting/data/examples/condorcet_winner/minimal_Weighted_Bucklin.soc +19 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Anti-Plurality.soc +17 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Borda.soc +17 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Bracket_Voting.soc +20 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Bucklin.soc +19 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Coombs.soc +21 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Coombs_PUT.soc +21 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Coombs_TB.soc +20 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Dowdall.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Instant_Runoff.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Instant_Runoff_PUT.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Instant_Runoff_TB.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Plurality.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_PluralityWRunoff_PUT.soc +18 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Positive-Negative_Voting.soc +17 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Simplified_Bucklin.soc +20 -0
- pref_voting/data/examples/condorcet_winner/minimal_resolute_Weighted_Bucklin.soc +19 -0
- pref_voting/data/voting_methods_properties.json +414 -0
- pref_voting/data/voting_methods_properties.json.lock +0 -0
- pref_voting/dominance_axioms.py +387 -0
- pref_voting/generate_profiles.py +801 -0
- pref_voting/generate_spatial_profiles.py +198 -0
- pref_voting/generate_utility_profiles.py +160 -0
- pref_voting/generate_weighted_majority_graphs.py +506 -0
- pref_voting/grade_methods.py +184 -0
- pref_voting/grade_profiles.py +357 -0
- pref_voting/helper.py +370 -0
- pref_voting/invariance_axioms.py +671 -0
- pref_voting/io/__init__.py +0 -0
- pref_voting/io/readers.py +432 -0
- pref_voting/io/writers.py +256 -0
- pref_voting/iterative_methods.py +2425 -0
- pref_voting/maj_graph_ex1.png +0 -0
- pref_voting/mappings.py +577 -0
- pref_voting/margin_based_methods.py +2345 -0
- pref_voting/monotonicity_axioms.py +872 -0
- pref_voting/num_evaluation_method.py +77 -0
- pref_voting/other_axioms.py +161 -0
- pref_voting/other_methods.py +939 -0
- pref_voting/pairwise_profiles.py +547 -0
- pref_voting/prob_voting_method.py +105 -0
- pref_voting/probabilistic_methods.py +287 -0
- pref_voting/profiles.py +856 -0
- pref_voting/profiles_with_ties.py +1069 -0
- pref_voting/rankings.py +466 -0
- pref_voting/scoring_methods.py +481 -0
- pref_voting/social_welfare_function.py +59 -0
- pref_voting/social_welfare_functions.py +7 -0
- pref_voting/spatial_profiles.py +448 -0
- pref_voting/stochastic_methods.py +99 -0
- pref_voting/strategic_axioms.py +1394 -0
- pref_voting/swf_axioms.py +173 -0
- pref_voting/utility_functions.py +102 -0
- pref_voting/utility_methods.py +178 -0
- pref_voting/utility_profiles.py +333 -0
- pref_voting/variable_candidate_axioms.py +640 -0
- pref_voting/variable_voter_axioms.py +3747 -0
- pref_voting/voting_method.py +355 -0
- pref_voting/voting_method_properties.py +92 -0
- pref_voting/voting_methods.py +8 -0
- pref_voting/voting_methods_registry.py +136 -0
- pref_voting/weighted_majority_graphs.py +1539 -0
- pref_voting-1.16.31.dist-info/METADATA +208 -0
- pref_voting-1.16.31.dist-info/RECORD +92 -0
- pref_voting-1.16.31.dist-info/WHEEL +4 -0
- 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
|