Spatial Model Example

The following example illustrates how to generate simulated preference profiles from a spatial model and evaluate whether the Borda count voting system satisfies independence of irrelevant alternatives (IIA). A spatial model describes the beliefs of voters as points in an n-dimensional space where each dimension represents a belief about a policy or value. Candidates are represented as points in the n-dimensional space. The model assumes voters rank order candidates as an increasing function of distance, where the minimum distance is ranked 1, the second shortest distances is ranked 2 and so forth.

Load Dependencies

The first step is to load the required dependencies.

using LinearAlgebra
using Plots
using RankChoiceVoting
using Random
using StatsBase
Random.seed!(54)
Random.TaskLocalRNG()

Define Helper Functions

In the code block below, we define two helper functions. The function rank_candidates computes the distance between a voter and the candidatess and returns a rank ordering based on distance. The function select returns the index associated with the most preferred candidate.

function rank_candidates(beliefs, candidates)
    return competerank(map(c -> norm(beliefs .- c), candidates))
end

function select(beliefs, candidates)
    _,choice = findmin(rank_candidates(beliefs, candidates))
    return choice
end
select (generic function with 1 method)

Define Candidates

Lets assume the belief space defined over $[0,1]^2$ and the candidates occupy the following positions in that space.

candidates = [(.3,.3), (.3,.5),(.8,.5)]
3-element Vector{Tuple{Float64, Float64}}:
 (0.3, 0.3)
 (0.3, 0.5)
 (0.8, 0.5)

The plot below illustrates the area of the belief space to which the canidates appeal. For example, voters in the top left quadrant will vote for candidate 2.

points = [Tuple(rand(2)) for _ ∈ 1:10_000]
first_choices = map(x -> select(x, candidates), points)
scatter(points, lims=(0,1), grid=false, legendtitle="candidates", group=first_choices,  legend=:outerright)
Example block output

Define Voters

Next, we will define a set of 100 voters by sampling uniformly over $[0,1]^2$:

voters = [Tuple(rand(2)) for _ ∈ 1:100]
votes = map(v -> rank_candidates(v, candidates), voters)
ranks = Ranks(votes)
Ranks
┌────────┬───────────┐
│ Counts │ Ranks     │
├────────┼───────────┤
│ 32     │ [3, 2, 1] │
│ 15     │ [2, 3, 1] │
│ 19     │ [1, 2, 3] │
│ 26     │ [2, 1, 3] │
│ 7      │ [3, 1, 2] │
│ 1      │ [1, 3, 2] │
└────────┴───────────┘

Evaluate

The last code blocks tests whether the Borda count system satisisfies IIA for the simulated preference profile.

satisfies(Borda(), IIA(), ranks)
true

In this case, it does satisfy IIA. Does hold in general? Let's call satisfies without the preference profile to answer that question.

satisfies(Borda(), IIA())
false

The result above indicates that there are some cases in which the Borda count system violates IIA.