Skip to content

Commit

Permalink
C API for MNMG ECG (#4194)
Browse files Browse the repository at this point in the history
C API for MNMG ECG


Closes #3571 
Closes #3572 
Closes #3573 
Closes #3575

Authors:
  - Naim (https://github.com/naimnv)

Approvers:
  - Seunghwa Kang (https://github.com/seunghwak)
  - Joseph Nke (https://github.com/jnke2016)
  - Brad Rees (https://github.com/BradReesWork)

URL: #4194
  • Loading branch information
naimnv authored Mar 7, 2024
1 parent 7eee588 commit 72401a0
Show file tree
Hide file tree
Showing 11 changed files with 624 additions and 45 deletions.
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ add_library(cugraph_c
src/c_api/core_number.cpp
src/c_api/core_result.cpp
src/c_api/extract_ego.cpp
src/c_api/ecg.cpp
src/c_api/k_core.cpp
src/c_api/hierarchical_clustering_result.cpp
src/c_api/induced_subgraph.cpp
Expand Down
49 changes: 42 additions & 7 deletions cpp/include/cugraph_c/community_algorithms.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,41 @@ double cugraph_hierarchical_clustering_result_get_modularity(
*/
void cugraph_hierarchical_clustering_result_free(cugraph_hierarchical_clustering_result_t* result);

/**
* @brief Compute ECG clustering
*
* @param [in] handle Handle for accessing resources
* @param [in/out] rng_state State of the random number generator, updated with each call
* @param [in] graph Pointer to graph. NOTE: Graph might be modified if the storage
* needs to be transposed
* @param [in] min_weight Minimum edge weight in final graph
* @param [in] ensemble_size The number of Louvain iterations to run
* @param [in] max_level Maximum level in hierarchy for final Louvain
* @param [in] threshold Threshold parameter, defines convergence at each level of hierarchy
* for final Louvain
* @param [in] resolution Resolution parameter (gamma) in modularity formula.
* This changes the size of the communities. Higher resolutions
* lead to more smaller communities, lower resolutions lead to
* fewer larger communities.
* @param [in] do_expensive_check
* A flag to run expensive checks for input arguments (if set to true)
* @param [out] result Output from the Louvain 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_ecg(const cugraph_resource_handle_t* handle,
cugraph_rng_state_t* rng_state,
cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
size_t max_level,
double threshold,
double resolution,
bool_t do_expensive_check,
cugraph_hierarchical_clustering_result_t** result,
cugraph_error_t** error);

/**
* @brief Compute ECG clustering of the given graph
*
Expand All @@ -200,13 +235,13 @@ void cugraph_hierarchical_clustering_result_free(cugraph_hierarchical_clustering
* be populated if error code is not CUGRAPH_SUCCESS
* @return error code
*/
cugraph_error_code_t cugraph_ecg(const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
bool_t do_expensive_check,
cugraph_hierarchical_clustering_result_t** result,
cugraph_error_t** error);
cugraph_error_code_t cugraph_legacy_ecg(const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
bool_t do_expensive_check,
cugraph_hierarchical_clustering_result_t** result,
cugraph_error_t** error);

/**
* @brief Extract ego graphs
Expand Down
154 changes: 154 additions & 0 deletions cpp/src/c_api/ecg.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2022-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 <cugraph_c/community_algorithms.h>

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

#include <c_api/abstract_functor.hpp>
#include <c_api/graph.hpp>
#include <c_api/graph_helper.hpp>
#include <c_api/hierarchical_clustering_result.hpp>
#include <c_api/random.hpp>
#include <c_api/resource_handle.hpp>
#include <c_api/utils.hpp>

#include <optional>

namespace {

struct ecg_functor : public cugraph::c_api::abstract_functor {
raft::handle_t const& handle_;
cugraph::c_api::cugraph_rng_state_t* rng_state_{nullptr};
cugraph::c_api::cugraph_graph_t* graph_{nullptr};
double min_weight_{0.1};
size_t ensemble_size_{10};
size_t max_level_{0};
double threshold_{0.001};
double resolution_{1};
bool do_expensive_check_{false};
cugraph::c_api::cugraph_hierarchical_clustering_result_t* result_{};

ecg_functor(::cugraph_resource_handle_t const* handle,
::cugraph_rng_state_t* rng_state,
::cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
size_t max_level,
double threshold,
double resolution,
bool do_expensive_check)
: abstract_functor(),
handle_(*reinterpret_cast<cugraph::c_api::cugraph_resource_handle_t const*>(handle)->handle_),
rng_state_(reinterpret_cast<cugraph::c_api::cugraph_rng_state_t*>(rng_state)),
graph_(reinterpret_cast<cugraph::c_api::cugraph_graph_t*>(graph)),
max_level_(max_level),
threshold_(threshold),
resolution_(resolution),
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()()
{
if constexpr (!cugraph::is_candidate<vertex_t, edge_t, weight_t>::value) {
unsupported();
} else {
// ecg 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, false, multi_gpu>*>(graph_->graph_);

auto graph_view = graph->view();

auto edge_weights = reinterpret_cast<
cugraph::edge_property_t<cugraph::graph_view_t<vertex_t, edge_t, false, multi_gpu>,
weight_t>*>(graph_->edge_weights_);

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

rmm::device_uvector<vertex_t> clusters(0, handle_.get_stream());

weight_t modularity;

std::tie(clusters, std::ignore, modularity) =
cugraph::ecg(handle_,
rng_state_->rng_state_,
graph_view,
(edge_weights != nullptr)
? std::make_optional(edge_weights->view())
: std::make_optional(cugraph::c_api::create_constant_edge_property(
handle_, graph_view, weight_t{1})
.view()),
static_cast<weight_t>(min_weight_),
ensemble_size_,
max_level_,
static_cast<weight_t>(threshold_),
static_cast<weight_t>(resolution_));

rmm::device_uvector<vertex_t> vertices(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_hierarchical_clustering_result_t{
modularity,
new cugraph::c_api::cugraph_type_erased_device_array_t(vertices, graph_->vertex_type_),
new cugraph::c_api::cugraph_type_erased_device_array_t(clusters, graph_->vertex_type_)};
}
}
};

} // namespace

extern "C" cugraph_error_code_t cugraph_ecg(const cugraph_resource_handle_t* handle,
cugraph_rng_state_t* rng_state,
cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
size_t max_level,
double threshold,
double resolution,
bool_t do_expensive_check,
cugraph_hierarchical_clustering_result_t** result,
cugraph_error_t** error)
{
ecg_functor functor(handle,
rng_state,
graph,
min_weight,
ensemble_size,
max_level,
threshold,
resolution,
do_expensive_check);

return cugraph::c_api::run_algorithm(graph, functor, result, error);
}
29 changes: 15 additions & 14 deletions cpp/src/c_api/legacy_ecg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,19 @@

namespace {

struct ecg_functor : public cugraph::c_api::abstract_functor {
struct legacy_ecg_functor : public cugraph::c_api::abstract_functor {
raft::handle_t const& handle_;
cugraph::c_api::cugraph_graph_t* graph_;
double min_weight_;
size_t ensemble_size_;
bool do_expensive_check_;
cugraph::c_api::cugraph_hierarchical_clustering_result_t* result_{};

ecg_functor(::cugraph_resource_handle_t const* handle,
::cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
bool do_expensive_check)
legacy_ecg_functor(::cugraph_resource_handle_t const* handle,
::cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
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)),
Expand Down Expand Up @@ -120,15 +120,16 @@ struct ecg_functor : public cugraph::c_api::abstract_functor {

} // namespace

extern "C" cugraph_error_code_t cugraph_ecg(const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
bool_t do_expensive_check,
cugraph_hierarchical_clustering_result_t** result,
cugraph_error_t** error)
extern "C" cugraph_error_code_t cugraph_legacy_ecg(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
double min_weight,
size_t ensemble_size,
bool_t do_expensive_check,
cugraph_hierarchical_clustering_result_t** result,
cugraph_error_t** error)
{
ecg_functor functor(handle, graph, min_weight, ensemble_size, do_expensive_check);
legacy_ecg_functor functor(handle, graph, min_weight, ensemble_size, do_expensive_check);

return cugraph::c_api::run_algorithm(graph, functor, result, error);
}
6 changes: 4 additions & 2 deletions cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ ConfigureTest(LOUVAIN_TEST community/louvain_test.cpp)
ConfigureTest(LEIDEN_TEST community/leiden_test.cpp)

###################################################################################################
# - ECG tests -------------------------------------------------------------------------------------
ConfigureTest(ECG_TEST community/ecg_test.cpp)
# - Legacy ECG tests -------------------------------------------------------------------------------------
ConfigureTest(LEGACY_ECG_TEST community/legacy_ecg_test.cpp)

###################################################################################################
# - Balanced cut clustering tests -----------------------------------------------------------------
Expand Down Expand Up @@ -679,6 +679,7 @@ if(BUILD_CUGRAPH_MG_TESTS)
ConfigureCTestMG(MG_CAPI_TRIANGLE_COUNT_TEST c_api/mg_triangle_count_test.c)
ConfigureCTestMG(MG_CAPI_LOUVAIN_TEST c_api/mg_louvain_test.c)
ConfigureCTestMG(MG_CAPI_LEIDEN_TEST c_api/mg_leiden_test.c)
ConfigureCTestMG(MG_CAPI_ECG_TEST c_api/mg_ecg_test.c)
ConfigureCTestMG(MG_CAPI_CORE_NUMBER_TEST c_api/mg_core_number_test.c)
ConfigureCTestMG(MG_CAPI_SIMILARITY_TEST c_api/mg_similarity_test.c)
ConfigureCTestMG(MG_CAPI_K_CORE_TEST c_api/mg_k_core_test.c)
Expand Down Expand Up @@ -737,6 +738,7 @@ ConfigureCTest(CAPI_RANDOM_WALKS_TEST c_api/sg_random_walks_test.c)
ConfigureCTest(CAPI_TRIANGLE_COUNT_TEST c_api/triangle_count_test.c)
ConfigureCTest(CAPI_LOUVAIN_TEST c_api/louvain_test.c)
ConfigureCTest(CAPI_LEIDEN_TEST c_api/leiden_test.c)
ConfigureCTest(CAPI_ECG_TEST c_api/ecg_test.c)
#############################################################################
# Skipping due to CUDA 12.2 failure that traces back to RAFT #
# TODO: Uncomment this once the issue is fixed. #
Expand Down
Loading

0 comments on commit 72401a0

Please sign in to comment.