Exemple #1
0
def __revseq_thiele_irresolute(profile, committeesize, scorefct_str):
    """Compute an *irresolute* sequential Thiele method

    Consider all possible ways to break ties between candidates
    (aka parallel universe tiebreaking)
    """
    scorefct = scores.get_scorefct(scorefct_str, committeesize)

    allcandcomm = tuple(range(profile.num_cand))
    comm_scores = {allcandcomm: scores.thiele_score(
        scorefct_str, profile, allcandcomm)}

    for _ in range(profile.num_cand - committeesize):
        comm_scores_next = {}
        for committee, score in comm_scores.items():
            marg_util_cand = scores.marginal_thiele_scores_remove(
                scorefct, profile, committee)
            score_reduction = min(marg_util_cand)
            # find smallest elements in marg_util_cand and return indices
            cands_to_remove = [cand for cand in range(profile.num_cand)
                               if marg_util_cand[cand] == min(marg_util_cand)]
            for c in cands_to_remove:
                next_comm = tuple(set(committee) - set([c]))
                comm_scores_next[next_comm] = score - score_reduction
            comm_scores = comm_scores_next
    return sort_committees(list(comm_scores.keys()))
Exemple #2
0
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)
Exemple #3
0
def compute_thiele_method(scorefct_str, profile, committeesize,
                          algorithm="gurobi", resolute=False, verbose=0):
    """Thiele methods

    Compute winning committees of the Thiele method specified
    by the score function (scorefct_str)
    """
    enough_approved_candidates(profile, committeesize)
    scorefct = scores.get_scorefct(scorefct_str, committeesize)

    # optional output
    if verbose:
        print(header(rules[scorefct_str].longname))
        if resolute:
            print("Computing only one winning committee (resolute=True)\n")
    if verbose >= 3:
        if algorithm == "gurobi":
            print("Using the Gurobi ILP solver\n")
        if algorithm == "branch-and-bound":
            print("Using a branch-and-bound algorithm\n")
    # end of optional output

    if algorithm == "gurobi":
        committees = abcrules_gurobi.__gurobi_thiele_methods(
            profile, committeesize, scorefct, resolute)

        committees = sort_committees(committees)
    elif algorithm == "branch-and-bound":
        committees = __thiele_methods_branchandbound(
            profile, committeesize, scorefct_str, resolute)
    else:
        raise NotImplementedError(
            "Algorithm " + str(algorithm)
            + " not specified for compute_thiele_method")

    # optional output
    if verbose >= 2:
        print("Optimal " + scorefct_str.upper() + "-score: "
              + str(scores.thiele_score(scorefct_str, profile, committees[0])))
        print()
    if verbose:
        print(str_committees_header(committees, winning=True))
        print(str_candsets(committees, names=profile.names))
    # end of optional output

    return committees
Exemple #4
0
def __revseq_thiele_resolute(profile, committeesize, scorefct_str, verbose):
    """Compute a *resolute* reverse sequential Thiele method

    Tiebreaking between candidates in favor of candidate with smaller
    number/index (candidates with smaller numbers are added first).
    """
    scorefct = scores.get_scorefct(scorefct_str, committeesize)

    committee = set(range(profile.num_cand))

    # optional output
    if verbose >= 2:
        output = "full committee (" + str(len(committee))
        output += " candidates) has a total score of "
        output += str(scores.thiele_score(
            scorefct_str, profile, committee))
        print(output + "\n")
    # end of optional output

    for _ in range(profile.num_cand - committeesize):
        marg_util_cand = scores.marginal_thiele_scores_remove(
            scorefct, profile, committee)
        score_reduction = min(marg_util_cand)
        # find smallest elements in marg_util_cand and return indices
        cands_to_remove = [cand for cand in range(profile.num_cand)
                           if marg_util_cand[cand] == min(marg_util_cand)]
        committee.remove(cands_to_remove[-1])

        # optional output
        if verbose >= 2:
            rem_cand = cands_to_remove[-1]
            output = "removing candidate number "
            output += str(profile.num_cand - len(committee)) + ": "
            output += profile.names[rem_cand] + "\n"
            output += " score decreases by "
            output += str(score_reduction)
            output += " to a total of "
            output += str(scores.thiele_score(
                scorefct_str, profile, committee))
            if len(cands_to_remove) > 1:
                output += " (tie between candidates "
                output += str_candset(cands_to_remove) + ")\n"
            print(output + "\n")
        # end of optional output

    return [committee]
Exemple #5
0
def __seq_thiele_resolute(profile, committeesize, scorefct_str, verbose):
    """Compute a *resolute* reverse sequential Thiele method

    Tiebreaking between candidates in favor of candidate with smaller
    number/index (candidates with larger numbers get deleted first).
    """
    committee = []

    scorefct = scores.get_scorefct(scorefct_str, committeesize)

    # optional output
    if verbose >= 2:
        output = "starting with the empty committee (score = "
        output += str(scores.thiele_score(
            scorefct_str, profile, committee)) + ")"
        print(output + "\n")
    # end of optional output

    # build a committee starting with the empty set
    for _ in range(committeesize):
        additional_score_cand = scores.marginal_thiele_scores_add(
            scorefct, profile, committee)
        next_cand = additional_score_cand.index(max(additional_score_cand))
        committee.append(next_cand)
        # optional output
        if verbose >= 2:
            output = "adding candidate number "
            output += str(len(committee)) + ": "
            output += profile.names[next_cand] + "\n"
            output += " score increases by "
            output += str(max(additional_score_cand))
            output += " to a total of "
            output += str(scores.thiele_score(
                scorefct_str, profile, committee))
            tied_cands = [c for c in range(len(additional_score_cand))
                          if (c > next_cand and
                              (additional_score_cand[c]
                               == max(additional_score_cand)))]
            if len(tied_cands) > 0:
                output += " tie broken in favor of " + str(next_cand)
                output += " candidates " + str_candset(tied_cands)
                output += " would increase the score by the same amount ("
                output += str(max(additional_score_cand)) + ")"
            print(output + "\n")
        # end of optional output
    return [committee]
Exemple #6
0
def __thiele_methods_branchandbound(profile, committeesize,
                                    scorefct_str, resolute):
    """Branch-and-bound algorithm to compute winning committees
    for Thiele methods"""
    enough_approved_candidates(profile, committeesize)
    scorefct = scores.get_scorefct(scorefct_str, committeesize)

    best_committees = []
    init_com = compute_seq_thiele_method(
        profile, committeesize, scorefct_str, resolute=True)[0]
    best_score = scores.thiele_score(scorefct_str, profile, init_com)
    part_coms = [[]]
    while part_coms:
        part_com = part_coms.pop(0)
        # potential committee, check if at least as good
        # as previous best committee
        if len(part_com) == committeesize:
            score = scores.thiele_score(scorefct_str, profile, part_com)
            if score == best_score:
                best_committees.append(part_com)
            elif score > best_score:
                best_committees = [part_com]
                best_score = score
        else:
            if len(part_com) > 0:
                largest_cand = part_com[-1]
            else:
                largest_cand = -1
            missing = committeesize - len(part_com)
            marg_util_cand = scores.marginal_thiele_scores_add(
                scorefct, profile, part_com)
            upper_bound = (
                sum(sorted(marg_util_cand[largest_cand + 1:])[-missing:])
                + scores.thiele_score(scorefct_str, profile, part_com))
            if upper_bound >= best_score:
                for c in range(largest_cand + 1,
                               profile.num_cand - missing + 1):
                    part_coms.insert(0, part_com + [c])

    committees = sort_committees(best_committees)
    if resolute:
        committees = [committees[0]]

    return committees
Exemple #7
0
def __seq_thiele_irresolute(profile, committeesize, scorefct_str):
    """Compute an *irresolute* reverse sequential Thiele method

    Consider all possible ways to break ties between candidates
    (aka parallel universe tiebreaking)
    """
    scorefct = scores.get_scorefct(scorefct_str, committeesize)

    comm_scores = {(): 0}
    # build committees starting with the empty set
    for _ in range(committeesize):
        comm_scores_next = {}
        for committee, score in comm_scores.items():
            # marginal utility gained by adding candidate to the committee
            additional_score_cand = scores.marginal_thiele_scores_add(
                scorefct, profile, committee)
            for c in range(profile.num_cand):
                if additional_score_cand[c] >= max(additional_score_cand):
                    next_comm = tuple(sorted(committee + (c,)))
                    comm_scores_next[next_comm] = (
                        score + additional_score_cand[c])
        comm_scores = comm_scores_next
    return sort_committees(list(comm_scores.keys()))