From 2b1f45280f1f30daa01b0d1cfa4171905cc4b5f7 Mon Sep 17 00:00:00 2001 From: Naim <110031745+naimnv@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:30:13 +0200 Subject: [PATCH] c_api and plc binding for lookup src dst using edge ids and type(s) (#4494) c_api and plc binding for lookup src dst using edge ids and type(s) Authors: - Naim (https://github.com/naimnv) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Alex Barghi (https://github.com/alexbarghi-nv) URL: https://github.com/rapidsai/cugraph/pull/4494 --- cpp/CMakeLists.txt | 1 + .../cugraph/src_dst_lookup_container.hpp | 3 +- cpp/include/cugraph_c/algorithms.h | 4 +- cpp/include/cugraph_c/lookup_src_dst.h | 141 +++++++ cpp/include/cugraph_c/sampling_algorithms.h | 10 +- cpp/src/c_api/lookup_src_dst.cpp | 389 ++++++++++++++++++ cpp/src/c_api/lookup_src_dst.hpp | 43 ++ cpp/src/lookup/lookup_src_dst_impl.cuh | 11 +- cpp/src/lookup/lookup_src_dst_mg.cu | 85 ++++ cpp/src/lookup/lookup_src_dst_sg.cu | 84 ++++ cpp/tests/CMakeLists.txt | 1 + cpp/tests/c_api/mg_lookup_src_dst_test.c | 266 ++++++++++++ .../_cugraph_c/lookup_src_dst.pxd | 72 ++++ 13 files changed, 1101 insertions(+), 9 deletions(-) create mode 100644 cpp/include/cugraph_c/lookup_src_dst.h create mode 100644 cpp/src/c_api/lookup_src_dst.cpp create mode 100644 cpp/src/c_api/lookup_src_dst.hpp create mode 100644 cpp/src/lookup/lookup_src_dst_mg.cu create mode 100644 cpp/src/lookup/lookup_src_dst_sg.cu create mode 100644 cpp/tests/c_api/mg_lookup_src_dst_test.c create mode 100644 python/pylibcugraph/pylibcugraph/_cugraph_c/lookup_src_dst.pxd diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index d33d9b6c31d..9a9c445ed54 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -652,6 +652,7 @@ add_library(cugraph_c src/c_api/random.cpp src/c_api/similarity.cpp src/c_api/leiden.cpp + src/c_api/lookup_src_dst.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/src_dst_lookup_container.hpp b/cpp/include/cugraph/src_dst_lookup_container.hpp index 4b1509f0367..7f5c68b408f 100644 --- a/cpp/include/cugraph/src_dst_lookup_container.hpp +++ b/cpp/include/cugraph/src_dst_lookup_container.hpp @@ -54,8 +54,9 @@ class lookup_container_t { lookup_container_t(raft::handle_t const& handle, std::vector types, std::vector type_counts); - lookup_container_t(const lookup_container_t&); + lookup_container_t(lookup_container_t&& other); + lookup_container_t& operator=(lookup_container_t&& other); void insert(raft::handle_t const& handle, edge_type_t typ, raft::device_span edge_ids_to_insert, diff --git a/cpp/include/cugraph_c/algorithms.h b/cpp/include/cugraph_c/algorithms.h index 1ccbc7bf868..ea8629a8217 100644 --- a/cpp/include/cugraph_c/algorithms.h +++ b/cpp/include/cugraph_c/algorithms.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, 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. @@ -28,10 +28,10 @@ #include #include #include +#include #include #include #include - /** * @} */ diff --git a/cpp/include/cugraph_c/lookup_src_dst.h b/cpp/include/cugraph_c/lookup_src_dst.h new file mode 100644 index 00000000000..f4d63572e82 --- /dev/null +++ b/cpp/include/cugraph_c/lookup_src_dst.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Opaque src-dst lookup container type + */ + +typedef struct { + int32_t align_; +} cugraph_lookup_container_t; + +/** + * @brief Opaque src-dst lookup result type + */ + +typedef struct { + int32_t align_; +} cugraph_lookup_result_t; + +/** + * @brief Build map to lookup source and destination using edge id and type + * + * @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 [out] lookup_container Lookup map + * @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_build_edge_id_and_type_to_src_dst_lookup_map( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + cugraph_lookup_container_t** lookup_container, + cugraph_error_t** error); + +/** + * @brief Lookup edge sources and destinations using edge ids and a single edge type. + * + * Use this function to lookup endpoints of edges belonging to the same edge type. + * + * @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] lookup_container Lookup map + * @param[in] edge_ids_to_lookup Edge ids to lookup + * @param[in] edge_type_to_lookup Edge types corresponding to edge ids in @p edge_ids_to_lookup + * @param [out] result Output from the lookup 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_lookup_endpoints_from_edge_ids_and_single_type( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + const cugraph_lookup_container_t* lookup_container, + const cugraph_type_erased_device_array_view_t* edge_ids_to_lookup, + int edge_type_to_lookup, + cugraph_lookup_result_t** result, + cugraph_error_t** error); + +/** + * @brief Lookup edge sources and destinations using edge ids and edge types. + * + * Use this function to lookup endpoints of edges belonging to different edge types. + * + * @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] lookup_container Lookup map + * @param[in] edge_ids_to_lookup Edge ids to lookup + * @param[in] edge_types_to_lookup Edge types corresponding to the edge ids in @p + * edge_ids_to_lookup + * @param [out] result Output from the lookup 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_lookup_endpoints_from_edge_ids_and_types( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + const cugraph_lookup_container_t* lookup_container, + const cugraph_type_erased_device_array_view_t* edge_ids_to_lookup, + const cugraph_type_erased_device_array_view_t* edge_types_to_lookup, + cugraph_lookup_result_t** result, + cugraph_error_t** error); + +/** + * @ingroup samplingC + * @brief Get the edge sources from the lookup result + * + * @param [in] result The result from src-dst lookup using edge ids and type(s) + * @return type erased array pointing to the edge sources + */ +cugraph_type_erased_device_array_view_t* cugraph_lookup_result_get_srcs( + const cugraph_lookup_result_t* result); + +/** + * @ingroup samplingC + * @brief Get the edge destinations from the lookup result + * + * @param [in] result The result from src-dst lookup using edge ids and type(s) + * @return type erased array pointing to the edge destinations + */ +cugraph_type_erased_device_array_view_t* cugraph_lookup_result_get_dsts( + const cugraph_lookup_result_t* result); + +/** + * @ingroup samplingC + * @brief Free a src-dst lookup result + * + * @param [in] result The result from src-dst lookup using edge ids and type(s) + */ +void cugraph_lookup_result_free(cugraph_lookup_result_t* result); + +#ifdef __cplusplus +} +#endif diff --git a/cpp/include/cugraph_c/sampling_algorithms.h b/cpp/include/cugraph_c/sampling_algorithms.h index 35f60e195be..a7490ad2c63 100644 --- a/cpp/include/cugraph_c/sampling_algorithms.h +++ b/cpp/include/cugraph_c/sampling_algorithms.h @@ -43,7 +43,7 @@ typedef struct { * needs to be transposed * @param [in] start_vertices Array of source vertices * @param [in] max_length Maximum length of the generated path - * @param [in] result Output from the node2vec call + * @param [out] result Output from the node2vec 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 @@ -64,7 +64,7 @@ cugraph_error_code_t cugraph_uniform_random_walks( * needs to be transposed * @param [in] start_vertices Array of source vertices * @param [in] max_length Maximum length of the generated path - * @param [in] result Output from the node2vec call + * @param [out] result Output from the node2vec 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 @@ -89,7 +89,7 @@ cugraph_error_code_t cugraph_biased_random_walks( * otherwise return as a dense matrix * @param [in] p The return parameter * @param [in] q The in/out parameter - * @param [in] result Output from the node2vec call + * @param [out] result Output from the node2vec 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 @@ -117,7 +117,7 @@ cugraph_error_code_t cugraph_node2vec_random_walks( * otherwise return as a dense matrix * @param [in] p The return parameter * @param [in] q The in/out parameter - * @param [in] result Output from the node2vec call + * @param [out] result Output from the node2vec 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 @@ -353,7 +353,7 @@ void cugraph_sampling_options_free(cugraph_sampling_options_t* options); * Opaque pointer defining the sampling options. * @param [in] do_expensive_check * A flag to run expensive checks for input arguments (if set to true) - * @param [in] result Output from the uniform_neighbor_sample call + * @param [out] result Output from the uniform_neighbor_sample 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 diff --git a/cpp/src/c_api/lookup_src_dst.cpp b/cpp/src/c_api/lookup_src_dst.cpp new file mode 100644 index 00000000000..1be2137ef2f --- /dev/null +++ b/cpp/src/c_api/lookup_src_dst.cpp @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "c_api/lookup_src_dst.hpp" + +#include "c_api/abstract_functor.hpp" +#include "c_api/graph.hpp" +#include "c_api/graph_helper.hpp" +#include "c_api/resource_handle.hpp" +#include "c_api/utils.hpp" + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +namespace { + +struct build_lookup_map_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_graph_t* graph_{nullptr}; + cugraph::c_api::cugraph_lookup_container_t* result_{}; + + build_lookup_map_functor(::cugraph_resource_handle_t const* handle, ::cugraph_graph_t* graph) + : abstract_functor(), + handle_(*reinterpret_cast(handle)->handle_), + graph_(reinterpret_cast(graph)) + { + } + + 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_ids = reinterpret_cast< + cugraph::edge_property_t, + edge_t>*>(graph_->edge_ids_); + + auto edge_types = reinterpret_cast< + cugraph::edge_property_t, + edge_type_type_t>*>(graph_->edge_types_); + + auto renumber_map = reinterpret_cast*>(graph_->number_map_); + + auto lookup_container = new cugraph::lookup_container_t(); + + *lookup_container = std::move(cugraph::build_edge_id_and_type_to_src_dst_lookup_map( + handle_, graph_view, edge_ids->view(), edge_types->view())); + + auto result = new cugraph::c_api::cugraph_lookup_container_t{ + graph_->edge_type_, graph_->edge_type_id_type_, graph_->vertex_type_, lookup_container}; + + result_ = reinterpret_cast(result); + } + } +}; + +struct lookup_using_edge_ids_of_single_type_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_graph_t* graph_{nullptr}; + cugraph::c_api::cugraph_lookup_container_t const* lookup_container_{nullptr}; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids_to_lookup_{nullptr}; + int edge_type_to_lookup_{}; + cugraph::c_api::cugraph_lookup_result_t* result_{nullptr}; + + lookup_using_edge_ids_of_single_type_functor( + cugraph_resource_handle_t const* handle, + cugraph_graph_t* graph, + cugraph_lookup_container_t const* lookup_container, + cugraph_type_erased_device_array_view_t const* edge_ids_to_lookup, + int edge_type_to_lookup) + : abstract_functor(), + handle_(*reinterpret_cast(handle)->handle_), + graph_(reinterpret_cast(graph)), + lookup_container_( + reinterpret_cast(lookup_container)), + edge_ids_to_lookup_( + reinterpret_cast( + edge_ids_to_lookup)), + edge_type_to_lookup_(edge_type_to_lookup) + { + } + + 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(); + + assert(edge_ids_to_lookup_); + + auto result = cugraph::lookup_endpoints_from_edge_ids_and_single_type( + handle_, + *(reinterpret_cast*>( + lookup_container_->lookup_container_)), + raft::device_span(edge_ids_to_lookup_->as_type(), + edge_ids_to_lookup_->size_), + edge_type_to_lookup_); + + auto renumber_map = reinterpret_cast*>(graph_->number_map_); + + auto result_srcs = std::move(std::get<0>(result)); + auto result_dsts = std::move(std::get<1>(result)); + + cugraph::unrenumber_int_vertices( + handle_, + result_srcs.data(), + result_srcs.size(), + renumber_map->data(), + graph_view.vertex_partition_range_lasts(), + false); + + cugraph::unrenumber_int_vertices( + handle_, + result_dsts.data(), + result_dsts.size(), + renumber_map->data(), + graph_view.vertex_partition_range_lasts(), + false); + + result_ = new cugraph::c_api::cugraph_lookup_result_t{ + new cugraph::c_api::cugraph_type_erased_device_array_t(result_srcs, + lookup_container_->vertex_type_), + new cugraph::c_api::cugraph_type_erased_device_array_t(result_dsts, + lookup_container_->vertex_type_)}; + } + } +}; + +struct lookup_using_edge_ids_and_types_functor : public cugraph::c_api::abstract_functor { + raft::handle_t const& handle_; + cugraph::c_api::cugraph_graph_t* graph_{nullptr}; + cugraph::c_api::cugraph_lookup_container_t const* lookup_container_{nullptr}; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_ids_to_lookup_{nullptr}; + cugraph::c_api::cugraph_type_erased_device_array_view_t const* edge_types_to_lookup_{nullptr}; + cugraph::c_api::cugraph_lookup_result_t* result_{nullptr}; + + lookup_using_edge_ids_and_types_functor( + cugraph_resource_handle_t const* handle, + cugraph_graph_t* graph, + cugraph_lookup_container_t const* lookup_container, + cugraph_type_erased_device_array_view_t const* edge_ids_to_lookup, + cugraph_type_erased_device_array_view_t const* edge_types_to_lookup) + : abstract_functor(), + handle_(*reinterpret_cast(handle)->handle_), + graph_(reinterpret_cast(graph)), + lookup_container_( + reinterpret_cast(lookup_container)), + edge_ids_to_lookup_( + reinterpret_cast( + edge_ids_to_lookup)), + edge_types_to_lookup_( + reinterpret_cast( + edge_types_to_lookup)) + { + } + + 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(); + + assert(edge_ids_to_lookup_); + assert(edge_types_to_lookup_); + + auto result = cugraph:: + lookup_endpoints_from_edge_ids_and_types( + handle_, + *(reinterpret_cast*>( + lookup_container_->lookup_container_)), + raft::device_span(edge_ids_to_lookup_->as_type(), + edge_ids_to_lookup_->size_), + raft::device_span( + edge_types_to_lookup_->as_type(), edge_types_to_lookup_->size_)); + + auto renumber_map = reinterpret_cast*>(graph_->number_map_); + + auto result_srcs = std::move(std::get<0>(result)); + auto result_dsts = std::move(std::get<1>(result)); + + cugraph::unrenumber_int_vertices( + handle_, + result_srcs.data(), + result_srcs.size(), + renumber_map->data(), + graph_view.vertex_partition_range_lasts(), + false); + + cugraph::unrenumber_int_vertices( + handle_, + result_dsts.data(), + result_dsts.size(), + renumber_map->data(), + graph_view.vertex_partition_range_lasts(), + false); + + result_ = new cugraph::c_api::cugraph_lookup_result_t{ + new cugraph::c_api::cugraph_type_erased_device_array_t(result_srcs, + lookup_container_->vertex_type_), + new cugraph::c_api::cugraph_type_erased_device_array_t(result_dsts, + lookup_container_->vertex_type_)}; + } + } +}; + +} // namespace + +extern "C" cugraph_error_code_t cugraph_build_edge_id_and_type_to_src_dst_lookup_map( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + cugraph_lookup_container_t** lookup_container, + cugraph_error_t** error) +{ + build_lookup_map_functor functor(handle, graph); + + return cugraph::c_api::run_algorithm(graph, functor, lookup_container, error); +} + +extern "C" cugraph_error_code_t cugraph_lookup_endpoints_from_edge_ids_and_types( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + const cugraph_lookup_container_t* lookup_container, + const cugraph_type_erased_device_array_view_t* edge_ids_to_lookup, + const cugraph_type_erased_device_array_view_t* edge_types_to_lookup, + cugraph_lookup_result_t** result, + cugraph_error_t** error) +{ + CAPI_EXPECTS( + reinterpret_cast(graph)->vertex_type_ == + reinterpret_cast(lookup_container)->vertex_type_, + CUGRAPH_INVALID_INPUT, + "vertex type of graph and lookup_container must match", + *error); + CAPI_EXPECTS( + reinterpret_cast(graph)->edge_type_ == + reinterpret_cast(lookup_container)->edge_type_, + CUGRAPH_INVALID_INPUT, + "edge type of graph and lookup_container must match", + *error); + + CAPI_EXPECTS(reinterpret_cast(graph)->edge_type_id_type_ == + reinterpret_cast(lookup_container) + ->edge_type_id_type_, + CUGRAPH_INVALID_INPUT, + "edge type id type of graph and lookup_container must match", + *error); + + lookup_using_edge_ids_and_types_functor functor( + handle, graph, lookup_container, edge_ids_to_lookup, edge_types_to_lookup); + return cugraph::c_api::run_algorithm(graph, functor, result, error); +} + +extern "C" cugraph_error_code_t cugraph_lookup_endpoints_from_edge_ids_and_single_type( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + const cugraph_lookup_container_t* lookup_container, + const cugraph_type_erased_device_array_view_t* edge_ids_to_lookup, + int edge_type_to_lookup, + cugraph_lookup_result_t** result, + cugraph_error_t** error) +{ + CAPI_EXPECTS( + reinterpret_cast(graph)->vertex_type_ == + reinterpret_cast(lookup_container)->vertex_type_, + CUGRAPH_INVALID_INPUT, + "vertex type of graph and lookup_container must match", + *error); + CAPI_EXPECTS( + reinterpret_cast(graph)->edge_type_ == + reinterpret_cast(lookup_container)->edge_type_, + CUGRAPH_INVALID_INPUT, + "edge type of graph and lookup_container must match", + *error); + + CAPI_EXPECTS(reinterpret_cast(graph)->edge_type_id_type_ == + reinterpret_cast(lookup_container) + ->edge_type_id_type_, + CUGRAPH_INVALID_INPUT, + "edge type id type of graph and lookup_container must match", + *error); + + lookup_using_edge_ids_of_single_type_functor functor( + handle, graph, lookup_container, edge_ids_to_lookup, edge_type_to_lookup); + return cugraph::c_api::run_algorithm(graph, functor, result, error); +} + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_lookup_result_get_srcs( + const cugraph_lookup_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + return reinterpret_cast( + internal_pointer->srcs_->view()); +} + +extern "C" cugraph_type_erased_device_array_view_t* cugraph_lookup_result_get_dsts( + const cugraph_lookup_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + return reinterpret_cast( + internal_pointer->dsts_->view()); +} + +extern "C" void cugraph_lookup_result_free(cugraph_lookup_result_t* result) +{ + auto internal_pointer = reinterpret_cast(result); + delete internal_pointer->srcs_; + delete internal_pointer->dsts_; + delete internal_pointer; +} diff --git a/cpp/src/c_api/lookup_src_dst.hpp b/cpp/src/c_api/lookup_src_dst.hpp new file mode 100644 index 00000000000..9dad3a59b97 --- /dev/null +++ b/cpp/src/c_api/lookup_src_dst.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "c_api/array.hpp" +#include "c_api/error.hpp" + +#include +#include + +#include + +namespace cugraph { +namespace c_api { + +struct cugraph_lookup_container_t { + cugraph_data_type_id_t edge_type_; + cugraph_data_type_id_t edge_type_id_type_; + cugraph_data_type_id_t vertex_type_; + + void* lookup_container_; +}; + +struct cugraph_lookup_result_t { + cugraph_type_erased_device_array_t* srcs_{nullptr}; + cugraph_type_erased_device_array_t* dsts_{nullptr}; +}; + +} // namespace c_api +} // namespace cugraph diff --git a/cpp/src/lookup/lookup_src_dst_impl.cuh b/cpp/src/lookup/lookup_src_dst_impl.cuh index 4182cb5f65e..1c8c39fd6dd 100644 --- a/cpp/src/lookup/lookup_src_dst_impl.cuh +++ b/cpp/src/lookup/lookup_src_dst_impl.cuh @@ -310,10 +310,19 @@ lookup_container_t::lookup_container_ template lookup_container_t::lookup_container_t( - const lookup_container_t&) + lookup_container_t&& other) + : pimpl{std::move(other.pimpl)} { } +template +lookup_container_t& +lookup_container_t::operator=(lookup_container_t&& other) +{ + pimpl = std::move(other.pimpl); + return *this; +} + template void lookup_container_t::insert( raft::handle_t const& handle, diff --git a/cpp/src/lookup/lookup_src_dst_mg.cu b/cpp/src/lookup/lookup_src_dst_mg.cu new file mode 100644 index 00000000000..417f881f1a3 --- /dev/null +++ b/cpp/src/lookup/lookup_src_dst_mg.cu @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lookup/lookup_src_dst_impl.cuh" + +namespace cugraph { + +template class lookup_container_t; +template class lookup_container_t; +template class lookup_container_t; + +template lookup_container_t build_edge_id_and_type_to_src_dst_lookup_map( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_property_view_t edge_id_view, + edge_property_view_t edge_type_view); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_single_type( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + int32_t edge_type_to_lookup); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_types( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + raft::device_span edge_types_to_lookup); + +template lookup_container_t build_edge_id_and_type_to_src_dst_lookup_map( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_property_view_t edge_id_view, + edge_property_view_t edge_type_view); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_single_type( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + int32_t edge_type_to_lookup); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_types( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + raft::device_span edge_types_to_lookup); + +template lookup_container_t build_edge_id_and_type_to_src_dst_lookup_map( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_property_view_t edge_id_view, + edge_property_view_t edge_type_view); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_single_type( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + int32_t edge_type_to_lookup); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_types( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + raft::device_span edge_types_to_lookup); + +} // namespace cugraph diff --git a/cpp/src/lookup/lookup_src_dst_sg.cu b/cpp/src/lookup/lookup_src_dst_sg.cu new file mode 100644 index 00000000000..af4594cbe51 --- /dev/null +++ b/cpp/src/lookup/lookup_src_dst_sg.cu @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "lookup/lookup_src_dst_impl.cuh" + +namespace cugraph { + +template class lookup_container_t; +template class lookup_container_t; +template class lookup_container_t; + +template lookup_container_t build_edge_id_and_type_to_src_dst_lookup_map( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_property_view_t edge_id_view, + edge_property_view_t edge_type_view); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_single_type( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + int32_t const edge_type_to_lookup); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_types( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + raft::device_span edge_types_to_lookup); + +template lookup_container_t build_edge_id_and_type_to_src_dst_lookup_map( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_property_view_t edge_id_view, + edge_property_view_t edge_type_view); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_single_type( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + int32_t edge_type_to_lookup); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_types( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + raft::device_span edge_types_to_lookup); + +template lookup_container_t build_edge_id_and_type_to_src_dst_lookup_map( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_property_view_t edge_id_view, + edge_property_view_t edge_type_view); + +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_single_type( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + int32_t edge_type_to_lookup); +template std::tuple, rmm::device_uvector> +lookup_endpoints_from_edge_ids_and_types( + raft::handle_t const& handle, + lookup_container_t const& search_container, + raft::device_span edge_ids_to_lookup, + raft::device_span edge_types_to_lookup); + +} // namespace cugraph diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index c94ffed3b9f..3ad27b503a4 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -772,6 +772,7 @@ if(BUILD_CUGRAPH_MG_TESTS) ConfigureCTestMG(MG_CAPI_EDGE_BETWEENNESS_CENTRALITY_TEST c_api/mg_edge_betweenness_centrality_test.c) ConfigureCTestMG(MG_CAPI_HITS_TEST c_api/mg_hits_test.c) ConfigureCTestMG(MG_CAPI_UNIFORM_NEIGHBOR_SAMPLE_TEST c_api/mg_uniform_neighbor_sample_test.c) + ConfigureCTestMG(MG_CAPI_LOOKUP_SRC_DST_TEST c_api/mg_lookup_src_dst_test.c) ConfigureCTestMG(MG_CAPI_RANDOM_WALKS_TEST c_api/mg_random_walks_test.c) ConfigureCTestMG(MG_CAPI_TRIANGLE_COUNT_TEST c_api/mg_triangle_count_test.c) ConfigureCTestMG(MG_CAPI_LOUVAIN_TEST c_api/mg_louvain_test.c) diff --git a/cpp/tests/c_api/mg_lookup_src_dst_test.c b/cpp/tests/c_api/mg_lookup_src_dst_test.c new file mode 100644 index 00000000000..0cffffeb425 --- /dev/null +++ b/cpp/tests/c_api/mg_lookup_src_dst_test.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2024, NVIDIA CORPORATION. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mg_test_utils.h" /* RUN_TEST */ + +#include +#include + +#include + +typedef int32_t vertex_t; +typedef int32_t edge_t; +typedef int32_t edge_type_t; + +typedef float weight_t; + +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; + +int generic_lookup_src_dst_test(const cugraph_resource_handle_t* handle, + vertex_t* h_srcs, + vertex_t* h_dsts, + edge_t* h_edge_ids, + edge_type_t* h_edge_types, + size_t num_vertices, + size_t num_edges, + bool_t store_transposed, + edge_t* edge_ids_to_lookup, + edge_type_t* edge_types_to_lookup, + size_t num_edge_ids_to_lookup, + vertex_t* h_expected_srcs, + vertex_t* h_expected_dsts) +{ + int test_ret_value = 0; + + cugraph_error_code_t ret_code = CUGRAPH_SUCCESS; + cugraph_error_t* ret_error; + + cugraph_graph_t* graph = NULL; + cugraph_lookup_container_t* lookup_container = NULL; + cugraph_lookup_result_t* result = NULL; + + int rank = cugraph_resource_handle_get_rank(handle); + + ret_code = create_mg_test_graph_new(handle, + vertex_tid, + edge_tid, + h_srcs, + h_dsts, + weight_tid, + NULL, + edge_type_tid, + h_edge_types, + edge_id_tid, + h_edge_ids, + num_edges, + FALSE, /*store_transposed*/ + TRUE, /*renumber*/ + TRUE, /*is_symmetric*/ + FALSE, /*is_multigraph*/ + &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_build_edge_id_and_type_to_src_dst_lookup_map( + handle, graph, &lookup_container, &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."); + + cugraph_type_erased_device_array_t* d_edge_ids_to_lookup = NULL; + cugraph_type_erased_device_array_view_t* d_edge_ids_to_lookup_view = NULL; + + ret_code = cugraph_type_erased_device_array_create( + handle, num_edge_ids_to_lookup, edge_id_tid, &d_edge_ids_to_lookup, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "d_edge_ids_to_lookup create failed."); + + d_edge_ids_to_lookup_view = cugraph_type_erased_device_array_view(d_edge_ids_to_lookup); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, d_edge_ids_to_lookup_view, (byte_t*)edge_ids_to_lookup, &ret_error); + + cugraph_type_erased_device_array_t* d_edge_types_to_lookup = NULL; + cugraph_type_erased_device_array_view_t* d_edge_types_to_lookup_view = NULL; + + ret_code = cugraph_type_erased_device_array_create( + handle, num_edge_ids_to_lookup, edge_type_tid, &d_edge_types_to_lookup, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "d_edge_types_to_lookup create failed."); + + d_edge_types_to_lookup_view = cugraph_type_erased_device_array_view(d_edge_types_to_lookup); + + ret_code = cugraph_type_erased_device_array_view_copy_from_host( + handle, d_edge_types_to_lookup_view, (byte_t*)edge_types_to_lookup, &ret_error); + + ret_code = cugraph_lookup_endpoints_from_edge_ids_and_types(handle, + graph, + lookup_container, + d_edge_ids_to_lookup_view, + d_edge_types_to_lookup_view, + &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* d_srcs; + cugraph_type_erased_device_array_view_t* d_dsts; + + d_srcs = cugraph_lookup_result_get_srcs(result); + d_dsts = cugraph_lookup_result_get_dsts(result); + + vertex_t h_result_srcs[num_edge_ids_to_lookup]; + edge_t h_result_dsts[num_edge_ids_to_lookup]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_result_srcs, d_srcs, &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_result_dsts, d_dsts, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + size_t result_num_edges = cugraph_type_erased_device_array_view_size(d_srcs); + + TEST_ALWAYS_ASSERT(result_num_edges == num_edge_ids_to_lookup, + "number of edges in returned result") + + for (int i = 0; i < num_edge_ids_to_lookup; i++) { + vertex_t src = (h_result_srcs[i] < h_result_dsts[i]) ? h_result_srcs[i] : h_result_dsts[i]; + vertex_t dst = (h_result_srcs[i] >= h_result_dsts[i]) ? h_result_srcs[i] : h_result_dsts[i]; + TEST_ASSERT(test_ret_value, + src == h_expected_srcs[i], + "expected sources don't match with returned ones"); + TEST_ASSERT(test_ret_value, + dst == h_expected_dsts[i], + "expected destinations don't match with returned ones"); + } + } + + cugraph_lookup_result_free(result); + + ret_code = cugraph_lookup_endpoints_from_edge_ids_and_single_type(handle, + graph, + lookup_container, + d_edge_ids_to_lookup_view, + edge_types_to_lookup[0], + &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* d_srcs; + cugraph_type_erased_device_array_view_t* d_dsts; + + d_srcs = cugraph_lookup_result_get_srcs(result); + d_dsts = cugraph_lookup_result_get_dsts(result); + + vertex_t h_result_srcs[num_edge_ids_to_lookup]; + edge_t h_result_dsts[num_edge_ids_to_lookup]; + + ret_code = cugraph_type_erased_device_array_view_copy_to_host( + handle, (byte_t*)h_result_srcs, d_srcs, &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_result_dsts, d_dsts, &ret_error); + TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); + + size_t result_num_edges = cugraph_type_erased_device_array_view_size(d_srcs); + + TEST_ALWAYS_ASSERT(result_num_edges == num_edge_ids_to_lookup, + "number of edges in returned result") + + for (int i = 0; i < num_edge_ids_to_lookup; i++) { + vertex_t src = (h_result_srcs[i] < h_result_dsts[i]) ? h_result_srcs[i] : h_result_dsts[i]; + vertex_t dst = (h_result_srcs[i] >= h_result_dsts[i]) ? h_result_srcs[i] : h_result_dsts[i]; + TEST_ASSERT(test_ret_value, + src == h_expected_srcs[i], + "expected sources don't match with returned ones"); + TEST_ASSERT(test_ret_value, + dst == h_expected_dsts[i], + "expected destinations don't match with returned ones"); + } + } + + cugraph_lookup_result_free(result); + + cugraph_mg_graph_free(graph); + cugraph_error_free(ret_error); + + return test_ret_value; +} + +int test_lookup_src_dst_test(const cugraph_resource_handle_t* handle) +{ + size_t num_edges = 10; + size_t num_vertices = 5; + + vertex_t h_srcs[] = {7, 1, 1, 2, 1, 8, 3, 4, 0, 0}; + vertex_t h_dsts[] = {8, 3, 4, 0, 0, 7, 1, 1, 2, 1}; + + edge_t h_edge_ids[] = {78, 13, 14, 20, 10, 78, 13, 14, 20, 10}; + + edge_type_t h_edge_types[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2}; + + edge_t edge_ids_to_lookup[] = {10, 12, 78, 20}; + edge_t edge_types_to_lookup[] = {2, 0, 3, 2}; + + // expected results + vertex_t h_expected_srcs[] = {0, -1, -1, 0}; + vertex_t h_expected_dsts[] = {1, -1, -1, 2}; + size_t num_edge_ids_to_lookup = 4; + + return generic_lookup_src_dst_test(handle, + h_srcs, + h_dsts, + h_edge_ids, + h_edge_types, + num_vertices, + num_edges, + FALSE, + edge_ids_to_lookup, + edge_types_to_lookup, + num_edge_ids_to_lookup, + h_expected_srcs, + h_expected_dsts); + + return 0; +} + +/******************************************************************************/ + +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_lookup_src_dst_test, handle); + + cugraph_free_resource_handle(handle); + free_mg_raft_handle(raft_handle); + + return result; +} diff --git a/python/pylibcugraph/pylibcugraph/_cugraph_c/lookup_src_dst.pxd b/python/pylibcugraph/pylibcugraph/_cugraph_c/lookup_src_dst.pxd new file mode 100644 index 00000000000..710ca7d113b --- /dev/null +++ b/python/pylibcugraph/pylibcugraph/_cugraph_c/lookup_src_dst.pxd @@ -0,0 +1,72 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Have cython use python 3 syntax +# cython: language_level = 3 + + +from pylibcugraph._cugraph_c.resource_handle cimport ( + bool_t, + cugraph_resource_handle_t, +) +from pylibcugraph._cugraph_c.error cimport ( + cugraph_error_code_t, + cugraph_error_t, +) +from pylibcugraph._cugraph_c.array cimport ( + cugraph_type_erased_device_array_view_t, +) +from pylibcugraph._cugraph_c.graph cimport ( + cugraph_graph_t, +) + +cdef extern from "cugraph_c/lookup_src_dst.h": + ########################################################################### + + ctypedef struct cugraph_lookup_container_t: + pass + + ctypedef struct cugraph_lookup_result_t: + pass + + cdef cugraph_error_code_t cugraph_build_edge_id_and_type_to_src_dst_lookup_map( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + cugraph_lookup_container_t** lookup_container, + cugraph_error_t** error) + + cdef cugraph_error_code_t cugraph_lookup_endpoints_from_edge_ids_and_single_type( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + const cugraph_lookup_container_t* lookup_container, + const cugraph_type_erased_device_array_view_t* edge_ids_to_lookup, + int edge_type_to_lookup, + cugraph_lookup_result_t** result, + cugraph_error_t** error) + + cdef cugraph_error_code_t cugraph_lookup_endpoints_from_edge_ids_and_types( + const cugraph_resource_handle_t* handle, + cugraph_graph_t* graph, + const cugraph_lookup_container_t* lookup_container, + const cugraph_type_erased_device_array_view_t* edge_ids_to_lookup, + const cugraph_type_erased_device_array_view_t* edge_types_to_lookup, + cugraph_lookup_result_t** result, + cugraph_error_t** error) + + cdef cugraph_type_erased_device_array_view_t* cugraph_lookup_result_get_srcs( + const cugraph_lookup_result_t* result) + + cdef cugraph_type_erased_device_array_view_t* cugraph_lookup_result_get_dsts( + const cugraph_lookup_result_t* result) + + cdef void cugraph_lookup_result_free(cugraph_lookup_result_t* result)