Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define C API and implement induced subgraph #2854

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ endif()
# which should give us a better parallel schedule.

set(CUGRAPH_SOURCES
src/detail/shuffle_wrappers.cu
src/detail/shuffle_vertices.cu
src/detail/shuffle_edgelist.cu
src/detail/collect_local_vertex_values.cu
src/detail/groupby_and_count.cu
src/sampling/random_walks_mg.cu
src/community/detail/common_methods_mg.cu
src/community/detail/common_methods_sg.cu
Expand Down Expand Up @@ -350,6 +353,9 @@ add_library(cugraph_c
src/c_api/core_number.cpp
src/c_api/core_result.cpp
src/c_api/k_core.cpp
src/c_api/induced_subgraph.cpp
src/c_api/induced_subgraph_helper.cu
src/c_api/induced_subgraph_result.cpp
src/c_api/hits.cpp
src/c_api/bfs.cpp
src/c_api/sssp.cpp
Expand All @@ -373,13 +379,23 @@ set_target_properties(cugraph_c
# set target compile options
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CUDA_STANDARD 17
CUDA_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE ON
INTERFACE_POSITION_INDEPENDENT_CODE ON
)

target_compile_options(cugraph_c
PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CUGRAPH_CXX_FLAGS}>"
PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CUGRAPH_CXX_FLAGS}>"
"$<$<COMPILE_LANGUAGE:CUDA>:${CUGRAPH_CUDA_FLAGS}>"
)

# Per-thread default stream option see https://docs.nvidia.com/cuda/cuda-runtime-api/stream-sync-behavior.html
# The per-thread default stream does not synchronize with other streams
target_compile_definitions(cugraph_c PUBLIC CUDA_API_PER_THREAD_DEFAULT_STREAM)

target_link_options(cugraph_c PRIVATE "${CUGRAPH_BINARY_DIR}/fatbin.ld")

################################################################################
# - C-API include paths --------------------------------------------------------
target_include_directories(cugraph_c
Expand Down
3 changes: 1 addition & 2 deletions cpp/include/cugraph/graph_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,7 @@ void relabel(raft::handle_t const& handle,
* handles to various CUDA libraries) to run graph algorithms.
* @param graph_view Graph view object, we extract induced subgraphs from @p graph_view.
* @param subgraph_offsets Span pointing to subgraph vertex offsets
* @param subgraph_vertices Span pointing to subgraph vertices The elements of @p subgraph_vertices
* for each subgraph should be sorted in ascending order and unique.
* @param subgraph_vertices Span pointing to subgraph vertices.
* @p subgraph_offsets and @p subgraph_vertices provide vertex sets (or local vertex sets in
* multi-GPU) for @p subgraph_offsets.size() - 1 subgraphs to extract. For the i'th subgraph to
* extract, one can extract the (local-)vertex set by accessing a subset of @p subgraph_vertices,
Expand Down
12 changes: 11 additions & 1 deletion cpp/include/cugraph/visitors/enum_mapping.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
namespace cugraph {
namespace visitors {

enum class DTypes { INT32 = 0, INT64, FLOAT32, FLOAT64, NTYPES };
enum class DTypes { INT32 = 0, INT64, FLOAT32, FLOAT64, SIZE_T, NTYPES };

template <DTypes>
struct DMapType;
Expand All @@ -49,6 +49,11 @@ struct DMapType<DTypes::FLOAT64> {
using type = double;
};

template <>
struct DMapType<DTypes::SIZE_T> {
using type = size_t;
};

template <typename T>
struct reverse_dmap_t;

Expand All @@ -72,5 +77,10 @@ struct reverse_dmap_t<double> {
static constexpr DTypes type_id = DTypes::FLOAT64;
};

template <>
struct reverse_dmap_t<size_t> {
static constexpr DTypes type_id = DTypes::SIZE_T;
};

} // namespace visitors
} // namespace cugraph
84 changes: 83 additions & 1 deletion cpp/include/cugraph_c/graph_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void cugraph_vertex_pairs_free(cugraph_vertex_pairs_t* vertex_pairs);
* @param [in] start_vertices Optional type erased array of starting vertices
* If NULL use all, if specified compute two-hop
* neighbors for these starting vertices
* @param [out] vertex_pairs Opaque pointer to resulting vertex pairs
* @param [out] result Opaque pointer to resulting vertex pairs
* @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
Expand All @@ -99,6 +99,88 @@ cugraph_error_code_t cugraph_two_hop_neighbors(
cugraph_vertex_pairs_t** result,
cugraph_error_t** error);

/**
* @brief Opaque induced subgraph type
*/
typedef struct {
int32_t align_;
} cugraph_induced_subgraph_result_t;

/**
* @brief Get the source vertex ids
*
* @param [in] induced_subgraph Opaque pointer to induced subgraph
* @return type erased array view of source vertex ids
*/
cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_sources(
cugraph_induced_subgraph_result_t* induced_subgraph);

/**
* @brief Get the destination vertex ids
*
* @param [in] induced_subgraph Opaque pointer to induced subgraph
* @return type erased array view of destination vertex ids
*/
cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_destinations(
cugraph_induced_subgraph_result_t* induced_subgraph);

/**
* @brief Get the edge weights
*
* @param [in] induced_subgraph Opaque pointer to induced subgraph
* @return type erased array view of edge weights
*/
cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_edge_weights(
cugraph_induced_subgraph_result_t* induced_subgraph);

/**
* @brief Get the subgraph offsets
*
* @param [in] induced_subgraph Opaque pointer to induced subgraph
* @return type erased array view of subgraph identifiers
*/
cugraph_type_erased_device_array_view_t* cugraph_induced_subgraph_get_subgraph_offsets(
cugraph_induced_subgraph_result_t* induced_subgraph);

/**
* @brief Free induced subgraph
*
* @param [in] induced subgraph Opaque pointer to induced subgraph
*/
void cugraph_induced_subgraph_result_free(cugraph_induced_subgraph_result_t* induced_subgraph);

/**
* @brief Extract induced subgraph(s)
*
* Given a list of vertex ids, extract a list of edges that represent the subgraph
* containing only the specified vertex ids.
*
* This function will do multiple subgraph extractions concurrently. The vertex ids
* are specified in CSR-style, with @p subgraph_vertices being a list of vertex ids
* and @p subgraph_offsets[i] identifying the start offset for each extracted subgraph
*
* @param [in] handle Handle for accessing resources
* @param [in] graph Pointer to graph
* @param [in] subgraph_offsets Type erased array of subgraph offsets into
* @p subgraph_vertices
* @param [in] subgraph_vertices Type erased array of vertices to include in
* extracted subgraph.
* @param [in] do_expensive_check A flag to run expensive checks for input arguments (if set to
* `true`).
* @param [out] result Opaque pointer to induced subgraph result
* @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_extract_induced_subgraph(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
const cugraph_type_erased_device_array_view_t* subgraph_offsets,
const cugraph_type_erased_device_array_view_t* subgraph_vertices,
bool_t do_expensive_check,
cugraph_induced_subgraph_result_t** result,
cugraph_error_t** error);

#ifdef __cplusplus
}
#endif
2 changes: 1 addition & 1 deletion cpp/include/cugraph_c/resource_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ typedef enum bool_ { FALSE = 0, TRUE = 1 } bool_t;

typedef int8_t byte_t;

typedef enum data_type_id_ { INT32 = 0, INT64, FLOAT32, FLOAT64, NTYPES } data_type_id_t;
typedef enum data_type_id_ { INT32 = 0, INT64, FLOAT32, FLOAT64, SIZE_T, NTYPES } data_type_id_t;

typedef struct cugraph_resource_handle_ {
int32_t align_;
Expand Down
15 changes: 9 additions & 6 deletions cpp/src/c_api/array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
namespace cugraph {
namespace c_api {

cugraph::visitors::DTypes dtypes_mapping[] = {cugraph::visitors::DTypes::INT32,
cugraph::visitors::DTypes::INT64,
cugraph::visitors::DTypes::FLOAT32,
cugraph::visitors::DTypes::FLOAT64};

size_t data_type_sz[] = {4, 8, 4, 8};
cugraph::visitors::DTypes dtypes_mapping[] = {
cugraph::visitors::DTypes::INT32,
cugraph::visitors::DTypes::INT64,
cugraph::visitors::DTypes::FLOAT32,
cugraph::visitors::DTypes::FLOAT64,
cugraph::visitors::DTypes::SIZE_T,
};

size_t data_type_sz[] = {4, 8, 4, 8, 8};

} // namespace c_api
} // namespace cugraph
Expand Down
179 changes: 179 additions & 0 deletions cpp/src/c_api/induced_subgraph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* 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 <cugraph_c/algorithms.h>

#include <c_api/abstract_functor.hpp>
#include <c_api/graph.hpp>
#include <c_api/induced_subgraph_helper.hpp>
#include <c_api/induced_subgraph_result.hpp>
#include <c_api/resource_handle.hpp>
#include <c_api/utils.hpp>

#include <cugraph/algorithms.hpp>
#include <cugraph/detail/shuffle_wrappers.hpp>
#include <cugraph/detail/utility_wrappers.hpp>
#include <cugraph/graph_functions.hpp>

namespace {

struct induced_subgraph_functor : public cugraph::c_api::abstract_functor {
raft::handle_t const& handle_;
cugraph::c_api::cugraph_graph_t* graph_{};
cugraph::c_api::cugraph_type_erased_device_array_view_t const* subgraph_offsets_{};
cugraph::c_api::cugraph_type_erased_device_array_view_t const* subgraph_vertices_{};
bool do_expensive_check_{};
cugraph::c_api::cugraph_induced_subgraph_result_t* result_{};

induced_subgraph_functor(cugraph_resource_handle_t const* handle,
cugraph_graph_t* graph,
cugraph_type_erased_device_array_view_t const* subgraph_offsets,
cugraph_type_erased_device_array_view_t const* subgraph_vertices,
bool do_expensive_check)
: abstract_functor(),
handle_(*reinterpret_cast<cugraph::c_api::cugraph_resource_handle_t const*>(handle)->handle_),
graph_(reinterpret_cast<cugraph::c_api::cugraph_graph_t*>(graph)),
subgraph_offsets_(
reinterpret_cast<cugraph::c_api::cugraph_type_erased_device_array_view_t const*>(
subgraph_offsets)),
subgraph_vertices_(
reinterpret_cast<cugraph::c_api::cugraph_type_erased_device_array_view_t const*>(
subgraph_vertices)),
do_expensive_check_(do_expensive_check)
{
}

template <typename vertex_t,
typename edge_t,
typename weight_t,
typename edge_type_type_t,
bool store_transposed,
bool multi_gpu>
void operator()()
{
// FIXME: Think about how to handle SG vice MG
if constexpr (!cugraph::is_candidate<vertex_t, edge_t, weight_t>::value) {
unsupported();
} else {
// induced subgraph expects store_transposed == false
if constexpr (store_transposed) {
error_code_ = cugraph::c_api::
transpose_storage<vertex_t, edge_t, weight_t, store_transposed, multi_gpu>(
handle_, graph_, error_.get());
if (error_code_ != CUGRAPH_SUCCESS) return;
}

auto graph =
reinterpret_cast<cugraph::graph_t<vertex_t, edge_t, weight_t, false, multi_gpu>*>(
graph_->graph_);

auto graph_view = graph->view();

auto number_map = reinterpret_cast<rmm::device_uvector<vertex_t>*>(graph_->number_map_);

rmm::device_uvector<size_t> subgraph_offsets(0, handle_.get_stream());
rmm::device_uvector<vertex_t> subgraph_vertices(subgraph_vertices_->size_,
handle_.get_stream());

raft::copy(subgraph_vertices.data(),
subgraph_vertices_->as_type<vertex_t>(),
subgraph_vertices_->size_,
handle_.get_stream());

if constexpr (multi_gpu) {
std::tie(subgraph_vertices, subgraph_offsets) =
cugraph::c_api::detail::shuffle_vertex_ids_and_offsets(
handle_,
std::move(subgraph_vertices),
raft::device_span<size_t const>{subgraph_offsets_->as_type<size_t>(),
subgraph_offsets_->size_});
} else {
subgraph_offsets.resize(subgraph_offsets_->size_, handle_.get_stream());
raft::copy(subgraph_offsets.data(),
subgraph_offsets_->as_type<size_t>(),
subgraph_offsets_->size_,
handle_.get_stream());
}

//
// Need to renumber subgraph_vertices
//
cugraph::renumber_local_ext_vertices<vertex_t, multi_gpu>(
handle_,
subgraph_vertices.data(),
subgraph_vertices.size(),
number_map->data(),
graph_view.local_vertex_partition_range_first(),
graph_view.local_vertex_partition_range_last(),
do_expensive_check_);

auto [src, dst, wgt, graph_offsets] = cugraph::extract_induced_subgraphs(
handle_,
graph_view,
raft::device_span<size_t const>{subgraph_offsets.data(), subgraph_offsets.size()},
raft::device_span<vertex_t const>{subgraph_vertices.data(), subgraph_vertices.size()},
do_expensive_check_);

cugraph::unrenumber_int_vertices<vertex_t, multi_gpu>(
handle_,
src.data(),
src.size(),
number_map->data(),
graph_view.vertex_partition_range_lasts(),
do_expensive_check_);

cugraph::unrenumber_int_vertices<vertex_t, multi_gpu>(
handle_,
dst.data(),
dst.size(),
number_map->data(),
graph_view.vertex_partition_range_lasts(),
do_expensive_check_);

result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{
new cugraph::c_api::cugraph_type_erased_device_array_t(src, graph_->vertex_type_),
new cugraph::c_api::cugraph_type_erased_device_array_t(dst, graph_->vertex_type_),
wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*wgt, graph_->weight_type_)
: NULL,
new cugraph::c_api::cugraph_type_erased_device_array_t(graph_offsets, SIZE_T)};
}
}
};

} // namespace

extern "C" cugraph_error_code_t cugraph_extract_induced_subgraph(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
const cugraph_type_erased_device_array_view_t* subgraph_offsets,
const cugraph_type_erased_device_array_view_t* subgraph_vertices,
bool_t do_expensive_check,
cugraph_induced_subgraph_result_t** result,
cugraph_error_t** error)
{
CAPI_EXPECTS(reinterpret_cast<cugraph::c_api::cugraph_graph_t*>(graph)->vertex_type_ ==
reinterpret_cast<cugraph::c_api::cugraph_type_erased_device_array_view_t const*>(
subgraph_vertices)
->type_,
CUGRAPH_INVALID_INPUT,
"vertex type of graph and subgraph_vertices must match",
*error);

induced_subgraph_functor functor(
handle, graph, subgraph_offsets, subgraph_vertices, do_expensive_check);

return cugraph::c_api::run_algorithm(graph, functor, result, error);
}
Loading