Esempio n. 1
0
        public static ElectionResults GetElectionResults <T>(CandidateComparerCollection <T> ballots)
            where T : ScoreBallot
        {
            var totals = new int[ballots.CandidateCount];

            foreach (var(ballot, count) in ballots.Comparers)
            {
                for (int i = 0; i < totals.Length; i++)
                {
                    totals[i] += ballot.m_scoresByCandidate[i] * count;
                }
            }

            var scores = totals
                         .Select((s, i) => (Candidate: i, Score: s))
                         .OrderByDescending(a => a.Score)
                         .ToList();

            var ranking = scores
                          .GroupBy(a => a.Score, a => a.Candidate)
                          .Select(gp => gp.ToList())
                          .ToList();

            var results = new ElectionResults(ranking);

            results.AddHeading("Scores");
            results.AddCandidateTable(scores);

            return(results);
        }
 void switchResults_Click(object sender, EventArgs e)
 {
     electionResults    = electionResults == ElectionResults.e2012 ? ElectionResults.e2016 : ElectionResults.e2012;
     switchResults.Text = electionResults == ElectionResults.e2016 ? "Show 2012" : "Show 2016";
     LoadElectionData();
     tsElectionSimilutor.IsOn = electionData.IsPlayGround;
 }
Esempio n. 3
0
 public void AddDetails(ElectionResults results)
 {
     results.AddHeading("Beat Matrix");
     results.AddTable(m_beatMatrix.SelectMany((d, i) => d.Select(kvp => new ElectionResults.Value[] {
         (ElectionResults.Candidate)i,
         kvp.Key,
         kvp.Value,
     })),
                      "Cand.",
                      "Coa.",
                      "Support"
                      );
 }
Esempio n. 4
0
        public async Task <ElectionResults> GetResults(Election election)
        {
            var request = _service.Spreadsheets.Values.BatchGet(_spreadsheetId);
            var ranges  = new List <string>();

            foreach (var race in election.Races)
            {
                var range = $"{StarSymbol}{race.Caption}!A:{ToColumnName(race.Candidates.Count+1)}";
                ranges.Add(range);
            }
            request.Ranges = new Repeatable <string>(ranges);
            var response = await request.ExecuteAsync().ConfigureAwait(false);

            var valueRanges = response.ValueRanges.ToArray();
            var results     = new ElectionResults
            {
                Title = election.Title,
                Races = new List <RaceResults>()
            };

            for (var raceIndex = 0; raceIndex < election.Races.Count; raceIndex++)
            {
                var race        = election.Races[raceIndex];
                var valueRange  = valueRanges[raceIndex];
                var raceResults = new RaceResults
                {
                    Title      = race.Caption,
                    Candidates = race.Candidates.ToArray(),
                    Votes      = new Vote[valueRange.Values.Count - 1]
                };
                var index = 0;
                foreach (var range in valueRange.Values.ToArray().Slice(1))
                {
                    var array  = range.ToArray();
                    var scores = new int[array.Length - 1];
                    for (var columnIndex = 1; columnIndex < array.Length; columnIndex++)
                    {
                        var score = int.Parse(array[columnIndex].ToString());
                        scores[columnIndex - 1] = score;
                    }
                    var vote = new Vote
                    {
                        VoterId = array[0].ToString(),
                        Scores  = scores.ToArray()
                    };
                    raceResults.Votes[index++] = vote;
                }
                results.Races.Add(raceResults);
            }
            return(results);
        }
 public Map()
 {
     InitializeComponent();
     this.mapControl.EnableScrolling = EnableMapScrollingInStateView;
     this.usStateInfo = new LoadedMapInfo()
     {
         Layer = this.stateLayer
     };
     this.usCountyInfo = new LoadedMapInfo();
     LoadUSCountyShapes();
     this.electionResults = ElectionResults.e2012;
     LoadElectionData();
     CreateColorLegend();
     switchResults.Visible = true;
 }
Esempio n. 6
0
        public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <Bucket> > ballots)
        {
            var firstChoices         = new int[ballots.CandidateCount];
            var maximumApprovalCount = new int[ballots.CandidateCount];

            foreach (var(ballot, count) in ballots.Comparers)
            {
                foreach (var c in ballot.Buckets.IndexesWhere(a => a == Bucket.Best))
                {
                    firstChoices[c]         += count;
                    maximumApprovalCount[c] += count;
                }

                foreach (var c in ballot.Buckets.IndexesWhere(a => a == Bucket.Good))
                {
                    maximumApprovalCount[c] += count;
                }
            }

            var firstChoiceWinners     = firstChoices.MaxIndexes().ToList();
            var maximumApprovalWinners = maximumApprovalCount.MaxIndexes().ToList();

            var approvalCount = firstChoices.SelectToArray(c => c);

            var winners = ballots.Compare(firstChoiceWinners[0], maximumApprovalWinners[0]) > 0 ? firstChoiceWinners : maximumApprovalWinners;

            var results = new ElectionResults(new List <List <int> > {
                winners
            });

            results.AddHeading("Votes");
            results.AddTable(
                approvalCount.IndexOrderByDescending()
                .Select(c => new ElectionResults.Value[] {
                (ElectionResults.Candidate)c,
                approvalCount[c],
                firstChoices[c],
                approvalCount[c] - firstChoices[c],
            }),
                "Votes",
                "First",
                "Comp.");

            return(results);
        }
Esempio n. 7
0
        /// <summary>
        /// Run an election with a set of randomly generated poeple. Once they are run,
        /// remove each non-winning candidate in turn and count the number of times
        /// that the winner changes.
        /// </summary>
        /// <returns></returns>
        public async Task<ElectionResults> RunElection()
        {
            // Generate the people.
            var people = GeneratePeople().ToArray();

            // Next, run the election
            var result = await RunSingleElectionInternal(people);

            // Now, loop over each candidate and remove them... unless they are the winner!
            var winner = result[0].candidate;
            int flips = 0;
            for (int i_cand = 0; i_cand < NumberOfCandidates; i_cand++)
            {
                if (i_cand != winner)
                {
                    var peopleWithOut = people.Select(p => p.RemoveCandidates(i_cand)).ToArray();
                    var resultWithOut = await RunSingleElectionInternal(peopleWithOut);
                    if (resultWithOut[0].candidate != winner)
                        flips++;
                }
            }

            // Build the result

            var r = new ElectionResults();
            r.flips = flips;
            r.candidateOrdering = (from rs in result
                                   orderby rs.ranking descending
                                   select rs.candidate).ToArray();

            return r;
        }
Esempio n. 8
0
        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 List <MsgBase> HandleResoultRequest(MsgBase msg)
        {
            List <MsgBase>         results        = new List <MsgBase>();
            ElectionResultsRequest resultsRequest = msg as ElectionResultsRequest;

            if (resultsRequest.State == "")
            {
                ElectionResults result = new ElectionResults()
                {
                    RequestUID = resultsRequest.Base_MsgUID,
                    Election   = resultsRequest.ElectionID,
                    State      = ""
                };

                Dictionary <string, VotingResult> summaryResults = new Dictionary <string, VotingResult>();
                foreach (var state in m_Votes)
                {
                    foreach (VotesDetails votes in state.Value.Values)
                    {
                        if (summaryResults.ContainsKey(votes.CandidateId))
                        {
                            summaryResults[votes.CandidateId].VoteCount         += votes.VoteCount;
                            summaryResults[votes.CandidateId].ElectoralVotesWon += votes.ElectoralVotesWon;
                        }
                        else
                        {
                            CandidateDetails candidate = m_Candidates[votes.CandidateId];
                            VotingResult     vResult   = new VotingResult()
                            {
                                CandidateId        = votes.CandidateId,
                                CandidateFirstName = candidate.FirstName,
                                CandidateLastName  = candidate.LastName,
                                Party             = candidate.Party,
                                ElectoralVotes    = 348,
                                ElectoralVotesWon = votes.ElectoralVotesWon,
                                VoteCount         = votes.VoteCount,
                                FinalResults      = false
                            };
                            summaryResults.Add(vResult.CandidateId, vResult);
                        }
                    }
                }
                int allVotes = summaryResults.Values.Sum(v => v.VoteCount);

                foreach (VotingResult vote in summaryResults.Values)
                {
                    vote.VotePercent = (vote.VoteCount / allVotes) * 100;
                    result.Voting.Add(vote);
                }

                results.Add(result);
            }
            else if (resultsRequest.State == "All")
            {
                foreach (var state in m_Votes)
                {
                    ElectionResults result = new ElectionResults()
                    {
                        RequestUID = resultsRequest.Base_MsgUID,
                        Election   = resultsRequest.ElectionID,
                        State      = state.Key
                    };

                    foreach (VotesDetails votes in state.Value.Values)
                    {
                        CandidateDetails candidate = m_Candidates[votes.CandidateId];
                        result.Voting.Add(new VotingResult()
                        {
                            CandidateId        = votes.CandidateId,
                            CandidateFirstName = candidate.FirstName,
                            CandidateLastName  = candidate.LastName,
                            Party             = candidate.Party,
                            ElectoralVotes    = votes.ElectoralVotes,
                            ElectoralVotesWon = votes.ElectoralVotesWon,
                            VoteCount         = votes.VoteCount,
                            VotePercent       = votes.VotePercent,
                            FinalResults      = votes.FinalResults
                        });
                    }

                    results.Add(result);
                }
            }
            else
            {
                if (m_Votes.ContainsKey(resultsRequest.State))
                {
                    ElectionResults result = new ElectionResults()
                    {
                        RequestUID = resultsRequest.Base_MsgUID,
                        Election   = resultsRequest.ElectionID,
                        State      = resultsRequest.State
                    };

                    foreach (VotesDetails votes in m_Votes[resultsRequest.State].Values)
                    {
                        CandidateDetails candidate = m_Candidates[votes.CandidateId];
                        result.Voting.Add(new VotingResult()
                        {
                            CandidateId        = votes.CandidateId,
                            CandidateFirstName = candidate.FirstName,
                            CandidateLastName  = candidate.LastName,
                            Party             = candidate.Party,
                            ElectoralVotes    = votes.ElectoralVotes,
                            ElectoralVotesWon = votes.ElectoralVotesWon,
                            VoteCount         = votes.VoteCount,
                            VotePercent       = votes.VotePercent,
                            FinalResults      = votes.FinalResults
                        });
                    }

                    results.Add(result);
                }
                else
                {
                    results.Add(new ElectionResults());
                }
            }

            return(results);
        }
        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);
        }
Esempio n. 11
0
        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);
        }
Esempio n. 12
0
        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 previousWinners = winners.ToHashSet();

            while (true)
            {
                var newApprovalCount = new int[ballots.CandidateCount];
                var compromises      = new CountedList <(ulong, int)>();

                foreach (var(ballot, count) in ballots.Comparers)
                {
                    var lowestWinnerRank = previousWinners
                                           .Select(w => ballot.RanksByCandidate[w])
                                           .Min();

                    // If all winners are ranked one or two, no compromise is necessary.
                    if (lowestWinnerRank == 0 || lowestWinnerRank == -1)
                    {
                        continue;
                    }

                    var approvedCoalition = approvalByBallot[ballot];

                    // Approve of the each candidate `c` that one likes better than any of the previous winners
                    // which one has not already approved of
                    var newApprovals = ballot.RanksByCandidate
                                       .IndexesWhere((rank, candidate) => rank > lowestWinnerRank && (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 newWinners = winners.Where(w => !previousWinners.Contains(w));

                if (!newWinners.Any())
                {
                    break;
                }

                foreach (var w in newWinners)
                {
                    previousWinners.Add(w);
                }
            }

            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());

                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 compromiseBallots = new HashSet <BucketBallot <Bucket> >();
            var history           = new List <(int[] NewApprovalCount, List <int> Winners, CountedList <(ulong Preferred, int Candidate)> Compromises)>();

            // 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 winningScore = approvalCount.Max();
            var winners      = approvalCount.IndexesWhere(a => a == winningScore).ToList();

            history.Add((firstChoices, winners, new CountedList <(ulong, int)>()));

            var previousWinners = winners.ToHashSet();

            while (true)
            {
                var newApprovalCount = new int[ballots.CandidateCount];
                var compromises      = new CountedList <(ulong, int)>();

                foreach (var(ballot, count) in ballots.Comparers)
                {
                    // Can only compromise once
                    if (compromiseBallots.Contains(ballot))
                    {
                        continue;
                    }

                    // Compromise is only necessary if a winner is bad.
                    if (previousWinners.All(w => ballot.Buckets[w] != Bucket.Bad))
                    {
                        continue;
                    }

                    compromiseBallots.Add(ballot);

                    var bestCoalition = GetCoalition(ballot.Buckets.IndexesWhere(a => a == Bucket.Best));

                    foreach (var candidate in ballot.Buckets.IndexesWhere(a => a == Bucket.Good))
                    {
                        approvalCount[candidate]    += count;
                        newApprovalCount[candidate] += count;
                        compromises.Add((bestCoalition, candidate), count);
                    }
                }

                winningScore = approvalCount.Max();
                winners      = approvalCount.IndexesWhere(a => a == winningScore).ToList();

                if (newApprovalCount.Any(c => c > 0))
                {
                    history.Add((newApprovalCount, winners, compromises));
                }

                var newWinners = winners.Where(w => !previousWinners.Contains(w));

                if (!newWinners.Any())
                {
                    break;
                }

                foreach (var w in newWinners)
                {
                    previousWinners.Add(w);
                }
            }

            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());

                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 <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);
        }
Esempio n. 15
0
        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);
        }
Esempio n. 16
0
        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 previousWinners = approvalCount.IndexesWhere(a => a == winningScore).ToList();

            history.Add((firstChoices, previousWinners, new CountedList <(ulong, int)>()));

            while (true)
            {
                // Test each candidate's "saviour" `s` potential versus each of the previous winners' bogeyman `b`.
                // Each voter who prefers the saviour `s` over any of the bogeymen `b` will approve of the saviour (and each other candidate one prefers to the saviour).
                // If no saviour changes the winner, each ballot approves of every candidate they prefer to the current winner and the election ends.
                // If there exists a saviour who changes the winner, choose the saviour(s) which require the **fewest** voters to change their vote.
                // Lock in the approvals for that saviour, compute a new winner, and repeat.
                var previousWinnerCoalition = GetCoalition(previousWinners);
                var potentialSaviours       = Enumerable.Range(0, ballots.CandidateCount).Select(s =>
                {
                    var newApprovalCount  = new int[ballots.CandidateCount];
                    var votersRequired    = 0;
                    var approvalsRequired = new List <RankedBallot>();

                    foreach (var(ballot, count) in ballots.Comparers)
                    {
                        var saviourRank = ballot.RanksByCandidate[s];

                        var lowestBogeymanRank = previousWinners
                                                 .Select(b => ballot.RanksByCandidate[b])
                                                 .Min();

                        if (lowestBogeymanRank < saviourRank && (approvalByBallot[ballot] & GetCoalition(s)) == 0ul)
                        {
                            newApprovalCount[s] += count;
                            approvalsRequired.Add(ballot);
                            votersRequired += count;
                        }
                    }

                    var potentialApprovalCount   = ballots.CandidateCount.LengthArray(i => approvalCount[i] + newApprovalCount[i]);
                    var potentialWinningScore    = potentialApprovalCount.Max();
                    var potentialWinnerCoalition = GetCoalition(potentialApprovalCount.IndexesWhere(a => a == potentialWinningScore).ToList());

                    return(Saviour: s, Ballots: approvalsRequired, votersRequired, potentialWinnerCoalition);
                })
                                              .Where(a => a.potentialWinnerCoalition != previousWinnerCoalition)
                                              .ToList();

                var newApprovalCount = new int[ballots.CandidateCount];
                var compromises      = new CountedList <(ulong, int)>();

                if (!potentialSaviours.Any())
                {
                    // No-one can beat the current winner, so just support as much as possible.
                    foreach (var(ballot, count) in ballots.Comparers)
                    {
                        var lowestWinnerRank = previousWinners
                                               .Select(b => ballot.RanksByCandidate[b])
                                               .Min();

                        // No compromise necessary if we rank the winner first or second.
                        if (lowestWinnerRank >= -1)
                        {
                            continue;
                        }

                        var approvedCoalition = approvalByBallot[ballot];

                        // Approve of each candidate `c` that one likes better than the worst winner
                        // which one has not already approved of
                        var newApprovals = ballot.RanksByCandidate
                                           .IndexesWhere((rank, candidate) => (rank > lowestWinnerRank) && (approvedCoalition & GetCoalition(candidate)) == 0ul)
                                           .ToList();

                        foreach (var candidate in newApprovals)
                        {
                            approvalCount[candidate]    += count;
                            newApprovalCount[candidate] += count;
                            compromises.Add((approvedCoalition, candidate), count);
                        }
                    }
                }
                else
                {
                    // Choosing the maximum `votersRequired` makes this method less satisfactory and more suceptible to tactical voting.
                    // Approving of all candidates we like better than the saviour *at this step* makes us more suceptible to tactical voting without affecting satisfaction.
                    var minimalVotersRequired = potentialSaviours.Select(a => a.votersRequired).Min();

                    foreach (var(ballot, newApprovals) in potentialSaviours
                             .Where(a => a.votersRequired == minimalVotersRequired)
                             .SelectMany(a => a.Ballots.Select(b => (Ballot: b, Saviour: a.Saviour)))
                             .GroupBy(a => a.Ballot, a => a.Saviour)
                             .Select(gp => (gp.Key, gp.ToList())))
                    {
                        ballots.Comparers.TryGetCount(ballot, out var count);

                        var preferredCoalition = approvalByBallot[ballot];

                        foreach (var candidate in newApprovals)
                        {
                            approvalCount[candidate]    += count;
                            newApprovalCount[candidate] += count;
                            compromises.Add((preferredCoalition, candidate), count);
                        }

                        approvalByBallot[ballot] = preferredCoalition | GetCoalition(newApprovals);
                    }
                }

                winningScore    = approvalCount.Max();
                previousWinners = approvalCount.IndexesWhere(a => a == winningScore).ToList();

                if (newApprovalCount.Any(c => c > 0))
                {
                    history.Add((newApprovalCount, previousWinners, compromises));
                }

                if (!potentialSaviours.Any())
                {
                    break;
                }
            }

            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());

                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);
        }
Esempio n. 17
0
        static void Main(string[] args)
        {
            bool findDifferences = args.Contains("FindDifferences");
            bool findStrategies  = args.Contains("FindStrategies");

            var voterCountsList        = InterestingVoterCounts().ToList();
            var votersByCandidateCount = new [] { 4, 5 }
            .ToDictionary(c => c, c => GetAllVoters(c).ToList().OrderedAtRandom().ToList());

            var elections = from candidateCount in votersByCandidateCount.Keys
                            from voterCounts in voterCountsList
                            from permutation in GetUniquePermutations(voterCounts.Length, votersByCandidateCount[candidateCount].Count)
                            select new CandidateComparerCollection <Voter>(
                candidateCount,
                permutation
                .Select((rankingIndex, countIndex) => (votersByCandidateCount[candidateCount][rankingIndex], voterCounts[countIndex]))
                .ToCountedList()
                );

            var votingMethods = Assembly.GetAssembly(typeof(VotingMethodBase))
                                .GetTypes()
                                .Where(t => t.IsAssignableTo(typeof(VotingMethodBase)) && !t.IsAbstract && !t.CustomAttributes.Any(a => a.AttributeType == typeof(ObsoleteAttribute)))
                                .Select(t => Activator.CreateInstance(t) as VotingMethodBase)
                                .ToList();

            if (args.Length > (findDifferences ? 1 : 0) + (findStrategies ? 1 : 0))
            {
                votingMethods = votingMethods.Where(vm => args.Contains(vm.GetType().Name)).ToList();

                if (!votingMethods.Any())
                {
                    throw new InvalidOperationException("Args must contain valid voting systems.");
                }
            }

            var effectiveStrategiesFavorite = votingMethods.Count.LengthArray(_ => new CountedList <string>());
            var effectiveStrategiesUtility  = votingMethods.Count.LengthArray(_ => new CountedList <string>());

            int index         = 0;
            var examplesFound = 0;
            var random        = new Random();

            Console.WriteLine();
            foreach (var ballots in elections)
            {
                index++;
                Console.Write("\r" + index);

                try
                {
                    var possibleOutcomes = votingMethods.Select(m => m.GetPossibleResults(ballots)).ToList();

                    if (findDifferences)
                    {
                        FindDifferences(ballots, possibleOutcomes.Select(m => m.Honest).ToList());
                    }

                    if (findStrategies)
                    {
                        for (int i = 0; i < votingMethods.Count; i++)
                        {
                            var(favorite, utility) = possibleOutcomes[i].GetPlausibleStrategies();

                            if (!favorite.Any())
                            {
                                effectiveStrategiesFavorite[i].Add("Honesty");
                            }

                            foreach (var strategy in favorite)
                            {
                                effectiveStrategiesFavorite[i].Add(strategy);
                            }

                            if (!utility.Any())
                            {
                                effectiveStrategiesUtility[i].Add("Honesty");
                            }

                            foreach (var strategy in utility)
                            {
                                effectiveStrategiesUtility[i].Add(strategy);
                            }

                            if (votingMethods[i].GetType().Name == "BucketConsensusSimple" && utility.Contains("Abstain"))
                            {
                                Console.WriteLine(ballots);
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine();
                    Console.WriteLine(ballots.ToString());
                    throw;
                }

                if (index % 1000 == 0)
                {
                    GC.Collect();

                    var sb = new StringBuilder();
                    sb.AppendLine("Favorite");
                    AppendTable(effectiveStrategiesFavorite);

                    sb.AppendLine();
                    sb.AppendLine("Utility");
                    AppendTable(effectiveStrategiesUtility);

                    Console.Clear();
                    Console.WriteLine(sb.ToString());

                    void AppendTable(CountedList <string>[] effectiveStrategies)
                    {
                        var strategies = effectiveStrategies.SelectMany(m => m.Select(a => a.Item)).Distinct().ToList();

                        ElectionResults.AppendTable(sb,
                                                    votingMethods.SelectToArray((m, i) =>
                                                                                strategies
                                                                                .Select(s => (ElectionResults.Value)(effectiveStrategies[i].TryGetCount(s, out var count) ? (count * 100d / index).ToString("N2") : ""))
                                                                                .Prepend(m.GetType().Name)
                                                                                .ToArray()),
                                                    strategies.SelectToArray(s => (ElectionResults.Value)s.ToString()));
                    }
                }
            }

            void FindDifferences(CandidateComparerCollection <Voter> voters, List <ElectionResults> results)
            {
                if (results.Select(r => RankedConsensusBase.GetCoalition(r.Winners)).Distinct().Count() > 1)
                {
                    if (random.Next((examplesFound + 10) * (examplesFound + 10)) == 0)
                    {
                        Console.WriteLine();
                        Console.WriteLine("Voters: " + voters);
                        Console.WriteLine();

                        for (int i = 0; i < votingMethods.Count; i++)
                        {
                            Console.Write(votingMethods[i].GetType().Name + " ");
                            Console.WriteLine(results[i].Details);
                            Console.WriteLine();
                        }

                        examplesFound++;
                    }
                }
            }
        }
Esempio n. 18
0
        public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <Bucket> > ballots)
        {
            var approvalCount     = new int[ballots.CandidateCount];
            var firstChoices      = new int[ballots.CandidateCount];
            var compromiseBallots = new HashSet <BucketBallot <Bucket> >();
            var history           = new List <(int[] NewApprovalCount, List <int> Winners, CountedList <(ulong Preferred, int Candidate)> Compromises)>();

            // 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;
                }
            }

            for (int first = 0; first < ballots.CandidateCount; first++)
            {
                for (int second = 0; second < ballots.CandidateCount; second++)
                {
                    if (first != second)
                    {
                        int    firstApproved = 0;
                        int    firstApprovedAndSecondApproved = 0;
                        double secondPreferredCompromisers    = 0;

                        foreach (var(ballot, count) in ballots.Comparers)
                        {
                            if (ballot.Buckets[first] != Bucket.Bad)
                            {
                                firstApproved += count;
                            }

                            if (ballot.Buckets[first] != Bucket.Bad && ballot.Buckets[second] != Bucket.Bad)
                            {
                                firstApprovedAndSecondApproved += count;
                            }

                            // We'll evaluate this ballot's contribution to `first` once for each of the candidates `second` it prefers.
                            if (ballot.Buckets[first] == Bucket.Good && ballot.Buckets[second] == Bucket.Best)
                            {
                                secondPreferredCompromisers += count / ballot.Buckets.Count(b => b == Bucket.Best);
                            }
                        }

                        approvalCount[first] += (int)(firstApproved > 0 ? secondPreferredCompromisers * firstApprovedAndSecondApproved / firstApproved : secondPreferredCompromisers);
                    }
                }
            }

            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.");

            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);
        }
Esempio n. 20
0
        public override ElectionResults GetElectionResults(CandidateComparerCollection <RankedBallot> ballots)
        {
            var approvalCount = new int[ballots.CandidateCount];
            var firstChoices  = new int[ballots.CandidateCount];
            var compromises   = new CountedList <(int Compromise, ulong Preferred, ulong Bogeymen)>();

            // First, compute "coalition" beat matrix
            var beatMatrix = new CoalitionBeatMatrix(ballots);

            // Then, use it to determine which candidates each ballot approves of.
            foreach (var(ballot, count) in ballots.Comparers)
            {
                var(approvedCandidates, bogeymen) = GetApprovedCandidates(beatMatrix, ballot);
                foreach (var(candidate, preferred) in approvedCandidates)
                {
                    approvalCount[candidate] += count;

                    if (preferred.HasValue)
                    {
                        compromises.Add((candidate, preferred.Value, bogeymen), count);
                    }
                    else
                    {
                        firstChoices[candidate] += 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"
                );

            beatMatrix.AddDetails(results);

            return(results);
        }
        public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <Bucket> > ballots)
        {
            var approvalCount = new int[ballots.CandidateCount];
            var firstChoices  = new int[ballots.CandidateCount];

            var history = new List <(int[] NewApprovalCount, List <int> Winners, CountedList <(ulong Preferred, int Candidate)> Compromises)>();

            // 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 winningScore    = approvalCount.Max();
            var previousWinners = approvalCount.IndexesWhere(a => a == winningScore).ToList();

            history.Add((firstChoices, previousWinners, new CountedList <(ulong, int)>()));

            var potentialApprovalByBallot = ballots.Comparers.ToDictionary(
                b => b.Item,
                b => GetCoalition(b.Item.Buckets.IndexesWhere(b => b == Bucket.Good))
                );

            while (true)
            {
                // Test each candidate's "saviour" `s` potential versus each of the previous winners' bogeyman `b`.
                // Each voter who prefers the saviour `s` over any of the bogeymen `b` will approve of the saviour.
                // Each voter who prefers any bogeyman `b` over the saviour `s` will approve of the bogeyman.
                // If no saviour changes the winner, each ballot approves of every candidate they prefer to the current winner and the election ends.
                // If there exists a saviour who changes the winner, choose the saviour(s) which require the **fewest** voters to change their vote.
                // Lock in the approvals for that saviour, compute a new winner, and repeat.
                var previousWinnerCoalition = GetCoalition(previousWinners);
                var potentialSaviours       = Enumerable.Range(0, ballots.CandidateCount).Select(s =>
                {
                    var newApprovalCount  = new int[ballots.CandidateCount];
                    var votersRequired    = 0;
                    var approvalsRequired = new List <(BucketBallot <Bucket> Ballot, int Candidate)>();

                    foreach (var(ballot, count) in ballots.Comparers)
                    {
                        if ((potentialApprovalByBallot[ballot] & GetCoalition(s)) > 0ul &&
                            previousWinners.Any(b => ballot.Buckets[b] == Bucket.Bad))
                        {
                            newApprovalCount[s] += count;
                            approvalsRequired.Add((ballot, s));
                            votersRequired += count;
                        }

                        if (ballot.Buckets[s] == Bucket.Bad)
                        {
                            foreach (var b in previousWinners.Where(b => (potentialApprovalByBallot[ballot] & GetCoalition(b)) > 0ul))
                            {
                                newApprovalCount[b] += count;
                                approvalsRequired.Add((ballot, b));
                                votersRequired += count;
                            }
                        }
                    }

                    var potentialApprovalCount   = ballots.CandidateCount.LengthArray(i => approvalCount[i] + newApprovalCount[i]);
                    var potentialWinningScore    = potentialApprovalCount.Max();
                    var potentialWinnerCoalition = GetCoalition(potentialApprovalCount.IndexesWhere(a => a == potentialWinningScore).ToList());

                    return(Saviour: s, Ballots: approvalsRequired, votersRequired, potentialWinnerCoalition);
                })
                                              .Where(a => a.potentialWinnerCoalition != previousWinnerCoalition)
                                              .ToList();

                var newApprovalCount = new int[ballots.CandidateCount];
                var compromises      = new CountedList <(ulong, int)>();

                if (!potentialSaviours.Any())
                {
                    // No-one can beat the current winner, so just support as much as possible.
                    foreach (var(ballot, count) in ballots.Comparers)
                    {
                        // No compromise necessary if we rank the winner first or second.
                        if (!previousWinners.Any(b => ballot.Buckets[b] == Bucket.Bad))
                        {
                            continue;
                        }

                        var bestCoalition = GetCoalition(ballot.Buckets.IndexesWhere(b => b == Bucket.Best));

                        foreach (var candidate in GetCandidates(potentialApprovalByBallot[ballot]))
                        {
                            approvalCount[candidate]    += count;
                            newApprovalCount[candidate] += count;
                            compromises.Add((bestCoalition, candidate), count);
                        }
                    }

                    // Make sure this doesn't output a scoring that doesn't preserve the winner :)
                    foreach (var s in approvalCount.IndexesWhere((a, s) => a >= winningScore && !previousWinners.Contains(s)))
                    {
                        foreach (var(ballot, count) in ballots.Comparers)
                        {
                            if (ballot.Buckets[s] == Bucket.Bad)
                            {
                                var bestCoalition = GetCoalition(ballot.Buckets.IndexesWhere(b => b == Bucket.Best));
                                foreach (var b in previousWinners.Where(b => (potentialApprovalByBallot[ballot] & GetCoalition(b)) > 0ul))
                                {
                                    approvalCount[b]    += count;
                                    newApprovalCount[b] += count;
                                    compromises.Add((bestCoalition, b), count);
                                }
                            }
                        }
                    }
                }
                else
                {
                    // Choosing the maximum `votersRequired` makes this method less satisfactory and more suceptible to tactical voting.
                    // Approving of all candidates we like better than the saviour *at this step* makes us more suceptible to tactical voting without affecting satisfaction.
                    var minimalVotersRequired = potentialSaviours.Select(a => a.votersRequired).Min();

                    foreach (var(ballot, newApprovals) in potentialSaviours
                             .Where(a => a.votersRequired == minimalVotersRequired)
                             .SelectMany(a => a.Ballots)
                             .GroupBy(a => a.Ballot, a => a.Candidate)
                             .Select(gp => (gp.Key, gp.ToList())))
                    {
                        ballots.Comparers.TryGetCount(ballot, out var count);

                        var bestCoalition = GetCoalition(ballot.Buckets.IndexesWhere(b => b == Bucket.Best));

                        foreach (var candidate in newApprovals)
                        {
                            approvalCount[candidate]    += count;
                            newApprovalCount[candidate] += count;
                            compromises.Add((bestCoalition, candidate), count);
                        }

                        potentialApprovalByBallot[ballot] ^= GetCoalition(newApprovals);
                    }
                }

                winningScore    = approvalCount.Max();
                previousWinners = approvalCount.IndexesWhere(a => a == winningScore).ToList();

                if (newApprovalCount.Any(c => c > 0))
                {
                    history.Add((newApprovalCount, previousWinners, compromises));
                }

                if (!potentialSaviours.Any())
                {
                    break;
                }
            }

            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());

                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 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);
        }