Source code for whalrus.scorers.scorer

# -*- 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 import Ballot
from whalrus.scales.scale import Scale
from whalrus.utils.utils import DeleteCacheMixin, cached_property, NiceDict, NiceSet


[docs]class Scorer(DeleteCacheMixin): """ A "scorer". A :class:`Scorer` is a callable whose inputs are a ballot, a voter and a set of candidates (the set of candidates of the election). When the scorer is called, it loads its arguments. The output of the call is the scorer itself. But after the call, you can access to the computed variables (ending with an underscore), such as :attr:`scores_`. At the initialization of a :class:`Scorer` object, some options can be given, such as a scale. In some subclasses, there can be some additional options. Parameters ---------- args If present, these parameters will be passed to ``__call__`` immediately after initialization. scale : Scale The scale in which scores are computed. kwargs If present, these parameters will be passed to ``__call__`` immediately after initialization. Attributes ---------- ballot_: Ballot This attribute stores the ballot given in argument of the ``__call__``. voter_: object This attribute stores the voter given in argument of the ``__call__``. candidates_: NiceSet This attribute stores the candidates given in argument of the ``__call__``. Examples -------- Cf. :class:`ScorerLevels` for some examples. """ def __init__(self, *args, scale: Scale = None, **kwargs): if scale is None: scale = Scale() # Parameters self.scale = scale # Computed variables self.ballot_ = None self.voter_ = None self.candidates_ = None # Optional: load a ballot at initialization if args or kwargs: self(*args, **kwargs) def __call__(self, ballot: Ballot, voter: object = None, candidates: set = None): """ Load the arguments. Parameters ---------- ballot : Ballot We assume that it is already in the correct subclass of :class:`Ballot` and that it is already restricted to the candidates of the election (if necessary). voter : object The voter. candidates : set The candidates. """ self.ballot_ = ballot self.voter_ = voter self.candidates_ = candidates self.delete_cache() return self @cached_property def scores_(self) -> NiceDict: """NiceDict: The scores. To each candidate, this dictionary associates either a level in the scale or None. For the meaning of None, cf. :class:`RuleRangeVoting` for example. Intuitively: a score of 0 means that the value 0 is counted in the average, whereas None is not counted at all (i.e. the weight of the voter is not even counted in the denominator when computing the average). """ raise NotImplementedError @cached_property def scores_as_floats_(self) -> NiceDict: """NiceDict: The scores, given as floats. It is the same as :attr:`scores_`, but converted to floats. Like all conversions to floats, it is advised to use this attribute for display purposes only. For computation, you should always use :attr:`scores_`, which usually manipulates fractions and therefore allows for exact computation. Raises ------ ValueError If the scores cannot be converted to floats. """ try: return NiceDict({c: float(v) for c, v in self.scores_.items()}) except ValueError: raise ValueError('These scores cannot be converted to floats: %r.' % self.scores_)