Пример #1
0
 def test_addition(self):
     addition = self.undirected + self.undirected
     expected = SparseLR(2 * house(), [(np.ones(5), 2 * np.ones(5))])
     err = (addition.sparse_mat - expected.sparse_mat).count_nonzero()
     self.assertEqual(err, 0)
     x = np.random.rand(5)
     self.assertAlmostEqual(
         np.linalg.norm(addition.dot(x) - expected.dot(x)), 0)
class TestSparseLowRank(unittest.TestCase):
    def setUp(self):
        self.undirected = SparseLR(house(), [(np.ones(5), np.ones(5))])
        self.bipartite = SparseLR(star_wars_villains(),
                                  [(np.ones(4), np.ones(3))])

    def test_addition(self):
        addition = self.undirected + self.undirected
        expected = SparseLR(2 * house(), [(np.ones(5), 2 * np.ones(5))])
        err = (addition.sparse_mat - expected.sparse_mat).count_nonzero()
        self.assertEqual(err, 0)
        random_vector = np.random.rand(5)
        self.assertAlmostEqual(
            np.linalg.norm(
                addition.dot(random_vector) - expected.dot(random_vector)), 0)

    def test_product(self):
        prod = self.undirected.dot(np.ones(5))
        self.assertEqual(prod.shape, (5, ))
        prod = self.bipartite.dot(np.ones(3))
        self.assertEqual(np.linalg.norm(prod - np.array([5., 4., 6., 5.])), 0.)
        prod = self.bipartite.dot(0.5 * np.ones(3))
        self.assertEqual(np.linalg.norm(prod - np.array([2.5, 2., 3., 2.5])),
                         0.)

    def test_transposition(self):
        transposed = self.undirected.T
        error = (self.undirected.sparse_mat - transposed.sparse_mat).data
        self.assertEqual(abs(error).sum(), 0.)
        transposed = self.bipartite.T
        x, y = transposed.low_rank_tuples[0]
        self.assertTrue((x == np.ones(3)).all())
        self.assertTrue((y == np.ones(4)).all())

    def test_decomposition(self):
        eigenvalues, eigenvectors = randomized_eig(self.undirected,
                                                   n_components=2,
                                                   which='LM')
        self.assertEqual(eigenvalues.shape, (2, ))
        self.assertEqual(eigenvectors.shape, (5, 2))

        eigenvalues, eigenvectors = randomized_eig(self.undirected,
                                                   n_components=2,
                                                   which='SM')
        self.assertEqual(eigenvalues.shape, (2, ))
        self.assertEqual(eigenvectors.shape, (5, 2))

        left_sv, sv, right_sv = randomized_svd(self.bipartite, n_components=2)
        self.assertEqual(left_sv.shape, (4, 2))
        self.assertEqual(sv.shape, (2, ))
        self.assertEqual(right_sv.shape, (2, 3))
class TestSparseLowRank(unittest.TestCase):
    def setUp(self):
        """Simple regularized adjacency and biadjacency for tests."""
        self.undirected = SparseLR(house(), [(np.ones(5), np.ones(5))])
        self.bipartite = SparseLR(star_wars(), [(np.ones(4), np.ones(3))])

    def test_init(self):
        with self.assertRaises(ValueError):
            SparseLR(house(), [(np.ones(5), np.ones(4))])
        with self.assertRaises(ValueError):
            SparseLR(house(), [(np.ones(4), np.ones(5))])

    def test_addition(self):
        addition = self.undirected + self.undirected
        expected = SparseLR(2 * house(), [(np.ones(5), 2 * np.ones(5))])
        err = (addition.sparse_mat - expected.sparse_mat).count_nonzero()
        self.assertEqual(err, 0)
        x = np.random.rand(5)
        self.assertAlmostEqual(
            np.linalg.norm(addition.dot(x) - expected.dot(x)), 0)

    def test_operations(self):
        adjacency = self.undirected.sparse_mat
        slr = -self.undirected
        slr += adjacency
        slr -= adjacency
        slr.left_sparse_dot(adjacency)
        slr.right_sparse_dot(adjacency)
        slr.astype(float)

    def test_product(self):
        prod = self.undirected.dot(np.ones(5))
        self.assertEqual(prod.shape, (5, ))
        prod = self.bipartite.dot(np.ones(3))
        self.assertEqual(np.linalg.norm(prod - np.array([5., 4., 6., 5.])), 0.)
        prod = self.bipartite.dot(0.5 * np.ones(3))
        self.assertEqual(np.linalg.norm(prod - np.array([2.5, 2., 3., 2.5])),
                         0.)
        prod = (2 * self.bipartite).dot(0.5 * np.ones(3))
        self.assertEqual(
            np.linalg.norm(prod - 2 * np.array([2.5, 2., 3., 2.5])), 0.)

    def test_transposition(self):
        transposed = self.undirected.T
        error = (self.undirected.sparse_mat - transposed.sparse_mat).data
        self.assertEqual(abs(error).sum(), 0.)
        transposed = self.bipartite.T
        x, y = transposed.low_rank_tuples[0]
        self.assertTrue((x == np.ones(3)).all())
        self.assertTrue((y == np.ones(4)).all())
Пример #4
0
def bipartite2directed(
    biadjacency: Union[sparse.csr_matrix, SparseLR]
) -> Union[sparse.csr_matrix, SparseLR]:
    """Adjacency matrix of the directed graph associated with a bipartite graph
    (with edges from one part to the other).

    The returned adjacency matrix is:

    :math:`A  = \\begin{bmatrix} 0 & B \\\\ 0 & 0 \\end{bmatrix}`

    where :math:`B` is the biadjacency matrix.

    Parameters
    ----------
    biadjacency :
        Biadjacency matrix of the graph.

    Returns
    -------
    adjacency :
        Adjacency matrix (same format as input).
    """
    check_csr_or_slr(biadjacency)
    n_row, n_col = biadjacency.shape
    if type(biadjacency) == sparse.csr_matrix:
        adjacency = sparse.bmat(
            [[None, biadjacency], [sparse.csr_matrix((n_col, n_row)), None]],
            format='csr')
        adjacency.sort_indices()
        return adjacency
    else:
        new_tuples = [(np.hstack(
            (x, np.zeros(n_col))), np.hstack((np.zeros(n_row), y)))
                      for (x, y) in biadjacency.low_rank_tuples]
        return SparseLR(bipartite2directed(biadjacency.sparse_mat), new_tuples)
Пример #5
0
def bipartite2directed(
    biadjacency: Union[sparse.csr_matrix, SparseLR]
) -> Union[sparse.csr_matrix, SparseLR]:
    """
    Returns the adjacency matrix of the directed graph associated with a bipartite graph
    (with edges from one part to the other).

    The returned adjacency matrix is:

    :math:`A  = \\begin{bmatrix} 0 & B \\\\ 0 & 0 \\end{bmatrix}`

    where :math:`B` is the biadjacency matrix.

    Parameters
    ----------
    biadjacency:
        Biadjacency matrix of the graph.

    Returns
    -------
    Adjacency matrix.
    """
    n1, n2 = biadjacency.shape
    if type(biadjacency) == sparse.csr_matrix:
        return sparse.bmat(
            [[None, biadjacency], [sparse.csr_matrix((n2, n1)), None]],
            format='csr')
    elif type(biadjacency) == SparseLR:
        new_tuples = [(np.hstack(
            (x, np.zeros(n2))), np.hstack((np.zeros(n1), y)))
                      for (x, y) in biadjacency.low_rank_tuples]
        return SparseLR(bipartite2directed(biadjacency.sparse_mat), new_tuples)
    else:
        raise TypeError(
            'Input must be a scipy CSR matrix or a SparseLR object.')
Пример #6
0
def directed2undirected(
        adjacency: Union[sparse.csr_matrix, SparseLR],
        weighted: bool = True) -> Union[sparse.csr_matrix, SparseLR]:
    """Adjacency matrix of the undirected graph associated with some directed graph.

    The new adjacency matrix becomes either:

    :math:`A+A^T` (default)

    or

    :math:`\\max(A,A^T)`

    If the initial adjacency matrix :math:`A` is binary, bidirectional edges have weight 2
    (first method, default) or 1 (second method).

    Parameters
    ----------
    adjacency :
        Adjacency matrix.
    weighted :
        If ``True``, return the sum of the weights in both directions of each edge.

    Returns
    -------
    new_adjacency :
        New adjacency matrix (same format as input).
    """
    check_csr_or_slr(adjacency)
    if type(adjacency) == sparse.csr_matrix:
        if weighted:
            data_type = int
            if len(adjacency.data) and type(adjacency.data[0].item()) == float:
                data_type = float
            new_adjacency = adjacency.astype(data_type)
            new_adjacency += adjacency.T
        else:
            new_adjacency = (adjacency + adjacency.T).astype(bool)
        new_adjacency.tocsr().sort_indices()
        return new_adjacency
    else:
        if weighted:
            new_tuples = [(y, x) for (x, y) in adjacency.low_rank_tuples]
            return SparseLR(directed2undirected(adjacency.sparse_mat),
                            adjacency.low_rank_tuples + new_tuples)
        else:
            raise ValueError(
                'This function only works with ``weighted=True`` for SparseLR objects.'
            )
Пример #7
0
    def test_dot(self):
        n = 5
        x = np.random.randn(n)
        mat = sparse.eye(n, format='csr')
        slr = SparseLR(mat, [(np.zeros(n), np.zeros(n))])

        y1 = safe_sparse_dot(x, slr)
        y2 = safe_sparse_dot(slr, x)
        self.assertAlmostEqual(np.linalg.norm(y1 - y2), 0)

        with self.assertRaises(NotImplementedError):
            safe_sparse_dot(slr, slr)

        y1 = safe_sparse_dot(slr, mat).dot(x)
        y2 = safe_sparse_dot(mat, slr).dot(x)
        self.assertAlmostEqual(np.linalg.norm(y1 - y2), 0)
Пример #8
0
def directed2undirected(
        adjacency: Union[sparse.csr_matrix, SparseLR],
        weight_sum: bool = True) -> Union[sparse.csr_matrix, SparseLR]:
    """
    Returns the adjacency matrix of the undirected graph associated with some directed graph.

    The new adjacency matrix becomes either:

    :math:`A+A^T` (default)

    or

    :math:`\\max(A,A^T)`

    If the initial adjacency matrix :math:`A` is binary, bidirectional edges have weight 2
    (first method, default) or 1 (second method).

    Parameters
    ----------
    adjacency:
        Adjacency matrix.
    weight_sum:
        If True, return the sum of the weights in both directions of each edge.

    Returns
    -------
    New adjacency matrix (symmetric).
    """
    if type(adjacency) == sparse.csr_matrix:
        if weight_sum:
            return sparse.csr_matrix(adjacency + adjacency.T)
        else:
            return adjacency.maximum(adjacency.T)
    elif type(adjacency) == SparseLR:
        if weight_sum:
            new_tuples = [(y, x) for (x, y) in adjacency.low_rank_tuples]
            return SparseLR(directed2undirected(adjacency.sparse_mat),
                            adjacency.low_rank_tuples + new_tuples)
        else:
            raise ValueError(
                'This function only works with ``weight_sum=True`` for SparseLR objects.'
            )
    else:
        raise TypeError(
            'Input must be a scipy CSR matrix or a SparseLR object.')
Пример #9
0
def bipartite2undirected(
    biadjacency: Union[sparse.csr_matrix, SparseLR]
) -> Union[sparse.csr_matrix, SparseLR]:
    """Adjacency matrix of a bigraph defined by its biadjacency matrix.

    The returned adjacency matrix is:

    :math:`A  = \\begin{bmatrix} 0 & B \\\\ B^T & 0 \\end{bmatrix}`

    where :math:`B` is the biadjacency matrix of the bipartite graph.

    Parameters
    ----------
    biadjacency:
        Biadjacency matrix of the graph.

    Returns
    -------
    adjacency :
        Adjacency matrix (same format as input).
    """
    check_csr_or_slr(biadjacency)
    if type(biadjacency) == sparse.csr_matrix:
        adjacency = sparse.bmat([[None, biadjacency], [biadjacency.T, None]],
                                format='csr')
        adjacency.sort_indices()
        return adjacency
    else:
        n_row, n_col = biadjacency.shape
        new_tuples = []
        for (x, y) in biadjacency.low_rank_tuples:
            new_tuples.append((np.hstack(
                (x, np.zeros(n_col))), np.hstack((np.zeros(n_row), y))))
            new_tuples.append((np.hstack(
                (np.zeros(n_row), y)), np.hstack((x, np.zeros(n_col)))))
        return SparseLR(bipartite2undirected(biadjacency.sparse_mat),
                        new_tuples)
Пример #10
0
def bipartite2undirected(
    biadjacency: Union[sparse.csr_matrix, SparseLR]
) -> Union[sparse.csr_matrix, SparseLR]:
    """
    Returns the adjacency matrix of a biadjacency adjacency defined by its biadjacency matrix.

    The returned adjacency matrix is:

    :math:`A  = \\begin{bmatrix} 0 & B \\\\ B^T & 0 \\end{bmatrix}`

    where :math:`B` is the biadjacency matrix of the bipartite graph.

    Parameters
    ----------
    biadjacency:
        Biadjacency matrix of the graph.

    Returns
    -------
    Adjacency matrix (symmetric).
    """
    if type(biadjacency) == sparse.csr_matrix:
        return sparse.bmat([[None, biadjacency], [biadjacency.T, None]],
                           format='csr')
    elif type(biadjacency) == SparseLR:
        n1, n2 = biadjacency.shape
        new_tuples = []
        for (x, y) in biadjacency.low_rank_tuples:
            new_tuples.append((np.hstack(
                (x, np.zeros(n2))), np.hstack((np.zeros(n1), y))))
            new_tuples.append((np.hstack(
                (np.zeros(n1), y)), np.hstack((x, np.zeros(n2)))))
        return SparseLR(bipartite2undirected(biadjacency.sparse_mat),
                        new_tuples)
    else:
        raise TypeError(
            'Input must be a scipy CSR matrix or a SparseLR object.')
Пример #11
0
 def test_init(self):
     with self.assertRaises(ValueError):
         SparseLR(house(), [(np.ones(5), np.ones(4))])
     with self.assertRaises(ValueError):
         SparseLR(house(), [(np.ones(4), np.ones(5))])
Пример #12
0
 def setUp(self):
     """Simple regularized adjacency and biadjacency for tests."""
     self.undirected = SparseLR(house(), [(np.ones(5), np.ones(5))])
     self.bipartite = SparseLR(star_wars(), [(np.ones(4), np.ones(3))])
Пример #13
0
def randomized_eig(matrix, n_components: int, which='LM', n_oversamples: int = 10, n_iter='auto',
                   power_iteration_normalizer: Union[str, None] = 'auto', random_state=None, one_pass: bool = False):
    """Randomized eigenvalue decomposition.

    Parameters
    ----------
    matrix: ndarray or sparse matrix
        Matrix to decompose
    n_components: int
        Number of singular values and vectors to extract.
    which: str
        which eigenvalues to compute. ``'LM'`` for Largest Magnitude and ``'SM'`` for Smallest Magnitude.
        Any other entry will result in Largest Magnitude.
    n_oversamples : int (default=10)
        Additional number of random vectors to sample the range of ``matrix`` so as
        to ensure proper conditioning. The total number of random vectors
        used to find the range of ``matrix`` is ``n_components + n_oversamples``. Smaller number can improve speed
        but can negatively impact the quality of approximation of singular vectors and singular values.
    n_iter: int or 'auto' (default is 'auto')
        See :meth:`randomized_range_finder`
    power_iteration_normalizer: ``'auto'`` (default), ``'QR'``, ``'LU'``, ``None``
        See :meth:`randomized_range_finder`
    random_state: int, RandomState instance or None, optional (default=None)
        See :meth:`randomized_range_finder`
    one_pass: bool (default=False)
        whether to use algorithm 5.6 instead of 5.3. 5.6 requires less access to the original matrix,
        while 5.3 is more accurate.

    Returns
    -------
    eigenvalues: np.ndarray
    eigenvectors: np.ndarray

    References
    ----------
    Finding structure with randomness: Stochastic algorithms for constructing
    approximate matrix decompositions
    Halko, et al., 2009
    http://arxiv.org/abs/arXiv:0909.4061
    """

    random_state = check_random_state(random_state)
    n_random = n_components + n_oversamples
    n_samples, n_features = matrix.shape
    lambda_max = 0.

    if n_samples != n_features:
        raise ValueError('The input matrix is not square.')

    if which == 'SM':
        lambda_max: float = 1.1 * randomized_eig(matrix, n_components=1)[0][0]
        matrix *= -1
        if isinstance(matrix, SparseLR):
            matrix += SparseLR(lambda_max * sparse.identity(matrix.shape[0]), [])
        else:
            matrix += lambda_max * sparse.identity(matrix.shape[0])

    if n_iter == 'auto':
        # Checks if the number of iterations is explicitly specified
        # Adjust n_iter. 7 was found a good compromise for PCA. See #5299
        n_iter = 7 if n_components < .1 * min(matrix.shape) else 4

    range_matrix, random_matrix, random_proj = randomized_range_finder(matrix, n_random, n_iter,
                                                                       power_iteration_normalizer, random_state, True)
    if one_pass:
        approx_matrix = np.linalg.lstsq(random_matrix.T.dot(range_matrix), random_proj.T.dot(range_matrix), None)[0].T
    else:
        approx_matrix = (matrix.dot(range_matrix)).T.dot(range_matrix)

    eigenvalues, eigenvectors = np.linalg.eig(approx_matrix)

    del approx_matrix
    # eigenvalues indices in decreasing order
    values_order = np.argsort(eigenvalues)[::-1]
    eigenvalues = eigenvalues[values_order]
    eigenvectors = np.dot(range_matrix, eigenvectors)[:, values_order]

    if which == 'SM':
        eigenvalues = lambda_max - eigenvalues

    return eigenvalues[:n_components], eigenvectors[:, :n_components]
 def setUp(self):
     self.undirected = SparseLR(house(), [(np.ones(5), np.ones(5))])
     self.bipartite = SparseLR(star_wars_villains(),
                               [(np.ones(4), np.ones(3))])