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.
- scikit_network-0.28.3.dist-info/AUTHORS.rst +41 -0
- scikit_network-0.28.3.dist-info/LICENSE +34 -0
- scikit_network-0.28.3.dist-info/METADATA +457 -0
- scikit_network-0.28.3.dist-info/RECORD +240 -0
- scikit_network-0.28.3.dist-info/WHEEL +5 -0
- scikit_network-0.28.3.dist-info/top_level.txt +1 -0
- sknetwork/__init__.py +21 -0
- sknetwork/classification/__init__.py +8 -0
- sknetwork/classification/base.py +84 -0
- sknetwork/classification/base_rank.py +143 -0
- sknetwork/classification/diffusion.py +134 -0
- sknetwork/classification/knn.py +162 -0
- sknetwork/classification/metrics.py +205 -0
- sknetwork/classification/pagerank.py +66 -0
- sknetwork/classification/propagation.py +152 -0
- sknetwork/classification/tests/__init__.py +1 -0
- sknetwork/classification/tests/test_API.py +35 -0
- sknetwork/classification/tests/test_diffusion.py +37 -0
- sknetwork/classification/tests/test_knn.py +24 -0
- sknetwork/classification/tests/test_metrics.py +53 -0
- sknetwork/classification/tests/test_pagerank.py +20 -0
- sknetwork/classification/tests/test_propagation.py +24 -0
- sknetwork/classification/vote.cpython-39-darwin.so +0 -0
- sknetwork/classification/vote.pyx +58 -0
- sknetwork/clustering/__init__.py +7 -0
- sknetwork/clustering/base.py +102 -0
- sknetwork/clustering/kmeans.py +142 -0
- sknetwork/clustering/louvain.py +255 -0
- sknetwork/clustering/louvain_core.cpython-39-darwin.so +0 -0
- sknetwork/clustering/louvain_core.pyx +134 -0
- sknetwork/clustering/metrics.py +91 -0
- sknetwork/clustering/postprocess.py +66 -0
- sknetwork/clustering/propagation_clustering.py +108 -0
- sknetwork/clustering/tests/__init__.py +1 -0
- sknetwork/clustering/tests/test_API.py +37 -0
- sknetwork/clustering/tests/test_kmeans.py +47 -0
- sknetwork/clustering/tests/test_louvain.py +104 -0
- sknetwork/clustering/tests/test_metrics.py +50 -0
- sknetwork/clustering/tests/test_post_processing.py +23 -0
- sknetwork/clustering/tests/test_postprocess.py +39 -0
- sknetwork/data/__init__.py +5 -0
- sknetwork/data/load.py +408 -0
- sknetwork/data/models.py +459 -0
- sknetwork/data/parse.py +621 -0
- sknetwork/data/test_graphs.py +84 -0
- sknetwork/data/tests/__init__.py +1 -0
- sknetwork/data/tests/test_API.py +30 -0
- sknetwork/data/tests/test_load.py +95 -0
- sknetwork/data/tests/test_models.py +52 -0
- sknetwork/data/tests/test_parse.py +253 -0
- sknetwork/data/tests/test_test_graphs.py +30 -0
- sknetwork/data/tests/test_toy_graphs.py +68 -0
- sknetwork/data/toy_graphs.py +619 -0
- sknetwork/embedding/__init__.py +10 -0
- sknetwork/embedding/base.py +90 -0
- sknetwork/embedding/force_atlas.py +197 -0
- sknetwork/embedding/louvain_embedding.py +174 -0
- sknetwork/embedding/louvain_hierarchy.py +142 -0
- sknetwork/embedding/metrics.py +66 -0
- sknetwork/embedding/random_projection.py +133 -0
- sknetwork/embedding/spectral.py +214 -0
- sknetwork/embedding/spring.py +198 -0
- sknetwork/embedding/svd.py +363 -0
- sknetwork/embedding/tests/__init__.py +1 -0
- sknetwork/embedding/tests/test_API.py +73 -0
- sknetwork/embedding/tests/test_force_atlas.py +35 -0
- sknetwork/embedding/tests/test_louvain_embedding.py +33 -0
- sknetwork/embedding/tests/test_louvain_hierarchy.py +19 -0
- sknetwork/embedding/tests/test_metrics.py +29 -0
- sknetwork/embedding/tests/test_random_projection.py +28 -0
- sknetwork/embedding/tests/test_spectral.py +84 -0
- sknetwork/embedding/tests/test_spring.py +50 -0
- sknetwork/embedding/tests/test_svd.py +37 -0
- sknetwork/flow/__init__.py +3 -0
- sknetwork/flow/flow.py +73 -0
- sknetwork/flow/tests/__init__.py +1 -0
- sknetwork/flow/tests/test_flow.py +17 -0
- sknetwork/flow/tests/test_utils.py +69 -0
- sknetwork/flow/utils.py +91 -0
- sknetwork/gnn/__init__.py +10 -0
- sknetwork/gnn/activation.py +117 -0
- sknetwork/gnn/base.py +155 -0
- sknetwork/gnn/base_activation.py +89 -0
- sknetwork/gnn/base_layer.py +109 -0
- sknetwork/gnn/gnn_classifier.py +381 -0
- sknetwork/gnn/layer.py +153 -0
- sknetwork/gnn/layers.py +127 -0
- sknetwork/gnn/loss.py +180 -0
- sknetwork/gnn/neighbor_sampler.py +65 -0
- sknetwork/gnn/optimizer.py +163 -0
- sknetwork/gnn/tests/__init__.py +1 -0
- sknetwork/gnn/tests/test_activation.py +56 -0
- sknetwork/gnn/tests/test_base.py +79 -0
- sknetwork/gnn/tests/test_base_layer.py +37 -0
- sknetwork/gnn/tests/test_gnn_classifier.py +192 -0
- sknetwork/gnn/tests/test_layers.py +80 -0
- sknetwork/gnn/tests/test_loss.py +33 -0
- sknetwork/gnn/tests/test_neigh_sampler.py +23 -0
- sknetwork/gnn/tests/test_optimizer.py +43 -0
- sknetwork/gnn/tests/test_utils.py +93 -0
- sknetwork/gnn/utils.py +219 -0
- sknetwork/hierarchy/__init__.py +7 -0
- sknetwork/hierarchy/base.py +69 -0
- sknetwork/hierarchy/louvain_hierarchy.py +264 -0
- sknetwork/hierarchy/metrics.py +234 -0
- sknetwork/hierarchy/paris.cpython-39-darwin.so +0 -0
- sknetwork/hierarchy/paris.pyx +317 -0
- sknetwork/hierarchy/postprocess.py +350 -0
- sknetwork/hierarchy/tests/__init__.py +1 -0
- sknetwork/hierarchy/tests/test_API.py +25 -0
- sknetwork/hierarchy/tests/test_algos.py +29 -0
- sknetwork/hierarchy/tests/test_metrics.py +62 -0
- sknetwork/hierarchy/tests/test_postprocess.py +57 -0
- sknetwork/hierarchy/tests/test_ward.py +25 -0
- sknetwork/hierarchy/ward.py +94 -0
- sknetwork/linalg/__init__.py +9 -0
- sknetwork/linalg/basics.py +37 -0
- sknetwork/linalg/diteration.cpython-39-darwin.so +0 -0
- sknetwork/linalg/diteration.pyx +49 -0
- sknetwork/linalg/eig_solver.py +93 -0
- sknetwork/linalg/laplacian.py +15 -0
- sknetwork/linalg/normalization.py +66 -0
- sknetwork/linalg/operators.py +225 -0
- sknetwork/linalg/polynome.py +76 -0
- sknetwork/linalg/ppr_solver.py +170 -0
- sknetwork/linalg/push.cpython-39-darwin.so +0 -0
- sknetwork/linalg/push.pyx +73 -0
- sknetwork/linalg/sparse_lowrank.py +142 -0
- sknetwork/linalg/svd_solver.py +91 -0
- sknetwork/linalg/tests/__init__.py +1 -0
- sknetwork/linalg/tests/test_eig.py +44 -0
- sknetwork/linalg/tests/test_laplacian.py +18 -0
- sknetwork/linalg/tests/test_normalization.py +38 -0
- sknetwork/linalg/tests/test_operators.py +70 -0
- sknetwork/linalg/tests/test_polynome.py +38 -0
- sknetwork/linalg/tests/test_ppr.py +50 -0
- sknetwork/linalg/tests/test_sparse_lowrank.py +61 -0
- sknetwork/linalg/tests/test_svd.py +38 -0
- sknetwork/linkpred/__init__.py +4 -0
- sknetwork/linkpred/base.py +80 -0
- sknetwork/linkpred/first_order.py +508 -0
- sknetwork/linkpred/first_order_core.cpython-39-darwin.so +0 -0
- sknetwork/linkpred/first_order_core.pyx +315 -0
- sknetwork/linkpred/postprocessing.py +98 -0
- sknetwork/linkpred/tests/__init__.py +1 -0
- sknetwork/linkpred/tests/test_API.py +49 -0
- sknetwork/linkpred/tests/test_postprocessing.py +21 -0
- sknetwork/path/__init__.py +4 -0
- sknetwork/path/metrics.py +148 -0
- sknetwork/path/search.py +65 -0
- sknetwork/path/shortest_path.py +186 -0
- sknetwork/path/tests/__init__.py +1 -0
- sknetwork/path/tests/test_metrics.py +29 -0
- sknetwork/path/tests/test_search.py +25 -0
- sknetwork/path/tests/test_shortest_path.py +45 -0
- sknetwork/ranking/__init__.py +9 -0
- sknetwork/ranking/base.py +56 -0
- sknetwork/ranking/betweenness.cpython-39-darwin.so +0 -0
- sknetwork/ranking/betweenness.pyx +99 -0
- sknetwork/ranking/closeness.py +95 -0
- sknetwork/ranking/harmonic.py +82 -0
- sknetwork/ranking/hits.py +94 -0
- sknetwork/ranking/katz.py +81 -0
- sknetwork/ranking/pagerank.py +107 -0
- sknetwork/ranking/postprocess.py +25 -0
- sknetwork/ranking/tests/__init__.py +1 -0
- sknetwork/ranking/tests/test_API.py +34 -0
- sknetwork/ranking/tests/test_betweenness.py +38 -0
- sknetwork/ranking/tests/test_closeness.py +34 -0
- sknetwork/ranking/tests/test_hits.py +20 -0
- sknetwork/ranking/tests/test_pagerank.py +69 -0
- sknetwork/regression/__init__.py +4 -0
- sknetwork/regression/base.py +56 -0
- sknetwork/regression/diffusion.py +190 -0
- sknetwork/regression/tests/__init__.py +1 -0
- sknetwork/regression/tests/test_API.py +34 -0
- sknetwork/regression/tests/test_diffusion.py +48 -0
- sknetwork/sknetwork.py +3 -0
- sknetwork/topology/__init__.py +9 -0
- sknetwork/topology/dag.py +74 -0
- sknetwork/topology/dag_core.cpython-39-darwin.so +0 -0
- sknetwork/topology/dag_core.pyx +38 -0
- sknetwork/topology/kcliques.cpython-39-darwin.so +0 -0
- sknetwork/topology/kcliques.pyx +193 -0
- sknetwork/topology/kcore.cpython-39-darwin.so +0 -0
- sknetwork/topology/kcore.pyx +120 -0
- sknetwork/topology/structure.py +234 -0
- sknetwork/topology/tests/__init__.py +1 -0
- sknetwork/topology/tests/test_cliques.py +28 -0
- sknetwork/topology/tests/test_cores.py +21 -0
- sknetwork/topology/tests/test_dag.py +26 -0
- sknetwork/topology/tests/test_structure.py +99 -0
- sknetwork/topology/tests/test_triangles.py +42 -0
- sknetwork/topology/tests/test_wl_coloring.py +49 -0
- sknetwork/topology/tests/test_wl_kernel.py +31 -0
- sknetwork/topology/triangles.cpython-39-darwin.so +0 -0
- sknetwork/topology/triangles.pyx +166 -0
- sknetwork/topology/weisfeiler_lehman.py +163 -0
- sknetwork/topology/weisfeiler_lehman_core.cpython-39-darwin.so +0 -0
- sknetwork/topology/weisfeiler_lehman_core.pyx +116 -0
- sknetwork/utils/__init__.py +40 -0
- sknetwork/utils/base.py +35 -0
- sknetwork/utils/check.py +354 -0
- sknetwork/utils/co_neighbor.py +71 -0
- sknetwork/utils/format.py +219 -0
- sknetwork/utils/kmeans.py +89 -0
- sknetwork/utils/knn.py +166 -0
- sknetwork/utils/knn1d.cpython-39-darwin.so +0 -0
- sknetwork/utils/knn1d.pyx +80 -0
- sknetwork/utils/membership.py +82 -0
- sknetwork/utils/minheap.cpython-39-darwin.so +0 -0
- sknetwork/utils/minheap.pxd +22 -0
- sknetwork/utils/minheap.pyx +111 -0
- sknetwork/utils/neighbors.py +115 -0
- sknetwork/utils/seeds.py +75 -0
- sknetwork/utils/simplex.py +140 -0
- sknetwork/utils/tests/__init__.py +1 -0
- sknetwork/utils/tests/test_base.py +28 -0
- sknetwork/utils/tests/test_bunch.py +16 -0
- sknetwork/utils/tests/test_check.py +190 -0
- sknetwork/utils/tests/test_co_neighbor.py +43 -0
- sknetwork/utils/tests/test_format.py +61 -0
- sknetwork/utils/tests/test_kmeans.py +21 -0
- sknetwork/utils/tests/test_knn.py +32 -0
- sknetwork/utils/tests/test_membership.py +24 -0
- sknetwork/utils/tests/test_neighbors.py +41 -0
- sknetwork/utils/tests/test_projection_simplex.py +33 -0
- sknetwork/utils/tests/test_seeds.py +67 -0
- sknetwork/utils/tests/test_verbose.py +15 -0
- sknetwork/utils/tests/test_ward.py +20 -0
- sknetwork/utils/timeout.py +38 -0
- sknetwork/utils/verbose.py +37 -0
- sknetwork/utils/ward.py +60 -0
- sknetwork/visualization/__init__.py +4 -0
- sknetwork/visualization/colors.py +34 -0
- sknetwork/visualization/dendrograms.py +229 -0
- sknetwork/visualization/graphs.py +819 -0
- sknetwork/visualization/tests/__init__.py +1 -0
- sknetwork/visualization/tests/test_dendrograms.py +53 -0
- sknetwork/visualization/tests/test_graphs.py +167 -0
|
@@ -0,0 +1,166 @@
|
|
|
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 Jun 3, 2020
|
|
7
|
+
@author: Julien Simonnet <julien.simonnet@etu.upmc.fr>
|
|
8
|
+
@author: Yohann Robert <yohann.robert@etu.upmc.fr>
|
|
9
|
+
@author: Nathan de Lara <nathan.delara@polytechnique.org>
|
|
10
|
+
"""
|
|
11
|
+
from libcpp.vector cimport vector
|
|
12
|
+
from scipy import sparse
|
|
13
|
+
from scipy.special import comb
|
|
14
|
+
from cython.parallel import prange
|
|
15
|
+
|
|
16
|
+
from sknetwork.topology.dag import DAG
|
|
17
|
+
from sknetwork.utils.base import Algorithm
|
|
18
|
+
|
|
19
|
+
cimport cython
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@cython.boundscheck(False)
|
|
23
|
+
@cython.wraparound(False)
|
|
24
|
+
cdef long count_local_triangles(int source, vector[int] indptr, vector[int] indices) nogil:
|
|
25
|
+
"""Counts the number of nodes in the intersection of a node and its neighbors in a DAG.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
source :
|
|
30
|
+
Index of the node to study.
|
|
31
|
+
indptr :
|
|
32
|
+
CSR format index pointer array of the normalized adjacency matrix of a DAG.
|
|
33
|
+
indices :
|
|
34
|
+
CSR format index array of the normalized adjacency matrix of a DAG.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
n_triangles :
|
|
39
|
+
Number of nodes in the intersection
|
|
40
|
+
"""
|
|
41
|
+
cdef int i, j, k
|
|
42
|
+
cdef int v
|
|
43
|
+
cdef long n_triangles = 0 # number of nodes in the intersection
|
|
44
|
+
|
|
45
|
+
for k in range(indptr[source], indptr[source+1]):
|
|
46
|
+
v = indices[k]
|
|
47
|
+
i = indptr[source]
|
|
48
|
+
j = indptr[v]
|
|
49
|
+
|
|
50
|
+
# calculates the intersection of the neighbors of u and v
|
|
51
|
+
while (i < indptr[source+1]) and (j < indptr[v+1]):
|
|
52
|
+
if indices[i] == indices[j]:
|
|
53
|
+
i += 1
|
|
54
|
+
j += 1
|
|
55
|
+
n_triangles += 1
|
|
56
|
+
else :
|
|
57
|
+
if indices[i] < indices[j]:
|
|
58
|
+
i += 1
|
|
59
|
+
else :
|
|
60
|
+
j += 1
|
|
61
|
+
|
|
62
|
+
return n_triangles
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@cython.boundscheck(False)
|
|
66
|
+
@cython.wraparound(False)
|
|
67
|
+
cdef long fit_core(vector[int] indptr, vector[int] indices, bint parallelize):
|
|
68
|
+
"""Counts the number of triangles directly without exporting the graph.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
indptr :
|
|
73
|
+
CSR format index pointer array of the normalized adjacency matrix of a DAG.
|
|
74
|
+
indices :
|
|
75
|
+
CSR format index array of the normalized adjacency matrix of a DAG.
|
|
76
|
+
parallelize :
|
|
77
|
+
If ``True``, use a parallel range to count triangles.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
n_triangles :
|
|
82
|
+
Number of triangles in the graph
|
|
83
|
+
"""
|
|
84
|
+
cdef int n = indptr.size() - 1
|
|
85
|
+
cdef int u
|
|
86
|
+
cdef long n_triangles = 0
|
|
87
|
+
|
|
88
|
+
if parallelize:
|
|
89
|
+
for u in prange(n, nogil=True):
|
|
90
|
+
n_triangles += count_local_triangles(u, indptr, indices)
|
|
91
|
+
else:
|
|
92
|
+
for u in range(n):
|
|
93
|
+
n_triangles += count_local_triangles(u, indptr, indices)
|
|
94
|
+
|
|
95
|
+
return n_triangles
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Triangles(Algorithm):
|
|
99
|
+
"""Count the number of triangles in a graph, and evaluate the clustering coefficient.
|
|
100
|
+
|
|
101
|
+
* Graphs
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
parallelize :
|
|
106
|
+
If ``True``, use a parallel range while listing the triangles.
|
|
107
|
+
|
|
108
|
+
Attributes
|
|
109
|
+
----------
|
|
110
|
+
n_triangles_ : int
|
|
111
|
+
Number of triangles.
|
|
112
|
+
clustering_coef_ : float
|
|
113
|
+
Global clustering coefficient of the graph.
|
|
114
|
+
|
|
115
|
+
Example
|
|
116
|
+
-------
|
|
117
|
+
>>> from sknetwork.data import karate_club
|
|
118
|
+
>>> triangles = Triangles()
|
|
119
|
+
>>> adjacency = karate_club()
|
|
120
|
+
>>> triangles.fit_transform(adjacency)
|
|
121
|
+
45
|
|
122
|
+
"""
|
|
123
|
+
def __init__(self, parallelize : bool = False):
|
|
124
|
+
super(Triangles, self).__init__()
|
|
125
|
+
self.parallelize = parallelize
|
|
126
|
+
self.n_triangles_ = None
|
|
127
|
+
self.clustering_coef_ = None
|
|
128
|
+
|
|
129
|
+
def fit(self, adjacency: sparse.csr_matrix) -> 'Triangles':
|
|
130
|
+
"""Count triangles.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
adjacency :
|
|
135
|
+
Adjacency matrix of the graph.
|
|
136
|
+
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
self: :class:`Triangles`
|
|
140
|
+
"""
|
|
141
|
+
degrees = adjacency.indptr[1:] - adjacency.indptr[:-1]
|
|
142
|
+
edge_pairs = comb(degrees, 2).sum()
|
|
143
|
+
|
|
144
|
+
dag = DAG(ordering='degree')
|
|
145
|
+
dag.fit(adjacency)
|
|
146
|
+
indptr = dag.indptr_
|
|
147
|
+
indices = dag.indices_
|
|
148
|
+
|
|
149
|
+
self.n_triangles_ = fit_core(indptr, indices, self.parallelize)
|
|
150
|
+
if edge_pairs > 0:
|
|
151
|
+
self.clustering_coef_ = 3 * self.n_triangles_ / edge_pairs
|
|
152
|
+
else:
|
|
153
|
+
self.clustering_coef_ = 0.
|
|
154
|
+
|
|
155
|
+
return self
|
|
156
|
+
|
|
157
|
+
def fit_transform(self, adjacency: sparse.csr_matrix) -> int:
|
|
158
|
+
""" Fit algorithm to the data and return the number of triangles. Same parameters as the ``fit`` method.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
n_triangles_ : int
|
|
163
|
+
Number of triangles.
|
|
164
|
+
"""
|
|
165
|
+
self.fit(adjacency)
|
|
166
|
+
return self.n_triangles_
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on July 2, 2020
|
|
5
|
+
@author: Pierre Pebereau <pierre.pebereau@telecom-paris.fr>
|
|
6
|
+
@author: Alexis Barreaux <alexis.barreaux@telecom-paris.fr>
|
|
7
|
+
"""
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from scipy import sparse
|
|
12
|
+
from sknetwork.topology.weisfeiler_lehman_core import weisfeiler_lehman_coloring
|
|
13
|
+
|
|
14
|
+
from sknetwork.utils.base import Algorithm
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WeisfeilerLehman(Algorithm):
|
|
18
|
+
"""Weisfeiler-Lehman algorithm for coloring/labeling graphs in order to check similarity.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
max_iter : int
|
|
23
|
+
Maximum number of iterations. Negative value means until convergence.
|
|
24
|
+
|
|
25
|
+
Attributes
|
|
26
|
+
----------
|
|
27
|
+
labels_ : np.ndarray
|
|
28
|
+
Label of each node.
|
|
29
|
+
|
|
30
|
+
Example
|
|
31
|
+
-------
|
|
32
|
+
>>> from sknetwork.topology import WeisfeilerLehman
|
|
33
|
+
>>> from sknetwork.data import house
|
|
34
|
+
>>> weisfeiler_lehman = WeisfeilerLehman()
|
|
35
|
+
>>> adjacency = house()
|
|
36
|
+
>>> labels = weisfeiler_lehman.fit_transform(adjacency)
|
|
37
|
+
>>> labels
|
|
38
|
+
array([0, 2, 1, 1, 2], dtype=int32)
|
|
39
|
+
|
|
40
|
+
References
|
|
41
|
+
----------
|
|
42
|
+
* Douglas, B. L. (2011).
|
|
43
|
+
`The Weisfeiler-Lehman Method and Graph Isomorphism Testing.
|
|
44
|
+
<https://arxiv.org/pdf/1101.5211.pdf>`_
|
|
45
|
+
|
|
46
|
+
* Shervashidze, N., Schweitzer, P., van Leeuwen, E. J., Melhorn, K., Borgwardt, K. M. (2011)
|
|
47
|
+
`Weisfeiler-Lehman graph kernels.
|
|
48
|
+
<http://www.jmlr.org/papers/volume12/shervashidze11a/shervashidze11a.pdf>`_
|
|
49
|
+
Journal of Machine Learning Research 12, 2011.
|
|
50
|
+
"""
|
|
51
|
+
def __init__(self, max_iter: int = -1):
|
|
52
|
+
super(WeisfeilerLehman, self).__init__()
|
|
53
|
+
self.max_iter = max_iter
|
|
54
|
+
self.labels_ = None
|
|
55
|
+
|
|
56
|
+
def fit(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> 'WeisfeilerLehman':
|
|
57
|
+
"""Fit algorithm to the data.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
adjacency : Union[sparse.csr_matrix, np.ndarray]
|
|
62
|
+
Adjacency matrix of the graph.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
self: :class:`WeisfeilerLehman`
|
|
67
|
+
"""
|
|
68
|
+
n: int = adjacency.shape[0]
|
|
69
|
+
if self.max_iter < 0 or self.max_iter > n:
|
|
70
|
+
max_iter = np.int32(n)
|
|
71
|
+
else:
|
|
72
|
+
max_iter = np.int32(self.max_iter)
|
|
73
|
+
|
|
74
|
+
labels = np.zeros(n, dtype=np.int32)
|
|
75
|
+
powers = (-np.pi / 3.15) ** np.arange(n, dtype=np.double)
|
|
76
|
+
indptr = adjacency.indptr.astype(np.int32)
|
|
77
|
+
indices = adjacency.indices.astype(np.int32)
|
|
78
|
+
|
|
79
|
+
labels, _ = weisfeiler_lehman_coloring(indptr, indices, labels, powers, max_iter)
|
|
80
|
+
self.labels_ = np.asarray(labels).astype(np.int32)
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def fit_transform(self, adjacency: Union[sparse.csr_matrix, np.ndarray]) -> np.ndarray:
|
|
84
|
+
"""Fit algorithm to the data and return the labels. Same parameters as the ``fit`` method.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
labels : np.ndarray
|
|
89
|
+
Labels.
|
|
90
|
+
"""
|
|
91
|
+
self.fit(adjacency)
|
|
92
|
+
return self.labels_
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def are_isomorphic(adjacency1: sparse.csr_matrix,
|
|
96
|
+
adjacency2: sparse.csr_matrix, max_iter: int = -1) -> bool:
|
|
97
|
+
"""Weisfeiler-Lehman isomorphism test. If the test is False, the graphs cannot be isomorphic,
|
|
98
|
+
otherwise, they might be.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
-----------
|
|
102
|
+
adjacency1 :
|
|
103
|
+
First adjacency matrix.
|
|
104
|
+
adjacency2 :
|
|
105
|
+
Second adjacency matrix.
|
|
106
|
+
max_iter : int
|
|
107
|
+
Maximum number of coloring iterations. Negative value means until convergence.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
test_result : bool
|
|
112
|
+
|
|
113
|
+
Example
|
|
114
|
+
-------
|
|
115
|
+
>>> from sknetwork.topology import are_isomorphic
|
|
116
|
+
>>> from sknetwork.data import house, bow_tie
|
|
117
|
+
>>> are_isomorphic(house(), bow_tie())
|
|
118
|
+
False
|
|
119
|
+
|
|
120
|
+
References
|
|
121
|
+
----------
|
|
122
|
+
* Douglas, B. L. (2011).
|
|
123
|
+
`The Weisfeiler-Lehman Method and Graph Isomorphism Testing.
|
|
124
|
+
<https://arxiv.org/pdf/1101.5211.pdf>`_
|
|
125
|
+
|
|
126
|
+
* Shervashidze, N., Schweitzer, P., van Leeuwen, E. J., Melhorn, K., Borgwardt, K. M. (2011)
|
|
127
|
+
`Weisfeiler-Lehman graph kernels.
|
|
128
|
+
<http://www.jmlr.org/papers/volume12/shervashidze11a/shervashidze11a.pdf>`_
|
|
129
|
+
Journal of Machine Learning Research 12, 2011.
|
|
130
|
+
"""
|
|
131
|
+
if (adjacency1.shape != adjacency2.shape) or (adjacency1.nnz != adjacency2.nnz):
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
n = adjacency1.shape[0]
|
|
135
|
+
|
|
136
|
+
if max_iter < 0 or max_iter > n:
|
|
137
|
+
max_iter = n
|
|
138
|
+
|
|
139
|
+
indptr1 = adjacency1.indptr.astype(np.int32)
|
|
140
|
+
indptr2 = adjacency2.indptr.astype(np.int32)
|
|
141
|
+
indices1 = adjacency1.indices.astype(np.int32)
|
|
142
|
+
indices2 = adjacency2.indices.astype(np.int32)
|
|
143
|
+
|
|
144
|
+
labels_1 = np.zeros(n, dtype=np.int32)
|
|
145
|
+
labels_2 = np.zeros(n, dtype=np.int32)
|
|
146
|
+
|
|
147
|
+
powers = (- np.pi / 3.15) ** np.arange(n, dtype=np.double)
|
|
148
|
+
|
|
149
|
+
iteration = 0
|
|
150
|
+
has_changed_1, has_changed_2 = True, True
|
|
151
|
+
while iteration < max_iter and (has_changed_1 or has_changed_2):
|
|
152
|
+
labels_1, has_changed_1 = weisfeiler_lehman_coloring(indptr1, indices1, labels_1, powers, max_iter=1)
|
|
153
|
+
labels_2, has_changed_2 = weisfeiler_lehman_coloring(indptr2, indices2, labels_2, powers, max_iter=1)
|
|
154
|
+
|
|
155
|
+
colors_1, counts_1 = np.unique(np.asarray(labels_1), return_counts=True)
|
|
156
|
+
colors_2, counts_2 = np.unique(np.asarray(labels_2), return_counts=True)
|
|
157
|
+
|
|
158
|
+
if (colors_1.shape != colors_2.shape) or (counts_1 != counts_2).any():
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
iteration += 1
|
|
162
|
+
|
|
163
|
+
return True
|
|
Binary file
|
|
@@ -0,0 +1,116 @@
|
|
|
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 July 1, 2020
|
|
7
|
+
@author: Pierre Pebereau <pierre.pebereau@telecom-paris.fr>
|
|
8
|
+
@author: Alexis Barreaux <alexis.barreaux@telecom-paris.fr>
|
|
9
|
+
"""
|
|
10
|
+
from libcpp.vector cimport vector
|
|
11
|
+
from libc.math cimport pow
|
|
12
|
+
cimport cython
|
|
13
|
+
|
|
14
|
+
ctypedef (int, double, int) ctuple
|
|
15
|
+
|
|
16
|
+
cdef extern from "<algorithm>" namespace "std":
|
|
17
|
+
# fixed sort as per https://stackoverflow.com/questions/57584909/unable-to-use-cdef-function-in-stdsort-as-comparison-function
|
|
18
|
+
void sort(...)
|
|
19
|
+
void csort "sort"(...)
|
|
20
|
+
|
|
21
|
+
cdef bint is_lower(ctuple a, ctuple b) :
|
|
22
|
+
"""Lexicographic comparison between triplets based on the first two values.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
a:
|
|
27
|
+
First triplet.
|
|
28
|
+
b:
|
|
29
|
+
Second triplet.
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
``True`` if a < b, and ``False`` otherwise.
|
|
34
|
+
"""
|
|
35
|
+
cdef int a1, b1
|
|
36
|
+
cdef double a2, b2
|
|
37
|
+
|
|
38
|
+
a1, a2, _ = a
|
|
39
|
+
b1, b2, _ = b
|
|
40
|
+
if a1 == b1 :
|
|
41
|
+
return a2 < b2
|
|
42
|
+
return a1 < b1
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@cython.boundscheck(False)
|
|
46
|
+
@cython.wraparound(False)
|
|
47
|
+
def weisfeiler_lehman_coloring(int[:] indptr, int[:] indices, int[:] labels, double [:] powers, int max_iter):
|
|
48
|
+
"""Weisfeiler-Lehman coloring.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
indptr :
|
|
53
|
+
Indptr of the CSR.
|
|
54
|
+
indices :
|
|
55
|
+
Indices of the CSR.
|
|
56
|
+
labels : int[:]
|
|
57
|
+
Labels to be changed.
|
|
58
|
+
powers : double [:]
|
|
59
|
+
Powers being used as hash and put in a memory view to limit several identical calculations.
|
|
60
|
+
max_iter : int
|
|
61
|
+
Maximum number of iterations.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
labels : int[:]
|
|
66
|
+
Colors of the nodes.
|
|
67
|
+
has_changed : bint
|
|
68
|
+
True if the output labels are not the same as the input ones. False otherwise.
|
|
69
|
+
"""
|
|
70
|
+
cdef int n = indptr.shape[0] -1
|
|
71
|
+
cdef int iteration = 0
|
|
72
|
+
cdef int i, j, j1, j2, jj, label
|
|
73
|
+
|
|
74
|
+
cdef double epsilon = pow(10, -10)
|
|
75
|
+
|
|
76
|
+
cdef vector[ctuple] new_labels
|
|
77
|
+
cdef ctuple tuple_ref, tuple_new
|
|
78
|
+
cdef double hash_ref, hash_new
|
|
79
|
+
cdef int label_ref, label_new
|
|
80
|
+
|
|
81
|
+
cdef bint has_changed = True
|
|
82
|
+
|
|
83
|
+
while iteration < max_iter and has_changed:
|
|
84
|
+
new_labels.clear()
|
|
85
|
+
has_changed = False
|
|
86
|
+
|
|
87
|
+
for i in range(n):
|
|
88
|
+
hash_ref = 0
|
|
89
|
+
j1 = indptr[i]
|
|
90
|
+
j2 = indptr[i + 1]
|
|
91
|
+
|
|
92
|
+
for jj in range(j1, j2):
|
|
93
|
+
j = indices[jj]
|
|
94
|
+
hash_ref += powers[labels[j]]
|
|
95
|
+
|
|
96
|
+
new_labels.push_back((labels[i], hash_ref, i))
|
|
97
|
+
csort(new_labels.begin(), new_labels.end(), is_lower)
|
|
98
|
+
label = 0
|
|
99
|
+
tuple_new = new_labels[0]
|
|
100
|
+
labels[tuple_new[2]] = label
|
|
101
|
+
for j in range(1, n):
|
|
102
|
+
tuple_ref = tuple_new
|
|
103
|
+
tuple_new = new_labels[j]
|
|
104
|
+
label_ref, hash_ref, _ = tuple_ref
|
|
105
|
+
label_new, hash_new, i = tuple_new
|
|
106
|
+
|
|
107
|
+
if abs(hash_new - hash_ref) > epsilon or label_new != label_ref :
|
|
108
|
+
label += 1
|
|
109
|
+
|
|
110
|
+
if labels[i] != label:
|
|
111
|
+
has_changed = True
|
|
112
|
+
|
|
113
|
+
labels[i] = label
|
|
114
|
+
iteration += 1
|
|
115
|
+
|
|
116
|
+
return labels, has_changed
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""utils module"""
|
|
2
|
+
from sknetwork.utils.check import is_symmetric
|
|
3
|
+
from sknetwork.utils.co_neighbor import co_neighbor_graph
|
|
4
|
+
from sknetwork.utils.format import *
|
|
5
|
+
from sknetwork.utils.kmeans import KMeansDense
|
|
6
|
+
from sknetwork.utils.knn import KNNDense, CNNDense
|
|
7
|
+
from sknetwork.utils.membership import get_membership, from_membership
|
|
8
|
+
from sknetwork.utils.neighbors import get_neighbors, get_degrees, get_weights
|
|
9
|
+
from sknetwork.utils.simplex import projection_simplex, projection_simplex_array, projection_simplex_csr
|
|
10
|
+
from sknetwork.utils.ward import WardDense
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Bunch(dict):
|
|
14
|
+
"""Container object for datasets.
|
|
15
|
+
Dictionary-like object that exposes its keys as attributes.
|
|
16
|
+
|
|
17
|
+
This code is taken from scikit-learn.
|
|
18
|
+
>>> bunch = Bunch(a=1, b=2)
|
|
19
|
+
>>> bunch['a']
|
|
20
|
+
1
|
|
21
|
+
>>> bunch.a
|
|
22
|
+
1
|
|
23
|
+
>>> bunch.b = 3
|
|
24
|
+
>>> bunch['b']
|
|
25
|
+
3
|
|
26
|
+
>>> bunch.c = 4
|
|
27
|
+
>>> bunch['c']
|
|
28
|
+
4
|
|
29
|
+
"""
|
|
30
|
+
def __init__(self, **kwargs):
|
|
31
|
+
super().__init__(kwargs)
|
|
32
|
+
|
|
33
|
+
def __setattr__(self, key, value):
|
|
34
|
+
self[key] = value
|
|
35
|
+
|
|
36
|
+
def __getattr__(self, key):
|
|
37
|
+
try:
|
|
38
|
+
return self[key]
|
|
39
|
+
except KeyError:
|
|
40
|
+
raise AttributeError(key)
|
sknetwork/utils/base.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Created on Jun 28, 2019
|
|
5
|
+
@author: Quentin Lutz <qlutz@enst.fr>
|
|
6
|
+
"""
|
|
7
|
+
import inspect
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Algorithm:
|
|
11
|
+
"""Base class for all algorithms."""
|
|
12
|
+
def __repr__(self):
|
|
13
|
+
# parameters not to display
|
|
14
|
+
arg_black_list = ['self', 'random_state', 'verbose']
|
|
15
|
+
output = self.__class__.__name__ + '('
|
|
16
|
+
signature = inspect.signature(self.__class__.__init__)
|
|
17
|
+
arguments = [arg.name for arg in signature.parameters.values() if arg.name not in arg_black_list]
|
|
18
|
+
for p in arguments:
|
|
19
|
+
try:
|
|
20
|
+
val = self.__dict__[p]
|
|
21
|
+
except KeyError:
|
|
22
|
+
continue
|
|
23
|
+
if type(val) == str:
|
|
24
|
+
val = "'" + val + "'"
|
|
25
|
+
else:
|
|
26
|
+
val = str(val)
|
|
27
|
+
output += p + '=' + val + ', '
|
|
28
|
+
if output[-1] != '(':
|
|
29
|
+
return output[:-2] + ')'
|
|
30
|
+
else:
|
|
31
|
+
return output + ')'
|
|
32
|
+
|
|
33
|
+
def fit(self, *args, **kwargs):
|
|
34
|
+
"""Fit Algorithm to the data."""
|
|
35
|
+
raise NotImplementedError
|