scikit-network 0.33.3__cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of scikit-network might be problematic. Click here for more details.
- scikit_network-0.33.3.dist-info/METADATA +122 -0
- scikit_network-0.33.3.dist-info/RECORD +229 -0
- scikit_network-0.33.3.dist-info/WHEEL +6 -0
- scikit_network-0.33.3.dist-info/licenses/AUTHORS.rst +43 -0
- scikit_network-0.33.3.dist-info/licenses/LICENSE +34 -0
- scikit_network-0.33.3.dist-info/top_level.txt +1 -0
- scikit_network.libs/libgomp-d22c30c5.so.1.0.0 +0 -0
- sknetwork/__init__.py +21 -0
- sknetwork/base.py +67 -0
- sknetwork/classification/__init__.py +8 -0
- sknetwork/classification/base.py +142 -0
- sknetwork/classification/base_rank.py +133 -0
- sknetwork/classification/diffusion.py +134 -0
- sknetwork/classification/knn.py +139 -0
- sknetwork/classification/metrics.py +205 -0
- sknetwork/classification/pagerank.py +66 -0
- sknetwork/classification/propagation.py +152 -0
- sknetwork/classification/tests/__init__.py +1 -0
- sknetwork/classification/tests/test_API.py +30 -0
- sknetwork/classification/tests/test_diffusion.py +77 -0
- sknetwork/classification/tests/test_knn.py +23 -0
- sknetwork/classification/tests/test_metrics.py +53 -0
- sknetwork/classification/tests/test_pagerank.py +20 -0
- sknetwork/classification/tests/test_propagation.py +24 -0
- sknetwork/classification/vote.cpp +27587 -0
- sknetwork/classification/vote.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/classification/vote.pyx +56 -0
- sknetwork/clustering/__init__.py +8 -0
- sknetwork/clustering/base.py +172 -0
- sknetwork/clustering/kcenters.py +253 -0
- sknetwork/clustering/leiden.py +242 -0
- sknetwork/clustering/leiden_core.cpp +31578 -0
- sknetwork/clustering/leiden_core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/clustering/leiden_core.pyx +124 -0
- sknetwork/clustering/louvain.py +286 -0
- sknetwork/clustering/louvain_core.cpp +31223 -0
- sknetwork/clustering/louvain_core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/clustering/louvain_core.pyx +124 -0
- sknetwork/clustering/metrics.py +91 -0
- sknetwork/clustering/postprocess.py +66 -0
- sknetwork/clustering/propagation_clustering.py +104 -0
- sknetwork/clustering/tests/__init__.py +1 -0
- sknetwork/clustering/tests/test_API.py +38 -0
- sknetwork/clustering/tests/test_kcenters.py +60 -0
- sknetwork/clustering/tests/test_leiden.py +34 -0
- sknetwork/clustering/tests/test_louvain.py +135 -0
- sknetwork/clustering/tests/test_metrics.py +50 -0
- sknetwork/clustering/tests/test_postprocess.py +39 -0
- sknetwork/data/__init__.py +6 -0
- sknetwork/data/base.py +33 -0
- sknetwork/data/load.py +406 -0
- sknetwork/data/models.py +459 -0
- sknetwork/data/parse.py +644 -0
- sknetwork/data/test_graphs.py +84 -0
- sknetwork/data/tests/__init__.py +1 -0
- sknetwork/data/tests/test_API.py +30 -0
- sknetwork/data/tests/test_base.py +14 -0
- sknetwork/data/tests/test_load.py +95 -0
- sknetwork/data/tests/test_models.py +52 -0
- sknetwork/data/tests/test_parse.py +250 -0
- sknetwork/data/tests/test_test_graphs.py +29 -0
- sknetwork/data/tests/test_toy_graphs.py +68 -0
- sknetwork/data/timeout.py +38 -0
- sknetwork/data/toy_graphs.py +611 -0
- sknetwork/embedding/__init__.py +8 -0
- sknetwork/embedding/base.py +94 -0
- sknetwork/embedding/force_atlas.py +198 -0
- sknetwork/embedding/louvain_embedding.py +148 -0
- sknetwork/embedding/random_projection.py +135 -0
- sknetwork/embedding/spectral.py +141 -0
- sknetwork/embedding/spring.py +198 -0
- sknetwork/embedding/svd.py +359 -0
- sknetwork/embedding/tests/__init__.py +1 -0
- sknetwork/embedding/tests/test_API.py +49 -0
- sknetwork/embedding/tests/test_force_atlas.py +35 -0
- sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
- sknetwork/embedding/tests/test_random_projection.py +28 -0
- sknetwork/embedding/tests/test_spectral.py +81 -0
- sknetwork/embedding/tests/test_spring.py +50 -0
- sknetwork/embedding/tests/test_svd.py +43 -0
- sknetwork/gnn/__init__.py +10 -0
- sknetwork/gnn/activation.py +117 -0
- sknetwork/gnn/base.py +181 -0
- sknetwork/gnn/base_activation.py +90 -0
- sknetwork/gnn/base_layer.py +109 -0
- sknetwork/gnn/gnn_classifier.py +305 -0
- sknetwork/gnn/layer.py +153 -0
- sknetwork/gnn/loss.py +180 -0
- sknetwork/gnn/neighbor_sampler.py +65 -0
- sknetwork/gnn/optimizer.py +164 -0
- sknetwork/gnn/tests/__init__.py +1 -0
- sknetwork/gnn/tests/test_activation.py +56 -0
- sknetwork/gnn/tests/test_base.py +75 -0
- sknetwork/gnn/tests/test_base_layer.py +37 -0
- sknetwork/gnn/tests/test_gnn_classifier.py +130 -0
- sknetwork/gnn/tests/test_layers.py +80 -0
- sknetwork/gnn/tests/test_loss.py +33 -0
- sknetwork/gnn/tests/test_neigh_sampler.py +23 -0
- sknetwork/gnn/tests/test_optimizer.py +43 -0
- sknetwork/gnn/tests/test_utils.py +41 -0
- sknetwork/gnn/utils.py +127 -0
- sknetwork/hierarchy/__init__.py +6 -0
- sknetwork/hierarchy/base.py +96 -0
- sknetwork/hierarchy/louvain_hierarchy.py +272 -0
- sknetwork/hierarchy/metrics.py +234 -0
- sknetwork/hierarchy/paris.cpp +37871 -0
- sknetwork/hierarchy/paris.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/hierarchy/paris.pyx +316 -0
- sknetwork/hierarchy/postprocess.py +350 -0
- sknetwork/hierarchy/tests/__init__.py +1 -0
- sknetwork/hierarchy/tests/test_API.py +24 -0
- sknetwork/hierarchy/tests/test_algos.py +34 -0
- sknetwork/hierarchy/tests/test_metrics.py +62 -0
- sknetwork/hierarchy/tests/test_postprocess.py +57 -0
- sknetwork/linalg/__init__.py +9 -0
- sknetwork/linalg/basics.py +37 -0
- sknetwork/linalg/diteration.cpp +27403 -0
- sknetwork/linalg/diteration.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/linalg/diteration.pyx +47 -0
- sknetwork/linalg/eig_solver.py +93 -0
- sknetwork/linalg/laplacian.py +15 -0
- sknetwork/linalg/normalizer.py +86 -0
- sknetwork/linalg/operators.py +225 -0
- sknetwork/linalg/polynome.py +76 -0
- sknetwork/linalg/ppr_solver.py +170 -0
- sknetwork/linalg/push.cpp +31075 -0
- sknetwork/linalg/push.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/linalg/push.pyx +71 -0
- sknetwork/linalg/sparse_lowrank.py +142 -0
- sknetwork/linalg/svd_solver.py +91 -0
- sknetwork/linalg/tests/__init__.py +1 -0
- sknetwork/linalg/tests/test_eig.py +44 -0
- sknetwork/linalg/tests/test_laplacian.py +18 -0
- sknetwork/linalg/tests/test_normalization.py +34 -0
- sknetwork/linalg/tests/test_operators.py +66 -0
- sknetwork/linalg/tests/test_polynome.py +38 -0
- sknetwork/linalg/tests/test_ppr.py +50 -0
- sknetwork/linalg/tests/test_sparse_lowrank.py +61 -0
- sknetwork/linalg/tests/test_svd.py +38 -0
- sknetwork/linkpred/__init__.py +2 -0
- sknetwork/linkpred/base.py +46 -0
- sknetwork/linkpred/nn.py +126 -0
- sknetwork/linkpred/tests/__init__.py +1 -0
- sknetwork/linkpred/tests/test_nn.py +27 -0
- sknetwork/log.py +19 -0
- sknetwork/path/__init__.py +5 -0
- sknetwork/path/dag.py +54 -0
- sknetwork/path/distances.py +98 -0
- sknetwork/path/search.py +31 -0
- sknetwork/path/shortest_path.py +61 -0
- sknetwork/path/tests/__init__.py +1 -0
- sknetwork/path/tests/test_dag.py +37 -0
- sknetwork/path/tests/test_distances.py +62 -0
- sknetwork/path/tests/test_search.py +40 -0
- sknetwork/path/tests/test_shortest_path.py +40 -0
- sknetwork/ranking/__init__.py +8 -0
- sknetwork/ranking/base.py +61 -0
- sknetwork/ranking/betweenness.cpp +9710 -0
- sknetwork/ranking/betweenness.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/ranking/betweenness.pyx +97 -0
- sknetwork/ranking/closeness.py +92 -0
- sknetwork/ranking/hits.py +94 -0
- sknetwork/ranking/katz.py +83 -0
- sknetwork/ranking/pagerank.py +110 -0
- sknetwork/ranking/postprocess.py +37 -0
- sknetwork/ranking/tests/__init__.py +1 -0
- sknetwork/ranking/tests/test_API.py +32 -0
- sknetwork/ranking/tests/test_betweenness.py +38 -0
- sknetwork/ranking/tests/test_closeness.py +30 -0
- sknetwork/ranking/tests/test_hits.py +20 -0
- sknetwork/ranking/tests/test_pagerank.py +62 -0
- sknetwork/ranking/tests/test_postprocess.py +26 -0
- sknetwork/regression/__init__.py +4 -0
- sknetwork/regression/base.py +61 -0
- sknetwork/regression/diffusion.py +210 -0
- sknetwork/regression/tests/__init__.py +1 -0
- sknetwork/regression/tests/test_API.py +32 -0
- sknetwork/regression/tests/test_diffusion.py +56 -0
- sknetwork/sknetwork.py +3 -0
- sknetwork/test_base.py +35 -0
- sknetwork/test_log.py +15 -0
- sknetwork/topology/__init__.py +8 -0
- sknetwork/topology/cliques.cpp +32568 -0
- sknetwork/topology/cliques.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/cliques.pyx +149 -0
- sknetwork/topology/core.cpp +30654 -0
- sknetwork/topology/core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/core.pyx +90 -0
- sknetwork/topology/cycles.py +243 -0
- sknetwork/topology/minheap.cpp +27335 -0
- sknetwork/topology/minheap.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/minheap.pxd +20 -0
- sknetwork/topology/minheap.pyx +109 -0
- sknetwork/topology/structure.py +194 -0
- sknetwork/topology/tests/__init__.py +1 -0
- sknetwork/topology/tests/test_cliques.py +28 -0
- sknetwork/topology/tests/test_core.py +19 -0
- sknetwork/topology/tests/test_cycles.py +65 -0
- sknetwork/topology/tests/test_structure.py +85 -0
- sknetwork/topology/tests/test_triangles.py +38 -0
- sknetwork/topology/tests/test_wl.py +72 -0
- sknetwork/topology/triangles.cpp +8897 -0
- sknetwork/topology/triangles.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/triangles.pyx +151 -0
- sknetwork/topology/weisfeiler_lehman.py +133 -0
- sknetwork/topology/weisfeiler_lehman_core.cpp +27638 -0
- sknetwork/topology/weisfeiler_lehman_core.cpython-313-aarch64-linux-gnu.so +0 -0
- sknetwork/topology/weisfeiler_lehman_core.pyx +114 -0
- sknetwork/utils/__init__.py +7 -0
- sknetwork/utils/check.py +355 -0
- sknetwork/utils/format.py +221 -0
- sknetwork/utils/membership.py +82 -0
- sknetwork/utils/neighbors.py +115 -0
- sknetwork/utils/tests/__init__.py +1 -0
- sknetwork/utils/tests/test_check.py +190 -0
- sknetwork/utils/tests/test_format.py +63 -0
- sknetwork/utils/tests/test_membership.py +24 -0
- sknetwork/utils/tests/test_neighbors.py +41 -0
- sknetwork/utils/tests/test_tfidf.py +18 -0
- sknetwork/utils/tests/test_values.py +66 -0
- sknetwork/utils/tfidf.py +37 -0
- sknetwork/utils/values.py +76 -0
- sknetwork/visualization/__init__.py +4 -0
- sknetwork/visualization/colors.py +34 -0
- sknetwork/visualization/dendrograms.py +277 -0
- sknetwork/visualization/graphs.py +1039 -0
- sknetwork/visualization/tests/__init__.py +1 -0
- sknetwork/visualization/tests/test_dendrograms.py +53 -0
- sknetwork/visualization/tests/test_graphs.py +176 -0
|
Binary file
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# distutils: language = c++
|
|
2
|
+
# cython: language_level=3
|
|
3
|
+
"""
|
|
4
|
+
Created on Mars 2021
|
|
5
|
+
@author: Wenzhuo Zhao <wenzhuo.zhao@etu.sorbonne-universite.fr>
|
|
6
|
+
"""
|
|
7
|
+
from libcpp.queue cimport queue
|
|
8
|
+
from cython.parallel cimport prange
|
|
9
|
+
import numpy as np
|
|
10
|
+
cimport numpy as cnp
|
|
11
|
+
cimport cython
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@cython.boundscheck(False)
|
|
15
|
+
@cython.wraparound(False)
|
|
16
|
+
def push_pagerank(int n, cnp.ndarray[cnp.int32_t, ndim=1] degrees,
|
|
17
|
+
int[:] indptr, int[:] indices,
|
|
18
|
+
int[:] rev_indptr, int[:] rev_indices,
|
|
19
|
+
cnp.ndarray[cnp.float32_t, ndim=1] seeds,
|
|
20
|
+
cnp.float32_t damping_factor, cnp.float32_t tol):
|
|
21
|
+
"""Push-based PageRank"""
|
|
22
|
+
cdef cnp.ndarray[cnp.float32_t, ndim=1] residuals
|
|
23
|
+
cdef int vertex
|
|
24
|
+
cdef int neighbor
|
|
25
|
+
cdef int j1
|
|
26
|
+
cdef int j2
|
|
27
|
+
cdef int j
|
|
28
|
+
cdef int[:] indexes
|
|
29
|
+
cdef int index
|
|
30
|
+
cdef float probability
|
|
31
|
+
cdef queue[int] worklist
|
|
32
|
+
cdef cnp.ndarray[cnp.float32_t, ndim=1] scores
|
|
33
|
+
cdef cnp.float32_t tmp
|
|
34
|
+
cdef float norm
|
|
35
|
+
|
|
36
|
+
residuals = np.zeros(n, dtype=np.float32)
|
|
37
|
+
for vertex in prange(n, nogil=True):
|
|
38
|
+
j1 = rev_indptr[vertex]
|
|
39
|
+
j2 = rev_indptr[vertex + 1]
|
|
40
|
+
# iterate node's in-coming neighbors
|
|
41
|
+
for j in range(j1, j2):
|
|
42
|
+
neighbor = rev_indices[j]
|
|
43
|
+
residuals[vertex] += 1 / degrees[neighbor]
|
|
44
|
+
"""add the probability of seeds"""
|
|
45
|
+
residuals[vertex] *= (1 - damping_factor) * \
|
|
46
|
+
damping_factor * (1 + seeds[vertex])
|
|
47
|
+
|
|
48
|
+
# node with high residual value will be processed first
|
|
49
|
+
indexes = np.argsort(-residuals).astype(np.int32)
|
|
50
|
+
for index in indexes:
|
|
51
|
+
worklist.push(index)
|
|
52
|
+
scores = np.full(n, (1 - damping_factor), dtype=np.float32)
|
|
53
|
+
|
|
54
|
+
while not worklist.empty():
|
|
55
|
+
vertex = worklist.front()
|
|
56
|
+
worklist.pop()
|
|
57
|
+
# scores[v]_new
|
|
58
|
+
scores[vertex] += residuals[vertex]
|
|
59
|
+
# iterate node's out-coming neighbors
|
|
60
|
+
j1 = indptr[vertex]
|
|
61
|
+
j2 = indptr[vertex + 1]
|
|
62
|
+
for j in prange(j1, j2, nogil=True):
|
|
63
|
+
neighbor = indices[j]
|
|
64
|
+
tmp = residuals[neighbor]
|
|
65
|
+
residuals[neighbor] += residuals[vertex] * \
|
|
66
|
+
(1 - damping_factor) / degrees[vertex]
|
|
67
|
+
if residuals[neighbor] > tol > tmp:
|
|
68
|
+
worklist.push(neighbor)
|
|
69
|
+
norm = np.linalg.norm(scores, 1)
|
|
70
|
+
scores /= norm
|
|
71
|
+
return scores
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Apr 19 2019
|
|
5
|
+
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
6
|
+
"""
|
|
7
|
+
from typing import Union, Tuple
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from scipy import sparse
|
|
11
|
+
from scipy.sparse.linalg import LinearOperator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SparseLR(LinearOperator):
|
|
15
|
+
"""Class for matrices with "sparse + low rank" structure.
|
|
16
|
+
Example:
|
|
17
|
+
|
|
18
|
+
:math:`A + xy^T`
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
sparse_mat: scipy.spmatrix
|
|
23
|
+
Sparse component. Is converted to csr format automatically.
|
|
24
|
+
low_rank_tuples: list
|
|
25
|
+
Single tuple of arrays of list of tuples, representing the low rank components [(x1, y1), (x2, y2),...].
|
|
26
|
+
Each low rank component is of the form :math:`xy^T`.
|
|
27
|
+
|
|
28
|
+
Examples
|
|
29
|
+
--------
|
|
30
|
+
>>> from scipy import sparse
|
|
31
|
+
>>> from sknetwork.linalg import SparseLR
|
|
32
|
+
>>> adjacency = sparse.eye(2, format='csr')
|
|
33
|
+
>>> slr = SparseLR(adjacency, (np.ones(2), np.ones(2)))
|
|
34
|
+
>>> x = np.ones(2)
|
|
35
|
+
>>> slr.dot(x)
|
|
36
|
+
array([3., 3.])
|
|
37
|
+
>>> slr.sum(axis=0)
|
|
38
|
+
array([3., 3.])
|
|
39
|
+
>>> slr.sum(axis=1)
|
|
40
|
+
array([3., 3.])
|
|
41
|
+
>>> float(slr.sum())
|
|
42
|
+
6.0
|
|
43
|
+
|
|
44
|
+
References
|
|
45
|
+
----------
|
|
46
|
+
De Lara (2019). `The Sparse + Low Rank trick for Matrix Factorization-Based Graph Algorithms.
|
|
47
|
+
<http://www.mlgworkshop.org/2019/papers/MLG2019_paper_1.pdf>`_
|
|
48
|
+
Proceedings of the 15th International Workshop on Mining and Learning with Graphs (MLG).
|
|
49
|
+
"""
|
|
50
|
+
def __init__(self, sparse_mat: Union[sparse.csr_matrix, sparse.csc_matrix], low_rank_tuples: Union[list, Tuple],
|
|
51
|
+
dtype=float):
|
|
52
|
+
n_row, n_col = sparse_mat.shape
|
|
53
|
+
self.sparse_mat = sparse_mat.tocsr().astype(dtype)
|
|
54
|
+
super(SparseLR, self).__init__(dtype=dtype, shape=(n_row, n_col))
|
|
55
|
+
|
|
56
|
+
if isinstance(low_rank_tuples, Tuple):
|
|
57
|
+
low_rank_tuples = [low_rank_tuples]
|
|
58
|
+
self.low_rank_tuples = []
|
|
59
|
+
for x, y in low_rank_tuples:
|
|
60
|
+
if x.shape == (n_row,) and y.shape == (n_col,):
|
|
61
|
+
self.low_rank_tuples.append((x.astype(self.dtype), y.astype(self.dtype)))
|
|
62
|
+
else:
|
|
63
|
+
raise ValueError('For each low rank tuple, x (resp. y) should be a vector of length {} (resp. {})'
|
|
64
|
+
.format(n_row, n_col))
|
|
65
|
+
|
|
66
|
+
def __neg__(self):
|
|
67
|
+
return SparseLR(-self.sparse_mat, [(-x, y) for (x, y) in self.low_rank_tuples])
|
|
68
|
+
|
|
69
|
+
def __add__(self, other: 'SparseLR'):
|
|
70
|
+
if type(other) == sparse.csr_matrix:
|
|
71
|
+
return SparseLR(self.sparse_mat + other, self.low_rank_tuples)
|
|
72
|
+
else:
|
|
73
|
+
return SparseLR(self.sparse_mat + other.sparse_mat, self.low_rank_tuples + other.low_rank_tuples)
|
|
74
|
+
|
|
75
|
+
def __sub__(self, other):
|
|
76
|
+
return self.__add__(-other)
|
|
77
|
+
|
|
78
|
+
def __mul__(self, other):
|
|
79
|
+
return SparseLR(other * self.sparse_mat, [(other * x, y) for (x, y) in self.low_rank_tuples])
|
|
80
|
+
|
|
81
|
+
def _matvec(self, matrix: np.ndarray):
|
|
82
|
+
"""Right dot product with a dense matrix.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
matrix:
|
|
87
|
+
Matrix.
|
|
88
|
+
|
|
89
|
+
Returns
|
|
90
|
+
-------
|
|
91
|
+
Dot product as a dense array
|
|
92
|
+
"""
|
|
93
|
+
prod = self.sparse_mat.dot(matrix)
|
|
94
|
+
if len(matrix.shape) == 1:
|
|
95
|
+
for (x, y) in self.low_rank_tuples:
|
|
96
|
+
prod += x * matrix.dot(y)
|
|
97
|
+
else:
|
|
98
|
+
transposed = matrix.T
|
|
99
|
+
for (x, y) in self.low_rank_tuples:
|
|
100
|
+
prod += x[:, np.newaxis].dot(transposed.dot(y)[:, np.newaxis].T)
|
|
101
|
+
return prod
|
|
102
|
+
|
|
103
|
+
def _transpose(self):
|
|
104
|
+
"""Transposed operator."""
|
|
105
|
+
transposed_sparse = sparse.csr_matrix(self.sparse_mat.T)
|
|
106
|
+
transposed_tuples = [(y, x) for (x, y) in self.low_rank_tuples]
|
|
107
|
+
return SparseLR(transposed_sparse, transposed_tuples)
|
|
108
|
+
|
|
109
|
+
def _adjoint(self):
|
|
110
|
+
return self.transpose()
|
|
111
|
+
|
|
112
|
+
def left_sparse_dot(self, matrix: sparse.csr_matrix):
|
|
113
|
+
"""Left dot product with a sparse matrix."""
|
|
114
|
+
return SparseLR(matrix.dot(self.sparse_mat), [(matrix.dot(x), y) for (x, y) in self.low_rank_tuples])
|
|
115
|
+
|
|
116
|
+
def right_sparse_dot(self, matrix: sparse.csr_matrix):
|
|
117
|
+
"""Right dot product with a sparse matrix."""
|
|
118
|
+
return SparseLR(self.sparse_mat.dot(matrix), [(x, matrix.T.dot(y)) for (x, y) in self.low_rank_tuples])
|
|
119
|
+
|
|
120
|
+
def sum(self, axis=None):
|
|
121
|
+
"""Row-wise, column-wise or total sum of operator's coefficients.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
axis :
|
|
126
|
+
If 0, return column-wise sum. If 1, return row-wise sum. Otherwise, return total sum.
|
|
127
|
+
"""
|
|
128
|
+
if axis == 0:
|
|
129
|
+
s = self.T.dot(np.ones(self.shape[0]))
|
|
130
|
+
elif axis == 1:
|
|
131
|
+
s = self.dot(np.ones(self.shape[1]))
|
|
132
|
+
else:
|
|
133
|
+
s = self.dot(np.ones(self.shape[1])).sum()
|
|
134
|
+
return s
|
|
135
|
+
|
|
136
|
+
def astype(self, dtype: Union[str, np.dtype]):
|
|
137
|
+
"""Change dtype of the object."""
|
|
138
|
+
self.sparse_mat = self.sparse_mat.astype(dtype)
|
|
139
|
+
self.low_rank_tuples = [(x.astype(dtype), y.astype(dtype)) for (x, y) in self.low_rank_tuples]
|
|
140
|
+
self.dtype = np.dtype(dtype)
|
|
141
|
+
|
|
142
|
+
return self
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
"""
|
|
4
|
+
Created on July 10 2019
|
|
5
|
+
|
|
6
|
+
Authors:
|
|
7
|
+
Nathan De Lara <nathan.delara@telecom-paris.fr>
|
|
8
|
+
"""
|
|
9
|
+
from abc import ABC
|
|
10
|
+
from typing import Union
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from scipy import sparse
|
|
14
|
+
from scipy.sparse.linalg import svds
|
|
15
|
+
|
|
16
|
+
from sknetwork.base import Algorithm
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SVDSolver(Algorithm, ABC):
|
|
20
|
+
"""Generic class for SVD-solvers.
|
|
21
|
+
|
|
22
|
+
Attributes
|
|
23
|
+
----------
|
|
24
|
+
singular_vectors_left_: np.ndarray
|
|
25
|
+
Two-dimensional array, each column is a left singular vector of the input.
|
|
26
|
+
singular_vectors_right_: np.ndarray
|
|
27
|
+
Two-dimensional array, each column is a right singular vector of the input.
|
|
28
|
+
singular_values_: np.ndarray
|
|
29
|
+
Singular values.
|
|
30
|
+
"""
|
|
31
|
+
def __init__(self):
|
|
32
|
+
self.singular_vectors_left_ = None
|
|
33
|
+
self.singular_vectors_right_ = None
|
|
34
|
+
self.singular_values_ = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LanczosSVD(SVDSolver):
|
|
38
|
+
"""SVD solver using Lanczos method on :math:`AA^T` or :math:`A^TA`.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
n_iter : int
|
|
43
|
+
Maximum number of Arnoldi update iterations allowed.
|
|
44
|
+
Default = 10 * nb or rows or columns.
|
|
45
|
+
tol : float
|
|
46
|
+
Relative accuracy for eigenvalues (stopping criterion).
|
|
47
|
+
Default = 0 (machine precision).
|
|
48
|
+
|
|
49
|
+
Attributes
|
|
50
|
+
----------
|
|
51
|
+
singular_vectors_left_: np.ndarray
|
|
52
|
+
Two-dimensional array, each column is a left singular vector of the input.
|
|
53
|
+
singular_vectors_right_: np.ndarray
|
|
54
|
+
Two-dimensional array, each column is a right singular vector of the input.
|
|
55
|
+
singular_values_: np.ndarray
|
|
56
|
+
Singular values.
|
|
57
|
+
|
|
58
|
+
See Also
|
|
59
|
+
--------
|
|
60
|
+
scipy.sparse.linalg.svds
|
|
61
|
+
"""
|
|
62
|
+
def __init__(self, n_iter: int = None, tol: float = 0.):
|
|
63
|
+
super(LanczosSVD, self).__init__()
|
|
64
|
+
self.n_iter = n_iter
|
|
65
|
+
self.tol = tol
|
|
66
|
+
|
|
67
|
+
def fit(self, matrix: Union[sparse.csr_matrix, sparse.linalg.LinearOperator], n_components: int,
|
|
68
|
+
init_vector: np.ndarray = None):
|
|
69
|
+
"""Perform singular value decomposition on input matrix.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
matrix :
|
|
74
|
+
Matrix to decompose.
|
|
75
|
+
n_components : int
|
|
76
|
+
Number of singular values to compute
|
|
77
|
+
init_vector : np.ndarray
|
|
78
|
+
Starting vector for iteration.
|
|
79
|
+
Default = random.
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
self: :class:`SVDSolver`
|
|
83
|
+
"""
|
|
84
|
+
u, s, vt = svds(matrix.astype(float), n_components, v0=init_vector)
|
|
85
|
+
# order the singular values by decreasing order
|
|
86
|
+
index = np.argsort(-s)
|
|
87
|
+
self.singular_vectors_left_ = u[:, index]
|
|
88
|
+
self.singular_vectors_right_ = vt.T[:, index]
|
|
89
|
+
self.singular_values_ = s[index]
|
|
90
|
+
|
|
91
|
+
return self
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Tests for linalg"""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for eigenvalue solver."""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from sknetwork.data import miserables, karate_club
|
|
10
|
+
from sknetwork.linalg import LanczosEig, SparseLR
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def eigenvector_err(matrix, eigenvectors, eigenvalues):
|
|
14
|
+
"""Approximation error for eigenvectors."""
|
|
15
|
+
err = matrix.dot(eigenvectors) - eigenvectors * eigenvalues
|
|
16
|
+
return np.linalg.norm(err)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# noinspection DuplicatedCode
|
|
20
|
+
class TestSolvers(unittest.TestCase):
|
|
21
|
+
|
|
22
|
+
def setUp(self):
|
|
23
|
+
"""Load les Miserables and regularized version"""
|
|
24
|
+
self.adjacency = miserables()
|
|
25
|
+
self.random_state = np.random.RandomState(123)
|
|
26
|
+
n = self.adjacency.shape[0]
|
|
27
|
+
x = np.random.random(n)
|
|
28
|
+
self.slr = SparseLR(self.adjacency, [(x, x)])
|
|
29
|
+
|
|
30
|
+
def test_lanczos(self):
|
|
31
|
+
solver = LanczosEig('LM')
|
|
32
|
+
solver.fit(self.adjacency, 2)
|
|
33
|
+
self.assertEqual(len(solver.eigenvalues_), 2)
|
|
34
|
+
self.assertAlmostEqual(eigenvector_err(self.adjacency, solver.eigenvectors_, solver.eigenvalues_), 0)
|
|
35
|
+
|
|
36
|
+
solver.fit(self.slr, 2)
|
|
37
|
+
self.assertEqual(len(solver.eigenvalues_), 2)
|
|
38
|
+
self.assertAlmostEqual(eigenvector_err(self.slr, solver.eigenvectors_, solver.eigenvalues_), 0)
|
|
39
|
+
|
|
40
|
+
adjacency = karate_club()
|
|
41
|
+
solver = LanczosEig('SM')
|
|
42
|
+
solver.fit(adjacency, 2)
|
|
43
|
+
self.assertEqual(len(solver.eigenvalues_), 2)
|
|
44
|
+
self.assertAlmostEqual(eigenvector_err(adjacency, solver.eigenvectors_, solver.eigenvalues_), 0)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for laplacian."""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from sknetwork.data.test_graphs import test_graph
|
|
10
|
+
from sknetwork.linalg import get_laplacian
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestLaplacian(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
def test(self):
|
|
16
|
+
adjacency = test_graph()
|
|
17
|
+
laplacian = get_laplacian(adjacency)
|
|
18
|
+
self.assertEqual(np.linalg.norm(laplacian.dot(np.ones(adjacency.shape[0]))), 0)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created in April 2020
|
|
5
|
+
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import unittest
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from scipy import sparse
|
|
12
|
+
|
|
13
|
+
from sknetwork.linalg import normalize
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestNormalization(unittest.TestCase):
|
|
17
|
+
|
|
18
|
+
def test_formats(self):
|
|
19
|
+
n = 5
|
|
20
|
+
mat1 = normalize(np.eye(n))
|
|
21
|
+
mat2 = normalize(sparse.eye(n))
|
|
22
|
+
|
|
23
|
+
x = np.random.randn(n)
|
|
24
|
+
self.assertAlmostEqual(np.linalg.norm(mat1.dot(x) - x), 0)
|
|
25
|
+
self.assertAlmostEqual(np.linalg.norm(mat2.dot(x) - x), 0)
|
|
26
|
+
|
|
27
|
+
mat1 = np.random.rand(n**2).reshape((n, n))
|
|
28
|
+
mat2 = sparse.csr_matrix(mat1)
|
|
29
|
+
mat1 = normalize(mat1, p=2)
|
|
30
|
+
mat2 = normalize(mat2, p=2)
|
|
31
|
+
self.assertAlmostEqual(np.linalg.norm(mat1.dot(x) - mat2.dot(x)), 0)
|
|
32
|
+
|
|
33
|
+
with self.assertRaises(ValueError):
|
|
34
|
+
normalize(mat1, p=3)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created in April 2020
|
|
5
|
+
@author: Thomas Bonald <bonald@enst.fr>
|
|
6
|
+
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
7
|
+
"""
|
|
8
|
+
import unittest
|
|
9
|
+
|
|
10
|
+
from sknetwork.data.test_graphs import *
|
|
11
|
+
from sknetwork.linalg import Laplacian, Normalizer, CoNeighbor
|
|
12
|
+
from sknetwork.linalg.basics import safe_sparse_dot
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestOperators(unittest.TestCase):
|
|
16
|
+
|
|
17
|
+
def test_laplacian(self):
|
|
18
|
+
for adjacency in [test_graph(), test_disconnected_graph()]:
|
|
19
|
+
n = adjacency.shape[1]
|
|
20
|
+
# regular Laplacian
|
|
21
|
+
laplacian = Laplacian(adjacency)
|
|
22
|
+
self.assertAlmostEqual(np.linalg.norm(laplacian.dot(np.ones(n))), 0)
|
|
23
|
+
# normalized Laplacian
|
|
24
|
+
laplacian = Laplacian(adjacency, normalized_laplacian=True)
|
|
25
|
+
weights = adjacency.dot(np.ones(n))
|
|
26
|
+
self.assertAlmostEqual(np.linalg.norm(laplacian.dot(np.sqrt(weights))), 0)
|
|
27
|
+
# regularization
|
|
28
|
+
regularization = 0.1
|
|
29
|
+
laplacian = Laplacian(adjacency, regularization=regularization, normalized_laplacian=True)
|
|
30
|
+
weights = adjacency.dot(np.ones(n)) + regularization
|
|
31
|
+
self.assertAlmostEqual(np.linalg.norm(laplacian.dot(np.sqrt(weights))), 0)
|
|
32
|
+
# product
|
|
33
|
+
shape = (n, 3)
|
|
34
|
+
self.assertEqual(laplacian.dot(np.ones(shape)).shape, shape)
|
|
35
|
+
self.assertEqual(safe_sparse_dot(laplacian, np.ones(shape)).shape, shape)
|
|
36
|
+
|
|
37
|
+
def test_normalizer(self):
|
|
38
|
+
for adjacency in [test_graph(), test_disconnected_graph()]:
|
|
39
|
+
n_row, n_col = adjacency.shape
|
|
40
|
+
# square matrix
|
|
41
|
+
normalizer = Normalizer(adjacency)
|
|
42
|
+
non_zeros = adjacency.dot(np.ones(n_col)) > 0
|
|
43
|
+
self.assertAlmostEqual(np.linalg.norm(normalizer.dot(np.ones(n_col)) - non_zeros), 0)
|
|
44
|
+
# single row
|
|
45
|
+
normalizer = Normalizer(adjacency[1])
|
|
46
|
+
self.assertAlmostEqual(float(normalizer.dot(np.ones(n_col))), 1)
|
|
47
|
+
normalizer = Normalizer(adjacency[2].toarray().ravel())
|
|
48
|
+
self.assertAlmostEqual(float(normalizer.dot(np.ones(n_col))), 1)
|
|
49
|
+
# regularization
|
|
50
|
+
normalizer = Normalizer(adjacency, 1)
|
|
51
|
+
self.assertAlmostEqual(np.linalg.norm(normalizer.dot(np.ones(n_col)) - np.ones(n_row)), 0)
|
|
52
|
+
|
|
53
|
+
def test_coneighbors(self):
|
|
54
|
+
biadjacency = test_bigraph()
|
|
55
|
+
operator = CoNeighbor(biadjacency)
|
|
56
|
+
operator.astype('float')
|
|
57
|
+
operator.right_sparse_dot(sparse.eye(operator.shape[1], format='csr'))
|
|
58
|
+
|
|
59
|
+
operator1 = CoNeighbor(biadjacency, normalized=False)
|
|
60
|
+
operator2 = CoNeighbor(biadjacency, normalized=False)
|
|
61
|
+
x = np.random.randn(operator.shape[1])
|
|
62
|
+
x1 = (-operator1).dot(x)
|
|
63
|
+
x2 = (operator2 * -1).dot(x)
|
|
64
|
+
x3 = operator1.T.dot(x)
|
|
65
|
+
self.assertAlmostEqual(np.linalg.norm(x1 - x2), 0)
|
|
66
|
+
self.assertAlmostEqual(np.linalg.norm(x2 - x3), 0)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for polynomials."""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy import sparse
|
|
9
|
+
|
|
10
|
+
from sknetwork.data.test_graphs import test_graph
|
|
11
|
+
from sknetwork.linalg import Polynome
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestPolynome(unittest.TestCase):
|
|
15
|
+
|
|
16
|
+
def test_init(self):
|
|
17
|
+
adjacency = test_graph()
|
|
18
|
+
with self.assertRaises(ValueError):
|
|
19
|
+
Polynome(adjacency, np.array([]))
|
|
20
|
+
|
|
21
|
+
def test_operations(self):
|
|
22
|
+
adjacency = test_graph()
|
|
23
|
+
n = adjacency.shape[0]
|
|
24
|
+
polynome = Polynome(adjacency, np.arange(3))
|
|
25
|
+
x = np.random.randn(n)
|
|
26
|
+
|
|
27
|
+
y1 = (polynome * 2).dot(x)
|
|
28
|
+
y2 = (-polynome).dot(x)
|
|
29
|
+
self.assertAlmostEqual(np.linalg.norm(0.5 * y1 + y2), 0)
|
|
30
|
+
|
|
31
|
+
def test_dot(self):
|
|
32
|
+
adjacency = sparse.eye(5, format='csr')
|
|
33
|
+
polynome = Polynome(adjacency, np.arange(2))
|
|
34
|
+
|
|
35
|
+
x = np.random.randn(5, 3)
|
|
36
|
+
y = polynome.dot(x)
|
|
37
|
+
self.assertAlmostEqual(np.linalg.norm(x - y), 0)
|
|
38
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for d-iteration"""
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from sknetwork.data import house, karate_club
|
|
9
|
+
from sknetwork.data.parse import from_edge_list
|
|
10
|
+
from sknetwork.data.test_graphs import *
|
|
11
|
+
from sknetwork.linalg.operators import Regularizer
|
|
12
|
+
from sknetwork.linalg.ppr_solver import get_pagerank
|
|
13
|
+
from sknetwork.utils.check import is_proba_array
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestPPR(unittest.TestCase):
|
|
17
|
+
|
|
18
|
+
def test_diteration(self):
|
|
19
|
+
# test convergence by tolerance
|
|
20
|
+
for adjacency in [house(), test_graph(), test_digraph()]:
|
|
21
|
+
seeds = np.ones(adjacency.shape[0]) / adjacency.shape[0]
|
|
22
|
+
pr = get_pagerank(adjacency, damping_factor=0.85, n_iter=100, tol=10, solver='diteration', seeds=seeds)
|
|
23
|
+
self.assertTrue(is_proba_array(pr))
|
|
24
|
+
|
|
25
|
+
# test graph with some null out-degree
|
|
26
|
+
adjacency = from_edge_list([(0, 1)])
|
|
27
|
+
seeds = np.ones(adjacency.shape[0]) / adjacency.shape[0]
|
|
28
|
+
pr = get_pagerank(adjacency, damping_factor=0.85, n_iter=100, tol=10, solver='diteration', seeds=seeds)
|
|
29
|
+
self.assertTrue(is_proba_array(pr))
|
|
30
|
+
|
|
31
|
+
# test invalid entry
|
|
32
|
+
adjacency = Regularizer(house(), 0.1)
|
|
33
|
+
seeds = np.ones(adjacency.shape[0]) / adjacency.shape[0]
|
|
34
|
+
with self.assertRaises(ValueError):
|
|
35
|
+
get_pagerank(adjacency, damping_factor=0.85, n_iter=100, tol=10, solver='diteration', seeds=seeds)
|
|
36
|
+
|
|
37
|
+
def test_push(self):
|
|
38
|
+
# test convergence by tolerance
|
|
39
|
+
adjacency = karate_club()
|
|
40
|
+
seeds = np.ones(adjacency.shape[0]) / adjacency.shape[0]
|
|
41
|
+
pr = get_pagerank(adjacency, damping_factor=0.85,
|
|
42
|
+
n_iter=100, tol=1e-1, solver='push', seeds=seeds)
|
|
43
|
+
self.assertTrue(is_proba_array(pr))
|
|
44
|
+
|
|
45
|
+
def test_piteration(self):
|
|
46
|
+
# test on SparseLR matrix
|
|
47
|
+
adjacency = Regularizer(house(), 0.1)
|
|
48
|
+
seeds = np.ones(adjacency.shape[0]) / adjacency.shape[0]
|
|
49
|
+
pr = get_pagerank(adjacency, damping_factor=0.85, n_iter=100, tol=10, solver='piteration', seeds=seeds)
|
|
50
|
+
self.assertTrue(is_proba_array(pr))
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for embeddings metrics."""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from sknetwork.data import house, star_wars
|
|
10
|
+
from sknetwork.linalg.sparse_lowrank import SparseLR
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestSparseLowRank(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
def setUp(self):
|
|
16
|
+
"""Simple regularized adjacency and biadjacency for tests."""
|
|
17
|
+
self.undirected = SparseLR(house(), [(np.ones(5), np.ones(5))])
|
|
18
|
+
self.bipartite = SparseLR(star_wars(), [(np.ones(4), np.ones(3))])
|
|
19
|
+
|
|
20
|
+
def test_init(self):
|
|
21
|
+
with self.assertRaises(ValueError):
|
|
22
|
+
SparseLR(house(), [(np.ones(5), np.ones(4))])
|
|
23
|
+
with self.assertRaises(ValueError):
|
|
24
|
+
SparseLR(house(), [(np.ones(4), np.ones(5))])
|
|
25
|
+
|
|
26
|
+
def test_addition(self):
|
|
27
|
+
addition = self.undirected + self.undirected
|
|
28
|
+
expected = SparseLR(2 * house(), [(np.ones(5), 2 * np.ones(5))])
|
|
29
|
+
err = (addition.sparse_mat - expected.sparse_mat).count_nonzero()
|
|
30
|
+
self.assertEqual(err, 0)
|
|
31
|
+
x = np.random.rand(5)
|
|
32
|
+
self.assertAlmostEqual(np.linalg.norm(addition.dot(x) - expected.dot(x)), 0)
|
|
33
|
+
|
|
34
|
+
def test_operations(self):
|
|
35
|
+
adjacency = self.undirected.sparse_mat
|
|
36
|
+
slr = -self.undirected
|
|
37
|
+
slr += adjacency
|
|
38
|
+
slr -= adjacency
|
|
39
|
+
slr.left_sparse_dot(adjacency)
|
|
40
|
+
slr.right_sparse_dot(adjacency)
|
|
41
|
+
slr.astype(float)
|
|
42
|
+
|
|
43
|
+
def test_product(self):
|
|
44
|
+
prod = self.undirected.dot(np.ones(5))
|
|
45
|
+
self.assertEqual(prod.shape, (5,))
|
|
46
|
+
prod = self.bipartite.dot(np.ones(3))
|
|
47
|
+
self.assertEqual(np.linalg.norm(prod - np.array([5., 4., 6., 5.])), 0.)
|
|
48
|
+
prod = self.bipartite.dot(0.5 * np.ones(3))
|
|
49
|
+
self.assertEqual(np.linalg.norm(prod - np.array([2.5, 2., 3., 2.5])), 0.)
|
|
50
|
+
prod = (2 * self.bipartite).dot(0.5 * np.ones(3))
|
|
51
|
+
self.assertEqual(np.linalg.norm(prod - 2 * np.array([2.5, 2., 3., 2.5])), 0.)
|
|
52
|
+
|
|
53
|
+
def test_transposition(self):
|
|
54
|
+
transposed = self.undirected.T
|
|
55
|
+
error = (self.undirected.sparse_mat - transposed.sparse_mat).data
|
|
56
|
+
self.assertEqual(abs(error).sum(), 0.)
|
|
57
|
+
transposed = self.bipartite.T
|
|
58
|
+
x, y = transposed.low_rank_tuples[0]
|
|
59
|
+
self.assertTrue((x == np.ones(3)).all())
|
|
60
|
+
self.assertTrue((y == np.ones(4)).all())
|
|
61
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for svd."""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from sknetwork.data import movie_actor
|
|
10
|
+
from sknetwork.linalg import LanczosSVD, SparseLR
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def svd_err(matrix, u, v, sigma):
|
|
14
|
+
"""Approximation error for singular vectors."""
|
|
15
|
+
err = matrix.dot(v) - u * sigma
|
|
16
|
+
return np.linalg.norm(err)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# noinspection DuplicatedCode
|
|
20
|
+
class TestSolvers(unittest.TestCase):
|
|
21
|
+
|
|
22
|
+
def setUp(self):
|
|
23
|
+
"""Simple biadjacency for tests."""
|
|
24
|
+
self.biadjacency = movie_actor()
|
|
25
|
+
n_row, n_col = self.biadjacency.shape
|
|
26
|
+
self.slr = SparseLR(self.biadjacency, [(np.random.rand(n_row), np.random.rand(n_col))])
|
|
27
|
+
|
|
28
|
+
def test_lanczos(self):
|
|
29
|
+
solver = LanczosSVD()
|
|
30
|
+
solver.fit(self.biadjacency, 2)
|
|
31
|
+
self.assertEqual(len(solver.singular_values_), 2)
|
|
32
|
+
self.assertAlmostEqual(svd_err(self.biadjacency, solver.singular_vectors_left_, solver.singular_vectors_right_,
|
|
33
|
+
solver.singular_values_), 0)
|
|
34
|
+
|
|
35
|
+
solver.fit(self.slr, 2)
|
|
36
|
+
self.assertEqual(len(solver.singular_values_), 2)
|
|
37
|
+
self.assertAlmostEqual(svd_err(self.slr, solver.singular_vectors_left_, solver.singular_vectors_right_,
|
|
38
|
+
solver.singular_values_), 0)
|