def test_abcrules_correct_simple(rule_id, algorithm, resolute): def simple_checks(_committees): if rule_id == "rule-x-without-phragmen-phase": assert _committees == [set()] return if resolute: assert len(_committees) == 1 else: assert len(_committees) == 6 profile = Profile(4) profile.add_voters([{0}, {1}, {2}, {3}]) committeesize = 2 committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) simple_checks(committees) # call abcrules function differently, results should be the same committees = abcrules.get_rule(rule_id).compute( profile, committeesize, algorithm=algorithm, resolute=resolute, ) simple_checks(committees) # using the default algorithm committees = abcrules.compute(rule_id, profile, committeesize, resolute=resolute) simple_checks(committees)
def random_urn_party_list_profile(num_cand, num_voters, num_parties, replace, uniform=False): """Generate Polya Urn profile from a number of parties. If uniform each party gets the same amount of candidates.""" currsize = 1.0 approval_sets = [] replacedsets = {} parties = list(range(num_parties)) party_cands = __distribute_candidates_to_parties(num_cand, parties, uniform=uniform) for _ in range(num_voters): r = random.random() * currsize if r < 1.0: # base case: sample uniformly at random party = random.choice(parties) randpartyset = list(party_cands[party]) approval_sets.append(randpartyset) if party in replacedsets: replacedsets[party] += 1 else: replacedsets[party] = 1 currsize += replace else: # sample from one of the parties r = random.randint(0, sum(replacedsets.values())) for party in replacedsets: count = replacedsets[party] if r <= count: approval_sets.append(list(party_cands[party])) break else: r -= count profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def random_urn_profile(num_cand, num_voters, setsize, replace): """Generate Polya Urn profile with fixed size approval sets.""" currsize = 1.0 approval_sets = [] replacedsets = {} for _ in range(num_voters): r = random.random() * currsize if r < 1.0: # base case: sample uniformly at random randset = random.sample(range(num_cand), setsize) approval_sets.append(randset) key = tuple(set(randset)) if key in replacedsets: replacedsets[key] += 1 else: replacedsets[key] = 1 currsize += replace else: # sample from one of the replaced ballots r = random.randint(0, sum(replacedsets.values())) for approval_set in replacedsets: count = replacedsets[approval_set] if r <= count: approval_sets.append(list(approval_set)) break else: r -= count profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def test_cvxpy_cant_compute_av(): profile = Profile(4) profile.add_voters([[0, 1], [1, 2]]) committeesize = 2 with pytest.raises(ValueError): cvxpy_thiele_methods(profile, committeesize, "av", resolute=False, solver_id="glpk_mi")
def compute_abcvoting_rule(experiment=None, rule_name=None, committee_size=1, printing=False, resolute=False): all_winning_committees = {} for election in experiment.instances.values(): if printing: print(election.election_id) profile = Profile(election.num_candidates) if experiment.instance_type == 'ordinal': profile.add_voters(election.approval_votes) elif experiment.instance_type == 'approval': profile.add_voters(election.votes) try: winning_committees = abcrules.compute(rule_name, profile, committee_size, algorithm="gurobi", resolute=resolute) # print(winning_committees) except Exception: try: winning_committees = abcrules.compute(rule_name, profile, committee_size, resolute=resolute) except: winning_committees = {} all_winning_committees[election.election_id] = winning_committees store_committees_to_file(experiment.experiment_id, rule_name, all_winning_committees)
def random_ic_fixed_size_profile(num_voters, num_cand, setsize): """ Generate a random profile using the *IC with fixed-size approval sets* distribution. Parameters ---------- num_voters : int The desired number of voters in the profile. num_cand : int The desired number of candidates in the profile. setsize : int Number of candidates that each voter approves. Returns ------- abcvoting.preferences.Profile """ approval_sets = [] for _ in range(num_voters): randset = list(range(num_cand)) rng.shuffle(randset) randset = randset[:setsize] approval_sets.append(randset) profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def test_thiele_scores(scorefct_id, score, num_cand): profile = Profile(num_cand) approval_sets = [[0, 1], [1], [1, 3], [4], [1, 2, 3, 4, 5], [1, 5, 3], [0, 1, 2, 4, 5]] profile.add_voters(approval_sets) committee = [6, 7] assert scores.thiele_score(scorefct_id, profile, committee) == 0 committee = [1, 2, 3, 4] assert scores.thiele_score(scorefct_id, profile, committee) == score
def test_monroe_indivisible(algorithm): profile = Profile(4) profile.add_voters([[0], [0], [0], [1, 2], [1, 2], [1], [3]]) committeesize = 3 assert abcrules.compute_monroe( profile, committeesize, algorithm=algorithm, resolute=False ) == [{0, 1, 2}, {0, 1, 3}, {0, 2, 3}]
def test_seqphragmen_irresolute(): profile = Profile(3) profile.add_voters([[0, 1], [0, 1], [0], [1, 2], [2]]) committeesize = 2 committees = abcrules.compute("seqphragmen", profile, committeesize, resolute=False) assert committees == [{0, 1}, {0, 2}] committees = abcrules.compute("seqphragmen", profile, committeesize, resolute=True) assert committees == [{0, 2}]
def random_mallows_profile(num_voters, num_cand, setsize, dispersion): """ Generate a random profile using the *Truncated Mallows* probability distribution. Based on the definition for the repeated insertion model (RIM) in https://icml.cc/2011/papers/135_icmlpaper.pdf Parameters ---------- num_voters : int The desired number of voters in the profile. num_cand : int The desired number of candidates in the profile. setsize : int Number of candidates that each voter approves. dispersion : float in [0, 1] Dispersion parameter of the Mallows model. Returns ------- abcvoting.preferences.Profile """ def _select_pos(distribution): """Returns a randomly selected value with the help of the distribution.""" if round(sum(distribution), 10) != 1.0: raise Exception("Invalid Distribution", distribution, "sum:", sum(distribution)) r = round(rng.random(), 10) # or random.uniform(0, 1) pos = -1 s = 0 for p in distribution: pos += 1 s += p if s >= r: return pos return pos # in case of rounding errors if not 0 < dispersion <= 1: raise Exception("Invalid dispersion, needs to be in (0, 1].") reference_ranking = list(range(num_cand)) rng.shuffle(reference_ranking) insert_dist = _compute_mallows_insert_distributions(num_cand, dispersion) approval_sets = [] for _ in range(num_voters): vote = [] for i, distribution in enumerate(insert_dist): pos = _select_pos(distribution) vote.insert(pos, reference_ranking[i]) approval_sets.append(vote[:setsize]) profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def test_gurobi_cant_compute_av(): profile = Profile(4) profile.add_voters([[0, 1], [1, 2]]) committeesize = 2 scorefct = scores.get_scorefct("av", committeesize) with pytest.raises(ValueError): _gurobi_thiele_methods(profile, committeesize, scorefct, resolute=False)
def random_urn_fixed_size_profile(num_voters, num_cand, setsize, replace): """ Generate a random profile using the *Polya Urn with fixed-size approval sets* distribution. Parameters ---------- num_voters : int The desired number of voters in the profile. num_cand : int The desired number of candidates in the profile. setsize : int Number of candidates that each voter approves. replace : float New balls added to the urn in each iteration, relative to the original number. The urn starts with (`num_cand` choose `setsize`) balls, each representing a set of candidates with size `setsize`. This quantity is normalized to `1.0`. The `replace` value is a float that indicates how many balls are added using this normalization. Specifically, `replace` * (`num_cand` choose `setsize`) are added in each iteration. Returns ------- abcvoting.preferences.Profile """ currsize = 1.0 approval_sets = [] replacedsets = {} for _ in range(num_voters): r = rng.random() * currsize if r < 1.0: # base case: sample uniformly at random randset = list(range(num_cand)) rng.shuffle(randset) randset = randset[:setsize] approval_sets.append(randset) key = tuple(set(randset)) if key in replacedsets: replacedsets[key] += 1 else: replacedsets[key] = 1 currsize += replace else: # sample from one of the replaced ballots r = rng.integers(0, sum(replacedsets.values())) for approval_set, count in replacedsets.items(): if r <= count: approval_sets.append(list(approval_set)) break r -= count profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def random_IC_profile(num_cand, num_voters, setsize): """Generates profile with random assignment of candidates to the fix size of setsize.""" approval_sets = [] for _ in range(num_voters): randset = random.sample(range(num_cand), setsize) approval_sets.append(randset) profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def test_dominate(): profile = Profile(6) profile.add_voters([[0, 1], [0, 1], [1, 2, 3], [2, 3]]) assert not misc.dominate(profile, {0, 2}, {1, 3}) assert not misc.dominate(profile, {0, 2}, {1, 2}) assert misc.dominate(profile, {1, 3}, {0, 2}) assert misc.dominate(profile, {1, 2}, {0, 2}) assert not misc.dominate(profile, {0, 2}, {0, 2}) assert not misc.dominate(profile, {1}, {2}) assert not misc.dominate(profile, {2}, {1})
def test_fastest_available_algorithm(rule_id): profile = Profile(4) profile.add_voters([[0, 1], [1, 2], [0, 2, 3]]) committeesize = 2 algorithm = abcrules.get_rule(rule_id).fastest_available_algorithm if algorithm is None: pytest.skip("no supported algorithms for " + abcrules.get_rule(rule_id).shortname) for resolute in abcrules.get_rule(rule_id).resolute_values: abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute) # second possibility abcrules.compute(rule_id, profile, committeesize, algorithm="fastest")
def test_approved_candidates(): profile = Profile(10) profile.add_voter(Voter([1, 3, 5], 3)) profile.add_voter([0]) assert profile.approved_candidates() == {0, 1, 3, 5} profile.add_voter([4]) assert profile.approved_candidates() == {0, 1, 3, 4, 5} profile.add_voters([[7], [1, 8]]) assert profile.approved_candidates() == {0, 1, 3, 4, 5, 7, 8} profile[0].approved = [1, 5] assert profile.approved_candidates() == {0, 1, 4, 5, 7, 8}
def test_abcrules_return_lists_of_sets(rule_id, algorithm, resolute): profile = Profile(4) profile.add_voters([{0}, [1], [2], {3}]) committeesize = 2 committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) assert len(committees) >= 1 for committee in committees: assert isinstance(committee, set)
def test_cvxpy_wrong_score_fct(): profile = Profile(4) profile.add_voters([[0, 1], [2, 3]]) committeesize = 1 with pytest.raises(NotImplementedError): cvxpy_thiele_methods( profile=profile, committeesize=committeesize, scorefct_id="non_existing", resolute=False, solver_id="glpk_mi", )
def random_IC_party_list_profile(num_cand, num_voters, num_parties, uniform=False): """Generates profile with random assignment of parties. A party is a list of candidates. If uniform the number of candidates per party is the same, else at least 1.""" parties = list(range(num_parties)) party_cands = __distribute_candidates_to_parties(num_cand, parties, uniform=uniform) approval_sets = [] for _ in range(num_voters): approval_sets.append(party_cands[random.choice(parties)]) profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def test_unitweights(num_cand): profile = Profile(num_cand) profile.add_voters([]) profile.add_voter(Voter([0, 4, 5])) profile.add_voter([0, 4, 5]) p1 = Voter([0, 4, 5]) p2 = Voter([1, 2]) profile.add_voters([p1, p2]) assert profile.has_unit_weights() profile.add_voter(Voter([0, 4, 5], 2.4)) assert not profile.has_unit_weights() assert profile.totalweight() == 6.4
def test_unspecified_algorithms(rule_id, resolute): rule = abcrules.get_rule(rule_id) if resolute not in rule.resolute_values: return profile = Profile(3) profile.add_voters([[0, 1], [1, 2]]) committeesize = 2 with pytest.raises(NotImplementedError): rule.compute( profile, committeesize, algorithm="made-up-algorithm", resolute=resolute, )
def test_read_and_write_preflib_file(): currdir = os.path.dirname(os.path.abspath(__file__)) profile1 = Profile(6) profile1.add_voters([[3], [4, 1, 5], [0, 2], [], [0, 1, 2, 3, 4, 5], [5], [1], [1]]) fileio.write_profile_to_preflib_toi_file(currdir + "/data/test5.toi", profile1) for use_weights in [True, False]: profile2 = fileio.read_preflib_file(currdir + "/data/test5.toi", use_weights=use_weights) assert len(profile1) == len(profile2) for i, voter in enumerate(profile1): assert voter.weight == profile2[i].weight assert voter.approved == set(profile2[i].approved)
def pav_time(election): profile = Profile(election.num_candidates) profile.add_voters(election.votes) committee_size = 10 resolute = True rule_name = 'pav' start = time.time() winning_committees = abcrules.compute(rule_name, profile, committee_size, algorithm="gurobi", resolute=resolute) return time.time() - start
def test_read_special_abc_yaml_file2(): currdir = os.path.dirname(os.path.abspath(__file__)) filename = currdir + "/data/test8.abc.yaml" profile1 = Profile(6) profile1.add_voters([{3}, {1, 4, 5}, {0, 2}, {}, {0, 1, 2, 3, 4, 5}, {1, 3}, {1}, {1}]) profile2, committeesize, compute_instances, data = fileio.read_abcvoting_yaml_file( filename) assert str(profile1) == str(profile2) assert committeesize == 2 assert len(compute_instances) == 1 assert abcrules.compute(**compute_instances[0]) == [{1, 3}]
def run_single_experiment(varrules, num_voters, quality, perceived_quality, num_objective_voters=0): num_cand = len(quality) bestcands = [c for c in range(len(quality)) if quality[c] == max(quality)] assert len(bestcands) == 1 averqual = {} likelibest = {} aversize = {} varsize = {} # generate votes appr_sets = [] for i in range(0, num_voters): appr = [] for c in range(num_cand): if i < num_objective_voters: # voter i is objective (uses quality) if random.random() < quality[c]: appr.append(c) else: # voter i is objective (uses perceived_quality) if random.random() < perceived_quality[c]: appr.append(c) appr_sets.append(appr) profile = Profile(num_cand) profile.add_voters(appr_sets) scores = shortlisting.approval_scores(profile) maxapprovalscore = max(scores) / len(profile) # compute rules for rule in varrules: comm = shortlisting.compute(rule, scores, num_voters=len(profile)) if comm: # comm not empty averqual[rule] = np.average([quality[c] for c in comm]) else: averqual[rule] = 0 if set(bestcands).issubset(comm): likelibest[rule] = 1 else: likelibest[rule] = 0 aversize[rule] = len(comm) varsize[rule] = [len(comm)] return averqual, likelibest, aversize, varsize, maxapprovalscore
def test_minimaxphragmen_does_not_use_lexicographic_optimization(algorithm): # this test shows that lexicographic optimization is not (yet) # implemented for opt-Phragmen (as it is described in # http://martin.lackner.xyz/publications/phragmen.pdf) profile = Profile(7) profile.add_voters([[6], [6], [1, 3], [1, 3], [1, 4], [2, 4], [2, 5], [2, 5]]) committeesize = 3 # without lexicographic optimization, this profile has 12 winning committees # (with lexicographic optimization only {1, 2, 6} is winning) committees = abcrules.compute( "minimaxphragmen", profile, committeesize, algorithm=algorithm, resolute=False ) assert len(committees) == 12
def test_read_special_abc_yaml_file1(): currdir = os.path.dirname(os.path.abspath(__file__)) filename = currdir + "/data/test7.abc.yaml" profile1 = Profile(6) profile1.add_voters([[3], [4, 1, 5], [0, 2], [], [0, 1, 2, 3, 4, 5], [5], [1], [1]]) fileio.write_abcvoting_instance_to_yaml_file(filename, profile1, description="just a profile") profile2, committeesize, compute_instances2, data2 = fileio.read_abcvoting_yaml_file( filename) assert str(profile1) == str(profile2) assert committeesize is None assert compute_instances2 == []
def random_2d_points_profile( num_cand, num_voters, candpointmode, voterpointmode, sigma, approval_threshold ): """Generates profiles from randomly generated 2d points according to some distributions with the given sigma.""" voters = list(range(num_voters)) cands = list(range(num_cand)) voter_points = __generate_2d_points(voters, voterpointmode, sigma) cand_points = __generate_2d_points(cands, candpointmode, sigma) approval_sets = __get_profile_from_points( voters, cands, voter_points, cand_points, approval_threshold ) profile = Profile(num_cand) profile.add_voters(approval_sets) return profile
def test_resolute_parameter(rule_id): rule = abcrules.get_rule(rule_id) for algorithm in rule.algorithms: resolute_values = rule.resolute_values assert len(resolute_values) in [1, 2] # raise NotImplementedError if value for resolute is not implemented for resolute in [False, True]: if resolute not in resolute_values: profile = Profile(5) committeesize = 1 approval_sets = [{0, 1, 2}, {1}, {1, 2}, {0}] profile.add_voters(approval_sets) with pytest.raises(NotImplementedError): abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute )
def test_consensus_fails_lower_quota(): profile = Profile(31) profile.add_voters( [[0]] + [[1, 2]] * 3 + [[3, 4, 5]] * 5 + [[6, 7, 8, 9, 10, 11, 12, 13, 14, 15]] * 18 + [[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 29, 30]] * 27 ) committeesize = 30 committees = abcrules.compute("consensus-rule", profile, committeesize, resolute=True) for committee in committees: assert not all( cand in committee for cand in [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30] )