scikit-network 0.33.3__cp313-cp313-win_amd64.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.

Files changed (228) hide show
  1. scikit_network-0.33.3.dist-info/METADATA +122 -0
  2. scikit_network-0.33.3.dist-info/RECORD +228 -0
  3. scikit_network-0.33.3.dist-info/WHEEL +5 -0
  4. scikit_network-0.33.3.dist-info/licenses/AUTHORS.rst +43 -0
  5. scikit_network-0.33.3.dist-info/licenses/LICENSE +34 -0
  6. scikit_network-0.33.3.dist-info/top_level.txt +1 -0
  7. sknetwork/__init__.py +21 -0
  8. sknetwork/base.py +67 -0
  9. sknetwork/classification/__init__.py +8 -0
  10. sknetwork/classification/base.py +142 -0
  11. sknetwork/classification/base_rank.py +133 -0
  12. sknetwork/classification/diffusion.py +134 -0
  13. sknetwork/classification/knn.py +139 -0
  14. sknetwork/classification/metrics.py +205 -0
  15. sknetwork/classification/pagerank.py +66 -0
  16. sknetwork/classification/propagation.py +152 -0
  17. sknetwork/classification/tests/__init__.py +1 -0
  18. sknetwork/classification/tests/test_API.py +30 -0
  19. sknetwork/classification/tests/test_diffusion.py +77 -0
  20. sknetwork/classification/tests/test_knn.py +23 -0
  21. sknetwork/classification/tests/test_metrics.py +53 -0
  22. sknetwork/classification/tests/test_pagerank.py +20 -0
  23. sknetwork/classification/tests/test_propagation.py +24 -0
  24. sknetwork/classification/vote.cp313-win_amd64.pyd +0 -0
  25. sknetwork/classification/vote.cpp +27584 -0
  26. sknetwork/classification/vote.pyx +56 -0
  27. sknetwork/clustering/__init__.py +8 -0
  28. sknetwork/clustering/base.py +172 -0
  29. sknetwork/clustering/kcenters.py +253 -0
  30. sknetwork/clustering/leiden.py +242 -0
  31. sknetwork/clustering/leiden_core.cp313-win_amd64.pyd +0 -0
  32. sknetwork/clustering/leiden_core.cpp +31575 -0
  33. sknetwork/clustering/leiden_core.pyx +124 -0
  34. sknetwork/clustering/louvain.py +286 -0
  35. sknetwork/clustering/louvain_core.cp313-win_amd64.pyd +0 -0
  36. sknetwork/clustering/louvain_core.cpp +31220 -0
  37. sknetwork/clustering/louvain_core.pyx +124 -0
  38. sknetwork/clustering/metrics.py +91 -0
  39. sknetwork/clustering/postprocess.py +66 -0
  40. sknetwork/clustering/propagation_clustering.py +104 -0
  41. sknetwork/clustering/tests/__init__.py +1 -0
  42. sknetwork/clustering/tests/test_API.py +38 -0
  43. sknetwork/clustering/tests/test_kcenters.py +60 -0
  44. sknetwork/clustering/tests/test_leiden.py +34 -0
  45. sknetwork/clustering/tests/test_louvain.py +135 -0
  46. sknetwork/clustering/tests/test_metrics.py +50 -0
  47. sknetwork/clustering/tests/test_postprocess.py +39 -0
  48. sknetwork/data/__init__.py +6 -0
  49. sknetwork/data/base.py +33 -0
  50. sknetwork/data/load.py +406 -0
  51. sknetwork/data/models.py +459 -0
  52. sknetwork/data/parse.py +644 -0
  53. sknetwork/data/test_graphs.py +84 -0
  54. sknetwork/data/tests/__init__.py +1 -0
  55. sknetwork/data/tests/test_API.py +30 -0
  56. sknetwork/data/tests/test_base.py +14 -0
  57. sknetwork/data/tests/test_load.py +95 -0
  58. sknetwork/data/tests/test_models.py +52 -0
  59. sknetwork/data/tests/test_parse.py +250 -0
  60. sknetwork/data/tests/test_test_graphs.py +29 -0
  61. sknetwork/data/tests/test_toy_graphs.py +68 -0
  62. sknetwork/data/timeout.py +38 -0
  63. sknetwork/data/toy_graphs.py +611 -0
  64. sknetwork/embedding/__init__.py +8 -0
  65. sknetwork/embedding/base.py +94 -0
  66. sknetwork/embedding/force_atlas.py +198 -0
  67. sknetwork/embedding/louvain_embedding.py +148 -0
  68. sknetwork/embedding/random_projection.py +135 -0
  69. sknetwork/embedding/spectral.py +141 -0
  70. sknetwork/embedding/spring.py +198 -0
  71. sknetwork/embedding/svd.py +359 -0
  72. sknetwork/embedding/tests/__init__.py +1 -0
  73. sknetwork/embedding/tests/test_API.py +49 -0
  74. sknetwork/embedding/tests/test_force_atlas.py +35 -0
  75. sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
  76. sknetwork/embedding/tests/test_random_projection.py +28 -0
  77. sknetwork/embedding/tests/test_spectral.py +81 -0
  78. sknetwork/embedding/tests/test_spring.py +50 -0
  79. sknetwork/embedding/tests/test_svd.py +43 -0
  80. sknetwork/gnn/__init__.py +10 -0
  81. sknetwork/gnn/activation.py +117 -0
  82. sknetwork/gnn/base.py +181 -0
  83. sknetwork/gnn/base_activation.py +90 -0
  84. sknetwork/gnn/base_layer.py +109 -0
  85. sknetwork/gnn/gnn_classifier.py +305 -0
  86. sknetwork/gnn/layer.py +153 -0
  87. sknetwork/gnn/loss.py +180 -0
  88. sknetwork/gnn/neighbor_sampler.py +65 -0
  89. sknetwork/gnn/optimizer.py +164 -0
  90. sknetwork/gnn/tests/__init__.py +1 -0
  91. sknetwork/gnn/tests/test_activation.py +56 -0
  92. sknetwork/gnn/tests/test_base.py +75 -0
  93. sknetwork/gnn/tests/test_base_layer.py +37 -0
  94. sknetwork/gnn/tests/test_gnn_classifier.py +130 -0
  95. sknetwork/gnn/tests/test_layers.py +80 -0
  96. sknetwork/gnn/tests/test_loss.py +33 -0
  97. sknetwork/gnn/tests/test_neigh_sampler.py +23 -0
  98. sknetwork/gnn/tests/test_optimizer.py +43 -0
  99. sknetwork/gnn/tests/test_utils.py +41 -0
  100. sknetwork/gnn/utils.py +127 -0
  101. sknetwork/hierarchy/__init__.py +6 -0
  102. sknetwork/hierarchy/base.py +96 -0
  103. sknetwork/hierarchy/louvain_hierarchy.py +272 -0
  104. sknetwork/hierarchy/metrics.py +234 -0
  105. sknetwork/hierarchy/paris.cp313-win_amd64.pyd +0 -0
  106. sknetwork/hierarchy/paris.cpp +37868 -0
  107. sknetwork/hierarchy/paris.pyx +316 -0
  108. sknetwork/hierarchy/postprocess.py +350 -0
  109. sknetwork/hierarchy/tests/__init__.py +1 -0
  110. sknetwork/hierarchy/tests/test_API.py +24 -0
  111. sknetwork/hierarchy/tests/test_algos.py +34 -0
  112. sknetwork/hierarchy/tests/test_metrics.py +62 -0
  113. sknetwork/hierarchy/tests/test_postprocess.py +57 -0
  114. sknetwork/linalg/__init__.py +9 -0
  115. sknetwork/linalg/basics.py +37 -0
  116. sknetwork/linalg/diteration.cp313-win_amd64.pyd +0 -0
  117. sknetwork/linalg/diteration.cpp +27400 -0
  118. sknetwork/linalg/diteration.pyx +47 -0
  119. sknetwork/linalg/eig_solver.py +93 -0
  120. sknetwork/linalg/laplacian.py +15 -0
  121. sknetwork/linalg/normalizer.py +86 -0
  122. sknetwork/linalg/operators.py +225 -0
  123. sknetwork/linalg/polynome.py +76 -0
  124. sknetwork/linalg/ppr_solver.py +170 -0
  125. sknetwork/linalg/push.cp313-win_amd64.pyd +0 -0
  126. sknetwork/linalg/push.cpp +31072 -0
  127. sknetwork/linalg/push.pyx +71 -0
  128. sknetwork/linalg/sparse_lowrank.py +142 -0
  129. sknetwork/linalg/svd_solver.py +91 -0
  130. sknetwork/linalg/tests/__init__.py +1 -0
  131. sknetwork/linalg/tests/test_eig.py +44 -0
  132. sknetwork/linalg/tests/test_laplacian.py +18 -0
  133. sknetwork/linalg/tests/test_normalization.py +34 -0
  134. sknetwork/linalg/tests/test_operators.py +66 -0
  135. sknetwork/linalg/tests/test_polynome.py +38 -0
  136. sknetwork/linalg/tests/test_ppr.py +50 -0
  137. sknetwork/linalg/tests/test_sparse_lowrank.py +61 -0
  138. sknetwork/linalg/tests/test_svd.py +38 -0
  139. sknetwork/linkpred/__init__.py +2 -0
  140. sknetwork/linkpred/base.py +46 -0
  141. sknetwork/linkpred/nn.py +126 -0
  142. sknetwork/linkpred/tests/__init__.py +1 -0
  143. sknetwork/linkpred/tests/test_nn.py +27 -0
  144. sknetwork/log.py +19 -0
  145. sknetwork/path/__init__.py +5 -0
  146. sknetwork/path/dag.py +54 -0
  147. sknetwork/path/distances.py +98 -0
  148. sknetwork/path/search.py +31 -0
  149. sknetwork/path/shortest_path.py +61 -0
  150. sknetwork/path/tests/__init__.py +1 -0
  151. sknetwork/path/tests/test_dag.py +37 -0
  152. sknetwork/path/tests/test_distances.py +62 -0
  153. sknetwork/path/tests/test_search.py +40 -0
  154. sknetwork/path/tests/test_shortest_path.py +40 -0
  155. sknetwork/ranking/__init__.py +8 -0
  156. sknetwork/ranking/base.py +61 -0
  157. sknetwork/ranking/betweenness.cp313-win_amd64.pyd +0 -0
  158. sknetwork/ranking/betweenness.cpp +9707 -0
  159. sknetwork/ranking/betweenness.pyx +97 -0
  160. sknetwork/ranking/closeness.py +92 -0
  161. sknetwork/ranking/hits.py +94 -0
  162. sknetwork/ranking/katz.py +83 -0
  163. sknetwork/ranking/pagerank.py +110 -0
  164. sknetwork/ranking/postprocess.py +37 -0
  165. sknetwork/ranking/tests/__init__.py +1 -0
  166. sknetwork/ranking/tests/test_API.py +32 -0
  167. sknetwork/ranking/tests/test_betweenness.py +38 -0
  168. sknetwork/ranking/tests/test_closeness.py +30 -0
  169. sknetwork/ranking/tests/test_hits.py +20 -0
  170. sknetwork/ranking/tests/test_pagerank.py +62 -0
  171. sknetwork/ranking/tests/test_postprocess.py +26 -0
  172. sknetwork/regression/__init__.py +4 -0
  173. sknetwork/regression/base.py +61 -0
  174. sknetwork/regression/diffusion.py +210 -0
  175. sknetwork/regression/tests/__init__.py +1 -0
  176. sknetwork/regression/tests/test_API.py +32 -0
  177. sknetwork/regression/tests/test_diffusion.py +56 -0
  178. sknetwork/sknetwork.py +3 -0
  179. sknetwork/test_base.py +35 -0
  180. sknetwork/test_log.py +15 -0
  181. sknetwork/topology/__init__.py +8 -0
  182. sknetwork/topology/cliques.cp313-win_amd64.pyd +0 -0
  183. sknetwork/topology/cliques.cpp +32565 -0
  184. sknetwork/topology/cliques.pyx +149 -0
  185. sknetwork/topology/core.cp313-win_amd64.pyd +0 -0
  186. sknetwork/topology/core.cpp +30651 -0
  187. sknetwork/topology/core.pyx +90 -0
  188. sknetwork/topology/cycles.py +243 -0
  189. sknetwork/topology/minheap.cp313-win_amd64.pyd +0 -0
  190. sknetwork/topology/minheap.cpp +27332 -0
  191. sknetwork/topology/minheap.pxd +20 -0
  192. sknetwork/topology/minheap.pyx +109 -0
  193. sknetwork/topology/structure.py +194 -0
  194. sknetwork/topology/tests/__init__.py +1 -0
  195. sknetwork/topology/tests/test_cliques.py +28 -0
  196. sknetwork/topology/tests/test_core.py +19 -0
  197. sknetwork/topology/tests/test_cycles.py +65 -0
  198. sknetwork/topology/tests/test_structure.py +85 -0
  199. sknetwork/topology/tests/test_triangles.py +38 -0
  200. sknetwork/topology/tests/test_wl.py +72 -0
  201. sknetwork/topology/triangles.cp313-win_amd64.pyd +0 -0
  202. sknetwork/topology/triangles.cpp +8894 -0
  203. sknetwork/topology/triangles.pyx +151 -0
  204. sknetwork/topology/weisfeiler_lehman.py +133 -0
  205. sknetwork/topology/weisfeiler_lehman_core.cp313-win_amd64.pyd +0 -0
  206. sknetwork/topology/weisfeiler_lehman_core.cpp +27635 -0
  207. sknetwork/topology/weisfeiler_lehman_core.pyx +114 -0
  208. sknetwork/utils/__init__.py +7 -0
  209. sknetwork/utils/check.py +355 -0
  210. sknetwork/utils/format.py +221 -0
  211. sknetwork/utils/membership.py +82 -0
  212. sknetwork/utils/neighbors.py +115 -0
  213. sknetwork/utils/tests/__init__.py +1 -0
  214. sknetwork/utils/tests/test_check.py +190 -0
  215. sknetwork/utils/tests/test_format.py +63 -0
  216. sknetwork/utils/tests/test_membership.py +24 -0
  217. sknetwork/utils/tests/test_neighbors.py +41 -0
  218. sknetwork/utils/tests/test_tfidf.py +18 -0
  219. sknetwork/utils/tests/test_values.py +66 -0
  220. sknetwork/utils/tfidf.py +37 -0
  221. sknetwork/utils/values.py +76 -0
  222. sknetwork/visualization/__init__.py +4 -0
  223. sknetwork/visualization/colors.py +34 -0
  224. sknetwork/visualization/dendrograms.py +277 -0
  225. sknetwork/visualization/graphs.py +1039 -0
  226. sknetwork/visualization/tests/__init__.py +1 -0
  227. sknetwork/visualization/tests/test_dendrograms.py +53 -0
  228. sknetwork/visualization/tests/test_graphs.py +176 -0
@@ -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)
@@ -0,0 +1,2 @@
1
+ """link prediction module"""
2
+ from sknetwork.linkpred.nn import NNLinker