scikit-network 0.33.4__cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. scikit_network-0.33.4.dist-info/METADATA +122 -0
  2. scikit_network-0.33.4.dist-info/RECORD +229 -0
  3. scikit_network-0.33.4.dist-info/WHEEL +6 -0
  4. scikit_network-0.33.4.dist-info/licenses/AUTHORS.rst +43 -0
  5. scikit_network-0.33.4.dist-info/licenses/LICENSE +34 -0
  6. scikit_network-0.33.4.dist-info/top_level.txt +1 -0
  7. scikit_network.libs/libgomp-a34b3233.so.1.0.0 +0 -0
  8. sknetwork/__init__.py +21 -0
  9. sknetwork/base.py +67 -0
  10. sknetwork/classification/__init__.py +8 -0
  11. sknetwork/classification/base.py +138 -0
  12. sknetwork/classification/base_rank.py +129 -0
  13. sknetwork/classification/diffusion.py +127 -0
  14. sknetwork/classification/knn.py +131 -0
  15. sknetwork/classification/metrics.py +205 -0
  16. sknetwork/classification/pagerank.py +58 -0
  17. sknetwork/classification/propagation.py +144 -0
  18. sknetwork/classification/tests/__init__.py +1 -0
  19. sknetwork/classification/tests/test_API.py +30 -0
  20. sknetwork/classification/tests/test_diffusion.py +77 -0
  21. sknetwork/classification/tests/test_knn.py +23 -0
  22. sknetwork/classification/tests/test_metrics.py +53 -0
  23. sknetwork/classification/tests/test_pagerank.py +20 -0
  24. sknetwork/classification/tests/test_propagation.py +24 -0
  25. sknetwork/classification/vote.cpp +27593 -0
  26. sknetwork/classification/vote.cpython-312-x86_64-linux-gnu.so +0 -0
  27. sknetwork/classification/vote.pyx +56 -0
  28. sknetwork/clustering/__init__.py +8 -0
  29. sknetwork/clustering/base.py +168 -0
  30. sknetwork/clustering/kcenters.py +251 -0
  31. sknetwork/clustering/leiden.py +238 -0
  32. sknetwork/clustering/leiden_core.cpp +31928 -0
  33. sknetwork/clustering/leiden_core.cpython-312-x86_64-linux-gnu.so +0 -0
  34. sknetwork/clustering/leiden_core.pyx +124 -0
  35. sknetwork/clustering/louvain.py +282 -0
  36. sknetwork/clustering/louvain_core.cpp +31573 -0
  37. sknetwork/clustering/louvain_core.cpython-312-x86_64-linux-gnu.so +0 -0
  38. sknetwork/clustering/louvain_core.pyx +124 -0
  39. sknetwork/clustering/metrics.py +91 -0
  40. sknetwork/clustering/postprocess.py +66 -0
  41. sknetwork/clustering/propagation_clustering.py +100 -0
  42. sknetwork/clustering/tests/__init__.py +1 -0
  43. sknetwork/clustering/tests/test_API.py +38 -0
  44. sknetwork/clustering/tests/test_kcenters.py +60 -0
  45. sknetwork/clustering/tests/test_leiden.py +34 -0
  46. sknetwork/clustering/tests/test_louvain.py +135 -0
  47. sknetwork/clustering/tests/test_metrics.py +50 -0
  48. sknetwork/clustering/tests/test_postprocess.py +39 -0
  49. sknetwork/data/__init__.py +6 -0
  50. sknetwork/data/base.py +33 -0
  51. sknetwork/data/load.py +292 -0
  52. sknetwork/data/models.py +459 -0
  53. sknetwork/data/parse.py +644 -0
  54. sknetwork/data/test_graphs.py +93 -0
  55. sknetwork/data/tests/__init__.py +1 -0
  56. sknetwork/data/tests/test_API.py +30 -0
  57. sknetwork/data/tests/test_base.py +14 -0
  58. sknetwork/data/tests/test_load.py +61 -0
  59. sknetwork/data/tests/test_models.py +52 -0
  60. sknetwork/data/tests/test_parse.py +250 -0
  61. sknetwork/data/tests/test_test_graphs.py +29 -0
  62. sknetwork/data/tests/test_toy_graphs.py +68 -0
  63. sknetwork/data/timeout.py +38 -0
  64. sknetwork/data/toy_graphs.py +611 -0
  65. sknetwork/embedding/__init__.py +8 -0
  66. sknetwork/embedding/base.py +90 -0
  67. sknetwork/embedding/force_atlas.py +198 -0
  68. sknetwork/embedding/louvain_embedding.py +142 -0
  69. sknetwork/embedding/random_projection.py +131 -0
  70. sknetwork/embedding/spectral.py +137 -0
  71. sknetwork/embedding/spring.py +198 -0
  72. sknetwork/embedding/svd.py +351 -0
  73. sknetwork/embedding/tests/__init__.py +1 -0
  74. sknetwork/embedding/tests/test_API.py +49 -0
  75. sknetwork/embedding/tests/test_force_atlas.py +35 -0
  76. sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
  77. sknetwork/embedding/tests/test_random_projection.py +28 -0
  78. sknetwork/embedding/tests/test_spectral.py +81 -0
  79. sknetwork/embedding/tests/test_spring.py +50 -0
  80. sknetwork/embedding/tests/test_svd.py +43 -0
  81. sknetwork/gnn/__init__.py +10 -0
  82. sknetwork/gnn/activation.py +117 -0
  83. sknetwork/gnn/base.py +181 -0
  84. sknetwork/gnn/base_activation.py +90 -0
  85. sknetwork/gnn/base_layer.py +109 -0
  86. sknetwork/gnn/gnn_classifier.py +305 -0
  87. sknetwork/gnn/layer.py +153 -0
  88. sknetwork/gnn/loss.py +180 -0
  89. sknetwork/gnn/neighbor_sampler.py +65 -0
  90. sknetwork/gnn/optimizer.py +164 -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 +75 -0
  94. sknetwork/gnn/tests/test_base_layer.py +37 -0
  95. sknetwork/gnn/tests/test_gnn_classifier.py +130 -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 +41 -0
  101. sknetwork/gnn/utils.py +127 -0
  102. sknetwork/hierarchy/__init__.py +6 -0
  103. sknetwork/hierarchy/base.py +90 -0
  104. sknetwork/hierarchy/louvain_hierarchy.py +260 -0
  105. sknetwork/hierarchy/metrics.py +234 -0
  106. sknetwork/hierarchy/paris.cpp +37877 -0
  107. sknetwork/hierarchy/paris.cpython-312-x86_64-linux-gnu.so +0 -0
  108. sknetwork/hierarchy/paris.pyx +310 -0
  109. sknetwork/hierarchy/postprocess.py +350 -0
  110. sknetwork/hierarchy/tests/__init__.py +1 -0
  111. sknetwork/hierarchy/tests/test_API.py +24 -0
  112. sknetwork/hierarchy/tests/test_algos.py +34 -0
  113. sknetwork/hierarchy/tests/test_metrics.py +62 -0
  114. sknetwork/hierarchy/tests/test_postprocess.py +57 -0
  115. sknetwork/linalg/__init__.py +9 -0
  116. sknetwork/linalg/basics.py +37 -0
  117. sknetwork/linalg/diteration.cpp +27409 -0
  118. sknetwork/linalg/diteration.cpython-312-x86_64-linux-gnu.so +0 -0
  119. sknetwork/linalg/diteration.pyx +47 -0
  120. sknetwork/linalg/eig_solver.py +93 -0
  121. sknetwork/linalg/laplacian.py +15 -0
  122. sknetwork/linalg/normalizer.py +86 -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.cpp +31081 -0
  127. sknetwork/linalg/push.cpython-312-x86_64-linux-gnu.so +0 -0
  128. sknetwork/linalg/push.pyx +71 -0
  129. sknetwork/linalg/sparse_lowrank.py +142 -0
  130. sknetwork/linalg/svd_solver.py +91 -0
  131. sknetwork/linalg/tests/__init__.py +1 -0
  132. sknetwork/linalg/tests/test_eig.py +44 -0
  133. sknetwork/linalg/tests/test_laplacian.py +18 -0
  134. sknetwork/linalg/tests/test_normalization.py +34 -0
  135. sknetwork/linalg/tests/test_operators.py +66 -0
  136. sknetwork/linalg/tests/test_polynome.py +38 -0
  137. sknetwork/linalg/tests/test_ppr.py +50 -0
  138. sknetwork/linalg/tests/test_sparse_lowrank.py +61 -0
  139. sknetwork/linalg/tests/test_svd.py +38 -0
  140. sknetwork/linkpred/__init__.py +2 -0
  141. sknetwork/linkpred/base.py +46 -0
  142. sknetwork/linkpred/nn.py +126 -0
  143. sknetwork/linkpred/tests/__init__.py +1 -0
  144. sknetwork/linkpred/tests/test_nn.py +26 -0
  145. sknetwork/log.py +19 -0
  146. sknetwork/path/__init__.py +5 -0
  147. sknetwork/path/dag.py +54 -0
  148. sknetwork/path/distances.py +98 -0
  149. sknetwork/path/search.py +31 -0
  150. sknetwork/path/shortest_path.py +61 -0
  151. sknetwork/path/tests/__init__.py +1 -0
  152. sknetwork/path/tests/test_dag.py +37 -0
  153. sknetwork/path/tests/test_distances.py +62 -0
  154. sknetwork/path/tests/test_search.py +40 -0
  155. sknetwork/path/tests/test_shortest_path.py +40 -0
  156. sknetwork/ranking/__init__.py +8 -0
  157. sknetwork/ranking/base.py +57 -0
  158. sknetwork/ranking/betweenness.cpp +9716 -0
  159. sknetwork/ranking/betweenness.cpython-312-x86_64-linux-gnu.so +0 -0
  160. sknetwork/ranking/betweenness.pyx +97 -0
  161. sknetwork/ranking/closeness.py +92 -0
  162. sknetwork/ranking/hits.py +90 -0
  163. sknetwork/ranking/katz.py +79 -0
  164. sknetwork/ranking/pagerank.py +106 -0
  165. sknetwork/ranking/postprocess.py +37 -0
  166. sknetwork/ranking/tests/__init__.py +1 -0
  167. sknetwork/ranking/tests/test_API.py +32 -0
  168. sknetwork/ranking/tests/test_betweenness.py +38 -0
  169. sknetwork/ranking/tests/test_closeness.py +30 -0
  170. sknetwork/ranking/tests/test_hits.py +20 -0
  171. sknetwork/ranking/tests/test_pagerank.py +62 -0
  172. sknetwork/ranking/tests/test_postprocess.py +26 -0
  173. sknetwork/regression/__init__.py +4 -0
  174. sknetwork/regression/base.py +57 -0
  175. sknetwork/regression/diffusion.py +204 -0
  176. sknetwork/regression/tests/__init__.py +1 -0
  177. sknetwork/regression/tests/test_API.py +32 -0
  178. sknetwork/regression/tests/test_diffusion.py +56 -0
  179. sknetwork/sknetwork.py +3 -0
  180. sknetwork/test_base.py +35 -0
  181. sknetwork/test_log.py +15 -0
  182. sknetwork/topology/__init__.py +8 -0
  183. sknetwork/topology/cliques.cpp +32574 -0
  184. sknetwork/topology/cliques.cpython-312-x86_64-linux-gnu.so +0 -0
  185. sknetwork/topology/cliques.pyx +149 -0
  186. sknetwork/topology/core.cpp +30660 -0
  187. sknetwork/topology/core.cpython-312-x86_64-linux-gnu.so +0 -0
  188. sknetwork/topology/core.pyx +90 -0
  189. sknetwork/topology/cycles.py +243 -0
  190. sknetwork/topology/minheap.cpp +27341 -0
  191. sknetwork/topology/minheap.cpython-312-x86_64-linux-gnu.so +0 -0
  192. sknetwork/topology/minheap.pxd +20 -0
  193. sknetwork/topology/minheap.pyx +109 -0
  194. sknetwork/topology/structure.py +194 -0
  195. sknetwork/topology/tests/__init__.py +1 -0
  196. sknetwork/topology/tests/test_cliques.py +28 -0
  197. sknetwork/topology/tests/test_core.py +19 -0
  198. sknetwork/topology/tests/test_cycles.py +65 -0
  199. sknetwork/topology/tests/test_structure.py +85 -0
  200. sknetwork/topology/tests/test_triangles.py +38 -0
  201. sknetwork/topology/tests/test_wl.py +72 -0
  202. sknetwork/topology/triangles.cpp +8903 -0
  203. sknetwork/topology/triangles.cpython-312-x86_64-linux-gnu.so +0 -0
  204. sknetwork/topology/triangles.pyx +151 -0
  205. sknetwork/topology/weisfeiler_lehman.py +133 -0
  206. sknetwork/topology/weisfeiler_lehman_core.cpp +27644 -0
  207. sknetwork/topology/weisfeiler_lehman_core.cpython-312-x86_64-linux-gnu.so +0 -0
  208. sknetwork/topology/weisfeiler_lehman_core.pyx +114 -0
  209. sknetwork/utils/__init__.py +7 -0
  210. sknetwork/utils/check.py +355 -0
  211. sknetwork/utils/format.py +221 -0
  212. sknetwork/utils/membership.py +82 -0
  213. sknetwork/utils/neighbors.py +115 -0
  214. sknetwork/utils/tests/__init__.py +1 -0
  215. sknetwork/utils/tests/test_check.py +190 -0
  216. sknetwork/utils/tests/test_format.py +63 -0
  217. sknetwork/utils/tests/test_membership.py +24 -0
  218. sknetwork/utils/tests/test_neighbors.py +41 -0
  219. sknetwork/utils/tests/test_tfidf.py +18 -0
  220. sknetwork/utils/tests/test_values.py +66 -0
  221. sknetwork/utils/tfidf.py +37 -0
  222. sknetwork/utils/values.py +76 -0
  223. sknetwork/visualization/__init__.py +4 -0
  224. sknetwork/visualization/colors.py +34 -0
  225. sknetwork/visualization/dendrograms.py +277 -0
  226. sknetwork/visualization/graphs.py +1039 -0
  227. sknetwork/visualization/tests/__init__.py +1 -0
  228. sknetwork/visualization/tests/test_dendrograms.py +53 -0
  229. sknetwork/visualization/tests/test_graphs.py +176 -0
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in March 2022
5
+ @author: Thomas Bonald <thomas.bonald@telecom-paris.fr>
6
+ """
7
+ from abc import ABC
8
+
9
+ import numpy as np
10
+ from scipy import sparse
11
+
12
+ from sknetwork.base import Algorithm
13
+
14
+
15
+ class BaseLinker(Algorithm, ABC):
16
+ """Base class for link prediction.
17
+
18
+ Attributes
19
+ ----------
20
+ links_: sparse.csr_matrix
21
+ Link matrix.
22
+ """
23
+
24
+ def __init__(self):
25
+ self.links_ = None
26
+
27
+ def predict(self) -> sparse.csr_matrix:
28
+ """Return the predicted links.
29
+
30
+ Returns
31
+ -------
32
+ links_ : sparse.csr_matrix
33
+ Link matrix.
34
+ """
35
+ return self.links_
36
+
37
+ def fit_predict(self, *args, **kwargs) -> sparse.csr_matrix:
38
+ """Fit algorithm to data and return the links. Same parameters as the ``fit`` method.
39
+
40
+ Returns
41
+ -------
42
+ links_ : sparse.csr_matrix
43
+ Link matrix.
44
+ """
45
+ self.fit(*args, **kwargs)
46
+ return self.links_
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in March 2023
5
+ @author: Thomas Bonald <thomas.bonald@telecom-paris.fr>
6
+ """
7
+ from typing import Optional, Union
8
+
9
+ import numpy as np
10
+ from scipy import sparse
11
+
12
+ from sknetwork.linkpred.base import BaseLinker
13
+ from sknetwork.embedding.base import BaseEmbedding
14
+ from sknetwork.linalg.normalizer import normalize
15
+ from sknetwork.utils.check import check_n_neighbors
16
+ from sknetwork.utils.format import get_adjacency
17
+
18
+
19
+ class NNLinker(BaseLinker):
20
+ """Link prediction by nearest neighbors in the embedding space, using cosine similarity.
21
+
22
+ For bipartite graphs, predict links between rows and columns only.
23
+
24
+ Parameters
25
+ ----------
26
+ n_neighbors : int
27
+ Number of nearest neighbors. If ``None``, all nodes are considered.
28
+ threshold : float
29
+ Threshold on cosine similarity. Only links above this threshold are kept.
30
+ embedding_method : :class:`BaseEmbedding`
31
+ Embedding method used to represent nodes in vector space.
32
+ If ``None`` (default), use identity.
33
+
34
+ Attributes
35
+ ----------
36
+ links\_ : sparse.csr_matrix
37
+ Link matrix.
38
+
39
+ Example
40
+ -------
41
+ >>> from sknetwork.linkpred import NNLinker
42
+ >>> from sknetwork.data import karate_club
43
+ >>> linker = NNLinker(n_neighbors=5, threshold=0.5)
44
+ >>> graph = karate_club(metadata=True)
45
+ >>> adjacency = graph.adjacency
46
+ >>> links = linker.fit_predict(adjacency)
47
+ >>> links.shape
48
+ (34, 34)
49
+ """
50
+ def __init__(self, n_neighbors: Optional[int] = 10, threshold: float = 0,
51
+ embedding_method: Optional[BaseEmbedding] = None):
52
+ super(NNLinker, self).__init__()
53
+ self.n_neighbors = n_neighbors
54
+ self.threshold = threshold
55
+ self.embedding_method = embedding_method
56
+ self.bipartite = None
57
+
58
+ def _fit_core(self, embedding, mask):
59
+ n = embedding.shape[0]
60
+ n_row = len(mask)
61
+ if n_row < n:
62
+ # bipartite graphs
63
+ index_col = np.arange(n_row, n)
64
+ n_col = n - n_row
65
+ else:
66
+ index_col = np.arange(n)
67
+ n_col = n
68
+ n_neighbors = check_n_neighbors(self.n_neighbors, n_col)
69
+
70
+ row = []
71
+ col = []
72
+ data = []
73
+
74
+ for i in np.flatnonzero(mask):
75
+ vector = embedding[i]
76
+ if sparse.issparse(vector):
77
+ vector = vector.toarray().ravel()
78
+ similarities = embedding[index_col].dot(vector)
79
+ nn = np.argpartition(-similarities, n_neighbors)[:n_neighbors]
80
+ mask_nn = np.zeros(n_col, dtype=bool)
81
+ mask_nn[nn] = 1
82
+ mask_nn[similarities < self.threshold] = 0
83
+ nn = np.flatnonzero(mask_nn)
84
+
85
+ row += len(nn) * [i]
86
+ col += list(nn)
87
+ data += list(similarities[nn])
88
+
89
+ links = sparse.csr_matrix((data, (row, col)), shape=(n_row, n_col))
90
+
91
+ return links
92
+
93
+ def fit(self, input_matrix: Union[sparse.csr_matrix, np.ndarray], index: Optional[np.ndarray] = None) -> 'NNLinker':
94
+ """Link prediction by nearest neighbors in the embedding space, using cosine similarity
95
+
96
+ Parameters
97
+ ----------
98
+ input_matrix : sparse.csr_matrix, np.ndarray
99
+ Adjacency matrix or biadjacency matrix of the graph.
100
+ index : np.ndarray
101
+ Index of source nodes to consider. If ``None``, the links are predicted for all nodes.
102
+
103
+ Returns
104
+ -------
105
+ self: :class:`NN`
106
+ """
107
+ n_row, _ = input_matrix.shape
108
+
109
+ adjacency, self.bipartite = get_adjacency(input_matrix)
110
+
111
+ if index is None:
112
+ index = np.arange(n_row)
113
+ mask = np.zeros(n_row, dtype=bool)
114
+ mask[index] = 1
115
+
116
+ if self.embedding_method is None:
117
+ embedding = adjacency
118
+ else:
119
+ embedding = self.embedding_method.fit_transform(adjacency)
120
+
121
+ embedding = normalize(embedding, p=2)
122
+ links = self._fit_core(embedding, mask)
123
+
124
+ self.links_ = links
125
+
126
+ return self
@@ -0,0 +1 @@
1
+ """tests for link prediction"""
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Tests for link prediction by nearest neighbors"""
4
+ import unittest
5
+
6
+ from sknetwork.linkpred import NNLinker
7
+ from sknetwork.data.test_graphs import *
8
+ from sknetwork.embedding import Spectral
9
+ from sknetwork.utils import get_degrees
10
+
11
+
12
+ class TestNNLinker(unittest.TestCase):
13
+
14
+ def test_link_prediction(self):
15
+ for input_matrix in [test_graph(), test_digraph(), test_bigraph()]:
16
+ n_neighbors = 2
17
+ threshold = 0.5
18
+ algo = NNLinker(n_neighbors=n_neighbors, threshold=threshold)
19
+ links = algo.fit_predict(input_matrix)
20
+ self.assertTrue(links.shape == input_matrix.shape)
21
+ self.assertTrue(np.all(get_degrees(links) <= n_neighbors))
22
+ self.assertTrue(np.all(links.data >= threshold))
23
+
24
+ algo = NNLinker(embedding_method=Spectral(2))
25
+ links = algo.fit_predict(input_matrix)
26
+ self.assertTrue(links.shape == input_matrix.shape)
sknetwork/log.py ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in December 2019
5
+ @author: Quentin Lutz <qlutz@enst.fr>
6
+ """
7
+
8
+
9
+ class Log:
10
+ """Log class for verbosity features"""
11
+ def __init__(self, verbose: bool = False):
12
+ self.verbose = verbose
13
+ self.log = ''
14
+
15
+ def print_log(self, *args):
16
+ """Fill log with text."""
17
+ if self.verbose:
18
+ print(*args)
19
+ self.log += ' '.join(map(str, args)) + '\n'
@@ -0,0 +1,5 @@
1
+ """Path module"""
2
+ from sknetwork.path.dag import get_dag
3
+ from sknetwork.path.distances import get_distances
4
+ from sknetwork.path.search import breadth_first_search
5
+ from sknetwork.path.shortest_path import get_shortest_path
sknetwork/path/dag.py ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in May 2023
5
+ @author: Thomas Bonald <bonald@enst.fr>
6
+ """
7
+ from typing import Iterable, Optional, Union
8
+
9
+ import numpy as np
10
+ from scipy import sparse
11
+
12
+ from sknetwork.path.distances import get_distances
13
+ from sknetwork.utils.check import check_format, check_square
14
+
15
+
16
+ def get_dag(adjacency: sparse.csr_matrix, source: Optional[Union[int, Iterable]] = None,
17
+ order: Optional[np.ndarray] = None) -> sparse.csr_matrix:
18
+ """Get a Directed Acyclic Graph (DAG) from a graph.
19
+ If the order is specified, keep only edges i -> j such that 0 <= order[i] < order[j].
20
+ If the source is specified, use the distances from this source node (or set of source nodes) as order.
21
+ If neither the order nor the source is specified, use the node indices as order.
22
+
23
+ Parameters
24
+ ----------
25
+ adjacency :
26
+ Adjacency matrix of the graph.
27
+ source :
28
+ Source node (or set of source nodes).
29
+ order :
30
+ Order of nodes. Negative values ignored.
31
+
32
+ Returns
33
+ -------
34
+ dag :
35
+ Adjacency matrix of the directed acyclic graph.
36
+ """
37
+ adjacency = check_format(adjacency, allow_empty=True)
38
+ check_square(adjacency)
39
+
40
+ if order is None:
41
+ if source is None:
42
+ order = np.arange(adjacency.shape[0])
43
+ else:
44
+ order = get_distances(adjacency, source)
45
+
46
+ dag = adjacency.astype(bool).tocoo()
47
+ for value in np.unique(order):
48
+ if value < 0:
49
+ dag.data[order[dag.row] == value] = 0
50
+ else:
51
+ dag.data[(order[dag.row] == value) & (order[dag.col] <= value)] = 0
52
+ dag.eliminate_zeros()
53
+
54
+ return dag.tocsr()
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in May 2023
5
+ @author: Thomas Bonald <bonald@enst.fr>
6
+ """
7
+ from typing import Iterable, Optional, Union, Tuple
8
+
9
+ import numpy as np
10
+ from scipy import sparse
11
+
12
+ from sknetwork.utils.format import get_adjacency
13
+
14
+
15
+ def get_distances(input_matrix: sparse.csr_matrix, source: Optional[Union[int, Iterable]] = None,
16
+ source_row: Optional[Union[int, Iterable]] = None,
17
+ source_col: Optional[Union[int, Iterable]] = None, transpose: bool = False,
18
+ force_bipartite: bool = False) \
19
+ -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray]]:
20
+ """Get the distances from a source (or a set of sources) in number of hops.
21
+
22
+ Parameters
23
+ ----------
24
+ input_matrix :
25
+ Adjacency matrix or biadjacency matrix of the graph.
26
+ source :
27
+ If an integer, index of the source node.
28
+ If a list or array, indices of source nodes (the shortest distances to one of these nodes is returned).
29
+ source_row, source_col :
30
+ For bipartite graphs, index of source nodes on rows and columns.
31
+ The parameter source_row is an alias for source (at least one of them must be ``None``).
32
+ transpose :
33
+ If ``True``, transpose the input matrix.
34
+ force_bipartite :
35
+ If ``True``, consider the input matrix as the biadjacency matrix of a bipartite graph.
36
+ Set to ``True`` is the parameters source_row or source_col re specified.
37
+
38
+ Returns
39
+ -------
40
+ distances : np.ndarray of shape (n_nodes,)
41
+ Vector of distances from source (distance = -1 if no path exists from the source).
42
+ For a bipartite graph, two vectors are returned, one for the rows and one for the columns.
43
+
44
+ Examples
45
+ --------
46
+ >>> from sknetwork.data import cyclic_digraph
47
+ >>> adjacency = cyclic_digraph(3)
48
+ >>> get_distances(adjacency, source=0)
49
+ array([0, 1, 2])
50
+ >>> get_distances(adjacency, source=[0, 2])
51
+ array([0, 1, 0])
52
+ """
53
+ if transpose:
54
+ matrix = sparse.csr_matrix(input_matrix.T)
55
+ else:
56
+ matrix = input_matrix
57
+ if source_row is not None or source_col is not None:
58
+ force_bipartite = True
59
+ adjacency, bipartite = get_adjacency(matrix, force_bipartite=force_bipartite, allow_empty=True)
60
+ adjacency_transpose = adjacency.astype(bool).T.tocsr()
61
+ n_row, n_col = matrix.shape
62
+ n_nodes = adjacency.shape[0]
63
+
64
+ mask = np.zeros(n_nodes, dtype=bool)
65
+ if bipartite:
66
+ if source is not None:
67
+ if source_row is not None:
68
+ raise ValueError('Only one of the parameters source and source_row can be specified.')
69
+ source_row = source
70
+ if source_row is None and source_col is None:
71
+ raise ValueError('At least one of the parameters source_row or source_col must be specified.')
72
+ if source_row is not None:
73
+ mask[source_row] = 1
74
+ if source_col is not None:
75
+ mask[n_row + np.array(source_col)] = 1
76
+ else:
77
+ if source is None:
78
+ raise ValueError('The parameter source must be specified.')
79
+ mask[source] = 1
80
+
81
+ distances = -np.ones(n_nodes, dtype=int)
82
+ distances[mask] = 0
83
+
84
+ distance = 0
85
+ reach = mask
86
+
87
+ while 1:
88
+ distance += 1
89
+ mask = adjacency_transpose.dot(reach).astype(bool) & ~reach
90
+ if np.sum(mask) == 0:
91
+ break
92
+ distances[mask] = distance
93
+ reach |= mask
94
+
95
+ if bipartite:
96
+ return distances[:n_row], distances[n_row:]
97
+ else:
98
+ return distances
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in May 2023
5
+ """
6
+ import numpy as np
7
+ from scipy import sparse
8
+
9
+ from sknetwork.path.distances import get_distances
10
+
11
+
12
+ def breadth_first_search(adjacency: sparse.csr_matrix, source: int):
13
+ """Breadth-first ordering starting from some node.
14
+
15
+ Parameters
16
+ ----------
17
+ adjacency :
18
+ Adjacency matrix of the graph.
19
+ source : int
20
+ Source node.
21
+
22
+ Returns
23
+ -------
24
+ index : np.ndarray
25
+ Node index corresponding to the breadth-first-search from the source.
26
+ The length of the vector is the number of nodes reachable from the source.
27
+ """
28
+ distances = get_distances(adjacency, source)
29
+ indices = np.argsort(distances)
30
+ n = np.sum(distances < 0)
31
+ return indices[n:]
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in May 2023
5
+ @author: Thomas Bonald <bonald@enst.fr>
6
+ """
7
+ from typing import Iterable, Optional, Union, Tuple
8
+
9
+ import numpy as np
10
+ from scipy import sparse
11
+
12
+ from sknetwork.path.dag import get_dag
13
+ from sknetwork.utils.format import bipartite2undirected
14
+ from sknetwork.path.distances import get_distances
15
+
16
+
17
+ def get_shortest_path(input_matrix: sparse.csr_matrix, source: Optional[Union[int, Iterable]] = None,
18
+ source_row: Optional[Union[int, Iterable]] = None,
19
+ source_col: Optional[Union[int, Iterable]] = None, force_bipartite: bool = False) \
20
+ -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray]]:
21
+ """Get the shortest paths from a source (or a set of sources) in number of hops.
22
+
23
+ Parameters
24
+ ----------
25
+ input_matrix :
26
+ Adjacency matrix or biadjacency matrix of the graph.
27
+ source :
28
+ If an integer, index of the source node.
29
+ If a list, indices of source nodes (the shortest distances to one of these nodes in returned).
30
+ source_row, source_col :
31
+ For bipartite graphs, index of source nodes on rows and columns.
32
+ The parameter source_row is an alias for source (at least one of them must be ``None``).
33
+ force_bipartite :
34
+ If ``True``, consider the input matrix as the biadjacency matrix of a bipartite graph.
35
+ Set to ``True`` is the parameters source_row or source_col are specified.
36
+
37
+ Returns
38
+ -------
39
+ path : sparse.csr_matrix
40
+ Adjacency matrix of the graph of the shortest paths from the source node (or the set of source nodes).
41
+ If the input graph is a bipartite graph, the shape of the matrix is (n_row + n_col, n_row + n_col) with the new
42
+ index corresponding to the rows then the columns of the original graph.
43
+
44
+ Examples
45
+ --------
46
+ >>> from sknetwork.data import cyclic_digraph
47
+ >>> adjacency = cyclic_digraph(3)
48
+ >>> path = get_shortest_path(adjacency, source=0)
49
+ >>> path.toarray().astype(int)
50
+ array([[0, 1, 0],
51
+ [0, 0, 1],
52
+ [0, 0, 0]])
53
+ """
54
+ distances = get_distances(input_matrix, source, source_row, source_col, force_bipartite)
55
+ if type(distances) == tuple:
56
+ adjacency = bipartite2undirected(input_matrix)
57
+ distances = np.hstack(distances)
58
+ else:
59
+ adjacency = input_matrix
60
+ return get_dag(adjacency, order=distances)
61
+
@@ -0,0 +1 @@
1
+ """tests for path module"""
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """"tests for search.py"""
4
+
5
+ import unittest
6
+
7
+ import numpy as np
8
+
9
+ from sknetwork.data import cyclic_digraph
10
+ from sknetwork.data.test_graphs import *
11
+ from sknetwork.path import get_dag
12
+
13
+
14
+ class TestSearch(unittest.TestCase):
15
+
16
+ def test(self):
17
+ adjacency = cyclic_digraph(3)
18
+ dag = get_dag(adjacency)
19
+ self.assertEqual(dag.nnz, 2)
20
+
21
+ adjacency = test_graph_empty()
22
+ dag = get_dag(adjacency)
23
+ self.assertEqual(dag.nnz, 0)
24
+
25
+ adjacency = test_graph()
26
+ dag = get_dag(adjacency)
27
+ self.assertEqual(dag.nnz, 12)
28
+ dag = get_dag(adjacency, order=np.arange(10) % 3)
29
+ self.assertEqual(dag.nnz, 10)
30
+
31
+ adjacency = test_disconnected_graph()
32
+ dag = get_dag(adjacency, 3)
33
+ self.assertEqual(dag.nnz, 1)
34
+
35
+ adjacency = test_digraph()
36
+ dag = get_dag(adjacency, 1)
37
+ self.assertEqual(dag.nnz, 4)
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """"tests for distances.py"""
4
+ import unittest
5
+
6
+ from sknetwork.data.test_graphs import *
7
+ from sknetwork.path.distances import get_distances
8
+
9
+
10
+ class TestDistances(unittest.TestCase):
11
+
12
+ def test_input(self):
13
+ adjacency = test_graph()
14
+ with self.assertRaises(ValueError):
15
+ get_distances(adjacency)
16
+ with self.assertRaises(ValueError):
17
+ get_distances(adjacency, source=0, source_row=5)
18
+
19
+ def test_algo(self):
20
+ adjacency = test_graph()
21
+ distances = get_distances(adjacency, 0)
22
+ distances_ = np.array([0, 1, 3, 2, 2, 3, 2, 3, 4, 4])
23
+ self.assertTrue(all(distances == distances_))
24
+ distances = get_distances(adjacency, 0, transpose=True)
25
+ self.assertTrue(all(distances == distances_))
26
+ distances = get_distances(adjacency, [0, 5])
27
+ distances_ = np.array([0, 1, 3, 2, 1, 0, 1, 2, 4, 3])
28
+ self.assertTrue(all(distances == distances_))
29
+
30
+ adjacency = test_graph_empty()
31
+ source = [0, 3]
32
+ distances = get_distances(adjacency, source)
33
+ distances_ = -np.ones(len(distances), dtype=int)
34
+ distances_[source] = 0
35
+ self.assertTrue(all(distances == distances_))
36
+
37
+ adjacency = test_digraph()
38
+ distances = get_distances(adjacency, [0])
39
+ distances_ = np.array([0, 1, 3, 2, 2, 3, -1, -1, -1, -1])
40
+ self.assertTrue(all(distances == distances_))
41
+ distances = get_distances(adjacency, [0], transpose=True)
42
+ self.assertTrue(sum(distances < 0) == 9)
43
+ distances = get_distances(adjacency, [0, 5], transpose=True)
44
+ distances_ = np.array([0, 2, -1, -1, 1, 0, 1, -1, -1, -1])
45
+ self.assertTrue(all(distances == distances_))
46
+
47
+ biadjacency = test_bigraph()
48
+ distances_row, distances_col = get_distances(biadjacency, [0])
49
+ distances_row_, distances_col_ = np.array([0, -1, 2, -1, -1, -1]), np.array([3, 1, -1, -1, -1, -1, -1, -1])
50
+ self.assertTrue(all(distances_row == distances_row_))
51
+ self.assertTrue(all(distances_col == distances_col_))
52
+
53
+ adjacency = test_graph()
54
+ distances_row, distances_col = get_distances(adjacency, source_col=[0])
55
+ self.assertTrue(all(distances_row % 2))
56
+ self.assertTrue(all((distances_col + 1) % 2))
57
+
58
+ biadjacency = test_bigraph()
59
+ distances_row, distances_col = get_distances(biadjacency, source=0, source_col=[1, 2])
60
+ distances_row_, distances_col_ = np.array([0, 1, 1, -1, -1, -1]), np.array([2, 0, 0, 2, -1, -1, -1, -1])
61
+ self.assertTrue(all(distances_row == distances_row_))
62
+ self.assertTrue(all(distances_col == distances_col_))
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """"tests for search.py"""
4
+
5
+ import unittest
6
+
7
+ import numpy as np
8
+
9
+ from sknetwork.data import cyclic_digraph
10
+ from sknetwork.data.test_graphs import *
11
+ from sknetwork.path import breadth_first_search
12
+
13
+
14
+ class TestSearch(unittest.TestCase):
15
+
16
+ def test_bfs(self):
17
+ adjacency = cyclic_digraph(3)
18
+ search = breadth_first_search(adjacency, 0)
19
+ search_ = np.arange(3)
20
+ self.assertTrue(all(search == search_))
21
+
22
+ adjacency = test_graph_empty()
23
+ search = breadth_first_search(adjacency, 0)
24
+ search_ = np.array([0])
25
+ self.assertTrue(all(search == search_))
26
+
27
+ adjacency = test_graph()
28
+ search = breadth_first_search(adjacency, 3)
29
+ search_ = np.array([3, 1, 2, 0, 4, 6, 8, 5, 7, 9])
30
+ self.assertTrue(all(search == search_))
31
+
32
+ adjacency = test_disconnected_graph()
33
+ search = breadth_first_search(adjacency, 2)
34
+ search_ = np.array([2, 3])
35
+ self.assertTrue(all(search == search_))
36
+
37
+ adjacency = test_digraph()
38
+ search = breadth_first_search(adjacency, 1)
39
+ search_ = {1, 3, 4, 2, 5}
40
+ self.assertTrue(set(list(search)) == search_)
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """"tests for shortest_path.py"""
4
+ import unittest
5
+
6
+ from sknetwork.data.test_graphs import *
7
+ from sknetwork.path.shortest_path import get_shortest_path
8
+
9
+
10
+ class TestShortestPath(unittest.TestCase):
11
+
12
+ def test_path(self):
13
+ adjacency = test_graph_empty()
14
+ path = get_shortest_path(adjacency, 0)
15
+ self.assertEqual(path.nnz, 0)
16
+
17
+ adjacency = test_graph()
18
+ path = get_shortest_path(adjacency, 0)
19
+ self.assertEqual(path.nnz, 10)
20
+ path = get_shortest_path(adjacency, [0, 4, 6])
21
+ self.assertEqual(path.nnz, 10)
22
+ path = get_shortest_path(adjacency, np.arange(10))
23
+ self.assertEqual(path.nnz, 0)
24
+ path = get_shortest_path(adjacency, [0, 5])
25
+ self.assertEqual(path.nnz, 9)
26
+
27
+ adjacency = test_disconnected_graph()
28
+ path = get_shortest_path(adjacency, 4)
29
+ self.assertEqual(path.nnz, 5)
30
+
31
+ adjacency = test_digraph()
32
+ path = get_shortest_path(adjacency, 0)
33
+ self.assertEqual(path.nnz, 5)
34
+
35
+ biadjacency = test_bigraph()
36
+ path = get_shortest_path(biadjacency, 0)
37
+ self.assertEqual(path.nnz, 3)
38
+ self.assertTrue(path.shape[0] == np.sum(biadjacency.shape))
39
+ path = get_shortest_path(biadjacency, source_col=np.arange(biadjacency.shape[1]))
40
+ self.assertEqual(path.nnz, biadjacency.nnz)
@@ -0,0 +1,8 @@
1
+ """ranking module"""
2
+ from sknetwork.ranking.base import BaseRanking
3
+ from sknetwork.ranking.betweenness import Betweenness
4
+ from sknetwork.ranking.closeness import Closeness
5
+ from sknetwork.ranking.hits import HITS
6
+ from sknetwork.ranking.katz import Katz
7
+ from sknetwork.ranking.pagerank import PageRank
8
+ from sknetwork.ranking.postprocess import top_k