Example #1
0
 def set_profile(self, ratings, embeddings=None):
     if embeddings is not None:
         self.embeddings = Embeddings(embeddings)
     self.ratings = Ratings(ratings)
     global_rule = self.rule(self.ratings, self.embeddings)
     self.winner_ = global_rule.winner_
     self.scores_ = global_rule.scores_
     self.welfare_ = global_rule.welfare_
     self.delete_cache()
     return self
Example #2
0
 def __init__(self, ratings, embeddings, rule=None):
     self.ratings = Ratings(ratings)
     self.embeddings = Embeddings(embeddings)
     self.rule = rule
     if rule is not None:
         global_rule = self.rule(self.ratings, self.embeddings)
         self.winner_ = global_rule.winner_
         self.scores_ = global_rule.scores_
         self.welfare_ = global_rule.welfare_
     else:
         self.winner_ = None
         self.scores_ = None
         self.welfare_ = None
Example #3
0
    def __call__(self, ratings, train=None):
        """
        This function run an election using the :attr:`embedder` and the scores.

        Parameters
        ----------
        ratings: np.ndarray or list
            The matrix of scores given by the voters. ``ratings[i,j]`` corresponds to the
            score given by the voter i to candidate j.

        train: bool
            If True, we retrain the :attr:`embedder` before doing the election (using the
            data of the election).
        """
        ratings = Ratings(ratings)
        if self.ratings_history is None:
            self.ratings_history = ratings
        else:
            self.ratings_history = np.concatenate(
                [self.ratings_history, ratings], axis=1)

        if self.embeddings is None or (train is None
                                       and self.default_train) or train:
            self.train()

        self.rule.delete_cache()

        return self.rule(ratings, self.embeddings)
Example #4
0
def test_features():
    ratings = Ratings(np.array([[.5, .6, .3], [.7, 0, .2], [.2, 1, .8]]))
    embeddings = Embeddings(np.array([[1, 1, 0], [1, 0, 1], [0, 1, 0]]))
    election = FeaturesRule()(ratings, embeddings)
    election.plot_features("3D", show=False)
    election.plot_features("ternary", show=False)
    with pytest.raises(ValueError):
        election.plot_features("3D", dim=[0, 1], show=False)
Example #5
0
def test_multi():
    ratings = Ratings(np.array([[.5, .6, .3], [.7, 0, .2], [.2, 1, .8]]))
    embeddings = Embeddings(np.array([[1, 1, 0], [1, 0, 1], [0, 1, 0]]))
    election = IterSVD(k=2)(ratings, embeddings)
    election.plot_weights("3D", show=False)
    election.plot_weights("ternary", show=False)
    with pytest.raises(ValueError):
        election.plot_weights("3D", dim=[0, 1], show=False)

    election.plot_winners("3D", show=False)
Example #6
0
 def __call__(self, n_candidates=1, *args):
     self.ground_truth_ = self.generate_true_values(n_candidates=n_candidates)
     ratings = np.zeros((self.n_voters, n_candidates))
     for i in range(n_candidates):
         v_dependent_noise = np.random.multivariate_normal(
             mean=np.zeros(self.n_voters), cov=self.covariance_matrix)
         v_independent_noise = np.random.normal(
             loc=0, scale=self.independent_noise, size=self.n_voters)
         ratings[:, i] = self.ground_truth_[i] + v_dependent_noise + v_independent_noise
     return Ratings(ratings)
Example #7
0
 def __call__(self, ratings, embeddings=None, k=None):
     self.ratings = Ratings(ratings)
     if embeddings is None:
         self.embeddings = EmbeddingsFromRatingsIdentity()(self.ratings)
     else:
         self.embeddings = Embeddings(embeddings)
     if k is not None:
         self.k_ = k
     self.delete_cache()
     return self
Example #8
0
 def __call__(self, n_candidates=1, *args):
     self.ground_truth_ = self.generate_true_values(n_candidates=n_candidates)
     ratings = np.zeros((self.n_voters, n_candidates))
     for i in range(n_candidates):
         sigma_groups = np.abs(np.random.normal(loc=0, scale=self.group_noise, size=self.n_groups))
         sigma_voters = self.m_voter_group @ sigma_groups
         v_noise = np.random.multivariate_normal(
             mean=np.zeros(self.n_voters), cov=np.diag(sigma_voters))
         ratings[:, i] = self.ground_truth_[i] + v_noise
     return Ratings(ratings)
Example #9
0
    def __call__(self, ratings, embeddings=None):
        ratings = Ratings(ratings)
        modified_ratings = np.zeros(ratings.shape)
        for i in range(ratings.n_voters):
            modified_ratings[i] = self.f(ratings.voter_ratings(i))
        self.ratings_ = ratings
        self._modified_ratings = modified_ratings

        if self.embeddings_as_history or embeddings is None:
            embedder = EmbeddingsFromRatingsCorrelation()
            if embeddings is None:
                self.embeddings_ = embedder(self.ratings_)
            else:
                self.embeddings_ = embedder(np.concatenate([embeddings, self.ratings_], axis=1))
        else:
            self.embeddings_ = Embeddings(embeddings)
            self.embeddings_.n_sing_val_ = embeddings.n_sing_val_

        self.n_v = self.embeddings_.n_sing_val_ #embedder.n_sing_val_
        self.delete_cache()

        return self
Example #10
0
    def set_profile(self, ratings, embeddings=None):
        """
        This function update the ratings of voters
        on which we do the analysis.

        Parameters
        ----------
        ratings : Ratings or np.ndarray
        embeddings : Embeddings

        Return
        ------
        SingleVoterManipulation
            The object itself.
        """
        if embeddings is not None:
            self.embeddings = Embeddings(embeddings)
        self.ratings = Ratings(ratings)
        global_rule = self.rule(self.ratings, self.embeddings)
        self.winner_ = global_rule.winner_
        self.scores_ = global_rule.scores_
        self.welfare_ = global_rule.welfare_
        self.delete_cache()
        return self
    def __call__(self, ratings):
        ratings = Ratings(ratings)
        positions = (ratings.T / np.sqrt((ratings**2).sum(axis=1))).T
        n_voters, n_candidates = ratings.shape
        self.n_dim = n_candidates

        u, s, v = np.linalg.svd(positions)

        n_voters, n_candidates = positions.shape
        s = np.sqrt(s)
        s /= s.sum()
        n_v = 0
        for s_e in s:
            if s_e >= max(1 / n_voters, 1 / n_candidates):
                n_v += 1

        embeddings = Embeddings(np.dot(positions, positions.T))
        embeddings.n_sing_val_ = n_v
        return embeddings
Example #12
0
    def __call__(self, ratings, embeddings=None):
        """
        Parameters
        ----------
        ratings : Ratings or list or np.ndarray
            The ratings of voters on which we run the election.
        embeddings : Embeddings or list or np.ndarray
            The embeddings of the voters on which we run the election.

        Return
        ------
        ScoringRule
            The object itself
        """
        self.delete_cache()
        self.ratings_ = Ratings(ratings)
        if embeddings is None:
            self.embeddings_ = self.embedder(self.ratings_)
        elif embeddings is not None:
            self.embeddings_ = Embeddings(embeddings)
        return self
Example #13
0
    def __call__(self, rule, ratings=None):
        """
        This function is used to update the rule used, and also the ratings

        Parameters
        ----------
        rule: ScoringRule
            The rule we will use
        ratings: np.ndarray or Ratings
            The ratings of the candidates

        Return
        ------
        MovingVoter
            The object
        """
        self.rule = rule
        if ratings is None:
            ratings = np.array([[.8, .8, .8, .5], [.1, .1, 1, .5],
                                [.1, 1, .1, .5], [1, .1, .1, .5]])
        self.ratings_ = Ratings(ratings)
        return self
def test_embeddings():
    emb = Embeddings(np.array([[.2, .5, .3], [.3, .2, .2], [.6, .2, .3]]))
    emb.dilate(approx=False)
    emb.recenter(approx=False)

    emb = Embeddings(np.array([[1, 1, 1], [1, 1, 1]]))
    emb.dilate()

    emb = Embeddings(np.array([[1, 1]]))
    with pytest.raises(ValueError):
        emb.recenter()
    with pytest.raises(ValueError):
        emb.dilate()

    emb = Embeddings(np.array([[0, 1, 0], [1, 0, 0], [0, 0, 1]]))
    emb.recenter()
    emb.plot("3D", show=False)
    emb.plot("ternary", dim=[1, 2, 0], show=False)
    with pytest.raises(ValueError):
        emb.plot("test", show=False)
    with pytest.raises(ValueError):
        emb.plot("3D", dim=[1, 2], show=False)

    ratings = np.array([[1, .8, .5], [.6, .5, .2], [.6, .9, .5]])
    emb.plot_candidate(ratings, 0, "3D", show=False)
    emb.plot_candidate(ratings, 0, "ternary", dim=[1, 2, 0], show=False)
    with pytest.raises(ValueError):
        emb.plot_candidate(ratings, 0, "test", show=False)

    emb.plot_candidates(Ratings(ratings), "3D", show=False)
    emb.plot_candidates(ratings,
                        "ternary",
                        show=False,
                        list_titles=["c_1", "c_2", "c_3"])
    with pytest.raises(ValueError):
        emb.plot_candidates(ratings, "test", show=False)

    Embeddings(np.array([[0, -1], [-1, 0]])).recenter()
 def __call__(self, ratings):
     ratings = Ratings(ratings)
     n_dim = ratings.shape[0]
     return Embeddings(np.eye(n_dim))
 def __call__(self, ratings):
     ratings = Ratings(ratings)
     n_voters = ratings.shape[0]
     embs = np.abs(np.random.randn(n_voters, self.n_dim))
     return Embeddings(embs, norm=True)
Example #17
0
 def __init__(self, ratings, embeddings, rule=None):
     ratings = Ratings(ratings)
     super().__init__(ratings, embeddings,
                      InstantRunoffExtension(ratings.n_candidates), rule)
 def __call__(self, n_candidates, **kwargs):
     return Ratings(np.random.rand(self.n_voters, n_candidates))
Example #19
0
 def __init__(self, ratings, embeddings, rule=None):
     ratings = Ratings(ratings)
     super().__init__(ratings, embeddings,
                      BordaExtension(ratings.n_candidates, rule), rule)
Example #20
0
class SingleVoterManipulation(DeleteCacheMixin):
    """
    This general class is used for the
    analysis of the manipulability of some :class:`ScoringRule`
    by a single voter.

    For instance, what proportion of voters can
    change the result of the rule (to their advantage)
    by giving false preferences ?

    Parameters
    ----------
    ratings: Ratings or np.ndarray
        The ratings of voters to candidates
    embeddings: Embeddings
        The embeddings of the voters
    rule : ScoringRule
        The aggregation rule we want to analysis.

    Attributes
    ----------
    ratings : Profile
        The ratings of voters on which we do the analysis.
    rule : ScoringRule
        The aggregation rule we want to analysis.
    winner_ : int
        The index of the winner of the election without manipulation.
    scores_ : float list
        The scores of the candidates without manipulation.
    welfare_ : float list
        The welfares of the candidates without manipulation.

    Examples
    --------
    >>> np.random.seed(42)
    >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
    >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
    >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
    >>> manipulation = SingleVoterManipulation(ratings, embeddings, SVDNash())
    >>> manipulation.winner_
    1
    >>> manipulation.welfare_
    [0.89..., 1.0, 0.0]
    """
    def __init__(self, ratings, embeddings, rule=None):
        self.ratings = Ratings(ratings)
        self.embeddings = Embeddings(embeddings)
        self.rule = rule
        if rule is not None:
            global_rule = self.rule(self.ratings, self.embeddings)
            self.winner_ = global_rule.winner_
            self.scores_ = global_rule.scores_
            self.welfare_ = global_rule.welfare_
        else:
            self.winner_ = None
            self.scores_ = None
            self.welfare_ = None

    def __call__(self, rule):
        self.rule = rule
        global_rule = self.rule(self.ratings, self.embeddings)
        self.winner_ = global_rule.winner_
        self.scores_ = global_rule.scores_
        self.welfare_ = global_rule.welfare_
        self.delete_cache()
        return self

    def set_profile(self, ratings, embeddings=None):
        """
        This function update the ratings of voters
        on which we do the analysis.

        Parameters
        ----------
        ratings : Ratings or np.ndarray
        embeddings : Embeddings

        Return
        ------
        SingleVoterManipulation
            The object itself.
        """
        if embeddings is not None:
            self.embeddings = Embeddings(embeddings)
        self.ratings = Ratings(ratings)
        global_rule = self.rule(self.ratings, self.embeddings)
        self.winner_ = global_rule.winner_
        self.scores_ = global_rule.scores_
        self.welfare_ = global_rule.welfare_
        self.delete_cache()
        return self

    def manipulation_voter(self, i):
        """
        This function return, for the `i^th` voter,
        its favorite candidate that he can turn to
        a winner by manipulating the election.

        Parameters
        ----------
        i : int
            The index of the voter.

        Return
        ------
        int
            The index of the best candidate
            that can be elected by manipulation.
        """
        score_i = self.ratings.voter_ratings(i).copy()
        preferences_order = np.argsort(score_i)[::-1]

        # If the favorite of the voter is the winner, he will not manipulate
        if preferences_order[0] == self.winner_:
            return self.winner_

        n_candidates = self.ratings.n_candidates
        self.ratings[i] = np.ones(n_candidates)
        scores_max = self.rule(self.ratings, self.embeddings).scores_
        self.ratings[i] = np.zeros(n_candidates)
        scores_min = self.rule(self.ratings, self.embeddings).scores_
        self.ratings[i] = score_i

        all_scores = [(s, i, 1) for i, s in enumerate(scores_max)]
        all_scores += [(s, i, 0) for i, s in enumerate(scores_min)]

        all_scores.sort()
        all_scores = all_scores[::-1]

        best_manipulation = np.where(preferences_order == self.winner_)[0][0]
        for (_, i, k) in all_scores:
            if k == 0:
                break

            index_candidate = np.where(preferences_order == i)[0][0]
            if index_candidate < best_manipulation:
                best_manipulation = index_candidate

        best_manipulation = preferences_order[best_manipulation]

        return best_manipulation

    @cached_property
    def manipulation_global_(self):
        """
        This function applies the function
        :meth:`manipulation_voter` to every voter.

        Return
        ------
        int list
            The list of the best candidates that can be
            turned into the winner for each voter.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = SingleVoterManipulation(ratings, embeddings, SVDNash())
        >>> manipulation.manipulation_global_
        [1, 0, 0, 0, 1, 1, 1, 1, 1, 0]
        """
        return [
            self.manipulation_voter(i) for i in range(self.ratings.n_voters)
        ]

    @cached_property
    def prop_manipulator_(self):
        """
        This function computes the proportion
        of voters that can manipulate the election.

        Return
        ------
        float
            The proportion of voters
            that can manipulate the election.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = SingleVoterManipulation(ratings, embeddings, SVDNash())
        >>> manipulation.prop_manipulator_
        0.4
        """
        return len([x for x in self.manipulation_global_ if x != self.winner_
                    ]) / self.ratings.n_voters

    @cached_property
    def avg_welfare_(self):
        """
        The function computes the average welfare
        of the winning candidate after a voter manipulation.

        Return
        ------
        float
            The average welfare.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = SingleVoterManipulation(ratings, embeddings, SVDNash())
        >>> manipulation.avg_welfare_
        0.956...
        """
        return np.mean([self.welfare_[x] for x in self.manipulation_global_])

    @cached_property
    def worst_welfare_(self):
        """
        This function computes the worst possible welfare
        achievable by single voter manipulation.

        Return
        ------
        float
            The worst welfare.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = SingleVoterManipulation(ratings, embeddings, SVDNash())
        >>> manipulation.worst_welfare_
        0.891...
        """
        return np.min([self.welfare_[x] for x in self.manipulation_global_])

    @cached_property
    def is_manipulable_(self):
        """
        This function quickly computes
        if the ratings is manipulable or not.

        Return
        ------
        bool
            If True, the ratings is
            manipulable by a single voter.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = SingleVoterManipulation(ratings, embeddings, SVDNash())
        >>> manipulation.is_manipulable_
        True
        """
        for i in range(self.ratings.n_voters):
            if self.manipulation_voter(i) != self.winner_:
                return True
        return False

    def manipulation_map(self, map_size=20, scores_matrix=None, show=True):
        """
        A function to plot the manipulability
        of the ratings when the ``polarisation`` and the ``coherence``
        of the :class:`ParametricProfile` vary.
        The number of voters, dimensions, and candidates
        are those of the :attr:`profile_`.

        Parameters
        ----------
        map_size : int
            The number of different ``coherence``
            and ``polarisation`` parameters tested.
            The total number of test is `map_size` `^2`.
        scores_matrix : np.ndarray
            Matrix of shape :attr:`~embedded_voting.Profile.embeddings.n_dim`,
            :attr:`~embedded_voting.Profile.n_candidates` containing
            the scores given by each group.
            More precisely, `scores_matrix[i,j]` is the score given by the group
            represented by the dimension `i` to the candidate `j`.
            If None specified, a new matrix is generated for each test.
        show : bool
            If True, display the manipulation maps
            at the end of the function.

        Return
        ------
        dict
            The manipulation maps :
            ``manipulator`` for the proportion of manipulator,
            ``worst_welfare`` and ``avg_welfare``
            for the welfare maps.

        Examples
        --------
        >>> np.random.seed(42)
        >>> emb = EmbeddingsGeneratorPolarized(100, 3)(0)
        >>> rat = RatingsFromEmbeddingsCorrelated(5, 3)(emb)
        >>> manipulation = SingleVoterManipulation(rat, emb, rule=SVDNash())
        >>> maps = manipulation.manipulation_map(map_size=5, show=False)
        >>> maps['manipulator']
        array([[0.02, 0.49, 0.  , 0.  , 0.  ],
               [0.  , 0.  , 0.  , 0.  , 0.  ],
               [0.54, 0.  , 0.  , 0.4 , 0.  ],
               [0.01, 0.51, 0.  , 0.  , 0.  ],
               [0.  , 0.  , 0.  , 0.36, 0.  ]])
        """

        manipulator = np.zeros((map_size, map_size))
        worst_welfare = np.zeros((map_size, map_size))
        avg_welfare = np.zeros((map_size, map_size))

        n_voters, n_candidates = self.ratings.shape
        n_dim = self.embeddings.n_dim

        embeddings_generator = EmbeddingsGeneratorPolarized(n_voters, n_dim)
        ratings_generator = RatingsFromEmbeddingsCorrelated(
            n_candidates, n_dim)

        if scores_matrix is not None:
            ratings_generator.set_scores(scores_matrix)

        for i in range(map_size):
            for j in range(map_size):
                if scores_matrix is None:
                    ratings_generator.set_scores()
                embeddings = embeddings_generator(polarisation=i /
                                                  (map_size - 1))
                ratings = ratings_generator(embeddings,
                                            coherence=j / (map_size - 1))
                self.set_profile(ratings, embeddings)
                manipulator[i, j] = self.prop_manipulator_
                worst_welfare[i, j] = self.worst_welfare_
                avg_welfare[i, j] = self.avg_welfare_

        if show:
            fig = plt.figure(figsize=(15, 5))

            create_map_plot(fig, manipulator, [1, 3, 1],
                            "Proportion of manipulators")
            create_map_plot(fig, avg_welfare, [1, 3, 2], "Average welfare")
            create_map_plot(fig, worst_welfare, [1, 3, 3], "Worst welfare")

            plt.show()

        return {
            "manipulator": manipulator,
            "worst_welfare": worst_welfare,
            "avg_welfare": avg_welfare
        }
Example #21
0
 def __init__(self, ratings, embeddings, k=2, rule=None):
     ratings = Ratings(ratings)
     super().__init__(ratings, embeddings,
                      KApprovalExtension(ratings.n_candidates, k=k), rule)
 def __call__(self, n_candidates=1, *args):
     self.ground_truth_ = self.generate_true_values(
         n_candidates=n_candidates)
     m_noises_candidates = np.random.randn(self.n_noises, n_candidates)
     return Ratings(self.ground_truth_[np.newaxis, :] +
                    self.m_voters_noises @ m_noises_candidates)
Example #23
0
 def __call__(self, ratings, embeddings=None):
     ratings = Ratings(ratings)
     self.aggregation_rule = lambda x: np.sum(np.log(1+x*ratings.n_voters))
     super().__call__(ratings, embeddings)
     return self
Example #24
0
class ManipulationCoalition(DeleteCacheMixin):
    """
    This general class is used for the analysis of
    the manipulability of the rule by a coalition of voter.

    It only look if there is a trivial
    manipulation by a coalition of voter.
    That means, for some candidate `c`
    different than the current winner `w`,
    gather every voter who prefers `c` to `w`,
    and ask them to put `c` first and `w` last.
    If `c` is the new winner, then
    the ratings can be manipulated.

    Parameters
    ----------
    ratings: Ratings or np.ndarray
        The ratings of voters to candidates
    embeddings: Embeddings
        The embeddings of the voters
    rule : ScoringRule
        The aggregation rule we want to analysis.

    Attributes
    ----------
    ratings : Profile
        The ratings of voter on which we do the analysis.
    rule : ScoringRule
        The aggregation rule we want to analysis.
    winner_ : int
        The index of the winner of the election without manipulation.
    scores_ : float list
        The scores of the candidates without manipulation.
    welfare_ : float list
        The welfare of the candidates without manipulation.

    Examples
    --------
    >>> np.random.seed(42)
    >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
    >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
    >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
    >>> manipulation = ManipulationCoalition(ratings, embeddings, SVDNash())
    >>> manipulation.winner_
    1
    >>> manipulation.welfare_
    [0.89..., 1.0, 0.0]

    """
    def __init__(self, ratings, embeddings, rule=None):
        self.ratings = Ratings(ratings)
        self.embeddings = Embeddings(embeddings)
        self.rule = rule
        if rule is not None:
            global_rule = self.rule(self.ratings, self.embeddings)
            self.winner_ = global_rule.winner_
            self.scores_ = global_rule.scores_
            self.welfare_ = global_rule.welfare_
        else:
            self.winner_ = None
            self.scores_ = None
            self.welfare_ = None

    def __call__(self, rule):
        self.rule = rule
        global_rule = self.rule(self.ratings, self.embeddings)
        self.winner_ = global_rule.winner_
        self.scores_ = global_rule.scores_
        self.welfare_ = global_rule.welfare_
        self.delete_cache()
        return self

    def set_profile(self, ratings, embeddings=None):
        if embeddings is not None:
            self.embeddings = Embeddings(embeddings)
        self.ratings = Ratings(ratings)
        global_rule = self.rule(self.ratings, self.embeddings)
        self.winner_ = global_rule.winner_
        self.scores_ = global_rule.scores_
        self.welfare_ = global_rule.welfare_
        self.delete_cache()
        return self

    def trivial_manipulation(self, candidate, verbose=False):
        """
        This function computes if
        a trivial manipulation is
        possible for the candidate
        passed as parameter.

        Parameters
        ----------
        candidate : int
            The index of the candidate
            for which we manipulate.
        verbose : bool
            Verbose mode.
            By default, is set to False.

        Return
        ------
        bool
            If True, the ratings is manipulable
            for this candidate.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = ManipulationCoalition(ratings, embeddings, SVDNash())
        >>> manipulation.trivial_manipulation(0, verbose=True)
        2 voters interested to elect 0 instead of 1
        Winner is 0
        True
        """

        voters_interested = []
        for i in range(self.ratings.shape[1]):
            score_i = self.ratings[i]
            if score_i[self.winner_] < score_i[candidate]:
                voters_interested.append(i)

        if verbose:
            print("%i voters interested to elect %i instead of %i" %
                  (len(voters_interested), candidate, self.winner_))

        profile = self.ratings.copy()
        for i in voters_interested:
            profile[i] = np.zeros(self.ratings.shape[1])
            profile[i][candidate] = 1

        new_winner = self.rule(profile, self.embeddings).winner_

        if verbose:
            print("Winner is %i" % new_winner)

        return new_winner == candidate

    @cached_property
    def is_manipulable_(self):
        """
        A function that quickly computes
        if the ratings is manipulable.

        Return
        ------
        bool
            If True, the ratings is
            manipulable for some candidate.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = ManipulationCoalition(ratings, embeddings, SVDNash())
        >>> manipulation.is_manipulable_
        True
        """

        for i in range(self.ratings.n_candidates):
            if i == self.winner_:
                continue
            if self.trivial_manipulation(i):
                return True
        return False

    @cached_property
    def worst_welfare_(self):
        """
        A function that compute the worst
        welfare attainable by coalition manipulation.

        Return
        ------
        float
            The worst welfare.

        Examples
        --------
        >>> np.random.seed(42)
        >>> scores_matrix = [[1, .2, 0], [.5, .6, .9], [.1, .8, .3]]
        >>> embeddings = EmbeddingsGeneratorPolarized(10, 3)(.8)
        >>> ratings = RatingsFromEmbeddingsCorrelated(3, 3, scores_matrix)(embeddings, .8)
        >>> manipulation = ManipulationCoalition(ratings, embeddings, SVDNash())
        >>> manipulation.worst_welfare_
        0.0
        """
        worst_welfare = self.welfare_[self.winner_]
        for i in range(self.ratings.n_candidates):
            if i == self.winner_:
                continue
            if self.trivial_manipulation(i):
                worst_welfare = min(worst_welfare, self.welfare_[i])
        return worst_welfare

    def manipulation_map(self, map_size=20, scores_matrix=None, show=True):
        """
        A function to plot the manipulability
        of the ratings when the
        ``polarisation`` and the ``coherence`` vary.

        Parameters
        ----------
        map_size : int
            The number of different coherence and polarisation parameters tested.
            The total number of test is `map_size` ^2.
        scores_matrix : np.ndarray
            Matrix of shape :attr:`~embedded_voting.Profile.n_dim`,
            :attr:`~embedded_voting.Profile.n_candidates`
            containing the scores given by each group.
            More precisely, `scores_matrix[i,j]` is the
            score given by the group represented by
            the dimension `i` to the candidate `j`.
            If not specified, a new matrix is
            generated for each test.
        show : bool
            If True, displays the manipulation
            maps at the end of the function.

        Return
        ------
        dict
            The manipulation maps :
            ``manipulator`` for the proportion of manipulator,
            ``worst_welfare`` and ``avg_welfare``
            for the welfare maps.

        Examples
        --------
        >>> np.random.seed(42)
        >>> emb = EmbeddingsGeneratorPolarized(100, 3)(0)
        >>> rat = RatingsFromEmbeddingsCorrelated(5, 3)(emb)
        >>> manipulation = ManipulationCoalition(rat, emb, SVDNash())
        >>> maps = manipulation.manipulation_map(map_size=5, show=False)
        >>> maps['worst_welfare']
        array([[1.        , 0.98024051, 1.        , 1.        , 1.        ],
               [1.        , 1.        , 1.        , 1.        , 1.        ],
               [0.87833419, 1.        , 1.        , 0.97819913, 1.        ],
               [0.80173984, 0.74016286, 1.        , 1.        , 1.        ],
               [0.81466796, 1.        , 1.        , 0.92431457, 1.        ]])
        """

        manipulator_map = np.zeros((map_size, map_size))
        worst_welfare_map = np.zeros((map_size, map_size))

        n_voters, n_candidates = self.ratings.shape
        n_dim = self.embeddings.n_dim

        embeddings_generator = EmbeddingsGeneratorPolarized(n_voters, n_dim)
        ratings_generator = RatingsFromEmbeddingsCorrelated(
            n_candidates, n_dim)

        if scores_matrix is not None:
            ratings_generator.set_scores(scores_matrix)
        for i in range(map_size):
            for j in range(map_size):
                if scores_matrix is None:
                    ratings_generator.set_scores()

                embeddings = embeddings_generator(polarisation=i /
                                                  (map_size - 1))
                ratings = ratings_generator(embeddings,
                                            coherence=j / (map_size - 1))
                self.set_profile(ratings, embeddings)

                worst_welfare = self.welfare_[self.winner_]
                is_manipulable = 0
                for candidate in range(self.ratings.n_candidates):
                    if candidate == self.winner_:
                        continue
                    if self.trivial_manipulation(candidate):
                        is_manipulable = 1
                        worst_welfare = min(worst_welfare,
                                            self.welfare_[candidate])

                manipulator_map[i, j] = is_manipulable
                worst_welfare_map[i, j] = worst_welfare

        if show:
            fig = plt.figure(figsize=(10, 5))

            create_map_plot(fig, manipulator_map, [1, 2, 1],
                            "Proportion of manipulators")
            create_map_plot(fig, worst_welfare_map, [1, 2, 2], "Worst welfare")

            plt.show()

        return {
            "manipulator": manipulator_map,
            "worst_welfare": worst_welfare_map
        }