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,363 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in May 2018
5
+ @author: Nathan de Lara <nathan.delara@polytechnique.org>
6
+ @author: Thomas Bonald <bonald@enst.fr>
7
+ """
8
+ from typing import Union
9
+
10
+ import numpy as np
11
+ from scipy import sparse
12
+
13
+ from sknetwork.embedding.base import BaseEmbedding
14
+ from sknetwork.linalg import SVDSolver, LanczosSVD, safe_sparse_dot, diag_pinv, normalize, Regularizer, SparseLR
15
+ from sknetwork.utils.check import check_format, check_adjacency_vector, check_nonnegative, check_n_components
16
+
17
+
18
+ class GSVD(BaseEmbedding):
19
+ """Graph embedding by Generalized Singular Value Decomposition of the adjacency or biadjacency matrix :math:`A`.
20
+ This is equivalent to the Singular Value Decomposition of the matrix :math:`D_1^{- \\alpha_1}AD_2^{- \\alpha_2}`
21
+ where :math:`D_1, D_2` are the diagonal matrices of row weights and columns weights, respectively, and
22
+ :math:`\\alpha_1, \\alpha_2` are parameters.
23
+
24
+ Parameters
25
+ -----------
26
+ n_components : int
27
+ Dimension of the embedding.
28
+ regularization : ``None`` or float (default = ``None``)
29
+ Regularization factor :math:`\\alpha` so that the matrix is :math:`A + \\alpha \\frac{11^T}{n}`.
30
+ factor_row : float (default = 0.5)
31
+ Power factor :math:`\\alpha_1` applied to the diagonal matrix of row weights.
32
+ factor_col : float (default = 0.5)
33
+ Power factor :math:`\\alpha_2` applied to the diagonal matrix of column weights.
34
+ factor_singular : float (default = 0.)
35
+ Parameter :math:`\\alpha` applied to the singular values on right singular vectors.
36
+ The embedding of rows and columns are respectively :math:`D_1^{- \\alpha_1}U \\Sigma^{1-\\alpha}` and
37
+ :math:`D_2^{- \\alpha_2}V \\Sigma^\\alpha` where:
38
+
39
+ * :math:`U` is the matrix of left singular vectors, shape (n_row, n_components)
40
+ * :math:`V` is the matrix of right singular vectors, shape (n_col, n_components)
41
+ * :math:`\\Sigma` is the diagonal matrix of singular values, shape (n_components, n_components)
42
+
43
+ normalized : bool (default = ``True``)
44
+ If ``True``, normalized the embedding so that each vector has norm 1 in the embedding space, i.e.,
45
+ each vector lies on the unit sphere.
46
+ solver : ``'lanczos'`` (Lanczos algorithm, default) or :class:`SVDSolver` (custom solver)
47
+ Which solver to use.
48
+
49
+ Attributes
50
+ ----------
51
+ embedding_ : array, shape = (n, n_components)
52
+ Embedding of the nodes.
53
+ embedding_row_ : array, shape = (n_row, n_components)
54
+ Embedding of the rows, for bipartite graphs.
55
+ embedding_col_ : array, shape = (n_col, n_components)
56
+ Embedding of the columns, for bipartite graphs.
57
+ singular_values_ : np.ndarray, shape = (n_components)
58
+ Singular values.
59
+ singular_vectors_left_ : np.ndarray, shape = (n_row, n_components)
60
+ Left singular vectors.
61
+ singular_vectors_right_ : np.ndarray, shape = (n_col, n_components)
62
+ Right singular vectors.
63
+ weights_col_ : np.ndarray, shape = (n2)
64
+ Weights applied to columns.
65
+
66
+ Example
67
+ -------
68
+ >>> from sknetwork.embedding import GSVD
69
+ >>> from sknetwork.data import karate_club
70
+ >>> gsvd = GSVD()
71
+ >>> adjacency = karate_club()
72
+ >>> embedding = gsvd.fit_transform(adjacency)
73
+ >>> embedding.shape
74
+ (34, 2)
75
+
76
+ References
77
+ ----------
78
+ Abdi, H. (2007).
79
+ `Singular value decomposition (SVD) and generalized singular value decomposition.
80
+ <https://www.cs.cornell.edu/cv/ResearchPDF/Generalizing%20The%20Singular%20Value%20Decomposition.pdf>`_
81
+ Encyclopedia of measurement and statistics, 907-912.
82
+ """
83
+ def __init__(self, n_components=2, regularization: Union[None, float] = None,
84
+ factor_row: float = 0.5, factor_col: float = 0.5, factor_singular: float = 0., normalized: bool = True,
85
+ solver: Union[str, SVDSolver] = 'lanczos'):
86
+ super(GSVD, self).__init__()
87
+
88
+ self.n_components = n_components
89
+ if regularization == 0:
90
+ self.regularization = None
91
+ else:
92
+ self.regularization = regularization
93
+ self.factor_row = factor_row
94
+ self.factor_col = factor_col
95
+ self.factor_singular = factor_singular
96
+ self.normalized = normalized
97
+ self.solver = solver
98
+
99
+ self.singular_values_ = None
100
+ self.singular_vectors_left_ = None
101
+ self.singular_vectors_right_ = None
102
+ self.regularization_ = None
103
+ self.weights_col_ = None
104
+
105
+ def fit(self, input_matrix: Union[sparse.csr_matrix, np.ndarray]) -> 'GSVD':
106
+ """Compute the embedding of the graph.
107
+
108
+ Parameters
109
+ ----------
110
+ input_matrix :
111
+ Adjacency matrix or biadjacency matrix of the graph.
112
+
113
+ Returns
114
+ -------
115
+ self: :class:`GSVD`
116
+ """
117
+ self._init_vars()
118
+
119
+ adjacency = check_format(input_matrix).asfptype()
120
+ n_row, n_col = adjacency.shape
121
+ n_components = check_n_components(self.n_components, min(n_row, n_col) - 1)
122
+
123
+ if isinstance(self.solver, str):
124
+ self.solver = LanczosSVD()
125
+ regularization = self.regularization
126
+ if regularization:
127
+ adjacency_reg = Regularizer(adjacency, regularization)
128
+ else:
129
+ adjacency_reg = adjacency
130
+
131
+ weights_row = adjacency_reg.dot(np.ones(n_col))
132
+ weights_col = adjacency_reg.T.dot(np.ones(n_row))
133
+ diag_row = diag_pinv(np.power(weights_row, self.factor_row))
134
+ diag_col = diag_pinv(np.power(weights_col, self.factor_col))
135
+ self.solver.fit(safe_sparse_dot(diag_row, safe_sparse_dot(adjacency_reg, diag_col)), n_components)
136
+
137
+ singular_values = self.solver.singular_values_
138
+ index = np.argsort(-singular_values)
139
+ singular_values = singular_values[index]
140
+ singular_vectors_left = self.solver.singular_vectors_left_[:, index]
141
+ singular_vectors_right = self.solver.singular_vectors_right_[:, index]
142
+ singular_left_diag = sparse.diags(np.power(singular_values, 1 - self.factor_singular))
143
+ singular_right_diag = sparse.diags(np.power(singular_values, self.factor_singular))
144
+
145
+ embedding_row = diag_row.dot(singular_vectors_left)
146
+ embedding_col = diag_col.dot(singular_vectors_right)
147
+ embedding_row = singular_left_diag.dot(embedding_row.T).T
148
+ embedding_col = singular_right_diag.dot(embedding_col.T).T
149
+
150
+ if self.normalized:
151
+ embedding_row = normalize(embedding_row, p=2)
152
+ embedding_col = normalize(embedding_col, p=2)
153
+
154
+ self.embedding_row_ = embedding_row
155
+ self.embedding_col_ = embedding_col
156
+ self.embedding_ = embedding_row
157
+ self.singular_values_ = singular_values
158
+ self.singular_vectors_left_ = singular_vectors_left
159
+ self.singular_vectors_right_ = singular_vectors_right
160
+ self.weights_col_ = weights_col
161
+
162
+ return self
163
+
164
+ @staticmethod
165
+ def _check_adj_vector(adjacency_vectors):
166
+ check_nonnegative(adjacency_vectors)
167
+
168
+ def predict(self, adjacency_vectors: Union[sparse.csr_matrix, np.ndarray]) -> np.ndarray:
169
+ """Predict the embedding of new rows, defined by their adjacency vectors.
170
+
171
+ Parameters
172
+ ----------
173
+ adjacency_vectors :
174
+ Adjacency vectors of nodes.
175
+ Array of shape (n_col,) (single vector) or (n_vectors, n_col)
176
+
177
+ Returns
178
+ -------
179
+ embedding_vectors : np.ndarray
180
+ Embedding of the nodes.
181
+ """
182
+ self._check_fitted()
183
+ singular_vectors_right = self.singular_vectors_right_
184
+ singular_values = self.singular_values_
185
+
186
+ n_row, _ = self.embedding_row_.shape
187
+ n_col, _ = self.embedding_col_.shape
188
+
189
+ adjacency_vectors = check_adjacency_vector(adjacency_vectors, n_col)
190
+ self._check_adj_vector(adjacency_vectors)
191
+
192
+ # regularization
193
+ if self.regularization:
194
+ adjacency_vectors = Regularizer(adjacency_vectors, self.regularization)
195
+
196
+ # weighting
197
+ weights_row = adjacency_vectors.dot(np.ones(n_col))
198
+ diag_row = diag_pinv(np.power(weights_row, self.factor_row))
199
+ diag_col = diag_pinv(np.power(self.weights_col_, self.factor_col))
200
+ adjacency_vectors = safe_sparse_dot(diag_row, safe_sparse_dot(adjacency_vectors, diag_col))
201
+
202
+ # projection in the embedding space
203
+ averaging = adjacency_vectors
204
+ embedding_vectors = diag_row.dot(averaging.dot(singular_vectors_right))
205
+
206
+ # scaling
207
+ embedding_vectors /= np.power(singular_values, self.factor_singular)
208
+
209
+ if self.normalized:
210
+ embedding_vectors = normalize(embedding_vectors, p=2)
211
+
212
+ if len(embedding_vectors) == 1:
213
+ embedding_vectors = embedding_vectors.ravel()
214
+
215
+ return embedding_vectors
216
+
217
+
218
+ class SVD(GSVD):
219
+ """Graph embedding by Singular Value Decomposition of the adjacency or biadjacency matrix of the graph.
220
+
221
+ Parameters
222
+ ----------
223
+ n_components : int
224
+ Dimension of the embedding.
225
+ regularization : ``None`` or float (default = ``None``)
226
+ Regularization factor :math:`\\alpha` so that the matrix is :math:`A + \\alpha \\frac{11^T}{n}`.
227
+ factor_singular : float (default = 0.)
228
+ Power factor :math:`\\alpha` applied to the singular values on right singular vectors.
229
+ The embedding of rows and columns are respectively :math:`U \\Sigma^{1-\\alpha}` and
230
+ :math:`V \\Sigma^\\alpha` where:
231
+
232
+ * :math:`U` is the matrix of left singular vectors, shape (n_row, n_components)
233
+ * :math:`V` is the matrix of right singular vectors, shape (n_col, n_components)
234
+ * :math:`\\Sigma` is the diagonal matrix of singular values, shape (n_components, n_components)
235
+
236
+ normalized : bool (default = ``False``)
237
+ If ``True``, normalized the embedding so that each vector has norm 1 in the embedding space, i.e.,
238
+ each vector lies on the unit sphere.
239
+ solver : ``'lanczos'`` (Lanczos algorithm, default) or :class:`SVDSolver` (custom solver)
240
+ Which solver to use.
241
+
242
+ Attributes
243
+ ----------
244
+ embedding_ : array, shape = (n, n_components)
245
+ Embedding of the nodes.
246
+ embedding_row_ : array, shape = (n_row, n_components)
247
+ Embedding of the rows, for bipartite graphs.
248
+ embedding_col_ : array, shape = (n_col, n_components)
249
+ Embedding of the columns, for bipartite graphs.
250
+ singular_values_ : np.ndarray, shape = (n_components)
251
+ Singular values.
252
+ singular_vectors_left_ : np.ndarray, shape = (n_row, n_components)
253
+ Left singular vectors.
254
+ singular_vectors_right_ : np.ndarray, shape = (n_col, n_components)
255
+ Right singular vectors.
256
+
257
+ Example
258
+ -------
259
+ >>> from sknetwork.embedding import SVD
260
+ >>> from sknetwork.data import karate_club
261
+ >>> svd = SVD()
262
+ >>> adjacency = karate_club()
263
+ >>> embedding = svd.fit_transform(adjacency)
264
+ >>> embedding.shape
265
+ (34, 2)
266
+
267
+ References
268
+ ----------
269
+ Abdi, H. (2007).
270
+ `Singular value decomposition (SVD) and generalized singular value decomposition.
271
+ <https://www.cs.cornell.edu/cv/ResearchPDF/Generalizing%20The%20Singular%20Value%20Decomposition.pdf>`_
272
+ Encyclopedia of measurement and statistics.
273
+ """
274
+ def __init__(self, n_components=2, regularization: Union[None, float] = None, factor_singular: float = 0.,
275
+ normalized: bool = False, solver: Union[str, SVDSolver] = 'lanczos'):
276
+ super(SVD, self).__init__(n_components=n_components, regularization=regularization,
277
+ factor_singular=factor_singular, factor_row=0., factor_col=0., normalized=normalized,
278
+ solver=solver)
279
+
280
+ @staticmethod
281
+ def _check_adj_vector(adjacency_vectors: np.ndarray):
282
+ return
283
+
284
+
285
+ class PCA(SVD):
286
+ """Graph embedding by Principal Component Analysis of the adjacency or biadjacency matrix.
287
+
288
+ Parameters
289
+ ----------
290
+ n_components : int
291
+ Dimension of the embedding.
292
+ normalized : bool (default = ``False``)
293
+ If ``True``, normalized the embedding so that each vector has norm 1 in the embedding space, i.e.,
294
+ each vector lies on the unit sphere.
295
+ solver : ``'lanczos'`` (Lanczos algorithm, default) or :class:`SVDSolver` (custom solver)
296
+ Which solver to use.
297
+
298
+ Attributes
299
+ ----------
300
+ embedding_ : array, shape = (n, n_components)
301
+ Embedding of the nodes.
302
+ embedding_row_ : array, shape = (n_row, n_components)
303
+ Embedding of the rows, for bipartite graphs.
304
+ embedding_col_ : array, shape = (n_col, n_components)
305
+ Embedding of the columns, for bipartite graphs.
306
+ singular_values_ : np.ndarray, shape = (n_components)
307
+ Singular values.
308
+ singular_vectors_left_ : np.ndarray, shape = (n_row, n_components)
309
+ Left singular vectors.
310
+ singular_vectors_right_ : np.ndarray, shape = (n_col, n_components)
311
+ Right singular vectors.
312
+
313
+ Example
314
+ -------
315
+ >>> from sknetwork.embedding import PCA
316
+ >>> from sknetwork.data import karate_club
317
+ >>> pca = PCA()
318
+ >>> adjacency = karate_club()
319
+ >>> embedding = pca.fit_transform(adjacency)
320
+ >>> embedding.shape
321
+ (34, 2)
322
+
323
+ References
324
+ ----------
325
+ Jolliffe, I.T. (2002).
326
+ `Principal Component Analysis`
327
+ Series: Springer Series in Statistics.
328
+ """
329
+ def __init__(self, n_components=2, normalized: bool = False, solver: Union[str, SVDSolver] = 'lanczos'):
330
+ super(PCA, self).__init__()
331
+ self.n_components = n_components
332
+ self.normalized = normalized
333
+ if isinstance(solver, str):
334
+ self.solver = LanczosSVD()
335
+ else:
336
+ self.solver = solver
337
+
338
+ def fit(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> 'PCA':
339
+ """Compute the embedding of the graph.
340
+
341
+ Parameters
342
+ ----------
343
+ adjacency :
344
+ Adjacency or biadjacency matrix of the graph.
345
+
346
+ Returns
347
+ -------
348
+ self: :class:`PCA`
349
+ """
350
+ adjacency = check_format(adjacency).asfptype()
351
+ n_row, n_col = adjacency.shape
352
+ adjacency_centered = SparseLR(adjacency, (-np.ones(n_row), adjacency.T.dot(np.ones(n_row)) / n_row))
353
+
354
+ svd = self.solver
355
+ svd.fit(adjacency_centered, self.n_components)
356
+ self.embedding_row_ = svd.singular_vectors_left_
357
+ self.embedding_col_ = svd.singular_vectors_right_
358
+ self.embedding_ = svd.singular_vectors_left_
359
+ self.singular_values_ = svd.singular_values_
360
+ self.singular_vectors_left_ = svd.singular_vectors_left_
361
+ self.singular_vectors_right_ = svd.singular_vectors_right_
362
+
363
+ return self
@@ -0,0 +1 @@
1
+ """tests for embedding"""
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """tests for embeddings"""
4
+
5
+ import unittest
6
+
7
+ from sknetwork.data.test_graphs import *
8
+ from sknetwork.embedding import Spectral, SVD, GSVD, Spring
9
+
10
+
11
+ class TestEmbeddings(unittest.TestCase):
12
+
13
+ def setUp(self):
14
+ """Algorithms by input types."""
15
+ self.methods = [Spectral(), GSVD(), SVD()]
16
+ self.bimethods = [GSVD(), SVD()]
17
+
18
+ def test_undirected(self):
19
+ adjacency = test_graph()
20
+ n = adjacency.shape[0]
21
+
22
+ method = Spring()
23
+ embedding = method.fit_transform(adjacency)
24
+ self.assertEqual(embedding.shape, (n, 2))
25
+ pred1 = method.predict(adjacency[0])
26
+ pred2 = method.predict(adjacency[0].toarray())
27
+ self.assertEqual(pred1.shape, (2,))
28
+ self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0)
29
+
30
+ pred1 = method.predict(adjacency)
31
+ pred2 = method.predict(adjacency.toarray())
32
+ self.assertTupleEqual(pred1.shape, (n, 2))
33
+ self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0)
34
+
35
+ def test_bimethods(self):
36
+
37
+ for adjacency in [test_digraph(), test_bigraph()]:
38
+ n_row, n_col = adjacency.shape
39
+
40
+ for method in self.bimethods:
41
+ method.fit(adjacency)
42
+
43
+ self.assertEqual(method.embedding_.shape, (n_row, 2))
44
+ self.assertEqual(method.embedding_row_.shape, (n_row, 2))
45
+ self.assertEqual(method.embedding_col_.shape, (n_col, 2))
46
+
47
+ ref = method.embedding_[0]
48
+ pred1 = method.predict(adjacency[0])
49
+ pred2 = method.predict(adjacency[0].toarray())
50
+
51
+ self.assertEqual(pred1.shape, (2,))
52
+ self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0)
53
+ self.assertAlmostEqual(np.linalg.norm(pred1 - ref), 0)
54
+
55
+ ref = method.embedding_
56
+ pred1 = method.predict(adjacency)
57
+ pred2 = method.predict(adjacency.toarray())
58
+
59
+ self.assertTupleEqual(pred1.shape, (n_row, 2))
60
+ self.assertAlmostEqual(np.linalg.norm(pred1 - pred2), 0)
61
+ self.assertAlmostEqual(np.linalg.norm(pred1 - ref), 0)
62
+
63
+ def test_disconnected(self):
64
+ n = 10
65
+ adjacency = np.eye(n)
66
+ for method in self.methods:
67
+ embedding = method.fit_transform(adjacency)
68
+ self.assertEqual(embedding.shape, (n, 2))
69
+
70
+ def test_regularization(self):
71
+ adjacency = test_graph()
72
+ method = Spectral()
73
+ self.assertEqual(method._get_regularization(-1, adjacency), 0)
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """tests for force atlas2 embeddings"""
4
+ import unittest
5
+
6
+ import numpy as np
7
+
8
+ from sknetwork.data.test_graphs import test_graph, test_digraph
9
+ from sknetwork.embedding.force_atlas import ForceAtlas
10
+
11
+
12
+ class TestEmbeddings(unittest.TestCase):
13
+
14
+ def test_options(self):
15
+ for adjacency in [test_graph(), test_digraph()]:
16
+ n = adjacency.shape[0]
17
+
18
+ force_atlas = ForceAtlas()
19
+ layout = force_atlas.fit_transform(adjacency)
20
+ self.assertEqual((n, 2), layout.shape)
21
+
22
+ force_atlas = ForceAtlas(lin_log=True)
23
+ layout = force_atlas.fit_transform(adjacency)
24
+ self.assertEqual((n, 2), layout.shape)
25
+
26
+ force_atlas = ForceAtlas(approx_radius=1.)
27
+ layout = force_atlas.fit_transform(adjacency)
28
+ self.assertEqual((n, 2), layout.shape)
29
+
30
+ force_atlas.fit(adjacency, pos_init=layout, n_iter=1)
31
+
32
+ def test_errors(self):
33
+ adjacency = test_graph()
34
+ with self.assertRaises(ValueError):
35
+ ForceAtlas().fit(adjacency, pos_init=np.ones((5, 7)))
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """tests for Louvain embedding"""
4
+ import unittest
5
+
6
+ import numpy as np
7
+
8
+ from sknetwork.data.test_graphs import test_graph, test_bigraph
9
+ from sknetwork.embedding import LouvainEmbedding
10
+
11
+
12
+ class TestLouvainEmbedding(unittest.TestCase):
13
+
14
+ def test_predict(self):
15
+ louvain = LouvainEmbedding()
16
+ louvain.fit(test_graph())
17
+ self.assertEqual(louvain.embedding_.shape[0], 10)
18
+ louvain.fit(test_graph(), force_bipartite=True)
19
+ self.assertEqual(louvain.embedding_.shape[0], 10)
20
+
21
+ for method in ['remove', 'merge', 'keep']:
22
+ louvain = LouvainEmbedding(isolated_nodes=method)
23
+ louvain.fit(test_graph())
24
+ embedding_vector = louvain.predict(np.array([1, 0, 0, 0, 1, 1, 0, 0, 0, 1]))
25
+ self.assertEqual(embedding_vector.shape[0], 1)
26
+
27
+ for method in ['remove', 'merge', 'keep']:
28
+ bilouvain = LouvainEmbedding(isolated_nodes=method)
29
+ bilouvain.fit(test_bigraph())
30
+ embedding_vector = bilouvain.predict(np.array([1, 0, 0, 0, 1, 1, 0, 1]))
31
+ self.assertEqual(embedding_vector.shape[0], 1)
32
+
33
+
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """tests for LouvainNE"""
4
+ import unittest
5
+
6
+ from sknetwork.data.test_graphs import test_graph, test_graph_disconnect, test_digraph, test_bigraph
7
+ from sknetwork.embedding import LouvainNE
8
+
9
+
10
+ class TestLouvainNE(unittest.TestCase):
11
+
12
+ def test_louvain_hierarchy(self):
13
+ louvain = LouvainNE()
14
+ for adjacency in [test_graph(), test_graph_disconnect(), test_digraph()]:
15
+ self.assertTupleEqual(louvain.fit_transform(adjacency).shape, (10, 2))
16
+ louvain.fit(test_bigraph())
17
+ self.assertTupleEqual(louvain.embedding_.shape, (6, 2))
18
+ louvain.fit(test_graph(), force_bipartite=True)
19
+ self.assertTupleEqual(louvain.embedding_.shape, (10, 2))
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """tests for embeddings metrics"""
4
+
5
+ import unittest
6
+
7
+ from sknetwork.data.test_graphs import test_bigraph, test_graph
8
+ from sknetwork.embedding import Spectral
9
+ from sknetwork.embedding.metrics import get_cosine_similarity
10
+
11
+
12
+ class TestClusteringMetrics(unittest.TestCase):
13
+
14
+ def test_cosine(self):
15
+ biadjacency = test_bigraph()
16
+ method = Spectral(3)
17
+
18
+ embedding = method.fit_transform(biadjacency)
19
+ embedding_col = method.embedding_col_
20
+ similarity = get_cosine_similarity(biadjacency, embedding, embedding_col)
21
+ self.assertAlmostEqual(similarity, 0.56, 2)
22
+
23
+ adjacency = test_graph()
24
+ embedding = method.fit_transform(adjacency)
25
+ similarity = get_cosine_similarity(adjacency, embedding)
26
+ self.assertAlmostEqual(similarity, 0.32, 2)
27
+
28
+ with self.assertRaises(ValueError):
29
+ get_cosine_similarity(biadjacency, embedding)
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Tests for random projection"""
4
+ import unittest
5
+
6
+ from sknetwork.data.test_graphs import test_graph, test_bigraph, test_digraph, test_graph_disconnect
7
+ from sknetwork.embedding import RandomProjection
8
+
9
+
10
+ class TestEmbeddings(unittest.TestCase):
11
+
12
+ def test_random_projection(self):
13
+ for algo in [RandomProjection(), RandomProjection(random_walk=True)]:
14
+ adjacency = test_graph()
15
+ embedding = algo.fit_transform(adjacency)
16
+ self.assertEqual(embedding.shape[1], 2)
17
+ embedding = algo.fit_transform(adjacency, force_bipartite=True)
18
+ self.assertEqual(embedding.shape[1], 2)
19
+ adjacency = test_digraph()
20
+ embedding = algo.fit_transform(adjacency)
21
+ self.assertEqual(embedding.shape[1], 2)
22
+ adjacency = test_graph_disconnect()
23
+ embedding = algo.fit_transform(adjacency)
24
+ self.assertEqual(embedding.shape[1], 2)
25
+ biadjacency = test_bigraph()
26
+ embedding = algo.fit_transform(biadjacency)
27
+ self.assertEqual(embedding.shape[1], 2)
28
+ self.assertEqual(algo.embedding_col_.shape[1], 2)
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Tests for spectral embedding."""
4
+
5
+ import unittest
6
+
7
+ from sknetwork.data.test_graphs import *
8
+ from sknetwork.embedding import Spectral
9
+ from sknetwork.utils.check import is_weakly_connected
10
+ from sknetwork.utils.format import bipartite2undirected
11
+
12
+
13
+ class TestEmbeddings(unittest.TestCase):
14
+
15
+ def test_undirected(self):
16
+ for adjacency in [test_graph(), test_graph_disconnect()]:
17
+ n = adjacency.shape[0]
18
+ # random walk
19
+ spectral = Spectral(3, normalized=False)
20
+ embedding = spectral.fit_transform(adjacency)
21
+ weights = adjacency.dot(np.ones(n))
22
+ if not is_weakly_connected(adjacency):
23
+ weights += 1
24
+ self.assertAlmostEqual(np.linalg.norm(embedding.T.dot(weights)), 0)
25
+ self.assertAlmostEqual(np.linalg.norm(embedding[1:4] - spectral.predict(adjacency[1:4])), 0)
26
+ # Laplacian
27
+ spectral = Spectral(3, decomposition='laplacian', normalized=False)
28
+ embedding = spectral.fit_transform(adjacency)
29
+ self.assertAlmostEqual(np.linalg.norm(embedding.sum(axis=0)), 0)
30
+ self.assertAlmostEqual(np.linalg.norm(embedding[1:4] - spectral.predict(adjacency[1:4])), 0)
31
+
32
+ def test_directed(self):
33
+ for adjacency in [test_digraph(), test_digraph().astype(bool)]:
34
+ # random walk
35
+ spectral = Spectral(3, normalized=False)
36
+ embedding = spectral.fit_transform(adjacency)
37
+ self.assertAlmostEqual(np.linalg.norm(embedding[6:8] - spectral.predict(adjacency[6:8])), 0)
38
+ # Laplacian
39
+ spectral = Spectral(3, decomposition='laplacian', normalized=False)
40
+ embedding = spectral.fit_transform(adjacency)
41
+ self.assertAlmostEqual(np.linalg.norm(spectral.eigenvectors_.sum(axis=0)), 0)
42
+ self.assertAlmostEqual(np.linalg.norm(embedding[6:8] - spectral.predict(adjacency[6:8])), 0)
43
+
44
+ def test_regularization(self):
45
+ for adjacency in [test_graph(), test_graph_disconnect()]:
46
+ n = adjacency.shape[0]
47
+ # random walk
48
+ regularization = 0.1
49
+ spectral = Spectral(3, regularization=regularization, normalized=False)
50
+ embedding = spectral.fit_transform(adjacency)
51
+ weights = adjacency.dot(np.ones(n)) + regularization
52
+ self.assertAlmostEqual(np.linalg.norm(embedding.T.dot(weights)), 0)
53
+ # Laplacian
54
+ spectral = Spectral(3, decomposition='laplacian', regularization=1, normalized=False)
55
+ embedding = spectral.fit_transform(adjacency)
56
+ self.assertAlmostEqual(np.linalg.norm(embedding.sum(axis=0)), 0)
57
+ # without regularization
58
+ spectral = Spectral(3, decomposition='laplacian', regularization=-1, normalized=False)
59
+ embedding = spectral.fit_transform(adjacency)
60
+ self.assertAlmostEqual(np.linalg.norm(embedding.sum(axis=0)), 0)
61
+
62
+ def test_bipartite(self):
63
+ for biadjacency in [test_digraph(), test_bigraph(), test_bigraph_disconnect()]:
64
+ n_row, n_col = biadjacency.shape
65
+ adjacency = bipartite2undirected(biadjacency)
66
+ # random walk
67
+ spectral = Spectral(3, normalized=False)
68
+ spectral.fit(biadjacency)
69
+ embedding_full = np.vstack([spectral.embedding_row_, spectral.embedding_col_])
70
+ weights = adjacency.dot(np.ones(n_row + n_col))
71
+ if not is_weakly_connected(adjacency):
72
+ weights += 1
73
+ self.assertAlmostEqual(np.linalg.norm(embedding_full.T.dot(weights)), 0)
74
+ # Laplacian
75
+ spectral = Spectral(3, decomposition='laplacian', normalized=False)
76
+ spectral.fit(biadjacency)
77
+ embedding_full = np.vstack([spectral.embedding_row_, spectral.embedding_col_])
78
+ self.assertAlmostEqual(np.linalg.norm(embedding_full.sum(axis=0)), 0)
79
+
80
+ def test_normalization(self):
81
+ for adjacency in [test_graph(), test_graph_disconnect()]:
82
+ spectral = Spectral(3)
83
+ embedding = spectral.fit_transform(adjacency)
84
+ self.assertAlmostEqual(np.linalg.norm(np.linalg.norm(embedding, axis=1) - np.ones(adjacency.shape[0])), 0)