scikit-network 0.28.3__cp39-cp39-macosx_12_0_arm64.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.28.3.dist-info/AUTHORS.rst +41 -0
- scikit_network-0.28.3.dist-info/LICENSE +34 -0
- scikit_network-0.28.3.dist-info/METADATA +457 -0
- scikit_network-0.28.3.dist-info/RECORD +240 -0
- scikit_network-0.28.3.dist-info/WHEEL +5 -0
- scikit_network-0.28.3.dist-info/top_level.txt +1 -0
- sknetwork/__init__.py +21 -0
- sknetwork/classification/__init__.py +8 -0
- sknetwork/classification/base.py +84 -0
- sknetwork/classification/base_rank.py +143 -0
- sknetwork/classification/diffusion.py +134 -0
- sknetwork/classification/knn.py +162 -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 +35 -0
- sknetwork/classification/tests/test_diffusion.py +37 -0
- sknetwork/classification/tests/test_knn.py +24 -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-39-darwin.so +0 -0
- sknetwork/classification/vote.pyx +58 -0
- sknetwork/clustering/__init__.py +7 -0
- sknetwork/clustering/base.py +102 -0
- sknetwork/clustering/kmeans.py +142 -0
- sknetwork/clustering/louvain.py +255 -0
- sknetwork/clustering/louvain_core.cpython-39-darwin.so +0 -0
- sknetwork/clustering/louvain_core.pyx +134 -0
- sknetwork/clustering/metrics.py +91 -0
- sknetwork/clustering/postprocess.py +66 -0
- sknetwork/clustering/propagation_clustering.py +108 -0
- sknetwork/clustering/tests/__init__.py +1 -0
- sknetwork/clustering/tests/test_API.py +37 -0
- sknetwork/clustering/tests/test_kmeans.py +47 -0
- sknetwork/clustering/tests/test_louvain.py +104 -0
- sknetwork/clustering/tests/test_metrics.py +50 -0
- sknetwork/clustering/tests/test_post_processing.py +23 -0
- sknetwork/clustering/tests/test_postprocess.py +39 -0
- sknetwork/data/__init__.py +5 -0
- sknetwork/data/load.py +408 -0
- sknetwork/data/models.py +459 -0
- sknetwork/data/parse.py +621 -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_load.py +95 -0
- sknetwork/data/tests/test_models.py +52 -0
- sknetwork/data/tests/test_parse.py +253 -0
- sknetwork/data/tests/test_test_graphs.py +30 -0
- sknetwork/data/tests/test_toy_graphs.py +68 -0
- sknetwork/data/toy_graphs.py +619 -0
- sknetwork/embedding/__init__.py +10 -0
- sknetwork/embedding/base.py +90 -0
- sknetwork/embedding/force_atlas.py +197 -0
- sknetwork/embedding/louvain_embedding.py +174 -0
- sknetwork/embedding/louvain_hierarchy.py +142 -0
- sknetwork/embedding/metrics.py +66 -0
- sknetwork/embedding/random_projection.py +133 -0
- sknetwork/embedding/spectral.py +214 -0
- sknetwork/embedding/spring.py +198 -0
- sknetwork/embedding/svd.py +363 -0
- sknetwork/embedding/tests/__init__.py +1 -0
- sknetwork/embedding/tests/test_API.py +73 -0
- sknetwork/embedding/tests/test_force_atlas.py +35 -0
- sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
- sknetwork/embedding/tests/test_louvain_hierarchy.py +19 -0
- sknetwork/embedding/tests/test_metrics.py +29 -0
- sknetwork/embedding/tests/test_random_projection.py +28 -0
- sknetwork/embedding/tests/test_spectral.py +84 -0
- sknetwork/embedding/tests/test_spring.py +50 -0
- sknetwork/embedding/tests/test_svd.py +37 -0
- sknetwork/flow/__init__.py +3 -0
- sknetwork/flow/flow.py +73 -0
- sknetwork/flow/tests/__init__.py +1 -0
- sknetwork/flow/tests/test_flow.py +17 -0
- sknetwork/flow/tests/test_utils.py +69 -0
- sknetwork/flow/utils.py +91 -0
- sknetwork/gnn/__init__.py +10 -0
- sknetwork/gnn/activation.py +117 -0
- sknetwork/gnn/base.py +155 -0
- sknetwork/gnn/base_activation.py +89 -0
- sknetwork/gnn/base_layer.py +109 -0
- sknetwork/gnn/gnn_classifier.py +381 -0
- sknetwork/gnn/layer.py +153 -0
- sknetwork/gnn/layers.py +127 -0
- sknetwork/gnn/loss.py +180 -0
- sknetwork/gnn/neighbor_sampler.py +65 -0
- sknetwork/gnn/optimizer.py +163 -0
- sknetwork/gnn/tests/__init__.py +1 -0
- sknetwork/gnn/tests/test_activation.py +56 -0
- sknetwork/gnn/tests/test_base.py +79 -0
- sknetwork/gnn/tests/test_base_layer.py +37 -0
- sknetwork/gnn/tests/test_gnn_classifier.py +192 -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 +93 -0
- sknetwork/gnn/utils.py +219 -0
- sknetwork/hierarchy/__init__.py +7 -0
- sknetwork/hierarchy/base.py +69 -0
- sknetwork/hierarchy/louvain_hierarchy.py +264 -0
- sknetwork/hierarchy/metrics.py +234 -0
- sknetwork/hierarchy/paris.cpython-39-darwin.so +0 -0
- sknetwork/hierarchy/paris.pyx +317 -0
- sknetwork/hierarchy/postprocess.py +350 -0
- sknetwork/hierarchy/tests/__init__.py +1 -0
- sknetwork/hierarchy/tests/test_API.py +25 -0
- sknetwork/hierarchy/tests/test_algos.py +29 -0
- sknetwork/hierarchy/tests/test_metrics.py +62 -0
- sknetwork/hierarchy/tests/test_postprocess.py +57 -0
- sknetwork/hierarchy/tests/test_ward.py +25 -0
- sknetwork/hierarchy/ward.py +94 -0
- sknetwork/linalg/__init__.py +9 -0
- sknetwork/linalg/basics.py +37 -0
- sknetwork/linalg/diteration.cpython-39-darwin.so +0 -0
- sknetwork/linalg/diteration.pyx +49 -0
- sknetwork/linalg/eig_solver.py +93 -0
- sknetwork/linalg/laplacian.py +15 -0
- sknetwork/linalg/normalization.py +66 -0
- sknetwork/linalg/operators.py +225 -0
- sknetwork/linalg/polynome.py +76 -0
- sknetwork/linalg/ppr_solver.py +170 -0
- sknetwork/linalg/push.cpython-39-darwin.so +0 -0
- sknetwork/linalg/push.pyx +73 -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 +38 -0
- sknetwork/linalg/tests/test_operators.py +70 -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 +4 -0
- sknetwork/linkpred/base.py +80 -0
- sknetwork/linkpred/first_order.py +508 -0
- sknetwork/linkpred/first_order_core.cpython-39-darwin.so +0 -0
- sknetwork/linkpred/first_order_core.pyx +315 -0
- sknetwork/linkpred/postprocessing.py +98 -0
- sknetwork/linkpred/tests/__init__.py +1 -0
- sknetwork/linkpred/tests/test_API.py +49 -0
- sknetwork/linkpred/tests/test_postprocessing.py +21 -0
- sknetwork/path/__init__.py +4 -0
- sknetwork/path/metrics.py +148 -0
- sknetwork/path/search.py +65 -0
- sknetwork/path/shortest_path.py +186 -0
- sknetwork/path/tests/__init__.py +1 -0
- sknetwork/path/tests/test_metrics.py +29 -0
- sknetwork/path/tests/test_search.py +25 -0
- sknetwork/path/tests/test_shortest_path.py +45 -0
- sknetwork/ranking/__init__.py +9 -0
- sknetwork/ranking/base.py +56 -0
- sknetwork/ranking/betweenness.cpython-39-darwin.so +0 -0
- sknetwork/ranking/betweenness.pyx +99 -0
- sknetwork/ranking/closeness.py +95 -0
- sknetwork/ranking/harmonic.py +82 -0
- sknetwork/ranking/hits.py +94 -0
- sknetwork/ranking/katz.py +81 -0
- sknetwork/ranking/pagerank.py +107 -0
- sknetwork/ranking/postprocess.py +25 -0
- sknetwork/ranking/tests/__init__.py +1 -0
- sknetwork/ranking/tests/test_API.py +34 -0
- sknetwork/ranking/tests/test_betweenness.py +38 -0
- sknetwork/ranking/tests/test_closeness.py +34 -0
- sknetwork/ranking/tests/test_hits.py +20 -0
- sknetwork/ranking/tests/test_pagerank.py +69 -0
- sknetwork/regression/__init__.py +4 -0
- sknetwork/regression/base.py +56 -0
- sknetwork/regression/diffusion.py +190 -0
- sknetwork/regression/tests/__init__.py +1 -0
- sknetwork/regression/tests/test_API.py +34 -0
- sknetwork/regression/tests/test_diffusion.py +48 -0
- sknetwork/sknetwork.py +3 -0
- sknetwork/topology/__init__.py +9 -0
- sknetwork/topology/dag.py +74 -0
- sknetwork/topology/dag_core.cpython-39-darwin.so +0 -0
- sknetwork/topology/dag_core.pyx +38 -0
- sknetwork/topology/kcliques.cpython-39-darwin.so +0 -0
- sknetwork/topology/kcliques.pyx +193 -0
- sknetwork/topology/kcore.cpython-39-darwin.so +0 -0
- sknetwork/topology/kcore.pyx +120 -0
- sknetwork/topology/structure.py +234 -0
- sknetwork/topology/tests/__init__.py +1 -0
- sknetwork/topology/tests/test_cliques.py +28 -0
- sknetwork/topology/tests/test_cores.py +21 -0
- sknetwork/topology/tests/test_dag.py +26 -0
- sknetwork/topology/tests/test_structure.py +99 -0
- sknetwork/topology/tests/test_triangles.py +42 -0
- sknetwork/topology/tests/test_wl_coloring.py +49 -0
- sknetwork/topology/tests/test_wl_kernel.py +31 -0
- sknetwork/topology/triangles.cpython-39-darwin.so +0 -0
- sknetwork/topology/triangles.pyx +166 -0
- sknetwork/topology/weisfeiler_lehman.py +163 -0
- sknetwork/topology/weisfeiler_lehman_core.cpython-39-darwin.so +0 -0
- sknetwork/topology/weisfeiler_lehman_core.pyx +116 -0
- sknetwork/utils/__init__.py +40 -0
- sknetwork/utils/base.py +35 -0
- sknetwork/utils/check.py +354 -0
- sknetwork/utils/co_neighbor.py +71 -0
- sknetwork/utils/format.py +219 -0
- sknetwork/utils/kmeans.py +89 -0
- sknetwork/utils/knn.py +166 -0
- sknetwork/utils/knn1d.cpython-39-darwin.so +0 -0
- sknetwork/utils/knn1d.pyx +80 -0
- sknetwork/utils/membership.py +82 -0
- sknetwork/utils/minheap.cpython-39-darwin.so +0 -0
- sknetwork/utils/minheap.pxd +22 -0
- sknetwork/utils/minheap.pyx +111 -0
- sknetwork/utils/neighbors.py +115 -0
- sknetwork/utils/seeds.py +75 -0
- sknetwork/utils/simplex.py +140 -0
- sknetwork/utils/tests/__init__.py +1 -0
- sknetwork/utils/tests/test_base.py +28 -0
- sknetwork/utils/tests/test_bunch.py +16 -0
- sknetwork/utils/tests/test_check.py +190 -0
- sknetwork/utils/tests/test_co_neighbor.py +43 -0
- sknetwork/utils/tests/test_format.py +61 -0
- sknetwork/utils/tests/test_kmeans.py +21 -0
- sknetwork/utils/tests/test_knn.py +32 -0
- sknetwork/utils/tests/test_membership.py +24 -0
- sknetwork/utils/tests/test_neighbors.py +41 -0
- sknetwork/utils/tests/test_projection_simplex.py +33 -0
- sknetwork/utils/tests/test_seeds.py +67 -0
- sknetwork/utils/tests/test_verbose.py +15 -0
- sknetwork/utils/tests/test_ward.py +20 -0
- sknetwork/utils/timeout.py +38 -0
- sknetwork/utils/verbose.py +37 -0
- sknetwork/utils/ward.py +60 -0
- sknetwork/visualization/__init__.py +4 -0
- sknetwork/visualization/colors.py +34 -0
- sknetwork/visualization/dendrograms.py +229 -0
- sknetwork/visualization/graphs.py +819 -0
- sknetwork/visualization/tests/__init__.py +1 -0
- sknetwork/visualization/tests/test_dendrograms.py +53 -0
- sknetwork/visualization/tests/test_graphs.py +167 -0
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from scipy import sparse
|
|
10
|
+
from scipy.sparse.linalg import LinearOperator
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def safe_sparse_dot(a, b):
|
|
14
|
+
"""Dot product with proper use of the sparse matrix format.
|
|
15
|
+
Use BLAS instead of numpy.dot when possible to avoid unnecessary copies.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
a : array, sparse matrix or LinearOperator
|
|
20
|
+
b : array, sparse matrix or LinearOperator
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
dot_product : array or sparse matrix
|
|
24
|
+
sparse if ``a`` or ``b`` is sparse.
|
|
25
|
+
"""
|
|
26
|
+
if type(a) == np.ndarray:
|
|
27
|
+
return b.T.dot(a.T).T
|
|
28
|
+
if isinstance(a, LinearOperator) and isinstance(b, LinearOperator):
|
|
29
|
+
raise NotImplementedError
|
|
30
|
+
if hasattr(a, 'right_sparse_dot') and type(b) == sparse.csr_matrix:
|
|
31
|
+
if callable(a.right_sparse_dot):
|
|
32
|
+
return a.right_sparse_dot(b)
|
|
33
|
+
if hasattr(b, 'left_sparse_dot') and type(a) == sparse.csr_matrix:
|
|
34
|
+
if callable(b.left_sparse_dot):
|
|
35
|
+
return b.left_sparse_dot(a)
|
|
36
|
+
else:
|
|
37
|
+
return a.dot(b)
|
|
Binary file
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# distutils: language = c++
|
|
2
|
+
# cython: language_level=3
|
|
3
|
+
# cython: linetrace=True
|
|
4
|
+
# distutils: define_macros=CYTHON_TRACE_NOGIL=1
|
|
5
|
+
"""
|
|
6
|
+
Created on Apr 2020
|
|
7
|
+
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
8
|
+
"""
|
|
9
|
+
cimport cython
|
|
10
|
+
from cython.parallel import prange
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@cython.boundscheck(False)
|
|
14
|
+
@cython.wraparound(False)
|
|
15
|
+
def diffusion(int[:] indptr, int[:] indices, float[:] data, float[:] scores, float[:] fluid,
|
|
16
|
+
float damping_factor, int n_iter, float tol):
|
|
17
|
+
"""One loop of fluid diffusion."""
|
|
18
|
+
cdef int n = fluid.shape[0]
|
|
19
|
+
cdef int i
|
|
20
|
+
cdef int j
|
|
21
|
+
cdef int j1
|
|
22
|
+
cdef int j2
|
|
23
|
+
cdef int jj
|
|
24
|
+
cdef float sent
|
|
25
|
+
cdef float tmp
|
|
26
|
+
cdef float removed
|
|
27
|
+
cdef float restart_prob = 1 - damping_factor
|
|
28
|
+
cdef float residu = restart_prob
|
|
29
|
+
|
|
30
|
+
for k in range(n_iter):
|
|
31
|
+
for i in prange(n, nogil=True, schedule='guided'):
|
|
32
|
+
sent = fluid[i]
|
|
33
|
+
if sent > 0:
|
|
34
|
+
scores[i] += sent
|
|
35
|
+
fluid[i] = 0
|
|
36
|
+
j1 = indptr[i]
|
|
37
|
+
j2 = indptr[i+1]
|
|
38
|
+
tmp = sent * damping_factor
|
|
39
|
+
if j2 != j1:
|
|
40
|
+
for jj in range(j1, j2):
|
|
41
|
+
j = indices[jj]
|
|
42
|
+
fluid[j] += tmp * data[jj]
|
|
43
|
+
removed = sent * restart_prob
|
|
44
|
+
else:
|
|
45
|
+
removed = sent
|
|
46
|
+
residu -= removed
|
|
47
|
+
if residu < tol * restart_prob:
|
|
48
|
+
return
|
|
49
|
+
return
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
"""
|
|
4
|
+
Created on July 9 2019
|
|
5
|
+
@author: Nathan De Lara <nathan.delara@polytechnique.org>
|
|
6
|
+
"""
|
|
7
|
+
from abc import ABC
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from scipy import sparse
|
|
12
|
+
from scipy.sparse.linalg import eigsh
|
|
13
|
+
from sknetwork.utils.base import Algorithm
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EigSolver(Algorithm, ABC):
|
|
17
|
+
"""Generic class for eigensolvers.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
which: str
|
|
22
|
+
Which eigenvectors and eigenvalues to find:
|
|
23
|
+
|
|
24
|
+
* ``'LM'`` : Largest (in magnitude) eigenvalues.
|
|
25
|
+
* ``'SM'` : Smallest (in magnitude) eigenvalues.
|
|
26
|
+
|
|
27
|
+
Attributes
|
|
28
|
+
----------
|
|
29
|
+
eigenvectors_: np.ndarray
|
|
30
|
+
Two-dimensional array, each column is an eigenvector of the input.
|
|
31
|
+
eigenvalues_: np.ndarray
|
|
32
|
+
Eigenvalues associated to each eigenvector.
|
|
33
|
+
"""
|
|
34
|
+
def __init__(self, which='LM'):
|
|
35
|
+
self.which = which
|
|
36
|
+
|
|
37
|
+
self.eigenvectors_ = None
|
|
38
|
+
self.eigenvalues_ = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class LanczosEig(EigSolver):
|
|
42
|
+
"""Eigenvalue solver using Lanczos method.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
which : str
|
|
47
|
+
Which eigenvectors and eigenvalues to find:
|
|
48
|
+
|
|
49
|
+
* ``'LM'`` : Largest (in modulus) eigenvalues.
|
|
50
|
+
* ``'SM'`` : Smallest (in modulus) eigenvalues.
|
|
51
|
+
* ``'LA'`` : Largest (algebraic) eigenvalues.
|
|
52
|
+
* ``'SA'`` : Smallest (algebraic) eigenvalues.
|
|
53
|
+
|
|
54
|
+
n_iter : int
|
|
55
|
+
Maximum number of Arnoldi update iterations allowed.
|
|
56
|
+
Default = 10 * nb of rows.
|
|
57
|
+
tol : float
|
|
58
|
+
Relative accuracy for eigenvalues (stopping criterion).
|
|
59
|
+
Default = 0 (machine precision).
|
|
60
|
+
Attributes
|
|
61
|
+
----------
|
|
62
|
+
eigenvectors_: np.ndarray
|
|
63
|
+
Two-dimensional array, each column is an eigenvector of the input.
|
|
64
|
+
eigenvalues_: np.ndarray
|
|
65
|
+
Eigenvalues associated to each eigenvector.
|
|
66
|
+
|
|
67
|
+
See Also
|
|
68
|
+
--------
|
|
69
|
+
scipy.sparse.linalg.eigsh
|
|
70
|
+
"""
|
|
71
|
+
def __init__(self, which='LM', n_iter: int = None, tol: float = 0.):
|
|
72
|
+
super(LanczosEig, self).__init__(which=which)
|
|
73
|
+
self.n_iter = n_iter
|
|
74
|
+
self.tol = tol
|
|
75
|
+
|
|
76
|
+
def fit(self, matrix: Union[sparse.csr_matrix, sparse.linalg.LinearOperator], n_components: int = 2):
|
|
77
|
+
"""Perform spectral decomposition on symmetric input matrix.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
matrix : sparse.csr_matrix or linear operator
|
|
82
|
+
Matrix to decompose.
|
|
83
|
+
n_components : int
|
|
84
|
+
Number of eigenvectors to compute
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
self: :class:`EigSolver`
|
|
89
|
+
"""
|
|
90
|
+
self.eigenvalues_, self.eigenvectors_ = eigsh(matrix.astype(float), n_components, which=self.which,
|
|
91
|
+
maxiter=self.n_iter, tol=self.tol)
|
|
92
|
+
|
|
93
|
+
return self
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created in July 2022
|
|
5
|
+
@author: Thomas Bonald <thomas.bonald@telecom-paris.fr>
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from scipy import sparse
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_laplacian(adjacency: sparse.csr_matrix) -> sparse.csr_matrix:
|
|
13
|
+
"""Return the Laplacian matrix of a graph."""
|
|
14
|
+
weights = adjacency.dot(np.ones(adjacency.shape[0]))
|
|
15
|
+
return sparse.diags(weights) - adjacency
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created in November 2019
|
|
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 LinearOperator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def diag_pinv(weights: np.ndarray) -> sparse.csr_matrix:
|
|
15
|
+
"""Compute :math:`W^+ = \\text{diag}(w)^+`, the pseudo inverse of the diagonal matrix
|
|
16
|
+
with diagonal the weights :math:`w`.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
weights:
|
|
21
|
+
The weights to invert.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
sparse.csr_matrix
|
|
26
|
+
:math:`W^+`
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
diag: sparse.csr_matrix = sparse.diags(weights, format='csr')
|
|
30
|
+
diag.data = 1 / diag.data
|
|
31
|
+
return diag
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def normalize(matrix: Union[sparse.csr_matrix, np.ndarray, LinearOperator], p=1):
|
|
35
|
+
"""Normalize rows of a matrix. Null rows remain null.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
matrix :
|
|
40
|
+
Input matrix.
|
|
41
|
+
p :
|
|
42
|
+
Order of the norm.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
normalized matrix : same as input
|
|
47
|
+
"""
|
|
48
|
+
if p == 1:
|
|
49
|
+
norm = matrix.dot(np.ones(matrix.shape[1]))
|
|
50
|
+
elif p == 2:
|
|
51
|
+
if isinstance(matrix, np.ndarray):
|
|
52
|
+
norm = np.linalg.norm(matrix, axis=1)
|
|
53
|
+
elif isinstance(matrix, sparse.csr_matrix):
|
|
54
|
+
data = matrix.data.copy()
|
|
55
|
+
matrix.data = data ** 2
|
|
56
|
+
norm = np.sqrt(matrix.dot(np.ones(matrix.shape[1])))
|
|
57
|
+
matrix.data = data
|
|
58
|
+
else:
|
|
59
|
+
raise NotImplementedError('Norm 2 is not available for LinearOperator.')
|
|
60
|
+
else:
|
|
61
|
+
raise NotImplementedError('Only norms 1 and 2 are available at the moment.')
|
|
62
|
+
|
|
63
|
+
diag = diag_pinv(norm)
|
|
64
|
+
if hasattr(matrix, 'left_sparse_dot') and callable(matrix.left_sparse_dot):
|
|
65
|
+
return matrix.left_sparse_dot(diag)
|
|
66
|
+
return diag.dot(matrix)
|
|
@@ -0,0 +1,225 @@
|
|
|
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
|
+
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.linalg import diag_pinv
|
|
15
|
+
from sknetwork.linalg.normalization import normalize
|
|
16
|
+
from sknetwork.linalg.sparse_lowrank import SparseLR
|
|
17
|
+
from sknetwork.utils.check import check_format
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Regularizer(SparseLR):
|
|
21
|
+
"""Regularized matrix as a Scipy LinearOperator.
|
|
22
|
+
|
|
23
|
+
Defined by :math:`A + \\alpha \\frac{11^T}n` where :math:`A` is the input matrix
|
|
24
|
+
and :math:`\\alpha` the regularization factor.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
input_matrix :
|
|
29
|
+
Input matrix.
|
|
30
|
+
regularization : float
|
|
31
|
+
Regularization factor.
|
|
32
|
+
Default value = 1.
|
|
33
|
+
|
|
34
|
+
Examples
|
|
35
|
+
--------
|
|
36
|
+
>>> from sknetwork.data import house
|
|
37
|
+
>>> adjacency = house()
|
|
38
|
+
>>> regularizer = Regularizer(adjacency)
|
|
39
|
+
>>> regularizer.dot(np.ones(5))
|
|
40
|
+
array([3., 4., 3., 3., 4.])
|
|
41
|
+
"""
|
|
42
|
+
def __init__(self, input_matrix: Union[sparse.csr_matrix, np.ndarray], regularization: float = 1):
|
|
43
|
+
n_row, n_col = input_matrix.shape
|
|
44
|
+
u = regularization * np.ones(n_row)
|
|
45
|
+
v = np.ones(n_col) / n_col
|
|
46
|
+
super(Regularizer, self).__init__(input_matrix, (u, v))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Normalizer(LinearOperator):
|
|
50
|
+
"""Normalized matrix as a Scipy LinearOperator.
|
|
51
|
+
|
|
52
|
+
Defined by :math:`D^{-1}A` where :math:`A` is the regularized adjacency matrix and :math:`D` the corresponding
|
|
53
|
+
diagonal matrix of degrees (sums over rows).
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
adjacency :
|
|
58
|
+
:term:`Adjacency <adjacency>` matrix of the graph.
|
|
59
|
+
regularization : float
|
|
60
|
+
Regularization factor.
|
|
61
|
+
Default value = 0.
|
|
62
|
+
|
|
63
|
+
Examples
|
|
64
|
+
--------
|
|
65
|
+
>>> from sknetwork.data import house
|
|
66
|
+
>>> adjacency = house()
|
|
67
|
+
>>> normalizer = Normalizer(adjacency)
|
|
68
|
+
>>> normalizer.dot(np.ones(5))
|
|
69
|
+
array([1., 1., 1., 1., 1.])
|
|
70
|
+
"""
|
|
71
|
+
def __init__(self, adjacency: Union[sparse.csr_matrix, np.ndarray], regularization: float = 0):
|
|
72
|
+
if adjacency.ndim == 1:
|
|
73
|
+
adjacency = adjacency.reshape(1, -1)
|
|
74
|
+
super(Normalizer, self).__init__(dtype=float, shape=adjacency.shape)
|
|
75
|
+
n_col = adjacency.shape[1]
|
|
76
|
+
self.regularization = regularization
|
|
77
|
+
self.adjacency = adjacency
|
|
78
|
+
self.norm_diag = diag_pinv(adjacency.dot(np.ones(n_col)) + regularization)
|
|
79
|
+
|
|
80
|
+
def _matvec(self, matrix: np.ndarray):
|
|
81
|
+
prod = self.adjacency.dot(matrix)
|
|
82
|
+
if self.regularization > 0:
|
|
83
|
+
n_row = self.shape[0]
|
|
84
|
+
if matrix.ndim == 2:
|
|
85
|
+
prod += self.regularization * np.outer(np.ones(n_row), matrix.mean(axis=0))
|
|
86
|
+
else:
|
|
87
|
+
prod += self.regularization * matrix.mean() * np.ones(n_row)
|
|
88
|
+
return self.norm_diag.dot(prod)
|
|
89
|
+
|
|
90
|
+
def _transpose(self):
|
|
91
|
+
return self
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class Laplacian(LinearOperator):
|
|
95
|
+
"""Laplacian matrix as a Scipy LinearOperator.
|
|
96
|
+
|
|
97
|
+
Defined by :math:`L = D - A` where :math:`A` is the regularized adjacency matrix and :math:`D` the corresponding
|
|
98
|
+
diagonal matrix of degrees.
|
|
99
|
+
|
|
100
|
+
If normalized, defined by :math:`L = I - D^{-1/2}AD^{-1/2}`.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
adjacency :
|
|
105
|
+
:term:`Adjacency <adjacency>` matrix of the graph.
|
|
106
|
+
regularization : float
|
|
107
|
+
Regularization factor.
|
|
108
|
+
Default value = 0.
|
|
109
|
+
normalized_laplacian : bool
|
|
110
|
+
If ``True``, use normalized Laplacian.
|
|
111
|
+
Default value = ``False``.
|
|
112
|
+
|
|
113
|
+
Examples
|
|
114
|
+
--------
|
|
115
|
+
>>> from sknetwork.data import house
|
|
116
|
+
>>> adjacency = house()
|
|
117
|
+
>>> laplacian = Laplacian(adjacency)
|
|
118
|
+
>>> laplacian.dot(np.ones(5))
|
|
119
|
+
array([0., 0., 0., 0., 0.])
|
|
120
|
+
"""
|
|
121
|
+
def __init__(self, adjacency: Union[sparse.csr_matrix, np.ndarray], regularization: float = 0,
|
|
122
|
+
normalized_laplacian: bool = False):
|
|
123
|
+
super(Laplacian, self).__init__(dtype=float, shape=adjacency.shape)
|
|
124
|
+
n = adjacency.shape[0]
|
|
125
|
+
self.regularization = regularization
|
|
126
|
+
self.normalized_laplacian = normalized_laplacian
|
|
127
|
+
self.weights = adjacency.dot(np.ones(n))
|
|
128
|
+
self.laplacian = sparse.diags(self.weights, format='csr') - adjacency
|
|
129
|
+
if self.normalized_laplacian:
|
|
130
|
+
self.norm_diag = diag_pinv(np.sqrt(self.weights + regularization))
|
|
131
|
+
|
|
132
|
+
def _matvec(self, matrix: np.ndarray):
|
|
133
|
+
if self.normalized_laplacian:
|
|
134
|
+
matrix = self.norm_diag.dot(matrix)
|
|
135
|
+
prod = self.laplacian.dot(matrix)
|
|
136
|
+
if self.regularization > 0:
|
|
137
|
+
n = self.shape[0]
|
|
138
|
+
if matrix.ndim == 2:
|
|
139
|
+
prod += self.regularization * (matrix - np.outer(np.ones(n), matrix.mean(axis=0)))
|
|
140
|
+
else:
|
|
141
|
+
prod += self.regularization * (matrix - matrix.mean())
|
|
142
|
+
if self.normalized_laplacian:
|
|
143
|
+
prod = self.norm_diag.dot(prod)
|
|
144
|
+
return prod
|
|
145
|
+
|
|
146
|
+
def _transpose(self):
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def astype(self, dtype: Union[str, np.dtype]):
|
|
150
|
+
"""Change dtype of the object."""
|
|
151
|
+
self.dtype = np.dtype(dtype)
|
|
152
|
+
self.laplacian = self.laplacian.astype(self.dtype)
|
|
153
|
+
return self
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class CoNeighbor(LinearOperator):
|
|
157
|
+
"""Co-neighborhood adjacency as a LinearOperator.
|
|
158
|
+
|
|
159
|
+
:math:`\\tilde{A} = AF^{-1}A^T`, or :math:`\\tilde{B} = BF^{-1}B^T`.
|
|
160
|
+
|
|
161
|
+
where F is a weight matrix.
|
|
162
|
+
|
|
163
|
+
Parameters
|
|
164
|
+
----------
|
|
165
|
+
adjacency:
|
|
166
|
+
Adjacency or biadjacency of the input graph.
|
|
167
|
+
normalized:
|
|
168
|
+
If ``True``, F is the diagonal in-degree matrix :math:`F = \\text{diag}(A^T1)`.
|
|
169
|
+
Otherwise, F is the identity matrix.
|
|
170
|
+
|
|
171
|
+
Examples
|
|
172
|
+
--------
|
|
173
|
+
>>> from sknetwork.data import star_wars
|
|
174
|
+
>>> biadjacency = star_wars(metadata=False)
|
|
175
|
+
>>> d_out = biadjacency.dot(np.ones(3))
|
|
176
|
+
>>> coneighbor = CoNeighbor(biadjacency)
|
|
177
|
+
>>> np.allclose(d_out, coneighbor.dot(np.ones(4)))
|
|
178
|
+
True
|
|
179
|
+
"""
|
|
180
|
+
def __init__(self, adjacency: Union[sparse.csr_matrix, np.ndarray], normalized: bool = True):
|
|
181
|
+
adjacency = check_format(adjacency).astype(float)
|
|
182
|
+
n = adjacency.shape[0]
|
|
183
|
+
super(CoNeighbor, self).__init__(dtype=float, shape=(n, n))
|
|
184
|
+
|
|
185
|
+
if normalized:
|
|
186
|
+
self.forward = normalize(adjacency.T).tocsr()
|
|
187
|
+
else:
|
|
188
|
+
self.forward = adjacency.T
|
|
189
|
+
|
|
190
|
+
self.backward = adjacency
|
|
191
|
+
|
|
192
|
+
def __neg__(self):
|
|
193
|
+
self.backward *= -1
|
|
194
|
+
return self
|
|
195
|
+
|
|
196
|
+
def __mul__(self, other):
|
|
197
|
+
self.backward *= other
|
|
198
|
+
return self
|
|
199
|
+
|
|
200
|
+
def _matvec(self, matrix: np.ndarray):
|
|
201
|
+
return self.backward.dot(self.forward.dot(matrix))
|
|
202
|
+
|
|
203
|
+
def _transpose(self):
|
|
204
|
+
"""Transposed operator"""
|
|
205
|
+
operator = CoNeighbor(self.backward)
|
|
206
|
+
operator.backward = self.forward.T.tocsr()
|
|
207
|
+
operator.forward = self.backward.T.tocsr()
|
|
208
|
+
return operator
|
|
209
|
+
|
|
210
|
+
def left_sparse_dot(self, matrix: sparse.csr_matrix):
|
|
211
|
+
"""Left dot product with a sparse matrix"""
|
|
212
|
+
self.backward = matrix.dot(self.backward)
|
|
213
|
+
return self
|
|
214
|
+
|
|
215
|
+
def right_sparse_dot(self, matrix: sparse.csr_matrix):
|
|
216
|
+
"""Right dot product with a sparse matrix"""
|
|
217
|
+
self.forward = self.forward.dot(matrix)
|
|
218
|
+
return self
|
|
219
|
+
|
|
220
|
+
def astype(self, dtype: Union[str, np.dtype]):
|
|
221
|
+
"""Change dtype of the object."""
|
|
222
|
+
self.backward.astype(dtype)
|
|
223
|
+
self.forward.astype(dtype)
|
|
224
|
+
self.dtype = dtype
|
|
225
|
+
return self
|
|
@@ -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 an adjacency matrix as a linear operator
|
|
19
|
+
|
|
20
|
+
:math:`P(A) = \\alpha_k A^k + ... + \\alpha_1 A + \\alpha_0`.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
adjacency :
|
|
25
|
+
Adjacency matrix of the graph
|
|
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
|
+
>>> adjacency = sparse.eye(2, format='csr')
|
|
34
|
+
>>> polynome = Polynome(adjacency, 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, adjacency: 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(adjacency, LinearOperator):
|
|
51
|
+
adjacency = check_format(adjacency)
|
|
52
|
+
check_square(adjacency)
|
|
53
|
+
shape = adjacency.shape
|
|
54
|
+
dtype = adjacency.dtype
|
|
55
|
+
super(Polynome, self).__init__(dtype=dtype, shape=shape)
|
|
56
|
+
|
|
57
|
+
self.adjacency = adjacency
|
|
58
|
+
self.coeffs = coeffs
|
|
59
|
+
|
|
60
|
+
def __neg__(self):
|
|
61
|
+
return Polynome(self.adjacency, -self.coeffs)
|
|
62
|
+
|
|
63
|
+
def __mul__(self, other):
|
|
64
|
+
return Polynome(self.adjacency, 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.adjacency.dot(y) + a * matrix
|
|
72
|
+
return y
|
|
73
|
+
|
|
74
|
+
def _transpose(self):
|
|
75
|
+
"""Transposed operator."""
|
|
76
|
+
return Polynome(self.adjacency.T.tocsr(), self.coeffs)
|