scikit-network 0.30.0__cp310-cp310-win_amd64.whl → 0.32.1__cp310-cp310-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.
- {scikit_network-0.30.0.dist-info → scikit_network-0.32.1.dist-info}/AUTHORS.rst +3 -0
- {scikit_network-0.30.0.dist-info → scikit_network-0.32.1.dist-info}/METADATA +31 -3
- scikit_network-0.32.1.dist-info/RECORD +228 -0
- {scikit_network-0.30.0.dist-info → scikit_network-0.32.1.dist-info}/WHEEL +1 -1
- sknetwork/__init__.py +1 -1
- sknetwork/base.py +67 -0
- sknetwork/classification/base.py +24 -24
- sknetwork/classification/base_rank.py +17 -25
- sknetwork/classification/diffusion.py +35 -35
- sknetwork/classification/knn.py +24 -21
- sknetwork/classification/metrics.py +1 -1
- sknetwork/classification/pagerank.py +10 -10
- sknetwork/classification/propagation.py +23 -20
- sknetwork/classification/tests/test_diffusion.py +13 -3
- sknetwork/classification/vote.cp310-win_amd64.pyd +0 -0
- sknetwork/classification/vote.cpp +14482 -10351
- sknetwork/classification/vote.pyx +1 -3
- sknetwork/clustering/__init__.py +3 -1
- sknetwork/clustering/base.py +36 -40
- sknetwork/clustering/kcenters.py +253 -0
- sknetwork/clustering/leiden.py +241 -0
- sknetwork/clustering/leiden_core.cp310-win_amd64.pyd +0 -0
- sknetwork/clustering/leiden_core.cpp +31564 -0
- sknetwork/clustering/leiden_core.pyx +124 -0
- sknetwork/clustering/louvain.py +133 -102
- sknetwork/clustering/louvain_core.cp310-win_amd64.pyd +0 -0
- sknetwork/clustering/louvain_core.cpp +22457 -18792
- sknetwork/clustering/louvain_core.pyx +86 -96
- sknetwork/clustering/postprocess.py +2 -2
- sknetwork/clustering/propagation_clustering.py +15 -19
- sknetwork/clustering/tests/test_API.py +8 -4
- sknetwork/clustering/tests/test_kcenters.py +92 -0
- sknetwork/clustering/tests/test_leiden.py +34 -0
- sknetwork/clustering/tests/test_louvain.py +3 -4
- sknetwork/data/__init__.py +2 -1
- sknetwork/data/base.py +28 -0
- sknetwork/data/load.py +38 -37
- sknetwork/data/models.py +18 -18
- sknetwork/data/parse.py +54 -33
- sknetwork/data/test_graphs.py +2 -2
- sknetwork/data/tests/test_API.py +1 -1
- sknetwork/data/tests/test_base.py +14 -0
- sknetwork/data/tests/test_load.py +1 -1
- sknetwork/data/tests/test_parse.py +9 -12
- sknetwork/data/tests/test_test_graphs.py +1 -2
- sknetwork/data/toy_graphs.py +18 -18
- sknetwork/embedding/__init__.py +0 -1
- sknetwork/embedding/base.py +21 -20
- sknetwork/embedding/force_atlas.py +3 -2
- sknetwork/embedding/louvain_embedding.py +2 -2
- sknetwork/embedding/random_projection.py +5 -3
- sknetwork/embedding/spectral.py +0 -73
- sknetwork/embedding/tests/test_API.py +4 -28
- sknetwork/embedding/tests/test_louvain_embedding.py +4 -9
- sknetwork/embedding/tests/test_random_projection.py +2 -2
- sknetwork/embedding/tests/test_spectral.py +5 -8
- sknetwork/embedding/tests/test_svd.py +1 -1
- sknetwork/gnn/base.py +4 -4
- sknetwork/gnn/base_layer.py +3 -3
- sknetwork/gnn/gnn_classifier.py +45 -89
- sknetwork/gnn/layer.py +1 -1
- sknetwork/gnn/loss.py +1 -1
- sknetwork/gnn/optimizer.py +4 -3
- sknetwork/gnn/tests/test_base_layer.py +4 -4
- sknetwork/gnn/tests/test_gnn_classifier.py +12 -35
- sknetwork/gnn/utils.py +8 -8
- sknetwork/hierarchy/base.py +29 -2
- sknetwork/hierarchy/louvain_hierarchy.py +45 -41
- sknetwork/hierarchy/paris.cp310-win_amd64.pyd +0 -0
- sknetwork/hierarchy/paris.cpp +27369 -22852
- sknetwork/hierarchy/paris.pyx +7 -9
- sknetwork/hierarchy/postprocess.py +16 -16
- sknetwork/hierarchy/tests/test_API.py +1 -1
- sknetwork/hierarchy/tests/test_algos.py +5 -0
- sknetwork/hierarchy/tests/test_metrics.py +1 -1
- sknetwork/linalg/__init__.py +1 -1
- sknetwork/linalg/diteration.cp310-win_amd64.pyd +0 -0
- sknetwork/linalg/diteration.cpp +13474 -9454
- sknetwork/linalg/diteration.pyx +0 -2
- sknetwork/linalg/eig_solver.py +1 -1
- sknetwork/linalg/{normalization.py → normalizer.py} +18 -15
- sknetwork/linalg/operators.py +1 -1
- sknetwork/linalg/ppr_solver.py +1 -1
- sknetwork/linalg/push.cp310-win_amd64.pyd +0 -0
- sknetwork/linalg/push.cpp +22993 -18807
- sknetwork/linalg/push.pyx +0 -2
- sknetwork/linalg/svd_solver.py +1 -1
- sknetwork/linalg/tests/test_normalization.py +3 -7
- sknetwork/linalg/tests/test_operators.py +4 -8
- sknetwork/linalg/tests/test_ppr.py +1 -1
- sknetwork/linkpred/base.py +13 -2
- sknetwork/linkpred/nn.py +6 -6
- sknetwork/log.py +19 -0
- sknetwork/path/__init__.py +4 -3
- sknetwork/path/dag.py +54 -0
- sknetwork/path/distances.py +98 -0
- sknetwork/path/search.py +13 -47
- sknetwork/path/shortest_path.py +37 -162
- sknetwork/path/tests/test_dag.py +37 -0
- sknetwork/path/tests/test_distances.py +62 -0
- sknetwork/path/tests/test_search.py +26 -11
- sknetwork/path/tests/test_shortest_path.py +31 -36
- sknetwork/ranking/__init__.py +0 -1
- sknetwork/ranking/base.py +13 -8
- sknetwork/ranking/betweenness.cp310-win_amd64.pyd +0 -0
- sknetwork/ranking/betweenness.cpp +5709 -3017
- sknetwork/ranking/betweenness.pyx +0 -2
- sknetwork/ranking/closeness.py +7 -10
- sknetwork/ranking/pagerank.py +14 -14
- sknetwork/ranking/postprocess.py +12 -3
- sknetwork/ranking/tests/test_API.py +2 -4
- sknetwork/ranking/tests/test_betweenness.py +3 -3
- sknetwork/ranking/tests/test_closeness.py +3 -7
- sknetwork/ranking/tests/test_pagerank.py +11 -5
- sknetwork/ranking/tests/test_postprocess.py +5 -0
- sknetwork/regression/base.py +19 -2
- sknetwork/regression/diffusion.py +24 -10
- sknetwork/regression/tests/test_diffusion.py +8 -0
- sknetwork/test_base.py +35 -0
- sknetwork/test_log.py +15 -0
- sknetwork/topology/__init__.py +7 -8
- sknetwork/topology/cliques.cp310-win_amd64.pyd +0 -0
- sknetwork/topology/{kcliques.cpp → cliques.cpp} +23412 -20276
- sknetwork/topology/cliques.pyx +149 -0
- sknetwork/topology/core.cp310-win_amd64.pyd +0 -0
- sknetwork/topology/{kcore.cpp → core.cpp} +21732 -18867
- sknetwork/topology/core.pyx +90 -0
- sknetwork/topology/cycles.py +243 -0
- sknetwork/topology/minheap.cp310-win_amd64.pyd +0 -0
- sknetwork/{utils → topology}/minheap.cpp +19452 -15368
- sknetwork/{utils → topology}/minheap.pxd +1 -3
- sknetwork/{utils → topology}/minheap.pyx +1 -3
- sknetwork/topology/structure.py +3 -43
- sknetwork/topology/tests/test_cliques.py +11 -11
- sknetwork/topology/tests/test_core.py +19 -0
- sknetwork/topology/tests/test_cycles.py +65 -0
- sknetwork/topology/tests/test_structure.py +2 -16
- sknetwork/topology/tests/test_triangles.py +11 -15
- sknetwork/topology/tests/test_wl.py +72 -0
- sknetwork/topology/triangles.cp310-win_amd64.pyd +0 -0
- sknetwork/topology/triangles.cpp +5056 -2696
- sknetwork/topology/triangles.pyx +74 -89
- sknetwork/topology/weisfeiler_lehman.py +56 -86
- sknetwork/topology/weisfeiler_lehman_core.cp310-win_amd64.pyd +0 -0
- sknetwork/topology/weisfeiler_lehman_core.cpp +14727 -10622
- sknetwork/topology/weisfeiler_lehman_core.pyx +0 -2
- sknetwork/utils/__init__.py +1 -31
- sknetwork/utils/check.py +2 -2
- sknetwork/utils/format.py +5 -3
- sknetwork/utils/membership.py +2 -2
- sknetwork/utils/tests/test_check.py +3 -3
- sknetwork/utils/tests/test_format.py +3 -1
- sknetwork/utils/values.py +1 -1
- sknetwork/visualization/__init__.py +2 -2
- sknetwork/visualization/dendrograms.py +55 -7
- sknetwork/visualization/graphs.py +292 -72
- sknetwork/visualization/tests/test_dendrograms.py +9 -9
- sknetwork/visualization/tests/test_graphs.py +71 -62
- scikit_network-0.30.0.dist-info/RECORD +0 -227
- sknetwork/embedding/louvain_hierarchy.py +0 -142
- sknetwork/embedding/tests/test_louvain_hierarchy.py +0 -19
- sknetwork/path/metrics.py +0 -148
- sknetwork/path/tests/test_metrics.py +0 -29
- sknetwork/ranking/harmonic.py +0 -82
- sknetwork/topology/dag.py +0 -74
- sknetwork/topology/dag_core.cp310-win_amd64.pyd +0 -0
- sknetwork/topology/dag_core.cpp +0 -23350
- sknetwork/topology/dag_core.pyx +0 -38
- sknetwork/topology/kcliques.cp310-win_amd64.pyd +0 -0
- sknetwork/topology/kcliques.pyx +0 -193
- sknetwork/topology/kcore.cp310-win_amd64.pyd +0 -0
- sknetwork/topology/kcore.pyx +0 -120
- sknetwork/topology/tests/test_cores.py +0 -21
- sknetwork/topology/tests/test_dag.py +0 -26
- sknetwork/topology/tests/test_wl_coloring.py +0 -49
- sknetwork/topology/tests/test_wl_kernel.py +0 -31
- sknetwork/utils/base.py +0 -35
- sknetwork/utils/minheap.cp310-win_amd64.pyd +0 -0
- sknetwork/utils/simplex.py +0 -140
- sknetwork/utils/tests/test_base.py +0 -28
- sknetwork/utils/tests/test_bunch.py +0 -16
- sknetwork/utils/tests/test_projection_simplex.py +0 -33
- sknetwork/utils/tests/test_verbose.py +0 -15
- sknetwork/utils/verbose.py +0 -37
- {scikit_network-0.30.0.dist-info → scikit_network-0.32.1.dist-info}/LICENSE +0 -0
- {scikit_network-0.30.0.dist-info → scikit_network-0.32.1.dist-info}/top_level.txt +0 -0
- /sknetwork/{utils → data}/timeout.py +0 -0
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# distutils: language = c++
|
|
2
2
|
# cython: language_level=3
|
|
3
|
-
# cython: linetrace=True
|
|
4
|
-
# distutils: define_macros=CYTHON_TRACE_NOGIL=1
|
|
5
3
|
"""
|
|
6
|
-
Created
|
|
4
|
+
Created in June 2020
|
|
7
5
|
@author: Julien Simonnet <julien.simonnet@etu.upmc.fr>
|
|
8
6
|
@author: Yohann Robert <yohann.robert@etu.upmc.fr>
|
|
9
7
|
"""
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# distutils: language = c++
|
|
2
2
|
# cython: language_level=3
|
|
3
|
-
# cython: linetrace=True
|
|
4
|
-
# distutils: define_macros=CYTHON_TRACE_NOGIL=1
|
|
5
3
|
"""
|
|
6
|
-
Created
|
|
4
|
+
Created in June 2020
|
|
7
5
|
@author: Julien Simonnet <julien.simonnet@etu.upmc.fr>
|
|
8
6
|
@author: Yohann Robert <yohann.robert@etu.upmc.fr>
|
|
9
7
|
"""
|
sknetwork/topology/structure.py
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
"""
|
|
4
|
-
Created
|
|
4
|
+
Created in July 2019
|
|
5
5
|
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
6
6
|
@author: Quentin Lutz <qlutz@enst.fr>
|
|
7
7
|
@author: Thomas Bonald <tbonald@enst.fr>
|
|
8
8
|
"""
|
|
9
|
-
from typing import Tuple, Optional, Union
|
|
9
|
+
from typing import Tuple, Optional, Union, List
|
|
10
10
|
|
|
11
11
|
import numpy as np
|
|
12
12
|
from scipy import sparse
|
|
13
13
|
|
|
14
14
|
from sknetwork.utils.check import is_symmetric, check_format
|
|
15
15
|
from sknetwork.utils.format import get_adjacency
|
|
16
|
+
from sknetwork.path import get_distances
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def get_connected_components(input_matrix: sparse.csr_matrix, connection: str = 'weak', force_bipartite: bool = False) \
|
|
@@ -191,44 +192,3 @@ def is_bipartite(adjacency: sparse.csr_matrix, return_biadjacency: bool = False)
|
|
|
191
192
|
return True
|
|
192
193
|
|
|
193
194
|
|
|
194
|
-
def is_acyclic(adjacency: sparse.csr_matrix, directed: Optional[bool] = None) -> bool:
|
|
195
|
-
"""Check whether a graph has no cycle.
|
|
196
|
-
|
|
197
|
-
Parameters
|
|
198
|
-
----------
|
|
199
|
-
adjacency:
|
|
200
|
-
Adjacency matrix of the graph.
|
|
201
|
-
directed:
|
|
202
|
-
Whether to consider the graph as directed (inferred if not specified).
|
|
203
|
-
Returns
|
|
204
|
-
-------
|
|
205
|
-
is_acyclic : bool
|
|
206
|
-
A boolean with value True if the graph has no cycle and False otherwise.
|
|
207
|
-
|
|
208
|
-
Example
|
|
209
|
-
-------
|
|
210
|
-
>>> from sknetwork.topology import is_acyclic
|
|
211
|
-
>>> from sknetwork.data import star, grid
|
|
212
|
-
>>> is_acyclic(star())
|
|
213
|
-
True
|
|
214
|
-
>>> is_acyclic(grid())
|
|
215
|
-
False
|
|
216
|
-
"""
|
|
217
|
-
if directed is False:
|
|
218
|
-
# the graph must be undirected
|
|
219
|
-
if not is_symmetric(adjacency):
|
|
220
|
-
raise ValueError("The adjacency matrix is not symmetric. The parameter 'directed' must be True.")
|
|
221
|
-
elif directed is None:
|
|
222
|
-
# if not specified, infer from the graph
|
|
223
|
-
directed = not is_symmetric(adjacency)
|
|
224
|
-
has_loops = (adjacency.diagonal() > 0).any()
|
|
225
|
-
if has_loops:
|
|
226
|
-
return False
|
|
227
|
-
else:
|
|
228
|
-
n_cc = sparse.csgraph.connected_components(adjacency, directed, connection='strong', return_labels=False)
|
|
229
|
-
n_nodes = adjacency.shape[0]
|
|
230
|
-
if directed:
|
|
231
|
-
return n_cc == n_nodes
|
|
232
|
-
else:
|
|
233
|
-
n_edges = adjacency.nnz // 2
|
|
234
|
-
return n_cc == n_nodes - n_edges
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
"""Tests for
|
|
3
|
+
"""Tests for cliques"""
|
|
4
4
|
import unittest
|
|
5
5
|
|
|
6
6
|
from scipy.special import comb
|
|
7
7
|
|
|
8
8
|
from sknetwork.data.test_graphs import *
|
|
9
|
-
from sknetwork.topology import
|
|
9
|
+
from sknetwork.topology.cliques import count_cliques
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class TestClique(unittest.TestCase):
|
|
13
13
|
|
|
14
14
|
def test_empty(self):
|
|
15
15
|
adjacency = test_graph_empty()
|
|
16
|
-
self.assertEqual(
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
self.assertEqual(count_cliques(adjacency), 0)
|
|
17
|
+
with self.assertRaises(ValueError):
|
|
18
|
+
count_cliques(adjacency, 1)
|
|
19
19
|
|
|
20
20
|
def test_disconnected(self):
|
|
21
|
-
adjacency =
|
|
22
|
-
self.assertEqual(
|
|
21
|
+
adjacency = test_disconnected_graph()
|
|
22
|
+
self.assertEqual(count_cliques(adjacency), 1)
|
|
23
23
|
|
|
24
24
|
def test_cliques(self):
|
|
25
|
-
adjacency =
|
|
25
|
+
adjacency = test_clique()
|
|
26
26
|
n = adjacency.shape[0]
|
|
27
|
-
self.assertEqual(
|
|
28
|
-
self.assertEqual(
|
|
27
|
+
self.assertEqual(count_cliques(adjacency), comb(n, 3, exact=True))
|
|
28
|
+
self.assertEqual(count_cliques(adjacency, 4), comb(n, 4, exact=True))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for k-core decomposition"""
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
from sknetwork.data.test_graphs import *
|
|
7
|
+
from sknetwork.topology.core import get_core_decomposition
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestCoreDecomposition(unittest.TestCase):
|
|
11
|
+
|
|
12
|
+
def test_empty(self):
|
|
13
|
+
adjacency = test_graph_empty()
|
|
14
|
+
self.assertEqual(max(get_core_decomposition(adjacency)), 0)
|
|
15
|
+
|
|
16
|
+
def test_cliques(self):
|
|
17
|
+
adjacency = test_clique()
|
|
18
|
+
n = adjacency.shape[0]
|
|
19
|
+
self.assertEqual(max(get_core_decomposition(adjacency)), n - 1)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
""""tests for cycles.py"""
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from scipy import sparse
|
|
8
|
+
|
|
9
|
+
from sknetwork.data import star_wars, house, cyclic_digraph, cyclic_graph, linear_digraph, linear_graph
|
|
10
|
+
from sknetwork.topology import is_connected, is_acyclic, get_cycles, break_cycles
|
|
11
|
+
from sknetwork.utils.format import bipartite2undirected, directed2undirected
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestCycle(unittest.TestCase):
|
|
15
|
+
|
|
16
|
+
def test_is_acyclic(self):
|
|
17
|
+
adjacency_with_self_loops = sparse.identity(2, format='csr')
|
|
18
|
+
self.assertFalse(is_acyclic(adjacency_with_self_loops))
|
|
19
|
+
self.assertFalse(is_acyclic(adjacency_with_self_loops, directed=True))
|
|
20
|
+
directed_cycle = cyclic_digraph(3)
|
|
21
|
+
self.assertFalse(is_acyclic(directed_cycle))
|
|
22
|
+
with self.assertRaises(ValueError):
|
|
23
|
+
is_acyclic(directed_cycle, directed=False)
|
|
24
|
+
undirected_line = linear_graph(2)
|
|
25
|
+
self.assertTrue(is_acyclic(undirected_line))
|
|
26
|
+
self.assertFalse(is_acyclic(undirected_line, directed=True))
|
|
27
|
+
acyclic_graph = linear_digraph(2)
|
|
28
|
+
self.assertTrue(is_acyclic(acyclic_graph))
|
|
29
|
+
|
|
30
|
+
def test_get_cycles(self):
|
|
31
|
+
adjacency_with_self_loops = sparse.identity(2, format='csr')
|
|
32
|
+
node_cycles = get_cycles(adjacency_with_self_loops, directed=True)
|
|
33
|
+
self.assertEqual(node_cycles, [[0], [1]])
|
|
34
|
+
|
|
35
|
+
cycle_adjacency = cyclic_digraph(4)
|
|
36
|
+
node_cycles = get_cycles(cycle_adjacency, directed=True)
|
|
37
|
+
self.assertEqual(sorted(node_cycles[0]), [0, 1, 2, 3])
|
|
38
|
+
adjacency_with_subcycles = cycle_adjacency + sparse.csr_matrix(([1], ([1], [3])), shape=cycle_adjacency.shape)
|
|
39
|
+
node_cycles = get_cycles(adjacency_with_subcycles, directed=True)
|
|
40
|
+
self.assertEqual(node_cycles, [[0, 1, 3], [0, 1, 2, 3]])
|
|
41
|
+
|
|
42
|
+
undirected_cycle = cyclic_graph(4)
|
|
43
|
+
node_cycles = get_cycles(undirected_cycle, directed=False)
|
|
44
|
+
self.assertEqual(sorted(node_cycles[0]), [0, 1, 2, 3])
|
|
45
|
+
|
|
46
|
+
disconnected_cycles = sparse.csr_matrix(([1, 1, 1], ([1, 2, 3], [2, 3, 1])), shape=(4, 4))
|
|
47
|
+
node_cycles = get_cycles(disconnected_cycles, directed=True)
|
|
48
|
+
self.assertEqual(sorted(node_cycles[0]), [1, 2, 3])
|
|
49
|
+
|
|
50
|
+
def test_break_cycles(self):
|
|
51
|
+
cycle_adjacency = cyclic_digraph(4)
|
|
52
|
+
acyclic_graph = break_cycles(cycle_adjacency, root=0, directed=True)
|
|
53
|
+
self.assertTrue(is_acyclic(acyclic_graph))
|
|
54
|
+
adjacency_with_subcycles = cycle_adjacency + sparse.csr_matrix(([1], ([1], [0])), shape=cycle_adjacency.shape)
|
|
55
|
+
acyclic_graph = break_cycles(adjacency_with_subcycles, root=0, directed=True)
|
|
56
|
+
self.assertTrue(is_acyclic(acyclic_graph))
|
|
57
|
+
|
|
58
|
+
undirected_cycle = house(metadata=False)
|
|
59
|
+
acyclic_graph = break_cycles(undirected_cycle, root=0, directed=False)
|
|
60
|
+
self.assertTrue(is_acyclic(acyclic_graph))
|
|
61
|
+
|
|
62
|
+
disconnected_cycles = sparse.csr_matrix(([1, 1, 1, 1, 1], ([0, 1, 2, 3, 4], [1, 0, 3, 4, 2])), shape=(5, 5))
|
|
63
|
+
self.assertFalse(is_connected(disconnected_cycles))
|
|
64
|
+
acyclic_graph = break_cycles(disconnected_cycles, root=[0, 2], directed=True)
|
|
65
|
+
self.assertTrue(is_acyclic(acyclic_graph))
|
|
@@ -6,9 +6,9 @@ import unittest
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from scipy import sparse
|
|
8
8
|
|
|
9
|
-
from sknetwork.data import star_wars, cyclic_digraph, linear_digraph, linear_graph
|
|
9
|
+
from sknetwork.data import star_wars, house, cyclic_digraph, cyclic_graph, linear_digraph, linear_graph
|
|
10
10
|
from sknetwork.topology import get_connected_components, get_largest_connected_component
|
|
11
|
-
from sknetwork.topology import is_connected, is_bipartite
|
|
11
|
+
from sknetwork.topology import is_connected, is_bipartite
|
|
12
12
|
from sknetwork.utils.format import bipartite2undirected, directed2undirected
|
|
13
13
|
|
|
14
14
|
|
|
@@ -83,17 +83,3 @@ class TestStructure(unittest.TestCase):
|
|
|
83
83
|
adjacency = directed2undirected(cyclic_digraph(3))
|
|
84
84
|
bipartite = is_bipartite(adjacency, return_biadjacency=False)
|
|
85
85
|
self.assertEqual(bipartite, False)
|
|
86
|
-
|
|
87
|
-
def test_is_acyclic(self):
|
|
88
|
-
adjacency_with_self_loops = sparse.identity(2, format='csr')
|
|
89
|
-
self.assertFalse(is_acyclic(adjacency_with_self_loops))
|
|
90
|
-
self.assertFalse(is_acyclic(adjacency_with_self_loops, directed=True))
|
|
91
|
-
directed_cycle = cyclic_digraph(3)
|
|
92
|
-
self.assertFalse(is_acyclic(directed_cycle))
|
|
93
|
-
with self.assertRaises(ValueError):
|
|
94
|
-
is_acyclic(directed_cycle, directed=False)
|
|
95
|
-
undirected_line = linear_graph(2)
|
|
96
|
-
self.assertTrue(is_acyclic(undirected_line))
|
|
97
|
-
self.assertFalse(is_acyclic(undirected_line, directed=True))
|
|
98
|
-
acyclic_graph = linear_digraph(2)
|
|
99
|
-
self.assertTrue(is_acyclic(acyclic_graph))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
"""Tests for
|
|
3
|
+
"""Tests for triangle counting"""
|
|
4
4
|
|
|
5
5
|
import unittest
|
|
6
6
|
|
|
@@ -9,34 +9,30 @@ from scipy.special import comb
|
|
|
9
9
|
from sknetwork.data import karate_club
|
|
10
10
|
from sknetwork.data.parse import from_edge_list
|
|
11
11
|
from sknetwork.data.test_graphs import *
|
|
12
|
-
from sknetwork.topology import
|
|
12
|
+
from sknetwork.topology.triangles import count_triangles, get_clustering_coefficient
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class
|
|
15
|
+
class TestTriangle(unittest.TestCase):
|
|
16
16
|
|
|
17
17
|
def test_empty(self):
|
|
18
18
|
adjacency = test_graph_empty()
|
|
19
|
-
self.assertEqual(
|
|
19
|
+
self.assertEqual(count_triangles(adjacency), 0)
|
|
20
20
|
|
|
21
21
|
def test_disconnected(self):
|
|
22
|
-
adjacency =
|
|
23
|
-
self.assertEqual(
|
|
22
|
+
adjacency = test_disconnected_graph()
|
|
23
|
+
self.assertEqual(count_triangles(adjacency), 1)
|
|
24
24
|
|
|
25
25
|
def test_cliques(self):
|
|
26
|
-
adjacency =
|
|
26
|
+
adjacency = test_clique()
|
|
27
27
|
n = adjacency.shape[0]
|
|
28
|
-
|
|
29
|
-
self.assertEqual(nb, comb(n, 3, exact=True))
|
|
28
|
+
self.assertEqual(count_triangles(adjacency), comb(n, 3, exact=True))
|
|
30
29
|
|
|
31
30
|
def test_clustering_coefficient(self):
|
|
32
31
|
edges = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)]
|
|
33
32
|
adjacency = from_edge_list(edges, directed=False, matrix_only=True)
|
|
34
|
-
|
|
35
|
-
triangles = Triangles().fit(adjacency)
|
|
36
|
-
self.assertEqual(0.75, triangles.clustering_coef_)
|
|
33
|
+
self.assertEqual(0.75, get_clustering_coefficient(adjacency))
|
|
37
34
|
|
|
38
35
|
def test_options(self):
|
|
39
36
|
adjacency = karate_club()
|
|
40
|
-
|
|
41
|
-
self.assertEqual(
|
|
42
|
-
self.assertEqual(Triangles(parallelize=True).fit_transform(adjacency), 45)
|
|
37
|
+
self.assertEqual(count_triangles(adjacency, parallelize=False), 45)
|
|
38
|
+
self.assertEqual(count_triangles(adjacency, parallelize=True), 45)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tests for Weisfeiler-Lehman"""
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from sknetwork.data import house, bow_tie, linear_graph
|
|
9
|
+
from sknetwork.data.test_graphs import *
|
|
10
|
+
from sknetwork.topology import color_weisfeiler_lehman, are_isomorphic
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestWLKernel(unittest.TestCase):
|
|
14
|
+
|
|
15
|
+
def test_isomorphism(self):
|
|
16
|
+
ref = house()
|
|
17
|
+
n = ref.shape[0]
|
|
18
|
+
|
|
19
|
+
adjacency = house()
|
|
20
|
+
reorder = list(range(n))
|
|
21
|
+
np.random.shuffle(reorder)
|
|
22
|
+
adjacency = adjacency[reorder][:, reorder]
|
|
23
|
+
self.assertTrue(are_isomorphic(ref, adjacency))
|
|
24
|
+
|
|
25
|
+
adjacency = bow_tie()
|
|
26
|
+
self.assertFalse(are_isomorphic(ref, adjacency))
|
|
27
|
+
|
|
28
|
+
adjacency = linear_graph(n)
|
|
29
|
+
self.assertFalse(are_isomorphic(ref, adjacency))
|
|
30
|
+
|
|
31
|
+
adjacency = linear_graph(n + 1)
|
|
32
|
+
self.assertFalse(are_isomorphic(ref, adjacency))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TestWLColoring(unittest.TestCase):
|
|
36
|
+
|
|
37
|
+
def test_empty(self):
|
|
38
|
+
adjacency = test_graph_empty()
|
|
39
|
+
labels = color_weisfeiler_lehman(adjacency)
|
|
40
|
+
self.assertTrue((labels == np.zeros(10)).all())
|
|
41
|
+
|
|
42
|
+
def test_cliques(self):
|
|
43
|
+
adjacency = test_clique()
|
|
44
|
+
labels = color_weisfeiler_lehman(adjacency)
|
|
45
|
+
self.assertTrue((labels == np.zeros(10)).all())
|
|
46
|
+
|
|
47
|
+
def test_house(self):
|
|
48
|
+
adjacency = house()
|
|
49
|
+
labels = color_weisfeiler_lehman(adjacency)
|
|
50
|
+
self.assertTrue((labels == np.array([0, 2, 1, 1, 2])).all())
|
|
51
|
+
|
|
52
|
+
def test_bow_tie(self):
|
|
53
|
+
adjacency = bow_tie()
|
|
54
|
+
labels = color_weisfeiler_lehman(adjacency)
|
|
55
|
+
self.assertTrue((labels == np.array([1, 0, 0, 0, 0])).all())
|
|
56
|
+
|
|
57
|
+
def test_iso(self):
|
|
58
|
+
adjacency = house()
|
|
59
|
+
n = adjacency.indptr.shape[0] - 1
|
|
60
|
+
reorder = list(range(n))
|
|
61
|
+
np.random.shuffle(reorder)
|
|
62
|
+
adjacency2 = adjacency[reorder][:, reorder]
|
|
63
|
+
l1 = color_weisfeiler_lehman(adjacency)
|
|
64
|
+
l2 = color_weisfeiler_lehman(adjacency2)
|
|
65
|
+
l1.sort()
|
|
66
|
+
l2.sort()
|
|
67
|
+
self.assertTrue((l1 == l2).all())
|
|
68
|
+
|
|
69
|
+
def test_early_stop(self):
|
|
70
|
+
adjacency = house()
|
|
71
|
+
labels = color_weisfeiler_lehman(adjacency, max_iter=1)
|
|
72
|
+
self.assertTrue((labels == np.array([0, 1, 0, 0, 1])).all())
|
|
Binary file
|