Source code for whalrus.eliminations.elimination_last

# -*- coding: utf-8 -*-
Copyright Sylvain Bouveret, Yann Chevaleyre and Fran├žois Durand,,

This file is part of Whalrus.

Whalrus is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Whalrus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Whalrus.  If not, see <>.
from whalrus.utils.utils import cached_property, NiceSet
from whalrus.eliminations.elimination import Elimination
from whalrus.rules.rule_plurality import RulePlurality
from whalrus.priorities.priority import Priority

[docs]class EliminationLast(Elimination): """ Elimination of the last candidates (with a fixed number of candidates to eliminate, or to qualify). Parameters ---------- args Cf. parent class. k : int A nonzero integer. The number of eliminated candidates. If this number is negative, then ``len(rule.candidates_) - abs(k)`` candidates are eliminated, i.e. ``abs(k)`` candidates are qualified. kwargs` Cf. parent class. Examples -------- In the most general syntax, firstly, you define the elimination method: >>> elimination = EliminationLast(k=1) Secondly, you use it as a callable to load a particular election (rule, profile, candidates): >>> rule = RulePlurality(ballots=['a', 'a', 'b', 'b', 'c']) >>> elimination(rule) # doctest:+ELLIPSIS <... object at ...> Finally, you can access the computed variables: >>> elimination.eliminated_ {'c'} Later, if you wish, you can load another election with the same elimination method, and so on. Optionally, you can specify an election (rule, profile, candidates) as soon as the :class:`Elimination` object is initialized. This allows for one-liners such as: >>> EliminationLast(rule=RulePlurality(ballots=['a', 'a', 'b', 'b', 'c']), k=1).eliminated_ {'c'} Typical usage with ``k = 1`` (e.g. for :class:`RuleIRV`): >>> rule = RulePlurality(ballots=['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'e'], ... tie_break=Priority.ASCENDING) >>> EliminationLast(rule=rule, k=1).eliminated_ {'e'} Typical usage with ``k = -2`` (e.g. for :class:`RuleTwoRound`): >>> rule = RulePlurality(ballots=['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'e'], ... tie_break=Priority.ASCENDING) >>> EliminationLast(rule=rule, k=-2).qualified_ {'a', 'b'} Order of elimination: >>> rule = RulePlurality(ballots=['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'e'], ... tie_break=Priority.ASCENDING) >>> EliminationLast(rule=rule, k=-2).eliminated_order_ [{'c'}, {'d', 'e'}] There must always be at least one eliminated candidate. If it is not possible to eliminate (case ``k`` > 0) or keep (case ``k`` < 0) as many candidates as required, then everybody is eliminated: >>> rule = RulePlurality(ballots=['a']) >>> EliminationLast(rule=rule, k=1).eliminated_ {'a'} >>> EliminationLast(rule=rule, k=-2).eliminated_ {'a'} """ def __init__(self, *args, k: int = 1, **kwargs): self.k = k super().__init__(*args, **kwargs) @cached_property def eliminated_order_(self): if self.k > 0: n_wanted = self.k else: n_wanted = self.rule_.n_candidates_ + self.k if n_wanted <= 0 or n_wanted >= self.rule_.n_candidates_: return self.rule_.order_ worst_first = [] for tie_class in self.rule_.order_[::-1]: size_class = len(tie_class) if size_class <= n_wanted: worst_first.append(tie_class) n_wanted -= size_class if n_wanted == 0: break else: worst_first.append(NiceSet(self.rule_.tie_break.sort(tie_class)[-1:-1 - n_wanted:-1])) break return worst_first[::-1]