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,124 @@
1
+ # distutils: language=c++
2
+ # cython: language_level=3
3
+ from libcpp.set cimport set
4
+ from libc.stdlib cimport rand
5
+
6
+ cimport cython
7
+
8
+ ctypedef fused int_or_long:
9
+ int
10
+ long long
11
+
12
+ @cython.boundscheck(False)
13
+ @cython.wraparound(False)
14
+ def optimize_refine_core(int_or_long[:] labels, int_or_long[:] labels_refined, int_or_long[:] indices,
15
+ int_or_long[:] indptr, float[:] data, float[:] out_weights, float[:] in_weights, float[:] out_cluster_weights,
16
+ float[:] in_cluster_weights, float[:] cluster_weights, float[:] self_loops, float resolution): # pragma: no cover
17
+ """Refine clusters while maximizing modularity.
18
+
19
+ Parameters
20
+ ----------
21
+ labels :
22
+ Labels (initial partition).
23
+ labels_refined :
24
+ Refined labels.
25
+ indices :
26
+ CSR format index array of the normalized adjacency matrix.
27
+ indptr :
28
+ CSR format index pointer array of the normalized adjacency matrix.
29
+ data :
30
+ CSR format data array of the normalized adjacency matrix.
31
+ out_weights :
32
+ Out-weights of nodes (sum to 1).
33
+ in_weights :
34
+ In-weights of nodes (sum to 1).
35
+ out_cluster_weights :
36
+ Out-weights of clusters (sum to 1).
37
+ in_cluster_weights :
38
+ In-weights of clusters (sum to 1).
39
+ cluster_weights :
40
+ Weights of clusters (initialized to 0).
41
+ self_loops :
42
+ Weights of self loops.
43
+ resolution :
44
+ Resolution parameter (positive).
45
+
46
+ Returns
47
+ -------
48
+ labels_refined :
49
+ Refined labels.
50
+ """
51
+ cdef int_or_long n
52
+ cdef int_or_long label
53
+ cdef int_or_long label_refined
54
+ cdef int_or_long label_target
55
+ cdef int_or_long label_best
56
+ cdef int_or_long i
57
+ cdef int_or_long j
58
+ cdef int_or_long start
59
+ cdef int_or_long end
60
+
61
+ cdef float increase = 1
62
+ cdef float delta
63
+ cdef float delta_local
64
+ cdef float delta_best
65
+ cdef float in_weight
66
+ cdef float out_weight
67
+
68
+ cdef set[int_or_long] label_set
69
+ cdef set[int_or_long] label_target_set
70
+
71
+ n = labels.shape[0]
72
+ while increase:
73
+ increase = 0
74
+
75
+ for i in range(n):
76
+ label_set = ()
77
+ label = labels[i]
78
+ label_refined = labels_refined[i]
79
+ start = indptr[i]
80
+ end = indptr[i+1]
81
+
82
+ # neighboring clusters
83
+ for j in range(start, end):
84
+ if labels[indices[j]] == label:
85
+ label_target = labels_refined[indices[j]]
86
+ label_set.insert(label_target)
87
+ cluster_weights[label_target] += data[j]
88
+ label_set.erase(label_refined)
89
+
90
+ if not label_set.empty():
91
+ out_weight = out_weights[i]
92
+ in_weight = in_weights[i]
93
+
94
+ # node leaving the current cluster
95
+ delta = 2 * (cluster_weights[label_refined] - self_loops[i])
96
+ delta -= resolution * out_weight * (in_cluster_weights[label_refined] - in_weight)
97
+ delta -= resolution * in_weight * (out_cluster_weights[label_refined] - out_weight)
98
+
99
+ label_target_set = ()
100
+ for label_target in label_set:
101
+ delta_local = 2 * cluster_weights[label_target]
102
+ delta_local -= resolution * out_weight * in_cluster_weights[label_target]
103
+ delta_local -= resolution * in_weight * out_cluster_weights[label_target]
104
+ delta_local -= delta
105
+ if delta_local > 0:
106
+ label_target_set.insert(label_target)
107
+ cluster_weights[label_target] = 0
108
+
109
+ if not label_target_set.empty():
110
+ increase = 1
111
+ k = rand() % label_target_set.size()
112
+ for label_target in label_target_set:
113
+ k -= 1
114
+ if k == 0:
115
+ break
116
+ labels_refined[i] = label_target
117
+ # update weights
118
+ out_cluster_weights[label_refined] -= out_weight
119
+ in_cluster_weights[label_refined] -= in_weight
120
+ out_cluster_weights[label_target] += out_weight
121
+ in_cluster_weights[label_target] += in_weight
122
+ cluster_weights[label_refined] = 0
123
+
124
+ return labels_refined
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Created in November 2018
5
+ @author: Nathan de Lara <nathan.delara@polytechnique.org>
6
+ @author: Quentin Lutz <qlutz@enst.fr>
7
+ @author: Thomas Bonald <bonald@enst.fr>
8
+ """
9
+ from typing import Union, Optional
10
+
11
+ import numpy as np
12
+ from scipy import sparse
13
+
14
+ from sknetwork.clustering.base import BaseClustering
15
+ from sknetwork.clustering.louvain_core import optimize_core
16
+ from sknetwork.clustering.postprocess import reindex_labels
17
+ from sknetwork.utils.check import check_format, check_random_state, get_probs
18
+ from sknetwork.utils.format import get_adjacency, directed2undirected
19
+ from sknetwork.utils.membership import get_membership
20
+ from sknetwork.log import Log
21
+
22
+
23
+ class Louvain(BaseClustering, Log):
24
+ """Louvain algorithm for clustering graphs by maximization of modularity.
25
+
26
+ For bipartite graphs, the algorithm maximizes Barber's modularity by default.
27
+
28
+ Parameters
29
+ ----------
30
+ resolution :
31
+ Resolution parameter.
32
+ modularity : str
33
+ Type of modularity to maximize. Can be ``'Dugue'``, ``'Newman'`` or ``'Potts'`` (default = ``'dugue'``).
34
+ tol_optimization :
35
+ Minimum increase in modularity to enter a new optimization pass in the local search.
36
+ tol_aggregation :
37
+ Minimum increase in modularity to enter a new aggregation pass.
38
+ n_aggregations :
39
+ Maximum number of aggregations.
40
+ A negative value is interpreted as no limit.
41
+ shuffle_nodes :
42
+ Enables node shuffling before optimization.
43
+ sort_clusters :
44
+ If ``True``, sort labels in decreasing order of cluster size.
45
+ return_probs :
46
+ If ``True``, return the probability distribution over clusters (soft clustering).
47
+ return_aggregate :
48
+ If ``True``, return the adjacency matrix of the graph between clusters.
49
+ random_state :
50
+ Random number generator or random seed. If None, numpy.random is used.
51
+ verbose :
52
+ Verbose mode.
53
+
54
+ Attributes
55
+ ----------
56
+ labels\_ : np.ndarray, shape (n_nodes,)
57
+ Label of each node.
58
+ probs\_ : sparse.csr_matrix, shape (n_nodes, n_labels)
59
+ Probability distribution over labels.
60
+ aggregate\_ : sparse.csr_matrix
61
+ Aggregate adjacency matrix or biadjacency matrix between clusters.
62
+
63
+ Example
64
+ -------
65
+ >>> from sknetwork.clustering import Louvain
66
+ >>> from sknetwork.data import karate_club
67
+ >>> louvain = Louvain()
68
+ >>> adjacency = karate_club()
69
+ >>> labels = louvain.fit_predict(adjacency)
70
+ >>> len(set(labels))
71
+ 4
72
+
73
+ References
74
+ ----------
75
+ * Blondel, V. D., Guillaume, J. L., Lambiotte, R., & Lefebvre, E. (2008).
76
+ `Fast unfolding of communities in large networks.
77
+ <https://arxiv.org/abs/0803.0476>`_
78
+ Journal of statistical mechanics: theory and experiment, 2008.
79
+
80
+ * Dugué, N., & Perez, A. (2015).
81
+ `Directed Louvain: maximizing modularity in directed networks
82
+ <https://hal.archives-ouvertes.fr/hal-01231784/document>`_
83
+ (Doctoral dissertation, Université d'Orléans).
84
+
85
+ * Barber, M. J. (2007).
86
+ `Modularity and community detection in bipartite networks
87
+ <https://arxiv.org/pdf/0707.1616>`_
88
+ Physical Review E, 76(6).
89
+ """
90
+
91
+ def __init__(self, resolution: float = 1, modularity: str = 'dugue', tol_optimization: float = 1e-3,
92
+ tol_aggregation: float = 1e-3, n_aggregations: int = -1, shuffle_nodes: bool = False,
93
+ sort_clusters: bool = True, return_probs: bool = True, return_aggregate: bool = True,
94
+ random_state: Optional[Union[np.random.RandomState, int]] = None, verbose: bool = False):
95
+ super(Louvain, self).__init__(sort_clusters=sort_clusters, return_probs=return_probs,
96
+ return_aggregate=return_aggregate)
97
+ Log.__init__(self, verbose)
98
+
99
+ self.labels_ = None
100
+ self.resolution = resolution
101
+ self.modularity = modularity.lower()
102
+ self.tol_optimization = tol_optimization
103
+ self.tol_aggregation = tol_aggregation
104
+ self.n_aggregations = n_aggregations
105
+ self.shuffle_nodes = shuffle_nodes
106
+ self.random_state = check_random_state(random_state)
107
+ self.bipartite = None
108
+
109
+ def _optimize(self, labels, adjacency, out_weights, in_weights):
110
+ """One optimization pass of the Louvain algorithm.
111
+
112
+ Parameters
113
+ ----------
114
+ labels :
115
+ Labels of nodes.
116
+ adjacency :
117
+ Adjacency matrix.
118
+ out_weights :
119
+ Out-weights of nodes.
120
+ in_weights :
121
+ In-weights of nodes
122
+
123
+ Returns
124
+ -------
125
+ labels :
126
+ Labels of nodes after optimization.
127
+ increase :
128
+ Gain in modularity after optimization.
129
+ """
130
+ labels = labels.astype(np.int64)
131
+ indices = adjacency.indices.astype(np.int64)
132
+ indptr = adjacency.indptr.astype(np.int64)
133
+ data = adjacency.data.astype(np.float32)
134
+ out_weights = out_weights.astype(np.float32)
135
+ in_weights = in_weights.astype(np.float32)
136
+ out_cluster_weights = out_weights.copy()
137
+ in_cluster_weights = in_weights.copy()
138
+ cluster_weights = np.zeros_like(out_cluster_weights).astype(np.float32)
139
+ self_loops = adjacency.diagonal().astype(np.float32)
140
+ return optimize_core(labels, indices, indptr, data, out_weights, in_weights, out_cluster_weights,
141
+ in_cluster_weights, cluster_weights, self_loops, self.resolution, self.tol_optimization)
142
+
143
+ @staticmethod
144
+ def _aggregate(labels, adjacency, out_weights, in_weights):
145
+ """Aggregate nodes belonging to the same cluster.
146
+
147
+ Parameters
148
+ ----------
149
+ labels :
150
+ Labels of nodes.
151
+ adjacency :
152
+ Adjacency matrix.
153
+ out_weights :
154
+ Out-weights of nodes.
155
+ in_weights :
156
+ In-weights of nodes.
157
+
158
+ Returns
159
+ -------
160
+ Aggregate graph (adjacency matrix, out-weights, in-weights).
161
+ """
162
+ membership = get_membership(labels)
163
+ adjacency_ = membership.T.tocsr().dot(adjacency.dot(membership))
164
+ out_weights_ = membership.T.dot(out_weights)
165
+ in_weights_ = membership.T.dot(in_weights)
166
+ return adjacency_, out_weights_, in_weights_
167
+
168
+ def _pre_processing(self, input_matrix, force_bipartite):
169
+ """Pre-processing for Louvain.
170
+
171
+ Parameters
172
+ ----------
173
+ input_matrix :
174
+ Adjacency matrix or biadjacency matrix of the graph.
175
+ force_bipartite :
176
+ If ``True``, force the input matrix to be considered as a biadjacency matrix even if square.
177
+
178
+ Returns
179
+ -------
180
+ adjacency :
181
+ Adjacency matrix.
182
+ out_weights, in_weights :
183
+ Node weights.
184
+ membership :
185
+ Membership matrix (labels).
186
+ index :
187
+ Index of nodes.
188
+ """
189
+ self._init_vars()
190
+
191
+ # adjacency matrix
192
+ force_directed = self.modularity == 'dugue'
193
+ adjacency, self.bipartite = get_adjacency(input_matrix, force_directed=force_directed,
194
+ force_bipartite=force_bipartite)
195
+
196
+ # shuffling
197
+ n = adjacency.shape[0]
198
+ index = np.arange(n)
199
+ if self.shuffle_nodes:
200
+ index = self.random_state.permutation(index)
201
+ adjacency = adjacency[index][:, index]
202
+
203
+ # node weights
204
+ if self.modularity == 'potts':
205
+ out_weights = get_probs('uniform', adjacency)
206
+ in_weights = out_weights.copy()
207
+ elif self.modularity == 'newman':
208
+ out_weights = get_probs('degree', adjacency)
209
+ in_weights = out_weights.copy()
210
+ elif self.modularity == 'dugue':
211
+ out_weights = get_probs('degree', adjacency)
212
+ in_weights = get_probs('degree', adjacency.T)
213
+ else:
214
+ raise ValueError('Unknown modularity function.')
215
+
216
+ # normalized, symmetric adjacency matrix (sums to 1)
217
+ adjacency = directed2undirected(adjacency)
218
+ adjacency = adjacency / adjacency.data.sum()
219
+
220
+ # cluster membership
221
+ membership = sparse.identity(n, format='csr')
222
+
223
+ return adjacency, out_weights, in_weights, membership, index
224
+
225
+ def _post_processing(self, input_matrix, membership, index):
226
+ """Post-processing for Louvain.
227
+
228
+ Parameters
229
+ ----------
230
+ input_matrix :
231
+ Adjacency matrix or biadjacency matrix of the graph.
232
+ membership :
233
+ Membership matrix (labels).
234
+ index :
235
+ Index of nodes.
236
+ """
237
+ if self.sort_clusters:
238
+ labels = reindex_labels(membership.indices)
239
+ else:
240
+ labels = membership.indices
241
+ if self.shuffle_nodes:
242
+ reverse = np.empty(index.size, index.dtype)
243
+ reverse[index] = np.arange(index.size)
244
+ labels = labels[reverse]
245
+ self.labels_ = labels
246
+ if self.bipartite:
247
+ self._split_vars(input_matrix.shape)
248
+ self._secondary_outputs(input_matrix)
249
+
250
+ def fit(self, input_matrix: Union[sparse.csr_matrix, np.ndarray], force_bipartite: bool = False) -> 'Louvain':
251
+ """Fit algorithm to data.
252
+
253
+ Parameters
254
+ ----------
255
+ input_matrix :
256
+ Adjacency matrix or biadjacency matrix of the graph.
257
+ force_bipartite :
258
+ If ``True``, force the input matrix to be considered as a biadjacency matrix even if square.
259
+
260
+ Returns
261
+ -------
262
+ self : :class:`Louvain`
263
+ """
264
+ input_matrix = check_format(input_matrix)
265
+ adjacency, out_weights, in_weights, membership, index = self._pre_processing(input_matrix, force_bipartite)
266
+ n = adjacency.shape[0]
267
+ count = 0
268
+ stop = False
269
+ while not stop:
270
+ count += 1
271
+ labels = np.arange(n)
272
+ labels, increase = self._optimize(labels, adjacency, out_weights, in_weights)
273
+ _, labels = np.unique(labels, return_inverse=True)
274
+ adjacency, out_weights, in_weights = self._aggregate(labels, adjacency, out_weights, in_weights)
275
+ membership = membership.dot(get_membership(labels))
276
+ n = adjacency.shape[0]
277
+ stop = n == 1
278
+ stop |= increase <= self.tol_aggregation
279
+ stop |= count == self.n_aggregations
280
+ self.print_log("Aggregation:", count, " Clusters:", n, " Increase:", increase)
281
+ self._post_processing(input_matrix, membership, index)
282
+ return self