Beispiel #1
0
            public CoalitionBeatMatrix(CandidateComparerCollection <RankedBallot> ballots)
            {
                m_ballots = ballots;

                m_beatMatrix = ballots.CandidateCount.LengthArray(_ => new Dictionary <ulong, int>());
                m_cycles     = ballots.CandidateCount.LengthArray(_ => new List <HashSet <ulong> >());
            }
Beispiel #2
0
        static void Main(string[] args)
        {
            var methodName = args[0];

            if (string.IsNullOrWhiteSpace(methodName))
            {
                Console.WriteLine("Must provide a voding method in the first argument.");
                return;
            }

            var methodType = Assembly.GetAssembly(typeof(VotingMethodBase))
                             .GetType("Consensus.Methods." + methodName);

            if (methodType == null)
            {
                Console.WriteLine("Must provide a valid voding method in the first argument.");
                return;
            }

            var method = Activator.CreateInstance(methodType) as VotingMethodBase;

            if (method == null)
            {
                Console.WriteLine("Must provide a valid voding method in the first argument.");
                return;
            }

            var ballots = args[1];

            if (string.IsNullOrWhiteSpace(ballots))
            {
                Console.WriteLine("Must provide a set of ballots as the second argument.");
                return;
            }

            if (ballots == "voters")
            {
                var voters = args[2];

                if (string.IsNullOrWhiteSpace(voters))
                {
                    Console.WriteLine("Must provide a set of voters as the third argument when the second is 'honest'.");
                    return;
                }

                var results = method.GetPossibleResults(CandidateComparerCollection <Voter> .Parse(voters));

                Console.WriteLine(results.GetHonestBallot());
                Console.WriteLine(results.Honest.Details);
                var(favorite, utility) = results.GetPlausibleStrategies();
                Console.WriteLine("Favorite: " + string.Join(", ", favorite));
                Console.WriteLine("Utility: " + string.Join(", ", utility));
            }
            else
            {
                var results = method.GetElectionResults(ballots);

                Console.Write(results.Details);
            }
        }
Beispiel #3
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);
        }
Beispiel #4
0
        public void SingleVoter(string election, int[] expected)
        {
            var actualVoter = CandidateComparerCollection <Voter> .Parse(election).Comparers.Single();

            var expectedVoter = AsVoter(expected);

            AssertEx.Assert(() => actualVoter == expectedVoter);
        }
Beispiel #5
0
        public void MultiVoter(string voters, params int[][] expected)
        {
            var actualVoters = CandidateComparerCollection <Voter> .Parse(voters);

            var expectedVoters = new CandidateComparerCollection <Voter>(expected[0].Length, expected.Select(AsVoter).ToCountedList());

            AssertEx.Assert(() => actualVoters == expectedVoters);
        }
Beispiel #6
0
        public override ElectionResults GetElectionResults(CandidateComparerCollection <ScoreBallot> ballots)
        {
            // Choose the top two scorers
            // NOTE: No tiebreaking
            var sortedCandidates = ScoreBallot.GetElectionResults(ballots).Ranking;

            int Pop()
            {
                var value = sortedCandidates[0][0];

                sortedCandidates[0].RemoveAt(0);
                if (sortedCandidates[0].Count == 0)
                {
                    sortedCandidates.RemoveAt(0);
                }

                return(value);
            }

            var first  = Pop();
            var second = Pop();

            // Then compare those two head-to-head
            var compare = ballots.Compare(first, second);

            if (compare == 0)
            {
                sortedCandidates.Insert(0, new List <int> {
                    first, second
                });
            }
            else if (compare < 0)
            {
                sortedCandidates.Insert(0, new List <int> {
                    first
                });
                sortedCandidates.Insert(0, new List <int> {
                    second
                });
            }
            else
            {
                sortedCandidates.Insert(0, new List <int> {
                    second
                });
                sortedCandidates.Insert(0, new List <int> {
                    first
                });
            }

            return(new ElectionResults(sortedCandidates));
        }
Beispiel #7
0
        public static IReadOnlyDictionary <T, int[]> GetBucketCounts(CandidateComparerCollection <BucketBallot <T> > ballots)
        {
            int candidateCount = ballots.CandidateCount;
            var result         = System.Enum.GetValues <T>()
                                 .ToDictionary(x => x, _ => new int[candidateCount]);

            foreach (var(ballot, count) in ballots.Comparers)
            {
                for (var c = 0; c < candidateCount; c++)
                {
                    var bucket = ballot.m_bucketsByCandidate[c];
                    result[bucket][c] += count;
                }
            }

            return(result);
        }
Beispiel #8
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);
        }
Beispiel #9
0
        public override ElectionResults GetElectionResults(CandidateComparerCollection <BucketBallot <V321.Result> > ballots)
        {
            // Find 3 Semifinalists: the candidates with the most “good” ratings.
            // Find 2 Finalists: the semifinalists with the fewest "bad" ratings".
            // Find 1 winner: the finalist who is rated above the other on more ballots.
            var bucketCounts = BucketBallot <V321.Result> .GetBucketCounts(ballots);

            var finalists = bucketCounts[Result.Good]
                            .IndexOrderByDescending()
                            .Take(3)
                            .OrderBy(c => bucketCounts[Result.Bad][c])
                            .ToList();

            var first   = finalists[0];
            var second  = finalists[1];
            var compare = ballots.Compare(first, second);

            return(new ElectionResults((compare == 0 ? new List <List <int> > {
                new List <int> {
                    first, second
                }
            }
                : compare < 0 ? new List <List <int> > {
                new List <int> {
                    second
                }, new List <int> {
                    first
                }
            }
                : new List <List <int> > {
                new List <int> {
                    first
                }, new List <int> {
                    second
                }
            })
                                       .Concat(bucketCounts[Result.Good]
                                               .IndexOrderByDescending()
                                               .Skip(3)
                                               .Select(c => new List <int> {
                c
            }))
                                       .ToList()));
        }
        public PossibleResultCollection(CandidateComparerCollection <Voter> voters, VotingMethodBase <TBallot> method)
        {
            m_voters     = voters;
            m_method     = method;
            m_voterCount = voters.Comparers.Count();

            Honest = method.GetElectionResults(voters.Bind(method.GetHonestBallot));

            var possibleStrategies = voters.Comparers
                                     .SelectToArray(a => method.GetPotentialStrategicBallots(Honest.Ranking, a.Item)
                                                    .Prepend((null, 0, method.GetHonestBallot(a.Item)))
                                                    .ToList());

            m_possibleBallots = possibleStrategies
                                .SelectToArray(v => v.SelectToArray(s => s.Ballot));

            m_strategyNames = possibleStrategies
                              .SelectToArray(v => v.SelectToArray(s => s.Strategy));

            m_strategyTiebreakers = possibleStrategies
                                    .SelectToArray(v => v.SelectToArray(s => s.Preference));
        }
Beispiel #11
0
        public override Dictionary <Strategy, double> CalculateSatisfaction(Random random, CandidateComparerCollection <Voter> voters)
        {
            var randomVoter  = voters.Poll(random, 1);
            var winner       = randomVoter.Comparers.Single().FirstPreference;
            var satisfaction = GetSatisfactionWith(voters)(new [] { winner });

            // *literally* strategy-proof!
            return(Enum.GetValues <Strategy>().ToDictionary(a => a, _ => satisfaction));
        }
Beispiel #12
0
        public void StringRoundTrip(string expected)
        {
            var actual = CandidateComparerCollection <Voter> .Parse(expected).ToString();

            AssertEx.Assert(() => actual == expected);
        }
        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);
        }
Beispiel #14
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);
        }
        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);
        }
Beispiel #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 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);
        }
Beispiel #17
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);
        }
        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 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);
        }
Beispiel #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);
        }
Beispiel #21
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);
        }
Beispiel #22
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);
        }
        public override ElectionResults GetElectionResults(CandidateComparerCollection <RankedBallot> ballots)
        {
            HashSet <int>      eliminatedCandidates = new HashSet <int>();
            List <List <int> > eliminationOrder     = new List <List <int> >();

            while (true)
            {
                var votesByCandidate = GetCount();

                var totalVotes   = votesByCandidate.Sum();
                var maximumVotes = votesByCandidate.Max();

                if (maximumVotes * 2 >= totalVotes)
                {
                    eliminationOrder.Reverse();

                    return(new ElectionResults(votesByCandidate
                                               .Select((v, i) => (Votes: v, Candidate: i))
                                               .Where(a => !eliminatedCandidates.Contains(a.Candidate))
                                               .GroupBy(a => a.Votes, a => a.Candidate)
                                               .OrderByDescending(gp => gp.Key)
                                               .Select(gp => gp.ToList())
                                               .Concat(eliminationOrder)
                                               .ToList()));
                }

                // TODO: Some sort of tiebreaker.
                var minimumVotes = votesByCandidate.Where((_, c) => !eliminatedCandidates.Contains(c)).Min();
                var last         = votesByCandidate.IndexesWhere(v => v == minimumVotes).First(c => !eliminatedCandidates.Contains(c));

                eliminatedCandidates.Add(last);
                eliminationOrder.Add(new List <int> {
                    last
                });
            }

            double[] GetCount()
            {
                var votesByCandidate = new double[ballots.CandidateCount];

                foreach (var(ballot, count) in ballots.Comparers)
                {
                    // Cast one's ballot for the highest-ranked candidate which is neither eliminated nor ranked last.
                    // Ties of `n` candidates are counted as if there were `1/n`th of the vote listed that canidate first
                    var candidates = ballot.Ranking
                                     .Select(tier => tier.Where(c => !eliminatedCandidates.Contains(c)).ToList())
                                     .Where(tier => tier.Any())
                                     .FirstOrDefault();

                    if (candidates != null)
                    {
                        foreach (var c in candidates)
                        {
                            votesByCandidate[c] += count / (double)candidates.Count;
                        }
                    }
                }

                return(votesByCandidate);
            }
        }
Beispiel #24
0
 public static CandidateComparerCollection <Voter> Voters(string source) => CandidateComparerCollection <Voter> .Parse(source);
Beispiel #25
0
 public override ElectionResults GetElectionResults(CandidateComparerCollection <ApprovalBallot> ballots)
 {
     return(ScoreBallot.GetElectionResults(ballots));
 }
        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 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 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 },
            });
        }
Beispiel #29
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);
        }
Beispiel #30
0
 public static CandidateComparerCollection <TBallot> Ballots(string source, int?candidateCount = null)
 => CandidateComparerCollection <TBallot> .Parse(source, candidateCount);