scikit-network 0.33.0__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.

Potentially problematic release.


This version of scikit-network might be problematic. Click here for more details.

Files changed (217) hide show
  1. scikit_network-0.33.0.dist-info/AUTHORS.rst +43 -0
  2. scikit_network-0.33.0.dist-info/LICENSE +34 -0
  3. scikit_network-0.33.0.dist-info/METADATA +517 -0
  4. scikit_network-0.33.0.dist-info/RECORD +217 -0
  5. scikit_network-0.33.0.dist-info/WHEEL +6 -0
  6. scikit_network-0.33.0.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 +142 -0
  12. sknetwork/classification/base_rank.py +133 -0
  13. sknetwork/classification/diffusion.py +134 -0
  14. sknetwork/classification/knn.py +139 -0
  15. sknetwork/classification/metrics.py +205 -0
  16. sknetwork/classification/pagerank.py +66 -0
  17. sknetwork/classification/propagation.py +152 -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.cpython-312-x86_64-linux-gnu.so +0 -0
  26. sknetwork/classification/vote.pyx +56 -0
  27. sknetwork/clustering/__init__.py +8 -0
  28. sknetwork/clustering/base.py +172 -0
  29. sknetwork/clustering/kcenters.py +253 -0
  30. sknetwork/clustering/leiden.py +242 -0
  31. sknetwork/clustering/leiden_core.cpython-312-x86_64-linux-gnu.so +0 -0
  32. sknetwork/clustering/leiden_core.pyx +124 -0
  33. sknetwork/clustering/louvain.py +286 -0
  34. sknetwork/clustering/louvain_core.cpython-312-x86_64-linux-gnu.so +0 -0
  35. sknetwork/clustering/louvain_core.pyx +124 -0
  36. sknetwork/clustering/metrics.py +91 -0
  37. sknetwork/clustering/postprocess.py +66 -0
  38. sknetwork/clustering/propagation_clustering.py +104 -0
  39. sknetwork/clustering/tests/__init__.py +1 -0
  40. sknetwork/clustering/tests/test_API.py +38 -0
  41. sknetwork/clustering/tests/test_kcenters.py +60 -0
  42. sknetwork/clustering/tests/test_leiden.py +34 -0
  43. sknetwork/clustering/tests/test_louvain.py +129 -0
  44. sknetwork/clustering/tests/test_metrics.py +50 -0
  45. sknetwork/clustering/tests/test_postprocess.py +39 -0
  46. sknetwork/data/__init__.py +6 -0
  47. sknetwork/data/base.py +33 -0
  48. sknetwork/data/load.py +406 -0
  49. sknetwork/data/models.py +459 -0
  50. sknetwork/data/parse.py +644 -0
  51. sknetwork/data/test_graphs.py +84 -0
  52. sknetwork/data/tests/__init__.py +1 -0
  53. sknetwork/data/tests/test_API.py +30 -0
  54. sknetwork/data/tests/test_base.py +14 -0
  55. sknetwork/data/tests/test_load.py +95 -0
  56. sknetwork/data/tests/test_models.py +52 -0
  57. sknetwork/data/tests/test_parse.py +250 -0
  58. sknetwork/data/tests/test_test_graphs.py +29 -0
  59. sknetwork/data/tests/test_toy_graphs.py +68 -0
  60. sknetwork/data/timeout.py +38 -0
  61. sknetwork/data/toy_graphs.py +611 -0
  62. sknetwork/embedding/__init__.py +8 -0
  63. sknetwork/embedding/base.py +94 -0
  64. sknetwork/embedding/force_atlas.py +198 -0
  65. sknetwork/embedding/louvain_embedding.py +148 -0
  66. sknetwork/embedding/random_projection.py +135 -0
  67. sknetwork/embedding/spectral.py +141 -0
  68. sknetwork/embedding/spring.py +198 -0
  69. sknetwork/embedding/svd.py +359 -0
  70. sknetwork/embedding/tests/__init__.py +1 -0
  71. sknetwork/embedding/tests/test_API.py +49 -0
  72. sknetwork/embedding/tests/test_force_atlas.py +35 -0
  73. sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
  74. sknetwork/embedding/tests/test_random_projection.py +28 -0
  75. sknetwork/embedding/tests/test_spectral.py +81 -0
  76. sknetwork/embedding/tests/test_spring.py +50 -0
  77. sknetwork/embedding/tests/test_svd.py +43 -0
  78. sknetwork/gnn/__init__.py +10 -0
  79. sknetwork/gnn/activation.py +117 -0
  80. sknetwork/gnn/base.py +181 -0
  81. sknetwork/gnn/base_activation.py +89 -0
  82. sknetwork/gnn/base_layer.py +109 -0
  83. sknetwork/gnn/gnn_classifier.py +305 -0
  84. sknetwork/gnn/layer.py +153 -0
  85. sknetwork/gnn/loss.py +180 -0
  86. sknetwork/gnn/neighbor_sampler.py +65 -0
  87. sknetwork/gnn/optimizer.py +164 -0
  88. sknetwork/gnn/tests/__init__.py +1 -0
  89. sknetwork/gnn/tests/test_activation.py +56 -0
  90. sknetwork/gnn/tests/test_base.py +75 -0
  91. sknetwork/gnn/tests/test_base_layer.py +37 -0
  92. sknetwork/gnn/tests/test_gnn_classifier.py +130 -0
  93. sknetwork/gnn/tests/test_layers.py +80 -0
  94. sknetwork/gnn/tests/test_loss.py +33 -0
  95. sknetwork/gnn/tests/test_neigh_sampler.py +23 -0
  96. sknetwork/gnn/tests/test_optimizer.py +43 -0
  97. sknetwork/gnn/tests/test_utils.py +41 -0
  98. sknetwork/gnn/utils.py +127 -0
  99. sknetwork/hierarchy/__init__.py +6 -0
  100. sknetwork/hierarchy/base.py +96 -0
  101. sknetwork/hierarchy/louvain_hierarchy.py +272 -0
  102. sknetwork/hierarchy/metrics.py +234 -0
  103. sknetwork/hierarchy/paris.cpython-312-x86_64-linux-gnu.so +0 -0
  104. sknetwork/hierarchy/paris.pyx +316 -0
  105. sknetwork/hierarchy/postprocess.py +350 -0
  106. sknetwork/hierarchy/tests/__init__.py +1 -0
  107. sknetwork/hierarchy/tests/test_API.py +24 -0
  108. sknetwork/hierarchy/tests/test_algos.py +34 -0
  109. sknetwork/hierarchy/tests/test_metrics.py +62 -0
  110. sknetwork/hierarchy/tests/test_postprocess.py +57 -0
  111. sknetwork/linalg/__init__.py +9 -0
  112. sknetwork/linalg/basics.py +37 -0
  113. sknetwork/linalg/diteration.cpython-312-x86_64-linux-gnu.so +0 -0
  114. sknetwork/linalg/diteration.pyx +47 -0
  115. sknetwork/linalg/eig_solver.py +93 -0
  116. sknetwork/linalg/laplacian.py +15 -0
  117. sknetwork/linalg/normalizer.py +86 -0
  118. sknetwork/linalg/operators.py +225 -0
  119. sknetwork/linalg/polynome.py +76 -0
  120. sknetwork/linalg/ppr_solver.py +170 -0
  121. sknetwork/linalg/push.cpython-312-x86_64-linux-gnu.so +0 -0
  122. sknetwork/linalg/push.pyx +71 -0
  123. sknetwork/linalg/sparse_lowrank.py +142 -0
  124. sknetwork/linalg/svd_solver.py +91 -0
  125. sknetwork/linalg/tests/__init__.py +1 -0
  126. sknetwork/linalg/tests/test_eig.py +44 -0
  127. sknetwork/linalg/tests/test_laplacian.py +18 -0
  128. sknetwork/linalg/tests/test_normalization.py +34 -0
  129. sknetwork/linalg/tests/test_operators.py +66 -0
  130. sknetwork/linalg/tests/test_polynome.py +38 -0
  131. sknetwork/linalg/tests/test_ppr.py +50 -0
  132. sknetwork/linalg/tests/test_sparse_lowrank.py +61 -0
  133. sknetwork/linalg/tests/test_svd.py +38 -0
  134. sknetwork/linkpred/__init__.py +2 -0
  135. sknetwork/linkpred/base.py +46 -0
  136. sknetwork/linkpred/nn.py +126 -0
  137. sknetwork/linkpred/tests/__init__.py +1 -0
  138. sknetwork/linkpred/tests/test_nn.py +27 -0
  139. sknetwork/log.py +19 -0
  140. sknetwork/path/__init__.py +5 -0
  141. sknetwork/path/dag.py +54 -0
  142. sknetwork/path/distances.py +98 -0
  143. sknetwork/path/search.py +31 -0
  144. sknetwork/path/shortest_path.py +61 -0
  145. sknetwork/path/tests/__init__.py +1 -0
  146. sknetwork/path/tests/test_dag.py +37 -0
  147. sknetwork/path/tests/test_distances.py +62 -0
  148. sknetwork/path/tests/test_search.py +40 -0
  149. sknetwork/path/tests/test_shortest_path.py +40 -0
  150. sknetwork/ranking/__init__.py +8 -0
  151. sknetwork/ranking/base.py +61 -0
  152. sknetwork/ranking/betweenness.cpython-312-x86_64-linux-gnu.so +0 -0
  153. sknetwork/ranking/betweenness.pyx +97 -0
  154. sknetwork/ranking/closeness.py +92 -0
  155. sknetwork/ranking/hits.py +94 -0
  156. sknetwork/ranking/katz.py +83 -0
  157. sknetwork/ranking/pagerank.py +110 -0
  158. sknetwork/ranking/postprocess.py +37 -0
  159. sknetwork/ranking/tests/__init__.py +1 -0
  160. sknetwork/ranking/tests/test_API.py +32 -0
  161. sknetwork/ranking/tests/test_betweenness.py +38 -0
  162. sknetwork/ranking/tests/test_closeness.py +30 -0
  163. sknetwork/ranking/tests/test_hits.py +20 -0
  164. sknetwork/ranking/tests/test_pagerank.py +62 -0
  165. sknetwork/ranking/tests/test_postprocess.py +26 -0
  166. sknetwork/regression/__init__.py +4 -0
  167. sknetwork/regression/base.py +61 -0
  168. sknetwork/regression/diffusion.py +210 -0
  169. sknetwork/regression/tests/__init__.py +1 -0
  170. sknetwork/regression/tests/test_API.py +32 -0
  171. sknetwork/regression/tests/test_diffusion.py +56 -0
  172. sknetwork/sknetwork.py +3 -0
  173. sknetwork/test_base.py +35 -0
  174. sknetwork/test_log.py +15 -0
  175. sknetwork/topology/__init__.py +8 -0
  176. sknetwork/topology/cliques.cpython-312-x86_64-linux-gnu.so +0 -0
  177. sknetwork/topology/cliques.pyx +149 -0
  178. sknetwork/topology/core.cpython-312-x86_64-linux-gnu.so +0 -0
  179. sknetwork/topology/core.pyx +90 -0
  180. sknetwork/topology/cycles.py +243 -0
  181. sknetwork/topology/minheap.cpython-312-x86_64-linux-gnu.so +0 -0
  182. sknetwork/topology/minheap.pxd +20 -0
  183. sknetwork/topology/minheap.pyx +109 -0
  184. sknetwork/topology/structure.py +194 -0
  185. sknetwork/topology/tests/__init__.py +1 -0
  186. sknetwork/topology/tests/test_cliques.py +28 -0
  187. sknetwork/topology/tests/test_core.py +19 -0
  188. sknetwork/topology/tests/test_cycles.py +65 -0
  189. sknetwork/topology/tests/test_structure.py +85 -0
  190. sknetwork/topology/tests/test_triangles.py +38 -0
  191. sknetwork/topology/tests/test_wl.py +72 -0
  192. sknetwork/topology/triangles.cpython-312-x86_64-linux-gnu.so +0 -0
  193. sknetwork/topology/triangles.pyx +151 -0
  194. sknetwork/topology/weisfeiler_lehman.py +133 -0
  195. sknetwork/topology/weisfeiler_lehman_core.cpython-312-x86_64-linux-gnu.so +0 -0
  196. sknetwork/topology/weisfeiler_lehman_core.pyx +114 -0
  197. sknetwork/utils/__init__.py +7 -0
  198. sknetwork/utils/check.py +355 -0
  199. sknetwork/utils/format.py +221 -0
  200. sknetwork/utils/membership.py +82 -0
  201. sknetwork/utils/neighbors.py +115 -0
  202. sknetwork/utils/tests/__init__.py +1 -0
  203. sknetwork/utils/tests/test_check.py +190 -0
  204. sknetwork/utils/tests/test_format.py +63 -0
  205. sknetwork/utils/tests/test_membership.py +24 -0
  206. sknetwork/utils/tests/test_neighbors.py +41 -0
  207. sknetwork/utils/tests/test_tfidf.py +18 -0
  208. sknetwork/utils/tests/test_values.py +66 -0
  209. sknetwork/utils/tfidf.py +37 -0
  210. sknetwork/utils/values.py +76 -0
  211. sknetwork/visualization/__init__.py +4 -0
  212. sknetwork/visualization/colors.py +34 -0
  213. sknetwork/visualization/dendrograms.py +277 -0
  214. sknetwork/visualization/graphs.py +1039 -0
  215. sknetwork/visualization/tests/__init__.py +1 -0
  216. sknetwork/visualization/tests/test_dendrograms.py +53 -0
  217. sknetwork/visualization/tests/test_graphs.py +176 -0
@@ -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
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in November 2019
5
+ @author: Nathan de Lara <nathan.delara@polytechnique.org>
6
+ """
7
+ from abc import ABC
8
+
9
+ import numpy as np
10
+
11
+ from sknetwork.base import Algorithm
12
+
13
+
14
+ class BaseRanking(Algorithm, ABC):
15
+ """Base class for ranking algorithms.
16
+
17
+ Attributes
18
+ ----------
19
+ scores_ : np.ndarray
20
+ Score of each node.
21
+ scores_row_: np.ndarray
22
+ Scores of rows, for bipartite graphs.
23
+ scores_col_: np.ndarray
24
+ Scores of columns, for bipartite graphs.
25
+ """
26
+ def __init__(self):
27
+ self.scores_ = None
28
+
29
+ def predict(self, columns: bool = False) -> np.ndarray:
30
+ """Return the scores predicted by the algorithm.
31
+
32
+ Parameters
33
+ ----------
34
+ columns : bool
35
+ If ``True``, return the prediction for columns.
36
+
37
+ Returns
38
+ -------
39
+ scores : np.ndarray
40
+ Scores.
41
+ """
42
+ if columns:
43
+ return self.scores_col_
44
+ return self.scores_
45
+
46
+ def fit_predict(self, *args, **kwargs) -> np.ndarray:
47
+ """Fit algorithm to data and return the scores. Same parameters as the ``fit`` method.
48
+
49
+ Returns
50
+ -------
51
+ scores : np.ndarray
52
+ Scores.
53
+ """
54
+ self.fit(*args, **kwargs)
55
+ return self.scores_
56
+
57
+ def _split_vars(self, shape):
58
+ n_row = shape[0]
59
+ self.scores_row_ = self.scores_[:n_row]
60
+ self.scores_col_ = self.scores_[n_row:]
61
+ self.scores_ = self.scores_row_
@@ -0,0 +1,97 @@
1
+ # distutils: language = c++
2
+ # cython: language_level=3
3
+ """
4
+ Created on September 17 2020
5
+ @author: Tiphaine Viard <tiphaine.viard@telecom-paris.fr>
6
+ """
7
+ from typing import Union
8
+ import numpy as np
9
+ from scipy import sparse
10
+
11
+ from sknetwork.ranking.base import BaseRanking
12
+ from sknetwork.utils.check import check_format, check_square, check_connected
13
+
14
+ from libcpp.vector cimport vector
15
+ from libcpp.queue cimport queue
16
+
17
+ class Betweenness(BaseRanking):
18
+ """ Betweenness centrality, based on Brandes' algorithm.
19
+
20
+ Attributes
21
+ ----------
22
+ scores_ : np.ndarray
23
+ Betweenness centrality value of each node
24
+
25
+ Example
26
+ -------
27
+ >>> from sknetwork.ranking import Betweenness
28
+ >>> from sknetwork.data.toy_graphs import bow_tie
29
+ >>> betweenness = Betweenness()
30
+ >>> adjacency = bow_tie()
31
+ >>> scores = betweenness.fit_transform(adjacency)
32
+ >>> scores
33
+ array([4., 0., 0., 0., 0.])
34
+
35
+ References
36
+ ----------
37
+ Brandes, Ulrik (2001). A faster algorithm for betweenness centrality. Journal of Mathematical Sociology.
38
+ """
39
+
40
+ def __init__(self, normalized: bool = False):
41
+ super(Betweenness, self).__init__()
42
+ self.normalized_ = normalized
43
+
44
+ def fit(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> 'Betweenness':
45
+ adjacency = check_format(adjacency)
46
+ check_square(adjacency)
47
+ check_connected(adjacency)
48
+
49
+ cdef int source
50
+ cdef vector[ vector[int] ] preds
51
+ cdef vector[int] sigma
52
+ cdef vector[int] dists
53
+ cdef int i
54
+ cdef int j
55
+ cdef vector[float] delta
56
+
57
+ cdef int n = adjacency.shape[0]
58
+ self.scores_ = np.zeros(n)
59
+ cdef vector[int] seen # Using list as stack
60
+ cdef queue[int] bfs_queue
61
+
62
+ for source in range(n):
63
+ preds = [[] for _ in range(n)]
64
+ sigma = np.zeros(n)
65
+ sigma[source] = 1
66
+ dists = -np.ones(n, dtype=int)
67
+ dists[source] = 0
68
+ bfs_queue.push(source)
69
+
70
+ while bfs_queue.size() != 0:
71
+ i = bfs_queue.front()
72
+ bfs_queue.pop()
73
+
74
+ seen.push_back(i)
75
+ neighbors = adjacency.indices[adjacency.indptr[i]:adjacency.indptr[i + 1]]
76
+ for j in neighbors:
77
+ if dists[j] < 0: # j found for the first time?
78
+ dists[j] = dists[i] + 1
79
+ bfs_queue.push(j)
80
+ if dists[j] == dists[i] + 1: # shortest path to j via i?
81
+ sigma[j] += sigma[i]
82
+ preds[j].push_back(i)
83
+
84
+ # Now backtrack to compute betweenness scores
85
+ delta = np.zeros(n)
86
+ while len(seen) != 0:
87
+ j = seen.back()
88
+ seen.pop_back()
89
+ for i in preds[j]:
90
+ delta[i] += sigma[i] / sigma[j] * (1 + delta[j])
91
+ if j != source:
92
+ self.scores_[j] += delta[j]
93
+
94
+ # Undirected graph, divide all values by two
95
+ self.scores_ = 1 / 2 * self.scores_
96
+
97
+ return self
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on November 12 2019
5
+ @author: Quentin Lutz <qlutz@enst.fr>
6
+ """
7
+ from math import log
8
+ from typing import Union, Optional
9
+
10
+ import numpy as np
11
+ from scipy import sparse
12
+
13
+ from sknetwork.path.shortest_path import get_distances
14
+ from sknetwork.ranking.base import BaseRanking
15
+ from sknetwork.utils.check import check_format, check_square, check_connected
16
+
17
+
18
+ class Closeness(BaseRanking):
19
+ """Ranking by closeness centrality of each node in a connected graph, corresponding to the average length of the
20
+ shortest paths from that node to all the other ones.
21
+
22
+ Parameters
23
+ ----------
24
+ method :
25
+ Denotes if the results should be exact or approximate.
26
+ tol: float
27
+ If ``method=='approximate'``, the allowed tolerance on each score entry.
28
+
29
+ Attributes
30
+ ----------
31
+ scores_ : np.ndarray
32
+ Closeness centrality of each node.
33
+
34
+ Example
35
+ -------
36
+ >>> from sknetwork.ranking import Closeness
37
+ >>> from sknetwork.data import cyclic_digraph
38
+ >>> closeness = Closeness()
39
+ >>> adjacency = cyclic_digraph(3)
40
+ >>> scores = closeness.fit_predict(adjacency)
41
+ >>> np.round(scores, 2)
42
+ array([0.67, 0.67, 0.67])
43
+
44
+ References
45
+ ----------
46
+ Eppstein, D., & Wang, J. (2001, January).
47
+ `Fast approximation of centrality.
48
+ <http://jgaa.info/accepted/2004/EppsteinWang2004.8.1.pdf>`_
49
+ In Proceedings of the twelfth annual ACM-SIAM symposium on Discrete algorithms (pp. 228-229).
50
+ Society for Industrial and Applied Mathematics.
51
+ """
52
+
53
+ def __init__(self, method: str = 'exact', tol: float = 1e-1):
54
+ super(Closeness, self).__init__()
55
+
56
+ self.method = method
57
+ self.tol = tol
58
+
59
+ def fit(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> 'Closeness':
60
+ """Closeness centrality for connected graphs.
61
+
62
+ Parameters
63
+ ----------
64
+ adjacency :
65
+ Adjacency matrix of the graph.
66
+
67
+ Returns
68
+ -------
69
+ self: :class:`Closeness`
70
+ """
71
+ adjacency = check_format(adjacency)
72
+ check_square(adjacency)
73
+ check_connected(adjacency)
74
+ n = adjacency.shape[0]
75
+
76
+ if self.method == 'exact':
77
+ n_sources = n
78
+ sources = np.arange(n)
79
+ elif self.method == 'approximate':
80
+ n_sources = min(int(log(n) / self.tol ** 2), n)
81
+ sources = np.random.choice(np.arange(n), n_sources, replace=False)
82
+ else:
83
+ raise ValueError("Method should be either 'exact' or 'approximate'.")
84
+
85
+ distances = np.array([get_distances(adjacency, source=source) for source in sources])
86
+
87
+ distances_min = np.min(distances, axis=1)
88
+ scores = (n - 1) / n / np.mean(distances, axis=1)
89
+ scores[distances_min < 0] = 0
90
+ self.scores_ = scores
91
+
92
+ return self
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on Oct 07 2019
5
+ @author: Nathan de Lara <nathan.delara@polytechnique.org>
6
+ """
7
+
8
+ from typing import Union
9
+
10
+ import numpy as np
11
+ from scipy import sparse
12
+
13
+ from sknetwork.linalg import SVDSolver, LanczosSVD
14
+ from sknetwork.ranking.base import BaseRanking
15
+ from sknetwork.utils.check import check_format
16
+
17
+
18
+ class HITS(BaseRanking):
19
+ """Hub and authority scores of each node.
20
+ For bipartite graphs, the hub score is computed on rows and the authority score on columns.
21
+
22
+ Parameters
23
+ ----------
24
+ solver : ``'lanczos'`` (default, Lanczos algorithm) or :class:`SVDSolver` (custom solver)
25
+ Which solver to use.
26
+
27
+ Attributes
28
+ ----------
29
+ scores_ : np.ndarray
30
+ Hub score of each node.
31
+ scores_row_ : np.ndarray
32
+ Hub score of each row, for bipartite graphs.
33
+ scores_col_ : np.ndarray
34
+ Authority score of each column, for bipartite graphs.
35
+
36
+ Example
37
+ -------
38
+ >>> from sknetwork.ranking import HITS
39
+ >>> from sknetwork.data import star_wars
40
+ >>> hits = HITS()
41
+ >>> biadjacency = star_wars()
42
+ >>> scores = hits.fit_predict(biadjacency)
43
+ >>> np.round(scores, 2)
44
+ array([0.5 , 0.23, 0.69, 0.46])
45
+
46
+ References
47
+ ----------
48
+ Kleinberg, J. M. (1999). Authoritative sources in a hyperlinked environment.
49
+ Journal of the ACM, 46(5), 604-632.
50
+ """
51
+ def __init__(self, solver: Union[str, SVDSolver] = 'lanczos'):
52
+ super(HITS, self).__init__()
53
+
54
+ if type(solver) == str:
55
+ self.solver: SVDSolver = LanczosSVD()
56
+ else:
57
+ self.solver = solver
58
+
59
+ def fit(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> 'HITS':
60
+ """Compute HITS algorithm with a spectral method.
61
+
62
+ Parameters
63
+ ----------
64
+ adjacency :
65
+ Adjacency or biadjacency matrix of the graph.
66
+
67
+ Returns
68
+ -------
69
+ self: :class:`HITS`
70
+ """
71
+ adjacency = check_format(adjacency)
72
+
73
+ self.solver.fit(adjacency, 1)
74
+ hubs: np.ndarray = self.solver.singular_vectors_left_.reshape(-1)
75
+ authorities: np.ndarray = self.solver.singular_vectors_right_.reshape(-1)
76
+
77
+ h_pos, h_neg = (hubs > 0).sum(), (hubs < 0).sum()
78
+ a_pos, a_neg = (authorities > 0).sum(), (authorities < 0).sum()
79
+
80
+ if h_pos > h_neg:
81
+ hubs = np.clip(hubs, a_min=0., a_max=None)
82
+ else:
83
+ hubs = np.clip(-hubs, a_min=0., a_max=None)
84
+
85
+ if a_pos > a_neg:
86
+ authorities = np.clip(authorities, a_min=0., a_max=None)
87
+ else:
88
+ authorities = np.clip(-authorities, a_min=0., a_max=None)
89
+
90
+ self.scores_row_ = hubs
91
+ self.scores_col_ = authorities
92
+ self.scores_ = hubs
93
+
94
+ return self
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on May 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 LinearOperator
12
+
13
+ from sknetwork.linalg.polynome import Polynome
14
+ from sknetwork.ranking.base import BaseRanking
15
+ from sknetwork.utils.check import check_format
16
+ from sknetwork.utils.format import get_adjacency
17
+
18
+
19
+ class Katz(BaseRanking):
20
+ """Katz centrality, defined by:
21
+
22
+ :math:`\\sum_{k=1}^K\\alpha^k(A^k)^T\\mathbf{1}`
23
+
24
+ where :math:`A` is the adjacency matrix, :math:`\\alpha` is the damping factor and :math:`K` is the path length.
25
+
26
+ Parameters
27
+ ----------
28
+ damping_factor : float
29
+ Damping factor for path contributions.
30
+ path_length : int
31
+ Maximum length of the paths.
32
+
33
+ Attributes
34
+ ----------
35
+ scores_ : np.ndarray
36
+ Score of each node.
37
+ scores_row_: np.ndarray
38
+ Scores of rows, for bipartite graphs.
39
+ scores_col_: np.ndarray
40
+ Scores of columns, for bipartite graphs.
41
+
42
+ Examples
43
+ --------
44
+ >>> from sknetwork.data.toy_graphs import house
45
+ >>> adjacency = house()
46
+ >>> katz = Katz()
47
+ >>> scores = katz.fit_predict(adjacency)
48
+ >>> np.round(scores, 2)
49
+ array([6.5 , 8.25, 5.62, 5.62, 8.25])
50
+
51
+ References
52
+ ----------
53
+ Katz, L. (1953). `A new status index derived from sociometric analysis
54
+ <https://link.springer.com/content/pdf/10.1007/BF02289026.pdf>`_. Psychometrika, 18(1), 39-43.
55
+ """
56
+ def __init__(self, damping_factor: float = 0.5, path_length: int = 4):
57
+ super(Katz, self).__init__()
58
+ self.damping_factor = damping_factor
59
+ self.path_length = path_length
60
+ self.bipartite = None
61
+
62
+ def fit(self, input_matrix: Union[sparse.csr_matrix, np.ndarray, LinearOperator]) -> 'Katz':
63
+ """Katz centrality.
64
+
65
+ Parameters
66
+ ----------
67
+ input_matrix :
68
+ Adjacency matrix or biadjacency matrix of the graph.
69
+
70
+ Returns
71
+ -------
72
+ self: :class:`Katz`
73
+ """
74
+ input_matrix = check_format(input_matrix)
75
+ adjacency, self.bipartite = get_adjacency(input_matrix)
76
+ n = adjacency.shape[0]
77
+ coefs = self.damping_factor ** np.arange(self.path_length + 1)
78
+ coefs[0] = 0.
79
+ polynome = Polynome(adjacency.T.astype(bool).tocsr(), coefs)
80
+ self.scores_ = polynome.dot(np.ones(n))
81
+ if self.bipartite:
82
+ self._split_vars(input_matrix.shape)
83
+ return self