Source code for embedded_voting.rules.singlewinner_rules.rule_instant_runoff

import numpy as np
from embedded_voting.embeddings.embeddings import Embeddings
from embedded_voting.embeddings_from_ratings.embeddings_from_ratings_identity import EmbeddingsFromRatingsIdentity
from embedded_voting.ratings.ratings import Ratings
from embedded_voting.rules.singlewinner_rules.rule import Rule
from embedded_voting.utils.cached import cached_property
from embedded_voting.rules.singlewinner_rules.rule_svd_nash import RuleSVDNash


[docs]class RuleInstantRunoff(Rule): """ This class enables to extend a voting rule to an ordinal input with Instant Runoff ranking. You cannot access to the :attr:`~embedded_voting.ScoringRule.scores_` because IRV only compute the ranking of the candidates. Parameters ---------- rule : Rule The aggregation rule used to determine the aggregated scores of the candidates. Examples -------- >>> ratings = np.array([[.1, .2, .8, 1], [.7, .9, .8, .6], [1, .6, .1, .3]]) >>> embeddings = Embeddings(np.array([[1, 0], [1, 1], [0, 1]]), norm=True) >>> election = RuleInstantRunoff(RuleSVDNash())(ratings, embeddings) >>> election.ranking_ [1, 0, 2, 3] """ def __init__(self, rule=None): super().__init__() self.rule = rule def __call__(self, ratings, embeddings=None): ratings = Ratings(ratings) if embeddings is None: embeddings = EmbeddingsFromRatingsIdentity()(ratings) self.embeddings_ = Embeddings(embeddings, norm=True) self.ratings_ = ratings self.delete_cache() return self def set_rule(self, rule): self.rule = rule self.delete_cache() return self def _score_(self, candidate): raise NotImplementedError @cached_property def ranking_(self): n_candidates = self.ratings_.n_candidates ranking = np.zeros(n_candidates, dtype=int) eliminated = [] for i in range(n_candidates): fake_ratings = self._create_fake_ratings(eliminated) rule_i = self.rule(fake_ratings, self.embeddings_) loser = rule_i.ranking_[n_candidates-1-i] ranking[n_candidates-i-1] = loser eliminated.append(loser) return list(ranking) @cached_property def winner_(self): return self.ranking_[0] def _create_fake_ratings(self, eliminated): """ This function creates a fake ratings for the election, based on the candidates already eliminated during the previous steps. Return ------ np.ndarray The fake ratings. """ fake_ratings = np.zeros(self.ratings_.shape) points = np.zeros(self.ratings_.n_candidates) points[0] = 1 for i in range(self.ratings_.n_voters): scores_i = self.ratings_[i].copy() scores_i[eliminated] = 0 ord_i = np.argsort(scores_i)[::-1] ord_i = np.argsort(ord_i) fake_ratings[i] = points[ord_i] return fake_ratings