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,65 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on Jul 24, 2019
5
+ """
6
+ import numpy as np
7
+ from scipy import sparse
8
+
9
+ from sknetwork.utils.check import is_symmetric
10
+
11
+
12
+ def breadth_first_search(adjacency: sparse.csr_matrix, source: int, return_predecessors: bool = True):
13
+ """Breadth-first ordering starting with specified node.
14
+
15
+ Based on SciPy (scipy.sparse.csgraph.breadth_first_order)
16
+
17
+ Parameters
18
+ ----------
19
+ adjacency :
20
+ The adjacency matrix of the graph
21
+ source : int
22
+ The node from which to start the ordering
23
+ return_predecessors : bool
24
+ If ``True``, the size predecessor matrix is returned
25
+
26
+ Returns
27
+ -------
28
+ node_array : np.ndarray
29
+ The breadth-first list of nodes, starting with specified node. The length of node_array is the number of nodes
30
+ reachable from the specified node.
31
+ predecessors : np.ndarray
32
+ Returned only if ``return_predecessors == True``. The list of predecessors of each node in a breadth-first tree.
33
+ If node ``i`` is in the tree, then its parent is given by ``predecessors[i]``. If node ``i`` is not in the tree
34
+ (and for the parent node) then ``predecessors[i] = -9999``.
35
+ """
36
+ directed = not is_symmetric(adjacency)
37
+ return sparse.csgraph.breadth_first_order(adjacency, source, directed, return_predecessors)
38
+
39
+
40
+ def depth_first_search(adjacency: sparse.csr_matrix, source: int, return_predecessors: bool = True):
41
+ """Depth-first ordering starting with specified node.
42
+
43
+ Based on SciPy (scipy.sparse.csgraph.depth_first_order)
44
+
45
+ Parameters
46
+ ----------
47
+ adjacency :
48
+ The adjacency matrix of the graph
49
+ source :
50
+ The node from which to start the ordering
51
+ return_predecessors:
52
+ If ``True``, the size predecessor matrix is returned
53
+
54
+ Returns
55
+ -------
56
+ node_array : np.ndarray
57
+ The depth-first list of nodes, starting with specified node. The length of node_array is the number of nodes
58
+ reachable from the specified node.
59
+ predecessors : np.ndarray
60
+ Returned only if ``return_predecessors == True``. The list of predecessors of each node in a depth-first tree.
61
+ If node ``i`` is in the tree, then its parent is given by ``predecessors[i]``. If node ``i`` is not in the tree
62
+ (and for the parent node) then ``predecessors[i] = -9999``.
63
+ """
64
+ directed = not is_symmetric(adjacency)
65
+ return sparse.csgraph.depth_first_order(adjacency, source, directed, return_predecessors)
@@ -0,0 +1,186 @@
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 functools import partial
8
+ from multiprocessing import Pool
9
+ from typing import Optional, Union, Iterable
10
+
11
+ import numpy as np
12
+ from scipy import sparse
13
+
14
+ from sknetwork.utils.check import check_n_jobs, is_symmetric
15
+
16
+
17
+ def get_distances(adjacency: sparse.csr_matrix, sources: Optional[Union[int, Iterable]] = None, method: str = 'D',
18
+ return_predecessors: bool = False, unweighted: bool = False, n_jobs: Optional[int] = None):
19
+ """Compute distances between nodes.
20
+
21
+ * Graphs
22
+ * Digraphs
23
+
24
+
25
+ Based on SciPy (scipy.sparse.csgraph.shortest_path)
26
+
27
+ Parameters
28
+ ----------
29
+ adjacency :
30
+ The adjacency matrix of the graph
31
+ sources :
32
+ If specified, only compute the paths for the points at the given indices. Will not work with ``method =='FW'``.
33
+ method :
34
+ The method to be used.
35
+
36
+ * ``'D'`` (Dijkstra),
37
+ * ``'BF'`` (Bellman-Ford),
38
+ * ``'J'`` (Johnson).
39
+ return_predecessors :
40
+ If ``True``, the size predecessor matrix is returned
41
+ unweighted :
42
+ If ``True``, the weights of the edges are ignored
43
+ n_jobs :
44
+ If an integer value is given, denotes the number of workers to use (-1 means the maximum number will be used).
45
+ If ``None``, no parallel computations are made.
46
+
47
+ Returns
48
+ -------
49
+ dist_matrix : np.ndarray
50
+ Matrix of distances between nodes. ``dist_matrix[i,j]`` gives the shortest
51
+ distance from the ``i``-th source to node ``j`` in the graph (infinite if no path exists
52
+ from the ``i``-th source to node ``j``).
53
+ predecessors : np.ndarray, optional
54
+ Returned only if ``return_predecessors == True``. The matrix of predecessors, which can be used to reconstruct
55
+ the shortest paths. Row ``i`` of the predecessor matrix contains information on the shortest paths from the
56
+ ``i``-th source: each entry ``predecessors[i, j]`` gives the index of the previous node in the path from
57
+ the ``i``-th source to node ``j`` (-1 if no path exists from the ``i``-th source to node ``j``).
58
+
59
+ Examples
60
+ --------
61
+ >>> from sknetwork.data import cyclic_digraph
62
+ >>> adjacency = cyclic_digraph(3)
63
+ >>> get_distances(adjacency, sources=0)
64
+ array([0., 1., 2.])
65
+ >>> get_distances(adjacency, sources=0, return_predecessors=True)
66
+ (array([0., 1., 2.]), array([-1, 0, 1]))
67
+
68
+ """
69
+ n_jobs = check_n_jobs(n_jobs)
70
+ if method == 'FW' and n_jobs != 1:
71
+ raise ValueError('The Floyd-Warshall algorithm cannot be used with parallel computations.')
72
+ if sources is None:
73
+ sources = np.arange(adjacency.shape[0])
74
+ elif np.issubdtype(type(sources), np.integer):
75
+ sources = np.array([sources])
76
+ n = len(sources)
77
+ directed = not is_symmetric(adjacency)
78
+ local_function = partial(sparse.csgraph.shortest_path,
79
+ adjacency, method, directed, return_predecessors, unweighted, False)
80
+ if n_jobs == 1 or n == 1:
81
+ try:
82
+ res = sparse.csgraph.shortest_path(adjacency, method, directed, return_predecessors,
83
+ unweighted, False, sources)
84
+ except sparse.csgraph.NegativeCycleError:
85
+ raise ValueError("The shortest path computation could not be completed because a negative cycle is present.")
86
+ else:
87
+ try:
88
+ with Pool(n_jobs) as pool:
89
+ res = np.array(pool.map(local_function, sources))
90
+ except sparse.csgraph.NegativeCycleError:
91
+ pool.terminate()
92
+ raise ValueError("The shortest path computation could not be completed because a negative cycle is present.")
93
+ if return_predecessors:
94
+ res[1][res[1] < 0] = -1
95
+ if n == 1:
96
+ return res[0].ravel(), res[1].astype(int).ravel()
97
+ else:
98
+ return res[0], res[1].astype(int)
99
+ else:
100
+ if n == 1:
101
+ return res.ravel()
102
+ else:
103
+ return res
104
+
105
+
106
+ def get_shortest_path(adjacency: sparse.csr_matrix, sources: Union[int, Iterable], targets: Union[int, Iterable],
107
+ method: str = 'D', unweighted: bool = False, n_jobs: Optional[int] = None):
108
+ """Compute the shortest paths in the graph.
109
+
110
+ Parameters
111
+ ----------
112
+ adjacency :
113
+ The adjacency matrix of the graph
114
+ sources : int or iterable
115
+ Sources nodes.
116
+ targets : int or iterable
117
+ Target nodes.
118
+ method :
119
+ The method to be used.
120
+
121
+ * ``'D'`` (Dijkstra),
122
+ * ``'BF'`` (Bellman-Ford),
123
+ * ``'J'`` (Johnson).
124
+ unweighted :
125
+ If ``True``, the weights of the edges are ignored
126
+ n_jobs :
127
+ If an integer value is given, denotes the number of workers to use (-1 means the maximum number will be used).
128
+ If ``None``, no parallel computations are made.
129
+
130
+ Returns
131
+ -------
132
+ paths : list
133
+ If single source and single target, return a list containing the nodes on the path from source to target.
134
+ If multiple sources or multiple targets, return a list of paths as lists.
135
+ An empty list means that the path does not exist.
136
+
137
+ Examples
138
+ --------
139
+ >>> from sknetwork.data import linear_digraph
140
+ >>> adjacency = linear_digraph(3)
141
+ >>> get_shortest_path(adjacency, 0, 2)
142
+ [0, 1, 2]
143
+ >>> get_shortest_path(adjacency, 2, 0)
144
+ []
145
+ >>> get_shortest_path(adjacency, 0, [1, 2])
146
+ [[0, 1], [0, 1, 2]]
147
+ >>> get_shortest_path(adjacency, [0, 1], 2)
148
+ [[0, 1, 2], [1, 2]]
149
+ """
150
+ if np.issubdtype(type(sources), np.integer):
151
+ sources = [sources]
152
+ if np.issubdtype(type(targets), np.integer):
153
+ targets = [targets]
154
+
155
+ if len(sources) == 1:
156
+ source2target = True
157
+ source = sources[0]
158
+ elif len(targets) == 1:
159
+ source2target = False
160
+ source = targets[0]
161
+ targets = sources
162
+ else:
163
+ raise ValueError(
164
+ 'This request is ambiguous. Either use one source and multiple targets or multiple sources and one target.')
165
+
166
+ if source2target:
167
+ dists, preds = get_distances(adjacency, source, method, True, unweighted, n_jobs)
168
+ else:
169
+ dists, preds = get_distances(adjacency.T, source, method, True, unweighted, n_jobs)
170
+
171
+ paths = []
172
+ for target in targets:
173
+ if dists[target] == np.inf:
174
+ path = []
175
+ else:
176
+ path = [target]
177
+ node = target
178
+ while node != source:
179
+ node = preds[node]
180
+ path.append(node)
181
+ if source2target:
182
+ path.reverse()
183
+ paths.append(path)
184
+ if len(paths) == 1:
185
+ paths = paths[0]
186
+ return paths
@@ -0,0 +1 @@
1
+ """tests for path module"""
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """"tests for metrics.py"""
4
+ import unittest
5
+
6
+ from sknetwork.data import house
7
+ from sknetwork.path import get_diameter, get_eccentricity, get_radius
8
+
9
+
10
+
11
+ class TestMetrics(unittest.TestCase):
12
+
13
+ def test_diameter_1(self):
14
+ adjacency = house()
15
+ with self.assertRaises(ValueError):
16
+ get_diameter(adjacency, 2.5)
17
+ def test_diameter_2(self):
18
+ adjacency = house()
19
+ self.assertEqual(get_diameter(adjacency), 2)
20
+ def test_eccentricity_1(self):
21
+ adjacency = house()
22
+ self.assertEqual(get_eccentricity(adjacency, 1), 2)
23
+ def test_radius_1(self):
24
+ adjacency = house()
25
+ self.assertEqual(get_radius(adjacency), 2)
26
+ def test_radius_2(self):
27
+ adjacency = house()
28
+ self.assertEqual(get_radius(adjacency,[0,1]), 2)
29
+
@@ -0,0 +1,25 @@
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.path import breadth_first_search, depth_first_search
11
+
12
+
13
+ class TestSearch(unittest.TestCase):
14
+
15
+ def setUp(self) -> None:
16
+ """Load graph for tests."""
17
+ self.adjacency = cyclic_digraph(3).astype(bool)
18
+
19
+ def test_bfs(self):
20
+ self.assertTrue((breadth_first_search(
21
+ self.adjacency, 0, return_predecessors=False) == np.array([0, 1, 2])).all())
22
+
23
+ def test_dfs(self):
24
+ self.assertTrue((depth_first_search(
25
+ self.adjacency, 0, return_predecessors=False) == np.array([0, 1, 2])).all())
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """"tests for shortest_path.py"""
4
+ import unittest
5
+
6
+ from sknetwork.data import karate_club, cyclic_digraph
7
+ from sknetwork.path.shortest_path import get_distances, get_shortest_path
8
+ import numpy as np
9
+ from scipy.sparse import csr_matrix
10
+
11
+
12
+ class TestShortestPath(unittest.TestCase):
13
+
14
+ def test_parallel(self):
15
+ adjacency = karate_club()
16
+ dist1 = get_distances(adjacency)
17
+ dist2 = get_distances(adjacency, n_jobs=-1)
18
+ self.assertTrue((dist1 == dist2).all())
19
+
20
+ def test_predecessors(self):
21
+ adjacency = karate_club()
22
+ _, predecessors = get_distances(adjacency, return_predecessors=True)
23
+ self.assertTupleEqual(predecessors.shape, adjacency.shape)
24
+
25
+ def test_shortest_paths(self):
26
+ with self.assertRaises(ValueError):
27
+ get_shortest_path(cyclic_digraph(3), [0, 1], [0, 1])
28
+
29
+ def test_error_on_parallel_FW(self):
30
+ adjacency = karate_club()
31
+ self.assertRaises(ValueError, get_distances, adjacency, n_jobs=2, method='FW')
32
+
33
+ def test_error_neg_cycle_parallel(self):
34
+ adj = np.ones((5, 5), dtype=int)
35
+ adj[:][:] = -1
36
+ adj = csr_matrix(adj)
37
+ with self.assertRaises(ValueError):
38
+ get_distances(adj, method='BF', n_jobs=2)
39
+
40
+ def test_error_neg_cycle(self):
41
+ adj = np.ones((5, 5), dtype=int)
42
+ adj[:][:] = -1
43
+ adj = csr_matrix(adj)
44
+ with self.assertRaises(ValueError):
45
+ get_distances(adj, method='BF', n_jobs=1)
@@ -0,0 +1,9 @@
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.harmonic import Harmonic
6
+ from sknetwork.ranking.hits import HITS
7
+ from sknetwork.ranking.katz import Katz
8
+ from sknetwork.ranking.pagerank import PageRank
9
+ from sknetwork.ranking.postprocess import top_k
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created on 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.utils.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 fit_predict(self, *args, **kwargs) -> np.ndarray:
30
+ """Fit algorithm to data and return the scores. Same parameters as the ``fit`` method.
31
+
32
+ Returns
33
+ -------
34
+ scores : np.ndarray
35
+ Scores.
36
+ """
37
+ self.fit(*args, **kwargs)
38
+ return self.scores_
39
+
40
+ def fit_transform(self, *args, **kwargs) -> np.ndarray:
41
+ """Fit algorithm to data and return the scores. Alias for ``fit_predict``.
42
+ Same parameters as the ``fit`` method.
43
+
44
+ Returns
45
+ -------
46
+ scores : np.ndarray
47
+ Scores.
48
+ """
49
+ self.fit(*args, **kwargs)
50
+ return self.scores_
51
+
52
+ def _split_vars(self, shape):
53
+ n_row = shape[0]
54
+ self.scores_row_ = self.scores_[:n_row]
55
+ self.scores_col_ = self.scores_[n_row:]
56
+ self.scores_ = self.scores_row_
@@ -0,0 +1,99 @@
1
+ # distutils: language = c++
2
+ # cython: language_level=3
3
+ # cython: linetrace=True
4
+ # distutils: define_macros=CYTHON_TRACE_NOGIL=1
5
+ """
6
+ Created on September 17 2020
7
+ @author: Tiphaine Viard <tiphaine.viard@telecom-paris.fr>
8
+ """
9
+ from typing import Union
10
+ import numpy as np
11
+ from scipy import sparse
12
+
13
+ from sknetwork.ranking.base import BaseRanking
14
+ from sknetwork.utils.check import check_format, check_square, check_connected
15
+
16
+ from libcpp.vector cimport vector
17
+ from libcpp.queue cimport queue
18
+
19
+ class Betweenness(BaseRanking):
20
+ """ Betweenness centrality, based on Brandes' algorithm.
21
+
22
+ Attributes
23
+ ----------
24
+ scores_ : np.ndarray
25
+ Betweenness centrality value of each node
26
+
27
+ Example
28
+ -------
29
+ >>> from sknetwork.ranking import Betweenness
30
+ >>> from sknetwork.data.toy_graphs import bow_tie
31
+ >>> betweenness = Betweenness()
32
+ >>> adjacency = bow_tie()
33
+ >>> scores = betweenness.fit_transform(adjacency)
34
+ >>> scores
35
+ array([4., 0., 0., 0., 0.])
36
+
37
+ References
38
+ ----------
39
+ Brandes, Ulrik (2001). A faster algorithm for betweenness centrality. Journal of Mathematical Sociology.
40
+ """
41
+
42
+ def __init__(self, normalized: bool = False):
43
+ super(Betweenness, self).__init__()
44
+ self.normalized_ = normalized
45
+
46
+ def fit(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> 'Betweenness':
47
+ adjacency = check_format(adjacency)
48
+ check_square(adjacency)
49
+ check_connected(adjacency)
50
+
51
+ cdef int source
52
+ cdef vector[ vector[int] ] preds
53
+ cdef vector[int] sigma
54
+ cdef vector[int] dists
55
+ cdef int i
56
+ cdef int j
57
+ cdef vector[float] delta
58
+
59
+ cdef int n = adjacency.shape[0]
60
+ self.scores_ = np.zeros(n)
61
+ cdef vector[int] seen # Using list as stack
62
+ cdef queue[int] bfs_queue
63
+
64
+ for source in range(n):
65
+ preds = [[] for _ in range(n)]
66
+ sigma = np.zeros(n)
67
+ sigma[source] = 1
68
+ dists = -np.ones(n, dtype=int)
69
+ dists[source] = 0
70
+ bfs_queue.push(source)
71
+
72
+ while bfs_queue.size() != 0:
73
+ i = bfs_queue.front()
74
+ bfs_queue.pop()
75
+
76
+ seen.push_back(i)
77
+ neighbors = adjacency.indices[adjacency.indptr[i]:adjacency.indptr[i + 1]]
78
+ for j in neighbors:
79
+ if dists[j] < 0: # j found for the first time?
80
+ dists[j] = dists[i] + 1
81
+ bfs_queue.push(j)
82
+ if dists[j] == dists[i] + 1: # shortest path to j via i?
83
+ sigma[j] += sigma[i]
84
+ preds[j].push_back(i)
85
+
86
+ # Now backtrack to compute betweenness scores
87
+ delta = np.zeros(n)
88
+ while len(seen) != 0:
89
+ j = seen.back()
90
+ seen.pop_back()
91
+ for i in preds[j]:
92
+ delta[i] += sigma[i] / sigma[j] * (1 + delta[j])
93
+ if j != source:
94
+ self.scores_[j] += delta[j]
95
+
96
+ # Undirected graph, divide all values by two
97
+ self.scores_ = 1 / 2 * self.scores_
98
+
99
+ return self
@@ -0,0 +1,95 @@
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
+ """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
+ For a directed graph, the closeness centrality is computed in terms of outgoing paths.
23
+
24
+ Parameters
25
+ ----------
26
+ method :
27
+ Denotes if the results should be exact or approximate.
28
+ tol: float
29
+ If ``method=='approximate'``, the allowed tolerance on each score entry.
30
+ n_jobs:
31
+ If an integer value is given, denotes the number of workers to use (-1 means the maximum number will be used).
32
+ If ``None``, no parallel computations are made.
33
+
34
+ Attributes
35
+ ----------
36
+ scores_ : np.ndarray
37
+ Closeness centrality of each node.
38
+
39
+ Example
40
+ -------
41
+ >>> from sknetwork.ranking import Closeness
42
+ >>> from sknetwork.data import cyclic_digraph
43
+ >>> closeness = Closeness()
44
+ >>> adjacency = cyclic_digraph(3)
45
+ >>> scores = closeness.fit_predict(adjacency)
46
+ >>> np.round(scores, 2)
47
+ array([0.67, 0.67, 0.67])
48
+
49
+ References
50
+ ----------
51
+ Eppstein, D., & Wang, J. (2001, January).
52
+ `Fast approximation of centrality.
53
+ <http://jgaa.info/accepted/2004/EppsteinWang2004.8.1.pdf>`_
54
+ In Proceedings of the twelfth annual ACM-SIAM symposium on Discrete algorithms (pp. 228-229).
55
+ Society for Industrial and Applied Mathematics.
56
+ """
57
+
58
+ def __init__(self, method: str = 'exact', tol: float = 1e-1, n_jobs: Optional[int] = None):
59
+ super(Closeness, self).__init__()
60
+
61
+ self.method = method
62
+ self.tol = tol
63
+ self.n_jobs = n_jobs
64
+
65
+ def fit(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> 'Closeness':
66
+ """Closeness centrality for connected graphs.
67
+
68
+ Parameters
69
+ ----------
70
+ adjacency :
71
+ Adjacency matrix of the graph.
72
+
73
+ Returns
74
+ -------
75
+ self: :class:`Closeness`
76
+ """
77
+ adjacency = check_format(adjacency)
78
+ check_square(adjacency)
79
+ check_connected(adjacency)
80
+ n = adjacency.shape[0]
81
+
82
+ if self.method == 'exact':
83
+ n_sources = n
84
+ sources = np.arange(n)
85
+ elif self.method == 'approximate':
86
+ n_sources = min(int(log(n) / self.tol ** 2), n)
87
+ sources = np.random.choice(np.arange(n), n_sources, replace=False)
88
+ else:
89
+ raise ValueError("Method should be either 'exact' or 'approximate'.")
90
+
91
+ dists = get_distances(adjacency, n_jobs=self.n_jobs, sources=sources)
92
+
93
+ self.scores_ = ((n - 1) * n_sources / n) / dists.T.dot(np.ones(n_sources))
94
+
95
+ return self