From 9496ebe9b02490dc7962f50008dd958c8cda0e11 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Fri, 22 Nov 2024 16:50:53 -0600 Subject: [PATCH 1/2] Add experimental version of `leiden_communities` This function is not yet defined or implemented in NetworkX, and there is high probability the API may change once added to NetworkX. --- nx_cugraph/algorithms/community/__init__.py | 3 +- nx_cugraph/algorithms/community/leiden.py | 56 +++++++++++++++++++++ nx_cugraph/tests/test_leiden.py | 21 ++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 nx_cugraph/algorithms/community/leiden.py create mode 100644 nx_cugraph/tests/test_leiden.py diff --git a/nx_cugraph/algorithms/community/__init__.py b/nx_cugraph/algorithms/community/__init__.py index 51a4f5c19..a9c745a2e 100644 --- a/nx_cugraph/algorithms/community/__init__.py +++ b/nx_cugraph/algorithms/community/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023, NVIDIA CORPORATION. +# Copyright (c) 2023-2024, NVIDIA CORPORATION. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -10,4 +10,5 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from .leiden import * from .louvain import * diff --git a/nx_cugraph/algorithms/community/leiden.py b/nx_cugraph/algorithms/community/leiden.py new file mode 100644 index 000000000..8c2a02d7c --- /dev/null +++ b/nx_cugraph/algorithms/community/leiden.py @@ -0,0 +1,56 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import pylibcugraph as plc + +from nx_cugraph.convert import _to_undirected_graph +from nx_cugraph.utils import ( + _get_float_dtype, + _groupby, + _seed_to_int, + not_implemented_for, +) + +__all__ = ["leiden_communities"] + + +@not_implemented_for("directed") +# @networkx_algorithm(extra_params=_dtype_param, version_added="25.02", _plc="leiden") +def leiden_communities( + G, weight="weight", resolution=1, theta=1, max_level=None, seed=None, *, dtype=None +): + """Find the best partition of a graph using Leiden Community Detection algorithm. + + **Warning: this API is experimental and may change. It is not yet in NetworkX.** + + See Also + -------- + louvain_communities + """ + seed = _seed_to_int(seed) + G = _to_undirected_graph(G, weight, 1, np.float32) + dtype = _get_float_dtype(dtype, graph=G, weight=weight) + if max_level is None or max_level < 0: + max_level = 500 + node_ids, clusters, modularity = plc.leiden( + resource_handle=plc.ResourceHandle(), + random_state=seed, + graph=G._get_plc_graph(weight, 1, dtype), + max_level=max_level, + resolution=resolution, + theta=theta, + do_expensive_check=False, + ) + groups = _groupby(clusters, node_ids, groups_are_canonical=True) + return [set(G._nodearray_to_list(ids)) for ids in groups.values()] diff --git a/nx_cugraph/tests/test_leiden.py b/nx_cugraph/tests/test_leiden.py new file mode 100644 index 000000000..df5ab072f --- /dev/null +++ b/nx_cugraph/tests/test_leiden.py @@ -0,0 +1,21 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import nx_cugraph as nxcg + + +def test_leiden_karate(): + G = nxcg.karate_club_graph() + leiden = nxcg.community.leiden_communities(G) + louvain = nxcg.community.louvain_communities(G) + assert leiden == louvain From 31dc9a328e580eec8dceb96a6c477c62516dee3b Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Tue, 26 Nov 2024 19:38:37 -0600 Subject: [PATCH 2/2] Remove unused `theta` parameter; minor updates --- _nx_cugraph/__init__.py | 4 ++++ nx_cugraph/algorithms/community/leiden.py | 18 +++++++----------- nx_cugraph/tests/test_leiden.py | 5 +++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/_nx_cugraph/__init__.py b/_nx_cugraph/__init__.py index 6b905e8db..e37daad0a 100644 --- a/_nx_cugraph/__init__.py +++ b/_nx_cugraph/__init__.py @@ -114,6 +114,7 @@ "katz_centrality", "krackhardt_kite_graph", "ladder_graph", + "leiden_communities", "les_miserables_graph", "lollipop_graph", "louvain_communities", @@ -234,6 +235,9 @@ "katz_centrality": { "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", }, + "leiden_communities": { + "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", + }, "louvain_communities": { "dtype : dtype or None, optional": "The data type (np.float32, np.float64, or None) to use for the edge weights in the algorithm. If None, then dtype is determined by the edge values.", }, diff --git a/nx_cugraph/algorithms/community/leiden.py b/nx_cugraph/algorithms/community/leiden.py index 8c2a02d7c..6005bdcae 100644 --- a/nx_cugraph/algorithms/community/leiden.py +++ b/nx_cugraph/algorithms/community/leiden.py @@ -16,9 +16,11 @@ from nx_cugraph.convert import _to_undirected_graph from nx_cugraph.utils import ( + _dtype_param, _get_float_dtype, _groupby, _seed_to_int, + networkx_algorithm, not_implemented_for, ) @@ -26,18 +28,12 @@ @not_implemented_for("directed") -# @networkx_algorithm(extra_params=_dtype_param, version_added="25.02", _plc="leiden") +@networkx_algorithm(extra_params=_dtype_param, version_added="25.02", _plc="leiden") def leiden_communities( - G, weight="weight", resolution=1, theta=1, max_level=None, seed=None, *, dtype=None + G, weight="weight", resolution=1, max_level=None, seed=None, *, dtype=None ): - """Find the best partition of a graph using Leiden Community Detection algorithm. - - **Warning: this API is experimental and may change. It is not yet in NetworkX.** - - See Also - -------- - louvain_communities - """ + # Warning: this API is experimental and may change. It is not yet in NetworkX. + # See: https://github.com/networkx/networkx/pull/7743 seed = _seed_to_int(seed) G = _to_undirected_graph(G, weight, 1, np.float32) dtype = _get_float_dtype(dtype, graph=G, weight=weight) @@ -49,7 +45,7 @@ def leiden_communities( graph=G._get_plc_graph(weight, 1, dtype), max_level=max_level, resolution=resolution, - theta=theta, + theta=1, # TODO: expose theta as a backend-only parameter once it's used do_expensive_check=False, ) groups = _groupby(clusters, node_ids, groups_are_canonical=True) diff --git a/nx_cugraph/tests/test_leiden.py b/nx_cugraph/tests/test_leiden.py index df5ab072f..4fae29c3c 100644 --- a/nx_cugraph/tests/test_leiden.py +++ b/nx_cugraph/tests/test_leiden.py @@ -15,7 +15,8 @@ def test_leiden_karate(): + # Basic smoke test; if something here changes, we want to know! G = nxcg.karate_club_graph() - leiden = nxcg.community.leiden_communities(G) - louvain = nxcg.community.louvain_communities(G) + leiden = nxcg.community.leiden_communities(G, seed=123) + louvain = nxcg.community.louvain_communities(G, seed=123) assert leiden == louvain