elsim.elections module

Implements various election types.

Currently these are randomly-generated elections.

elsim.elections.impartial_culture(n_voters, n_cands, random_state=None)[source]

Generate ranked ballots using the impartial culture / random society model.

The impartial culture model selects complete preference rankings from the set of all possible preference rankings using a uniform distribution.

This model is unrealistic, but is commonly used because it has some worst-case properties and is comparable between researchers. [2]_

Parameters:
  • n_voters (int) – Number of voters

  • n_cands (int) – Number of candidates

  • random_state ({None, int, np.random.Generator}, optional) – Initializes the random number generator. If random_state is int, a new Generator instance is used, seeded with its value. (If the same int is given twice, the function will return the same values.) If None (default), an existing Generator is used. If random_state is already a Generator instance, then that object is used.

Returns:

election – A collection of ranked ballots. Rows represent voters and columns represent rankings, from best to worst, with no tied rankings. Each cell contains the ID number of a candidate, starting at 0.

For example, if a voter ranks Curie > Avogadro > Bohr, the ballot line would read [2, 0, 1] (with IDs in alphabetical order).

Return type:

numpy.ndarray

Notes

This implementation first generates a set of independent, uniformly distributed random utilities, which are then converted into rankings. [1]_

It can (extremely rarely) generate tied utilities, which are always ranked in order from lowest to highest, so there is a very slight bias in favor of lower-numbered candidates?

References

Examples

Generate an election with 4 voters and 3 candidates:

>>> impartial_culture(4, 3, random_state=1968)  # random_state for doctest
array([[0, 1, 2],
       [2, 1, 0],
       [0, 2, 1],
       [1, 0, 2]], dtype=uint8)

Here, Voter 2 prefers Candidate 0, then Candidate 2, then Candidate 1.

elsim.elections.normal_electorate(n_voters, n_cands, dims=2, corr=0.0, disp=1.0, random_state=None)[source]

Generate normally distributed voters and candidates in issue space.

Parameters:
  • n_voters (int) – Number of voters

  • n_cands (int) – Number of candidates

  • dims (int) – Number of dimensions

  • corr (float) – Correlation between each pair of random variables

  • disp (float) – The relative dispersions of voters vs candidates, as a ratio of standard deviations. For example, 1.0 means they are distributed by the same amount, while 0.5 means that candidates are more tightly concentrated than voters.

  • random_state ({None, int, np.random.Generator}, optional) – Initializes the random number generator. If random_state is int, a new Generator instance is used, seeded with its value. (If the same int is given twice, the function will return the same values.) If None (default), an existing Generator is used. If random_state is already a Generator instance, then that object is used.

Returns:

  • voters (numpy.ndarray) – Positions of voters in N-dimensional space, of shape (n_voters, n_dimensions).

  • cands (numpy.ndarray) – Positions of candidates in N-dimensional space, of shape (n_candidates, n_dimensions).

Notes

For computational efficiency, this behaves as if it had first generated an n×n correlation matrix with corr correlation between each pair of variables, then diagonalized it, rotating it to principal axes, so the variables became uncorrelated (though the distribution kept the same shape), then generated independent normally distributed variables from it.

For 4 dimensions and a correlation of C, for instance, the correlation matrix is:

[[1, C, C, C],
 [C, 1, C, C],
 [C, C, 1, C],
 [C, C, C, 1]]

This can also be interpreted as a covariance matrix, since only relative variance matters, not absolute variance.

After rotating the distribution, the covariance matrix becomes:

[[A, 0, 0, 0],
 [0, B, 0, 0],
 [0, 0, B, 0],
 [0, 0, 0, B]]

where B = 1 - corr and A = 1 + (dims - 1)*corr

And to simplify even more, the first variable becomes scaled by A/B, while the rest are 0 or 1, since again, only the relative variance matters.

References

elsim.elections.normed_dist_utilities(voters, cands)[source]

Generate normalized utilities from a spatial model.

Given the positions of voters and candidates, calculate the distance from each voter to each candidate, and then assign proportional utilities, where the farthest candidate from each voter has a utility of 0 and the nearest has a utility of 1.

Parameters:
  • voters (array_like) – Positions of voters in N-dimensional space, of shape (n_voters, n_dimensions).

  • cands (array_like) – Positions of candidates in N-dimensional space, of shape (n_candidates, n_dimensions).

Returns:

utilities – A collection of utilities between 0 and 1, inclusive. Rows represent voters and columns represent candidates.

Return type:

numpy.ndarray

References

Examples

Given an election with 3 voters and 3 candidates in a 2D space:

>>> voters = ((1, 1),
...           (6, 3),
...           (1, 7))
>>> cands = ((2, 3),
...          (5, 1),
...          (4, 6))

Calculate their normalized utilities:

>>> normed_dist_utilities(voters, cands)
array([[1.        , 0.50932156, 0.        ],
       [0.        , 1.        , 0.22361901],
       [0.76268967, 0.        , 1.        ]])
elsim.elections.random_utilities(n_voters, n_cands, random_state=None)[source]

Generate utilities using the impartial culture / random society model.

The random society [1]_ or random uniform utilities [2]_ model selects independent candidate utilities for each voter from a uniform distribution in the interval [0, 1).

This model is unrealistic, but is commonly used because it has some worst-case properties and is comparable between researchers. [3]

Parameters:
  • n_voters (int) – Number of voters

  • n_cands (int) – Number of candidates

  • random_state ({None, int, np.random.Generator}, optional) – Initializes the random number generator. If random_state is int, a new Generator instance is used, seeded with its value. (If the same int is given twice, the function will return the same values.) If None (default), an existing Generator is used. If random_state is already a Generator instance, then that object is used.

Returns:

utilities – A collection of utilities between 0 and 1, inclusive. Rows represent voters and columns represent candidates.

Return type:

numpy.ndarray

References

Examples

Generate an election with 4 voters and 3 candidates:

>>> random_utilities(4, 3, random_state=1978)  # random_state for doctest
array([[0.93206637, 0.98841683, 0.31440049],
       [0.9124727 , 0.77671832, 0.69045119],
       [0.29916278, 0.08604875, 0.71802998],
       [0.32856141, 0.16991956, 0.46150403]])

Here, Voter 2 prefers Candidate 2, considers Candidate 0 mediocre, and strongly dislikes Candidate 1.