scikit-network 0.33.0__cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.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.0.dist-info/AUTHORS.rst +43 -0
- scikit_network-0.33.0.dist-info/LICENSE +34 -0
- scikit_network-0.33.0.dist-info/METADATA +517 -0
- scikit_network-0.33.0.dist-info/RECORD +217 -0
- scikit_network-0.33.0.dist-info/WHEEL +6 -0
- scikit_network-0.33.0.dist-info/top_level.txt +1 -0
- scikit_network.libs/libgomp-a34b3233.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.cpython-312-x86_64-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.cpython-312-x86_64-linux-gnu.so +0 -0
- sknetwork/clustering/leiden_core.pyx +124 -0
- sknetwork/clustering/louvain.py +286 -0
- sknetwork/clustering/louvain_core.cpython-312-x86_64-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 +129 -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 +89 -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.cpython-312-x86_64-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.cpython-312-x86_64-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.cpython-312-x86_64-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.cpython-312-x86_64-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.cpython-312-x86_64-linux-gnu.so +0 -0
- sknetwork/topology/cliques.pyx +149 -0
- sknetwork/topology/core.cpython-312-x86_64-linux-gnu.so +0 -0
- sknetwork/topology/core.pyx +90 -0
- sknetwork/topology/cycles.py +243 -0
- sknetwork/topology/minheap.cpython-312-x86_64-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.cpython-312-x86_64-linux-gnu.so +0 -0
- sknetwork/topology/triangles.pyx +151 -0
- sknetwork/topology/weisfeiler_lehman.py +133 -0
- sknetwork/topology/weisfeiler_lehman_core.cpython-312-x86_64-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
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from scipy import sparse
|
|
12
|
+
from scipy.sparse.linalg import LinearOperator
|
|
13
|
+
|
|
14
|
+
from sknetwork.utils.check import check_format, check_square
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Polynome(LinearOperator):
|
|
18
|
+
"""Polynome of a matrix as a linear operator
|
|
19
|
+
|
|
20
|
+
:math:`P(A) = \\alpha_k A^k + ... + \\alpha_1 A + \\alpha_0`.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
matrix :
|
|
25
|
+
Square matrix
|
|
26
|
+
coeffs : np.ndarray
|
|
27
|
+
Coefficients of the polynome by increasing order of power.
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
--------
|
|
31
|
+
>>> from scipy import sparse
|
|
32
|
+
>>> from sknetwork.linalg import Polynome
|
|
33
|
+
>>> matrix = sparse.eye(2, format='csr')
|
|
34
|
+
>>> polynome = Polynome(matrix, np.arange(3))
|
|
35
|
+
>>> x = np.ones(2)
|
|
36
|
+
>>> polynome.dot(x)
|
|
37
|
+
array([3., 3.])
|
|
38
|
+
>>> polynome.T.dot(x)
|
|
39
|
+
array([3., 3.])
|
|
40
|
+
|
|
41
|
+
Notes
|
|
42
|
+
-----
|
|
43
|
+
The polynome is evaluated using the `Ruffini-Horner method
|
|
44
|
+
<https://en.wikipedia.org/wiki/Horner%27s_method>`_.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, matrix: Union[sparse.csr_matrix, np.ndarray], coeffs: np.ndarray):
|
|
48
|
+
if coeffs.shape[0] == 0:
|
|
49
|
+
raise ValueError('A polynome requires at least one coefficient.')
|
|
50
|
+
if not isinstance(matrix, LinearOperator):
|
|
51
|
+
matrix = check_format(matrix)
|
|
52
|
+
check_square(matrix)
|
|
53
|
+
shape = matrix.shape
|
|
54
|
+
dtype = matrix.dtype
|
|
55
|
+
super(Polynome, self).__init__(dtype=dtype, shape=shape)
|
|
56
|
+
|
|
57
|
+
self.matrix = matrix
|
|
58
|
+
self.coeffs = coeffs
|
|
59
|
+
|
|
60
|
+
def __neg__(self):
|
|
61
|
+
return Polynome(self.matrix, -self.coeffs)
|
|
62
|
+
|
|
63
|
+
def __mul__(self, other):
|
|
64
|
+
return Polynome(self.matrix, other * self.coeffs)
|
|
65
|
+
|
|
66
|
+
def _matvec(self, matrix: np.ndarray):
|
|
67
|
+
"""Right dot product with a dense matrix.
|
|
68
|
+
"""
|
|
69
|
+
y = self.coeffs[-1] * matrix
|
|
70
|
+
for a in self.coeffs[::-1][1:]:
|
|
71
|
+
y = self.matrix.dot(y) + a * matrix
|
|
72
|
+
return y
|
|
73
|
+
|
|
74
|
+
def _transpose(self):
|
|
75
|
+
"""Transposed operator."""
|
|
76
|
+
return Polynome(self.matrix.T.tocsr(), self.coeffs)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Apr 2020
|
|
5
|
+
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
6
|
+
"""
|
|
7
|
+
from typing import Union
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from scipy import sparse
|
|
11
|
+
from scipy.sparse.linalg import eigs, LinearOperator, bicgstab
|
|
12
|
+
|
|
13
|
+
from sknetwork.linalg.diteration import diffusion
|
|
14
|
+
from sknetwork.linalg.push import push_pagerank
|
|
15
|
+
from sknetwork.linalg.normalizer import normalize
|
|
16
|
+
from sknetwork.linalg.polynome import Polynome
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RandomSurferOperator(LinearOperator):
|
|
20
|
+
"""Random surfer as a LinearOperator
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
adjacency :
|
|
25
|
+
Adjacency matrix of the graph as a CSR or a LinearOperator.
|
|
26
|
+
damping_factor : float
|
|
27
|
+
Probability to continue the random walk.
|
|
28
|
+
seeds :
|
|
29
|
+
Probability vector for seeds.
|
|
30
|
+
|
|
31
|
+
Attributes
|
|
32
|
+
----------
|
|
33
|
+
a : sparse.csr_matrix
|
|
34
|
+
Scaled transposed transition matrix.
|
|
35
|
+
b : np.ndarray
|
|
36
|
+
Scaled restart probability vector.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, adjacency: Union[sparse.csr_matrix, LinearOperator], seeds: np.ndarray, damping_factor):
|
|
40
|
+
super(RandomSurferOperator, self).__init__(shape=adjacency.shape, dtype=float)
|
|
41
|
+
|
|
42
|
+
n = adjacency.shape[0]
|
|
43
|
+
out_degrees = adjacency.dot(np.ones(n)).astype(bool)
|
|
44
|
+
|
|
45
|
+
if hasattr(adjacency, 'left_sparse_dot'):
|
|
46
|
+
self.a = damping_factor * normalize(adjacency).T
|
|
47
|
+
else:
|
|
48
|
+
self.a = (damping_factor * normalize(adjacency)).T.tocsr()
|
|
49
|
+
self.b = (np.ones(n) - damping_factor * out_degrees) * seeds
|
|
50
|
+
|
|
51
|
+
def _matvec(self, x: np.ndarray):
|
|
52
|
+
return self.a.dot(x) + self.b * x.sum()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_pagerank(adjacency: Union[sparse.csr_matrix, LinearOperator], seeds: np.ndarray, damping_factor: float,
|
|
56
|
+
n_iter: int, tol: float = 1e-6, solver: str = 'piteration') -> np.ndarray:
|
|
57
|
+
"""Solve the Pagerank problem. Formally,
|
|
58
|
+
|
|
59
|
+
:math:`x = \\alpha Px + (1-\\alpha)y`,
|
|
60
|
+
|
|
61
|
+
where :math:`P = (D^{-1}A)^T` is the transition matrix and :math:`y` is the personalization probability vector.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
adjacency : sparse.csr_matrix
|
|
66
|
+
Adjacency matrix of the graph.
|
|
67
|
+
seeds : np.ndarray
|
|
68
|
+
Personalization array. Must be a valid probability vector.
|
|
69
|
+
damping_factor : float
|
|
70
|
+
Probability to continue the random walk.
|
|
71
|
+
n_iter : int
|
|
72
|
+
Number of iterations for some of the solvers such as ``'piteration'`` or ``'diteration'``.
|
|
73
|
+
tol : float
|
|
74
|
+
Tolerance for the convergence of some solvers such as ``'bicgstab'`` or ``'lanczos'`` or ``'push'``.
|
|
75
|
+
solver : :obj:`str`
|
|
76
|
+
Which solver to use: ``'piteration'``, ``'diteration'``, ``'bicgstab'``, ``'lanczos'``, ``̀'RH'``, ``'push'``.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
pagerank : np.ndarray
|
|
81
|
+
Probability vector.
|
|
82
|
+
|
|
83
|
+
Examples
|
|
84
|
+
--------
|
|
85
|
+
>>> from sknetwork.data import house
|
|
86
|
+
>>> adjacency = house()
|
|
87
|
+
>>> seeds = np.array([1, 0, 0, 0, 0])
|
|
88
|
+
>>> scores = get_pagerank(adjacency, seeds, damping_factor=0.85, n_iter=10)
|
|
89
|
+
>>> np.round(scores, 2)
|
|
90
|
+
array([0.29, 0.24, 0.12, 0.12, 0.24])
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
References
|
|
94
|
+
----------
|
|
95
|
+
* Hong, D. (2012). `Optimized on-line computation of pagerank algorithm.
|
|
96
|
+
<https://arxiv.org/pdf/1202.6158.pdf>`_
|
|
97
|
+
arXiv preprint arXiv:1202.6158.
|
|
98
|
+
* Van der Vorst, H. A. (1992). `Bi-CGSTAB:
|
|
99
|
+
<https://en.wikipedia.org/wiki/Biconjugate_gradient_stabilized_method>`_
|
|
100
|
+
A fast and smoothly converging variant of Bi-CG for the solution of nonsymmetric linear systems.
|
|
101
|
+
SIAM Journal on scientific and Statistical Computing, 13(2), 631-644.
|
|
102
|
+
* Lanczos, C. (1950).
|
|
103
|
+
`An iteration method for the solution of the eigenvalue problem of linear differential and integral operators.
|
|
104
|
+
<http://www.cs.umd.edu/~oleary/lanczos1950.pdf>`_
|
|
105
|
+
Los Angeles, CA: United States Governm. Press Office.
|
|
106
|
+
* Whang, J. , Lenharth, A. , Dhillon, I. , & Pingali, K. . (2015).
|
|
107
|
+
`Scalable Data-Driven PageRank: Algorithms, System Issues, and Lessons Learned`. 9233, 438-450.
|
|
108
|
+
<https://www.cs.utexas.edu/users/inderjit/public_papers/scalable_pagerank_europar15.pdf>
|
|
109
|
+
"""
|
|
110
|
+
n = adjacency.shape[0]
|
|
111
|
+
|
|
112
|
+
if solver == 'diteration':
|
|
113
|
+
if not isinstance(adjacency, sparse.csr_matrix):
|
|
114
|
+
raise ValueError('D-iteration is not compatible with linear operators.')
|
|
115
|
+
adjacency = normalize(adjacency, p=1)
|
|
116
|
+
indptr = adjacency.indptr.astype(np.int32)
|
|
117
|
+
indices = adjacency.indices.astype(np.int32)
|
|
118
|
+
data = adjacency.data.astype(np.float32)
|
|
119
|
+
damping_factor = np.float32(damping_factor)
|
|
120
|
+
n_iter = np.int32(n_iter)
|
|
121
|
+
tol = np.float32(tol)
|
|
122
|
+
|
|
123
|
+
scores = np.zeros(n, dtype=np.float32)
|
|
124
|
+
fluid = (1 - damping_factor) * seeds.astype(np.float32)
|
|
125
|
+
diffusion(indptr, indices, data, scores, fluid, damping_factor, n_iter, tol)
|
|
126
|
+
|
|
127
|
+
elif solver == 'push':
|
|
128
|
+
n = adjacency.shape[0]
|
|
129
|
+
damping_factor = np.float32(damping_factor)
|
|
130
|
+
tol = np.float32(tol)
|
|
131
|
+
degrees = adjacency.dot(np.ones(n)).astype(np.int32)
|
|
132
|
+
rev_adjacency = adjacency.transpose().tocsr()
|
|
133
|
+
|
|
134
|
+
indptr = adjacency.indptr.astype(np.int32)
|
|
135
|
+
indices = adjacency.indices.astype(np.int32)
|
|
136
|
+
rev_indptr = rev_adjacency.indptr.astype(np.int32)
|
|
137
|
+
rev_indices = rev_adjacency.indices.astype(np.int32)
|
|
138
|
+
|
|
139
|
+
scores = push_pagerank(n, degrees, indptr, indices,
|
|
140
|
+
rev_indptr, rev_indices,
|
|
141
|
+
seeds.astype(np.float32),
|
|
142
|
+
damping_factor, tol)
|
|
143
|
+
|
|
144
|
+
elif solver == 'RH':
|
|
145
|
+
coeffs = np.ones(n_iter + 1)
|
|
146
|
+
polynome = Polynome(damping_factor * normalize(adjacency, p=1).T.tocsr(), coeffs)
|
|
147
|
+
scores = polynome.dot(seeds)
|
|
148
|
+
|
|
149
|
+
else:
|
|
150
|
+
rso = RandomSurferOperator(adjacency, seeds, damping_factor)
|
|
151
|
+
v0 = rso.b
|
|
152
|
+
if solver == 'bicgstab':
|
|
153
|
+
scores, info = bicgstab(sparse.eye(n, format='csr') - rso.a, rso.b, atol=tol, x0=v0)
|
|
154
|
+
elif solver == 'lanczos':
|
|
155
|
+
# noinspection PyTypeChecker
|
|
156
|
+
_, scores = sparse.linalg.eigs(rso, k=1, tol=tol, v0=v0)
|
|
157
|
+
scores = abs(scores.flatten().real)
|
|
158
|
+
elif solver == 'piteration':
|
|
159
|
+
scores = v0
|
|
160
|
+
for i in range(n_iter):
|
|
161
|
+
scores_ = rso.dot(scores)
|
|
162
|
+
scores_ /= scores_.sum()
|
|
163
|
+
if np.linalg.norm(scores - scores_, ord=1) < tol:
|
|
164
|
+
break
|
|
165
|
+
else:
|
|
166
|
+
scores = scores_
|
|
167
|
+
else:
|
|
168
|
+
raise ValueError('Unknown solver.')
|
|
169
|
+
|
|
170
|
+
return scores / scores.sum()
|
|
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
|
+
>>> 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)
|