Core#

Core functions that are not specific to any ballot format.

Composition of Samplers#

concatenation(num_voters_per_sampler: list[int], num_candidates: int, samplers: list[Callable], sampler_parameters: list[dict]) list[source]#

Generate votes from different samplers and concatenate them together to form the final set of votes.

Note that votes are not sampled one after the other from the samplers but all at once. This is important if you are using samplers that are not independent.

It is assumed that you pass samplers that are all about the same type of ballots (only ordinal or only approval for instance). If you don’t, the code will probably fail.

Parameters:
  • num_voters_per_sampler (int) – List of numbers of voters to be sampled from each sampler.

  • num_candidates (int) – Number of Candidates.

  • samplers (list,) – List of samplers.

  • sampler_parameters (list,) – List of dictionaries passed as keyword parameters of the samplers. Number of voters and number of candidates of these dictionaries are not taken into account.

Returns:

The concatenated votes.

Return type:

list

Examples

from prefsampling.core import concatenation
from prefsampling.ordinal import mallows

# A concatenation of two Mallows' models with different phi and central votes.
# 4 votes are sampled from the first model and 6 votes from the second.
# There are 5 candidates

concatenation(
    [4, 6],
    5,
    [mallows, mallows],
    [
        {'phi': 0.2, 'central_vote': range(5)},
        {'phi': 0.9, 'central_vote': [4, 3, 2, 1, 0]}
    ],
)
mixture(num_voters: int, num_candidates: int, samplers: list[Callable], weights: list[float], sampler_parameters: list[dict], seed: int = None) list[source]#

Generates a mixture of samplers. The process works as follows: for each vote, we sample which sample will be used to generate it based on the weight distribution of the samplers, then, the corresponding number of votes are sampled from the samplers and concatenated together to form the final set of votes.

Note that votes are not sampled one after the other from the samplers but all at once. This is important if you are using samplers that are not independent.

It is assumed that you pass samplers that are all about the same type of ballots (only ordinal or only approval for instance). If you don’t, the code will probably fail.

Parameters:
  • num_voters (int) – Number of Voters.

  • num_candidates (int) – Number of Candidates.

  • samplers (list[Callable]) – List of samplers.

  • weights (list[float]) – Probability distribution over the samplers, the sampler in position k has weight weights[k].

  • sampler_parameters (list[dict]) – List of dictionaries passed as keyword parameters of the samplers. Number of voters and number of candidates of these dictionaries are not taken into account.

  • seed (int, default: None) – Seed for numpy random number generator. Note that this is only the seed for this function. If you want to use particular seed for the functions generating votes, you should pass it as parameter within the sampler_parameters list.

Returns:

The votes sampled from the mixture.

Return type:

list

Examples

from prefsampling.core import mixture
from prefsampling.ordinal import mallows

# A mixture of two Mallows' models with different phi and central votes.
# The first model has weight 0.7 and the second 0.3.
# There are 10 voters and 5 candidates.

mixture(
    10,
    5,
    [mallows, mallows],
    [0.7, 0.3],
    [
        {'phi': 0.2, 'central_vote': range(5)},
        {'phi': 0.9, 'central_vote': [4, 3, 2, 1, 0]}
    ],
)

# The weights are re-normalised if they don't add up to one.
# The real weights here would be 3/4, 1/4.

from prefsampling.approval import noise, identity

mixture(
    10,
    5,
    [noise, identity],
    [0.3, 0.1],
    [
        {'rel_size_central_vote': 0.2, 'phi': 0.4},
        {'rel_num_approvals': 0.6}
    ],
)

Filters#

Filters are functions that operate on collections of votes and apply some random operation to them.

coin_flip_ties(ordinal_votes: list[list[int]], p: float, seed: int = None) list[list[list[int]]][source]#

In the coin-flip models, a complete ordered is turned into a weak order by adding ties between the candidates as follows: for each pair of consecutively ranked candidates, we add a tie between them with probability p.

Parameters:
  • ordinal_votes (list[list[int]]) – The (strict) ordinal votes.

  • p (float) – The probability of forming a tie.

  • seed (int, default: None) – Seed for numpy random number generator.

Returns:

The weak orders.

Return type:

list[list[list[int]]]

Examples

from prefsampling.ordinal import urn
from prefsampling.core import coin_flip_ties

# Get some votes
strict_ordinal_votes = urn(2, 3, 0.2)

# We randomly introduce ties
weak_ordinal_votes = coin_flip_ties(strict_ordinal_votes, 0.34)

# You can set the seed to ensure reproducibility
weak_ordinal_votes = coin_flip_ties(strict_ordinal_votes, 0.34, seed=265)

References

Generalizing Instant Runoff Voting to Allow Indifferences, Théo Delemazure and Dominik Peters, arXiv:2404.11407, 2024.

permute_voters(votes: list, seed: int = None) list[source]#

Randomly permutes the voters in an ordered collection of votes.

Parameters:
  • votes (list) – The votes.

  • seed (int, default: None) – Seed for numpy random number generator.

Returns:

The votes permuted.

Return type:

list

Examples

from prefsampling.ordinal import didi
from prefsampling.core import permute_voters

# Get some votes
ordinal_votes = didi(2, 3, (0.5, 0.2, 0.1))

# Randomly permute the voters
permute_voters(ordinal_votes)

# The syntax is the same with approval votes

from prefsampling.approval import resampling

approval_votes = resampling(2, 3, 0.5, 0.2)
permute_voters(approval_votes)

# You can set the seed for reproducibility

permute_voters(approval_votes, seed=234)
rename_candidates(votes: list[set[int]] | ndarray, num_candidates: int = None, seed: int = None) list[source]#

Renames the candidates in approval or ordinal votes.

Note that if the votes can be incomplete (e.g., in the case of approval voting), you need to provide the number of candidates as input. If it is not provided, it is assumed to be the largest integer appearing in the ballots (candidates are represented as int).

Parameters:
  • votes (list[set[int]] or np.ndarray) – Approval or ordinal votes.

  • num_candidates (int) – Number of Candidates. Needed for incomplete (e.g., approval) votes.

  • seed (int, default: None) – Seed for numpy random number generator.

Returns:

Votes with renamed candidates.

Return type:

list

Examples

from prefsampling.ordinal import didi
from prefsampling.core import rename_candidates

# Get some votes
ordinal_votes = didi(2, 3, (0.5, 0.2, 0.1))

# Randomly permute the voters
rename_candidates(ordinal_votes)

# With approval votes, you need to give the number of candidates

from prefsampling.approval import resampling

approval_votes = resampling(2, 3, 0.5, 0.2)
rename_candidates(approval_votes, num_candidates=3)

# You can set the seed for reproducibility

rename_candidates(approval_votes, num_candidates=3, seed=234)
resample_as_central_vote(votes: ndarray | list[set[int]], sampler: Callable, sampler_parameters: dict, num_candidates: int = None) list[source]#

Resamples the votes by using them as the central vote of a given sampler. The outcome is obtained as follows: for each input vote, we pass it to the sampler as central vote; a single vote is then resampled and added to the outcome.

Only samplers that accept a central_vote argument can be used.

Note that if the votes can be incomplete (e.g., in the case of approval voting), you need to provide the number of candidates as input. If it is not provided, it is assumed to be the largest integer appearing in the ballots (candidates are represented as int).

Votes are copied before being returned to avoid loss of data.

Parameters:
  • votes (list[set[int]] or np.ndarray) – Approval or ordinal votes.

  • sampler (Callable) – The sampler used to resample the votes.

  • sampler_parameters (dict) –

    Dictionary passed as keyword parameters of the sampler. Number of voters or central vote

    of this dictionary are not taken into account.

  • num_candidates (int, defaults: None) – The number of candidates, useful for incomplete ballots in which this information cannot be retrieved from the votes.

Returns:

Votes resampled.

Return type:

list

Examples

from prefsampling.ordinal import urn, mallows
from prefsampling.core import resample_as_central_vote

# Get some votes
ordinal_votes = urn(2, 3, 0.2)

# We resample them by passing them as central vote to a Mallows' model
resample_as_central_vote(ordinal_votes, mallows, {'phi': 0.3})

# The syntax is the same with approval votes

from prefsampling.approval import urn, resampling

approval_votes = urn(2, 3, 0.5, 0.2)
resample_as_central_vote(
    approval_votes,
    resampling,
    {'phi': 0.4, 'rel_size_central_vote': 0.8}
)

# To ensure reproducibility, you need to pass the seed everywhere
seed = 4234
approval_votes = urn(2, 3, 0.5, 0.2, seed=seed)
resample_as_central_vote(
    approval_votes,
    resampling,
    {'phi': 0.4, 'rel_size_central_vote': 0.8, 'seed':seed}
)

Module core.euclidean#

class EuclideanSpace(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]#

Constants for some pre-defined Euclidean distributions.

GAUSSIAN_BALL = 'gaussian_ball'#

Constants representing a Gaussian ball with center point at the origin and width of 1 for all dimensions. The inner Gaussian sampler has mean 0 and standard deviation 0.33.

GAUSSIAN_CUBE = 'gaussian_cube'#

Constants representing a Gaussian ball with center point at the origin and width of 1 for all dimensions.

UNBOUNDED_GAUSSIAN = 'unbounded_gaussian'#

Constants representing an unbounded Gaussian space.The inner Gaussian sampler has mean 0 and standard deviation 1.

UNIFORM_BALL = 'uniform_ball'#

Constants representing a uniform ball with center point at the origin and width of 1 for all dimensions.

UNIFORM_CUBE = 'uniform_cube'#

Constants representing a uniform cube with center point at the origin and width of 1 for all dimensions.

UNIFORM_SPHERE = 'uniform_sphere'#

Constants representing a uniform sphere with center point at the origin and width of 1 for all dimensions. This is the envelope of the uniform ball.

euclidean_space_to_sampler(space: EuclideanSpace, num_dimensions: int, seed: int = None)[source]#

Returns the point sampler together with its arguments corresponding to the EuclideanSpace passed as argument.

sample_election_positions(num_voters: int, num_candidates: int, num_dimensions: int, voters_positions: EuclideanSpace | Callable | Iterable[Iterable[float]], candidates_positions: EuclideanSpace | Callable | Iterable[Iterable[float]], voters_positions_args: dict = None, candidates_positions_args: dict = None, seed: int = None) tuple[ndarray, ndarray][source]#
Parameters:
  • num_voters (int) – Number of Voters.

  • num_candidates (int) – Number of Candidates.

  • num_dimensions (int) – The number of dimensions to use. Using this argument is mandatory when passing a space as argument. If you pass samplers as arguments and use the num_dimensions, then, the value of num_dimensions is passed as a kwarg to the samplers.

  • voters_positions (EuclideanSpace | Callable | Iterable[Iterable[float]]) – The positions of the voters, or a way to determine them. If an Iterable is passed, then it is assumed to be the positions themselves. Otherwise, it is assumed that a sampler for the positions is passed. It can be either the nickname of a sampler—when passing a EuclideanSpace; or a sampler. A sampler is a function that takes as keywords arguments: ‘num_points’, ‘num_dimensions’, and ‘seed’. Additional arguments can be provided with by using the voters_positions_args argument.

  • candidates_positions (EuclideanSpace | Callable | Iterable[Iterable[float]]) – The positions of the candidates, or a way to determine them. If an Iterable is passed, then it is assumed to be the positions themselves. Otherwise, it is assumed that a sampler for the positions is passed. It can be either the nickname of a sampler—when passing a EuclideanSpace; or a sampler. A sampler is a function that takes as keywords arguments: ‘num_points’, ‘num_dimensions’, and ‘seed’. Additional arguments can be provided with by using the candidates_positions_args argument.

  • voters_positions_args (dict, default: dict()) – Additional keyword arguments passed to the voters_positions sampler when the latter is a Callable.

  • candidates_positions_args (dict, default: dict()) – Additional keyword arguments passed to the candidates_positions sampler when the latter is a Callable.

  • seed (int, default: None) – Seed for numpy random number generator. Also passed to the point samplers if a value is provided.

Returns:

The positions of the voters and of the candidates.

Return type:

tuple[np.ndarray, np.ndarray]