Skip to content
Open
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
37 changes: 36 additions & 1 deletion cpp/include/cugraph_c/graph.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -327,6 +327,41 @@ CUGRAPH_EXPORT cugraph_error_code_t cugraph_graph_create_with_times_mg(
cugraph_graph_t** graph,
cugraph_error_t** error);

/**
* @brief Get the number of vertices in a graph
*
* Returns the global number of vertices. For multi-GPU graphs this value is
* identical on all ranks.
*
* @param [in] graph Pointer to graph
* @param [out] result Where to store the number of vertices
* @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_EXPORT cugraph_error_code_t cugraph_graph_number_of_vertices(cugraph_graph_t* graph,
size_t* result,
cugraph_error_t** error);

/**
* @brief Get the number of edges in a graph
*
* Returns the total number of edges in the graph. For multi-GPU graphs this is the
* global edge count, identical on all ranks.
*
* @param [in] handle Handle for accessing resources
* @param [in] graph Pointer to graph
* @param [out] result Where to store the number of edges
* @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_EXPORT cugraph_error_code_t
cugraph_graph_number_of_edges(const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
size_t* result,
cugraph_error_t** error);

/**
* @brief Destroy an graph
*
Expand Down
83 changes: 82 additions & 1 deletion cpp/src/c_api/graph_functions.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -206,6 +206,66 @@ struct two_hop_neighbors_functor : public cugraph::c_api::abstract_functor {
}
};

struct number_of_vertices_functor : public cugraph::c_api::abstract_functor {
cugraph::c_api::cugraph_graph_t* graph_{nullptr};
size_t result_{};

explicit number_of_vertices_functor(::cugraph_graph_t* graph)
: abstract_functor(), graph_(reinterpret_cast<cugraph::c_api::cugraph_graph_t*>(graph))
{
}

template <typename vertex_t,
typename edge_t,
typename weight_t,
typename edge_type_t,
typename time_stamp_t,
bool store_transposed,
bool multi_gpu>
void operator()()
{
if constexpr (!cugraph::is_candidate<vertex_t, edge_t, weight_t>::value) {
unsupported();
} else {
auto graph =
reinterpret_cast<cugraph::graph_t<vertex_t, edge_t, store_transposed, multi_gpu>*>(
graph_->graph_);

result_ = static_cast<size_t>(graph->view().number_of_vertices());
}
}
};

struct number_of_edges_functor : public cugraph::c_api::abstract_functor {
cugraph::c_api::cugraph_graph_t* graph_{nullptr};
size_t result_{};

explicit number_of_edges_functor(::cugraph_graph_t* graph)
: abstract_functor(), graph_(reinterpret_cast<cugraph::c_api::cugraph_graph_t*>(graph))
{
}

template <typename vertex_t,
typename edge_t,
typename weight_t,
typename edge_type_t,
typename time_stamp_t,
bool store_transposed,
bool multi_gpu>
void operator()()
{
if constexpr (!cugraph::is_candidate<vertex_t, edge_t, weight_t>::value) {
unsupported();
} else {
auto graph =
reinterpret_cast<cugraph::graph_t<vertex_t, edge_t, store_transposed, multi_gpu>*>(
graph_->graph_);

result_ = static_cast<size_t>(graph->number_of_edges());
}
}
};

struct count_multi_edges_functor : public cugraph::c_api::abstract_functor {
raft::handle_t const& handle_{};
cugraph::c_api::cugraph_graph_t* graph_{nullptr};
Expand Down Expand Up @@ -377,6 +437,27 @@ extern "C" cugraph_error_code_t cugraph_two_hop_neighbors(
return cugraph::c_api::run_algorithm(graph, functor, result, error);
}

extern "C" cugraph_error_code_t cugraph_graph_number_of_vertices(cugraph_graph_t* graph,
size_t* result,
cugraph_error_t** error)
{
number_of_vertices_functor functor(graph);

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

extern "C" cugraph_error_code_t cugraph_graph_number_of_edges(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
size_t* result,
cugraph_error_t** error)
{
(void)handle;
number_of_edges_functor functor(graph);

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

extern "C" cugraph_error_code_t cugraph_count_multi_edges(const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
bool_t do_expensive_check,
Expand Down
16 changes: 16 additions & 0 deletions cpp/src/structure/graph_impl.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,22 @@ graph_t<vertex_t, edge_t, store_transposed, multi_gpu, std::enable_if_t<multi_gp
edge_partition_dcs_nzd_range_bitmaps_ =
compute_edge_partition_dcs_nzd_range_bitmaps(handle, meta, *edge_partition_dcs_nzd_vertices_);
}

// Global logical |E|: sum local CSR index counts, then SUM allreduce (each edge on one GPU).
{
edge_t local_count{0};
for (auto const& indices : edge_partition_indices_) {
local_count += static_cast<edge_t>(indices.size());
}
#if 1 // FIXME: we should add host_allreduce to raft
this->number_of_edges_ = host_scalar_allreduce(
handle.get_comms(), local_count, raft::comms::op_t::SUM, handle.get_stream());
#else
handle.get_comms().host_allreduce(
std::addressof(local_count), std::addressof(local_count), size_t{1}, raft::comms::op_t::SUM);
this->number_of_edges_ = local_count;
#endif
}
}

template <typename vertex_t, typename edge_t, bool store_transposed, bool multi_gpu>
Expand Down
15 changes: 14 additions & 1 deletion cpp/tests/c_api/create_graph_test.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2021-2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -100,6 +100,19 @@ int test_create_sg_graph_simple()
&ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed.");

size_t result_num_vertices = 0;
size_t result_num_edges = 0;

ret_code = cugraph_graph_number_of_vertices(graph, &result_num_vertices, &ret_error);
TEST_ASSERT(
test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_graph_number_of_vertices failed.");
TEST_ASSERT(
test_ret_value, result_num_vertices == num_vertices, "number of vertices did not match");

ret_code = cugraph_graph_number_of_edges(handle, graph, &result_num_edges, &ret_error);
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_graph_number_of_edges failed.");
TEST_ASSERT(test_ret_value, result_num_edges == num_edges, "number of edges did not match");

cugraph_graph_free(graph);

cugraph_type_erased_device_array_view_free(wgt_view);
Expand Down
24 changes: 23 additions & 1 deletion cpp/tests/c_api/mg_create_graph_test.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2022, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#include "c_test_utils.h" /* RUN_TEST */
#include "mg_test_utils.h" /* RUN_TEST */

#include <cugraph_c/algorithms.h>
#include <cugraph_c/graph.h>
#include <cugraph_c/resource_handle.h>

#include <math.h>
#include <stdio.h>
Expand Down Expand Up @@ -107,6 +109,26 @@ int test_create_mg_graph_simple(const cugraph_resource_handle_t* handle)
TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "graph creation failed.");
TEST_ALWAYS_ASSERT(ret_code == CUGRAPH_SUCCESS, cugraph_error_message(ret_error));

{
int comm_size = cugraph_resource_handle_get_comm_size(handle);
size_t expected_vertices = num_vertices * comm_size;
size_t expected_edges = num_edges * comm_size;
size_t result_num_vertices;
size_t result_num_edges;

ret_code = cugraph_graph_number_of_vertices(graph, &result_num_vertices, &ret_error);
TEST_ASSERT(
test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_graph_number_of_vertices failed.");
TEST_ASSERT(
test_ret_value, result_num_vertices == expected_vertices, "number of vertices did not match");

ret_code = cugraph_graph_number_of_edges(handle, graph, &result_num_edges, &ret_error);
TEST_ASSERT(
test_ret_value, ret_code == CUGRAPH_SUCCESS, "cugraph_graph_number_of_edges failed.");
TEST_ASSERT(
test_ret_value, result_num_edges == expected_edges, "number of edges did not match");
}

cugraph_graph_free(graph);

cugraph_type_erased_device_array_view_free(wgt_view);
Expand Down
15 changes: 14 additions & 1 deletion python/pylibcugraph/pylibcugraph/_cugraph_c/graph.pxd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Have cython use python 3 syntax
Expand Down Expand Up @@ -70,6 +70,19 @@ cdef extern from "cugraph_c/graph.h":
cugraph_graph_t** graph,
cugraph_error_t** error);

cdef cugraph_error_code_t \
cugraph_graph_number_of_vertices(
cugraph_graph_t* graph,
size_t* result,
cugraph_error_t** error)

cdef cugraph_error_code_t \
cugraph_graph_number_of_edges(
const cugraph_resource_handle_t* handle,
cugraph_graph_t* graph,
size_t* result,
cugraph_error_t** error)

cdef void \
cugraph_graph_free(
cugraph_graph_t* graph
Expand Down
54 changes: 54 additions & 0 deletions python/pylibcugraph/pylibcugraph/graphs.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ from pylibcugraph._cugraph_c.graph cimport (
cugraph_graph_create_with_times_mg,
cugraph_graph_create_sg_from_csr,
cugraph_graph_free,
cugraph_graph_number_of_vertices,
cugraph_graph_number_of_edges,
)
from pylibcugraph._cugraph_c.array cimport (
cugraph_type_erased_device_array_view_type,
Expand All @@ -38,6 +40,58 @@ from pylibcugraph.utilities.api_tools import ensure_valid_dtypes
from libc.stdlib cimport malloc


cdef class _GPUGraph:

def number_of_vertices(self):
"""
Return the total number of vertices in the graph.

For multi-GPU graphs this value is identical on all ranks.

Returns
-------
int
The number of vertices in the graph.
"""
cdef size_t result
cdef cugraph_error_code_t error_code
cdef cugraph_error_t* error_ptr

error_code = cugraph_graph_number_of_vertices(
self.c_graph_ptr, &result, &error_ptr)
assert_success(error_code, error_ptr, "cugraph_graph_number_of_vertices")

return result

def number_of_edges(self, ResourceHandle resource_handle):
"""
Return the total number of edges in the graph.

For multi-GPU graphs this value is identical on all ranks.

Parameters
----------
resource_handle : ResourceHandle
Handle to the underlying device resources.

Returns
-------
int
The number of edges in the graph.
"""
cdef size_t result
cdef cugraph_error_code_t error_code
cdef cugraph_error_t* error_ptr

error_code = cugraph_graph_number_of_edges(
resource_handle.c_resource_handle_ptr,
self.c_graph_ptr,
&result,
&error_ptr)
assert_success(error_code, error_ptr, "cugraph_graph_number_of_edges")

return result


cdef class SGGraph(_GPUGraph):
"""
Expand Down
38 changes: 34 additions & 4 deletions python/pylibcugraph/pylibcugraph/tests/test_graph_sg.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright (c) 2022-2023, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

import pytest
Expand Down Expand Up @@ -130,8 +130,9 @@ def test_SGGraph_create_from_cudf():

graph_props = GraphProperties(is_multigraph=False, is_symmetric=False)

resource_handle = ResourceHandle()
plc_graph = SGGraph(
resource_handle=ResourceHandle(),
resource_handle=resource_handle,
graph_properties=graph_props,
src_or_offset_array=edgelist["src"],
dst_or_index_array=edgelist["dst"],
Expand All @@ -143,5 +144,34 @@ def test_SGGraph_create_from_cudf():
do_expensive_check=True,
input_array_format="COO",
)
print("done", flush=True)
print(f"created SGGraph {plc_graph=}", flush=True)
assert plc_graph.number_of_vertices() == 5
assert plc_graph.number_of_edges(resource_handle) == 3


def test_sg_graph_number_of_vertices_and_edges():
import cupy as cp
import numpy as np
from pylibcugraph import ResourceHandle, GraphProperties, SGGraph

resource_handle = ResourceHandle()
graph_props = GraphProperties(is_symmetric=False, is_multigraph=False)

device_srcs = cp.asarray([0, 1, 1, 2, 2, 2, 3, 4], dtype=np.int32)
device_dsts = cp.asarray([1, 3, 4, 0, 1, 3, 5, 5], dtype=np.int32)
device_weights = cp.asarray(
[0.1, 2.1, 1.1, 5.1, 3.1, 4.1, 7.2, 3.2], dtype=np.float32
)

graph = SGGraph(
resource_handle=resource_handle,
graph_properties=graph_props,
src_or_offset_array=device_srcs,
dst_or_index_array=device_dsts,
weight_array=device_weights,
store_transposed=False,
renumber=False,
do_expensive_check=False,
)

assert graph.number_of_vertices() == 6
assert graph.number_of_edges(resource_handle) == 8
Loading