Source code for whalrus.scorers.scorer_borda

# -*- coding: utf-8 -*-
"""
Copyright Sylvain Bouveret, Yann Chevaleyre and François Durand
sylvain.bouveret@imag.fr, yann.chevaleyre@dauphine.fr, fradurand@gmail.com

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
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
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 <http://www.gnu.org/licenses/>.
"""
from whalrus.ballots.ballot_order import BallotOrder
from whalrus.utils.utils import cached_property, NiceDict, my_division
from whalrus.scorers.scorer import Scorer
from typing import Union


[docs]class ScorerBorda(Scorer): """ A Borda scorer for :class:`BallotOrder`. Parameters ---------- args Cf. parent class. absent_give_points : bool Whether absent candidates give points to the other candidates. absent_receive_points : bool or None Whether absent candidates receives points. Remark: 0 means that any absent candidate receives the score 0 (which will be counted in its average Borda score, median Borda score, etc); in contrast, None means that the absent candidate receives no score (hence this voter will be excluded from the computation of its average Borda score, median Borda score, etc). unordered_give_points : bool Whether unordered candidates give points to the ordered candidates, i.e. they are considered as being in a lower position in the ranking. unordered_receive_points : bool or None Whether unordered candidates receive points. Like for ``absent_receive_points``, None means that an unordered candidate receives no score at all. kwargs Cf. parent class. Examples -------- Typical usage: >>> ScorerBorda(ballot=BallotOrder('a > b > c'), voter='Alice', ... candidates={'a', 'b', 'c'}).scores_ {'a': 2, 'b': 1, 'c': 0} In the example below, candidates `a`, `b` and `c` are "ordered", `d` and `e` are "unordered", and `f` and `g` are "absent" in the ballot, meaning that these candidates were not even available when the voter cast her ballot. The options allows for different ways to take these special cases into account: >>> ballot = BallotOrder('a > b ~ c', candidates={'a', 'b', 'c', 'd', 'e'}) >>> candidates_election = {'a', 'b', 'c', 'd', 'e', 'f', 'g'} >>> ScorerBorda(ballot, candidates=candidates_election).scores_as_floats_ {'a': 6.0, 'b': 4.5, 'c': 4.5, 'd': 2.5, 'e': 2.5, 'f': 0.5, 'g': 0.5} >>> ScorerBorda(ballot, candidates=candidates_election, ... absent_receive_points=False).scores_as_floats_ {'a': 6.0, 'b': 4.5, 'c': 4.5, 'd': 2.5, 'e': 2.5, 'f': 0.0, 'g': 0.0} >>> ScorerBorda(ballot, candidates=candidates_election, ... absent_receive_points=False, absent_give_points=False).scores_as_floats_ {'a': 4.0, 'b': 2.5, 'c': 2.5, 'd': 0.5, 'e': 0.5, 'f': 0.0, 'g': 0.0} >>> ScorerBorda(ballot, candidates=candidates_election, ... absent_receive_points=False, absent_give_points=False, ... unordered_receive_points=False).scores_as_floats_ {'a': 4.0, 'b': 2.5, 'c': 2.5, 'd': 0.0, 'e': 0.0, 'f': 0.0, 'g': 0.0} >>> ScorerBorda(ballot, candidates=candidates_election, ... absent_receive_points=False, absent_give_points=False, ... unordered_receive_points=False, unordered_give_points=False).scores_as_floats_ {'a': 2.0, 'b': 0.5, 'c': 0.5, 'd': 0.0, 'e': 0.0, 'f': 0.0, 'g': 0.0} Usage of None in the options: >>> ScorerBorda(ballot, candidates=candidates_election, ... absent_receive_points=None, unordered_receive_points=None).scores_as_floats_ {'a': 6.0, 'b': 4.5, 'c': 4.5} """ def __init__(self, *args, absent_give_points: bool = True, absent_receive_points: Union[bool, None] = True, unordered_give_points: bool = True, unordered_receive_points: Union[bool, None] = True, **kwargs): self.absent_give_points = absent_give_points self.absent_receive_points = absent_receive_points self.unordered_give_points = unordered_give_points self.unordered_receive_points = unordered_receive_points super().__init__(*args, **kwargs) @cached_property def scores_(self) -> NiceDict: scores = NiceDict() points_from_lower_candidates = 0 # Absent candidates if self.absent_give_points or self.absent_receive_points is not None: absent = self.candidates_ - self.ballot_.candidates n_absent = len(absent) if self.absent_receive_points is False: scores.update({c: 0 for c in absent}) if self.absent_receive_points is True: points_temp = my_division(n_absent - 1, 2) if self.absent_give_points else 0 scores.update({c: points_temp for c in absent}) if self.absent_give_points: points_from_lower_candidates += n_absent # Unordered candidates if self.unordered_give_points or self.unordered_receive_points is not None: unordered = self.ballot_.candidates_not_in_b n_unordered = len(unordered) if self.unordered_receive_points is False: scores.update({c: 0 for c in unordered}) if self.unordered_receive_points is True: points_temp = points_from_lower_candidates if self.unordered_give_points: points_temp += my_division(n_unordered - 1, 2) scores.update({c: points_temp for c in unordered}) if self.unordered_give_points: points_from_lower_candidates += n_unordered # Ordered candidates for indifference_class in self.ballot_.as_weak_order[::-1]: n_indifference = len(indifference_class) points_temp = points_from_lower_candidates + my_division(n_indifference - 1, 2) scores.update({c: points_temp for c in indifference_class}) points_from_lower_candidates += n_indifference return scores