-
Notifications
You must be signed in to change notification settings - Fork 0
/
numpy_power_function.py
112 lines (86 loc) · 3.9 KB
/
numpy_power_function.py
1
2
3
4
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
33
34
35
36
37
38
39
40
41
42
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import numpy as np
def computer_power_numpy(
u_matrix: np.ndarray,
subset_masks: np.ndarray,
T: float,
):
"""
This function computes the difference between the proportion of voting power for a locality
and the proportion of the population that town has.
Equivalent to `compute_power_numpy_simple` with the exception that the subsets in `subset_masks`
assumes that one town, in particular, always votes in the affirmative. This reduces the
memory cost of the computation but otherwise does not affect the speed significantly.
Args:
u_matrix (numpy.ndarray): Array of size (num_towns, 1) that contains the weights to assign
to each town.
subset_masks (numpy.ndarray): Array of size (num_towns, num_subsets) that contains
boolean masks for whether a town voted in favor of a particular measure. Note:
it is generally assumed that one town, in particular, always votes in the affirmative
in this version.
T (float): The threshold for the power.
Returns:
numpy.ndarray:
An array of size (num_towns,) that contains the computed voting power for when each
town is given weight determined by the u_matrix.
"""
totu = u_matrix.sum()
T = T * totu
a_matrix = np.transpose(u_matrix) @ subset_masks
a_minus_T = a_matrix - T
totu_minus_a = totu - a_matrix
totu_minus_a_minus_T = totu_minus_a - T
condition1 = np.logical_and(u_matrix >= a_minus_T, a_matrix > T)
condition2 = np.logical_and(u_matrix >= totu_minus_a_minus_T, totu_minus_a > T)
X = np.logical_or(
np.logical_and(subset_masks, condition1),
np.logical_and(~subset_masks, condition2),
)
non_zero_count = np.count_nonzero(X, axis=1)
p = non_zero_count / np.count_nonzero(X)
return p
def computer_power_numpy_simple(
u_matrix: np.ndarray,
subset_masks: np.ndarray,
T: float,
):
"""
This function computes the difference between the proportion of voting power for a locality
and the proportion of the population that town has.
Equivalent to `compute_power_numpy` with the exception that the subsets in `subset_masks`
includes all possible, non-empty, affirmative coalitions in this implementation.
Note: This is slower than the `compute_power_numpy` function.
Args:
u_matrix (numpy.ndarray): Array of size (num_towns, 1) that contains the weights to assign
to each town.
subset_masks (numpy.ndarray): Array of size (num_towns, num_subsets) that contains
boolean masks for whether a town voted in favor of a particular measure. Note:
this should contain all possible, non-empty coalitions.
T (float): The threshold for the power.
Returns:
numpy.ndarray:
An array of size (num_towns,) that contains the computed voting power for when each
town is given weight determined by the u_matrix.
"""
totu = u_matrix.sum()
T = T * totu
a_matrix = u_matrix.T @ subset_masks
a_minus_T = a_matrix - T
X = subset_masks & (u_matrix >= a_minus_T) & (a_matrix > T)
non_zero_count = np.count_nonzero(X, axis=1)
p = non_zero_count / np.sum(non_zero_count)
return p
def computer_power_helper_numpy_relaxed(
m: np.ndarray,
weights: np.ndarray,
subset_masks: np.ndarray,
T: float,
):
# This computes the power discrepancy if the functions aren’t linear threshold functions, but linear bounded functions
# as in the Diakonikolas et al. paper.
n = len(m)
contained_matrix = 2. * subset_masks - 1. # (num_towns, num_subsets)
subset_weights = np.transpose(contained_matrix).dot(weights) # num_subsets
subset_weights_min_T = subset_weights - T
payoffs = np.clip(subset_weights_min_T, a_min=-1, a_max=1) # num_subsets
banzhaf_indices = contained_matrix.dot(payoffs) / 2 ** (n - 1)
return banzhaf_indices - m