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_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 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 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_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__toofewcandidates(rule_instance, verbose): rule_id, algorithm, resolute = rule_instance profile = Profile(5) committeesize = 4 preflist = [[0, 1, 2], [1], [1, 2], [0]] profile.add_preferences(preflist) with pytest.raises(ValueError): abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute, verbose=verbose)
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_output(capfd, rule_instance, verbose): rule_id, algorithm, resolute = rule_instance profile = Profile(2) profile.add_preferences([[0]]) committeesize = 1 abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute, verbose=verbose) out = capfd.readouterr().out if verbose == 0: assert out == "" else: assert len(out) > 0
def test_read_and_write_abc_yaml_file(): currdir = os.path.dirname(os.path.abspath(__file__)) filename = currdir + "/data/test6.abc.yaml" profile1 = Profile(6) profile1.add_voters([[3], [4, 1, 5], [0, 2], [], [0, 1, 2, 3, 4, 5], [5], [1], [1]]) committeesize1 = 3 compute_instances1 = [ { "rule_id": "pav", "resolute": True }, { "rule_id": "seqphragmen", "algorithm": "float-fractions" }, { "rule_id": "equal-shares", "algorithm": "standard-fractions", "skip_phragmen_phase": True, }, ] fileio.write_abcvoting_instance_to_yaml_file( filename, profile1, committeesize=committeesize1, compute_instances=compute_instances1) profile2, committeesize2, compute_instances2, _ = fileio.read_abcvoting_yaml_file( filename) assert committeesize1 == committeesize2 assert len(profile1) == len(profile2) for i, voter in enumerate(profile1): assert voter.weight == profile2[i].weight assert voter.approved == set(profile2[i].approved) for i in range(len(compute_instances1)): assert abcrules.compute(profile=profile1, committeesize=committeesize1, **compute_instances1[i]) == abcrules.compute( **compute_instances2[i]) for compute_instance in compute_instances2: compute_instance.pop("profile") compute_instance.pop("committeesize") assert compute_instances1 == compute_instances2
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_abcrules__toofewcandidates(rule_id, algorithm, resolute): profile = Profile(5) committeesize = 4 approval_sets = [{0, 1, 2}, {1}, {1, 2}, {0}] profile.add_voters(approval_sets) with pytest.raises(ValueError): abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute, ) with pytest.raises(ValueError): abcrules.get_rule(rule_id).compute( profile, committeesize, algorithm=algorithm, resolute=resolute, )
def test_abcrules_handling_empty_ballots(rule_id, algorithm, resolute): profile = Profile(4) profile.add_voters([{0}, {1}, {2}]) committeesize = 3 committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) assert committees == [{0, 1, 2}] profile.add_voters([[]]) committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) if rule_id == "rule-x-without-phragmen-phase": assert committees == [set()] else: assert committees == [{0, 1, 2}]
def test_abcrules_weightsconsidered(rule_instance, verbose): rule_id, algorithm, resolute = rule_instance profile = Profile(3) profile.add_preferences(DichotomousPreferences([0])) profile.add_preferences(DichotomousPreferences([0])) profile.add_preferences(DichotomousPreferences([1], 5)) profile.add_preferences(DichotomousPreferences([0])) committeesize = 1 if (("monroe" in rule_id or rule_id in ["lexmav", "rule-x", "phrag-enestr"])): with pytest.raises(ValueError): abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, verbose=verbose) elif rule_id == "mav": # Minimax AV ignores weights by definition result = abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute, verbose=verbose) if resolute: assert result == [[0]] else: assert result == [[0], [1], [2]] else: result = abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute, verbose=verbose) assert len(result) == 1 assert result[0] == [1]
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 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_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 calculate_committees(election: ApprovalElection, resolute=False, committee_size: int = 10, rule_name=None) -> set: profile = preferences.Profile(num_cand=election.num_candidates) profile.add_voters(election.votes) try: committees = abcrules.compute(rule_name, profile, committee_size, resolute=resolute) except Exception: committees = {} return committees
def test_abcrules_correct(rule_instance, verbose, instance): rule_id, algorithm, resolute = rule_instance profile, exp_results, committeesize = instance print(profile) committees = abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, verbose=verbose, resolute=resolute) if resolute: assert len(committees) == 1 assert committees[0] in exp_results[rule_id] else: assert committees == exp_results[rule_id]
def test_abcrules_correct_simple(rule_instance, verbose): rule_id, algorithm, resolute = rule_instance profile = Profile(4) profile.add_preferences([[0], [1], [2], [3]]) committeesize = 2 committees = abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute, verbose=verbose) if resolute: assert len(committees) == 1 else: assert len(committees) == 6
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] )
def test_jansonexamples(rule_id, algorithm): # example from Janson's survey (https://arxiv.org/pdf/1611.08826.pdf), # Example 3.7, 18.1 profile = Profile(6) a = 0 b = 1 c = 2 p = 3 q = 4 r = 5 profile.add_voters( [[a, b, c]] * 1034 + [[p, q, r]] * 519 + [[a, b, q]] * 90 + [[a, p, q]] * 90 ) committeesize = 3 committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=False ) assert committees == [{a, b, q}]
def test_geometric_rules_with_arbitrary_parameter(parameter, prefix, algorithm, resolute): profile = Profile(4) profile.add_voters([{0}, {1}, {2}, {3}]) committeesize = 2 rule_id = f"{prefix}geom{parameter}" committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) if resolute: assert len(committees) == 1 else: assert len(committees) == 6 rule = abcrules.get_rule(rule_id) committees = rule.compute(profile, committeesize, algorithm=algorithm, resolute=resolute) if resolute: assert len(committees) == 1 else: assert len(committees) == 6
def test_jansonexamples(rule_id, algorithm): # example from Janson's survey (https://arxiv.org/pdf/1611.08826.pdf), # Example 3.7, 18.1 profile = Profile(6) A = 0 B = 1 C = 2 P = 3 Q = 4 R = 5 profile.add_preferences([[A, B, C]] * 1034 + [[P, Q, R]] * 519 + [[A, B, Q]] * 90 + [[A, P, Q]] * 90) committeesize = 3 committees = abcrules.compute(rule_id, profile, committeesize, algorithm=algorithm, resolute=False) assert committees == [[A, B, Q]]
def test_abcrules_correct(rule_id, algorithm, resolute, profile, exp_results, committeesize): if rule_id.startswith("geom") and rule_id != "geom2": return # correctness tests only for geom2 if rule_id.startswith("seq") and rule_id not in ("seqpav", "seqslav", "seqcc"): return # correctness tests only for selected sequential rules if rule_id.startswith("revseq") and rule_id != "revseqpav": return # correctness tests only for selected reverse sequential rules (only for revseqpav) print(profile) committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) print(f"output: {committees}") print(f"expected: {exp_results[rule_id]}") if resolute: assert len(committees) == 1 assert committees[0] in exp_results[rule_id] else: # test unordered equality, this requires sets of sets, only possible with frozensets committees_ = {frozenset(committee) for committee in committees} exp_results_ = {frozenset(committee) for committee in exp_results[rule_id]} assert committees_ == exp_results_
for inst in manipulations: print(misc.header(abcrules.Rule(inst.rule_id).longname, "-")) profile = Profile(num_cand, cand_names=cand_names) profile.add_voters(inst.approval_sets) truepref = profile[0].approved print(profile.str_compact()) parameters = {} if inst.rule_id == "leximaxphragmen": parameters["lexicographic_tiebreaking"] = True committees = abcrules.compute(inst.rule_id, profile, inst.committeesize, resolute=True, **parameters) committee1 = committees[0] print("original winning committee:\n " + misc.str_set_of_candidates(committee1, cand_names)) # verify correctness assert ( committee1 in inst.committees_first ), f"({inst.rule_id}) {committees[0]} not in {inst.committees_first}" print("\nManipulation by voter 0: " + misc.str_set_of_candidates(inst.approval_sets[0], cand_names) + " --> " + misc.str_set_of_candidates(inst.manipulated_vote, cand_names))
def generate_abc_yaml_testinstances( batchname, committeesizes, num_voters_values, num_cand_values, prob_distributions, av_neq_pav=False, ): generate.rng = np.random.default_rng(24121838) # seed for numpy RNG parameter_tuples = [] for committeesize, num_voters, num_cand, prob_distribution in product( committeesizes, num_voters_values, num_cand_values, prob_distributions ): if committeesize >= num_cand: continue parameter_tuples.append((num_voters, num_cand, prob_distribution, committeesize)) parameter_tuples.sort(key=itemgetter(1)) print(f"Generating {len(parameter_tuples)} instances for batch {batchname}...") num_instances = 0 for index, (num_voters, num_cand, prob_distribution, committeesize) in enumerate( parameter_tuples ): num_instances += 1 # write instance to .abc.yaml file currdir = os.path.dirname(os.path.abspath(__file__)) filename = currdir + f"/instance{batchname}{index:04d}.abc.yaml" print(f"generating {filename} ({prob_distribution})") while True: profile = generate.random_profile(num_voters, num_cand, prob_distribution) committees_av = abcrules.compute("av", profile, committeesize, resolute=False) committees_pav = abcrules.compute("pav", profile, committeesize, resolute=False) if not av_neq_pav: break intersection = set(tuple(sorted(committee)) for committee in committees_pav) & set( tuple(sorted(committee)) for committee in committees_av ) if not intersection: break rule_instances = [] for rule_id in abcrules.MAIN_RULE_IDS: rule = abcrules.Rule(rule_id) # if irresolute (resolute = False) is supported, then "result" should be # the list of committees returned for resolute=False. if False in rule.resolute_values: resolute = False else: resolute = True if rule_id == "rsd": committees = None # result is random, not sensible for unit tests elif rule_id == "leximaxphragmen" and (num_cand > 7 or num_voters > 8): committees = None # too slow else: committees = abcrules.compute(rule_id, profile, committeesize, resolute=resolute) for resolute in rule.resolute_values: rule_instances.append( {"rule_id": rule_id, "resolute": resolute, "result": committees} ) fileio.write_abcvoting_instance_to_yaml_file( filename, profile, committeesize=committeesize, description=( f"profile generated via prob_distribution={prob_distribution}, " f"num_voters={num_voters}, " f"num_cand={num_cand}" ), compute_instances=rule_instances, ) print("Done.")
### num_cand = 3 a, b, c = (0, 1, 2) approval_sets = [{a}] * 2 + [{a, c}] * 3 + [{b, c}] * 3 + [{b}] * 2 cand_names = "abcde" profile = Profile(num_cand, cand_names=cand_names) profile.add_voters(approval_sets) print(misc.header("1st profile:")) print(profile.str_compact()) print("winning committees for k=1 and k=2:") for rule_id in ["pav", "cc", "monroe", "minimaxphragmen", "minimaxav"]: comm1 = abcrules.compute(rule_id, profile, 1, resolute=True)[0] comm2 = abcrules.compute(rule_id, profile, 2, resolute=True)[0] print(" " + abcrules.Rule(rule_id).shortname + ": " + misc.str_set_of_candidates(comm1, cand_names) + " vs " + misc.str_set_of_candidates(comm2, cand_names)) assert not all(cand in comm1 for cand in comm2) ### num_cand = 4 a, b, c, d = 0, 1, 2, 3 approval_sets = ([{a}] * 6 + [{a, c}] * 4 + [{a, b, c}] * 2 + [{a}] * 2 + [{a, d}] * 1 + [{b, d}] * 3) cand_names = "abcde" profile = Profile(num_cand, cand_names=cand_names) profile.add_voters(approval_sets)
# between committees ([a, b, c] should be prefered to [b, c, d]) ] for manip in manipulations: (rule_id, resolute, committeesize, approval_sets, modvote, commsfirst, commsafter) = manip print(misc.header(abcrules.get_rule(rule_id).longname, "-")) profile = Profile(num_cand, cand_names=cand_names) profile.add_voters(approval_sets) truepref = profile[0].approved print(profile.str_compact()) committees = abcrules.compute(rule_id, profile, committeesize, resolute=resolute) print("original winning committees:\n" + misc.str_sets_of_candidates(committees, cand_names)) # verify correctness assert committees == commsfirst print("Manipulation by voter 0: " + misc.str_set_of_candidates(approval_sets[0], cand_names) + " --> " + misc.str_set_of_candidates(modvote, cand_names)) if not all(cand in truepref for cand in modvote): print(" (not a subset!)") approval_sets[0] = modvote profile = Profile(num_cand, cand_names=cand_names)
with_additional_voter=False, mod_approval_set={1, 2}, committees_first=[{1, 2}], committees_after=[{0, 2}], ), ] for inst in monotonicity_instances: print(misc.header(abcrules.Rule(inst.rule_id).longname, "-")) profile = Profile(num_cand, cand_names=cand_names) profile.add_voters(inst.approval_sets) original_approval_set = set(inst.approval_sets[0]) print(profile.str_compact()) committees = abcrules.compute(inst.rule_id, profile, inst.committeesize) print("original winning committees:\n" + misc.str_sets_of_candidates(committees, cand_names)) # verify correctness assert (committees == inst.committees_first ), f"({inst.rule_id}) {committees} != {inst.committees_first}" some_variant = any( all(cand in committee for cand in inst.mod_approval_set) for committee in inst.committees_first) all_variant = all( all(cand in committee for cand in inst.mod_approval_set) for committee in inst.committees_first) assert some_variant or all_variant if all_variant: assert not all( all(cand in committee for cand in inst.mod_approval_set)