Skip to content

Bugfix: max candidates ranked > max_ranking_length#334

Merged
peterrrock2 merged 2 commits intomggg:3.4.0from
ross-i:max-cands-ranked-safety
Feb 28, 2026
Merged

Bugfix: max candidates ranked > max_ranking_length#334
peterrrock2 merged 2 commits intomggg:3.4.0from
ross-i:max-cands-ranked-safety

Conversation

@ross-i
Copy link
Copy Markdown
Contributor

@ross-i ross-i commented Feb 27, 2026

If a RankProfile contains a ballot that ranks more than max_ranking_length candidates, some of the scoring functions can either crash or silently return incorrect results.

For example, consider a profile with a single ballot that ranks candidates A and B as tied for first. first_place_votes(profile, scoring_tie_convention='low') should return 0 for each candidate, but it instead returns 1. The score functions create a score_vector whose length is max_ranking_length, so, in this case, the score_vector is just [1], and when we have two candidates tied for first, taking min([1]) gives both 1. The correct behavior should be: the score_vector is [1, 0], so min yields 0.

To fix this, we should consider such profiles to be malformed, and insist that the user explicitly pass max_ranking_length.

The implementation: RankProfile now has a cached_property max_candidates_ranked, which, at initialization, is computed and compared against max_ranking_length. If max_candidates_ranked > max_ranking_length, RankProfile throws an error and suggests that the user either clean the profile to remove ties or explicitly pass max_ranking_length equal to whatever it computed max_candidates_ranked to be.

I updated the following tests:

  1. utils - added a check that this error is raised
  2. random dictator and boosted random dictator - these created profiles that are now considered malformed, so I added explicit passing of max_ranking_length.
  3. test_rank_pp_ranking_length - this created a malformed profile (4 candidates ranked, but max_ranking_length implicitly equal to 3) and insisted that the result was a profile whose max_ranking_length is 3. I changed this to be a check for the error (same as utils essentially)

…ed more than max_ranking_length

candidates (via ties)
Copy link
Copy Markdown
Collaborator

@peterrrock2 peterrrock2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great!

Small nit: I am trying to unify the doc string style accoss the repository. Can you modify the doc strings to look more like:

def  foo( arg1: str | None, arg2: int = 3) -> str:
    """
    Brief description (max 100 chars including indents)

    More details (max 100 chars per line, but as many lines as you would like)

    Example: 
        <Stuff goes here. This section is optional>

    Args:
        arg1 (str | None): description
        arg2 (int, optional): description. Defaults to 3.

    Returns:
        str: The string "baz"
    """
   ...

This is a mild modification of the Google-style doc string and adheres to the PEP 257 recommendation but with the line width modified to 100 chars rather than 80 (better for 16:9 screens IMO)

@ross-i
Copy link
Copy Markdown
Contributor Author

ross-i commented Feb 28, 2026

This is looking great!

Small nit: I am trying to unify the doc string style accoss the repository. Can you modify the doc strings to look more like:

def  foo( arg1: str | None, arg2: int = 3) -> str:
    """
    Brief description (max 100 chars including indents)

    More details (max 100 chars per line, but as many lines as you would like)

    Example: 
        <Stuff goes here. This section is optional>

    Args:
        arg1 (str | None): description
        arg2 (int, optional): description. Defaults to 3.

    Returns:
        str: The string "baz"
    """
   ...

This is a mild modification of the Google-style doc string and adheres to the PEP 257 recommendation but with the line width modified to 100 chars rather than 80 (better for 16:9 screens IMO)

Done!

@peterrrock2 peterrrock2 merged commit 92c5314 into mggg:3.4.0 Feb 28, 2026
3 checks passed
@peterrrock2 peterrrock2 mentioned this pull request Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants