Search code examples
python-3.xmatrixmaxrankpairwise

How to select winner of condorcet election via matrix?


I would like to understand how the condorcet winner is elected in a ranked-choice election.

There is an example on wikipedia - under the sub-heading "Pairwise counting and matrices" under the heading "Basic procedure", in which the pairwise comparison matrix is constructed for candidates A, B, C, and D in an election. The matrix looks like:

From this matrix, it is concluded that A is the condorcet winner. I can see that the result is correct, but do not understand how to algorithmically approach this problem.

`

[[0, 2, 2, 2],

[1, 0, 1, 2],

[1, 2, 0, 2],

[1, 1, 1, 0]]

`

A more complicated example can be found on table 2-a at accuratedemocracy; when considering a different election with candidates A, B, C, and D; they find that C is the winner, but one can click cells C and D to verify that they earn the same number of votes in the pairwise comparison matrix. Why is C the winner and not D?

Suppose one has constructed the pair-wise comparison matrix. How can one select the condorcet winner from this matrix?

An example, one can construct the pairwise comparison matrix in python using the following code:

`

import numpy as np

unranked_choices = ["A", "B", "C", "D"]

def generate_preference_matrix(ranked_choices):
    num_candidates = len(ranked_choices)
    preference_matrix = np.zeros((num_candidates, num_candidates))

    for i, candidate_i in enumerate(unranked_choices):
        for j, candidate_j in enumerate(unranked_choices):
            if i != j:
                if ranked_choices.index(candidate_i) < ranked_choices.index(candidate_j):
                    preference_matrix[i, j] += 1  # Candidate_i is preferred over candidate_j
                # else:
                #     preference_matrix[i, j] = 0  # No preference or candidate_j is preferred over candidate_i

    return preference_matrix


# Test with example ranked choices
ranked_choices = ['B', 'C', 'A', 'D']  # Ranked choices in order of preference
preference_matrix = generate_preference_matrix(ranked_choices)
print(preference_matrix)

ranked_choices = ['A', 'D', 'B', 'C']  # Ranked choices in order of preference
preference_matrix = generate_preference_matrix(ranked_choices)
print(preference_matrix)

`


Solution

  • You need to sum the preference matrices in order to calculate the ranking. The candidate with the highest total is the declared the winner.

    import numpy as np
    from itertools import combinations
    
    
    def get_ranked_order(candidates, preferences):
    
        def get_preference_matrix(preference_order):
            nonlocal candidates
            matrix = np.zeros((len(preference_order), len(preference_order)))
            for a, b in combinations(candidates, 2):
                # Head to Head ranking comparisons
                if preference_order.index(a) < preference_order.index(b):
                    matrix[candidates.index(a), candidates.index(b)] += 1
                if preference_order.index(b) < preference_order.index(a):
                    matrix[candidates.index(b), candidates.index(a)] += 1
            return matrix
    
        # Sum the preference matrices
        sum_matrix = np.zeros((len(candidates), len(candidates)))
        for p in preferences:
            sum_matrix = np.add(sum_matrix, get_preference_matrix(p))
    
        # Sort candidates by decreasing scores
        return sorted(zip(candidates, [sum(r) for r in sum_matrix]),
                      key=lambda t: t[1], reverse=True)
    

    Using the data from the example on the Wikipedia page:

    vote_candidates = ("A", "B", "C", "D")
    voter_preferences = [
        ("B", "C", "A", "D"), 
        ("A", "D", "B", "C"), 
        ("A", "C", "B", "D")
    ]
    
    rankings = get_ranked_order(vote_candidates, voter_preferences)
    
    if rankings[0][1] > rankings[1][1]:
        print(f"Condorcet Winner is {rankings[0]}")
    else:
        print(rankings)
    

    Result:

    Condorcet Winner is ('A', 7.0)