def remove(self, ballot: object = None, voter: object = None) -> None: """ Remove a ballot from the profile. :param ballot: the ballot or, more generally, an input that can be interpreted by :class:`ConverterBallotGeneral`. :param voter: the voter. If only the ballot is specified, remove the first matching ballot in the profile. If only the voter is specified, remove the first ballot whose voter matches the given voter. If both are specified, remove the first ballot matching both descriptions. >>> profile = Profile(['a > b', 'b > a']) >>> profile.remove('b > a') >>> print(profile) a > b """ if ballot is None: i = next(i for i, v in enumerate(self.voters) if v == voter) elif voter is None: i = next(i for i, b in enumerate(self.ballots) if b == ConverterBallotGeneral()(ballot)) else: i = next(i for i, b in enumerate(self.ballots) if b == ConverterBallotGeneral()(ballot) and self.voters[i] == voter) del self._ballots[i] del self._voters[i] del self._weights[i] self.delete_cache()
def __call__(self, x: object, candidates: set = None) -> BallotOrder: x = ConverterBallotGeneral()(x, candidates=None) if isinstance(x, BallotOrder): return x.restrict(candidates=candidates) if isinstance(x, BallotVeto): return BallotOrder([x.candidates_not_in_b, {x.last()}]).restrict(candidates=candidates) if isinstance(x, BallotOneName): return BallotOrder([{x.first()}, x.candidates_not_in_b ]).restrict(candidates=candidates) raise NotImplementedError
def __call__(self, x: object, candidates: set = None) -> BallotPlurality: x = ConverterBallotGeneral()(x, candidates=None) if isinstance(x, BallotPlurality): return x.restrict(candidates=candidates, priority=self.plurality_priority) if isinstance(x, BallotVeto): first = x.first(candidates=candidates, priority=self.veto_priority) if candidates is None: candidates = x.candidates else: candidates = x.candidates & candidates return BallotPlurality(first, candidates=candidates) if isinstance(x, BallotOneName): x = BallotPlurality(x.candidate, candidates=x.candidates) return x.restrict(candidates=candidates, priority=self.one_name_priority) if isinstance(x, BallotOrder): x = x.restrict(candidates=candidates) return BallotPlurality(x.first(priority=self.order_priority), candidates=x.candidates) if isinstance(x, Ballot): x = ConverterBallotGeneral()(x, candidates=candidates) return BallotPlurality(x.first(), candidates=x.candidates)
def __init__(self, *args, converter: ConverterBallot = None, **kwargs): """ Remark: this `__init__` must always be called at the end of the subclasses' `__init__`. """ # Parameters if converter is None: converter = ConverterBallotGeneral() self.converter = converter # Computed variables self.profile_original_ = None self.profile_converted_ = None self.candidates_ = None # Optional: load a profile at initialization if args or kwargs: self(*args, **kwargs)
def __call__(self, x: object, candidates: set = None) -> BallotLevels: x = ConverterBallotGeneral()(x, candidates=None) if isinstance(x, BallotLevels) and any( [level in self.scale.levels for level in x.values()]): if all([level in self.scale.levels for level in x.values()]): return BallotLevels(x.as_dict, scale=self.scale) else: logging.warning( 'Not all levels of ballot ``%s`` are in the scale.' % x) x = ConverterBallotToLevelsRange( scale=ScaleRange(low=0, high=len(self.scale.levels) - 1), borda_unordered_give_points=self.borda_unordered_give_points)( x, candidates=None) return BallotLevels({c: self.scale.levels[v] for c, v in x.items()}, candidates=x.candidates, scale=self.scale).restrict(candidates=candidates)
def __setitem__(self, key: int, value: object) -> None: """ Set. :param key: an integer. :param value: the new ballot or, more generally, an input that is understandable by a :class:`ConverterBallotGeneral`. Set the corresponding ballot (it does not change the weight or the voter). >>> profile = Profile(['a > b', 'b > a']) >>> profile[0] = 'a ~ b' >>> print(profile) a ~ b b > a """ self._ballots[key] = ConverterBallotGeneral()(value) self.delete_cache()
def __init__(self, ballots: Union[list, 'Profile'], weights: list = None, voters: list = None): converter = ConverterBallotGeneral() self._ballots = [converter(b) for b in ballots] if weights is None: if isinstance(ballots, Profile): weights = ballots.weights else: weights = [1] * len(ballots) else: weights = [convert_number(w) for w in weights] self._weights = weights if voters is None: if isinstance(ballots, Profile): self._voters = ballots.voters else: self._voters = [None] * len(ballots) else: self._voters = voters
def append(self, ballot: object, weight: Number = 1, voter: object = None) -> None: """ Append a ballot to the profile. :param ballot: a ballot or, more generally, an input that can be interpreted by :class:`ConverterBallotGeneral`. :param weight: the weight of the ballot. :param voter: the voter. >>> profile = Profile(['a > b']) >>> profile.append('b > a') >>> print(profile) a > b b > a """ self._ballots.append(ConverterBallotGeneral()(ballot)) self._weights.append(convert_number(weight)) self._voters.append(voter) self.delete_cache()
def __call__(self, x: object, candidates: set = None) -> BallotLevels: x = ConverterBallotGeneral()(x, candidates=None) if isinstance(x, BallotVeto): if x.candidate is None: return BallotLevels( dict(), candidates=x.candidates, scale=self.scale).restrict(candidates=candidates) return BallotLevels( { c: self.low if c == x.candidate else self.high for c in x.candidates }, candidates=x.candidates, scale=self.scale).restrict(candidates=candidates) if isinstance(x, BallotOneName): # Including Plurality if x.candidate is None: return BallotLevels( dict(), candidates=x.candidates, scale=self.scale).restrict(candidates=candidates) return BallotLevels( { c: self.high if c == x.candidate else self.low for c in x.candidates }, candidates=x.candidates, scale=self.scale).restrict(candidates=candidates) if isinstance(x, BallotLevels): if not x.scale.is_bounded: if all([isinstance(v, numbers.Number) for v in x.values()]): x_min, x_max = min(x.values()), max(x.values()) if x_min >= self.low and x_max <= self.high: return BallotLevels( x.as_dict, candidates=x.candidates, scale=ScaleInterval(low=self.low, high=self.high)).restrict( candidates=candidates) else: x = BallotLevels(x.as_dict, candidates=x.candidates, scale=ScaleInterval( low=min(x.values()), high=max(x.values()))) else: x = BallotLevels(x.as_dict, candidates=x.candidates, scale=ScaleFromSet(set(x.values()))) try: # Interpret as a cardinal ballot return BallotLevels( { c: self.low + my_division( (self.high - self.low) * (v - x.scale.low), (x.scale.high - x.scale.low)) for c, v in x.items() }, candidates=x.candidates, scale=self.scale).restrict(candidates=candidates) except (TypeError, AttributeError): x_scale = x.scale if isinstance(x_scale, ScaleFromList): return BallotLevels( { c: self.low + my_division( (self.high - self.low) * x_scale.as_dict[x[c]], (len(x_scale.levels) - 1)) for c, v in x.items() }, candidates=x.candidates, scale=self.scale).restrict(candidates=candidates) if isinstance(x, BallotOrder): borda = ScorerBorda( ballot=x, candidates=x.candidates, unordered_give_points=self.borda_unordered_give_points).scores_ score_max = len( x.candidates) - 1 if self.borda_unordered_give_points else len( x.candidates_in_b) - 1 return BallotLevels( { c: self.low + my_division( (self.high - self.low) * borda[c], score_max) for c in x.candidates_in_b }, candidates=x.candidates, scale=self.scale).restrict(candidates=candidates) raise NotImplementedError