Skip to content

Commit

Permalink
Define triangle_count C API (rapidsai#2271)
Browse files Browse the repository at this point in the history
This defines the C API for triangle counting.

This PR is independent of rapidsai#2253 and can be merged independently.  The change here defines the C API and returns the `CUGRAPH_NOT_IMPLEMENTED` error when called.  Once rapidsai#2253 is completed and merged, a follow-up PR will fill in the C API implementation for triangle counting (although the code is written and untested in this PR).

Authors:
  - Chuck Hastings (https://github.com/ChuckHastings)

Approvers:
  - Seunghwa Kang (https://github.com/seunghwak)
  - Rick Ratzel (https://github.com/rlratzel)

URL: rapidsai#2271
  • Loading branch information
ChuckHastings authored May 16, 2022
1 parent 4a6263a commit 046cbd2
Show file tree
Hide file tree
Showing 8 changed files with 571 additions and 2 deletions.
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ add_library(cugraph_c
src/c_api/sssp.cpp
src/c_api/extract_paths.cpp
src/c_api/random_walks.cpp
src/c_api/triangle_count.cpp
src/c_api/uniform_neighbor_sampling.cpp
src/c_api/labeling_result.cpp
src/c_api/weakly_connected_components.cpp
Expand Down
4 changes: 2 additions & 2 deletions cpp/include/cugraph_c/algorithms.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
#include <cugraph_c/graph.h>
#include <cugraph_c/resource_handle.h>

#include <cugraph_c/centrality_algorithms.h>

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -405,4 +403,6 @@ void cugraph_sample_result_free(cugraph_sample_result_t* result);
}
#endif

#include <cugraph_c/centrality_algorithms.h>
#include <cugraph_c/community_algorithms.h>
#include <cugraph_c/labeling_algorithms.h>
77 changes: 77 additions & 0 deletions cpp/include/cugraph_c/community_algorithms.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 <cugraph_c/error.h>
#include <cugraph_c/graph.h>
#include <cugraph_c/resource_handle.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Opaque triangle counting result type
*/
typedef struct {
int32_t align_;
} cugraph_triangle_count_result_t;

/**
* @brief Triangle Counting
*
* @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] start Device array of vertices we want to count triangles for. If NULL
* the entire set of vertices in the graph is processed
* @param [in] do_expensive_check
* A flag to run expensive checks for input arguments (if set to true)
* @param [in] result Output from the triangle_count 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_triangle_count(const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
const cugraph_type_erased_device_array_view_t* start,
bool_t do_expensive_check,
cugraph_triangle_count_result_t** result,
cugraph_error_t** error);

/**
* @brief Get triangle counting vertices
*/
cugraph_type_erased_device_array_view_t* cugraph_triangle_count_result_get_vertices(
cugraph_triangle_count_result_t* result);

/**
* @brief Get triangle counting counts
*/
cugraph_type_erased_device_array_view_t* cugraph_triangle_count_result_get_counts(
cugraph_triangle_count_result_t* result);

/**
* @brief Free a triangle count result
*
* @param [in] result The result from a sampling algorithm
*/
void cugraph_triangle_count_result_free(cugraph_triangle_count_result_t* result);

#ifdef __cplusplus
}
#endif
179 changes: 179 additions & 0 deletions cpp/src/c_api/triangle_count.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/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>

#include <optional>

namespace cugraph {
namespace c_api {

struct cugraph_triangle_count_result_t {
cugraph_type_erased_device_array_t* vertices_;
cugraph_type_erased_device_array_t* counts_;
};

} // namespace c_api
} // namespace cugraph

namespace {

struct triangle_count_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* vertices_;
bool do_expensive_check_;
cugraph::c_api::cugraph_triangle_count_result_t* result_{};

triangle_count_functor(::cugraph_resource_handle_t const* handle,
::cugraph_graph_t* graph,
::cugraph_type_erased_device_array_view_t const* 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)),
vertices_(
reinterpret_cast<cugraph::c_api::cugraph_type_erased_device_array_view_t const*>(vertices)),
do_expensive_check_(do_expensive_check)
{
}

template <typename vertex_t,
typename edge_t,
typename weight_t,
bool store_transposed,
bool multi_gpu>
void operator()()
{
if constexpr (!cugraph::is_candidate<vertex_t, edge_t, weight_t>::value) {
unsupported();
} else {
#if 1
error_code_ = CUGRAPH_NOT_IMPLEMENTED;
error_->error_message_ = "Triangle Counting not implemented yet";
#else
// triangle counting 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<vertex_t> vertices(0, handle_.get_stream());
rmm::device_uvector<edge_t> counts(0, handle_.get_stream());
if (vertices_ != nullptr) {
vertices.resize(vertices_->size_, handle_.get_stream());
counts.resize(vertices_->size_, handle_.get_stream());
raft::copy(
vertices.data(), vertices_->as_type<vertex_t>(), vertices.size(), handle_.get_stream());
cugraph::renumber_ext_vertices<vertex_t, multi_gpu>(
handle_,
vertices.data(),
vertices.size(),
number_map->data(),
graph_view.local_vertex_partition_range_first(),
graph_view.local_vertex_partition_range_last(),
do_expensive_check_);
} else {
counts.resize(graph_view.local_vertex_partition_range_size(), handle_.get_stream());
}
// cugraph::triangle_count<vertex_t, edge_t, weight_t, multi_gpu>(
cugraph::triangle_counts<vertex_t, edge_t, weight_t, multi_gpu>(
handle_,
graph_view,
vertices_ == nullptr
? std::nullopt
: std::make_optional(raft::device_span<vertex_t>{vertices.data(), vertices.size()}),
raft::device_span<edge_t>{counts.data(), counts.size()},
do_expensive_check_);
if (vertices_ == nullptr) {
vertices.resize(graph_view.local_vertex_partition_range_size(), handle_.get_stream());
raft::copy(vertices.data(), number_map->data(), vertices.size(), handle_.get_stream());
}
result_ = new cugraph::c_api::cugraph_triangle_count_result_t{
new cugraph::c_api::cugraph_type_erased_device_array_t(vertices, graph_->vertex_type_),
new cugraph::c_api::cugraph_type_erased_device_array_t(counts, graph_->edge_type_)};
#endif
}
}
};

} // namespace

extern "C" cugraph_type_erased_device_array_view_t* cugraph_triangle_count_result_get_vertices(
cugraph_triangle_count_result_t* result)
{
auto internal_pointer =
reinterpret_cast<cugraph::c_api::cugraph_triangle_count_result_t*>(result);
return reinterpret_cast<cugraph_type_erased_device_array_view_t*>(
internal_pointer->vertices_->view());
}

extern "C" cugraph_type_erased_device_array_view_t* cugraph_triangle_count_result_get_counts(
cugraph_triangle_count_result_t* result)
{
auto internal_pointer =
reinterpret_cast<cugraph::c_api::cugraph_triangle_count_result_t*>(result);
return reinterpret_cast<cugraph_type_erased_device_array_view_t*>(
internal_pointer->counts_->view());
}

extern "C" void cugraph_triangle_count_result_free(cugraph_triangle_count_result_t* result)
{
auto internal_pointer =
reinterpret_cast<cugraph::c_api::cugraph_triangle_count_result_t*>(result);
delete internal_pointer->vertices_;
delete internal_pointer->counts_;
delete internal_pointer;
}

extern "C" cugraph_error_code_t cugraph_triangle_count(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
const cugraph_type_erased_device_array_view_t* start,
bool_t do_expensive_check,
cugraph_triangle_count_result_t** result,
cugraph_error_t** error)
{
triangle_count_functor functor(handle, graph, start, do_expensive_check);

return cugraph::c_api::run_algorithm(graph, functor, result, error);
}
2 changes: 2 additions & 0 deletions cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ if(BUILD_CUGRAPH_MG_TESTS)
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)
ConfigureCTestMG(MG_CAPI_TRIANGLE_COUNT c_api/mg_triangle_count_test.c c_api/mg_test_utils.cpp)
else()
message(FATAL_ERROR "OpenMPI NOT found, cannot build MG tests.")
endif()
Expand Down Expand Up @@ -684,6 +685,7 @@ ConfigureCTest(CAPI_NODE2VEC_TEST c_api/node2vec_test.c)
ConfigureCTest(CAPI_WEAKLY_CONNECTED_COMPONENTS c_api/weakly_connected_components_test.c)
ConfigureCTest(CAPI_STRONGLY_CONNECTED_COMPONENTS c_api/strongly_connected_components_test.c)
ConfigureCTest(CAPI_UNIFORM_NEIGHBOR_SAMPLE c_api/uniform_neighbor_sample_test.c)
ConfigureCTest(CAPI_TRIANGLE_COUNT c_api/triangle_count_test.c)

###################################################################################################
### enable testing ################################################################################
Expand Down
6 changes: 6 additions & 0 deletions cpp/tests/c_api/c_test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
} \
}

#define TEST_ALWAYS_ASSERT(STATEMENT, MESSAGE) \
{ \
int tmp = !(STATEMENT); \
if (tmp) { printf("ASSERTION FAILED: %s\n", (MESSAGE)); } \
}

#ifdef __cplusplus
extern "C" {
#endif
Expand Down
Loading

0 comments on commit 046cbd2

Please sign in to comment.