diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 950e9ddc823..fdf1e86ae22 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -355,6 +355,8 @@ add_library(cugraph_c SHARED src/c_api/graph_sg.cpp src/c_api/graph_mg.cpp src/c_api/pagerank.cpp + src/c_api/centrality_result.cpp + src/c_api/eigenvector_centrality.cpp src/c_api/hits.cpp src/c_api/bfs.cpp src/c_api/sssp.cpp diff --git a/cpp/include/cugraph_c/algorithms.h b/cpp/include/cugraph_c/algorithms.h index cfaa89bb490..987f6d5a687 100644 --- a/cpp/include/cugraph_c/algorithms.h +++ b/cpp/include/cugraph_c/algorithms.h @@ -25,36 +25,36 @@ extern "C" { #endif /** - * @brief Opaque pagerank result type + * @brief Opaque centrality result type */ typedef struct { int32_t align_; -} cugraph_pagerank_result_t; +} cugraph_centrality_result_t; /** - * @brief Get the vertex ids from the pagerank result + * @brief Get the vertex ids from the centrality result * - * @param [in] result The result from pagerank + * @param [in] result The result from a centrality algorithm * @return type erased array of vertex ids */ -cugraph_type_erased_device_array_view_t* cugraph_pagerank_result_get_vertices( - cugraph_pagerank_result_t* result); +cugraph_type_erased_device_array_view_t* cugraph_centrality_result_get_vertices( + cugraph_centrality_result_t* result); /** - * @brief Get the pagerank values from the pagerank result + * @brief Get the centrality values from a centrality algorithm result * - * @param [in] result The result from pagerank - * @return type erased array of pagerank values + * @param [in] result The result from a centrality algorithm + * @return type erased array view of centrality values */ -cugraph_type_erased_device_array_view_t* cugraph_pagerank_result_get_pageranks( - cugraph_pagerank_result_t* result); +cugraph_type_erased_device_array_view_t* cugraph_centrality_result_get_values( + cugraph_centrality_result_t* result); /** - * @brief Free pagerank result + * @brief Free centrality result * - * @param [in] result The result from pagerank + * @param [in] result The result from a centrality algorithm */ -void cugraph_pagerank_result_free(cugraph_pagerank_result_t* result); +void cugraph_centrality_result_free(cugraph_centrality_result_t* result); /** * @brief Compute pagerank @@ -90,7 +90,7 @@ cugraph_error_code_t cugraph_pagerank( size_t max_iterations, bool_t has_initial_guess, bool_t do_expensive_check, - cugraph_pagerank_result_t** result, + cugraph_centrality_result_t** result, cugraph_error_t** error); /** @@ -136,9 +136,35 @@ cugraph_error_code_t cugraph_personalized_pagerank( size_t max_iterations, bool_t has_initial_guess, bool_t do_expensive_check, - cugraph_pagerank_result_t** result, + cugraph_centrality_result_t** result, cugraph_error_t** error); +/** + * @brief Compute eigenvector centrality + * + * Computed using the power method. + * + * @param [in] handle Handle for accessing resources + * @param [in] graph Pointer to graph + * @param [in] epsilon Error tolerance to check convergence. Convergence is measured + * comparing the L1 norm until it is less than epsilon + * @param [in] max_iterations Maximum number of power iterations, will not exceed this number + * of iterations even if we haven't converged + * @param [in] do_expensive_check A flag to run expensive checks for input arguments (if set to + * `true`). + * @param [out] result Opaque pointer to eigenvector centrality results + * @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_eigenvector_centrality(const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + double epsilon, + size_t max_iterations, + bool_t do_expensive_check, + cugraph_centrality_result_t** result, + cugraph_error_t** error); + /** * @brief Opaque hits result type */ diff --git a/cpp/src/c_api/centrality_result.cpp b/cpp/src/c_api/centrality_result.cpp new file mode 100644 index 00000000000..20794dff4bc --- /dev/null +++ b/cpp/src/c_api/centrality_result.cpp @@ -0,0 +1,43 @@ +/* + * 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 + +#include + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_centrality_result_get_vertices( + cugraph_centrality_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + return reinterpret_cast( + internal_pointer->vertex_ids_->view()); +} + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_centrality_result_get_values( + cugraph_centrality_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + return reinterpret_cast( + internal_pointer->values_->view()); +} + +extern "C" void cugraph_centrality_result_free(cugraph_centrality_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + delete internal_pointer->vertex_ids_; + delete internal_pointer->values_; + delete internal_pointer; +} diff --git a/cpp/src/c_api/centrality_result.hpp b/cpp/src/c_api/centrality_result.hpp new file mode 100644 index 00000000000..230ad2f2ab9 --- /dev/null +++ b/cpp/src/c_api/centrality_result.hpp @@ -0,0 +1,30 @@ +/* + * 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. + */ + +#pragma once + +#include + +namespace cugraph { +namespace c_api { + +struct cugraph_centrality_result_t { + cugraph_type_erased_device_array_t* vertex_ids_{}; + cugraph_type_erased_device_array_t* values_{}; +}; + +} // namespace c_api +} // namespace cugraph diff --git a/cpp/src/c_api/eigenvector_centrality.cpp b/cpp/src/c_api/eigenvector_centrality.cpp new file mode 100644 index 00000000000..a7e90bff14a --- /dev/null +++ b/cpp/src/c_api/eigenvector_centrality.cpp @@ -0,0 +1,125 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace { + +struct eigenvector_centrality_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_graph_t* graph_{}; + double epsilon_{}; + size_t max_iterations_{}; + bool do_expensive_check_{}; + cugraph::c_api::cugraph_centrality_result_t* result_{}; + + eigenvector_centrality_functor(cugraph_resource_handle_t const* handle, + cugraph_graph_t* graph, + double epsilon, + size_t max_iterations, + bool do_expensive_check) + : abstract_functor(), + handle_(*reinterpret_cast(handle)->handle_), + graph_(reinterpret_cast(graph)), + epsilon_(epsilon), + max_iterations_(max_iterations), + do_expensive_check_(do_expensive_check) + { + } + + template + void operator()() + { + // FIXME: Think about how to handle SG vice MG + if constexpr (!cugraph::is_candidate::value) { + unsupported(); + } else { + // Eigenvector Centrality expects store_transposed == true + 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 number_map = reinterpret_cast*>(graph_->number_map_); + + rmm::device_uvector centralities(graph_view.get_number_of_local_vertices(), + handle_.get_stream()); + + // FIXME: For now we'll call pagerank which returns a similarly formatted thing + cugraph::pagerank( + handle_, + graph_view, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + centralities.data(), + weight_t{0.95}, + static_cast(epsilon_), + max_iterations_, + false, + do_expensive_check_); + + rmm::device_uvector vertex_ids(graph_view.get_number_of_local_vertices(), + handle_.get_stream()); + raft::copy(vertex_ids.data(), number_map->data(), vertex_ids.size(), handle_.get_stream()); + + result_ = new cugraph::c_api::cugraph_centrality_result_t{ + new cugraph::c_api::cugraph_type_erased_device_array_t(vertex_ids, graph_->vertex_type_), + new cugraph::c_api::cugraph_type_erased_device_array_t(centralities, graph_->weight_type_)}; + } + } +}; + +} // namespace + +extern "C" cugraph_error_code_t cugraph_eigenvector_centrality( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + double epsilon, + size_t max_iterations, + bool_t do_expensive_check, + cugraph_centrality_result_t** result, + cugraph_error_t** error) +{ + eigenvector_centrality_functor functor( + handle, graph, epsilon, max_iterations, do_expensive_check); + + return cugraph::c_api::run_algorithm(graph, functor, result, error); +} diff --git a/cpp/src/c_api/pagerank.cpp b/cpp/src/c_api/pagerank.cpp index 24c0425d2d2..c1e4ca5785a 100644 --- a/cpp/src/c_api/pagerank.cpp +++ b/cpp/src/c_api/pagerank.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -27,33 +28,28 @@ #include -namespace cugraph { -namespace c_api { +namespace { -struct cugraph_pagerank_result_t { - cugraph_type_erased_device_array_t* vertex_ids_; - cugraph_type_erased_device_array_t* pageranks_; -}; - -struct pagerank_functor : public abstract_functor { +struct pagerank_functor : public cugraph::c_api::abstract_functor { raft::handle_t const& handle_; - cugraph_graph_t* graph_; - cugraph_type_erased_device_array_view_t const* precomputed_vertex_out_weight_sums_; - cugraph_type_erased_device_array_view_t* personalization_vertices_; - cugraph_type_erased_device_array_view_t const* personalization_values_; - double alpha_; - double epsilon_; - size_t max_iterations_; - bool has_initial_guess_; - bool do_expensive_check_; - cugraph_pagerank_result_t* result_{}; + cugraph::c_api::cugraph_graph_t* graph_{}; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* + precomputed_vertex_out_weight_sums_{}; + cugraph::c_api::cugraph_type_erased_device_array_view_t* personalization_vertices_{}; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* personalization_values_{}; + double alpha_{}; + double epsilon_{}; + size_t max_iterations_{}; + bool has_initial_guess_{}; + bool do_expensive_check_{}; + cugraph::c_api::cugraph_centrality_result_t* result_{}; pagerank_functor( - ::cugraph_resource_handle_t const* handle, - ::cugraph_graph_t* graph, - ::cugraph_type_erased_device_array_view_t const* precomputed_vertex_out_weight_sums, - ::cugraph_type_erased_device_array_view_t* personalization_vertices, - ::cugraph_type_erased_device_array_view_t const* personalization_values, + cugraph_resource_handle_t const* handle, + cugraph_graph_t* graph, + cugraph_type_erased_device_array_view_t const* precomputed_vertex_out_weight_sums, + cugraph_type_erased_device_array_view_t* personalization_vertices, + cugraph_type_erased_device_array_view_t const* personalization_values, double alpha, double epsilon, size_t max_iterations, @@ -112,13 +108,14 @@ struct pagerank_functor : public abstract_functor { // // Need to renumber personalization_vertices // - renumber_ext_vertices(handle_, - personalization_vertices_->as_type(), - personalization_vertices_->size_, - number_map->data(), - graph_view.get_local_vertex_first(), - graph_view.get_local_vertex_last(), - do_expensive_check_); + cugraph::renumber_ext_vertices( + handle_, + personalization_vertices_->as_type(), + personalization_vertices_->size_, + number_map->data(), + graph_view.get_local_vertex_first(), + graph_view.get_local_vertex_last(), + do_expensive_check_); } cugraph::pagerank( @@ -147,39 +144,14 @@ struct pagerank_functor : public abstract_functor { handle_.get_stream()); raft::copy(vertex_ids.data(), number_map->data(), vertex_ids.size(), handle_.get_stream()); - result_ = new cugraph_pagerank_result_t{ - new cugraph_type_erased_device_array_t(vertex_ids, graph_->vertex_type_), - new cugraph_type_erased_device_array_t(pageranks, graph_->weight_type_)}; + result_ = new cugraph::c_api::cugraph_centrality_result_t{ + new cugraph::c_api::cugraph_type_erased_device_array_t(vertex_ids, graph_->vertex_type_), + new cugraph::c_api::cugraph_type_erased_device_array_t(pageranks, graph_->weight_type_)}; } } }; -} // namespace c_api -} // namespace cugraph - -extern "C" cugraph_type_erased_device_array_view_t* cugraph_pagerank_result_get_vertices( - cugraph_pagerank_result_t* result) -{ - auto internal_pointer = reinterpret_cast(result); - return reinterpret_cast( - internal_pointer->vertex_ids_->view()); -} - -extern "C" cugraph_type_erased_device_array_view_t* cugraph_pagerank_result_get_pageranks( - cugraph_pagerank_result_t* result) -{ - auto internal_pointer = reinterpret_cast(result); - return reinterpret_cast( - internal_pointer->pageranks_->view()); -} - -extern "C" void cugraph_pagerank_result_free(cugraph_pagerank_result_t* result) -{ - auto internal_pointer = reinterpret_cast(result); - delete internal_pointer->vertex_ids_; - delete internal_pointer->pageranks_; - delete internal_pointer; -} +} // namespace extern "C" cugraph_error_code_t cugraph_pagerank( const cugraph_resource_handle_t* handle, @@ -190,19 +162,19 @@ extern "C" cugraph_error_code_t cugraph_pagerank( size_t max_iterations, bool_t has_initial_guess, bool_t do_expensive_check, - cugraph_pagerank_result_t** result, + cugraph_centrality_result_t** result, cugraph_error_t** error) { - cugraph::c_api::pagerank_functor functor(handle, - graph, - precomputed_vertex_out_weight_sums, - nullptr, - nullptr, - alpha, - epsilon, - max_iterations, - has_initial_guess, - do_expensive_check); + pagerank_functor functor(handle, + graph, + precomputed_vertex_out_weight_sums, + nullptr, + nullptr, + alpha, + epsilon, + max_iterations, + has_initial_guess, + do_expensive_check); return cugraph::c_api::run_algorithm(graph, functor, result, error); } @@ -218,19 +190,19 @@ extern "C" cugraph_error_code_t cugraph_personalized_pagerank( size_t max_iterations, bool_t has_initial_guess, bool_t do_expensive_check, - cugraph_pagerank_result_t** result, + cugraph_centrality_result_t** result, cugraph_error_t** error) { - cugraph::c_api::pagerank_functor functor(handle, - graph, - precomputed_vertex_out_weight_sums, - personalization_vertices, - personalization_values, - alpha, - epsilon, - max_iterations, - has_initial_guess, - do_expensive_check); + pagerank_functor functor(handle, + graph, + precomputed_vertex_out_weight_sums, + personalization_vertices, + personalization_values, + alpha, + epsilon, + max_iterations, + has_initial_guess, + 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 d79bbab5b29..b9530f66e5e 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -620,6 +620,7 @@ if(BUILD_CUGRAPH_MG_TESTS) # - MG C API tests ------------------------------------------------------------------------ ConfigureCTestMG(MG_CAPI_CREATE_GRAPH c_api/mg_create_graph_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_PAGERANK c_api/mg_pagerank_test.c c_api/mg_test_utils.cpp) + ConfigureCTestMG(MG_CAPI_EIGENVECTOR_CENTRALITY c_api/mg_eigenvector_centrality_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_HITS c_api/mg_hits_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_UNIFORM_NEIGHBOR_SAMPLE c_api/mg_uniform_neighbor_sample_test.c c_api/mg_test_utils.cpp) else() @@ -662,6 +663,7 @@ target_link_libraries(cugraph_c_testutil ConfigureCTest(CAPI_CREATE_GRAPH_TEST c_api/create_graph_test.c) ConfigureCTest(CAPI_PAGERANK_TEST c_api/pagerank_test.c) +ConfigureCTest(CAPI_EIGENVECTOR_CENTRALITY_TEST c_api/eigenvector_centrality_test.c) ConfigureCTest(CAPI_HITS_TEST c_api/hits_test.c) ConfigureCTest(CAPI_BFS_TEST c_api/bfs_test.c) ConfigureCTest(CAPI_SSSP_TEST c_api/sssp_test.c) diff --git a/cpp/tests/c_api/eigenvector_centrality_test.c b/cpp/tests/c_api/eigenvector_centrality_test.c new file mode 100644 index 00000000000..5e82449b7f4 --- /dev/null +++ b/cpp/tests/c_api/eigenvector_centrality_test.c @@ -0,0 +1,118 @@ +/* + * 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_eigenvector_centrality_test(vertex_t* h_src, + vertex_t* h_dst, + weight_t* h_wgt, + weight_t* h_result, + size_t num_vertices, + size_t num_edges, + bool_t store_transposed, + double alpha, + double epsilon, + size_t max_iterations) +{ + 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_centrality_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, &p_graph, &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "create_test_graph failed."); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + + ret_code = cugraph_eigenvector_centrality( + p_handle, p_graph, epsilon, max_iterations, FALSE, &p_result, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_eigenvector_centrality failed."); + + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* centralities; + + vertices = cugraph_centrality_result_get_vertices(p_result); + centralities = cugraph_centrality_result_get_values(p_result); + + vertex_t h_vertices[num_vertices]; + weight_t h_centralities[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_centralities, centralities, &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, + nearlyEqual(h_result[h_vertices[i]], h_centralities[i], 0.001), + "centralities results don't match"); + } + + cugraph_centrality_result_free(p_result); + cugraph_sg_graph_free(p_graph); + cugraph_free_resource_handle(p_handle); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_eigenvector_centrality() +{ + size_t num_edges = 8; + size_t num_vertices = 6; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + weight_t h_result[] = {0.0915528, 0.168382, 0.0656831, 0.191468, 0.120677, 0.362237}; + + double alpha = 0.95; + double epsilon = 0.0001; + size_t max_iterations = 20; + + // Pagerank wants store_transposed = TRUE + return generic_eigenvector_centrality_test( + h_src, h_dst, h_wgt, h_result, num_vertices, num_edges, TRUE, alpha, epsilon, max_iterations); +} + +/******************************************************************************/ + +int main(int argc, char** argv) +{ + int result = 0; + result |= RUN_TEST(test_eigenvector_centrality); + return result; +} diff --git a/cpp/tests/c_api/mg_eigenvector_centrality_test.c b/cpp/tests/c_api/mg_eigenvector_centrality_test.c new file mode 100644 index 00000000000..d8bf291edef --- /dev/null +++ b/cpp/tests/c_api/mg_eigenvector_centrality_test.c @@ -0,0 +1,169 @@ +/* + * 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_eigenvector_centrality_test(const cugraph_resource_handle_t* handle, + vertex_t* h_src, + vertex_t* h_dst, + weight_t* h_wgt, + weight_t* h_result, + size_t num_vertices, + size_t num_edges, + bool_t store_transposed, + double alpha, + double epsilon, + size_t max_iterations) +{ + 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_centrality_result_t* p_result = NULL; + + ret_code = create_mg_test_graph( + handle, h_src, h_dst, h_wgt, num_edges, store_transposed, &p_graph, &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "create_mg_test_graph failed."); + + ret_code = cugraph_eigenvector_centrality( + handle, p_graph, epsilon, max_iterations, FALSE, &p_result, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_eigenvector_centrality failed."); + + // NOTE: Because we get back vertex ids and centralities, we can simply compare + // the returned values with the expected results for the entire + // graph. Each GPU will have a subset of the total vertices, so + // they will do a subset of the comparisons. + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* centralities; + + vertices = cugraph_centrality_result_get_vertices(p_result); + centralities = cugraph_centrality_result_get_values(p_result); + + vertex_t h_vertices[num_vertices]; + weight_t h_centralities[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_centralities, centralities, &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); + + for (int i = 0; (i < num_local_vertices) && (test_ret_value == 0); ++i) { + TEST_ASSERT(test_ret_value, + nearlyEqual(h_result[h_vertices[i]], h_centralities[i], 0.001), + "eigenvector centrality results don't match"); + } + + cugraph_centrality_result_free(p_result); + cugraph_mg_graph_free(p_graph); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_eigenvector_centrality(const cugraph_resource_handle_t* handle) +{ + size_t num_edges = 8; + size_t num_vertices = 6; + + vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + weight_t h_wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + weight_t h_result[] = {0.0915528, 0.168382, 0.0656831, 0.191468, 0.120677, 0.362237}; + + double alpha = 0.95; + double epsilon = 0.0001; + size_t max_iterations = 20; + + // Pagerank wants store_transposed = TRUE + return generic_eigenvector_centrality_test(handle, + h_src, + h_dst, + h_wgt, + h_result, + num_vertices, + num_edges, + TRUE, + alpha, + epsilon, + max_iterations); +} + +/******************************************************************************/ + +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)); + +#if 0 + // TODO: Need something a bit more sophisticated for bigger systems + prows = (int)sqrt((double)comm_size); + while (comm_size % prows != 0) { + --prows; + } + + ret_code = cugraph_resource_handle_init_comms(handle, prows, &ret_error); + TEST_ASSERT(result, ret_code == CUGRAPH_SUCCESS, "handle create failed."); + TEST_ASSERT(result, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); +#endif + + void* raft_handle = create_raft_handle(prows); + handle = cugraph_create_resource_handle(raft_handle); + + if (result == 0) { + result |= RUN_MG_TEST(test_eigenvector_centrality, handle); + + cugraph_free_resource_handle(handle); + } + + free_raft_handle(raft_handle); + + C_MPI_TRY(MPI_Finalize()); + + return result; +} diff --git a/cpp/tests/c_api/mg_pagerank_test.c b/cpp/tests/c_api/mg_pagerank_test.c index 941e5e09c73..e7ead863c4d 100644 --- a/cpp/tests/c_api/mg_pagerank_test.c +++ b/cpp/tests/c_api/mg_pagerank_test.c @@ -43,7 +43,7 @@ int generic_pagerank_test(const cugraph_resource_handle_t* handle, cugraph_error_t* ret_error; cugraph_graph_t* p_graph = NULL; - cugraph_pagerank_result_t* p_result = NULL; + cugraph_centrality_result_t* p_result = NULL; ret_code = create_mg_test_graph( handle, h_src, h_dst, h_wgt, num_edges, store_transposed, &p_graph, &ret_error); @@ -61,8 +61,8 @@ int generic_pagerank_test(const cugraph_resource_handle_t* handle, cugraph_type_erased_device_array_view_t* vertices; cugraph_type_erased_device_array_view_t* pageranks; - vertices = cugraph_pagerank_result_get_vertices(p_result); - pageranks = cugraph_pagerank_result_get_pageranks(p_result); + vertices = cugraph_centrality_result_get_vertices(p_result); + pageranks = cugraph_centrality_result_get_values(p_result); vertex_t h_vertices[num_vertices]; weight_t h_pageranks[num_vertices]; @@ -83,9 +83,7 @@ int generic_pagerank_test(const cugraph_resource_handle_t* handle, "pagerank results don't match"); } - cugraph_type_erased_device_array_view_free(pageranks); - cugraph_type_erased_device_array_view_free(vertices); - cugraph_pagerank_result_free(p_result); + cugraph_centrality_result_free(p_result); cugraph_mg_graph_free(p_graph); cugraph_error_free(ret_error); diff --git a/cpp/tests/c_api/pagerank_test.c b/cpp/tests/c_api/pagerank_test.c index 60ac9ddf5e4..715692aaa0d 100644 --- a/cpp/tests/c_api/pagerank_test.c +++ b/cpp/tests/c_api/pagerank_test.c @@ -43,7 +43,7 @@ int generic_pagerank_test(vertex_t* h_src, cugraph_resource_handle_t* p_handle = NULL; cugraph_graph_t* p_graph = NULL; - cugraph_pagerank_result_t* p_result = NULL; + cugraph_centrality_result_t* p_result = NULL; p_handle = cugraph_create_resource_handle(NULL); TEST_ASSERT(test_ret_value, p_handle != NULL, "resource handle creation failed."); @@ -61,8 +61,8 @@ int generic_pagerank_test(vertex_t* h_src, cugraph_type_erased_device_array_view_t* vertices; cugraph_type_erased_device_array_view_t* pageranks; - vertices = cugraph_pagerank_result_get_vertices(p_result); - pageranks = cugraph_pagerank_result_get_pageranks(p_result); + vertices = cugraph_centrality_result_get_vertices(p_result); + pageranks = cugraph_centrality_result_get_values(p_result); vertex_t h_vertices[num_vertices]; weight_t h_pageranks[num_vertices]; @@ -81,9 +81,7 @@ int generic_pagerank_test(vertex_t* h_src, "pagerank results don't match"); } - cugraph_type_erased_device_array_view_free(pageranks); - cugraph_type_erased_device_array_view_free(vertices); - cugraph_pagerank_result_free(p_result); + cugraph_centrality_result_free(p_result); cugraph_sg_graph_free(p_graph); cugraph_free_resource_handle(p_handle); cugraph_error_free(ret_error); diff --git a/python/cugraph/cugraph/structure/hypergraph.py b/python/cugraph/cugraph/structure/hypergraph.py index b22caef15ba..365c26da6be 100644 --- a/python/cugraph/cugraph/structure/hypergraph.py +++ b/python/cugraph/cugraph/structure/hypergraph.py @@ -185,7 +185,7 @@ def hypergraph( else _str_scalar_to_category(len(events), "event") if not dropna: - for key, col in events[columns].iteritems(): + for key, col in events[columns].items(): if cudf.api.types.is_string_dtype(col.dtype): events[key].fillna("null", inplace=True) @@ -297,10 +297,10 @@ def _create_entity_nodes( )) ] + [ (key, cudf.core.column.column_empty(0, col.dtype)) - for key, col in events[columns].iteritems() + for key, col in events[columns].items() ]))] - for key, col in events[columns].iteritems(): + for key, col in events[columns].items(): cat = categories.get(key, key) col = col.unique() col = col.nans_to_nulls().dropna() if dropna else col @@ -378,11 +378,11 @@ def _create_hyper_edges( ]) + ([] if drop_edge_attrs else [ (key, cudf.core.column.column_empty(0, col.dtype)) - for key, col in events[edge_attrs].iteritems() + for key, col in events[edge_attrs].items() ]) ))] - for key, col in events[columns].iteritems(): + for key, col in events[columns].items(): cat = categories.get(key, key) fs = [EVENTID] + ([key] if drop_edge_attrs else edge_attrs) df = events[fs].dropna(subset=[key]) if dropna else events[fs] @@ -448,11 +448,11 @@ def _create_direct_edges( ]) + ([] if drop_edge_attrs else [ (key, cudf.core.column.column_empty(0, col.dtype)) - for key, col in events[edge_attrs].iteritems() + for key, col in events[edge_attrs].items() ]) ))] - for key1, col1 in events[sorted(edge_shape.keys())].iteritems(): + for key1, col1 in events[sorted(edge_shape.keys())].items(): cat1 = categories.get(key1, key1) if isinstance(edge_shape[key1], str): @@ -462,7 +462,7 @@ def _create_direct_edges( elif not isinstance(edge_shape[key1], (set, list, tuple)): raise ValueError("EDGES must be a dict of column name(s)") - for key2, col2 in events[sorted(edge_shape[key1])].iteritems(): + for key2, col2 in events[sorted(edge_shape[key1])].items(): cat2 = categories.get(key2, key2) fs = [EVENTID] + ([key1, key2] if drop_edge_attrs else edge_attrs) df = ( diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd index d7a0755df85..d4f1d083c73 100644 --- a/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/algorithms.pxd @@ -34,22 +34,22 @@ from pylibcugraph._cugraph_c.graph cimport ( cdef extern from "cugraph_c/algorithms.h": ########################################################################### # pagerank - ctypedef struct cugraph_pagerank_result_t: + ctypedef struct cugraph_centrality_result_t: pass cdef cugraph_type_erased_device_array_view_t* \ - cugraph_pagerank_result_get_vertices( - cugraph_pagerank_result_t* result + cugraph_centrality_result_get_vertices( + cugraph_centrality_result_t* result ) cdef cugraph_type_erased_device_array_view_t* \ - cugraph_pagerank_result_get_pageranks( - cugraph_pagerank_result_t* result + cugraph_centrality_result_get_values( + cugraph_centrality_result_t* result ) cdef void \ - cugraph_pagerank_result_free( - cugraph_pagerank_result_t* result + cugraph_centrality_result_free( + cugraph_centrality_result_t* result ) cdef cugraph_error_code_t \ @@ -62,7 +62,7 @@ cdef extern from "cugraph_c/algorithms.h": size_t max_iterations, bool_t has_initial_guess, bool_t do_expensive_check, - cugraph_pagerank_result_t** result, + cugraph_centrality_result_t** result, cugraph_error_t** error ) @@ -78,7 +78,7 @@ cdef extern from "cugraph_c/algorithms.h": size_t max_iterations, bool_t has_initial_guess, bool_t do_expensive_check, - cugraph_pagerank_result_t** result, + cugraph_centrality_result_t** result, cugraph_error_t** error ) diff --git a/python/pylibcugraph/pylibcugraph/pagerank.pyx b/python/pylibcugraph/pylibcugraph/pagerank.pyx index 0c46d2f97b7..53a47fc5bcc 100644 --- a/python/pylibcugraph/pylibcugraph/pagerank.pyx +++ b/python/pylibcugraph/pylibcugraph/pagerank.pyx @@ -30,11 +30,11 @@ from pylibcugraph._cugraph_c.graph cimport ( cugraph_graph_t, ) from pylibcugraph._cugraph_c.algorithms cimport ( - cugraph_pagerank_result_t, + cugraph_centrality_result_t, cugraph_pagerank, - cugraph_pagerank_result_get_vertices, - cugraph_pagerank_result_get_pageranks, - cugraph_pagerank_result_free, + cugraph_centrality_result_get_vertices, + cugraph_centrality_result_get_values, + cugraph_centrality_result_free, ) from pylibcugraph.resource_handle cimport ( EXPERIMENTAL__ResourceHandle, @@ -169,7 +169,7 @@ def EXPERIMENTAL__pagerank(EXPERIMENTAL__ResourceHandle resource_handle, raise NotImplementedError("None is temporarily the only supported " "value for precomputed_vertex_out_weight_sums") - cdef cugraph_pagerank_result_t* result_ptr + cdef cugraph_centrality_result_t* result_ptr cdef cugraph_error_code_t error_code cdef cugraph_error_t* error_ptr @@ -188,13 +188,13 @@ def EXPERIMENTAL__pagerank(EXPERIMENTAL__ResourceHandle resource_handle, # Extract individual device array pointers from result and copy to cupy # arrays for returning. cdef cugraph_type_erased_device_array_view_t* vertices_ptr = \ - cugraph_pagerank_result_get_vertices(result_ptr) + cugraph_centrality_result_get_vertices(result_ptr) cdef cugraph_type_erased_device_array_view_t* pageranks_ptr = \ - cugraph_pagerank_result_get_pageranks(result_ptr) + cugraph_centrality_result_get_values(result_ptr) cupy_vertices = copy_to_cupy_array(c_resource_handle_ptr, vertices_ptr) cupy_pageranks = copy_to_cupy_array(c_resource_handle_ptr, pageranks_ptr) - cugraph_pagerank_result_free(result_ptr) + cugraph_centrality_result_free(result_ptr) return (cupy_vertices, cupy_pageranks)