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

Add HITS to the C API #2123

Merged
merged 5 commits into from
Mar 18, 2022
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
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ add_library(cugraph_c SHARED
src/c_api/graph_sg.cpp
src/c_api/graph_mg.cpp
src/c_api/pagerank.cpp
src/c_api/hits.cpp
src/c_api/bfs.cpp
src/c_api/sssp.cpp
src/c_api/extract_paths.cpp
Expand Down
99 changes: 99 additions & 0 deletions cpp/include/cugraph_c/algorithms.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,105 @@ cugraph_error_code_t cugraph_personalized_pagerank(
cugraph_pagerank_result_t** result,
cugraph_error_t** error);

/**
* @brief Opaque hits result type
*/
typedef struct {
int32_t align_;
} cugraph_hits_result_t;

/**
* @brief Get the vertex ids from the hits result
*
* @param [in] result The result from hits
* @return type erased array of vertex ids
*/
cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_vertices(
cugraph_hits_result_t* result);

/**
* @brief Get the hubs values from the hits result
*
* @param [in] result The result from hits
* @return type erased array of hubs values
*/
cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_hubs(
cugraph_hits_result_t* result);

/**
* @brief Get the authorities values from the hits result
*
* @param [in] result The result from hits
* @return type erased array of authorities values
*/
cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_authorities(
cugraph_hits_result_t* result);

/**
* @brief Get the score differences between the last two iterations
*
* @param [in] result The result from hits
* @return score differences
*/
double cugraph_hits_result_get_hub_score_differences(cugraph_hits_result_t* result);

/**
* @brief Get the actual number of iterations
*
* @param [in] result The result from hits
* @return actual number of iterations
*/
size_t cugraph_hits_result_get_number_of_iterations(cugraph_hits_result_t* result);

/**
* @brief Free hits result
*
* @param [in] result The result from hits
*/
void cugraph_hits_result_free(cugraph_hits_result_t* result);

/**
* @brief Compute hits
*
* @param [in] handle Handle for accessing resources
* @param [in] graph Pointer to graph
* @param [in] epsilon Error tolerance to check convergence. Convergence is assumed
* if the sum of the differences in Hits values between two
* consecutive iterations is less than the number of vertices
* in the graph multiplied by @p epsilon.
* @param [in] max_iterations
* Maximum number of Hits iterations.
* @param [in] initial_hubs_guess_vertices
* Pointer to optional type erased device array containing
* the vertex ids for an initial hubs guess. If set to NULL
* there is no initial guess.
* @param [in] initial_hubs_guess_values
* Pointer to optional type erased device array containing
* the values for an initial hubs guess. If set to NULL
* there is no initial guess. Note that both
* @p initial_hubs_guess_vertices and @p initial_hubs_guess_values
* have to be specified (or they both have to be NULL). Otherwise
* this will be treated as an error.
* @param [in] normalize A flag to normalize the results (if set to `true`)
* @param [in] do_expensive_check A flag to run expensive checks for input arguments (if set to
* `true`).
* @param [out] result Opaque pointer to hits results
* @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_hits(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
double epsilon,
size_t max_iterations,
const cugraph_type_erased_device_array_view_t* initial_hubs_guess_vertices,
const cugraph_type_erased_device_array_view_t* initial_hubs_guess_values,
bool_t normalize,
bool_t do_expensive_check,
cugraph_hits_result_t** result,
cugraph_error_t** error);

/**
* @brief Opaque paths result type
*
Expand Down
230 changes: 230 additions & 0 deletions cpp/src/c_api/hits.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* 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/utility_wrappers.hpp>
#include <cugraph/graph_functions.hpp>

#include <optional>

namespace cugraph {
namespace c_api {

struct cugraph_hits_result_t {
cugraph_type_erased_device_array_t* vertex_ids_;
cugraph_type_erased_device_array_t* hubs_;
cugraph_type_erased_device_array_t* authorities_;
double hub_score_differences_;
size_t number_of_iterations_;
};

} // namespace c_api
} // namespace cugraph

namespace {

struct hits_functor : public cugraph::c_api::abstract_functor {
raft::handle_t const& handle_;
cugraph::c_api::cugraph_graph_t* graph_;
double epsilon_;
size_t max_iterations_;
cugraph::c_api::cugraph_type_erased_device_array_view_t const* initial_hubs_guess_vertices_;
cugraph::c_api::cugraph_type_erased_device_array_view_t const* initial_hubs_guess_values_;
bool normalize_;
bool do_expensive_check_;
cugraph::c_api::cugraph_hits_result_t* result_{};

hits_functor(::cugraph_resource_handle_t const* handle,
::cugraph_graph_t* graph,
double epsilon,
size_t max_iterations,
::cugraph_type_erased_device_array_view_t const* initial_hubs_guess_vertices,
::cugraph_type_erased_device_array_view_t const* initial_hubs_guess_values,
bool normalize,
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)),
epsilon_(epsilon),
max_iterations_(max_iterations),
initial_hubs_guess_vertices_(
reinterpret_cast<cugraph::c_api::cugraph_type_erased_device_array_view_t const*>(
initial_hubs_guess_vertices)),
initial_hubs_guess_values_(
reinterpret_cast<cugraph::c_api::cugraph_type_erased_device_array_view_t const*>(
initial_hubs_guess_values)),
normalize_(normalize),
do_expensive_check_(do_expensive_check)
{
}

template <typename vertex_t,
typename edge_t,
typename weight_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 {
// HITS expects store_transposed == true
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, true, 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> vertex_ids(graph_view.get_number_of_local_vertices(),
handle_.get_stream());
rmm::device_uvector<weight_t> hubs(graph_view.get_number_of_local_vertices(),
handle_.get_stream());
rmm::device_uvector<weight_t> authorities(graph_view.get_number_of_local_vertices(),
handle_.get_stream());
weight_t hub_score_differences{0};
size_t number_of_iterations{0};

#if 0
// FIXME: Implementation will look something like this.

if (initial_hubs_guess_ != nullptr) {
//
// Need to renumber initial_hubs_guess_vertices
// Need to shuffle and populate hubs
//
// This is the original pagerank code, it will be sort of like this
renumber_ext_vertices<vertex_t, multi_gpu>(handle_,
personalization_vertices_->as_type<vertex_t>(),
personalization_vertices_->size_,
number_map->data(),
graph_view.get_local_vertex_first(),
graph_view.get_local_vertex_last(),
do_expensive_check_);
}

// TODO: Add these to the result
std::tie(hub_score_differences, number_of_iterations) =
cugraph::hits<vertex_t, edge_t, weight_t, multi_gpu>(handle_,
graph_view,
hubs.data(),
authorities.data(),
epsilon_,
max_iterations_,
has_initial_hubs_guess,
normalize_,
do_expensive_check_);

raft::copy(vertex_ids.data(), number_map->data(), vertex_ids.size(), handle_.get_stream());
#else
unsupported();
#endif

result_ = new cugraph::c_api::cugraph_hits_result_t{
new cugraph::c_api::cugraph_type_erased_device_array_t(vertex_ids, graph_->vertex_type_),
new cugraph::c_api::cugraph_type_erased_device_array_t(hubs, graph_->weight_type_),
new cugraph::c_api::cugraph_type_erased_device_array_t(authorities, graph_->weight_type_),
hub_score_differences,
number_of_iterations};
}
}
};

} // namespace

extern "C" cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_vertices(
cugraph_hits_result_t* result)
{
auto internal_pointer = reinterpret_cast<cugraph::c_api::cugraph_hits_result_t*>(result);
return reinterpret_cast<cugraph_type_erased_device_array_view_t*>(
internal_pointer->vertex_ids_->view());
}

extern "C" cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_hubs(
cugraph_hits_result_t* result)
{
auto internal_pointer = reinterpret_cast<cugraph::c_api::cugraph_hits_result_t*>(result);
return reinterpret_cast<cugraph_type_erased_device_array_view_t*>(
internal_pointer->hubs_->view());
}

extern "C" cugraph_type_erased_device_array_view_t* cugraph_hits_result_get_authorities(
cugraph_hits_result_t* result)
{
auto internal_pointer = reinterpret_cast<cugraph::c_api::cugraph_hits_result_t*>(result);
return reinterpret_cast<cugraph_type_erased_device_array_view_t*>(
internal_pointer->authorities_->view());
}

extern "C" double cugraph_hits_result_get_hub_score_differences(cugraph_hits_result_t* result)
{
auto internal_pointer = reinterpret_cast<cugraph::c_api::cugraph_hits_result_t*>(result);
return internal_pointer->hub_score_differences_;
}

extern "C" size_t cugraph_hits_result_get_number_of_iterations(cugraph_hits_result_t* result)
{
auto internal_pointer = reinterpret_cast<cugraph::c_api::cugraph_hits_result_t*>(result);
return internal_pointer->number_of_iterations_;
}

extern "C" void cugraph_hits_result_free(cugraph_hits_result_t* result)
{
auto internal_pointer = reinterpret_cast<cugraph::c_api::cugraph_hits_result_t*>(result);
delete internal_pointer->vertex_ids_;
delete internal_pointer->hubs_;
delete internal_pointer->authorities_;
delete internal_pointer;
}

extern "C" cugraph_error_code_t cugraph_hits(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
double epsilon,
size_t max_iterations,
const cugraph_type_erased_device_array_view_t* initial_hubs_guess_vertices,
const cugraph_type_erased_device_array_view_t* initial_hubs_guess_values,
bool_t normalize,
bool_t do_expensive_check,
cugraph_hits_result_t** result,
cugraph_error_t** error)
{
hits_functor functor(handle,
graph,
epsilon,
max_iterations,
initial_hubs_guess_vertices,
initial_hubs_guess_values,
normalize,
do_expensive_check);

return cugraph::c_api::run_algorithm(graph, functor, result, error);
}
1 change: 1 addition & 0 deletions cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ target_link_libraries(cugraph_c_testutil

ConfigureCTest(CAPI_CREATE_GRAPH_TEST c_api/create_graph_test.c)
ConfigureCTest(CAPI_PAGERANK_TEST c_api/pagerank_test.c)
ConfigureCTest(CAPI_HITS_TEST c_api/hits_test.c)
ConfigureCTest(CAPI_BFS_TEST c_api/bfs_test.c)
ConfigureCTest(CAPI_SSSP_TEST c_api/sssp_test.c)
ConfigureCTest(CAPI_EXTRACT_PATHS_TEST c_api/extract_paths_test.c)
Expand Down
Loading