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 __init__(self): self.rule_algorithm_resolute = [] self.rule_algorithm_onlyresolute = [] self.rule_algorithm_onlyirresolute = [] for rule_id in abcrules.MAIN_RULE_IDS: rule = abcrules.get_rule(rule_id) for algorithm in list(rule.algorithms) + ["fastest"]: for resolute in rule.resolute_values: if algorithm in MARKS: if algorithm == "fastest": actual_algorithm = rule.fastest_available_algorithm if actual_algorithm is None: continue else: actual_algorithm = algorithm instance = pytest.param( rule_id, algorithm, resolute, marks=MARKS[actual_algorithm] ) instance_no_resolute_param = pytest.param( rule_id, algorithm, marks=MARKS[actual_algorithm] ) else: raise ValueError( f"Algorithm {algorithm} (for {rule_id}) " f"not known in unit tests " f"(pytest marks are missing)." ) self.rule_algorithm_resolute.append(instance) if resolute: self.rule_algorithm_onlyresolute.append(instance_no_resolute_param) else: self.rule_algorithm_onlyirresolute.append(instance_no_resolute_param)
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 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_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_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_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 _list_abc_yaml_compute_instances(): _abc_yaml_instances = [] currdir = os.path.dirname(os.path.abspath(__file__)) filenames = [ currdir + "/test_instances/" + filename for filename in os.listdir(currdir + "/test_instances/") if filename.endswith(".abc.yaml") ] for filename in filenames: for rule_id in abcrules.MAIN_RULE_IDS: rule = abcrules.get_rule(rule_id) for algorithm in rule.algorithms: if "instanceS" in filename: marks = [] # small instances, rather fast elif "instanceVL" in filename: marks = [pytest.mark.slow, pytest.mark.veryslow] # very large instances elif rule_id == "monroe" and algorithm in ["mip_cbc"]: marks = [pytest.mark.slow, pytest.mark.veryslow] else: marks = [pytest.mark.slow] _abc_yaml_instances.append( pytest.param(filename, rule_id, algorithm, marks=marks + MARKS[algorithm]) ) return filenames, _abc_yaml_instances
{1, 2, 3}, ], {2}, [{0, 1, 3}], [{1, 2, 4}], ), # ("minimaxphragmen", True, 3, [{a, b}] + [{b, c, d}] * 3, [a}, {{b, c, d}}, {{a, b, c}]), # this does not work because minimax-Phragmen does not support a specified tiebreaking # 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
def test_output(capfd, rule_id, algorithm, resolute, verbosity): if algorithm == "fastest": return # not necessary, output for "fastest" is the same as # whatever algorithm is selected as fastest # (and "fastest" depends on the available solvers) if algorithm == "cvxpy_glpk_mi": # TODO unfortunately GLPK_MI prints "Long-step dual simplex will be used" to stderr and it # would be very complicated to capture this on all platforms reliably, changing # sys.stderr doesn't help. # This seems to be fixed in GLPK 5.0 but not in GLPK 4.65. For some weird reason this # test succeeds and does not need to be skipped when using conda-forge, although the # version from conda-forge is given as glpk 4.65 he80fd80_1002. # This could help to introduce a workaround: https://github.com/xolox/python-capturer # Sage math is fighting the same problem: https://trac.sagemath.org/ticket/24824 pytest.skip("GLPK_MI prints something to stderr, not easy to capture") output.set_verbosity(verbosity=verbosity) try: profile = Profile(2) profile.add_voters([[0]]) committeesize = 1 committees = abcrules.compute( rule_id, profile, committeesize, algorithm=algorithm, resolute=resolute ) out = str(capfd.readouterr().out) # remove unwanted solver output out = remove_solver_output(out) if verbosity >= WARNING: assert out == "" else: assert len(out) > 0 rule = abcrules.get_rule(rule_id) start_output = misc.header(rule.longname) + "\n" if resolute and rule.resolute_values[0] == False: # only if irresolute is default but resolute is chosen start_output += "Computing only one winning committee (resolute=True)\n\n" if not resolute and rule.resolute_values[0] == True: # only if resolute is default but resolute=False is chosen start_output += ( "Computing all possible winning committees for any tiebreaking order\n" " (aka parallel universes tiebreaking) (resolute=False)\n\n" ) if verbosity <= DETAILS: start_output += "Algorithm: " + abcrules.ALGORITHM_NAMES[algorithm] + "\n" if verbosity <= DEBUG: assert start_output in out else: assert out.startswith(start_output) end_output = ( f"{misc.str_committees_header(committees, winning=True)}\n" f"{misc.str_sets_of_candidates(committees, cand_names=profile.cand_names)}\n" ) if verbosity == INFO: assert out.endswith(end_output) else: assert end_output in out finally: output.set_verbosity(verbosity=WARNING)
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}] @pytest.mark.parametrize( "algorithm", [ pytest.param(algorithm, marks=MARKS[algorithm]) for algorithm in abcrules.get_rule("monroe").algorithms ], ) 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}] @pytest.mark.parametrize( "algorithm", [
[{1, 2, 3, 6, 7}], ), ] for inst in monotonicity_instances: ( rule_id, committeesize, approval_sets, addvoter, new_approval_set, commsfirst, commsafter, ) = inst print(misc.header(abcrules.get_rule(rule_id).longname, "-")) profile = Profile(num_cand, cand_names=cand_names) profile.add_voters(approval_sets) original_approval_set = set(approval_sets[0]) print(profile.str_compact()) # irresolute if possible if False in abcrules.get_rule(rule_id).resolute_values: resolute = False else: resolute = True committees = abcrules.compute(rule_id, profile, committeesize,
def generate_abc_yaml_testinstances( batchname, committeesizes, num_voters_values, num_cand_values, prob_distributions, approval_setsizes, av_neq_pav=False, ): random.seed(24121838) parameter_tuples = [] for committeesize, num_voters, num_cand, prob_distribution, setsize in product( committeesizes, num_voters_values, num_cand_values, prob_distributions, approval_setsizes): if committeesize >= num_cand: continue if setsize == "committeesize": setsize = committeesize parameter_tuples.append( (committeesize, num_voters, num_cand, prob_distribution, setsize)) parameter_tuples.sort(key=itemgetter(2)) print( f"Generating {len(parameter_tuples)} instances for batch {batchname}..." ) num_instances = 0 for index, (committeesize, num_voters, num_cand, prob_distribution, setsize) 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_profile(num_voters, num_cand, committeesize, prob_distribution, setsize) 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.get_rule(rule_id) # if rule_id == "minimaxphragmen": # algorithm = "mip_gurobi" # else: # algorithm = "fastest" # if irresolute (resolute = False) is supported, then "expected_committees" should be # the list of committees returned for resolute=False. if False in rule.resolute_values: resolute = False else: resolute = True committees = abcrules.compute(rule_id, profile, committeesize, resolute=resolute) for resolute in rule.resolute_values: rule_instances.append({ "rule_id": rule_id, "resolute": resolute, "expected_committees": 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}, " f"setsize={setsize}"), compute_instances=rule_instances, ) print("Done.")
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.get_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"