Skip to content

RankedPairs: recorded tiebreak resolutions don't match the ranking actually used #388

Description

@peterrrock2

Summary

In RankedPairs._run_step, tied dominating tiers are resolved twice, inconsistently. The resolution stored in ElectionState.tiebreaks comes from tiebreak_set(tier, tiebreak=self.tiebreak), but the final ordering (and therefore who gets elected) is built separately with sort_candidates_pseudo_lexicographically, which ignores that resolution.

For tiebreak="lexicographic" (the default) the two happen to agree, so the bug is currently invisible.For any other setting, e.g. tiebreak="random", the reported resolution and the actual outcome can disagree.

Reproduction Code

from votekit.ballot import RankBallot
from votekit.pref_profile import RankProfile
from votekit.elections import RankedPairs

profile = RankProfile(
    ballots=[
        RankBallot(ranking=(frozenset({"A"}), frozenset({"B"})), weight=1),
        RankBallot(ranking=(frozenset({"B"}), frozenset({"A"})), weight=1),
    ]
)

# A and B tie head to head, so the final digraph has them in one dominating tier.
# Repeat a few times since the random resolution lands on (A, B) half the time.
for _ in range(20):
    e = RankedPairs(profile, tiebreak="random")
    state = e.election_states[-1]
    print(state.tiebreaks, state.elected)

The output then includes rounds like:

{frozenset({'A', 'B'}): (frozenset({'B'}), frozenset({'A'}))} (frozenset({'A'}),)

i.e. the state says the tie resolved with B first, but A was elected. Elected candidates are
always in lexicographic order regardless of tiebreak.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions