public override Dictionary <Strategy, double> CalculateSatisfaction(Random random, CandidateComparerCollection <Voter> voters) { // This is here for comparison purposes, not analysis. var satisfaction = GetSatisfactionWith(voters)(voters.GetBeatMatrix().GetSchulzeSet()); return(new Dictionary <Strategy, double> { { Strategy.Honest, satisfaction }, }); }
public override ElectionResults GetElectionResults(CandidateComparerCollection <RankedBallot> ballots) { var approvalCount = new int[ballots.CandidateCount]; var firstChoices = new int[ballots.CandidateCount]; var approvalByBallot = new Dictionary <RankedBallot, ulong>(); var history = new List <(int[] NewApprovalCount, List <int> Winners, CountedList <(ulong Preferred, int Candidate)> Compromises)>(); foreach (var(ballot, count) in ballots.Comparers) { var firstChoiceCandidates = ballot.RanksByCandidate.IndexesWhere(a => a == 0).ToList(); approvalByBallot[ballot] = GetCoalition(firstChoiceCandidates); // Approve of one's first choices. foreach (var c in firstChoiceCandidates) { approvalCount[c] += count; firstChoices[c] += count; } } var winningScore = approvalCount.Max(); var winners = approvalCount.IndexesWhere(a => a == winningScore).ToList(); history.Add((firstChoices, winners, new CountedList <(ulong, int)>())); var beatMatrix = ballots.GetBeatMatrix(); var previousWinners = winners.ToDictionary( b => b, b => Enumerable.Range(0, ballots.CandidateCount) .Where(s => beatMatrix.Beats(s, b)) .ToList()); while (true) { var newApprovalCount = new int[ballots.CandidateCount]; var compromises = new CountedList <(ulong, int)>(); foreach (var(ballot, count) in ballots.Comparers) { var approveUntilRank = 0; var approvalCoalition = 0ul; // If one approves of all candidates which beat one of the previous winners, do so (and approve of candidates one likes better than the worst of those) // (if we don't, neither will the people who prefer the other saviours -- and we won't be able to beat the bogeyman) // otherwise, approve of all candidates one likes better than said winner. foreach (var(bogeyman, saviours) in previousWinners) { var bogeymanRank = ballot.RanksByCandidate[bogeyman]; var saviourRanks = saviours.Select(s => ballot.RanksByCandidate[s]).ToList(); if (saviours.Any() && saviourRanks.All(sr => sr > bogeymanRank)) { approvalCoalition |= GetCoalition(saviours); approveUntilRank = saviourRanks.Append(approveUntilRank).Min(); } else { approveUntilRank = Math.Min(approveUntilRank, bogeymanRank); } } var approvedCoalition = approvalByBallot[ballot]; var newApprovals = ballot.RanksByCandidate .IndexesWhere((rank, candidate) => (rank > approveUntilRank || (approvalCoalition & GetCoalition(candidate)) > 0) && (approvedCoalition & GetCoalition(candidate)) == 0ul) .ToList(); foreach (var candidate in newApprovals) { approvalCount[candidate] += count; newApprovalCount[candidate] += count; compromises.Add((approvedCoalition, candidate), count); } if (newApprovals.Any()) { approvalByBallot[ballot] = approvedCoalition | GetCoalition(newApprovals); } } winningScore = approvalCount.Max(); winners = approvalCount.IndexesWhere(a => a == winningScore).ToList(); if (newApprovalCount.Any(c => c > 0)) { history.Add((newApprovalCount, winners, compromises)); } var newBogeymen = winners.Where(w => !previousWinners.ContainsKey(w)); if (!newBogeymen.Any()) { break; } foreach (var b in newBogeymen) { previousWinners[b] = Enumerable.Range(0, ballots.CandidateCount) .Where(s => beatMatrix.Beats(s, b)) .ToList(); } } var results = new ElectionResults(approvalCount.IndexRanking()); if (history.Count == 1) { results.AddHeading("Approval"); results.AddCandidateTable(approvalCount); } else { results.AddHeading("Rounds"); results.AddTable(history.Select((h, i) => new ElectionResults.Value[] { i + 1, h.Winners, }.Concat(h.NewApprovalCount.Select(a => (ElectionResults.Value)a)) .ToArray()) .Append(approvalCount.Select(a => (ElectionResults.Value)a).Prepend(results.Ranking[0]).Prepend("Total").ToArray()), Enumerable.Range(0, ballots.CandidateCount).Select(c => (ElectionResults.Candidate)c).Prepend <ElectionResults.Value>("Winner").ToArray()); results.AddHeading("Winners"); results.AddTable( previousWinners.Select(kvp => new ElectionResults.Value[] { (ElectionResults.Candidate)kvp.Key, kvp.Value }), "Beaten By"); var roundNumber = 1; foreach (var round in history.Skip(1)) { roundNumber++; results.AddHeading("Round " + roundNumber + " Compromises"); results.AddTable(round.Compromises.Select(a => new ElectionResults.Value[] { (ElectionResults.Candidate)a.Item.Candidate, a.Item.Preferred, a.Count }), "Comp.", "Pref.", "Count"); } } return(results); }
public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <Bucket> > ballots) { var approvalCount = new int[ballots.CandidateCount]; var firstChoices = new int[ballots.CandidateCount]; var approvalByBallot = new Dictionary <RankedBallot, ulong>(); // Approve of one's first choices. foreach (var(ballot, count) in ballots.Comparers) { foreach (var c in ballot.Buckets.IndexesWhere(a => a == Bucket.Best)) { approvalCount[c] += count; firstChoices[c] += count; } } var beatMatrix = ballots.GetBeatMatrix(); var candidates = Enumerable.Range(0, ballots.CandidateCount); var compromises = new CountedList <(ulong Preferred, int Compromise, ulong Bogeymen)>(); foreach (var(ballot, count) in ballots.Comparers) { var best = ballot.Buckets.IndexesWhere(a => a == Bucket.Best).ToList(); var good = ballot.Buckets.IndexesWhere(a => a == Bucket.Good).ToList(); var bad = ballot.Buckets.IndexesWhere(a => a == Bucket.Bad).ToList(); // Support each "good" candidate `g` such that // For each "best" candidate `c` // There exists a "bad" candidate `b` (the 'bogeyman') such that `b` beats `c` in first-choice votes but loses to `g` one-on-one if (good.Any() && bad.Any()) { var bogeymen = bad .Where(b => best.All(c => firstChoices[b] > firstChoices[c])) .ToList(); if (bogeymen.Any()) { var bestCoalition = GetCoalition(best); var bogeymenCoalition = GetCoalition(bogeymen); foreach (var c in good.Where(g => bogeymen.Any(b => beatMatrix.Beats(g, b)))) { approvalCount[c] += count; compromises.Add((bestCoalition, c, bogeymenCoalition), count); } } } } var results = new ElectionResults(approvalCount.IndexRanking()); results.AddHeading("Votes"); results.AddTable( approvalCount.IndexOrderByDescending() .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c, approvalCount[c], firstChoices[c], approvalCount[c] - firstChoices[c], }), "Total", "First", "Comp."); results.AddHeading("Compromises"); results.AddTable(compromises .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c.Item.Compromise, c.Item.Preferred, c.Item.Bogeymen, c.Count }), "Comp.", "Pref.", "Bogey.", "Count"); return(results); }
public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <Bucket> > ballots) { // The [Mutual majority criterion] states that if there is a subset S of the candidates, // such that more than half of the voters strictly prefer every member of S to every candidate outside of S, // this majority voting sincerely, the winner must come from S. // // For each "mutual majority" set, each voter in said majority approves of all members of the majority set. // The winner is the one with the most approvals. var approvalCount = new int[ballots.CandidateCount]; var firstChoices = new int[ballots.CandidateCount]; var approvalByBallot = new Dictionary <RankedBallot, ulong>(); // Approve of one's first choices. foreach (var(ballot, count) in ballots.Comparers) { foreach (var c in ballot.Buckets.IndexesWhere(a => a == Bucket.Best)) { approvalCount[c] += count; firstChoices[c] += count; } } var beatMatrix = ballots.GetBeatMatrix(); var candidates = Enumerable.Range(0, ballots.CandidateCount); var compromises = new CountedList <(ulong Preferred, int Compromise, ulong Bogeymen)>(); foreach (var(ballot, count) in ballots.Comparers) { var best = ballot.Buckets.IndexesWhere(a => a == Bucket.Best).ToList(); var good = ballot.Buckets.IndexesWhere(a => a == Bucket.Good).ToList(); var bad = ballot.Buckets.IndexesWhere(a => a == Bucket.Bad).ToList(); // Support each "good" candidate `g` such that // For each "best" candidate `c` // There exists a "bad" candidate `b` (the 'bogeyman') such that `b` beats `c` in first-choice votes but loses to `g` one-on-one if (good.Any() && bad.Any()) { var bogeymen = bad .Where(b => best.All(c => firstChoices[b] > firstChoices[c])) .ToList(); if (bogeymen.Any()) { var bestCoalition = GetCoalition(best); var bogeymenCoalition = GetCoalition(bogeymen); foreach (var c in good.Where(g => bogeymen.Any(b => beatMatrix.Beats(g, b)))) { approvalCount[c] += count; compromises.Add((bestCoalition, c, bogeymenCoalition), count); } } } } var results = new ElectionResults(approvalCount.IndexRanking()); results.AddHeading("Votes"); results.AddTable( approvalCount.IndexOrderByDescending() .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c, approvalCount[c], firstChoices[c], approvalCount[c] - firstChoices[c], }), "Total", "First", "Comp."); results.AddHeading("Compromises"); results.AddTable(compromises .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c.Item.Compromise, c.Item.Preferred, c.Item.Bogeymen, c.Count }), "Comp.", "Pref.", "Bogey.", "Count"); return(results); }
public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <Bucket> > ballots) { var beatMatrix = ballots.GetBeatMatrix(); var bogeymen = beatMatrix .GetSchulzeSet() .ToList(); var approvalCount = new int[ballots.CandidateCount]; var firstChoices = new int[ballots.CandidateCount]; var compromises = new CountedList <(ulong Preferred, int Compromise, ulong Bogeymen)>(); foreach (var(ballot, count) in ballots.Comparers) { // Always approve of first choices. foreach (var c in ballot.Buckets.IndexesWhere(b => b == Bucket.Best)) { approvalCount[c] += count; firstChoices[c] += count; } // If one disapproves of any bogeymen, support one's second choices too. var bogeymanCoalition = GetCoalition(bogeymen.Where(b => ballot.Buckets[b] == Bucket.Bad)); if (bogeymanCoalition != 0ul) { var best = GetCoalition(ballot.Buckets.IndexesWhere(b => b == Bucket.Best)); foreach (var c in ballot.Buckets.IndexesWhere(b => b == Bucket.Good)) { approvalCount[c] += count; compromises.Add((best, c, bogeymanCoalition), count); } } } var results = new ElectionResults(approvalCount.IndexRanking()); results.AddHeading("Votes"); results.AddTable( approvalCount.IndexOrderByDescending() .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c, approvalCount[c], firstChoices[c], approvalCount[c] - firstChoices[c], }), "Total", "First", "Comp."); results.AddHeading("Compromises"); results.AddTable( compromises.Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c.Item.Compromise, c.Item.Preferred, c.Item.Bogeymen, c.Count }), "Comp.", "Pref.", "Bogey", "Count" ); return(results); }
public override ElectionResults GetElectionResults(CandidateComparerCollection <RankedBallot> ballots) { var beatMatrix = ballots.GetBeatMatrix(); var candidates = Enumerable.Range(0, ballots.CandidateCount); var approvalCount = new int[ballots.CandidateCount]; var firstChoices = new int[ballots.CandidateCount]; var compromises = new CountedList <(ulong Preferred, int Compromise, ulong Bogeymen)>(); foreach (var(ballot, count) in ballots.Comparers) { var ranking = ballot.Ranking; // Approve of the each candidate `c` which is a first choice. foreach (var c in ranking[0]) { firstChoices[c] += count; approvalCount[c] += count; } // Approve of the each candidate `c` such that // For each candidate `a` which one prefers to `c`, // There exists a candidate `b` (the 'bogeyman') such that one prefers `c` to `b` and `b` beats `a` one-on-one if (ranking.Count >= 3) { var preferredCandidates = ranking[0].ToList(); var potentialBogeymen = Enumerable.Range(0, ballots.CandidateCount).ToHashSet(); potentialBogeymen.ExceptWith(preferredCandidates); foreach (var tier in ranking.Skip(1).Take(ranking.Count - 2)) { potentialBogeymen.ExceptWith(tier); if (preferredCandidates.All(a => potentialBogeymen.Any(b => beatMatrix.Beats(b, a)))) { var preferredCoalition = GetCoalition(preferredCandidates); var bogeymen = GetCoalition(potentialBogeymen.Where(b => preferredCandidates.Any(a => beatMatrix.Beats(b, a)))); foreach (var c in tier) { approvalCount[c] += count; compromises.Add((preferredCoalition, c, bogeymen), count); } } preferredCandidates.AddRange(tier); } } } var results = new ElectionResults(approvalCount.IndexRanking()); results.AddHeading("Votes"); results.AddTable( approvalCount.IndexOrderByDescending() .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c, approvalCount[c], firstChoices[c], approvalCount[c] - firstChoices[c], }), "Total", "First", "Comp."); results.AddHeading("Compromises"); results.AddTable(compromises .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c.Item.Compromise, c.Item.Preferred, c.Item.Bogeymen, c.Count }), "Comp.", "Pref.", "Bogey.", "Count"); return(results); }
public override ElectionResults GetElectionResults(CandidateComparerCollection <RankedBallot> ballots) { var beatMatrix = ballots.GetBeatMatrix(); var bogeymen = beatMatrix .GetSchulzeSet() .Select(b => ( Bogeyman: b, Saviours: Enumerable.Range(0, ballots.CandidateCount) .Where(s => beatMatrix.Beats(s, b)) .ToList())) .ToList(); var approvalCount = new int[ballots.CandidateCount]; var firstChoices = new int[ballots.CandidateCount]; var compromises = new CountedList <(ulong Preferred, int Compromise, ulong Bogeymen)>(); foreach (var(ballot, count) in ballots.Comparers) { // Always approve of first choices. foreach (var c in ballot.Ranking[0]) { approvalCount[c] += count; firstChoices[c] += count; } // If one prefers all canidates who beat a bogeyman to said bogeyman, do so. // (if we don't, neither will the people who prefer the other saviours -- and we won't be able to beat the bogeyman) // Otherwise, one approve of *all* candidates one likes better than the bogeyman. // Finally, approve the bogeymen we like best, so long as we don't rank them all last. var approveUntilRank = 0; var preferredBogeymenRank = 2 - ballot.Ranking.Count; var preferredBogeymenCoalition = 0ul; var saviourCoalition = 0ul; foreach (var(bogeyman, saviours) in bogeymen) { var bogeymanRank = ballot.RanksByCandidate[bogeyman]; if (bogeymanRank > preferredBogeymenRank) { preferredBogeymenCoalition = GetCoalition(bogeyman); preferredBogeymenRank = bogeymanRank; } var saviourRanks = saviours.Select(s => ballot.RanksByCandidate[s]).ToList(); if (saviours.Any() && saviourRanks.All(sr => sr > bogeymanRank)) { saviourCoalition |= GetCoalition(saviours); approveUntilRank = saviourRanks.Append(approveUntilRank).Min(); } else { approveUntilRank = Math.Min(approveUntilRank, bogeymanRank); if (bogeymanRank == preferredBogeymenRank) { preferredBogeymenCoalition |= GetCoalition(bogeyman); } } } if (approveUntilRank >= preferredBogeymenRank) { approveUntilRank = preferredBogeymenRank; } var approvalCoalition = saviourCoalition | preferredBogeymenCoalition; // Shortcut -- we already approve of rank one. if (approveUntilRank == 0) { continue; } var preferred = GetCoalition(ballot.Ranking[0]); var bogeymenCoalition = GetCoalition(bogeymen .Select(b => b.Bogeyman) .Where(b => ballot.RanksByCandidate[b] <= approveUntilRank && !((approvalCoalition & GetCoalition(b)) > 0))); foreach (var tier in ballot.Ranking.Skip(1)) { foreach (var c in tier) { if (ballot.RanksByCandidate[c] > approveUntilRank || (approvalCoalition & GetCoalition(c)) > 0) { approvalCount[c] += count; compromises.Add((preferred, c, bogeymenCoalition), count); } } preferred |= GetCoalition(tier); } } var results = new ElectionResults(approvalCount.IndexRanking()); results.AddHeading("Votes"); results.AddTable( approvalCount.IndexOrderByDescending() .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c, approvalCount[c], firstChoices[c], approvalCount[c] - firstChoices[c], }), "Total", "First", "Comp."); results.AddHeading("Bogeymen"); results.AddTable( bogeymen.Select(b => new ElectionResults.Value[] { (ElectionResults.Candidate)b.Bogeyman, b.Saviours }), "Saviours"); results.AddHeading("Compromises"); results.AddTable( compromises.Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c.Item.Compromise, c.Item.Preferred, c.Item.Bogeymen, c.Count }), "Comp.", "Pref.", "Bogey", "Count" ); return(results); }
public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <Bucket> > ballots) { var beatMatrix = ballots.GetBeatMatrix(); var bogeymen = beatMatrix .GetSchulzeSet() .Select(b => ( Bogeyman: b, Saviours: Enumerable.Range(0, ballots.CandidateCount) .Where(s => beatMatrix.Beats(s, b)) .ToList())) .ToList(); var approvalCount = new int[ballots.CandidateCount]; var firstChoices = new int[ballots.CandidateCount]; var compromises = new CountedList <(ulong Preferred, int Compromise, ulong Bogeymen)>(); foreach (var(ballot, count) in ballots.Comparers) { // Always approve of first choices. foreach (var c in ballot.Buckets.IndexesWhere(b => b == Bucket.Best)) { approvalCount[c] += count; firstChoices[c] += count; } // If one prefers all canidates who beat a bogeyman to said bogeyman, support them all. // (if we don't, neither will the people who prefer the other saviours -- and we won't be able to beat the bogeyman) // Otherwise, support of *all* candidates one likes better than the bogeyman. var bogeymanCoalition = 0ul; var saviourCoalition = 0ul; foreach (var(bogeyman, saviours) in bogeymen) { var bogeymanRank = ballot.Buckets[bogeyman]; if (ballot.Buckets[bogeyman] != Bucket.Bad) { continue; } if (saviours.Any() && saviours.All(s => ballot.Buckets[s] == Bucket.Best)) { continue; } if (saviours.Any() && saviours.All(s => ballot.Buckets[s] != Bucket.Bad)) { bogeymanCoalition |= GetCoalition(bogeyman); saviourCoalition |= GetCoalition(saviours.Where(s => ballot.Buckets[s] == Bucket.Good)); } else { bogeymanCoalition |= GetCoalition(bogeyman); saviourCoalition = GetCoalition(ballot.Buckets.IndexesWhere(b => b == Bucket.Good)); } } if (saviourCoalition == 0ul) { continue; } var best = GetCoalition(ballot.Buckets.IndexesWhere(b => b == Bucket.Best)); foreach (var c in GetCandidates(saviourCoalition)) { approvalCount[c] += count; compromises.Add((best, c, bogeymanCoalition), count); } } var results = new ElectionResults(approvalCount.IndexRanking()); results.AddHeading("Votes"); results.AddTable( approvalCount.IndexOrderByDescending() .Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c, approvalCount[c], firstChoices[c], approvalCount[c] - firstChoices[c], }), "Total", "First", "Comp."); results.AddHeading("Bogeymen"); results.AddTable( bogeymen.Select(b => new ElectionResults.Value[] { (ElectionResults.Candidate)b.Bogeyman, b.Saviours }), "Saviours"); results.AddHeading("Compromises"); results.AddTable( compromises.Select(c => new ElectionResults.Value[] { (ElectionResults.Candidate)c.Item.Compromise, c.Item.Preferred, c.Item.Bogeymen, c.Count }), "Comp.", "Pref.", "Bogey", "Count" ); return(results); }