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 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 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 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_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_2d_points_party_list_profile(num_cand, num_voters, num_parties, partypointmode, voterpointmode, sigma, uniform=False): """Generates profiles from randomly generated 2d points according to some distributions with the given sigma. This selects parties for each voter, the parties are either uniform (equal size) or randomly generated (at least 1) candidate lists.""" parties = list(range(num_parties)) party_cands = __distribute_candidates_to_parties(num_cand, parties, uniform=uniform) voters = list(range(num_voters)) voter_points = __generate_2d_points(voters, voterpointmode, sigma) party_points = __generate_2d_points(parties, partypointmode, sigma) party_sets = __get_profile_from_points(voters, parties, voter_points, party_points, 1.0) apprsets = [] for p in party_sets: apprsets.append(party_cands[p[0]]) profile = Profile(num_cand) profile.add_preferences(apprsets) 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_fastest_algorithms(rule, resolute): profile = Profile(4) profile.add_preferences([[0, 1], [1, 2], [0, 2, 3]]) committeesize = 2 algo = rule.fastest_algo() if algo is None: pytest.skip("no supported algorithms for " + rule.shortname) rule.compute(profile, committeesize, algorithm=algo, resolute=resolute)
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 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 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 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 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 test_thiele_scores(scorefct_str, score, num_cand): profile = Profile(num_cand) preflist = [[0, 1], [1], [1, 3], [4], [1, 2, 3, 4, 5], [1, 5, 3], [0, 1, 2, 4, 5]] profile.add_preferences(preflist) committee = [6, 7] assert scores.thiele_score(scorefct_str, profile, committee) == 0 committee = [1, 2, 3, 4] assert scores.thiele_score(scorefct_str, profile, committee) == score
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 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 convert_election_to_profile(election): profile = Profile(num_cand=election.num_candidates) voters = [] for i, vote in enumerate(election.votes): voter = Voter(vote) voters.append(voter) profile._voters = voters return profile
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_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_unspecified_algorithms(rule, verbose, resolute): if resolute not in rule.resolute: return profile = Profile(3) profile.add_preferences([[0, 1], [1, 2]]) committeesize = 2 with pytest.raises(NotImplementedError): rule.compute(profile, committeesize, algorithm="made-up-algorithm", resolute=resolute, verbose=verbose)
def test_monroe_indivisible(algorithm): if algorithm == "gurobi": pytest.importorskip("gurobipy") profile = Profile(4) profile.add_preferences([[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_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 test_tiebreaking_order(rule_instance, verbose): rule_id, algorithm = rule_instance profile = Profile(4) profile.add_preferences([[1]] * 2 + [[0]] * 2 + [[2]] * 2) committeesize = 1 committees = abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=True, verbose=verbose) assert committees == [[0]]
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_seqphragmen_irresolute(): profile = Profile(3) profile.add_preferences([[0, 1], [0, 1], [0], [1, 2], [2]]) committeesize = 2 committees = abcrules.rules["seqphrag"].compute(profile, committeesize, resolute=False) assert committees == [[0, 1], [0, 2]] committees = abcrules.rules["seqphrag"].compute(profile, committeesize, resolute=True) assert committees == [[0, 2]]
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 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 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_abcrules_weightsconsidered(rule_id, algorithm, resolute): profile = Profile(3) profile.add_voter(Voter([0])) profile.add_voter(Voter([0])) profile.add_voter(Voter([1], 5)) profile.add_voter(Voter([0])) committeesize = 1 if "monroe" in rule_id or rule_id in [ "lexminimaxav", "rule-x", "rule-x-without-phragmen-phase", "phragmen-enestroem", ]: with pytest.raises(ValueError): abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm) return result = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) if rule_id == "minimaxav": # Minimax AV ignores weights by definition if resolute: assert result == [{0}] or result == [{1}] or result == [{2}] else: assert result == [{0}, {1}, {2}] else: assert len(result) == 1 assert result[0] == {1}
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, )