diff --git a/src/vse_sim/methods/borda.py b/src/vse_sim/methods/borda.py index e19bab0..13b9ee7 100644 --- a/src/vse_sim/methods/borda.py +++ b/src/vse_sim/methods/borda.py @@ -5,6 +5,13 @@ class Borda(Method): + """Implement Borda count with larger rank values representing preference. + + Honest ballots assign consecutive scores from least to most preferred. + Ranked methods also inherit this class's ballot construction and strategy + helpers. + """ + candScore = staticmethod(mean) nRanks = 999 # infinity diff --git a/src/vse_sim/methods/bullety_approval.py b/src/vse_sim/methods/bullety_approval.py index a3dc7ba..4ea4428 100644 --- a/src/vse_sim/methods/bullety_approval.py +++ b/src/vse_sim/methods/bullety_approval.py @@ -6,10 +6,15 @@ def BulletyApprovalWith(bullets=0.5, asClass=False): + """Create approval voting with a configurable share of bullet voters. - + Each honest ballot is either ordinary normalized approval or a bullet vote + for all utility-maximizing candidates. ``bullets`` is the probability of + bullet voting. + """ class BulletyApproval((Score(1,True))): + """Implement the configured mixture of approval and bullet voting.""" bulletiness = bullets diff --git a/src/vse_sim/methods/irnr.py b/src/vse_sim/methods/irnr.py index 6a7a881..12511d5 100644 --- a/src/vse_sim/methods/irnr.py +++ b/src/vse_sim/methods/irnr.py @@ -3,6 +3,13 @@ class IRNR(RankedMethod): + """Implement Instant Runoff Normalized Ratings. + + In each round, every ballot is normalized by the absolute ratings of its + remaining candidates. The candidate with the lowest normalized total is + eliminated until one remains. + """ + stratMax = 10 stratTargetFor = Method.stratTarget3 # strategize in favor of third place, because second place is pointless (can't change pairwise) diff --git a/src/vse_sim/methods/irv.py b/src/vse_sim/methods/irv.py index b3f4775..3c37b82 100644 --- a/src/vse_sim/methods/irv.py +++ b/src/vse_sim/methods/irv.py @@ -3,10 +3,12 @@ class Irv(Method): - """ - IRV. + """Implement Instant-Runoff Voting over complete ranked ballots. - High numbers are good for both results and votes (pretty sure). + Ballots are candidate-aligned rank vectors where larger values indicate + stronger preference. Tabulation repeatedly eliminates the candidate with + the fewest active first preferences and returns candidate-aligned finish + scores, again with larger values preferred. """ stratTargetFor = Method.stratTarget3 diff --git a/src/vse_sim/methods/irv_prime.py b/src/vse_sim/methods/irv_prime.py index 555be71..857e398 100644 --- a/src/vse_sim/methods/irv_prime.py +++ b/src/vse_sim/methods/irv_prime.py @@ -3,10 +3,11 @@ class IrvPrime(Irv): - """ - IRV Prime. + """Implement IRV Prime, preserving pairwise challengers during elimination. - See https://electowiki.org/wiki/IRV_Prime + The classic IRV winner and candidates that defeat it pairwise are protected + until all other candidates have been eliminated. See + https://electowiki.org/wiki/IRV_Prime. """ stratTargetFor = Method.stratTarget3 diff --git a/src/vse_sim/methods/mav.py b/src/vse_sim/methods/mav.py index d40a710..776befc 100644 --- a/src/vse_sim/methods/mav.py +++ b/src/vse_sim/methods/mav.py @@ -15,7 +15,11 @@ def toVote(cutoffs, util): class Mav(Method): - """Majority Approval Voting. + """Implement Majority Approval Voting with five ordered grades. + + Honest ballots map utilities through election-scoped percentile cutoffs. + Candidates are ordered by their median grade with a fractional tiebreak + derived from support above that grade. """ bias5 = 1.0970202515275356 diff --git a/src/vse_sim/methods/mj.py b/src/vse_sim/methods/mj.py index 76ef612..b4661ee 100644 --- a/src/vse_sim/methods/mj.py +++ b/src/vse_sim/methods/mj.py @@ -2,6 +2,12 @@ class Mj(Mav): + """Implement Majority Judgment using Mav's five-grade ballots. + + Candidate scores refine the median grade according to the balance of + grades immediately above and below the median. + """ + def candScore(self, scores): """This formula will always give numbers within 0.5 of the raw median. Unfortunately, with 5 grade levels, these will tend to be within 0.1 of diff --git a/src/vse_sim/methods/plurality.py b/src/vse_sim/methods/plurality.py index 3b0d848..445633b 100644 --- a/src/vse_sim/methods/plurality.py +++ b/src/vse_sim/methods/plurality.py @@ -4,6 +4,11 @@ class Plurality(RankedMethod): + """Implement plurality voting with one vote for each voter's favorite. + + Ballots are binary candidate-aligned vectors: the favorite receives one + and every other candidate receives zero. + """ nRanks = 2 diff --git a/src/vse_sim/methods/ranked_pairs.py b/src/vse_sim/methods/ranked_pairs.py index 9e61edc..4cd6e24 100644 --- a/src/vse_sim/methods/ranked_pairs.py +++ b/src/vse_sim/methods/ranked_pairs.py @@ -3,6 +3,12 @@ class Rp(Schulze): + """Implement Ranked Pairs using Schulze's pairwise tallying interface. + + Pairwise victories are considered from strongest to weakest and locked + unless doing so would contradict an already locked path. + """ + def resolveCycle(self, cmat, n): """Note: mutates cmat destructively. diff --git a/src/vse_sim/methods/schulze.py b/src/vse_sim/methods/schulze.py index 50a533f..984f49b 100644 --- a/src/vse_sim/methods/schulze.py +++ b/src/vse_sim/methods/schulze.py @@ -5,6 +5,13 @@ class Schulze(RankedMethod): + """Implement the Schulze strongest-path Condorcet method. + + Candidate pairs are compared by ballot rank, then cycles are resolved by + comparing the strongest paths through the pairwise preference graph. + Deterministic candidate-index tiebreaks produce candidate-aligned scores. + """ + def resolveCycle(self, cmat, n): beatStrength = [[0] * n for _ in range(n)] diff --git a/src/vse_sim/methods/score.py b/src/vse_sim/methods/score.py index e44f868..274b9cd 100644 --- a/src/vse_sim/methods/score.py +++ b/src/vse_sim/methods/score.py @@ -5,9 +5,15 @@ def Score(topRank=10, asClass=False): + """Create a score voting method with ratings from zero through ``topRank``. + + Honest ballots linearly normalize each voter's utilities to the configured + scale. By default the factory returns a method instance; ``asClass=True`` + returns the generated class for use by related method factories. + """ class Score0to(Method): - """Score voting, 0-10. + """Implement score voting on the scale configured by :func:`Score`. Strategy establishes pivots diff --git a/src/vse_sim/methods/srv.py b/src/vse_sim/methods/srv.py index 9679e27..e6e5006 100644 --- a/src/vse_sim/methods/srv.py +++ b/src/vse_sim/methods/srv.py @@ -7,7 +7,12 @@ def Srv(topRank=10): - """Score Runoff Voting + """Create Score Runoff Voting on a zero-through-``topRank`` scale. + + The two highest score totals advance to a pairwise runoff. Candidate + results remain candidate-aligned scores, with a runoff upset represented + by placing the pairwise winner just above the score winner. + >>> Srv().resultsFor(DeterministicModel(3)(5,3),Irv().honBallot)["results"] [0.8, 1.2, 1.21] >>> Srv().results([[0,1,2]])[2] @@ -27,6 +32,7 @@ def Srv(topRank=10): score0to = Score(topRank,True) class Srv0to(score0to): + """Implement Score Runoff Voting on the factory's configured scale.""" stratTargetFor = Method.stratTarget3 diff --git a/src/vse_sim/methods/v321.py b/src/vse_sim/methods/v321.py index 22b3ef1..fa6a1e3 100644 --- a/src/vse_sim/methods/v321.py +++ b/src/vse_sim/methods/v321.py @@ -7,6 +7,14 @@ class V321(Mav): + """Implement 3-2-1 Voting with three-level candidate ratings. + + The three candidates with the most top ratings advance, the candidate with + the most bottom ratings is removed, and the remaining pair is compared + head-to-head. Result values encode the complete candidate ordering used by + the simulator. + """ + baseCuts = [-.1,.8] specificPercentiles = [45, 75]