Skip to content

Commit 0af82b2

Browse files
committed
approval validation
1 parent 1ee65d9 commit 0af82b2

File tree

15 files changed

+1196
-867
lines changed

15 files changed

+1196
-867
lines changed

prefsampling/filters/approval_filters.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ def permute_approval_voters(votes: list[set[int]], seed: int = None) -> list[set
2525

2626

2727
def rename_approval_candidates(
28-
votes: list[set[int]], seed: int = None
28+
votes: list[set[int]],
29+
seed: int = None,
30+
num_candidates: int = None
2931
) -> list[set[int]]:
3032
"""
3133
Renames the candidates in approval votes.
@@ -36,18 +38,20 @@ def rename_approval_candidates(
3638
Approval votes.
3739
seed : int
3840
Seed for numpy random number generator.
41+
num_candidates : int
42+
Number of Candidates.
3943
4044
Returns
4145
-------
4246
list[set[int]]
4347
Approval votes.
4448
"""
45-
rng = np.random.default_rng(seed)
46-
max_id = max([max(vote) for vote in votes if len(vote) > 0])
47-
mapping = rng.permutation(max_id + 1)
4849

50+
rng = np.random.default_rng(seed)
51+
if num_candidates is None:
52+
num_candidates = max([max(vote) for vote in votes if len(vote) > 0]) + 1
53+
mapping = rng.permutation(num_candidates)
4954
votes = [{mapping[c] for c in vote} for vote in votes]
50-
5155
return votes
5256

5357

prefsampling/ordinal/singlecrossing.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ def single_crossing(
8585
votes[i, :] = domain[index]
8686
last_sampled_index = index
8787

88-
# vote_indices = np.sort(rng.choice(np.arange(domain_size), size=num_voters))
89-
# for i, index in enumerate(vote_indices):
90-
# votes[i, :] = domain[index]
88+
vote_indices = np.sort(rng.choice(np.arange(domain_size), size=num_voters))
89+
for i, index in enumerate(vote_indices):
90+
votes[i, :] = domain[index]
9191
return votes
9292

9393

prefsampling/tree/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@
1313
"schroeder_tree_lescanne",
1414
"schroeder_tree_brute_force",
1515
"all_schroeder_tree",
16+
"caterpillar_tree",
17+
"caterpillar_tree",
1618
]

validation/approval/__init__.py

Whitespace-only changes.

validation/approval/euclidean.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from prefsampling.core.euclidean import EuclideanSpace
2+
from prefsampling.ordinal import euclidean
3+
from validation.utils import get_all_subsets
4+
from validation.validator import Validator
5+
6+
7+
class ApprovalEuclideanValidatorUniform(Validator):
8+
def __init__(self):
9+
parameters_list = []
10+
for space in EuclideanSpace:
11+
for dimension in [2, 3]:
12+
parameters_list.append(
13+
{
14+
"num_voters": 50,
15+
"num_candidates": 5,
16+
"space": space,
17+
"dimension": dimension,
18+
},
19+
)
20+
super(ApprovalEuclideanValidatorUniform, self).__init__(
21+
parameters_list,
22+
"Euclidean",
23+
"euclidean_uniform",
24+
True,
25+
sampler_func=euclidean,
26+
constant_parameters=("num_voters", "num_candidates"),
27+
faceted_parameters=("space", "dimension"),
28+
)
29+
30+
def sample_cast(self, sample):
31+
return tuple(sample[0])
32+
33+
def all_outcomes(self, sampler_parameters):
34+
return get_all_ranks(sampler_parameters["num_candidates"])
35+
36+
def theoretical_distribution(self, sampler_parameters, all_outcomes) -> dict:
37+
return {o: 1 / len(all_outcomes) for o in all_outcomes}
38+
39+
40+
class OrdinalEuclideanValidator(Validator):
41+
def __init__(self):
42+
parameters_list = []
43+
for space in EuclideanSpace:
44+
for dimension in [2, 3]:
45+
parameters_list.append(
46+
{
47+
"num_voters": 3,
48+
"num_candidates": 3,
49+
"space": space,
50+
"dimension": dimension,
51+
},
52+
)
53+
super(OrdinalEuclideanValidator, self).__init__(
54+
parameters_list,
55+
"Euclidean",
56+
"euclidean",
57+
False,
58+
sampler_func=euclidean,
59+
constant_parameters=("num_voters", "num_candidates"),
60+
faceted_parameters=("space", "dimension"),
61+
)
62+
63+
def sample_cast(self, sample):
64+
return tuple(tuple(r) for r in sample)

validation/approval/identity.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from prefsampling.approval import identity
2+
from validation.utils import get_all_subsets
3+
from validation.validator import Validator
4+
5+
6+
class ApprovalIdentityValidator(Validator):
7+
def __init__(self):
8+
parameters_list = [
9+
{"num_voters": 1, "num_candidates": 4, "p": 0.25},
10+
{"num_voters": 1, "num_candidates": 4, "p": 0.5},
11+
]
12+
super(ApprovalIdentityValidator, self).__init__(
13+
parameters_list,
14+
"Identity",
15+
"identity",
16+
True,
17+
sampler_func=identity,
18+
constant_parameters=("num_voters", "num_candidates"),
19+
faceted_parameters="p",
20+
)
21+
22+
def all_outcomes(self, sampler_parameters):
23+
return get_all_subsets(sampler_parameters["num_candidates"])
24+
25+
def theoretical_distribution(self, sampler_parameters, all_outcomes) -> dict:
26+
k = int(sampler_parameters["p"] * sampler_parameters["num_candidates"])
27+
return {str(k): 1}
28+
29+
def sample_cast(self, sample):
30+
return str(len(sample[0]))

validation/approval/impartial.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from prefsampling.approval import impartial
2+
from validation.utils import get_all_subsets
3+
from validation.validator import Validator
4+
from math import factorial
5+
6+
7+
class ApprovalImpartialValidator(Validator):
8+
def __init__(self):
9+
parameters_list = [
10+
{"num_voters": 1, "num_candidates": 4, "p": 0.3},
11+
{"num_voters": 1, "num_candidates": 4, "p": 0.5},
12+
{"num_voters": 1, "num_candidates": 4, "p": 0.7},
13+
{"num_voters": 1, "num_candidates": 5, "p": 0.3},
14+
{"num_voters": 1, "num_candidates": 5, "p": 0.5},
15+
{"num_voters": 1, "num_candidates": 5, "p": 0.7},
16+
]
17+
super(ApprovalImpartialValidator, self).__init__(
18+
parameters_list,
19+
"Impartial",
20+
"impartial_apr",
21+
True,
22+
sampler_func=impartial,
23+
constant_parameters="num_voters",
24+
faceted_parameters=("p", "num_candidates"),
25+
)
26+
27+
def all_outcomes(self, sampler_parameters):
28+
return get_all_subsets(sampler_parameters["num_candidates"])
29+
30+
def theoretical_distribution(self, sampler_parameters, all_outcomes) -> dict:
31+
32+
m = sampler_parameters["num_candidates"]
33+
p = sampler_parameters["p"]
34+
35+
probabilities = [None for _ in range(m+1)]
36+
for k in range(m+1):
37+
binomial_coefficient = factorial(m) / (factorial(k) * factorial(m - k))
38+
probability = binomial_coefficient * (p ** k) * ((1 - p) ** (m - k))
39+
probabilities[k] = probability
40+
41+
return {str(len(o)): probabilities[len(o)] for o in all_outcomes}
42+
43+
def sample_cast(self, sample):
44+
return str(len(sample[0]))

validation/approval/noise.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import math
2+
3+
from prefsampling.approval import noise
4+
from validation.utils import get_all_subsets, hamming
5+
from validation.validator import Validator
6+
7+
8+
class ApprovalNoiseValidator(Validator):
9+
def __init__(self):
10+
parameters_list = [
11+
{"num_voters": 1, "num_candidates": 4, "phi": 0.25, "p": 0.25},
12+
{"num_voters": 1, "num_candidates": 4, "phi": 0.25, "p": 0.5},
13+
{"num_voters": 1, "num_candidates": 4, "phi": 0.25, "p": 0.75},
14+
{"num_voters": 1, "num_candidates": 4, "phi": 0.5, "p": 0.25},
15+
{"num_voters": 1, "num_candidates": 4, "phi": 0.5, "p": 0.5},
16+
{"num_voters": 1, "num_candidates": 4, "phi": 0.5, "p": 0.75},
17+
]
18+
super(ApprovalNoiseValidator, self).__init__(
19+
parameters_list,
20+
"Noise",
21+
"noise",
22+
True,
23+
sampler_func=noise,
24+
constant_parameters=("num_voters", "num_candidates"),
25+
faceted_parameters=("phi", "p"),
26+
)
27+
28+
def all_outcomes(self, sampler_parameters):
29+
return get_all_subsets(sampler_parameters["num_candidates"])
30+
31+
def theoretical_distribution(self, sampler_parameters, all_outcomes) -> dict:
32+
m = sampler_parameters["num_candidates"]
33+
p = sampler_parameters["p"]
34+
phi = sampler_parameters["phi"]
35+
k = math.floor(p*m)
36+
central_vote = {i for i in range(k)}
37+
tmp_dict = {str(o): phi**hamming(central_vote, o) for o in all_outcomes}
38+
denom = sum(tmp_dict.values())
39+
return {str(o): tmp_dict[str(o)]/denom for o in all_outcomes}
40+
41+
def sample_cast(self, sample):
42+
return str(sample[0])

validation/approval/partylist.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from prefsampling.approval import partylist
2+
# from validation.utils import get_all_ranks, get_all_anonymous_profiles
3+
from validation.validator import Validator
4+
5+
6+
class ApprovalImpartialValidator(Validator):
7+
def __init__(self):
8+
parameters_list = [
9+
{"num_voters": 1, "num_candidates": 4},
10+
{"num_voters": 1, "num_candidates": 5},
11+
{"num_voters": 1, "num_candidates": 6},
12+
]
13+
super(ApprovalImpartialValidator, self).__init__(
14+
parameters_list,
15+
"Partylist",
16+
"partylist",
17+
True,
18+
sampler_func=partylist,
19+
constant_parameters="num_voters",
20+
faceted_parameters="num_candidates",
21+
)
22+
23+
# def all_outcomes(self, sampler_parameters):
24+
# return get_all_ranks(sampler_parameters["num_candidates"])
25+
26+
# def theoretical_distribution(self, sampler_parameters, all_outcomes) -> dict:
27+
# return {o: 1 / len(all_outcomes) for o in all_outcomes}
28+
29+
# def sample_cast(self, sample):
30+
# return tuple(sample[0])

validation/approval/resampling.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import math
2+
3+
from prefsampling.approval import resampling
4+
from validation.utils import get_all_subsets
5+
from validation.validator import Validator
6+
7+
8+
class ApprovalResamplingValidator(Validator):
9+
def __init__(self):
10+
parameters_list = [
11+
{"num_voters": 1, "num_candidates": 6, "phi": 0.25, "p": 0.5},
12+
{"num_voters": 1, "num_candidates": 6, "phi": 0.5, "p": 0.5},
13+
{"num_voters": 1, "num_candidates": 6, "phi": 0.75, "p": 0.5},
14+
{"num_voters": 1, "num_candidates": 6, "phi": 0.25, "p": 0.34},
15+
{"num_voters": 1, "num_candidates": 6, "phi": 0.5, "p": 0.34},
16+
{"num_voters": 1, "num_candidates": 6, "phi": 0.75, "p": 0.34},
17+
]
18+
super(ApprovalResamplingValidator, self).__init__(
19+
parameters_list,
20+
"Resampling",
21+
"resampling",
22+
True,
23+
sampler_func=resampling,
24+
constant_parameters=("num_voters", "num_candidates"),
25+
faceted_parameters=("phi", "p"),
26+
)
27+
28+
def all_outcomes(self, sampler_parameters):
29+
return get_all_subsets(sampler_parameters["num_candidates"])
30+
31+
def theoretical_distribution(self, sampler_parameters, all_outcomes) -> dict:
32+
m = sampler_parameters["num_candidates"]
33+
p = sampler_parameters["p"]
34+
phi = sampler_parameters["phi"]
35+
k = math.floor(p*m)
36+
central_vote = {i for i in range(k)}
37+
38+
A = {}
39+
for outcome in all_outcomes:
40+
prob = 1
41+
for c in range(m):
42+
if c in central_vote and c in outcome:
43+
prob *= (1-phi) + phi*p
44+
elif c in central_vote and c not in outcome:
45+
prob *= phi*(1-p)
46+
elif c not in central_vote and c in outcome:
47+
prob *= phi*p
48+
else:
49+
prob *= (1-phi) + phi*(1-p)
50+
A[str(outcome)] = prob
51+
return A
52+
53+
def sample_cast(self, sample):
54+
return str(sample[0])
55+

validation/approval/truncated_urn.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from prefsampling.approval import impartial
2+
from validation.utils import get_all_subsets
3+
from validation.validator import Validator
4+
5+
6+
class ApprovalImpartialValidator(Validator):
7+
def __init__(self):
8+
parameters_list = [
9+
{"num_voters": 1, "num_candidates": 4},
10+
{"num_voters": 1, "num_candidates": 5},
11+
{"num_voters": 1, "num_candidates": 6},
12+
]
13+
super(ApprovalImpartialValidator, self).__init__(
14+
parameters_list,
15+
"Impartial",
16+
"impartial",
17+
True,
18+
sampler_func=impartial,
19+
constant_parameters="num_voters",
20+
faceted_parameters="num_candidates",
21+
)
22+
23+
def all_outcomes(self, sampler_parameters):
24+
return get_all_subsets(sampler_parameters["num_candidates"])
25+
26+
# def theoretical_distribution(self, sampler_parameters, all_outcomes) -> dict:
27+
# return {o: 1 / len(all_outcomes) for o in all_outcomes}
28+
29+
# def sample_cast(self, sample):
30+
# return tuple(sample[0])

0 commit comments

Comments
 (0)