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,198 @@
1
+ import numpy as np
2
+ from pref_voting.spatial_profiles import SpatialProfile
3
+
4
+
5
+ def generate_covariance(n_dimensions, std, rho):
6
+ """
7
+ Generates a covariance matrix for a multivariate normal distribution with the given standard deviation and correlation coefficient.
8
+
9
+ Parameters
10
+ ----------
11
+ n_dimensions : int
12
+ The number of dimensions.
13
+ std : float
14
+ The standard deviation.
15
+ rho : float
16
+ The correlation coefficient.
17
+
18
+ Returns
19
+ -------
20
+ cov : numpy.ndarray
21
+ The covariance matrix for a multivariate normal distribution.
22
+ """
23
+ assert std > 0, "Standard deviation must be positive"
24
+ assert rho >= 0 and rho <= 1, "Correlation coefficient must be between 0 and 1"
25
+ assert n_dimensions > 0, "Number of dimensions must be positive"
26
+
27
+ cov = (std**2) * (np.eye(n_dimensions) + rho * (np.ones((n_dimensions, n_dimensions)) - np.eye(n_dimensions)))
28
+
29
+ return cov
30
+
31
+ def generate_spatial_profile(num_cands, num_voters, num_dims, cand_cov = None, voter_cov = None, num_profiles = 1):
32
+
33
+ """
34
+ Generates a spatial profile with the candidate and voter positions generated by a multivariate normal distribution with num_dims dimensions and cand_cov the covariance matrix for candidates and voter_cov the covariance matrix for voters.
35
+
36
+ Parameters
37
+ ----------
38
+ num_cands : int
39
+ The number of candidates.
40
+ num_voters : int
41
+ The number of voters.
42
+ num_dims : int
43
+ The number of dimensions.
44
+ cand_cov : numpy.ndarray, optional
45
+ The covariance matrix for the multivariate normal distribution for candidates. The default is None.
46
+ voter_cov : numpy.ndarray, optional
47
+ The covariance matrix for the multivariate normal distribution for voters. The default is the identity matrix.
48
+
49
+ Returns
50
+ -------
51
+ SpatialProfile
52
+ A spatial profile with the candidate and voter positions generated by a multivariate normal distribution with num_dims dimensions and cand_cov the covariance matrix for candidates and voter_cov the covariance matrix for voters.
53
+
54
+ """
55
+ cand_cov = np.eye(num_dims) if cand_cov is None else cand_cov
56
+ voter_cov = np.eye(num_dims) if voter_cov is None else voter_cov
57
+
58
+ assert num_dims == cand_cov.shape[0] == cand_cov.shape[1], "Candidate covariance matrix must be square and have the same number of dimensions as the number of dimensions specified"
59
+ assert num_dims == voter_cov.shape[0] == voter_cov.shape[1], "Voter covariance matrix must be square and have the same number of dimensions as the number of dimensions specified"
60
+
61
+ cand_mean = np.zeros(num_dims)
62
+ voter_mean = np.zeros(num_dims)
63
+
64
+ cand_samples = np.random.multivariate_normal(cand_mean, cand_cov, size=(num_profiles,num_cands))
65
+ voter_samples = np.random.multivariate_normal(voter_mean, voter_cov, size=(num_profiles,num_voters))
66
+
67
+ profs = [SpatialProfile({c: cand_samples[pidx][c] for c in range(num_cands)},
68
+ {v: voter_samples[pidx][v] for v in range(num_voters)})
69
+ for pidx in range(num_profiles)]
70
+
71
+ return profs[0] if num_profiles == 1 else profs
72
+
73
+ def generate_spatial_profile_polarized(
74
+ cand_clusters,
75
+ voter_clusters,
76
+ cluster_types = None,
77
+ num_profiles = 1):
78
+ """
79
+ Generates a spatial profile with polarized clusters of candidates and voters.
80
+
81
+ Parameters
82
+ ----------
83
+ cand_clusters : list
84
+ A list of tuples of the form (mean, covariance, number of candidates) for each cluster of candidates.
85
+ voter_clusters : list
86
+ A list of tuples of the form (mean, covariance, number of voters) for each cluster of voters.
87
+ cluster_types : dict, optional
88
+ A list of the same length as cand_cluster that associates each cluster to the type of candidate. The default is None.
89
+
90
+ num_profiles : int, optional
91
+ The number of profiles to generate. The default is 1.
92
+
93
+ Returns
94
+ -------
95
+ SpatialProfile
96
+ A spatial profile with polarized clusters of candidates and voters.
97
+ """
98
+
99
+ cluster_types = cluster_types if cluster_types is not None else list(range(len(cand_clusters)))
100
+ cand_samples = list()
101
+ candidate_types = {}
102
+ total_num_cands = 0
103
+ for cluster_idx, cand_cluster in enumerate(cand_clusters):
104
+ cand_mean, cand_cov, num_cands = cand_cluster
105
+ candidate_types.update({cidx: cluster_types[cluster_idx] for cidx in range(total_num_cands, total_num_cands+num_cands)})
106
+ total_num_cands += num_cands
107
+ cluster_samples = np.random.multivariate_normal(cand_mean, cand_cov,
108
+ size=(num_profiles,num_cands))
109
+ if len(cand_samples) == 0:
110
+ cand_samples = cluster_samples
111
+ else:
112
+ cand_samples = np.concatenate([cand_samples, cluster_samples], axis=1)
113
+
114
+ voter_samples = list()
115
+ total_num_voters = 0
116
+ for voter_cluster in voter_clusters:
117
+ voter_mean, voter_cov, num_voters = voter_cluster
118
+ total_num_voters += num_voters
119
+ cluster_samples = np.random.multivariate_normal(voter_mean, voter_cov,
120
+ size=(num_profiles,num_voters))
121
+ if len(voter_samples) == 0:
122
+ voter_samples = cluster_samples
123
+ else:
124
+ voter_samples = np.concatenate([voter_samples, cluster_samples], axis=1)
125
+
126
+
127
+ profs = [SpatialProfile({cidx: cand_samples[pidx][cidx]
128
+ for cidx in range(total_num_cands)},
129
+ {vidx: voter_samples[pidx][vidx]
130
+ for vidx in range(total_num_voters)},
131
+ candidate_types=candidate_types)
132
+ for pidx in range(num_profiles)]
133
+
134
+ return profs[0] if num_profiles == 1 else profs
135
+
136
+
137
+ def generate_spatial_profile_polarized_cands_randomly_polarized_voters(
138
+ cand_clusters,
139
+ num_voters,
140
+ voter_distributions,
141
+ cluster_types = None,
142
+ num_profiles = 1):
143
+ """
144
+ Generates a spatial profile with polarized clusters of candidates and voters.
145
+
146
+ Parameters
147
+ ----------
148
+ cand_clusters : list
149
+ A list of tuples of the form (mean, covariance, number of candidates) for each cluster of candidates.
150
+ num_voters : int
151
+ The number of voters.
152
+ voter_distributions : list
153
+ A list of tuples of the form (mean, covariance, prob) for each distribution of voters, where prob is the probability that a voter is assigned to this cluster.
154
+ cluster_types : dict, optional
155
+ A list of the same length as cand_cluster that associates each cluster to the type of candidate. The default is None.
156
+ num_profiles : int, optional
157
+ The number of profiles to generate. The default is 1.
158
+
159
+ Returns
160
+ -------
161
+ SpatialProfile
162
+ A spatial profile with polarized clusters of candidates and voters.
163
+ """
164
+
165
+ cluster_types = cluster_types if cluster_types is not None else list(range(len(cand_clusters)))
166
+ cand_samples = list()
167
+ candidate_types = {}
168
+ total_num_cands = 0
169
+ for cluster_idx, cand_cluster in enumerate(cand_clusters):
170
+ cand_mean, cand_cov, num_cands = cand_cluster
171
+ candidate_types.update({cidx: cluster_types[cluster_idx] for cidx in range(total_num_cands, total_num_cands+num_cands)})
172
+ total_num_cands += num_cands
173
+ cluster_samples = np.random.multivariate_normal(cand_mean, cand_cov, size=(num_profiles,num_cands))
174
+ if len(cand_samples) == 0:
175
+ cand_samples = cluster_samples
176
+ else:
177
+ cand_samples = np.concatenate([cand_samples, cluster_samples], axis=1)
178
+
179
+ all_potential_voters = list()
180
+ voter_cluster_probs = list()
181
+ for voter_distribution in voter_distributions:
182
+ voter_mean, voter_cov, prob = voter_distribution
183
+ all_potential_voters.append(np.random.multivariate_normal(voter_mean, voter_cov, size=(num_profiles, num_voters)))
184
+ voter_cluster_probs.append(prob)
185
+
186
+ # for each profile, randomly assign voters to clusters
187
+
188
+ #voters = range(num_voters)
189
+
190
+ voters_clusters_assignment = np.random.choice(len(all_potential_voters), size=(num_profiles, num_voters), p = voter_cluster_probs)
191
+ profs = [SpatialProfile({cidx: cand_samples[pidx][cidx]
192
+ for cidx in range(total_num_cands)},
193
+ {vidx: all_potential_voters[voters_clusters_assignment[pidx][vidx]][pidx][vidx]
194
+ for vidx in range(num_voters)},
195
+ candidate_types=candidate_types)
196
+ for pidx in range(num_profiles)]
197
+
198
+ return profs[0] if num_profiles == 1 else profs
@@ -0,0 +1,160 @@
1
+
2
+ '''
3
+ File: generate_utility_profiles.py
4
+ Author: Wes Holliday (wesholliday@berkeley.edu) and Eric Pacuit (epacuit@umd.edu)
5
+ Date: May 26, 2023
6
+
7
+ Functions to generate utility profiles.
8
+ '''
9
+
10
+
11
+ from math import ceil
12
+ import numpy as np
13
+ from scipy.spatial import distance
14
+ from functools import partial
15
+
16
+ from pref_voting.utility_profiles import UtilityProfile
17
+ from pref_voting.utility_functions import *
18
+
19
+ # turn off future warnings.
20
+ # getting the following warning when calling tabulate to display a profile:
21
+ # /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/tabulate.py:1027: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
22
+ # if headers == "keys" and not rows:
23
+ # see https://stackoverflow.com/questions/40659212/futurewarning-elementwise-comparison-failed-returning-scalar-but-in-the-futur
24
+ #
25
+ import warnings
26
+ warnings.simplefilter(action='ignore', category=FutureWarning)
27
+
28
+
29
+ def generate_utility_profile_uniform(num_candidates, num_voters, num_profiles = 1):
30
+ """
31
+ Generate a utility profile where each voter assigns a random number between 0 and 1 to each candidate.
32
+
33
+ Args:
34
+ num_candidates (int): The number of candidates.
35
+ num_voters (int): The number of voters.
36
+
37
+ Returns:
38
+ UtilityProfile: A utility profile.
39
+
40
+ """
41
+
42
+ cand_utils = np.random.uniform(size=(num_profiles, num_voters, num_candidates))
43
+
44
+ uprofs = [UtilityProfile([{c: cand_utils[pidx][v][c]
45
+ for c in range(num_candidates)}
46
+ for v in range(num_voters)])
47
+ for pidx in range(num_profiles)]
48
+
49
+ return uprofs if num_profiles > 1 else uprofs[0]
50
+
51
+ def generate_utility_profile_normal(num_candidates, num_voters, std = 0.1, normalize = None, num_profiles = 1):
52
+ """
53
+ Generate a utility profile where each voter assigns a random number drawn from a normal distribution with a randomly chosen mean (between 0 and 1) with standard deviation ``std`` to each candidate.
54
+
55
+ Args:
56
+ num_candidates (int): The number of candidates.
57
+ num_voters (int): The number of voters.
58
+ std (float): The standard deviation of the normal distribution. The default is 0.1.
59
+ normalize (str): The normalization method to use. The default is None.
60
+
61
+ Returns:
62
+ UtilityProfile: A utility profile.
63
+ """
64
+
65
+ mean_utilities = {c: np.random.uniform(0, 1) for c in range(num_candidates)}
66
+ cand_utils = {c: np.random.normal(mean_utilities[c], std, size=(num_profiles, num_voters)) for c in range(num_candidates)}
67
+
68
+ if normalize == "range":
69
+ uprofs = [UtilityProfile([{c: cand_utils[c][pidx][vidx]
70
+ for c in range(num_candidates)}
71
+ for vidx in range(num_voters)]).normalize_by_range()
72
+ for pidx in range(num_profiles)]
73
+ elif normalize == "score":
74
+ uprofs = [UtilityProfile([{c: cand_utils[c][pidx][vidx]
75
+ for c in range(num_candidates)}
76
+ for vidx in range(num_voters)]).normalize_by_standard_score()
77
+ for pidx in range(num_profiles)]
78
+ else: # do not normalize
79
+ uprofs = [UtilityProfile([{c: cand_utils[c][pidx][vidx]
80
+ for c in range(num_candidates)}
81
+ for vidx in range(num_voters)])
82
+ for pidx in range(num_profiles)]
83
+
84
+
85
+ return uprofs if num_profiles > 1 else uprofs[0]
86
+ utility_functions = {
87
+ "RM": {
88
+ "func": mixed_rm_utility,
89
+ "param": 1
90
+ },
91
+ "Linear": {
92
+ "func": linear_utility,
93
+ "param": None
94
+ },
95
+ "Quadratic":
96
+ {
97
+ "func": quadratic_utility,
98
+ "param": None
99
+ },
100
+ "Shepsle": {
101
+ "func": shepsle_utility,
102
+ "param": None
103
+ },
104
+ "City Block": {
105
+ "func": city_block_utility,
106
+ "param": None
107
+ },
108
+ "Matthews": {
109
+ "func": matthews_utility,
110
+ "param": None
111
+ }
112
+
113
+ }
114
+ def generate_spatial_utility_profile(num_cands,
115
+ num_voters,
116
+ num_dims = 2,
117
+ utility_function = "Quadratic",
118
+ utility_function_param = None):
119
+
120
+ """
121
+ Create a spatial utility profile using specified utility functions.
122
+
123
+
124
+ Args:
125
+ num_cands (int): The number of candidates.
126
+ num_voters (int): The number of voters.
127
+ num_dims (int): The number of dimensions. The default is 2.
128
+ utility_function (str): The utility function to use. The default is "Linear".
129
+ utility_function_param (float): The parameter of the utility function. The default is None.
130
+
131
+ Returns:
132
+ UtilityProfile: A spatial utility profile.
133
+ """
134
+
135
+ # the first component of the parameter is the number of dimensions,
136
+ # the second component is used to define the mixed model:
137
+ # beta = 1 is proximity model (i.e., squared Euclidean distance)
138
+
139
+ mean = [0] * num_dims # mean is 0 for each dimension
140
+ cov = np.diag([1] * num_dims) # diagonal covariance
141
+
142
+ _utility_fnc = utility_functions[utility_function]["func"]
143
+
144
+ if utility_functions[utility_function]["param"] is not None or utility_function_param is not None:
145
+ util_parm = utility_function_param if utility_function_param is not None else utility_functions[utility_function]["param"]
146
+ utility_fnc = partial(_utility_fnc, util_parm)
147
+
148
+ else:
149
+ utility_fnc = _utility_fnc
150
+
151
+ # sample candidate/voter positions using a multivariate normal distribution
152
+ cand_positions = np.random.multivariate_normal(np.array(mean), cov, num_cands)
153
+ voter_positions = np.random.multivariate_normal(np.array(mean), cov, num_voters)
154
+
155
+ utilities = [{c: utility_fnc(v_pos, c_pos) for c, c_pos in enumerate(cand_positions)}
156
+ for _, v_pos in enumerate(voter_positions)]
157
+
158
+ return UtilityProfile(utilities)
159
+
160
+