From c17ba39111e3215cef15e58f6852f549b20f54b5 Mon Sep 17 00:00:00 2001 From: Don Acosta <97529984+acostadon@users.noreply.github.com> Date: Wed, 6 Mar 2024 09:08:53 -0500 Subject: [PATCH 1/6] Fix in Docs to address question asked in issue 4182 (#4204) Added compute comment and changed DiGraph to Graph(directed=True) Tested in a notebook. Addresses question in #4182 Authors: - Don Acosta (https://github.com/acostadon) Approvers: - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/4204 --- docs/cugraph/source/api_docs/cugraph/dask-cugraph.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/cugraph/source/api_docs/cugraph/dask-cugraph.rst b/docs/cugraph/source/api_docs/cugraph/dask-cugraph.rst index 772d3abc45f..a2d49789053 100644 --- a/docs/cugraph/source/api_docs/cugraph/dask-cugraph.rst +++ b/docs/cugraph/source/api_docs/cugraph/dask-cugraph.rst @@ -60,12 +60,15 @@ Example ) # create graph from input data - G = cugraph.DiGraph() + G = cugraph.Graph(directed=True) G.from_dask_cudf_edgelist(e_list, source='src', destination='dst') # run PageRank pr_df = dask_cugraph.pagerank(G, tol=1e-4) + # need to call compute to generate results + pr_df.compute() + # cluster clean up Comms.destroy() client.close() From 5cfea53777ed2232ad274af5b74bf014bdec9ff8 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Wed, 6 Mar 2024 18:33:06 -0600 Subject: [PATCH 2/6] Fix `louvain_communities`, `PropertyGraph`, cudf `column.full`, dgl 2.1.0 CI failures (#4215) * Updates cudf usage in hypergraph to use `cudf.core.column.as_column` instead of the deprecated `cudf.core.column.full` API * Adds code to call louvain using a signature that's compatible with both pre and post NX 3.3 * Adds an upper bound to the DGL version pin to ensure a compatible version is used * Updates a PropertyGraph test to handle minor ordering changes in the result DataFrame * Test matrices starting 24.04 have changed, resulting in an "empty matrix" error when attempting to use the supported combination used for PyG (PyG wheel tests need pytorch with CUDA 12.2 on arm64), so the matrix was simplified to remove the CUDA 11.8 requirement. cc @tingyu66 Authors: - Erik Welch (https://github.com/eriknw) - Naim (https://github.com/naimnv) Approvers: - Rick Ratzel (https://github.com/rlratzel) - Ray Douglass (https://github.com/raydouglass) URL: https://github.com/rapidsai/cugraph/pull/4215 --- .github/workflows/pr.yaml | 2 +- .github/workflows/test.yaml | 2 +- ci/test_python.sh | 2 +- ci/test_wheel_cugraph-dgl.sh | 2 +- .../cugraph/cugraph/structure/hypergraph.py | 4 +- .../tests/data_store/test_property_graph.py | 6 +- python/nx-cugraph/_nx_cugraph/__init__.py | 1 + .../algorithms/community/louvain.py | 93 ++++++++++++++----- .../nx_cugraph/tests/test_match_api.py | 5 - 9 files changed, 80 insertions(+), 37 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 7753f52d799..7f0b95e3573 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -162,7 +162,7 @@ jobs: with: build_type: pull-request script: ci/test_wheel_cugraph-pyg.sh - matrix_filter: map(select(.ARCH == "amd64" and .CUDA_VER == "11.8.0")) + matrix_filter: map(select(.ARCH == "amd64")) wheel-build-cugraph-equivariant: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-24.04 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f6a04fcc776..32fb2d62b29 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -76,7 +76,7 @@ jobs: date: ${{ inputs.date }} sha: ${{ inputs.sha }} script: ci/test_wheel_cugraph-pyg.sh - matrix_filter: map(select(.ARCH == "amd64" and .CUDA_VER == "11.8.0")) + matrix_filter: map(select(.ARCH == "amd64")) wheel-tests-cugraph-equivariant: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@branch-24.04 diff --git a/ci/test_python.sh b/ci/test_python.sh index 9fa1de2e5e7..e05160239ab 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -166,7 +166,7 @@ if [[ "${RAPIDS_CUDA_VERSION}" == "11.8.0" ]]; then pylibcugraphops \ cugraph \ cugraph-dgl \ - 'dgl>=1.1.0.cu*' \ + 'dgl>=1.1.0.cu*,<=2.0.0.cu*' \ 'pytorch>=2.0' \ 'pytorch-cuda>=11.8' diff --git a/ci/test_wheel_cugraph-dgl.sh b/ci/test_wheel_cugraph-dgl.sh index 9ecaa75a86b..367b169bd13 100755 --- a/ci/test_wheel_cugraph-dgl.sh +++ b/ci/test_wheel_cugraph-dgl.sh @@ -34,6 +34,6 @@ DGL_URL="https://data.dgl.ai/wheels/cu${PYTORCH_CUDA_VER}/repo.html" rapids-logger "Installing PyTorch and DGL" rapids-retry python -m pip install torch --index-url ${PYTORCH_URL} -rapids-retry python -m pip install dgl --find-links ${DGL_URL} +rapids-retry python -m pip install dgl==2.0.0 --find-links ${DGL_URL} python -m pytest python/cugraph-dgl/tests diff --git a/python/cugraph/cugraph/structure/hypergraph.py b/python/cugraph/cugraph/structure/hypergraph.py index 4e9975e6b8a..4add74d6061 100644 --- a/python/cugraph/cugraph/structure/hypergraph.py +++ b/python/cugraph/cugraph/structure/hypergraph.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020-2023, NVIDIA CORPORATION. +# Copyright (c) 2020-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 @@ -581,7 +581,7 @@ def _create_direct_edges( def _str_scalar_to_category(size, val): return cudf.core.column.build_categorical_column( categories=cudf.core.column.as_column([val], dtype="str"), - codes=cudf.core.column.column.full(size, 0, dtype=np.int32), + codes=cudf.core.column.as_column(0, length=size, dtype=np.int32), mask=None, size=size, offset=0, diff --git a/python/cugraph/cugraph/tests/data_store/test_property_graph.py b/python/cugraph/cugraph/tests/data_store/test_property_graph.py index a33d4f753db..da5608e0193 100644 --- a/python/cugraph/cugraph/tests/data_store/test_property_graph.py +++ b/python/cugraph/cugraph/tests/data_store/test_property_graph.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2023, NVIDIA CORPORATION. +# Copyright (c) 2021-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 @@ -1424,6 +1424,10 @@ def test_extract_subgraph_graph_without_vert_props(as_pg_first): actual_edgelist = G.edgelist.edgelist_df assert G.is_directed() + expected_edgelist = expected_edgelist.sort_values( + by=["src", "dst"], ignore_index=True + ) + actual_edgelist = actual_edgelist.sort_values(by=["src", "dst"], ignore_index=True) assert_frame_equal(expected_edgelist, actual_edgelist, check_like=True) diff --git a/python/nx-cugraph/_nx_cugraph/__init__.py b/python/nx-cugraph/_nx_cugraph/__init__.py index c6c43110ac6..b2f13d25ff3 100644 --- a/python/nx-cugraph/_nx_cugraph/__init__.py +++ b/python/nx-cugraph/_nx_cugraph/__init__.py @@ -164,6 +164,7 @@ }, "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.", + "max_level : int, optional": "Upper limit of the number of macro-iterations (max: 500).", }, "pagerank": { "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/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py index f7638f47aad..ea1318060e0 100644 --- a/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py +++ b/python/nx-cugraph/nx_cugraph/algorithms/community/louvain.py @@ -37,18 +37,23 @@ _max_level_param = {} -@not_implemented_for("directed") -@networkx_algorithm( - extra_params={ - **_max_level_param, - **_dtype_param, - }, - is_incomplete=True, # seed not supported; self-loops not supported - is_different=True, # RNG different - version_added="23.10", - _plc="louvain", -) -def louvain_communities( +def _louvain_communities_nx32( + G, + weight="weight", + resolution=1, + threshold=0.0000001, + seed=None, + *, + max_level=None, + dtype=None, +): + """`seed` parameter is currently ignored, and self-loops are not yet supported.""" + return _louvain_communities( + G, weight, resolution, threshold, max_level, seed, dtype=dtype + ) + + +def _louvain_communities( G, weight="weight", resolution=1, @@ -85,16 +90,54 @@ def louvain_communities( return [set(G._nodearray_to_list(ids)) for ids in groups.values()] -@louvain_communities._can_run -def _( - G, - weight="weight", - resolution=1, - threshold=0.0000001, - max_level=None, - seed=None, - *, - dtype=None, -): - # NetworkX allows both directed and undirected, but cugraph only allows undirected. - return not G.is_directed() +_louvain_decorator = networkx_algorithm( + extra_params={ + **_max_level_param, + **_dtype_param, + }, + is_incomplete=True, # seed not supported; self-loops not supported + is_different=True, # RNG different + version_added="23.10", + _plc="louvain", + name="louvain_communities", +) + +if _max_level_param: # networkx <= 3.2 + _louvain_communities_nx32.__name__ = "louvain_communities" + louvain_communities = not_implemented_for("directed")( + _louvain_decorator(_louvain_communities_nx32) + ) + + @louvain_communities._can_run + def _( + G, + weight="weight", + resolution=1, + threshold=0.0000001, + seed=None, + *, + max_level=None, + dtype=None, + ): + # NetworkX allows both directed and undirected, but cugraph only undirected. + return not G.is_directed() + +else: # networkx >= 3.3 + _louvain_communities.__name__ = "louvain_communities" + louvain_communities = not_implemented_for("directed")( + _louvain_decorator(_louvain_communities) + ) + + @louvain_communities._can_run + def _( + G, + weight="weight", + resolution=1, + threshold=0.0000001, + max_level=None, + seed=None, + *, + dtype=None, + ): + # NetworkX allows both directed and undirected, but cugraph only undirected. + return not G.is_directed() diff --git a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py index 595f7819ac1..bd6f20e84f0 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_match_api.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_match_api.py @@ -44,11 +44,6 @@ def test_match_signature_and_names(): else: orig_func = dispatchable_func.orig_func - if nxver.major == 3 and nxver.minor <= 2 and name == "louvain_communities": - # The signature of louvain_communities changed in NetworkX 3.3, and - # we updated to match, so we skip this check in older versions. - continue - # Matching signatures? orig_sig = inspect.signature(orig_func) func_sig = inspect.signature(func) From 4597e1f9bca589660c922594462a4ebc30b9fe56 Mon Sep 17 00:00:00 2001 From: jakirkham Date: Wed, 6 Mar 2024 21:03:08 -0800 Subject: [PATCH 3/6] Add Python 3.11 to Conda docs (#4213) xref: https://github.com/rapidsai/cugraph/pull/4207#discussion_r1511881073 Authors: - https://github.com/jakirkham Approvers: - Don Acosta (https://github.com/acostadon) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/4213 --- docs/cugraph/source/installation/getting_cugraph.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cugraph/source/installation/getting_cugraph.md b/docs/cugraph/source/installation/getting_cugraph.md index d9d3fa55c9a..0695c690a3b 100644 --- a/docs/cugraph/source/installation/getting_cugraph.md +++ b/docs/cugraph/source/installation/getting_cugraph.md @@ -45,7 +45,7 @@ conda install -c rapidsai -c conda-forge -c nvidia cugraph cuda-version=12.0 Alternatively, use `cuda-version=11.8` for packages supporting CUDA 11. -Note: This conda installation only applies to Linux and Python versions 3.9/3.10. +Note: This conda installation only applies to Linux and Python versions 3.9/3.10/3.11.
From 7eee5883a38210390137dfdf4a5c6ce3286905e6 Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Thu, 7 Mar 2024 06:13:39 +0100 Subject: [PATCH 4/6] Remove CUGRAPH_EXPECTS(!(this->has_edge_mask()), unimplemented.) (#4214) Remove CUGRAPH_EXPECTS(!(this->has_edge_mask()), unimplemented.) Authors: - Naim (https://github.com/naimnv) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Chuck Hastings (https://github.com/ChuckHastings) URL: https://github.com/rapidsai/cugraph/pull/4214 --- cpp/src/structure/graph_view_impl.cuh | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/cpp/src/structure/graph_view_impl.cuh b/cpp/src/structure/graph_view_impl.cuh index 623e795d5be..4ee5ad5ca02 100644 --- a/cpp/src/structure/graph_view_impl.cuh +++ b/cpp/src/structure/graph_view_impl.cuh @@ -602,7 +602,6 @@ graph_view_tpartition_, this->edge_partition_segment_offsets_); } else { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } } @@ -622,7 +621,6 @@ graph_view_tlocal_vertex_partition_range_size()); } else { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } } @@ -633,7 +631,6 @@ graph_view_thas_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } else { std::optional>> edge_partition_masks{ @@ -663,7 +660,6 @@ graph_view_thas_edge_mask()), "unimplemented."); return compute_minor_degrees(handle, *this); } else { return compute_major_degrees(handle, @@ -681,8 +677,6 @@ template >:: compute_max_in_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - auto in_degrees = compute_in_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), in_degrees.begin(), in_degrees.end()); rmm::device_scalar ret(edge_t{0}, handle.get_stream()); @@ -699,8 +693,6 @@ template >:: compute_max_in_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - auto in_degrees = compute_in_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), in_degrees.begin(), in_degrees.end()); edge_t ret{0}; @@ -713,8 +705,6 @@ template >:: compute_max_out_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - auto out_degrees = compute_out_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), out_degrees.begin(), out_degrees.end()); rmm::device_scalar ret(edge_t{0}, handle.get_stream()); @@ -731,8 +721,6 @@ template >:: compute_max_out_degree(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - auto out_degrees = compute_out_degrees(handle); auto it = thrust::max_element(handle.get_thrust_policy(), out_degrees.begin(), out_degrees.end()); edge_t ret{0}; @@ -745,8 +733,6 @@ template >:: count_self_loops(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - return count_if_e( handle, *this, @@ -760,8 +746,6 @@ template >:: count_self_loops(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - return count_if_e( handle, *this, @@ -775,8 +759,6 @@ template >:: count_multi_edges(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - if (!this->is_multigraph()) { return edge_t{0}; } edge_t count{0}; @@ -795,8 +777,6 @@ template >:: count_multi_edges(raft::handle_t const& handle) const { - CUGRAPH_EXPECTS(!(this->has_edge_mask()), "unimplemented."); - if (!this->is_multigraph()) { return edge_t{0}; } return count_edge_partition_multi_edges( From 72401a06bb20d0a0a17426f42e09d9630499dee1 Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:42:31 +0100 Subject: [PATCH 5/6] C API for MNMG ECG (#4194) C API for MNMG ECG Closes #3571 Closes #3572 Closes #3573 Closes #3575 Authors: - Naim (https://github.com/naimnv) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Joseph Nke (https://github.com/jnke2016) - Brad Rees (https://github.com/BradReesWork) URL: https://github.com/rapidsai/cugraph/pull/4194 --- cpp/CMakeLists.txt | 1 + cpp/include/cugraph_c/community_algorithms.h | 49 ++++- cpp/src/c_api/ecg.cpp | 154 ++++++++++++++ cpp/src/c_api/legacy_ecg.cpp | 29 +-- cpp/tests/CMakeLists.txt | 6 +- cpp/tests/c_api/ecg_test.c | 195 ++++++++++++++++++ cpp/tests/c_api/legacy_ecg_test.c | 23 +-- cpp/tests/c_api/mg_ecg_test.c | 181 ++++++++++++++++ .../{ecg_test.cpp => legacy_ecg_test.cpp} | 8 +- .../_cugraph_c/community_algorithms.pxd | 18 +- python/pylibcugraph/pylibcugraph/ecg.pyx | 5 +- 11 files changed, 624 insertions(+), 45 deletions(-) create mode 100644 cpp/src/c_api/ecg.cpp create mode 100644 cpp/tests/c_api/ecg_test.c create mode 100644 cpp/tests/c_api/mg_ecg_test.c rename cpp/tests/community/{ecg_test.cpp => legacy_ecg_test.cpp} (98%) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index c812cd8e4b3..a3392627fb8 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -423,6 +423,7 @@ add_library(cugraph_c src/c_api/core_number.cpp src/c_api/core_result.cpp src/c_api/extract_ego.cpp + src/c_api/ecg.cpp src/c_api/k_core.cpp src/c_api/hierarchical_clustering_result.cpp src/c_api/induced_subgraph.cpp diff --git a/cpp/include/cugraph_c/community_algorithms.h b/cpp/include/cugraph_c/community_algorithms.h index e8a71a40162..cb3d6b6375a 100644 --- a/cpp/include/cugraph_c/community_algorithms.h +++ b/cpp/include/cugraph_c/community_algorithms.h @@ -177,6 +177,41 @@ double cugraph_hierarchical_clustering_result_get_modularity( */ void cugraph_hierarchical_clustering_result_free(cugraph_hierarchical_clustering_result_t* result); +/** + * @brief Compute ECG clustering + * + * @param [in] handle Handle for accessing resources + * @param [in/out] rng_state State of the random number generator, updated with each call + * @param [in] graph Pointer to graph. NOTE: Graph might be modified if the storage + * needs to be transposed + * @param [in] min_weight Minimum edge weight in final graph + * @param [in] ensemble_size The number of Louvain iterations to run + * @param [in] max_level Maximum level in hierarchy for final Louvain + * @param [in] threshold Threshold parameter, defines convergence at each level of hierarchy + * for final Louvain + * @param [in] resolution Resolution parameter (gamma) in modularity formula. + * This changes the size of the communities. Higher resolutions + * lead to more smaller communities, lower resolutions lead to + * fewer larger communities. + * @param [in] do_expensive_check + * A flag to run expensive checks for input arguments (if set to true) + * @param [out] result Output from the Louvain call + * @param [out] error Pointer to an error object storing details of any error. Will + * be populated if error code is not CUGRAPH_SUCCESS + * @return error code + */ +cugraph_error_code_t cugraph_ecg(const cugraph_resource_handle_t* handle, + cugraph_rng_state_t* rng_state, + cugraph_graph_t* graph, + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution, + bool_t do_expensive_check, + cugraph_hierarchical_clustering_result_t** result, + cugraph_error_t** error); + /** * @brief Compute ECG clustering of the given graph * @@ -200,13 +235,13 @@ void cugraph_hierarchical_clustering_result_free(cugraph_hierarchical_clustering * be populated if error code is not CUGRAPH_SUCCESS * @return error code */ -cugraph_error_code_t cugraph_ecg(const cugraph_resource_handle_t* handle, - cugraph_graph_t* graph, - double min_weight, - size_t ensemble_size, - bool_t do_expensive_check, - cugraph_hierarchical_clustering_result_t** result, - cugraph_error_t** error); +cugraph_error_code_t cugraph_legacy_ecg(const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + double min_weight, + size_t ensemble_size, + bool_t do_expensive_check, + cugraph_hierarchical_clustering_result_t** result, + cugraph_error_t** error); /** * @brief Extract ego graphs diff --git a/cpp/src/c_api/ecg.cpp b/cpp/src/c_api/ecg.cpp new file mode 100644 index 00000000000..53e034916b7 --- /dev/null +++ b/cpp/src/c_api/ecg.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2022-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. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +struct ecg_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_rng_state_t* rng_state_{nullptr}; + cugraph::c_api::cugraph_graph_t* graph_{nullptr}; + double min_weight_{0.1}; + size_t ensemble_size_{10}; + size_t max_level_{0}; + double threshold_{0.001}; + double resolution_{1}; + bool do_expensive_check_{false}; + cugraph::c_api::cugraph_hierarchical_clustering_result_t* result_{}; + + ecg_functor(::cugraph_resource_handle_t const* handle, + ::cugraph_rng_state_t* rng_state, + ::cugraph_graph_t* graph, + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution, + bool do_expensive_check) + : abstract_functor(), + handle_(*reinterpret_cast(handle)->handle_), + rng_state_(reinterpret_cast(rng_state)), + graph_(reinterpret_cast(graph)), + max_level_(max_level), + threshold_(threshold), + resolution_(resolution), + do_expensive_check_(do_expensive_check) + { + } + + template + void operator()() + { + if constexpr (!cugraph::is_candidate::value) { + unsupported(); + } else { + // ecg expects store_transposed == false + if constexpr (store_transposed) { + error_code_ = cugraph::c_api:: + transpose_storage( + handle_, graph_, error_.get()); + if (error_code_ != CUGRAPH_SUCCESS) return; + } + + auto graph = + reinterpret_cast*>(graph_->graph_); + + auto graph_view = graph->view(); + + auto edge_weights = reinterpret_cast< + cugraph::edge_property_t, + weight_t>*>(graph_->edge_weights_); + + auto number_map = reinterpret_cast*>(graph_->number_map_); + + rmm::device_uvector clusters(0, handle_.get_stream()); + + weight_t modularity; + + std::tie(clusters, std::ignore, modularity) = + cugraph::ecg(handle_, + rng_state_->rng_state_, + graph_view, + (edge_weights != nullptr) + ? std::make_optional(edge_weights->view()) + : std::make_optional(cugraph::c_api::create_constant_edge_property( + handle_, graph_view, weight_t{1}) + .view()), + static_cast(min_weight_), + ensemble_size_, + max_level_, + static_cast(threshold_), + static_cast(resolution_)); + + rmm::device_uvector vertices(graph_view.local_vertex_partition_range_size(), + handle_.get_stream()); + raft::copy(vertices.data(), number_map->data(), vertices.size(), handle_.get_stream()); + + result_ = new cugraph::c_api::cugraph_hierarchical_clustering_result_t{ + modularity, + new cugraph::c_api::cugraph_type_erased_device_array_t(vertices, graph_->vertex_type_), + new cugraph::c_api::cugraph_type_erased_device_array_t(clusters, graph_->vertex_type_)}; + } + } +}; + +} // namespace + +extern "C" cugraph_error_code_t cugraph_ecg(const cugraph_resource_handle_t* handle, + cugraph_rng_state_t* rng_state, + cugraph_graph_t* graph, + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution, + bool_t do_expensive_check, + cugraph_hierarchical_clustering_result_t** result, + cugraph_error_t** error) +{ + ecg_functor functor(handle, + rng_state, + graph, + min_weight, + ensemble_size, + max_level, + threshold, + resolution, + do_expensive_check); + + return cugraph::c_api::run_algorithm(graph, functor, result, error); +} \ No newline at end of file diff --git a/cpp/src/c_api/legacy_ecg.cpp b/cpp/src/c_api/legacy_ecg.cpp index 304d9272910..6fee219f303 100644 --- a/cpp/src/c_api/legacy_ecg.cpp +++ b/cpp/src/c_api/legacy_ecg.cpp @@ -31,7 +31,7 @@ namespace { -struct ecg_functor : public cugraph::c_api::abstract_functor { +struct legacy_ecg_functor : public cugraph::c_api::abstract_functor { raft::handle_t const& handle_; cugraph::c_api::cugraph_graph_t* graph_; double min_weight_; @@ -39,11 +39,11 @@ struct ecg_functor : public cugraph::c_api::abstract_functor { bool do_expensive_check_; cugraph::c_api::cugraph_hierarchical_clustering_result_t* result_{}; - ecg_functor(::cugraph_resource_handle_t const* handle, - ::cugraph_graph_t* graph, - double min_weight, - size_t ensemble_size, - bool do_expensive_check) + legacy_ecg_functor(::cugraph_resource_handle_t const* handle, + ::cugraph_graph_t* graph, + double min_weight, + size_t ensemble_size, + bool do_expensive_check) : abstract_functor(), handle_(*reinterpret_cast(handle)->handle_), graph_(reinterpret_cast(graph)), @@ -120,15 +120,16 @@ struct ecg_functor : public cugraph::c_api::abstract_functor { } // namespace -extern "C" cugraph_error_code_t cugraph_ecg(const cugraph_resource_handle_t* handle, - cugraph_graph_t* graph, - double min_weight, - size_t ensemble_size, - bool_t do_expensive_check, - cugraph_hierarchical_clustering_result_t** result, - cugraph_error_t** error) +extern "C" cugraph_error_code_t cugraph_legacy_ecg( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + double min_weight, + size_t ensemble_size, + bool_t do_expensive_check, + cugraph_hierarchical_clustering_result_t** result, + cugraph_error_t** error) { - ecg_functor functor(handle, graph, min_weight, ensemble_size, do_expensive_check); + legacy_ecg_functor functor(handle, graph, min_weight, ensemble_size, do_expensive_check); return cugraph::c_api::run_algorithm(graph, functor, result, error); } diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 1e5d0489b1f..cb57b96f955 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -248,8 +248,8 @@ ConfigureTest(LOUVAIN_TEST community/louvain_test.cpp) ConfigureTest(LEIDEN_TEST community/leiden_test.cpp) ################################################################################################### -# - ECG tests ------------------------------------------------------------------------------------- -ConfigureTest(ECG_TEST community/ecg_test.cpp) +# - Legacy ECG tests ------------------------------------------------------------------------------------- +ConfigureTest(LEGACY_ECG_TEST community/legacy_ecg_test.cpp) ################################################################################################### # - Balanced cut clustering tests ----------------------------------------------------------------- @@ -679,6 +679,7 @@ if(BUILD_CUGRAPH_MG_TESTS) ConfigureCTestMG(MG_CAPI_TRIANGLE_COUNT_TEST c_api/mg_triangle_count_test.c) ConfigureCTestMG(MG_CAPI_LOUVAIN_TEST c_api/mg_louvain_test.c) ConfigureCTestMG(MG_CAPI_LEIDEN_TEST c_api/mg_leiden_test.c) + ConfigureCTestMG(MG_CAPI_ECG_TEST c_api/mg_ecg_test.c) ConfigureCTestMG(MG_CAPI_CORE_NUMBER_TEST c_api/mg_core_number_test.c) ConfigureCTestMG(MG_CAPI_SIMILARITY_TEST c_api/mg_similarity_test.c) ConfigureCTestMG(MG_CAPI_K_CORE_TEST c_api/mg_k_core_test.c) @@ -737,6 +738,7 @@ ConfigureCTest(CAPI_RANDOM_WALKS_TEST c_api/sg_random_walks_test.c) ConfigureCTest(CAPI_TRIANGLE_COUNT_TEST c_api/triangle_count_test.c) ConfigureCTest(CAPI_LOUVAIN_TEST c_api/louvain_test.c) ConfigureCTest(CAPI_LEIDEN_TEST c_api/leiden_test.c) +ConfigureCTest(CAPI_ECG_TEST c_api/ecg_test.c) ############################################################################# # Skipping due to CUDA 12.2 failure that traces back to RAFT # # TODO: Uncomment this once the issue is fixed. # diff --git a/cpp/tests/c_api/ecg_test.c b/cpp/tests/c_api/ecg_test.c new file mode 100644 index 00000000000..bd759ecae74 --- /dev/null +++ b/cpp/tests/c_api/ecg_test.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2022-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. + */ + +#include "c_test_utils.h" /* RUN_TEST */ + +#include +#include + +#include + +typedef int32_t vertex_t; +typedef int32_t edge_t; +typedef float weight_t; + +int generic_ecg_test(vertex_t* h_src, + vertex_t* h_dst, + weight_t* h_wgt, + size_t num_vertices, + size_t num_edges, + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution, + bool_t store_transposed) +{ + int test_ret_value = 0; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + + cugraph_resource_handle_t* handle = NULL; + cugraph_graph_t* graph = NULL; + cugraph_hierarchical_clustering_result_t* result = NULL; + + data_type_id_t vertex_tid = INT32; + data_type_id_t edge_tid = INT32; + data_type_id_t weight_tid = FLOAT32; + data_type_id_t edge_id_tid = INT32; + data_type_id_t edge_type_tid = INT32; + + handle = cugraph_create_resource_handle(NULL); + TEST_ASSERT(test_ret_value, handle != NULL, "resource handle creation failed."); + + ret_code = create_sg_test_graph(handle, + vertex_tid, + edge_tid, + h_src, + h_dst, + weight_tid, + h_wgt, + edge_type_tid, + NULL, + edge_id_tid, + NULL, + num_edges, + store_transposed, + FALSE, + FALSE, + FALSE, + &graph, + &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "create_test_graph failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + cugraph_rng_state_t* rng_state; + ret_code = cugraph_rng_state_create(handle, 0, &rng_state, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "rng_state create failed."); + + ret_code = cugraph_ecg(handle, + rng_state, + graph, + min_weight, + ensemble_size, + max_level, + threshold, + resolution, + FALSE, + &result, + &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "cugraph_ecg failed."); + + if (test_ret_value == 0) { + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* clusters; + + vertices = cugraph_hierarchical_clustering_result_get_vertices(result); + clusters = cugraph_hierarchical_clustering_result_get_clusters(result); + double modularity = cugraph_hierarchical_clustering_result_get_modularity(result); + + vertex_t h_vertices[num_vertices]; + edge_t h_clusters[num_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_vertices, vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_clusters, clusters, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + // Louvain and permute_range are both tested, here we only make + // sure that function calls succeed as expected. + + cugraph_hierarchical_clustering_result_free(result); + } + + cugraph_sg_graph_free(graph); + cugraph_free_resource_handle(handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_ecg() +{ + size_t num_edges = 16; + size_t num_vertices = 6; + size_t max_level = 10; + weight_t threshold = 1e-7; + weight_t resolution = 1.0; + weight_t min_weight = 0.001; + size_t ensemble_size = 10; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; + weight_t h_wgt[] = { + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + + // Louvain wants store_transposed = FALSE + return generic_ecg_test(h_src, + h_dst, + h_wgt, + num_vertices, + num_edges, + min_weight, + ensemble_size, + max_level, + threshold, + resolution, + FALSE); +} + +int test_ecg_no_weight() +{ + size_t num_edges = 16; + size_t num_vertices = 6; + size_t max_level = 10; + weight_t threshold = 1e-7; + weight_t resolution = 1.0; + weight_t min_weight = 0.001; + size_t ensemble_size = 10; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; + + // Louvain wants store_transposed = FALSE + return generic_ecg_test(h_src, + h_dst, + NULL, + num_vertices, + num_edges, + min_weight, + ensemble_size, + max_level, + threshold, + resolution, + FALSE); +} + +/******************************************************************************/ + +int main(int argc, char** argv) +{ + int result = 0; + result |= RUN_TEST(test_ecg); + result |= RUN_TEST(test_ecg_no_weight); + return result; +} \ No newline at end of file diff --git a/cpp/tests/c_api/legacy_ecg_test.c b/cpp/tests/c_api/legacy_ecg_test.c index 5ea1ce79796..b702426b0aa 100644 --- a/cpp/tests/c_api/legacy_ecg_test.c +++ b/cpp/tests/c_api/legacy_ecg_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-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. @@ -53,8 +53,8 @@ int generic_ecg_test(vertex_t* h_src, TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "create_test_graph failed."); TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); - ret_code = - cugraph_ecg(p_handle, p_graph, minimum_weight, ensemble_size, FALSE, &p_result, &ret_error); + ret_code = cugraph_legacy_ecg( + p_handle, p_graph, minimum_weight, ensemble_size, FALSE, &p_result, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "cugraph_ecg failed."); @@ -63,8 +63,8 @@ int generic_ecg_test(vertex_t* h_src, cugraph_type_erased_device_array_view_t* vertices; cugraph_type_erased_device_array_view_t* clusters; - vertices = cugraph_hierarchical_clustering_result_get_vertices(p_result); - clusters = cugraph_hierarchical_clustering_result_get_clusters(p_result); + vertices = cugraph_hierarchical_clustering_result_get_vertices(p_result); + clusters = cugraph_hierarchical_clustering_result_get_clusters(p_result); vertex_t h_vertices[num_vertices]; edge_t h_clusters[num_vertices]; @@ -103,18 +103,11 @@ int test_ecg() vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; weight_t h_wgt[] = { 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_result[] = {0, 1, 0, 1, 1, 1}; + vertex_t h_result[] = {0, 1, 0, 1, 1, 1}; // Louvain wants store_transposed = FALSE - return generic_ecg_test(h_src, - h_dst, - h_wgt, - h_result, - num_vertices, - num_edges, - min_weight, - ensemble_size, - FALSE); + return generic_ecg_test( + h_src, h_dst, h_wgt, h_result, num_vertices, num_edges, min_weight, ensemble_size, FALSE); } /******************************************************************************/ diff --git a/cpp/tests/c_api/mg_ecg_test.c b/cpp/tests/c_api/mg_ecg_test.c new file mode 100644 index 00000000000..846be8125e0 --- /dev/null +++ b/cpp/tests/c_api/mg_ecg_test.c @@ -0,0 +1,181 @@ +/* + * 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. + */ + +#include "mg_test_utils.h" /* RUN_TEST */ + +#include +#include + +#include + +typedef int32_t vertex_t; +typedef int32_t edge_t; +typedef float weight_t; + +int generic_ecg_test(const cugraph_resource_handle_t* handle, + vertex_t* h_src, + vertex_t* h_dst, + weight_t* h_wgt, + size_t num_vertices, + size_t num_edges, + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution, + bool_t store_transposed) +{ + int test_ret_value = 0; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + + cugraph_graph_t* graph = NULL; + cugraph_hierarchical_clustering_result_t* result = NULL; + + int rank = cugraph_resource_handle_get_rank(handle); + + ret_code = create_mg_test_graph( + handle, h_src, h_dst, h_wgt, num_edges, store_transposed, FALSE, &graph, &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "create_test_graph failed."); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + cugraph_rng_state_t* rng_state; + ret_code = cugraph_rng_state_create(handle, rank, &rng_state, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "rng_state create failed."); + + ret_code = cugraph_ecg(handle, + rng_state, + graph, + min_weight, + ensemble_size, + max_level, + threshold, + resolution, + FALSE, + &result, + &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "cugraph_ecg failed."); + + if (test_ret_value == 0) { + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* clusters; + + vertices = cugraph_hierarchical_clustering_result_get_vertices(result); + clusters = cugraph_hierarchical_clustering_result_get_clusters(result); + double modularity = cugraph_hierarchical_clustering_result_get_modularity(result); + + vertex_t h_vertices[num_vertices]; + edge_t h_clusters[num_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_vertices, vertices, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_clusters, clusters, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + size_t num_local_vertices = cugraph_type_erased_device_array_view_size(vertices); + + // Louvain and permute_range are both tested, here we only make + // sure that function calls succeed as expected. + + cugraph_hierarchical_clustering_result_free(result); + } + + cugraph_mg_graph_free(graph); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_ecg(const cugraph_resource_handle_t* handle) +{ + size_t num_edges = 16; + size_t num_vertices = 6; + size_t max_level = 10; + weight_t threshold = 1e-7; + weight_t resolution = 1.0; + weight_t min_weight = 0.001; + size_t ensemble_size = 10; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; + weight_t h_wgt[] = { + 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + + // Louvain wants store_transposed = FALSE + return generic_ecg_test(handle, + h_src, + h_dst, + h_wgt, + num_vertices, + num_edges, + min_weight, + ensemble_size, + max_level, + threshold, + resolution, + FALSE); +} + +int test_ecg_no_weight(const cugraph_resource_handle_t* handle) +{ + size_t num_edges = 16; + size_t num_vertices = 6; + size_t max_level = 10; + weight_t threshold = 1e-7; + weight_t resolution = 1.0; + weight_t min_weight = 0.001; + size_t ensemble_size = 10; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; + + // Louvain wants store_transposed = FALSE + return generic_ecg_test(handle, + h_src, + h_dst, + NULL, + num_vertices, + num_edges, + min_weight, + ensemble_size, + max_level, + threshold, + resolution, + FALSE); +} + +/******************************************************************************/ + +int main(int argc, char** argv) +{ + void* raft_handle = create_mg_raft_handle(argc, argv); + cugraph_resource_handle_t* handle = cugraph_create_resource_handle(raft_handle); + + int result = 0; + result |= RUN_MG_TEST(test_ecg, handle); + + cugraph_free_resource_handle(handle); + free_mg_raft_handle(raft_handle); + + return result; +} \ No newline at end of file diff --git a/cpp/tests/community/ecg_test.cpp b/cpp/tests/community/legacy_ecg_test.cpp similarity index 98% rename from cpp/tests/community/ecg_test.cpp rename to cpp/tests/community/legacy_ecg_test.cpp index 66950b97787..c061215415a 100644 --- a/cpp/tests/community/ecg_test.cpp +++ b/cpp/tests/community/legacy_ecg_test.cpp @@ -8,8 +8,6 @@ * license agreement from NVIDIA CORPORATION is strictly prohibited. * */ -#include "utilities/base_fixture.hpp" - #include #include @@ -17,12 +15,14 @@ #include +#include + // FIXME: Temporarily disable this test. Something is wrong with // ECG, or the expectation of this test. If I run ensemble size // of 24 this fails. It also fails with the SG Louvain change // for PR 1271 #if 0 -TEST(ecg, success) +TEST(legacy_ecg, success) { // FIXME: verify that this is the karate dataset std::vector off_h = {0, 16, 25, 35, 41, 44, 48, 52, 56, 61, 63, 66, @@ -77,7 +77,7 @@ TEST(ecg, success) } #endif -TEST(ecg, dolphin) +TEST(legacy_ecg, dolphin) { raft::handle_t handle; diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd index 3c273b7d3fa..b7729bfba06 100644 --- a/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/community_algorithms.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2023, NVIDIA CORPORATION. +# Copyright (c) 2022-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 @@ -153,13 +153,29 @@ cdef extern from "cugraph_c/community_algorithms.h": cugraph_error_t** error ) ########################################################################### + # Legacy ECG + cdef cugraph_error_code_t \ + cugraph_legacy_ecg( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + double min_weight, + size_t ensemble_size, + bool_t do_expensive_check, + cugraph_hierarchical_clustering_result_t** result, + cugraph_error_t** error + ) + # ECG cdef cugraph_error_code_t \ cugraph_ecg( const cugraph_resource_handle_t* handle, + cugraph_rng_state_t* rng_state, cugraph_graph_t* graph, double min_weight, size_t ensemble_size, + size_t max_level, + double threshold, + double resolution, bool_t do_expensive_check, cugraph_hierarchical_clustering_result_t** result, cugraph_error_t** error diff --git a/python/pylibcugraph/pylibcugraph/ecg.pyx b/python/pylibcugraph/pylibcugraph/ecg.pyx index 4188aaa213e..9e414fccb0f 100644 --- a/python/pylibcugraph/pylibcugraph/ecg.pyx +++ b/python/pylibcugraph/pylibcugraph/ecg.pyx @@ -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 @@ -31,6 +31,7 @@ from pylibcugraph._cugraph_c.graph cimport ( ) from pylibcugraph._cugraph_c.community_algorithms cimport ( cugraph_hierarchical_clustering_result_t, + cugraph_legacy_ecg, cugraph_ecg, cugraph_hierarchical_clustering_result_get_vertices, cugraph_hierarchical_clustering_result_get_clusters, @@ -124,7 +125,7 @@ def ecg(ResourceHandle resource_handle, cdef cugraph_error_code_t error_code cdef cugraph_error_t* error_ptr - error_code = cugraph_ecg(c_resource_handle_ptr, + error_code = cugraph_legacy_ecg(c_resource_handle_ptr, c_graph_ptr, min_weight, ensemble_size, From 92daf6e6189a51a0ed4c9ed741f37ec82cb0d4e2 Mon Sep 17 00:00:00 2001 From: Ralph Liu <137829296+nv-rliu@users.noreply.github.com> Date: Thu, 7 Mar 2024 08:42:53 -0500 Subject: [PATCH 6/6] Add `DeprecationWarning` to `batched_ego_graphs` (#4209) This PR addresses #4191 Since the plan is to add "batched" support to the regular `ego_graph` method, a `DeprecationWarning` has been added to `batched_ego_graphs`. -- Minor change: use proper indexing when accessing a pd.Series value. Authors: - Ralph Liu (https://github.com/nv-rliu) Approvers: - Rick Ratzel (https://github.com/rlratzel) URL: https://github.com/rapidsai/cugraph/pull/4209 --- python/cugraph/cugraph/community/egonet.py | 20 ++++++++++++++----- .../cugraph/cugraph/dask/community/egonet.py | 6 +++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/python/cugraph/cugraph/community/egonet.py b/python/cugraph/cugraph/community/egonet.py index 01bbc41d8cd..b7341ca3bae 100644 --- a/python/cugraph/cugraph/community/egonet.py +++ b/python/cugraph/cugraph/community/egonet.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2023, NVIDIA CORPORATION. +# Copyright (c) 2021-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 @@ -11,18 +11,18 @@ # See the License for the specific language governing permissions and # limitations under the License. + +import warnings + +import cudf from cugraph.utilities import ( ensure_cugraph_obj, is_nx_graph_type, ) from cugraph.utilities import cugraph_to_nx -import cudf - from pylibcugraph import ego_graph as pylibcugraph_ego_graph - from pylibcugraph import ResourceHandle -import warnings def _convert_graph_to_output_type(G, input_type): @@ -49,6 +49,7 @@ def _convert_df_series_to_output_type(df, offsets, input_type): return df, offsets +# TODO: add support for a 'batch-mode' option. def ego_graph(G, n, radius=1, center=True, undirected=None, distance=None): """ Compute the induced subgraph of neighbors centered at node n, @@ -118,6 +119,7 @@ def ego_graph(G, n, radius=1, center=True, undirected=None, distance=None): # Match the seed to the vertex dtype n_type = G.edgelist.edgelist_df["src"].dtype + # FIXME: 'n' should represent a single vertex, but is not being verified n = n.astype(n_type) do_expensive_check = False @@ -154,6 +156,11 @@ def ego_graph(G, n, radius=1, center=True, undirected=None, distance=None): def batched_ego_graphs(G, seeds, radius=1, center=True, undirected=None, distance=None): """ + This function is deprecated. + + Deprecated since 24.04. Batched support for multiple seeds will be added + to `ego_graph`. + Compute the induced subgraph of neighbors for each node in seeds within a given radius. @@ -196,6 +203,9 @@ def batched_ego_graphs(G, seeds, radius=1, center=True, undirected=None, distanc ... radius=2) """ + warning_msg = "This function is deprecated. Batched support for multiple vertices \ + will be added to `ego_graph`" + warnings.warn(warning_msg, DeprecationWarning) (G, input_type) = ensure_cugraph_obj(G, nx_weight_attr="weight") diff --git a/python/cugraph/cugraph/dask/community/egonet.py b/python/cugraph/cugraph/dask/community/egonet.py index e49d4777cef..e4282786653 100644 --- a/python/cugraph/cugraph/dask/community/egonet.py +++ b/python/cugraph/cugraph/dask/community/egonet.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2023, NVIDIA CORPORATION. +# Copyright (c) 2022-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. @@ -129,9 +129,9 @@ def ego_graph(input_graph, n, radius=1, center=True): # renumbered, the node ID must also be renumbered. if input_graph.renumbered: n = input_graph.lookup_internal_vertex_id(n) - n_type = input_graph.edgelist.edgelist_df.dtypes[0] + n_type = input_graph.edgelist.edgelist_df.dtypes.iloc[0] else: - n_type = input_graph.input_df.dtypes[0] + n_type = input_graph.input_df.dtypes.iloc[0] if isinstance(n, (cudf.Series, cudf.DataFrame)): n = dask_cudf.from_cudf(n, npartitions=min(input_graph._npartitions, len(n)))