Source code for embedded_voting.rules.singlewinner_rules.rule_svd_max

import numpy as np
import matplotlib.pyplot as plt
from embedded_voting.embeddings.embeddings import Embeddings
from embedded_voting.ratings.ratings import Ratings
from embedded_voting.rules.singlewinner_rules.rule_svd import RuleSVD
from embedded_voting.utils.cached import cached_property
from embedded_voting.utils.miscellaneous import normalize


[docs]class RuleSVDMax(RuleSVD): """ Voting rule in which the aggregated score of a candidate is the maximum singular value of his embedding matrix (cf :meth:`~embedded_voting.Embeddings.times_ratings_candidate`). Parameters ---------- square_root: boolean If True, use the square root of score in the matrix. By default, it is True. use_rank : boolean If True, consider the rank of the matrix when doing the ranking. By default, it is False. embedded_from_ratings: EmbeddingsFromRatings If no embeddings are specified in the call, this `EmbeddingsFromRatings` object is use to generate the embeddings from the ratings. Default: `EmbeddingsFromRatingsIdentity()`. Examples -------- >>> ratings = Ratings(np.array([[.5, .6, .3], [.7, 0, .2], [.2, 1, .8]])) >>> embeddings = Embeddings(np.array([[1, 1], [1, 0], [0, 1]]), norm=True) >>> election = RuleSVDMax()(ratings, embeddings) >>> election.scores_ # DOCTEST: +ELLIPSIS [1.0264274892038..., 1.1760506747094..., 0.9926782946277...] >>> election.ranking_ [1, 0, 2] >>> election.winner_ 1 >>> election.welfare_ # DOCTEST: +ELLIPSIS [0.184047317055..., 1.0, 0.0] """ def __init__(self, square_root=True, use_rank=False, embedded_from_ratings=None): super().__init__(aggregation_rule=np.max, square_root=square_root, use_rank=use_rank, embedded_from_ratings=embedded_from_ratings) def _feature(self, candidate): """ A function to get the feature vector of the candidate passed as parameter. The feature vector is defined as the singular vector associated to the maximal singular value. Parameters ---------- candidate : int The index of the candidate for which we want the feature vector. Return ------ np.ndarray The feature vector of the candidate, of length :attr:`~embedded_voting.Embeddings.n_dim`. """ if self.square_root: m_candidate = self.embeddings_.times_ratings_candidate(np.sqrt(self.ratings_[::, candidate])) else: m_candidate = self.embeddings_.times_ratings_candidate(self.ratings_[::, candidate]) _, vp, vec = np.linalg.svd(m_candidate) vec = vec[0] if vec.sum() < 0: return - vec * vp[0] else: return vec * vp[0] @cached_property def features_(self): """ A function to get the feature vectors of all the candidates. The feature vector is defined as the singular vector associated to the maximal singular value. Return ------ np.ndarray The feature vectors of all the candidates, of shape :attr:`~embedded_voting.Ratings.n_candidates`, :attr:`~embedded_voting.Embeddings.n_dim`. Examples -------- >>> ratings = Ratings(np.array([[.5, .6, .3], [.7, 0, .2], [.2, 1, .8]])) >>> embeddings = Embeddings(np.array([[1, 1], [1, 0], [0, 1]]), norm=True) >>> election = RuleSVDMax()(ratings, embeddings) >>> election.features_ array([[0.94829535, 0.39279679], [0.31392742, 1.13337759], [0.22807074, 0.96612315]]) """ return np.array([self._feature(candidate) for candidate in range(self.ratings_.n_candidates)])
[docs] def plot_features(self, plot_kind="3D", dim=None, row_size=5, show=True): """ This function plot the features vector of every 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 Number of subplots by row. By default, it is set to 5 by rows. show : bool If True, displays 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_candidate = self.ratings_.shape[1] n_rows = (n_candidate - 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_candidate): 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