From aece453568cead997de7a2f297069fce51baa975 Mon Sep 17 00:00:00 2001 From: Chuck Hastings <45364586+ChuckHastings@users.noreply.github.com> Date: Thu, 19 May 2022 21:57:26 -0400 Subject: [PATCH] Fix MG BFS through C API (#2291) @alexbarghi-nv found some issues testing BFS for MG through the C API. Essential problem is that the C API needs to shuffle the source vertices across the GPUs. Did the following: * Add Alex B's mg_bfs_test * Add missing mg_sssp_test * Add shuffle of vertex ids for MG in bfs * Fix sizing of array in sssp Authors: - Chuck Hastings (https://github.com/ChuckHastings) Approvers: - Seunghwa Kang (https://github.com/seunghwak) - Alex Barghi (https://github.com/alexbarghi-nv) URL: https://github.com/rapidsai/cugraph/pull/2291 --- cpp/src/c_api/bfs.cpp | 19 +++- cpp/src/c_api/sssp.cpp | 4 +- cpp/tests/CMakeLists.txt | 2 + cpp/tests/c_api/mg_bfs_test.c | 180 +++++++++++++++++++++++++++++++ cpp/tests/c_api/mg_sssp_test.c | 188 +++++++++++++++++++++++++++++++++ 5 files changed, 386 insertions(+), 7 deletions(-) create mode 100644 cpp/tests/c_api/mg_bfs_test.c create mode 100644 cpp/tests/c_api/mg_sssp_test.c diff --git a/cpp/src/c_api/bfs.cpp b/cpp/src/c_api/bfs.cpp index 3e2367be423..dcfadf5944f 100644 --- a/cpp/src/c_api/bfs.cpp +++ b/cpp/src/c_api/bfs.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -89,15 +90,23 @@ struct bfs_functor : public abstract_functor { rmm::device_uvector predecessors(0, handle_.get_stream()); if (compute_predecessors_) { - predecessors.resize(graph->number_of_vertices(), handle_.get_stream()); + predecessors.resize(graph_view.local_vertex_partition_range_size(), handle_.get_stream()); + } + + rmm::device_uvector sources(sources_->size_, handle_.get_stream()); + raft::copy( + sources.data(), sources_->as_type(), sources_->size_, handle_.get_stream()); + + if constexpr (multi_gpu) { + sources = detail::shuffle_ext_vertices_by_gpu_id(handle_, std::move(sources)); } // // Need to renumber sources // renumber_ext_vertices(handle_, - sources_->as_type(), - sources_->size_, + sources.data(), + sources.size(), number_map->data(), graph_view.local_vertex_partition_range_first(), graph_view.local_vertex_partition_range_last(), @@ -108,8 +117,8 @@ struct bfs_functor : public abstract_functor { graph_view, distances.data(), compute_predecessors_ ? predecessors.data() : nullptr, - sources_->as_type(), - sources_->size_, + sources.data(), + sources.size(), direction_optimizing_, static_cast(depth_limit_), do_expensive_check_); diff --git a/cpp/src/c_api/sssp.cpp b/cpp/src/c_api/sssp.cpp index 24ef5f751e2..5e494f69d2b 100644 --- a/cpp/src/c_api/sssp.cpp +++ b/cpp/src/c_api/sssp.cpp @@ -87,7 +87,7 @@ struct sssp_functor : public abstract_functor { rmm::device_uvector predecessors(0, handle_.get_stream()); if (compute_predecessors_) { - predecessors.resize(graph->number_of_vertices(), handle_.get_stream()); + predecessors.resize(graph_view.local_vertex_partition_range_size(), handle_.get_stream()); } vertex_t src = static_cast(source_); @@ -98,7 +98,7 @@ struct sssp_functor : public abstract_functor { // renumber_ext_vertices(handle_, source_ids.data(), - 1, + source_ids.size(), number_map->data(), graph_view.local_vertex_partition_range_first(), graph_view.local_vertex_partition_range_last(), diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index cab8966c0f6..15ff56acfb6 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -636,6 +636,8 @@ 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_BFS c_api/mg_bfs_test.c c_api/mg_test_utils.cpp) + ConfigureCTestMG(MG_CAPI_SSSP c_api/mg_sssp_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_WEAKLY_CONNECTED_COMPONENTS c_api/mg_weakly_connected_components_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_STRONGLY_CONNECTED_COMPONENTS c_api/mg_strongly_connected_components_test.c c_api/mg_test_utils.cpp) ConfigureCTestMG(MG_CAPI_KATZ c_api/mg_katz_test.c c_api/mg_test_utils.cpp) diff --git a/cpp/tests/c_api/mg_bfs_test.c b/cpp/tests/c_api/mg_bfs_test.c new file mode 100644 index 00000000000..0fc1fc431b5 --- /dev/null +++ b/cpp/tests/c_api/mg_bfs_test.c @@ -0,0 +1,180 @@ +/* + * 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_bfs_test( + const cugraph_resource_handle_t* p_handle, + vertex_t* h_src, + vertex_t* h_dst, + weight_t* h_wgt, + vertex_t* h_seeds, + vertex_t const* expected_distances, + vertex_t const* expected_predecessors, + size_t num_vertices, + size_t num_edges, + size_t num_seeds, + size_t depth_limit, + 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_paths_result_t* paths_result = NULL; + cugraph_type_erased_device_array_t* p_sources = NULL; + cugraph_type_erased_device_array_view_t* p_source_view = NULL; + + ret_code = + cugraph_type_erased_device_array_create(p_handle, num_seeds, INT32, &p_sources, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "p_sources create failed."); + + p_source_view = cugraph_type_erased_device_array_view(p_sources); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + p_handle, p_source_view, (byte_t*)h_seeds, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "src copy_from_host failed."); + + 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_mg_test_graph failed."); + + ret_code = cugraph_bfs(p_handle, + p_graph, + p_source_view, + FALSE, + 10000000, + TRUE, + TRUE, + &paths_result, + &ret_error); + + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_bfs failed."); + + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* distances; + cugraph_type_erased_device_array_view_t* predecessors; + + vertices = cugraph_paths_result_get_vertices(paths_result); + predecessors = cugraph_paths_result_get_predecessors(paths_result); + distances = cugraph_paths_result_get_distances(paths_result); + + vertex_t h_vertices[num_vertices]; + vertex_t h_predecessors[num_vertices]; + vertex_t h_distances[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_distances, distances, &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_predecessors, predecessors, &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, + expected_distances[h_vertices[i]] == h_distances[i], + "bfs distances don't match"); + + + TEST_ASSERT(test_ret_value, + expected_predecessors[h_vertices[i]] == h_predecessors[i], + "bfs predecessors don't match"); + } + + cugraph_paths_result_free(paths_result); + cugraph_mg_graph_free(p_graph); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_bfs(const cugraph_resource_handle_t* p_handle) +{ + size_t num_edges = 8; + size_t num_vertices = 6; + + vertex_t src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + weight_t wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + vertex_t seeds[] = {0}; + vertex_t expected_distances[] = {0, 1, 2147483647, 2, 2, 3}; + vertex_t expected_predecessors[] = {-1, 0, -1, 1, 1, 3}; + + // Bfs wants store_transposed = FALSE + return generic_bfs_test(p_handle, + src, + dst, + wgt, + seeds, + expected_distances, + expected_predecessors, + num_vertices, + num_edges, + 1, + 10, + 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; + 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); + int result = 0; + result |= RUN_MG_TEST(test_bfs, 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_sssp_test.c b/cpp/tests/c_api/mg_sssp_test.c new file mode 100644 index 00000000000..3aca77dca28 --- /dev/null +++ b/cpp/tests/c_api/mg_sssp_test.c @@ -0,0 +1,188 @@ +/* + * 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 +#include + +typedef int32_t vertex_t; +typedef int32_t edge_t; +typedef float weight_t; + +const weight_t EPSILON = 0.001; + +int generic_sssp_test(const cugraph_resource_handle_t* p_handle, + vertex_t* h_src, + vertex_t* h_dst, + weight_t* h_wgt, + vertex_t source, + weight_t const* expected_distances, + vertex_t const* expected_predecessors, + size_t num_vertices, + size_t num_edges, + weight_t cutoff, + 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_paths_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); + + ret_code = cugraph_sssp(p_handle, p_graph, source, cutoff, TRUE, FALSE, &p_result, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error)); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_sssp failed."); + + cugraph_type_erased_device_array_view_t* vertices; + cugraph_type_erased_device_array_view_t* distances; + cugraph_type_erased_device_array_view_t* predecessors; + + vertices = cugraph_paths_result_get_vertices(p_result); + distances = cugraph_paths_result_get_distances(p_result); + predecessors = cugraph_paths_result_get_predecessors(p_result); + + vertex_t h_vertices[num_vertices]; + weight_t h_distances[num_vertices]; + vertex_t h_predecessors[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_distances, distances, &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_predecessors, predecessors, &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(expected_distances[h_vertices[i]], h_distances[i], EPSILON), + "sssp distances don't match"); + + TEST_ASSERT(test_ret_value, + expected_predecessors[h_vertices[i]] == h_predecessors[i], + "sssp predecessors don't match"); + } + + cugraph_type_erased_device_array_view_free(vertices); + cugraph_type_erased_device_array_view_free(distances); + cugraph_type_erased_device_array_view_free(predecessors); + cugraph_paths_result_free(p_result); + cugraph_sg_graph_free(p_graph); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_sssp(const cugraph_resource_handle_t* p_handle) +{ + size_t num_edges = 8; + size_t num_vertices = 6; + + vertex_t src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + weight_t wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + weight_t expected_distances[] = {0.0f, 0.1f, FLT_MAX, 2.2f, 1.2f, 4.4f}; + vertex_t expected_predecessors[] = {-1, 0, -1, 1, 1, 4}; + + // Bfs wants store_transposed = FALSE + return generic_sssp_test(p_handle, + src, + dst, + wgt, + 0, + expected_distances, + expected_predecessors, + num_vertices, + num_edges, + 10, + FALSE); +} + +int test_sssp_with_transpose(const cugraph_resource_handle_t* p_handle) +{ + size_t num_edges = 8; + size_t num_vertices = 6; + + vertex_t src[] = {0, 1, 1, 2, 2, 2, 3, 4}; + vertex_t dst[] = {1, 3, 4, 0, 1, 3, 5, 5}; + weight_t wgt[] = {0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; + weight_t expected_distances[] = {0.0f, 0.1f, FLT_MAX, 2.2f, 1.2f, 4.4f}; + vertex_t expected_predecessors[] = {-1, 0, -1, 1, 1, 4}; + + // Bfs wants store_transposed = FALSE + // This call will force cugraph_sssp to transpose the graph + return generic_sssp_test(p_handle, + src, + dst, + wgt, + 0, + expected_distances, + expected_predecessors, + num_vertices, + num_edges, + 10, + TRUE); +} + +/******************************************************************************/ + +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; + 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); + int result = 0; + result |= RUN_MG_TEST(test_sssp, handle); + result |= RUN_MG_TEST(test_sssp_with_transpose, handle); + + cugraph_free_resource_handle(handle); + free_raft_handle(raft_handle); + + C_MPI_TRY(MPI_Finalize()); + + return result; +}