Merged
Conversation
…the STVAnimation initialization, instead of supplying it to the call.
…contain all the winners.
Bug fix: now considers unranked candidates as tied for last place, and thus able to be vetoed. Various optimizations, most importantly relying on profile.df instead of duplicated Ballot objects to conduct election. For deterministic tiebreak, approximately 50-60x faster than previous implementation (testing on a 3-candidate election with 26k voters; was ~2.8s, now <0.05s). For random tiebreak, only about 8-10x faster. Now supports `tiebreak` = 'random', 'first_place', 'borda', and 'lex'. `tiebreak` is no longer permitted to be `None`. The default value is 'first_place', and the backup tiebreak is always 'lex', i.e., lexicographic/alphabetical. Because PluralityVeto requires tiebreaking at the ballot level (rather than round level), PluralityVeto does not report tiebreaks to ElectionState. However, a new attribute `tiebreak_order` allows users to inspect the tiebreak order when using a non-random tiebreak method. Refactored significantly for readability. Corrected existing documentation and added thorough documentation and comments.
Tech debt/move to full astral stack
…in-elections Tech debt/rename default constants in elections
BlockPlurality can be equally well defined as a Scoring rule and as a Ranking rul, so we make a dispatch class that creates the appropriate subclass.
Update docs and clean up some errors
Bugfix/boosted dictator and other misc
This will be used to improve user's ability to zip large numbers of profiles for modeling experiments
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR bumps VoteKit to v3.4.0 and modernizes the project’s packaging/tooling while adding and exposing new election methods and improving documentation/public import paths.
Changes:
- Introduces new ranking elections (e.g.,
SimultaneousVeto,Schulze) and a unifiedBlockPluralitythat dispatches by profile type. - Renames the election seat parameter from
mton_seatsacross election APIs with deprecation handling. - Migrates dev tooling from Poetry/Black/Mypy to uv/go-task/Ruff/Ty, updates docs, and patches
__module__for Sphinx-friendly public import paths.
Reviewed changes
Copilot reviewed 84 out of 303 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/votekit/elections/election_types/ranking/stv/init.py | Re-export STV types and patch __module__ for Sphinx canonical paths |
| src/votekit/elections/election_types/ranking/simultaneous_veto.py | Adds new SimultaneousVeto ranking election implementation |
| src/votekit/elections/election_types/ranking/schulze.py | Adds new Schulze ranking election implementation |
| src/votekit/elections/election_types/ranking/ranked_pairs.py | Renames m→n_seats and updates initialization/logic accordingly |
| src/votekit/elections/election_types/ranking/random_dictator.py | Renames m→n_seats and adds deprecated kwarg handling |
| src/votekit/elections/election_types/ranking/plurality.py | Renames m→n_seats and updates docs/selection logic |
| src/votekit/elections/election_types/ranking/dominating_sets.py | Minor formatting/import ordering cleanup |
| src/votekit/elections/election_types/ranking/condo_borda.py | Renames m→n_seats with deprecated kwarg handling |
| src/votekit/elections/election_types/ranking/borda.py | Renames m→n_seats with deprecated kwarg handling |
| src/votekit/elections/election_types/ranking/boosted_random_dictator.py | Renames m→n_seats and adds deprecated kwarg handling |
| src/votekit/elections/election_types/ranking/block_plurality.py | Adds ranked-profile implementation for block plurality via Borda scoring |
| src/votekit/elections/election_types/ranking/alaska.py | Tightens typing for quota/tiebreak and cleans imports/formatting |
| src/votekit/elections/election_types/ranking/abstract_ranking.py | Adds n_seats to base ranking election and strengthens profile validation |
| src/votekit/elections/election_types/ranking/init.py | Re-exports more elections and patches __module__ for Sphinx |
| src/votekit/elections/election_types/block_plurality.py | Adds BlockPlurality dispatcher supporting RankProfile + ScoreProfile |
| src/votekit/elections/election_types/approval/approval.py | Refactors approval/“bloc plurality” implementation and deprecation handling |
| src/votekit/elections/election_types/approval/init.py | Restricts exports and patches __module__ |
| src/votekit/elections/election_types/init.py | Aggregates exports, adds new elections, patches __module__ |
| src/votekit/elections/election_state.py | Minor formatting cleanup |
| src/votekit/elections/_deprecation.py | Adds helper for deprecated kwarg renames |
| src/votekit/elections/init.py | Re-exports elections and patches __module__ for Sphinx |
| src/votekit/cvr_loaders/load_scottish.py | Import ordering and minor formatting |
| src/votekit/cvr_loaders/load_ranking_csv.py | Formatting + ensures Voter Set is object dtype when inserted |
| src/votekit/cvr_loaders/init.py | Patches __module__ for canonical import paths |
| src/votekit/cleaning/score_profiles_cleaning.py | Import ordering + formatting |
| src/votekit/cleaning/score_ballots_cleaning.py | Import ordering |
| src/votekit/cleaning/rank_profiles_cleaning.py | Import ordering + formatting |
| src/votekit/cleaning/rank_ballots_cleaning.py | Import ordering |
| src/votekit/cleaning/init.py | Reorders exports + patches __module__ |
| src/votekit/ballot_generator/std_generator/spacial.py | Docstring improvements + dataframe Voter Set dtype fix |
| src/votekit/ballot_generator/std_generator/impartial_culture.py | Changes IC sampling approach, likely for performance |
| src/votekit/ballot_generator/std_generator/impartial_anon_culture.py | Reworks IAC generation via stars-and-bars sampling |
| src/votekit/ballot_generator/std_generator/init.py | Export ordering + patches __module__ |
| src/votekit/ballot_generator/bloc_slate_generator/slate_utils.py | Updates imports and ensures Voter Set dtype is object |
| src/votekit/ballot_generator/bloc_slate_generator/slate_plackett_luce.py | Updates config import path + adds numeric validation |
| src/votekit/ballot_generator/bloc_slate_generator/slate_bradley_terry.py | Updates config import path + formatting fixes |
| src/votekit/ballot_generator/bloc_slate_generator/name_plackett_luce.py | Updates config import path + ensures Voter Set dtype is object |
| src/votekit/ballot_generator/bloc_slate_generator/name_bradley_terry.py | Updates config import path + formatting fixes |
| src/votekit/ballot_generator/bloc_slate_generator/cumulative.py | Updates config import path + formatting fixes |
| src/votekit/ballot_generator/bloc_slate_generator/config/validation.py | Adds new bloc-slate config validation utilities |
| src/votekit/ballot_generator/bloc_slate_generator/config/collections.py | Adds mutable collection wrappers for config objects |
| src/votekit/ballot_generator/bloc_slate_generator/config/init.py | Exposes config utilities + patches __module__ |
| src/votekit/ballot_generator/bloc_slate_generator/cambridge.py | Updates config import path + formatting fixes |
| src/votekit/ballot_generator/bloc_slate_generator/init.py | Re-exports + patches __module__ |
| src/votekit/ballot_generator/ballot_generator.py | Import ordering + formatting fixes |
| src/votekit/ballot_generator/init.py | Re-exports + patches __module__ |
| src/votekit/ballot.py | Adds overloads + enforces mutual exclusivity of ranking vs scores |
| src/votekit/init.py | Export ordering + patches __module__ |
| scripts/draft_release.py | Adds script to bump version and insert changelog header |
| run_tests.sh | Removes Poetry-based test runner script |
| pyproject.toml | Bumps version, migrates to uv dependency groups + hatch build backend |
| poetry.toml | Removes Poetry virtualenv config |
| notebooks/Portland_D1_cleaned_votekit_pref_profile.csv | Reorders candidate list output (data artifact) |
| notebooks/5_score_based.ipynb | Updates examples to new parameter names and new output formatting |
| docs/user/tutorial/7_portland_case_study.rst | Updates tutorial outputs and parameter renames |
| docs/user/tutorial/5_score_based.rst | Updates tutorial for new score election parameter names |
| docs/user/tutorial/3_viz.rst | Fixes doc link to a Sphinx ref target |
| docs/user/tutorial/1_intro.rst | Updates displayed RankProfile output formatting |
| docs/sync_contributing.py | Adds script to sync CONTRIBUTING.md into docs |
| docs/social_choice_docs/scr.rst | Adds new sections + anchors for docs cross-references |
| docs/package_info/contributing.rst | Removes old RST contributing page (replaced by generated MD) |
| docs/package_info/api.rst | Switches docs to public re-exports and explicit autoclass/autofunction targets |
| docs/index.rst | Updates contributing doc reference |
| docs/conf.py | Runs contributing sync during Sphinx build and adjusts sys.path |
| Taskfile.yml | Adds go-task workflow for formatting/linting/typecheck/tests/docs |
| README.md | Updates contributor workflow to uv/go-task and new commands |
| MANIFEST.in | Removes manifest rules (packaging strategy changed) |
| CONTRIBUTING.md | Adds new contributor guide for uv/go-task/Ruff/Ty |
| CHANGELOG.md | Adds a detailed Unreleased section for 3.4.0 changes |
| .pre-commit-config.yaml | Switches hooks to local uv/go-task based commands |
| .github/workflows/test.yml | Migrates CI tests from Poetry to go-task/uv |
| .github/workflows/lint.yml | Migrates CI lint/typecheck from Poetry to go-task/uv |
Comments suppressed due to low confidence (1)
src/votekit/elections/election_types/approval/approval.py:1
Approvalcurrently has no__init__enforcing approval constraints (e.g.,per_candidate_limit=1and no total budget), while the new initializer that setsper_candidate_limit=1is attached toBlocPlurality. This makesApproval(...)behave like the baseGeneralRatingand breaks the intended API/export (approval/__init__.pyexports onlyApproval). Move the initializer toApproval(or rename the class appropriately) soApprovalactually implements approval voting, and keepBlocPlurality(if still needed) as the budget-limited variant with the correct semantics.
from typing import Optional
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v3.4.0
Added
STVAnimationmodule (optionalmanimdependency) for visualizing STV elections (PR Animations for STV Elections #249):ColorPalettedataclass for customizing candidate colors.save()method for writing to fileSimultaneousVetoelection method with support for harmonic Borda scores (PR SimultaneousVeto implementation #333)SerialVetoelection class;PluralityVetoandSerialVetoare now subclasses of a sharedSequentialVetobase class (PR Plurality veto major rework #325)strictparameter toget_condorcet_cyclesfor detecting strict Condorcet cycles (PR feat: add strict parameter to get_condorcet_cycles (#327) #330, closes Add option for strong vs weak condorcet cycles into pwcg #327)FastIRVandFastSequentialRCVelection methods built onNumpyInnerSTVAlbanySTVelection method (closes Add Albany, CA STV method? #281)allow_zero_support_candidatesparameter toBlocSlateConfig, allowing candidates with zero support in preference intervals (PR Tech debt/refactor bsc #338, closes In new ballot generator config copy on assign for prefrence df #298)go-tasktask runner and updated contributing guide (PR Tech debt/migrate to uv #342, closes Update test/linter commands in contributing #277)Changed
uvand task running togo-task(PR Tech debt/migrate to uv #342, Tech debt/move to full astral stack #352)black/isort/mypywithruff/tyfor formatting and type checking (PR Tech debt/move to full astral stack #352)__init__parameter names made more descriptive (PR Tech debt/rename default constants in elections #355)PluralityVeto(PR Plurality veto major rework #325):tiebreak=Noneis no longer permitted; default is now'first_place'with'lex'as backuptiebreak_orderattribute for inspecting the tiebreak orderprofile.dfdirectlyBlocSlateConfigrefactored to reduce nesting; error and warning strings are now dynamically formatted (PR Tech debt/refactor bsc #338, closes Another function for setting bloc + slate preference interval #299)get_preference_interval_for_bloc_and_slatenow only validates the target bloc/slate rather than the full config (PR Tech debt/refactor bsc #338)NumpyElectionrefactored into the abstract base classNumpyInnerSTVElectionCore,STVCore, andNumpyElectionclassesPreferenceProfile.to_csvnow encodes candidate names with integer IDs (e.g.(Aleine:0),(Alex:1)) instead of the previous shortened prefix strings (e.g.(Aleine:Alei),(Alex:Alex)). This avoids ambiguity when candidates share long common prefixes. Old prefix-format CSVs are still fully readable byfrom_csv(PR Bugfix/boosted dictator and other misc #361)BlockPluralitymoved out of theapprovalsubmodule and now accepts bothRankProfileandScoreProfileinputs, dispatching to the appropriate ranked or score-based implementation (PR Update docs and clean up some errors #360, closes Bloc Plurality -> Block Plurality #351)RankingElectionbase class now validates that the profile is non-empty, contains at least one ranked candidate, and has enough candidates who received votes to fill the requested seats (PR Update docs and clean up some errors #360, closes Elections with no votes #356)Fixed
index_to_lexicographic_ballotto correctly handle short ballots (PR Update index_to_lexicographic_ballot to get short ballots correctly #348)max_ranking_lengthvia ties (PR Bugfix: max candidates ranked > max_ranking_length #334)BlocSlateConfig(PR Tech debt/refactor bsc #338)PluralityVetoto treat unranked candidates as tied for last place and thus eligible to be vetoed (PR Plurality veto major rework #325)pairwise_dictto usecandidates_castinstead ofcandidates, preventing errors in elections likeRankedPairswhen a candidate received no votes (PR Bugfix/boosted dictator and other misc #361, closes Ranked-Pairs doesn't work when there is a candidate that was never cast #309)BoostedRandomDictatorandRandomDictatortests by increasing sample sizes (PR Bugfix/boosted dictator and other misc #361, closes Fix constraints on the boosted random dictator test #339)pytest-xdist(-n auto). Bijection tests write totmp_pathinstead of fixed data directories, and random seed usage is properly isolated.