From 9e943cd7f12d09748a00a031c98e10a95aca97b0 Mon Sep 17 00:00:00 2001 From: Naim Date: Mon, 26 Feb 2024 23:11:49 +0100 Subject: [PATCH 1/9] C API for MNMG ECG --- 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 | 31 +-- cpp/tests/CMakeLists.txt | 6 +- cpp/tests/c_api/ecg_test.c | 211 ++++++++++++++++++ cpp/tests/c_api/legacy_ecg_test.c | 23 +- cpp/tests/c_api/mg_ecg_test.c | 168 ++++++++++++++ .../{ecg_test.cpp => legacy_ecg_test.cpp} | 6 +- .../_cugraph_c/community_algorithms.pxd | 18 +- python/pylibcugraph/pylibcugraph/ecg.pyx | 5 +- 11 files changed, 627 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..cb7109bbed2 --- /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 b8f83734c69..82d66076c60 100644 --- a/cpp/src/c_api/legacy_ecg.cpp +++ b/cpp/src/c_api/legacy_ecg.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -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..1b285783b49 --- /dev/null +++ b/cpp/tests/c_api/ecg_test.c @@ -0,0 +1,211 @@ +/* + * 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, + vertex_t* h_result, + weight_t expected_modularity, + 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."); + + for (int i = 0; (i < num_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT( + test_ret_value, h_result[h_vertices[i]] == h_clusters[i], "cluster results don't match"); + } + + TEST_ASSERT(test_ret_value, + nearlyEqual(modularity, expected_modularity, 0.001), + "modularity doesn't match"); + + 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}; + vertex_t h_result[] = {0, 0, 0, 1, 1, 1}; + weight_t expected_modularity = 0.215969; + + // Louvain wants store_transposed = FALSE + return generic_ecg_test(h_src, + h_dst, + h_wgt, + h_result, + expected_modularity, + 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}; + vertex_t h_result[] = {1, 1, 1, 2, 0, 0}; + weight_t expected_modularity = 0.0859375; + + // Louvain wants store_transposed = FALSE + return generic_ecg_test(h_src, + h_dst, + NULL, + h_result, + expected_modularity, + 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..455ef80c13a --- /dev/null +++ b/cpp/tests/c_api/mg_ecg_test.c @@ -0,0 +1,168 @@ +/* + * 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 "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, + vertex_t* h_result, + 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); + + 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); + + vertex_t max_component_id = -1; + for (vertex_t i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { + if (h_clusters[i] > max_component_id) max_component_id = h_clusters[i]; + } + + vertex_t component_mapping[max_component_id + 1]; + for (vertex_t i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { + component_mapping[h_clusters[i]] = h_result[h_vertices[i]]; + } + + for (vertex_t i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + h_result[h_vertices[i]] == component_mapping[h_clusters[i]], + "cluster results don't match"); + } + + 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 = 8; + 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}; + vertex_t h_result[] = {1, 0, 1, 0, 0, 0}; + + // Louvain wants store_transposed = FALSE + return generic_ecg_test(handle, + h_src, + h_dst, + h_wgt, + h_result, + 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 262e2bd23af..4d5d8774a61 100644 --- a/cpp/tests/community/ecg_test.cpp +++ b/cpp/tests/community/legacy_ecg_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2024, NVIDIA CORPORATION. All rights reserved. * * NVIDIA CORPORATION and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation @@ -22,7 +22,7 @@ // 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 98cee663b0a594b54f4c751a062a2995b0012539 Mon Sep 17 00:00:00 2001 From: Naim Date: Mon, 26 Feb 2024 23:39:36 +0100 Subject: [PATCH 2/9] Update ECG test code --- cpp/tests/c_api/ecg_test.c | 10 ++-------- cpp/tests/c_api/mg_ecg_test.c | 17 ++--------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/cpp/tests/c_api/ecg_test.c b/cpp/tests/c_api/ecg_test.c index 1b285783b49..5a8db8f4a7c 100644 --- a/cpp/tests/c_api/ecg_test.c +++ b/cpp/tests/c_api/ecg_test.c @@ -117,14 +117,8 @@ int generic_ecg_test(vertex_t* h_src, handle, (byte_t*)h_clusters, clusters, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); - for (int i = 0; (i < num_vertices) && (test_ret_value == 0); ++i) { - TEST_ASSERT( - test_ret_value, h_result[h_vertices[i]] == h_clusters[i], "cluster results don't match"); - } - - TEST_ASSERT(test_ret_value, - nearlyEqual(modularity, expected_modularity, 0.001), - "modularity doesn't match"); + // Louvain and permute_range are both tested, here we only make + // sure that function calls succeed as expected. cugraph_hierarchical_clustering_result_free(result); } diff --git a/cpp/tests/c_api/mg_ecg_test.c b/cpp/tests/c_api/mg_ecg_test.c index 455ef80c13a..ea71d40591d 100644 --- a/cpp/tests/c_api/mg_ecg_test.c +++ b/cpp/tests/c_api/mg_ecg_test.c @@ -94,21 +94,8 @@ int generic_ecg_test(const cugraph_resource_handle_t* handle, size_t num_local_vertices = cugraph_type_erased_device_array_view_size(vertices); - vertex_t max_component_id = -1; - for (vertex_t i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { - if (h_clusters[i] > max_component_id) max_component_id = h_clusters[i]; - } - - vertex_t component_mapping[max_component_id + 1]; - for (vertex_t i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { - component_mapping[h_clusters[i]] = h_result[h_vertices[i]]; - } - - for (vertex_t i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { - TEST_ASSERT(test_ret_value, - h_result[h_vertices[i]] == component_mapping[h_clusters[i]], - "cluster results don't match"); - } + // Louvain and permute_range are both tested, here we only make + // sure that function calls succeed as expected. cugraph_hierarchical_clustering_result_free(result); } From cf5c8a0a1f8d90928800373d7482b3f0c041f841 Mon Sep 17 00:00:00 2001 From: Naim Date: Mon, 26 Feb 2024 23:41:05 +0100 Subject: [PATCH 3/9] Fix copyright --- cpp/tests/c_api/mg_ecg_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/c_api/mg_ecg_test.c b/cpp/tests/c_api/mg_ecg_test.c index ea71d40591d..66c48e904b3 100644 --- a/cpp/tests/c_api/mg_ecg_test.c +++ b/cpp/tests/c_api/mg_ecg_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2024, NVIDIA CORPORATION. + * 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. From 86b0faa98e4a329ac5df357a99b8a61aa238d7cc Mon Sep 17 00:00:00 2001 From: Naim Date: Thu, 29 Feb 2024 13:27:08 +0100 Subject: [PATCH 4/9] Update test functions to remove unused parameters --- cpp/tests/c_api/ecg_test.c | 14 ++------------ cpp/tests/c_api/mg_ecg_test.c | 10 ++++------ 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/cpp/tests/c_api/ecg_test.c b/cpp/tests/c_api/ecg_test.c index 5a8db8f4a7c..bd759ecae74 100644 --- a/cpp/tests/c_api/ecg_test.c +++ b/cpp/tests/c_api/ecg_test.c @@ -28,8 +28,6 @@ typedef float weight_t; int generic_ecg_test(vertex_t* h_src, vertex_t* h_dst, weight_t* h_wgt, - vertex_t* h_result, - weight_t expected_modularity, size_t num_vertices, size_t num_edges, double min_weight, @@ -144,15 +142,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, 0, 0, 1, 1, 1}; - weight_t expected_modularity = 0.215969; // Louvain wants store_transposed = FALSE return generic_ecg_test(h_src, h_dst, h_wgt, - h_result, - expected_modularity, num_vertices, num_edges, min_weight, @@ -173,17 +167,13 @@ int test_ecg_no_weight() 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}; - vertex_t h_result[] = {1, 1, 1, 2, 0, 0}; - weight_t expected_modularity = 0.0859375; + 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, - h_result, - expected_modularity, num_vertices, num_edges, min_weight, diff --git a/cpp/tests/c_api/mg_ecg_test.c b/cpp/tests/c_api/mg_ecg_test.c index 66c48e904b3..ef9f9815298 100644 --- a/cpp/tests/c_api/mg_ecg_test.c +++ b/cpp/tests/c_api/mg_ecg_test.c @@ -29,7 +29,6 @@ int generic_ecg_test(const cugraph_resource_handle_t* handle, vertex_t* h_src, vertex_t* h_dst, weight_t* h_wgt, - vertex_t* h_result, size_t num_vertices, size_t num_edges, double min_weight, @@ -78,8 +77,9 @@ int generic_ecg_test(const cugraph_resource_handle_t* handle, 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); + 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]; @@ -108,7 +108,7 @@ int generic_ecg_test(const cugraph_resource_handle_t* handle, int test_ecg(const cugraph_resource_handle_t* handle) { - size_t num_edges = 8; + size_t num_edges = 16; size_t num_vertices = 6; size_t max_level = 10; weight_t threshold = 1e-7; @@ -120,14 +120,12 @@ int test_ecg(const cugraph_resource_handle_t* handle) 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[] = {1, 0, 1, 0, 0, 0}; // Louvain wants store_transposed = FALSE return generic_ecg_test(handle, h_src, h_dst, h_wgt, - h_result, num_vertices, num_edges, min_weight, From cecac326a6c7fc1551182f4d4b372ef101368232 Mon Sep 17 00:00:00 2001 From: Naim Date: Thu, 29 Feb 2024 13:36:31 +0100 Subject: [PATCH 5/9] Update mg capi ecg test --- cpp/tests/c_api/mg_ecg_test.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/cpp/tests/c_api/mg_ecg_test.c b/cpp/tests/c_api/mg_ecg_test.c index ef9f9815298..846be8125e0 100644 --- a/cpp/tests/c_api/mg_ecg_test.c +++ b/cpp/tests/c_api/mg_ecg_test.c @@ -136,6 +136,34 @@ int test_ecg(const cugraph_resource_handle_t* handle) 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) From f9e97efb34f963c03ab87507c9080c92affd35d5 Mon Sep 17 00:00:00 2001 From: Naim Date: Thu, 29 Feb 2024 22:25:27 +0100 Subject: [PATCH 6/9] Remove merge conflict --- cpp/tests/community/ecg_test.cpp | 149 +++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 cpp/tests/community/ecg_test.cpp diff --git a/cpp/tests/community/ecg_test.cpp b/cpp/tests/community/ecg_test.cpp new file mode 100644 index 00000000000..4d5d8774a61 --- /dev/null +++ b/cpp/tests/community/ecg_test.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019-2024, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA CORPORATION and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA CORPORATION is strictly prohibited. + * + */ +#include + +#include +#include + +#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(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, + 67, 69, 74, 76, 78, 80, 82, 84, 87, 89, 91, 93, + 98, 101, 104, 106, 110, 113, 117, 121, 127, 139, 156}; + std::vector ind_h = { + 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 17, 19, 21, 31, 0, 2, 3, 7, 13, 17, 19, + 21, 30, 0, 1, 3, 7, 8, 9, 13, 27, 28, 32, 0, 1, 2, 7, 12, 13, 0, 6, 10, 0, 6, + 10, 16, 0, 4, 5, 16, 0, 1, 2, 3, 0, 2, 30, 32, 33, 2, 33, 0, 4, 5, 0, 0, 3, + 0, 1, 2, 3, 33, 32, 33, 32, 33, 5, 6, 0, 1, 32, 33, 0, 1, 33, 32, 33, 0, 1, 32, + 33, 25, 27, 29, 32, 33, 25, 27, 31, 23, 24, 31, 29, 33, 2, 23, 24, 33, 2, 31, 33, 23, 26, + 32, 33, 1, 8, 32, 33, 0, 24, 25, 28, 32, 33, 2, 8, 14, 15, 18, 20, 22, 23, 29, 30, 31, + 33, 8, 9, 13, 14, 15, 18, 19, 20, 22, 23, 26, 27, 28, 29, 30, 31, 32}; + + std::vector w_h(ind_h.size(), float{1.0}); + + int num_verts = off_h.size() - 1; + int num_edges = ind_h.size(); + + thrust::host_vector cluster_id(num_verts, -1); + + rmm::device_vector offsets_v(off_h); + rmm::device_vector indices_v(ind_h); + rmm::device_vector weights_v(w_h); + rmm::device_vector result_v(cluster_id); + + cugraph::legacy::GraphCSRView graph_csr( + offsets_v.data().get(), indices_v.data().get(), weights_v.data().get(), num_verts, num_edges); + + raft::handle_t handle; + cugraph::ecg(handle, graph_csr, .05, 16, result_v.data().get()); + + cluster_id = result_v; + int max = *max_element(cluster_id.begin(), cluster_id.end()); + int min = *min_element(cluster_id.begin(), cluster_id.end()); + + ASSERT_EQ((min >= 0), 1); + + std::set cluster_ids; + for (auto c : cluster_id) { cluster_ids.insert(c); } + + ASSERT_EQ(cluster_ids.size(), size_t(max + 1)); + + float modularity{0.0}; + + cugraph::ext_raft::analyzeClustering_modularity( + graph_csr, max + 1, result_v.data().get(), &modularity); + + // 0.399 is 5% below the reference value returned in + // /python/utils/ECG_Golden.ipynb on the same dataset + ASSERT_GT(modularity, 0.399); +} +#endif + +TEST(legacy_ecg, dolphin) +{ + raft::handle_t handle; + + auto stream = handle.get_stream(); + + std::vector off_h = {0, 6, 14, 18, 21, 22, 26, 32, 37, 43, 50, 55, 56, + 57, 65, 77, 84, 90, 99, 106, 110, 119, 125, 126, 129, 135, + 138, 141, 146, 151, 160, 165, 166, 169, 179, 184, 185, 192, 203, + 211, 213, 221, 226, 232, 239, 243, 254, 256, 262, 263, 265, 272, + 282, 286, 288, 295, 297, 299, 308, 309, 314, 315, 318}; + std::vector ind_h = { + 10, 14, 15, 40, 42, 47, 17, 19, 26, 27, 28, 36, 41, 54, 10, 42, 44, 61, 8, 14, 59, 51, 9, + 13, 56, 57, 9, 13, 17, 54, 56, 57, 19, 27, 30, 40, 54, 3, 20, 28, 37, 45, 59, 5, 6, 13, + 17, 32, 41, 57, 0, 2, 29, 42, 47, 51, 33, 5, 6, 9, 17, 32, 41, 54, 57, 0, 3, 16, 24, + 33, 34, 37, 38, 40, 43, 50, 52, 0, 18, 24, 40, 45, 55, 59, 14, 20, 33, 37, 38, 50, 1, 6, + 9, 13, 22, 25, 27, 31, 57, 15, 20, 21, 24, 29, 45, 51, 1, 7, 30, 54, 8, 16, 18, 28, 36, + 38, 44, 47, 50, 18, 29, 33, 37, 45, 51, 17, 36, 45, 51, 14, 15, 18, 29, 45, 51, 17, 26, 27, + 1, 25, 27, 1, 7, 17, 25, 26, 1, 8, 20, 30, 47, 10, 18, 21, 24, 35, 43, 45, 51, 52, 7, + 19, 28, 42, 47, 17, 9, 13, 60, 12, 14, 16, 21, 34, 37, 38, 40, 43, 50, 14, 33, 37, 44, 49, + 29, 1, 20, 23, 37, 39, 40, 59, 8, 14, 16, 21, 33, 34, 36, 40, 43, 45, 61, 14, 16, 20, 33, + 43, 44, 52, 58, 36, 57, 0, 7, 14, 15, 33, 36, 37, 52, 1, 9, 13, 54, 57, 0, 2, 10, 30, + 47, 50, 14, 29, 33, 37, 38, 46, 53, 2, 20, 34, 38, 8, 15, 18, 21, 23, 24, 29, 37, 50, 51, + 59, 43, 49, 0, 10, 20, 28, 30, 42, 57, 34, 46, 14, 16, 20, 33, 42, 45, 51, 4, 11, 18, 21, + 23, 24, 29, 45, 50, 55, 14, 29, 38, 40, 43, 61, 1, 6, 7, 13, 19, 41, 57, 15, 51, 5, 6, + 5, 6, 9, 13, 17, 39, 41, 48, 54, 38, 3, 8, 15, 36, 45, 32, 2, 37, 53}; + + std::vector w_h(ind_h.size(), float{1.0}); + + int num_verts = off_h.size() - 1; + int num_edges = ind_h.size(); + + rmm::device_uvector offsets_v(num_verts + 1, stream); + rmm::device_uvector indices_v(num_edges, stream); + rmm::device_uvector weights_v(num_edges, stream); + rmm::device_uvector result_v(num_verts, stream); + + raft::update_device(offsets_v.data(), off_h.data(), off_h.size(), stream); + raft::update_device(indices_v.data(), ind_h.data(), ind_h.size(), stream); + raft::update_device(weights_v.data(), w_h.data(), w_h.size(), stream); + + cugraph::legacy::GraphCSRView graph_csr( + offsets_v.data(), indices_v.data(), weights_v.data(), num_verts, num_edges); + + cugraph::ecg(handle, graph_csr, .05, 16, result_v.data()); + + auto cluster_id = cugraph::test::to_host(handle, result_v); + + int max = *max_element(cluster_id.begin(), cluster_id.end()); + int min = *min_element(cluster_id.begin(), cluster_id.end()); + + ASSERT_EQ((min >= 0), 1); + + std::set cluster_ids; + for (auto c : cluster_id) { + cluster_ids.insert(c); + } + + ASSERT_EQ(cluster_ids.size(), size_t(max + 1)); + + float modularity{0.0}; + + cugraph::ext_raft::analyzeClustering_modularity(graph_csr, max + 1, result_v.data(), &modularity); + + float random_modularity{0.95 * 0.4962422251701355}; + + ASSERT_GT(modularity, random_modularity); +} + +CUGRAPH_TEST_PROGRAM_MAIN() From 6cbfe44c916a0448c5a55e942ff8e488a7f2b833 Mon Sep 17 00:00:00 2001 From: Naim Date: Thu, 29 Feb 2024 22:34:17 +0100 Subject: [PATCH 7/9] Remove duplicate test file --- cpp/tests/community/ecg_test.cpp | 149 ------------------------------- 1 file changed, 149 deletions(-) delete mode 100644 cpp/tests/community/ecg_test.cpp diff --git a/cpp/tests/community/ecg_test.cpp b/cpp/tests/community/ecg_test.cpp deleted file mode 100644 index d5e5ef8680c..00000000000 --- a/cpp/tests/community/ecg_test.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2019-2024, NVIDIA CORPORATION. All rights reserved. - * - * NVIDIA CORPORATION and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA CORPORATION is strictly prohibited. - * - */ -#include "utilities/base_fixture.hpp" - -#include -#include - -#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(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, - 67, 69, 74, 76, 78, 80, 82, 84, 87, 89, 91, 93, - 98, 101, 104, 106, 110, 113, 117, 121, 127, 139, 156}; - std::vector ind_h = { - 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 17, 19, 21, 31, 0, 2, 3, 7, 13, 17, 19, - 21, 30, 0, 1, 3, 7, 8, 9, 13, 27, 28, 32, 0, 1, 2, 7, 12, 13, 0, 6, 10, 0, 6, - 10, 16, 0, 4, 5, 16, 0, 1, 2, 3, 0, 2, 30, 32, 33, 2, 33, 0, 4, 5, 0, 0, 3, - 0, 1, 2, 3, 33, 32, 33, 32, 33, 5, 6, 0, 1, 32, 33, 0, 1, 33, 32, 33, 0, 1, 32, - 33, 25, 27, 29, 32, 33, 25, 27, 31, 23, 24, 31, 29, 33, 2, 23, 24, 33, 2, 31, 33, 23, 26, - 32, 33, 1, 8, 32, 33, 0, 24, 25, 28, 32, 33, 2, 8, 14, 15, 18, 20, 22, 23, 29, 30, 31, - 33, 8, 9, 13, 14, 15, 18, 19, 20, 22, 23, 26, 27, 28, 29, 30, 31, 32}; - - std::vector w_h(ind_h.size(), float{1.0}); - - int num_verts = off_h.size() - 1; - int num_edges = ind_h.size(); - - thrust::host_vector cluster_id(num_verts, -1); - - rmm::device_vector offsets_v(off_h); - rmm::device_vector indices_v(ind_h); - rmm::device_vector weights_v(w_h); - rmm::device_vector result_v(cluster_id); - - cugraph::legacy::GraphCSRView graph_csr( - offsets_v.data().get(), indices_v.data().get(), weights_v.data().get(), num_verts, num_edges); - - raft::handle_t handle; - cugraph::ecg(handle, graph_csr, .05, 16, result_v.data().get()); - - cluster_id = result_v; - int max = *max_element(cluster_id.begin(), cluster_id.end()); - int min = *min_element(cluster_id.begin(), cluster_id.end()); - - ASSERT_EQ((min >= 0), 1); - - std::set cluster_ids; - for (auto c : cluster_id) { cluster_ids.insert(c); } - - ASSERT_EQ(cluster_ids.size(), size_t(max + 1)); - - float modularity{0.0}; - - cugraph::ext_raft::analyzeClustering_modularity( - graph_csr, max + 1, result_v.data().get(), &modularity); - - // 0.399 is 5% below the reference value returned in - // /python/utils/ECG_Golden.ipynb on the same dataset - ASSERT_GT(modularity, 0.399); -} -#endif - -TEST(legacy_ecg, dolphin) -{ - raft::handle_t handle; - - auto stream = handle.get_stream(); - - std::vector off_h = {0, 6, 14, 18, 21, 22, 26, 32, 37, 43, 50, 55, 56, - 57, 65, 77, 84, 90, 99, 106, 110, 119, 125, 126, 129, 135, - 138, 141, 146, 151, 160, 165, 166, 169, 179, 184, 185, 192, 203, - 211, 213, 221, 226, 232, 239, 243, 254, 256, 262, 263, 265, 272, - 282, 286, 288, 295, 297, 299, 308, 309, 314, 315, 318}; - std::vector ind_h = { - 10, 14, 15, 40, 42, 47, 17, 19, 26, 27, 28, 36, 41, 54, 10, 42, 44, 61, 8, 14, 59, 51, 9, - 13, 56, 57, 9, 13, 17, 54, 56, 57, 19, 27, 30, 40, 54, 3, 20, 28, 37, 45, 59, 5, 6, 13, - 17, 32, 41, 57, 0, 2, 29, 42, 47, 51, 33, 5, 6, 9, 17, 32, 41, 54, 57, 0, 3, 16, 24, - 33, 34, 37, 38, 40, 43, 50, 52, 0, 18, 24, 40, 45, 55, 59, 14, 20, 33, 37, 38, 50, 1, 6, - 9, 13, 22, 25, 27, 31, 57, 15, 20, 21, 24, 29, 45, 51, 1, 7, 30, 54, 8, 16, 18, 28, 36, - 38, 44, 47, 50, 18, 29, 33, 37, 45, 51, 17, 36, 45, 51, 14, 15, 18, 29, 45, 51, 17, 26, 27, - 1, 25, 27, 1, 7, 17, 25, 26, 1, 8, 20, 30, 47, 10, 18, 21, 24, 35, 43, 45, 51, 52, 7, - 19, 28, 42, 47, 17, 9, 13, 60, 12, 14, 16, 21, 34, 37, 38, 40, 43, 50, 14, 33, 37, 44, 49, - 29, 1, 20, 23, 37, 39, 40, 59, 8, 14, 16, 21, 33, 34, 36, 40, 43, 45, 61, 14, 16, 20, 33, - 43, 44, 52, 58, 36, 57, 0, 7, 14, 15, 33, 36, 37, 52, 1, 9, 13, 54, 57, 0, 2, 10, 30, - 47, 50, 14, 29, 33, 37, 38, 46, 53, 2, 20, 34, 38, 8, 15, 18, 21, 23, 24, 29, 37, 50, 51, - 59, 43, 49, 0, 10, 20, 28, 30, 42, 57, 34, 46, 14, 16, 20, 33, 42, 45, 51, 4, 11, 18, 21, - 23, 24, 29, 45, 50, 55, 14, 29, 38, 40, 43, 61, 1, 6, 7, 13, 19, 41, 57, 15, 51, 5, 6, - 5, 6, 9, 13, 17, 39, 41, 48, 54, 38, 3, 8, 15, 36, 45, 32, 2, 37, 53}; - - std::vector w_h(ind_h.size(), float{1.0}); - - int num_verts = off_h.size() - 1; - int num_edges = ind_h.size(); - - rmm::device_uvector offsets_v(num_verts + 1, stream); - rmm::device_uvector indices_v(num_edges, stream); - rmm::device_uvector weights_v(num_edges, stream); - rmm::device_uvector result_v(num_verts, stream); - - raft::update_device(offsets_v.data(), off_h.data(), off_h.size(), stream); - raft::update_device(indices_v.data(), ind_h.data(), ind_h.size(), stream); - raft::update_device(weights_v.data(), w_h.data(), w_h.size(), stream); - - cugraph::legacy::GraphCSRView graph_csr( - offsets_v.data(), indices_v.data(), weights_v.data(), num_verts, num_edges); - - cugraph::ecg(handle, graph_csr, .05, 16, result_v.data()); - - auto cluster_id = cugraph::test::to_host(handle, result_v); - - int max = *max_element(cluster_id.begin(), cluster_id.end()); - int min = *min_element(cluster_id.begin(), cluster_id.end()); - - ASSERT_EQ((min >= 0), 1); - - std::set cluster_ids; - for (auto c : cluster_id) { - cluster_ids.insert(c); - } - - ASSERT_EQ(cluster_ids.size(), size_t(max + 1)); - - float modularity{0.0}; - - cugraph::ext_raft::analyzeClustering_modularity(graph_csr, max + 1, result_v.data(), &modularity); - - float random_modularity{0.95 * 0.4962422251701355}; - - ASSERT_GT(modularity, random_modularity); -} - -CUGRAPH_TEST_PROGRAM_MAIN() From 248ca4e9242dabffabd58277ba81eb960423a3c7 Mon Sep 17 00:00:00 2001 From: Naim Date: Thu, 29 Feb 2024 22:47:57 +0100 Subject: [PATCH 8/9] clang format --- cpp/src/c_api/ecg.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/src/c_api/ecg.cpp b/cpp/src/c_api/ecg.cpp index cb7109bbed2..53e034916b7 100644 --- a/cpp/src/c_api/ecg.cpp +++ b/cpp/src/c_api/ecg.cpp @@ -16,6 +16,11 @@ #include +#include +#include +#include +#include + #include #include #include @@ -24,11 +29,6 @@ #include #include -#include -#include -#include -#include - #include namespace { From 9b284921835c0bbe9e7ffb650a566f9d8f066c0e Mon Sep 17 00:00:00 2001 From: Naim Date: Thu, 29 Feb 2024 23:29:15 +0100 Subject: [PATCH 9/9] clang format --- cpp/tests/community/legacy_ecg_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/community/legacy_ecg_test.cpp b/cpp/tests/community/legacy_ecg_test.cpp index 4d5d8774a61..c061215415a 100644 --- a/cpp/tests/community/legacy_ecg_test.cpp +++ b/cpp/tests/community/legacy_ecg_test.cpp @@ -8,8 +8,6 @@ * license agreement from NVIDIA CORPORATION is strictly prohibited. * */ -#include - #include #include @@ -17,6 +15,8 @@ #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