Example #1
0
    def plot_features(self, plot_kind="3D", dim=None, row_size=5, show=True):
        """
        This function plot the features vector of
        all candidates in the given dimensions.

        Parameters
        ----------
        plot_kind : str
            The kind of plot we want to show.
            Can be ``'3D'`` or ``'ternary'``.
        dim : list
            The 3 dimensions we are using for our plot.
            By default, it is set to ``[0, 1, 2]``.
        row_size : int
            The number of subplots by row.
            By default, it is set to 5 plots by row.
        show : bool
            If True, plot the figure
            at the end of the function.
        """
        if dim is None:
            dim = [0, 1, 2]
        else:
            if len(dim) != 3:
                raise ValueError("The number of dimensions should be 3")

        n_candidates = self.ratings_.n_candidates
        n_rows = (n_candidates - 1) // row_size + 1
        fig = plt.figure(figsize=(row_size * 5, n_rows * 5))
        plot_position = [n_rows, row_size, 1]
        features = self.features_
        for candidate in range(n_candidates):
            ax = self.embeddings_.plot_candidate(self.ratings_,
                                                 candidate,
                                                 plot_kind=plot_kind,
                                                 dim=dim,
                                                 fig=fig,
                                                 plot_position=plot_position,
                                                 show=False)
            if plot_kind == "3D":
                x1 = features[candidate, dim[0]]
                x2 = features[candidate, dim[1]]
                x3 = features[candidate, dim[2]]
                ax.plot([0, x1], [0, x2], [0, x3], color='k', linewidth=2)
                ax.scatter([x1], [x2], [x3], color='k', s=5)
            elif plot_kind == "ternary":
                x1 = features[candidate, dim[0]]
                x2 = features[candidate, dim[2]]
                x3 = features[candidate, dim[1]]
                feature_bis = [x1, x2, x3]
                feature_bis = np.maximum(feature_bis, 0)
                size_features = np.linalg.norm(feature_bis)
                feature_bis = normalize(feature_bis)
                ax.scatter([feature_bis**2],
                           color='k',
                           s=50 * size_features + 1)
            plot_position[2] += 1

        if show:
            plt.show()  # pragma: no cover
Example #2
0
    def _plot_ternary(self, fig, dim, plot_position=None):
        """
        Plot a figure of the ratings on a 2D space
        representing the surface of the unit sphere
        on the non-negative orthant.

        Parameters
        ----------
        fig : matplotlib figure
            The figure on which we add the plot.
        dim : list
            The 3 dimensions we are using for our plot.
        plot_position : list
            The position of the plot on the figure.
            Should be of the form
            ``[n_rows, n_columns, position]``.

        Return
        ------
        matplotlib ax
            The matplotlib ax with the figure,
            if you want to add something to it.

        """
        tax = create_ternary_plot(fig, plot_position)
        for i, v in enumerate(self):
            x1 = v[dim[0]]
            x2 = v[dim[2]]
            x3 = v[dim[1]]
            vec = [x1, x2, x3]
            tax.scatter([normalize(vec)**2], color=(x1**2 * 0.8, x3**2 * 0.8, x2**2 * 0.8), alpha=0.9, s=30)

        return tax
Example #3
0
    def _get_center(self):
        """
        Return the center of the ratings, computed
        as the center of the :attr:`n_dim`-dimensional
        cube of maximal volume.

        Return
        ------
        np.ndarray
            The position of the center vector. Should be of length :attr:`n_dim`.
        """

        positions = np.array(self)
        matrix_rank = np.linalg.matrix_rank(positions)
        volume = 0
        n_voters = self.n_voters
        current_subset = list(np.arange(matrix_rank))
        mean = np.zeros(self.n_dim)
        while current_subset[0] <= n_voters - matrix_rank:
            current_embeddings = positions[current_subset, ...]
            new_volume = np.sqrt(np.linalg.det(np.dot(current_embeddings, current_embeddings.T)))
            if new_volume > volume:
                volume = new_volume
                mean = normalize(current_embeddings.sum(axis=0))
            x = 1
            while current_subset[matrix_rank - x] == n_voters - x:
                x += 1
            val = current_subset[matrix_rank - x] + 1
            while x > 0:
                current_subset[matrix_rank - x] = val
                val += 1
                x -= 1

        return mean
Example #4
0
    def __call__(self, polarisation=0.0):
        """
        Update the parameter of the parametric embeddings
        and create a new ratings.

        Parameters
        _________
        polarisation : float
            Should be between `0` and `1`.
            If it is equal to `0`, then the
            embeddings are uniformly distributed.
            If it is equal to `1`, then each voter's
            embeddings align to the dimension of its group.

        Return
        ------
        Embeddings
            The embeddings generated

        Examples
        --------
        >>> np.random.seed(42)
        >>> generator = EmbeddingsGeneratorPolarized(100, 3)
        >>> embs = generator(.8)
        >>> embs.voter_embeddings(0)
        array([0.12915167, 0.03595039, 0.99097296])
        """

        if polarisation > 1 or polarisation < 0:
            raise ValueError("Polarisation should be between 0 and 1")

        n = len(self._thetas)
        positions = np.zeros((n, self.n_dim))
        for i in range(n):
            p_1 = np.dot(self._orthogonal_profile[i],
                         self._random_profile[i]) * self._orthogonal_profile[i]
            p_2 = self._random_profile[i] - p_1
            e_2 = normalize(p_2)
            positions[i] = self._orthogonal_profile[i] * np.cos(
                self._thetas[i] *
                (1 - polarisation)) + e_2 * np.sin(self._thetas[i] *
                                                   (1 - polarisation))

        return Embeddings(positions)
Example #5
0
    def plot_scores_evolution(self, show=True):
        """
        This function plot the evolution
        of the scores of the candidates
        when the moving voters' embeddings
        are changing.

        Parameters
        ----------
        show : bool
            If True, displays the figure
            at the end of the function.

        Examples
        --------
        >>> p = MovingVoter()(SVDNash())
        >>> p.plot_scores_evolution(show=False)
        """

        tab_x = np.linspace(0, 1, 50)
        tab_y = []
        for x in tab_x:
            self.embeddings[self.moving_voter] = normalize([1 - x, x, 0])
            tab_y.append(
                self.rule(self.ratings_, self.embeddings).scores_float_)

        tab_y = np.array(tab_y).T
        name = ["Start", "End", "Orthogonal", "Consensus"]
        for i in range(self.ratings_.n_candidates):
            plt.plot(tab_x, tab_y[i], label=name[i])

        plt.title("Evolution of the score")
        plt.xlabel("X coordinate of moving voter")
        plt.ylabel("Score")
        plt.xlim(0, 1)
        plt.legend()
        if show:
            plt.show()  # pragma: no cover
Example #6
0
    def _build_profiles(self):
        """
        This function build the two embeddings
        of the parametrized embeddings (uniform and orthogonal).

        Return
        ------
        EmbeddingsGeneratorPolarized
            The object itself.
        """

        for i in range(self.n_voters):
            new_vec = np.abs(np.random.randn(self.n_dim))
            r = np.argmax(new_vec * self.prob)
            new_vec = normalize(new_vec)
            self._orthogonal_profile[i, r] = 1
            self._random_profile[i] = new_vec

            theta = np.arccos(
                np.dot(self._random_profile[i], self._orthogonal_profile[i]))
            self._thetas[i] = theta

        return self
Example #7
0
    def _plot_scores_ternary(self, sizes, fig, plot_position, dim):
        """
        Plot a figure of the ratings on a 2D space
        representing the sphere in the non-negative orthant,
        with the voters dots having the sizes passed
        as parameters.

        Parameters
        ----------
        sizes : np.ndarray
            The size of the dots.
            Should be of length :attr:`n_voters`.
        fig : matplotlib figure
            The figure on which we add the plot.
        plot_position : list
            The position of the plot on the figure. Should be of the form
            ``[n_rows, n_columns, position]``.
        dim : list
            The 3 dimensions we are using for our plot.

        Return
        ------
        matplotlib ax
            The matplotlib ax with the figure,
            if you want to add something to it.

        """
        tax = create_ternary_plot(fig, plot_position)
        for i, (v, s) in enumerate(zip(np.array(self), sizes)):
            x1 = v[dim[0]]
            x2 = v[dim[1]]
            x3 = v[dim[2]]
            vec = [x1, x2, x3]
            tax.scatter([normalize(vec)**2], color=(x1**2 * 0.8, x3**2 * 0.8, x2**2 * 0.8), alpha=0.7, s=max(s * 50, 1))

        return tax
Example #8
0
    def plot_features_evolution(self, show=True):
        """
        This function plot the evolution
        of the features of the candidates
        when the moving voters' embeddings
        are changing. Only works for :class:`SVDMax` and :class:`FeaturesRule`.

        Parameters
        ----------
        show : bool
            If True, displays the figure
            at the end of the function.

        Examples
        --------
        >>> p = MovingVoter()(SVDMax())
        >>> p.plot_features_evolution(show=False)
        """
        tab_x = np.linspace(0, 1, 20)
        tab_y = []
        for x in tab_x:
            self.embeddings[self.moving_voter] = normalize([1 - x, x, 0])
            tab_y.append(self.rule(self.ratings_, self.embeddings).features_)
        tab_y = np.array(tab_y)

        fig = plt.figure(figsize=(10, 5))

        ax = create_3D_plot(fig, position=[1, 2, 1])
        name = ["Start", "End", "Orthogonal", "Consensus"]
        for i in range(self.ratings_.n_candidates):
            vec_init = normalize(tab_y[0, i])**2
            ax.plot(tab_y[::, i, 0],
                    tab_y[::, i, 1],
                    tab_y[::, i, 2],
                    color=(vec_init[0] * 0.8, vec_init[1] * 0.8,
                           vec_init[2] * 0.8),
                    alpha=0.5,
                    label=name[i])
            for j, v in enumerate(tab_y[::, i]):
                vec_normalized = normalize(v)
                ax.plot([0, v[0]], [0, v[1]], [0, v[2]],
                        color=(vec_normalized[0] * 0.8,
                               vec_normalized[1] * 0.8,
                               vec_normalized[2] * 0.8),
                        alpha=j / 60)
        ax.set_title("Evolution of the features")
        plt.legend()

        tax = create_ternary_plot(fig, position=[1, 2, 2])
        for i in range(self.ratings_.n_candidates):
            points = [normalize(x[[0, 2, 1]])**2 for x in tab_y[::, i]]
            vec_init = normalize(tab_y[0, i, [0, 2, 1]])**2
            tax.plot(points,
                     color=(vec_init[0] * 0.8, vec_init[2] * 0.8,
                            vec_init[1] * 0.8),
                     alpha=0.8)
            tax.scatter([vec_init],
                        color=(vec_init[0] * 0.8, vec_init[2] * 0.8,
                               vec_init[1] * 0.8),
                        alpha=0.7,
                        s=50)

        if show:
            plt.show()  # pragma: no cover
Example #9
0
    def plot_weights(self, plot_kind="3D", dim=None, row_size=5, verbose=True, show=True):
        """
        This function plot the evolution of
        the voters' weights after each step of the rule.

        Parameters
        ----------
        plot_kind : str
            The kind of plot we want to show.
            Can be ``3D`` or ``ternary``.
        dim : list
            The 3 dimensions we are using for our plot.
            By default, it is set to ``[0, 1, 2]``.
        row_size : int
            Number of subplots by row.
            By default, it is set to 5.
        verbose : bool
            If True, print the total weight divided by
            the number of remaining candidates at
            the end of each step.
        show : bool
            If True, displays the figure
            at the end of the function.

        """
        ls_weight = self._ruleResults["weights_list"]
        vectors = self._ruleResults["vectors"]
        n_candidates = len(ls_weight)
        n_rows = (n_candidates - 1) // row_size + 1
        fig = plt.figure(figsize=(5 * row_size, n_rows * 5))
        plot_position = [n_rows, row_size, 1]
        if dim is None:
            dim = [0, 1, 2]
        for i in range(n_candidates):
            ax = self.embeddings.plot_scores(ls_weight[i],
                                             plot_kind=plot_kind,
                                             title="Step %i" % i,
                                             dim=dim,
                                             fig=fig,
                                             plot_position=plot_position,
                                             show=False)

            if i < n_candidates - 1:
                x1 = vectors[i][dim[0]]
                x2 = vectors[i][dim[1]]
                x3 = vectors[i][dim[2]]
                if plot_kind == "3D":
                    ax.plot([0, x1], [0, x2], [0, x3], color='k', linewidth=2)
                    ax.scatter([x1], [x2], [x3], color='k', s=5)
                elif plot_kind == "ternary":
                    feature_bis = [x1, x3, x2]
                    feature_bis = np.maximum(feature_bis, 0)
                    size_features = np.linalg.norm(feature_bis)
                    feature_bis = normalize(feature_bis)
                    ax.scatter([feature_bis ** 2], color='k', s=50*size_features+1)

            plot_position[2] += 1

        if verbose:
            sum_w = [ls_weight[i].sum() / (n_candidates - i - 1) for i in range(n_candidates - 1)]
            print("Weight / remaining candidate : ", sum_w)

        if show:
            plt.show() # pragma: no cover
Example #10
0
    def recenter(self, approx=True):
        """
        Recenter the embeddings of the
        voters so that they are the most
        possible on the non-negative orthant.

        Parameters
        ----------
        approx : bool
            If True, we compute the center of the population
            with a polynomial time algorithm. If False, we use
            an algorithm exponential in :attr:`n_dim`.

        Return
        ------
        Embeddings
            The embeddings itself.

        Examples
        --------
        >>> embs = Embeddings(-np.array([[.5,.9,.4],[.4,.7,.5],[.4,.2,.4]])).normalize()
        >>> embs
        Embeddings([[-0.45267873, -0.81482171, -0.36214298],
                    [-0.42163702, -0.73786479, -0.52704628],
                    [-0.66666667, -0.33333333, -0.66666667]])
        >>> embs.recenter()
        Embeddings([[0.40215359, 0.75125134, 0.52334875],
                    [0.56352875, 0.6747875 , 0.47654713],
                    [0.70288844, 0.24253193, 0.66867489]])
        """

        if self.n_voters < 2:
            raise ValueError("Cannot recenter a ratings with less than 2 candidates")

        positions = np.array(self)
        if approx:
            center = normalize(positions.sum(axis=0))
        else:
            center = self._get_center()

        target_center = np.ones(self.n_dim)
        target_center = normalize(target_center)
        if np.dot(center, target_center) == -1:
            new_positions = - self
        elif np.dot(center, target_center) == 1:
            new_positions = self
        else:
            orthogonal_center = center - np.dot(center, target_center)*target_center
            orthogonal_center = normalize(orthogonal_center)
            theta = -np.arccos(np.dot(center, target_center))
            rotation_matrix = np.array([[np.cos(theta), - np.sin(theta)], [np.sin(theta), np.cos(theta)]])
            new_positions = np.zeros((self.n_voters, self.n_dim))
            for i in range(self.n_voters):
                position_i = self.voter_embeddings(i)
                comp_1 = position_i.dot(target_center)
                comp_2 = position_i.dot(orthogonal_center)
                vector = [comp_1, comp_2]
                remainder = position_i - comp_1*target_center - comp_2*orthogonal_center
                new_vector = rotation_matrix.dot(vector)
                new_positions[i] = new_vector[0]*target_center + new_vector[1]*orthogonal_center + remainder

        return Embeddings(new_positions, False)
Example #11
0
    def dilate(self, approx=True):
        """
        Dilate the embeddings of the
        voters so that they take all
        the space possible in the non-negative orthant.

        Parameters
        ----------
        approx : bool
            If True, we compute the center of the population
            with a polynomial time algorithm. If False, we use
            an algorithm exponential in :attr:`n_dim`.

        Return
        ------
        Embeddings
            The embeddings itself.

        Examples
        --------
        >>> embs = Embeddings(np.array([[.5,.4,.4],[.4,.4,.5],[.4,.5,.4]])).normalize()
        >>> embs
        Embeddings([[0.66226618, 0.52981294, 0.52981294],
                    [0.52981294, 0.52981294, 0.66226618],
                    [0.52981294, 0.66226618, 0.52981294]])
        >>> embs.dilate()
        Embeddings([[0.98559856, 0.11957316, 0.11957316],
                    [0.11957316, 0.11957316, 0.98559856],
                    [0.11957316, 0.98559856, 0.11957316]])
        """

        if self.n_voters < 2:
            raise ValueError("Cannot dilate a ratings with less than 2 candidates")

        positions = np.array(self)
        if approx:
            center = normalize(positions.sum(axis=0))
        else:
            center = self._get_center()

        min_value = np.dot(self.voter_embeddings(0), center)
        for i in range(self.n_voters):
            val = np.dot(self.voter_embeddings(i), center)
            if val < min_value:
                min_value = val

        theta_max = np.arccos(min_value)
        k = np.pi / (4 * theta_max)

        new_positions = np.zeros((self.n_voters, self.n_dim))
        for i in range(self.n_voters):
            v_i = self.voter_embeddings(i)
            theta_i = np.arccos(np.dot(v_i, center))
            if theta_i == 0:
                new_positions[i] = v_i
            else:
                p_1 = np.dot(center, v_i) * center
                p_2 = v_i - p_1
                e_2 = normalize(p_2)
                new_positions[i] = center * np.cos(k * theta_i) + e_2 * np.sin(k * theta_i)

        return Embeddings(new_positions, False)