Utility functions
Vector Distances
distribution_distance_l1(dist_1, dist_2)
Normalized L1 distance between distributions (0..1).
L1 max for distributions is 2, so normalize by 2.
Source code in src/utils/distance_functions.py
133 134 135 136 137 138 139 140 141 142 143 144 | |
kendall_tau_on_ranks(rank_arr_1, rank_arr_2, search_pairs, color_vec)
Beware: don't use this for orderings!
This function calculates the kendal tau distance between two rank vektors. (The Kendall tau rank distance is a metric that counts the number of pairwise disagreements between two orderings. The larger the distance, the more dissimilar the two lists are. Kendall tau distance is also called bubble-sort distance). Rank vectors hold the rank of each option (option = index). Not to be confused with an ordering (or sequence) where the vector holds options and the index is the rank.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rank_arr_1
|
FloatArray
|
First Array containing the ranks of each option. |
required |
rank_arr_2
|
FloatArray
|
The second rank array. |
required |
search_pairs
|
Sequence[tuple[int, int]]
|
The pairs of indices. |
required |
color_vec
|
IntArray
|
(IntArray): The vector of colors (for efficiency). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
Kendall tau distance. |
Source code in src/utils/distance_functions.py
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
kendall_tau_order(ordering_1, ordering_2, search_pairs)
Normalized Kendall tau distance on orderings (permutations).
Ordering: index = rank, value = option id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ordering_1
|
IntArray
|
First (NumPy) array containing ranked options. |
required |
ordering_2
|
IntArray
|
The second ordering array. |
required |
search_pairs
|
Sequence[tuple[int, int]]
|
Index pairs. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
float
|
Normalized kendall tau distance |
Source code in src/utils/distance_functions.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | |
l1_score_distance(score_1, score_2)
L1 distance between score vectors (lower=better).
Source code in src/utils/distance_functions.py
122 123 124 125 126 127 128 129 130 | |
spearman_distance(rank_arr_1, rank_arr_2)
Beware: don't use this for orderings!
This function calculates the Spearman distance between two rank vektors. Spearman's foot rule is a measure of the distance between ranked lists. It is given as the sum of the absolute differences between the ranks of the two lists. This function is meant to work with numeric values as well. Hence, we only assume the rank values to be comparable (e.q. normalized).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rank_arr_1
|
FloatArray
|
Array containing the ranks of each option |
required |
rank_arr_2
|
FloatArray
|
The second rank array. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
The Spearman distance |
Source code in src/utils/distance_functions.py
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
spearman_footrule_ranks(rank_arr_1, rank_arr_2)
Normalized Spearman footrule on rank vectors.
Rank vector: index = option id, value = rank (0 best).
Source code in src/utils/distance_functions.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | |
spearman_fr_order(ordering_1, ordering_2, _search_pairs=None)
Normalized Spearman footrule on orderings.
Ordering: index = rank, value = option id.
Source code in src/utils/distance_functions.py
100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
unnormalized_kendall_tau(ordering_1, ordering_2, search_pairs)
This function calculates the kendal tau distance on two orderings. An ordering holds the option names in the order of their rank (rank=index).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ordering_1
|
IntArray
|
First Array containing ranked options. |
required |
ordering_2
|
IntArray
|
The second ordering array. |
required |
search_pairs
|
Sequence[tuple[int, int]]
|
Index pairs (for efficiency). |
required |
Returns:
| Type | Description |
|---|---|
int
|
The kendall tau distance |
Source code in src/utils/distance_functions.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
Simulation Indicators
get_grid_colors(model)
Return the current grid state as an array of rows (row-major): result[y, x] == color at position (x, y)
Source code in src/utils/metrics.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
gini_index_0_100(values)
Compute the Gini index (0-100) for a 1D sequence of non-negative values.
Edge cases
- empty / 1 element -> 0
- all zeros -> 0
Source code in src/utils/metrics.py
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
Social Choice
Representation contract: - Input: ScoreVector table (pref_table) * rows = agents * columns = options * values = disagreement/oppose scores (lower = better) - Output: Ordering (permutation) with best option first.
This design allows non-discrete and non-equidistant preferences.
Implemented rules (schema B1): - plurality_rule (first-choice plurality after tie-prep) - approval_voting (canonical fixed-threshold approval mapping) - approval_voting_custom (legacy adaptive approval mapping; non-canonical) - utilitarian_rule (minimize total disagreement) - borda_rule (positional scoring derived from per-voter orderings) - schulze_rule (pairwise strongest-path ranking on options) - random_rule (uniform full random ranking, seed-deterministic)
approval_voting(pref_table, *, rng)
Canonical approval voting with fixed threshold mapping.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pref_table
|
ndarray
|
ScoreVector table (disagreement values). per agent (rows) per option (cols), lower = better. |
required |
rng
|
Generator
|
Random number generator. |
required |
Returns: np.ndarray: Ordering (permutation) of options.
Source code in src/utils/social_welfare_functions.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | |
approval_voting_custom(pref_table, *, rng)
Legacy/custom approval mapping using adaptive per-voter threshold (mean-variance).
Kept for exploratory comparisons; not used as canonical approval in thesis baseline.
Source code in src/utils/social_welfare_functions.py
171 172 173 174 175 176 177 178 179 180 181 | |
borda_rule(pref_table, *, rng)
Borda count derived from per-voter orderings of the ScoreVector.
We interpret each row as a (possibly tied) preference ordering by sorting scores ascending (lower=better). Then assign Borda points: best gets m-1, next m-2, ... worst gets 0.
Tie-breaking:
- per-voter randomized tie-breaking using rng via scores_to_ordering.
- final ordering tie-breaks by a secondary random key.
Source code in src/utils/social_welfare_functions.py
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
complete_ranking(ordering, num_options, *, rng)
This function adds options that are not in the ordering in a random order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ordering
|
ndarray
|
Partial ordering (permutation) of option indices. |
required |
num_options
|
int
|
The total number of options. |
required |
rng
|
Generator
|
Random number generator. |
required |
Returns:
np.ndarray: Completed ordering of length num_options.
Source code in src/utils/social_welfare_functions.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | |
continuous_score_voting(pref_table, *, rng)
Continuous score voting returning an Ordering (permutation) with RNG noise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pref_table
|
ndarray
|
ScoreVector table (disagreement values). per agent (rows) per option (cols), lower = better. |
required |
rng
|
Generator
|
Random number generator. |
required |
Source code in src/utils/social_welfare_functions.py
359 360 361 362 363 364 365 366 367 368 369 370 371 | |
imp_prepr_for_approval(pref_table)
This is just like preprocessing_for_approval, but more intelligent. It sets the threshold depending on the variances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pref_table
|
ndarray
|
Preferences table. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: Binary approvals with shape of |
Source code in src/utils/social_welfare_functions.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
plurality_rule(pref_table, *, rng)
This function implements the plurality-rule social welfare function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pref_table
|
ndarray
|
ScoreVector table (disagreement values) per agent (rows) per option (cols), lower = better. |
required |
rng
|
Generator
|
Random number generator. |
required |
Returns: np.ndarray: Ordering (permutation) of options.
Source code in src/utils/social_welfare_functions.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
preprocessing_for_approval(pref_table, threshold=None)
Interpret values below threshold as approval.
This function prepares the preference table for approval voting by interpreting every value below a threshold as an approval. Beware: the values are distance/disagreement => smaller = less disagreement The standard threshold is 1/m (m = number of options). The reasoning is that if the preferences are normalized, 1/m ensures the threshold to be proportionate to the number of options. It also ensures that, on average, half of the options will be approved. The actual number of approved options, however, can still vary depending on the specific values in the preference table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pref_table
|
ndarray
|
Preferences table. |
required |
threshold
|
float | None
|
Approval threshold; defaults to 1/m. |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: Binary approvals with shape of |
Source code in src/utils/social_welfare_functions.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
random_rule(pref_table, *, rng)
Uniform random full ranking of options.
Semantics:
- Ignores preference magnitudes intentionally.
- Returns a full permutation sampled uniformly from all m! rankings.
- Deterministic for fixed RNG state/seed.
Source code in src/utils/social_welfare_functions.py
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | |
run_tie_breaking_preparation_for_plurality(pref_table, eps=1e-09, *, rng)
Prepare ballots for plurality rule by breaking only first-choice ties.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pref_table
|
ndarray
|
Preferences per agent (rows) per option (cols). |
required |
eps
|
float
|
Tiny jitter amplitude used only on tied minimal entries. |
1e-09
|
rng
|
Generator
|
Random number generator. |
required |
Returns: np.ndarray: Table where per-row minimal-score ties are broken.
Source code in src/utils/social_welfare_functions.py
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
schulze_rule(pref_table, *, rng)
Schulze method on option candidates using pairwise strongest paths.
Semantics:
- Candidates are option ids (columns of pref_table).
- Lower score = better. For voter v, candidate i is preferred to j iff
pref_table[v, i] < pref_table[v, j] (strict comparison).
- Exact score ties are neutral and contribute to neither side.
Complexity:
- O(n * m^2 + m^3), with n voters and m candidates.
- Guarded by SCHULZE_MAX_OPTIONS for predictable runtime.
Source code in src/utils/social_welfare_functions.py
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | |
utilitarian_rule(pref_table, *, rng)
Utilitarian (score) rule on disagreement scores.
Semantics: - pref_table entries are disagreement/oppose scores in [0,1], lower=better. - Aggregate by minimizing total disagreement across voters: total[j] = sum_i pref_table[i, j] Lower total => better.
Tie-breaking:
- Random but deterministic given rng, using a secondary random key.
Source code in src/utils/social_welfare_functions.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |