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.

Files changed (240) hide show
  1. scikit_network-0.28.3.dist-info/AUTHORS.rst +41 -0
  2. scikit_network-0.28.3.dist-info/LICENSE +34 -0
  3. scikit_network-0.28.3.dist-info/METADATA +457 -0
  4. scikit_network-0.28.3.dist-info/RECORD +240 -0
  5. scikit_network-0.28.3.dist-info/WHEEL +5 -0
  6. scikit_network-0.28.3.dist-info/top_level.txt +1 -0
  7. sknetwork/__init__.py +21 -0
  8. sknetwork/classification/__init__.py +8 -0
  9. sknetwork/classification/base.py +84 -0
  10. sknetwork/classification/base_rank.py +143 -0
  11. sknetwork/classification/diffusion.py +134 -0
  12. sknetwork/classification/knn.py +162 -0
  13. sknetwork/classification/metrics.py +205 -0
  14. sknetwork/classification/pagerank.py +66 -0
  15. sknetwork/classification/propagation.py +152 -0
  16. sknetwork/classification/tests/__init__.py +1 -0
  17. sknetwork/classification/tests/test_API.py +35 -0
  18. sknetwork/classification/tests/test_diffusion.py +37 -0
  19. sknetwork/classification/tests/test_knn.py +24 -0
  20. sknetwork/classification/tests/test_metrics.py +53 -0
  21. sknetwork/classification/tests/test_pagerank.py +20 -0
  22. sknetwork/classification/tests/test_propagation.py +24 -0
  23. sknetwork/classification/vote.cpython-39-darwin.so +0 -0
  24. sknetwork/classification/vote.pyx +58 -0
  25. sknetwork/clustering/__init__.py +7 -0
  26. sknetwork/clustering/base.py +102 -0
  27. sknetwork/clustering/kmeans.py +142 -0
  28. sknetwork/clustering/louvain.py +255 -0
  29. sknetwork/clustering/louvain_core.cpython-39-darwin.so +0 -0
  30. sknetwork/clustering/louvain_core.pyx +134 -0
  31. sknetwork/clustering/metrics.py +91 -0
  32. sknetwork/clustering/postprocess.py +66 -0
  33. sknetwork/clustering/propagation_clustering.py +108 -0
  34. sknetwork/clustering/tests/__init__.py +1 -0
  35. sknetwork/clustering/tests/test_API.py +37 -0
  36. sknetwork/clustering/tests/test_kmeans.py +47 -0
  37. sknetwork/clustering/tests/test_louvain.py +104 -0
  38. sknetwork/clustering/tests/test_metrics.py +50 -0
  39. sknetwork/clustering/tests/test_post_processing.py +23 -0
  40. sknetwork/clustering/tests/test_postprocess.py +39 -0
  41. sknetwork/data/__init__.py +5 -0
  42. sknetwork/data/load.py +408 -0
  43. sknetwork/data/models.py +459 -0
  44. sknetwork/data/parse.py +621 -0
  45. sknetwork/data/test_graphs.py +84 -0
  46. sknetwork/data/tests/__init__.py +1 -0
  47. sknetwork/data/tests/test_API.py +30 -0
  48. sknetwork/data/tests/test_load.py +95 -0
  49. sknetwork/data/tests/test_models.py +52 -0
  50. sknetwork/data/tests/test_parse.py +253 -0
  51. sknetwork/data/tests/test_test_graphs.py +30 -0
  52. sknetwork/data/tests/test_toy_graphs.py +68 -0
  53. sknetwork/data/toy_graphs.py +619 -0
  54. sknetwork/embedding/__init__.py +10 -0
  55. sknetwork/embedding/base.py +90 -0
  56. sknetwork/embedding/force_atlas.py +197 -0
  57. sknetwork/embedding/louvain_embedding.py +174 -0
  58. sknetwork/embedding/louvain_hierarchy.py +142 -0
  59. sknetwork/embedding/metrics.py +66 -0
  60. sknetwork/embedding/random_projection.py +133 -0
  61. sknetwork/embedding/spectral.py +214 -0
  62. sknetwork/embedding/spring.py +198 -0
  63. sknetwork/embedding/svd.py +363 -0
  64. sknetwork/embedding/tests/__init__.py +1 -0
  65. sknetwork/embedding/tests/test_API.py +73 -0
  66. sknetwork/embedding/tests/test_force_atlas.py +35 -0
  67. sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
  68. sknetwork/embedding/tests/test_louvain_hierarchy.py +19 -0
  69. sknetwork/embedding/tests/test_metrics.py +29 -0
  70. sknetwork/embedding/tests/test_random_projection.py +28 -0
  71. sknetwork/embedding/tests/test_spectral.py +84 -0
  72. sknetwork/embedding/tests/test_spring.py +50 -0
  73. sknetwork/embedding/tests/test_svd.py +37 -0
  74. sknetwork/flow/__init__.py +3 -0
  75. sknetwork/flow/flow.py +73 -0
  76. sknetwork/flow/tests/__init__.py +1 -0
  77. sknetwork/flow/tests/test_flow.py +17 -0
  78. sknetwork/flow/tests/test_utils.py +69 -0
  79. sknetwork/flow/utils.py +91 -0
  80. sknetwork/gnn/__init__.py +10 -0
  81. sknetwork/gnn/activation.py +117 -0
  82. sknetwork/gnn/base.py +155 -0
  83. sknetwork/gnn/base_activation.py +89 -0
  84. sknetwork/gnn/base_layer.py +109 -0
  85. sknetwork/gnn/gnn_classifier.py +381 -0
  86. sknetwork/gnn/layer.py +153 -0
  87. sknetwork/gnn/layers.py +127 -0
  88. sknetwork/gnn/loss.py +180 -0
  89. sknetwork/gnn/neighbor_sampler.py +65 -0
  90. sknetwork/gnn/optimizer.py +163 -0
  91. sknetwork/gnn/tests/__init__.py +1 -0
  92. sknetwork/gnn/tests/test_activation.py +56 -0
  93. sknetwork/gnn/tests/test_base.py +79 -0
  94. sknetwork/gnn/tests/test_base_layer.py +37 -0
  95. sknetwork/gnn/tests/test_gnn_classifier.py +192 -0
  96. sknetwork/gnn/tests/test_layers.py +80 -0
  97. sknetwork/gnn/tests/test_loss.py +33 -0
  98. sknetwork/gnn/tests/test_neigh_sampler.py +23 -0
  99. sknetwork/gnn/tests/test_optimizer.py +43 -0
  100. sknetwork/gnn/tests/test_utils.py +93 -0
  101. sknetwork/gnn/utils.py +219 -0
  102. sknetwork/hierarchy/__init__.py +7 -0
  103. sknetwork/hierarchy/base.py +69 -0
  104. sknetwork/hierarchy/louvain_hierarchy.py +264 -0
  105. sknetwork/hierarchy/metrics.py +234 -0
  106. sknetwork/hierarchy/paris.cpython-39-darwin.so +0 -0
  107. sknetwork/hierarchy/paris.pyx +317 -0
  108. sknetwork/hierarchy/postprocess.py +350 -0
  109. sknetwork/hierarchy/tests/__init__.py +1 -0
  110. sknetwork/hierarchy/tests/test_API.py +25 -0
  111. sknetwork/hierarchy/tests/test_algos.py +29 -0
  112. sknetwork/hierarchy/tests/test_metrics.py +62 -0
  113. sknetwork/hierarchy/tests/test_postprocess.py +57 -0
  114. sknetwork/hierarchy/tests/test_ward.py +25 -0
  115. sknetwork/hierarchy/ward.py +94 -0
  116. sknetwork/linalg/__init__.py +9 -0
  117. sknetwork/linalg/basics.py +37 -0
  118. sknetwork/linalg/diteration.cpython-39-darwin.so +0 -0
  119. sknetwork/linalg/diteration.pyx +49 -0
  120. sknetwork/linalg/eig_solver.py +93 -0
  121. sknetwork/linalg/laplacian.py +15 -0
  122. sknetwork/linalg/normalization.py +66 -0
  123. sknetwork/linalg/operators.py +225 -0
  124. sknetwork/linalg/polynome.py +76 -0
  125. sknetwork/linalg/ppr_solver.py +170 -0
  126. sknetwork/linalg/push.cpython-39-darwin.so +0 -0
  127. sknetwork/linalg/push.pyx +73 -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 +38 -0
  134. sknetwork/linalg/tests/test_operators.py +70 -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 +4 -0
  140. sknetwork/linkpred/base.py +80 -0
  141. sknetwork/linkpred/first_order.py +508 -0
  142. sknetwork/linkpred/first_order_core.cpython-39-darwin.so +0 -0
  143. sknetwork/linkpred/first_order_core.pyx +315 -0
  144. sknetwork/linkpred/postprocessing.py +98 -0
  145. sknetwork/linkpred/tests/__init__.py +1 -0
  146. sknetwork/linkpred/tests/test_API.py +49 -0
  147. sknetwork/linkpred/tests/test_postprocessing.py +21 -0
  148. sknetwork/path/__init__.py +4 -0
  149. sknetwork/path/metrics.py +148 -0
  150. sknetwork/path/search.py +65 -0
  151. sknetwork/path/shortest_path.py +186 -0
  152. sknetwork/path/tests/__init__.py +1 -0
  153. sknetwork/path/tests/test_metrics.py +29 -0
  154. sknetwork/path/tests/test_search.py +25 -0
  155. sknetwork/path/tests/test_shortest_path.py +45 -0
  156. sknetwork/ranking/__init__.py +9 -0
  157. sknetwork/ranking/base.py +56 -0
  158. sknetwork/ranking/betweenness.cpython-39-darwin.so +0 -0
  159. sknetwork/ranking/betweenness.pyx +99 -0
  160. sknetwork/ranking/closeness.py +95 -0
  161. sknetwork/ranking/harmonic.py +82 -0
  162. sknetwork/ranking/hits.py +94 -0
  163. sknetwork/ranking/katz.py +81 -0
  164. sknetwork/ranking/pagerank.py +107 -0
  165. sknetwork/ranking/postprocess.py +25 -0
  166. sknetwork/ranking/tests/__init__.py +1 -0
  167. sknetwork/ranking/tests/test_API.py +34 -0
  168. sknetwork/ranking/tests/test_betweenness.py +38 -0
  169. sknetwork/ranking/tests/test_closeness.py +34 -0
  170. sknetwork/ranking/tests/test_hits.py +20 -0
  171. sknetwork/ranking/tests/test_pagerank.py +69 -0
  172. sknetwork/regression/__init__.py +4 -0
  173. sknetwork/regression/base.py +56 -0
  174. sknetwork/regression/diffusion.py +190 -0
  175. sknetwork/regression/tests/__init__.py +1 -0
  176. sknetwork/regression/tests/test_API.py +34 -0
  177. sknetwork/regression/tests/test_diffusion.py +48 -0
  178. sknetwork/sknetwork.py +3 -0
  179. sknetwork/topology/__init__.py +9 -0
  180. sknetwork/topology/dag.py +74 -0
  181. sknetwork/topology/dag_core.cpython-39-darwin.so +0 -0
  182. sknetwork/topology/dag_core.pyx +38 -0
  183. sknetwork/topology/kcliques.cpython-39-darwin.so +0 -0
  184. sknetwork/topology/kcliques.pyx +193 -0
  185. sknetwork/topology/kcore.cpython-39-darwin.so +0 -0
  186. sknetwork/topology/kcore.pyx +120 -0
  187. sknetwork/topology/structure.py +234 -0
  188. sknetwork/topology/tests/__init__.py +1 -0
  189. sknetwork/topology/tests/test_cliques.py +28 -0
  190. sknetwork/topology/tests/test_cores.py +21 -0
  191. sknetwork/topology/tests/test_dag.py +26 -0
  192. sknetwork/topology/tests/test_structure.py +99 -0
  193. sknetwork/topology/tests/test_triangles.py +42 -0
  194. sknetwork/topology/tests/test_wl_coloring.py +49 -0
  195. sknetwork/topology/tests/test_wl_kernel.py +31 -0
  196. sknetwork/topology/triangles.cpython-39-darwin.so +0 -0
  197. sknetwork/topology/triangles.pyx +166 -0
  198. sknetwork/topology/weisfeiler_lehman.py +163 -0
  199. sknetwork/topology/weisfeiler_lehman_core.cpython-39-darwin.so +0 -0
  200. sknetwork/topology/weisfeiler_lehman_core.pyx +116 -0
  201. sknetwork/utils/__init__.py +40 -0
  202. sknetwork/utils/base.py +35 -0
  203. sknetwork/utils/check.py +354 -0
  204. sknetwork/utils/co_neighbor.py +71 -0
  205. sknetwork/utils/format.py +219 -0
  206. sknetwork/utils/kmeans.py +89 -0
  207. sknetwork/utils/knn.py +166 -0
  208. sknetwork/utils/knn1d.cpython-39-darwin.so +0 -0
  209. sknetwork/utils/knn1d.pyx +80 -0
  210. sknetwork/utils/membership.py +82 -0
  211. sknetwork/utils/minheap.cpython-39-darwin.so +0 -0
  212. sknetwork/utils/minheap.pxd +22 -0
  213. sknetwork/utils/minheap.pyx +111 -0
  214. sknetwork/utils/neighbors.py +115 -0
  215. sknetwork/utils/seeds.py +75 -0
  216. sknetwork/utils/simplex.py +140 -0
  217. sknetwork/utils/tests/__init__.py +1 -0
  218. sknetwork/utils/tests/test_base.py +28 -0
  219. sknetwork/utils/tests/test_bunch.py +16 -0
  220. sknetwork/utils/tests/test_check.py +190 -0
  221. sknetwork/utils/tests/test_co_neighbor.py +43 -0
  222. sknetwork/utils/tests/test_format.py +61 -0
  223. sknetwork/utils/tests/test_kmeans.py +21 -0
  224. sknetwork/utils/tests/test_knn.py +32 -0
  225. sknetwork/utils/tests/test_membership.py +24 -0
  226. sknetwork/utils/tests/test_neighbors.py +41 -0
  227. sknetwork/utils/tests/test_projection_simplex.py +33 -0
  228. sknetwork/utils/tests/test_seeds.py +67 -0
  229. sknetwork/utils/tests/test_verbose.py +15 -0
  230. sknetwork/utils/tests/test_ward.py +20 -0
  231. sknetwork/utils/timeout.py +38 -0
  232. sknetwork/utils/verbose.py +37 -0
  233. sknetwork/utils/ward.py +60 -0
  234. sknetwork/visualization/__init__.py +4 -0
  235. sknetwork/visualization/colors.py +34 -0
  236. sknetwork/visualization/dendrograms.py +229 -0
  237. sknetwork/visualization/graphs.py +819 -0
  238. sknetwork/visualization/tests/__init__.py +1 -0
  239. sknetwork/visualization/tests/test_dendrograms.py +53 -0
  240. sknetwork/visualization/tests/test_graphs.py +167 -0
@@ -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.normalization 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()
@@ -0,0 +1,73 @@
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 Mars 2021
7
+ @author: Wenzhuo Zhao <wenzhuo.zhao@etu.sorbonne-universite.fr>
8
+ """
9
+ from libcpp.queue cimport queue
10
+ from cython.parallel cimport prange
11
+ import numpy as np
12
+ cimport numpy as cnp
13
+ cimport cython
14
+
15
+
16
+ @cython.boundscheck(False)
17
+ @cython.wraparound(False)
18
+ def push_pagerank(int n, cnp.ndarray[cnp.int32_t, ndim=1] degrees,
19
+ int[:] indptr, int[:] indices,
20
+ int[:] rev_indptr, int[:] rev_indices,
21
+ cnp.ndarray[cnp.float32_t, ndim=1] seeds,
22
+ cnp.float32_t damping_factor, cnp.float32_t tol):
23
+ """Push-based PageRank"""
24
+ cdef cnp.ndarray[cnp.float32_t, ndim=1] residuals
25
+ cdef int vertex
26
+ cdef int neighbor
27
+ cdef int j1
28
+ cdef int j2
29
+ cdef int j
30
+ cdef int[:] indexes
31
+ cdef int index
32
+ cdef float probability
33
+ cdef queue[int] worklist
34
+ cdef cnp.ndarray[cnp.float32_t, ndim=1] scores
35
+ cdef cnp.float32_t tmp
36
+ cdef float norm
37
+
38
+ residuals = np.zeros(n, dtype=np.float32)
39
+ for vertex in prange(n, nogil=True):
40
+ j1 = rev_indptr[vertex]
41
+ j2 = rev_indptr[vertex + 1]
42
+ # iterate node's in-coming neighbors
43
+ for j in range(j1, j2):
44
+ neighbor = rev_indices[j]
45
+ residuals[vertex] += 1 / degrees[neighbor]
46
+ """add the probability of seeds"""
47
+ residuals[vertex] *= (1 - damping_factor) * \
48
+ damping_factor * (1 + seeds[vertex])
49
+
50
+ # node with high residual value will be processed first
51
+ indexes = np.argsort(-residuals).astype(np.int32)
52
+ for index in indexes:
53
+ worklist.push(index)
54
+ scores = np.full(n, (1 - damping_factor), dtype=np.float32)
55
+
56
+ while not worklist.empty():
57
+ vertex = worklist.front()
58
+ worklist.pop()
59
+ # scores[v]_new
60
+ scores[vertex] += residuals[vertex]
61
+ # iterate node's out-coming neighbors
62
+ j1 = indptr[vertex]
63
+ j2 = indptr[vertex + 1]
64
+ for j in prange(j1, j2, nogil=True):
65
+ neighbor = indices[j]
66
+ tmp = residuals[neighbor]
67
+ residuals[neighbor] += residuals[vertex] * \
68
+ (1 - damping_factor) / degrees[vertex]
69
+ if residuals[neighbor] > tol > tmp:
70
+ worklist.push(neighbor)
71
+ norm = np.linalg.norm(scores, 1)
72
+ scores /= norm
73
+ 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.utils.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,38 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on 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, CoNeighbor
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
+ mat3 = normalize(CoNeighbor(mat2))
23
+
24
+ x = np.random.randn(n)
25
+ self.assertAlmostEqual(np.linalg.norm(mat1.dot(x) - x), 0)
26
+ self.assertAlmostEqual(np.linalg.norm(mat2.dot(x) - x), 0)
27
+ self.assertAlmostEqual(np.linalg.norm(mat3.dot(x) - x), 0)
28
+
29
+ mat1 = np.random.rand(n**2).reshape((n, n))
30
+ mat2 = sparse.csr_matrix(mat1)
31
+ mat1 = normalize(mat1, p=2)
32
+ mat2 = normalize(mat2, p=2)
33
+ self.assertAlmostEqual(np.linalg.norm(mat1.dot(x) - mat2.dot(x)), 0)
34
+
35
+ with self.assertRaises(NotImplementedError):
36
+ normalize(mat3, p=2)
37
+ with self.assertRaises(NotImplementedError):
38
+ normalize(mat1, p=3)
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on Apr 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, normalize
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_graph_disconnect()]:
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_graph_disconnect()]:
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
+ transition = normalize(operator)
57
+ x = transition.dot(np.ones(transition.shape[1]))
58
+
59
+ self.assertAlmostEqual(np.linalg.norm(x - np.ones(operator.shape[0])), 0)
60
+ operator.astype('float')
61
+ operator.right_sparse_dot(sparse.eye(operator.shape[1], format='csr'))
62
+
63
+ operator1 = CoNeighbor(biadjacency, normalized=False)
64
+ operator2 = CoNeighbor(biadjacency, normalized=False)
65
+ x = np.random.randn(operator.shape[1])
66
+ x1 = (-operator1).dot(x)
67
+ x2 = (operator2 * -1).dot(x)
68
+ x3 = operator1.T.dot(x)
69
+ self.assertAlmostEqual(np.linalg.norm(x1 - x2), 0)
70
+ 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
+