public override Dictionary <Strategy, double> CalculateSatisfaction(Random random, CandidateComparerCollection <Voter> voters)
        {
            // This is here for comparison purposes, not analysis.
            var satisfaction = GetSatisfactionWith(voters)(voters.GetBeatMatrix().GetSchulzeSet());

            return(new Dictionary <Strategy, double>
            {
                { Strategy.Honest, satisfaction },
            });
        }
        public override ElectionResults GetElectionResults(CandidateComparerCollection <RankedBallot> ballots)
        {
            var approvalCount    = new int[ballots.CandidateCount];
            var firstChoices     = new int[ballots.CandidateCount];
            var approvalByBallot = new Dictionary <RankedBallot, ulong>();
            var history          = new List <(int[] NewApprovalCount, List <int> Winners, CountedList <(ulong Preferred, int Candidate)> Compromises)>();

            foreach (var(ballot, count) in ballots.Comparers)
            {
                var firstChoiceCandidates = ballot.RanksByCandidate.IndexesWhere(a => a == 0).ToList();
                approvalByBallot[ballot] = GetCoalition(firstChoiceCandidates);

                // Approve of one's first choices.
                foreach (var c in firstChoiceCandidates)
                {
                    approvalCount[c] += count;
                    firstChoices[c]  += count;
                }
            }

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

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

            var beatMatrix      = ballots.GetBeatMatrix();
            var previousWinners = winners.ToDictionary(
                b => b,
                b => Enumerable.Range(0, ballots.CandidateCount)
                .Where(s => beatMatrix.Beats(s, b))
                .ToList());

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

                foreach (var(ballot, count) in ballots.Comparers)
                {
                    var approveUntilRank  = 0;
                    var approvalCoalition = 0ul;

                    // If one approves of all candidates which beat one of the previous winners, do so (and approve of candidates one likes better than the worst of those)
                    // (if we don't, neither will the people who prefer the other saviours -- and we won't be able to beat the bogeyman)
                    // otherwise, approve of all candidates one likes better than said winner.
                    foreach (var(bogeyman, saviours) in previousWinners)
                    {
                        var bogeymanRank = ballot.RanksByCandidate[bogeyman];
                        var saviourRanks = saviours.Select(s => ballot.RanksByCandidate[s]).ToList();

                        if (saviours.Any() && saviourRanks.All(sr => sr > bogeymanRank))
                        {
                            approvalCoalition |= GetCoalition(saviours);
                            approveUntilRank   = saviourRanks.Append(approveUntilRank).Min();
                        }
                        else
                        {
                            approveUntilRank = Math.Min(approveUntilRank, bogeymanRank);
                        }
                    }

                    var approvedCoalition = approvalByBallot[ballot];

                    var newApprovals = ballot.RanksByCandidate
                                       .IndexesWhere((rank, candidate) =>
                                                     (rank > approveUntilRank || (approvalCoalition & GetCoalition(candidate)) > 0) && (approvedCoalition & GetCoalition(candidate)) == 0ul)
                                       .ToList();

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

                    if (newApprovals.Any())
                    {
                        approvalByBallot[ballot] = approvedCoalition | GetCoalition(newApprovals);
                    }
                }

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

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

                var newBogeymen = winners.Where(w => !previousWinners.ContainsKey(w));

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

                foreach (var b in newBogeymen)
                {
                    previousWinners[b] = Enumerable.Range(0, ballots.CandidateCount)
                                         .Where(s => beatMatrix.Beats(s, b))
                                         .ToList();
                }
            }

            var results = new ElectionResults(approvalCount.IndexRanking());

            if (history.Count == 1)
            {
                results.AddHeading("Approval");
                results.AddCandidateTable(approvalCount);
            }
            else
            {
                results.AddHeading("Rounds");
                results.AddTable(history.Select((h, i) => new ElectionResults.Value[] {
                    i + 1,
                    h.Winners,
                }.Concat(h.NewApprovalCount.Select(a => (ElectionResults.Value)a))
                                                .ToArray())
                                 .Append(approvalCount.Select(a => (ElectionResults.Value)a).Prepend(results.Ranking[0]).Prepend("Total").ToArray()),
                                 Enumerable.Range(0, ballots.CandidateCount).Select(c => (ElectionResults.Candidate)c).Prepend <ElectionResults.Value>("Winner").ToArray());

                results.AddHeading("Winners");
                results.AddTable(
                    previousWinners.Select(kvp => new ElectionResults.Value[] {
                    (ElectionResults.Candidate)kvp.Key,
                    kvp.Value
                }),
                    "Beaten By");

                var roundNumber = 1;
                foreach (var round in history.Skip(1))
                {
                    roundNumber++;
                    results.AddHeading("Round " + roundNumber + " Compromises");

                    results.AddTable(round.Compromises.Select(a => new ElectionResults.Value[] {
                        (ElectionResults.Candidate)a.Item.Candidate,
                        a.Item.Preferred,
                        a.Count
                    }),
                                     "Comp.",
                                     "Pref.",
                                     "Count");
                }
            }

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

            // Approve of one's first choices.
            foreach (var(ballot, count) in ballots.Comparers)
            {
                foreach (var c in ballot.Buckets.IndexesWhere(a => a == Bucket.Best))
                {
                    approvalCount[c] += count;
                    firstChoices[c]  += count;
                }
            }

            var beatMatrix = ballots.GetBeatMatrix();

            var candidates  = Enumerable.Range(0, ballots.CandidateCount);
            var compromises = new CountedList <(ulong Preferred, int Compromise, ulong Bogeymen)>();

            foreach (var(ballot, count) in ballots.Comparers)
            {
                var best = ballot.Buckets.IndexesWhere(a => a == Bucket.Best).ToList();
                var good = ballot.Buckets.IndexesWhere(a => a == Bucket.Good).ToList();
                var bad  = ballot.Buckets.IndexesWhere(a => a == Bucket.Bad).ToList();

                // Support each "good" candidate `g` such that
                // For each "best" candidate `c`
                // There exists a "bad" candidate `b` (the 'bogeyman') such that `b` beats `c` in first-choice votes but loses to `g` one-on-one
                if (good.Any() && bad.Any())
                {
                    var bogeymen = bad
                                   .Where(b => best.All(c => firstChoices[b] > firstChoices[c]))
                                   .ToList();

                    if (bogeymen.Any())
                    {
                        var bestCoalition     = GetCoalition(best);
                        var bogeymenCoalition = GetCoalition(bogeymen);

                        foreach (var c in good.Where(g => bogeymen.Any(b => beatMatrix.Beats(g, b))))
                        {
                            approvalCount[c] += count;
                            compromises.Add((bestCoalition, c, bogeymenCoalition), count);
                        }
                    }
                }
            }

            var results = new ElectionResults(approvalCount.IndexRanking());

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

            results.AddHeading("Compromises");
            results.AddTable(compromises
                             .Select(c => new ElectionResults.Value[] {
                (ElectionResults.Candidate)c.Item.Compromise,
                c.Item.Preferred,
                c.Item.Bogeymen,
                c.Count
            }),
                             "Comp.",
                             "Pref.",
                             "Bogey.",
                             "Count");

            return(results);
        }
Esempio n. 4
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. 5
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 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. 7
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 <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);
        }