From 8440d17927851db07e22416f2a1a84d8730c63b8 Mon Sep 17 00:00:00 2001 From: Charles Hastings Date: Fri, 20 Jan 2023 11:59:00 -0800 Subject: [PATCH 1/4] Initial implementation of the Leiden C API --- cpp/CMakeLists.txt | 2 + cpp/include/cugraph_c/community_algorithms.h | 26 +++ .../c_api/heirarchical_clustering_result.cpp | 57 +++++++ .../c_api/heirarchical_clustering_result.hpp | 32 ++++ cpp/src/c_api/leiden.cpp | 126 ++++++++++++++ cpp/src/c_api/louvain.cpp | 55 +----- cpp/tests/CMakeLists.txt | 2 + cpp/tests/c_api/leiden_test.c | 138 +++++++++++++++ cpp/tests/c_api/mg_leiden_test.c | 161 ++++++++++++++++++ 9 files changed, 547 insertions(+), 52 deletions(-) create mode 100644 cpp/src/c_api/heirarchical_clustering_result.cpp create mode 100644 cpp/src/c_api/heirarchical_clustering_result.hpp create mode 100644 cpp/src/c_api/leiden.cpp create mode 100644 cpp/tests/c_api/leiden_test.c create mode 100644 cpp/tests/c_api/mg_leiden_test.c diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 3dd99278d79..630714bfc0f 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -393,6 +393,7 @@ add_library(cugraph_c src/c_api/core_result.cpp src/c_api/extract_ego.cpp src/c_api/k_core.cpp + src/c_api/heirarchical_clustering_result.cpp src/c_api/induced_subgraph.cpp src/c_api/induced_subgraph_helper.cu src/c_api/graph_helper.cu @@ -404,6 +405,7 @@ add_library(cugraph_c src/c_api/random_walks.cpp src/c_api/random.cpp src/c_api/similarity.cpp + src/c_api/leiden.cpp src/c_api/louvain.cpp src/c_api/triangle_count.cpp src/c_api/uniform_neighbor_sampling.cpp diff --git a/cpp/include/cugraph_c/community_algorithms.h b/cpp/include/cugraph_c/community_algorithms.h index 9ec17a20486..65f495c7ff2 100644 --- a/cpp/include/cugraph_c/community_algorithms.h +++ b/cpp/include/cugraph_c/community_algorithms.h @@ -111,6 +111,32 @@ cugraph_error_code_t cugraph_louvain(const cugraph_resource_handle_t* handle, cugraph_heirarchical_clustering_result_t** result, cugraph_error_t** error); +/** + * @brief Compute Leiden + * + * @param [in] handle Handle for accessing resources + * @param [in] graph Pointer to graph. NOTE: Graph might be modified if the storage + * needs to be transposed + * @param [in] max_level Maximum level in hierarchy + * @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_leiden(const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + size_t max_level, + double resolution, + bool_t do_expensive_check, + cugraph_heirarchical_clustering_result_t** result, + cugraph_error_t** error); + /** * @brief Get heirarchical clustering vertices */ diff --git a/cpp/src/c_api/heirarchical_clustering_result.cpp b/cpp/src/c_api/heirarchical_clustering_result.cpp new file mode 100644 index 00000000000..853008f2971 --- /dev/null +++ b/cpp/src/c_api/heirarchical_clustering_result.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022-2023, 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 + +extern "C" cugraph_type_erased_device_array_view_t* +cugraph_heirarchical_clustering_result_get_vertices( + cugraph_heirarchical_clustering_result_t* result) +{ + auto internal_pointer = + reinterpret_cast(result); + return reinterpret_cast( + internal_pointer->vertices_->view()); +} + +extern "C" cugraph_type_erased_device_array_view_t* +cugraph_heirarchical_clustering_result_get_clusters( + cugraph_heirarchical_clustering_result_t* result) +{ + auto internal_pointer = + reinterpret_cast(result); + return reinterpret_cast( + internal_pointer->clusters_->view()); +} + +extern "C" double cugraph_heirarchical_clustering_result_get_modularity( + cugraph_heirarchical_clustering_result_t* result) +{ + auto internal_pointer = + reinterpret_cast(result); + return internal_pointer->modularity; +} + +extern "C" void cugraph_heirarchical_clustering_result_free( + cugraph_heirarchical_clustering_result_t* result) +{ + auto internal_pointer = + reinterpret_cast(result); + delete internal_pointer->vertices_; + delete internal_pointer->clusters_; + delete internal_pointer; +} diff --git a/cpp/src/c_api/heirarchical_clustering_result.hpp b/cpp/src/c_api/heirarchical_clustering_result.hpp new file mode 100644 index 00000000000..4b64dc56f17 --- /dev/null +++ b/cpp/src/c_api/heirarchical_clustering_result.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023, 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. + */ +#pragma once + +#include + +#include + +namespace cugraph { +namespace c_api { + +struct cugraph_heirarchical_clustering_result_t { + double modularity{0}; + cugraph_type_erased_device_array_t* vertices_{nullptr}; + cugraph_type_erased_device_array_t* clusters_{nullptr}; +}; + +} // namespace c_api +} // namespace cugraph diff --git a/cpp/src/c_api/leiden.cpp b/cpp/src/c_api/leiden.cpp new file mode 100644 index 00000000000..f44831ec7a8 --- /dev/null +++ b/cpp/src/c_api/leiden.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023, 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 + +namespace { + +struct leiden_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_graph_t* graph_; + size_t max_level_; + double resolution_; + bool do_expensive_check_; + cugraph::c_api::cugraph_heirarchical_clustering_result_t* result_{}; + + leiden_functor(::cugraph_resource_handle_t const* handle, + ::cugraph_graph_t* graph, + size_t max_level, + double resolution, + bool do_expensive_check) + : abstract_functor(), + handle_(*reinterpret_cast(handle)->handle_), + graph_(reinterpret_cast(graph)), + max_level_(max_level), + resolution_(resolution), + do_expensive_check_(do_expensive_check) + { + } + + template + void operator()() + { + if constexpr (!cugraph::is_candidate::value) { + unsupported(); + } else { + // leiden 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(graph_view.local_vertex_partition_range_size(), + handle_.get_stream()); + +#if 0 + auto [level, modularity] = cugraph::leiden( + handle_, + graph_view, + (edge_weights != nullptr) ? std::make_optional(edge_weights->view()) : std::nullopt, + clusters.data(), + max_level_, + 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_heirarchical_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_)}; +#else + CUGRAPH_FAIL("NOT IMPLEMENTED YET"); +#endif + } + } +}; + +} // namespace + +extern "C" cugraph_error_code_t cugraph_leiden(const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + size_t max_level, + double resolution, + bool_t do_expensive_check, + cugraph_heirarchical_clustering_result_t** result, + cugraph_error_t** error) +{ + leiden_functor functor(handle, graph, max_level, resolution, do_expensive_check); + + return cugraph::c_api::run_algorithm(graph, functor, result, error); +} diff --git a/cpp/src/c_api/louvain.cpp b/cpp/src/c_api/louvain.cpp index 9442966106a..789a3b72fbf 100644 --- a/cpp/src/c_api/louvain.cpp +++ b/cpp/src/c_api/louvain.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,11 @@ * limitations under the License. */ -#include +#include #include #include +#include #include #include @@ -28,18 +29,6 @@ #include -namespace cugraph { -namespace c_api { - -struct cugraph_heirarchical_clustering_result_t { - double modularity{0}; - cugraph_type_erased_device_array_t* vertices_{nullptr}; - cugraph_type_erased_device_array_t* clusters_{nullptr}; -}; - -} // namespace c_api -} // namespace cugraph - namespace { struct louvain_functor : public cugraph::c_api::abstract_functor { @@ -119,44 +108,6 @@ struct louvain_functor : public cugraph::c_api::abstract_functor { } // namespace -extern "C" cugraph_type_erased_device_array_view_t* -cugraph_heirarchical_clustering_result_get_vertices( - cugraph_heirarchical_clustering_result_t* result) -{ - auto internal_pointer = - reinterpret_cast(result); - return reinterpret_cast( - internal_pointer->vertices_->view()); -} - -extern "C" cugraph_type_erased_device_array_view_t* -cugraph_heirarchical_clustering_result_get_clusters( - cugraph_heirarchical_clustering_result_t* result) -{ - auto internal_pointer = - reinterpret_cast(result); - return reinterpret_cast( - internal_pointer->clusters_->view()); -} - -extern "C" double cugraph_heirarchical_clustering_result_get_modularity( - cugraph_heirarchical_clustering_result_t* result) -{ - auto internal_pointer = - reinterpret_cast(result); - return internal_pointer->modularity; -} - -extern "C" void cugraph_heirarchical_clustering_result_free( - cugraph_heirarchical_clustering_result_t* result) -{ - auto internal_pointer = - reinterpret_cast(result); - delete internal_pointer->vertices_; - delete internal_pointer->clusters_; - delete internal_pointer; -} - extern "C" cugraph_error_code_t cugraph_louvain(const cugraph_resource_handle_t* handle, cugraph_graph_t* graph, size_t max_level, diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 621a14ce8c7..19b2bbb54d9 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -595,6 +595,7 @@ if(BUILD_CUGRAPH_MG_TESTS) ConfigureCTestMG(MG_CAPI_RANDOM_WALKS_TEST c_api/mg_random_walks_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_TRIANGLE_COUNT_TEST c_api/mg_triangle_count_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_LOUVAIN_TEST c_api/mg_louvain_test.c c_api/mg_test_utils.cpp) + ConfigureCTestMG(MG_CAPI_LEIDEN_TEST c_api/mg_leiden_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_CORE_NUMBER_TEST c_api/mg_core_number_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_SIMILARITY_TEST c_api/mg_similarity_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_K_CORE_TEST c_api/mg_k_core_test.c c_api/mg_test_utils.cpp) @@ -651,6 +652,7 @@ ConfigureCTest(CAPI_UNIFORM_NEIGHBOR_SAMPLE_TEST c_api/uniform_neighbor_sample_t 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_CORE_NUMBER_TEST c_api/core_number_test.c) ConfigureCTest(CAPI_SIMILARITY_TEST c_api/similarity_test.c) ConfigureCTest(CAPI_K_CORE_TEST c_api/k_core_test.c) diff --git a/cpp/tests/c_api/leiden_test.c b/cpp/tests/c_api/leiden_test.c new file mode 100644 index 00000000000..6378fe16d12 --- /dev/null +++ b/cpp/tests/c_api/leiden_test.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2022, 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_leiden_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, + size_t max_level, + 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* p_handle = NULL; + cugraph_graph_t* p_graph = NULL; + cugraph_heirarchical_clustering_result_t* p_result = NULL; + + p_handle = cugraph_create_resource_handle(NULL); + TEST_ASSERT(test_ret_value, p_handle != NULL, "resource handle creation failed."); + + ret_code = create_test_graph( + p_handle, h_src, h_dst, h_wgt, num_edges, store_transposed, FALSE, FALSE, &p_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)); + + ret_code = cugraph_leiden(p_handle, p_graph, max_level, resolution, FALSE, &p_result, &ret_error); + +#if 1 + TEST_ASSERT(test_ret_value, ret_code != CUGRAPH_SUCCESS, "cugraph_leiden should have failed"); +#else + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "cugraph_leiden failed."); + + if (test_ret_value == 0) { + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* clusters; + + vertices = cugraph_heirarchical_clustering_result_get_vertices(p_result); + clusters = cugraph_heirarchical_clustering_result_get_clusters(p_result); + double modularity = cugraph_heirarchical_clustering_result_get_modularity(p_result); + + vertex_t h_vertices[num_vertices]; + edge_t h_clusters[num_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + p_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( + p_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_heirarchical_clustering_result_free(p_result); + } +#endif + + cugraph_sg_graph_free(p_graph); + cugraph_free_resource_handle(p_handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_leiden() +{ + size_t num_edges = 8; + size_t num_vertices = 6; + size_t max_level = 10; + weight_t resolution = 1.0; + + 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, 1, 0, 1, 1, 1}; + weight_t expected_modularity = 0.218166; + + // Louvain wants store_transposed = FALSE + return generic_leiden_test(h_src, + h_dst, + h_wgt, + h_result, + expected_modularity, + num_vertices, + num_edges, + max_level, + resolution, + FALSE); +} + +/******************************************************************************/ + +int main(int argc, char** argv) +{ + int result = 0; + result |= RUN_TEST(test_leiden); + return result; +} diff --git a/cpp/tests/c_api/mg_leiden_test.c b/cpp/tests/c_api/mg_leiden_test.c new file mode 100644 index 00000000000..4b3fada9241 --- /dev/null +++ b/cpp/tests/c_api/mg_leiden_test.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2022, 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_leiden_test(const cugraph_resource_handle_t* p_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, + size_t max_level, + 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* p_graph = NULL; + cugraph_heirarchical_clustering_result_t* p_result = NULL; + + ret_code = create_mg_test_graph( + p_handle, h_src, h_dst, h_wgt, num_edges, store_transposed, FALSE, &p_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)); + + ret_code = cugraph_leiden(p_handle, p_graph, max_level, resolution, FALSE, &p_result, &ret_error); + +#if 1 + TEST_ASSERT(test_ret_value, ret_code != CUGRAPH_SUCCESS, "cugraph_leiden should have failed"); +#else + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, "cugraph_leiden failed."); + + if (test_ret_value == 0) { + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* clusters; + + vertices = cugraph_heirarchical_clustering_result_get_vertices(p_result); + clusters = cugraph_heirarchical_clustering_result_get_clusters(p_result); + + vertex_t h_vertices[num_vertices]; + edge_t h_clusters[num_vertices]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + p_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( + p_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_heirarchical_clustering_result_free(p_result); + } +#endif + + cugraph_mg_graph_free(p_graph); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_leiden(const cugraph_resource_handle_t* handle) +{ + size_t num_edges = 8; + size_t num_vertices = 6; + size_t max_level = 10; + weight_t resolution = 1.0; + + 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_leiden_test( + handle, h_src, h_dst, h_wgt, h_result, num_vertices, num_edges, max_level, resolution, FALSE); +} + +/******************************************************************************/ + +int main(int argc, char** argv) +{ + // Set up MPI: + int comm_rank; + int comm_size; + int num_gpus_per_node; + cudaError_t status; + int mpi_status; + int result = 0; + cugraph_resource_handle_t* handle = NULL; + cugraph_error_t* ret_error; + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + int prows = 1; + + C_MPI_TRY(MPI_Init(&argc, &argv)); + C_MPI_TRY(MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank)); + C_MPI_TRY(MPI_Comm_size(MPI_COMM_WORLD, &comm_size)); + C_CUDA_TRY(cudaGetDeviceCount(&num_gpus_per_node)); + C_CUDA_TRY(cudaSetDevice(comm_rank % num_gpus_per_node)); + + void* raft_handle = create_raft_handle(prows); + handle = cugraph_create_resource_handle(raft_handle); + + if (result == 0) { + result |= RUN_MG_TEST(test_leiden, handle); + + cugraph_free_resource_handle(handle); + } + + free_raft_handle(raft_handle); + + C_MPI_TRY(MPI_Finalize()); + + return result; +} From 87b9a5f64e016d10450b77d866b4882ce47e385e Mon Sep 17 00:00:00 2001 From: Charles Hastings Date: Thu, 9 Feb 2023 13:33:19 -0800 Subject: [PATCH 2/4] pre-commit --- cpp/include/cugraph_c/community_algorithms.h | 2 +- cpp/tests/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/include/cugraph_c/community_algorithms.h b/cpp/include/cugraph_c/community_algorithms.h index 65f495c7ff2..6de4dcf76b6 100644 --- a/cpp/include/cugraph_c/community_algorithms.h +++ b/cpp/include/cugraph_c/community_algorithms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * Copyright (c) 2022-2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 19b2bbb54d9..b5f8bf5a1e3 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,5 +1,5 @@ #============================================================================= -# Copyright (c) 2019-2022, NVIDIA CORPORATION. +# Copyright (c) 2019-2023, 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 6ae2a01330fd2d61393a474b7087c42141b4ba0d Mon Sep 17 00:00:00 2001 From: Charles Hastings Date: Tue, 14 Mar 2023 09:59:11 -0700 Subject: [PATCH 3/4] fix typo --- cpp/include/cugraph_c/community_algorithms.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cugraph_c/community_algorithms.h b/cpp/include/cugraph_c/community_algorithms.h index 6de4dcf76b6..0da291f86f6 100644 --- a/cpp/include/cugraph_c/community_algorithms.h +++ b/cpp/include/cugraph_c/community_algorithms.h @@ -124,7 +124,7 @@ cugraph_error_code_t cugraph_louvain(const cugraph_resource_handle_t* handle, * 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] result Output from the Leiden 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 From 36aa4f0d722cc1a55fc8054226fc91eab897145b Mon Sep 17 00:00:00 2001 From: Charles Hastings Date: Wed, 15 Mar 2023 15:56:25 -0700 Subject: [PATCH 4/4] update mg_leiden_test.c to reflect new C API structure from Seunghwa's PR --- cpp/tests/c_api/mg_leiden_test.c | 34 ++++++-------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/cpp/tests/c_api/mg_leiden_test.c b/cpp/tests/c_api/mg_leiden_test.c index 4b3fada9241..9c815c860b8 100644 --- a/cpp/tests/c_api/mg_leiden_test.c +++ b/cpp/tests/c_api/mg_leiden_test.c @@ -126,36 +126,14 @@ int test_leiden(const cugraph_resource_handle_t* handle) int main(int argc, char** argv) { - // Set up MPI: - int comm_rank; - int comm_size; - int num_gpus_per_node; - cudaError_t status; - int mpi_status; - int result = 0; - cugraph_resource_handle_t* handle = NULL; - cugraph_error_t* ret_error; - cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; - int prows = 1; - - C_MPI_TRY(MPI_Init(&argc, &argv)); - C_MPI_TRY(MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank)); - C_MPI_TRY(MPI_Comm_size(MPI_COMM_WORLD, &comm_size)); - C_CUDA_TRY(cudaGetDeviceCount(&num_gpus_per_node)); - C_CUDA_TRY(cudaSetDevice(comm_rank % num_gpus_per_node)); - - void* raft_handle = create_raft_handle(prows); - handle = cugraph_create_resource_handle(raft_handle); - - if (result == 0) { - result |= RUN_MG_TEST(test_leiden, handle); - - cugraph_free_resource_handle(handle); - } + void* raft_handle = create_mg_raft_handle(argc, argv); + cugraph_resource_handle_t* handle = cugraph_create_resource_handle(raft_handle); - free_raft_handle(raft_handle); + int result = 0; + result |= RUN_MG_TEST(test_leiden, handle); - C_MPI_TRY(MPI_Finalize()); + cugraph_free_resource_handle(handle); + free_mg_raft_handle(raft_handle); return result; }