Skip to content

Commit 3c70ad0

Browse files
authored
Resolve inconsistencies in algorithms that do not support self-loops & multi-edges. (rapidsai#5109)
Core number, K-Core, K-Truss, Triangle Count, and Edge Triangle Count do not support graphs with self-loops and multi-edges. But they have been inconsistent in handling graphs with self-loops and multi-edges. Core number throws an exception if the input graph has a self-loop but other algorithms ignores (by edge masking) self-loops in computing. Most algorithms throws an exception if the input graph is a multi-graph but edge triangle count does not check for this. In C++ tests, some tests include graphs with self-loops but some tests don't (drop_self_loops is set to true in graph generation). We can simply check whether the input graph is a multi graph or not by just calling is_multigraph() (even though this does not necessarily mean the input graph actually has multi-edges) and this PR updates cugraph to consistently throw an exception if graph_view.is_multigraph() returns true. Regarding self-loops, we mask out self-loops if count_self_loops() returns a non-zero value. In C++ tests, we consistently set drop_self_loops to false; so we can test input graphs with self-loops. Marked as breaking as some functions' expected behavior changes (e.g. core_number no longer throws an exception if the input graph has self-loops). Authors: - Seunghwa Kang (https://github.com/seunghwak) Approvers: - Chuck Hastings (https://github.com/ChuckHastings) - Joseph Nke (https://github.com/jnke2016) URL: rapidsai#5109
1 parent d3fa655 commit 3c70ad0

File tree

10 files changed

+185
-150
lines changed

10 files changed

+185
-150
lines changed

cpp/include/cugraph/algorithms.hpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,6 @@ enum class cugraph_cc_t {
436436
* @ingroup components_cpp
437437
* @brief Compute connected components.
438438
*
439-
* The weak version (for undirected graphs, only) was imported from cuML.
440439
* This implementation comes from [1] and solves component labeling problem in
441440
* parallel on CSR-indexes based upon the vertex degree and adjacency graph.
442441
*
@@ -1782,8 +1781,8 @@ enum class k_core_degree_type_t { IN = 0, OUT = 1, INOUT = 2 };
17821781
.* @ingroup core_cpp
17831782
* @brief Compute core numbers of individual vertices from K-Core decomposition.
17841783
*
1785-
* The input graph should not have self-loops nor multi-edges. Currently, only undirected graphs are
1786-
* supported.
1784+
* This algorithms does not support multi-graphs. Self-loops are excluded in computing core
1785+
nuumbers.
17871786
*
17881787
* @tparam vertex_t Type of vertex identifiers. Needs to be an integral type.
17891788
* @tparam edge_t Type of edge identifiers. Needs to be an integral type.
@@ -1814,7 +1813,9 @@ void core_number(raft::handle_t const& handle,
18141813
.* @ingroup core_cpp
18151814
* @brief Extract K-Core of a graph
18161815
*
1817-
* @throws cugraph::logic_error when an error occurs.
1816+
* This function internally calls core_number (if @p core_numbers.has_value() is false). core_number
1817+
does not support multi-graphs. Self-loops are excluded in computing core nuumbers. Note that the
1818+
extracted K-Core can still include self-loops.
18181819
*
18191820
* @tparam vertex_t Type of vertex identifiers. Needs to be an integral type.
18201821
* @tparam edge_t Type of edge identifiers. Needs to be an integral type.
@@ -1851,6 +1852,9 @@ k_core(raft::handle_t const& handle,
18511852
* Compute triangle counts for the entire set of vertices (if @p vertices is std::nullopt) or the
18521853
* given vertices (@p vertices.has_value() is true).
18531854
*
1855+
* This algorithms does not support multi-graphs. Self-loops are excluded in computing triangle
1856+
* counts.
1857+
*
18541858
* @tparam vertex_t Type of vertex identifiers. Needs to be an integral type.
18551859
* @tparam edge_t Type of edge identifiers. Needs to be an integral type.
18561860
* @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false)
@@ -1876,6 +1880,9 @@ void triangle_count(raft::handle_t const& handle,
18761880
* @brief Compute edge triangle counts.
18771881
*
18781882
* Compute edge triangle counts for the entire set of edges.
1883+
*
1884+
* This algorithms does not support multi-graphs. Self-loops are excluded in computing edge triangle
1885+
counts (they will have a triangle count of 0).
18791886
*
18801887
* @tparam vertex_t Type of vertex identifiers. Needs to be an integral type.
18811888
* @tparam edge_t Type of edge identifiers. Needs to be an integral type.
@@ -1900,6 +1907,8 @@ edge_property_t<edge_t, edge_t> edge_triangle_count(
19001907
*
19011908
* Extract the K-Truss subgraph of a graph
19021909
*
1910+
* This algorithms does not support multi-graphs. Self-loops are excluded in computing K-Truss.
1911+
*
19031912
* @tparam vertex_t Type of vertex identifiers. Needs to be an integral type.
19041913
* @tparam edge_t Type of edge identifiers. Needs to be an integral type.
19051914
* @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false)

cpp/src/community/edge_triangle_count_impl.cuh

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "detail/graph_partition_utils.cuh"
2020
#include "prims/edge_bucket.cuh"
21+
#include "prims/fill_edge_property.cuh"
2122
#include "prims/per_v_pair_dst_nbr_intersection.cuh"
2223
#include "prims/transform_e.cuh"
2324

@@ -130,11 +131,37 @@ edge_property_t<edge_t, edge_t> edge_triangle_count_impl(
130131
bool do_expensive_check)
131132
{
132133
using weight_t = float;
134+
135+
CUGRAPH_EXPECTS(
136+
!graph_view.is_multigraph(),
137+
"Invalid input argument: edge triangle count currently does not support multi-graphs.");
138+
139+
// Exclude self-loops
140+
141+
std::optional<cugraph::edge_property_t<edge_t, bool>> self_loop_edge_mask{std::nullopt};
142+
auto cur_graph_view = graph_view;
143+
if (cur_graph_view.count_self_loops(handle) > edge_t{0}) {
144+
self_loop_edge_mask = cugraph::edge_property_t<edge_t, bool>(handle, cur_graph_view);
145+
if (cur_graph_view.has_edge_mask()) { cur_graph_view.clear_edge_mask(); }
146+
cugraph::fill_edge_property(handle, cur_graph_view, self_loop_edge_mask->mutable_view(), false);
147+
148+
transform_e(handle,
149+
graph_view,
150+
edge_src_dummy_property_t{}.view(),
151+
edge_dst_dummy_property_t{}.view(),
152+
edge_dummy_property_t{}.view(),
153+
cuda::proclaim_return_type<bool>(
154+
[] __device__(auto src, auto dst, auto, auto, auto) { return src != dst; }),
155+
self_loop_edge_mask->mutable_view());
156+
157+
cur_graph_view.attach_edge_mask(self_loop_edge_mask->view());
158+
}
159+
133160
rmm::device_uvector<vertex_t> edgelist_srcs(0, handle.get_stream());
134161
rmm::device_uvector<vertex_t> edgelist_dsts(0, handle.get_stream());
135162
std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore, std::ignore) =
136163
decompress_to_edgelist<vertex_t, edge_t, weight_t, int32_t>(
137-
handle, graph_view, std::nullopt, std::nullopt, std::nullopt, std::nullopt);
164+
handle, cur_graph_view, std::nullopt, std::nullopt, std::nullopt, std::nullopt);
138165

139166
auto edge_first = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin());
140167

@@ -162,7 +189,7 @@ edge_property_t<edge_t, edge_t> edge_triangle_count_impl(
162189
// Perform 'nbr_intersection' in chunks to reduce peak memory.
163190
auto [intersection_offsets, intersection_indices] =
164191
per_v_pair_dst_nbr_intersection(handle,
165-
graph_view,
192+
cur_graph_view,
166193
edge_first + prev_chunk_size,
167194
edge_first + prev_chunk_size + chunk_size,
168195
do_expensive_check);
@@ -272,7 +299,7 @@ edge_property_t<edge_t, edge_t> edge_triangle_count_impl(
272299
std::nullopt,
273300
std::nullopt,
274301
std::nullopt,
275-
graph_view.vertex_partition_range_lasts());
302+
cur_graph_view.vertex_partition_range_lasts());
276303

277304
thrust::for_each(
278305
handle.get_thrust_policy(),
@@ -335,16 +362,19 @@ edge_property_t<edge_t, edge_t> edge_triangle_count_impl(
335362
prev_chunk_size += chunk_size;
336363
}
337364

338-
cugraph::edge_property_t<edge_t, edge_t> counts(handle, graph_view);
365+
cugraph::edge_property_t<edge_t, edge_t> counts(handle, cur_graph_view);
366+
{
367+
auto unmasked_graph_view = cur_graph_view;
368+
if (unmasked_graph_view.has_edge_mask()) { unmasked_graph_view.clear_edge_mask(); }
369+
cugraph::fill_edge_property(handle, unmasked_graph_view, counts.mutable_view(), edge_t{0});
370+
}
339371

340372
cugraph::edge_bucket_t<vertex_t, void, true, multi_gpu, true> valid_edges(handle);
341373
valid_edges.insert(edgelist_srcs.begin(), edgelist_srcs.end(), edgelist_dsts.begin());
342374

343-
auto cur_graph_view = graph_view;
344-
345375
cugraph::transform_e(
346376
handle,
347-
graph_view,
377+
cur_graph_view,
348378
valid_edges,
349379
cugraph::edge_src_dummy_property_t{}.view(),
350380
cugraph::edge_dst_dummy_property_t{}.view(),

cpp/src/community/triangle_count_impl.cuh

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -358,19 +358,19 @@ void triangle_count(raft::handle_t const& handle,
358358

359359
// 3. Exclude self-loops
360360

361-
{
361+
if (cur_graph_view.count_self_loops(handle) > edge_t{0}) {
362362
cugraph::edge_property_t<edge_t, bool> self_loop_edge_mask(handle, cur_graph_view);
363363
cugraph::fill_edge_property(
364364
handle, unmasked_cur_graph_view, self_loop_edge_mask.mutable_view(), false);
365365

366-
transform_e(
367-
handle,
368-
cur_graph_view,
369-
edge_src_dummy_property_t{}.view(),
370-
edge_dst_dummy_property_t{}.view(),
371-
edge_dummy_property_t{}.view(),
372-
[] __device__(auto src, auto dst, auto, auto, auto) { return src != dst; },
373-
self_loop_edge_mask.mutable_view());
366+
transform_e(handle,
367+
cur_graph_view,
368+
edge_src_dummy_property_t{}.view(),
369+
edge_dst_dummy_property_t{}.view(),
370+
edge_dummy_property_t{}.view(),
371+
cuda::proclaim_return_type<bool>(
372+
[] __device__(auto src, auto dst, auto, auto, auto) { return src != dst; }),
373+
self_loop_edge_mask.mutable_view());
374374

375375
edge_mask = std::move(self_loop_edge_mask);
376376
if (cur_graph_view.has_edge_mask()) { cur_graph_view.clear_edge_mask(); }

0 commit comments

Comments
 (0)