scikit-network 0.31.0__cp311-cp311-win_amd64.whl → 0.32.1__cp311-cp311-win_amd64.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 (114) hide show
  1. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/AUTHORS.rst +3 -0
  2. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/METADATA +19 -3
  3. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/RECORD +112 -105
  4. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/WHEEL +1 -1
  5. sknetwork/__init__.py +1 -1
  6. sknetwork/classification/base.py +1 -1
  7. sknetwork/classification/base_rank.py +3 -3
  8. sknetwork/classification/diffusion.py +21 -13
  9. sknetwork/classification/knn.py +19 -13
  10. sknetwork/classification/metrics.py +1 -1
  11. sknetwork/classification/pagerank.py +12 -8
  12. sknetwork/classification/propagation.py +22 -15
  13. sknetwork/classification/tests/test_diffusion.py +10 -0
  14. sknetwork/classification/vote.cp311-win_amd64.pyd +0 -0
  15. sknetwork/classification/vote.cpp +14549 -8668
  16. sknetwork/clustering/__init__.py +3 -1
  17. sknetwork/clustering/base.py +1 -1
  18. sknetwork/clustering/kcenters.py +253 -0
  19. sknetwork/clustering/leiden.py +241 -0
  20. sknetwork/clustering/leiden_core.cp311-win_amd64.pyd +0 -0
  21. sknetwork/clustering/leiden_core.cpp +31564 -0
  22. sknetwork/clustering/leiden_core.pyx +124 -0
  23. sknetwork/clustering/louvain.py +118 -83
  24. sknetwork/clustering/louvain_core.cp311-win_amd64.pyd +0 -0
  25. sknetwork/clustering/louvain_core.cpp +21876 -16332
  26. sknetwork/clustering/louvain_core.pyx +86 -94
  27. sknetwork/clustering/postprocess.py +2 -2
  28. sknetwork/clustering/propagation_clustering.py +4 -4
  29. sknetwork/clustering/tests/test_API.py +7 -3
  30. sknetwork/clustering/tests/test_kcenters.py +92 -0
  31. sknetwork/clustering/tests/test_leiden.py +34 -0
  32. sknetwork/clustering/tests/test_louvain.py +2 -3
  33. sknetwork/data/load.py +2 -4
  34. sknetwork/data/parse.py +41 -20
  35. sknetwork/data/tests/test_parse.py +9 -12
  36. sknetwork/embedding/__init__.py +0 -1
  37. sknetwork/embedding/base.py +20 -19
  38. sknetwork/embedding/force_atlas.py +3 -2
  39. sknetwork/embedding/louvain_embedding.py +1 -1
  40. sknetwork/embedding/random_projection.py +5 -3
  41. sknetwork/embedding/spectral.py +0 -73
  42. sknetwork/embedding/tests/test_API.py +4 -28
  43. sknetwork/embedding/tests/test_louvain_embedding.py +4 -9
  44. sknetwork/embedding/tests/test_spectral.py +2 -5
  45. sknetwork/embedding/tests/test_svd.py +1 -1
  46. sknetwork/gnn/base_layer.py +3 -3
  47. sknetwork/gnn/gnn_classifier.py +40 -86
  48. sknetwork/gnn/layer.py +1 -1
  49. sknetwork/gnn/loss.py +1 -1
  50. sknetwork/gnn/optimizer.py +4 -3
  51. sknetwork/gnn/tests/test_base_layer.py +4 -4
  52. sknetwork/gnn/tests/test_gnn_classifier.py +12 -39
  53. sknetwork/gnn/utils.py +8 -8
  54. sknetwork/hierarchy/base.py +27 -0
  55. sknetwork/hierarchy/louvain_hierarchy.py +45 -41
  56. sknetwork/hierarchy/paris.cp311-win_amd64.pyd +0 -0
  57. sknetwork/hierarchy/paris.cpp +27521 -20771
  58. sknetwork/hierarchy/paris.pyx +7 -7
  59. sknetwork/hierarchy/postprocess.py +16 -16
  60. sknetwork/hierarchy/tests/test_algos.py +5 -0
  61. sknetwork/linalg/__init__.py +1 -1
  62. sknetwork/linalg/diteration.cp311-win_amd64.pyd +0 -0
  63. sknetwork/linalg/diteration.cpp +13916 -8050
  64. sknetwork/linalg/{normalization.py → normalizer.py} +17 -14
  65. sknetwork/linalg/operators.py +1 -1
  66. sknetwork/linalg/ppr_solver.py +1 -1
  67. sknetwork/linalg/push.cp311-win_amd64.pyd +0 -0
  68. sknetwork/linalg/push.cpp +23187 -16973
  69. sknetwork/linalg/tests/test_normalization.py +3 -7
  70. sknetwork/linalg/tests/test_operators.py +2 -6
  71. sknetwork/linalg/tests/test_ppr.py +1 -1
  72. sknetwork/linkpred/base.py +12 -1
  73. sknetwork/linkpred/nn.py +6 -6
  74. sknetwork/path/distances.py +11 -4
  75. sknetwork/path/shortest_path.py +1 -1
  76. sknetwork/path/tests/test_distances.py +7 -0
  77. sknetwork/path/tests/test_search.py +2 -2
  78. sknetwork/ranking/base.py +11 -6
  79. sknetwork/ranking/betweenness.cp311-win_amd64.pyd +0 -0
  80. sknetwork/ranking/betweenness.cpp +5256 -2190
  81. sknetwork/ranking/pagerank.py +13 -12
  82. sknetwork/ranking/tests/test_API.py +0 -2
  83. sknetwork/ranking/tests/test_betweenness.py +1 -1
  84. sknetwork/ranking/tests/test_pagerank.py +11 -5
  85. sknetwork/regression/base.py +18 -1
  86. sknetwork/regression/diffusion.py +24 -10
  87. sknetwork/regression/tests/test_diffusion.py +8 -0
  88. sknetwork/topology/__init__.py +3 -1
  89. sknetwork/topology/cliques.cp311-win_amd64.pyd +0 -0
  90. sknetwork/topology/cliques.cpp +23528 -16848
  91. sknetwork/topology/core.cp311-win_amd64.pyd +0 -0
  92. sknetwork/topology/core.cpp +22849 -16581
  93. sknetwork/topology/cycles.py +243 -0
  94. sknetwork/topology/minheap.cp311-win_amd64.pyd +0 -0
  95. sknetwork/topology/minheap.cpp +19495 -13469
  96. sknetwork/topology/structure.py +2 -42
  97. sknetwork/topology/tests/test_cycles.py +65 -0
  98. sknetwork/topology/tests/test_structure.py +2 -16
  99. sknetwork/topology/triangles.cp311-win_amd64.pyd +0 -0
  100. sknetwork/topology/triangles.cpp +5283 -1397
  101. sknetwork/topology/triangles.pyx +7 -4
  102. sknetwork/topology/weisfeiler_lehman_core.cp311-win_amd64.pyd +0 -0
  103. sknetwork/topology/weisfeiler_lehman_core.cpp +14781 -8915
  104. sknetwork/utils/format.py +1 -1
  105. sknetwork/utils/membership.py +2 -2
  106. sknetwork/visualization/__init__.py +2 -2
  107. sknetwork/visualization/dendrograms.py +55 -7
  108. sknetwork/visualization/graphs.py +261 -44
  109. sknetwork/visualization/tests/test_dendrograms.py +9 -9
  110. sknetwork/visualization/tests/test_graphs.py +63 -57
  111. sknetwork/embedding/louvain_hierarchy.py +0 -142
  112. sknetwork/embedding/tests/test_louvain_hierarchy.py +0 -19
  113. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/LICENSE +0 -0
  114. {scikit_network-0.31.0.dist-info → scikit_network-0.32.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- """tests for visualization/graphs.py"""
3
+ """tests for visualization of graphs"""
4
4
 
5
5
  import tempfile
6
6
  import unittest
@@ -10,7 +10,7 @@ from scipy import sparse
10
10
 
11
11
  from sknetwork.data.test_graphs import test_disconnected_graph, test_bigraph_disconnect
12
12
  from sknetwork.data.toy_graphs import karate_club, painters, movie_actor, bow_tie, star_wars
13
- from sknetwork.visualization.graphs import svg_graph, svg_bigraph, svg_text, rescale
13
+ from sknetwork.visualization.graphs import visualize_graph, visualize_bigraph, svg_graph, svg_bigraph, svg_text, rescale
14
14
 
15
15
 
16
16
  # noinspection DuplicatedCode
@@ -21,74 +21,77 @@ class TestVisualization(unittest.TestCase):
21
21
  adjacency = graph.adjacency
22
22
  position = graph.position
23
23
  labels = graph.labels
24
+ image = visualize_graph(adjacency, position, labels=labels)
25
+ self.assertEqual(image[1:4], 'svg')
26
+ # alias
24
27
  image = svg_graph(adjacency, position, labels=labels)
25
28
  self.assertEqual(image[1:4], 'svg')
26
- image = svg_graph(adjacency, position, labels=list(labels))
29
+ image = visualize_graph(adjacency, position, labels=list(labels))
27
30
  self.assertEqual(image[1:4], 'svg')
28
- image = svg_graph(adjacency, position, display_edges=False)
31
+ image = visualize_graph(adjacency, position, display_edges=False)
29
32
  self.assertEqual(image[1:4], 'svg')
30
- image = svg_graph(adjacency, position, height=None)
33
+ image = visualize_graph(adjacency, position, height=None)
31
34
  self.assertEqual(image[1:4], 'svg')
32
- image = svg_graph(adjacency, position, height=300, width=None)
35
+ image = visualize_graph(adjacency, position, height=300, width=None)
33
36
  self.assertEqual(image[1:4], 'svg')
34
- image = svg_graph(adjacency, position, height=None, width=200)
37
+ image = visualize_graph(adjacency, position, height=None, width=200)
35
38
  self.assertEqual(image[1:4], 'svg')
36
39
  n = adjacency.shape[0]
37
40
  edge_labels = [(0, 1, 0), (1, 1, 1), (3, 10, 2)]
38
- image = svg_graph(adjacency, position=None, names=np.arange(n), labels=np.arange(n), scores=np.arange(n),
39
- seeds=[0, 1], width=200, height=200, margin=10, margin_text=5, scale=3,
40
- node_order=np.flip(np.arange(n)),
41
- node_size=5, node_size_min=2, node_size_max=6, display_node_weight=True,
42
- node_weights=np.arange(n),
43
- node_width=2, node_width_max=5, node_color='red', edge_width=2, edge_width_min=2,
44
- edge_width_max=4, edge_color='blue', edge_labels=edge_labels, display_edge_weight=True,
45
- font_size=14)
41
+ image = visualize_graph(adjacency, position=None, names=np.arange(n), labels=np.arange(n), scores=np.arange(n),
42
+ seeds=[0, 1], width=200, height=200, margin=10, margin_text=5, scale=3,
43
+ node_order=np.flip(np.arange(n)),
44
+ node_size=5, node_size_min=2, node_size_max=6, display_node_weight=True,
45
+ node_weights=np.arange(n),
46
+ node_width=2, node_width_max=5, node_color='red', edge_width=2, edge_width_min=2,
47
+ edge_width_max=4, edge_color='blue', edge_labels=edge_labels, display_edge_weight=True,
48
+ font_size=14)
46
49
  self.assertEqual(image[1:4], 'svg')
47
- image = svg_graph(adjacency, position=None, labels={0: 0})
50
+ image = visualize_graph(adjacency, position=None, labels={0: 0})
48
51
  self.assertEqual(image[1:4], 'svg')
49
- image = svg_graph(adjacency, position=None, scores={0: 0})
52
+ image = visualize_graph(adjacency, position=None, scores={0: 0})
50
53
  self.assertEqual(image[1:4], 'svg')
51
- image = svg_graph(adjacency=None, position=position)
54
+ image = visualize_graph(adjacency=None, position=position)
52
55
  self.assertEqual(image[1:4], 'svg')
53
- image = svg_graph(adjacency=None, position=position, edge_labels=edge_labels)
56
+ image = visualize_graph(adjacency=None, position=position, edge_labels=edge_labels)
54
57
  self.assertEqual(image[1:4], 'svg')
55
- image = svg_graph(adjacency, position, labels, label_colors={0: "red", 1: "blue"})
58
+ image = visualize_graph(adjacency, position, labels, label_colors={0: "red", 1: "blue"})
56
59
  self.assertEqual(image[1:4], 'svg')
57
- image = svg_graph(adjacency, position, labels, label_colors=["red", "blue"])
60
+ image = visualize_graph(adjacency, position, labels, label_colors=["red", "blue"])
58
61
  self.assertEqual(image[1:4], 'svg')
59
- image = svg_graph(adjacency, position, labels, node_weights=np.arange(adjacency.shape[0]))
62
+ image = visualize_graph(adjacency, position, labels, node_weights=np.arange(adjacency.shape[0]))
60
63
  self.assertEqual(image[1:4], 'svg')
61
- image = svg_graph(adjacency, position, scores=list(np.arange(n)))
64
+ image = visualize_graph(adjacency, position, scores=list(np.arange(n)))
62
65
  self.assertEqual(image[1:4], 'svg')
63
- image = svg_graph(adjacency, position, seeds={0: 1, 2: 1})
66
+ image = visualize_graph(adjacency, position, seeds={0: 1, 2: 1})
64
67
  self.assertEqual(image[1:4], 'svg')
65
- image = svg_graph(adjacency, position, labels=np.arange(n), name_position='left')
68
+ image = visualize_graph(adjacency, position, labels=np.arange(n), name_position='left')
66
69
  self.assertEqual(image[1:4], 'svg')
67
- image = svg_graph(adjacency, position, scale=2, labels=np.arange(n), name_position='left')
70
+ image = visualize_graph(adjacency, position, scale=2, labels=np.arange(n), name_position='left')
68
71
  self.assertEqual(image[1:4], 'svg')
69
72
  with self.assertRaises(ValueError):
70
- svg_graph(adjacency, position, labels=[0, 1])
73
+ visualize_graph(adjacency, position, labels=[0, 1])
71
74
  with self.assertRaises(ValueError):
72
- svg_graph(adjacency, position, scores=[0, 1])
73
- svg_graph(adjacency, position, scale=2, labels=np.arange(n), name_position='left')
75
+ visualize_graph(adjacency, position, scores=[0, 1])
76
+ visualize_graph(adjacency, position, scale=2, labels=np.arange(n), name_position='left')
74
77
 
75
78
  def test_directed(self):
76
79
  graph = painters(True)
77
80
  adjacency = graph.adjacency
78
81
  position = graph.position
79
82
  names = graph.names
80
- image = svg_graph(adjacency, position, names=names)
83
+ image = visualize_graph(adjacency, position, names=names)
81
84
  self.assertEqual(image[1:4], 'svg')
82
- image = svg_graph(adjacency, position, display_edges=False)
85
+ image = visualize_graph(adjacency, position, display_edges=False)
83
86
  self.assertEqual(image[1:4], 'svg')
84
87
  n = adjacency.shape[0]
85
- image = svg_graph(adjacency, position=None, names=np.arange(n), labels=np.arange(n), scores=np.arange(n),
86
- name_position='below', seeds=[0, 1], width=200, height=200, margin=10, margin_text=5,
87
- scale=3, node_order=np.flip(np.arange(n)),
88
- node_size=5, node_size_min=2, node_size_max=6, display_node_weight=True,
89
- node_weights=np.arange(n),
90
- node_width=2, node_width_max=5, node_color='red', edge_width=2, edge_width_min=2,
91
- edge_width_max=4, edge_color='blue', display_edge_weight=True, font_size=14)
88
+ image = visualize_graph(adjacency, position=None, names=np.arange(n), labels=np.arange(n), scores=np.arange(n),
89
+ name_position='below', seeds=[0, 1], width=200, height=200, margin=10, margin_text=5,
90
+ scale=3, node_order=np.flip(np.arange(n)),
91
+ node_size=5, node_size_min=2, node_size_max=6, display_node_weight=True,
92
+ node_weights=np.arange(n),
93
+ node_width=2, node_width_max=5, node_color='red', edge_width=2, edge_width_min=2,
94
+ edge_width_max=4, edge_color='blue', display_edge_weight=True, font_size=14)
92
95
  self.assertEqual(image[1:4], 'svg')
93
96
 
94
97
  def test_bipartite(self):
@@ -96,54 +99,57 @@ class TestVisualization(unittest.TestCase):
96
99
  biadjacency = graph.biadjacency
97
100
  names_row = graph.names_row
98
101
  names_col = graph.names_col
102
+ image = visualize_bigraph(biadjacency, names_row, names_col)
103
+ self.assertEqual(image[1:4], 'svg')
104
+ # alias
99
105
  image = svg_bigraph(biadjacency, names_row, names_col)
100
106
  self.assertEqual(image[1:4], 'svg')
101
- image = svg_bigraph(biadjacency, display_edges=False)
107
+ image = visualize_bigraph(biadjacency, display_edges=False)
102
108
  self.assertEqual(image[1:4], 'svg')
103
- image = svg_bigraph(biadjacency, reorder=False)
109
+ image = visualize_bigraph(biadjacency, reorder=False)
104
110
  self.assertEqual(image[1:4], 'svg')
105
111
  n_row, n_col = biadjacency.shape
106
112
  position_row = np.random.random((n_row, 2))
107
113
  position_col = np.random.random((n_col, 2))
108
114
  edge_labels = [(0, 1, 0), (1, 1, 1), (3, 10, 2)]
109
- image = svg_bigraph(biadjacency=biadjacency, names_row=np.arange(n_row), names_col=np.arange(n_col),
110
- labels_row=np.arange(n_row), labels_col=np.arange(n_col), scores_row=np.arange(n_row),
111
- scores_col=np.arange(n_col), seeds_row=[0, 1], seeds_col=[1, 2],
112
- position_row=position_row, position_col=position_col, color_row='red', color_col='white',
113
- width=200, height=200, margin=10, margin_text=5, scale=3, node_size=5,
114
- node_size_min=1, node_size_max=30, node_weights_row=np.arange(n_row),
115
- node_weights_col=np.arange(n_col), display_node_weight=True, node_width=2, node_width_max=5,
116
- edge_labels=edge_labels, edge_width=2, edge_width_min=0.3, edge_width_max=4,
117
- edge_color='red', display_edge_weight=True, font_size=14)
115
+ image = visualize_bigraph(biadjacency=biadjacency, names_row=np.arange(n_row), names_col=np.arange(n_col),
116
+ labels_row=np.arange(n_row), labels_col=np.arange(n_col), scores_row=np.arange(n_row),
117
+ scores_col=np.arange(n_col), seeds_row=[0, 1], seeds_col=[1, 2],
118
+ position_row=position_row, position_col=position_col, color_row='red', color_col='white',
119
+ width=200, height=200, margin=10, margin_text=5, scale=3, node_size=5,
120
+ node_size_min=1, node_size_max=30, node_weights_row=np.arange(n_row),
121
+ node_weights_col=np.arange(n_col), display_node_weight=True, node_width=2, node_width_max=5,
122
+ edge_labels=edge_labels, edge_width=2, edge_width_min=0.3, edge_width_max=4,
123
+ edge_color='red', display_edge_weight=True, font_size=14)
118
124
  self.assertEqual(image[1:4], 'svg')
119
125
 
120
126
  def test_disconnect(self):
121
127
  adjacency = test_disconnected_graph()
122
128
  position = np.random.random((adjacency.shape[0], 2))
123
- image = svg_graph(adjacency, position)
129
+ image = visualize_graph(adjacency, position)
124
130
  self.assertEqual(image[1:4], 'svg')
125
131
  biadjacency = test_bigraph_disconnect()
126
- image = svg_bigraph(biadjacency)
132
+ image = visualize_bigraph(biadjacency)
127
133
  self.assertEqual(image[1:4], 'svg')
128
134
 
129
135
  def test_probs(self):
130
136
  adjacency = bow_tie()
131
137
  probs = np.array([[.5, .5], [0, 0], [1, 0], [0, 1], [0, 1]])
132
- image = svg_graph(adjacency, probs=probs)
138
+ image = visualize_graph(adjacency, probs=probs)
133
139
  self.assertEqual(image[1:4], 'svg')
134
140
  probs = sparse.csr_matrix(probs)
135
- image = svg_graph(adjacency, probs=probs)
141
+ image = visualize_graph(adjacency, probs=probs)
136
142
  self.assertEqual(image[1:4], 'svg')
137
143
  biadjacency = star_wars()
138
144
  probs_row = sparse.csr_matrix([[.5, .5], [0, 0], [1, 0], [0, 1]])
139
145
  probs_col = sparse.csr_matrix([[.5, .5], [0, 0], [1, 0]])
140
- image = svg_bigraph(biadjacency, probs_row=probs_row, probs_col=probs_col)
146
+ image = visualize_bigraph(biadjacency, probs_row=probs_row, probs_col=probs_col)
141
147
  self.assertEqual(image[1:4], 'svg')
142
148
 
143
149
  def test_labels(self):
144
150
  adjacency = bow_tie()
145
151
  names = ["aa", "bb", "<>", "a&b", ""]
146
- image = svg_graph(adjacency, names=names)
152
+ image = visualize_graph(adjacency, names=names)
147
153
  self.assertEqual(image[1:4], 'svg')
148
154
 
149
155
  def test_text(self):
@@ -160,11 +166,11 @@ class TestVisualization(unittest.TestCase):
160
166
  graph = karate_club(True)
161
167
  adjacency = graph.adjacency
162
168
  position = graph.position
163
- _ = svg_graph(adjacency, position, filename=filename)
169
+ _ = visualize_graph(adjacency, position, filename=filename)
164
170
  with open(filename + '.svg', 'r') as f:
165
171
  row = f.readline()
166
172
  self.assertEqual(row[1:4], 'svg')
167
- _ = svg_bigraph(adjacency, position, filename=filename)
173
+ _ = visualize_bigraph(adjacency, position, filename=filename)
168
174
  with open(filename + '.svg', 'r') as f:
169
175
  row = f.readline()
170
176
  self.assertEqual(row[1:4], 'svg')
@@ -1,142 +0,0 @@
1
- #!/usr/bin/env python3
2
- # coding: utf-8
3
- """
4
- Created on Dec 2020
5
- @author: Quentin Lutz <qlutz@enst.fr>
6
- """
7
- from typing import Optional, Union
8
-
9
- import numpy as np
10
- from scipy import sparse
11
-
12
- from sknetwork.utils.check import check_format, check_random_state
13
- from sknetwork.utils.format import get_adjacency
14
- from sknetwork.clustering.louvain import Louvain
15
- from sknetwork.embedding.base import BaseEmbedding
16
-
17
-
18
- class LouvainNE(BaseEmbedding):
19
- """Embedding of graphs based on the hierarchical Louvain algorithm with random scattering per level.
20
-
21
- Parameters
22
- ----------
23
- n_components : int
24
- Dimension of the embedding.
25
- scale : float
26
- Dilution factor to be applied on the random vector to be added at each iteration of the clustering method.
27
- resolution :
28
- Resolution parameter.
29
- tol_optimization :
30
- Minimum increase in the objective function to enter a new optimization pass.
31
- tol_aggregation :
32
- Minimum increase in the objective function to enter a new aggregation pass.
33
- n_aggregations :
34
- Maximum number of aggregations.
35
- A negative value is interpreted as no limit.
36
- shuffle_nodes :
37
- Enables node shuffling before optimization.
38
- random_state :
39
- Random number generator or random seed. If None, numpy.random is used.
40
-
41
- Attributes
42
- ----------
43
- embedding_ : array, shape = (n, n_components)
44
- Embedding of the nodes.
45
- embedding_row_ : array, shape = (n_row, n_components)
46
- Embedding of the rows, for bipartite graphs.
47
- embedding_col_ : array, shape = (n_col, n_components)
48
- Embedding of the columns, for bipartite graphs.
49
- Example
50
- -------
51
- >>> from sknetwork.embedding import LouvainNE
52
- >>> from sknetwork.data import karate_club
53
- >>> louvain = LouvainNE(n_components=3)
54
- >>> adjacency = karate_club()
55
- >>> embedding = louvain.fit_transform(adjacency)
56
- >>> embedding.shape
57
- (34, 3)
58
-
59
- References
60
- ----------
61
- Bhowmick, A. K., Meneni, K., Danisch, M., Guillaume, J. L., & Mitra, B. (2020, January).
62
- `LouvainNE: Hierarchical Louvain Method for High Quality and Scalable Network Embedding.
63
- <https://hal.archives-ouvertes.fr/hal-02999888/document>`_
64
- In Proceedings of the 13th International Conference on Web Search and Data Mining (pp. 43-51).
65
- """
66
- def __init__(self, n_components: int = 2, scale: float = .1, resolution: float = 1, tol_optimization: float = 1e-3,
67
- tol_aggregation: float = 1e-3, n_aggregations: int = -1, shuffle_nodes: bool = False,
68
- random_state: Optional[Union[np.random.RandomState, int]] = None, verbose: bool = False):
69
- super(LouvainNE, self).__init__()
70
-
71
- self.n_components = n_components
72
- self.scale = scale
73
- self._clustering_method = Louvain(resolution=resolution, tol_optimization=tol_optimization,
74
- tol_aggregation=tol_aggregation, n_aggregations=n_aggregations,
75
- shuffle_nodes=shuffle_nodes, random_state=random_state, verbose=verbose)
76
- self.random_state = check_random_state(random_state)
77
- self.bipartite = None
78
-
79
- def _recursive_louvain(self, adjacency: Union[sparse.csr_matrix, np.ndarray], depth: int,
80
- nodes: Optional[np.ndarray] = None):
81
- """Recursive function for fit, modifies the embedding in place.
82
-
83
- Parameters
84
- ----------
85
- adjacency :
86
- Adjacency matrix of the graph.
87
- depth :
88
- Depth of the recursion.
89
- nodes :
90
- The indices of the current nodes in the original graph.
91
- """
92
- n = adjacency.shape[0]
93
- if nodes is None:
94
- nodes = np.arange(n)
95
-
96
- if adjacency.nnz:
97
- labels = self._clustering_method.fit_transform(adjacency)
98
- else:
99
- labels = np.zeros(n)
100
-
101
- clusters = np.unique(labels)
102
-
103
- if len(clusters) != 1:
104
- random_vectors = (self.scale ** depth) * self.random_state.rand(self.n_components, len(clusters))
105
- for index, cluster in enumerate(clusters):
106
- mask = (labels == cluster)
107
- nodes_cluster = nodes[mask]
108
- self.embedding_[nodes_cluster, :] += random_vectors[:, index]
109
- n_row = len(mask)
110
- indptr = np.zeros(n_row + 1, dtype=int)
111
- indptr[1:] = np.cumsum(mask)
112
- n_col = indptr[-1]
113
- combiner = sparse.csr_matrix((np.ones(n_col), np.arange(n_col, dtype=int), indptr),
114
- shape=(n_row, n_col))
115
- adjacency_cluster = adjacency[mask, :].dot(combiner)
116
- self._recursive_louvain(adjacency_cluster, depth + 1, nodes_cluster)
117
-
118
- def fit(self, input_matrix: Union[sparse.csr_matrix, np.ndarray], force_bipartite: bool = False):
119
- """Embedding of graphs from a clustering obtained with Louvain.
120
-
121
- Parameters
122
- ----------
123
- input_matrix :
124
- Adjacency matrix or biadjacency matrix of the graph.
125
- force_bipartite :
126
- If ``True``, force the input matrix to be considered as a biadjacency matrix even if square.
127
- Returns
128
- -------
129
- self: :class:`LouvainNE`
130
- """
131
- # input
132
- input_matrix = check_format(input_matrix)
133
- adjacency, self.bipartite = get_adjacency(input_matrix, force_bipartite=force_bipartite)
134
- n = adjacency.shape[0]
135
-
136
- # embedding
137
- self.embedding_ = np.zeros((n, self.n_components))
138
- self._recursive_louvain(adjacency, 0)
139
-
140
- if self.bipartite:
141
- self._split_vars(input_matrix.shape)
142
- return self
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """tests for LouvainNE"""
4
- import unittest
5
-
6
- from sknetwork.data.test_graphs import test_graph, test_disconnected_graph, test_digraph, test_bigraph
7
- from sknetwork.embedding import LouvainNE
8
-
9
-
10
- class TestLouvainNE(unittest.TestCase):
11
-
12
- def test_louvain_hierarchy(self):
13
- louvain = LouvainNE()
14
- for adjacency in [test_graph(), test_disconnected_graph(), test_digraph()]:
15
- self.assertTupleEqual(louvain.fit_transform(adjacency).shape, (10, 2))
16
- louvain.fit(test_bigraph())
17
- self.assertTupleEqual(louvain.embedding_.shape, (6, 2))
18
- louvain.fit(test_graph(), force_bipartite=True)
19
- self.assertTupleEqual(louvain.embedding_.shape, (10, 2))