예제 #1
0
def test_iterate(num_cand):
    profile = Profile(num_cand)
    profile.add_voter(Voter([1, 3, 5], 3))
    profile.add_voter([0, 4, 5])
    assert len(profile) == 2
    for p in profile:
        assert isinstance(p, Voter)
예제 #2
0
def test_empty_approval(num_cand):
    profile = Profile(num_cand)
    profile.add_voter([])
    profile.add_voters([[]])
    profile.add_voters([[], [0, 3], []])

    profile = Profile(num_cand)
    profile.add_voters([[]])
예제 #3
0
def test_party_list(num_cand, additional_approval_set):
    profile = Profile(num_cand)
    profile.add_voter(Voter([1, 3, 5], 3))
    profile.add_voter([0, 4, 6])
    profile.add_voter([0, 4, 6])
    profile.add_voter([2, 7])
    profile.add_voter(Voter([1, 3, 5], 3))
    assert profile.is_party_list()
    profile.add_voter(additional_approval_set)
    assert not profile.is_party_list()
예제 #4
0
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_voter(Voter([3], weight=1.3))
    profile1.add_voters([[4, 1, 5], [0, 2], [], [0, 1, 2, 3, 4, 5], [5]])
    profile1.add_voter(Voter([1], weight=2))
    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 == []
예제 #5
0
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}
예제 #6
0
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}
예제 #7
0
def test_invalidprofiles(num_cand):
    with pytest.raises(ValueError):
        Profile(0)
    with pytest.raises(ValueError):
        Profile(-8)
    with pytest.raises(ValueError):
        Profile(4, ["a", "b", "c"])
    Profile(4, ["a", 3, "b", "c"])
    profile = Profile(num_cand, "abcdefgh")
    voter = Voter([num_cand])
    with pytest.raises(ValueError):
        profile.add_voter(voter)
    with pytest.raises(TypeError):
        profile.add_voter([0, 4, 5, "1"])
    with pytest.raises(TypeError):
        profile.add_voter(["1", 0, 4, 5])

    with pytest.raises(TypeError):
        # note: this raises a TypeError because a list of lists can't be converted to a set,
        # but that's fine too
        profile.add_voter([[0, 4, 5]])
예제 #8
0
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
예제 #9
0
# verify correctness
assert committees_pav == [{a, c}]
assert committees_seqpav == [{c, d}]
assert committees_revseqpav == [{c, d}]

print("\n")
print(misc.header("Example from Janson's survey (Example 13.3) / Thiele:",
                  "*"))

# Approval profile
num_cand = 4
a, b, c, d = range(4)  # a = 0, b = 1, c = 2, ...
cand_names = "abcd"

profile = Profile(num_cand, cand_names=cand_names)
profile.add_voter(Voter([a, c, d], 960))
profile.add_voter(Voter([b, c, d], 3000))
profile.add_voter(Voter([b, c], 520))
profile.add_voter(Voter([a, b], 1620))
profile.add_voter(Voter([a, d], 1081))
profile.add_voter(Voter([a, c], 1240))
profile.add_voter(Voter([b, d], 360))
profile.add_voter(Voter([d], 360))
profile.add_voter(Voter([c], 120))
profile.add_voter(Voter([b], 60))

print(misc.header("Input:"))
print(profile.str_compact())

committees_pav = abcrules.compute_pav(profile, 2)
예제 #10
0
파일: partylist.py 프로젝트: szufix/mapel
def partylistdistance(election, feature_params=None):

    if 'largepartysize' in feature_params:
        largepartysize = feature_params['largepartysize']
    else:
        largepartysize = 5

    if 'time_limit' in feature_params:
        time_limit = feature_params['time_limit']
    else:
        time_limit = 5

    profile = convert_election_to_profile(election)

    model = gb.Model()
    model.setParam("OutputFlag", False)
    model.setParam('TimeLimit', time_limit)  # in seconds

    same_party = {}
    for c1 in profile.candidates:
        for c2 in range(c1):
            same_party[(c1, c2)] = model.addVar(vtype=gb.GRB.BINARY)

    edit = {}
    for v, _ in enumerate(profile):
        for c in profile.candidates:
            edit[(v, c)] = model.addVar(vtype=gb.GRB.BINARY)

    for v, vote in enumerate(profile):
        for c1 in profile.candidates:
            for c2 in range(c1):
                if c1 in vote.approved and c2 in vote.approved:
                    model.addConstr((same_party[(c1, c2)] == 1) >> (edit[
                        (v, c1)] == edit[(v, c2)]))
                    model.addConstr(
                        # (same_party[(c1, c2)] == 0) >> (edit[(v, c1)] + edit[(v, c2)] >= 1)
                        (same_party[(c1, c2)] + edit[(v, c1)] + edit[(v, c2)]
                         >= 1))
                elif c1 not in vote.approved and c2 not in vote.approved:
                    model.addConstr((same_party[(c1, c2)] == 1) >> (edit[
                        (v, c1)] == edit[(v, c2)]))
                    model.addConstr(
                        # (same_party[(c1, c2)] == 0) >> (edit[(v, c1)] + edit[(v, c2)] <= 1)
                        (edit[(v, c1)] + edit[(v, c2)] <=
                         1 + same_party[(c1, c2)]))
                else:
                    model.addConstr((same_party[(c1, c2)] == 1) >> (edit[
                        (v, c1)] + edit[(v, c2)] == 1))
                    if c1 in vote.approved:
                        model.addConstr(
                            (same_party[(c1, c2)] + edit[(v, c1)] >=
                             edit[(v, c2)])
                            # (same_party[(c1, c2)] == 0) >> (edit[(v, c1)] >= edit[(v, c2)])
                        )
                    else:
                        model.addConstr(
                            (same_party[(c1, c2)] + edit[(v, c2)] >=
                             edit[(v, c1)])
                            # (same_party[(c1, c2)] == 0) >> (edit[(v, c2)] >= edit[(v, c1)])
                        )

    model.setObjective(
        gb.quicksum(edit[(v, c)] for v, _ in enumerate(profile)
                    for c in profile.candidates),
        gb.GRB.MINIMIZE,
    )

    model.optimize()

    newprofile = Profile(num_cand=profile.num_cand)
    for v, vote in enumerate(profile):
        new_approved = {
            c
            for c in profile.candidates
            if (c in vote.approved and edit[(v, c)].X <= 0.1) or (
                c not in vote.approved and edit[(v, c)].X >= 0.9)
        }
        newprofile.add_voter(new_approved)

    # number of parties
    parties = set(profile.candidates)
    for c1 in profile.candidates:
        for c2 in range(c1):
            if same_party[(c1, c2)].X >= 0.9:
                parties.discard(c2)
    num_large_parties = 0
    for party in parties:
        support = len(
            [voter for voter in newprofile if party in voter.approved])
        if support >= largepartysize:
            num_large_parties += 1

    # print(f"new {newprofile}")
    # print([sum(edit[(v, c)].X for c in profile.candidates) for v, _ in enumerate(profile)])
    # print([(c1, c2, same_party[(c1, c2)].X) for c1 in profile.candidates for c2 in range(c1)])

    # return model.objVal, num_large_parties, newprofile
    print(model.objVal, model.objbound, num_large_parties)
    return model.objVal, model.objbound, num_large_parties
예제 #11
0
        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)
            for committee in inst.committees_after)
    else:
        assert not any(
            all(cand in committee for cand in inst.mod_approval_set)
            for committee in inst.committees_after)

    if inst.with_additional_voter:
        print("additional voter: " +
              misc.str_set_of_candidates(inst.mod_approval_set, cand_names))
        new_approval_set = inst.mod_approval_set
        profile.add_voter(new_approval_set)
    else:
        new_approval_set = list(
            set(inst.mod_approval_set) | set(inst.approval_sets[0]))
        print("change of voter 0: " + misc.str_set_of_candidates(
            list(original_approval_set), cand_names) + " --> " +
              misc.str_set_of_candidates(new_approval_set, cand_names))
        profile[0] = new_approval_set

    committees = abcrules.compute(inst.rule_id, profile, inst.committeesize)
    print("\nwinning committees after the modification:\n" +
          misc.str_sets_of_candidates(committees, cand_names))

    # verify correctness
    assert (committees == inst.committees_after
            ), f"({inst.rule_id}) {committees} != {inst.committees_after}"
예제 #12
0
def read_preflib_file(filename, setsize=1, relative_setsize=None, use_weights=False):
    """Reads a single preflib file (soi, toi, soc or toc).

    Parameters:

        filename: str
            Name of the preflib file.

        setsize: int
            Number of top-ranked candidates that voters approve.
            In case of ties, more than `setsize` candidates are approved.

            Paramer `setsize` is ignored if `relative_setsize` is used.

        relative_setsize: float in (0, 1]
            Indicates which proportion of candidates of the ranking
            are approved (rounded up). In case of ties, more
            candidates are approved.
            E.g., if a voter has 10 approved candidates and `relative_setsize` is 0.75,
            then the approval set contains the top 8 candidates.

        use_weights: bool
            If False, treat vote count in preflib file as the number of duplicate ballots,
            i.e., the number of voters that have this approval set.
            If True, treat vote count as weight and use this weight in class Voter.

    Returns:
        profile: abcvoting.preferences.Profile
            Preference profile extracted from preflib file,
            including names of candidates
    """
    if setsize <= 0:
        raise ValueError("Parameter setsize must be > 0")
    if relative_setsize and (relative_setsize <= 0.0 or relative_setsize > 1.0):
        raise ValueError("Parameter relative_setsize not in interval (0, 1]")
    with open(filename, "r") as f:
        line = f.readline()
        num_cand = int(line.strip())
        candidate_map = {}
        for _ in range(num_cand):
            parts = f.readline().strip().split(",")
            candidate_map[int(parts[0].strip())] = ",".join(parts[1:]).strip()

        parts = f.readline().split(",")
        try:
            voter_count, _, unique_orders = [int(p.strip()) for p in parts]
        except ValueError:
            raise PreflibException(
                f"Number of voters ill specified ({str(parts)}), should be triple of integers"
            )

        approval_sets = []
        lines = [line.strip() for line in f.readlines() if line.strip()]
        if len(lines) != unique_orders:
            raise PreflibException(
                f"Expected {unique_orders} lines that specify voters in the input, "
                f"encountered {len(lines)}"
            )

    for line in lines:
        parts = line.split(",")
        if len(parts) < 1:
            continue
        try:
            count = int(parts[0])
        except ValueError:
            raise PreflibException(f"Each ranking must start with count/weight ({line})")
        ranking = parts[1:]  # ranking starts after count
        if len(ranking) == 0:
            raise PreflibException("Empty ranking: " + str(line))
        if relative_setsize:
            num_appr = int(ceil(len(ranking) * relative_setsize))
        else:
            num_appr = setsize
        approval_set = _approval_set_from_preflib_datastructures(num_appr, ranking, candidate_map)
        approval_sets.append((count, approval_set))

    # normalize candidates to 0, 1, 2, ...
    cand_names = []
    normalize_map = {}
    for cand in candidate_map.keys():
        cand_names.append(candidate_map[cand])
        normalize_map[cand] = len(cand_names) - 1

    profile = Profile(num_cand, cand_names=cand_names)

    for count, approval_set in approval_sets:
        normalized_approval_set = []
        for cand in approval_set:
            normalized_approval_set.append(normalize_map[cand])
        if use_weights:
            profile.add_voter(Voter(normalized_approval_set, weight=count))
        else:
            profile.add_voters([normalized_approval_set] * count)
    if use_weights:
        if len(profile) != unique_orders:
            raise PreflibException("Number of voters wrongly specified in preflib file.")
    else:
        if len(profile) != voter_count:
            raise PreflibException("Number of voters wrongly specified in preflib file.")
    return profile
예제 #13
0
def read_abcvoting_yaml_file(filename):
    """
    Read contents of an abcvoting yaml file (ending with .abc.yaml).

    Parameters
    ----------
        filename : str
            File name of the .abc.yaml file.

    Returns
    -------
        profile : abcvoting.preferences.Profile
            A profile.

        committeesize : int or None
            The desired committee size.

        compute_instances : list of dict
            A list of compute instances, which are dictionaries.

            Compute instances can be passed to `Rule.compute`.

        data : dict
            The YAML data from `filename`.
    """
    yaml = ruamel.yaml.YAML(typ="safe", pure=True)
    with open(filename) as inputfile:
        data = yaml.load(inputfile)
    if "profile" not in data.keys():
        raise MalformattedFileException(
            f"{filename} does not contain a profile.")
    if "num_cand" in data.keys():
        num_cand = int(data["num_cand"])
    else:
        num_cand = max(cand for approval_set in data["profile"]
                       for cand in approval_set) + 1
    profile = Profile(num_cand)
    approval_sets = data["profile"]
    if "voter_weights" in data.keys():
        weights = data["voter_weights"]
        if len(weights) != len(approval_sets):
            raise MalformattedFileException(
                f"{filename}: the number of voters differs from the number of voter weights."
            )
        for appr_set, weight in zip(approval_sets, weights):
            profile.add_voter(Voter(appr_set, weight=weight))
    else:
        profile.add_voters(approval_sets)

    if "committeesize" in data.keys():
        committeesize = int(data["committeesize"])
    else:
        committeesize = None

    if "compute" in data.keys():
        compute_instances = data["compute"]
    else:
        compute_instances = []
    for compute_instance in compute_instances:
        if "rule_id" not in compute_instance.keys():
            raise MalformattedFileException(
                'Each rule instance (dict) requires key "rule_id".')
        compute_instance["profile"] = profile
        compute_instance["committeesize"] = committeesize
        if "result" in compute_instance.keys():
            if compute_instance["result"] is not None:
                # compute_instance["result"] should be a list of CandidateSet
                compute_instance["result"] = [
                    misc.CandidateSet(committee)
                    for committee in compute_instance["result"]
                ]

    for key in data.keys():
        if key not in ABC_YAML_VALID_KEYS:
            raise MalformattedFileException(
                f'Key "{key}" is not valid (undefined).')

    return profile, committeesize, compute_instances, data
예제 #14
0
def read_preflib_file(filename,
                      setsize=1,
                      relative_setsize=None,
                      use_weights=False):
    """
    Read a Preflib file (soi, toi, soc or toc).

    Parameters
    ----------
        filename : str
            Name of the Preflib file.

        setsize : int
            Minimum number of candidates that voters approve.

            These candidates are taken from the top of ranking.
            In case of ties, more than setsize candidates are approved.

            Paramer `setsize` is ignored if `relative_setsize` is used.

        relative_setsize : float
            Proportion (number between 0 and 1) of candidates that voters approve (rounded up).

            In case of ties, more candidates are approved.
            E.g., if there are 10 candidates and `relative_setsize=0.75`,
            then the voter approves the top 8 candidates.

        use_weights : bool, default=False
            Use weights of voters instead of individual voters.

            If False, treat vote count in Preflib file as the number of identical ballots,
            i.e., the number of voters that approve this set of candidates.
            If True, treat vote count as weight and use this weight in class Voter.

    Returns
    -------
        abcvoting.preferences.Profile
            Preference profile extracted from Preflib file.
    """
    if setsize <= 0:
        raise ValueError("Parameter setsize must be > 0")
    if relative_setsize and (relative_setsize <= 0.0
                             or relative_setsize > 1.0):
        raise ValueError("Parameter relative_setsize not in interval (0, 1]")
    with open(filename) as f:
        line = f.readline()
        num_cand = int(line.strip())
        candidate_map = {}
        for _ in range(num_cand):
            parts = f.readline().strip().split(",")
            candidate_map[int(parts[0].strip())] = ",".join(parts[1:]).strip()

        parts = f.readline().split(",")
        try:
            voter_count, _, unique_orders = (int(p.strip()) for p in parts)
        except ValueError as error:
            raise MalformattedFileException(
                f"Number of voters ill specified ({str(parts)}), should be triple of integers"
            ) from error

        approval_sets = []
        lines = [line.strip() for line in f.readlines() if line.strip()]
        if len(lines) != unique_orders:
            raise MalformattedFileException(
                f"Expected {unique_orders} lines that specify voters in the input, "
                f"encountered {len(lines)}")

    for line in lines:
        parts = line.split(",")
        if len(parts) < 1:
            continue
        try:
            count = int(parts[0])
        except ValueError as error:
            raise MalformattedFileException(
                f"Each ranking must start with count/weight ({line})."
            ) from error
        ranking = parts[1:]  # ranking starts after count
        if len(ranking) == 0:
            raise MalformattedFileException("Empty ranking: " + str(line))
        if relative_setsize:
            num_appr = int(ceil(len(ranking) * relative_setsize))
        else:
            num_appr = setsize
        approval_set = _approval_set_from_preflib_datastructures(
            num_appr, ranking, candidate_map)
        approval_sets.append((count, approval_set))

    # normalize candidates to 0, 1, 2, ...
    cand_names = []
    normalize_map = {}
    for cand, name in candidate_map.items():
        cand_names.append(name)
        normalize_map[cand] = len(cand_names) - 1

    profile = Profile(num_cand, cand_names=cand_names)

    for count, approval_set in approval_sets:
        normalized_approval_set = []
        for cand in approval_set:
            normalized_approval_set.append(normalize_map[cand])
        if use_weights:
            profile.add_voter(Voter(normalized_approval_set, weight=count))
        else:
            profile.add_voters([normalized_approval_set] * count)
    if use_weights:
        if len(profile) != unique_orders:
            raise MalformattedFileException(
                "Number of voters wrongly specified in preflib file.")
    else:
        if len(profile) != voter_count:
            raise MalformattedFileException(
                "Number of voters wrongly specified in preflib file.")
    return profile