From fad7a8399786e0a2da4a0248bd94c32f9fefc81d Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 13 Dec 2023 07:32:04 -0800 Subject: [PATCH 001/155] define ktruss and add it to the cmake list --- cpp/CMakeLists.txt | 1 + cpp/include/cugraph/algorithms.hpp | 20 ++++++++++++++++ cpp/src/community/ktruss_sg.cu | 37 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 cpp/src/community/ktruss_sg.cu diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 836d5569ef7..2faaf5860ee 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -225,6 +225,7 @@ set(CUGRAPH_SOURCES src/community/legacy/ecg.cu src/community/egonet_sg.cu src/community/egonet_mg.cu + src/community/ktruss_sg.cu src/sampling/random_walks.cu src/sampling/random_walks_sg.cu src/sampling/detail/prepare_next_frontier_sg.cu diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 8501eedce5c..26b419bd90c 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -1978,6 +1978,26 @@ void triangle_count(raft::handle_t const& handle, raft::device_span counts, bool do_expensive_check = false); +/* + * @brief Compute ktruss. + * + * Extract the ktruss subgraph of a graph + * + * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. + * @tparam edge_t Type of edge identifiers. Needs to be an integral type. + * @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false) + * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and + * handles to various CUDA libraries) to run graph algorithms. + * @param graph_view Graph view object. + * @param k The desired k to be used for extracting the k-truss subgraph + * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). + */ +template +void ktruss(raft::handle_t const& handle, + graph_view_t const& graph_view, + vertex_t k, + bool do_expensive_check = false); + /** * @brief Compute Jaccard similarity coefficient * diff --git a/cpp/src/community/ktruss_sg.cu b/cpp/src/community/ktruss_sg.cu new file mode 100644 index 00000000000..0f77d3b5f4e --- /dev/null +++ b/cpp/src/community/ktruss_sg.cu @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, 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 + +namespace cugraph { + +template void ktruss(raft::handle_t const& handle, + graph_view_t const& graph_view, + int32_t k, + bool do_expensive_check); + +template void ktruss(raft::handle_t const& handle, + graph_view_t const& graph_view, + int32_t k, + bool do_expensive_check); + +/* +template void ktruss(raft::handle_t const& handle, + graph_view_t const& graph_view, + int64_t k, + bool do_expensive_check); +*/ +} // namespace cugraph From 6afb913067b6f5c7c53d5db560caa0401f91013e Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 13 Dec 2023 07:32:29 -0800 Subject: [PATCH 002/155] ktruss implementation --- cpp/src/community/ktruss_impl.cuh | 843 ++++++++++++++++++++++++++++++ 1 file changed, 843 insertions(+) create mode 100644 cpp/src/community/ktruss_impl.cuh diff --git a/cpp/src/community/ktruss_impl.cuh b/cpp/src/community/ktruss_impl.cuh new file mode 100644 index 00000000000..13d8077dfb5 --- /dev/null +++ b/cpp/src/community/ktruss_impl.cuh @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2020-2023, 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 + +// FIXME: remove all unused imports +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cugraph { + +namespace { + +template +struct exclude_self_loop_t { + __device__ thrust::optional> operator()( + vertex_t src, vertex_t dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) const + { + return src != dst + ? thrust::optional>{thrust::make_tuple(src, dst)} + : thrust::nullopt; + } +}; + +template +struct is_two_or_greater_t { + __device__ bool operator()(edge_t core_number) const { return core_number >= edge_t{2}; } +}; + +template +struct extract_two_core_t { + __device__ thrust::optional> operator()( + vertex_t src, vertex_t dst, bool src_in_two_core, bool dst_in_two_core, thrust::nullopt_t) const + { + return (src_in_two_core && dst_in_two_core) + ? thrust::optional>{thrust::make_tuple(src, dst)} + : thrust::nullopt; + } +}; + + + +template +struct extract_low_to_high_degree_edges_t { + __device__ thrust::optional> operator()(vertex_t src, + vertex_t dst, + edge_t src_out_degree, + edge_t dst_out_degree, + thrust::nullopt_t) const + { + return (src_out_degree < dst_out_degree) + ? thrust::optional>{thrust::make_tuple(src, dst)} + : (((src_out_degree == dst_out_degree) && + (src < dst) /* tie-breaking using vertex ID */) + ? thrust::optional>{thrust::make_tuple(src, + dst)} + : thrust::nullopt); + } +}; + + +// primary key: major_comm_rank secondary key: local edge partition index => primary key: local edge +// partition index secondary key: major_comm_rank +template +struct update_edge_id { + //vertex_pairs_first + raft::device_span intersection_offsets{}; + raft::device_span intersection_indices{}; + //raft::device_span intersection_nbr_prop_0{}; + //raft::device_span intersection_nbr_prop_1{}; + + + __device__ edge_t operator()(edge_t i) + { + //printf("\nin operator"); + return intersection_offsets[i+1] - intersection_offsets[i]; + } +}; + + + +template +struct update_edge_id_ { + raft::device_span intersection_offsets{}; + raft::device_span intersection_indices{}; + //thrust::zip_iterator> vertex_pairs; + //typedef rmm::device_uvector::iterator VtxItr + //typedef thrust::tuple IteratorTuple; + //thrust::zip_iterator::iterator, rmm::device_uvector::iterator>> vertex_pairs_begin; + //ZipIterator vertex_pairs_begin; + //thust::tuple + + VertexPairIterator vertex_pairs_begin; + //printf("\nvalue = %d", *vertex_pairs_begin); + + __device__ thrust::tuple operator()(edge_t i) const + { + printf("\n i = %d", i); + auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + printf("\nthe idx = %d", idx); + + //printf("\n dereference = %d", *(vertex_pairs_begin + idx)); + printf("\n major = %d and minor = %d", thrust::get<0>(*(vertex_pairs_begin + idx)), thrust::get<1>(*(vertex_pairs_begin + idx))); + printf("\nintersection = %d\n", intersection_offsets[i+1] - intersection_offsets[i]); + /* + auto local_edge_partition_id = static_cast(i) / major_comm_size; + auto major_comm_rank = static_cast(i) % major_comm_size; + return group_counts[major_comm_rank * minor_comm_size + local_edge_partition_id]; + */ + //auto y = intersection_indices[0]; + + thrust::tuple pair = *(vertex_pairs_begin + idx); + //thrust::tuple pair_ = thrust::make_tuple(*(vertex_pairs_begin + idx), 1); + + //auto x = thrust::get<0>(*(vertex_pairs_begin + idx)); + //auto y = thrust::get<1>(*(vertex_pairs_begin + idx)); + //int z = pair; + + //auto pair_ = thrust::make_tuple(x, y, 1); + //return 0; + return pair; + //return *(vertex_pairs_begin + idx); + } +}; + + +/* +template +struct update_num_triangles{ + + raft::device_span num_triangles{}; + // should it be 'edge_t' instead of 'vertex_t' + //__device__ vertex_t operator()(edge_t i) const + //auto i = 0; + __device__ vertex_t operator()(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) const + { + return num_triangles[0]; + } + +}; +*/ + + + +template +struct intersection_op_t { + __device__ thrust::tuple operator()( + vertex_t v0, + vertex_t v1, + edge_t v0_prop /* out degree */, + edge_t v1_prop /* out degree */, + raft::device_span intersection, + std::byte, /* dummy */ + std::byte /* dummy */ + ) const + { + return thrust::make_tuple(v0_prop + v1_prop, static_cast(intersection.size())); + } +}; + + +} // namespace + +template +void ktruss(raft::handle_t const& handle, + graph_view_t const& graph_view, + vertex_t k, + bool do_expensive_check) +{ + using weight_t = float; // dummy + + std::cout << "k = " << k << std::endl; + + // 1. Check input arguments. + + CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); + + CUGRAPH_EXPECTS( + graph_view.is_symmetric(), + "Invalid input arguments: triangle_count currently supports undirected graphs only."); + CUGRAPH_EXPECTS( + !graph_view.is_multigraph(), + "Invalid input arguments: triangle_count currently does not support multi-graphs."); + + + // 2. Exclude self-loops (FIXME: better mask-out once we add masking support). + + std::optional> modified_graph{std::nullopt}; + std::optional> modified_graph_view{std::nullopt}; + std::optional> renumber_map{std::nullopt}; + std::optional, edge_t>> edge_ids{std::nullopt}; + // FIXME: Maybe should not be optional + std::optional> wgts{std::nullopt}; + std::optional> wgts_{std::nullopt}; + //RAFT_CUDA_TRY(cudaDeviceSynchronize()); + //raft::print_device_vector("edge property before statement", (*wgts).data(), (*wgts).size(), std::cout); + + if (graph_view.count_self_loops(handle) > edge_t{0}) { + auto [srcs, dsts] = extract_transform_e(handle, + graph_view, + edge_src_dummy_property_t{}.view(), + edge_dst_dummy_property_t{}.view(), + edge_dummy_property_t{}.view(), + exclude_self_loop_t{}); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("src - self loop ", srcs.data(), srcs.size(), std::cout); + raft::print_device_vector("dst - self loop ", dsts.data(), dsts.size(), std::cout); + + if constexpr (multi_gpu) { + std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = + detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning( + handle, std::move(srcs), std::move(dsts), std::nullopt, std::nullopt, std::nullopt); + } + + std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, renumber_map) = + create_graph_from_edgelist( + handle, + std::nullopt, + std::move(srcs), + std::move(dsts), + std::nullopt, + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{true, graph_view.is_multigraph()}, + true); + + modified_graph_view = (*modified_graph).view(); + } + + // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core (FIXME: better mask-out once we + // add masking support). + + { + auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + auto vertex_partition_range_lasts = + renumber_map + ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) + : std::nullopt; + + rmm::device_uvector core_numbers(cur_graph_view.number_of_vertices(), + handle.get_stream()); + core_number( + handle, cur_graph_view, core_numbers.data(), k_core_degree_type_t::OUT, size_t{k+1}, size_t{k+1}); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("core_number after ", core_numbers.data(), core_numbers.size(), std::cout); + + edge_src_property_t edge_src_in_two_cores(handle, + cur_graph_view); + edge_dst_property_t edge_dst_in_two_cores(handle, + cur_graph_view); + auto in_two_core_first = + thrust::make_transform_iterator(core_numbers.begin(), is_two_or_greater_t{}); + rmm::device_uvector in_two_core_flags(core_numbers.size(), handle.get_stream()); + thrust::copy(handle.get_thrust_policy(), + in_two_core_first, + in_two_core_first + core_numbers.size(), + in_two_core_flags.begin()); + update_edge_src_property( + handle, cur_graph_view, in_two_core_flags.begin(), edge_src_in_two_cores); + update_edge_dst_property( + handle, cur_graph_view, in_two_core_flags.begin(), edge_dst_in_two_cores); + auto [srcs, dsts] = extract_transform_e(handle, + cur_graph_view, + edge_src_in_two_cores.view(), + edge_dst_in_two_cores.view(), + edge_dummy_property_t{}.view(), + extract_two_core_t{}); + + if constexpr (multi_gpu) { + std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = + detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning( + handle, std::move(srcs), std::move(dsts), std::nullopt, std::nullopt, std::nullopt); + } + + std::optional> tmp_renumber_map{std::nullopt}; + + std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, tmp_renumber_map) = + create_graph_from_edgelist( + handle, + std::nullopt, + std::move(srcs), + std::move(dsts), + std::nullopt, + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{true, graph_view.is_multigraph()}, + true); + + modified_graph_view = (*modified_graph).view(); + + if (renumber_map) { // collapse renumber_map + unrenumber_int_vertices(handle, + (*tmp_renumber_map).data(), + (*tmp_renumber_map).size(), + (*renumber_map).data(), + *vertex_partition_range_lasts); + } + renumber_map = std::move(tmp_renumber_map); + //RAFT_CUDA_TRY(cudaDeviceSynchronize()); + //raft::print_device_vector("rneumber map - low - to - high", (*renumber_map).data(), (*renumber_map).size(), std::cout); + } + + // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. + { + auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + auto vertex_partition_range_lasts = + renumber_map + ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) + : std::nullopt; + + auto out_degrees = cur_graph_view.compute_out_degrees(handle); + + edge_src_property_t edge_src_out_degrees(handle, + cur_graph_view); + edge_dst_property_t edge_dst_out_degrees(handle, + cur_graph_view); + update_edge_src_property(handle, cur_graph_view, out_degrees.begin(), edge_src_out_degrees); + update_edge_dst_property(handle, cur_graph_view, out_degrees.begin(), edge_dst_out_degrees); + auto [srcs, dsts] = extract_transform_e(handle, + cur_graph_view, + edge_src_out_degrees.view(), + edge_dst_out_degrees.view(), + edge_dummy_property_t{}.view(), + extract_low_to_high_degree_edges_t{}); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("src - low - to - high", srcs.data(), srcs.size(), std::cout); + raft::print_device_vector("dst - low - to - high", dsts.data(), dsts.size(), std::cout); + + if constexpr (multi_gpu) { + std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = + detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning( + handle, std::move(srcs), std::move(dsts), std::nullopt, std::nullopt, std::nullopt); + } + + // This should be renamed as it is an edge property + // FIXME: Do this only if there is at least one pair + wgts = std::make_optional>(srcs.size(), handle.get_stream()); + wgts_ = std::make_optional>(srcs.size(), handle.get_stream()); + + + //rmm::device_uvector wgts(srcs.size(), handle.get_stream()); + + thrust::uninitialized_fill(handle.get_thrust_policy(), + (*wgts).begin(), + (*wgts).end(), + edge_t{0}); + + //auto wgts_ = + + // std::optional> modified_graph{std::nullopt}; + + + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("edge property", (*wgts_).data(), (*wgts_).size(), std::cout); + + //std::optional> edge_ids{std::nullopt}; + + thrust::copy(handle.get_thrust_policy(), + (*wgts).begin(), + (*wgts).end(), + (*wgts_).begin()); + + std::cout<<"done copying"<> tmp_renumber_map{std::nullopt}; + std::tie(*modified_graph, std::ignore, edge_ids, std::ignore, tmp_renumber_map) = + create_graph_from_edgelist( + handle, + std::nullopt, + std::move(srcs), + std::move(dsts), + std::nullopt, + std::move(wgts), + //std::nullopt, + std::nullopt, + cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, + true); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("edge property before statement", (*wgts).data(), (*wgts).size(), std::cout); + + //rmm::device_uvector&& edgelist_srcs, + //rmm::device_uvector, + + + //std::optional>&& edgelist_edge_ids + //std::optional> + + + + modified_graph_view = (*modified_graph).view(); + + if (renumber_map) { // collapse renumber_map + unrenumber_int_vertices(handle, + (*tmp_renumber_map).data(), + (*tmp_renumber_map).size(), + (*renumber_map).data(), + *vertex_partition_range_lasts); + } + renumber_map = std::move(tmp_renumber_map); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("renumber map - low - to - high", (*renumber_map).data(), (*renumber_map).size(), std::cout); + } + + // 5. Decompress the resulting graph to an edge list + + rmm::device_uvector cur_graph_counts(size_t{0}, handle.get_stream()); + { + //RAFT_CUDA_TRY(cudaDeviceSynchronize()); + //raft::print_device_vector("edge property after statement", (*wgts).data(), (*wgts).size(), std::cout); + auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + cur_graph_counts.resize(cur_graph_view.local_vertex_partition_range_size(), + handle.get_stream()); + std::cout<<"local_vertex_partition_range_size = " << cur_graph_view.local_vertex_partition_range_size() << std::endl; + + rmm::device_uvector edgelist_srcs(0, handle.get_stream()); + rmm::device_uvector edgelist_dsts(0, handle.get_stream()); + std::optional> edgelist_prop {std::nullopt}; + + /* + std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::make_optional>((*renumber_map).data(), + (*renumber_map).size())); + */ + + + //auto edge_id = std::optional>{(*wgts)}; + + std::optional> edge_id_view = (*edge_ids).view(); + + std::tie(edgelist_srcs, edgelist_dsts, std::ignore, wgts) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + //std::optional>{std::nullopt}, + edge_id_view, + //wgts, + std::optional>(std::nullopt)); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("edge property after decompressing", (*wgts).data(), (*wgts).size(), std::cout); + if (edgelist_prop){ + std::cout<<"there are edge properties"<( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()) + }); + */ + + std::cout<<"the weights size = " << (*wgts).size() << std::endl; + /* + rmm::device_uvector wgts_(6, handle.get_stream()); + thrust::uninitialized_fill(handle.get_thrust_policy(), + (wgts_).begin(), + (wgts_).end(), + edge_t{0}); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("new edge property --- ", (wgts_).data(), (wgts_).size(), std::cout); + */ + + /* + thrust::tabulate(handle.get_thrust_policy(), + (*wgts).begin(), + (*wgts).end(), + update_edge_id{ + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + raft::device_span( + r_nbr_intersection_property_values0.data(), r_nbr_intersection_property_values0.size()) + raft::device_span( + r_nbr_intersection_property_values1.data(), r_nbr_intersection_property_values1.size()) + }); + */ + + //create src_dst_intersection_size + //call adjacency diff on offsets. + /* + (p, q, 'intersection_size') + (p, r, 1) + (q, r, 1) + */ + // tabulate with the size of intersection_indices, and call binary search on intersection_offsets + // sort and reduce + + //thrust::tabulate(vertex_pairs_begin,) + //typedef rmm::device_uvector::iterator VtxItr + //typedef thrust::tuple IteratorTuple; + //typedef thrust::zip_iterator ZipIterator; + //assert(std::is_same_v) + + //allocate_dataframe_buffer (4, hand.get_stream) + + /* + auto vertex_pair_buffer = allocate_dataframe_buffer( + 4, handle.get_stream()); + */ + + auto vertex_pair_buffer__ = allocate_dataframe_buffer>( + 3, handle.get_stream()); + + auto vertex_pair_buffer = allocate_dataframe_buffer>( + 4, handle.get_stream()); + + //thust::tuple pair = thrust::make_tuple(10, 12); + //thrust::tuple pair(10, 12); + //thrust::tuple t(13, 0.1f, "thrust"); + printf("\ntabulate\n"); + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_begin(vertex_pair_buffer) + 4, + update_edge_id_{ + // vertex_pairs + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin + }); + + + thrust::sort(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer)); + + + rmm::device_uvector num_triangles(4, handle.get_stream()); + + thrust::fill(handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), size_t{1}); + + rmm::device_uvector num_triangles_(3, handle.get_stream()); + //thrust::fill(num_triangles.begin(), num_triangles.end(), size_t{1}); + + //int tie_ = std::tie(vertex_pair_buffer); + //auto v = std::get<0>(tie_); + + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + num_triangles.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer__), + num_triangles_.begin(), + thrust::equal_to>{} + + ); + + printf("\nDone reducing\n"); + + + + //cugraph::edge_bucket_t edge_list_(handle); + //auto store_transposed = false; + //auto multi_gpu = false; + // Note: ensure 'edge_list_' and 'cur_graph_view' have the same transpose flag + cugraph::edge_bucket_t edge_list_(handle); + + edge_list_.insert(std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).end(), + std::get<1>(vertex_pair_buffer).begin()); + + + cugraph::edge_property_t edge_value_output(handle, + cur_graph_view); + + /* + cugraph::transform_e( + handle, + cur_graph_view, + edge_list_, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + update_num_triangles{ + // num_triangles + raft::device_span( + num_triangles_.data(), num_triangles_.size()), + + }, + edge_value_output.mutable_view(), + false); + */ + + auto x = num_triangles_.data(); + + cugraph::transform_e( + handle, + cur_graph_view, + edge_list_, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + return 2; + }, + edge_value_output.mutable_view(), + false); + + + + raft::print_device_vector("new num triangles", num_triangles_.data(), num_triangles_.size(), std::cout); + + + /* + thrust::reduce_by_key(handle_.get_thrust_policy(), + std::tie(std::get<0>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin()), + std::tie(std::get<0>(vertex_pair_buffer).end(), std::get<1>(vertex_pair_buffer).end()), + num_triangles.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer__), + num_triangles_.begin() + ); + + */ + + //auto vertex_pairs_begin_ = thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer), get_dataframe_buffer_begin(vertex_pair_buffer)+4); + //auto vertex_pairs_begin_ = thrust::make_zip_iterator(vertex_pair_buffer, vertex_pair_buffer+4); + //auto x_ = get_dataframe_buffer_begin(vertex_pair_buffer); + //int y = thrust::get<0>(*(x_ + 1)); + //int a = vertex_pair_buffer; + //int z = std::get<0>(vertex_pair_buffer); + //auto vertex_pairs_begin__ = std::tie(std::get<0>(vertex_pair_buffer), std::get<1>(vertex_pair_buffer)) + + + vertex_pairs_begin = thrust::make_zip_iterator(std::get<0>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin()); + raft::print_device_vector("edge ids", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); + + + + //auto y = thrust::get<2>(*get_dataframe_buffer_begin(vertex_pair_buffer)); + //printf("x_ = %d\n", thrust::get<0>(*(x_))); + //printf("y = %d = \n", y+1); + + //printf("\n major = %d and minor = %d\n", thrust::get<0>(*(vertex_pairs_begin_)), thrust::get<1>(*(vertex_pairs_begin_))); + + + + + + //RAFT_CUDA_TRY(cudaDeviceSynchronize()); + //raft::print_device_vector("new edge property", (*wgts).data(), (*wgts).size(), std::cout); + + + + + + + // intersection_offsets.size() + //for (size_t i = 0; i < 1; i++) { + /* + auto intersesection = raft::device_span( + intersection_indices + intersection_offsets[i], intersection_indices + intersection_offsets[i + 1]); + */ + //std::cout<< "The intersection size = " << intersection_offsets.size() << std::endl; + //raft::print_device_vector("intersection_indices[0] ", intersection_indices.data()[0], 1, std::cout); + //std::cout<< "intersection offsets = " << (intersection_offsets) << std::endl; + + //auto intersection = raft::device_span( + // intersection_indices.data() + intersection_offsets.data()[i], intersection_indices.data() + intersection_offsets.data()[i + 1]); + + // raft::print_device_vector("intersection = ", intersection.data(), intersection.size(), std::cout); + //} + + + + } + + + + + /* + cugraph::per_v_pair_transform_dst_nbr_intersection( + *handle, + cur_graph_view, + cugraph::edge_dummy_property_t{}.view(), + vertex_pairs_begin, + vertex_pairs_begin + num_vertex_pairs, + out_degrees.begin(), + intersection_op_t{}, + cugraph::get_dataframe_buffer_begin(mg_result_buffer)); + */ + + +} + + +} // namespace cugraph + + + +/* +/home/nfs/jnke/debug_jaccard/cugraph/cpp/src/community/ktruss_impl.cuh(148): error: no suitable conversion function from "thrust::tuple" to "int32_t" exists +*/ \ No newline at end of file From b68381f6e6bda5cb549aca70e3e5065bc02f3339 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 13 Dec 2023 07:32:48 -0800 Subject: [PATCH 003/155] add tests --- cpp/tests/CMakeLists.txt | 4 + cpp/tests/community/ktruss_test.cpp | 129 ++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 cpp/tests/community/ktruss_test.cpp diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 6530a25d178..3d2f51f7df8 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -550,6 +550,10 @@ if(BUILD_CUGRAPH_MG_TESTS) # - MG TRIANGLE COUNT tests ------------------------------------------------------------------- ConfigureTestMG(MG_TRIANGLE_COUNT_TEST community/mg_triangle_count_test.cpp) + ############################################################################################### + # - Ktruss tests -------------------------------------------------------------------------- + ConfigureTest(KTRUSS_TEST community/ktruss_test.cpp) + ############################################################################################### # - MG INDUCED SUBGRAPH tests ----------------------------------------------------------------- ConfigureTestMG(MG_INDUCED_SUBGRAPH_TEST structure/mg_induced_subgraph_test.cu) diff --git a/cpp/tests/community/ktruss_test.cpp b/cpp/tests/community/ktruss_test.cpp new file mode 100644 index 00000000000..51d7f949f4c --- /dev/null +++ b/cpp/tests/community/ktruss_test.cpp @@ -0,0 +1,129 @@ +/* + * 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 governin_from_mtxg permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + + +struct Ktruss_Usecase { + int32_t k{10}; + bool check_correctness{true}; +}; + +template +class Tests_Ktruss + : public ::testing::TestWithParam> { + public: + Tests_Ktruss() {} + + static void SetUpTestCase() {} + static void TearDownTestCase() {} + + virtual void SetUp() {} + virtual void TearDown() {} + + template + void run_current_test( + std::tuple const& param) + { + constexpr bool renumber = false; + + using weight_t = float; + + auto [ktruss_usecase, input_usecase] = param; + + raft::handle_t handle{}; + HighResTimer hr_timer{}; + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.start("MG Construct graph"); + } + + cugraph::graph_t graph(handle); + std::optional> d_renumber_map_labels{std::nullopt}; + std::tie(graph, std::ignore, d_renumber_map_labels) = + cugraph::test::construct_graph( + handle, input_usecase, false, renumber, false, true); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + auto graph_view = graph.view(); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.start("Ktruss"); + } + + cugraph::ktruss( + handle, + graph_view, + ktruss_usecase.k, + false); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + } +}; + +using Tests_Ktruss_File = Tests_Ktruss; + +// FIXME: add tests for type combinations +TEST_P(Tests_Ktruss_File, CheckInt32Int32) +{ + run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + + +INSTANTIATE_TEST_SUITE_P( + file_test, + Tests_Ktruss_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(Ktruss_Usecase{2}), + ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/debug_jaccard/cugraph/datasets/dummy.mtx")))); + +CUGRAPH_TEST_PROGRAM_MAIN() From cc284e4d4b852bbcdbb17f3be89d35870b088231 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 28 Dec 2023 08:59:04 -0800 Subject: [PATCH 004/155] update ktruss implementation --- cpp/include/cugraph/algorithms.hpp | 2 +- cpp/src/community/ktruss_impl.cuh | 616 +++++++---------------------- cpp/src/community/ktruss_sg.cu | 3 +- 3 files changed, 146 insertions(+), 475 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 26b419bd90c..3df099e60ae 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -1995,7 +1995,7 @@ void triangle_count(raft::handle_t const& handle, template void ktruss(raft::handle_t const& handle, graph_view_t const& graph_view, - vertex_t k, + edge_t counts, bool do_expensive_check = false); /** diff --git a/cpp/src/community/ktruss_impl.cuh b/cpp/src/community/ktruss_impl.cuh index 13d8077dfb5..d1c76e0a212 100644 --- a/cpp/src/community/ktruss_impl.cuh +++ b/cpp/src/community/ktruss_impl.cuh @@ -58,16 +58,17 @@ struct exclude_self_loop_t { }; template -struct is_two_or_greater_t { - __device__ bool operator()(edge_t core_number) const { return core_number >= edge_t{2}; } +struct in_k_plus_one_or_greater_t { + edge_t k; + __device__ bool operator()(edge_t core_number) const {return core_number >= k; } }; template -struct extract_two_core_t { +struct extract_k_plus_one_core_t { __device__ thrust::optional> operator()( - vertex_t src, vertex_t dst, bool src_in_two_core, bool dst_in_two_core, thrust::nullopt_t) const + vertex_t src, vertex_t dst, bool src_in_k_plus_one_core, bool dst_in_k_plus_one_core, thrust::nullopt_t) const { - return (src_in_two_core && dst_in_two_core) + return (src_in_k_plus_one_core && dst_in_k_plus_one_core) ? thrust::optional>{thrust::make_tuple(src, dst)} : thrust::nullopt; } @@ -94,89 +95,61 @@ struct extract_low_to_high_degree_edges_t { }; -// primary key: major_comm_rank secondary key: local edge partition index => primary key: local edge -// partition index secondary key: major_comm_rank -template -struct update_edge_id { - //vertex_pairs_first + + +template +struct extract_p_q { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - //raft::device_span intersection_nbr_prop_0{}; - //raft::device_span intersection_nbr_prop_1{}; + VertexPairIterator vertex_pairs_begin; - __device__ edge_t operator()(edge_t i) + __device__ thrust::tuple operator()(edge_t i) const { - //printf("\nin operator"); - return intersection_offsets[i+1] - intersection_offsets[i]; + auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + thrust::tuple pair = *(vertex_pairs_begin + idx); + + return pair; } }; + template -struct update_edge_id_ { +struct extract_p_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - //thrust::zip_iterator> vertex_pairs; - //typedef rmm::device_uvector::iterator VtxItr - //typedef thrust::tuple IteratorTuple; - //thrust::zip_iterator::iterator, rmm::device_uvector::iterator>> vertex_pairs_begin; - //ZipIterator vertex_pairs_begin; - //thust::tuple - VertexPairIterator vertex_pairs_begin; - //printf("\nvalue = %d", *vertex_pairs_begin); __device__ thrust::tuple operator()(edge_t i) const { - printf("\n i = %d", i); auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - printf("\nthe idx = %d", idx); - - //printf("\n dereference = %d", *(vertex_pairs_begin + idx)); - printf("\n major = %d and minor = %d", thrust::get<0>(*(vertex_pairs_begin + idx)), thrust::get<1>(*(vertex_pairs_begin + idx))); - printf("\nintersection = %d\n", intersection_offsets[i+1] - intersection_offsets[i]); - /* - auto local_edge_partition_id = static_cast(i) / major_comm_size; - auto major_comm_rank = static_cast(i) % major_comm_size; - return group_counts[major_comm_rank * minor_comm_size + local_edge_partition_id]; - */ - //auto y = intersection_indices[0]; - - thrust::tuple pair = *(vertex_pairs_begin + idx); - //thrust::tuple pair_ = thrust::make_tuple(*(vertex_pairs_begin + idx), 1); - - //auto x = thrust::get<0>(*(vertex_pairs_begin + idx)); - //auto y = thrust::get<1>(*(vertex_pairs_begin + idx)); - //int z = pair; - - //auto pair_ = thrust::make_tuple(x, y, 1); - //return 0; - return pair; - //return *(vertex_pairs_begin + idx); + auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + + return pair; } }; -/* -template -struct update_num_triangles{ - raft::device_span num_triangles{}; - // should it be 'edge_t' instead of 'vertex_t' - //__device__ vertex_t operator()(edge_t i) const - //auto i = 0; - __device__ vertex_t operator()(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) const +template +struct extract_q_r { + raft::device_span intersection_offsets{}; + raft::device_span intersection_indices{}; + VertexPairIterator vertex_pairs_begin; + + __device__ thrust::tuple operator()(edge_t i) const { - return num_triangles[0]; - } + auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + return pair; + } }; -*/ - template @@ -201,12 +174,11 @@ struct intersection_op_t { template void ktruss(raft::handle_t const& handle, graph_view_t const& graph_view, - vertex_t k, + edge_t k, bool do_expensive_check) { using weight_t = float; // dummy - std::cout << "k = " << k << std::endl; // 1. Check input arguments. @@ -229,8 +201,6 @@ void ktruss(raft::handle_t const& handle, // FIXME: Maybe should not be optional std::optional> wgts{std::nullopt}; std::optional> wgts_{std::nullopt}; - //RAFT_CUDA_TRY(cudaDeviceSynchronize()); - //raft::print_device_vector("edge property before statement", (*wgts).data(), (*wgts).size(), std::cout); if (graph_view.count_self_loops(handle) > edge_t{0}) { auto [srcs, dsts] = extract_transform_e(handle, @@ -239,10 +209,6 @@ void ktruss(raft::handle_t const& handle, edge_dst_dummy_property_t{}.view(), edge_dummy_property_t{}.view(), exclude_self_loop_t{}); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("src - self loop ", srcs.data(), srcs.size(), std::cout); - raft::print_device_vector("dst - self loop ", dsts.data(), dsts.size(), std::cout); if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = @@ -283,30 +249,27 @@ void ktruss(raft::handle_t const& handle, core_number( handle, cur_graph_view, core_numbers.data(), k_core_degree_type_t::OUT, size_t{k+1}, size_t{k+1}); - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("core_number after ", core_numbers.data(), core_numbers.size(), std::cout); - - edge_src_property_t edge_src_in_two_cores(handle, + edge_src_property_t edge_src_in_k_plus_one_cores(handle, cur_graph_view); - edge_dst_property_t edge_dst_in_two_cores(handle, + edge_dst_property_t edge_dst_in_k_plus_one_cores(handle, cur_graph_view); - auto in_two_core_first = - thrust::make_transform_iterator(core_numbers.begin(), is_two_or_greater_t{}); - rmm::device_uvector in_two_core_flags(core_numbers.size(), handle.get_stream()); + auto in_k_plus_one_core_first = + thrust::make_transform_iterator(core_numbers.begin(), in_k_plus_one_or_greater_t{k}); + rmm::device_uvector in_k_plus_one_core_flags(core_numbers.size(), handle.get_stream()); thrust::copy(handle.get_thrust_policy(), - in_two_core_first, - in_two_core_first + core_numbers.size(), - in_two_core_flags.begin()); + in_k_plus_one_core_first, + in_k_plus_one_core_first + core_numbers.size(), + in_k_plus_one_core_flags.begin()); update_edge_src_property( - handle, cur_graph_view, in_two_core_flags.begin(), edge_src_in_two_cores); + handle, cur_graph_view, in_k_plus_one_core_flags.begin(), edge_src_in_k_plus_one_cores); update_edge_dst_property( - handle, cur_graph_view, in_two_core_flags.begin(), edge_dst_in_two_cores); + handle, cur_graph_view, in_k_plus_one_core_flags.begin(), edge_dst_in_k_plus_one_cores); auto [srcs, dsts] = extract_transform_e(handle, cur_graph_view, - edge_src_in_two_cores.view(), - edge_dst_in_two_cores.view(), + edge_src_in_k_plus_one_cores.view(), + edge_dst_in_k_plus_one_cores.view(), edge_dummy_property_t{}.view(), - extract_two_core_t{}); + extract_k_plus_one_core_t{}); if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = @@ -341,8 +304,6 @@ void ktruss(raft::handle_t const& handle, *vertex_partition_range_lasts); } renumber_map = std::move(tmp_renumber_map); - //RAFT_CUDA_TRY(cudaDeviceSynchronize()); - //raft::print_device_vector("rneumber map - low - to - high", (*renumber_map).data(), (*renumber_map).size(), std::cout); } // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. @@ -367,10 +328,6 @@ void ktruss(raft::handle_t const& handle, edge_dst_out_degrees.view(), edge_dummy_property_t{}.view(), extract_low_to_high_degree_edges_t{}); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("src - low - to - high", srcs.data(), srcs.size(), std::cout); - raft::print_device_vector("dst - low - to - high", dsts.data(), dsts.size(), std::cout); if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = @@ -381,65 +338,21 @@ void ktruss(raft::handle_t const& handle, handle, std::move(srcs), std::move(dsts), std::nullopt, std::nullopt, std::nullopt); } - // This should be renamed as it is an edge property - // FIXME: Do this only if there is at least one pair - wgts = std::make_optional>(srcs.size(), handle.get_stream()); - wgts_ = std::make_optional>(srcs.size(), handle.get_stream()); - - - //rmm::device_uvector wgts(srcs.size(), handle.get_stream()); - - thrust::uninitialized_fill(handle.get_thrust_policy(), - (*wgts).begin(), - (*wgts).end(), - edge_t{0}); - - //auto wgts_ = - - // std::optional> modified_graph{std::nullopt}; - - - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("edge property", (*wgts_).data(), (*wgts_).size(), std::cout); - - //std::optional> edge_ids{std::nullopt}; - - thrust::copy(handle.get_thrust_policy(), - (*wgts).begin(), - (*wgts).end(), - (*wgts_).begin()); - - std::cout<<"done copying"<> tmp_renumber_map{std::nullopt}; - std::tie(*modified_graph, std::ignore, edge_ids, std::ignore, tmp_renumber_map) = + std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, tmp_renumber_map) = create_graph_from_edgelist( handle, std::nullopt, std::move(srcs), std::move(dsts), std::nullopt, - std::move(wgts), - //std::nullopt, + std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, true); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("edge property before statement", (*wgts).data(), (*wgts).size(), std::cout); - - //rmm::device_uvector&& edgelist_srcs, - //rmm::device_uvector, - - - //std::optional>&& edgelist_edge_ids - //std::optional> - - modified_graph_view = (*modified_graph).view(); - + if (renumber_map) { // collapse renumber_map unrenumber_int_vertices(handle, (*tmp_renumber_map).data(), @@ -448,396 +361,153 @@ void ktruss(raft::handle_t const& handle, *vertex_partition_range_lasts); } renumber_map = std::move(tmp_renumber_map); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("renumber map - low - to - high", (*renumber_map).data(), (*renumber_map).size(), std::cout); } - // 5. Decompress the resulting graph to an edge list + // 5. Decompress the resulting graph to an edge list and ind intersection of edge endpoints + // for each partition using detail::nbr_intersection rmm::device_uvector cur_graph_counts(size_t{0}, handle.get_stream()); { - //RAFT_CUDA_TRY(cudaDeviceSynchronize()); - //raft::print_device_vector("edge property after statement", (*wgts).data(), (*wgts).size(), std::cout); auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; cur_graph_counts.resize(cur_graph_view.local_vertex_partition_range_size(), handle.get_stream()); - std::cout<<"local_vertex_partition_range_size = " << cur_graph_view.local_vertex_partition_range_size() << std::endl; rmm::device_uvector edgelist_srcs(0, handle.get_stream()); rmm::device_uvector edgelist_dsts(0, handle.get_stream()); std::optional> edgelist_prop {std::nullopt}; - /* - std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::make_optional>((*renumber_map).data(), - (*renumber_map).size())); - */ - - - //auto edge_id = std::optional>{(*wgts)}; - - std::optional> edge_id_view = (*edge_ids).view(); - - std::tie(edgelist_srcs, edgelist_dsts, std::ignore, wgts) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - //std::optional>{std::nullopt}, - edge_id_view, - //wgts, - std::optional>(std::nullopt)); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("edge property after decompressing", (*wgts).data(), (*wgts).size(), std::cout); - if (edgelist_prop){ - std::cout<<"there are edge properties"<>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); - //vertex_t* v_begin = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + auto vertex_pairs_begin = thrust::make_zip_iterator( + edgelist_srcs.begin(), edgelist_dsts.begin()); size_t num_vertex_pairs = edgelist_srcs.size(); auto out_degrees = cur_graph_view.compute_out_degrees(handle); - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - std::cout<< "number of vertex pairs = " << num_vertex_pairs << std::endl; - raft::print_device_vector("src ", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dst ", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("out_degrees ", out_degrees.data(), out_degrees.size(), std::cout); - //std::cout<< "outdegree = " << out_degrees << std::endl; - - - // sort and reduce - - //auto vertex_pairs_end = thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end()); - //auto vertex_pairs_begin = thrust::make_zip_iterator(std::get<0>(vertex_pairs).data(), std::get<1>(vertex_pairs).data()); - - // 6. Find intersection of edge endppints for each partition using detail::nbr_intersection - // FIXME: Move this in its own {} rmm::device_uvector intersection_offsets(size_t{0}, handle.get_stream()); rmm::device_uvector intersection_indices(size_t{0}, handle.get_stream()); rmm::device_uvector r_nbr_intersection_property_values0(size_t{0}, handle.get_stream()); rmm::device_uvector r_nbr_intersection_property_values1(size_t{0}, handle.get_stream()); - /* - [[maybe_unused]] rmm::device_uvector - r_nbr_intersection_property_values0(size_t{0}, handle.get_stream()); - [[maybe_unused]] rmm::device_uvector - r_nbr_intersection_property_values1(size_t{0}, handle.get_stream()); - */ - - // do_expensive_check = true; - // std::optional>{std::nullopt} - // auto view_ = (*edge_ids).view(); - //std::tie(intersection_offsets, intersection_indices) = - - //create src_dst_intersection_size - //call adjacency diff on offsets. - /* - (p, q, 'intersection_size') - (p, r, 1) - (q, r, 1) - */ - // tabulate with the size of intersection_indices, and call binary search on intersection_offsets - // sort and reduce - /* - std::tie(intersection_offsets, intersection_indices, r_nbr_intersection_property_values0, r_nbr_intersection_property_values1) = - detail::nbr_intersection(handle, - cur_graph_view, - //cugraph::edge_dummy_property_t{}.view(), - (*edge_ids).view(), - vertex_pairs_begin, - vertex_pairs_begin + num_vertex_pairs, - std::array{true, true}, - do_expensive_check); - */ - - // FIXME: Initially each eadge should have an edge property of 0 - - + // FIXME: Initially each edge should have an edge property of 0 std::tie(intersection_offsets, intersection_indices) = detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - //(*edge_ids).view(), vertex_pairs_begin, vertex_pairs_begin + num_vertex_pairs, std::array{true, true}, do_expensive_check); - - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("intersection_offsets ", intersection_offsets.data(), intersection_offsets.size(), std::cout); - raft::print_device_vector("intersection_indices ", intersection_indices.data(), intersection_indices.size(), std::cout); - //raft::print_device_vector("r_nbr_intersection_property_values0 ", r_nbr_intersection_property_values0.data(), r_nbr_intersection_property_values0.size(), std::cout); - //raft::print_device_vector("r_nbr_intersection_property_values1 ", r_nbr_intersection_property_values1.data(), r_nbr_intersection_property_values1.size(), std::cout); - - - - //auto x = intersection_offsets; - /* - thrust::tabulate(handle.get_thrust_policy(), - intersection_offsets.begin(), - intersection_offsets.end(), - update_edge_id_{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()) - }); - */ - - std::cout<<"the weights size = " << (*wgts).size() << std::endl; - /* - rmm::device_uvector wgts_(6, handle.get_stream()); - thrust::uninitialized_fill(handle.get_thrust_policy(), - (wgts_).begin(), - (wgts_).end(), - edge_t{0}); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("new edge property --- ", (wgts_).data(), (wgts_).size(), std::cout); - */ - - /* - thrust::tabulate(handle.get_thrust_policy(), - (*wgts).begin(), - (*wgts).end(), - update_edge_id{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - raft::device_span( - r_nbr_intersection_property_values0.data(), r_nbr_intersection_property_values0.size()) - raft::device_span( - r_nbr_intersection_property_values1.data(), r_nbr_intersection_property_values1.size()) - }); - */ - - //create src_dst_intersection_size - //call adjacency diff on offsets. - /* - (p, q, 'intersection_size') - (p, r, 1) - (q, r, 1) - */ - // tabulate with the size of intersection_indices, and call binary search on intersection_offsets - // sort and reduce - - //thrust::tabulate(vertex_pairs_begin,) - //typedef rmm::device_uvector::iterator VtxItr - //typedef thrust::tuple IteratorTuple; - //typedef thrust::zip_iterator ZipIterator; - //assert(std::is_same_v) - - //allocate_dataframe_buffer (4, hand.get_stream) - - /* - auto vertex_pair_buffer = allocate_dataframe_buffer( - 4, handle.get_stream()); - */ - - auto vertex_pair_buffer__ = allocate_dataframe_buffer>( - 3, handle.get_stream()); - auto vertex_pair_buffer = allocate_dataframe_buffer>( - 4, handle.get_stream()); + num_vertex_pairs, handle.get_stream()); - //thust::tuple pair = thrust::make_tuple(10, 12); - //thrust::tuple pair(10, 12); - //thrust::tuple t(13, 0.1f, "thrust"); - printf("\ntabulate\n"); - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_begin(vertex_pair_buffer) + 4, - update_edge_id_{ - // vertex_pairs - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin - }); - + // stores all the pairs (p, q), (p, r) and (q, r) + auto vertex_pair_buffer_ = allocate_dataframe_buffer>( + intersection_indices.size() * 3, handle.get_stream()); + // FIXME: optmize this part to not have to iterate over all the edges again + // tabulate with the size of intersection_indices, and call binary search on intersection_offsets + // to get (p, q). + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_), + get_dataframe_buffer_begin(vertex_pair_buffer_) + intersection_indices.size(), + extract_p_q{ + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin + }); + + // tabulate with the size of intersection_indices, and call binary search on intersection_offsets + // to get (p, r). + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_) + intersection_indices.size(), + get_dataframe_buffer_begin(vertex_pair_buffer_) + (2 * intersection_indices.size()), + extract_p_r{ + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin + }); + + // tabulate with the size of intersection_indices, and call binary search on intersection_offsets + // to get (q, r). + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_) + (2 * intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_) + (3 * intersection_indices.size()), + extract_q_r{ + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin + }); + thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer)); + get_dataframe_buffer_begin(vertex_pair_buffer_), + get_dataframe_buffer_end(vertex_pair_buffer_)); - rmm::device_uvector num_triangles(4, handle.get_stream()); - - thrust::fill(handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), size_t{1}); - - rmm::device_uvector num_triangles_(3, handle.get_stream()); - //thrust::fill(num_triangles.begin(), num_triangles.end(), size_t{1}); + rmm::device_uvector num_triangles_(3 * intersection_indices.size(), handle.get_stream()); - //int tie_ = std::tie(vertex_pair_buffer); - //auto v = std::get<0>(tie_); + thrust::fill(handle.get_thrust_policy(), num_triangles_.begin(), num_triangles_.end(), size_t{1}); + rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_), + get_dataframe_buffer_end(vertex_pair_buffer_), + num_triangles_.begin(), get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), num_triangles.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer__), - num_triangles_.begin(), - thrust::equal_to>{} - - ); - - printf("\nDone reducing\n"); - + thrust::equal_to>{}); - //cugraph::edge_bucket_t edge_list_(handle); - //auto store_transposed = false; - //auto multi_gpu = false; - // Note: ensure 'edge_list_' and 'cur_graph_view' have the same transpose flag - cugraph::edge_bucket_t edge_list_(handle); + // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag + cugraph::edge_bucket_tedge_list(handle); - edge_list_.insert(std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).end(), - std::get<1>(vertex_pair_buffer).begin()); + edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).end(), + std::get<1>(vertex_pair_buffer).begin()); cugraph::edge_property_t edge_value_output(handle, - cur_graph_view); - - /* - cugraph::transform_e( - handle, - cur_graph_view, - edge_list_, - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - update_num_triangles{ - // num_triangles - raft::device_span( - num_triangles_.data(), num_triangles_.size()), - - }, - edge_value_output.mutable_view(), - false); - */ - - auto x = num_triangles_.data(); + cur_graph_view); cugraph::transform_e( - handle, - cur_graph_view, - edge_list_, - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - return 2; - }, - edge_value_output.mutable_view(), - false); - - - - raft::print_device_vector("new num triangles", num_triangles_.data(), num_triangles_.size(), std::cout); - - - /* - thrust::reduce_by_key(handle_.get_thrust_policy(), - std::tie(std::get<0>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin()), - std::tie(std::get<0>(vertex_pair_buffer).end(), std::get<1>(vertex_pair_buffer).end()), - num_triangles.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer__), - num_triangles_.begin() - ); - - */ - - //auto vertex_pairs_begin_ = thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer), get_dataframe_buffer_begin(vertex_pair_buffer)+4); - //auto vertex_pairs_begin_ = thrust::make_zip_iterator(vertex_pair_buffer, vertex_pair_buffer+4); - //auto x_ = get_dataframe_buffer_begin(vertex_pair_buffer); - //int y = thrust::get<0>(*(x_ + 1)); - //int a = vertex_pair_buffer; - //int z = std::get<0>(vertex_pair_buffer); - //auto vertex_pairs_begin__ = std::tie(std::get<0>(vertex_pair_buffer), std::get<1>(vertex_pair_buffer)) - - - vertex_pairs_begin = thrust::make_zip_iterator(std::get<0>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin()); - raft::print_device_vector("edge ids", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); - - - - //auto y = thrust::get<2>(*get_dataframe_buffer_begin(vertex_pair_buffer)); - //printf("x_ = %d\n", thrust::get<0>(*(x_))); - //printf("y = %d = \n", y+1); - - //printf("\n major = %d and minor = %d\n", thrust::get<0>(*(vertex_pairs_begin_)), thrust::get<1>(*(vertex_pairs_begin_))); - - - - - - //RAFT_CUDA_TRY(cudaDeviceSynchronize()); - //raft::print_device_vector("new edge property", (*wgts).data(), (*wgts).size(), std::cout); - - - - - - - // intersection_offsets.size() - //for (size_t i = 0; i < 1; i++) { - /* - auto intersesection = raft::device_span( - intersection_indices + intersection_offsets[i], intersection_indices + intersection_offsets[i + 1]); - */ - //std::cout<< "The intersection size = " << intersection_offsets.size() << std::endl; - //raft::print_device_vector("intersection_indices[0] ", intersection_indices.data()[0], 1, std::cout); - //std::cout<< "intersection offsets = " << (intersection_offsets) << std::endl; - - //auto intersection = raft::device_span( - // intersection_indices.data() + intersection_offsets.data()[i], intersection_indices.data() + intersection_offsets.data()[i + 1]); - - // raft::print_device_vector("intersection = ", intersection.data(), intersection.size(), std::cout); - //} - - - - } - - - - - /* - cugraph::per_v_pair_transform_dst_nbr_intersection( - *handle, + handle, cur_graph_view, + edge_list, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), - vertex_pairs_begin, - vertex_pairs_begin + num_vertex_pairs, - out_degrees.begin(), - intersection_op_t{}, - cugraph::get_dataframe_buffer_begin(mg_result_buffer)); - */ - + [num_triangles = num_triangles.data(), + vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), + size=get_dataframe_buffer_end(vertex_pair_buffer) - get_dataframe_buffer_begin(vertex_pair_buffer)] + __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + + auto it = thrust::upper_bound(thrust::seq, + vertex_pair_buffer, + vertex_pair_buffer + size, + thrust::make_tuple(src, dst), + thrust::equal_to>{}); + + auto idx = thrust::distance(vertex_pair_buffer, it); + return num_triangles[idx]; + }, + edge_value_output.mutable_view(), + false); + } } - } // namespace cugraph - - - -/* -/home/nfs/jnke/debug_jaccard/cugraph/cpp/src/community/ktruss_impl.cuh(148): error: no suitable conversion function from "thrust::tuple" to "int32_t" exists -*/ \ No newline at end of file diff --git a/cpp/src/community/ktruss_sg.cu b/cpp/src/community/ktruss_sg.cu index 0f77d3b5f4e..2bc6ab5fefa 100644 --- a/cpp/src/community/ktruss_sg.cu +++ b/cpp/src/community/ktruss_sg.cu @@ -23,9 +23,10 @@ template void ktruss(raft::handle_t const& handle, int32_t k, bool do_expensive_check); + template void ktruss(raft::handle_t const& handle, graph_view_t const& graph_view, - int32_t k, + int64_t k, bool do_expensive_check); /* From 1635f874f26727a0b7bf3fb1d65511fec6ab8ee4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 8 Jan 2024 10:38:46 -0800 Subject: [PATCH 005/155] update implementation --- cpp/src/community/ktruss_impl.cuh | 415 ++++++++++++++++++++++++++++-- 1 file changed, 397 insertions(+), 18 deletions(-) diff --git a/cpp/src/community/ktruss_impl.cuh b/cpp/src/community/ktruss_impl.cuh index d1c76e0a212..c6662c1f043 100644 --- a/cpp/src/community/ktruss_impl.cuh +++ b/cpp/src/community/ktruss_impl.cuh @@ -106,11 +106,18 @@ struct extract_p_q { __device__ thrust::tuple operator()(edge_t i) const { + printf("\n i = %d", i); auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - thrust::tuple pair = *(vertex_pairs_begin + idx); + printf("\nthe idx = %d", idx); + + printf("\n major = %d and minor = %d", thrust::get<0>(*(vertex_pairs_begin + idx)), thrust::get<1>(*(vertex_pairs_begin + idx))); + printf("\nintersection = %d\n", intersection_offsets[i+1] - intersection_offsets[i]); + + thrust::tuple pair = *(vertex_pairs_begin + idx); - return pair; + return pair; } }; @@ -121,15 +128,20 @@ template struct extract_p_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; + VertexPairIterator vertex_pairs_begin; + __device__ thrust::tuple operator()(edge_t i) const { auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - return pair; + printf("\n ** (p, q) major = %d and minor = %d", thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + + return pair; } }; @@ -139,19 +151,86 @@ template struct extract_q_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; + VertexPairIterator vertex_pairs_begin; + __device__ thrust::tuple operator()(edge_t i) const { + printf("\n i = %d", i); auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - return pair; + printf("\n ** (p, q) major = %d and minor = %d", thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + + auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + + return pair; } }; +template +struct triangle_counts_less_t { + raft::device_span num_triangles{}; + edge_t k; + +}; + +template +struct find_intersection { + size_t vertex_pair_buffer_size; + size_t num_invalid_edges; + raft::device_span intersection_offsets{}; + raft::device_span intersection_indices{}; + + VertexPairBuffer vertex_pair_buffer; + Iterator invalid_edge_first; + + __device__ size_t operator()(edge_t i) const + { + printf("\n ***** i = %d\n", i); + printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); + printf("\nthe vertex pair buffer size = %d\n", vertex_pair_buffer_size); + //auto x = thrust::get<1>(*invalid_edge_first); + //printf("\nx = %d\n", x); + // Run binary search + //if (i < vertex_pair_buffer_size) { + if (i < num_invalid_edges) { + auto pair = thrust::get<0>(*(invalid_edge_first + i)); // FIXME only increment if it is less than 'invalid_edge_first.size()' + printf("\nsrc_pair = %d, dst_pair = %d\n", thrust::get<0>(pair), thrust::get<1>(pair)); + + auto itr = thrust::upper_bound(thrust::seq, + vertex_pair_buffer, + vertex_pair_buffer + vertex_pair_buffer_size, + //pair, + thrust::make_tuple(thrust::get<0>(pair), thrust::get<1>(pair)), + thrust::less>{}); + + printf("\n itr = %d\n", itr); + auto idx = thrust::distance(vertex_pair_buffer + 1, itr); + printf("\n idx = %d \n", idx); + + return 1; + } + else { + return 0; + } + + + return 0; + } +}; + + + + + + + + + template struct intersection_op_t { __device__ thrust::tuple operator()( @@ -386,6 +465,10 @@ void ktruss(raft::handle_t const& handle, auto vertex_pairs_begin = thrust::make_zip_iterator( edgelist_srcs.begin(), edgelist_dsts.begin()); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("src ", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dst ", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); size_t num_vertex_pairs = edgelist_srcs.size(); auto out_degrees = cur_graph_view.compute_out_degrees(handle); @@ -404,10 +487,15 @@ void ktruss(raft::handle_t const& handle, vertex_pairs_begin + num_vertex_pairs, std::array{true, true}, do_expensive_check); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector("intersection_offsets ", intersection_offsets.data(), intersection_offsets.size(), std::cout); + raft::print_device_vector("intersection_indices ", intersection_indices.data(), intersection_indices.size(), std::cout); auto vertex_pair_buffer = allocate_dataframe_buffer>( num_vertex_pairs, handle.get_stream()); + // stores all the pairs (p, q), (p, r) and (q, r) auto vertex_pair_buffer_ = allocate_dataframe_buffer>( intersection_indices.size() * 3, handle.get_stream()); @@ -452,6 +540,11 @@ void ktruss(raft::handle_t const& handle, vertex_pairs_begin }); + printf("\nbefore sorting\n"); + raft::print_device_vector("src", std::get<0>(vertex_pair_buffer_).data(), std::get<0>(vertex_pair_buffer_).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer_).data(), std::get<1>(vertex_pair_buffer_).size(), std::cout); + + thrust::sort(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_), get_dataframe_buffer_end(vertex_pair_buffer_)); @@ -473,39 +566,325 @@ void ktruss(raft::handle_t const& handle, // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag + // one of the void cugraph::edge_bucket_tedge_list(handle); edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), std::get<0>(vertex_pair_buffer).end(), + // num triangle here std::get<1>(vertex_pair_buffer).begin()); cugraph::edge_property_t edge_value_output(handle, cur_graph_view); - + thrust::sort(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + thrust::less>{}); + raft::print_device_vector("sorted - src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); + raft::print_device_vector("sorted - dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); + raft::print_device_vector("new num triangles", num_triangles.data(), num_triangles.size(), std::cout); + handle.sync_stream(); cugraph::transform_e( handle, cur_graph_view, - edge_list, + edge_list, // assigning th e number of triangles as an edge property cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), [num_triangles = num_triangles.data(), - vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), - size=get_dataframe_buffer_end(vertex_pair_buffer) - get_dataframe_buffer_begin(vertex_pair_buffer)] - __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), + size=get_dataframe_buffer_end(vertex_pair_buffer) - get_dataframe_buffer_begin(vertex_pair_buffer)] // FIXME: use 'size_dataframe_buffer(vertex_pair_buffer)' + // FIXME: Maybe: the last 'thrust::nullopt_t' for the edge prop containing the number of triangles. + // Take a look at the 'vertex_bucket_t' for context + __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + /* + for ( int i =0 ; i < size; i++){ + //printf("\n i = %d major = %d and minor = %d", i, thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); + printf("hello"); + } + */ + + + + + auto x = thrust::make_tuple(thrust::get<0>(*(vertex_pair_buffer)), thrust::get<1>(*(vertex_pair_buffer))); + printf("\nsrc = %d , dst = %d\n", src, dst); + //printf("\ny = %d\n", vertex_pair_buffer); + + auto it = thrust::lower_bound(thrust::seq, + vertex_pair_buffer, + vertex_pair_buffer + size, + thrust::make_tuple(src, dst)); + printf("\ntransform_e - itr = %d\n", it); + auto idx = thrust::distance(vertex_pair_buffer, it); + printf("\nthe distance = %d\n", idx); - auto it = thrust::upper_bound(thrust::seq, - vertex_pair_buffer, - vertex_pair_buffer + size, - thrust::make_tuple(src, dst), - thrust::equal_to>{}); + /* + auto it_ = thrust::lower_bound(thrust::seq, + vertex_pair_buffer, + vertex_pair_buffer + size, + //thrust::make_tuple(src, dst), + thrust::make_tuple(1, 3), + thrust::less>{}); + printf("\ntransform_e - itr____ = %d\n", it_); + auto idx_ = thrust::distance(vertex_pair_buffer, it_); + printf("\nthe distance = %d\n", idx_); + */ - auto idx = thrust::distance(vertex_pair_buffer, it); - return num_triangles[idx]; + //printf("\nitr = %d\n", *it); + return num_triangles[idx]; }, edge_value_output.mutable_view(), false); + + + /* + rmm::device_uvector intersection_size_(intersection_offsets.size() - 1, handle.get_stream()); // take the worst case where all edges are invalidated + // FIXME: might not be necessary to fill it with zeros + thrust::fill(handle.get_thrust_policy(), intersection_size_.begin(), intersection_size_.end(), size_t{0}); + printf("\n*******dummy******\n"); + thrust::tabulate(handle.get_thrust_policy(), + intersection_size_.begin(), + intersection_size_.end(), + [vertex_pair_buffer_size=size_dataframe_buffer(vertex_pair_buffer), + intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + intersection_indices = raft::device_span(intersection_indices.data(), intersection_indices.size()), + vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer) + ] + __device__(edge_t i){ + printf("\n ***** i = %d\n", i); + printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); + printf("\nthe vertex pair buffer size = %d\n", vertex_pair_buffer_size); + auto num_invalid_edges = 6; + if (i < num_invalid_edges) { + //auto pair = thrust::get<0>(*(invalid_edge_first + i)); // FIXME only increment if it is less than 'invalid_edge_first.size()' + //printf("\nsrc_pair = %d, dst_pair = %d\n", thrust::get<0>(pair), thrust::get<1>(pair)); + + auto itr = thrust::lower_bound(thrust::seq, + vertex_pair_buffer, + vertex_pair_buffer + vertex_pair_buffer_size, + //pair, + //thrust::make_tuple(thrust::get<0>(pair), thrust::get<1>(pair)), + thrust::make_tuple(2, 3), + thrust::less>{}); + + printf("\n itr = %d\n", itr); + auto idx = thrust::distance(vertex_pair_buffer, itr); + printf("\n idx = %d \n", idx); + + return 1; + } + else { + return 0; + } + + }); + printf("\n*******Done dummy******\n"); + */ + + + + + + + + + + + + auto value_firsts = edge_value_output.view().value_firsts(); + auto edge_counts = edge_value_output.view().edge_counts(); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); + raft::print_device_vector( + "num_triangles_:", value_firsts[0], edge_counts[0], std::cout); + + vertex_pairs_begin = thrust::make_zip_iterator(std::get<0>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin()); // FIXME: not used below but used up + printf("\nafter sorting\n"); + raft::print_device_vector("src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); + + // FIXME: shuffle before sorting and reducing - Bring this part up + if constexpr (multi_gpu) { + thrust::copy(handle.get_thrust_policy(), + std::get<0>(vertex_pair_buffer).data(), + std::get<0>(vertex_pair_buffer).data() + std::get<0>(vertex_pair_buffer).size(), + edgelist_srcs.begin()); + + thrust::copy(handle.get_thrust_policy(), + std::get<1>(vertex_pair_buffer).data(), + std::get<1>(vertex_pair_buffer).data() + std::get<1>(vertex_pair_buffer).size(), + edgelist_dsts.begin()); + + edgelist_srcs.resize(std::get<0>(vertex_pair_buffer).size(), handle.get_stream()); + edgelist_dsts.resize(std::get<0>(vertex_pair_buffer).size(), handle.get_stream()); + + std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore, std::ignore) = + detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning( + handle, std::move(edgelist_srcs), std::move(edgelist_dsts), std::nullopt, std::nullopt, std::nullopt); + } + + // Run thrust::partition to place the edges with triangle counts smaller than K-2 at the end + // zip iterator for the pair + auto edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + // FIXME: rename it to edge_to_remove_first + auto invalid_edge_first = thrust::partition( + handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_triangles.size(), + [k]__device__(auto e){ + auto num_triangles = thrust::get<1>(e); + auto is_in_k_truss = num_triangles == 2; + printf("\n is in k_truss = %d\n", is_in_k_truss); + return num_triangles > k; // FIXME k-2 + + } + ); + + // FIXME: rename it num_edges_to_remove + size_t num_invalid_edges{0}; + num_invalid_edges = + static_cast(thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); + + printf("\nthe number of invalid edges = %d\n", num_invalid_edges); + /* + void resize_dataframe_buffer(BufferType& buffer, + size_t new_buffer_size, + rmm::cuda_stream_view stream_view) + */ + + if (num_invalid_edges != 0) { // unroll and remove/mask edges + // Find (p, q, intersection of p&q's neighbor lists) + // before that, determine the interection size of each edge to be removed + rmm::device_uvector intersection_size(intersection_offsets.size() - 1, handle.get_stream()); // take the worst case where all edges are invalidated + // FIXME: might not be necessary to fill it with zeros + thrust::fill(handle.get_thrust_policy(), intersection_size.begin(), intersection_size.end(), size_t{0}); + /* + thrust::tabulate(handle.get_thrust_policy(), + intersection_size.begin(), + intersection_size.end(), + find_intersection{ + //find_intersection{ + size_dataframe_buffer(vertex_pair_buffer), // FIXME: should we just do this inside the functor? + num_invalid_edges, + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_), // FIXME: rename to vertex_pairs_buffer? maybe I should pass a pointer here 'get_dataframe_buffer_begin(vertex_pair_buffer)' + //vertex_pairs_begin, + invalid_edge_first + }); + */ + + thrust::tabulate(handle.get_thrust_policy(), + intersection_size.begin(), + intersection_size.end(), + [vertex_pair_buffer_size=size_dataframe_buffer(vertex_pair_buffer), + num_invalid_edges=num_invalid_edges, + intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + intersection_indices = raft::device_span(intersection_indices.data(), intersection_indices.size()), + vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), + invalid_edge_first = invalid_edge_first + ] + __device__(edge_t i){ + + printf("\n ***** i = %d\n", i); + printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); + printf("\nthe vertex pair buffer size = %d\n", vertex_pair_buffer_size); + if (i < num_invalid_edges) { + //auto pair = thrust::get<0>(*(invalid_edge_first + i)); // FIXME only increment if it is less than 'invalid_edge_first.size()' + vertex_t src = thrust::get<0>(thrust::get<0>(*(invalid_edge_first + i))); + vertex_t dst = thrust::get<1>(thrust::get<0>(*(invalid_edge_first + i))); + //printf("\nsrc_pair = %d, dst_pair = %d, pair = %d\n", thrust::get<0>(pair), thrust::get<1>(pair), pair); + printf("\nsrc_pair = %d, dst_pair = %d", src, dst); + + auto itr = thrust::lower_bound(thrust::seq, + vertex_pair_buffer, + vertex_pair_buffer + vertex_pair_buffer_size, + //pair, + //thrust::make_tuple(thrust::get<0>(pair), thrust::get<1>(pair)), + //thrust::make_tuple(1, 3), + thrust::make_tuple(src, dst), + thrust::less>{}); + + printf("\n itr = %d\n", itr); + auto idx = thrust::distance(vertex_pair_buffer, itr); + printf("\n distance = %d \n", idx); + + return 1; // dummy + } + else { + return 0; //dummy + } + + }); + + + + + + + + + } + + /* + thrust::partition( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + [num_triangles = num_triangles.data(), + vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), + //size=get_dataframe_buffer_end(vertex_pair_buffer) - get_dataframe_buffer_begin(vertex_pair_buffer) + size = size_dataframe_buffer(vertex_pair_buffer)] //size_dataframe_buffer + __device__(auto e){ + //printf("src = %d, dst = %d\n", std::get<0>(e), std::get<1>(e)); + auto[src, dst] = e; + auto x = thrust::make_tuple(src, dst); + printf("src_ = %d, dst_ = %d\n", thrust::get<0>(x), thrust::get<1>(x)); + //vertex_t src_ = thrust::get<0>(x); + //vertex_t dst_ = thrust::get<1>(x); + vertex_t dst_ = thrust::get<1>(e); + //vertex_t dst__ = e; + + + //auto x = thrust::make_tuple(src, dst); + //printf("src = %d, dst = %d\n", src, dst); + //printf("src_ = %d, dst_ = %d\n", thrust::get<0>(x), thrust::get<1>(x)); + //vertex_t src_ = thrust::get<0>(x); + //printf("src___ = %d\n", src_); + //vertex_t src_ = thrust::get<0>(x); + //vertex_t dst_ = thrust::get<1>(x); // error: no suitable conversion function from "thrust::detail::cons" to "int32_t" exists + + + + // sort by key. key num of triangles and the values =vertex pairs. thrust::greater + printf("the isze of the buffer = %d\n", size); + auto it = thrust::lower_bound(thrust::seq, + vertex_pair_buffer, + vertex_pair_buffer + size, + e, + thrust::less>{}); + + + auto idx = thrust::distance(vertex_pair_buffer, it); + printf("\nthe distance = %d\n", idx); + + return 0; + + } + ); + */ + + + } } From fa61f9b396c1c321c1b10f7a8fd0991d81715cd2 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 9 Jan 2024 05:14:22 -0800 Subject: [PATCH 006/155] rename to k_truss --- cpp/CMakeLists.txt | 2 +- cpp/include/cugraph/algorithms.hpp | 8 +++--- .../{ktruss_impl.cuh => k_truss_impl.cuh} | 2 +- .../community/{ktruss_sg.cu => k_truss_sg.cu} | 6 ++--- cpp/tests/CMakeLists.txt | 8 +++--- .../{ktruss_test.cpp => k_truss_test.cpp} | 26 +++++++++---------- 6 files changed, 26 insertions(+), 26 deletions(-) rename cpp/src/community/{ktruss_impl.cuh => k_truss_impl.cuh} (99%) rename cpp/src/community/{ktruss_sg.cu => k_truss_sg.cu} (89%) rename cpp/tests/community/{ktruss_test.cpp => k_truss_test.cpp} (86%) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 8e4dfbbf23c..224aad4264a 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -225,7 +225,7 @@ set(CUGRAPH_SOURCES src/community/legacy/ecg.cu src/community/egonet_sg.cu src/community/egonet_mg.cu - src/community/ktruss_sg.cu + src/community/k_truss_sg.cu src/sampling/random_walks.cu src/sampling/random_walks_sg.cu src/sampling/detail/prepare_next_frontier_sg.cu diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 3df099e60ae..961524d56d2 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -1979,9 +1979,9 @@ void triangle_count(raft::handle_t const& handle, bool do_expensive_check = false); /* - * @brief Compute ktruss. + * @brief Compute k_truss. * - * Extract the ktruss subgraph of a graph + * Extract the k_truss subgraph of a graph * * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. * @tparam edge_t Type of edge identifiers. Needs to be an integral type. @@ -1993,9 +1993,9 @@ void triangle_count(raft::handle_t const& handle, * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). */ template -void ktruss(raft::handle_t const& handle, +void k_truss(raft::handle_t const& handle, graph_view_t const& graph_view, - edge_t counts, + edge_t k, bool do_expensive_check = false); /** diff --git a/cpp/src/community/ktruss_impl.cuh b/cpp/src/community/k_truss_impl.cuh similarity index 99% rename from cpp/src/community/ktruss_impl.cuh rename to cpp/src/community/k_truss_impl.cuh index c6662c1f043..b0792ef41b0 100644 --- a/cpp/src/community/ktruss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -251,7 +251,7 @@ struct intersection_op_t { } // namespace template -void ktruss(raft::handle_t const& handle, +void k_truss(raft::handle_t const& handle, graph_view_t const& graph_view, edge_t k, bool do_expensive_check) diff --git a/cpp/src/community/ktruss_sg.cu b/cpp/src/community/k_truss_sg.cu similarity index 89% rename from cpp/src/community/ktruss_sg.cu rename to cpp/src/community/k_truss_sg.cu index 2bc6ab5fefa..38d713cf022 100644 --- a/cpp/src/community/ktruss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -14,17 +14,17 @@ * limitations under the License. */ -#include +#include namespace cugraph { -template void ktruss(raft::handle_t const& handle, +template void k_truss(raft::handle_t const& handle, graph_view_t const& graph_view, int32_t k, bool do_expensive_check); -template void ktruss(raft::handle_t const& handle, +template void k_truss(raft::handle_t const& handle, graph_view_t const& graph_view, int64_t k, bool do_expensive_check); diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index d3224df2860..c827582099a 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -402,6 +402,10 @@ ConfigureTest(CORE_NUMBER_TEST cores/core_number_test.cpp) # - Core Number tests ----------------------------------------------------------------------------- ConfigureTest(K_CORE_TEST cores/k_core_test.cpp) +############################################################################################### +# - K-truss tests -------------------------------------------------------------------------- +ConfigureTest(K_TRUSS_TEST community/k_truss_test.cpp) + ################################################################################################### # - Triangle Count tests -------------------------------------------------------------------------- ConfigureTest(TRIANGLE_COUNT_TEST community/triangle_count_test.cpp) @@ -550,10 +554,6 @@ if(BUILD_CUGRAPH_MG_TESTS) # - MG TRIANGLE COUNT tests ------------------------------------------------------------------- ConfigureTestMG(MG_TRIANGLE_COUNT_TEST community/mg_triangle_count_test.cpp) - ############################################################################################### - # - Ktruss tests -------------------------------------------------------------------------- - ConfigureTest(KTRUSS_TEST community/ktruss_test.cpp) - ############################################################################################### # - MG INDUCED SUBGRAPH tests ----------------------------------------------------------------- ConfigureTestMG(MG_INDUCED_SUBGRAPH_TEST structure/mg_induced_subgraph_test.cu) diff --git a/cpp/tests/community/ktruss_test.cpp b/cpp/tests/community/k_truss_test.cpp similarity index 86% rename from cpp/tests/community/ktruss_test.cpp rename to cpp/tests/community/k_truss_test.cpp index 51d7f949f4c..4559572e617 100644 --- a/cpp/tests/community/ktruss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -40,16 +40,16 @@ #include -struct Ktruss_Usecase { +struct KTruss_Usecase { int32_t k{10}; bool check_correctness{true}; }; template -class Tests_Ktruss - : public ::testing::TestWithParam> { +class Tests_KTruss + : public ::testing::TestWithParam> { public: - Tests_Ktruss() {} + Tests_KTruss() {} static void SetUpTestCase() {} static void TearDownTestCase() {} @@ -59,13 +59,13 @@ class Tests_Ktruss template void run_current_test( - std::tuple const& param) + std::tuple const& param) { constexpr bool renumber = false; using weight_t = float; - auto [ktruss_usecase, input_usecase] = param; + auto [k_truss_usecase, input_usecase] = param; raft::handle_t handle{}; HighResTimer hr_timer{}; @@ -91,13 +91,13 @@ class Tests_Ktruss if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement - hr_timer.start("Ktruss"); + hr_timer.start("K-truss"); } - cugraph::ktruss( + cugraph::k_truss( handle, graph_view, - ktruss_usecase.k, + k_truss_usecase.k, false); if (cugraph::test::g_perf) { @@ -109,10 +109,10 @@ class Tests_Ktruss } }; -using Tests_Ktruss_File = Tests_Ktruss; +using Tests_KTruss_File = Tests_KTruss; // FIXME: add tests for type combinations -TEST_P(Tests_Ktruss_File, CheckInt32Int32) +TEST_P(Tests_KTruss_File, CheckInt32Int32) { run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); } @@ -120,10 +120,10 @@ TEST_P(Tests_Ktruss_File, CheckInt32Int32) INSTANTIATE_TEST_SUITE_P( file_test, - Tests_Ktruss_File, + Tests_KTruss_File, ::testing::Combine( // enable correctness checks - ::testing::Values(Ktruss_Usecase{2}), + ::testing::Values(KTruss_Usecase{2}), ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/debug_jaccard/cugraph/datasets/dummy.mtx")))); CUGRAPH_TEST_PROGRAM_MAIN() From 87f45beda76d90917faff3d59298d23999bcbceb Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 9 Jan 2024 05:32:26 -0800 Subject: [PATCH 007/155] remove unused variables --- cpp/src/community/k_truss_impl.cuh | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index b0792ef41b0..558f13dc25e 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -172,7 +172,7 @@ struct extract_q_r { template -struct triangle_counts_less_t { +struct K-trusss_less_t { raft::device_span num_triangles{}; edge_t k; @@ -258,17 +258,20 @@ void k_truss(raft::handle_t const& handle, { using weight_t = float; // dummy - // 1. Check input arguments. CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); CUGRAPH_EXPECTS( graph_view.is_symmetric(), - "Invalid input arguments: triangle_count currently supports undirected graphs only."); + "Invalid input arguments: K-truss currently supports undirected graphs only."); CUGRAPH_EXPECTS( !graph_view.is_multigraph(), - "Invalid input arguments: triangle_count currently does not support multi-graphs."); + "Invalid input arguments: K-truss currently does not support multi-graphs."); + + if (do_expensive_check) { + // nothing to do + } // 2. Exclude self-loops (FIXME: better mask-out once we add masking support). @@ -276,10 +279,6 @@ void k_truss(raft::handle_t const& handle, std::optional> modified_graph{std::nullopt}; std::optional> modified_graph_view{std::nullopt}; std::optional> renumber_map{std::nullopt}; - std::optional, edge_t>> edge_ids{std::nullopt}; - // FIXME: Maybe should not be optional - std::optional> wgts{std::nullopt}; - std::optional> wgts_{std::nullopt}; if (graph_view.count_self_loops(handle) > edge_t{0}) { auto [srcs, dsts] = extract_transform_e(handle, From 74d24bef026d122aa32358db6a6eb433f46c6d3f Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 9 Jan 2024 09:43:38 -0800 Subject: [PATCH 008/155] remove unsued variables --- cpp/src/community/k_truss_impl.cuh | 61 +++++++++++------------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 558f13dc25e..39763fce100 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -59,8 +59,8 @@ struct exclude_self_loop_t { template struct in_k_plus_one_or_greater_t { - edge_t k; - __device__ bool operator()(edge_t core_number) const {return core_number >= k; } + edge_t k{}; + __device__ bool operator()(edge_t core_number) const {return core_number >= k+1; } }; template @@ -74,8 +74,6 @@ struct extract_k_plus_one_core_t { } }; - - template struct extract_low_to_high_degree_edges_t { __device__ thrust::optional> operator()(vertex_t src, @@ -171,13 +169,6 @@ struct extract_q_r { }; -template -struct K-trusss_less_t { - raft::device_span num_triangles{}; - edge_t k; - -}; - template struct find_intersection { size_t vertex_pair_buffer_size; @@ -322,7 +313,7 @@ void k_truss(raft::handle_t const& handle, ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) : std::nullopt; - rmm::device_uvector core_numbers(cur_graph_view.number_of_vertices(), + rmm::device_uvector core_numbers(cur_graph_view.local_vertex_partition_range_size(), handle.get_stream()); core_number( handle, cur_graph_view, core_numbers.data(), k_core_degree_type_t::OUT, size_t{k+1}, size_t{k+1}); @@ -444,16 +435,10 @@ void k_truss(raft::handle_t const& handle, // 5. Decompress the resulting graph to an edge list and ind intersection of edge endpoints // for each partition using detail::nbr_intersection - rmm::device_uvector cur_graph_counts(size_t{0}, handle.get_stream()); { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - cur_graph_counts.resize(cur_graph_view.local_vertex_partition_range_size(), - handle.get_stream()); - rmm::device_uvector edgelist_srcs(0, handle.get_stream()); rmm::device_uvector edgelist_dsts(0, handle.get_stream()); - std::optional> edgelist_prop {std::nullopt}; - std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( handle, @@ -478,7 +463,7 @@ void k_truss(raft::handle_t const& handle, rmm::device_uvector r_nbr_intersection_property_values1(size_t{0}, handle.get_stream()); // FIXME: Initially each edge should have an edge property of 0 - std::tie(intersection_offsets, intersection_indices) = + auto[intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), @@ -496,15 +481,15 @@ void k_truss(raft::handle_t const& handle, // stores all the pairs (p, q), (p, r) and (q, r) - auto vertex_pair_buffer_ = allocate_dataframe_buffer>( + auto vertex_pair_buffer_tmp = allocate_dataframe_buffer>( intersection_indices.size() * 3, handle.get_stream()); // FIXME: optmize this part to not have to iterate over all the edges again // tabulate with the size of intersection_indices, and call binary search on intersection_offsets // to get (p, q). thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_), - get_dataframe_buffer_begin(vertex_pair_buffer_) + intersection_indices.size(), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), extract_p_q{ raft::device_span( intersection_offsets.data(), intersection_offsets.size()), @@ -516,8 +501,8 @@ void k_truss(raft::handle_t const& handle, // tabulate with the size of intersection_indices, and call binary search on intersection_offsets // to get (p, r). thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_) + intersection_indices.size(), - get_dataframe_buffer_begin(vertex_pair_buffer_) + (2 * intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), extract_p_r{ raft::device_span( intersection_offsets.data(), intersection_offsets.size()), @@ -529,8 +514,8 @@ void k_truss(raft::handle_t const& handle, // tabulate with the size of intersection_indices, and call binary search on intersection_offsets // to get (q, r). thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_) + (2 * intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_) + (3 * intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (3 * intersection_indices.size()), extract_q_r{ raft::device_span( intersection_offsets.data(), intersection_offsets.size()), @@ -540,25 +525,25 @@ void k_truss(raft::handle_t const& handle, }); printf("\nbefore sorting\n"); - raft::print_device_vector("src", std::get<0>(vertex_pair_buffer_).data(), std::get<0>(vertex_pair_buffer_).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer_).data(), std::get<1>(vertex_pair_buffer_).size(), std::cout); + raft::print_device_vector("src", std::get<0>(vertex_pair_buffer_tmp).data(), std::get<0>(vertex_pair_buffer_tmp).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer_tmp).data(), std::get<1>(vertex_pair_buffer_tmp).size(), std::cout); thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_), - get_dataframe_buffer_end(vertex_pair_buffer_)); + get_dataframe_buffer_begin(vertex_pair_buffer_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_tmp)); - rmm::device_uvector num_triangles_(3 * intersection_indices.size(), handle.get_stream()); + rmm::device_uvector num_triangles_tmp(3 * intersection_indices.size(), handle.get_stream()); - thrust::fill(handle.get_thrust_policy(), num_triangles_.begin(), num_triangles_.end(), size_t{1}); + thrust::fill(handle.get_thrust_policy(), num_triangles_tmp.begin(), num_triangles_tmp.end(), size_t{1}); rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_), - get_dataframe_buffer_end(vertex_pair_buffer_), - num_triangles_.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_tmp), + num_triangles_tmp.begin(), get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin(), thrust::equal_to>{}); @@ -697,7 +682,7 @@ void k_truss(raft::handle_t const& handle, RAFT_CUDA_TRY(cudaDeviceSynchronize()); raft::print_device_vector( - "num_triangles_:", value_firsts[0], edge_counts[0], std::cout); + "num_triangles_tmp:", value_firsts[0], edge_counts[0], std::cout); vertex_pairs_begin = thrust::make_zip_iterator(std::get<0>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin()); // FIXME: not used below but used up printf("\nafter sorting\n"); @@ -768,7 +753,7 @@ void k_truss(raft::handle_t const& handle, thrust::tabulate(handle.get_thrust_policy(), intersection_size.begin(), intersection_size.end(), - find_intersection{ + find_intersection{ //find_intersection{ size_dataframe_buffer(vertex_pair_buffer), // FIXME: should we just do this inside the functor? num_invalid_edges, @@ -776,7 +761,7 @@ void k_truss(raft::handle_t const& handle, intersection_offsets.data(), intersection_offsets.size()), raft::device_span( intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_), // FIXME: rename to vertex_pairs_buffer? maybe I should pass a pointer here 'get_dataframe_buffer_begin(vertex_pair_buffer)' + get_dataframe_buffer_begin(vertex_pair_buffer_tmp), // FIXME: rename to vertex_pairs_buffer? maybe I should pass a pointer here 'get_dataframe_buffer_begin(vertex_pair_buffer)' //vertex_pairs_begin, invalid_edge_first }); From dfbc33a9702fd0874a96915dbc3cfde54915585f Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Tue, 16 Jan 2024 10:37:16 -0800 Subject: [PATCH 009/155] add an empty line between two functions --- cpp/include/cugraph/edge_partition_device_view.cuh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/include/cugraph/edge_partition_device_view.cuh b/cpp/include/cugraph/edge_partition_device_view.cuh index 213f9b9497a..00ad4421c79 100644 --- a/cpp/include/cugraph/edge_partition_device_view.cuh +++ b/cpp/include/cugraph/edge_partition_device_view.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, NVIDIA CORPORATION. + * Copyright (c) 2020-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. @@ -339,6 +339,7 @@ class edge_partition_device_view_t{(*dcs_nzd_vertices_).data()} : thrust::nullopt; } + __host__ __device__ thrust::optional dcs_nzd_vertex_count() const { return dcs_nzd_vertices_ From e6f67845a22107f5266aa4f8cadd24a02740a54a Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Tue, 16 Jan 2024 15:12:15 -0800 Subject: [PATCH 010/155] added major_idx_from_major_nocheck --- .../cugraph/edge_partition_device_view.cuh | 19 ++++++ cpp/src/prims/detail/nbr_intersection.cuh | 67 +++---------------- cpp/src/prims/transform_e.cuh | 41 +++--------- 3 files changed, 38 insertions(+), 89 deletions(-) diff --git a/cpp/include/cugraph/edge_partition_device_view.cuh b/cpp/include/cugraph/edge_partition_device_view.cuh index 00ad4421c79..d1c2cf3df52 100644 --- a/cpp/include/cugraph/edge_partition_device_view.cuh +++ b/cpp/include/cugraph/edge_partition_device_view.cuh @@ -298,6 +298,20 @@ class edge_partition_device_view_t major_idx_from_major_nocheck(vertex_t major) const noexcept + { + if (major_hypersparse_first_ && (major >= *major_hypersparse_first_)) { + auto major_hypersparse_idx = + detail::major_hypersparse_idx_from_major_nocheck_impl(*dcs_nzd_vertices_, major); + return major_hypersparse_idx + ? thrust::make_optional((*major_hypersparse_first_ - major_range_first_) + + *major_hypersparse_idx) + : thrust::nullopt; + } else { + return major - major_range_first_; + } + } + __device__ vertex_t major_from_major_idx_nocheck(vertex_t major_idx) const noexcept { if (major_hypersparse_first_) { @@ -461,6 +475,11 @@ class edge_partition_device_view_t major_idx_from_major_nocheck(vertex_t major) const noexcept + { + return major_offset_from_major_nocheck(major); + } + __device__ vertex_t major_from_major_idx_nocheck(vertex_t major_idx) const noexcept { return major_from_major_offset_nocheck(major_idx); diff --git a/cpp/src/prims/detail/nbr_intersection.cuh b/cpp/src/prims/detail/nbr_intersection.cuh index cefc1836fa6..703ee45eaff 100644 --- a/cpp/src/prims/detail/nbr_intersection.cuh +++ b/cpp/src/prims/detail/nbr_intersection.cuh @@ -154,24 +154,11 @@ struct update_rx_major_local_degree_t { auto major = rx_majors[rx_group_firsts[major_comm_rank * minor_comm_size + local_edge_partition_idx] + offset_in_local_edge_partition]; - vertex_t major_idx{0}; - edge_t local_degree{0}; - if (multi_gpu && (edge_partition.major_hypersparse_first() && - (major >= *(edge_partition.major_hypersparse_first())))) { - auto major_hypersparse_idx = edge_partition.major_hypersparse_idx_from_major_nocheck(major); - if (major_hypersparse_idx) { - major_idx = - (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + - *major_hypersparse_idx; - local_degree = edge_partition.local_degree(major_idx); - } - } else { - major_idx = edge_partition.major_offset_from_major_nocheck(major); - local_degree = edge_partition.local_degree(major_idx); - } + auto major_idx = edge_partition.major_idx_from_major_nocheck(major); + auto local_degree = major_idx ? edge_partition.local_degree(*major_idx) : edge_t{0}; if (edge_partition_e_mask && (local_degree > edge_t{0})) { - auto local_offset = edge_partition.local_offset(major_idx); + auto local_offset = edge_partition.local_offset(*major_idx); local_degree = static_cast( count_set_bits((*edge_partition_e_mask).value_first(), local_offset, local_degree)); } @@ -325,29 +312,11 @@ struct pick_min_degree_t { edge_t local_degree0{0}; vertex_t major0 = thrust::get<0>(pair); if constexpr (std::is_same_v) { - vertex_t major_idx{0}; - if constexpr (multi_gpu) { - if (edge_partition.major_hypersparse_first() && - (major0 >= *(edge_partition.major_hypersparse_first()))) { - auto major_hypersparse_idx = - edge_partition.major_hypersparse_idx_from_major_nocheck(major0); - if (major_hypersparse_idx) { - major_idx = - (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + - *major_hypersparse_idx; - local_degree0 = edge_partition.local_degree(major_idx); - } - } else { - major_idx = edge_partition.major_offset_from_major_nocheck(major0); - local_degree0 = edge_partition.local_degree(major_idx); - } - } else { - major_idx = edge_partition.major_offset_from_major_nocheck(major0); - local_degree0 = edge_partition.local_degree(major_idx); - } + auto major_idx = edge_partition.major_idx_from_major_nocheck(major0); + local_degree0 = major_idx ? edge_partition.local_degree(*major_idx) : edge_t{0}; if (edge_partition_e_mask && (local_degree0 > edge_t{0})) { - auto local_offset = edge_partition.local_offset(major_idx); + auto local_offset = edge_partition.local_offset(*major_idx); local_degree0 = count_set_bits((*edge_partition_e_mask).value_first(), local_offset, local_degree0); } @@ -360,29 +329,11 @@ struct pick_min_degree_t { edge_t local_degree1{0}; vertex_t major1 = thrust::get<1>(pair); if constexpr (std::is_same_v) { - vertex_t major_idx{0}; - if constexpr (multi_gpu) { - if (edge_partition.major_hypersparse_first() && - (major1 >= *(edge_partition.major_hypersparse_first()))) { - auto major_hypersparse_idx = - edge_partition.major_hypersparse_idx_from_major_nocheck(major1); - if (major_hypersparse_idx) { - major_idx = - (*(edge_partition.major_hypersparse_first()) - edge_partition.major_range_first()) + - *major_hypersparse_idx; - local_degree1 = edge_partition.local_degree(major_idx); - } - } else { - major_idx = edge_partition.major_offset_from_major_nocheck(major1); - local_degree1 = edge_partition.local_degree(major_idx); - } - } else { - major_idx = edge_partition.major_offset_from_major_nocheck(major1); - local_degree1 = edge_partition.local_degree(major_idx); - } + auto major_idx = edge_partition.major_idx_from_major_nocheck(major1); + local_degree1 = major_idx ? edge_partition.local_degree(*major_idx) : edge_t{0}; if (edge_partition_e_mask && (local_degree1 > edge_t{0})) { - auto local_offset = edge_partition.local_offset(major_idx); + auto local_offset = edge_partition.local_offset(*major_idx); local_degree1 = count_set_bits((*edge_partition_e_mask).value_first(), local_offset, local_degree1); } diff --git a/cpp/src/prims/transform_e.cuh b/cpp/src/prims/transform_e.cuh index c6623621d24..6a92e2fdb69 100644 --- a/cpp/src/prims/transform_e.cuh +++ b/cpp/src/prims/transform_e.cuh @@ -426,28 +426,15 @@ void transform_e(raft::handle_t const& handle, edge_first + edge_partition_offsets[i + 1], [edge_partition, edge_partition_e_mask] __device__(thrust::tuple edge) { - auto major = thrust::get<0>(edge); - auto minor = thrust::get<1>(edge); - vertex_t major_idx{}; - auto major_hypersparse_first = edge_partition.major_hypersparse_first(); - if (major_hypersparse_first) { - if (major < *major_hypersparse_first) { - major_idx = edge_partition.major_offset_from_major_nocheck(major); - } else { - auto major_hypersparse_idx = - edge_partition.major_hypersparse_idx_from_major_nocheck(major); - if (!major_hypersparse_idx) { return true; } - major_idx = - edge_partition.major_offset_from_major_nocheck(*major_hypersparse_first) + - *major_hypersparse_idx; - } - } else { - major_idx = edge_partition.major_offset_from_major_nocheck(major); - } + auto major = thrust::get<0>(edge); + auto minor = thrust::get<1>(edge); + auto major_idx = edge_partition.major_idx_from_major_nocheck(major); + if (!major_idx) { return true; } vertex_t const* indices{nullptr}; edge_t edge_offset{}; edge_t local_degree{}; - thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges(major_idx); + thrust::tie(indices, edge_offset, local_degree) = + edge_partition.local_edges(*major_idx); auto lower_it = thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); if (*lower_it != minor) { return true; } @@ -494,24 +481,16 @@ void transform_e(raft::handle_t const& handle, auto major = thrust::get<0>(edge); auto minor = thrust::get<1>(edge); - auto major_hypersparse_first = edge_partition.major_hypersparse_first(); - auto major_offset = edge_partition.major_offset_from_major_nocheck(major); - vertex_t major_idx{major_offset}; - - if ((major_hypersparse_first) && (major >= *major_hypersparse_first)) { - auto major_hypersparse_idx = - edge_partition.major_hypersparse_idx_from_major_nocheck(major); - assert(major_hypersparse_idx); - major_idx = edge_partition.major_offset_from_major_nocheck(*major_hypersparse_first) + - *major_hypersparse_idx; - } + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + auto major_idx = edge_partition.major_idx_from_major_nocheck(major); + assert(major_idx); auto minor_offset = edge_partition.minor_offset_from_minor_nocheck(minor); vertex_t const* indices{nullptr}; edge_t edge_offset{}; edge_t local_degree{}; - thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges(major_idx); + thrust::tie(indices, edge_offset, local_degree) = edge_partition.local_edges(*major_idx); auto lower_it = thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); auto upper_it = thrust::upper_bound(thrust::seq, lower_it, indices + local_degree, minor); From c9c3a2bb9b636fb860e7f92a945a9b86c001c658 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Tue, 16 Jan 2024 16:09:54 -0800 Subject: [PATCH 011/155] add initial implementation of has_edge() and compute_multiplicity --- cpp/include/cugraph/graph_view.hpp | 25 +- cpp/src/structure/graph_view_impl.cuh | 359 +++++++++++++++++++++++++- 2 files changed, 382 insertions(+), 2 deletions(-) diff --git a/cpp/include/cugraph/graph_view.hpp b/cpp/include/cugraph/graph_view.hpp index 53c66c6483e..93d884a56d9 100644 --- a/cpp/include/cugraph/graph_view.hpp +++ b/cpp/include/cugraph/graph_view.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, NVIDIA CORPORATION. + * Copyright (c) 2020-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. @@ -631,6 +631,19 @@ class graph_view_t has_edge(raft::handle_t const& handle, + /* (edge_srcs, edge_dsts) should be pre-shuffled */ + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check = false); + + rmm::device_uvector compute_multiplicity( + raft::handle_t const& handle, + /* (edge_srcs, edge_dsts) should be pre-shuffled */ + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check = false); + template std::enable_if_t>> local_sorted_unique_edge_srcs() const @@ -928,6 +941,16 @@ class graph_view_t has_edge(raft::handle_t const& handle, + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check = false); + + rmm::device_uvector compute_multiplicity(raft::handle_t const& handle, + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check = false); + template std::enable_if_t>> local_sorted_unique_edge_srcs() const diff --git a/cpp/src/structure/graph_view_impl.cuh b/cpp/src/structure/graph_view_impl.cuh index da0ecc991df..6a40197be19 100644 --- a/cpp/src/structure/graph_view_impl.cuh +++ b/cpp/src/structure/graph_view_impl.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, NVIDIA CORPORATION. + * Copyright (c) 2020-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. @@ -18,6 +18,8 @@ #include #include +// FIXME: better move count_invalid_vertex_pairs to somewhere else +#include #include #include @@ -751,4 +753,359 @@ edge_t graph_view_tlocal_edge_partition_segment_offsets()); } +template +rmm::device_uvector +graph_view_t>::has_edge( + raft::handle_t const& handle, + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check) +{ + CUGRAPH_EXPECTS( + edge_srcs.size() == edge_dsts.size(), + "Invalid input arguments: edge_srcs.size() does not coincide with edge_dsts.size()."); + + auto edge_first = + thrust::make_zip_iterator(store_transposed ? edge_dsts.begin() : edge_srcs.begin(), + store_transposed ? edge_srcs.begin() : edge_dsts.begin()); + + if (do_expensive_check) { + auto num_invalids = + detail::count_invalid_vertex_pairs(handle, *this, edge_first, edge_first + edge_srcs.size()); + CUGRAPH_EXPECTS(num_invalids == 0, + "Invalid input argument: there are invalid edge (src, dst) pairs."); + } + + rmm::device_uvector edge_indices(edge_srcs.size(), handle.get_stream()); + thrust::sequence(handle.get_thrust_policy(), edge_indices.begin(), edge_indices.end(), size_t{0}); + thrust::sort(handle.get_thrust_policy(), + edge_indices.begin(), + edge_indices.end(), + [edge_first] __device__(size_t lhs, size_t rhs) { + return *(edge_first + lhs) < *(edge_first + rhs); + }); + + std::vector h_major_range_lasts(this->number_of_local_edge_partitions()); + for (size_t i = 0; i < h_major_range_lasts.size(); ++i) { + if constexpr (store_transposed) { + h_major_range_lasts[i] = this->local_edge_partition_dst_range_last(i); + } else { + h_major_range_lasts[i] = this->local_edge_partition_src_range_last(i); + } + } + rmm::device_uvector d_major_range_lasts(h_major_range_lasts.size(), handle.get_stream()); + raft::update_device(d_major_range_lasts.data(), + h_major_range_lasts.data(), + h_major_range_lasts.size(), + handle.get_stream()); + rmm::device_uvector d_lower_bounds(d_major_range_lasts.size(), handle.get_stream()); + auto major_first = store_transposed ? edge_dsts.begin() : edge_srcs.begin(); + auto sorted_major_first = thrust::make_transform_iterator( + edge_indices.begin(), + cugraph::detail::indirection_t{major_first}); + thrust::lower_bound(handle.get_thrust_policy(), + sorted_major_first, + sorted_major_first + edge_indices.size(), + d_major_range_lasts.begin(), + d_major_range_lasts.end(), + d_lower_bounds.begin()); + std::vector edge_partition_offsets(d_lower_bounds.size() + 1, 0); + raft::update_host(edge_partition_offsets.data() + 1, + d_lower_bounds.data(), + d_lower_bounds.size(), + handle.get_stream()); + handle.sync_stream(); + + auto edge_mask_view = this->edge_mask_view(); + + auto sorted_edge_first = thrust::make_transform_iterator( + edge_indices.begin(), cugraph::detail::indirection_t{edge_first}); + rmm::device_uvector ret(edge_srcs.size(), handle.get_stream()); + + for (size_t i = 0; i < this->number_of_local_edge_partitions(); ++i) { + auto edge_partition = + edge_partition_device_view_t(this->local_edge_partition_view(i)); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; + thrust::transform(handle.get_thrust_policy(), + sorted_edge_first + edge_partition_offsets[i], + sorted_edge_first + edge_partition_offsets[i + 1], + thrust::make_permutation_iterator( + ret.begin(), edge_indices.begin() + edge_partition_offsets[i]), + [edge_partition, edge_partition_e_mask] __device__(auto e) { + auto major = thrust::get<0>(e); + auto minor = thrust::get<1>(e); + auto major_idx = edge_partition.major_idx_from_major_nocheck(major); + if (major_idx) { + vertex_t const* indices{nullptr}; + edge_t local_edge_offset{}; + edge_t local_degree{}; + thrust::tie(indices, local_edge_offset, local_degree) = + edge_partition.local_edges(*major_idx); + auto it = thrust::lower_bound( + thrust::seq, indices, indices + local_degree, minor); + if (*it == minor) { + if (edge_partition_e_mask) { + return (*edge_partition_e_mask) + .get(local_edge_offset + thrust::distance(indices, it)); + } else { + return true; + } + } else { + return false; + } + } else { + return false; + } + }); + } + + return ret; +} + +template +rmm::device_uvector +graph_view_t>::has_edge( + raft::handle_t const& handle, + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check) +{ + CUGRAPH_EXPECTS( + edge_srcs.size() == edge_dsts.size(), + "Invalid input arguments: edge_srcs.size() does not coincide with edge_dsts.size()."); + + auto edge_first = + thrust::make_zip_iterator(store_transposed ? edge_dsts.begin() : edge_srcs.begin(), + store_transposed ? edge_srcs.begin() : edge_dsts.begin()); + + if (do_expensive_check) { + auto num_invalids = + detail::count_invalid_vertex_pairs(handle, *this, edge_first, edge_first + edge_srcs.size()); + CUGRAPH_EXPECTS(num_invalids == 0, + "Invalid input argument: there are invalid edge (src, dst) pairs."); + } + + auto edge_mask_view = this->edge_mask_view(); + + rmm::device_uvector ret(edge_srcs.size(), handle.get_stream()); + + auto edge_partition = + edge_partition_device_view_t(this->local_edge_partition_view()); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, 0) + : thrust::nullopt; + thrust::transform( + handle.get_thrust_policy(), + edge_first, + edge_first + edge_srcs.size(), + ret.begin(), + [edge_partition, edge_partition_e_mask] __device__(auto e) { + auto major = thrust::get<0>(e); + auto minor = thrust::get<1>(e); + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + vertex_t const* indices{nullptr}; + edge_t local_edge_offset{}; + edge_t local_degree{}; + thrust::tie(indices, local_edge_offset, local_degree) = + edge_partition.local_edges(major_offset); + auto it = thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); + if (*it == minor) { + if (edge_partition_e_mask) { + return (*edge_partition_e_mask).get(local_edge_offset + thrust::distance(indices, it)); + } else { + return true; + } + } else { + return false; + } + }); + + return ret; +} + +template +rmm::device_uvector +graph_view_t>:: + compute_multiplicity(raft::handle_t const& handle, + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check) +{ + CUGRAPH_EXPECTS( + edge_srcs.size() == edge_dsts.size(), + "Invalid input arguments: edge_srcs.size() does not coincide with edge_dsts.size()."); + + auto edge_first = + thrust::make_zip_iterator(store_transposed ? edge_dsts.begin() : edge_srcs.begin(), + store_transposed ? edge_srcs.begin() : edge_dsts.begin()); + + if (do_expensive_check) { + auto num_invalids = + detail::count_invalid_vertex_pairs(handle, *this, edge_first, edge_first + edge_srcs.size()); + CUGRAPH_EXPECTS(num_invalids == 0, + "Invalid input argument: there are invalid edge (src, dst) pairs."); + } + + rmm::device_uvector edge_indices(edge_srcs.size(), handle.get_stream()); + thrust::sequence(handle.get_thrust_policy(), edge_indices.begin(), edge_indices.end(), size_t{0}); + thrust::sort(handle.get_thrust_policy(), + edge_indices.begin(), + edge_indices.end(), + [edge_first] __device__(size_t lhs, size_t rhs) { + return *(edge_first + lhs) < *(edge_first + rhs); + }); + + std::vector h_major_range_lasts(this->number_of_local_edge_partitions()); + for (size_t i = 0; i < h_major_range_lasts.size(); ++i) { + if constexpr (store_transposed) { + h_major_range_lasts[i] = this->local_edge_partition_dst_range_last(i); + } else { + h_major_range_lasts[i] = this->local_edge_partition_src_range_last(i); + } + } + rmm::device_uvector d_major_range_lasts(h_major_range_lasts.size(), handle.get_stream()); + raft::update_device(d_major_range_lasts.data(), + h_major_range_lasts.data(), + h_major_range_lasts.size(), + handle.get_stream()); + rmm::device_uvector d_lower_bounds(d_major_range_lasts.size(), handle.get_stream()); + auto major_first = store_transposed ? edge_dsts.begin() : edge_srcs.begin(); + auto sorted_major_first = thrust::make_transform_iterator( + edge_indices.begin(), + cugraph::detail::indirection_t{major_first}); + thrust::lower_bound(handle.get_thrust_policy(), + sorted_major_first, + sorted_major_first + edge_indices.size(), + d_major_range_lasts.begin(), + d_major_range_lasts.end(), + d_lower_bounds.begin()); + std::vector edge_partition_offsets(d_lower_bounds.size() + 1, 0); + raft::update_host(edge_partition_offsets.data() + 1, + d_lower_bounds.data(), + d_lower_bounds.size(), + handle.get_stream()); + handle.sync_stream(); + + auto edge_mask_view = this->edge_mask_view(); + + auto sorted_edge_first = thrust::make_transform_iterator( + edge_indices.begin(), cugraph::detail::indirection_t{edge_first}); + rmm::device_uvector ret(edge_srcs.size(), handle.get_stream()); + + for (size_t i = 0; i < this->number_of_local_edge_partitions(); ++i) { + auto edge_partition = + edge_partition_device_view_t(this->local_edge_partition_view(i)); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, i) + : thrust::nullopt; + thrust::transform( + handle.get_thrust_policy(), + sorted_edge_first + edge_partition_offsets[i], + sorted_edge_first + edge_partition_offsets[i + 1], + thrust::make_permutation_iterator(ret.begin(), + edge_indices.begin() + edge_partition_offsets[i]), + [edge_partition, edge_partition_e_mask] __device__(auto e) { + auto major = thrust::get<0>(e); + auto minor = thrust::get<1>(e); + auto major_idx = edge_partition.major_idx_from_major_nocheck(major); + if (major_idx) { + vertex_t const* indices{nullptr}; + edge_t local_edge_offset{}; + edge_t local_degree{}; + thrust::tie(indices, local_edge_offset, local_degree) = + edge_partition.local_edges(*major_idx); + auto lower_it = thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); + auto upper_it = thrust::upper_bound(thrust::seq, indices, indices + local_degree, minor); + auto multiplicity = static_cast(thrust::distance(lower_it, upper_it)); + if (edge_partition_e_mask && (multiplicity > 0)) { + multiplicity = static_cast(detail::count_set_bits( + (*edge_partition_e_mask).value_first(), + static_cast(local_edge_offset + thrust::distance(indices, lower_it)), + static_cast(multiplicity))); + } + return multiplicity; + } else { + return edge_t{0}; + } + }); + } + + return ret; +} + +template +rmm::device_uvector +graph_view_t>:: + compute_multiplicity(raft::handle_t const& handle, + raft::device_span edge_srcs, + raft::device_span edge_dsts, + bool do_expensive_check) +{ + CUGRAPH_EXPECTS( + edge_srcs.size() == edge_dsts.size(), + "Invalid input arguments: edge_srcs.size() does not coincide with edge_dsts.size()."); + + auto edge_first = + thrust::make_zip_iterator(store_transposed ? edge_dsts.begin() : edge_srcs.begin(), + store_transposed ? edge_srcs.begin() : edge_dsts.begin()); + + if (do_expensive_check) { + auto num_invalids = + detail::count_invalid_vertex_pairs(handle, *this, edge_first, edge_first + edge_srcs.size()); + CUGRAPH_EXPECTS(num_invalids == 0, + "Invalid input argument: there are invalid edge (src, dst) pairs."); + } + + auto edge_mask_view = this->edge_mask_view(); + + rmm::device_uvector ret(edge_srcs.size(), handle.get_stream()); + + auto edge_partition = + edge_partition_device_view_t(this->local_edge_partition_view()); + auto edge_partition_e_mask = + edge_mask_view + ? thrust::make_optional< + detail::edge_partition_edge_property_device_view_t>( + *edge_mask_view, 0) + : thrust::nullopt; + thrust::transform( + handle.get_thrust_policy(), + edge_first, + edge_first + edge_srcs.size(), + ret.begin(), + [edge_partition, edge_partition_e_mask] __device__(auto e) { + auto major = thrust::get<0>(e); + auto minor = thrust::get<1>(e); + auto major_offset = edge_partition.major_offset_from_major_nocheck(major); + vertex_t const* indices{nullptr}; + edge_t local_edge_offset{}; + edge_t local_degree{}; + thrust::tie(indices, local_edge_offset, local_degree) = + edge_partition.local_edges(major_offset); + auto lower_it = thrust::lower_bound(thrust::seq, indices, indices + local_degree, minor); + auto upper_it = thrust::upper_bound(thrust::seq, indices, indices + local_degree, minor); + auto multiplicity = static_cast(thrust::distance(lower_it, upper_it)); + if (edge_partition_e_mask && (multiplicity > 0)) { + multiplicity = static_cast(detail::count_set_bits( + (*edge_partition_e_mask).value_first(), + static_cast(local_edge_offset + thrust::distance(indices, lower_it)), + static_cast(multiplicity))); + } + return multiplicity; + }); + + return ret; +} + } // namespace cugraph From 06d4f775589f3dd4b457cc829cd5f836ba535d1a Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Tue, 16 Jan 2024 18:03:00 -0800 Subject: [PATCH 012/155] move count_invalid_vertex_pairs to error_check_utils.cuh --- cpp/src/link_prediction/similarity_impl.cuh | 3 +- cpp/src/prims/detail/nbr_intersection.cuh | 103 +------------ ..._v_pair_transform_dst_nbr_intersection.cuh | 1 + cpp/src/utilities/error_check_utils.cuh | 136 ++++++++++++++++++ 4 files changed, 141 insertions(+), 102 deletions(-) create mode 100644 cpp/src/utilities/error_check_utils.cuh diff --git a/cpp/src/link_prediction/similarity_impl.cuh b/cpp/src/link_prediction/similarity_impl.cuh index 55e8f5c88d7..7ac294d7719 100644 --- a/cpp/src/link_prediction/similarity_impl.cuh +++ b/cpp/src/link_prediction/similarity_impl.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, NVIDIA CORPORATION. + * 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. @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/cpp/src/prims/detail/nbr_intersection.cuh b/cpp/src/prims/detail/nbr_intersection.cuh index 703ee45eaff..8261ec747f9 100644 --- a/cpp/src/prims/detail/nbr_intersection.cuh +++ b/cpp/src/prims/detail/nbr_intersection.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, NVIDIA CORPORATION. + * 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. @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -63,35 +64,6 @@ namespace cugraph { namespace detail { -// check vertices in the pair are valid and first element of the pair is within the local vertex -// partition range -template -struct is_invalid_input_vertex_pair_t { - vertex_t num_vertices{}; - raft::device_span edge_partition_major_range_firsts{}; - raft::device_span edge_partition_major_range_lasts{}; - vertex_t edge_partition_minor_range_first{}; - vertex_t edge_partition_minor_range_last{}; - - __device__ bool operator()(thrust::tuple pair) const - { - auto major = thrust::get<0>(pair); - auto minor = thrust::get<1>(pair); - if (!is_valid_vertex(num_vertices, major) || !is_valid_vertex(num_vertices, minor)) { - return true; - } - auto it = thrust::upper_bound(thrust::seq, - edge_partition_major_range_lasts.begin(), - edge_partition_major_range_lasts.end(), - major); - if (it == edge_partition_major_range_lasts.end()) { return true; } - auto edge_partition_idx = - static_cast(thrust::distance(edge_partition_major_range_lasts.begin(), it)); - if (major < edge_partition_major_range_firsts[edge_partition_idx]) { return true; } - return (minor < edge_partition_minor_range_first) || (minor >= edge_partition_minor_range_last); - } -}; - // group index determined by major_comm_rank (primary key) and local edge partition index (secondary // key) template @@ -650,77 +622,6 @@ struct gatherv_indices_t { } }; -template -size_t count_invalid_vertex_pairs(raft::handle_t const& handle, - GraphViewType const& graph_view, - VertexPairIterator vertex_pair_first, - VertexPairIterator vertex_pair_last) -{ - using vertex_t = typename GraphViewType::vertex_type; - - std::vector h_edge_partition_major_range_firsts( - graph_view.number_of_local_edge_partitions()); - std::vector h_edge_partition_major_range_lasts( - h_edge_partition_major_range_firsts.size()); - vertex_t edge_partition_minor_range_first{}; - vertex_t edge_partition_minor_range_last{}; - if constexpr (GraphViewType::is_multi_gpu) { - for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); i++) { - if constexpr (GraphViewType::is_storage_transposed) { - h_edge_partition_major_range_firsts[i] = graph_view.local_edge_partition_dst_range_first(i); - h_edge_partition_major_range_lasts[i] = graph_view.local_edge_partition_dst_range_last(i); - } else { - h_edge_partition_major_range_firsts[i] = graph_view.local_edge_partition_src_range_first(i); - h_edge_partition_major_range_lasts[i] = graph_view.local_edge_partition_src_range_last(i); - } - } - if constexpr (GraphViewType::is_storage_transposed) { - edge_partition_minor_range_first = graph_view.local_edge_partition_src_range_first(); - edge_partition_minor_range_last = graph_view.local_edge_partition_src_range_last(); - } else { - edge_partition_minor_range_first = graph_view.local_edge_partition_dst_range_first(); - edge_partition_minor_range_last = graph_view.local_edge_partition_dst_range_last(); - } - } else { - h_edge_partition_major_range_firsts[0] = vertex_t{0}; - h_edge_partition_major_range_lasts[0] = graph_view.number_of_vertices(); - edge_partition_minor_range_first = vertex_t{0}; - edge_partition_minor_range_last = graph_view.number_of_vertices(); - } - rmm::device_uvector d_edge_partition_major_range_firsts( - h_edge_partition_major_range_firsts.size(), handle.get_stream()); - rmm::device_uvector d_edge_partition_major_range_lasts( - h_edge_partition_major_range_lasts.size(), handle.get_stream()); - raft::update_device(d_edge_partition_major_range_firsts.data(), - h_edge_partition_major_range_firsts.data(), - h_edge_partition_major_range_firsts.size(), - handle.get_stream()); - raft::update_device(d_edge_partition_major_range_lasts.data(), - h_edge_partition_major_range_lasts.data(), - h_edge_partition_major_range_lasts.size(), - handle.get_stream()); - - auto num_invalid_pairs = thrust::count_if( - handle.get_thrust_policy(), - vertex_pair_first, - vertex_pair_last, - is_invalid_input_vertex_pair_t{ - graph_view.number_of_vertices(), - raft::device_span(d_edge_partition_major_range_firsts.begin(), - d_edge_partition_major_range_firsts.end()), - raft::device_span(d_edge_partition_major_range_lasts.begin(), - d_edge_partition_major_range_lasts.end()), - edge_partition_minor_range_first, - edge_partition_minor_range_last}); - if constexpr (GraphViewType::is_multi_gpu) { - auto& comm = handle.get_comms(); - num_invalid_pairs = - host_scalar_allreduce(comm, num_invalid_pairs, raft::comms::op_t::SUM, handle.get_stream()); - } - - return num_invalid_pairs; -} - // In multi-GPU, the first element of every vertex pair in [vertex_pair_first, vertex_pair) should // be within the valid edge partition major range assigned to this process and the second element // should be within the valid edge partition minor range assigned to this process. diff --git a/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh b/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh index 201c08325d7..aa2f97eebec 100644 --- a/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh +++ b/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/cpp/src/utilities/error_check_utils.cuh b/cpp/src/utilities/error_check_utils.cuh new file mode 100644 index 00000000000..d25e18af600 --- /dev/null +++ b/cpp/src/utilities/error_check_utils.cuh @@ -0,0 +1,136 @@ +/* + * Copyright (c) 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. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace cugraph { +namespace detail { + +// check vertices in the pair are in [0, num_vertices) and belongs to one of the local edge partitions. +template +struct is_invalid_input_vertex_pair_t { + vertex_t num_vertices{}; + raft::device_span edge_partition_major_range_firsts{}; + raft::device_span edge_partition_major_range_lasts{}; + vertex_t edge_partition_minor_range_first{}; + vertex_t edge_partition_minor_range_last{}; + + __device__ bool operator()(thrust::tuple pair) const + { + auto major = thrust::get<0>(pair); + auto minor = thrust::get<1>(pair); + if (!is_valid_vertex(num_vertices, major) || !is_valid_vertex(num_vertices, minor)) { + return true; + } + auto it = thrust::upper_bound(thrust::seq, + edge_partition_major_range_lasts.begin(), + edge_partition_major_range_lasts.end(), + major); + if (it == edge_partition_major_range_lasts.end()) { return true; } + auto edge_partition_idx = + static_cast(thrust::distance(edge_partition_major_range_lasts.begin(), it)); + if (major < edge_partition_major_range_firsts[edge_partition_idx]) { return true; } + return (minor < edge_partition_minor_range_first) || (minor >= edge_partition_minor_range_last); + } +}; + +template +size_t count_invalid_vertex_pairs(raft::handle_t const& handle, + GraphViewType const& graph_view, + VertexPairIterator vertex_pair_first, + VertexPairIterator vertex_pair_last) +{ + using vertex_t = typename GraphViewType::vertex_type; + + std::vector h_edge_partition_major_range_firsts( + graph_view.number_of_local_edge_partitions()); + std::vector h_edge_partition_major_range_lasts( + h_edge_partition_major_range_firsts.size()); + vertex_t edge_partition_minor_range_first{}; + vertex_t edge_partition_minor_range_last{}; + if constexpr (GraphViewType::is_multi_gpu) { + for (size_t i = 0; i < graph_view.number_of_local_edge_partitions(); i++) { + if constexpr (GraphViewType::is_storage_transposed) { + h_edge_partition_major_range_firsts[i] = graph_view.local_edge_partition_dst_range_first(i); + h_edge_partition_major_range_lasts[i] = graph_view.local_edge_partition_dst_range_last(i); + } else { + h_edge_partition_major_range_firsts[i] = graph_view.local_edge_partition_src_range_first(i); + h_edge_partition_major_range_lasts[i] = graph_view.local_edge_partition_src_range_last(i); + } + } + if constexpr (GraphViewType::is_storage_transposed) { + edge_partition_minor_range_first = graph_view.local_edge_partition_src_range_first(); + edge_partition_minor_range_last = graph_view.local_edge_partition_src_range_last(); + } else { + edge_partition_minor_range_first = graph_view.local_edge_partition_dst_range_first(); + edge_partition_minor_range_last = graph_view.local_edge_partition_dst_range_last(); + } + } else { + h_edge_partition_major_range_firsts[0] = vertex_t{0}; + h_edge_partition_major_range_lasts[0] = graph_view.number_of_vertices(); + edge_partition_minor_range_first = vertex_t{0}; + edge_partition_minor_range_last = graph_view.number_of_vertices(); + } + rmm::device_uvector d_edge_partition_major_range_firsts( + h_edge_partition_major_range_firsts.size(), handle.get_stream()); + rmm::device_uvector d_edge_partition_major_range_lasts( + h_edge_partition_major_range_lasts.size(), handle.get_stream()); + raft::update_device(d_edge_partition_major_range_firsts.data(), + h_edge_partition_major_range_firsts.data(), + h_edge_partition_major_range_firsts.size(), + handle.get_stream()); + raft::update_device(d_edge_partition_major_range_lasts.data(), + h_edge_partition_major_range_lasts.data(), + h_edge_partition_major_range_lasts.size(), + handle.get_stream()); + + auto num_invalid_pairs = thrust::count_if( + handle.get_thrust_policy(), + vertex_pair_first, + vertex_pair_last, + is_invalid_input_vertex_pair_t{ + graph_view.number_of_vertices(), + raft::device_span(d_edge_partition_major_range_firsts.begin(), + d_edge_partition_major_range_firsts.end()), + raft::device_span(d_edge_partition_major_range_lasts.begin(), + d_edge_partition_major_range_lasts.end()), + edge_partition_minor_range_first, + edge_partition_minor_range_last}); + if constexpr (GraphViewType::is_multi_gpu) { + auto& comm = handle.get_comms(); + num_invalid_pairs = + host_scalar_allreduce(comm, num_invalid_pairs, raft::comms::op_t::SUM, handle.get_stream()); + } + + return num_invalid_pairs; +} + +} // namespace detail +} // namespace cugraph From 4b4fb464d154c340ddab737d9d205e6a454b9bc1 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Tue, 16 Jan 2024 18:03:23 -0800 Subject: [PATCH 013/155] refactor has_edge() and compute_multiplicity() --- cpp/src/structure/graph_view_impl.cuh | 146 ++++++++++++-------------- 1 file changed, 66 insertions(+), 80 deletions(-) diff --git a/cpp/src/structure/graph_view_impl.cuh b/cpp/src/structure/graph_view_impl.cuh index 6a40197be19..18b1088aebc 100644 --- a/cpp/src/structure/graph_view_impl.cuh +++ b/cpp/src/structure/graph_view_impl.cuh @@ -18,10 +18,9 @@ #include #include -// FIXME: better move count_invalid_vertex_pairs to somewhere else -#include #include #include +#include #include #include @@ -416,6 +415,59 @@ edge_t count_edge_partition_multi_edges( } } +template +std::tuple, std::vector> +compute_edge_indices_and_edge_partition_offsets( + raft::handle_t const& handle, + graph_view_t const& graph_view, + raft::device_span edge_majors, + raft::device_span edge_minors) +{ + auto edge_first = thrust::make_zip_iterator(edge_majors.begin(), edge_minors.begin()); + + rmm::device_uvector edge_indices(edge_majors.size(), handle.get_stream()); + thrust::sequence(handle.get_thrust_policy(), edge_indices.begin(), edge_indices.end(), size_t{0}); + thrust::sort(handle.get_thrust_policy(), + edge_indices.begin(), + edge_indices.end(), + [edge_first] __device__(size_t lhs, size_t rhs) { + return *(edge_first + lhs) < *(edge_first + rhs); + }); + + std::vector h_major_range_lasts(graph_view.number_of_local_edge_partitions()); + for (size_t i = 0; i < h_major_range_lasts.size(); ++i) { + if constexpr (store_transposed) { + h_major_range_lasts[i] = graph_view.local_edge_partition_dst_range_last(i); + } else { + h_major_range_lasts[i] = graph_view.local_edge_partition_src_range_last(i); + } + } + rmm::device_uvector d_major_range_lasts(h_major_range_lasts.size(), handle.get_stream()); + raft::update_device(d_major_range_lasts.data(), + h_major_range_lasts.data(), + h_major_range_lasts.size(), + handle.get_stream()); + rmm::device_uvector d_lower_bounds(d_major_range_lasts.size(), handle.get_stream()); + auto major_first = edge_majors.begin(); + auto sorted_major_first = thrust::make_transform_iterator( + edge_indices.begin(), + cugraph::detail::indirection_t{major_first}); + thrust::lower_bound(handle.get_thrust_policy(), + sorted_major_first, + sorted_major_first + edge_indices.size(), + d_major_range_lasts.begin(), + d_major_range_lasts.end(), + d_lower_bounds.begin()); + std::vector edge_partition_offsets(d_lower_bounds.size() + 1, 0); + raft::update_host(edge_partition_offsets.data() + 1, + d_lower_bounds.data(), + d_lower_bounds.size(), + handle.get_stream()); + handle.sync_stream(); + + return std::make_tuple(std::move(edge_indices), edge_partition_offsets); +} + } // namespace template @@ -776,45 +828,11 @@ graph_view_t edge_indices(edge_srcs.size(), handle.get_stream()); - thrust::sequence(handle.get_thrust_policy(), edge_indices.begin(), edge_indices.end(), size_t{0}); - thrust::sort(handle.get_thrust_policy(), - edge_indices.begin(), - edge_indices.end(), - [edge_first] __device__(size_t lhs, size_t rhs) { - return *(edge_first + lhs) < *(edge_first + rhs); - }); - - std::vector h_major_range_lasts(this->number_of_local_edge_partitions()); - for (size_t i = 0; i < h_major_range_lasts.size(); ++i) { - if constexpr (store_transposed) { - h_major_range_lasts[i] = this->local_edge_partition_dst_range_last(i); - } else { - h_major_range_lasts[i] = this->local_edge_partition_src_range_last(i); - } - } - rmm::device_uvector d_major_range_lasts(h_major_range_lasts.size(), handle.get_stream()); - raft::update_device(d_major_range_lasts.data(), - h_major_range_lasts.data(), - h_major_range_lasts.size(), - handle.get_stream()); - rmm::device_uvector d_lower_bounds(d_major_range_lasts.size(), handle.get_stream()); - auto major_first = store_transposed ? edge_dsts.begin() : edge_srcs.begin(); - auto sorted_major_first = thrust::make_transform_iterator( - edge_indices.begin(), - cugraph::detail::indirection_t{major_first}); - thrust::lower_bound(handle.get_thrust_policy(), - sorted_major_first, - sorted_major_first + edge_indices.size(), - d_major_range_lasts.begin(), - d_major_range_lasts.end(), - d_lower_bounds.begin()); - std::vector edge_partition_offsets(d_lower_bounds.size() + 1, 0); - raft::update_host(edge_partition_offsets.data() + 1, - d_lower_bounds.data(), - d_lower_bounds.size(), - handle.get_stream()); - handle.sync_stream(); + auto [edge_indices, edge_partition_offsets] = + compute_edge_indices_and_edge_partition_offsets(handle, + *this, + store_transposed ? edge_dsts : edge_srcs, + store_transposed ? edge_srcs : edge_dsts); auto edge_mask_view = this->edge_mask_view(); @@ -939,6 +957,7 @@ graph_view_t edge_dsts, bool do_expensive_check) { + CUGRAPH_EXPECTS(this->is_multigraph(), "Use has_edge() instead for non-multigraphs."); CUGRAPH_EXPECTS( edge_srcs.size() == edge_dsts.size(), "Invalid input arguments: edge_srcs.size() does not coincide with edge_dsts.size()."); @@ -954,45 +973,11 @@ graph_view_t edge_indices(edge_srcs.size(), handle.get_stream()); - thrust::sequence(handle.get_thrust_policy(), edge_indices.begin(), edge_indices.end(), size_t{0}); - thrust::sort(handle.get_thrust_policy(), - edge_indices.begin(), - edge_indices.end(), - [edge_first] __device__(size_t lhs, size_t rhs) { - return *(edge_first + lhs) < *(edge_first + rhs); - }); - - std::vector h_major_range_lasts(this->number_of_local_edge_partitions()); - for (size_t i = 0; i < h_major_range_lasts.size(); ++i) { - if constexpr (store_transposed) { - h_major_range_lasts[i] = this->local_edge_partition_dst_range_last(i); - } else { - h_major_range_lasts[i] = this->local_edge_partition_src_range_last(i); - } - } - rmm::device_uvector d_major_range_lasts(h_major_range_lasts.size(), handle.get_stream()); - raft::update_device(d_major_range_lasts.data(), - h_major_range_lasts.data(), - h_major_range_lasts.size(), - handle.get_stream()); - rmm::device_uvector d_lower_bounds(d_major_range_lasts.size(), handle.get_stream()); - auto major_first = store_transposed ? edge_dsts.begin() : edge_srcs.begin(); - auto sorted_major_first = thrust::make_transform_iterator( - edge_indices.begin(), - cugraph::detail::indirection_t{major_first}); - thrust::lower_bound(handle.get_thrust_policy(), - sorted_major_first, - sorted_major_first + edge_indices.size(), - d_major_range_lasts.begin(), - d_major_range_lasts.end(), - d_lower_bounds.begin()); - std::vector edge_partition_offsets(d_lower_bounds.size() + 1, 0); - raft::update_host(edge_partition_offsets.data() + 1, - d_lower_bounds.data(), - d_lower_bounds.size(), - handle.get_stream()); - handle.sync_stream(); + auto [edge_indices, edge_partition_offsets] = + compute_edge_indices_and_edge_partition_offsets(handle, + *this, + store_transposed ? edge_dsts : edge_srcs, + store_transposed ? edge_srcs : edge_dsts); auto edge_mask_view = this->edge_mask_view(); @@ -1052,6 +1037,7 @@ graph_view_t edge_dsts, bool do_expensive_check) { + CUGRAPH_EXPECTS(this->is_multigraph(), "Use has_edge() instead for non-multigraphs."); CUGRAPH_EXPECTS( edge_srcs.size() == edge_dsts.size(), "Invalid input arguments: edge_srcs.size() does not coincide with edge_dsts.size()."); From a7d0fff7f43561748d8d76995de2cf8283231286 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Tue, 16 Jan 2024 18:16:05 -0800 Subject: [PATCH 014/155] clang-format and copyright year --- .../prims/per_v_pair_transform_dst_nbr_intersection.cuh | 2 +- cpp/src/prims/transform_e.cuh | 2 +- cpp/src/utilities/error_check_utils.cuh | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh b/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh index aa2f97eebec..469bfcb4e47 100644 --- a/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh +++ b/cpp/src/prims/per_v_pair_transform_dst_nbr_intersection.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023, NVIDIA CORPORATION. + * 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. diff --git a/cpp/src/prims/transform_e.cuh b/cpp/src/prims/transform_e.cuh index 6a92e2fdb69..93a2d040b60 100644 --- a/cpp/src/prims/transform_e.cuh +++ b/cpp/src/prims/transform_e.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, NVIDIA CORPORATION. + * Copyright (c) 2023-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. diff --git a/cpp/src/utilities/error_check_utils.cuh b/cpp/src/utilities/error_check_utils.cuh index d25e18af600..baaf513d93d 100644 --- a/cpp/src/utilities/error_check_utils.cuh +++ b/cpp/src/utilities/error_check_utils.cuh @@ -20,20 +20,21 @@ #include #include -#include #include +#include #include #include -#include #include +#include #include namespace cugraph { namespace detail { -// check vertices in the pair are in [0, num_vertices) and belongs to one of the local edge partitions. +// check vertices in the pair are in [0, num_vertices) and belongs to one of the local edge +// partitions. template struct is_invalid_input_vertex_pair_t { vertex_t num_vertices{}; From becf1338848299f9b2e1f3baa8fbafd5fc16903f Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Wed, 17 Jan 2024 10:28:41 -0800 Subject: [PATCH 015/155] to_host, to_device specialization for std::vector --- cpp/tests/utilities/test_utilities.hpp | 46 ++++++++++++++------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/cpp/tests/utilities/test_utilities.hpp b/cpp/tests/utilities/test_utilities.hpp index 321a0536e02..5480309413b 100644 --- a/cpp/tests/utilities/test_utilities.hpp +++ b/cpp/tests/utilities/test_utilities.hpp @@ -377,18 +377,24 @@ template std::vector to_host(raft::handle_t const& handle, raft::device_span data) { std::vector h_data(data.size()); - raft::update_host(h_data.data(), data.data(), data.size(), handle.get_stream()); - handle.sync_stream(); + if constexpr (std::is_same_v) { // std::vector stores values in a packed format + auto h_tmp = new bool[data.size()]; + raft::update_host(h_tmp, data.data(), data.size(), handle.get_stream()); + handle.sync_stream(); + std::transform( + h_tmp, h_tmp + data.size(), h_data.begin(), [](uint8_t v) { return static_cast(v); }); + delete[] h_tmp; + } else { + raft::update_host(h_data.data(), data.data(), data.size(), handle.get_stream()); + handle.sync_stream(); + } return h_data; } template std::vector to_host(raft::handle_t const& handle, rmm::device_uvector const& data) { - std::vector h_data(data.size()); - raft::update_host(h_data.data(), data.data(), data.size(), handle.get_stream()); - handle.sync_stream(); - return h_data; + return to_host(handle, raft::device_span(data.data(), data.size())); } template @@ -396,11 +402,7 @@ std::optional> to_host(raft::handle_t const& handle, std::optional> data) { std::optional> h_data{std::nullopt}; - if (data) { - h_data = std::vector((*data).size()); - raft::update_host((*h_data).data(), (*data).data(), (*data).size(), handle.get_stream()); - handle.sync_stream(); - } + if (data) { h_data = to_host(handle, *data); } return h_data; } @@ -410,9 +412,7 @@ std::optional> to_host(raft::handle_t const& handle, { std::optional> h_data{std::nullopt}; if (data) { - h_data = std::vector((*data).size()); - raft::update_host((*h_data).data(), (*data).data(), (*data).size(), handle.get_stream()); - handle.sync_stream(); + h_data = to_hoast(handle, raft::device_span((*data).data(), (*data).size())); } return h_data; } @@ -430,8 +430,16 @@ template rmm::device_uvector to_device(raft::handle_t const& handle, std::vector const& data) { rmm::device_uvector d_data(data.size(), handle.get_stream()); - raft::update_device(d_data.data(), data.data(), data.size(), handle.get_stream()); - handle.sync_stream(); + if constexpr (std::is_same_v) { // std::vector stores values in a packed format + auto h_tmp = new bool[data.size()]; + std::copy(data.begin(), data.end(), h_tmp); + raft::update_device(d_data.data(), h_tmp, h_tmp + data.size(), handle.get_stream()); + handle.sync_stream(); + delete[] h_tmp; + } else { + raft::update_device(d_data.data(), data.data(), data.size(), handle.get_stream()); + handle.sync_stream(); + } return d_data; } @@ -453,11 +461,7 @@ std::optional> to_device(raft::handle_t const& handle, std::optional> const& data) { std::optional> d_data{std::nullopt}; - if (data) { - d_data = rmm::device_uvector(data->size(), handle.get_stream()); - raft::update_host(d_data->data(), data->data(), data->size(), handle.get_stream()); - handle.sync_stream(); - } + if (data) { d_data = to_device(handle, *data); } return d_data; } From 5d3ed2a7381bc8f7879518521255db54b8670333 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Wed, 17 Jan 2024 10:30:04 -0800 Subject: [PATCH 016/155] remove repetitive tests --- .../count_self_loops_and_multi_edges_test.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp b/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp index 68828d5eee1..c3fc13e9a71 100644 --- a/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp +++ b/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp @@ -208,10 +208,7 @@ INSTANTIATE_TEST_SUITE_P( Tests_CountSelfLoopsAndMultiEdges_File, ::testing::Combine( // enable correctness checks - ::testing::Values(CountSelfLoopsAndMultiEdges_Usecase{}, - CountSelfLoopsAndMultiEdges_Usecase{}, - CountSelfLoopsAndMultiEdges_Usecase{}, - CountSelfLoopsAndMultiEdges_Usecase{}), + ::testing::Values(CountSelfLoopsAndMultiEdges_Usecase{}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), cugraph::test::File_Usecase("test/datasets/webbase-1M.mtx")))); @@ -220,10 +217,7 @@ INSTANTIATE_TEST_SUITE_P( Tests_CountSelfLoopsAndMultiEdges_Rmat, ::testing::Combine( // enable correctness checks - ::testing::Values(CountSelfLoopsAndMultiEdges_Usecase{}, - CountSelfLoopsAndMultiEdges_Usecase{}, - CountSelfLoopsAndMultiEdges_Usecase{}, - CountSelfLoopsAndMultiEdges_Usecase{}), + ::testing::Values(CountSelfLoopsAndMultiEdges_Usecase{}), ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); INSTANTIATE_TEST_SUITE_P( @@ -235,10 +229,7 @@ INSTANTIATE_TEST_SUITE_P( Tests_CountSelfLoopsAndMultiEdges_Rmat, ::testing::Combine( // disable correctness checks for large graphs - ::testing::Values(CountSelfLoopsAndMultiEdges_Usecase{false}, - CountSelfLoopsAndMultiEdges_Usecase{false}, - CountSelfLoopsAndMultiEdges_Usecase{false}, - CountSelfLoopsAndMultiEdges_Usecase{false}), + ::testing::Values(CountSelfLoopsAndMultiEdges_Usecase{false}), ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); CUGRAPH_TEST_PROGRAM_MAIN() From e8d0ccc15eeefd7995d026296fc060d84d90a5db Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Wed, 17 Jan 2024 14:23:11 -0800 Subject: [PATCH 017/155] fix compile error --- cpp/tests/utilities/test_utilities.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/utilities/test_utilities.hpp b/cpp/tests/utilities/test_utilities.hpp index 5480309413b..590ff8c0bfe 100644 --- a/cpp/tests/utilities/test_utilities.hpp +++ b/cpp/tests/utilities/test_utilities.hpp @@ -412,7 +412,7 @@ std::optional> to_host(raft::handle_t const& handle, { std::optional> h_data{std::nullopt}; if (data) { - h_data = to_hoast(handle, raft::device_span((*data).data(), (*data).size())); + h_data = to_host(handle, raft::device_span((*data).data(), (*data).size())); } return h_data; } From 737f438f8a6eb8b2ddab0289f95f730883dc3890 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Wed, 17 Jan 2024 14:23:31 -0800 Subject: [PATCH 018/155] add bool specialization for device_gatherv and device_allgatherv --- cpp/tests/utilities/device_comm_wrapper.cu | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cpp/tests/utilities/device_comm_wrapper.cu b/cpp/tests/utilities/device_comm_wrapper.cu index cfc65b5d741..db9adfd42cd 100644 --- a/cpp/tests/utilities/device_comm_wrapper.cu +++ b/cpp/tests/utilities/device_comm_wrapper.cu @@ -40,9 +40,10 @@ rmm::device_uvector device_gatherv(raft::handle_t const& handle, rmm::device_uvector gathered_v( is_root ? std::reduce(rx_sizes.begin(), rx_sizes.end()) : size_t{0}, handle.get_stream()); + using comm_datatype_t = std::conditional_t, uint8_t, T>; cugraph::device_gatherv(handle.get_comms(), - d_input.data(), - gathered_v.data(), + reinterpret_cast(d_input.data()), + reinterpret_cast(gathered_v.data()), d_input.size(), rx_sizes, rx_displs, @@ -64,9 +65,10 @@ rmm::device_uvector device_allgatherv(raft::handle_t const& handle, rmm::device_uvector gathered_v(std::reduce(rx_sizes.begin(), rx_sizes.end()), handle.get_stream()); + using comm_datatype_t = std::conditional_t, uint8_t, T>; cugraph::device_allgatherv(handle.get_comms(), - d_input.data(), - gathered_v.data(), + reinterpret_cast(d_input.data()), + reinterpret_cast(gathered_v.data()), rx_sizes, rx_displs, handle.get_stream()); @@ -76,6 +78,9 @@ rmm::device_uvector device_allgatherv(raft::handle_t const& handle, // explicit instantiation +template rmm::device_uvector device_gatherv(raft::handle_t const& handle, + raft::device_span d_input); + template rmm::device_uvector device_gatherv(raft::handle_t const& handle, raft::device_span d_input); @@ -91,6 +96,9 @@ template rmm::device_uvector device_gatherv(raft::handle_t const& handle, template rmm::device_uvector device_gatherv(raft::handle_t const& handle, raft::device_span d_input); +template rmm::device_uvector device_allgatherv(raft::handle_t const& handle, + raft::device_span d_input); + template rmm::device_uvector device_allgatherv(raft::handle_t const& handle, raft::device_span d_input); From 00789bf7013cf76827b3a17e2547563802af6e7e Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Wed, 17 Jan 2024 14:24:28 -0800 Subject: [PATCH 019/155] add tests for has_edge() and compute_multiplicity --- cpp/tests/CMakeLists.txt | 10 + ...has_edge_and_compute_multiplicity_test.cpp | 282 +++++++++++++++ ...has_edge_and_compute_multiplicity_test.cpp | 322 ++++++++++++++++++ 3 files changed, 614 insertions(+) create mode 100644 cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp create mode 100644 cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index d9d2f677abc..3df979fe5c2 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -313,6 +313,11 @@ ConfigureTest(DEGREE_TEST structure/degree_test.cpp) ConfigureTest(COUNT_SELF_LOOPS_AND_MULTI_EDGES_TEST "structure/count_self_loops_and_multi_edges_test.cpp") +################################################################################################### +# - Query edge existence and multiplicity tests --------------------------------------------------- +ConfigureTest(HAS_EDGE_AND_COMPUTE_MULTIPLICITY_TEST + "structure/has_edge_and_compute_multiplicity_test.cpp") + ################################################################################################### # - Coarsening tests ------------------------------------------------------------------------------ ConfigureTest(COARSEN_GRAPH_TEST structure/coarsen_graph_test.cpp) @@ -479,6 +484,11 @@ if(BUILD_CUGRAPH_MG_TESTS) ConfigureTestMG(MG_COUNT_SELF_LOOPS_AND_MULTI_EDGES_TEST "structure/mg_count_self_loops_and_multi_edges_test.cpp") + ############################################################################################### + # - MG Query edge existence and multiplicity tests -------------------------------------------- + ConfigureTestMG(MG_HAS_EDGE_AND_COMPUTE_MULTIPLICITY_TEST + "structure/mg_has_edge_and_compute_multiplicity_test.cpp") + ############################################################################################### # - MG PAGERANK tests ------------------------------------------------------------------------- ConfigureTestMG(MG_PAGERANK_TEST link_analysis/mg_pagerank_test.cpp) diff --git a/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp new file mode 100644 index 00000000000..d2d9bbb0d30 --- /dev/null +++ b/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2020-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 governin_from_mtxg permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +struct HasEdgeAndComputeMultiplicity_Usecase { + size_t num_vertex_pairs{}; + bool check_correctness{true}; +}; + +template +class Tests_HasEdgeAndComputeMultiplicity + : public ::testing::TestWithParam< + std::tuple> { + public: + Tests_HasEdgeAndComputeMultiplicity() {} + + static void SetUpTestCase() {} + static void TearDownTestCase() {} + + virtual void SetUp() {} + virtual void TearDown() {} + + template + void run_current_test( + HasEdgeAndComputeMultiplicity_Usecase const& has_edge_and_compute_multiplicity_usecase, + input_usecase_t const& input_usecase) + { + using weight_t = float; + + constexpr bool renumber = true; + + raft::handle_t handle{}; + HighResTimer hr_timer{}; + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.start("Construct graph"); + } + + cugraph::graph_t graph(handle); + std::optional> d_renumber_map_labels{std::nullopt}; + std::tie(graph, std::ignore, d_renumber_map_labels) = + cugraph::test::construct_graph( + handle, input_usecase, false, renumber); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + auto graph_view = graph.view(); + + raft::random::RngState rng_state(0); + auto edge_srcs = cugraph::select_random_vertices( + handle, + graph_view, + std::nullopt, + rng_state, + has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, + true, + false); + auto edge_dsts = cugraph::select_random_vertices( + handle, + graph_view, + std::nullopt, + rng_state, + has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, + true, + false); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.start("Querying edge existence"); + } + + auto edge_exists = + graph_view.has_edge(handle, + raft::device_span(edge_srcs.data(), edge_srcs.size()), + raft::device_span(edge_dsts.data(), edge_dsts.size())); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.start("Computing multiplicity"); + } + + auto edge_multiplicities = graph_view.compute_multiplicity( + handle, + raft::device_span(edge_srcs.data(), edge_srcs.size()), + raft::device_span(edge_dsts.data(), edge_dsts.size())); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + if (has_edge_and_compute_multiplicity_usecase.check_correctness) { + cugraph::graph_t unrenumbered_graph(handle); + if (renumber) { + std::tie(unrenumbered_graph, std::ignore, std::ignore) = + cugraph::test::construct_graph( + handle, input_usecase, false, false); + } + auto unrenumbered_graph_view = renumber ? unrenumbered_graph.view() : graph_view; + + std::vector h_offsets = cugraph::test::to_host( + handle, unrenumbered_graph_view.local_edge_partition_view().offsets()); + std::vector h_indices = cugraph::test::to_host( + handle, unrenumbered_graph_view.local_edge_partition_view().indices()); + + rmm::device_uvector d_unrenumbered_edge_srcs(edge_srcs.size(), handle.get_stream()); + rmm::device_uvector d_unrenumbered_edge_dsts(edge_dsts.size(), handle.get_stream()); + raft::copy_async( + d_unrenumbered_edge_srcs.data(), edge_srcs.data(), edge_srcs.size(), handle.get_stream()); + raft::copy_async( + d_unrenumbered_edge_dsts.data(), edge_dsts.data(), edge_dsts.size(), handle.get_stream()); + if (renumber) { + cugraph::unrenumber_local_int_vertices(handle, + d_unrenumbered_edge_srcs.data(), + d_unrenumbered_edge_srcs.size(), + (*d_renumber_map_labels).data(), + vertex_t{0}, + graph_view.number_of_vertices()); + cugraph::unrenumber_local_int_vertices(handle, + d_unrenumbered_edge_dsts.data(), + d_unrenumbered_edge_dsts.size(), + (*d_renumber_map_labels).data(), + vertex_t{0}, + graph_view.number_of_vertices()); + } + auto h_unrenumbered_edge_srcs = cugraph::test::to_host(handle, d_unrenumbered_edge_srcs); + auto h_unrenumbered_edge_dsts = cugraph::test::to_host(handle, d_unrenumbered_edge_dsts); + + auto h_cugraph_edge_exists = cugraph::test::to_host(handle, edge_exists); + auto h_cugraph_edge_multiplicities = cugraph::test::to_host(handle, edge_multiplicities); + std::vector h_reference_edge_exists(edge_srcs.size()); + std::vector h_reference_edge_multiplicities(edge_srcs.size()); + for (size_t i = 0; i < edge_srcs.size(); ++i) { + auto src = h_unrenumbered_edge_srcs[i]; + auto dst = h_unrenumbered_edge_dsts[i]; + auto major = store_transposed ? dst : src; + auto minor = store_transposed ? src : dst; + auto lower_it = std::lower_bound( + h_indices.begin() + h_offsets[i], h_indices.begin() + h_offsets[i + 1], minor); + auto upper_it = std::upper_bound( + h_indices.begin() + h_offsets[i], h_indices.begin() + h_offsets[i + 1], minor); + auto multiplicity = static_cast(std::distance(lower_it, upper_it)); + h_reference_edge_exists[i] = multiplicity > 0 ? true : false; + h_reference_edge_multiplicities[i] = multiplicity; + } + + ASSERT_TRUE(std::equal(h_reference_edge_exists.begin(), + h_reference_edge_exists.end(), + h_cugraph_edge_exists.begin())) + << "has_edge() return values do not match with the reference values."; + ASSERT_TRUE(std::equal(h_reference_edge_multiplicities.begin(), + h_reference_edge_multiplicities.end(), + h_cugraph_edge_multiplicities.begin())) + << "compute_multiplicity() return values do not match with the reference values."; + } + } +}; + +using Tests_HasEdgeAndComputeMultiplicity_File = + Tests_HasEdgeAndComputeMultiplicity; +using Tests_HasEdgeAndComputeMultiplicity_Rmat = + Tests_HasEdgeAndComputeMultiplicity; + +TEST_P(Tests_HasEdgeAndComputeMultiplicity_File, CheckInt32Int32FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test(std::get<0>(param), std::get<1>(param)); +} + +TEST_P(Tests_HasEdgeAndComputeMultiplicity_Rmat, CheckInt32Int32FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_HasEdgeAndComputeMultiplicity_Rmat, CheckInt32Int64FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_HasEdgeAndComputeMultiplicity_Rmat, CheckInt64Int64FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_HasEdgeAndComputeMultiplicity_File, CheckInt32Int32FloatTransposeTrue) +{ + auto param = GetParam(); + run_current_test(std::get<0>(param), std::get<1>(param)); +} + +TEST_P(Tests_HasEdgeAndComputeMultiplicity_Rmat, CheckInt32Int32FloatTransposeTrue) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +INSTANTIATE_TEST_SUITE_P( + file_test, + Tests_HasEdgeAndComputeMultiplicity_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(HasEdgeAndComputeMultiplicity_Usecase{1024 * 128}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), + cugraph::test::File_Usecase("test/datasets/webbase-1M.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_small_test, + Tests_HasEdgeAndComputeMultiplicity_Rmat, + ::testing::Combine( + // enable correctness checks + ::testing::Values(HasEdgeAndComputeMultiplicity_Usecase{1024 * 128}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + +INSTANTIATE_TEST_SUITE_P( + rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with + --gtest_filter to select only the rmat_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one Rmat_Usecase that differ only in scale or edge + factor (to avoid running same benchmarks more than once) */ + Tests_HasEdgeAndComputeMultiplicity_Rmat, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(HasEdgeAndComputeMultiplicity_Usecase{1024 * 1024 * 128, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); + +CUGRAPH_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp new file mode 100644 index 00000000000..25362ea448e --- /dev/null +++ b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2021-2023, 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +struct HasEdgeAndComputeMultiplicity_Usecase { + size_t num_vertex_pairs{}; + bool check_correctness{true}; +}; + +template +class Tests_MGHasEdgeAndComputeMultiplicity + : public ::testing::TestWithParam< + std::tuple> { + public: + Tests_MGHasEdgeAndComputeMultiplicity() {} + + static void SetUpTestCase() { handle_ = cugraph::test::initialize_mg_handle(); } + + static void TearDownTestCase() { handle_.reset(); } + + virtual void SetUp() {} + virtual void TearDown() {} + + // Compare the results of running has_edge & compute_multiplicity on multiple GPUs to that of + // a single-GPU run + template + void run_current_test( + HasEdgeAndComputeMultiplicity_Usecase const& has_edge_and_compute_multiplicity_usecase, + input_usecase_t const& input_usecase) + { + using weight_t = float; + using edge_type_id_t = int32_t; + + HighResTimer hr_timer{}; + + // 1. create MG graph + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.start("MG Construct graph"); + } + + cugraph::graph_t mg_graph(*handle_); + std::optional> mg_renumber_map{std::nullopt}; + std::tie(mg_graph, std::ignore, mg_renumber_map) = + cugraph::test::construct_graph( + *handle_, input_usecase, false, true); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + auto mg_graph_view = mg_graph.view(); + + raft::random::RngState rng_state{0}; + auto d_mg_edge_srcs = cugraph::select_random_vertices( + *handle_, + mg_graph_view, + std::nullopt, + rng_state, + has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, + true, + false); + auto d_mg_edge_dsts = cugraph::select_random_vertices( + *handle_, + mg_graph_view, + std::nullopt, + rng_state, + has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, + true, + false); + + std::tie(store_transposed ? d_mg_edge_dsts : d_mg_edge_srcs, + store_transposed ? d_mg_edge_srcs : d_mg_edge_dsts, + std::ignore, + std::ignore, + std::ignore) = + cugraph::detail::shuffle_int_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning< + vertex_t, + edge_t, + weight_t, + edge_type_id_t>(*handle_, + store_transposed ? std::move(d_mg_edge_dsts) : std::move(d_mg_edge_srcs), + store_transposed ? std::move(d_mg_edge_srcs) : std::move(d_mg_edge_dsts), + std::nullopt, + std::nullopt, + std::nullopt, + mg_graph_view.vertex_partition_range_lasts()); + + // 2. run MG has_edge & compute_multiplicity + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.start("MG Querying edge existence"); + } + + auto d_mg_edge_exists = mg_graph_view.has_edge( + *handle_, + raft::device_span(d_mg_edge_srcs.data(), d_mg_edge_srcs.size()), + raft::device_span(d_mg_edge_dsts.data(), d_mg_edge_dsts.size())); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.start("MG Computing multiplicity"); + } + + auto d_mg_edge_multiplicities = mg_graph_view.compute_multiplicity( + *handle_, + raft::device_span(d_mg_edge_srcs.data(), d_mg_edge_srcs.size()), + raft::device_span(d_mg_edge_dsts.data(), d_mg_edge_dsts.size())); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + // 3. copmare SG & MG results + + if (has_edge_and_compute_multiplicity_usecase.check_correctness) { + // 3-1. aggregate MG results + + cugraph::unrenumber_int_vertices( + *handle_, + d_mg_edge_srcs.data(), + d_mg_edge_srcs.size(), + (*mg_renumber_map).data(), + mg_graph_view.vertex_partition_range_lasts()); + cugraph::unrenumber_int_vertices( + *handle_, + d_mg_edge_dsts.data(), + d_mg_edge_dsts.size(), + (*mg_renumber_map).data(), + mg_graph_view.vertex_partition_range_lasts()); + + auto d_mg_aggregate_edge_srcs = cugraph::test::device_gatherv( + *handle_, raft::device_span(d_mg_edge_srcs.data(), d_mg_edge_srcs.size())); + auto d_mg_aggregate_edge_dsts = cugraph::test::device_gatherv( + *handle_, raft::device_span(d_mg_edge_dsts.data(), d_mg_edge_dsts.size())); + auto d_mg_aggregate_edge_exists = cugraph::test::device_gatherv( + *handle_, raft::device_span(d_mg_edge_exists.data(), d_mg_edge_exists.size())); + auto d_mg_aggregate_edge_multiplicities = cugraph::test::device_gatherv( + *handle_, + raft::device_span(d_mg_edge_multiplicities.data(), + d_mg_edge_multiplicities.size())); + + cugraph::graph_t sg_graph(*handle_); + std::tie(sg_graph, std::ignore, std::ignore) = cugraph::test::mg_graph_to_sg_graph( + *handle_, + mg_graph_view, + std::optional>{std::nullopt}, + std::make_optional>((*mg_renumber_map).data(), + (*mg_renumber_map).size()), + false); + + if (handle_->get_comms().get_rank() == 0) { + auto sg_graph_view = sg_graph.view(); + + // 3-2. run SG count_self_loops & count_multi_edges + + auto d_sg_edge_exists = sg_graph_view.has_edge( + *handle_, + raft::device_span(d_mg_aggregate_edge_srcs.data(), + d_mg_aggregate_edge_srcs.size()), + raft::device_span(d_mg_aggregate_edge_dsts.data(), + d_mg_aggregate_edge_dsts.size())); + auto d_sg_edge_multiplicities = sg_graph_view.compute_multiplicity( + *handle_, + raft::device_span(d_mg_aggregate_edge_srcs.data(), + d_mg_aggregate_edge_srcs.size()), + raft::device_span(d_mg_aggregate_edge_dsts.data(), + d_mg_aggregate_edge_dsts.size())); + + // 3-3. compare + + auto h_mg_aggregate_edge_exists = + cugraph::test::to_host(*handle_, d_mg_aggregate_edge_exists); + auto h_mg_aggregate_edge_multiplicities = + cugraph::test::to_host(*handle_, d_mg_aggregate_edge_multiplicities); + auto h_sg_edge_exists = cugraph::test::to_host(*handle_, d_sg_edge_exists); + auto h_sg_edge_multiplicities = cugraph::test::to_host(*handle_, d_sg_edge_multiplicities); + + ASSERT_TRUE(std::equal(h_mg_aggregate_edge_exists.begin(), + h_mg_aggregate_edge_exists.end(), + h_sg_edge_exists.begin())); + ASSERT_TRUE(std::equal(h_mg_aggregate_edge_multiplicities.begin(), + h_mg_aggregate_edge_multiplicities.end(), + h_sg_edge_multiplicities.begin())); + } + } + } + + private: + static std::unique_ptr handle_; +}; + +template +std::unique_ptr Tests_MGHasEdgeAndComputeMultiplicity::handle_ = + nullptr; + +using Tests_MGHasEdgeAndComputeMultiplicity_File = + Tests_MGHasEdgeAndComputeMultiplicity; +using Tests_MGHasEdgeAndComputeMultiplicity_Rmat = + Tests_MGHasEdgeAndComputeMultiplicity; + +TEST_P(Tests_MGHasEdgeAndComputeMultiplicity_File, CheckInt32Int32FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test(std::get<0>(param), std::get<1>(param)); +} + +TEST_P(Tests_MGHasEdgeAndComputeMultiplicity_Rmat, CheckInt32Int32FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGHasEdgeAndComputeMultiplicity_Rmat, CheckInt32Int64FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGHasEdgeAndComputeMultiplicity_Rmat, CheckInt64Int64FloatTransposeFalse) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +TEST_P(Tests_MGHasEdgeAndComputeMultiplicity_File, CheckInt32Int32FloatTransposeTrue) +{ + auto param = GetParam(); + run_current_test(std::get<0>(param), std::get<1>(param)); +} + +TEST_P(Tests_MGHasEdgeAndComputeMultiplicity_Rmat, CheckInt32Int32FloatTransposeTrue) +{ + auto param = GetParam(); + run_current_test( + std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); +} + +INSTANTIATE_TEST_SUITE_P( + file_tests, + Tests_MGHasEdgeAndComputeMultiplicity_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(HasEdgeAndComputeMultiplicity_Usecase{1024 * 128}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), + cugraph::test::File_Usecase("test/datasets/webbase-1M.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_small_tests, + Tests_MGHasEdgeAndComputeMultiplicity_Rmat, + ::testing::Combine( + ::testing::Values(HasEdgeAndComputeMultiplicity_Usecase{1024 * 128}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, false, false)))); + +INSTANTIATE_TEST_SUITE_P( + rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with + --gtest_filter to select only the rmat_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one Rmat_Usecase that differ only in scale or edge + factor (to avoid running same benchmarks more than once) */ + Tests_MGHasEdgeAndComputeMultiplicity_Rmat, + ::testing::Combine( + ::testing::Values(HasEdgeAndComputeMultiplicity_Usecase{1024 * 1024 * 128, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, false, false)))); + +CUGRAPH_MG_TEST_PROGRAM_MAIN() From 0b45356b2d8ca026a6861e45560f689a76702077 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Wed, 17 Jan 2024 14:52:20 -0800 Subject: [PATCH 020/155] copyright year --- cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp | 2 +- cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp | 2 +- .../structure/mg_has_edge_and_compute_multiplicity_test.cpp | 2 +- cpp/tests/utilities/device_comm_wrapper.cu | 2 +- cpp/tests/utilities/test_utilities.hpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp b/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp index c3fc13e9a71..b7f1dce2023 100644 --- a/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp +++ b/cpp/tests/structure/count_self_loops_and_multi_edges_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. + * Copyright (c) 2020-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. diff --git a/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp index d2d9bbb0d30..99fd3349caf 100644 --- a/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp +++ b/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, NVIDIA CORPORATION. + * Copyright (c) 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. diff --git a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp index 25362ea448e..2fbd5b5c10c 100644 --- a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp +++ b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, NVIDIA CORPORATION. + * Copyright (c) 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. diff --git a/cpp/tests/utilities/device_comm_wrapper.cu b/cpp/tests/utilities/device_comm_wrapper.cu index db9adfd42cd..50727394ad7 100644 --- a/cpp/tests/utilities/device_comm_wrapper.cu +++ b/cpp/tests/utilities/device_comm_wrapper.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, NVIDIA CORPORATION. + * Copyright (c) 2021-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. diff --git a/cpp/tests/utilities/test_utilities.hpp b/cpp/tests/utilities/test_utilities.hpp index 590ff8c0bfe..3fa6ae089d3 100644 --- a/cpp/tests/utilities/test_utilities.hpp +++ b/cpp/tests/utilities/test_utilities.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION. + * Copyright (c) 2019-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. From 7cfe60ac43fd936f894af8c82b5fa2536f3e1c63 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Wed, 17 Jan 2024 17:00:32 -0800 Subject: [PATCH 021/155] bug fix --- cpp/src/structure/graph_view_impl.cuh | 4 ++-- .../has_edge_and_compute_multiplicity_test.cpp | 6 +++--- .../mg_has_edge_and_compute_multiplicity_test.cpp | 12 +++++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cpp/src/structure/graph_view_impl.cuh b/cpp/src/structure/graph_view_impl.cuh index 18b1088aebc..7928c61cf7b 100644 --- a/cpp/src/structure/graph_view_impl.cuh +++ b/cpp/src/structure/graph_view_impl.cuh @@ -866,7 +866,7 @@ graph_view_t h_reference_edge_exists(edge_srcs.size()); - std::vector h_reference_edge_multiplicities(edge_srcs.size()); + std::vector h_reference_edge_multiplicities(edge_srcs.size()); for (size_t i = 0; i < edge_srcs.size(); ++i) { auto src = h_unrenumbered_edge_srcs[i]; auto dst = h_unrenumbered_edge_dsts[i]; auto major = store_transposed ? dst : src; auto minor = store_transposed ? src : dst; auto lower_it = std::lower_bound( - h_indices.begin() + h_offsets[i], h_indices.begin() + h_offsets[i + 1], minor); + h_indices.begin() + h_offsets[major], h_indices.begin() + h_offsets[major + 1], minor); auto upper_it = std::upper_bound( - h_indices.begin() + h_offsets[i], h_indices.begin() + h_offsets[i + 1], minor); + h_indices.begin() + h_offsets[major], h_indices.begin() + h_offsets[major + 1], minor); auto multiplicity = static_cast(std::distance(lower_it, upper_it)); h_reference_edge_exists[i] = multiplicity > 0 ? true : false; h_reference_edge_multiplicities[i] = multiplicity; diff --git a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp index 2fbd5b5c10c..7b19b0b2024 100644 --- a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp +++ b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp @@ -91,6 +91,8 @@ class Tests_MGHasEdgeAndComputeMultiplicity auto mg_graph_view = mg_graph.view(); + // 2. create an edge list to query + raft::random::RngState rng_state{0}; auto d_mg_edge_srcs = cugraph::select_random_vertices( *handle_, @@ -126,7 +128,7 @@ class Tests_MGHasEdgeAndComputeMultiplicity std::nullopt, mg_graph_view.vertex_partition_range_lasts()); - // 2. run MG has_edge & compute_multiplicity + // 3. run MG has_edge & compute_multiplicity if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement @@ -164,10 +166,10 @@ class Tests_MGHasEdgeAndComputeMultiplicity hr_timer.display_and_clear(std::cout); } - // 3. copmare SG & MG results + // 4. copmare SG & MG results if (has_edge_and_compute_multiplicity_usecase.check_correctness) { - // 3-1. aggregate MG results + // 4-1. aggregate MG results cugraph::unrenumber_int_vertices( *handle_, @@ -205,7 +207,7 @@ class Tests_MGHasEdgeAndComputeMultiplicity if (handle_->get_comms().get_rank() == 0) { auto sg_graph_view = sg_graph.view(); - // 3-2. run SG count_self_loops & count_multi_edges + // 4-2. run SG count_self_loops & count_multi_edges auto d_sg_edge_exists = sg_graph_view.has_edge( *handle_, @@ -220,7 +222,7 @@ class Tests_MGHasEdgeAndComputeMultiplicity raft::device_span(d_mg_aggregate_edge_dsts.data(), d_mg_aggregate_edge_dsts.size())); - // 3-3. compare + // 4-3. compare auto h_mg_aggregate_edge_exists = cugraph::test::to_host(*handle_, d_mg_aggregate_edge_exists); From 43f84850b2187981fdc764a1d45e3bd9c8925ac8 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Fri, 19 Jan 2024 15:42:11 -0800 Subject: [PATCH 022/155] bug fix in tests --- ...has_edge_and_compute_multiplicity_test.cpp | 31 +++++----- ...has_edge_and_compute_multiplicity_test.cpp | 57 ++++++++----------- 2 files changed, 38 insertions(+), 50 deletions(-) diff --git a/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp index 8af9ad3c186..3ad6953ca03 100644 --- a/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp +++ b/cpp/tests/structure/has_edge_and_compute_multiplicity_test.cpp @@ -89,22 +89,21 @@ class Tests_HasEdgeAndComputeMultiplicity auto graph_view = graph.view(); raft::random::RngState rng_state(0); - auto edge_srcs = cugraph::select_random_vertices( - handle, - graph_view, - std::nullopt, - rng_state, - has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, - true, - false); - auto edge_dsts = cugraph::select_random_vertices( - handle, - graph_view, - std::nullopt, - rng_state, - has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, - true, - false); + rmm::device_uvector edge_srcs( + has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, handle.get_stream()); + rmm::device_uvector edge_dsts(edge_srcs.size(), handle.get_stream()); + cugraph::detail::uniform_random_fill(handle.get_stream(), + edge_srcs.data(), + edge_srcs.size(), + vertex_t{0}, + graph_view.number_of_vertices(), + rng_state); + cugraph::detail::uniform_random_fill(handle.get_stream(), + edge_dsts.data(), + edge_dsts.size(), + vertex_t{0}, + graph_view.number_of_vertices(), + rng_state); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement diff --git a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp index 7b19b0b2024..46b320bdf8d 100644 --- a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp +++ b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp @@ -68,6 +68,9 @@ class Tests_MGHasEdgeAndComputeMultiplicity HighResTimer hr_timer{}; + auto const comm_rank = handle_->get_comms().get_rank(); + auto const comm_size = handle_->get_comms().get_size(); + // 1. create MG graph if (cugraph::test::g_perf) { @@ -93,40 +96,26 @@ class Tests_MGHasEdgeAndComputeMultiplicity // 2. create an edge list to query - raft::random::RngState rng_state{0}; - auto d_mg_edge_srcs = cugraph::select_random_vertices( - *handle_, - mg_graph_view, - std::nullopt, - rng_state, - has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, - true, - false); - auto d_mg_edge_dsts = cugraph::select_random_vertices( - *handle_, - mg_graph_view, - std::nullopt, - rng_state, - has_edge_and_compute_multiplicity_usecase.num_vertex_pairs, - true, - false); - - std::tie(store_transposed ? d_mg_edge_dsts : d_mg_edge_srcs, - store_transposed ? d_mg_edge_srcs : d_mg_edge_dsts, - std::ignore, - std::ignore, - std::ignore) = - cugraph::detail::shuffle_int_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning< - vertex_t, - edge_t, - weight_t, - edge_type_id_t>(*handle_, - store_transposed ? std::move(d_mg_edge_dsts) : std::move(d_mg_edge_srcs), - store_transposed ? std::move(d_mg_edge_srcs) : std::move(d_mg_edge_dsts), - std::nullopt, - std::nullopt, - std::nullopt, - mg_graph_view.vertex_partition_range_lasts()); + raft::random::RngState rng_state(comm_rank); + size_t num_vertex_pairs_this_gpu = + (has_edge_and_compute_multiplicity_usecase.num_vertex_pairs / comm_size) + + ((comm_rank < has_edge_and_compute_multiplicity_usecase.num_vertex_pairs % comm_size) + ? size_t{1} + : size_t{0}); + rmm::device_uvector d_mg_edge_srcs(num_vertex_pairs_this_gpu, handle_->get_stream()); + rmm::device_uvector d_mg_edge_dsts(d_mg_edge_srcs.size(), handle_->get_stream()); + cugraph::detail::uniform_random_fill(handle_->get_stream(), + d_mg_edge_srcs.data(), + d_mg_edge_srcs.size(), + vertex_t{0}, + mg_graph_view.number_of_vertices(), + rng_state); + cugraph::detail::uniform_random_fill(handle_->get_stream(), + d_mg_edge_dsts.data(), + d_mg_edge_dsts.size(), + vertex_t{0}, + mg_graph_view.number_of_vertices(), + rng_state); // 3. run MG has_edge & compute_multiplicity From 8ff673a4659630cfb661c25e857db5ab111bec48 Mon Sep 17 00:00:00 2001 From: Seunghwa Kang Date: Fri, 19 Jan 2024 16:31:50 -0800 Subject: [PATCH 023/155] test bug fix --- ..._has_edge_and_compute_multiplicity_test.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp index 46b320bdf8d..8079de7ebfe 100644 --- a/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp +++ b/cpp/tests/structure/mg_has_edge_and_compute_multiplicity_test.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -117,6 +118,23 @@ class Tests_MGHasEdgeAndComputeMultiplicity mg_graph_view.number_of_vertices(), rng_state); + std::tie(store_transposed ? d_mg_edge_dsts : d_mg_edge_srcs, + store_transposed ? d_mg_edge_srcs : d_mg_edge_dsts, + std::ignore, + std::ignore, + std::ignore) = + cugraph::detail::shuffle_int_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning< + vertex_t, + edge_t, + weight_t, + edge_type_id_t>(*handle_, + std::move(store_transposed ? d_mg_edge_dsts : d_mg_edge_srcs), + std::move(store_transposed ? d_mg_edge_srcs : d_mg_edge_dsts), + std::nullopt, + std::nullopt, + std::nullopt, + mg_graph_view.vertex_partition_range_lasts()); + // 3. run MG has_edge & compute_multiplicity if (cugraph::test::g_perf) { From 99c973f784b25d7268c3627fcd9f99c855aefd24 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 21 Jan 2024 05:47:44 -0800 Subject: [PATCH 024/155] unroll 'p, q' edges --- cpp/src/community/k_truss_impl.cuh | 443 ++++++++--------------------- 1 file changed, 115 insertions(+), 328 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 39763fce100..77762d5ff67 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -93,8 +93,6 @@ struct extract_low_to_high_degree_edges_t { }; - - template struct extract_p_q { raft::device_span intersection_offsets{}; @@ -104,24 +102,14 @@ struct extract_p_q { __device__ thrust::tuple operator()(edge_t i) const { - printf("\n i = %d", i); auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - printf("\nthe idx = %d", idx); - - printf("\n major = %d and minor = %d", thrust::get<0>(*(vertex_pairs_begin + idx)), thrust::get<1>(*(vertex_pairs_begin + idx))); - printf("\nintersection = %d\n", intersection_offsets[i+1] - intersection_offsets[i]); - - thrust::tuple pair = *(vertex_pairs_begin + idx); - + thrust::tuple pair = *(vertex_pairs_begin + idx); return pair; } }; - - template struct extract_p_r { raft::device_span intersection_offsets{}; @@ -133,13 +121,10 @@ struct extract_p_r { __device__ thrust::tuple operator()(edge_t i) const { auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - printf("\n ** (p, q) major = %d and minor = %d", thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - return pair; + return pair; } }; @@ -149,77 +134,53 @@ template struct extract_q_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator vertex_pairs_begin; - __device__ thrust::tuple operator()(edge_t i) const { - printf("\n i = %d", i); auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - printf("\n ** (p, q) major = %d and minor = %d", thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - return pair; + return pair; } }; -template -struct find_intersection { - size_t vertex_pair_buffer_size; - size_t num_invalid_edges; +template +struct generate_pr { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairBuffer vertex_pair_buffer; - Iterator invalid_edge_first; + VertexPairIterator vertex_pairs_begin; - __device__ size_t operator()(edge_t i) const + __device__ thrust::tuple operator()(edge_t i) const { - printf("\n ***** i = %d\n", i); - printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); - printf("\nthe vertex pair buffer size = %d\n", vertex_pair_buffer_size); - //auto x = thrust::get<1>(*invalid_edge_first); - //printf("\nx = %d\n", x); - // Run binary search - //if (i < vertex_pair_buffer_size) { - if (i < num_invalid_edges) { - auto pair = thrust::get<0>(*(invalid_edge_first + i)); // FIXME only increment if it is less than 'invalid_edge_first.size()' - printf("\nsrc_pair = %d, dst_pair = %d\n", thrust::get<0>(pair), thrust::get<1>(pair)); - - auto itr = thrust::upper_bound(thrust::seq, - vertex_pair_buffer, - vertex_pair_buffer + vertex_pair_buffer_size, - //pair, - thrust::make_tuple(thrust::get<0>(pair), thrust::get<1>(pair)), - thrust::less>{}); - - printf("\n itr = %d\n", itr); - auto idx = thrust::distance(vertex_pair_buffer + 1, itr); - printf("\n idx = %d \n", idx); - - return 1; - } - else { - return 0; - } - + auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - return 0; + return pair; } }; +template +struct generate_qr { + raft::device_span intersection_offsets{}; + raft::device_span intersection_indices{}; + VertexPairIterator vertex_pairs_begin; + __device__ thrust::tuple operator()(edge_t i) const + { + auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - + return pair; + } +}; template @@ -450,17 +411,19 @@ void k_truss(raft::handle_t const& handle, auto vertex_pairs_begin = thrust::make_zip_iterator( edgelist_srcs.begin(), edgelist_dsts.begin()); - RAFT_CUDA_TRY(cudaDeviceSynchronize()); + thrust::sort(handle.get_thrust_policy(), + vertex_pairs_begin, + vertex_pairs_begin + edgelist_srcs.size()); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove raft::print_device_vector("src ", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); raft::print_device_vector("dst ", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); size_t num_vertex_pairs = edgelist_srcs.size(); auto out_degrees = cur_graph_view.compute_out_degrees(handle); - rmm::device_uvector intersection_offsets(size_t{0}, handle.get_stream()); - rmm::device_uvector intersection_indices(size_t{0}, handle.get_stream()); - rmm::device_uvector r_nbr_intersection_property_values0(size_t{0}, handle.get_stream()); - rmm::device_uvector r_nbr_intersection_property_values1(size_t{0}, handle.get_stream()); + rmm::device_uvector r_nbr_intersection_property_values0(size_t{0}, handle.get_stream()); // FIXME: remove as it is not used + rmm::device_uvector r_nbr_intersection_property_values1(size_t{0}, handle.get_stream()); // FIXME: remove as it is not used // FIXME: Initially each edge should have an edge property of 0 auto[intersection_offsets, intersection_indices] = @@ -472,9 +435,7 @@ void k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector("intersection_offsets ", intersection_offsets.data(), intersection_offsets.size(), std::cout); - raft::print_device_vector("intersection_indices ", intersection_indices.data(), intersection_indices.size(), std::cout); + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove auto vertex_pair_buffer = allocate_dataframe_buffer>( num_vertex_pairs, handle.get_stream()); @@ -523,23 +484,18 @@ void k_truss(raft::handle_t const& handle, intersection_indices.data(), intersection_indices.size()), vertex_pairs_begin }); - - printf("\nbefore sorting\n"); - raft::print_device_vector("src", std::get<0>(vertex_pair_buffer_tmp).data(), std::get<0>(vertex_pair_buffer_tmp).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer_tmp).data(), std::get<1>(vertex_pair_buffer_tmp).size(), std::cout); - thrust::sort(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_tmp), get_dataframe_buffer_end(vertex_pair_buffer_tmp)); - rmm::device_uvector num_triangles_tmp(3 * intersection_indices.size(), handle.get_stream()); thrust::fill(handle.get_thrust_policy(), num_triangles_tmp.begin(), num_triangles_tmp.end(), size_t{1}); rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); - + + // FIXME: Does a reduce by key preserve the sorting? I believe 'YES" thrust::reduce_by_key(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_tmp), get_dataframe_buffer_end(vertex_pair_buffer_tmp), @@ -558,136 +514,12 @@ void k_truss(raft::handle_t const& handle, // num triangle here std::get<1>(vertex_pair_buffer).begin()); - cugraph::edge_property_t edge_value_output(handle, cur_graph_view); thrust::sort(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer), get_dataframe_buffer_end(vertex_pair_buffer), - thrust::less>{}); - raft::print_device_vector("sorted - src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); - raft::print_device_vector("sorted - dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); - raft::print_device_vector("new num triangles", num_triangles.data(), num_triangles.size(), std::cout); - handle.sync_stream(); - cugraph::transform_e( - handle, - cur_graph_view, - edge_list, // assigning th e number of triangles as an edge property - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - [num_triangles = num_triangles.data(), - vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), - size=get_dataframe_buffer_end(vertex_pair_buffer) - get_dataframe_buffer_begin(vertex_pair_buffer)] // FIXME: use 'size_dataframe_buffer(vertex_pair_buffer)' - // FIXME: Maybe: the last 'thrust::nullopt_t' for the edge prop containing the number of triangles. - // Take a look at the 'vertex_bucket_t' for context - __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - /* - for ( int i =0 ; i < size; i++){ - //printf("\n i = %d major = %d and minor = %d", i, thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); - printf("hello"); - } - */ - - - - - auto x = thrust::make_tuple(thrust::get<0>(*(vertex_pair_buffer)), thrust::get<1>(*(vertex_pair_buffer))); - printf("\nsrc = %d , dst = %d\n", src, dst); - //printf("\ny = %d\n", vertex_pair_buffer); - - auto it = thrust::lower_bound(thrust::seq, - vertex_pair_buffer, - vertex_pair_buffer + size, - thrust::make_tuple(src, dst)); - printf("\ntransform_e - itr = %d\n", it); - auto idx = thrust::distance(vertex_pair_buffer, it); - printf("\nthe distance = %d\n", idx); - - /* - auto it_ = thrust::lower_bound(thrust::seq, - vertex_pair_buffer, - vertex_pair_buffer + size, - //thrust::make_tuple(src, dst), - thrust::make_tuple(1, 3), - thrust::less>{}); - printf("\ntransform_e - itr____ = %d\n", it_); - auto idx_ = thrust::distance(vertex_pair_buffer, it_); - printf("\nthe distance = %d\n", idx_); - */ - - //printf("\nitr = %d\n", *it); - return num_triangles[idx]; - }, - edge_value_output.mutable_view(), - false); - - - /* - rmm::device_uvector intersection_size_(intersection_offsets.size() - 1, handle.get_stream()); // take the worst case where all edges are invalidated - // FIXME: might not be necessary to fill it with zeros - thrust::fill(handle.get_thrust_policy(), intersection_size_.begin(), intersection_size_.end(), size_t{0}); - printf("\n*******dummy******\n"); - thrust::tabulate(handle.get_thrust_policy(), - intersection_size_.begin(), - intersection_size_.end(), - [vertex_pair_buffer_size=size_dataframe_buffer(vertex_pair_buffer), - intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - intersection_indices = raft::device_span(intersection_indices.data(), intersection_indices.size()), - vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer) - ] - __device__(edge_t i){ - printf("\n ***** i = %d\n", i); - printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); - printf("\nthe vertex pair buffer size = %d\n", vertex_pair_buffer_size); - auto num_invalid_edges = 6; - if (i < num_invalid_edges) { - //auto pair = thrust::get<0>(*(invalid_edge_first + i)); // FIXME only increment if it is less than 'invalid_edge_first.size()' - //printf("\nsrc_pair = %d, dst_pair = %d\n", thrust::get<0>(pair), thrust::get<1>(pair)); - - auto itr = thrust::lower_bound(thrust::seq, - vertex_pair_buffer, - vertex_pair_buffer + vertex_pair_buffer_size, - //pair, - //thrust::make_tuple(thrust::get<0>(pair), thrust::get<1>(pair)), - thrust::make_tuple(2, 3), - thrust::less>{}); - - printf("\n itr = %d\n", itr); - auto idx = thrust::distance(vertex_pair_buffer, itr); - printf("\n idx = %d \n", idx); - - return 1; - } - else { - return 0; - } - - }); - printf("\n*******Done dummy******\n"); - */ - - - - - - - - - - - - auto value_firsts = edge_value_output.view().value_firsts(); - auto edge_counts = edge_value_output.view().edge_counts(); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); - raft::print_device_vector( - "num_triangles_tmp:", value_firsts[0], edge_counts[0], std::cout); - - vertex_pairs_begin = thrust::make_zip_iterator(std::get<0>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin()); // FIXME: not used below but used up - printf("\nafter sorting\n"); - raft::print_device_vector("src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); + thrust::less>{}); // FIXME: Isn't this sorting redunddant? // FIXME: shuffle before sorting and reducing - Bring this part up if constexpr (multi_gpu) { @@ -712,13 +544,14 @@ void k_truss(raft::handle_t const& handle, handle, std::move(edgelist_srcs), std::move(edgelist_dsts), std::nullopt, std::nullopt, std::nullopt); } - // Run thrust::partition to place the edges with triangle counts smaller than K-2 at the end + // Run thrust::stable_partition to place the edges with triangle counts smaller than K-2 at the end // zip iterator for the pair + auto edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + - // FIXME: rename it to edge_to_remove_first - auto invalid_edge_first = thrust::partition( + auto invalid_edge_first = thrust::stable_partition( handle.get_thrust_policy(), edges_to_num_triangles, edges_to_num_triangles + num_triangles.size(), @@ -737,140 +570,94 @@ void k_truss(raft::handle_t const& handle, static_cast(thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); printf("\nthe number of invalid edges = %d\n", num_invalid_edges); - /* - void resize_dataframe_buffer(BufferType& buffer, - size_t new_buffer_size, - rmm::cuda_stream_view stream_view) - */ + if (num_invalid_edges != 0) { // unroll and remove/mask edges - // Find (p, q, intersection of p&q's neighbor lists) - // before that, determine the interection size of each edge to be removed - rmm::device_uvector intersection_size(intersection_offsets.size() - 1, handle.get_stream()); // take the worst case where all edges are invalidated - // FIXME: might not be necessary to fill it with zeros - thrust::fill(handle.get_thrust_policy(), intersection_size.begin(), intersection_size.end(), size_t{0}); - /* - thrust::tabulate(handle.get_thrust_policy(), - intersection_size.begin(), - intersection_size.end(), - find_intersection{ - //find_intersection{ - size_dataframe_buffer(vertex_pair_buffer), // FIXME: should we just do this inside the functor? - num_invalid_edges, - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp), // FIXME: rename to vertex_pairs_buffer? maybe I should pass a pointer here 'get_dataframe_buffer_begin(vertex_pair_buffer)' - //vertex_pairs_begin, - invalid_edge_first - }); - */ - thrust::tabulate(handle.get_thrust_policy(), - intersection_size.begin(), - intersection_size.end(), - [vertex_pair_buffer_size=size_dataframe_buffer(vertex_pair_buffer), - num_invalid_edges=num_invalid_edges, - intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - intersection_indices = raft::device_span(intersection_indices.data(), intersection_indices.size()), - vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), - invalid_edge_first = invalid_edge_first - ] - __device__(edge_t i){ - - printf("\n ***** i = %d\n", i); - printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pair_buffer+i)), thrust::get<1>(*(vertex_pair_buffer+i))); - printf("\nthe vertex pair buffer size = %d\n", vertex_pair_buffer_size); - if (i < num_invalid_edges) { - //auto pair = thrust::get<0>(*(invalid_edge_first + i)); // FIXME only increment if it is less than 'invalid_edge_first.size()' - vertex_t src = thrust::get<0>(thrust::get<0>(*(invalid_edge_first + i))); - vertex_t dst = thrust::get<1>(thrust::get<0>(*(invalid_edge_first + i))); - //printf("\nsrc_pair = %d, dst_pair = %d, pair = %d\n", thrust::get<0>(pair), thrust::get<1>(pair), pair); - printf("\nsrc_pair = %d, dst_pair = %d", src, dst); - - auto itr = thrust::lower_bound(thrust::seq, - vertex_pair_buffer, - vertex_pair_buffer + vertex_pair_buffer_size, - //pair, - //thrust::make_tuple(thrust::get<0>(pair), thrust::get<1>(pair)), - //thrust::make_tuple(1, 3), - thrust::make_tuple(src, dst), - thrust::less>{}); - - printf("\n itr = %d\n", itr); - auto idx = thrust::distance(vertex_pair_buffer, itr); - printf("\n distance = %d \n", idx); - - return 1; // dummy - } - else { - return 0; //dummy - } - - }); + // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) + auto[intersection_offsets, intersection_indices] = + detail::nbr_intersection(handle, + cur_graph_view, + cugraph::edge_dummy_property_t{}.view(), + thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()), // FIXME: use 'invalid_edge_first' instead as it marks the beginning of the edges to be unrolled, + thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()) + num_triangles.size(), + std::array{true, true}, + do_expensive_check); + + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove + - + auto prev_size = size_dataframe_buffer(vertex_pair_buffer); + auto new_size = prev_size + intersection_indices.size(); + resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); + // Unroll (p, q) + // 1) generating (p, r) + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size, + get_dataframe_buffer_begin(vertex_pair_buffer) + new_size, + generate_pr{ //FIXME: it is supposed to be of the type of the last argument + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin//thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()) + }); + + // resize num_triangles + num_triangles.resize(new_size, handle.get_stream()); + // Fill the newly allocated space with -1 + thrust::fill(handle.get_thrust_policy(), num_triangles.begin() + prev_size, num_triangles.end(), size_t{-1}); + + prev_size = size_dataframe_buffer(vertex_pair_buffer); + new_size = prev_size + intersection_indices.size(); + resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); + + // 2) generating (q, r) + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size, + get_dataframe_buffer_begin(vertex_pair_buffer) + new_size, + generate_qr{ + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin + }); + // resize num_triangles + num_triangles.resize(new_size, handle.get_stream()); + // Fill the newly allocated space with -1 + thrust::fill(handle.get_thrust_policy(), num_triangles.begin() + prev_size, num_triangles.end(), size_t{-1}); + raft::print_device_vector("new num_triangles q_r ", num_triangles.data(), num_triangles.size(), std::cout); + raft::print_device_vector("src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); - } - - /* - thrust::partition( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - [num_triangles = num_triangles.data(), - vertex_pair_buffer = get_dataframe_buffer_begin(vertex_pair_buffer), - //size=get_dataframe_buffer_end(vertex_pair_buffer) - get_dataframe_buffer_begin(vertex_pair_buffer) - size = size_dataframe_buffer(vertex_pair_buffer)] //size_dataframe_buffer - __device__(auto e){ - //printf("src = %d, dst = %d\n", std::get<0>(e), std::get<1>(e)); - auto[src, dst] = e; - auto x = thrust::make_tuple(src, dst); - printf("src_ = %d, dst_ = %d\n", thrust::get<0>(x), thrust::get<1>(x)); - //vertex_t src_ = thrust::get<0>(x); - //vertex_t dst_ = thrust::get<1>(x); - vertex_t dst_ = thrust::get<1>(e); - //vertex_t dst__ = e; - - - //auto x = thrust::make_tuple(src, dst); - //printf("src = %d, dst = %d\n", src, dst); - //printf("src_ = %d, dst_ = %d\n", thrust::get<0>(x), thrust::get<1>(x)); - //vertex_t src_ = thrust::get<0>(x); - //printf("src___ = %d\n", src_); - //vertex_t src_ = thrust::get<0>(x); - //vertex_t dst_ = thrust::get<1>(x); // error: no suitable conversion function from "thrust::detail::cons" to "int32_t" exists - - - - // sort by key. key num of triangles and the values =vertex pairs. thrust::greater - printf("the isze of the buffer = %d\n", size); - auto it = thrust::lower_bound(thrust::seq, - vertex_pair_buffer, - vertex_pair_buffer + size, - e, - thrust::less>{}); - - - auto idx = thrust::distance(vertex_pair_buffer, it); - printf("\nthe distance = %d\n", idx); - - return 0; - } - ); - */ - + // Once done unrolling, sortby keys before doing a reduction and then masking the edges + } + } } } // namespace cugraph + + +/* + + auto first_ = thrust::get<0>(*edges_to_num_triangles); + auto last_ = thrust::get<0>(*edges_to_num_triangles); + + rmm::device_uvector rando(1, handle.get_stream()); + + printf("\nfilling\n"); + thrust::fill(handle.get_thrust_policy(), rando.begin(), rando.end(), thrust::get<0>(first_)); + print("\n Done filling\n"); + raft::print_device_vector("rando - src ", rando.data(), rando.size(), std::cout); +*/ \ No newline at end of file From 66ec45fdbfd37e070403e950e16522ab64bd806f Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 25 Jan 2024 08:13:10 -0800 Subject: [PATCH 025/155] unroll invalid edges --- cpp/src/community/k_truss_impl.cuh | 179 +++++++++++++++++++++-------- 1 file changed, 134 insertions(+), 45 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 77762d5ff67..a00bd5c8089 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -549,8 +549,8 @@ void k_truss(raft::handle_t const& handle, auto edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' - + // 'invalid_edge_first' marks the beginning of the edges to be removed auto invalid_edge_first = thrust::stable_partition( handle.get_thrust_policy(), edges_to_num_triangles, @@ -558,39 +558,162 @@ void k_truss(raft::handle_t const& handle, [k]__device__(auto e){ auto num_triangles = thrust::get<1>(e); auto is_in_k_truss = num_triangles == 2; - printf("\n is in k_truss = %d\n", is_in_k_truss); return num_triangles > k; // FIXME k-2 } ); - - // FIXME: rename it num_edges_to_remove + size_t num_invalid_edges{0}; num_invalid_edges = static_cast(thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); - printf("\nthe number of invalid edges = %d\n", num_invalid_edges); if (num_invalid_edges != 0) { // unroll and remove/mask edges + + // case 2: unroll (q, r) + + auto incoming_vertex_pairs_begin = thrust::make_zip_iterator( + edgelist_dsts.begin(), edgelist_srcs.begin()); + + // Sort the 'incoming_vertex_pairs_begin' + thrust::sort(handle.get_thrust_policy(), + incoming_vertex_pairs_begin, + incoming_vertex_pairs_begin + edgelist_srcs.size()); // FIXME: No need to partition + + // For each (q, r) edge to unroll, find the incoming edges to 'r' let's say from 'p' and + // create the pair (p, q) + + rmm::device_uvector prefix_sum(edgelist_srcs.size() + 1, handle.get_stream()); + thrust::tabulate(handle.get_thrust_policy(), + prefix_sum.begin(), + prefix_sum.end(), + [ + invalid_first = thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()), + incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + num_edges = edgelist_srcs.size() + ] + __device__(auto idx){ + auto src = thrust::get<0>(*(invalid_first + idx)); + auto dst = thrust::get<1>(*(invalid_first + idx)); + auto dst_array_begin = thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()); + auto dst_array_end = thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()) + num_edges; + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops + auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_upper = thrust::distance(dst_array_begin, itr_upper); + auto dist = thrust::distance(itr_lower, itr_upper); + return dist; + } ); + + thrust::exclusive_scan(handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + + raft::print_device_vector("p_sum", prefix_sum.data(), prefix_sum.size(), std::cout); + + auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + + auto vertex_pair_buffer_p_r = allocate_dataframe_buffer>( + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + + rmm::device_uvector indices(edgelist_srcs.size(), handle.get_stream()); + thrust::tabulate(handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); + + thrust::for_each( + handle.get_thrust_policy(), + indices.begin(), + indices.end(), + [invalid_first_dst = thrust::get<1>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), + invalid_first_src = thrust::get<0>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + num_edges = edgelist_srcs.size()] + __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_begin = invalid_first_dst; + auto dst_array_end = invalid_first_dst + num_edges; + + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // Need a binary search to find the begining of the range + + auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; + + thrust::tabulate(thrust::seq, + vertex_pair_buffer_p_q + prefix_sum[idx], + vertex_pair_buffer_p_q + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), src); + }); + + thrust::tabulate(thrust::seq, + vertex_pair_buffer_p_r + prefix_sum[idx], + vertex_pair_buffer_p_r + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), dst); + }); + + }); + + // sort the edges + thrust::sort(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_p_q), + thrust::less>{}); // FIXME: Check if you need to sort this + + auto edge_exists = cur_graph_view.has_edge( + handle, + raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), + raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size())); + + auto edge_to_edge_exists = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), edge_exists.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + + auto has_edge_last = thrust::stable_partition( + handle.get_thrust_policy(), + edge_to_edge_exists, + edge_to_edge_exists + edge_exists.size(), + []__device__(auto e){ + auto edge_exists = thrust::get<1>(e); + return edge_exists; + + } + ); // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) auto[intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()), // FIXME: use 'invalid_edge_first' instead as it marks the beginning of the edges to be unrolled, + thrust::get<0>(invalid_edge_first.get_iterator_tuple()), thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()) + num_triangles.size(), std::array{true, true}, do_expensive_check); RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove - - auto prev_size = size_dataframe_buffer(vertex_pair_buffer); auto new_size = prev_size + intersection_indices.size(); resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); + // resize num_triangles + num_triangles.resize(new_size, handle.get_stream()); + // Fill the newly allocated space with -1 + thrust::fill(handle.get_thrust_policy(), num_triangles.begin() + prev_size, num_triangles.end(), size_t{-1}); + + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' // Unroll (p, q) @@ -606,38 +729,18 @@ void k_truss(raft::handle_t const& handle, vertex_pairs_begin//thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()) }); - // resize num_triangles - num_triangles.resize(new_size, handle.get_stream()); - // Fill the newly allocated space with -1 - thrust::fill(handle.get_thrust_policy(), num_triangles.begin() + prev_size, num_triangles.end(), size_t{-1}); + prev_size = size_dataframe_buffer(vertex_pair_buffer); new_size = prev_size + intersection_indices.size(); resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); - - // 2) generating (q, r) - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size, - get_dataframe_buffer_begin(vertex_pair_buffer) + new_size, - generate_qr{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin - }); - // resize num_triangles num_triangles.resize(new_size, handle.get_stream()); // Fill the newly allocated space with -1 thrust::fill(handle.get_thrust_policy(), num_triangles.begin() + prev_size, num_triangles.end(), size_t{-1}); - raft::print_device_vector("new num_triangles q_r ", num_triangles.data(), num_triangles.size(), std::cout); - raft::print_device_vector("src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); - - - // Once done unrolling, sortby keys before doing a reduction and then masking the edges + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' } @@ -647,17 +750,3 @@ void k_truss(raft::handle_t const& handle, } } // namespace cugraph - - -/* - - auto first_ = thrust::get<0>(*edges_to_num_triangles); - auto last_ = thrust::get<0>(*edges_to_num_triangles); - - rmm::device_uvector rando(1, handle.get_stream()); - - printf("\nfilling\n"); - thrust::fill(handle.get_thrust_policy(), rando.begin(), rando.end(), thrust::get<0>(first_)); - print("\n Done filling\n"); - raft::print_device_vector("rando - src ", rando.data(), rando.size(), std::cout); -*/ \ No newline at end of file From 246ec165dcd85fcb2b3a120c0a67d4fccae68a53 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 25 Jan 2024 15:55:46 -0800 Subject: [PATCH 026/155] update edge unrolling --- cpp/src/community/k_truss_impl.cuh | 146 ++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 23 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index a00bd5c8089..902b486505f 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -416,8 +416,6 @@ void k_truss(raft::handle_t const& handle, vertex_pairs_begin + edgelist_srcs.size()); RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove - raft::print_device_vector("src ", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dst ", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); size_t num_vertex_pairs = edgelist_srcs.size(); auto out_degrees = cur_graph_view.compute_out_degrees(handle); @@ -567,7 +565,18 @@ void k_truss(raft::handle_t const& handle, num_invalid_edges = static_cast(thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); + size_t invalid_edge_start_idx{0}; + invalid_edge_start_idx = + static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); + + // copy invalid edges + auto invalid_edges_buffer = allocate_dataframe_buffer>( + num_invalid_edges, handle.get_stream()); + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer) + invalid_edge_start_idx, + get_dataframe_buffer_end(vertex_pair_buffer), + get_dataframe_buffer_begin(invalid_edges_buffer)); if (num_invalid_edges != 0) { // unroll and remove/mask edges @@ -608,8 +617,6 @@ void k_truss(raft::handle_t const& handle, thrust::exclusive_scan(handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - raft::print_device_vector("p_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -653,6 +660,7 @@ void k_truss(raft::handle_t const& handle, thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), src); }); + // Not memory efficient as it will consider all possible (p,r) thrust::tabulate(thrust::seq, vertex_pair_buffer_p_r + prefix_sum[idx], vertex_pair_buffer_p_r + prefix_sum[idx] + dist, @@ -667,24 +675,21 @@ void k_truss(raft::handle_t const& handle, }); - // sort the edges - thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_p_q), - thrust::less>{}); // FIXME: Check if you need to sort this - auto edge_exists = cur_graph_view.has_edge( handle, raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size())); - auto edge_to_edge_exists = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), edge_exists.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' - + auto edge_to_existance = thrust::make_zip_iterator( + thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), + edge_exists.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + auto has_edge_last = thrust::stable_partition( handle.get_thrust_policy(), - edge_to_edge_exists, - edge_to_edge_exists + edge_exists.size(), + edge_to_existance, + edge_to_existance + edge_exists.size(), []__device__(auto e){ auto edge_exists = thrust::get<1>(e); return edge_exists; @@ -692,6 +697,51 @@ void k_truss(raft::handle_t const& handle, } ); + auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); + + // Resize both 'vertex_pair_buffer' + resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); + resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); + + rmm::device_uvector decrease_num_triangles_p_q_p_r_tmp(2 * num_edge_exists, handle.get_stream()); + thrust::fill(handle.get_thrust_policy(), decrease_num_triangles_p_q_p_r_tmp.begin(), decrease_num_triangles_p_q_p_r_tmp.end(), size_t{-1}); + + auto vertex_pair_buffer_p_q_p_r_tmp = allocate_dataframe_buffer>( + 2 * num_edge_exists, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp)); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_r), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp) + num_edge_exists); + + thrust::sort(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); + + auto count_p_q_p_r = thrust::unique_count(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); + + rmm::device_uvector decrease_num_triangles_p_q_p_r(count_p_q_p_r, handle.get_stream()); + + auto vertex_pair_buffer_p_q_p_r = allocate_dataframe_buffer>( + count_p_q_p_r, handle.get_stream()); + + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp), + decrease_num_triangles_p_q_p_r_tmp.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), + decrease_num_triangles_p_q_p_r.begin(), + thrust::equal_to>{}); + + // FIXME: Pending case 3 and masking + // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) auto[intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, @@ -702,8 +752,6 @@ void k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove - auto prev_size = size_dataframe_buffer(vertex_pair_buffer); auto new_size = prev_size + intersection_indices.size(); resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); @@ -715,25 +763,26 @@ void k_truss(raft::handle_t const& handle, edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + // FIXME: When partitioning or resizing, the original vertex_pair_buffer or device array are modified. + // revisit all resizing and partitioning calls + // Unroll (p, q) - // 1) generating (p, r) + // generating (p, r) thrust::tabulate(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size, get_dataframe_buffer_begin(vertex_pair_buffer) + new_size, - generate_pr{ //FIXME: it is supposed to be of the type of the last argument + generate_pr{ raft::device_span( intersection_offsets.data(), intersection_offsets.size()), raft::device_span( intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin//thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()) + get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - - prev_size = size_dataframe_buffer(vertex_pair_buffer); new_size = prev_size + intersection_indices.size(); - resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); + resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); // FIXME: use 'new_size' instead // resize num_triangles num_triangles.resize(new_size, handle.get_stream()); // Fill the newly allocated space with -1 @@ -741,6 +790,57 @@ void k_truss(raft::handle_t const& handle, edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + + + // 2) generating (q, r) + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size, + get_dataframe_buffer_begin(vertex_pair_buffer) + new_size, + generate_qr{ + raft::device_span( + intersection_offsets.data(), intersection_offsets.size()), + raft::device_span( + intersection_indices.data(), intersection_indices.size()), + get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate + }); + + prev_size = size_dataframe_buffer(vertex_pair_buffer); + new_size = prev_size + count_p_q_p_r; + resize_dataframe_buffer(vertex_pair_buffer, new_size, handle.get_stream()); + // resize num_triangles + num_triangles.resize(new_size, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), + get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size); + + thrust::copy(handle.get_thrust_policy(), + decrease_num_triangles_p_q_p_r.begin(), + decrease_num_triangles_p_q_p_r.end(), + num_triangles.begin() + prev_size); + + thrust::sort_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + num_triangles.begin()); + + auto count = thrust::unique_count(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer)); + + auto vertex_pair_buffer_final = allocate_dataframe_buffer>( + count, handle.get_stream()); + + rmm::device_uvector num_triangles_final(count, handle.get_stream()); + + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + num_triangles.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_final), + num_triangles_final.begin(), + thrust::equal_to>{}); } From 028693439653097b381860d6f382b1235d7d9acb Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 26 Jan 2024 03:12:42 -0800 Subject: [PATCH 027/155] update edge unrolling --- cpp/src/community/k_truss_impl.cuh | 162 +++++++++++++++++++++++++++-- 1 file changed, 153 insertions(+), 9 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 902b486505f..47d587a3563 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -414,8 +414,7 @@ void k_truss(raft::handle_t const& handle, thrust::sort(handle.get_thrust_policy(), vertex_pairs_begin, vertex_pairs_begin + edgelist_srcs.size()); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove + size_t num_vertex_pairs = edgelist_srcs.size(); auto out_degrees = cur_graph_view.compute_out_degrees(handle); @@ -502,6 +501,11 @@ void k_truss(raft::handle_t const& handle, num_triangles.begin(), thrust::equal_to>{}); + // FIXME: Each edges in a triangle will have it number of triangle count decrease twice by its neighbor + // therefore double the number of triangle of each edges before unrolling the invalid edges. + thrust::transform(handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), num_triangles.begin(), []__device__(auto value){ + return value * 2; // FIXME k-2 + }); // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag // one of the void @@ -556,7 +560,7 @@ void k_truss(raft::handle_t const& handle, [k]__device__(auto e){ auto num_triangles = thrust::get<1>(e); auto is_in_k_truss = num_triangles == 2; - return num_triangles > k; // FIXME k-2 + return num_triangles > k * 2; // FIXME (k-2) * 2 } ); @@ -567,7 +571,7 @@ void k_truss(raft::handle_t const& handle, size_t invalid_edge_start_idx{0}; invalid_edge_start_idx = - static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); + static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); // copy invalid edges auto invalid_edges_buffer = allocate_dataframe_buffer>( @@ -685,7 +689,7 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), edge_exists.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' - + auto has_edge_last = thrust::stable_partition( handle.get_thrust_policy(), edge_to_existance, @@ -722,7 +726,7 @@ void k_truss(raft::handle_t const& handle, thrust::sort(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); - + auto count_p_q_p_r = thrust::unique_count(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); @@ -740,7 +744,131 @@ void k_truss(raft::handle_t const& handle, decrease_num_triangles_p_q_p_r.begin(), thrust::equal_to>{}); - // FIXME: Pending case 3 and masking + + // case 3: unroll (p, r) + vertex_pair_buffer_p_q = allocate_dataframe_buffer>( + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + + auto vertex_pair_buffer_q_r = allocate_dataframe_buffer>( + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + + thrust::for_each( + handle.get_thrust_policy(), + indices.begin(), + indices.end(), + [invalid_first_dst = thrust::get<1>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), + invalid_first_src = thrust::get<0>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + num_edges = edgelist_srcs.size()] + __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_begin = invalid_first_dst; + auto dst_array_end = invalid_first_dst + num_edges; + + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // Need a binary search to find the begining of the range + + auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; + + thrust::tabulate(thrust::seq, + vertex_pair_buffer_p_q + prefix_sum[idx], + vertex_pair_buffer_p_q + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + + return thrust::make_tuple( + src, thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment))); + }); + + // Not memory efficient as it will consider all possible (p,r) + thrust::tabulate(thrust::seq, + vertex_pair_buffer_q_r + prefix_sum[idx], + vertex_pair_buffer_q_r + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), dst); + }); + + }); + + + + edge_exists = cur_graph_view.has_edge( + handle, + raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), + raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size())); + + edge_to_existance = thrust::make_zip_iterator( + thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), + edge_exists.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + + has_edge_last = thrust::stable_partition( + handle.get_thrust_policy(), + edge_to_existance, + edge_to_existance + edge_exists.size(), + []__device__(auto e){ + auto edge_exists = thrust::get<1>(e); + return edge_exists; + + } + ); + + num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); + + // Resize both 'vertex_pair_buffer' + resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); // disregard the second partition + resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); + + rmm::device_uvector decrease_num_triangles_p_q_q_r_tmp(2 * num_edge_exists, handle.get_stream()); + thrust::fill(handle.get_thrust_policy(), decrease_num_triangles_p_q_q_r_tmp.begin(), decrease_num_triangles_p_q_q_r_tmp.end(), size_t{-1}); + + auto vertex_pair_buffer_p_q_q_r_tmp = allocate_dataframe_buffer>( + 2 * num_edge_exists, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp)); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_q_r), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp) + num_edge_exists); + + thrust::sort(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); + + auto count_p_q_q_r = thrust::unique_count(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); + + rmm::device_uvector decrease_num_triangles_p_q_q_r(count_p_q_q_r, handle.get_stream()); + + auto vertex_pair_buffer_p_q_q_r = allocate_dataframe_buffer>( + count_p_q_q_r, handle.get_stream()); + + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp), + decrease_num_triangles_p_q_q_r_tmp.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), + decrease_num_triangles_p_q_q_r.begin(), + thrust::equal_to>{}); + + // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) auto[intersection_offsets, intersection_indices] = @@ -803,9 +931,9 @@ void k_truss(raft::handle_t const& handle, intersection_indices.data(), intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - + prev_size = size_dataframe_buffer(vertex_pair_buffer); - new_size = prev_size + count_p_q_p_r; + new_size = prev_size + count_p_q_p_r + count_p_q_q_r; resize_dataframe_buffer(vertex_pair_buffer, new_size, handle.get_stream()); // resize num_triangles num_triangles.resize(new_size, handle.get_stream()); @@ -815,20 +943,35 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size); + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), + get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size + count_p_q_p_r); + thrust::copy(handle.get_thrust_policy(), decrease_num_triangles_p_q_p_r.begin(), decrease_num_triangles_p_q_p_r.end(), num_triangles.begin() + prev_size); + + thrust::copy(handle.get_thrust_policy(), + decrease_num_triangles_p_q_q_r.begin(), + decrease_num_triangles_p_q_q_r.end(), + num_triangles.begin() + prev_size + count_p_q_p_r); thrust::sort_by_key(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer), get_dataframe_buffer_end(vertex_pair_buffer), num_triangles.begin()); + + + auto count = thrust::unique_count(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer), get_dataframe_buffer_end(vertex_pair_buffer)); + + auto vertex_pair_buffer_final = allocate_dataframe_buffer>( count, handle.get_stream()); @@ -842,6 +985,7 @@ void k_truss(raft::handle_t const& handle, num_triangles_final.begin(), thrust::equal_to>{}); + } From 98b09cf4d35eeb049c19ca4b2d78822e2172a087 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 29 Jan 2024 06:34:23 -0800 Subject: [PATCH 028/155] update edge unrolling --- cpp/src/community/k_truss_impl.cuh | 528 ++++++++++++++++++----------- 1 file changed, 325 insertions(+), 203 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 47d587a3563..6d38a5fbed8 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -398,6 +398,7 @@ void k_truss(raft::handle_t const& handle, { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + std::optional> renumber_map{std::nullopt}; rmm::device_uvector edgelist_srcs(0, handle.get_stream()); rmm::device_uvector edgelist_dsts(0, handle.get_stream()); @@ -415,14 +416,9 @@ void k_truss(raft::handle_t const& handle, vertex_pairs_begin, vertex_pairs_begin + edgelist_srcs.size()); - size_t num_vertex_pairs = edgelist_srcs.size(); auto out_degrees = cur_graph_view.compute_out_degrees(handle); - rmm::device_uvector r_nbr_intersection_property_values0(size_t{0}, handle.get_stream()); // FIXME: remove as it is not used - rmm::device_uvector r_nbr_intersection_property_values1(size_t{0}, handle.get_stream()); // FIXME: remove as it is not used - - // FIXME: Initially each edge should have an edge property of 0 auto[intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, @@ -431,8 +427,6 @@ void k_truss(raft::handle_t const& handle, vertex_pairs_begin + num_vertex_pairs, std::array{true, true}, do_expensive_check); - - RAFT_CUDA_TRY(cudaDeviceSynchronize()); // FIXME: Only for debugging purposes, remove auto vertex_pair_buffer = allocate_dataframe_buffer>( num_vertex_pairs, handle.get_stream()); @@ -492,7 +486,6 @@ void k_truss(raft::handle_t const& handle, rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); - // FIXME: Does a reduce by key preserve the sorting? I believe 'YES" thrust::reduce_by_key(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_tmp), get_dataframe_buffer_end(vertex_pair_buffer_tmp), @@ -501,56 +494,9 @@ void k_truss(raft::handle_t const& handle, num_triangles.begin(), thrust::equal_to>{}); - // FIXME: Each edges in a triangle will have it number of triangle count decrease twice by its neighbor - // therefore double the number of triangle of each edges before unrolling the invalid edges. - thrust::transform(handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), num_triangles.begin(), []__device__(auto value){ - return value * 2; // FIXME k-2 - }); - - // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag - // one of the void - cugraph::edge_bucket_tedge_list(handle); - - edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).end(), - // num triangle here - std::get<1>(vertex_pair_buffer).begin()); - - cugraph::edge_property_t edge_value_output(handle, - cur_graph_view); - thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - thrust::less>{}); // FIXME: Isn't this sorting redunddant? - - // FIXME: shuffle before sorting and reducing - Bring this part up - if constexpr (multi_gpu) { - thrust::copy(handle.get_thrust_policy(), - std::get<0>(vertex_pair_buffer).data(), - std::get<0>(vertex_pair_buffer).data() + std::get<0>(vertex_pair_buffer).size(), - edgelist_srcs.begin()); - - thrust::copy(handle.get_thrust_policy(), - std::get<1>(vertex_pair_buffer).data(), - std::get<1>(vertex_pair_buffer).data() + std::get<1>(vertex_pair_buffer).size(), - edgelist_dsts.begin()); - - edgelist_srcs.resize(std::get<0>(vertex_pair_buffer).size(), handle.get_stream()); - edgelist_dsts.resize(std::get<0>(vertex_pair_buffer).size(), handle.get_stream()); - - std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore, std::ignore) = - detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning( - handle, std::move(edgelist_srcs), std::move(edgelist_dsts), std::nullopt, std::nullopt, std::nullopt); - } - - // Run thrust::stable_partition to place the edges with triangle counts smaller than K-2 at the end - // zip iterator for the pair auto edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // 'invalid_edge_first' marks the beginning of the edges to be removed auto invalid_edge_first = thrust::stable_partition( @@ -560,7 +506,7 @@ void k_truss(raft::handle_t const& handle, [k]__device__(auto e){ auto num_triangles = thrust::get<1>(e); auto is_in_k_truss = num_triangles == 2; - return num_triangles > k * 2; // FIXME (k-2) * 2 + return num_triangles > k; // FIXME (k-2) * 2 } ); @@ -571,7 +517,8 @@ void k_truss(raft::handle_t const& handle, size_t invalid_edge_start_idx{0}; invalid_edge_start_idx = - static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); + static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); + // copy invalid edges auto invalid_edges_buffer = allocate_dataframe_buffer>( @@ -582,35 +529,38 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_end(vertex_pair_buffer), get_dataframe_buffer_begin(invalid_edges_buffer)); + + if (num_invalid_edges != 0) { // unroll and remove/mask edges + // case 2: unroll (q, r) auto incoming_vertex_pairs_begin = thrust::make_zip_iterator( edgelist_dsts.begin(), edgelist_srcs.begin()); - // Sort the 'incoming_vertex_pairs_begin' + vertex_t num_incomming_vertex_pairs = edgelist_srcs.size(); + + // Sort the 'incoming_vertex_pairs_begin' by 'dst' thrust::sort(handle.get_thrust_policy(), incoming_vertex_pairs_begin, incoming_vertex_pairs_begin + edgelist_srcs.size()); // FIXME: No need to partition // For each (q, r) edge to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - rmm::device_uvector prefix_sum(edgelist_srcs.size() + 1, handle.get_stream()); thrust::tabulate(handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), [ - invalid_first = thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()), - incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - num_edges = edgelist_srcs.size() + invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), + dst_array_begin = edgelist_dsts.begin(), + num_edges = num_incomming_vertex_pairs ] __device__(auto idx){ auto src = thrust::get<0>(*(invalid_first + idx)); auto dst = thrust::get<1>(*(invalid_first + idx)); - auto dst_array_begin = thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()); - auto dst_array_end = thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()) + num_edges; + auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); @@ -634,8 +584,8 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), indices.begin(), indices.end(), - [invalid_first_dst = thrust::get<1>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), - invalid_first_src = thrust::get<0>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), + [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), + invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), prefix_sum = prefix_sum.data(), incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), @@ -663,8 +613,7 @@ void k_truss(raft::handle_t const& handle, return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), src); }); - - // Not memory efficient as it will consider all possible (p,r) + thrust::tabulate(thrust::seq, vertex_pair_buffer_p_r + prefix_sum[idx], vertex_pair_buffer_p_r + prefix_sum[idx] + dist, @@ -679,6 +628,7 @@ void k_truss(raft::handle_t const& handle, }); + auto edge_exists = cur_graph_view.has_edge( handle, raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), @@ -688,8 +638,9 @@ void k_truss(raft::handle_t const& handle, thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), - edge_exists.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + edge_exists.begin()); + auto has_edge_last = thrust::stable_partition( handle.get_thrust_policy(), edge_to_existance, @@ -703,12 +654,15 @@ void k_truss(raft::handle_t const& handle, auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); - // Resize both 'vertex_pair_buffer' + // After pushing the non-existant edges to the second partition, + // remove them by resizing both vertex pair buffer resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); rmm::device_uvector decrease_num_triangles_p_q_p_r_tmp(2 * num_edge_exists, handle.get_stream()); - thrust::fill(handle.get_thrust_policy(), decrease_num_triangles_p_q_p_r_tmp.begin(), decrease_num_triangles_p_q_p_r_tmp.end(), size_t{-1}); + thrust::fill(handle.get_thrust_policy(), + decrease_num_triangles_p_q_p_r_tmp.begin(), + decrease_num_triangles_p_q_p_r_tmp.end(), size_t{-1}); auto vertex_pair_buffer_p_q_p_r_tmp = allocate_dataframe_buffer>( 2 * num_edge_exists, handle.get_stream()); @@ -722,20 +676,25 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_r), get_dataframe_buffer_end(vertex_pair_buffer_p_r), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp) + num_edge_exists); - + thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); - + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp) + ); // FIXME: Remove duplicated edges + + // FIXME: No need for a count if we do only one reduce_by_key at the end + // (vertex_pair_buffer_p_q_p_r + vertex_pair_buffer) Because the reduction + // of both will lead to a pair_buffer of size size_of(vertex_pair_buffer) + // Also no need for a tmp buffer 'vertex_pair_buffer_p_q_p_r_tmp' auto count_p_q_p_r = thrust::unique_count(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); - rmm::device_uvector decrease_num_triangles_p_q_p_r(count_p_q_p_r, handle.get_stream()); auto vertex_pair_buffer_p_q_p_r = allocate_dataframe_buffer>( count_p_q_p_r, handle.get_stream()); - + rmm::device_uvector decrease_num_triangles_p_q_p_r(count_p_q_p_r, handle.get_stream()); + thrust::reduce_by_key(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp), @@ -743,9 +702,137 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), decrease_num_triangles_p_q_p_r.begin(), thrust::equal_to>{}); + + // Add edges from vertex_pair_buffer + edge_t prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_p_r); + edge_t accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; + + resize_dataframe_buffer(vertex_pair_buffer_p_q_p_r, accumulate_pair_size, handle.get_stream()); + decrease_num_triangles_p_q_p_r.resize(accumulate_pair_size, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r) + prev_size); + + thrust::copy(handle.get_thrust_policy(), + num_triangles.begin(), + num_triangles.end(), + decrease_num_triangles_p_q_p_r.begin() + prev_size); + + thrust::sort_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), + decrease_num_triangles_p_q_p_r.begin()); + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), + decrease_num_triangles_p_q_p_r.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer), + num_triangles.begin(), + thrust::equal_to>{}); + + + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + edge_t num_edges = size_dataframe_buffer(vertex_pair_buffer); + + auto edges_to_num_triangles_last = thrust::stable_partition( + handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_edges, + []__device__(auto edge_to_num_triangles){ + auto edge_exists = thrust::get<1>(edge_to_num_triangles); + return edge_exists; + + } + ); + + auto last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_last); + + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + + // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag + cugraph::edge_bucket_tedge_list(handle); + + cugraph::edge_property_t edge_value_output(handle, + cur_graph_view); + edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + std::get<1>(vertex_pair_buffer).begin()); + + cugraph::transform_e( + handle, + cur_graph_view, + edge_list, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + [] + __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + return true; + }, + edge_value_output.mutable_view(), + false); + + cur_graph_view.attach_edge_mask(edge_value_output.view()); + + // resize the 'edgelist_srcs' and 'edgelsit_dst' + edgelist_srcs.resize(last_edge_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_idx, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_srcs.begin()); + + thrust::copy(handle.get_thrust_policy(), + std::get<1>(vertex_pair_buffer).begin(), + std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_dsts.begin()); + + // Get the new pair of incoming edges + incoming_vertex_pairs_begin = thrust::make_zip_iterator( + edgelist_dsts.begin(), edgelist_srcs.begin()); + + // Need to run prefix_sum again to get new ranges because some incoming edges were removed - // case 3: unroll (p, r) + num_incomming_vertex_pairs = edgelist_srcs.size(); + prefix_sum.resize(num_incomming_vertex_pairs + 1, handle.get_stream()); + + // FIXME: need to sort 'incoming_vertex_pairs_begin'. N0 need because a stable partition was performed that preserve the sorting + + thrust::tabulate(handle.get_thrust_policy(), + prefix_sum.begin(), + prefix_sum.end(), + [ + invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), + incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst_array_begin = edgelist_dsts.begin(), + src_array_begin = edgelist_srcs.begin(), + num_edges = num_incomming_vertex_pairs + ] + __device__(auto idx){ + auto src = thrust::get<0>(*(invalid_first + idx)); + auto dst = thrust::get<1>(*(invalid_first + idx)); + auto dst_array_end = thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()) + num_edges; + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops + auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_upper = thrust::distance(dst_array_begin, itr_upper); + auto dist = thrust::distance(itr_lower, itr_upper); + return dist; + } ); + + thrust::exclusive_scan(handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + + + // case 3 unroll (p, r) + vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -756,8 +843,8 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), indices.begin(), indices.end(), - [invalid_first_dst = thrust::get<1>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), - invalid_first_src = thrust::get<0>(thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()).get_iterator_tuple()), + [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), + invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), prefix_sum = prefix_sum.data(), incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), @@ -768,10 +855,8 @@ void k_truss(raft::handle_t const& handle, auto dst = invalid_first_dst[idx]; auto dst_array_begin = invalid_first_dst; auto dst_array_end = invalid_first_dst + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; thrust::tabulate(thrust::seq, @@ -785,8 +870,7 @@ void k_truss(raft::handle_t const& handle, return thrust::make_tuple( src, thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment))); }); - - // Not memory efficient as it will consider all possible (p,r) + thrust::tabulate(thrust::seq, vertex_pair_buffer_q_r + prefix_sum[idx], vertex_pair_buffer_q_r + prefix_sum[idx] + dist, @@ -801,20 +885,21 @@ void k_truss(raft::handle_t const& handle, }); - - + edge_exists = cur_graph_view.has_edge( handle, raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size())); - + + edge_to_existance = thrust::make_zip_iterator( thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), - edge_exists.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' - - has_edge_last = thrust::stable_partition( + edge_exists.begin()); + + + auto edges_to_num_triangles_p_r_last = thrust::stable_partition( handle.get_thrust_policy(), edge_to_existance, edge_to_existance + edge_exists.size(), @@ -824,15 +909,19 @@ void k_truss(raft::handle_t const& handle, } ); - - num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); + num_edge_exists = thrust::distance(edge_to_existance, edges_to_num_triangles_p_r_last); + // Resize both 'vertex_pair_buffer' - resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); // disregard the second partition + resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); + rmm::device_uvector decrease_num_triangles_p_q_q_r_tmp(2 * num_edge_exists, handle.get_stream()); - thrust::fill(handle.get_thrust_policy(), decrease_num_triangles_p_q_q_r_tmp.begin(), decrease_num_triangles_p_q_q_r_tmp.end(), size_t{-1}); + thrust::fill(handle.get_thrust_policy(), + decrease_num_triangles_p_q_q_r_tmp.begin(), + decrease_num_triangles_p_q_q_r_tmp.end(), + size_t{-1}); auto vertex_pair_buffer_p_q_q_r_tmp = allocate_dataframe_buffer>( 2 * num_edge_exists, handle.get_stream()); @@ -848,146 +937,179 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp) + num_edge_exists); thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); - - auto count_p_q_q_r = thrust::unique_count(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp) + ); // FIXME: Remove duplicated edges - rmm::device_uvector decrease_num_triangles_p_q_q_r(count_p_q_q_r, handle.get_stream()); + // FIXME: No need for a count if we do only one reduce_by_key at the end (vertex_pair_buffer_p_q_q_r + vertex_pair_buffer) + // Because the reduction of both will lead to a pair_buffer of size size_of(vertex_pair_buffer) + // Also no need for a tmp buffer 'vertex_pair_buffer_p_q_q_r_tmp' + auto count_p_q_q_r = thrust::unique_count(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); - auto vertex_pair_buffer_p_q_q_r = allocate_dataframe_buffer>( - count_p_q_q_r, handle.get_stream()); + auto vertex_pair_buffer_p_q_q_r = allocate_dataframe_buffer>( + count_p_q_q_r, handle.get_stream()); + rmm::device_uvector decrease_num_triangles_p_q_q_r(count_p_q_q_r, handle.get_stream()); - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp), - decrease_num_triangles_p_q_q_r_tmp.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), - decrease_num_triangles_p_q_q_r.begin(), - thrust::equal_to>{}); + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp), + decrease_num_triangles_p_q_q_r_tmp.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), + decrease_num_triangles_p_q_q_r.begin(), + thrust::equal_to>{}); + + // Add edges from vertex_pair_buffer + prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_q_r); + accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; + + resize_dataframe_buffer(vertex_pair_buffer_p_q_q_r, accumulate_pair_size, handle.get_stream()); + decrease_num_triangles_p_q_q_r.resize(accumulate_pair_size, handle.get_stream()); + + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r) + prev_size); + + thrust::copy(handle.get_thrust_policy(), + num_triangles.begin(), + num_triangles.end(), + decrease_num_triangles_p_q_q_r.begin() + prev_size); + + thrust::sort_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), + decrease_num_triangles_p_q_q_r.begin()); + + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), + decrease_num_triangles_p_q_q_r.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer), + num_triangles.begin(), + thrust::equal_to>{}); + + + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + num_edges = size_dataframe_buffer(vertex_pair_buffer); + + // FIXME: This variable cannot be reassigned. rename them appropriately + auto edges_to_num_triangles_p_r_last_ = thrust::stable_partition( + handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_edges, + []__device__(auto edge_to_num_triangles){ + auto edge_exists = thrust::get<1>(edge_to_num_triangles); + return edge_exists; + } + ); + + last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_p_r_last_); + + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag + // NOTE: This needs to be a seperate variable 'edge_list' + //cugraph::edge_bucket_tedge_list(handle); + // FIXME: seem + edge_list.clear(); // FIXME: is this needed? + + cugraph::edge_property_t edge_value_output_p_r(handle, + cur_graph_view); - + edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + std::get<1>(vertex_pair_buffer).begin()); + + cugraph::transform_e( + handle, + cur_graph_view, + edge_list, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + [] + __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + return true; + }, + edge_value_output_p_r.mutable_view(), + false); + + cur_graph_view.attach_edge_mask(edge_value_output_p_r.view()); + + // resize the 'edgelist_srcs' and 'edgelsit_dst' + edgelist_srcs.resize(last_edge_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_idx, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_srcs.begin()); + + thrust::copy(handle.get_thrust_policy(), + std::get<1>(vertex_pair_buffer).begin(), + std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_dsts.begin()); + + // Get the new pair of incoming edges + incoming_vertex_pairs_begin = thrust::make_zip_iterator( + edgelist_dsts.begin(), edgelist_srcs.begin()); + // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) + // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' + // which contains the ordering with the number of triangles. auto[intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - thrust::get<0>(invalid_edge_first.get_iterator_tuple()), - thrust::get<0>(edges_to_num_triangles.get_iterator_tuple()) + num_triangles.size(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), std::array{true, true}, do_expensive_check); - auto prev_size = size_dataframe_buffer(vertex_pair_buffer); - auto new_size = prev_size + intersection_indices.size(); - resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); - // resize num_triangles - num_triangles.resize(new_size, handle.get_stream()); - // Fill the newly allocated space with -1 - thrust::fill(handle.get_thrust_policy(), num_triangles.begin() + prev_size, num_triangles.end(), size_t{-1}); - - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' - - // FIXME: When partitioning or resizing, the original vertex_pair_buffer or device array are modified. - // revisit all resizing and partitioning calls - - // Unroll (p, q) - // generating (p, r) + edge_t vertex_pair_buffer_p_r_edge_p_q_size = intersection_indices.size(); // rename this var as accumulate_pair_size + rmm::device_uvector num_triangles_p_r(vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); // FIXME: Rename this + thrust::fill(handle.get_thrust_policy(), num_triangles_p_r.begin(), num_triangles_p_r.end(), size_t{-1}); + auto vertex_pair_buffer_p_r_edge_p_q = allocate_dataframe_buffer>( + vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); + thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size, - get_dataframe_buffer_begin(vertex_pair_buffer) + new_size, + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q) + vertex_pair_buffer_p_r_edge_p_q_size, generate_pr{ raft::device_span( intersection_offsets.data(), intersection_offsets.size()), raft::device_span( intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate + get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate }); - prev_size = size_dataframe_buffer(vertex_pair_buffer); - new_size = prev_size + intersection_indices.size(); - resize_dataframe_buffer(vertex_pair_buffer, prev_size + intersection_indices.size(), handle.get_stream()); // FIXME: use 'new_size' instead - // resize num_triangles - num_triangles.resize(new_size, handle.get_stream()); - // Fill the newly allocated space with -1 - thrust::fill(handle.get_thrust_policy(), num_triangles.begin() + prev_size, num_triangles.end(), size_t{-1}); - - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); // FIXME: Might want to rezip it after resizing the 'vertex_pair_buffer' + // generating (q, r) + edge_t vertex_pair_buffer_q_r_edge_p_q_size = intersection_indices.size(); // rename this var as accumulate_pair_size + rmm::device_uvector num_triangles_q_r(vertex_pair_buffer_q_r_edge_p_q_size, handle.get_stream()); // FIXME: Rename this + thrust::fill(handle.get_thrust_policy(), num_triangles_q_r.begin(), num_triangles_q_r.end(), size_t{-1}); + auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>( + vertex_pair_buffer_q_r_edge_p_q_size, handle.get_stream()); - - // 2) generating (q, r) thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size, - get_dataframe_buffer_begin(vertex_pair_buffer) + new_size, + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + vertex_pair_buffer_q_r_edge_p_q_size, generate_qr{ raft::device_span( intersection_offsets.data(), intersection_offsets.size()), raft::device_span( intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate + get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate }); - prev_size = size_dataframe_buffer(vertex_pair_buffer); - new_size = prev_size + count_p_q_p_r + count_p_q_q_r; - resize_dataframe_buffer(vertex_pair_buffer, new_size, handle.get_stream()); - // resize num_triangles - num_triangles.resize(new_size, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), - get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), - get_dataframe_buffer_begin(vertex_pair_buffer) + prev_size + count_p_q_p_r); - - thrust::copy(handle.get_thrust_policy(), - decrease_num_triangles_p_q_p_r.begin(), - decrease_num_triangles_p_q_p_r.end(), - num_triangles.begin() + prev_size); - - thrust::copy(handle.get_thrust_policy(), - decrease_num_triangles_p_q_q_r.begin(), - decrease_num_triangles_p_q_q_r.end(), - num_triangles.begin() + prev_size + count_p_q_p_r); - - thrust::sort_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - num_triangles.begin()); - - - - - auto count = thrust::unique_count(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer)); - - - - auto vertex_pair_buffer_final = allocate_dataframe_buffer>( - count, handle.get_stream()); - - rmm::device_uvector num_triangles_final(count, handle.get_stream()); - - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - num_triangles.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer_final), - num_triangles_final.begin(), - thrust::equal_to>{}); - - } - } From e3545654b939a59bd77161fbbefcf3fb719a1a1c Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 29 Jan 2024 06:36:33 -0800 Subject: [PATCH 029/155] fix style --- cpp/include/cugraph/algorithms.hpp | 6 +- cpp/src/community/k_truss_impl.cuh | 1038 ++++++++++++++-------------- cpp/src/community/k_truss_sg.cu | 13 +- 3 files changed, 525 insertions(+), 532 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 52c44acd36a..9be7d714c14 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -2058,9 +2058,9 @@ void triangle_count(raft::handle_t const& handle, */ template void k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - edge_t k, - bool do_expensive_check = false); + graph_view_t const& graph_view, + edge_t k, + bool do_expensive_check = false); /** * @brief Compute Jaccard similarity coefficient diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 6d38a5fbed8..2ef581006b3 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -16,12 +16,12 @@ #pragma once // FIXME: remove all unused imports +#include #include -#include -#include #include -#include #include +#include +#include #include #include @@ -60,13 +60,17 @@ struct exclude_self_loop_t { template struct in_k_plus_one_or_greater_t { edge_t k{}; - __device__ bool operator()(edge_t core_number) const {return core_number >= k+1; } + __device__ bool operator()(edge_t core_number) const { return core_number >= k + 1; } }; template struct extract_k_plus_one_core_t { __device__ thrust::optional> operator()( - vertex_t src, vertex_t dst, bool src_in_k_plus_one_core, bool dst_in_k_plus_one_core, thrust::nullopt_t) const + vertex_t src, + vertex_t dst, + bool src_in_k_plus_one_core, + bool dst_in_k_plus_one_core, + thrust::nullopt_t) const { return (src_in_k_plus_one_core && dst_in_k_plus_one_core) ? thrust::optional>{thrust::make_tuple(src, dst)} @@ -92,7 +96,6 @@ struct extract_low_to_high_degree_edges_t { } }; - template struct extract_p_q { raft::device_span intersection_offsets{}; @@ -102,14 +105,14 @@ struct extract_p_q { __device__ thrust::tuple operator()(edge_t i) const { - auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); + auto itr = thrust::upper_bound( + thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); thrust::tuple pair = *(vertex_pairs_begin + idx); - return pair; + return pair; } }; - template struct extract_p_r { raft::device_span intersection_offsets{}; @@ -117,19 +120,18 @@ struct extract_p_r { VertexPairIterator vertex_pairs_begin; - __device__ thrust::tuple operator()(edge_t i) const { - auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + auto itr = thrust::upper_bound( + thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = + thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); return pair; } }; - - template struct extract_q_r { raft::device_span intersection_offsets{}; @@ -138,15 +140,16 @@ struct extract_q_r { __device__ thrust::tuple operator()(edge_t i) const { - auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + auto itr = thrust::upper_bound( + thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = + thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); return pair; } }; - template struct generate_pr { raft::device_span intersection_offsets{}; @@ -156,15 +159,16 @@ struct generate_pr { __device__ thrust::tuple operator()(edge_t i) const { - auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - auto pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + auto itr = thrust::upper_bound( + thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = + thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); return pair; } }; - template struct generate_qr { raft::device_span intersection_offsets{}; @@ -174,15 +178,16 @@ struct generate_qr { __device__ thrust::tuple operator()(edge_t i) const { - auto itr = thrust::upper_bound(thrust::seq, intersection_offsets.begin()+1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin()+1, itr); - auto pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + auto itr = thrust::upper_bound( + thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = + thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); return pair; } }; - template struct intersection_op_t { __device__ thrust::tuple operator()( @@ -199,14 +204,13 @@ struct intersection_op_t { } }; - -} // namespace +} // namespace template void k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - edge_t k, - bool do_expensive_check) + graph_view_t const& graph_view, + edge_t k, + bool do_expensive_check) { using weight_t = float; // dummy @@ -214,17 +218,14 @@ void k_truss(raft::handle_t const& handle, CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); - CUGRAPH_EXPECTS( - graph_view.is_symmetric(), - "Invalid input arguments: K-truss currently supports undirected graphs only."); - CUGRAPH_EXPECTS( - !graph_view.is_multigraph(), - "Invalid input arguments: K-truss currently does not support multi-graphs."); - + CUGRAPH_EXPECTS(graph_view.is_symmetric(), + "Invalid input arguments: K-truss currently supports undirected graphs only."); + CUGRAPH_EXPECTS(!graph_view.is_multigraph(), + "Invalid input arguments: K-truss currently does not support multi-graphs."); + if (do_expensive_check) { // nothing to do } - // 2. Exclude self-loops (FIXME: better mask-out once we add masking support). @@ -264,8 +265,8 @@ void k_truss(raft::handle_t const& handle, modified_graph_view = (*modified_graph).view(); } - // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core (FIXME: better mask-out once we - // add masking support). + // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core (FIXME: better mask-out + // once we add masking support). { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; @@ -276,13 +277,17 @@ void k_truss(raft::handle_t const& handle, rmm::device_uvector core_numbers(cur_graph_view.local_vertex_partition_range_size(), handle.get_stream()); - core_number( - handle, cur_graph_view, core_numbers.data(), k_core_degree_type_t::OUT, size_t{k+1}, size_t{k+1}); - - edge_src_property_t edge_src_in_k_plus_one_cores(handle, - cur_graph_view); - edge_dst_property_t edge_dst_in_k_plus_one_cores(handle, - cur_graph_view); + core_number(handle, + cur_graph_view, + core_numbers.data(), + k_core_degree_type_t::OUT, + size_t{k + 1}, + size_t{k + 1}); + + edge_src_property_t edge_src_in_k_plus_one_cores( + handle, cur_graph_view); + edge_dst_property_t edge_dst_in_k_plus_one_cores( + handle, cur_graph_view); auto in_k_plus_one_core_first = thrust::make_transform_iterator(core_numbers.begin(), in_k_plus_one_or_greater_t{k}); rmm::device_uvector in_k_plus_one_core_flags(core_numbers.size(), handle.get_stream()); @@ -392,7 +397,7 @@ void k_truss(raft::handle_t const& handle, } renumber_map = std::move(tmp_renumber_map); } - + // 5. Decompress the resulting graph to an edge list and ind intersection of edge endpoints // for each partition using detail::nbr_intersection @@ -403,23 +408,22 @@ void k_truss(raft::handle_t const& handle, rmm::device_uvector edgelist_dsts(0, handle.get_stream()); std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); - auto vertex_pairs_begin = thrust::make_zip_iterator( - edgelist_srcs.begin(), edgelist_dsts.begin()); - - thrust::sort(handle.get_thrust_policy(), - vertex_pairs_begin, - vertex_pairs_begin + edgelist_srcs.size()); + auto vertex_pairs_begin = + thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + + thrust::sort( + handle.get_thrust_policy(), vertex_pairs_begin, vertex_pairs_begin + edgelist_srcs.size()); size_t num_vertex_pairs = edgelist_srcs.size(); - auto out_degrees = cur_graph_view.compute_out_degrees(handle); + auto out_degrees = cur_graph_view.compute_out_degrees(handle); - auto[intersection_offsets, intersection_indices] = + auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), @@ -429,60 +433,55 @@ void k_truss(raft::handle_t const& handle, do_expensive_check); auto vertex_pair_buffer = allocate_dataframe_buffer>( - num_vertex_pairs, handle.get_stream()); + num_vertex_pairs, handle.get_stream()); - // stores all the pairs (p, q), (p, r) and (q, r) auto vertex_pair_buffer_tmp = allocate_dataframe_buffer>( - intersection_indices.size() * 3, handle.get_stream()); + intersection_indices.size() * 3, handle.get_stream()); // FIXME: optmize this part to not have to iterate over all the edges again - // tabulate with the size of intersection_indices, and call binary search on intersection_offsets - // to get (p, q). - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), - extract_p_q{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin - }); - - // tabulate with the size of intersection_indices, and call binary search on intersection_offsets - // to get (p, r). - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), - extract_p_r{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin - }); - - // tabulate with the size of intersection_indices, and call binary search on intersection_offsets - // to get (q, r). - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (3 * intersection_indices.size()), - extract_q_r{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin - }); + // tabulate with the size of intersection_indices, and call binary search on + // intersection_offsets to get (p, q). + thrust::tabulate( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), + extract_p_q{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin}); + + // tabulate with the size of intersection_indices, and call binary search on + // intersection_offsets to get (p, r). + thrust::tabulate( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), + extract_p_r{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin}); + + // tabulate with the size of intersection_indices, and call binary search on + // intersection_offsets to get (q, r). + thrust::tabulate( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (3 * intersection_indices.size()), + extract_q_r{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + vertex_pairs_begin}); thrust::sort(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_tmp), get_dataframe_buffer_end(vertex_pair_buffer_tmp)); - - rmm::device_uvector num_triangles_tmp(3 * intersection_indices.size(), handle.get_stream()); - thrust::fill(handle.get_thrust_policy(), num_triangles_tmp.begin(), num_triangles_tmp.end(), size_t{1}); + rmm::device_uvector num_triangles_tmp(3 * intersection_indices.size(), + handle.get_stream()); + + thrust::fill( + handle.get_thrust_policy(), num_triangles_tmp.begin(), num_triangles_tmp.end(), size_t{1}); rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); @@ -494,277 +493,269 @@ void k_truss(raft::handle_t const& handle, num_triangles.begin(), thrust::equal_to>{}); - auto edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - + // 'invalid_edge_first' marks the beginning of the edges to be removed - auto invalid_edge_first = thrust::stable_partition( - handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_triangles.size(), - [k]__device__(auto e){ - auto num_triangles = thrust::get<1>(e); - auto is_in_k_truss = num_triangles == 2; - return num_triangles > k; // FIXME (k-2) * 2 - - } - ); + auto invalid_edge_first = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + auto is_in_k_truss = num_triangles == 2; + return num_triangles > k; // FIXME (k-2) * 2 + }); size_t num_invalid_edges{0}; - num_invalid_edges = - static_cast(thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); - + num_invalid_edges = static_cast( + thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); + size_t invalid_edge_start_idx{0}; invalid_edge_start_idx = static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); - - + // copy invalid edges auto invalid_edges_buffer = allocate_dataframe_buffer>( - num_invalid_edges, handle.get_stream()); + num_invalid_edges, handle.get_stream()); thrust::copy(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer) + invalid_edge_start_idx, get_dataframe_buffer_end(vertex_pair_buffer), get_dataframe_buffer_begin(invalid_edges_buffer)); - - - - if (num_invalid_edges != 0) { // unroll and remove/mask edges + if (num_invalid_edges != 0) { // unroll and remove/mask edges // case 2: unroll (q, r) - - auto incoming_vertex_pairs_begin = thrust::make_zip_iterator( - edgelist_dsts.begin(), edgelist_srcs.begin()); - + + auto incoming_vertex_pairs_begin = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + vertex_t num_incomming_vertex_pairs = edgelist_srcs.size(); - + // Sort the 'incoming_vertex_pairs_begin' by 'dst' - thrust::sort(handle.get_thrust_policy(), - incoming_vertex_pairs_begin, - incoming_vertex_pairs_begin + edgelist_srcs.size()); // FIXME: No need to partition - + thrust::sort( + handle.get_thrust_policy(), + incoming_vertex_pairs_begin, + incoming_vertex_pairs_begin + edgelist_srcs.size()); // FIXME: No need to partition + // For each (q, r) edge to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) rmm::device_uvector prefix_sum(edgelist_srcs.size() + 1, handle.get_stream()); - thrust::tabulate(handle.get_thrust_policy(), - prefix_sum.begin(), - prefix_sum.end(), - [ - invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), - dst_array_begin = edgelist_dsts.begin(), - num_edges = num_incomming_vertex_pairs - ] - __device__(auto idx){ - auto src = thrust::get<0>(*(invalid_first + idx)); - auto dst = thrust::get<1>(*(invalid_first + idx)); - auto dst_array_end = dst_array_begin + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops - auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_upper = thrust::distance(dst_array_begin, itr_upper); - auto dist = thrust::distance(itr_lower, itr_upper); - return dist; - } ); - - thrust::exclusive_scan(handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + thrust::tabulate( + handle.get_thrust_policy(), + prefix_sum.begin(), + prefix_sum.end(), + [invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), + dst_array_begin = edgelist_dsts.begin(), + num_edges = num_incomming_vertex_pairs] __device__(auto idx) { + auto src = thrust::get<0>(*(invalid_first + idx)); + auto dst = thrust::get<1>(*(invalid_first + idx)); + auto dst_array_end = dst_array_begin + num_edges; + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = + thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops + auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_upper = thrust::distance(dst_array_begin, itr_upper); + auto dist = thrust::distance(itr_lower, itr_upper); + return dist; + }); + + thrust::exclusive_scan( + handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); auto vertex_pair_buffer_p_r = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); rmm::device_uvector indices(edgelist_srcs.size(), handle.get_stream()); - thrust::tabulate(handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); + thrust::tabulate( + handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); thrust::for_each( handle.get_thrust_policy(), indices.begin(), indices.end(), - [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), - invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - num_edges = edgelist_srcs.size()] - __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; + [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), + invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + num_edges = edgelist_srcs.size()] __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; auto dst_array_begin = invalid_first_dst; auto dst_array_end = invalid_first_dst + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // Need a binary search to find the begining of the range + auto idx_lower = thrust::distance( + dst_array_begin, itr_lower); // Need a binary search to find the begining of the range auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; - - thrust::tabulate(thrust::seq, - vertex_pair_buffer_p_q + prefix_sum[idx], - vertex_pair_buffer_p_q + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), src); - }); - - thrust::tabulate(thrust::seq, - vertex_pair_buffer_p_r + prefix_sum[idx], - vertex_pair_buffer_p_r + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), dst); - }); - + + thrust::tabulate( + thrust::seq, + vertex_pair_buffer_p_q + prefix_sum[idx], + vertex_pair_buffer_p_q + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment)), src); + }); + + thrust::tabulate( + thrust::seq, + vertex_pair_buffer_p_r + prefix_sum[idx], + vertex_pair_buffer_p_r + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment)), dst); + }); }); + auto edge_exists = cur_graph_view.has_edge( + handle, + raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), + std::get<0>(vertex_pair_buffer_p_q).size()), + raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), + std::get<1>(vertex_pair_buffer_p_q).size())); + + auto edge_to_existance = thrust::make_zip_iterator( + thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), + edge_exists.begin()); + + auto has_edge_last = thrust::stable_partition(handle.get_thrust_policy(), + edge_to_existance, + edge_to_existance + edge_exists.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists; + }); - auto edge_exists = cur_graph_view.has_edge( - handle, - raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), - raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size())); - - auto edge_to_existance = thrust::make_zip_iterator( - thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), - edge_exists.begin()); - - - auto has_edge_last = thrust::stable_partition( - handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edge_exists.size(), - []__device__(auto e){ - auto edge_exists = thrust::get<1>(e); - return edge_exists; - - } - ); - auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); - + // After pushing the non-existant edges to the second partition, // remove them by resizing both vertex pair buffer resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); - rmm::device_uvector decrease_num_triangles_p_q_p_r_tmp(2 * num_edge_exists, handle.get_stream()); + rmm::device_uvector decrease_num_triangles_p_q_p_r_tmp(2 * num_edge_exists, + handle.get_stream()); thrust::fill(handle.get_thrust_policy(), - decrease_num_triangles_p_q_p_r_tmp.begin(), - decrease_num_triangles_p_q_p_r_tmp.end(), size_t{-1}); + decrease_num_triangles_p_q_p_r_tmp.begin(), + decrease_num_triangles_p_q_p_r_tmp.end(), + size_t{-1}); + + auto vertex_pair_buffer_p_q_p_r_tmp = + allocate_dataframe_buffer>(2 * num_edge_exists, + handle.get_stream()); - auto vertex_pair_buffer_p_q_p_r_tmp = allocate_dataframe_buffer>( - 2 * num_edge_exists, handle.get_stream()); - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp)); - + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp)); + thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_r), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp) + num_edge_exists); + get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_r), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp) + num_edge_exists); thrust::sort(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp) - ); // FIXME: Remove duplicated edges + get_dataframe_buffer_end( + vertex_pair_buffer_p_q_p_r_tmp)); // FIXME: Remove duplicated edges // FIXME: No need for a count if we do only one reduce_by_key at the end // (vertex_pair_buffer_p_q_p_r + vertex_pair_buffer) Because the reduction // of both will lead to a pair_buffer of size size_of(vertex_pair_buffer) // Also no need for a tmp buffer 'vertex_pair_buffer_p_q_p_r_tmp' - auto count_p_q_p_r = thrust::unique_count(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); + auto count_p_q_p_r = + thrust::unique_count(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); - - auto vertex_pair_buffer_p_q_p_r = allocate_dataframe_buffer>( - count_p_q_p_r, handle.get_stream()); - rmm::device_uvector decrease_num_triangles_p_q_p_r(count_p_q_p_r, handle.get_stream()); + auto vertex_pair_buffer_p_q_p_r = + allocate_dataframe_buffer>(count_p_q_p_r, + handle.get_stream()); + rmm::device_uvector decrease_num_triangles_p_q_p_r(count_p_q_p_r, + handle.get_stream()); thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp), - decrease_num_triangles_p_q_p_r_tmp.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), - decrease_num_triangles_p_q_p_r.begin(), - thrust::equal_to>{}); + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp), + decrease_num_triangles_p_q_p_r_tmp.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), + decrease_num_triangles_p_q_p_r.begin(), + thrust::equal_to>{}); // Add edges from vertex_pair_buffer - edge_t prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_p_r); + edge_t prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_p_r); edge_t accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; - resize_dataframe_buffer(vertex_pair_buffer_p_q_p_r, accumulate_pair_size, handle.get_stream()); + resize_dataframe_buffer( + vertex_pair_buffer_p_q_p_r, accumulate_pair_size, handle.get_stream()); decrease_num_triangles_p_q_p_r.resize(accumulate_pair_size, handle.get_stream()); - + thrust::copy(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer), get_dataframe_buffer_end(vertex_pair_buffer), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r) + prev_size); - + thrust::copy(handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), decrease_num_triangles_p_q_p_r.begin() + prev_size); thrust::sort_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), - decrease_num_triangles_p_q_p_r.begin()); - - thrust::reduce_by_key(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), - decrease_num_triangles_p_q_p_r.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer), - num_triangles.begin(), - thrust::equal_to>{}); + decrease_num_triangles_p_q_p_r.begin()); + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), + decrease_num_triangles_p_q_p_r.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer), + num_triangles.begin(), + thrust::equal_to>{}); edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); edge_t num_edges = size_dataframe_buffer(vertex_pair_buffer); - auto edges_to_num_triangles_last = thrust::stable_partition( - handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_edges, - []__device__(auto edge_to_num_triangles){ - auto edge_exists = thrust::get<1>(edge_to_num_triangles); - return edge_exists; - - } - ); - + auto edges_to_num_triangles_last = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_edges, + [] __device__(auto edge_to_num_triangles) { + auto edge_exists = thrust::get<1>(edge_to_num_triangles); + return edge_exists; + }); + auto last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_last); edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag - cugraph::edge_bucket_tedge_list(handle); + cugraph::edge_bucket_t edge_list(handle); cugraph::edge_property_t edge_value_output(handle, - cur_graph_view); + cur_graph_view); edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, std::get<1>(vertex_pair_buffer).begin()); - + cugraph::transform_e( handle, cur_graph_view, @@ -772,13 +763,12 @@ void k_truss(raft::handle_t const& handle, cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), - [] - __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { return true; }, edge_value_output.mutable_view(), false); - + cur_graph_view.attach_edge_mask(edge_value_output.view()); // resize the 'edgelist_srcs' and 'edgelsit_dst' @@ -786,211 +776,211 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_idx, handle.get_stream()); thrust::copy(handle.get_thrust_policy(), - std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_srcs.begin()); - + std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_srcs.begin()); + thrust::copy(handle.get_thrust_policy(), - std::get<1>(vertex_pair_buffer).begin(), - std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_dsts.begin()); + std::get<1>(vertex_pair_buffer).begin(), + std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_dsts.begin()); // Get the new pair of incoming edges - incoming_vertex_pairs_begin = thrust::make_zip_iterator( - edgelist_dsts.begin(), edgelist_srcs.begin()); + incoming_vertex_pairs_begin = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); // Need to run prefix_sum again to get new ranges because some incoming edges were removed - + num_incomming_vertex_pairs = edgelist_srcs.size(); prefix_sum.resize(num_incomming_vertex_pairs + 1, handle.get_stream()); - // FIXME: need to sort 'incoming_vertex_pairs_begin'. N0 need because a stable partition was performed that preserve the sorting - - thrust::tabulate(handle.get_thrust_policy(), - prefix_sum.begin(), - prefix_sum.end(), - [ - invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), - incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - dst_array_begin = edgelist_dsts.begin(), - src_array_begin = edgelist_srcs.begin(), - num_edges = num_incomming_vertex_pairs - ] - __device__(auto idx){ - auto src = thrust::get<0>(*(invalid_first + idx)); - auto dst = thrust::get<1>(*(invalid_first + idx)); - auto dst_array_end = thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()) + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops - auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_upper = thrust::distance(dst_array_begin, itr_upper); - auto dist = thrust::distance(itr_lower, itr_upper); - return dist; - } ); - - thrust::exclusive_scan(handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - - - // case 3 unroll (p, r) - - vertex_pair_buffer_p_q = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - - auto vertex_pair_buffer_q_r = allocate_dataframe_buffer>( + // FIXME: need to sort 'incoming_vertex_pairs_begin'. N0 need because a stable partition was + // performed that preserve the sorting + + thrust::tabulate( + handle.get_thrust_policy(), + prefix_sum.begin(), + prefix_sum.end(), + [invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), + incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst_array_begin = edgelist_dsts.begin(), + src_array_begin = edgelist_srcs.begin(), + num_edges = num_incomming_vertex_pairs] __device__(auto idx) { + auto src = thrust::get<0>(*(invalid_first + idx)); + auto dst = thrust::get<1>(*(invalid_first + idx)); + auto dst_array_end = + thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()) + num_edges; + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = + thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops + auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_upper = thrust::distance(dst_array_begin, itr_upper); + auto dist = thrust::distance(itr_lower, itr_upper); + return dist; + }); + + thrust::exclusive_scan( + handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + + // case 3 unroll (p, r) + + vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - thrust::for_each( - handle.get_thrust_policy(), - indices.begin(), - indices.end(), - [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), - invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - num_edges = edgelist_srcs.size()] - __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_begin = invalid_first_dst; - auto dst_array_end = invalid_first_dst + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; - - thrust::tabulate(thrust::seq, - vertex_pair_buffer_p_q + prefix_sum[idx], - vertex_pair_buffer_p_q + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - - return thrust::make_tuple( - src, thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment))); - }); - - thrust::tabulate(thrust::seq, - vertex_pair_buffer_q_r + prefix_sum[idx], - vertex_pair_buffer_q_r + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower +idx_in_segment)), dst); - }); - - }); - - + auto vertex_pair_buffer_q_r = allocate_dataframe_buffer>( + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + + thrust::for_each( + handle.get_thrust_policy(), + indices.begin(), + indices.end(), + [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), + invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + num_edges = edgelist_srcs.size()] __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_begin = invalid_first_dst; + auto dst_array_end = invalid_first_dst + num_edges; + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = thrust::distance( + dst_array_begin, itr_lower); // Need a binary search to find the begining of the range + auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; + + thrust::tabulate( + thrust::seq, + vertex_pair_buffer_p_q + prefix_sum[idx], + vertex_pair_buffer_p_q + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + src, thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment))); + }); + + thrust::tabulate( + thrust::seq, + vertex_pair_buffer_q_r + prefix_sum[idx], + vertex_pair_buffer_q_r + prefix_sum[idx] + dist, + [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment)), dst); + }); + }); + edge_exists = cur_graph_view.has_edge( handle, - raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), - raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size())); - - + raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), + std::get<0>(vertex_pair_buffer_p_q).size()), + raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), + std::get<1>(vertex_pair_buffer_p_q).size())); + edge_to_existance = thrust::make_zip_iterator( - thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), - edge_exists.begin()); - - - auto edges_to_num_triangles_p_r_last = thrust::stable_partition( - handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edge_exists.size(), - []__device__(auto e){ - auto edge_exists = thrust::get<1>(e); - return edge_exists; - - } - ); - - num_edge_exists = thrust::distance(edge_to_existance, edges_to_num_triangles_p_r_last); - - // Resize both 'vertex_pair_buffer' - resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); - resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); - - - rmm::device_uvector decrease_num_triangles_p_q_q_r_tmp(2 * num_edge_exists, handle.get_stream()); + thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), + edge_exists.begin()); + + auto edges_to_num_triangles_p_r_last = + thrust::stable_partition(handle.get_thrust_policy(), + edge_to_existance, + edge_to_existance + edge_exists.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists; + }); + + num_edge_exists = thrust::distance(edge_to_existance, edges_to_num_triangles_p_r_last); + + // Resize both 'vertex_pair_buffer' + resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); + resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); + + rmm::device_uvector decrease_num_triangles_p_q_q_r_tmp(2 * num_edge_exists, + handle.get_stream()); thrust::fill(handle.get_thrust_policy(), decrease_num_triangles_p_q_q_r_tmp.begin(), decrease_num_triangles_p_q_q_r_tmp.end(), size_t{-1}); - auto vertex_pair_buffer_p_q_q_r_tmp = allocate_dataframe_buffer>( - 2 * num_edge_exists, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp)); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_q_r), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp) + num_edge_exists); - - thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp) - ); // FIXME: Remove duplicated edges + auto vertex_pair_buffer_p_q_q_r_tmp = + allocate_dataframe_buffer>(2 * num_edge_exists, + handle.get_stream()); - // FIXME: No need for a count if we do only one reduce_by_key at the end (vertex_pair_buffer_p_q_q_r + vertex_pair_buffer) - // Because the reduction of both will lead to a pair_buffer of size size_of(vertex_pair_buffer) - // Also no need for a tmp buffer 'vertex_pair_buffer_p_q_q_r_tmp' - auto count_p_q_q_r = thrust::unique_count(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp)); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_q_r), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp) + num_edge_exists); + + thrust::sort(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end( + vertex_pair_buffer_p_q_q_r_tmp)); // FIXME: Remove duplicated edges - auto vertex_pair_buffer_p_q_q_r = allocate_dataframe_buffer>( - count_p_q_q_r, handle.get_stream()); - rmm::device_uvector decrease_num_triangles_p_q_q_r(count_p_q_q_r, handle.get_stream()); + // FIXME: No need for a count if we do only one reduce_by_key at the end + // (vertex_pair_buffer_p_q_q_r + vertex_pair_buffer) Because the reduction of both will lead + // to a pair_buffer of size size_of(vertex_pair_buffer) Also no need for a tmp buffer + // 'vertex_pair_buffer_p_q_q_r_tmp' + auto count_p_q_q_r = + thrust::unique_count(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); + + auto vertex_pair_buffer_p_q_q_r = + allocate_dataframe_buffer>(count_p_q_q_r, + handle.get_stream()); + rmm::device_uvector decrease_num_triangles_p_q_q_r(count_p_q_q_r, + handle.get_stream()); thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp), - decrease_num_triangles_p_q_q_r_tmp.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), - decrease_num_triangles_p_q_q_r.begin(), - thrust::equal_to>{}); + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp), + decrease_num_triangles_p_q_q_r_tmp.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), + decrease_num_triangles_p_q_q_r.begin(), + thrust::equal_to>{}); // Add edges from vertex_pair_buffer - prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_q_r); + prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_q_r); accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; - resize_dataframe_buffer(vertex_pair_buffer_p_q_q_r, accumulate_pair_size, handle.get_stream()); + resize_dataframe_buffer( + vertex_pair_buffer_p_q_q_r, accumulate_pair_size, handle.get_stream()); decrease_num_triangles_p_q_q_r.resize(accumulate_pair_size, handle.get_stream()); - thrust::copy(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer), get_dataframe_buffer_end(vertex_pair_buffer), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r) + prev_size); - + thrust::copy(handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), decrease_num_triangles_p_q_q_r.begin() + prev_size); thrust::sort_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), - decrease_num_triangles_p_q_q_r.begin()); - - thrust::reduce_by_key(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), - decrease_num_triangles_p_q_q_r.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer), - num_triangles.begin(), - thrust::equal_to>{}); + decrease_num_triangles_p_q_q_r.begin()); + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), + decrease_num_triangles_p_q_q_r.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer), + num_triangles.begin(), + thrust::equal_to>{}); edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); @@ -998,35 +988,33 @@ void k_truss(raft::handle_t const& handle, num_edges = size_dataframe_buffer(vertex_pair_buffer); // FIXME: This variable cannot be reassigned. rename them appropriately - auto edges_to_num_triangles_p_r_last_ = thrust::stable_partition( - handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_edges, - []__device__(auto edge_to_num_triangles){ - auto edge_exists = thrust::get<1>(edge_to_num_triangles); - return edge_exists; - } - ); - + auto edges_to_num_triangles_p_r_last_ = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_edges, + [] __device__(auto edge_to_num_triangles) { + auto edge_exists = thrust::get<1>(edge_to_num_triangles); + return edge_exists; + }); + last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_p_r_last_); edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag // NOTE: This needs to be a seperate variable 'edge_list' - //cugraph::edge_bucket_tedge_list(handle); + // cugraph::edge_bucket_tedge_list(handle); // FIXME: seem - edge_list.clear(); // FIXME: is this needed? + edge_list.clear(); // FIXME: is this needed? + + cugraph::edge_property_t edge_value_output_p_r( + handle, cur_graph_view); - cugraph::edge_property_t edge_value_output_p_r(handle, - cur_graph_view); - edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, std::get<1>(vertex_pair_buffer).begin()); - cugraph::transform_e( handle, cur_graph_view, @@ -1034,13 +1022,12 @@ void k_truss(raft::handle_t const& handle, cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), - [] - __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { return true; }, edge_value_output_p_r.mutable_view(), false); - + cur_graph_view.attach_edge_mask(edge_value_output_p_r.view()); // resize the 'edgelist_srcs' and 'edgelsit_dst' @@ -1048,71 +1035,78 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_idx, handle.get_stream()); thrust::copy(handle.get_thrust_policy(), - std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_srcs.begin()); - + std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_srcs.begin()); + thrust::copy(handle.get_thrust_policy(), - std::get<1>(vertex_pair_buffer).begin(), - std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_dsts.begin()); + std::get<1>(vertex_pair_buffer).begin(), + std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_dsts.begin()); // Get the new pair of incoming edges - incoming_vertex_pairs_begin = thrust::make_zip_iterator( - edgelist_dsts.begin(), edgelist_srcs.begin()); - - // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) + incoming_vertex_pairs_begin = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + + // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. - auto[intersection_offsets, intersection_indices] = + auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, - cur_graph_view, - cugraph::edge_dummy_property_t{}.view(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - std::array{true, true}, - do_expensive_check); + cur_graph_view, + cugraph::edge_dummy_property_t{}.view(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + std::array{true, true}, + do_expensive_check); // generating (p, r) - edge_t vertex_pair_buffer_p_r_edge_p_q_size = intersection_indices.size(); // rename this var as accumulate_pair_size - rmm::device_uvector num_triangles_p_r(vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); // FIXME: Rename this - thrust::fill(handle.get_thrust_policy(), num_triangles_p_r.begin(), num_triangles_p_r.end(), size_t{-1}); - auto vertex_pair_buffer_p_r_edge_p_q = allocate_dataframe_buffer>( - vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); - - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q) + vertex_pair_buffer_p_r_edge_p_q_size, - generate_pr{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate - }); + edge_t vertex_pair_buffer_p_r_edge_p_q_size = + intersection_indices.size(); // rename this var as accumulate_pair_size + rmm::device_uvector num_triangles_p_r(vertex_pair_buffer_p_r_edge_p_q_size, + handle.get_stream()); // FIXME: Rename this + thrust::fill( + handle.get_thrust_policy(), num_triangles_p_r.begin(), num_triangles_p_r.end(), size_t{-1}); + auto vertex_pair_buffer_p_r_edge_p_q = + allocate_dataframe_buffer>( + vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); + + thrust::tabulate( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q) + + vertex_pair_buffer_p_r_edge_p_q_size, + generate_pr{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate + }); // generating (q, r) - edge_t vertex_pair_buffer_q_r_edge_p_q_size = intersection_indices.size(); // rename this var as accumulate_pair_size - rmm::device_uvector num_triangles_q_r(vertex_pair_buffer_q_r_edge_p_q_size, handle.get_stream()); // FIXME: Rename this - thrust::fill(handle.get_thrust_policy(), num_triangles_q_r.begin(), num_triangles_q_r.end(), size_t{-1}); - auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>( - vertex_pair_buffer_q_r_edge_p_q_size, handle.get_stream()); - - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + vertex_pair_buffer_q_r_edge_p_q_size, - generate_qr{ - raft::device_span( - intersection_offsets.data(), intersection_offsets.size()), - raft::device_span( - intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate - }); - + edge_t vertex_pair_buffer_q_r_edge_p_q_size = + intersection_indices.size(); // rename this var as accumulate_pair_size + rmm::device_uvector num_triangles_q_r(vertex_pair_buffer_q_r_edge_p_q_size, + handle.get_stream()); // FIXME: Rename this + thrust::fill( + handle.get_thrust_policy(), num_triangles_q_r.begin(), num_triangles_q_r.end(), size_t{-1}); + auto vertex_pair_buffer_q_r_edge_p_q = + allocate_dataframe_buffer>( + vertex_pair_buffer_q_r_edge_p_q_size, handle.get_stream()); + + thrust::tabulate( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + + vertex_pair_buffer_q_r_edge_p_q_size, + generate_qr{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate + }); } - } - } -} // namespace cugraph +} // namespace cugraph diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index 38d713cf022..7742037e398 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -19,15 +19,14 @@ namespace cugraph { template void k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - int32_t k, - bool do_expensive_check); - + graph_view_t const& graph_view, + int32_t k, + bool do_expensive_check); template void k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - int64_t k, - bool do_expensive_check); + graph_view_t const& graph_view, + int64_t k, + bool do_expensive_check); /* template void ktruss(raft::handle_t const& handle, From 0ea3de063c693648b5aa70730f7de86eef7e2a63 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 29 Jan 2024 06:41:59 -0800 Subject: [PATCH 030/155] fix copyright --- cpp/tests/community/k_truss_test.cpp | 31 ++++++++++------------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 4559572e617..b2e80e867fa 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. + * 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. @@ -39,15 +39,13 @@ #include #include - struct KTruss_Usecase { int32_t k{10}; bool check_correctness{true}; }; template -class Tests_KTruss - : public ::testing::TestWithParam> { +class Tests_KTruss : public ::testing::TestWithParam> { public: Tests_KTruss() {} @@ -58,8 +56,7 @@ class Tests_KTruss virtual void TearDown() {} template - void run_current_test( - std::tuple const& param) + void run_current_test(std::tuple const& param) { constexpr bool renumber = false; @@ -94,18 +91,13 @@ class Tests_KTruss hr_timer.start("K-truss"); } - cugraph::k_truss( - handle, - graph_view, - k_truss_usecase.k, - false); + cugraph::k_truss(handle, graph_view, k_truss_usecase.k, false); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement hr_timer.stop(); hr_timer.display_and_clear(std::cout); } - } }; @@ -117,13 +109,12 @@ TEST_P(Tests_KTruss_File, CheckInt32Int32) run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); } - -INSTANTIATE_TEST_SUITE_P( - file_test, - Tests_KTruss_File, - ::testing::Combine( - // enable correctness checks - ::testing::Values(KTruss_Usecase{2}), - ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/debug_jaccard/cugraph/datasets/dummy.mtx")))); +INSTANTIATE_TEST_SUITE_P(file_test, + Tests_KTruss_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(KTruss_Usecase{2}), + ::testing::Values(cugraph::test::File_Usecase( + "/home/nfs/jnke/ktruss/cugraph/datasets/dummy.mtx")))); CUGRAPH_TEST_PROGRAM_MAIN() From 61d238cbb8ace5d08bb47e5969c27a14be97220a Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 29 Jan 2024 10:09:10 -0800 Subject: [PATCH 031/155] identify new set of edges to be deleted --- cpp/src/community/k_truss_impl.cuh | 180 ++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 5 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 2ef581006b3..90f9773a5dd 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -524,7 +524,7 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_end(vertex_pair_buffer), get_dataframe_buffer_begin(invalid_edges_buffer)); - if (num_invalid_edges != 0) { // unroll and remove/mask edges + while (num_invalid_edges != 0) { // unroll and remove/mask edges // case 2: unroll (q, r) @@ -733,13 +733,13 @@ void k_truss(raft::handle_t const& handle, edge_t num_edges = size_dataframe_buffer(vertex_pair_buffer); + // Put edges with triangle count == 0 in the second partition auto edges_to_num_triangles_last = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, edges_to_num_triangles + num_edges, [] __device__(auto edge_to_num_triangles) { - auto edge_exists = thrust::get<1>(edge_to_num_triangles); - return edge_exists; + return thrust::get<1>(edge_to_num_triangles); }); auto last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_last); @@ -988,13 +988,14 @@ void k_truss(raft::handle_t const& handle, num_edges = size_dataframe_buffer(vertex_pair_buffer); // FIXME: This variable cannot be reassigned. rename them appropriately + // Put edges with triangle count == 0 in the second partition + // FIXME: rename 'edges_to_num_triangles_p_r_last_'. auto edges_to_num_triangles_p_r_last_ = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, edges_to_num_triangles + num_edges, [] __device__(auto edge_to_num_triangles) { - auto edge_exists = thrust::get<1>(edge_to_num_triangles); - return edge_exists; + return thrust::get<1>(edge_to_num_triangles); }); last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_p_r_last_); @@ -1105,6 +1106,175 @@ void k_truss(raft::handle_t const& handle, intersection_indices.size()), get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate }); + + auto vertex_pair_buffer_p_r_q_r_tmp = + allocate_dataframe_buffer>(2 * vertex_pair_buffer_p_r_edge_p_q_size, + handle.get_stream()); + + rmm::device_uvector decrease_num_triangles_p_r_q_r_tmp(2 * vertex_pair_buffer_p_r_edge_p_q_size, + handle.get_stream()); + thrust::fill(handle.get_thrust_policy(), + decrease_num_triangles_p_r_q_r_tmp.begin(), + decrease_num_triangles_p_r_q_r_tmp.end(), + size_t{-1}); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp)); + + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_q_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp) + vertex_pair_buffer_p_r_edge_p_q_size); + + thrust::sort(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), + get_dataframe_buffer_end( + vertex_pair_buffer_p_r_q_r_tmp)); // FIXME: Remove duplicated edges + + auto count_p_r_q_r = + thrust::unique_count(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r_tmp)); + + auto vertex_pair_buffer_p_r_q_r = + allocate_dataframe_buffer>(count_p_r_q_r, + handle.get_stream()); + rmm::device_uvector decrease_num_triangles_p_r_q_r(count_p_r_q_r, + handle.get_stream()); + + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), + get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r_tmp), + decrease_num_triangles_p_r_q_r_tmp.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r), + decrease_num_triangles_p_r_q_r.begin(), + thrust::equal_to>{}); + + // Add edges from vertex_pair_buffer + prev_size = size_dataframe_buffer(vertex_pair_buffer_p_r_q_r); + accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; + + resize_dataframe_buffer( + vertex_pair_buffer_p_r_q_r, accumulate_pair_size, handle.get_stream()); + decrease_num_triangles_p_r_q_r.resize(accumulate_pair_size, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer), + get_dataframe_buffer_end(vertex_pair_buffer), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r) + prev_size); + + thrust::copy(handle.get_thrust_policy(), + num_triangles.begin(), + num_triangles.end(), + decrease_num_triangles_p_r_q_r.begin() + prev_size); + + thrust::sort_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r), + decrease_num_triangles_p_r_q_r.begin()); + + thrust::reduce_by_key(handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r), + get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r), + decrease_num_triangles_p_r_q_r.begin(), + get_dataframe_buffer_begin(vertex_pair_buffer), + num_triangles.begin(), + thrust::equal_to>{}); + + + raft::print_device_vector("'vertex_pair_buffer' - src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); + raft::print_device_vector("'vertex_pair_buffer' - dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); + raft::print_device_vector("'num_triangles_p_r_q_r' - ", num_triangles.data(), num_triangles.size(), std::cout); + + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + num_edges = size_dataframe_buffer(vertex_pair_buffer); + + // FIXME: This variable cannot be reassigned. rename them appropriately + // Put edges with triangle count == 0 in the second partition + // FIXME: rename 'edges_to_num_triangles_p_r_last_0'. + auto edges_to_num_triangles_p_r_last_0 = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_edges, + [] __device__(auto edge_to_num_triangles) { + return thrust::get<1>(edge_to_num_triangles); + }); + + last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_p_r_last_0); + + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag + // NOTE: This needs to be a seperate variable 'edge_list' + // cugraph::edge_bucket_tedge_list(handle); + // FIXME: seem + edge_list.clear(); // FIXME: is this needed? + + cugraph::edge_property_t edge_value_output_p_q( + handle, cur_graph_view); + + edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + std::get<1>(vertex_pair_buffer).begin()); + + cugraph::transform_e( + handle, + cur_graph_view, + edge_list, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + return true; + }, + edge_value_output_p_q.mutable_view(), + false); + + cur_graph_view.attach_edge_mask(edge_value_output_p_q.view()); + + // resize the 'edgelist_srcs' and 'edgelsit_dst' + edgelist_srcs.resize(last_edge_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_idx, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_srcs.begin()); + + thrust::copy(handle.get_thrust_policy(), + std::get<1>(vertex_pair_buffer).begin(), + std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, + edgelist_dsts.begin()); + + // Find if there exist edges with invalid triangle counts + auto invalid_edge_first = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + auto is_in_k_truss = num_triangles == 2; + return (num_triangles > k) || (num_triangles ==0) ; // FIXME (k-2) * 2 + }); + + num_invalid_edges = static_cast( + thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); + + printf("\nfinal number of invalid edges = %d\n", num_invalid_edges); + + + + + + + + } } } From edfabea5cb85620c8455b871063bf240c5faa507 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 29 Jan 2024 10:10:09 -0800 Subject: [PATCH 032/155] fix style --- cpp/src/community/k_truss_impl.cuh | 64 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 90f9773a5dd..e583a19d0cf 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -1108,11 +1108,11 @@ void k_truss(raft::handle_t const& handle, }); auto vertex_pair_buffer_p_r_q_r_tmp = - allocate_dataframe_buffer>(2 * vertex_pair_buffer_p_r_edge_p_q_size, - handle.get_stream()); - - rmm::device_uvector decrease_num_triangles_p_r_q_r_tmp(2 * vertex_pair_buffer_p_r_edge_p_q_size, - handle.get_stream()); + allocate_dataframe_buffer>( + 2 * vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); + + rmm::device_uvector decrease_num_triangles_p_r_q_r_tmp( + 2 * vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); thrust::fill(handle.get_thrust_policy(), decrease_num_triangles_p_r_q_r_tmp.begin(), decrease_num_triangles_p_r_q_r_tmp.end(), @@ -1122,18 +1122,18 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp)); - thrust::copy(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp) + vertex_pair_buffer_p_r_edge_p_q_size); - + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp) + + vertex_pair_buffer_p_r_edge_p_q_size); + thrust::sort(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), get_dataframe_buffer_end( vertex_pair_buffer_p_r_q_r_tmp)); // FIXME: Remove duplicated edges - + auto count_p_r_q_r = thrust::unique_count(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), @@ -1152,7 +1152,7 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r), decrease_num_triangles_p_r_q_r.begin(), thrust::equal_to>{}); - + // Add edges from vertex_pair_buffer prev_size = size_dataframe_buffer(vertex_pair_buffer_p_r_q_r); accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; @@ -1183,11 +1183,17 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin(), thrust::equal_to>{}); - - raft::print_device_vector("'vertex_pair_buffer' - src", std::get<0>(vertex_pair_buffer).data(), std::get<0>(vertex_pair_buffer).size(), std::cout); - raft::print_device_vector("'vertex_pair_buffer' - dst", std::get<1>(vertex_pair_buffer).data(), std::get<1>(vertex_pair_buffer).size(), std::cout); - raft::print_device_vector("'num_triangles_p_r_q_r' - ", num_triangles.data(), num_triangles.size(), std::cout); + raft::print_device_vector("'vertex_pair_buffer' - src", + std::get<0>(vertex_pair_buffer).data(), + std::get<0>(vertex_pair_buffer).size(), + std::cout); + raft::print_device_vector("'vertex_pair_buffer' - dst", + std::get<1>(vertex_pair_buffer).data(), + std::get<1>(vertex_pair_buffer).size(), + std::cout); + raft::print_device_vector( + "'num_triangles_p_r_q_r' - ", num_triangles.data(), num_triangles.size(), std::cout); edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); @@ -1251,30 +1257,22 @@ void k_truss(raft::handle_t const& handle, std::get<1>(vertex_pair_buffer).begin(), std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, edgelist_dsts.begin()); - + // Find if there exist edges with invalid triangle counts - auto invalid_edge_first = - thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - auto is_in_k_truss = num_triangles == 2; - return (num_triangles > k) || (num_triangles ==0) ; // FIXME (k-2) * 2 - }); + auto invalid_edge_first = thrust::stable_partition( + handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + auto is_in_k_truss = num_triangles == 2; + return (num_triangles > k) || (num_triangles == 0); // FIXME (k-2) * 2 + }); num_invalid_edges = static_cast( thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); - - printf("\nfinal number of invalid edges = %d\n", num_invalid_edges); - - - - - - - + printf("\nfinal number of invalid edges = %d\n", num_invalid_edges); } } } From 4dffcf5ea8d33f533b860dd03534352f26329f90 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 14 Feb 2024 06:24:33 -0800 Subject: [PATCH 033/155] cleanup code and remove memory preallocation --- cpp/src/community/k_truss_impl.cuh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index e583a19d0cf..cd19b204e90 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -155,7 +155,7 @@ struct generate_pr { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator vertex_pairs_begin; + VertexPairIterator vertex_pairs_begin{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -174,7 +174,7 @@ struct generate_qr { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator vertex_pairs_begin; + VertexPairIterator vertex_pairs_begin{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -421,7 +421,6 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), vertex_pairs_begin, vertex_pairs_begin + edgelist_srcs.size()); size_t num_vertex_pairs = edgelist_srcs.size(); - auto out_degrees = cur_graph_view.compute_out_degrees(handle); auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, @@ -432,9 +431,6 @@ void k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - auto vertex_pair_buffer = allocate_dataframe_buffer>( - num_vertex_pairs, handle.get_stream()); - // stores all the pairs (p, q), (p, r) and (q, r) auto vertex_pair_buffer_tmp = allocate_dataframe_buffer>( intersection_indices.size() * 3, handle.get_stream()); @@ -485,6 +481,8 @@ void k_truss(raft::handle_t const& handle, rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); + auto vertex_pair_buffer = allocate_dataframe_buffer>( + num_vertex_pairs, handle.get_stream()); thrust::reduce_by_key(handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_tmp), get_dataframe_buffer_end(vertex_pair_buffer_tmp), From d2694b721ebea3bd313567f665977282a5899608 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 20 Feb 2024 08:34:20 -0800 Subject: [PATCH 034/155] remove temporary buffers and cleanup code --- cpp/src/community/k_truss_impl.cuh | 575 ++++++++++++++++++++++------- 1 file changed, 438 insertions(+), 137 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index cd19b204e90..9f2e60892df 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -41,6 +41,7 @@ #include #include #include +#include namespace cugraph { @@ -100,75 +101,121 @@ template struct extract_p_q { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; + raft::device_span num_triangles{}; - VertexPairIterator vertex_pairs_begin; + VertexPairIterator vertex_pairs_begin{}; __device__ thrust::tuple operator()(edge_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - thrust::tuple pair = *(vertex_pairs_begin + idx); - return pair; + printf("\nbefore - value = %d, idx = %d\n", static_cast(*(num_triangles.data()+idx)), static_cast(idx)); + unsigned int r = atomicInc((unsigned int*)(num_triangles.data()+idx), (unsigned int)1); + printf("\nafter - value = %d, idx = %d, r = %d\n", static_cast(*(num_triangles.data()+idx)), static_cast(idx), static_cast(r)); } }; + template struct extract_p_r { + size_t num_vertex_pair{}; // rename to num_edges raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; + raft::device_span num_triangles{}; - VertexPairIterator vertex_pairs_begin; + VertexPairIterator vertex_pairs_begin{}; __device__ thrust::tuple operator()(edge_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = + auto p_r_pair = thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - return pair; + + // Find its position in 'vertex_pairs_begin' + auto itr_p_r = thrust::lower_bound(thrust::seq, + vertex_pairs_begin, + vertex_pairs_begin + num_vertex_pair, // pass the number of vertex pairs + p_r_pair); + + idx = thrust::distance(vertex_pairs_begin, itr_p_r); + auto r = atomicAdd(num_triangles.data()+idx, 1); + } }; + template struct extract_q_r { + size_t num_vertex_pair; // rename to num_edges raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator vertex_pairs_begin; + raft::device_span num_triangles{}; + + VertexPairIterator vertex_pairs_begin{}; __device__ thrust::tuple operator()(edge_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = + auto q_r_pair = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - return pair; + + // Find its position in 'vertex_pairs_begin' + auto itr_q_r = thrust::lower_bound(thrust::seq, + vertex_pairs_begin, + vertex_pairs_begin + num_vertex_pair, // pass the number of vertex pairs + q_r_pair); + + idx = thrust::distance(vertex_pairs_begin, itr_q_r); + auto r = atomicAdd(num_triangles.data()+idx, 1); + } }; -template -struct generate_pr { - raft::device_span intersection_offsets{}; - raft::device_span intersection_indices{}; +template +struct unroll_edge { + raft::device_span num_triangles{}; + VertexPairIterator edge_unrolled{}; VertexPairIterator vertex_pairs_begin{}; + VertexPairIterator vertex_pairs_end{}; + //size_t num_vertex_pair; // rename to num_edges __device__ thrust::tuple operator()(edge_t i) const { - auto itr = thrust::upper_bound( - thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + //auto num_vertex_pair_ = num_vertex_pair; auto pair = - thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - return pair; + thrust::make_tuple(thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i))); + + //auto pair = thrust::make_tuple(1, 2); + + // Find its position in 'vertex_pairs_begin' + //printf("\nnum_vertex_pairs = %d\n", num_vertex_pair); + //auto num_vertex_pair_ = 6; + printf("\nvertex_pairs begin = %p and vertex_pairs end = %p ", vertex_pairs_begin, vertex_pairs_end); + auto itr = thrust::lower_bound(thrust::seq, + vertex_pairs_begin, + //vertex_pairs_begin + 6, // pass the number of vertex pairs + vertex_pairs_end, + //thrust::make_tuple(0, 3) + pair + ); + //printf("\n--num_vertex_pairs = %d\n", num_vertex_pair); + printf("\n itr before %d\n", itr); + auto idx = thrust::distance(vertex_pairs_begin, itr); + printf("\n itr after %d\n", itr); + printf("\nEdge to unroll %d -> %d, idx = %d \n", thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i)), idx); + //printf("\nvertex_pairs_begin %d -> %d, idx = %d \n", thrust::get<0>(*(vertex_pairs_begin + i)), thrust::get<1>(*(vertex_pairs_begin + i)), idx); + auto r = atomicAdd(num_triangles.data() + idx, -1); + } }; + template struct generate_qr { raft::device_span intersection_offsets{}; @@ -420,7 +467,40 @@ void k_truss(raft::handle_t const& handle, thrust::sort( handle.get_thrust_policy(), vertex_pairs_begin, vertex_pairs_begin + edgelist_srcs.size()); - size_t num_vertex_pairs = edgelist_srcs.size(); + size_t num_vertex_pairs = edgelist_srcs.size(); // FIXME: rename to num_edges and always update values when removing edges + + + + + + + + // Dummy + + rmm::device_uvector edgelist_srcs_(num_vertex_pairs, handle.get_stream()); + rmm::device_uvector edgelist_dsts_(num_vertex_pairs, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + edgelist_srcs.begin(), + edgelist_srcs.end(), + edgelist_srcs_.begin()); + + thrust::copy(handle.get_thrust_policy(), + edgelist_dsts.begin(), + edgelist_dsts.end(), + edgelist_dsts_.begin()); + + auto vertex_pairs_begin_ = + thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + + thrust::sort( + handle.get_thrust_policy(), vertex_pairs_begin_, vertex_pairs_begin_ + edgelist_srcs.size()); + + + + + + auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, @@ -430,123 +510,194 @@ void k_truss(raft::handle_t const& handle, vertex_pairs_begin + num_vertex_pairs, std::array{true, true}, do_expensive_check); + + raft::print_device_vector("intersection_offsets ", intersection_offsets.data(), intersection_offsets.size(), std::cout); + raft::print_device_vector("intersection_indices ", intersection_indices.data(), intersection_indices.size(), std::cout); + - // stores all the pairs (p, q), (p, r) and (q, r) - auto vertex_pair_buffer_tmp = allocate_dataframe_buffer>( - intersection_indices.size() * 3, handle.get_stream()); - - // FIXME: optmize this part to not have to iterate over all the edges again - // tabulate with the size of intersection_indices, and call binary search on - // intersection_offsets to get (p, q). - thrust::tabulate( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), - extract_p_q{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin}); - - // tabulate with the size of intersection_indices, and call binary search on - // intersection_offsets to get (p, r). - thrust::tabulate( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + intersection_indices.size(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), - extract_p_r{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin}); - - // tabulate with the size of intersection_indices, and call binary search on - // intersection_offsets to get (q, r). - thrust::tabulate( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (2 * intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp) + (3 * intersection_indices.size()), - extract_q_r{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), intersection_indices.size()), - vertex_pairs_begin}); - - thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_tmp)); - - rmm::device_uvector num_triangles_tmp(3 * intersection_indices.size(), - handle.get_stream()); - - thrust::fill( - handle.get_thrust_policy(), num_triangles_tmp.begin(), num_triangles_tmp.end(), size_t{1}); + // For each edges, run binary search to find all p, q and update its triangle count rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); - - auto vertex_pair_buffer = allocate_dataframe_buffer>( - num_vertex_pairs, handle.get_stream()); - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_tmp), - num_triangles_tmp.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer), - num_triangles.begin(), - thrust::equal_to>{}); + thrust::fill( + handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), size_t{0}); + + // Update the number of triangles of each p, q edges byt heir intersection size + thrust::adjacent_difference(handle.get_thrust_policy(), + intersection_offsets.begin() + 1, + intersection_offsets.end(), + num_triangles.begin()); + + raft::print_device_vector("num_triangles ", num_triangles.data(), num_triangles.size(), std::cout); + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(intersection_indices.size()), + extract_p_r{ + num_vertex_pairs, + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), + vertex_pairs_begin}); + + raft::print_device_vector("p_q + p_r num_triangles ", num_triangles.data(), num_triangles.size(), std::cout); + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(intersection_indices.size()), + extract_q_r{ + num_vertex_pairs, + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), + vertex_pairs_begin}); + + raft::print_device_vector("p_q + p_r + q_r num_triangles", num_triangles.data(), num_triangles.size(), std::cout); + + //raft::print_device_vector("before zipping - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + //raft::print_device_vector("before zipping - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); auto edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + vertex_pairs_begin, num_triangles.begin()); + + + + //raft::print_device_vector("after zipping - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + //raft::print_device_vector("after zipping - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); // 'invalid_edge_first' marks the beginning of the edges to be removed - auto invalid_edge_first = + + auto invalid_edge_last = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, edges_to_num_triangles + num_triangles.size(), [k] __device__(auto e) { auto num_triangles = thrust::get<1>(e); - auto is_in_k_truss = num_triangles == 2; - return num_triangles > k; // FIXME (k-2) * 2 + //printf("\nnum_triangles = %d and k = %d\n", num_triangles, k); + return num_triangles < k + 1; // FIXME (k-2) * 2 }); - + size_t num_invalid_edges{0}; num_invalid_edges = static_cast( - thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); + thrust::distance(edges_to_num_triangles, invalid_edge_last)); - size_t invalid_edge_start_idx{0}; - invalid_edge_start_idx = - static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); // copy invalid edges auto invalid_edges_buffer = allocate_dataframe_buffer>( num_invalid_edges, handle.get_stream()); - + thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer) + invalid_edge_start_idx, - get_dataframe_buffer_end(vertex_pair_buffer), + vertex_pairs_begin, + vertex_pairs_begin + num_invalid_edges, get_dataframe_buffer_begin(invalid_edges_buffer)); + + // sort back the edges. + thrust::sort_by_key(handle.get_thrust_policy(), + vertex_pairs_begin, + vertex_pairs_begin + num_vertex_pairs, + num_triangles.begin()); + + // rezip the iterator + edges_to_num_triangles = thrust::make_zip_iterator( + vertex_pairs_begin, num_triangles.begin()); + + /* + auto invalid_edge_last = + thrust::stable_partition(handle.get_thrust_policy(), + get_dataframe_buffer_begin(invalid_edges_buffer), + get_dataframe_buffer_end(invalid_edges_buffer), + [k, + num_triangles] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles[i] < k; // FIXME (k-2) * 2 + }); + */ - while (num_invalid_edges != 0) { // unroll and remove/mask edges + + raft::print_device_vector("after partitioning - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("after partitioning - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - // case 2: unroll (q, r) + + printf("\nnumber of invalid edges = %d\n", num_invalid_edges); - auto incoming_vertex_pairs_begin = - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + //resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - vertex_t num_incomming_vertex_pairs = edgelist_srcs.size(); - // Sort the 'incoming_vertex_pairs_begin' by 'dst' + /* + size_t invalid_edge_start_idx{0}; + invalid_edge_start_idx = + static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); + */ + + + + /* + num_triangles.resize(num_invalid_edges, handle.get_stream()); + edges_to_num_triangles = thrust::make_zip_iterator( + invalid_edges_buffer, num_triangles.begin()); + */ + + while (num_invalid_edges != 0) { // unroll and remove/mask edges + + // case 2: unroll (q, r) + // FIXME: Update the num_vertex_pairs when removing edges + + + // FIXME: Need a buffer for the incomming vertex pairs because 'edges_to_num_triangles' + // is sorted in a way that matches the number of triangles per edges therefore + // can't use a zip iterator for 'incoming_vertex_pairs'. This adds increease memory + // footprint + auto incoming_vertex_pairs = allocate_dataframe_buffer>( + num_vertex_pairs, handle.get_stream()); // FIXME: This is an upper bound but + + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(incoming_vertex_pairs), + get_dataframe_buffer_end(incoming_vertex_pairs), + [vertex_pairs_begin=vertex_pairs_begin + ] + __device__(edge_t idx){ + printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pairs_begin+idx)), thrust::get<1>(*(vertex_pairs_begin+idx))); + auto edge = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), thrust::get<0>(*(vertex_pairs_begin + idx))); + return edge; + + }); + + raft::print_device_vector("src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); + + + //raft::print_device_vector("after tabulating - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + //raft::print_device_vector("after tabulating - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + + + + // Sort the 'incoming_vertex_pairs' by 'dst' thrust::sort( handle.get_thrust_policy(), - incoming_vertex_pairs_begin, - incoming_vertex_pairs_begin + edgelist_srcs.size()); // FIXME: No need to partition + get_dataframe_buffer_begin(incoming_vertex_pairs), + get_dataframe_buffer_end(incoming_vertex_pairs)); // FIXME: No need to partition + + printf("\nafter sorting\n"); + raft::print_device_vector("src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); // For each (q, r) edge to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - rmm::device_uvector prefix_sum(edgelist_srcs.size() + 1, handle.get_stream()); + // prefix_sum=[0,1,4,6,9,11,13]; -- wrong + // prefix_sum=[0,1,3,5,8,11,14]; -- correct + /* + thrust::sort( + handle.get_thrust_policy(), + edgelist_dsts.begin(), + edgelist_dsts.end()); + */ + rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), - dst_array_begin = edgelist_dsts.begin(), - num_edges = num_incomming_vertex_pairs] __device__(auto idx) { + [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), + num_edges = num_vertex_pairs] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); auto dst = thrust::get<1>(*(invalid_first + idx)); auto dst_array_end = dst_array_begin + num_edges; @@ -558,17 +709,21 @@ void k_truss(raft::handle_t const& handle, auto dist = thrust::distance(itr_lower, itr_upper); return dist; }); - thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); + + //num_invalid_edges = 0; + + auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); auto vertex_pair_buffer_p_r = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - rmm::device_uvector indices(edgelist_srcs.size(), handle.get_stream()); + rmm::device_uvector indices(num_vertex_pairs, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); @@ -576,17 +731,20 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), indices.begin(), indices.end(), - [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), - invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), + [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), + //invalid_first_dst = edgelist_dsts.begin(), + invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + //invalid_first_src = edgelist_srcs.begin(), prefix_sum = prefix_sum.data(), - incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { + num_edges = num_vertex_pairs] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_begin = invalid_first_dst; auto dst_array_end = invalid_first_dst + num_edges; + printf("\ninvalid src = %d, invalid dst = %d, idx = %d\n", src, dst, idx); auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( @@ -598,27 +756,35 @@ void k_truss(raft::handle_t const& handle, thrust::seq, vertex_pair_buffer_p_q + prefix_sum[idx], vertex_pair_buffer_p_q + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment)), src); + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), src); }); thrust::tabulate( thrust::seq, vertex_pair_buffer_p_r + prefix_sum[idx], vertex_pair_buffer_p_r + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment)), dst); + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); + printf("\ngetting all possible incomming edges\n"); + raft::print_device_vector("p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + + + raft::print_device_vector("p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); + raft::print_device_vector("p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); + auto edge_exists = cur_graph_view.has_edge( handle, raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), @@ -646,6 +812,45 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); + raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); + raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); + + raft::print_device_vector("before unrolling - invalid_srcs", edgelist_srcs_.data(), edgelist_srcs_.size(), std::cout); + raft::print_device_vector("before unrolling - invalid_dsts", edgelist_dsts_.data(), edgelist_dsts_.size(), std::cout); + const size_t x = 6; + auto vertex_pairs_end = vertex_pairs_begin + num_vertex_pairs; + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pairs_begin, + vertex_pairs_end, + }); + + raft::print_device_vector("num_triangles after unrolling p_q edges", num_triangles.data(), num_triangles.size(), std::cout); + + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + vertex_pairs_begin, + vertex_pairs_end, + }); + + raft::print_device_vector("num_triangles after unrolling p_r edges", num_triangles.data(), num_triangles.size(), std::cout); + + // create function to unroll number of triangles + + /* rmm::device_uvector decrease_num_triangles_p_q_p_r_tmp(2 * num_edge_exists, handle.get_stream()); thrust::fill(handle.get_thrust_policy(), @@ -694,6 +899,10 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), decrease_num_triangles_p_q_p_r.begin(), thrust::equal_to>{}); + + num_invalid_edges = 0; + raft::print_device_vector("p_q_p_r - src", std::get<0>(vertex_pair_buffer_p_q_p_r_tmp).data(), std::get<0>(vertex_pair_buffer_p_q_p_r_tmp).size(), std::cout); + raft::print_device_vector("p_q_p_r - dst", std::get<1>(vertex_pair_buffer_p_q_p_r_tmp).data(), std::get<1>(vertex_pair_buffer_p_q_p_r_tmp).size(), std::cout); // Add edges from vertex_pair_buffer edge_t prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_p_r); @@ -728,31 +937,48 @@ void k_truss(raft::handle_t const& handle, edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + + edge_t num_vertex_pairs = size_dataframe_buffer(vertex_pair_buffer); + */ + + + + - edge_t num_edges = size_dataframe_buffer(vertex_pair_buffer); + // Put edges with triangle count == 0 in the second partition + // FIXME: revisit all the 'stable_partition' and only used them + // when necessary otherwise simply call 'thrust::partition' auto edges_to_num_triangles_last = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, - edges_to_num_triangles + num_edges, + edges_to_num_triangles + num_vertex_pairs, [] __device__(auto edge_to_num_triangles) { - return thrust::get<1>(edge_to_num_triangles); + return thrust::get<1>(edge_to_num_triangles) > 0; }); auto last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_last); - + // rename the above it to last_edge_with_triangles + /* edges_to_num_triangles = thrust::make_zip_iterator( get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + */ // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edge_list(handle); cugraph::edge_property_t edge_value_output(handle, cur_graph_view); + /* edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, std::get<1>(vertex_pair_buffer).begin()); + */ + // rename the below to edges_with_triangles + edge_list.insert(edgelist_srcs.begin(), + edgelist_srcs.begin() + last_edge_idx, + edgelist_dsts.begin()); cugraph::transform_e( handle, @@ -773,6 +999,12 @@ void k_truss(raft::handle_t const& handle, edgelist_srcs.resize(last_edge_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_idx, handle.get_stream()); + num_vertex_pairs = edgelist_srcs.size(); + + raft::print_device_vector("after removing edges", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("after removing edges", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + + /* thrust::copy(handle.get_thrust_policy(), std::get<0>(vertex_pair_buffer).begin(), std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, @@ -784,30 +1016,91 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.begin()); // Get the new pair of incoming edges - incoming_vertex_pairs_begin = + incoming_vertex_pairs = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + */ + + /* + incoming_vertex_pairs = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + */ + + + resize_dataframe_buffer( + incoming_vertex_pairs, num_vertex_pairs, handle.get_stream()); + + + thrust::tabulate(handle.get_thrust_policy(), + get_dataframe_buffer_begin(incoming_vertex_pairs), + get_dataframe_buffer_end(incoming_vertex_pairs), + [vertex_pairs_begin=vertex_pairs_begin + ] + __device__(edge_t idx){ + printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pairs_begin+idx)), thrust::get<1>(*(vertex_pairs_begin+idx))); + auto edge = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), thrust::get<0>(*(vertex_pairs_begin + idx))); + return edge; + }); + + thrust::sort( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(incoming_vertex_pairs), + get_dataframe_buffer_end(incoming_vertex_pairs)); // FIXME: No need to partition + + + + + printf("the new number of vertex pairs = %d\n", num_vertex_pairs); + // FIXME: Among the invalid edges, identify those that were removed to + // avoid extra panalization. One way to achieve it is by calling thrust::set_intersection + // to filter out the removed edges. However this will require another array. + + vertex_pairs_begin = + thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + + raft::print_device_vector("zip - after removing edges", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("zip - after removing edges", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + + auto invalid_edge_last = + thrust::partition(handle.get_thrust_policy(), + get_dataframe_buffer_begin(invalid_edges_buffer), + get_dataframe_buffer_end(invalid_edges_buffer), + [edge_first = vertex_pairs_begin, // rename to 'edge' + edge_last = vertex_pairs_begin + num_vertex_pairs, + num_edges = num_vertex_pairs] + __device__(auto invalid_edge) { + + auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); + auto idx = thrust::distance(edge_first, itr); + printf("\n src = %d, dst = %d, idx_lower = %d", thrust::get<0>(invalid_edge), thrust::get<1>(invalid_edge), idx); + return idx < num_edges; + }); + + // get_dataframe_buffer_begin(invalid_edges_buffer) + 3 + num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); - // Need to run prefix_sum again to get new ranges because some incoming edges were removed - num_incomming_vertex_pairs = edgelist_srcs.size(); - prefix_sum.resize(num_incomming_vertex_pairs + 1, handle.get_stream()); + resize_dataframe_buffer( + invalid_edges_buffer, num_vertex_pairs, handle.get_stream()); + + printf("\n number of invalid edges = %d\n", num_invalid_edges); + + // Need to run prefix_sum again to get new ranges because some incoming edges were removed + prefix_sum.resize(num_vertex_pairs + 1, handle.get_stream()); - // FIXME: need to sort 'incoming_vertex_pairs_begin'. N0 need because a stable partition was + // FIXME: need to sort 'incoming_vertex_pairs'. N0 need because a stable partition was // performed that preserve the sorting thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(vertex_pair_buffer), - incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, - dst_array_begin = edgelist_dsts.begin(), - src_array_begin = edgelist_srcs.begin(), - num_edges = num_incomming_vertex_pairs] __device__(auto idx) { + [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), + num_edges = num_vertex_pairs] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); auto dst = thrust::get<1>(*(invalid_first + idx)); - auto dst_array_end = - thrust::get<0>(incoming_vertex_pairs_begin.get_iterator_tuple()) + num_edges; + printf("\nafter resizing: src = %d dst, = %d\n", src, dst); + auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops @@ -819,9 +1112,13 @@ void k_truss(raft::handle_t const& handle, thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + + raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - // case 3 unroll (p, r) + num_invalid_edges = 0; + // case 3 unroll (p, r) + /* vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -835,10 +1132,10 @@ void k_truss(raft::handle_t const& handle, [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), prefix_sum = prefix_sum.data(), - incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + incoming_vertex_pairs = incoming_vertex_pairs, vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { + num_edges = num_vertex_pairs] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_begin = invalid_first_dst; @@ -852,24 +1149,24 @@ void k_truss(raft::handle_t const& handle, thrust::seq, vertex_pair_buffer_p_q + prefix_sum[idx], vertex_pair_buffer_p_q + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( - src, thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment))); + src, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); }); thrust::tabulate( thrust::seq, vertex_pair_buffer_q_r + prefix_sum[idx], vertex_pair_buffer_q_r + prefix_sum[idx] + dist, - [incoming_vertex_pairs_begin = incoming_vertex_pairs_begin, + [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs_begin + idx_lower + idx_in_segment)), dst); + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); @@ -1044,7 +1341,7 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.begin()); // Get the new pair of incoming edges - incoming_vertex_pairs_begin = + incoming_vertex_pairs = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) @@ -1271,7 +1568,11 @@ void k_truss(raft::handle_t const& handle, thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); printf("\nfinal number of invalid edges = %d\n", num_invalid_edges); + */ } + + + } } From 113035457979161b4f3503186f81c5123973c79a Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 20 Feb 2024 14:27:46 -0800 Subject: [PATCH 035/155] cleanup code and remove tmp buffers --- cpp/src/community/k_truss_impl.cuh | 292 +++++++++++++++++++++++++++-- 1 file changed, 281 insertions(+), 11 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 9f2e60892df..0ddf1d03d31 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -216,6 +216,27 @@ struct unroll_edge { }; +template +struct generate_pr { + raft::device_span intersection_offsets{}; + raft::device_span intersection_indices{}; + + VertexPairIterator vertex_pairs_begin{}; + + __device__ thrust::tuple operator()(edge_t i) const + { + auto itr = thrust::upper_bound( + thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = + thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + + return pair; + } +}; + + + template struct generate_qr { raft::device_span intersection_offsets{}; @@ -950,6 +971,7 @@ void k_truss(raft::handle_t const& handle, // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' + // Stable_parition is needed because we want to keep src and dst sorted auto edges_to_num_triangles_last = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, @@ -998,6 +1020,7 @@ void k_truss(raft::handle_t const& handle, // resize the 'edgelist_srcs' and 'edgelsit_dst' edgelist_srcs.resize(last_edge_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_idx, handle.get_stream()); + num_triangles.resize(last_edge_idx, handle.get_stream()); num_vertex_pairs = edgelist_srcs.size(); @@ -1053,9 +1076,6 @@ void k_truss(raft::handle_t const& handle, // FIXME: Among the invalid edges, identify those that were removed to // avoid extra panalization. One way to achieve it is by calling thrust::set_intersection // to filter out the removed edges. However this will require another array. - - vertex_pairs_begin = - thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); raft::print_device_vector("zip - after removing edges", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); raft::print_device_vector("zip - after removing edges", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); @@ -1115,10 +1135,7 @@ void k_truss(raft::handle_t const& handle, raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - num_invalid_edges = 0; - // case 3 unroll (p, r) - /* vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -1129,10 +1146,12 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), indices.begin(), indices.end(), - [invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), - invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), + [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), + //invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), + invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + //invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = incoming_vertex_pairs, + incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), num_edges = num_vertex_pairs] __device__(auto idx) { @@ -1169,6 +1188,15 @@ void k_truss(raft::handle_t const& handle, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); + + + printf("\ngetting all possible incomming edges\n"); + raft::print_device_vector("p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + + + raft::print_device_vector("q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); + raft::print_device_vector("q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); edge_exists = cur_graph_view.has_edge( handle, @@ -1181,7 +1209,176 @@ void k_truss(raft::handle_t const& handle, thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), edge_exists.begin()); + + has_edge_last = thrust::stable_partition(handle.get_thrust_policy(), + edge_to_existance, + edge_to_existance + edge_exists.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists; + }); + + num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); + + // After pushing the non-existant edges to the second partition, + // remove them by resizing both vertex pair buffer + resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); + resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); + + raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); + raft::print_device_vector("***q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); + raft::print_device_vector("before unrolling - invalid_srcs", edgelist_srcs_.data(), edgelist_srcs_.size(), std::cout); + raft::print_device_vector("before unrolling - invalid_dsts", edgelist_dsts_.data(), edgelist_dsts_.size(), std::cout); + vertex_pairs_end = vertex_pairs_begin + num_vertex_pairs; + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pairs_begin, + vertex_pairs_end, + }); + + raft::print_device_vector("num_triangles after unrolling p_q edges", num_triangles.data(), num_triangles.size(), std::cout); + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + vertex_pairs_begin, + vertex_pairs_end, + }); + + raft::print_device_vector("num_triangles after unrolling q_r edges", num_triangles.data(), num_triangles.size(), std::cout); + + + + + + + + + + + + + + + + + + + + + + + + + + + + // Put edges with triangle count == 0 in the second partition + // FIXME: revisit all the 'stable_partition' and only used them + // when necessary otherwise simply call 'thrust::partition' + // Stable_parition is needed because we want to keep src and dst sorted + edges_to_num_triangles_last = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_vertex_pairs, + [] __device__(auto edge_to_num_triangles) { + return thrust::get<1>(edge_to_num_triangles) > 0; + }); + + last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_last); + // rename the above it to last_edge_with_triangles + /* + edges_to_num_triangles = thrust::make_zip_iterator( + get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + */ + + /* + edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), + std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, + std::get<1>(vertex_pair_buffer).begin()); + */ + // rename the below to edges_with_triangles + edge_list.clear(); // FIXME: is this needed? + + cugraph::edge_property_t edge_value_output_p_r( + handle, cur_graph_view); + edge_list.insert(edgelist_srcs.begin(), + edgelist_srcs.begin() + last_edge_idx, + edgelist_dsts.begin()); + + cugraph::transform_e( + handle, + cur_graph_view, + edge_list, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + return true; + }, + edge_value_output_p_r.mutable_view(), + false); + + cur_graph_view.attach_edge_mask(edge_value_output_p_r.view()); + + // resize the 'edgelist_srcs' and 'edgelsit_dst' + edgelist_srcs.resize(last_edge_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_idx, handle.get_stream()); + num_triangles.resize(last_edge_idx, handle.get_stream()); + + num_vertex_pairs = edgelist_srcs.size(); + + raft::print_device_vector("after removing edges", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("after removing edges", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /* auto edges_to_num_triangles_p_r_last = thrust::stable_partition(handle.get_thrust_policy(), edge_to_existance, @@ -1343,19 +1540,92 @@ void k_truss(raft::handle_t const& handle, // Get the new pair of incoming edges incoming_vertex_pairs = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + */ // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. + + invalid_edge_last = + thrust::partition(handle.get_thrust_policy(), + get_dataframe_buffer_begin(invalid_edges_buffer), + get_dataframe_buffer_end(invalid_edges_buffer), + [edge_first = vertex_pairs_begin, // rename to 'edge' + edge_last = vertex_pairs_begin + num_vertex_pairs, + num_edges = num_vertex_pairs] + __device__(auto invalid_edge) { + + auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); + auto idx = thrust::distance(edge_first, itr); + printf("\n src = %d, dst = %d, idx_lower = %d", thrust::get<0>(invalid_edge), thrust::get<1>(invalid_edge), idx); + return idx < num_edges; + }); + + // get_dataframe_buffer_begin(invalid_edges_buffer) + 3 + num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); + + + resize_dataframe_buffer( + invalid_edges_buffer, num_vertex_pairs, handle.get_stream()); + + printf("\n number of invalid edges = %d\n", num_invalid_edges); //L1084 + raft::print_device_vector("p->q invalid - src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("p->q invalid - dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + + + auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), + get_dataframe_buffer_begin(invalid_edges_buffer), + get_dataframe_buffer_end(invalid_edges_buffer), std::array{true, true}, do_expensive_check); + + printf("\nintersection size = %d\n", intersection_indices.size()); + if (intersection_indices.size() > 0) { + size_t accumulate_pair_size = + intersection_indices.size(); // rename this var as accumulate_pair_size + + auto vertex_pair_buffer_p_r_edge_p_q = + allocate_dataframe_buffer>( + accumulate_pair_size, handle.get_stream()); + + thrust::tabulate( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q) + generate_pr{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate + }); + + // unroll set of edges one at a time to reduce peak memory + + auto vertex_pair_buffer_q_r_edge_p_q = + allocate_dataframe_buffer>( + accumulate_pair_size, handle.get_stream()); + + thrust::tabulate( + handle.get_thrust_policy(), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + + accumulate_pair_size, + generate_qr{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate + }); + + } + + num_invalid_edges = 0; //****************** debugging purposes + /* // generating (p, r) edge_t vertex_pair_buffer_p_r_edge_p_q_size = intersection_indices.size(); // rename this var as accumulate_pair_size From 66a231097fe40718212fabd054eeb6e649364682 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 09:39:00 -0800 Subject: [PATCH 036/155] update docstrings for consistency --- cpp/include/cugraph/algorithms.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 9be7d714c14..6872db3e4bc 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -1844,7 +1844,7 @@ void weakly_connected_components(raft::handle_t const& handle, enum class k_core_degree_type_t { IN = 0, OUT = 1, INOUT = 2 }; /** - * @brief Compute core numbers of individual vertices from K-core decomposition. + * @brief Compute core numbers of individual vertices from K-Core decomposition. * * The input graph should not have self-loops nor multi-edges. Currently, only undirected graphs are * supported. @@ -1857,11 +1857,11 @@ enum class k_core_degree_type_t { IN = 0, OUT = 1, INOUT = 2 }; * handles to various CUDA libraries) to run graph algorithms. * @param graph_view Graph view object. * @param core_numbers Pointer to the output core number array. - * @param degree_type Dictate whether to compute the K-core decomposition based on in-degrees, + * @param degree_type Dictate whether to compute the K-Core decomposition based on in-degrees, * out-degrees, or in-degrees + out_degrees. - * @param k_first Find K-cores from K = k_first. Any vertices that do not belong to k_first-core + * @param k_first Find K-Cores from K = k_first. Any vertices that do not belong to k_first-core * will have core numbers of 0. - * @param k_last Find K-cores to K = k_last. Any vertices that belong to (k_last)-core will have + * @param k_last Find K-Cores to K = k_last. Any vertices that belong to (k_last)-core will have * their core numbers set to their degrees on k_last-core. * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). */ @@ -1875,7 +1875,7 @@ void core_number(raft::handle_t const& handle, bool do_expensive_check = false); /** - * @brief Extract K Core of a graph + * @brief Extract K-Core of a graph * * @throws cugraph::logic_error when an error occurs. * @@ -1886,7 +1886,7 @@ void core_number(raft::handle_t const& handle, * @param graph_view Graph view object. * @param edge_weight_view Optional view object holding edge weights for @p graph_view. * @param k Order of the core. This value must not be negative. - * @param degree_type Optional parameter to dictate whether to compute the K-core decomposition + * @param degree_type Optional parameter to dictate whether to compute the K-Core decomposition * based on in-degrees, out-degrees, or in-degrees + out_degrees. One of @p * degree_type and @p core_numbers must be specified. * @param core_numbers Optional output from core_number algorithm. If not specified then @@ -2053,7 +2053,7 @@ void triangle_count(raft::handle_t const& handle, * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and * handles to various CUDA libraries) to run graph algorithms. * @param graph_view Graph view object. - * @param k The desired k to be used for extracting the k-truss subgraph + * @param k The desired k to be used for extracting the K-Truss subgraph * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). */ template From f2dd1f3bdfbb6311720a01ac097087748ee51ead Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 09:41:27 -0800 Subject: [PATCH 037/155] update docstrings for consistency --- cpp/include/cugraph/algorithms.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 6872db3e4bc..194f2aa2e3d 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -2043,9 +2043,9 @@ void triangle_count(raft::handle_t const& handle, bool do_expensive_check = false); /* - * @brief Compute k_truss. + * @brief Compute K-Truss. * - * Extract the k_truss subgraph of a graph + * Extract the K-Truss subgraph of a graph * * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. * @tparam edge_t Type of edge identifiers. Needs to be an integral type. From 209335f1ac6bd4e4ba331fa35110160d70105048 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 12:13:51 -0800 Subject: [PATCH 038/155] cleanup code --- cpp/src/community/k_truss_impl.cuh | 1123 +++++----------------------- 1 file changed, 205 insertions(+), 918 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 0ddf1d03d31..c8882c0a041 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -103,16 +103,14 @@ struct extract_p_q { raft::device_span intersection_indices{}; raft::device_span num_triangles{}; - VertexPairIterator vertex_pairs_begin{}; + VertexPairIterator edges{}; __device__ thrust::tuple operator()(edge_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - printf("\nbefore - value = %d, idx = %d\n", static_cast(*(num_triangles.data()+idx)), static_cast(idx)); unsigned int r = atomicInc((unsigned int*)(num_triangles.data()+idx), (unsigned int)1); - printf("\nafter - value = %d, idx = %d, r = %d\n", static_cast(*(num_triangles.data()+idx)), static_cast(idx), static_cast(r)); } }; @@ -124,7 +122,7 @@ struct extract_p_r { raft::device_span intersection_indices{}; raft::device_span num_triangles{}; - VertexPairIterator vertex_pairs_begin{}; + VertexPairIterator edges{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -132,15 +130,14 @@ struct extract_p_r { thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); auto p_r_pair = - thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); - - // Find its position in 'vertex_pairs_begin' + thrust::make_tuple(thrust::get<0>(*(edges + idx)), intersection_indices[i]); + // Find its position in 'edges' auto itr_p_r = thrust::lower_bound(thrust::seq, - vertex_pairs_begin, - vertex_pairs_begin + num_vertex_pair, // pass the number of vertex pairs + edges, + edges + num_vertex_pair, // pass the number of vertex pairs p_r_pair); - idx = thrust::distance(vertex_pairs_begin, itr_p_r); + idx = thrust::distance(edges, itr_p_r); auto r = atomicAdd(num_triangles.data()+idx, 1); } @@ -154,7 +151,7 @@ struct extract_q_r { raft::device_span intersection_indices{}; raft::device_span num_triangles{}; - VertexPairIterator vertex_pairs_begin{}; + VertexPairIterator edges{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -162,15 +159,15 @@ struct extract_q_r { thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); auto q_r_pair = - thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + thrust::make_tuple(thrust::get<1>(*(edges + idx)), intersection_indices[i]); - // Find its position in 'vertex_pairs_begin' + // Find its position in 'edges' auto itr_q_r = thrust::lower_bound(thrust::seq, - vertex_pairs_begin, - vertex_pairs_begin + num_vertex_pair, // pass the number of vertex pairs + edges, + edges + num_vertex_pair, q_r_pair); - idx = thrust::distance(vertex_pairs_begin, itr_q_r); + idx = thrust::distance(edges, itr_q_r); auto r = atomicAdd(num_triangles.data()+idx, 1); } @@ -181,35 +178,23 @@ template struct unroll_edge { raft::device_span num_triangles{}; VertexPairIterator edge_unrolled{}; - VertexPairIterator vertex_pairs_begin{}; - VertexPairIterator vertex_pairs_end{}; - //size_t num_vertex_pair; // rename to num_edges + VertexPairIterator edges{}; + VertexPairIterator edges_last{}; __device__ thrust::tuple operator()(edge_t i) const { - //auto num_vertex_pair_ = num_vertex_pair; auto pair = - thrust::make_tuple(thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i))); - - //auto pair = thrust::make_tuple(1, 2); - - // Find its position in 'vertex_pairs_begin' - //printf("\nnum_vertex_pairs = %d\n", num_vertex_pair); - //auto num_vertex_pair_ = 6; - printf("\nvertex_pairs begin = %p and vertex_pairs end = %p ", vertex_pairs_begin, vertex_pairs_end); + thrust::make_tuple(thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i))); + // Find its position in 'edges' auto itr = thrust::lower_bound(thrust::seq, - vertex_pairs_begin, - //vertex_pairs_begin + 6, // pass the number of vertex pairs - vertex_pairs_end, + edges, + //edges + 6, // pass the number of vertex pairs + edges_last, //thrust::make_tuple(0, 3) pair ); - //printf("\n--num_vertex_pairs = %d\n", num_vertex_pair); - printf("\n itr before %d\n", itr); - auto idx = thrust::distance(vertex_pairs_begin, itr); - printf("\n itr after %d\n", itr); - printf("\nEdge to unroll %d -> %d, idx = %d \n", thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i)), idx); - //printf("\nvertex_pairs_begin %d -> %d, idx = %d \n", thrust::get<0>(*(vertex_pairs_begin + i)), thrust::get<1>(*(vertex_pairs_begin + i)), idx); + + auto idx = thrust::distance(edges, itr); auto r = atomicAdd(num_triangles.data() + idx, -1); } @@ -217,11 +202,11 @@ struct unroll_edge { template -struct generate_pr { +struct generate_p_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator vertex_pairs_begin{}; + VertexPairIterator edges{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -229,7 +214,7 @@ struct generate_pr { thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); auto pair = - thrust::make_tuple(thrust::get<0>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + thrust::make_tuple(thrust::get<0>(*(edges + idx)), intersection_indices[i]); return pair; } @@ -238,11 +223,11 @@ struct generate_pr { template -struct generate_qr { +struct generate_q_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator vertex_pairs_begin{}; + VertexPairIterator edges{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -250,7 +235,7 @@ struct generate_qr { thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); auto pair = - thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), intersection_indices[i]); + thrust::make_tuple(thrust::get<1>(*(edges + idx)), intersection_indices[i]); return pair; } @@ -466,7 +451,7 @@ void k_truss(raft::handle_t const& handle, renumber_map = std::move(tmp_renumber_map); } - // 5. Decompress the resulting graph to an edge list and ind intersection of edge endpoints + // 5. Decompress the resulting graph to an edges list and ind intersection of edges endpoints // for each partition using detail::nbr_intersection { @@ -482,108 +467,57 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - auto vertex_pairs_begin = + auto edges = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); thrust::sort( - handle.get_thrust_policy(), vertex_pairs_begin, vertex_pairs_begin + edgelist_srcs.size()); - - size_t num_vertex_pairs = edgelist_srcs.size(); // FIXME: rename to num_edges and always update values when removing edges - - - - - - - - // Dummy - - rmm::device_uvector edgelist_srcs_(num_vertex_pairs, handle.get_stream()); - rmm::device_uvector edgelist_dsts_(num_vertex_pairs, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - edgelist_srcs.begin(), - edgelist_srcs.end(), - edgelist_srcs_.begin()); - - thrust::copy(handle.get_thrust_policy(), - edgelist_dsts.begin(), - edgelist_dsts.end(), - edgelist_dsts_.begin()); - - auto vertex_pairs_begin_ = - thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); - - thrust::sort( - handle.get_thrust_policy(), vertex_pairs_begin_, vertex_pairs_begin_ + edgelist_srcs.size()); - - - - - + handle.get_thrust_policy(), edges, edges + edgelist_srcs.size()); + size_t num_edges = edgelist_srcs.size(); // FIXME: rename to num_edges and always update values when removing edges + // FIXME: Perform nbr_intersection in chuncks. auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - vertex_pairs_begin, - vertex_pairs_begin + num_vertex_pairs, + edges, + edges + num_edges, std::array{true, true}, do_expensive_check); - - raft::print_device_vector("intersection_offsets ", intersection_offsets.data(), intersection_offsets.size(), std::cout); - raft::print_device_vector("intersection_indices ", intersection_indices.data(), intersection_indices.size(), std::cout); - - - // For each edges, run binary search to find all p, q and update its triangle count - - rmm::device_uvector num_triangles(num_vertex_pairs, handle.get_stream()); + + rmm::device_uvector num_triangles(num_edges, handle.get_stream()); thrust::fill( handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), size_t{0}); - // Update the number of triangles of each p, q edges byt heir intersection size + // Update the number of triangles of each (p, q) edges by looking at their intersection + // size thrust::adjacent_difference(handle.get_thrust_policy(), intersection_offsets.begin() + 1, intersection_offsets.end(), num_triangles.begin()); - - raft::print_device_vector("num_triangles ", num_triangles.data(), num_triangles.size(), std::cout); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - extract_p_r{ - num_vertex_pairs, + extract_p_r{ + num_edges, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), raft::device_span(num_triangles.data(), num_triangles.size()), - vertex_pairs_begin}); + edges}); - raft::print_device_vector("p_q + p_r num_triangles ", num_triangles.data(), num_triangles.size(), std::cout); - thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - extract_q_r{ - num_vertex_pairs, + extract_q_r{ + num_edges, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), raft::device_span(num_triangles.data(), num_triangles.size()), - vertex_pairs_begin}); - - raft::print_device_vector("p_q + p_r + q_r num_triangles", num_triangles.data(), num_triangles.size(), std::cout); - - //raft::print_device_vector("before zipping - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - //raft::print_device_vector("before zipping - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + edges}); auto edges_to_num_triangles = thrust::make_zip_iterator( - vertex_pairs_begin, num_triangles.begin()); - - - - //raft::print_device_vector("after zipping - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - //raft::print_device_vector("after zipping - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + edges, num_triangles.begin()); // 'invalid_edge_first' marks the beginning of the edges to be removed @@ -593,8 +527,7 @@ void k_truss(raft::handle_t const& handle, edges_to_num_triangles + num_triangles.size(), [k] __device__(auto e) { auto num_triangles = thrust::get<1>(e); - //printf("\nnum_triangles = %d and k = %d\n", num_triangles, k); - return num_triangles < k + 1; // FIXME (k-2) * 2 + return num_triangles < k - 2; }); size_t num_invalid_edges{0}; @@ -607,110 +540,51 @@ void k_truss(raft::handle_t const& handle, num_invalid_edges, handle.get_stream()); thrust::copy(handle.get_thrust_policy(), - vertex_pairs_begin, - vertex_pairs_begin + num_invalid_edges, + edges, + edges + num_invalid_edges, get_dataframe_buffer_begin(invalid_edges_buffer)); - // sort back the edges. + // sort back the edges as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), - vertex_pairs_begin, - vertex_pairs_begin + num_vertex_pairs, + edges, + edges + num_edges, num_triangles.begin()); - - // rezip the iterator - edges_to_num_triangles = thrust::make_zip_iterator( - vertex_pairs_begin, num_triangles.begin()); - /* - auto invalid_edge_last = - thrust::stable_partition(handle.get_thrust_policy(), - get_dataframe_buffer_begin(invalid_edges_buffer), - get_dataframe_buffer_end(invalid_edges_buffer), - [k, - num_triangles] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles[i] < k; // FIXME (k-2) * 2 - }); - */ - - raft::print_device_vector("after partitioning - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("after partitioning - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - - - printf("\nnumber of invalid edges = %d\n", num_invalid_edges); - - //resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - - - /* - size_t invalid_edge_start_idx{0}; - invalid_edge_start_idx = - static_cast(thrust::distance(edges_to_num_triangles, invalid_edge_first)); - */ - - - - /* - num_triangles.resize(num_invalid_edges, handle.get_stream()); - edges_to_num_triangles = thrust::make_zip_iterator( - invalid_edges_buffer, num_triangles.begin()); - */ - - while (num_invalid_edges != 0) { // unroll and remove/mask edges + // Unroll and remove/mask edges as long as there are still edges part + // of the K-Truss. + while (num_invalid_edges != num_edges) { // case 2: unroll (q, r) - // FIXME: Update the num_vertex_pairs when removing edges + // FIXME: Update the num_edges when removing edges // FIXME: Need a buffer for the incomming vertex pairs because 'edges_to_num_triangles' - // is sorted in a way that matches the number of triangles per edges therefore - // can't use a zip iterator for 'incoming_vertex_pairs'. This adds increease memory + // is sorted in a way that matches the number of triangles per edges therefore, + // can't use a zip iterator for 'incoming_vertex_pairs'. This adds increase memory // footprint auto incoming_vertex_pairs = allocate_dataframe_buffer>( - num_vertex_pairs, handle.get_stream()); // FIXME: This is an upper bound but + num_edges, handle.get_stream()); thrust::tabulate(handle.get_thrust_policy(), get_dataframe_buffer_begin(incoming_vertex_pairs), get_dataframe_buffer_end(incoming_vertex_pairs), - [vertex_pairs_begin=vertex_pairs_begin + [edges=edges ] __device__(edge_t idx){ - printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pairs_begin+idx)), thrust::get<1>(*(vertex_pairs_begin+idx))); - auto edge = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), thrust::get<0>(*(vertex_pairs_begin + idx))); + auto edge = thrust::make_tuple(thrust::get<1>(*(edges + idx)), thrust::get<0>(*(edges + idx))); return edge; }); - raft::print_device_vector("src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); - - - //raft::print_device_vector("after tabulating - invalid_srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - //raft::print_device_vector("after tabulating - invalid_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - - - // Sort the 'incoming_vertex_pairs' by 'dst' thrust::sort( handle.get_thrust_policy(), get_dataframe_buffer_begin(incoming_vertex_pairs), - get_dataframe_buffer_end(incoming_vertex_pairs)); // FIXME: No need to partition + get_dataframe_buffer_end(incoming_vertex_pairs)); - printf("\nafter sorting\n"); - raft::print_device_vector("src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); - - // For each (q, r) edge to unroll, find the incoming edges to 'r' let's say from 'p' and + // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - // prefix_sum=[0,1,4,6,9,11,13]; -- wrong - // prefix_sum=[0,1,3,5,8,11,14]; -- correct - /* - thrust::sort( - handle.get_thrust_policy(), - edgelist_dsts.begin(), - edgelist_dsts.end()); - */ rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), @@ -718,7 +592,7 @@ void k_truss(raft::handle_t const& handle, prefix_sum.end(), [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), - num_edges = num_vertex_pairs] __device__(auto idx) { + num_edges = num_edges] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); auto dst = thrust::get<1>(*(invalid_first + idx)); auto dst_array_end = dst_array_begin + num_edges; @@ -732,11 +606,6 @@ void k_truss(raft::handle_t const& handle, }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - - raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - - //num_invalid_edges = 0; - auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -744,7 +613,7 @@ void k_truss(raft::handle_t const& handle, auto vertex_pair_buffer_p_r = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - rmm::device_uvector indices(num_vertex_pairs, handle.get_stream()); + rmm::device_uvector indices(num_edges, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); @@ -753,20 +622,16 @@ void k_truss(raft::handle_t const& handle, indices.begin(), indices.end(), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - //invalid_first_dst = edgelist_dsts.begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - //invalid_first_src = edgelist_srcs.begin(), prefix_sum = prefix_sum.data(), incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - num_edges = num_vertex_pairs] __device__(auto idx) { + num_edges = num_edges] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_begin = invalid_first_dst; auto dst_array_end = invalid_first_dst + num_edges; - printf("\ninvalid src = %d, invalid dst = %d, idx = %d\n", src, dst, idx); - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range @@ -798,14 +663,6 @@ void k_truss(raft::handle_t const& handle, }); }); - printf("\ngetting all possible incomming edges\n"); - raft::print_device_vector("p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - - - raft::print_device_vector("p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); - raft::print_device_vector("p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); - auto edge_exists = cur_graph_view.has_edge( handle, raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), @@ -818,7 +675,7 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), edge_exists.begin()); - auto has_edge_last = thrust::stable_partition(handle.get_thrust_policy(), + auto has_edge_last = thrust::partition(handle.get_thrust_policy(), edge_to_existance, edge_to_existance + edge_exists.size(), [] __device__(auto e) { @@ -833,179 +690,60 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); - raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); - raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); - - raft::print_device_vector("before unrolling - invalid_srcs", edgelist_srcs_.data(), edgelist_srcs_.size(), std::cout); - raft::print_device_vector("before unrolling - invalid_dsts", edgelist_dsts_.data(), edgelist_dsts_.size(), std::cout); - const size_t x = 6; - auto vertex_pairs_end = vertex_pairs_begin + num_vertex_pairs; + auto edges_last = edges + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pairs_begin, - vertex_pairs_end, + edges, + edges_last, }); - raft::print_device_vector("num_triangles after unrolling p_q edges", num_triangles.data(), num_triangles.size(), std::cout); - - thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - vertex_pairs_begin, - vertex_pairs_end, + edges, + edges_last, }); - - raft::print_device_vector("num_triangles after unrolling p_r edges", num_triangles.data(), num_triangles.size(), std::cout); - - // create function to unroll number of triangles - - /* - rmm::device_uvector decrease_num_triangles_p_q_p_r_tmp(2 * num_edge_exists, - handle.get_stream()); - thrust::fill(handle.get_thrust_policy(), - decrease_num_triangles_p_q_p_r_tmp.begin(), - decrease_num_triangles_p_q_p_r_tmp.end(), - size_t{-1}); - - auto vertex_pair_buffer_p_q_p_r_tmp = - allocate_dataframe_buffer>(2 * num_edge_exists, - handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp)); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_r), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp) + num_edge_exists); - - thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), - get_dataframe_buffer_end( - vertex_pair_buffer_p_q_p_r_tmp)); // FIXME: Remove duplicated edges - - // FIXME: No need for a count if we do only one reduce_by_key at the end - // (vertex_pair_buffer_p_q_p_r + vertex_pair_buffer) Because the reduction - // of both will lead to a pair_buffer of size size_of(vertex_pair_buffer) - // Also no need for a tmp buffer 'vertex_pair_buffer_p_q_p_r_tmp' - auto count_p_q_p_r = - thrust::unique_count(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp)); - - auto vertex_pair_buffer_p_q_p_r = - allocate_dataframe_buffer>(count_p_q_p_r, - handle.get_stream()); - rmm::device_uvector decrease_num_triangles_p_q_p_r(count_p_q_p_r, - handle.get_stream()); - - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r_tmp), - decrease_num_triangles_p_q_p_r_tmp.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), - decrease_num_triangles_p_q_p_r.begin(), - thrust::equal_to>{}); - - num_invalid_edges = 0; - raft::print_device_vector("p_q_p_r - src", std::get<0>(vertex_pair_buffer_p_q_p_r_tmp).data(), std::get<0>(vertex_pair_buffer_p_q_p_r_tmp).size(), std::cout); - raft::print_device_vector("p_q_p_r - dst", std::get<1>(vertex_pair_buffer_p_q_p_r_tmp).data(), std::get<1>(vertex_pair_buffer_p_q_p_r_tmp).size(), std::cout); - - // Add edges from vertex_pair_buffer - edge_t prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_p_r); - edge_t accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; - - resize_dataframe_buffer( - vertex_pair_buffer_p_q_p_r, accumulate_pair_size, handle.get_stream()); - decrease_num_triangles_p_q_p_r.resize(accumulate_pair_size, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r) + prev_size); - - thrust::copy(handle.get_thrust_policy(), - num_triangles.begin(), - num_triangles.end(), - decrease_num_triangles_p_q_p_r.begin() + prev_size); - - thrust::sort_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), - decrease_num_triangles_p_q_p_r.begin()); - - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_p_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_p_r), - decrease_num_triangles_p_q_p_r.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer), - num_triangles.begin(), - thrust::equal_to>{}); - - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - - edge_t num_vertex_pairs = size_dataframe_buffer(vertex_pair_buffer); - */ - - - - - - // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' // Stable_parition is needed because we want to keep src and dst sorted - auto edges_to_num_triangles_last = + // so that we don't need to sort it again. + // FIXME: Create a rountine capturing L719:L763 as this block of code gets + // repeated + auto last_edge_with_triangles = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, - edges_to_num_triangles + num_vertex_pairs, + edges_to_num_triangles + num_edges, [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); - auto last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_last); + auto last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); // rename the above it to last_edge_with_triangles - /* - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - */ - // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag - cugraph::edge_bucket_t edge_list(handle); + // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag + cugraph::edge_bucket_t edges_with_triangles(handle); cugraph::edge_property_t edge_value_output(handle, cur_graph_view); - /* - edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - std::get<1>(vertex_pair_buffer).begin()); - */ + // rename the below to edges_with_triangles - edge_list.insert(edgelist_srcs.begin(), - edgelist_srcs.begin() + last_edge_idx, - edgelist_dsts.begin()); + edges_with_triangles.insert(edgelist_srcs.begin(), + edgelist_srcs.begin() + last_edge_with_triangles_idx, + edgelist_dsts.begin()); cugraph::transform_e( handle, cur_graph_view, - edge_list, + edges_with_triangles, cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), @@ -1018,49 +756,22 @@ void k_truss(raft::handle_t const& handle, cur_graph_view.attach_edge_mask(edge_value_output.view()); // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_idx, handle.get_stream()); - num_triangles.resize(last_edge_idx, handle.get_stream()); - - num_vertex_pairs = edgelist_srcs.size(); - - raft::print_device_vector("after removing edges", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("after removing edges", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - - /* - thrust::copy(handle.get_thrust_policy(), - std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_srcs.begin()); - - thrust::copy(handle.get_thrust_policy(), - std::get<1>(vertex_pair_buffer).begin(), - std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_dsts.begin()); - - // Get the new pair of incoming edges - incoming_vertex_pairs = - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - */ - - /* - incoming_vertex_pairs = - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - */ - + edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); + num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); + + num_edges = edgelist_srcs.size(); resize_dataframe_buffer( - incoming_vertex_pairs, num_vertex_pairs, handle.get_stream()); - + incoming_vertex_pairs, num_edges, handle.get_stream()); thrust::tabulate(handle.get_thrust_policy(), get_dataframe_buffer_begin(incoming_vertex_pairs), get_dataframe_buffer_end(incoming_vertex_pairs), - [vertex_pairs_begin=vertex_pairs_begin + [edges=edges ] __device__(edge_t idx){ - printf("\n src = %d, dst = %d\n", thrust::get<0>(*(vertex_pairs_begin+idx)), thrust::get<1>(*(vertex_pairs_begin+idx))); - auto edge = thrust::make_tuple(thrust::get<1>(*(vertex_pairs_begin + idx)), thrust::get<0>(*(vertex_pairs_begin + idx))); + auto edge = thrust::make_tuple(thrust::get<1>(*(edges + idx)), thrust::get<0>(*(edges + idx))); return edge; }); @@ -1069,57 +780,46 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(incoming_vertex_pairs), get_dataframe_buffer_end(incoming_vertex_pairs)); // FIXME: No need to partition - - - - printf("the new number of vertex pairs = %d\n", num_vertex_pairs); // FIXME: Among the invalid edges, identify those that were removed to // avoid extra panalization. One way to achieve it is by calling thrust::set_intersection // to filter out the removed edges. However this will require another array. - raft::print_device_vector("zip - after removing edges", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("zip - after removing edges", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - + // Find the intersection of 'invalid_edges_buffer' and 'edges' to extract the remaining invalid + // edges that still need to be processed. Didn't used thrust::set_intersection because I didn't + // want to create a temporary array auto invalid_edge_last = thrust::partition(handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), get_dataframe_buffer_end(invalid_edges_buffer), - [edge_first = vertex_pairs_begin, // rename to 'edge' - edge_last = vertex_pairs_begin + num_vertex_pairs, - num_edges = num_vertex_pairs] + [edge_first = edges, // rename to 'edges' + edge_last = edges + num_edges, + num_edges = num_edges] __device__(auto invalid_edge) { - auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); auto idx = thrust::distance(edge_first, itr); - printf("\n src = %d, dst = %d, idx_lower = %d", thrust::get<0>(invalid_edge), thrust::get<1>(invalid_edge), idx); return idx < num_edges; }); - // get_dataframe_buffer_begin(invalid_edges_buffer) + 3 num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); resize_dataframe_buffer( - invalid_edges_buffer, num_vertex_pairs, handle.get_stream()); - - printf("\n number of invalid edges = %d\n", num_invalid_edges); + invalid_edges_buffer, num_edges, handle.get_stream()); // Need to run prefix_sum again to get new ranges because some incoming edges were removed - prefix_sum.resize(num_vertex_pairs + 1, handle.get_stream()); + prefix_sum.resize(num_edges + 1, handle.get_stream()); - // FIXME: need to sort 'incoming_vertex_pairs'. N0 need because a stable partition was + // FIXME: need to sort 'incoming_vertex_pairs'. No need because a stable partition was // performed that preserve the sorting - thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), - num_edges = num_vertex_pairs] __device__(auto idx) { + num_edges = num_edges] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); auto dst = thrust::get<1>(*(invalid_first + idx)); - printf("\nafter resizing: src = %d dst, = %d\n", src, dst); auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = @@ -1133,8 +833,6 @@ void k_truss(raft::handle_t const& handle, thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - // case 3 unroll (p, r) vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -1147,14 +845,12 @@ void k_truss(raft::handle_t const& handle, indices.begin(), indices.end(), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - //invalid_first_dst = std::get<1>(vertex_pair_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - //invalid_first_src = std::get<0>(vertex_pair_buffer).begin(), prefix_sum = prefix_sum.data(), incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - num_edges = num_vertex_pairs] __device__(auto idx) { + num_edges = num_edges] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_begin = invalid_first_dst; @@ -1188,15 +884,6 @@ void k_truss(raft::handle_t const& handle, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); - - - printf("\ngetting all possible incomming edges\n"); - raft::print_device_vector("p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - - - raft::print_device_vector("q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); - raft::print_device_vector("q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); edge_exists = cur_graph_view.has_edge( handle, @@ -1211,12 +898,12 @@ void k_truss(raft::handle_t const& handle, edge_exists.begin()); has_edge_last = thrust::stable_partition(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edge_exists.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists; - }); + edge_to_existance, + edge_to_existance + edge_exists.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists; + }); num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); @@ -1225,293 +912,54 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); - raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); - raft::print_device_vector("***q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); - - raft::print_device_vector("before unrolling - invalid_srcs", edgelist_srcs_.data(), edgelist_srcs_.size(), std::cout); - raft::print_device_vector("before unrolling - invalid_dsts", edgelist_dsts_.data(), edgelist_dsts_.size(), std::cout); - vertex_pairs_end = vertex_pairs_begin + num_vertex_pairs; + edges_last = edges + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pairs_begin, - vertex_pairs_end, + edges, + edges_last, }); - raft::print_device_vector("num_triangles after unrolling p_q edges", num_triangles.data(), num_triangles.size(), std::cout); - thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - //num_vertex_pairs, FIXME: Passing the 'num_vertex_pairs' instead of 'vertex_pairs_end_' yield wrong results + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - vertex_pairs_begin, - vertex_pairs_end, + edges, + edges_last, }); - - raft::print_device_vector("num_triangles after unrolling q_r edges", num_triangles.data(), num_triangles.size(), std::cout); - - - - - - - - - - - - - - - - - - - - - - - - - - // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' // Stable_parition is needed because we want to keep src and dst sorted - edges_to_num_triangles_last = + last_edge_with_triangles = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, - edges_to_num_triangles + num_vertex_pairs, + edges_to_num_triangles + num_edges, [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); - last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_last); + last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); // rename the above it to last_edge_with_triangles - /* - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - */ - - /* - edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - std::get<1>(vertex_pair_buffer).begin()); - */ // rename the below to edges_with_triangles - edge_list.clear(); // FIXME: is this needed? + edges_with_triangles.clear(); // FIXME: is this needed? cugraph::edge_property_t edge_value_output_p_r( handle, cur_graph_view); - edge_list.insert(edgelist_srcs.begin(), - edgelist_srcs.begin() + last_edge_idx, + edges_with_triangles.insert(edgelist_srcs.begin(), + edgelist_srcs.begin() + last_edge_with_triangles_idx, edgelist_dsts.begin()); cugraph::transform_e( handle, cur_graph_view, - edge_list, - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - return true; - }, - edge_value_output_p_r.mutable_view(), - false); - - cur_graph_view.attach_edge_mask(edge_value_output_p_r.view()); - - // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_idx, handle.get_stream()); - num_triangles.resize(last_edge_idx, handle.get_stream()); - - num_vertex_pairs = edgelist_srcs.size(); - - raft::print_device_vector("after removing edges", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("after removing edges", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - auto edges_to_num_triangles_p_r_last = - thrust::stable_partition(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edge_exists.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists; - }); - - num_edge_exists = thrust::distance(edge_to_existance, edges_to_num_triangles_p_r_last); - - // Resize both 'vertex_pair_buffer' - resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); - resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); - - rmm::device_uvector decrease_num_triangles_p_q_q_r_tmp(2 * num_edge_exists, - handle.get_stream()); - thrust::fill(handle.get_thrust_policy(), - decrease_num_triangles_p_q_q_r_tmp.begin(), - decrease_num_triangles_p_q_q_r_tmp.end(), - size_t{-1}); - - auto vertex_pair_buffer_p_q_q_r_tmp = - allocate_dataframe_buffer>(2 * num_edge_exists, - handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp)); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_q_r), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp) + num_edge_exists); - - thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end( - vertex_pair_buffer_p_q_q_r_tmp)); // FIXME: Remove duplicated edges - - // FIXME: No need for a count if we do only one reduce_by_key at the end - // (vertex_pair_buffer_p_q_q_r + vertex_pair_buffer) Because the reduction of both will lead - // to a pair_buffer of size size_of(vertex_pair_buffer) Also no need for a tmp buffer - // 'vertex_pair_buffer_p_q_q_r_tmp' - auto count_p_q_q_r = - thrust::unique_count(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp)); - - auto vertex_pair_buffer_p_q_q_r = - allocate_dataframe_buffer>(count_p_q_q_r, - handle.get_stream()); - rmm::device_uvector decrease_num_triangles_p_q_q_r(count_p_q_q_r, - handle.get_stream()); - - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r_tmp), - decrease_num_triangles_p_q_q_r_tmp.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), - decrease_num_triangles_p_q_q_r.begin(), - thrust::equal_to>{}); - - // Add edges from vertex_pair_buffer - prev_size = size_dataframe_buffer(vertex_pair_buffer_p_q_q_r); - accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; - - resize_dataframe_buffer( - vertex_pair_buffer_p_q_q_r, accumulate_pair_size, handle.get_stream()); - decrease_num_triangles_p_q_q_r.resize(accumulate_pair_size, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r) + prev_size); - - thrust::copy(handle.get_thrust_policy(), - num_triangles.begin(), - num_triangles.end(), - decrease_num_triangles_p_q_q_r.begin() + prev_size); - - thrust::sort_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), - decrease_num_triangles_p_q_q_r.begin()); - - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_q_q_r), - decrease_num_triangles_p_q_q_r.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer), - num_triangles.begin(), - thrust::equal_to>{}); - - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - - num_edges = size_dataframe_buffer(vertex_pair_buffer); - - // FIXME: This variable cannot be reassigned. rename them appropriately - // Put edges with triangle count == 0 in the second partition - // FIXME: rename 'edges_to_num_triangles_p_r_last_'. - auto edges_to_num_triangles_p_r_last_ = - thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_edges, - [] __device__(auto edge_to_num_triangles) { - return thrust::get<1>(edge_to_num_triangles); - }); - - last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_p_r_last_); - - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - - // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag - // NOTE: This needs to be a seperate variable 'edge_list' - // cugraph::edge_bucket_tedge_list(handle); - // FIXME: seem - edge_list.clear(); // FIXME: is this needed? - - cugraph::edge_property_t edge_value_output_p_r( - handle, cur_graph_view); - - edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - std::get<1>(vertex_pair_buffer).begin()); - - cugraph::transform_e( - handle, - cur_graph_view, - edge_list, + edges_with_triangles, cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), @@ -1524,55 +972,33 @@ void k_truss(raft::handle_t const& handle, cur_graph_view.attach_edge_mask(edge_value_output_p_r.view()); // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_idx, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_srcs.begin()); - - thrust::copy(handle.get_thrust_policy(), - std::get<1>(vertex_pair_buffer).begin(), - std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_dsts.begin()); + edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); + num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - // Get the new pair of incoming edges - incoming_vertex_pairs = - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - */ + num_edges = edgelist_srcs.size(); // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. - invalid_edge_last = thrust::partition(handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), get_dataframe_buffer_end(invalid_edges_buffer), - [edge_first = vertex_pairs_begin, // rename to 'edge' - edge_last = vertex_pairs_begin + num_vertex_pairs, - num_edges = num_vertex_pairs] + [edge_first = edges, // rename to 'edges' + edge_last = edges + num_edges, + num_edges = num_edges] __device__(auto invalid_edge) { auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); auto idx = thrust::distance(edge_first, itr); - printf("\n src = %d, dst = %d, idx_lower = %d", thrust::get<0>(invalid_edge), thrust::get<1>(invalid_edge), idx); return idx < num_edges; }); - // get_dataframe_buffer_begin(invalid_edges_buffer) + 3 num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); - resize_dataframe_buffer( - invalid_edges_buffer, num_vertex_pairs, handle.get_stream()); - - printf("\n number of invalid edges = %d\n", num_invalid_edges); //L1084 - raft::print_device_vector("p->q invalid - src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("p->q invalid - dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - - + invalid_edges_buffer, num_edges, handle.get_stream()); auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, @@ -1583,221 +1009,93 @@ void k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - printf("\nintersection size = %d\n", intersection_indices.size()); - if (intersection_indices.size() > 0) { - size_t accumulate_pair_size = - intersection_indices.size(); // rename this var as accumulate_pair_size - - auto vertex_pair_buffer_p_r_edge_p_q = - allocate_dataframe_buffer>( - accumulate_pair_size, handle.get_stream()); - - thrust::tabulate( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q) - generate_pr{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), - intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate - }); - - // unroll set of edges one at a time to reduce peak memory - - auto vertex_pair_buffer_q_r_edge_p_q = - allocate_dataframe_buffer>( - accumulate_pair_size, handle.get_stream()); - - thrust::tabulate( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + - accumulate_pair_size, - generate_qr{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), - intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate - }); - - } - - num_invalid_edges = 0; //****************** debugging purposes - - /* - // generating (p, r) - edge_t vertex_pair_buffer_p_r_edge_p_q_size = - intersection_indices.size(); // rename this var as accumulate_pair_size - rmm::device_uvector num_triangles_p_r(vertex_pair_buffer_p_r_edge_p_q_size, - handle.get_stream()); // FIXME: Rename this - thrust::fill( - handle.get_thrust_policy(), num_triangles_p_r.begin(), num_triangles_p_r.end(), size_t{-1}); + size_t accumulate_pair_size = + intersection_indices.size(); + auto vertex_pair_buffer_p_r_edge_p_q = allocate_dataframe_buffer>( - vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); - + accumulate_pair_size, handle.get_stream()); + thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q) + - vertex_pair_buffer_p_r_edge_p_q_size, - generate_pr{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), - intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate - }); + get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), + generate_p_r{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(invalid_edges_buffer) + }); + + edges_last = edges + num_edges; + num_edge_exists = accumulate_pair_size; + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + edges, + edges_last, + }); - // generating (q, r) - edge_t vertex_pair_buffer_q_r_edge_p_q_size = - intersection_indices.size(); // rename this var as accumulate_pair_size - rmm::device_uvector num_triangles_q_r(vertex_pair_buffer_q_r_edge_p_q_size, - handle.get_stream()); // FIXME: Rename this - thrust::fill( - handle.get_thrust_policy(), num_triangles_q_r.begin(), num_triangles_q_r.end(), size_t{-1}); auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>( - vertex_pair_buffer_q_r_edge_p_q_size, handle.get_stream()); + accumulate_pair_size, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + - vertex_pair_buffer_q_r_edge_p_q_size, - generate_qr{ + accumulate_pair_size, + generate_q_r{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(vertex_pair_buffer) // FIXME: verify this is accurate + get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - - auto vertex_pair_buffer_p_r_q_r_tmp = - allocate_dataframe_buffer>( - 2 * vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); - - rmm::device_uvector decrease_num_triangles_p_r_q_r_tmp( - 2 * vertex_pair_buffer_p_r_edge_p_q_size, handle.get_stream()); - thrust::fill(handle.get_thrust_policy(), - decrease_num_triangles_p_r_q_r_tmp.begin(), - decrease_num_triangles_p_r_q_r_tmp.end(), - size_t{-1}); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp)); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_end(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp) + - vertex_pair_buffer_p_r_edge_p_q_size); - - thrust::sort(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), - get_dataframe_buffer_end( - vertex_pair_buffer_p_r_q_r_tmp)); // FIXME: Remove duplicated edges - - auto count_p_r_q_r = - thrust::unique_count(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r_tmp)); - - auto vertex_pair_buffer_p_r_q_r = - allocate_dataframe_buffer>(count_p_r_q_r, - handle.get_stream()); - rmm::device_uvector decrease_num_triangles_p_r_q_r(count_p_r_q_r, - handle.get_stream()); - - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r_tmp), - get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r_tmp), - decrease_num_triangles_p_r_q_r_tmp.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r), - decrease_num_triangles_p_r_q_r.begin(), - thrust::equal_to>{}); - - // Add edges from vertex_pair_buffer - prev_size = size_dataframe_buffer(vertex_pair_buffer_p_r_q_r); - accumulate_pair_size = size_dataframe_buffer(vertex_pair_buffer) + prev_size; - - resize_dataframe_buffer( - vertex_pair_buffer_p_r_q_r, accumulate_pair_size, handle.get_stream()); - decrease_num_triangles_p_r_q_r.resize(accumulate_pair_size, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer), - get_dataframe_buffer_end(vertex_pair_buffer), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r) + prev_size); - - thrust::copy(handle.get_thrust_policy(), - num_triangles.begin(), - num_triangles.end(), - decrease_num_triangles_p_r_q_r.begin() + prev_size); - - thrust::sort_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r), - decrease_num_triangles_p_r_q_r.begin()); - - thrust::reduce_by_key(handle.get_thrust_policy(), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_q_r), - get_dataframe_buffer_end(vertex_pair_buffer_p_r_q_r), - decrease_num_triangles_p_r_q_r.begin(), - get_dataframe_buffer_begin(vertex_pair_buffer), - num_triangles.begin(), - thrust::equal_to>{}); - - raft::print_device_vector("'vertex_pair_buffer' - src", - std::get<0>(vertex_pair_buffer).data(), - std::get<0>(vertex_pair_buffer).size(), - std::cout); - raft::print_device_vector("'vertex_pair_buffer' - dst", - std::get<1>(vertex_pair_buffer).data(), - std::get<1>(vertex_pair_buffer).size(), - std::cout); - raft::print_device_vector( - "'num_triangles_p_r_q_r' - ", num_triangles.data(), num_triangles.size(), std::cout); - - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); - - num_edges = size_dataframe_buffer(vertex_pair_buffer); - - // FIXME: This variable cannot be reassigned. rename them appropriately + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + edges, + edges_last, + }); + // Put edges with triangle count == 0 in the second partition - // FIXME: rename 'edges_to_num_triangles_p_r_last_0'. - auto edges_to_num_triangles_p_r_last_0 = + // FIXME: revisit all the 'stable_partition' and only used them + // when necessary otherwise simply call 'thrust::partition' + // Stable_parition is needed because we want to keep src and dst sorted + // so that we don't need to sort it again. + last_edge_with_triangles = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, edges_to_num_triangles + num_edges, [] __device__(auto edge_to_num_triangles) { - return thrust::get<1>(edge_to_num_triangles); + return thrust::get<1>(edge_to_num_triangles) > 0; }); - last_edge_idx = thrust::distance(edges_to_num_triangles, edges_to_num_triangles_p_r_last_0); - - edges_to_num_triangles = thrust::make_zip_iterator( - get_dataframe_buffer_begin(vertex_pair_buffer), num_triangles.begin()); + last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); + // rename the above it to last_edge_with_triangles - // Note: ensure 'edge_list' and 'cur_graph_view' have the same transpose flag - // NOTE: This needs to be a seperate variable 'edge_list' - // cugraph::edge_bucket_tedge_list(handle); - // FIXME: seem - edge_list.clear(); // FIXME: is this needed? + // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag + edges_with_triangles.clear(); // FIXME: is this needed? - cugraph::edge_property_t edge_value_output_p_q( - handle, cur_graph_view); + cugraph::edge_property_t edge_value_output_p_q(handle, + cur_graph_view); - edge_list.insert(std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - std::get<1>(vertex_pair_buffer).begin()); + // rename the below to edges_with_triangles + edges_with_triangles.insert(edgelist_srcs.begin(), + edgelist_srcs.begin() + last_edge_with_triangles_idx, + edgelist_dsts.begin()); cugraph::transform_e( handle, cur_graph_view, - edge_list, + edges_with_triangles, cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), @@ -1810,35 +1108,24 @@ void k_truss(raft::handle_t const& handle, cur_graph_view.attach_edge_mask(edge_value_output_p_q.view()); // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_idx, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - std::get<0>(vertex_pair_buffer).begin(), - std::get<0>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_srcs.begin()); - - thrust::copy(handle.get_thrust_policy(), - std::get<1>(vertex_pair_buffer).begin(), - std::get<1>(vertex_pair_buffer).begin() + last_edge_idx, - edgelist_dsts.begin()); + edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); + num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - // Find if there exist edges with invalid triangle counts - auto invalid_edge_first = thrust::stable_partition( - handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - auto is_in_k_truss = num_triangles == 2; - return (num_triangles > k) || (num_triangles == 0); // FIXME (k-2) * 2 - }); + num_edges = edgelist_srcs.size(); - num_invalid_edges = static_cast( - thrust::distance(invalid_edge_first, edges_to_num_triangles + num_triangles.size())); + //num_invalid_edges = 0; //****************** debugging purposes + } - printf("\nfinal number of invalid edges = %d\n", num_invalid_edges); - */ + if (num_invalid_edges == num_edges) { + // return empty graph view + std::optional> empty_graph_view{std::nullopt}; + // FIXME: To be updated + // return empty_graph_view; + } + else{ + // FIXME: To be updated + // return cur_graph_view; } From 17a70133938dc8b344fd4a52e2cc6967eefe2e13 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 13:04:42 -0800 Subject: [PATCH 039/155] update stopping criteria --- cpp/src/community/k_truss_impl.cuh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index c8882c0a041..7ce807813da 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -552,8 +552,9 @@ void k_truss(raft::handle_t const& handle, // Unroll and remove/mask edges as long as there are still edges part - // of the K-Truss. - while (num_invalid_edges != num_edges) { + // of the K-Truss. + auto num_valid_edges = num_edges - num_invalid_edges; + while ((num_valid_edges != 0) && (num_invalid_edges !=0)) { // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges From e61d1b8e4f6979a11e5574d580d0050f410b4f76 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 13:13:21 -0800 Subject: [PATCH 040/155] fix typo --- cpp/src/community/k_truss_impl.cuh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 7ce807813da..57a1440cea6 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -553,6 +553,8 @@ void k_truss(raft::handle_t const& handle, // Unroll and remove/mask edges as long as there are still edges part // of the K-Truss. + printf("\nRight before the while loop and k = %d\n", k); + printf("the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, num_edges); auto num_valid_edges = num_edges - num_invalid_edges; while ((num_valid_edges != 0) && (num_invalid_edges !=0)) { From 1a64e0e35bc90c836f69a175d16eb568240970ab Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 15:05:48 -0800 Subject: [PATCH 041/155] fix typo --- cpp/src/community/k_truss_impl.cuh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 57a1440cea6..4394efbee5e 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -520,7 +520,6 @@ void k_truss(raft::handle_t const& handle, edges, num_triangles.begin()); // 'invalid_edge_first' marks the beginning of the edges to be removed - auto invalid_edge_last = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, @@ -553,11 +552,8 @@ void k_truss(raft::handle_t const& handle, // Unroll and remove/mask edges as long as there are still edges part // of the K-Truss. - printf("\nRight before the while loop and k = %d\n", k); - printf("the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, num_edges); auto num_valid_edges = num_edges - num_invalid_edges; while ((num_valid_edges != 0) && (num_invalid_edges !=0)) { - // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges @@ -609,14 +605,14 @@ void k_truss(raft::handle_t const& handle, }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - + auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); auto vertex_pair_buffer_p_r = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - rmm::device_uvector indices(num_edges, handle.get_stream()); + rmm::device_uvector indices(num_invalid_edges, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); @@ -635,6 +631,7 @@ void k_truss(raft::handle_t const& handle, auto dst = invalid_first_dst[idx]; auto dst_array_begin = invalid_first_dst; auto dst_array_end = invalid_first_dst + num_edges; + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range From 0355693e644684d23c8395abe598570c59eb1461 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 15:15:38 -0800 Subject: [PATCH 042/155] resize indices array --- cpp/src/community/k_truss_impl.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 4394efbee5e..3f231cb4e01 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -808,6 +808,7 @@ void k_truss(raft::handle_t const& handle, // Need to run prefix_sum again to get new ranges because some incoming edges were removed prefix_sum.resize(num_edges + 1, handle.get_stream()); + indices.resize(num_invalid_edges, handle.get_stream()); // FIXME: need to sort 'incoming_vertex_pairs'. No need because a stable partition was // performed that preserve the sorting From 28275b3403185365067b382de1d4ce4089a923a3 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 15:21:56 -0800 Subject: [PATCH 043/155] fix logic error --- cpp/src/community/k_truss_impl.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 3f231cb4e01..b0e23e9497c 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -807,7 +807,7 @@ void k_truss(raft::handle_t const& handle, invalid_edges_buffer, num_edges, handle.get_stream()); // Need to run prefix_sum again to get new ranges because some incoming edges were removed - prefix_sum.resize(num_edges + 1, handle.get_stream()); + prefix_sum.resize(num_invalid_edges + 1, handle.get_stream()); indices.resize(num_invalid_edges, handle.get_stream()); // FIXME: need to sort 'incoming_vertex_pairs'. No need because a stable partition was From c362f45647399caa833e598f1b1178c95a61ab09 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 21 Feb 2024 16:09:22 -0800 Subject: [PATCH 044/155] add a routine to update both the number of edges and invalid edges for the next iteration --- cpp/src/community/k_truss_impl.cuh | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index b0e23e9497c..5b4601397d6 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -1115,7 +1115,34 @@ void k_truss(raft::handle_t const& handle, num_edges = edgelist_srcs.size(); - //num_invalid_edges = 0; //****************** debugging purposes + // FIXME: Rename the variable below. Can't reuse 'invalid_edge_last' + // because the call below returns a type different than 'edges_to_num_triangles' + auto invalid_edge_last_1 = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles < k - 2; + }); + + num_invalid_edges = static_cast( + thrust::distance(edges_to_num_triangles, invalid_edge_last_1)); + + + // copy invalid edges + resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + edges, + edges + num_invalid_edges, + get_dataframe_buffer_begin(invalid_edges_buffer)); + + // sort back the edges as those are needed later when running a binary tree + thrust::sort_by_key(handle.get_thrust_policy(), + edges, + edges + num_edges, + num_triangles.begin()); } if (num_invalid_edges == num_edges) { From 55876aaf6e84b4d89f075df35f178f7750f8dec9 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 26 Feb 2024 03:26:59 -0800 Subject: [PATCH 045/155] remove unused functor --- cpp/src/community/k_truss_impl.cuh | 316 ++++++++++++++++++++++++----- 1 file changed, 269 insertions(+), 47 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 5b4601397d6..8efd995625d 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -61,7 +61,7 @@ struct exclude_self_loop_t { template struct in_k_plus_one_or_greater_t { edge_t k{}; - __device__ bool operator()(edge_t core_number) const { return core_number >= k + 1; } + __device__ bool operator()(edge_t core_number) const { return core_number >= k-1; } }; template @@ -97,23 +97,6 @@ struct extract_low_to_high_degree_edges_t { } }; -template -struct extract_p_q { - raft::device_span intersection_offsets{}; - raft::device_span intersection_indices{}; - raft::device_span num_triangles{}; - - VertexPairIterator edges{}; - - __device__ thrust::tuple operator()(edge_t i) const - { - auto itr = thrust::upper_bound( - thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - unsigned int r = atomicInc((unsigned int*)(num_triangles.data()+idx), (unsigned int)1); - } -}; - template struct extract_p_r { @@ -294,6 +277,7 @@ void k_truss(raft::handle_t const& handle, edge_dummy_property_t{}.view(), exclude_self_loop_t{}); + if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning edge_src_in_k_plus_one_cores( handle, cur_graph_view); @@ -393,10 +378,30 @@ void k_truss(raft::handle_t const& handle, } renumber_map = std::move(tmp_renumber_map); } + */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + + + // FIXME: REmove this********************************************************************************** + rmm::device_uvector srcs_(0, handle.get_stream()); + rmm::device_uvector dsts_(0, handle.get_stream()); + + std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + + printf("\nOriginal graph\n"); + raft::print_device_vector("srcs", srcs_.data(), srcs_.size(), std::cout); + raft::print_device_vector("dsts", dsts_.data(), dsts_.size(), std::cout); + //***************************************************************************************************** + + auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -437,7 +442,7 @@ void k_truss(raft::handle_t const& handle, std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, - true); + false); //******************************************************************************************************FIXME: hardcoded to False modified_graph_view = (*modified_graph).view(); @@ -488,6 +493,7 @@ void k_truss(raft::handle_t const& handle, rmm::device_uvector num_triangles(num_edges, handle.get_stream()); thrust::fill( handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), size_t{0}); + // Update the number of triangles of each (p, q) edges by looking at their intersection // size @@ -495,6 +501,12 @@ void k_truss(raft::handle_t const& handle, intersection_offsets.begin() + 1, intersection_offsets.end(), num_triangles.begin()); + /* + printf("\nnum triangles from (p, q)\n"); + raft::print_device_vector("intersection_offsets", intersection_offsets.data(), intersection_offsets.size(), std::cout); + raft::print_device_vector("intersection_indices", intersection_indices.data(), intersection_indices.size(), std::cout); + raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); + */ thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -506,6 +518,9 @@ void k_truss(raft::handle_t const& handle, raft::device_span(num_triangles.data(), num_triangles.size()), edges}); + //printf("\nnum triangles from (p, r)\n"); + //raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); + thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), @@ -515,11 +530,44 @@ void k_truss(raft::handle_t const& handle, raft::device_span(intersection_indices.data(), intersection_indices.size()), raft::device_span(num_triangles.data(), num_triangles.size()), edges}); + + //printf("\nnum triangles from (q, r)\n"); + //raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); auto edges_to_num_triangles = thrust::make_zip_iterator( edges, num_triangles.begin()); + + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + + // Remove edges that have a triangle count of zero. Those should not be accounted + // for during the unroling phase. + size_t num_edges_with_triangles{0}; + auto edges_with_triangle_last = + thrust::stable_partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles > 0; + }); + + num_edges_with_triangles = static_cast( + thrust::distance(edges_to_num_triangles, edges_with_triangle_last)); + + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); + edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); + num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + //printf("\ntot number of edges = %d\n", num_edges); + num_edges = edgelist_srcs.size(); + //printf("\nnum edges after removing those with no triangles = %d\n", num_edges); + + + // 'invalid_edge_first' marks the beginning of the edges to be removed + auto invalid_edge_last = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, @@ -533,6 +581,10 @@ void k_truss(raft::handle_t const& handle, num_invalid_edges = static_cast( thrust::distance(edges_to_num_triangles, invalid_edge_last)); + printf("\nafter sorting - 0\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); // copy invalid edges auto invalid_edges_buffer = allocate_dataframe_buffer>( @@ -543,6 +595,9 @@ void k_truss(raft::handle_t const& handle, edges + num_invalid_edges, get_dataframe_buffer_begin(invalid_edges_buffer)); + + + // sort back the edges as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), edges, @@ -552,8 +607,52 @@ void k_truss(raft::handle_t const& handle, // Unroll and remove/mask edges as long as there are still edges part // of the K-Truss. + //printf("\nRight before the while loop and k = %d\n", k); + printf("the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, num_edges); auto num_valid_edges = num_edges - num_invalid_edges; + + + // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag + cugraph::edge_bucket_t edges_with_triangles(handle); + + while ((num_valid_edges != 0) && (num_invalid_edges !=0)) { + // Remove those invalid edges from the edge list + printf("\nremoving invalid edges upfront\n"); + auto valid_edge_last = + thrust::partition(handle.get_thrust_policy(), + edges_to_num_triangles, + edges_to_num_triangles + num_edges, + [invalid_edge_first = get_dataframe_buffer_begin(invalid_edges_buffer), // rename to 'edges' + invalid_edge_last = get_dataframe_buffer_end(invalid_edges_buffer), + num_invalid_edges] + __device__(auto edge) { + auto itr = thrust::find(thrust::seq, invalid_edge_first, invalid_edge_last, thrust::get<0>(edge)); + auto idx = thrust::distance(invalid_edge_first, itr); + return idx == num_invalid_edges; + }); + + auto num_valid_edges = thrust::distance(edges_to_num_triangles, valid_edge_last); + + // resize the 'edgelist_srcs' and 'edgelist_dst' + edgelist_srcs.resize(num_valid_edges, handle.get_stream()); + edgelist_dsts.resize(num_valid_edges, handle.get_stream()); + num_triangles.resize(num_valid_edges, handle.get_stream()); + + num_edges = edgelist_srcs.size(); // FIXME: Use edgelist_srcs.size() instead + + + + printf("\n***inside*** the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, num_edges); + printf("\ninit invalid edges buffer\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + + + raft::print_device_vector("src", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dst", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_t", num_triangles.data(), num_triangles.size(), std::cout); + // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges @@ -563,7 +662,7 @@ void k_truss(raft::handle_t const& handle, // can't use a zip iterator for 'incoming_vertex_pairs'. This adds increase memory // footprint auto incoming_vertex_pairs = allocate_dataframe_buffer>( - num_edges, handle.get_stream()); + num_edges, handle.get_stream()); // FIXME: Use edgelist_srcs.size() instead thrust::tabulate(handle.get_thrust_policy(), get_dataframe_buffer_begin(incoming_vertex_pairs), @@ -605,7 +704,16 @@ void k_truss(raft::handle_t const& handle, }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - + + raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); + /* + 2 7 + 8 3 + + 0 1 2 3 5 0 7 + 8 8 8 8 8 3 3 + */ + auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -616,12 +724,18 @@ void k_truss(raft::handle_t const& handle, thrust::tabulate( handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); + //printf("\nbefore generating p_q, p_r\n"); + printf("\n unrolling q, r edges\n"); + + raft::print_device_vector("***incomming - src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); + raft::print_device_vector("***incomming - dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); thrust::for_each( handle.get_thrust_policy(), indices.begin(), indices.end(), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), prefix_sum = prefix_sum.data(), incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), @@ -629,9 +743,12 @@ void k_truss(raft::handle_t const& handle, num_edges = num_edges] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; - auto dst_array_begin = invalid_first_dst; - auto dst_array_end = invalid_first_dst + num_edges; + auto dst_array_end = dst_array_begin + num_edges; + + //auto dst_array_begin = thrust::get<0>(incoming_vertex_pairs); + //auto dst_array_end = thrust::get<0>(incoming_vertex_pairs + num_edges); + //printf("\ninvalid src = %d, invalid dst = %d, idx = %d\n", src, dst, idx); auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range @@ -657,13 +774,23 @@ void k_truss(raft::handle_t const& handle, [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, + idx, idx_lower = idx_lower](auto idx_in_segment) { + if (dst == 2){ + printf("\nincoming edge %d, %d, the begining of the range = %d and idx = %d", thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst, idx_lower, idx); + } return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); - - auto edge_exists = cur_graph_view.has_edge( + //printf("\nafter generating p_q, p_r\n"); + printf("\n (p, q), (p, r) has edge before\n"); + raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); + raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); + + auto edges_exist = cur_graph_view.has_edge( handle, raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), @@ -673,11 +800,11 @@ void k_truss(raft::handle_t const& handle, auto edge_to_existance = thrust::make_zip_iterator( thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), - edge_exists.begin()); + edges_exist.begin()); auto has_edge_last = thrust::partition(handle.get_thrust_policy(), edge_to_existance, - edge_to_existance + edge_exists.size(), + edge_to_existance + edges_exist.size(), [] __device__(auto e) { auto edge_exists = thrust::get<1>(e); return edge_exists; @@ -690,6 +817,12 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); + printf("\n (p, q), (p, r) has edge after\n"); + raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); + raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); + auto edges_last = edges + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -729,8 +862,7 @@ void k_truss(raft::handle_t const& handle, auto last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); // rename the above it to last_edge_with_triangles - // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag - cugraph::edge_bucket_t edges_with_triangles(handle); + edges_with_triangles.clear(); cugraph::edge_property_t edge_value_output(handle, cur_graph_view); @@ -755,12 +887,13 @@ void k_truss(raft::handle_t const& handle, cur_graph_view.attach_edge_mask(edge_value_output.view()); - // resize the 'edgelist_srcs' and 'edgelsit_dst' + // resize the 'edgelist_srcs' and 'edgelist_dst' edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); num_edges = edgelist_srcs.size(); + printf("\nnumber of edges after (q, r) = %d\n", num_edges); resize_dataframe_buffer( incoming_vertex_pairs, num_edges, handle.get_stream()); @@ -787,6 +920,7 @@ void k_truss(raft::handle_t const& handle, // Find the intersection of 'invalid_edges_buffer' and 'edges' to extract the remaining invalid // edges that still need to be processed. Didn't used thrust::set_intersection because I didn't // want to create a temporary array + /* auto invalid_edge_last = thrust::partition(handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), @@ -801,15 +935,24 @@ void k_truss(raft::handle_t const& handle, }); num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); - - + if (num_invalid_edges == 0){ + break; + } resize_dataframe_buffer( - invalid_edges_buffer, num_edges, handle.get_stream()); + invalid_edges_buffer, num_invalid_edges, handle.get_stream()); + */ + + printf("\nafter case -2 (q, r) invalid edges buffer\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); // Need to run prefix_sum again to get new ranges because some incoming edges were removed prefix_sum.resize(num_invalid_edges + 1, handle.get_stream()); indices.resize(num_invalid_edges, handle.get_stream()); + //printf("\npreparing p-r edges, the new num_invalid_edges = %d\n", num_invalid_edges); + raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); + // FIXME: need to sort 'incoming_vertex_pairs'. No need because a stable partition was // performed that preserve the sorting thrust::tabulate( @@ -847,6 +990,7 @@ void k_truss(raft::handle_t const& handle, indices.end(), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), prefix_sum = prefix_sum.data(), incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), @@ -854,8 +998,7 @@ void k_truss(raft::handle_t const& handle, num_edges = num_edges] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; - auto dst_array_begin = invalid_first_dst; - auto dst_array_end = invalid_first_dst + num_edges; + auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range @@ -885,8 +1028,13 @@ void k_truss(raft::handle_t const& handle, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); + + raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); + raft::print_device_vector("***q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); - edge_exists = cur_graph_view.has_edge( + edges_exist = cur_graph_view.has_edge( handle, raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size()), @@ -896,11 +1044,11 @@ void k_truss(raft::handle_t const& handle, edge_to_existance = thrust::make_zip_iterator( thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), - edge_exists.begin()); + edges_exist.begin()); has_edge_last = thrust::stable_partition(handle.get_thrust_policy(), edge_to_existance, - edge_to_existance + edge_exists.size(), + edge_to_existance + edges_exist.size(), [] __device__(auto e) { auto edge_exists = thrust::get<1>(e); return edge_exists; @@ -912,6 +1060,23 @@ void k_truss(raft::handle_t const& handle, // remove them by resizing both vertex pair buffer resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); + edges_exist.resize(size_dataframe_buffer(vertex_pair_buffer_p_q), handle.get_stream()); + + + + + printf("\n unrolling p, r edges\n"); + + raft::print_device_vector("***incomming - src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); + raft::print_device_vector("***incomming - dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); + + raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); + raft::print_device_vector("***q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); + raft::print_device_vector("***q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); + raft::print_device_vector("edges_exists", edges_exist.data(), edges_exist.size(), std::cout); + + edges_last = edges + num_edges; thrust::for_each(handle.get_thrust_policy(), @@ -945,6 +1110,10 @@ void k_truss(raft::handle_t const& handle, [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); + + raft::print_device_vector("after paritioning (q, r) srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("after paritioning (q, r) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("after paritioning (q, r) n_tr", num_triangles.data(), num_triangles.size(), std::cout); last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); // rename the above it to last_edge_with_triangles @@ -978,10 +1147,20 @@ void k_truss(raft::handle_t const& handle, num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); num_edges = edgelist_srcs.size(); + printf("\nnumber of edges after (p, r) = %d\n", num_edges); + raft::print_device_vector("after (p, r) srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("after (p, r) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("after (p, r) n_tr", num_triangles.data(), num_triangles.size(), std::cout); + + + printf("\np-r printing invalid edges buffer before\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. + /* invalid_edge_last = thrust::partition(handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), @@ -993,14 +1172,32 @@ void k_truss(raft::handle_t const& handle, auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); auto idx = thrust::distance(edge_first, itr); + if (idx > num_edges){ + printf("\nfound an invalid edge\n"); + } return idx < num_edges; }); num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); + printf("\nnumber of invalid edges for (p, q) = %d\n", num_invalid_edges); + if (num_invalid_edges == 0){ + break; + } resize_dataframe_buffer( - invalid_edges_buffer, num_edges, handle.get_stream()); - + invalid_edges_buffer, num_invalid_edges, handle.get_stream()); + */ + printf("\nafter case -3 (q, r) invalid edges buffer\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + + + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + //printf("\nprinting invalid edges buffer after\n"); + //raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + //raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, @@ -1009,7 +1206,9 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_end(invalid_edges_buffer), std::array{true, true}, do_expensive_check); - + + raft::print_device_vector("intersection_offsets", intersection_offsets.data(), intersection_offsets.size(), std::cout); + raft::print_device_vector("intersection_indices", intersection_indices.data(), intersection_indices.size(), std::cout); size_t accumulate_pair_size = intersection_indices.size(); @@ -1027,6 +1226,9 @@ void k_truss(raft::handle_t const& handle, intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) }); + + raft::print_device_vector("p_r", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); + raft::print_device_vector("p_r", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); edges_last = edges + num_edges; num_edge_exists = accumulate_pair_size; @@ -1056,6 +1258,9 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); + raft::print_device_vector("q_r", std::get<0>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); + raft::print_device_vector("q_r", std::get<1>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); + thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), @@ -1078,6 +1283,10 @@ void k_truss(raft::handle_t const& handle, [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); + + raft::print_device_vector("after paritioning (p, q) srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("after paritioning (p, q) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("after paritioning (p, q) n_tr", num_triangles.data(), num_triangles.size(), std::cout); last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); // rename the above it to last_edge_with_triangles @@ -1114,10 +1323,16 @@ void k_truss(raft::handle_t const& handle, num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); num_edges = edgelist_srcs.size(); + printf("\nnumber of edges after (p, q) = %d\n", num_edges); - // FIXME: Rename the variable below. Can't reuse 'invalid_edge_last' - // because the call below returns a type different than 'edges_to_num_triangles' - auto invalid_edge_last_1 = + //num_invalid_edges = 0; //****************** debugging purposes + printf("\n*******final*******\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + + + auto invalid_edge_last_ = thrust::stable_partition(handle.get_thrust_policy(), edges_to_num_triangles, edges_to_num_triangles + num_triangles.size(), @@ -1127,7 +1342,7 @@ void k_truss(raft::handle_t const& handle, }); num_invalid_edges = static_cast( - thrust::distance(edges_to_num_triangles, invalid_edge_last_1)); + thrust::distance(edges_to_num_triangles, invalid_edge_last_)); // copy invalid edges @@ -1143,17 +1358,24 @@ void k_truss(raft::handle_t const& handle, edges, edges + num_edges, num_triangles.begin()); + printf("\n num_invalid_edges = %d, num_edges = %d\n", num_invalid_edges, num_edges); + + + + } if (num_invalid_edges == num_edges) { // return empty graph view std::optional> empty_graph_view{std::nullopt}; + printf("\nreturning an empty graph"); // FIXME: To be updated // return empty_graph_view; } else{ // FIXME: To be updated // return cur_graph_view; + printf("\nreturning a non empty graph with num_edges = %d\n", num_edges); } From 873199fc515c08d64761867aba203fe1b75b4bfd Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 26 Feb 2024 03:28:33 -0800 Subject: [PATCH 046/155] rename variable --- cpp/src/community/k_truss_impl.cuh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 8efd995625d..30b8bc9f40c 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -100,7 +100,7 @@ struct extract_low_to_high_degree_edges_t { template struct extract_p_r { - size_t num_vertex_pair{}; // rename to num_edges + size_t num_edges{}; // rename to num_edges raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span num_triangles{}; @@ -117,7 +117,7 @@ struct extract_p_r { // Find its position in 'edges' auto itr_p_r = thrust::lower_bound(thrust::seq, edges, - edges + num_vertex_pair, // pass the number of vertex pairs + edges + num_edges, // pass the number of vertex pairs p_r_pair); idx = thrust::distance(edges, itr_p_r); @@ -129,7 +129,7 @@ struct extract_p_r { template struct extract_q_r { - size_t num_vertex_pair; // rename to num_edges + size_t num_edges; // rename to num_edges raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span num_triangles{}; @@ -147,7 +147,7 @@ struct extract_q_r { // Find its position in 'edges' auto itr_q_r = thrust::lower_bound(thrust::seq, edges, - edges + num_vertex_pair, + edges + num_edges, q_r_pair); idx = thrust::distance(edges, itr_q_r); From b1bdf25af06f68c82e702e1068fb57760ae55953 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 26 Feb 2024 03:37:28 -0800 Subject: [PATCH 047/155] update variable type --- cpp/src/community/k_truss_impl.cuh | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 30b8bc9f40c..05f55bb36c9 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -98,16 +98,16 @@ struct extract_low_to_high_degree_edges_t { }; -template +template struct extract_p_r { size_t num_edges{}; // rename to num_edges raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span num_triangles{}; - VertexPairIterator edges{}; + EdgeIterator edges{}; - __device__ thrust::tuple operator()(edge_t i) const + __device__ thrust::tuple operator()(size_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); @@ -127,16 +127,16 @@ struct extract_p_r { }; -template +template struct extract_q_r { size_t num_edges; // rename to num_edges raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span num_triangles{}; - VertexPairIterator edges{}; + EdgeIterator edges{}; - __device__ thrust::tuple operator()(edge_t i) const + __device__ thrust::tuple operator()(size_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); @@ -157,12 +157,12 @@ struct extract_q_r { }; -template +template struct unroll_edge { raft::device_span num_triangles{}; - VertexPairIterator edge_unrolled{}; - VertexPairIterator edges{}; - VertexPairIterator edges_last{}; + EdgeIterator edge_unrolled{}; + EdgeIterator edges{}; + EdgeIterator edges_last{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -184,12 +184,12 @@ struct unroll_edge { }; -template +template struct generate_p_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator edges{}; + EdgeIterator edges{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -205,12 +205,12 @@ struct generate_p_r { -template +template struct generate_q_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - VertexPairIterator edges{}; + EdgeIterator edges{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -511,7 +511,7 @@ void k_truss(raft::handle_t const& handle, thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - extract_p_r{ + extract_p_r{ num_edges, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), @@ -524,7 +524,7 @@ void k_truss(raft::handle_t const& handle, thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - extract_q_r{ + extract_q_r{ num_edges, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), From 12b1c3524117020da2873ad533b87cd9d0ce9820 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 26 Feb 2024 04:27:46 -0800 Subject: [PATCH 048/155] consolidate function --- cpp/src/community/k_truss_impl.cuh | 76 +++++++++++++----------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 05f55bb36c9..c18a8fad19a 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -98,9 +98,10 @@ struct extract_low_to_high_degree_edges_t { }; -template -struct extract_p_r { +template +struct update_edges_p_r_q_r_num_triangles { size_t num_edges{}; // rename to num_edges + const edge_t edge_first_or_second{}; raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span num_triangles{}; @@ -112,45 +113,30 @@ struct extract_p_r { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto p_r_pair = - thrust::make_tuple(thrust::get<0>(*(edges + idx)), intersection_indices[i]); - // Find its position in 'edges' - auto itr_p_r = thrust::lower_bound(thrust::seq, - edges, - edges + num_edges, // pass the number of vertex pairs - p_r_pair); - - idx = thrust::distance(edges, itr_p_r); - auto r = atomicAdd(num_triangles.data()+idx, 1); - - } -}; - - -template -struct extract_q_r { - size_t num_edges; // rename to num_edges - raft::device_span intersection_offsets{}; - raft::device_span intersection_indices{}; - raft::device_span num_triangles{}; - - EdgeIterator edges{}; - - __device__ thrust::tuple operator()(size_t i) const - { - auto itr = thrust::upper_bound( - thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto q_r_pair = - thrust::make_tuple(thrust::get<1>(*(edges + idx)), intersection_indices[i]); - - // Find its position in 'edges' - auto itr_q_r = thrust::lower_bound(thrust::seq, - edges, - edges + num_edges, - q_r_pair); + if (edge_first_or_second == 0){ + auto p_r_pair = + thrust::make_tuple(thrust::get<0>(*(edges + idx)), intersection_indices[i]); + + // Find its position in 'edges' + auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, + edges, + edges + num_edges, // pass the number of vertex pairs + p_r_pair); + idx = thrust::distance(edges, itr_p_r_p_q); + } + else { + auto p_r_pair = + thrust::make_tuple(thrust::get<1>(*(edges + idx)), intersection_indices[i]); + + // Find its position in 'edges' + auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, + edges, + edges + num_edges, // pass the number of vertex pairs + p_r_pair); + + idx = thrust::distance(edges, itr_p_r_p_q); - idx = thrust::distance(edges, itr_q_r); + } auto r = atomicAdd(num_triangles.data()+idx, 1); } @@ -508,11 +494,16 @@ void k_truss(raft::handle_t const& handle, raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); */ + // Given intersection offsets and indices that are used to update the number of + // triangles of (p, q) edges where `r`s are the intersection indices, update + // the number of triangles of the pairs (p, r) and (q, r). + thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - extract_p_r{ + update_edges_p_r_q_r_num_triangles{ num_edges, + 0, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), raft::device_span(num_triangles.data(), num_triangles.size()), @@ -524,8 +515,9 @@ void k_truss(raft::handle_t const& handle, thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - extract_q_r{ + update_edges_p_r_q_r_num_triangles{ num_edges, + 1, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), raft::device_span(num_triangles.data(), num_triangles.size()), From ca2081158c94b69524c45355a8e34c071cafab4e Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 26 Feb 2024 07:23:17 -0800 Subject: [PATCH 049/155] replace atomicAdd by atomic_ref --- cpp/src/community/k_truss_impl.cuh | 136 +++++++++++++++-------------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index c18a8fad19a..2c54b40dad7 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -106,7 +106,7 @@ struct update_edges_p_r_q_r_num_triangles { raft::device_span intersection_indices{}; raft::device_span num_triangles{}; - EdgeIterator edges{}; + EdgeIterator edge_first{}; __device__ thrust::tuple operator()(size_t i) const { @@ -115,29 +115,30 @@ struct update_edges_p_r_q_r_num_triangles { auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); if (edge_first_or_second == 0){ auto p_r_pair = - thrust::make_tuple(thrust::get<0>(*(edges + idx)), intersection_indices[i]); + thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); // Find its position in 'edges' auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, - edges, - edges + num_edges, // pass the number of vertex pairs + edge_first, + edge_first + num_edges, // pass the number of vertex pairs p_r_pair); - idx = thrust::distance(edges, itr_p_r_p_q); + idx = thrust::distance(edge_first, itr_p_r_p_q); } else { auto p_r_pair = - thrust::make_tuple(thrust::get<1>(*(edges + idx)), intersection_indices[i]); + thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); // Find its position in 'edges' auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, - edges, - edges + num_edges, // pass the number of vertex pairs + edge_first, + edge_first + num_edges, // pass the number of vertex pairs p_r_pair); - idx = thrust::distance(edges, itr_p_r_p_q); + idx = thrust::distance(edge_first, itr_p_r_p_q); } - auto r = atomicAdd(num_triangles.data()+idx, 1); + cuda::atomic_ref atomic_counter(num_triangles[idx]); + auto r = atomic_counter.fetch_add(vertex_t{1}, cuda::std::memory_order_relaxed); } }; @@ -147,8 +148,8 @@ template struct unroll_edge { raft::device_span num_triangles{}; EdgeIterator edge_unrolled{}; - EdgeIterator edges{}; - EdgeIterator edges_last{}; + EdgeIterator edge_first{}; + EdgeIterator edge_last{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -156,15 +157,16 @@ struct unroll_edge { thrust::make_tuple(thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i))); // Find its position in 'edges' auto itr = thrust::lower_bound(thrust::seq, - edges, + edge_first, //edges + 6, // pass the number of vertex pairs - edges_last, + edge_last, //thrust::make_tuple(0, 3) pair ); - auto idx = thrust::distance(edges, itr); - auto r = atomicAdd(num_triangles.data() + idx, -1); + auto idx = thrust::distance(edge_first, itr); + cuda::atomic_ref atomic_counter(num_triangles[idx]); + auto r = atomic_counter.fetch_sub(vertex_t{1}, cuda::std::memory_order_relaxed); } }; @@ -175,7 +177,7 @@ struct generate_p_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - EdgeIterator edges{}; + EdgeIterator edge_first{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -183,7 +185,7 @@ struct generate_p_r { thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); auto pair = - thrust::make_tuple(thrust::get<0>(*(edges + idx)), intersection_indices[i]); + thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); return pair; } @@ -196,7 +198,7 @@ struct generate_q_r { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - EdgeIterator edges{}; + EdgeIterator edge_first{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -204,7 +206,7 @@ struct generate_q_r { thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); auto pair = - thrust::make_tuple(thrust::get<1>(*(edges + idx)), intersection_indices[i]); + thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); return pair; } @@ -458,11 +460,11 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - auto edges = + auto edge_first = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); thrust::sort( - handle.get_thrust_policy(), edges, edges + edgelist_srcs.size()); + handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size()); size_t num_edges = edgelist_srcs.size(); // FIXME: rename to num_edges and always update values when removing edges @@ -471,8 +473,8 @@ void k_truss(raft::handle_t const& handle, detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - edges, - edges + num_edges, + edge_first, + edge_first + num_edges, std::array{true, true}, do_expensive_check); @@ -501,13 +503,13 @@ void k_truss(raft::handle_t const& handle, thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - update_edges_p_r_q_r_num_triangles{ + update_edges_p_r_q_r_num_triangles{ num_edges, 0, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), raft::device_span(num_triangles.data(), num_triangles.size()), - edges}); + edge_first}); //printf("\nnum triangles from (p, r)\n"); //raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); @@ -515,19 +517,19 @@ void k_truss(raft::handle_t const& handle, thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), - update_edges_p_r_q_r_num_triangles{ + update_edges_p_r_q_r_num_triangles{ num_edges, 1, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), raft::device_span(num_triangles.data(), num_triangles.size()), - edges}); + edge_first}); //printf("\nnum triangles from (q, r)\n"); //raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); auto edges_to_num_triangles = thrust::make_zip_iterator( - edges, num_triangles.begin()); + edge_first, num_triangles.begin()); raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); @@ -583,17 +585,17 @@ void k_truss(raft::handle_t const& handle, num_invalid_edges, handle.get_stream()); thrust::copy(handle.get_thrust_policy(), - edges, - edges + num_invalid_edges, + edge_first, + edge_first + num_invalid_edges, get_dataframe_buffer_begin(invalid_edges_buffer)); - // sort back the edges as those are needed later when running a binary tree + // sort back the edge as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), - edges, - edges + num_edges, + edge_first, + edge_first + num_edges, num_triangles.begin()); @@ -659,10 +661,10 @@ void k_truss(raft::handle_t const& handle, thrust::tabulate(handle.get_thrust_policy(), get_dataframe_buffer_begin(incoming_vertex_pairs), get_dataframe_buffer_end(incoming_vertex_pairs), - [edges=edges + [edge_first=edge_first ] __device__(edge_t idx){ - auto edge = thrust::make_tuple(thrust::get<1>(*(edges + idx)), thrust::get<0>(*(edges + idx))); + auto edge = thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), thrust::get<0>(*(edge_first + idx))); return edge; }); @@ -815,25 +817,25 @@ void k_truss(raft::handle_t const& handle, raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); - auto edges_last = edges + num_edges; + auto edge_last = edge_first + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edges, - edges_last, + edge_first, + edge_last, }); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - edges, - edges_last, + edge_first, + edge_last, }); // Put edges with triangle count == 0 in the second partition @@ -893,10 +895,10 @@ void k_truss(raft::handle_t const& handle, thrust::tabulate(handle.get_thrust_policy(), get_dataframe_buffer_begin(incoming_vertex_pairs), get_dataframe_buffer_end(incoming_vertex_pairs), - [edges=edges + [edge_first=edge_first ] __device__(edge_t idx){ - auto edge = thrust::make_tuple(thrust::get<1>(*(edges + idx)), thrust::get<0>(*(edges + idx))); + auto edge = thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), thrust::get<0>(*(edge_first + idx))); return edge; }); @@ -917,8 +919,8 @@ void k_truss(raft::handle_t const& handle, thrust::partition(handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), get_dataframe_buffer_end(invalid_edges_buffer), - [edge_first = edges, // rename to 'edges' - edge_last = edges + num_edges, + [edge_first = edge_first, // rename to 'edges' + edge_last = edge_first + num_edges, num_edges = num_edges] __device__(auto invalid_edge) { auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); @@ -1070,25 +1072,25 @@ void k_truss(raft::handle_t const& handle, - edges_last = edges + num_edges; + edge_last = edge_first + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edges, - edges_last, + edge_first, + edge_last, }); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - edges, - edges_last, + edge_first, + edge_last, }); // Put edges with triangle count == 0 in the second partition @@ -1157,8 +1159,8 @@ void k_truss(raft::handle_t const& handle, thrust::partition(handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), get_dataframe_buffer_end(invalid_edges_buffer), - [edge_first = edges, // rename to 'edges' - edge_last = edges + num_edges, + [edge_first = edge_first, // rename to 'edges' + edge_last = edge_first + num_edges, num_edges = num_edges] __device__(auto invalid_edge) { @@ -1222,16 +1224,16 @@ void k_truss(raft::handle_t const& handle, raft::print_device_vector("p_r", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); raft::print_device_vector("p_r", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); - edges_last = edges + num_edges; + edge_last = edge_first + num_edges; num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - edges, - edges_last, + edge_first, + edge_last, }); auto vertex_pair_buffer_q_r_edge_p_q = @@ -1256,11 +1258,11 @@ void k_truss(raft::handle_t const& handle, thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - edges, - edges_last, + edge_first, + edge_last, }); // Put edges with triangle count == 0 in the second partition @@ -1341,14 +1343,14 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); thrust::copy(handle.get_thrust_policy(), - edges, - edges + num_invalid_edges, + edge_first, + edge_first + num_invalid_edges, get_dataframe_buffer_begin(invalid_edges_buffer)); // sort back the edges as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), - edges, - edges + num_edges, + edge_first, + edge_first + num_edges, num_triangles.begin()); printf("\n num_invalid_edges = %d, num_edges = %d\n", num_invalid_edges, num_edges); From 990a8e93463acaa165868f88f5632f75e246d082 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 26 Feb 2024 16:39:01 -0800 Subject: [PATCH 050/155] add separate function for edge triangle count --- cpp/CMakeLists.txt | 1 + cpp/include/cugraph/graph_functions.hpp | 22 +++++ cpp/src/community/k_truss_impl.cuh | 111 ++---------------------- 3 files changed, 30 insertions(+), 104 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index f3813047b84..fd58f7d08de 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -200,6 +200,7 @@ set(CUGRAPH_SOURCES src/community/detail/mis_sg.cu src/community/detail/mis_mg.cu src/detail/utility_wrappers.cu + src/structure/edge_triangle_count_sg.cu src/structure/graph_view_mg.cu src/structure/remove_self_loops.cu src/structure/remove_multi_edges.cu diff --git a/cpp/include/cugraph/graph_functions.hpp b/cpp/include/cugraph/graph_functions.hpp index 6684d31d8fd..8c12fdf1ec3 100644 --- a/cpp/include/cugraph/graph_functions.hpp +++ b/cpp/include/cugraph/graph_functions.hpp @@ -1051,4 +1051,26 @@ remove_multi_edges(raft::handle_t const& handle, std::optional>&& edgelist_edge_types, bool keep_min_value_edge = false); + +/** + * @brief Count the number of triangles for each edge. + * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. + * @tparam edge_t Type of edge identifiers. Needs to be an integral type. + * + * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and + * handles to various CUDA libraries) to run graph algorithms. + * @param edgelist_srcs List of source vertex ids + * @param edgelist_dsts List of destination vertex ids + */ +template +rmm::device_uvector +edge_triangle_count( + raft::handle_t const& handle, + graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + } // namespace cugraph diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 2c54b40dad7..95d7c08d582 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -98,52 +98,6 @@ struct extract_low_to_high_degree_edges_t { }; -template -struct update_edges_p_r_q_r_num_triangles { - size_t num_edges{}; // rename to num_edges - const edge_t edge_first_or_second{}; - raft::device_span intersection_offsets{}; - raft::device_span intersection_indices{}; - raft::device_span num_triangles{}; - - EdgeIterator edge_first{}; - - __device__ thrust::tuple operator()(size_t i) const - { - auto itr = thrust::upper_bound( - thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - if (edge_first_or_second == 0){ - auto p_r_pair = - thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); - - // Find its position in 'edges' - auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, - edge_first, - edge_first + num_edges, // pass the number of vertex pairs - p_r_pair); - idx = thrust::distance(edge_first, itr_p_r_p_q); - } - else { - auto p_r_pair = - thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); - - // Find its position in 'edges' - auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, - edge_first, - edge_first + num_edges, // pass the number of vertex pairs - p_r_pair); - - idx = thrust::distance(edge_first, itr_p_r_p_q); - - } - cuda::atomic_ref atomic_counter(num_triangles[idx]); - auto r = atomic_counter.fetch_add(vertex_t{1}, cuda::std::memory_order_relaxed); - - } -}; - - template struct unroll_edge { raft::device_span num_triangles{}; @@ -158,9 +112,7 @@ struct unroll_edge { // Find its position in 'edges' auto itr = thrust::lower_bound(thrust::seq, edge_first, - //edges + 6, // pass the number of vertex pairs edge_last, - //thrust::make_tuple(0, 3) pair ); @@ -477,56 +429,14 @@ void k_truss(raft::handle_t const& handle, edge_first + num_edges, std::array{true, true}, do_expensive_check); - - rmm::device_uvector num_triangles(num_edges, handle.get_stream()); - thrust::fill( - handle.get_thrust_policy(), num_triangles.begin(), num_triangles.end(), size_t{0}); - - - // Update the number of triangles of each (p, q) edges by looking at their intersection - // size - thrust::adjacent_difference(handle.get_thrust_policy(), - intersection_offsets.begin() + 1, - intersection_offsets.end(), - num_triangles.begin()); - /* - printf("\nnum triangles from (p, q)\n"); - raft::print_device_vector("intersection_offsets", intersection_offsets.data(), intersection_offsets.size(), std::cout); - raft::print_device_vector("intersection_indices", intersection_indices.data(), intersection_indices.size(), std::cout); - raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); - */ - - // Given intersection offsets and indices that are used to update the number of - // triangles of (p, q) edges where `r`s are the intersection indices, update - // the number of triangles of the pairs (p, r) and (q, r). - - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(intersection_indices.size()), - update_edges_p_r_q_r_num_triangles{ - num_edges, - 0, - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), intersection_indices.size()), - raft::device_span(num_triangles.data(), num_triangles.size()), - edge_first}); - - //printf("\nnum triangles from (p, r)\n"); - //raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(intersection_indices.size()), - update_edges_p_r_q_r_num_triangles{ - num_edges, - 1, - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), intersection_indices.size()), - raft::device_span(num_triangles.data(), num_triangles.size()), - edge_first}); + auto num_triangles = edge_triangle_count(handle, + cur_graph_view, + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + - //printf("\nnum triangles from (q, r)\n"); - //raft::print_device_vector("num_triangles", num_triangles.data(), num_triangles.size(), std::cout); + auto edges_to_num_triangles = thrust::make_zip_iterator( edge_first, num_triangles.begin()); @@ -700,14 +610,7 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - /* - 2 7 - 8 3 - - 0 1 2 3 5 0 7 - 8 8 8 8 8 3 3 - */ - + auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); From 78562a2e97219cdb401fad1677bafc5a01b17c86 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 05:23:17 -0800 Subject: [PATCH 051/155] update trust call to remove edges and remove tmp variable --- cpp/src/community/k_truss_impl.cuh | 306 +++++++++++++---------------- 1 file changed, 136 insertions(+), 170 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 95d7c08d582..a224ea5cbf0 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -107,8 +107,9 @@ struct unroll_edge { __device__ thrust::tuple operator()(edge_t i) const { + // edges are sort with destination as key so reverse the edge when looking it auto pair = - thrust::make_tuple(thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i))); + thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), thrust::get<0>(*(edge_unrolled + i))); // Find its position in 'edges' auto itr = thrust::lower_bound(thrust::seq, edge_first, @@ -144,7 +145,6 @@ struct generate_p_r { }; - template struct generate_q_r { raft::device_span intersection_offsets{}; @@ -411,16 +411,25 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - + /* auto edge_first = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + */ + auto edge_first = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + + /* + // The above code might have sorted my arrays therefore sort after. + // Sorting the edges with 'dst' as keys. thrust::sort( handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size()); + */ - size_t num_edges = edgelist_srcs.size(); // FIXME: rename to num_edges and always update values when removing edges + //size_t num_edges = edgelist_srcs.size(); // FIXME: rename to num_edges and always update values when removing edges // FIXME: Perform nbr_intersection in chuncks. + /* auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, @@ -429,61 +438,73 @@ void k_truss(raft::handle_t const& handle, edge_first + num_edges, std::array{true, true}, do_expensive_check); + */ auto num_triangles = edge_triangle_count(handle, cur_graph_view, raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - - - auto edges_to_num_triangles = thrust::make_zip_iterator( + auto edge_triangle_count_pair_first = thrust::make_zip_iterator( edge_first, num_triangles.begin()); + + + printf("\nreduced edges from original\n"); raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); + + // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag + cugraph::edge_bucket_t edges_with_triangles(handle); + edge_t num_valid_edges = edgelist_srcs.size(); + edge_t num_invalid_edges{0}; + size_t num_edges_with_triangles{0}; + + while (true) { // Remove edges that have a triangle count of zero. Those should not be accounted // for during the unroling phase. - size_t num_edges_with_triangles{0}; auto edges_with_triangle_last = thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_triangles.size(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), [k] __device__(auto e) { auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); num_edges_with_triangles = static_cast( - thrust::distance(edges_to_num_triangles, edges_with_triangle_last)); + thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); //printf("\ntot number of edges = %d\n", num_edges); - num_edges = edgelist_srcs.size(); - //printf("\nnum edges after removing those with no triangles = %d\n", num_edges); - - - + //num_edges = edgelist_srcs.size(); // 'invalid_edge_first' marks the beginning of the edges to be removed - - auto invalid_edge_last = + auto invalid_edge_first = thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_triangles.size(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), [k] __device__(auto e) { auto num_triangles = thrust::get<1>(e); - return num_triangles < k - 2; + return num_triangles >= k - 2; }); - size_t num_invalid_edges{0}; num_invalid_edges = static_cast( - thrust::distance(edges_to_num_triangles, invalid_edge_last)); + thrust::distance(invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); + + printf("the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, edgelist_srcs.size()); + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; + + //if ((num_valid_edges != 0) && (num_invalid_edges !=0)){ + if (num_invalid_edges == 0){ + break; + } printf("\nafter sorting - 0\n"); raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); @@ -494,39 +515,43 @@ void k_truss(raft::handle_t const& handle, auto invalid_edges_buffer = allocate_dataframe_buffer>( num_invalid_edges, handle.get_stream()); + + + thrust::copy(handle.get_thrust_policy(), - edge_first, - edge_first + num_invalid_edges, + //edge_first + num_valid_edges, + thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, edgelist_dsts.begin() + num_valid_edges), + //thrust::get<0>(invalid_edge_first), + //edge_first + edgelist_srcs.size(), + thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), edgelist_dsts.begin() + edgelist_srcs.size()), get_dataframe_buffer_begin(invalid_edges_buffer)); + // resize the 'edgelist_srcs' and 'edgelist_dst' + edgelist_srcs.resize(num_valid_edges, handle.get_stream()); + edgelist_dsts.resize(num_valid_edges, handle.get_stream()); + num_triangles.resize(num_valid_edges, handle.get_stream()); + printf("\nafter resizing\n"); + raft::print_device_vector("src", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dst", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_t", num_triangles.data(), num_triangles.size(), std::cout); + + // sort back the edge as those are needed later when running a binary tree + /**/ thrust::sort_by_key(handle.get_thrust_policy(), edge_first, - edge_first + num_edges, + edge_first + edgelist_srcs.size(), num_triangles.begin()); - - - // Unroll and remove/mask edges as long as there are still edges part - // of the K-Truss. - //printf("\nRight before the while loop and k = %d\n", k); - printf("the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, num_edges); - auto num_valid_edges = num_edges - num_invalid_edges; - - - // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag - cugraph::edge_bucket_t edges_with_triangles(handle); - - - while ((num_valid_edges != 0) && (num_invalid_edges !=0)) { // Remove those invalid edges from the edge list + /* printf("\nremoving invalid edges upfront\n"); auto valid_edge_last = thrust::partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_edges, + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + edgelist_srcs.size(), [invalid_edge_first = get_dataframe_buffer_begin(invalid_edges_buffer), // rename to 'edges' invalid_edge_last = get_dataframe_buffer_end(invalid_edges_buffer), num_invalid_edges] @@ -535,23 +560,19 @@ void k_truss(raft::handle_t const& handle, auto idx = thrust::distance(invalid_edge_first, itr); return idx == num_invalid_edges; }); - - auto num_valid_edges = thrust::distance(edges_to_num_triangles, valid_edge_last); + */ - // resize the 'edgelist_srcs' and 'edgelist_dst' - edgelist_srcs.resize(num_valid_edges, handle.get_stream()); - edgelist_dsts.resize(num_valid_edges, handle.get_stream()); - num_triangles.resize(num_valid_edges, handle.get_stream()); - num_edges = edgelist_srcs.size(); // FIXME: Use edgelist_srcs.size() instead + //num_edges = edgelist_srcs.size(); // FIXME: Use edgelist_srcs.size() instead - printf("\n***inside*** the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, num_edges); + + printf("\n***inside*** the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, edgelist_srcs.size()); printf("\ninit invalid edges buffer\n"); raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - + printf("\n\n"); raft::print_device_vector("src", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); raft::print_device_vector("dst", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); @@ -560,30 +581,6 @@ void k_truss(raft::handle_t const& handle, // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges - - // FIXME: Need a buffer for the incomming vertex pairs because 'edges_to_num_triangles' - // is sorted in a way that matches the number of triangles per edges therefore, - // can't use a zip iterator for 'incoming_vertex_pairs'. This adds increase memory - // footprint - auto incoming_vertex_pairs = allocate_dataframe_buffer>( - num_edges, handle.get_stream()); // FIXME: Use edgelist_srcs.size() instead - - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(incoming_vertex_pairs), - get_dataframe_buffer_end(incoming_vertex_pairs), - [edge_first=edge_first - ] - __device__(edge_t idx){ - auto edge = thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), thrust::get<0>(*(edge_first + idx))); - return edge; - - }); - - // Sort the 'incoming_vertex_pairs' by 'dst' - thrust::sort( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(incoming_vertex_pairs), - get_dataframe_buffer_end(incoming_vertex_pairs)); // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) @@ -593,16 +590,14 @@ void k_truss(raft::handle_t const& handle, prefix_sum.begin(), prefix_sum.end(), [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), - num_edges = num_edges] __device__(auto idx) { + //dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), + dst_array_begin = edgelist_dsts.begin(), + num_edges = edgelist_srcs.size()] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); auto dst = thrust::get<1>(*(invalid_first + idx)); auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = - thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops - auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_upper = thrust::distance(dst_array_begin, itr_upper); + auto itr_upper = thrust::upper_bound(thrust::seq, itr_lower, dst_array_end, dst); auto dist = thrust::distance(itr_lower, itr_upper); return dist; }); @@ -624,38 +619,31 @@ void k_truss(raft::handle_t const& handle, //printf("\nbefore generating p_q, p_r\n"); printf("\n unrolling q, r edges\n"); - raft::print_device_vector("***incomming - src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); - raft::print_device_vector("***incomming - dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); thrust::for_each( handle.get_thrust_policy(), indices.begin(), indices.end(), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), + dst_array_begin = edgelist_dsts.begin(), prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), + incoming_vertex_pairs = edge_first, vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - num_edges = num_edges] __device__(auto idx) { + num_edges = edgelist_srcs.size()] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_end = dst_array_begin + num_edges; - //auto dst_array_begin = thrust::get<0>(incoming_vertex_pairs); - //auto dst_array_end = thrust::get<0>(incoming_vertex_pairs + num_edges); - //printf("\ninvalid src = %d, invalid dst = %d, idx = %d\n", src, dst, idx); auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; - thrust::tabulate( thrust::seq, vertex_pair_buffer_p_q + prefix_sum[idx], - vertex_pair_buffer_p_q + prefix_sum[idx] + dist, + vertex_pair_buffer_p_q + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, @@ -667,7 +655,7 @@ void k_truss(raft::handle_t const& handle, thrust::tabulate( thrust::seq, vertex_pair_buffer_p_r + prefix_sum[idx], - vertex_pair_buffer_p_r + prefix_sum[idx] + dist, + vertex_pair_buffer_p_r + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, @@ -699,14 +687,15 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), edges_exist.begin()); - auto has_edge_last = thrust::partition(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists; - }); - + auto has_edge_last = thrust::remove_if(handle.get_thrust_policy(), + edge_to_existance, + edge_to_existance + edges_exist.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + printf("\nedge exist = %d\n", edge_exists); + return edge_exists == 0; + }); + auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); // After pushing the non-existant edges to the second partition, @@ -714,13 +703,13 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); - printf("\n (p, q), (p, r) has edge after\n"); + printf("\n (p, q), (p, r) has edge after and size = %d\n", size_dataframe_buffer(vertex_pair_buffer_p_q)); raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); - auto edge_last = edge_first + num_edges; + auto edge_last = edge_first + edgelist_srcs.size(); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), @@ -750,13 +739,13 @@ void k_truss(raft::handle_t const& handle, // repeated auto last_edge_with_triangles = thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_edges, + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + edgelist_srcs.size(), [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); - auto last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); + auto last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // rename the above it to last_edge_with_triangles edges_with_triangles.clear(); @@ -789,26 +778,8 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_edges = edgelist_srcs.size(); - printf("\nnumber of edges after (q, r) = %d\n", num_edges); - - resize_dataframe_buffer( - incoming_vertex_pairs, num_edges, handle.get_stream()); - - thrust::tabulate(handle.get_thrust_policy(), - get_dataframe_buffer_begin(incoming_vertex_pairs), - get_dataframe_buffer_end(incoming_vertex_pairs), - [edge_first=edge_first - ] - __device__(edge_t idx){ - auto edge = thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), thrust::get<0>(*(edge_first + idx))); - return edge; - }); - - thrust::sort( - handle.get_thrust_policy(), - get_dataframe_buffer_begin(incoming_vertex_pairs), - get_dataframe_buffer_end(incoming_vertex_pairs)); // FIXME: No need to partition + //num_edges = edgelist_srcs.size(); + printf("\nnumber of edges after (q, r) = %d\n", edgelist_srcs.size()); // FIXME: Among the invalid edges, identify those that were removed to // avoid extra panalization. One way to achieve it is by calling thrust::set_intersection @@ -823,7 +794,7 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(invalid_edges_buffer), get_dataframe_buffer_end(invalid_edges_buffer), [edge_first = edge_first, // rename to 'edges' - edge_last = edge_first + num_edges, + edge_last = edge_first + edgelist_srcs.size(), num_edges = num_edges] __device__(auto invalid_edge) { auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); @@ -850,23 +821,19 @@ void k_truss(raft::handle_t const& handle, //printf("\npreparing p-r edges, the new num_invalid_edges = %d\n", num_invalid_edges); raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - // FIXME: need to sort 'incoming_vertex_pairs'. No need because a stable partition was - // performed that preserve the sorting thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), - num_edges = num_edges] __device__(auto idx) { + dst_array_begin = edgelist_dsts.begin(), + num_edges = edgelist_srcs.size()] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); auto dst = thrust::get<1>(*(invalid_first + idx)); auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = - thrust::distance(dst_array_begin, itr_lower); // FIXME: remove self loops - auto itr_upper = thrust::upper_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_upper = thrust::distance(dst_array_begin, itr_upper); + // FIXME: revisit dst_array_begin vs itr_lower + auto itr_upper = thrust::upper_bound(thrust::seq, itr_lower, dst_array_end, dst); auto dist = thrust::distance(itr_lower, itr_upper); return dist; }); @@ -887,24 +854,23 @@ void k_truss(raft::handle_t const& handle, indices.end(), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), + dst_array_begin = edgelist_dsts.begin(), prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = get_dataframe_buffer_begin(incoming_vertex_pairs), + incoming_vertex_pairs = edge_first, vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - num_edges = num_edges] __device__(auto idx) { + num_edges = edgelist_srcs.size()] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - auto dist = prefix_sum[idx + 1] - prefix_sum[idx]; thrust::tabulate( thrust::seq, vertex_pair_buffer_p_q + prefix_sum[idx], - vertex_pair_buffer_p_q + prefix_sum[idx] + dist, + vertex_pair_buffer_p_q + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, @@ -916,7 +882,7 @@ void k_truss(raft::handle_t const& handle, thrust::tabulate( thrust::seq, vertex_pair_buffer_q_r + prefix_sum[idx], - vertex_pair_buffer_q_r + prefix_sum[idx] + dist, + vertex_pair_buffer_q_r + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, @@ -943,14 +909,15 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), edges_exist.begin()); - has_edge_last = thrust::stable_partition(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists; - }); - + has_edge_last = thrust::remove_if(handle.get_thrust_policy(), + edge_to_existance, + edge_to_existance + edges_exist.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + printf("\nedge exist = %d\n", edge_exists); + return edge_exists == 0; + }); + num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); // After pushing the non-existant edges to the second partition, @@ -964,8 +931,6 @@ void k_truss(raft::handle_t const& handle, printf("\n unrolling p, r edges\n"); - raft::print_device_vector("***incomming - src", std::get<0>(incoming_vertex_pairs).data(), std::get<0>(incoming_vertex_pairs).size(), std::cout); - raft::print_device_vector("***incomming - dst", std::get<1>(incoming_vertex_pairs).data(), std::get<1>(incoming_vertex_pairs).size(), std::cout); raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); @@ -975,7 +940,7 @@ void k_truss(raft::handle_t const& handle, - edge_last = edge_first + num_edges; + edge_last = edge_first + edgelist_srcs.size(); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), @@ -1002,8 +967,8 @@ void k_truss(raft::handle_t const& handle, // Stable_parition is needed because we want to keep src and dst sorted last_edge_with_triangles = thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_edges, + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + edgelist_srcs.size(), [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); @@ -1012,7 +977,7 @@ void k_truss(raft::handle_t const& handle, raft::print_device_vector("after paritioning (q, r) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); raft::print_device_vector("after paritioning (q, r) n_tr", num_triangles.data(), num_triangles.size(), std::cout); - last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); + last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // rename the above it to last_edge_with_triangles // rename the below to edges_with_triangles edges_with_triangles.clear(); // FIXME: is this needed? @@ -1043,8 +1008,8 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_edges = edgelist_srcs.size(); - printf("\nnumber of edges after (p, r) = %d\n", num_edges); + //num_edges = edgelist_srcs.size(); + printf("\nnumber of edges after (p, r) = %d\n", edgelist_srcs.size()); raft::print_device_vector("after (p, r) srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); raft::print_device_vector("after (p, r) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); raft::print_device_vector("after (p, r) n_tr", num_triangles.data(), num_triangles.size(), std::cout); @@ -1057,6 +1022,7 @@ void k_truss(raft::handle_t const& handle, // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. + // FIXME: debug this stage. There are edges that have been removed that are still found in nbr intersection /* invalid_edge_last = thrust::partition(handle.get_thrust_policy(), @@ -1127,7 +1093,7 @@ void k_truss(raft::handle_t const& handle, raft::print_device_vector("p_r", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); raft::print_device_vector("p_r", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); - edge_last = edge_first + num_edges; + edge_last = edge_first + edgelist_srcs.size(); num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -1175,8 +1141,8 @@ void k_truss(raft::handle_t const& handle, // so that we don't need to sort it again. last_edge_with_triangles = thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_edges, + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + edgelist_srcs.size(), [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); @@ -1185,7 +1151,7 @@ void k_truss(raft::handle_t const& handle, raft::print_device_vector("after paritioning (p, q) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); raft::print_device_vector("after paritioning (p, q) n_tr", num_triangles.data(), num_triangles.size(), std::cout); - last_edge_with_triangles_idx = thrust::distance(edges_to_num_triangles, last_edge_with_triangles); + last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // rename the above it to last_edge_with_triangles // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag @@ -1219,8 +1185,8 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_edges = edgelist_srcs.size(); - printf("\nnumber of edges after (p, q) = %d\n", num_edges); + //num_edges = edgelist_srcs.size(); + printf("\nnumber of edges after (p, q) = %d\n", edgelist_srcs.size()); //num_invalid_edges = 0; //****************** debugging purposes printf("\n*******final*******\n"); @@ -1231,15 +1197,15 @@ void k_truss(raft::handle_t const& handle, auto invalid_edge_last_ = thrust::stable_partition(handle.get_thrust_policy(), - edges_to_num_triangles, - edges_to_num_triangles + num_triangles.size(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), [k] __device__(auto e) { auto num_triangles = thrust::get<1>(e); return num_triangles < k - 2; }); num_invalid_edges = static_cast( - thrust::distance(edges_to_num_triangles, invalid_edge_last_)); + thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_)); // copy invalid edges @@ -1253,16 +1219,16 @@ void k_truss(raft::handle_t const& handle, // sort back the edges as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), edge_first, - edge_first + num_edges, + edge_first + edgelist_srcs.size(), num_triangles.begin()); - printf("\n num_invalid_edges = %d, num_edges = %d\n", num_invalid_edges, num_edges); + printf("\n num_invalid_edges = %d, num_edges = %d\n", num_invalid_edges, edgelist_srcs.size()); } - if (num_invalid_edges == num_edges) { + if (num_invalid_edges == edgelist_srcs.size()) { // return empty graph view std::optional> empty_graph_view{std::nullopt}; printf("\nreturning an empty graph"); @@ -1272,7 +1238,7 @@ void k_truss(raft::handle_t const& handle, else{ // FIXME: To be updated // return cur_graph_view; - printf("\nreturning a non empty graph with num_edges = %d\n", num_edges); + printf("\nreturning a non empty graph with num_edges = %d\n", edgelist_srcs.size()); } From 4f8159c5f00919b54a5d7738abeb7d58ee90ff84 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 05:24:01 -0800 Subject: [PATCH 052/155] implement method for edge triangle count --- .../structure/edge_triangle_count_impl.cuh | 184 ++++++++++++++++++ cpp/src/structure/edge_triangle_count_sg.cu | 66 +++++++ 2 files changed, 250 insertions(+) create mode 100644 cpp/src/structure/edge_triangle_count_impl.cuh create mode 100644 cpp/src/structure/edge_triangle_count_sg.cu diff --git a/cpp/src/structure/edge_triangle_count_impl.cuh b/cpp/src/structure/edge_triangle_count_impl.cuh new file mode 100644 index 00000000000..d87fdb97ac1 --- /dev/null +++ b/cpp/src/structure/edge_triangle_count_impl.cuh @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2020-2023, 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace cugraph { + +namespace detail { + + + +template +struct update_edges_p_r_q_r_num_triangles { + size_t num_edges{}; // rename to num_edges + const edge_t edge_first_or_second{}; + raft::device_span intersection_offsets{}; + raft::device_span intersection_indices{}; + raft::device_span num_triangles{}; + + EdgeIterator edge_first{}; + + __device__ thrust::tuple operator()(size_t i) const + { + auto itr = thrust::upper_bound( + thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + if (edge_first_or_second == 0){ + auto p_r_pair = + thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); + + // Find its position in 'edges' + auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, + edge_first, + edge_first + num_edges, // pass the number of vertex pairs + p_r_pair); + idx = thrust::distance(edge_first, itr_p_r_p_q); + } + else { + auto p_r_pair = + thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); + + // Find its position in 'edges' + auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, + edge_first, + edge_first + num_edges, // pass the number of vertex pairs + p_r_pair); + + idx = thrust::distance(edge_first, itr_p_r_p_q); + + } + cuda::atomic_ref atomic_counter(num_triangles[idx]); + auto r = atomic_counter.fetch_add(vertex_t{1}, cuda::std::memory_order_relaxed); + + } +}; + +template +std::enable_if_t> +edge_triangle_count_impl( + raft::handle_t const& handle, + graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts) +{ + + + auto edge_first = + thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + + thrust::sort( + handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size()); + + auto [intersection_offsets, intersection_indices] = + detail::nbr_intersection(handle, + graph_view, + cugraph::edge_dummy_property_t{}.view(), + edge_first, + edge_first + edgelist_srcs.size(), + std::array{true, true}, + false /*FIXME: pass 'do_expensive_check' as argument*/); + + + rmm::device_uvector num_triangles( + edgelist_srcs.size(), handle.get_stream()); + + // Update the number of triangles of each (p, q) edges by looking at their intersection + // size + thrust::adjacent_difference(handle.get_thrust_policy(), + intersection_offsets.begin() + 1, + intersection_offsets.end(), + num_triangles.begin()); + + // Given intersection offsets and indices that are used to update the number of + // triangles of (p, q) edges where `r`s are the intersection indices, update + // the number of triangles of the pairs (p, r) and (q, r). + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(intersection_indices.size()), + update_edges_p_r_q_r_num_triangles{ + edgelist_srcs.size(), + 0, + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), + edge_first}); + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(intersection_indices.size()), + update_edges_p_r_q_r_num_triangles{ + edgelist_srcs.size(), + 1, + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), + edge_first}); + + + //rmm::device_uvector num_triangles( + // 2, handle.get_stream()); + return num_triangles; +} + +} // namespace + +template +rmm::device_uvector +edge_triangle_count( + raft::handle_t const& handle, + graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts) +{ + return detail::edge_triangle_count_impl(handle, graph_view, edgelist_srcs, edgelist_dsts); +} + +} // namespace cugraph diff --git a/cpp/src/structure/edge_triangle_count_sg.cu b/cpp/src/structure/edge_triangle_count_sg.cu new file mode 100644 index 00000000000..ad36be37acb --- /dev/null +++ b/cpp/src/structure/edge_triangle_count_sg.cu @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021-2023, 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 + +namespace cugraph { + +// SG instantiation +template rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + + +template rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + + +template rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + + +template rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + + + +/* +template rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + +template rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); +*/ + + + +} // namespace cugraph From bc7f18196ad466aa9419f8bdeb80b2af8919d9cf Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 07:21:10 -0800 Subject: [PATCH 053/155] mask edges only prior to calling nbr_intersection --- cpp/src/community/k_truss_impl.cuh | 366 +++-------------------------- 1 file changed, 33 insertions(+), 333 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index a224ea5cbf0..ef4431faf8a 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -244,7 +244,7 @@ void k_truss(raft::handle_t const& handle, // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core (FIXME: better mask-out // once we add masking support). - /* + // FIXME: Call k-core instead of core number { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; @@ -318,7 +318,6 @@ void k_truss(raft::handle_t const& handle, } renumber_map = std::move(tmp_renumber_map); } - */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { @@ -335,13 +334,7 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - - printf("\nOriginal graph\n"); - raft::print_device_vector("srcs", srcs_.data(), srcs_.size(), std::cout); - raft::print_device_vector("dsts", dsts_.data(), dsts_.size(), std::cout); - //***************************************************************************************************** - - + auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -382,7 +375,7 @@ void k_truss(raft::handle_t const& handle, std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, - false); //******************************************************************************************************FIXME: hardcoded to False + false); //******************FIXME: hardcoded to False modified_graph_view = (*modified_graph).view(); @@ -411,35 +404,10 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - /* - auto edge_first = - thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); - */ auto edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - /* - // The above code might have sorted my arrays therefore sort after. - // Sorting the edges with 'dst' as keys. - thrust::sort( - handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size()); - */ - - //size_t num_edges = edgelist_srcs.size(); // FIXME: rename to num_edges and always update values when removing edges - - // FIXME: Perform nbr_intersection in chuncks. - /* - auto [intersection_offsets, intersection_indices] = - detail::nbr_intersection(handle, - cur_graph_view, - cugraph::edge_dummy_property_t{}.view(), - edge_first, - edge_first + num_edges, - std::array{true, true}, - do_expensive_check); - */ - auto num_triangles = edge_triangle_count(handle, cur_graph_view, raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), @@ -448,17 +416,14 @@ void k_truss(raft::handle_t const& handle, auto edge_triangle_count_pair_first = thrust::make_zip_iterator( edge_first, num_triangles.begin()); - - - - printf("\nreduced edges from original\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edges_with_triangles(handle); + + + cugraph::edge_property_t edge_value_output(handle, + cur_graph_view); + edge_t num_valid_edges = edgelist_srcs.size(); edge_t num_invalid_edges{0}; size_t num_edges_with_triangles{0}; @@ -479,11 +444,11 @@ void k_truss(raft::handle_t const& handle, num_edges_with_triangles = static_cast( thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); + + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - //printf("\ntot number of edges = %d\n", num_edges); - //num_edges = edgelist_srcs.size(); // 'invalid_edge_first' marks the beginning of the edges to be removed auto invalid_edge_first = @@ -497,20 +462,12 @@ void k_truss(raft::handle_t const& handle, num_invalid_edges = static_cast( thrust::distance(invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - - printf("the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, edgelist_srcs.size()); - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - //if ((num_valid_edges != 0) && (num_invalid_edges !=0)){ + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; if (num_invalid_edges == 0){ break; } - printf("\nafter sorting - 0\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - // copy invalid edges auto invalid_edges_buffer = allocate_dataframe_buffer>( num_invalid_edges, handle.get_stream()); @@ -519,10 +476,7 @@ void k_truss(raft::handle_t const& handle, thrust::copy(handle.get_thrust_policy(), - //edge_first + num_valid_edges, thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, edgelist_dsts.begin() + num_valid_edges), - //thrust::get<0>(invalid_edge_first), - //edge_first + edgelist_srcs.size(), thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), edgelist_dsts.begin() + edgelist_srcs.size()), get_dataframe_buffer_begin(invalid_edges_buffer)); @@ -531,57 +485,15 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(num_valid_edges, handle.get_stream()); num_triangles.resize(num_valid_edges, handle.get_stream()); - printf("\nafter resizing\n"); - raft::print_device_vector("src", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dst", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_t", num_triangles.data(), num_triangles.size(), std::cout); - - - - + // sort back the edge as those are needed later when running a binary tree - /**/ thrust::sort_by_key(handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size(), num_triangles.begin()); - // Remove those invalid edges from the edge list - /* - printf("\nremoving invalid edges upfront\n"); - auto valid_edge_last = - thrust::partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + edgelist_srcs.size(), - [invalid_edge_first = get_dataframe_buffer_begin(invalid_edges_buffer), // rename to 'edges' - invalid_edge_last = get_dataframe_buffer_end(invalid_edges_buffer), - num_invalid_edges] - __device__(auto edge) { - auto itr = thrust::find(thrust::seq, invalid_edge_first, invalid_edge_last, thrust::get<0>(edge)); - auto idx = thrust::distance(invalid_edge_first, itr); - return idx == num_invalid_edges; - }); - */ - - - - //num_edges = edgelist_srcs.size(); // FIXME: Use edgelist_srcs.size() instead - - - - printf("\n***inside*** the number of invalid edges = %d, and num_edges = %d\n", num_invalid_edges, edgelist_srcs.size()); - printf("\ninit invalid edges buffer\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - printf("\n\n"); - - raft::print_device_vector("src", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dst", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_t", num_triangles.data(), num_triangles.size(), std::cout); - + // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges - - // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); @@ -603,8 +515,6 @@ void k_truss(raft::handle_t const& handle, }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - - raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -613,11 +523,9 @@ void k_truss(raft::handle_t const& handle, prefix_sum.back_element(handle.get_stream()), handle.get_stream()); rmm::device_uvector indices(num_invalid_edges, handle.get_stream()); - thrust::tabulate( - handle.get_thrust_policy(), indices.begin(), indices.end(), thrust::identity()); - - //printf("\nbefore generating p_q, p_r\n"); - printf("\n unrolling q, r edges\n"); + + thrust::sequence( + handle.get_thrust_policy(), indices.begin(), indices.end(), vertex_t{0}); thrust::for_each( handle.get_thrust_policy(), @@ -635,11 +543,10 @@ void k_truss(raft::handle_t const& handle, auto dst = invalid_first_dst[idx]; auto dst_array_end = dst_array_begin + num_edges; - //printf("\ninvalid src = %d, invalid dst = %d, idx = %d\n", src, dst, idx); auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - + thrust::tabulate( thrust::seq, vertex_pair_buffer_p_q + prefix_sum[idx], @@ -661,19 +568,10 @@ void k_truss(raft::handle_t const& handle, src = src, idx, idx_lower = idx_lower](auto idx_in_segment) { - if (dst == 2){ - printf("\nincoming edge %d, %d, the begining of the range = %d and idx = %d", thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst, idx_lower, idx); - } return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); - //printf("\nafter generating p_q, p_r\n"); - printf("\n (p, q), (p, r) has edge before\n"); - raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); - raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); auto edges_exist = cur_graph_view.has_edge( handle, @@ -692,7 +590,6 @@ void k_truss(raft::handle_t const& handle, edge_to_existance + edges_exist.size(), [] __device__(auto e) { auto edge_exists = thrust::get<1>(e); - printf("\nedge exist = %d\n", edge_exists); return edge_exists == 0; }); @@ -703,12 +600,6 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); - printf("\n (p, q), (p, r) has edge after and size = %d\n", size_dataframe_buffer(vertex_pair_buffer_p_q)); - raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_r - src", std::get<0>(vertex_pair_buffer_p_r).data(), std::get<0>(vertex_pair_buffer_p_r).size(), std::cout); - raft::print_device_vector("***p_r - dst", std::get<1>(vertex_pair_buffer_p_r).data(), std::get<1>(vertex_pair_buffer_p_r).size(), std::cout); - auto edge_last = edge_first + edgelist_srcs.size(); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -746,81 +637,11 @@ void k_truss(raft::handle_t const& handle, }); auto last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - // rename the above it to last_edge_with_triangles - - edges_with_triangles.clear(); - - cugraph::edge_property_t edge_value_output(handle, - cur_graph_view); - - // rename the below to edges_with_triangles - edges_with_triangles.insert(edgelist_srcs.begin(), - edgelist_srcs.begin() + last_edge_with_triangles_idx, - edgelist_dsts.begin()); - - cugraph::transform_e( - handle, - cur_graph_view, - edges_with_triangles, - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - return true; - }, - edge_value_output.mutable_view(), - false); - - cur_graph_view.attach_edge_mask(edge_value_output.view()); - - // resize the 'edgelist_srcs' and 'edgelist_dst' - edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - - //num_edges = edgelist_srcs.size(); - printf("\nnumber of edges after (q, r) = %d\n", edgelist_srcs.size()); - - // FIXME: Among the invalid edges, identify those that were removed to - // avoid extra panalization. One way to achieve it is by calling thrust::set_intersection - // to filter out the removed edges. However this will require another array. - - // Find the intersection of 'invalid_edges_buffer' and 'edges' to extract the remaining invalid - // edges that still need to be processed. Didn't used thrust::set_intersection because I didn't - // want to create a temporary array - /* - auto invalid_edge_last = - thrust::partition(handle.get_thrust_policy(), - get_dataframe_buffer_begin(invalid_edges_buffer), - get_dataframe_buffer_end(invalid_edges_buffer), - [edge_first = edge_first, // rename to 'edges' - edge_last = edge_first + edgelist_srcs.size(), - num_edges = num_edges] - __device__(auto invalid_edge) { - auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); - auto idx = thrust::distance(edge_first, itr); - return idx < num_edges; - }); - - num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); - if (num_invalid_edges == 0){ - break; - } - resize_dataframe_buffer( - invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - */ - - printf("\nafter case -2 (q, r) invalid edges buffer\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - + // Need to run prefix_sum again to get new ranges because some incoming edges were removed prefix_sum.resize(num_invalid_edges + 1, handle.get_stream()); indices.resize(num_invalid_edges, handle.get_stream()); - //printf("\npreparing p-r edges, the new num_invalid_edges = %d\n", num_invalid_edges); - raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); - thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), @@ -891,11 +712,6 @@ void k_truss(raft::handle_t const& handle, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); }); - - raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); - raft::print_device_vector("***q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); edges_exist = cur_graph_view.has_edge( handle, @@ -914,7 +730,6 @@ void k_truss(raft::handle_t const& handle, edge_to_existance + edges_exist.size(), [] __device__(auto e) { auto edge_exists = thrust::get<1>(e); - printf("\nedge exist = %d\n", edge_exists); return edge_exists == 0; }); @@ -926,20 +741,6 @@ void k_truss(raft::handle_t const& handle, resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); edges_exist.resize(size_dataframe_buffer(vertex_pair_buffer_p_q), handle.get_stream()); - - - - printf("\n unrolling p, r edges\n"); - - - raft::print_device_vector("***p_q - src", std::get<0>(vertex_pair_buffer_p_q).data(), std::get<0>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***p_q - dst", std::get<1>(vertex_pair_buffer_p_q).data(), std::get<1>(vertex_pair_buffer_p_q).size(), std::cout); - raft::print_device_vector("***q_r - src", std::get<0>(vertex_pair_buffer_q_r).data(), std::get<0>(vertex_pair_buffer_q_r).size(), std::cout); - raft::print_device_vector("***q_r - dst", std::get<1>(vertex_pair_buffer_q_r).data(), std::get<1>(vertex_pair_buffer_q_r).size(), std::cout); - raft::print_device_vector("edges_exists", edges_exist.data(), edges_exist.size(), std::cout); - - - edge_last = edge_first + edgelist_srcs.size(); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -972,21 +773,25 @@ void k_truss(raft::handle_t const& handle, [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); - - raft::print_device_vector("after paritioning (q, r) srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("after paritioning (q, r) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("after paritioning (q, r) n_tr", num_triangles.data(), num_triangles.size(), std::cout); last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + + + + // resize the 'edgelist_srcs' and 'edgelsit_dst' + edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); + num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); + + + edge_value_output.clear(handle); // rename the above it to last_edge_with_triangles // rename the below to edges_with_triangles edges_with_triangles.clear(); // FIXME: is this needed? - cugraph::edge_property_t edge_value_output_p_r( - handle, cur_graph_view); edges_with_triangles.insert(edgelist_srcs.begin(), - edgelist_srcs.begin() + last_edge_with_triangles_idx, - edgelist_dsts.begin()); + edgelist_srcs.begin(), + edgelist_dsts.end()); cugraph::transform_e( handle, @@ -998,69 +803,15 @@ void k_truss(raft::handle_t const& handle, [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { return true; }, - edge_value_output_p_r.mutable_view(), + edge_value_output.mutable_view(), false); - cur_graph_view.attach_edge_mask(edge_value_output_p_r.view()); - - // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - - //num_edges = edgelist_srcs.size(); - printf("\nnumber of edges after (p, r) = %d\n", edgelist_srcs.size()); - raft::print_device_vector("after (p, r) srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("after (p, r) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("after (p, r) n_tr", num_triangles.data(), num_triangles.size(), std::cout); - - - printf("\np-r printing invalid edges buffer before\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + cur_graph_view.attach_edge_mask(edge_value_output.view()); // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. // FIXME: debug this stage. There are edges that have been removed that are still found in nbr intersection - /* - invalid_edge_last = - thrust::partition(handle.get_thrust_policy(), - get_dataframe_buffer_begin(invalid_edges_buffer), - get_dataframe_buffer_end(invalid_edges_buffer), - [edge_first = edge_first, // rename to 'edges' - edge_last = edge_first + num_edges, - num_edges = num_edges] - __device__(auto invalid_edge) { - - auto itr = thrust::find(thrust::seq, edge_first, edge_last, invalid_edge); - auto idx = thrust::distance(edge_first, itr); - if (idx > num_edges){ - printf("\nfound an invalid edge\n"); - } - return idx < num_edges; - }); - - num_invalid_edges = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), invalid_edge_last); - printf("\nnumber of invalid edges for (p, q) = %d\n", num_invalid_edges); - if (num_invalid_edges == 0){ - break; - } - - resize_dataframe_buffer( - invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - */ - printf("\nafter case -3 (q, r) invalid edges buffer\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - - - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - //printf("\nprinting invalid edges buffer after\n"); - //raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - //raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, @@ -1070,8 +821,6 @@ void k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - raft::print_device_vector("intersection_offsets", intersection_offsets.data(), intersection_offsets.size(), std::cout); - raft::print_device_vector("intersection_indices", intersection_indices.data(), intersection_indices.size(), std::cout); size_t accumulate_pair_size = intersection_indices.size(); @@ -1089,9 +838,6 @@ void k_truss(raft::handle_t const& handle, intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) }); - - raft::print_device_vector("p_r", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); - raft::print_device_vector("p_r", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); edge_last = edge_first + edgelist_srcs.size(); num_edge_exists = accumulate_pair_size; @@ -1121,9 +867,6 @@ void k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - raft::print_device_vector("q_r", std::get<0>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); - raft::print_device_vector("q_r", std::get<1>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); - thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), @@ -1146,55 +889,16 @@ void k_truss(raft::handle_t const& handle, [] __device__(auto edge_to_num_triangles) { return thrust::get<1>(edge_to_num_triangles) > 0; }); - - raft::print_device_vector("after paritioning (p, q) srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("after paritioning (p, q) dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("after paritioning (p, q) n_tr", num_triangles.data(), num_triangles.size(), std::cout); + last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // rename the above it to last_edge_with_triangles - // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag - edges_with_triangles.clear(); // FIXME: is this needed? - - cugraph::edge_property_t edge_value_output_p_q(handle, - cur_graph_view); - - // rename the below to edges_with_triangles - edges_with_triangles.insert(edgelist_srcs.begin(), - edgelist_srcs.begin() + last_edge_with_triangles_idx, - edgelist_dsts.begin()); - - cugraph::transform_e( - handle, - cur_graph_view, - edges_with_triangles, - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - return true; - }, - edge_value_output_p_q.mutable_view(), - false); - - cur_graph_view.attach_edge_mask(edge_value_output_p_q.view()); - // resize the 'edgelist_srcs' and 'edgelsit_dst' edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - //num_edges = edgelist_srcs.size(); - printf("\nnumber of edges after (p, q) = %d\n", edgelist_srcs.size()); - - //num_invalid_edges = 0; //****************** debugging purposes - printf("\n*******final*******\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - - auto invalid_edge_last_ = thrust::stable_partition(handle.get_thrust_policy(), edge_triangle_count_pair_first, @@ -1221,10 +925,6 @@ void k_truss(raft::handle_t const& handle, edge_first, edge_first + edgelist_srcs.size(), num_triangles.begin()); - printf("\n num_invalid_edges = %d, num_edges = %d\n", num_invalid_edges, edgelist_srcs.size()); - - - } From 2cfd5536b8a5f281b8d904aa0a920a2b1d582dfa Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 07:26:39 -0800 Subject: [PATCH 054/155] fix style --- cpp/include/cugraph/graph_functions.hpp | 9 +- cpp/src/community/k_truss_impl.cuh | 460 ++++++++---------- .../structure/edge_triangle_count_impl.cuh | 145 +++--- cpp/src/structure/edge_triangle_count_sg.cu | 7 - cpp/tests/community/k_truss_test.cpp | 4 +- 5 files changed, 283 insertions(+), 342 deletions(-) diff --git a/cpp/include/cugraph/graph_functions.hpp b/cpp/include/cugraph/graph_functions.hpp index 8c12fdf1ec3..b4c21adfc89 100644 --- a/cpp/include/cugraph/graph_functions.hpp +++ b/cpp/include/cugraph/graph_functions.hpp @@ -1051,7 +1051,6 @@ remove_multi_edges(raft::handle_t const& handle, std::optional>&& edgelist_edge_types, bool keep_min_value_edge = false); - /** * @brief Count the number of triangles for each edge. * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. @@ -1062,12 +1061,8 @@ remove_multi_edges(raft::handle_t const& handle, * @param edgelist_srcs List of source vertex ids * @param edgelist_dsts List of destination vertex ids */ -template -rmm::device_uvector -edge_triangle_count( +template +rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, graph_view_t const& graph_view, raft::device_span edgelist_srcs, diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index ef4431faf8a..2e93589c2b2 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -41,7 +42,6 @@ #include #include #include -#include namespace cugraph { @@ -61,7 +61,7 @@ struct exclude_self_loop_t { template struct in_k_plus_one_or_greater_t { edge_t k{}; - __device__ bool operator()(edge_t core_number) const { return core_number >= k-1; } + __device__ bool operator()(edge_t core_number) const { return core_number >= k - 1; } }; template @@ -97,7 +97,6 @@ struct extract_low_to_high_degree_edges_t { } }; - template struct unroll_edge { raft::device_span num_triangles{}; @@ -108,23 +107,17 @@ struct unroll_edge { __device__ thrust::tuple operator()(edge_t i) const { // edges are sort with destination as key so reverse the edge when looking it - auto pair = - thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), thrust::get<0>(*(edge_unrolled + i))); + auto pair = thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), + thrust::get<0>(*(edge_unrolled + i))); // Find its position in 'edges' - auto itr = thrust::lower_bound(thrust::seq, - edge_first, - edge_last, - pair - ); + auto itr = thrust::lower_bound(thrust::seq, edge_first, edge_last, pair); auto idx = thrust::distance(edge_first, itr); cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_sub(vertex_t{1}, cuda::std::memory_order_relaxed); - } }; - template struct generate_p_r { raft::device_span intersection_offsets{}; @@ -136,15 +129,13 @@ struct generate_p_r { { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = - thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); return pair; } }; - template struct generate_q_r { raft::device_span intersection_offsets{}; @@ -156,9 +147,8 @@ struct generate_q_r { { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = - thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); return pair; } @@ -217,7 +207,6 @@ void k_truss(raft::handle_t const& handle, edge_dummy_property_t{}.view(), exclude_self_loop_t{}); - if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning edge_src_in_k_plus_one_cores( handle, cur_graph_view); @@ -323,8 +312,8 @@ void k_truss(raft::handle_t const& handle, { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - - // FIXME: REmove this********************************************************************************** + // FIXME: REmove + // this********************************************************************************** rmm::device_uvector srcs_(0, handle.get_stream()); rmm::device_uvector dsts_(0, handle.get_stream()); @@ -334,7 +323,7 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - + auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -375,7 +364,7 @@ void k_truss(raft::handle_t const& handle, std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, - false); //******************FIXME: hardcoded to False + false); //******************FIXME: hardcoded to False modified_graph_view = (*modified_graph).view(); @@ -405,93 +394,84 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - auto edge_first = - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - - auto num_triangles = edge_triangle_count(handle, - cur_graph_view, - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - - - auto edge_triangle_count_pair_first = thrust::make_zip_iterator( - edge_first, num_triangles.begin()); - + auto edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + + auto num_triangles = edge_triangle_count( + handle, + cur_graph_view, + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + + auto edge_triangle_count_pair_first = + thrust::make_zip_iterator(edge_first, num_triangles.begin()); + // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edges_with_triangles(handle); - cugraph::edge_property_t edge_value_output(handle, cur_graph_view); - + edge_t num_valid_edges = edgelist_srcs.size(); edge_t num_invalid_edges{0}; size_t num_edges_with_triangles{0}; while (true) { + // Remove edges that have a triangle count of zero. Those should not be accounted + // for during the unroling phase. + auto edges_with_triangle_last = + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles > 0; + }); - // Remove edges that have a triangle count of zero. Those should not be accounted - // for during the unroling phase. - auto edges_with_triangle_last = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles > 0; - }); - - num_edges_with_triangles = static_cast( - thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); - - - - edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); - edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); - num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - - // 'invalid_edge_first' marks the beginning of the edges to be removed - auto invalid_edge_first = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles >= k - 2; - }); - - num_invalid_edges = static_cast( - thrust::distance(invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - if (num_invalid_edges == 0){ - break; - } + num_edges_with_triangles = static_cast( + thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); + + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); + edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); + num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + + // 'invalid_edge_first' marks the beginning of the edges to be removed + auto invalid_edge_first = + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles >= k - 2; + }); - // copy invalid edges - auto invalid_edges_buffer = allocate_dataframe_buffer>( - num_invalid_edges, handle.get_stream()); - + num_invalid_edges = static_cast(thrust::distance( + invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; + if (num_invalid_edges == 0) { break; } + + // copy invalid edges + auto invalid_edges_buffer = allocate_dataframe_buffer>( + num_invalid_edges, handle.get_stream()); + + thrust::copy(handle.get_thrust_policy(), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, + edgelist_dsts.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), + edgelist_dsts.begin() + edgelist_srcs.size()), + get_dataframe_buffer_begin(invalid_edges_buffer)); + + // resize the 'edgelist_srcs' and 'edgelist_dst' + edgelist_srcs.resize(num_valid_edges, handle.get_stream()); + edgelist_dsts.resize(num_valid_edges, handle.get_stream()); + num_triangles.resize(num_valid_edges, handle.get_stream()); + + // sort back the edge as those are needed later when running a binary tree + thrust::sort_by_key(handle.get_thrust_policy(), + edge_first, + edge_first + edgelist_srcs.size(), + num_triangles.begin()); - - thrust::copy(handle.get_thrust_policy(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, edgelist_dsts.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), edgelist_dsts.begin() + edgelist_srcs.size()), - get_dataframe_buffer_begin(invalid_edges_buffer)); - - // resize the 'edgelist_srcs' and 'edgelist_dst' - edgelist_srcs.resize(num_valid_edges, handle.get_stream()); - edgelist_dsts.resize(num_valid_edges, handle.get_stream()); - num_triangles.resize(num_valid_edges, handle.get_stream()); - - - // sort back the edge as those are needed later when running a binary tree - thrust::sort_by_key(handle.get_thrust_policy(), - edge_first, - edge_first + edgelist_srcs.size(), - num_triangles.begin()); - // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and @@ -501,8 +481,8 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - //dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), + [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + // dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), dst_array_begin = edgelist_dsts.begin(), num_edges = edgelist_srcs.size()] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); @@ -523,38 +503,37 @@ void k_truss(raft::handle_t const& handle, prefix_sum.back_element(handle.get_stream()), handle.get_stream()); rmm::device_uvector indices(num_invalid_edges, handle.get_stream()); - - thrust::sequence( - handle.get_thrust_policy(), indices.begin(), indices.end(), vertex_t{0}); + + thrust::sequence(handle.get_thrust_policy(), indices.begin(), indices.end(), vertex_t{0}); thrust::for_each( handle.get_thrust_policy(), indices.begin(), indices.end(), - [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = edgelist_dsts.begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = edge_first, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_end = dst_array_begin + num_edges; + [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), + invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + dst_array_begin = edgelist_dsts.begin(), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs = edge_first, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + num_edges = edgelist_srcs.size()] __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - + thrust::tabulate( thrust::seq, vertex_pair_buffer_p_q + prefix_sum[idx], vertex_pair_buffer_p_q + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), src); }); @@ -564,10 +543,10 @@ void k_truss(raft::handle_t const& handle, vertex_pair_buffer_p_r + prefix_sum[idx], vertex_pair_buffer_p_r + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, + dst = dst, + src = src, idx, - idx_lower = idx_lower](auto idx_in_segment) { + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); @@ -589,10 +568,10 @@ void k_truss(raft::handle_t const& handle, edge_to_existance, edge_to_existance + edges_exist.size(), [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); - + auto edge_exists = thrust::get<1>(e); + return edge_exists == 0; + }); + auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); // After pushing the non-existant edges to the second partition, @@ -605,22 +584,22 @@ void k_truss(raft::handle_t const& handle, thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edge_first, - edge_last, + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + edge_first, + edge_last, }); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - edge_first, - edge_last, - }); - + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + edge_first, + edge_last, + }); + // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' @@ -636,8 +615,9 @@ void k_truss(raft::handle_t const& handle, return thrust::get<1>(edge_to_num_triangles) > 0; }); - auto last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - + auto last_edge_with_triangles_idx = + thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + // Need to run prefix_sum again to get new ranges because some incoming edges were removed prefix_sum.resize(num_invalid_edges + 1, handle.get_stream()); indices.resize(num_invalid_edges, handle.get_stream()); @@ -646,11 +626,11 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), dst_array_begin = edgelist_dsts.begin(), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = thrust::get<0>(*(invalid_first + idx)); - auto dst = thrust::get<1>(*(invalid_first + idx)); + num_edges = edgelist_srcs.size()] __device__(auto idx) { + auto src = thrust::get<0>(*(invalid_first + idx)); + auto dst = thrust::get<1>(*(invalid_first + idx)); auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); // FIXME: revisit dst_array_begin vs itr_lower @@ -661,7 +641,7 @@ void k_truss(raft::handle_t const& handle, thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - + // case 3 unroll (p, r) vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -673,17 +653,17 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), indices.begin(), indices.end(), - [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = edgelist_dsts.begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = edge_first, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_end = dst_array_begin + num_edges; + [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), + invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + dst_array_begin = edgelist_dsts.begin(), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs = edge_first, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + num_edges = edgelist_srcs.size()] __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range @@ -693,9 +673,9 @@ void k_truss(raft::handle_t const& handle, vertex_pair_buffer_p_q + prefix_sum[idx], vertex_pair_buffer_p_q + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( src, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); }); @@ -705,9 +685,9 @@ void k_truss(raft::handle_t const& handle, vertex_pair_buffer_q_r + prefix_sum[idx], vertex_pair_buffer_q_r + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); @@ -724,15 +704,15 @@ void k_truss(raft::handle_t const& handle, thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), edges_exist.begin()); - + has_edge_last = thrust::remove_if(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); - + edge_to_existance, + edge_to_existance + edges_exist.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists == 0; + }); + num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); // After pushing the non-existant edges to the second partition, @@ -746,21 +726,21 @@ void k_truss(raft::handle_t const& handle, thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edge_first, - edge_last, + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + edge_first, + edge_last, }); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - edge_first, - edge_last, - }); + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + edge_first, + edge_last, + }); // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them @@ -774,24 +754,21 @@ void k_truss(raft::handle_t const& handle, return thrust::get<1>(edge_to_num_triangles) > 0; }); - last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - - + last_edge_with_triangles_idx = + thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // resize the 'edgelist_srcs' and 'edgelsit_dst' edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - edge_value_output.clear(handle); // rename the above it to last_edge_with_triangles // rename the below to edges_with_triangles edges_with_triangles.clear(); // FIXME: is this needed? - edges_with_triangles.insert(edgelist_srcs.begin(), - edgelist_srcs.begin(), - edgelist_dsts.end()); + edges_with_triangles.insert( + edgelist_srcs.begin(), edgelist_srcs.begin(), edgelist_dsts.end()); cugraph::transform_e( handle, @@ -811,7 +788,8 @@ void k_truss(raft::handle_t const& handle, // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. - // FIXME: debug this stage. There are edges that have been removed that are still found in nbr intersection + // FIXME: debug this stage. There are edges that have been removed that are still found in nbr + // intersection auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, @@ -821,62 +799,59 @@ void k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - size_t accumulate_pair_size = - intersection_indices.size(); - + size_t accumulate_pair_size = intersection_indices.size(); + auto vertex_pair_buffer_p_r_edge_p_q = - allocate_dataframe_buffer>( - accumulate_pair_size, handle.get_stream()); - + allocate_dataframe_buffer>(accumulate_pair_size, + handle.get_stream()); + thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), - intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer) - }); - - edge_last = edge_first + edgelist_srcs.size(); + generate_p_r{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(invalid_edges_buffer)}); + + edge_last = edge_first + edgelist_srcs.size(); num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - edge_first, - edge_last, - }); + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + edge_first, + edge_last, + }); auto vertex_pair_buffer_q_r_edge_p_q = - allocate_dataframe_buffer>( - accumulate_pair_size, handle.get_stream()); + allocate_dataframe_buffer>(accumulate_pair_size, + handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + - accumulate_pair_size, + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + accumulate_pair_size, generate_q_r{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - + thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - edge_first, - edge_last, - }); - + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + edge_first, + edge_last, + }); + // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' @@ -890,8 +865,8 @@ void k_truss(raft::handle_t const& handle, return thrust::get<1>(edge_to_num_triangles) > 0; }); - - last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + last_edge_with_triangles_idx = + thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // rename the above it to last_edge_with_triangles // resize the 'edgelist_srcs' and 'edgelsit_dst' @@ -900,49 +875,44 @@ void k_truss(raft::handle_t const& handle, num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); auto invalid_edge_last_ = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles < k - 2; - }); - - num_invalid_edges = static_cast( - thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_)); + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles < k - 2; + }); + num_invalid_edges = + static_cast(thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_)); // copy invalid edges resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - + thrust::copy(handle.get_thrust_policy(), - edge_first, - edge_first + num_invalid_edges, - get_dataframe_buffer_begin(invalid_edges_buffer)); - + edge_first, + edge_first + num_invalid_edges, + get_dataframe_buffer_begin(invalid_edges_buffer)); + // sort back the edges as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size(), num_triangles.begin()); - } if (num_invalid_edges == edgelist_srcs.size()) { // return empty graph view - std::optional> empty_graph_view{std::nullopt}; + std::optional> empty_graph_view{ + std::nullopt}; printf("\nreturning an empty graph"); // FIXME: To be updated // return empty_graph_view; - } - else{ + } else { // FIXME: To be updated // return cur_graph_view; printf("\nreturning a non empty graph with num_edges = %d\n", edgelist_srcs.size()); } - - - } } diff --git a/cpp/src/structure/edge_triangle_count_impl.cuh b/cpp/src/structure/edge_triangle_count_impl.cuh index d87fdb97ac1..fdfc034ee3b 100644 --- a/cpp/src/structure/edge_triangle_count_impl.cuh +++ b/cpp/src/structure/edge_triangle_count_impl.cuh @@ -19,21 +19,21 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include +#include #include #include #include -#include #include #include @@ -46,12 +46,10 @@ namespace cugraph { namespace detail { - - template struct update_edges_p_r_q_r_num_triangles { - size_t num_edges{}; // rename to num_edges - const edge_t edge_first_or_second{}; + size_t num_edges{}; // rename to num_edges + const edge_t edge_first_or_second{}; raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span num_triangles{}; @@ -63,116 +61,101 @@ struct update_edges_p_r_q_r_num_triangles { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - if (edge_first_or_second == 0){ + if (edge_first_or_second == 0) { auto p_r_pair = thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); - + // Find its position in 'edges' - auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, - edge_first, - edge_first + num_edges, // pass the number of vertex pairs - p_r_pair); - idx = thrust::distance(edge_first, itr_p_r_p_q); - } - else { + auto itr_p_r_p_q = + thrust::lower_bound(thrust::seq, + edge_first, + edge_first + num_edges, // pass the number of vertex pairs + p_r_pair); + idx = thrust::distance(edge_first, itr_p_r_p_q); + } else { auto p_r_pair = thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); - + // Find its position in 'edges' - auto itr_p_r_p_q = thrust::lower_bound(thrust::seq, - edge_first, - edge_first + num_edges, // pass the number of vertex pairs - p_r_pair); - + auto itr_p_r_p_q = + thrust::lower_bound(thrust::seq, + edge_first, + edge_first + num_edges, // pass the number of vertex pairs + p_r_pair); + idx = thrust::distance(edge_first, itr_p_r_p_q); - } cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_add(vertex_t{1}, cuda::std::memory_order_relaxed); - } }; -template -std::enable_if_t> -edge_triangle_count_impl( +template +std::enable_if_t> edge_triangle_count_impl( raft::handle_t const& handle, graph_view_t const& graph_view, raft::device_span edgelist_srcs, raft::device_span edgelist_dsts) { + auto edge_first = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); - - auto edge_first = - thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + thrust::sort(handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size()); - thrust::sort( - handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size()); - auto [intersection_offsets, intersection_indices] = - detail::nbr_intersection(handle, - graph_view, - cugraph::edge_dummy_property_t{}.view(), - edge_first, - edge_first + edgelist_srcs.size(), - std::array{true, true}, - false /*FIXME: pass 'do_expensive_check' as argument*/); - - - rmm::device_uvector num_triangles( - edgelist_srcs.size(), handle.get_stream()); - + detail::nbr_intersection(handle, + graph_view, + cugraph::edge_dummy_property_t{}.view(), + edge_first, + edge_first + edgelist_srcs.size(), + std::array{true, true}, + false /*FIXME: pass 'do_expensive_check' as argument*/); + + rmm::device_uvector num_triangles(edgelist_srcs.size(), handle.get_stream()); + // Update the number of triangles of each (p, q) edges by looking at their intersection // size thrust::adjacent_difference(handle.get_thrust_policy(), intersection_offsets.begin() + 1, intersection_offsets.end(), num_triangles.begin()); - + // Given intersection offsets and indices that are used to update the number of // triangles of (p, q) edges where `r`s are the intersection indices, update // the number of triangles of the pairs (p, r) and (q, r). - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(intersection_indices.size()), - update_edges_p_r_q_r_num_triangles{ - edgelist_srcs.size(), - 0, - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), intersection_indices.size()), - raft::device_span(num_triangles.data(), num_triangles.size()), - edge_first}); - - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(intersection_indices.size()), - update_edges_p_r_q_r_num_triangles{ - edgelist_srcs.size(), - 1, - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), intersection_indices.size()), - raft::device_span(num_triangles.data(), num_triangles.size()), - edge_first}); - - - //rmm::device_uvector num_triangles( - // 2, handle.get_stream()); + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(intersection_indices.size()), + update_edges_p_r_q_r_num_triangles{ + edgelist_srcs.size(), + 0, + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), + edge_first}); + + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(intersection_indices.size()), + update_edges_p_r_q_r_num_triangles{ + edgelist_srcs.size(), + 1, + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), intersection_indices.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), + edge_first}); + + // rmm::device_uvector num_triangles( + // 2, handle.get_stream()); return num_triangles; } -} // namespace +} // namespace detail -template -rmm::device_uvector -edge_triangle_count( +template +rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, graph_view_t const& graph_view, raft::device_span edgelist_srcs, diff --git a/cpp/src/structure/edge_triangle_count_sg.cu b/cpp/src/structure/edge_triangle_count_sg.cu index ad36be37acb..c2bbf6aa8dd 100644 --- a/cpp/src/structure/edge_triangle_count_sg.cu +++ b/cpp/src/structure/edge_triangle_count_sg.cu @@ -24,29 +24,24 @@ template rmm::device_uvector edge_triangle_count( raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); - template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); - template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); - template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); - - /* template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, @@ -61,6 +56,4 @@ template rmm::device_uvector edge_triangle_count( raft::device_span edgelist_dsts); */ - - } // namespace cugraph diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index b2e80e867fa..26f85c3add8 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -113,8 +113,8 @@ INSTANTIATE_TEST_SUITE_P(file_test, Tests_KTruss_File, ::testing::Combine( // enable correctness checks - ::testing::Values(KTruss_Usecase{2}), + ::testing::Values(KTruss_Usecase{4}), ::testing::Values(cugraph::test::File_Usecase( - "/home/nfs/jnke/ktruss/cugraph/datasets/dummy.mtx")))); + "/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx")))); CUGRAPH_TEST_PROGRAM_MAIN() From f0fce6f764b7604ff5daf231ee1b09e778fa35de Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 08:24:13 -0800 Subject: [PATCH 055/155] update fixme and remove unused variables --- cpp/src/community/k_truss_impl.cuh | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 2e93589c2b2..466e57f7d32 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -234,6 +234,10 @@ void k_truss(raft::handle_t const& handle, // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core (FIXME: better mask-out // once we add masking support). // FIXME: Call k-core instead of core number + // FIXME: There is concern when calling k_core/core_number to filter out some vertices. In fact, + // this preprocessing step alters the structure of the original graph as it already reduces the + // number of triangles per edge therefore, we would need to reflect that on the new K Truss + // we would like to find. { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; @@ -312,18 +316,6 @@ void k_truss(raft::handle_t const& handle, { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - // FIXME: REmove - // this********************************************************************************** - rmm::device_uvector srcs_(0, handle.get_stream()); - rmm::device_uvector dsts_(0, handle.get_stream()); - - std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); - auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -383,7 +375,6 @@ void k_truss(raft::handle_t const& handle, { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - std::optional> renumber_map{std::nullopt}; rmm::device_uvector edgelist_srcs(0, handle.get_stream()); rmm::device_uvector edgelist_dsts(0, handle.get_stream()); @@ -502,14 +493,10 @@ void k_truss(raft::handle_t const& handle, auto vertex_pair_buffer_p_r = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - rmm::device_uvector indices(num_invalid_edges, handle.get_stream()); - - thrust::sequence(handle.get_thrust_policy(), indices.begin(), indices.end(), vertex_t{0}); - thrust::for_each( handle.get_thrust_policy(), - indices.begin(), - indices.end(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), dst_array_begin = edgelist_dsts.begin(), @@ -620,7 +607,6 @@ void k_truss(raft::handle_t const& handle, // Need to run prefix_sum again to get new ranges because some incoming edges were removed prefix_sum.resize(num_invalid_edges + 1, handle.get_stream()); - indices.resize(num_invalid_edges, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), @@ -651,8 +637,8 @@ void k_truss(raft::handle_t const& handle, thrust::for_each( handle.get_thrust_policy(), - indices.begin(), - indices.end(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), dst_array_begin = edgelist_dsts.begin(), From 979f715e91317a922a00957daf041013d06f7bd4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 11:05:24 -0800 Subject: [PATCH 056/155] update thrust call --- cpp/src/community/k_truss_impl.cuh | 482 ++++++++++++++++------------- 1 file changed, 261 insertions(+), 221 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 466e57f7d32..68bb9f42722 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -42,6 +41,7 @@ #include #include #include +#include namespace cugraph { @@ -61,7 +61,7 @@ struct exclude_self_loop_t { template struct in_k_plus_one_or_greater_t { edge_t k{}; - __device__ bool operator()(edge_t core_number) const { return core_number >= k - 1; } + __device__ bool operator()(edge_t core_number) const { return core_number >= k-1; } }; template @@ -97,6 +97,7 @@ struct extract_low_to_high_degree_edges_t { } }; + template struct unroll_edge { raft::device_span num_triangles{}; @@ -107,17 +108,23 @@ struct unroll_edge { __device__ thrust::tuple operator()(edge_t i) const { // edges are sort with destination as key so reverse the edge when looking it - auto pair = thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), - thrust::get<0>(*(edge_unrolled + i))); + auto pair = + thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), thrust::get<0>(*(edge_unrolled + i))); // Find its position in 'edges' - auto itr = thrust::lower_bound(thrust::seq, edge_first, edge_last, pair); + auto itr = thrust::lower_bound(thrust::seq, + edge_first, + edge_last, + pair + ); auto idx = thrust::distance(edge_first, itr); cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_sub(vertex_t{1}, cuda::std::memory_order_relaxed); + } }; + template struct generate_p_r { raft::device_span intersection_offsets{}; @@ -129,13 +136,15 @@ struct generate_p_r { { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = + thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); return pair; } }; + template struct generate_q_r { raft::device_span intersection_offsets{}; @@ -147,8 +156,9 @@ struct generate_q_r { { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); + auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); + auto pair = + thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); return pair; } @@ -207,6 +217,7 @@ void k_truss(raft::handle_t const& handle, edge_dummy_property_t{}.view(), exclude_self_loop_t{}); + if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning edge_src_in_k_plus_one_cores( handle, cur_graph_view); @@ -312,10 +318,23 @@ void k_truss(raft::handle_t const& handle, renumber_map = std::move(tmp_renumber_map); } + // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + + // FIXME: REmove this********************************************************************************** + rmm::device_uvector srcs_(0, handle.get_stream()); + rmm::device_uvector dsts_(0, handle.get_stream()); + + std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -356,7 +375,7 @@ void k_truss(raft::handle_t const& handle, std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, - false); //******************FIXME: hardcoded to False + false); //******************FIXME: hardcoded to False modified_graph_view = (*modified_graph).view(); @@ -385,95 +404,103 @@ void k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - auto edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - - auto num_triangles = edge_triangle_count( - handle, - cur_graph_view, - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - - auto edge_triangle_count_pair_first = - thrust::make_zip_iterator(edge_first, num_triangles.begin()); - + auto edge_first = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + + auto num_triangles = edge_triangle_count(handle, + cur_graph_view, + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + + + auto edge_triangle_count_pair_first = thrust::make_zip_iterator( + edge_first, num_triangles.begin()); + // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edges_with_triangles(handle); + cugraph::edge_property_t edge_value_output(handle, cur_graph_view); - + edge_t num_valid_edges = edgelist_srcs.size(); edge_t num_invalid_edges{0}; size_t num_edges_with_triangles{0}; while (true) { - // Remove edges that have a triangle count of zero. Those should not be accounted - // for during the unroling phase. - auto edges_with_triangle_last = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles > 0; - }); - - num_edges_with_triangles = static_cast( - thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); - - edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); - edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); - num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - - // 'invalid_edge_first' marks the beginning of the edges to be removed - auto invalid_edge_first = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles >= k - 2; - }); - - num_invalid_edges = static_cast(thrust::distance( - invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - if (num_invalid_edges == 0) { break; } + // Remove edges that have a triangle count of zero. Those should not be accounted + // for during the unroling phase. + auto edges_with_triangle_last = + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles > 0; + }); + + num_edges_with_triangles = static_cast( + thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); + + + + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); + edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); + num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + + // 'invalid_edge_first' marks the beginning of the edges to be removed + auto invalid_edge_first = + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles >= k - 2; + }); + + num_invalid_edges = static_cast( + thrust::distance(invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); + + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; + if (num_invalid_edges == 0){ + break; + } - // copy invalid edges - auto invalid_edges_buffer = allocate_dataframe_buffer>( - num_invalid_edges, handle.get_stream()); + // copy invalid edges + auto invalid_edges_buffer = allocate_dataframe_buffer>( + num_invalid_edges, handle.get_stream()); + - thrust::copy(handle.get_thrust_policy(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, - edgelist_dsts.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), - edgelist_dsts.begin() + edgelist_srcs.size()), - get_dataframe_buffer_begin(invalid_edges_buffer)); - - // resize the 'edgelist_srcs' and 'edgelist_dst' - edgelist_srcs.resize(num_valid_edges, handle.get_stream()); - edgelist_dsts.resize(num_valid_edges, handle.get_stream()); - num_triangles.resize(num_valid_edges, handle.get_stream()); - - // sort back the edge as those are needed later when running a binary tree - thrust::sort_by_key(handle.get_thrust_policy(), - edge_first, - edge_first + edgelist_srcs.size(), - num_triangles.begin()); + + thrust::copy(handle.get_thrust_policy(), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, edgelist_dsts.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), edgelist_dsts.begin() + edgelist_srcs.size()), + get_dataframe_buffer_begin(invalid_edges_buffer)); + + // resize the 'edgelist_srcs' and 'edgelist_dst' + edgelist_srcs.resize(num_valid_edges, handle.get_stream()); + edgelist_dsts.resize(num_valid_edges, handle.get_stream()); + num_triangles.resize(num_valid_edges, handle.get_stream()); + + + // sort back the edge as those are needed later when running a binary tree + thrust::sort_by_key(handle.get_thrust_policy(), + edge_first, + edge_first + edgelist_srcs.size(), + num_triangles.begin()); + // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); + rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - // dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), + [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + //dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), dst_array_begin = edgelist_dsts.begin(), num_edges = edgelist_srcs.size()] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); @@ -496,31 +523,32 @@ void k_truss(raft::handle_t const& handle, thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = edgelist_dsts.begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = edge_first, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_end = dst_array_begin + num_edges; + thrust::make_counting_iterator(num_invalid_edges), + [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), + invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + dst_array_begin = edgelist_dsts.begin(), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs = edge_first, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + num_edges = edgelist_srcs.size()] __device__(auto idx) { + printf("\nidx =%d\n", idx); + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - + thrust::tabulate( thrust::seq, vertex_pair_buffer_p_q + prefix_sum[idx], vertex_pair_buffer_p_q + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), src); }); @@ -530,10 +558,10 @@ void k_truss(raft::handle_t const& handle, vertex_pair_buffer_p_r + prefix_sum[idx], vertex_pair_buffer_p_r + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, + dst = dst, + src = src, idx, - idx_lower = idx_lower](auto idx_in_segment) { + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); @@ -555,10 +583,10 @@ void k_truss(raft::handle_t const& handle, edge_to_existance, edge_to_existance + edges_exist.size(), [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); - + auto edge_exists = thrust::get<1>(e); + return edge_exists == 0; + }); + auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); // After pushing the non-existant edges to the second partition, @@ -571,22 +599,22 @@ void k_truss(raft::handle_t const& handle, thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edge_first, - edge_last, + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + edge_first, + edge_last, }); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - edge_first, - edge_last, - }); - + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r), + edge_first, + edge_last, + }); + // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' @@ -602,9 +630,8 @@ void k_truss(raft::handle_t const& handle, return thrust::get<1>(edge_to_num_triangles) > 0; }); - auto last_edge_with_triangles_idx = - thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - + auto last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + // Need to run prefix_sum again to get new ranges because some incoming edges were removed prefix_sum.resize(num_invalid_edges + 1, handle.get_stream()); @@ -612,11 +639,11 @@ void k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), dst_array_begin = edgelist_dsts.begin(), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = thrust::get<0>(*(invalid_first + idx)); - auto dst = thrust::get<1>(*(invalid_first + idx)); + num_edges = edgelist_srcs.size()] __device__(auto idx) { + auto src = thrust::get<0>(*(invalid_first + idx)); + auto dst = thrust::get<1>(*(invalid_first + idx)); auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); // FIXME: revisit dst_array_begin vs itr_lower @@ -627,7 +654,7 @@ void k_truss(raft::handle_t const& handle, thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - + // case 3 unroll (p, r) vertex_pair_buffer_p_q = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -638,18 +665,18 @@ void k_truss(raft::handle_t const& handle, thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = edgelist_dsts.begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = edge_first, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_end = dst_array_begin + num_edges; + thrust::make_counting_iterator(num_invalid_edges), + [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), + invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + dst_array_begin = edgelist_dsts.begin(), + prefix_sum = prefix_sum.data(), + incoming_vertex_pairs = edge_first, + vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + num_edges = edgelist_srcs.size()] __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_end = dst_array_begin + num_edges; auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range @@ -659,9 +686,9 @@ void k_truss(raft::handle_t const& handle, vertex_pair_buffer_p_q + prefix_sum[idx], vertex_pair_buffer_p_q + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( src, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); }); @@ -671,9 +698,9 @@ void k_truss(raft::handle_t const& handle, vertex_pair_buffer_q_r + prefix_sum[idx], vertex_pair_buffer_q_r + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); }); @@ -690,15 +717,15 @@ void k_truss(raft::handle_t const& handle, thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), edges_exist.begin()); - + has_edge_last = thrust::remove_if(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); - + edge_to_existance, + edge_to_existance + edges_exist.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists == 0; + }); + num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); // After pushing the non-existant edges to the second partition, @@ -712,21 +739,21 @@ void k_truss(raft::handle_t const& handle, thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edge_first, - edge_last, + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_q), + edge_first, + edge_last, }); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - edge_first, - edge_last, - }); + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r), + edge_first, + edge_last, + }); // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them @@ -740,21 +767,22 @@ void k_truss(raft::handle_t const& handle, return thrust::get<1>(edge_to_num_triangles) > 0; }); - last_edge_with_triangles_idx = - thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + + // resize the 'edgelist_srcs' and 'edgelsit_dst' edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - - edge_value_output.clear(handle); + // rename the above it to last_edge_with_triangles // rename the below to edges_with_triangles edges_with_triangles.clear(); // FIXME: is this needed? - edges_with_triangles.insert( - edgelist_srcs.begin(), edgelist_srcs.begin(), edgelist_dsts.end()); + edges_with_triangles.insert(edgelist_srcs.begin(), + edgelist_srcs.begin(), + edgelist_dsts.end()); cugraph::transform_e( handle, @@ -774,8 +802,7 @@ void k_truss(raft::handle_t const& handle, // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. - // FIXME: debug this stage. There are edges that have been removed that are still found in nbr - // intersection + // FIXME: debug this stage. There are edges that have been removed that are still found in nbr intersection auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, @@ -785,59 +812,62 @@ void k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - size_t accumulate_pair_size = intersection_indices.size(); - + size_t accumulate_pair_size = + intersection_indices.size(); + auto vertex_pair_buffer_p_r_edge_p_q = - allocate_dataframe_buffer>(accumulate_pair_size, - handle.get_stream()); - + allocate_dataframe_buffer>( + accumulate_pair_size, handle.get_stream()); + thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r{ - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), - intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer)}); - - edge_last = edge_first + edgelist_srcs.size(); + generate_p_r{ + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(invalid_edges_buffer) + }); + + edge_last = edge_first + edgelist_srcs.size(); num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - edge_first, - edge_last, - }); + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + edge_first, + edge_last, + }); auto vertex_pair_buffer_q_r_edge_p_q = - allocate_dataframe_buffer>(accumulate_pair_size, - handle.get_stream()); + allocate_dataframe_buffer>( + accumulate_pair_size, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + accumulate_pair_size, + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + + accumulate_pair_size, generate_q_r{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - + thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - edge_first, - edge_last, - }); - + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + edge_first, + edge_last, + }); + // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' @@ -851,8 +881,8 @@ void k_truss(raft::handle_t const& handle, return thrust::get<1>(edge_to_num_triangles) > 0; }); - last_edge_with_triangles_idx = - thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + + last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // rename the above it to last_edge_with_triangles // resize the 'edgelist_srcs' and 'edgelsit_dst' @@ -860,45 +890,55 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); + printf("\n*******final*******\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + auto invalid_edge_last_ = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles < k - 2; - }); + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles < k - 2; + }); + + num_invalid_edges = static_cast( + thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_)); - num_invalid_edges = - static_cast(thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_)); // copy invalid edges resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - + thrust::copy(handle.get_thrust_policy(), - edge_first, - edge_first + num_invalid_edges, - get_dataframe_buffer_begin(invalid_edges_buffer)); - + edge_first, + edge_first + num_invalid_edges, + get_dataframe_buffer_begin(invalid_edges_buffer)); + // sort back the edges as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size(), num_triangles.begin()); + } if (num_invalid_edges == edgelist_srcs.size()) { // return empty graph view - std::optional> empty_graph_view{ - std::nullopt}; + std::optional> empty_graph_view{std::nullopt}; printf("\nreturning an empty graph"); // FIXME: To be updated // return empty_graph_view; - } else { + } + else{ // FIXME: To be updated // return cur_graph_view; printf("\nreturning a non empty graph with num_edges = %d\n", edgelist_srcs.size()); } + + + } } From d71e5b0ac06d71cae71d5b4a5799fdd4215059e4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 11:08:56 -0800 Subject: [PATCH 057/155] remove unused code --- cpp/src/community/k_truss_impl.cuh | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 68bb9f42722..ae9657b6f51 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -322,18 +322,6 @@ void k_truss(raft::handle_t const& handle, // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - - - // FIXME: REmove this********************************************************************************** - rmm::device_uvector srcs_(0, handle.get_stream()); - rmm::device_uvector dsts_(0, handle.get_stream()); - - std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); auto vertex_partition_range_lasts = renumber_map From d605fef3606a4d2731880754919cc87f945d19a8 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 12:04:19 -0800 Subject: [PATCH 058/155] return appropriate type for ktruss --- cpp/include/cugraph/algorithms.hpp | 10 ++++++---- cpp/src/community/k_truss_impl.cuh | 23 ++++------------------- cpp/src/community/k_truss_sg.cu | 21 +++++++++++++-------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 194f2aa2e3d..24bc8c3ac9f 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -2057,10 +2057,12 @@ void triangle_count(raft::handle_t const& handle, * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). */ template -void k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - edge_t k, - bool do_expensive_check = false); +std::tuple, + rmm::device_uvector> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_t k, + bool do_expensive_check = false); /** * @brief Compute Jaccard similarity coefficient diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index ae9657b6f51..a5695d1f1ee 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -183,7 +183,9 @@ struct intersection_op_t { } // namespace template -void k_truss(raft::handle_t const& handle, +std::tuple, + rmm::device_uvector> +k_truss(raft::handle_t const& handle, graph_view_t const& graph_view, edge_t k, bool do_expensive_check) @@ -520,7 +522,6 @@ void k_truss(raft::handle_t const& handle, vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), num_edges = edgelist_srcs.size()] __device__(auto idx) { - printf("\nidx =%d\n", idx); auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_end = dst_array_begin + num_edges; @@ -878,11 +879,6 @@ void k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - printf("\n*******final*******\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - auto invalid_edge_last_ = thrust::stable_partition(handle.get_thrust_policy(), edge_triangle_count_pair_first, @@ -912,18 +908,7 @@ void k_truss(raft::handle_t const& handle, } - if (num_invalid_edges == edgelist_srcs.size()) { - // return empty graph view - std::optional> empty_graph_view{std::nullopt}; - printf("\nreturning an empty graph"); - // FIXME: To be updated - // return empty_graph_view; - } - else{ - // FIXME: To be updated - // return cur_graph_view; - printf("\nreturning a non empty graph with num_edges = %d\n", edgelist_srcs.size()); - } + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index 7742037e398..2b17c88a6c3 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -17,16 +17,21 @@ #include namespace cugraph { +template std::tuple, + rmm::device_uvector> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + int32_t k, + bool do_expensive_check); -template void k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - int32_t k, - bool do_expensive_check); +template std::tuple, + rmm::device_uvector> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + int64_t k, + bool do_expensive_check); -template void k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - int64_t k, - bool do_expensive_check); +// FIXME: Add all possible combinations /* template void ktruss(raft::handle_t const& handle, From ce2e9f952df96bee8a9baa4eccc0538aacc1514d Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 14:22:55 -0800 Subject: [PATCH 059/155] call k_core instead of core_number --- cpp/src/community/k_truss_impl.cuh | 84 ++++++------------------------ 1 file changed, 16 insertions(+), 68 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index a5695d1f1ee..8f7c096efde 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -58,26 +58,6 @@ struct exclude_self_loop_t { } }; -template -struct in_k_plus_one_or_greater_t { - edge_t k{}; - __device__ bool operator()(edge_t core_number) const { return core_number >= k-1; } -}; - -template -struct extract_k_plus_one_core_t { - __device__ thrust::optional> operator()( - vertex_t src, - vertex_t dst, - bool src_in_k_plus_one_core, - bool dst_in_k_plus_one_core, - thrust::nullopt_t) const - { - return (src_in_k_plus_one_core && dst_in_k_plus_one_core) - ? thrust::optional>{thrust::make_tuple(src, dst)} - : thrust::nullopt; - } -}; template struct extract_low_to_high_degree_edges_t { @@ -164,21 +144,6 @@ struct generate_q_r { } }; -template -struct intersection_op_t { - __device__ thrust::tuple operator()( - vertex_t v0, - vertex_t v1, - edge_t v0_prop /* out degree */, - edge_t v1_prop /* out degree */, - raft::device_span intersection, - std::byte, /* dummy */ - std::byte /* dummy */ - ) const - { - return thrust::make_tuple(v0_prop + v1_prop, static_cast(intersection.size())); - } -}; } // namespace @@ -253,37 +218,22 @@ k_truss(raft::handle_t const& handle, renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) : std::nullopt; - - rmm::device_uvector core_numbers(cur_graph_view.local_vertex_partition_range_size(), + + rmm::device_uvector d_core_numbers(cur_graph_view.local_vertex_partition_range_size(), handle.get_stream()); - core_number(handle, - cur_graph_view, - core_numbers.data(), - k_core_degree_type_t::OUT, - size_t{k-1}, - size_t{k-1}); - - edge_src_property_t edge_src_in_k_plus_one_cores( - handle, cur_graph_view); - edge_dst_property_t edge_dst_in_k_plus_one_cores( - handle, cur_graph_view); - auto in_k_plus_one_core_first = - thrust::make_transform_iterator(core_numbers.begin(), in_k_plus_one_or_greater_t{k}); - rmm::device_uvector in_k_plus_one_core_flags(core_numbers.size(), handle.get_stream()); - thrust::copy(handle.get_thrust_policy(), - in_k_plus_one_core_first, - in_k_plus_one_core_first + core_numbers.size(), - in_k_plus_one_core_flags.begin()); - update_edge_src_property( - handle, cur_graph_view, in_k_plus_one_core_flags.begin(), edge_src_in_k_plus_one_cores); - update_edge_dst_property( - handle, cur_graph_view, in_k_plus_one_core_flags.begin(), edge_dst_in_k_plus_one_cores); - auto [srcs, dsts] = extract_transform_e(handle, - cur_graph_view, - edge_src_in_k_plus_one_cores.view(), - edge_dst_in_k_plus_one_cores.view(), - edge_dummy_property_t{}.view(), - extract_k_plus_one_core_t{}); + raft::device_span core_number_span{d_core_numbers.data(), d_core_numbers.size()}; + + rmm::device_uvector srcs{0, handle.get_stream()}; + rmm::device_uvector dsts{0, handle.get_stream()}; + std::tie(srcs, dsts, std::ignore) = k_core(handle, + cur_graph_view, + std::optional>{std::nullopt}, + size_t{k+1}, + std::make_optional(k_core_degree_type_t::OUT), + // Seems like the below argument is required. passing a std::nullopt + // create a compiler error + std::make_optional(core_number_span) + ); if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = @@ -907,9 +857,7 @@ k_truss(raft::handle_t const& handle, num_triangles.begin()); } - - return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); - + printf("\nreturning a non empty graph with num_edges = %d\n", edgelist_srcs.size()); } From 62170573deaec1ca725aa78df65713c87d6de84a Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 28 Feb 2024 15:25:48 -0800 Subject: [PATCH 060/155] merge redundant function and rename variable --- cpp/src/community/k_truss_impl.cuh | 59 ++++++++++++------------------ 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 8f7c096efde..c9458ff3968 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -106,10 +106,10 @@ struct unroll_edge { template -struct generate_p_r { +struct generate_p_r_q_r { + const edge_t edge_first_src_or_dst{}; raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - EdgeIterator edge_first{}; __device__ thrust::tuple operator()(edge_t i) const @@ -117,31 +117,14 @@ struct generate_p_r { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = - thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); - - return pair; + + if (edge_first_src_or_dst == 0) { + return thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); + } else { + return thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); } -}; - - -template -struct generate_q_r { - raft::device_span intersection_offsets{}; - raft::device_span intersection_indices{}; - - EdgeIterator edge_first{}; - - __device__ thrust::tuple operator()(edge_t i) const - { - auto itr = thrust::upper_bound( - thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); - auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - auto pair = - thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); - - return pair; } + }; @@ -360,8 +343,8 @@ k_truss(raft::handle_t const& handle, cugraph::edge_bucket_t edges_with_triangles(handle); - cugraph::edge_property_t edge_value_output(handle, - cur_graph_view); + cugraph::edge_property_t edge_mask(handle, + cur_graph_view); edge_t num_valid_edges = edgelist_srcs.size(); edge_t num_invalid_edges{0}; @@ -401,10 +384,11 @@ k_truss(raft::handle_t const& handle, num_invalid_edges = static_cast( thrust::distance(invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; if (num_invalid_edges == 0){ break; } + + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; // copy invalid edges auto invalid_edges_buffer = allocate_dataframe_buffer>( @@ -440,7 +424,6 @@ k_truss(raft::handle_t const& handle, prefix_sum.begin(), prefix_sum.end(), [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - //dst_array_begin = std::get<0>(incoming_vertex_pairs).begin(), dst_array_begin = edgelist_dsts.begin(), num_edges = edgelist_srcs.size()] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); @@ -733,10 +716,10 @@ k_truss(raft::handle_t const& handle, [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { return true; }, - edge_value_output.mutable_view(), + edge_mask.mutable_view(), false); - cur_graph_view.attach_edge_mask(edge_value_output.view()); + cur_graph_view.attach_edge_mask(edge_mask.view()); // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' @@ -762,7 +745,8 @@ k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r{ + generate_p_r_q_r{ + 0, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), @@ -790,7 +774,8 @@ k_truss(raft::handle_t const& handle, get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + accumulate_pair_size, - generate_q_r{ + generate_p_r_q_r{ + 1, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), @@ -829,7 +814,7 @@ k_truss(raft::handle_t const& handle, edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - auto invalid_edge_last_ = + auto invalid_edge_last_p_q = thrust::stable_partition(handle.get_thrust_policy(), edge_triangle_count_pair_first, edge_triangle_count_pair_first + num_triangles.size(), @@ -839,7 +824,7 @@ k_truss(raft::handle_t const& handle, }); num_invalid_edges = static_cast( - thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_)); + thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_p_q)); // copy invalid edges @@ -856,8 +841,10 @@ k_truss(raft::handle_t const& handle, edge_first + edgelist_srcs.size(), num_triangles.begin()); + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); + } - printf("\nreturning a non empty graph with num_edges = %d\n", edgelist_srcs.size()); + } From 67e64cee5b4d6649e5eed1e3cb7e5074b6ad0af9 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 29 Feb 2024 08:51:38 -0800 Subject: [PATCH 061/155] merge similar functions --- cpp/include/cugraph/algorithms.hpp | 11 +- cpp/src/community/k_truss_impl.cuh | 866 +++++++++++++---------------- cpp/src/community/k_truss_sg.cu | 22 +- 3 files changed, 401 insertions(+), 498 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 24bc8c3ac9f..3a307e18516 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -2057,12 +2057,11 @@ void triangle_count(raft::handle_t const& handle, * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). */ template -std::tuple, - rmm::device_uvector> -k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - edge_t k, - bool do_expensive_check = false); +std::tuple, rmm::device_uvector> k_truss( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_t k, + bool do_expensive_check = false); /** * @brief Compute Jaccard similarity coefficient diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index c9458ff3968..3b6ca8e19b0 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -41,10 +42,217 @@ #include #include #include -#include namespace cugraph { +template +struct unroll_edge { + raft::device_span num_triangles{}; + EdgeIterator edge_unrolled{}; + EdgeIterator edge_first{}; + EdgeIterator edge_last{}; + + __device__ thrust::tuple operator()(edge_t i) const + { + // edges are sorted with destination as key so reverse the edge when looking it + auto pair = thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), + thrust::get<0>(*(edge_unrolled + i))); + // Find its position in 'edges' + auto itr = thrust::lower_bound(thrust::seq, edge_first, edge_last, pair); + + auto idx = thrust::distance(edge_first, itr); + cuda::atomic_ref atomic_counter(num_triangles[idx]); + auto r = atomic_counter.fetch_sub(vertex_t{1}, cuda::std::memory_order_relaxed); + } +}; + +template +void find_unroll_p_r_and_q_r_edges( + raft::handle_t const& handle, + graph_view_t& graph_view, + vertex_t edge_type, + edge_t num_invalid_edges, + decltype(allocate_dataframe_buffer>( + 0, rmm::cuda_stream_view{}))&& invalid_edges_buffer, + rmm::device_uvector&& edgelist_srcs, + rmm::device_uvector&& edgelist_dsts, + rmm::device_uvector&& num_triangles, + EdgeIterator incoming_vertex_pairs, + EdgeTriangleCountIterator edge_triangle_count_pair_first) +{ + auto num_edges = edgelist_dsts.size(); + rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); + thrust::tabulate( + handle.get_thrust_policy(), + prefix_sum.begin(), + prefix_sum.end(), + [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + dst_array_begin = edgelist_dsts.begin(), + num_edges] __device__(auto idx) { + auto src = thrust::get<0>(*(invalid_first + idx)); + auto dst = thrust::get<1>(*(invalid_first + idx)); + auto dst_array_end = dst_array_begin + num_edges; + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto itr_upper = thrust::upper_bound(thrust::seq, itr_lower, dst_array_end, dst); + auto dist = thrust::distance(itr_lower, itr_upper); + return dist; + }); + thrust::exclusive_scan( + handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + + auto potential_closing_edges = allocate_dataframe_buffer>( + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + + auto incoming_edges_to_r = allocate_dataframe_buffer>( + prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_invalid_edges), + [incoming_vertex_pairs, + num_edges, + invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), + invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + dst_array_begin = edgelist_dsts.begin(), + prefix_sum = prefix_sum.data(), + potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), + incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r), + edge_type] __device__(auto idx) { + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; + auto dst_array_end = dst_array_begin + num_edges; + + auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); + auto idx_lower = thrust::distance( + dst_array_begin, itr_lower); // Need a binary search to find the begining of the range + + if (edge_type == 0) { + thrust::tabulate(thrust::seq, + potential_closing_edges + prefix_sum[idx], + potential_closing_edges + prefix_sum[idx + 1], + [incoming_vertex_pairs = incoming_vertex_pairs, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), + src); + }); + + thrust::tabulate(thrust::seq, + incoming_edges_to_r + prefix_sum[idx], + incoming_edges_to_r + prefix_sum[idx + 1], + [incoming_vertex_pairs = incoming_vertex_pairs, + dst = dst, + src = src, + idx, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), + dst); + }); + + } else { + thrust::tabulate(thrust::seq, + potential_closing_edges + prefix_sum[idx], + potential_closing_edges + prefix_sum[idx + 1], + [incoming_vertex_pairs = incoming_vertex_pairs, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + src, + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); + }); + + thrust::tabulate(thrust::seq, + incoming_edges_to_r + prefix_sum[idx], + incoming_edges_to_r + prefix_sum[idx + 1], + [incoming_vertex_pairs = incoming_vertex_pairs, + dst = dst, + src = src, + idx_lower = idx_lower](auto idx_in_segment) { + return thrust::make_tuple( + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), + dst); + }); + } + }); + + auto edges_exist = graph_view.has_edge( + handle, + raft::device_span(std::get<0>(potential_closing_edges).data(), + std::get<0>(potential_closing_edges).size()), + raft::device_span(std::get<1>(potential_closing_edges).data(), + std::get<1>(potential_closing_edges).size())); + + auto edge_to_existance = thrust::make_zip_iterator( + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), + get_dataframe_buffer_begin(incoming_edges_to_r)), + edges_exist.begin()); + + auto has_edge_last = thrust::remove_if(handle.get_thrust_policy(), + edge_to_existance, + edge_to_existance + edges_exist.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists == 0; + }); + + auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); + + // After pushing the non-existant edges to the second partition, + // remove them by resizing both vertex pair buffer + resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); + resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); + + auto incoming_vertex_pairs_last = incoming_vertex_pairs + num_edges; + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(potential_closing_edges), + incoming_vertex_pairs, + incoming_vertex_pairs_last}); + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(incoming_edges_to_r), + incoming_vertex_pairs, + incoming_vertex_pairs_last}); + + // Put edges with triangle count == 0 in the second partition + // FIXME: revisit all the 'stable_partition' and only used them + // when necessary otherwise simply call 'thrust::partition' + // Stable_parition is needed because we want to keep src and dst sorted + // so that we don't need to sort it again. + // FIXME: Create a rountine capturing L719:L763 as this block of code gets + // repeated + auto last_edge_with_triangles = + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_edges, + [] __device__(auto edge_to_num_triangles) { + return thrust::get<1>(edge_to_num_triangles) > 0; + }); + + auto last_edge_with_triangles_idx = + thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + // resize the 'edgelist_srcs' and 'edgelsit_dst' + edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); + edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); + num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); +} + namespace { template @@ -58,7 +266,6 @@ struct exclude_self_loop_t { } }; - template struct extract_low_to_high_degree_edges_t { __device__ thrust::optional> operator()(vertex_t src, @@ -77,34 +284,6 @@ struct extract_low_to_high_degree_edges_t { } }; - -template -struct unroll_edge { - raft::device_span num_triangles{}; - EdgeIterator edge_unrolled{}; - EdgeIterator edge_first{}; - EdgeIterator edge_last{}; - - __device__ thrust::tuple operator()(edge_t i) const - { - // edges are sort with destination as key so reverse the edge when looking it - auto pair = - thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), thrust::get<0>(*(edge_unrolled + i))); - // Find its position in 'edges' - auto itr = thrust::lower_bound(thrust::seq, - edge_first, - edge_last, - pair - ); - - auto idx = thrust::distance(edge_first, itr); - cuda::atomic_ref atomic_counter(num_triangles[idx]); - auto r = atomic_counter.fetch_sub(vertex_t{1}, cuda::std::memory_order_relaxed); - - } -}; - - template struct generate_p_r_q_r { const edge_t edge_first_src_or_dst{}; @@ -117,29 +296,24 @@ struct generate_p_r_q_r { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - - if (edge_first_src_or_dst == 0) { - return thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); - } else { + + if (edge_first_src_or_dst == 0) { + return thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); + } else { return thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); + } } - } - }; - - } // namespace template -std::tuple, - rmm::device_uvector> -k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - edge_t k, - bool do_expensive_check) +std::tuple, rmm::device_uvector> k_truss( + raft::handle_t const& handle, + graph_view_t const& graph_view, + edge_t k, + bool do_expensive_check) { using weight_t = float; // dummy - // 1. Check input arguments. CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); @@ -167,7 +341,6 @@ k_truss(raft::handle_t const& handle, edge_dummy_property_t{}.view(), exclude_self_loop_t{}); - if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning>(cur_graph_view.vertex_partition_range_lasts()) : std::nullopt; - + rmm::device_uvector d_core_numbers(cur_graph_view.local_vertex_partition_range_size(), - handle.get_stream()); + handle.get_stream()); raft::device_span core_number_span{d_core_numbers.data(), d_core_numbers.size()}; rmm::device_uvector srcs{0, handle.get_stream()}; rmm::device_uvector dsts{0, handle.get_stream()}; - std::tie(srcs, dsts, std::ignore) = k_core(handle, - cur_graph_view, - std::optional>{std::nullopt}, - size_t{k+1}, - std::make_optional(k_core_degree_type_t::OUT), - // Seems like the below argument is required. passing a std::nullopt - // create a compiler error - std::make_optional(core_number_span) - ); + std::tie(srcs, dsts, std::ignore) = + k_core(handle, + cur_graph_view, + std::optional>{std::nullopt}, + size_t{k + 1}, + std::make_optional(k_core_degree_type_t::OUT), + // Seems like the below argument is required. passing a std::nullopt + // create a compiler error + std::make_optional(core_number_span)); if constexpr (multi_gpu) { std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = @@ -253,11 +425,11 @@ k_truss(raft::handle_t const& handle, renumber_map = std::move(tmp_renumber_map); } - // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. + { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - + auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -298,7 +470,7 @@ k_truss(raft::handle_t const& handle, std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, - false); //******************FIXME: hardcoded to False + false); //******************FIXME: hardcoded to False modified_graph_view = (*modified_graph).view(); @@ -327,384 +499,123 @@ k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - auto edge_first = - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - - auto num_triangles = edge_triangle_count(handle, - cur_graph_view, - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - - - auto edge_triangle_count_pair_first = thrust::make_zip_iterator( - edge_first, num_triangles.begin()); - + auto edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + + auto num_triangles = edge_triangle_count( + handle, + cur_graph_view, + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + + auto edge_triangle_count_pair_first = + thrust::make_zip_iterator(edge_first, num_triangles.begin()); + // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edges_with_triangles(handle); - - cugraph::edge_property_t edge_mask(handle, - cur_graph_view); - + cugraph::edge_property_t edge_mask(handle, cur_graph_view); + edge_t num_valid_edges = edgelist_srcs.size(); edge_t num_invalid_edges{0}; size_t num_edges_with_triangles{0}; while (true) { - // Remove edges that have a triangle count of zero. Those should not be accounted - // for during the unroling phase. - auto edges_with_triangle_last = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles > 0; - }); - - num_edges_with_triangles = static_cast( - thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); - - - - edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); - edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); - num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - - // 'invalid_edge_first' marks the beginning of the edges to be removed - auto invalid_edge_first = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles >= k - 2; - }); - - num_invalid_edges = static_cast( - thrust::distance(invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - - if (num_invalid_edges == 0){ - break; - } - - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - - // copy invalid edges - auto invalid_edges_buffer = allocate_dataframe_buffer>( - num_invalid_edges, handle.get_stream()); - - - - - thrust::copy(handle.get_thrust_policy(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, edgelist_dsts.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), edgelist_dsts.begin() + edgelist_srcs.size()), - get_dataframe_buffer_begin(invalid_edges_buffer)); - - // resize the 'edgelist_srcs' and 'edgelist_dst' - edgelist_srcs.resize(num_valid_edges, handle.get_stream()); - edgelist_dsts.resize(num_valid_edges, handle.get_stream()); - num_triangles.resize(num_valid_edges, handle.get_stream()); - - - // sort back the edge as those are needed later when running a binary tree - thrust::sort_by_key(handle.get_thrust_policy(), - edge_first, - edge_first + edgelist_srcs.size(), - num_triangles.begin()); - - // case 2: unroll (q, r) - // FIXME: Update the num_edges when removing edges - // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and - // create the pair (p, q) - rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); - thrust::tabulate( - handle.get_thrust_policy(), - prefix_sum.begin(), - prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - dst_array_begin = edgelist_dsts.begin(), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = thrust::get<0>(*(invalid_first + idx)); - auto dst = thrust::get<1>(*(invalid_first + idx)); - auto dst_array_end = dst_array_begin + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto itr_upper = thrust::upper_bound(thrust::seq, itr_lower, dst_array_end, dst); - auto dist = thrust::distance(itr_lower, itr_upper); - return dist; - }); - thrust::exclusive_scan( - handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - - auto vertex_pair_buffer_p_q = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - - auto vertex_pair_buffer_p_r = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); - - thrust::for_each( - handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_invalid_edges), - [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = edgelist_dsts.begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = edge_first, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_p_r = get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_end = dst_array_begin + num_edges; - - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = thrust::distance( - dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - - thrust::tabulate( - thrust::seq, - vertex_pair_buffer_p_q + prefix_sum[idx], - vertex_pair_buffer_p_q + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), src); - }); - - thrust::tabulate( - thrust::seq, - vertex_pair_buffer_p_r + prefix_sum[idx], - vertex_pair_buffer_p_r + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); - }); - }); - - auto edges_exist = cur_graph_view.has_edge( - handle, - raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), - std::get<0>(vertex_pair_buffer_p_q).size()), - raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), - std::get<1>(vertex_pair_buffer_p_q).size())); - - auto edge_to_existance = thrust::make_zip_iterator( - thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r)), - edges_exist.begin()); - - auto has_edge_last = thrust::remove_if(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); - - auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); - - // After pushing the non-existant edges to the second partition, - // remove them by resizing both vertex pair buffer - resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); - resize_dataframe_buffer(vertex_pair_buffer_p_r, num_edge_exists, handle.get_stream()); - - auto edge_last = edge_first + edgelist_srcs.size(); - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edge_first, - edge_last, - }); - - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r), - edge_first, - edge_last, - }); - - // Put edges with triangle count == 0 in the second partition - // FIXME: revisit all the 'stable_partition' and only used them - // when necessary otherwise simply call 'thrust::partition' - // Stable_parition is needed because we want to keep src and dst sorted - // so that we don't need to sort it again. - // FIXME: Create a rountine capturing L719:L763 as this block of code gets - // repeated - auto last_edge_with_triangles = + // Remove edges that have a triangle count of zero. Those should not be accounted + // for during the unroling phase. + auto edges_with_triangle_last = thrust::stable_partition(handle.get_thrust_policy(), edge_triangle_count_pair_first, - edge_triangle_count_pair_first + edgelist_srcs.size(), - [] __device__(auto edge_to_num_triangles) { - return thrust::get<1>(edge_to_num_triangles) > 0; + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles > 0; }); - auto last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - - // Need to run prefix_sum again to get new ranges because some incoming edges were removed - prefix_sum.resize(num_invalid_edges + 1, handle.get_stream()); + num_edges_with_triangles = static_cast( + thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); - thrust::tabulate( - handle.get_thrust_policy(), - prefix_sum.begin(), - prefix_sum.end(), - [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - dst_array_begin = edgelist_dsts.begin(), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = thrust::get<0>(*(invalid_first + idx)); - auto dst = thrust::get<1>(*(invalid_first + idx)); - auto dst_array_end = dst_array_begin + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - // FIXME: revisit dst_array_begin vs itr_lower - auto itr_upper = thrust::upper_bound(thrust::seq, itr_lower, dst_array_end, dst); - auto dist = thrust::distance(itr_lower, itr_upper); - return dist; - }); + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); + edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); + num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - thrust::exclusive_scan( - handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); - - // case 3 unroll (p, r) - vertex_pair_buffer_p_q = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + // 'invalid_edge_first' marks the beginning of the edges to be removed + auto invalid_edge_first = + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles >= k - 2; + }); - auto vertex_pair_buffer_q_r = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + num_invalid_edges = static_cast(thrust::distance( + invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - thrust::for_each( - handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_invalid_edges), - [invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - dst_array_begin = edgelist_dsts.begin(), - prefix_sum = prefix_sum.data(), - incoming_vertex_pairs = edge_first, - vertex_pair_buffer_p_q = get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - vertex_pair_buffer_q_r = get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - num_edges = edgelist_srcs.size()] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_end = dst_array_begin + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = thrust::distance( - dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - - thrust::tabulate( - thrust::seq, - vertex_pair_buffer_p_q + prefix_sum[idx], - vertex_pair_buffer_p_q + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - src, thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); - }); - - thrust::tabulate( - thrust::seq, - vertex_pair_buffer_q_r + prefix_sum[idx], - vertex_pair_buffer_q_r + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), dst); - }); - }); + if (num_invalid_edges == 0) { break; } - edges_exist = cur_graph_view.has_edge( - handle, - raft::device_span(std::get<0>(vertex_pair_buffer_p_q).data(), - std::get<0>(vertex_pair_buffer_p_q).size()), - raft::device_span(std::get<1>(vertex_pair_buffer_p_q).data(), - std::get<1>(vertex_pair_buffer_p_q).size())); - - edge_to_existance = thrust::make_zip_iterator( - thrust::make_zip_iterator(get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r)), - edges_exist.begin()); - - has_edge_last = thrust::remove_if(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); - - num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); - - // After pushing the non-existant edges to the second partition, - // remove them by resizing both vertex pair buffer - resize_dataframe_buffer(vertex_pair_buffer_p_q, num_edge_exists, handle.get_stream()); - resize_dataframe_buffer(vertex_pair_buffer_q_r, num_edge_exists, handle.get_stream()); - edges_exist.resize(size_dataframe_buffer(vertex_pair_buffer_p_q), handle.get_stream()); - - edge_last = edge_first + edgelist_srcs.size(); - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_q), - edge_first, - edge_last, - }); + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r), - edge_first, - edge_last, - }); + // copy invalid edges + auto invalid_edges_buffer = allocate_dataframe_buffer>( + num_invalid_edges, handle.get_stream()); - // Put edges with triangle count == 0 in the second partition - // FIXME: revisit all the 'stable_partition' and only used them - // when necessary otherwise simply call 'thrust::partition' - // Stable_parition is needed because we want to keep src and dst sorted - last_edge_with_triangles = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + edgelist_srcs.size(), - [] __device__(auto edge_to_num_triangles) { - return thrust::get<1>(edge_to_num_triangles) > 0; - }); + thrust::copy(handle.get_thrust_policy(), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, + edgelist_dsts.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), + edgelist_dsts.begin() + edgelist_srcs.size()), + get_dataframe_buffer_begin(invalid_edges_buffer)); + + // resize the 'edgelist_srcs' and 'edgelist_dst' + edgelist_srcs.resize(num_valid_edges, handle.get_stream()); + edgelist_dsts.resize(num_valid_edges, handle.get_stream()); + num_triangles.resize(num_valid_edges, handle.get_stream()); + + // sort back the edge as those are needed later when running a binary tree + thrust::sort_by_key(handle.get_thrust_policy(), + edge_first, + edge_first + edgelist_srcs.size(), + num_triangles.begin()); - last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); + // case 2: unroll (q, r) + // FIXME: Update the num_edges when removing edges + // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and + // create the pair (p, q) - + cugraph::find_unroll_p_r_and_q_r_edges(handle, + cur_graph_view, + 0, // 0 for (q, r) and 1 for (p, r) edges + num_invalid_edges, + std::move(invalid_edges_buffer), + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(num_triangles), + edge_first, + edge_triangle_count_pair_first); + + cugraph::find_unroll_p_r_and_q_r_edges(handle, + cur_graph_view, + 1, // 0 for (q, r) and 1 for (p, r) edges + num_invalid_edges, + std::move(invalid_edges_buffer), + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(num_triangles), + edge_first, + edge_triangle_count_pair_first); - // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - - // rename the above it to last_edge_with_triangles - // rename the below to edges_with_triangles edges_with_triangles.clear(); // FIXME: is this needed? - edges_with_triangles.insert(edgelist_srcs.begin(), - edgelist_srcs.begin(), - edgelist_dsts.end()); + edges_with_triangles.insert( + edgelist_srcs.begin(), edgelist_srcs.begin(), edgelist_dsts.end()); cugraph::transform_e( handle, @@ -724,7 +635,8 @@ k_truss(raft::handle_t const& handle, // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. - // FIXME: debug this stage. There are edges that have been removed that are still found in nbr intersection + // FIXME: debug this stage. There are edges that have been removed that are still found in nbr + // intersection auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, cur_graph_view, @@ -734,70 +646,71 @@ k_truss(raft::handle_t const& handle, std::array{true, true}, do_expensive_check); - size_t accumulate_pair_size = - intersection_indices.size(); - + size_t accumulate_pair_size = intersection_indices.size(); + auto vertex_pair_buffer_p_r_edge_p_q = - allocate_dataframe_buffer>( - accumulate_pair_size, handle.get_stream()); - + allocate_dataframe_buffer>(accumulate_pair_size, + handle.get_stream()); + thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r_q_r{ - 0, - raft::device_span(intersection_offsets.data(), intersection_offsets.size()), - raft::device_span(intersection_indices.data(), - intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer) - }); - - edge_last = edge_first + edgelist_srcs.size(); - num_edge_exists = accumulate_pair_size; + generate_p_r_q_r{ + 0, + raft::device_span(intersection_offsets.data(), intersection_offsets.size()), + raft::device_span(intersection_indices.data(), + intersection_indices.size()), + get_dataframe_buffer_begin(invalid_edges_buffer)}); + + auto edge_last = edge_first + edgelist_srcs.size(); + auto num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - edge_first, - edge_last, - }); + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), + edge_first, + edge_last, + }); auto vertex_pair_buffer_q_r_edge_p_q = - allocate_dataframe_buffer>( - accumulate_pair_size, handle.get_stream()); + allocate_dataframe_buffer>(accumulate_pair_size, + handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + - accumulate_pair_size, - generate_p_r_q_r{ + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + accumulate_pair_size, + generate_p_r_q_r{ 1, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - + thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - edge_first, - edge_last, - }); - + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + edge_first, + edge_last, + }); + // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' // Stable_parition is needed because we want to keep src and dst sorted // so that we don't need to sort it again. - last_edge_with_triangles = + auto last_edge_with_triangles = thrust::stable_partition(handle.get_thrust_policy(), edge_triangle_count_pair_first, edge_triangle_count_pair_first + edgelist_srcs.size(), @@ -805,9 +718,8 @@ k_truss(raft::handle_t const& handle, return thrust::get<1>(edge_to_num_triangles) > 0; }); - - last_edge_with_triangles_idx = thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - // rename the above it to last_edge_with_triangles + auto last_edge_with_triangles_idx = + thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); // resize the 'edgelist_srcs' and 'edgelsit_dst' edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); @@ -815,26 +727,25 @@ k_truss(raft::handle_t const& handle, num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); auto invalid_edge_last_p_q = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles < k - 2; - }); - + thrust::stable_partition(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles < k - 2; + }); + num_invalid_edges = static_cast( thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_p_q)); - // copy invalid edges resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - + thrust::copy(handle.get_thrust_policy(), - edge_first, - edge_first + num_invalid_edges, - get_dataframe_buffer_begin(invalid_edges_buffer)); - + edge_first, + edge_first + num_invalid_edges, + get_dataframe_buffer_begin(invalid_edges_buffer)); + // sort back the edges as those are needed later when running a binary tree thrust::sort_by_key(handle.get_thrust_policy(), edge_first, @@ -842,12 +753,7 @@ k_truss(raft::handle_t const& handle, num_triangles.begin()); return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); - } - - - } } - } // namespace cugraph diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index 2b17c88a6c3..571302fefb2 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -17,19 +17,17 @@ #include namespace cugraph { -template std::tuple, - rmm::device_uvector> -k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - int32_t k, - bool do_expensive_check); +template std::tuple, rmm::device_uvector> k_truss( + raft::handle_t const& handle, + graph_view_t const& graph_view, + int32_t k, + bool do_expensive_check); -template std::tuple, - rmm::device_uvector> -k_truss(raft::handle_t const& handle, - graph_view_t const& graph_view, - int64_t k, - bool do_expensive_check); +template std::tuple, rmm::device_uvector> k_truss( + raft::handle_t const& handle, + graph_view_t const& graph_view, + int64_t k, + bool do_expensive_check); // FIXME: Add all possible combinations From 56fc13184af9138f4334c70abfd0c33350a37949 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 29 Feb 2024 09:05:45 -0800 Subject: [PATCH 062/155] remove redundant call --- cpp/src/community/k_truss_impl.cuh | 34 ++++++++++-------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 3b6ca8e19b0..05c6ec6470e 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -130,21 +130,8 @@ void find_unroll_p_r_and_q_r_edges( auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - - if (edge_type == 0) { - thrust::tabulate(thrust::seq, - potential_closing_edges + prefix_sum[idx], - potential_closing_edges + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), - src); - }); - - thrust::tabulate(thrust::seq, + + thrust::tabulate(thrust::seq, incoming_edges_to_r + prefix_sum[idx], incoming_edges_to_r + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, @@ -157,7 +144,7 @@ void find_unroll_p_r_and_q_r_edges( dst); }); - } else { + if (edge_type == 0) { thrust::tabulate(thrust::seq, potential_closing_edges + prefix_sum[idx], potential_closing_edges + prefix_sum[idx + 1], @@ -166,21 +153,22 @@ void find_unroll_p_r_and_q_r_edges( src = src, idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( - src, - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), + src); }); - + } else { thrust::tabulate(thrust::seq, - incoming_edges_to_r + prefix_sum[idx], - incoming_edges_to_r + prefix_sum[idx + 1], + potential_closing_edges + prefix_sum[idx], + potential_closing_edges + prefix_sum[idx + 1], [incoming_vertex_pairs = incoming_vertex_pairs, dst = dst, src = src, idx_lower = idx_lower](auto idx_in_segment) { return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), - dst); + src, + thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); }); + } }); From caf3dde92999a07486bfc3ce706123001d13e2e5 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 1 Mar 2024 05:11:59 -0800 Subject: [PATCH 063/155] remove unnecessary sorting --- cpp/src/community/k_truss_impl.cuh | 152 ++++++++++++++++++----------- 1 file changed, 96 insertions(+), 56 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 05c6ec6470e..bd297be9c8c 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -84,6 +84,10 @@ void find_unroll_p_r_and_q_r_edges( EdgeIterator incoming_vertex_pairs, EdgeTriangleCountIterator edge_triangle_count_pair_first) { + printf("\ninvalid edge buffer\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + printf("\n"); auto num_edges = edgelist_dsts.size(); rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); thrust::tabulate( @@ -172,6 +176,13 @@ void find_unroll_p_r_and_q_r_edges( } }); + printf("\npotential - incoming before\n"); + raft::print_device_vector("***potential_closing_edges - src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); + raft::print_device_vector("***potential_closing_edges - dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); + raft::print_device_vector("***incoming edges to r - src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); + raft::print_device_vector("***incoming edges to r - dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); + printf("\n\n"); + auto edges_exist = graph_view.has_edge( handle, raft::device_span(std::get<0>(potential_closing_edges).data(), @@ -199,6 +210,13 @@ void find_unroll_p_r_and_q_r_edges( resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); + printf("\npotential - incoming after\n"); + raft::print_device_vector("***potential_closing_edges - src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); + raft::print_device_vector("***potential_closing_edges - dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); + raft::print_device_vector("***incoming edges to r - src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); + raft::print_device_vector("***incoming edges to r - dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); + printf("\n\n"); + auto incoming_vertex_pairs_last = incoming_vertex_pairs + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -217,6 +235,12 @@ void find_unroll_p_r_and_q_r_edges( get_dataframe_buffer_begin(incoming_edges_to_r), incoming_vertex_pairs, incoming_vertex_pairs_last}); + + printf("\nafter unrolling\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them @@ -239,6 +263,12 @@ void find_unroll_p_r_and_q_r_edges( edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); + + printf("\nafter resizing and removing edges with no triangles\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); } namespace { @@ -354,7 +384,7 @@ std::tuple, rmm::device_uvector> k_truss } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - + /* { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -412,12 +442,30 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } + */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { + auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + // FIXME: REmove this********************************************************************************** + rmm::device_uvector srcs_(0, handle.get_stream()); + rmm::device_uvector dsts_(0, handle.get_stream()); + + std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + + printf("\nOriginal graph\n"); + raft::print_device_vector("srcs", srcs_.data(), srcs_.size(), std::cout); + raft::print_device_vector("dsts", dsts_.data(), dsts_.size(), std::cout); + //***************************************************************************************************** + auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -497,6 +545,13 @@ std::tuple, rmm::device_uvector> k_truss auto edge_triangle_count_pair_first = thrust::make_zip_iterator(edge_first, num_triangles.begin()); + + + printf("\nreduced edges from original\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edges_with_triangles(handle); @@ -518,6 +573,12 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); + + printf("\before removing edges with no triangles\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); num_edges_with_triangles = static_cast( thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); @@ -526,6 +587,12 @@ std::tuple, rmm::device_uvector> k_truss edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + printf("\nafter removing edges with no triangles\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); + // 'invalid_edge_first' marks the beginning of the edges to be removed auto invalid_edge_first = thrust::stable_partition(handle.get_thrust_policy(), @@ -538,6 +605,12 @@ std::tuple, rmm::device_uvector> k_truss num_invalid_edges = static_cast(thrust::distance( invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); + + printf("\nnumber of invalid edges = %d\n", num_invalid_edges); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); if (num_invalid_edges == 0) { break; } @@ -559,12 +632,6 @@ std::tuple, rmm::device_uvector> k_truss edgelist_dsts.resize(num_valid_edges, handle.get_stream()); num_triangles.resize(num_valid_edges, handle.get_stream()); - // sort back the edge as those are needed later when running a binary tree - thrust::sort_by_key(handle.get_thrust_policy(), - edge_first, - edge_first + edgelist_srcs.size(), - num_triangles.begin()); - // case 2: unroll (q, r) // FIXME: Update the num_edges when removing edges // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and @@ -601,7 +668,6 @@ std::tuple, rmm::device_uvector> k_truss edge_triangle_count_pair_first); edges_with_triangles.clear(); // FIXME: is this needed? - edges_with_triangles.insert( edgelist_srcs.begin(), edgelist_srcs.begin(), edgelist_dsts.end()); @@ -653,6 +719,14 @@ std::tuple, rmm::device_uvector> k_truss intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer)}); + printf("\nbefore unrolling p_q\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + + raft::print_device_vector("p_r", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); + raft::print_device_vector("p_r", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); + auto edge_last = edge_first + edgelist_srcs.size(); auto num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), @@ -682,6 +756,9 @@ std::tuple, rmm::device_uvector> k_truss intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); + + raft::print_device_vector("q_r", std::get<0>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); + raft::print_device_vector("q_r", std::get<1>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -692,56 +769,19 @@ std::tuple, rmm::device_uvector> k_truss edge_first, edge_last, }); + + printf("\nafter unrolling p_q\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - // Put edges with triangle count == 0 in the second partition - // FIXME: revisit all the 'stable_partition' and only used them - // when necessary otherwise simply call 'thrust::partition' - // Stable_parition is needed because we want to keep src and dst sorted - // so that we don't need to sort it again. - auto last_edge_with_triangles = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + edgelist_srcs.size(), - [] __device__(auto edge_to_num_triangles) { - return thrust::get<1>(edge_to_num_triangles) > 0; - }); - - auto last_edge_with_triangles_idx = - thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - - // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - - auto invalid_edge_last_p_q = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles < k - 2; - }); - - num_invalid_edges = static_cast( - thrust::distance(edge_triangle_count_pair_first, invalid_edge_last_p_q)); - - // copy invalid edges - resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edges, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - edge_first, - edge_first + num_invalid_edges, - get_dataframe_buffer_begin(invalid_edges_buffer)); - - // sort back the edges as those are needed later when running a binary tree - thrust::sort_by_key(handle.get_thrust_policy(), - edge_first, - edge_first + edgelist_srcs.size(), - num_triangles.begin()); - - return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } + printf("\n*********final results*********\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\nthe number of edges = %d\n", edgelist_srcs.size()); + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } } } // namespace cugraph From 2077dc340c3700a4fd827e76d7818a153f490eee Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 1 Mar 2024 06:14:39 -0800 Subject: [PATCH 064/155] update triangle count type --- cpp/include/cugraph/graph_functions.hpp | 2 +- cpp/src/community/k_truss_impl.cuh | 17 +++++++------- .../structure/edge_triangle_count_impl.cuh | 22 ++++++++++--------- cpp/src/structure/edge_triangle_count_sg.cu | 4 ++-- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/cpp/include/cugraph/graph_functions.hpp b/cpp/include/cugraph/graph_functions.hpp index b4c21adfc89..d8268596d25 100644 --- a/cpp/include/cugraph/graph_functions.hpp +++ b/cpp/include/cugraph/graph_functions.hpp @@ -1062,7 +1062,7 @@ remove_multi_edges(raft::handle_t const& handle, * @param edgelist_dsts List of destination vertex ids */ template -rmm::device_uvector edge_triangle_count( +rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, graph_view_t const& graph_view, raft::device_span edgelist_srcs, diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index bd297be9c8c..c9d5489b6ce 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -47,7 +47,7 @@ namespace cugraph { template struct unroll_edge { - raft::device_span num_triangles{}; + raft::device_span num_triangles{}; EdgeIterator edge_unrolled{}; EdgeIterator edge_first{}; EdgeIterator edge_last{}; @@ -59,10 +59,11 @@ struct unroll_edge { thrust::get<0>(*(edge_unrolled + i))); // Find its position in 'edges' auto itr = thrust::lower_bound(thrust::seq, edge_first, edge_last, pair); + assert(*itr == pair); auto idx = thrust::distance(edge_first, itr); - cuda::atomic_ref atomic_counter(num_triangles[idx]); - auto r = atomic_counter.fetch_sub(vertex_t{1}, cuda::std::memory_order_relaxed); + cuda::atomic_ref atomic_counter(num_triangles[idx]); + auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); } }; @@ -80,7 +81,7 @@ void find_unroll_p_r_and_q_r_edges( 0, rmm::cuda_stream_view{}))&& invalid_edges_buffer, rmm::device_uvector&& edgelist_srcs, rmm::device_uvector&& edgelist_dsts, - rmm::device_uvector&& num_triangles, + rmm::device_uvector&& num_triangles, EdgeIterator incoming_vertex_pairs, EdgeTriangleCountIterator edge_triangle_count_pair_first) { @@ -222,7 +223,7 @@ void find_unroll_p_r_and_q_r_edges( thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(potential_closing_edges), incoming_vertex_pairs, incoming_vertex_pairs_last}); @@ -231,7 +232,7 @@ void find_unroll_p_r_and_q_r_edges( thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(incoming_edges_to_r), incoming_vertex_pairs, incoming_vertex_pairs_last}); @@ -733,7 +734,7 @@ std::tuple, rmm::device_uvector> k_truss thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), edge_first, edge_last, @@ -764,7 +765,7 @@ std::tuple, rmm::device_uvector> k_truss thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ - raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), edge_first, edge_last, diff --git a/cpp/src/structure/edge_triangle_count_impl.cuh b/cpp/src/structure/edge_triangle_count_impl.cuh index fdfc034ee3b..29c94e58742 100644 --- a/cpp/src/structure/edge_triangle_count_impl.cuh +++ b/cpp/src/structure/edge_triangle_count_impl.cuh @@ -52,11 +52,11 @@ struct update_edges_p_r_q_r_num_triangles { const edge_t edge_first_or_second{}; raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - raft::device_span num_triangles{}; + raft::device_span num_triangles{}; EdgeIterator edge_first{}; - __device__ thrust::tuple operator()(size_t i) const + __device__ void operator()(size_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); @@ -71,6 +71,8 @@ struct update_edges_p_r_q_r_num_triangles { edge_first, edge_first + num_edges, // pass the number of vertex pairs p_r_pair); + + assert(*itr_p_r_p_q == p_r_pair); idx = thrust::distance(edge_first, itr_p_r_p_q); } else { auto p_r_pair = @@ -82,16 +84,16 @@ struct update_edges_p_r_q_r_num_triangles { edge_first, edge_first + num_edges, // pass the number of vertex pairs p_r_pair); - + assert(*itr_p_r_p_q == p_r_pair); idx = thrust::distance(edge_first, itr_p_r_p_q); } - cuda::atomic_ref atomic_counter(num_triangles[idx]); - auto r = atomic_counter.fetch_add(vertex_t{1}, cuda::std::memory_order_relaxed); + cuda::atomic_ref atomic_counter(num_triangles[idx]); + auto r = atomic_counter.fetch_add(edge_t{1}, cuda::std::memory_order_relaxed); } }; template -std::enable_if_t> edge_triangle_count_impl( +std::enable_if_t> edge_triangle_count_impl( raft::handle_t const& handle, graph_view_t const& graph_view, raft::device_span edgelist_srcs, @@ -110,7 +112,7 @@ std::enable_if_t> edge_triangle_count_ std::array{true, true}, false /*FIXME: pass 'do_expensive_check' as argument*/); - rmm::device_uvector num_triangles(edgelist_srcs.size(), handle.get_stream()); + rmm::device_uvector num_triangles(edgelist_srcs.size(), handle.get_stream()); // Update the number of triangles of each (p, q) edges by looking at their intersection // size @@ -132,7 +134,7 @@ std::enable_if_t> edge_triangle_count_ 0, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), - raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), edge_first}); thrust::for_each( @@ -144,7 +146,7 @@ std::enable_if_t> edge_triangle_count_ 1, raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), - raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles.data(), num_triangles.size()), edge_first}); // rmm::device_uvector num_triangles( @@ -155,7 +157,7 @@ std::enable_if_t> edge_triangle_count_ } // namespace detail template -rmm::device_uvector edge_triangle_count( +rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, graph_view_t const& graph_view, raft::device_span edgelist_srcs, diff --git a/cpp/src/structure/edge_triangle_count_sg.cu b/cpp/src/structure/edge_triangle_count_sg.cu index c2bbf6aa8dd..aa3a0cf72a1 100644 --- a/cpp/src/structure/edge_triangle_count_sg.cu +++ b/cpp/src/structure/edge_triangle_count_sg.cu @@ -24,7 +24,7 @@ template rmm::device_uvector edge_triangle_count( raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); -template rmm::device_uvector edge_triangle_count( +template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, raft::device_span edgelist_srcs, @@ -36,7 +36,7 @@ template rmm::device_uvector edge_triangle_count( raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); -template rmm::device_uvector edge_triangle_count( +template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, raft::device_span edgelist_srcs, From 1b4c5805b9a17f8a0b96df4b5c170b6e565c932d Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 1 Mar 2024 07:08:23 -0800 Subject: [PATCH 065/155] reorder cases --- cpp/src/community/k_truss_impl.cuh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index c9d5489b6ce..cfbec72351c 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -633,11 +633,9 @@ std::tuple, rmm::device_uvector> k_truss edgelist_dsts.resize(num_valid_edges, handle.get_stream()); num_triangles.resize(num_valid_edges, handle.get_stream()); - // case 2: unroll (q, r) - // FIXME: Update the num_edges when removing edges + // case 1: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - cugraph::find_unroll_p_r_and_q_r_edges, rmm::device_uvector> k_truss edge_first, edge_triangle_count_pair_first); + // case 2: unroll (p, r) cugraph::find_unroll_p_r_and_q_r_edges, rmm::device_uvector> k_truss cur_graph_view.attach_edge_mask(edge_mask.view()); - // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) + // case 3. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. // FIXME: debug this stage. There are edges that have been removed that are still found in nbr From 05ed54031db655765d546a46a1b7dde73f3d2f1e Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 1 Mar 2024 11:43:01 -0800 Subject: [PATCH 066/155] replace thrust 'tabulation' by 'copy' which is more explicit --- cpp/src/community/k_truss_impl.cuh | 180 +++++------------------------ 1 file changed, 31 insertions(+), 149 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index cfbec72351c..747b8797471 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -17,6 +17,7 @@ // FIXME: remove all unused imports #include +#include #include #include #include @@ -49,7 +50,7 @@ template struct unroll_edge { raft::device_span num_triangles{}; EdgeIterator edge_unrolled{}; - EdgeIterator edge_first{}; + EdgeIterator transposed_edge_first{}; EdgeIterator edge_last{}; __device__ thrust::tuple operator()(edge_t i) const @@ -58,10 +59,10 @@ struct unroll_edge { auto pair = thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), thrust::get<0>(*(edge_unrolled + i))); // Find its position in 'edges' - auto itr = thrust::lower_bound(thrust::seq, edge_first, edge_last, pair); + auto itr = thrust::lower_bound(thrust::seq, transposed_edge_first, edge_last, pair); assert(*itr == pair); - auto idx = thrust::distance(edge_first, itr); + auto idx = thrust::distance(transposed_edge_first, itr); cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); } @@ -85,16 +86,12 @@ void find_unroll_p_r_and_q_r_edges( EdgeIterator incoming_vertex_pairs, EdgeTriangleCountIterator edge_triangle_count_pair_first) { - printf("\ninvalid edge buffer\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - printf("\n"); auto num_edges = edgelist_dsts.size(); rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), - prefix_sum.end(), + prefix_sum.begin() + num_invalid_edges, [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), dst_array_begin = edgelist_dsts.begin(), num_edges] __device__(auto idx) { @@ -136,54 +133,21 @@ void find_unroll_p_r_and_q_r_edges( auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - thrust::tabulate(thrust::seq, - incoming_edges_to_r + prefix_sum[idx], - incoming_edges_to_r + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), - dst); - }); + auto potential_closing_edges_first = thrust::make_zip_iterator(dst_array_begin + idx_lower, thrust::make_constant_iterator(dst)); + thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), potential_closing_edges + prefix_sum[idx]); if (edge_type == 0) { - thrust::tabulate(thrust::seq, - potential_closing_edges + prefix_sum[idx], - potential_closing_edges + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment)), - src); - }); - } else { - thrust::tabulate(thrust::seq, - potential_closing_edges + prefix_sum[idx], - potential_closing_edges + prefix_sum[idx + 1], - [incoming_vertex_pairs = incoming_vertex_pairs, - dst = dst, - src = src, - idx_lower = idx_lower](auto idx_in_segment) { - return thrust::make_tuple( - src, - thrust::get<1>(*(incoming_vertex_pairs + idx_lower + idx_in_segment))); - }); + + auto potential_closing_edges_first = thrust::make_zip_iterator(dst_array_begin + idx_lower, thrust::make_constant_iterator(src)); + thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), potential_closing_edges + prefix_sum[idx]); + + } else { + auto potential_closing_edges_first = thrust::make_zip_iterator(thrust::make_constant_iterator(src), dst_array_begin + idx_lower); + thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), potential_closing_edges + prefix_sum[idx]); } }); - printf("\npotential - incoming before\n"); - raft::print_device_vector("***potential_closing_edges - src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); - raft::print_device_vector("***potential_closing_edges - dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); - raft::print_device_vector("***incoming edges to r - src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); - raft::print_device_vector("***incoming edges to r - dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); - printf("\n\n"); - auto edges_exist = graph_view.has_edge( handle, raft::device_span(std::get<0>(potential_closing_edges).data(), @@ -211,13 +175,6 @@ void find_unroll_p_r_and_q_r_edges( resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); - printf("\npotential - incoming after\n"); - raft::print_device_vector("***potential_closing_edges - src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); - raft::print_device_vector("***potential_closing_edges - dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); - raft::print_device_vector("***incoming edges to r - src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); - raft::print_device_vector("***incoming edges to r - dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); - printf("\n\n"); - auto incoming_vertex_pairs_last = incoming_vertex_pairs + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -237,12 +194,6 @@ void find_unroll_p_r_and_q_r_edges( incoming_vertex_pairs, incoming_vertex_pairs_last}); - printf("\nafter unrolling\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); - // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' @@ -264,12 +215,6 @@ void find_unroll_p_r_and_q_r_edges( edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); - - printf("\nafter resizing and removing edges with no triangles\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); } namespace { @@ -308,7 +253,7 @@ struct generate_p_r_q_r { const edge_t edge_first_src_or_dst{}; raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - EdgeIterator edge_first{}; + EdgeIterator transposed_edge_first{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -317,9 +262,9 @@ struct generate_p_r_q_r { auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); if (edge_first_src_or_dst == 0) { - return thrust::make_tuple(thrust::get<0>(*(edge_first + idx)), intersection_indices[i]); + return thrust::make_tuple(thrust::get<0>(*(transposed_edge_first + idx)), intersection_indices[i]); } else { - return thrust::make_tuple(thrust::get<1>(*(edge_first + idx)), intersection_indices[i]); + return thrust::make_tuple(thrust::get<1>(*(transposed_edge_first + idx)), intersection_indices[i]); } } }; @@ -385,7 +330,7 @@ std::tuple, rmm::device_uvector> k_truss } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - /* + { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -443,37 +388,18 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } - */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { - auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - // FIXME: REmove this********************************************************************************** - rmm::device_uvector srcs_(0, handle.get_stream()); - rmm::device_uvector dsts_(0, handle.get_stream()); - - std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); - - printf("\nOriginal graph\n"); - raft::print_device_vector("srcs", srcs_.data(), srcs_.size(), std::cout); - raft::print_device_vector("dsts", dsts_.data(), dsts_.size(), std::cout); - //***************************************************************************************************** - auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) : std::nullopt; auto out_degrees = cur_graph_view.compute_out_degrees(handle); - edge_src_property_t edge_src_out_degrees(handle, cur_graph_view); edge_dst_property_t edge_dst_out_degrees(handle, @@ -536,7 +462,7 @@ std::tuple, rmm::device_uvector> k_truss std::optional>{std::nullopt}, std::optional>(std::nullopt)); - auto edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); auto num_triangles = edge_triangle_count( handle, @@ -545,14 +471,7 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); auto edge_triangle_count_pair_first = - thrust::make_zip_iterator(edge_first, num_triangles.begin()); - - - printf("\nreduced edges from original\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); + thrust::make_zip_iterator(transposed_edge_first, num_triangles.begin()); // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edges_with_triangles(handle); @@ -575,12 +494,6 @@ std::tuple, rmm::device_uvector> k_truss return num_triangles > 0; }); - printf("\before removing edges with no triangles\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); - num_edges_with_triangles = static_cast( thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); @@ -588,12 +501,6 @@ std::tuple, rmm::device_uvector> k_truss edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - printf("\nafter removing edges with no triangles\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); - // 'invalid_edge_first' marks the beginning of the edges to be removed auto invalid_edge_first = thrust::stable_partition(handle.get_thrust_policy(), @@ -607,12 +514,6 @@ std::tuple, rmm::device_uvector> k_truss num_invalid_edges = static_cast(thrust::distance( invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - printf("\nnumber of invalid edges = %d\n", num_invalid_edges); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); - if (num_invalid_edges == 0) { break; } auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; @@ -638,7 +539,7 @@ std::tuple, rmm::device_uvector> k_truss // create the pair (p, q) cugraph::find_unroll_p_r_and_q_r_edges(handle, cur_graph_view, @@ -648,13 +549,13 @@ std::tuple, rmm::device_uvector> k_truss std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(num_triangles), - edge_first, + transposed_edge_first, edge_triangle_count_pair_first); // case 2: unroll (p, r) cugraph::find_unroll_p_r_and_q_r_edges(handle, cur_graph_view, @@ -664,9 +565,10 @@ std::tuple, rmm::device_uvector> k_truss std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(num_triangles), - edge_first, + transposed_edge_first, edge_triangle_count_pair_first); + cugraph::fill_edge_property(handle, cur_graph_view, false, edge_mask); edges_with_triangles.clear(); // FIXME: is this needed? edges_with_triangles.insert( edgelist_srcs.begin(), edgelist_srcs.begin(), edgelist_dsts.end()); @@ -719,23 +621,15 @@ std::tuple, rmm::device_uvector> k_truss intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer)}); - printf("\nbefore unrolling p_q\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - - raft::print_device_vector("p_r", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); - raft::print_device_vector("p_r", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); - - auto edge_last = edge_first + edgelist_srcs.size(); + auto edge_last = transposed_edge_first + edgelist_srcs.size(); auto num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - edge_first, + transposed_edge_first, edge_last, }); @@ -756,31 +650,19 @@ std::tuple, rmm::device_uvector> k_truss intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); - - raft::print_device_vector("q_r", std::get<0>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); - raft::print_device_vector("q_r", std::get<1>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + unroll_edge{ raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - edge_first, + transposed_edge_first, edge_last, }); - - printf("\nafter unrolling p_q\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); } - printf("\n*********final results*********\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\nthe number of edges = %d\n", edgelist_srcs.size()); + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } } From e824eb3ccf0033618e49862236296409d7477877 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 1 Mar 2024 11:44:09 -0800 Subject: [PATCH 067/155] fix style --- cpp/src/community/k_truss_impl.cuh | 52 ++++++++++++------- .../structure/edge_triangle_count_impl.cuh | 2 +- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 747b8797471..f06272f2860 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -17,8 +17,8 @@ // FIXME: remove all unused imports #include -#include #include +#include #include #include #include @@ -132,19 +132,29 @@ void find_unroll_p_r_and_q_r_edges( auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - - auto potential_closing_edges_first = thrust::make_zip_iterator(dst_array_begin + idx_lower, thrust::make_constant_iterator(dst)); - thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), potential_closing_edges + prefix_sum[idx]); - - if (edge_type == 0) { - - auto potential_closing_edges_first = thrust::make_zip_iterator(dst_array_begin + idx_lower, thrust::make_constant_iterator(src)); - thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), potential_closing_edges + prefix_sum[idx]); - } else { - auto potential_closing_edges_first = thrust::make_zip_iterator(thrust::make_constant_iterator(src), dst_array_begin + idx_lower); - thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), potential_closing_edges + prefix_sum[idx]); + auto potential_closing_edges_first = + thrust::make_zip_iterator(dst_array_begin + idx_lower, thrust::make_constant_iterator(dst)); + thrust::copy(thrust::seq, + potential_closing_edges_first, + potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), + potential_closing_edges + prefix_sum[idx]); + if (edge_type == 0) { + auto potential_closing_edges_first = thrust::make_zip_iterator( + dst_array_begin + idx_lower, thrust::make_constant_iterator(src)); + thrust::copy(thrust::seq, + potential_closing_edges_first, + potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), + potential_closing_edges + prefix_sum[idx]); + + } else { + auto potential_closing_edges_first = thrust::make_zip_iterator( + thrust::make_constant_iterator(src), dst_array_begin + idx_lower); + thrust::copy(thrust::seq, + potential_closing_edges_first, + potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), + potential_closing_edges + prefix_sum[idx]); } }); @@ -193,7 +203,7 @@ void find_unroll_p_r_and_q_r_edges( get_dataframe_buffer_begin(incoming_edges_to_r), incoming_vertex_pairs, incoming_vertex_pairs_last}); - + // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them // when necessary otherwise simply call 'thrust::partition' @@ -262,9 +272,11 @@ struct generate_p_r_q_r { auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); if (edge_first_src_or_dst == 0) { - return thrust::make_tuple(thrust::get<0>(*(transposed_edge_first + idx)), intersection_indices[i]); + return thrust::make_tuple(thrust::get<0>(*(transposed_edge_first + idx)), + intersection_indices[i]); } else { - return thrust::make_tuple(thrust::get<1>(*(transposed_edge_first + idx)), intersection_indices[i]); + return thrust::make_tuple(thrust::get<1>(*(transposed_edge_first + idx)), + intersection_indices[i]); } } }; @@ -462,7 +474,8 @@ std::tuple, rmm::device_uvector> k_truss std::optional>{std::nullopt}, std::optional>(std::nullopt)); - auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + auto transposed_edge_first = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); auto num_triangles = edge_triangle_count( handle, @@ -493,7 +506,7 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); - + num_edges_with_triangles = static_cast( thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); @@ -513,7 +526,7 @@ std::tuple, rmm::device_uvector> k_truss num_invalid_edges = static_cast(thrust::distance( invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); - + if (num_invalid_edges == 0) { break; } auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; @@ -660,10 +673,9 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first, edge_last, }); - } - return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } } } // namespace cugraph diff --git a/cpp/src/structure/edge_triangle_count_impl.cuh b/cpp/src/structure/edge_triangle_count_impl.cuh index 29c94e58742..7743c1ac533 100644 --- a/cpp/src/structure/edge_triangle_count_impl.cuh +++ b/cpp/src/structure/edge_triangle_count_impl.cuh @@ -71,7 +71,7 @@ struct update_edges_p_r_q_r_num_triangles { edge_first, edge_first + num_edges, // pass the number of vertex pairs p_r_pair); - + assert(*itr_p_r_p_q == p_r_pair); idx = thrust::distance(edge_first, itr_p_r_p_q); } else { From 5e95bb46ff29dfaf9668d79d57640437600f39d9 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 1 Mar 2024 11:53:23 -0800 Subject: [PATCH 068/155] add fixme --- cpp/src/community/k_truss_impl.cuh | 2 +- cpp/src/structure/edge_triangle_count_impl.cuh | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index f06272f2860..1b6d02a0ef3 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -48,7 +48,7 @@ namespace cugraph { template struct unroll_edge { - raft::device_span num_triangles{}; + raft::device_span num_triangles{}; // FIXME: invalid type for unsigned integers EdgeIterator edge_unrolled{}; EdgeIterator transposed_edge_first{}; EdgeIterator edge_last{}; diff --git a/cpp/src/structure/edge_triangle_count_impl.cuh b/cpp/src/structure/edge_triangle_count_impl.cuh index 7743c1ac533..11db6b45924 100644 --- a/cpp/src/structure/edge_triangle_count_impl.cuh +++ b/cpp/src/structure/edge_triangle_count_impl.cuh @@ -112,6 +112,7 @@ std::enable_if_t> edge_triangle_count_im std::array{true, true}, false /*FIXME: pass 'do_expensive_check' as argument*/); + // FIXME: invalid type for unsigned integers. rmm::device_uvector num_triangles(edgelist_srcs.size(), handle.get_stream()); // Update the number of triangles of each (p, q) edges by looking at their intersection @@ -149,8 +150,6 @@ std::enable_if_t> edge_triangle_count_im raft::device_span(num_triangles.data(), num_triangles.size()), edge_first}); - // rmm::device_uvector num_triangles( - // 2, handle.get_stream()); return num_triangles; } From 9a9749021fa6123c9e2c76b62c1719dd06577df1 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 4 Mar 2024 09:36:01 -0800 Subject: [PATCH 069/155] remove invalid edges at the end of each iterations --- cpp/src/community/k_truss_impl.cuh | 368 +++++++++++++++++++++++++-- cpp/tests/community/k_truss_test.cpp | 2 +- 2 files changed, 350 insertions(+), 20 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 1b6d02a0ef3..498f72d8bbe 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -63,6 +63,7 @@ struct unroll_edge { assert(*itr == pair); auto idx = thrust::distance(transposed_edge_first, itr); + printf("\nunrolled pair - src = %d, dst = %d and idx = %d\n", thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i)), idx); cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); } @@ -83,16 +84,19 @@ void find_unroll_p_r_and_q_r_edges( rmm::device_uvector&& edgelist_srcs, rmm::device_uvector&& edgelist_dsts, rmm::device_uvector&& num_triangles, - EdgeIterator incoming_vertex_pairs, + EdgeIterator incoming_vertex_pairs, // rename to 'transposed_edge_first' EdgeTriangleCountIterator edge_triangle_count_pair_first) { + auto num_edges = edgelist_dsts.size(); rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); + //raft::print_device_vector("edgelist_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.begin() + num_invalid_edges, [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), + src_array_begin = edgelist_srcs.begin(), dst_array_begin = edgelist_dsts.begin(), num_edges] __device__(auto idx) { auto src = thrust::get<0>(*(invalid_first + idx)); @@ -101,10 +105,18 @@ void find_unroll_p_r_and_q_r_edges( auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); auto itr_upper = thrust::upper_bound(thrust::seq, itr_lower, dst_array_end, dst); auto dist = thrust::distance(itr_lower, itr_upper); + printf("\ndst = %d, and distance = %d, pos_init = %d, pos_end = %d\n", dst, dist, thrust::distance(dst_array_begin, itr_lower), thrust::distance(itr_lower, itr_upper)); return dist; }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + + printf("\nbefore scan edgelist triangle\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + + raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); auto potential_closing_edges = allocate_dataframe_buffer>( prefix_sum.back_element(handle.get_stream()), handle.get_stream()); @@ -120,6 +132,7 @@ void find_unroll_p_r_and_q_r_edges( num_edges, invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), + src_array_begin = edgelist_srcs.begin(), dst_array_begin = edgelist_dsts.begin(), prefix_sum = prefix_sum.data(), potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), @@ -133,16 +146,16 @@ void find_unroll_p_r_and_q_r_edges( auto idx_lower = thrust::distance( dst_array_begin, itr_lower); // Need a binary search to find the begining of the range - auto potential_closing_edges_first = - thrust::make_zip_iterator(dst_array_begin + idx_lower, thrust::make_constant_iterator(dst)); + auto incoming_edges_to_r_first = + thrust::make_zip_iterator(src_array_begin + idx_lower, thrust::make_constant_iterator(dst)); thrust::copy(thrust::seq, - potential_closing_edges_first, - potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), - potential_closing_edges + prefix_sum[idx]); + incoming_edges_to_r_first, + incoming_edges_to_r_first + (prefix_sum[idx + 1] - prefix_sum[idx]), + incoming_edges_to_r + prefix_sum[idx]); if (edge_type == 0) { auto potential_closing_edges_first = thrust::make_zip_iterator( - dst_array_begin + idx_lower, thrust::make_constant_iterator(src)); + src_array_begin + idx_lower, thrust::make_constant_iterator(src)); thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), @@ -150,13 +163,37 @@ void find_unroll_p_r_and_q_r_edges( } else { auto potential_closing_edges_first = thrust::make_zip_iterator( - thrust::make_constant_iterator(src), dst_array_begin + idx_lower); + thrust::make_constant_iterator(src), src_array_begin + idx_lower); thrust::copy(thrust::seq, potential_closing_edges_first, potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), potential_closing_edges + prefix_sum[idx]); } }); + + printf("\nincoming_edges_to_invalid_r\n"); + raft::print_device_vector("src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); + + raft::print_device_vector("src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); + + + + + + + + rmm::device_uvector edgelist_srcs_(0, handle.get_stream()); + rmm::device_uvector edgelist_dsts_(0, handle.get_stream()); + + std::tie(edgelist_srcs_, edgelist_dsts_, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + auto edges_exist = graph_view.has_edge( handle, @@ -164,6 +201,13 @@ void find_unroll_p_r_and_q_r_edges( std::get<0>(potential_closing_edges).size()), raft::device_span(std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size())); + + raft::print_device_vector("exi", edges_exist.data(), edges_exist.size(), std::cout); + + + printf("\ngraph_view before checkign edges\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); auto edge_to_existance = thrust::make_zip_iterator( thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), @@ -185,6 +229,46 @@ void find_unroll_p_r_and_q_r_edges( resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); + printf("\npotential incoming edges before\n"); + raft::print_device_vector("valid_incoming_to_r: src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); + raft::print_device_vector("valid_incoming_to_r: dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); + raft::print_device_vector("potential_closing_e: src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); + raft::print_device_vector("potential_closing_e: dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); + + // FIXME: Extra processing because has_edge() seem to not be working + printf("\nnum_edges = %d\n", edgelist_srcs.size()); + auto has_edge_last_ = thrust::remove_if(handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges) + num_edge_exists, get_dataframe_buffer_begin(incoming_edges_to_r) + num_edge_exists), + [incoming_vertex_pairs, + num_edges = edgelist_srcs.size(), + incoming_vertex_pairs_last = incoming_vertex_pairs + edgelist_srcs.size()] __device__(auto e) { + //auto edge_exists = thrust::get<1>(e); + auto potential = thrust::get<0>(e); + auto transposed_potential = thrust::make_tuple(thrust::get<1>(potential), thrust::get<0>(potential)); + auto itr = thrust::find(thrust::seq, incoming_vertex_pairs, incoming_vertex_pairs_last, transposed_potential); + auto dist = thrust::distance(incoming_vertex_pairs, itr); + printf("\nsrc = %d, dst = %d, dist %d\n", thrust::get<0>(potential), thrust::get<1>(potential), dist); + return dist == num_edges; + }); + + + num_edge_exists = thrust::distance(thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), has_edge_last_); + resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); + resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); + + printf("\npotential incoming edges after\n"); + raft::print_device_vector("valid_incoming_to_r: src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); + raft::print_device_vector("valid_incoming_to_r: dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); + raft::print_device_vector("potential_closing_e: src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); + raft::print_device_vector("potential_closing_e: dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); + + printf("\nbefore unrolling\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); + auto incoming_vertex_pairs_last = incoming_vertex_pairs + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -203,6 +287,61 @@ void find_unroll_p_r_and_q_r_edges( get_dataframe_buffer_begin(incoming_edges_to_r), incoming_vertex_pairs, incoming_vertex_pairs_last}); + + printf("\nafter unrolling\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); + + // FIXME:: Remove invalid edges with triangle counts == 0 *************************************** + // because keeping them will cause over-compensation + + //raft::print_device_vector("incoming_vertex_pairs: src", thrust::get<0>(incoming_vertex_pairs.get_iterator_tuple()), edgelist_srcs.size(), std::cout); + //raft::print_device_vector("incoming_vertex_pairs: dst", thrust::get<1>(incoming_vertex_pairs.get_iterator_tuple()), edgelist_srcs.size(), std::cout); + + raft::print_device_vector("invalid_e_before: src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("invalid_e_before: dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + + auto has_invalid_edge_last = thrust::remove_if(handle.get_thrust_policy(), + get_dataframe_buffer_begin(invalid_edges_buffer), + get_dataframe_buffer_end(invalid_edges_buffer), + [edge_triangle_count_pair_first, + edgelist_srcs = edgelist_srcs.begin(), + edgelist_dsts = edgelist_dsts.begin(), + num_triangles = num_triangles.begin(), + incoming_vertex_pairs, + incoming_vertex_pairs_last = incoming_vertex_pairs + num_edges, + num_edges = edgelist_srcs.size()] __device__(auto invalid_e) { + //auto edge_exists = thrust::get<1>(e); + // Find its position in 'edges' + //auto itr = thrust::lower_bound(thrust::seq, thrust::get<0>(edge_triangle_count_pair_first), thrust::get<0>(edge_triangle_count_pair_first), invalid_e); + auto transposed_invalid_e = thrust::make_tuple(thrust::get<1>(invalid_e), thrust::get<0>(invalid_e)); + auto itr = thrust::lower_bound(thrust::seq, incoming_vertex_pairs, incoming_vertex_pairs_last, transposed_invalid_e); + + assert(*itr == invalid_e); + + auto idx = thrust::distance(incoming_vertex_pairs, itr); + printf("\ngoing over invalid_edges %d, %d and idx = %d\n", thrust::get<0>(transposed_invalid_e), thrust::get<1>(transposed_invalid_e), idx); + if (num_triangles[idx] == 0){ + printf("\nfound an invalid edge %d, %d with no triangle at idx = %d\n", thrust::get<0>(invalid_e), thrust::get<1>(invalid_e), idx); + return 1; + } else { + return 0; + } + }); + + auto num_invalid_edge_exists = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), has_invalid_edge_last); + + // After pushing the invalid edges with no triangle to the second partition, + // remove them by resizing both vertex pair buffer + printf("\nnumber of invalid edges with triangle(s) = %d\n", num_invalid_edge_exists); + resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edge_exists, handle.get_stream()); + + + raft::print_device_vector("invalid_e_after: src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("invalid_e_after: dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + // Put edges with triangle count == 0 in the second partition // FIXME: revisit all the 'stable_partition' and only used them @@ -342,7 +481,7 @@ std::tuple, rmm::device_uvector> k_truss } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - + /* { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -400,12 +539,29 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } + */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; + // FIXME: REmove this********************************************************************************** + rmm::device_uvector srcs_(0, handle.get_stream()); + rmm::device_uvector dsts_(0, handle.get_stream()); + + std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + + printf("\nOriginal graph\n"); + raft::print_device_vector("srcs", srcs_.data(), srcs_.size(), std::cout); + raft::print_device_vector("dsts", dsts_.data(), dsts_.size(), std::cout); + //***************************************************************************************************** + auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -473,18 +629,29 @@ std::tuple, rmm::device_uvector> k_truss std::optional>{std::nullopt}, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - - auto transposed_edge_first = - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - + auto num_triangles = edge_triangle_count( handle, cur_graph_view, raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + // FIXME 'edge_triangle_count' sorts the edges by 'src' but 'k-truss' needs + // the edges to be sorted with 'dst' as key so we need to sort the edges + // again. Should 'edge_triangle_count' be implemented edges sorted by 'dst' + // instead to avoid resorting? + auto transposed_edge_first = + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + + auto edge_triangle_count_pair_first = thrust::make_zip_iterator(transposed_edge_first, num_triangles.begin()); + + printf("\nreduced edges from original\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_bucket_t edges_with_triangles(handle); @@ -506,6 +673,12 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); + + printf("\nbefore removing edges with no triangles\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); num_edges_with_triangles = static_cast( thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); @@ -514,6 +687,12 @@ std::tuple, rmm::device_uvector> k_truss edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + printf("\nafter removing edges with no triangles\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); + // 'invalid_edge_first' marks the beginning of the edges to be removed auto invalid_edge_first = thrust::stable_partition(handle.get_thrust_policy(), @@ -543,9 +722,23 @@ std::tuple, rmm::device_uvector> k_truss get_dataframe_buffer_begin(invalid_edges_buffer)); // resize the 'edgelist_srcs' and 'edgelist_dst' - edgelist_srcs.resize(num_valid_edges, handle.get_stream()); - edgelist_dsts.resize(num_valid_edges, handle.get_stream()); - num_triangles.resize(num_valid_edges, handle.get_stream()); + // edgelist_srcs.resize(num_valid_edges, handle.get_stream()); + // edgelist_dsts.resize(num_valid_edges, handle.get_stream()); + // num_triangles.resize(num_valid_edges, handle.get_stream()); + + + thrust::sort_by_key( + handle.get_thrust_policy(), transposed_edge_first, transposed_edge_first + edgelist_srcs.size(), num_triangles.begin()); + + printf("\nnumber of invalid edges = %d\n", num_invalid_edges); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\n"); + + printf("\ninit invalid edges buffer\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); // case 1: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and @@ -581,11 +774,22 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first, edge_triangle_count_pair_first); + //cugraph::edge_bucket_t edges_with_triangles(handle); + cugraph::edge_property_t edge_mask(handle, cur_graph_view); cugraph::fill_edge_property(handle, cur_graph_view, false, edge_mask); - edges_with_triangles.clear(); // FIXME: is this needed? - edges_with_triangles.insert( - edgelist_srcs.begin(), edgelist_srcs.begin(), edgelist_dsts.end()); + //edges_with_triangles.resize(edgelist_srcs.size()); + + edges_with_triangles.insert( + edgelist_srcs.begin(), edgelist_srcs.end(), edgelist_dsts.begin()); + + printf("\nthe size of the e_bucket = %d\n", edges_with_triangles.size()); + + printf("\ncalling transform_e\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + cugraph::transform_e( handle, cur_graph_view, @@ -594,6 +798,7 @@ std::tuple, rmm::device_uvector> k_truss cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + printf("\n transform_e src = %d, dst = %d\n", src, dst); return true; }, edge_mask.mutable_view(), @@ -601,6 +806,56 @@ std::tuple, rmm::device_uvector> k_truss cur_graph_view.attach_edge_mask(edge_mask.view()); + /* + std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, renumber_map) = + create_graph_from_edgelist( + handle, + std::nullopt, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::nullopt, + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{true, graph_view.is_multigraph()}, + false); + + cur_graph_view = (*modified_graph).view(); + + std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + */ + + + rmm::device_uvector edgelist_srcs_(0, handle.get_stream()); + rmm::device_uvector edgelist_dsts_(0, handle.get_stream()); + + std::tie(edgelist_srcs_, edgelist_dsts_, std::ignore, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + std::optional>{std::nullopt}, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + + printf("\n edgelist in the device arrray\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + + printf("\n edgelist after tranform_e\n"); + raft::print_device_vector("srcs", edgelist_srcs_.data(), edgelist_srcs_.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts_.data(), edgelist_dsts_.size(), std::cout); + + + //raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + + + + + // FIXME: Check the edgelist left in 'cur_graph_view' ************************* + // case 3. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. @@ -633,6 +888,9 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(intersection_indices.data(), intersection_indices.size()), get_dataframe_buffer_begin(invalid_edges_buffer)}); + + raft::print_device_vector("vertex_pair_buffer_p_r_edge_p_q: src", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); + raft::print_device_vector("vertex_pair_buffer_p_r_edge_p_q: dst", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); auto edge_last = transposed_edge_first + edgelist_srcs.size(); auto num_edge_exists = accumulate_pair_size; @@ -664,6 +922,8 @@ std::tuple, rmm::device_uvector> k_truss get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate }); + raft::print_device_vector("vertex_pair_buffer_q_r_edge_p_q: src", std::get<0>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); + raft::print_device_vector("vertex_pair_buffer_q_r_edge_p_q: dst", std::get<1>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), @@ -673,8 +933,78 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first, edge_last, }); + + + printf("\nnum_edges = %d\n", edgelist_srcs.size()); + + printf("\nBefore removing invalid edges with no triangles\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\nthe number of edges = %d\n", edgelist_srcs.size()); + + // sort the invalid edgelist for a binary search + printf("\ninvalid edges buffer before sorting\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + + thrust::sort( + handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), get_dataframe_buffer_end(invalid_edges_buffer)); + + printf("\ninvalid edges buffer after sorting\n"); + raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); + raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); + + auto has_edge_last_ = thrust::remove_if(handle.get_thrust_policy(), + edge_triangle_count_pair_first, + edge_triangle_count_pair_first + edgelist_srcs.size(), + [invalid_edges_buffer_first = get_dataframe_buffer_begin(invalid_edges_buffer), + invalid_edges_buffer_last = get_dataframe_buffer_end(invalid_edges_buffer), + num_invalid_edges = size_dataframe_buffer(invalid_edges_buffer), + num_edges = edgelist_srcs.size()] __device__(auto e_triangle_count) { + //auto edge_exists = thrust::get<1>(e); + //auto potential = thrust::get<0>(e); + //auto transposed_potential = thrust::make_tuple(thrust::get<1>(potential), thrust::get<0>(potential)); + auto transposed_edge = thrust::get<0>(e_triangle_count); + auto edge = thrust::make_tuple(thrust::get<1>(transposed_edge), thrust::get<0>(transposed_edge)); + + auto itr = thrust::find(thrust::seq, invalid_edges_buffer_first, invalid_edges_buffer_last, edge); + auto dist = thrust::distance(invalid_edges_buffer_first, itr); + printf("\nsrc = %d, dst = %d, dist %d\n", thrust::get<0>(edge), thrust::get<1>(edge), dist); + return dist < num_invalid_edges; + }); + + + auto dist_ = thrust::distance(edge_triangle_count_pair_first, has_edge_last_); + + + edgelist_srcs.resize(dist_, handle.get_stream()); + edgelist_dsts.resize(dist_, handle.get_stream()); + num_triangles.resize(dist_, handle.get_stream()); + + printf("\nAfter removing invalid edges with no triangles\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\nthe number of edges = %d\n", edgelist_srcs.size()); + + + + + + + + cur_graph_view.clear_edge_mask(); + + edges_with_triangles.clear(); //masking not working in a loop. + edge_mask.clear(handle); // masking not working in a loop. } + printf("\n*********final results*********\n"); + raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + printf("\nthe number of edges = %d\n", edgelist_srcs.size()); return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } } diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 26f85c3add8..02fce9b527a 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -113,7 +113,7 @@ INSTANTIATE_TEST_SUITE_P(file_test, Tests_KTruss_File, ::testing::Combine( // enable correctness checks - ::testing::Values(KTruss_Usecase{4}), + ::testing::Values(KTruss_Usecase{5}), ::testing::Values(cugraph::test::File_Usecase( "/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx")))); From 4126d70ba938120ba9e7a7016b78fa1688361222 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 11 Mar 2024 14:42:54 -0700 Subject: [PATCH 070/155] remove unnecessary copy of edges --- cpp/src/community/k_truss_impl.cuh | 927 ++++++++++++--------------- cpp/tests/community/k_truss_test.cpp | 3 +- 2 files changed, 406 insertions(+), 524 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 498f72d8bbe..d1128b8e96b 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -48,152 +48,239 @@ namespace cugraph { template struct unroll_edge { + edge_t num_valid_edges{}; raft::device_span num_triangles{}; // FIXME: invalid type for unsigned integers EdgeIterator edge_unrolled{}; - EdgeIterator transposed_edge_first{}; - EdgeIterator edge_last{}; + EdgeIterator transposed_valid_edge_first{}; + EdgeIterator transposed_valid_edge_last{}; + EdgeIterator transposed_invalid_edge_last{}; __device__ thrust::tuple operator()(edge_t i) const { // edges are sorted with destination as key so reverse the edge when looking it auto pair = thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), thrust::get<0>(*(edge_unrolled + i))); - // Find its position in 'edges' - auto itr = thrust::lower_bound(thrust::seq, transposed_edge_first, edge_last, pair); - assert(*itr == pair); + // Find its position in either partition of the transposed edgelist + // An edge can be in found in either of the two partitions (valid or invalid) + + auto itr = thrust::lower_bound(thrust::seq, transposed_valid_edge_last, transposed_invalid_edge_last, pair); + auto idx = thrust::distance(transposed_valid_edge_last, itr) + num_valid_edges; - auto idx = thrust::distance(transposed_edge_first, itr); - printf("\nunrolled pair - src = %d, dst = %d and idx = %d\n", thrust::get<0>(*(edge_unrolled + i)), thrust::get<1>(*(edge_unrolled + i)), idx); + if (*itr != pair){ + // The edge must be in the first boundary + itr = thrust::lower_bound(thrust::seq, transposed_valid_edge_first, transposed_valid_edge_last, pair); + idx = thrust::distance(transposed_valid_edge_first, itr); + } + + assert(*itr == pair); cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); } }; -template -void find_unroll_p_r_and_q_r_edges( +template +struct unroll_edge_invalid { + edge_t num_valid_edges{}; + raft::device_span num_triangles{}; // FIXME: invalid type for unsigned integers + raft::device_span num_triangles_invalid{}; + + __device__ thrust::tuple operator()(edge_t idx) const + { + + cuda::atomic_ref atomic_counter(num_triangles[idx + num_valid_edges]); + auto r = atomic_counter.fetch_sub(edge_t{num_triangles_invalid[idx]}, cuda::std::memory_order_relaxed); + } +}; + +template +rmm::device_uvector prefix_sum_valid_and_invalid_edges( + // The edgelist is segmented into 2 partitions (valid and invalid edges) + // and edges to be unrolled can be either in the valid or the invalid edge + // partition. raft::handle_t const& handle, - graph_view_t& graph_view, - vertex_t edge_type, + edge_t num_edges, edge_t num_invalid_edges, - decltype(allocate_dataframe_buffer>( - 0, rmm::cuda_stream_view{}))&& invalid_edges_buffer, - rmm::device_uvector&& edgelist_srcs, - rmm::device_uvector&& edgelist_dsts, - rmm::device_uvector&& num_triangles, - EdgeIterator incoming_vertex_pairs, // rename to 'transposed_edge_first' - EdgeTriangleCountIterator edge_triangle_count_pair_first) + vertex_t* invalid_dst, + raft::device_span edgelist_dsts) { - - auto num_edges = edgelist_dsts.size(); rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); - //raft::print_device_vector("edgelist_dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), - prefix_sum.begin() + num_invalid_edges, - [invalid_first = get_dataframe_buffer_begin(invalid_edges_buffer), - src_array_begin = edgelist_srcs.begin(), - dst_array_begin = edgelist_dsts.begin(), - num_edges] __device__(auto idx) { - auto src = thrust::get<0>(*(invalid_first + idx)); - auto dst = thrust::get<1>(*(invalid_first + idx)); - auto dst_array_end = dst_array_begin + num_edges; - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto itr_upper = thrust::upper_bound(thrust::seq, itr_lower, dst_array_end, dst); - auto dist = thrust::distance(itr_lower, itr_upper); - printf("\ndst = %d, and distance = %d, pos_init = %d, pos_end = %d\n", dst, dist, thrust::distance(dst_array_begin, itr_lower), thrust::distance(itr_lower, itr_upper)); - return dist; + prefix_sum.begin() + num_invalid_edges, + [num_edges, + invalid_dst, + edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { + auto itr_lower_valid = thrust::lower_bound(thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dst[idx]); + auto itr_upper_valid = thrust::upper_bound(thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dst[idx]); + auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); + + return dist_valid; }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + + return prefix_sum; - printf("\nbefore scan edgelist triangle\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - - raft::print_device_vector("prefix_sum", prefix_sum.data(), prefix_sum.size(), std::cout); +} + +template +edge_t remove_overcompensating_edges( + raft::handle_t const& handle, + edge_t num_valid_edges, + edge_t num_invalid_edges, + edge_t dataframe_buffer_size, + EdgeIterator&& potential_closing_or_incoming_edges, + EdgeIterator&& incoming_or_potential_closing_edges, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts + ) +{ + // To avoid over-compensating, check whether the 'potential_closing_edges' + // are within the invalid edges. If yes, the was already unrolled + auto edges_not_overcomp = thrust::remove_if(handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges) + dataframe_buffer_size, get_dataframe_buffer_begin(incoming_or_potential_closing_edges) + dataframe_buffer_size), + [ + num_invalid_edges, + num_valid_edges, + invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), + invalid_last = thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto e) { + auto potential_edge = thrust::get<0>(e); + auto transposed_potential_or_incoming_edge = thrust::make_tuple(thrust::get<1>(potential_edge), thrust::get<0>(potential_edge)); + auto itr = thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_potential_or_incoming_edge); //very important when using lower or upperbound on edges that do not exist. Always make sure to compare with the queried edges + assert(*itr == transposed_potential_or_incoming_edge); + auto dist = thrust::distance(invalid_first, itr); + if (*itr != transposed_potential_or_incoming_edge){ + return false; + } + return dist < num_invalid_edges; + }); + + auto dist = thrust::distance( + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), + edges_not_overcomp); + + // After pushing the non-existant edges to the second partition, + // remove them by resizing both vertex pair buffer + resize_dataframe_buffer(potential_closing_or_incoming_edges, dist, handle.get_stream()); + resize_dataframe_buffer(incoming_or_potential_closing_edges, dist, handle.get_stream()); + + return dist; +} + +template +void find_unroll_p_r_and_q_r_edges( + raft::handle_t const& handle, + graph_view_t& graph_view, + edge_t num_invalid_edges, + edge_t num_valid_edges, + rmm::device_uvector&& edgelist_srcs, // FIXME: Use device_span instead + rmm::device_uvector&& edgelist_dsts, + rmm::device_uvector&& num_triangles) +{ + auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( + handle, + edge_t{num_valid_edges}, + edge_t{num_invalid_edges}, // num_edges == num_invalid_edges + edgelist_dsts.begin() + num_valid_edges, + raft::device_span(edgelist_dsts.data(), num_valid_edges)); + + auto prefix_sum_invalid = prefix_sum_valid_and_invalid_edges( + handle, + edge_t{num_invalid_edges}, + edge_t{num_invalid_edges}, + edgelist_dsts.begin() + num_valid_edges, + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); + auto potential_closing_edges = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + prefix_sum_valid.back_element(handle.get_stream()) + prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); auto incoming_edges_to_r = allocate_dataframe_buffer>( - prefix_sum.back_element(handle.get_stream()), handle.get_stream()); + prefix_sum_valid.back_element(handle.get_stream()) + prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); + const bool const_is_edge_q_r = is_edge_q_r; thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_invalid_edges), - [incoming_vertex_pairs, - num_edges, - invalid_first_dst = std::get<1>(invalid_edges_buffer).begin(), - invalid_first_src = std::get<0>(invalid_edges_buffer).begin(), - src_array_begin = edgelist_srcs.begin(), - dst_array_begin = edgelist_dsts.begin(), - prefix_sum = prefix_sum.data(), + [num_valid_edges, + num_invalid_edges, + invalid_first_dst = edgelist_dsts.begin() + num_valid_edges, + invalid_first_src = edgelist_srcs.begin() + num_valid_edges, + src_array_begin_valid = edgelist_srcs.begin(), + dst_array_begin_valid = edgelist_dsts.begin(), + prefix_sum_valid = prefix_sum_valid.data(), + prefix_sum_invalid = prefix_sum_invalid.data(), potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r), - edge_type] __device__(auto idx) { + const_is_edge_q_r] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; - auto dst_array_end = dst_array_begin + num_edges; - - auto itr_lower = thrust::lower_bound(thrust::seq, dst_array_begin, dst_array_end, dst); - auto idx_lower = thrust::distance( - dst_array_begin, itr_lower); // Need a binary search to find the begining of the range + auto dst_array_end_valid = dst_array_begin_valid + num_valid_edges; - auto incoming_edges_to_r_first = - thrust::make_zip_iterator(src_array_begin + idx_lower, thrust::make_constant_iterator(dst)); + auto itr_lower_valid = thrust::lower_bound(thrust::seq, dst_array_begin_valid, dst_array_end_valid, dst); + auto idx_lower_valid = thrust::distance( + dst_array_begin_valid, itr_lower_valid); // Need a binary search to find the begining of the range + + auto invalid_end_dst = invalid_first_dst + num_invalid_edges; + // FIXME: In case of wrong results, investigate lower bound when dst is not part of one partition + auto itr_lower_invalid = thrust::lower_bound(thrust::seq, invalid_first_dst, invalid_end_dst, dst); + auto idx_lower_invalid = thrust::distance( + invalid_first_dst, itr_lower_invalid); // Need a binary search to find the begining of the range + + auto incoming_edges_to_r_first_valid = + thrust::make_zip_iterator(src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(dst)); thrust::copy(thrust::seq, - incoming_edges_to_r_first, - incoming_edges_to_r_first + (prefix_sum[idx + 1] - prefix_sum[idx]), - incoming_edges_to_r + prefix_sum[idx]); - - if (edge_type == 0) { - auto potential_closing_edges_first = thrust::make_zip_iterator( - src_array_begin + idx_lower, thrust::make_constant_iterator(src)); + incoming_edges_to_r_first_valid, + incoming_edges_to_r_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), + incoming_edges_to_r + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); + + auto incoming_edges_to_r_first_invalid = + thrust::make_zip_iterator(invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(dst)); + thrust::copy(thrust::seq, + incoming_edges_to_r_first_invalid, + incoming_edges_to_r_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), + // FIXME remove prefix_sum_valid[idx] as it is substracted + incoming_edges_to_r + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); + + if constexpr (const_is_edge_q_r) { + auto potential_closing_edges_first_valid = thrust::make_zip_iterator( + src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(src)); + thrust::copy(thrust::seq, + potential_closing_edges_first_valid, + potential_closing_edges_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), + potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); + + auto potential_closing_edges_first_invalid = thrust::make_zip_iterator( + invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(src)); thrust::copy(thrust::seq, - potential_closing_edges_first, - potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), - potential_closing_edges + prefix_sum[idx]); + potential_closing_edges_first_invalid, + potential_closing_edges_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), + potential_closing_edges + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); } else { - auto potential_closing_edges_first = thrust::make_zip_iterator( - thrust::make_constant_iterator(src), src_array_begin + idx_lower); + auto potential_closing_edges_first_valid = thrust::make_zip_iterator( + thrust::make_constant_iterator(src), src_array_begin_valid + idx_lower_valid); thrust::copy(thrust::seq, - potential_closing_edges_first, - potential_closing_edges_first + (prefix_sum[idx + 1] - prefix_sum[idx]), - potential_closing_edges + prefix_sum[idx]); + potential_closing_edges_first_valid, + potential_closing_edges_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), + potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); + + auto potential_closing_edges_first_invalid = thrust::make_zip_iterator( + thrust::make_constant_iterator(src), invalid_first_src + idx_lower_invalid); + thrust::copy(thrust::seq, + potential_closing_edges_first_invalid, + potential_closing_edges_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), + potential_closing_edges + prefix_sum_invalid[idx] +(prefix_sum_valid[idx + 1])); } + }); - - printf("\nincoming_edges_to_invalid_r\n"); - raft::print_device_vector("src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); - - raft::print_device_vector("src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); - - - - - - - - rmm::device_uvector edgelist_srcs_(0, handle.get_stream()); - rmm::device_uvector edgelist_dsts_(0, handle.get_stream()); - - std::tie(edgelist_srcs_, edgelist_dsts_, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); - auto edges_exist = graph_view.has_edge( handle, @@ -201,26 +288,19 @@ void find_unroll_p_r_and_q_r_edges( std::get<0>(potential_closing_edges).size()), raft::device_span(std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size())); - - raft::print_device_vector("exi", edges_exist.data(), edges_exist.size(), std::cout); - - - printf("\ngraph_view before checkign edges\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); auto edge_to_existance = thrust::make_zip_iterator( thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), edges_exist.begin()); - + auto has_edge_last = thrust::remove_if(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); + edge_to_existance, + edge_to_existance + edges_exist.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists == 0; + }); auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); @@ -229,141 +309,98 @@ void find_unroll_p_r_and_q_r_edges( resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); - printf("\npotential incoming edges before\n"); - raft::print_device_vector("valid_incoming_to_r: src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); - raft::print_device_vector("valid_incoming_to_r: dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); - raft::print_device_vector("potential_closing_e: src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); - raft::print_device_vector("potential_closing_e: dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); - - // FIXME: Extra processing because has_edge() seem to not be working - printf("\nnum_edges = %d\n", edgelist_srcs.size()); - auto has_edge_last_ = thrust::remove_if(handle.get_thrust_policy(), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges) + num_edge_exists, get_dataframe_buffer_begin(incoming_edges_to_r) + num_edge_exists), - [incoming_vertex_pairs, - num_edges = edgelist_srcs.size(), - incoming_vertex_pairs_last = incoming_vertex_pairs + edgelist_srcs.size()] __device__(auto e) { - //auto edge_exists = thrust::get<1>(e); - auto potential = thrust::get<0>(e); - auto transposed_potential = thrust::make_tuple(thrust::get<1>(potential), thrust::get<0>(potential)); - auto itr = thrust::find(thrust::seq, incoming_vertex_pairs, incoming_vertex_pairs_last, transposed_potential); - auto dist = thrust::distance(incoming_vertex_pairs, itr); - printf("\nsrc = %d, dst = %d, dist %d\n", thrust::get<0>(potential), thrust::get<1>(potential), dist); - return dist == num_edges; - }); - - - num_edge_exists = thrust::distance(thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), has_edge_last_); - resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); - resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); + edge_t num_edges_not_overcomp = remove_overcompensating_edges( + handle, + edge_t{num_valid_edges}, + edge_t{num_invalid_edges}, + edge_t{num_edge_exists}, + std::move(potential_closing_edges), + std::move(incoming_edges_to_r), + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()) + ); + + // Extra check for 'incoming_edges_to_r' + if constexpr (!is_edge_q_r) { + // Exchange the arguments (incoming_edges_to_r, num_edges_not_overcomp) order + // To also check if the 'incoming_edges_to_r' belong the the invalid_edgelist + num_edges_not_overcomp = remove_overcompensating_edges( + handle, + edge_t{num_valid_edges}, + edge_t{num_invalid_edges}, + edge_t{num_edges_not_overcomp}, + std::move(incoming_edges_to_r), + std::move(potential_closing_edges), + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()) + ); + } - printf("\npotential incoming edges after\n"); - raft::print_device_vector("valid_incoming_to_r: src", std::get<0>(incoming_edges_to_r).data(), std::get<0>(incoming_edges_to_r).size(), std::cout); - raft::print_device_vector("valid_incoming_to_r: dst", std::get<1>(incoming_edges_to_r).data(), std::get<1>(incoming_edges_to_r).size(), std::cout); - raft::print_device_vector("potential_closing_e: src", std::get<0>(potential_closing_edges).data(), std::get<0>(potential_closing_edges).size(), std::cout); - raft::print_device_vector("potential_closing_e: dst", std::get<1>(potential_closing_edges).data(), std::get<1>(potential_closing_edges).size(), std::cout); + // FIXME: Combine both 'thrust::for_each' + if constexpr (is_edge_q_r) { + thrust::for_each(handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), + [num_triangles = num_triangles.begin(), + num_valid_edges, + invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), + invalid_last = thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end()) + ] __device__(auto potential_or_incoming_e){ + auto potential_e = thrust::get<0>(potential_or_incoming_e); + auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); + auto transposed_invalid_edge = thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<1>(potential_e)); + auto itr = thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); + assert(*itr == transposed_invalid_edge); + auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; + + cuda::atomic_ref atomic_counter(num_triangles[dist]); + auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); + }); + } + else{ + thrust::for_each(handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), + [num_triangles = num_triangles.begin(), + num_valid_edges, + invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), + invalid_last = thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end()) + ] __device__(auto potential_or_incoming_e){ + auto potential_e = thrust::get<0>(potential_or_incoming_e); + auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); + auto transposed_invalid_edge = thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<0>(potential_e)); + auto itr = thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); + assert(*itr == transposed_invalid_edge); + auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; + + cuda::atomic_ref atomic_counter(num_triangles[dist]); + auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); + }); - printf("\nbefore unrolling\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); + } - auto incoming_vertex_pairs_last = incoming_vertex_pairs + num_edges; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + thrust::make_counting_iterator(num_edges_not_overcomp), + unroll_edge{ + edge_t{num_valid_edges}, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(potential_closing_edges), - incoming_vertex_pairs, - incoming_vertex_pairs_last}); - + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), + thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())}); + thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ + thrust::make_counting_iterator(num_edges_not_overcomp), + unroll_edge{ + edge_t{num_valid_edges}, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(incoming_edges_to_r), - incoming_vertex_pairs, - incoming_vertex_pairs_last}); - - printf("\nafter unrolling\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); - - // FIXME:: Remove invalid edges with triangle counts == 0 *************************************** - // because keeping them will cause over-compensation - - //raft::print_device_vector("incoming_vertex_pairs: src", thrust::get<0>(incoming_vertex_pairs.get_iterator_tuple()), edgelist_srcs.size(), std::cout); - //raft::print_device_vector("incoming_vertex_pairs: dst", thrust::get<1>(incoming_vertex_pairs.get_iterator_tuple()), edgelist_srcs.size(), std::cout); - - raft::print_device_vector("invalid_e_before: src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("invalid_e_before: dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - - auto has_invalid_edge_last = thrust::remove_if(handle.get_thrust_policy(), - get_dataframe_buffer_begin(invalid_edges_buffer), - get_dataframe_buffer_end(invalid_edges_buffer), - [edge_triangle_count_pair_first, - edgelist_srcs = edgelist_srcs.begin(), - edgelist_dsts = edgelist_dsts.begin(), - num_triangles = num_triangles.begin(), - incoming_vertex_pairs, - incoming_vertex_pairs_last = incoming_vertex_pairs + num_edges, - num_edges = edgelist_srcs.size()] __device__(auto invalid_e) { - //auto edge_exists = thrust::get<1>(e); - // Find its position in 'edges' - //auto itr = thrust::lower_bound(thrust::seq, thrust::get<0>(edge_triangle_count_pair_first), thrust::get<0>(edge_triangle_count_pair_first), invalid_e); - auto transposed_invalid_e = thrust::make_tuple(thrust::get<1>(invalid_e), thrust::get<0>(invalid_e)); - auto itr = thrust::lower_bound(thrust::seq, incoming_vertex_pairs, incoming_vertex_pairs_last, transposed_invalid_e); - - assert(*itr == invalid_e); - - auto idx = thrust::distance(incoming_vertex_pairs, itr); - printf("\ngoing over invalid_edges %d, %d and idx = %d\n", thrust::get<0>(transposed_invalid_e), thrust::get<1>(transposed_invalid_e), idx); - if (num_triangles[idx] == 0){ - printf("\nfound an invalid edge %d, %d with no triangle at idx = %d\n", thrust::get<0>(invalid_e), thrust::get<1>(invalid_e), idx); - return 1; - } else { - return 0; - } - }); - - auto num_invalid_edge_exists = thrust::distance(get_dataframe_buffer_begin(invalid_edges_buffer), has_invalid_edge_last); + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), + thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())}); - // After pushing the invalid edges with no triangle to the second partition, - // remove them by resizing both vertex pair buffer - printf("\nnumber of invalid edges with triangle(s) = %d\n", num_invalid_edge_exists); - resize_dataframe_buffer(invalid_edges_buffer, num_invalid_edge_exists, handle.get_stream()); - - - raft::print_device_vector("invalid_e_after: src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("invalid_e_after: dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - - - // Put edges with triangle count == 0 in the second partition - // FIXME: revisit all the 'stable_partition' and only used them - // when necessary otherwise simply call 'thrust::partition' - // Stable_parition is needed because we want to keep src and dst sorted - // so that we don't need to sort it again. - // FIXME: Create a rountine capturing L719:L763 as this block of code gets - // repeated - auto last_edge_with_triangles = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_edges, - [] __device__(auto edge_to_num_triangles) { - return thrust::get<1>(edge_to_num_triangles) > 0; - }); - - auto last_edge_with_triangles_idx = - thrust::distance(edge_triangle_count_pair_first, last_edge_with_triangles); - // resize the 'edgelist_srcs' and 'edgelsit_dst' - edgelist_srcs.resize(last_edge_with_triangles_idx, handle.get_stream()); - edgelist_dsts.resize(last_edge_with_triangles_idx, handle.get_stream()); - num_triangles.resize(last_edge_with_triangles_idx, handle.get_stream()); } namespace { @@ -397,12 +434,14 @@ struct extract_low_to_high_degree_edges_t { } }; -template +template struct generate_p_r_q_r { - const edge_t edge_first_src_or_dst{}; raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; - EdgeIterator transposed_edge_first{}; + raft::device_span invalid_srcs{}; + raft::device_span invalid_dsts{}; + + __device__ thrust::tuple operator()(edge_t i) const { @@ -410,12 +449,14 @@ struct generate_p_r_q_r { thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - if (edge_first_src_or_dst == 0) { - return thrust::make_tuple(thrust::get<0>(*(transposed_edge_first + idx)), + if constexpr (generate_p_r) { + return thrust::make_tuple(invalid_srcs[idx], intersection_indices[i]); + } else { - return thrust::make_tuple(thrust::get<1>(*(transposed_edge_first + idx)), + return thrust::make_tuple(invalid_dsts[idx], intersection_indices[i]); + } } }; @@ -481,7 +522,7 @@ std::tuple, rmm::device_uvector> k_truss } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - /* + { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -499,7 +540,7 @@ std::tuple, rmm::device_uvector> k_truss k_core(handle, cur_graph_view, std::optional>{std::nullopt}, - size_t{k + 1}, + size_t{k+1}, std::make_optional(k_core_degree_type_t::OUT), // Seems like the below argument is required. passing a std::nullopt // create a compiler error @@ -539,29 +580,12 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } - */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; - // FIXME: REmove this********************************************************************************** - rmm::device_uvector srcs_(0, handle.get_stream()); - rmm::device_uvector dsts_(0, handle.get_stream()); - - std::tie(srcs_, dsts_, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); - - printf("\nOriginal graph\n"); - raft::print_device_vector("srcs", srcs_.data(), srcs_.size(), std::cout); - raft::print_device_vector("dsts", dsts_.data(), dsts_.size(), std::cout); - //***************************************************************************************************** - auto vertex_partition_range_lasts = renumber_map ? std::make_optional>(cur_graph_view.vertex_partition_range_lasts()) @@ -643,22 +667,12 @@ std::tuple, rmm::device_uvector> k_truss auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - - auto edge_triangle_count_pair_first = + auto transposed_edge_triangle_count_pair_first = thrust::make_zip_iterator(transposed_edge_first, num_triangles.begin()); - - printf("\nreduced edges from original\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag - cugraph::edge_bucket_t edges_with_triangles(handle); - cugraph::edge_property_t edge_mask(handle, cur_graph_view); - edge_t num_valid_edges = edgelist_srcs.size(); edge_t num_invalid_edges{0}; size_t num_edges_with_triangles{0}; @@ -667,197 +681,87 @@ std::tuple, rmm::device_uvector> k_truss // for during the unroling phase. auto edges_with_triangle_last = thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), + transposed_edge_triangle_count_pair_first, + transposed_edge_triangle_count_pair_first + num_triangles.size(), [k] __device__(auto e) { auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); - printf("\nbefore removing edges with no triangles\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); + /* + //FIXME: Getting different results than the above + auto edges_with_triangle_last = + thrust::remove_if(handle.get_thrust_policy(), + transposed_edge_triangle_count_pair_first, + transposed_edge_triangle_count_pair_first + num_triangles.size(), + [] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles == 0; + }); + */ num_edges_with_triangles = static_cast( - thrust::distance(edge_triangle_count_pair_first, edges_with_triangle_last)); - - edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); - edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); - num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - - printf("\nafter removing edges with no triangles\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); - - // 'invalid_edge_first' marks the beginning of the edges to be removed - auto invalid_edge_first = - thrust::stable_partition(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles >= k - 2; - }); - - num_invalid_edges = static_cast(thrust::distance( - invalid_edge_first, edge_triangle_count_pair_first + edgelist_srcs.size())); + thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); - if (num_invalid_edges == 0) { break; } - - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - - // copy invalid edges - auto invalid_edges_buffer = allocate_dataframe_buffer>( - num_invalid_edges, handle.get_stream()); - - thrust::copy(handle.get_thrust_policy(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, - edgelist_dsts.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_srcs.begin() + edgelist_srcs.size(), - edgelist_dsts.begin() + edgelist_srcs.size()), - get_dataframe_buffer_begin(invalid_edges_buffer)); - - // resize the 'edgelist_srcs' and 'edgelist_dst' - // edgelist_srcs.resize(num_valid_edges, handle.get_stream()); - // edgelist_dsts.resize(num_valid_edges, handle.get_stream()); - // num_triangles.resize(num_valid_edges, handle.get_stream()); - - - thrust::sort_by_key( - handle.get_thrust_policy(), transposed_edge_first, transposed_edge_first + edgelist_srcs.size(), num_triangles.begin()); - - printf("\nnumber of invalid edges = %d\n", num_invalid_edges); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\n"); - - printf("\ninit invalid edges buffer\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - - // case 1: unroll (q, r) - // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and - // create the pair (p, q) - cugraph::find_unroll_p_r_and_q_r_edges(handle, - cur_graph_view, - 0, // 0 for (q, r) and 1 for (p, r) edges - num_invalid_edges, - std::move(invalid_edges_buffer), - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::move(num_triangles), - transposed_edge_first, - edge_triangle_count_pair_first); - - // case 2: unroll (p, r) - cugraph::find_unroll_p_r_and_q_r_edges(handle, - cur_graph_view, - 1, // 0 for (q, r) and 1 for (p, r) edges - num_invalid_edges, - std::move(invalid_edges_buffer), - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::move(num_triangles), - transposed_edge_first, - edge_triangle_count_pair_first); - - //cugraph::edge_bucket_t edges_with_triangles(handle); cugraph::edge_property_t edge_mask(handle, cur_graph_view); - cugraph::fill_edge_property(handle, cur_graph_view, false, edge_mask); - - //edges_with_triangles.resize(edgelist_srcs.size()); + // Set edge property to 'True' for all edges then mask out invalid edges which can be significantly + // smaller than the valid edges + cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); - edges_with_triangles.insert( - edgelist_srcs.begin(), edgelist_srcs.end(), edgelist_dsts.begin()); - - printf("\nthe size of the e_bucket = %d\n", edges_with_triangles.size()); + thrust::sort( + handle.get_thrust_policy(), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, edgelist_dsts.begin() + num_edges_with_triangles), + thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); - printf("\ncalling transform_e\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + cugraph::edge_bucket_t edges_with_no_triangle(handle); + edges_with_no_triangle.insert( + edgelist_srcs.begin() + num_edges_with_triangles, edgelist_srcs.end(),edgelist_dsts.begin() + num_edges_with_triangles); + // FIXME: Cannot modify an edgemask that is still attached. + // This can lead to race conditions cugraph::transform_e( handle, cur_graph_view, - edges_with_triangles, + edges_with_no_triangle, cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - printf("\n transform_e src = %d, dst = %d\n", src, dst); - return true; + return false; }, edge_mask.mutable_view(), - false); + false); // FIXME: Remove expensive check. This is only here for debugging purposes ********************************** cur_graph_view.attach_edge_mask(edge_mask.view()); - /* - std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, renumber_map) = - create_graph_from_edgelist( - handle, - std::nullopt, - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::nullopt, - std::nullopt, - std::nullopt, - cugraph::graph_properties_t{true, graph_view.is_multigraph()}, - false); - - cur_graph_view = (*modified_graph).view(); - - std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); - */ - - - rmm::device_uvector edgelist_srcs_(0, handle.get_stream()); - rmm::device_uvector edgelist_dsts_(0, handle.get_stream()); - - std::tie(edgelist_srcs_, edgelist_dsts_, std::ignore, std::ignore) = decompress_to_edgelist( - handle, - cur_graph_view, - std::optional>{std::nullopt}, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); - - printf("\n edgelist in the device arrray\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - - printf("\n edgelist after tranform_e\n"); - raft::print_device_vector("srcs", edgelist_srcs_.data(), edgelist_srcs_.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts_.data(), edgelist_dsts_.size(), std::cout); - - - //raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); + edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); + num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + + thrust::sort_by_key( + handle.get_thrust_policy(), transposed_edge_first, transposed_edge_first + edgelist_srcs.size(), num_triangles.begin()); + // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be removed + // 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end of the edges to be removed + // 'edge_triangle_count_pair_first' marks the begining of the valid edges + auto invalid_transposed_edge_triangle_count_first = + thrust::stable_partition(handle.get_thrust_policy(), + transposed_edge_triangle_count_pair_first, + transposed_edge_triangle_count_pair_first + num_triangles.size(), + [k] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles >= k - 2; + }); + + num_invalid_edges = static_cast(thrust::distance( + invalid_transposed_edge_triangle_count_first, transposed_edge_triangle_count_pair_first + edgelist_srcs.size())); + if (num_invalid_edges == 0) { break; } - // FIXME: Check the edgelist left in 'cur_graph_view' ************************* + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; // case 3. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) - // FIXME: check if 'invalid_edge_first' is necessery as I operate on 'vertex_pair_buffer' + // FIXME: check if 'invalid_transposed_edge_triangle_count_first' is necessery as I operate on 'vertex_pair_buffer' // which contains the ordering with the number of triangles. // FIXME: debug this stage. There are edges that have been removed that are still found in nbr // intersection @@ -865,45 +769,59 @@ std::tuple, rmm::device_uvector> k_truss detail::nbr_intersection(handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - get_dataframe_buffer_begin(invalid_edges_buffer), - get_dataframe_buffer_end(invalid_edges_buffer), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, edgelist_dsts.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end()), std::array{true, true}, do_expensive_check); + rmm::device_uvector num_triangles_invalid(num_invalid_edges, handle.get_stream()); + + // Update the number of triangles of each (p, q) edges by looking at their intersection + // size + thrust::adjacent_difference(handle.get_thrust_policy(), + intersection_offsets.begin() + 1, + intersection_offsets.end(), + num_triangles_invalid.begin()); + + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_invalid_edges), + unroll_edge_invalid{ + edge_t{num_valid_edges}, + raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles_invalid.data(), num_triangles_invalid.size())}); + size_t accumulate_pair_size = intersection_indices.size(); + // FIXME: Find a way to not have to maintain a dataframe_buffer auto vertex_pair_buffer_p_r_edge_p_q = allocate_dataframe_buffer>(accumulate_pair_size, handle.get_stream()); - + thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r_q_r{ - 0, + generate_p_r_q_r{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer)}); - - raft::print_device_vector("vertex_pair_buffer_p_r_edge_p_q: src", std::get<0>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); - raft::print_device_vector("vertex_pair_buffer_p_r_edge_p_q: dst", std::get<1>(vertex_pair_buffer_p_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_p_r_edge_p_q).size(), std::cout); + raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)}); - auto edge_last = transposed_edge_first + edgelist_srcs.size(); auto num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ + edge_t{num_valid_edges}, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), transposed_edge_first, - edge_last, + transposed_edge_first + num_valid_edges, + transposed_edge_first + edgelist_srcs.size() }); - + auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>(accumulate_pair_size, handle.get_stream()); @@ -911,99 +829,62 @@ std::tuple, rmm::device_uvector> k_truss thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q) + accumulate_pair_size, - generate_p_r_q_r{ - 1, + get_dataframe_buffer_end(vertex_pair_buffer_q_r_edge_p_q), + generate_p_r_q_r{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), - get_dataframe_buffer_begin(invalid_edges_buffer) // FIXME: verify this is accurate - }); + raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)}); - raft::print_device_vector("vertex_pair_buffer_q_r_edge_p_q: src", std::get<0>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<0>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); - raft::print_device_vector("vertex_pair_buffer_q_r_edge_p_q: dst", std::get<1>(vertex_pair_buffer_q_r_edge_p_q).data(), std::get<1>(vertex_pair_buffer_q_r_edge_p_q).size(), std::cout); thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ + edge_t{num_valid_edges}, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), transposed_edge_first, - edge_last, + transposed_edge_first + num_valid_edges, + transposed_edge_first + edgelist_srcs.size() }); - - printf("\nnum_edges = %d\n", edgelist_srcs.size()); - - printf("\nBefore removing invalid edges with no triangles\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\nthe number of edges = %d\n", edgelist_srcs.size()); - - // sort the invalid edgelist for a binary search - printf("\ninvalid edges buffer before sorting\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - - thrust::sort( - handle.get_thrust_policy(), get_dataframe_buffer_begin(invalid_edges_buffer), get_dataframe_buffer_end(invalid_edges_buffer)); - - printf("\ninvalid edges buffer after sorting\n"); - raft::print_device_vector("src", std::get<0>(invalid_edges_buffer).data(), std::get<0>(invalid_edges_buffer).size(), std::cout); - raft::print_device_vector("dst", std::get<1>(invalid_edges_buffer).data(), std::get<1>(invalid_edges_buffer).size(), std::cout); - - auto has_edge_last_ = thrust::remove_if(handle.get_thrust_policy(), - edge_triangle_count_pair_first, - edge_triangle_count_pair_first + edgelist_srcs.size(), - [invalid_edges_buffer_first = get_dataframe_buffer_begin(invalid_edges_buffer), - invalid_edges_buffer_last = get_dataframe_buffer_end(invalid_edges_buffer), - num_invalid_edges = size_dataframe_buffer(invalid_edges_buffer), - num_edges = edgelist_srcs.size()] __device__(auto e_triangle_count) { - //auto edge_exists = thrust::get<1>(e); - //auto potential = thrust::get<0>(e); - //auto transposed_potential = thrust::make_tuple(thrust::get<1>(potential), thrust::get<0>(potential)); - auto transposed_edge = thrust::get<0>(e_triangle_count); - auto edge = thrust::make_tuple(thrust::get<1>(transposed_edge), thrust::get<0>(transposed_edge)); - - auto itr = thrust::find(thrust::seq, invalid_edges_buffer_first, invalid_edges_buffer_last, edge); - auto dist = thrust::distance(invalid_edges_buffer_first, itr); - printf("\nsrc = %d, dst = %d, dist %d\n", thrust::get<0>(edge), thrust::get<1>(edge), dist); - return dist < num_invalid_edges; - }); - - - auto dist_ = thrust::distance(edge_triangle_count_pair_first, has_edge_last_); - - - edgelist_srcs.resize(dist_, handle.get_stream()); - edgelist_dsts.resize(dist_, handle.get_stream()); - num_triangles.resize(dist_, handle.get_stream()); - - printf("\nAfter removing invalid edges with no triangles\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\nthe number of edges = %d\n", edgelist_srcs.size()); + // case 1: unroll (q, r) + // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and + // create the pair (p, q) + cugraph::find_unroll_p_r_and_q_r_edges(handle, + cur_graph_view, + num_invalid_edges, + num_valid_edges, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(num_triangles)); - - - - - - + // case 2: unroll (p, r) + cugraph::find_unroll_p_r_and_q_r_edges(handle, + cur_graph_view, + num_invalid_edges, + num_valid_edges, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(num_triangles)); + cur_graph_view.clear_edge_mask(); - - edges_with_triangles.clear(); //masking not working in a loop. + edges_with_no_triangle.clear(); edge_mask.clear(handle); // masking not working in a loop. + } printf("\n*********final results*********\n"); - raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + //raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + //raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + //raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); printf("\nthe number of edges = %d\n", edgelist_srcs.size()); return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 02fce9b527a..99f0c068a19 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -113,8 +113,9 @@ INSTANTIATE_TEST_SUITE_P(file_test, Tests_KTruss_File, ::testing::Combine( // enable correctness checks - ::testing::Values(KTruss_Usecase{5}), + ::testing::Values(KTruss_Usecase{4}), ::testing::Values(cugraph::test::File_Usecase( "/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx")))); +///home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx CUGRAPH_TEST_PROGRAM_MAIN() From e517abeaab9063f215e5b49a2cf9ae9612c48d16 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 11 Mar 2024 14:44:58 -0700 Subject: [PATCH 071/155] fix style --- cpp/src/community/k_truss_impl.cuh | 599 ++++++++++-------- .../structure/edge_triangle_count_impl.cuh | 7 +- cpp/tests/community/k_truss_test.cpp | 12 +- 3 files changed, 328 insertions(+), 290 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index d1128b8e96b..8972ad20e6a 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, NVIDIA CORPORATION. + * Copyright (c) 2020-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. @@ -16,14 +16,6 @@ #pragma once // FIXME: remove all unused imports -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -44,6 +36,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + namespace cugraph { template @@ -63,15 +63,17 @@ struct unroll_edge { // Find its position in either partition of the transposed edgelist // An edge can be in found in either of the two partitions (valid or invalid) - auto itr = thrust::lower_bound(thrust::seq, transposed_valid_edge_last, transposed_invalid_edge_last, pair); + auto itr = thrust::lower_bound( + thrust::seq, transposed_valid_edge_last, transposed_invalid_edge_last, pair); auto idx = thrust::distance(transposed_valid_edge_last, itr) + num_valid_edges; - if (*itr != pair){ + if (*itr != pair) { // The edge must be in the first boundary - itr = thrust::lower_bound(thrust::seq, transposed_valid_edge_first, transposed_valid_edge_last, pair); + itr = thrust::lower_bound( + thrust::seq, transposed_valid_edge_first, transposed_valid_edge_last, pair); idx = thrust::distance(transposed_valid_edge_first, itr); } - + assert(*itr == pair); cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); @@ -86,9 +88,10 @@ struct unroll_edge_invalid { __device__ thrust::tuple operator()(edge_t idx) const { - - cuda::atomic_ref atomic_counter(num_triangles[idx + num_valid_edges]); - auto r = atomic_counter.fetch_sub(edge_t{num_triangles_invalid[idx]}, cuda::std::memory_order_relaxed); + cuda::atomic_ref atomic_counter( + num_triangles[idx + num_valid_edges]); + auto r = + atomic_counter.fetch_sub(edge_t{num_triangles_invalid[idx]}, cuda::std::memory_order_relaxed); } }; @@ -108,13 +111,13 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), - prefix_sum.begin() + num_invalid_edges, - [num_edges, - invalid_dst, - edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { - auto itr_lower_valid = thrust::lower_bound(thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dst[idx]); - auto itr_upper_valid = thrust::upper_bound(thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dst[idx]); - auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); + prefix_sum.begin() + num_invalid_edges, + [num_edges, invalid_dst, edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { + auto itr_lower_valid = thrust::lower_bound( + thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dst[idx]); + auto itr_upper_valid = thrust::upper_bound( + thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dst[idx]); + auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); return dist_valid; }); @@ -122,45 +125,53 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); return prefix_sum; - } - + template -edge_t remove_overcompensating_edges( - raft::handle_t const& handle, - edge_t num_valid_edges, - edge_t num_invalid_edges, - edge_t dataframe_buffer_size, - EdgeIterator&& potential_closing_or_incoming_edges, - EdgeIterator&& incoming_or_potential_closing_edges, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts - ) +edge_t remove_overcompensating_edges(raft::handle_t const& handle, + edge_t num_valid_edges, + edge_t num_invalid_edges, + edge_t dataframe_buffer_size, + EdgeIterator&& potential_closing_or_incoming_edges, + EdgeIterator&& incoming_or_potential_closing_edges, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts) { // To avoid over-compensating, check whether the 'potential_closing_edges' // are within the invalid edges. If yes, the was already unrolled - auto edges_not_overcomp = thrust::remove_if(handle.get_thrust_policy(), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges) + dataframe_buffer_size, get_dataframe_buffer_begin(incoming_or_potential_closing_edges) + dataframe_buffer_size), - [ - num_invalid_edges, - num_valid_edges, - invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), - invalid_last = thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto e) { - auto potential_edge = thrust::get<0>(e); - auto transposed_potential_or_incoming_edge = thrust::make_tuple(thrust::get<1>(potential_edge), thrust::get<0>(potential_edge)); - auto itr = thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_potential_or_incoming_edge); //very important when using lower or upperbound on edges that do not exist. Always make sure to compare with the queried edges - assert(*itr == transposed_potential_or_incoming_edge); - auto dist = thrust::distance(invalid_first, itr); - if (*itr != transposed_potential_or_incoming_edge){ - return false; - } - return dist < num_invalid_edges; - }); + auto edges_not_overcomp = thrust::remove_if( + handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), + get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), + thrust::make_zip_iterator( + get_dataframe_buffer_begin(potential_closing_or_incoming_edges) + dataframe_buffer_size, + get_dataframe_buffer_begin(incoming_or_potential_closing_edges) + dataframe_buffer_size), + [num_invalid_edges, + num_valid_edges, + invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, + edgelist_srcs.begin() + num_valid_edges), + invalid_last = + thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto e) { + auto potential_edge = thrust::get<0>(e); + auto transposed_potential_or_incoming_edge = + thrust::make_tuple(thrust::get<1>(potential_edge), thrust::get<0>(potential_edge)); + auto itr = thrust::lower_bound( + thrust::seq, + invalid_first, + invalid_last, + transposed_potential_or_incoming_edge); // very important when using lower or upperbound on + // edges that do not exist. Always make sure to + // compare with the queried edges + assert(*itr == transposed_potential_or_incoming_edge); + auto dist = thrust::distance(invalid_first, itr); + if (*itr != transposed_potential_or_incoming_edge) { return false; } + return dist < num_invalid_edges; + }); auto dist = thrust::distance( - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), - edges_not_overcomp); + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), + get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), + edges_not_overcomp); // After pushing the non-existant edges to the second partition, // remove them by resizing both vertex pair buffer @@ -170,23 +181,20 @@ edge_t remove_overcompensating_edges( return dist; } -template +template void find_unroll_p_r_and_q_r_edges( raft::handle_t const& handle, graph_view_t& graph_view, edge_t num_invalid_edges, edge_t num_valid_edges, - rmm::device_uvector&& edgelist_srcs, // FIXME: Use device_span instead + rmm::device_uvector&& edgelist_srcs, // FIXME: Use device_span instead rmm::device_uvector&& edgelist_dsts, rmm::device_uvector&& num_triangles) { - auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( + auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( handle, edge_t{num_valid_edges}, - edge_t{num_invalid_edges}, // num_edges == num_invalid_edges + edge_t{num_invalid_edges}, // num_edges == num_invalid_edges edgelist_dsts.begin() + num_valid_edges, raft::device_span(edgelist_dsts.data(), num_valid_edges)); @@ -197,12 +205,15 @@ void find_unroll_p_r_and_q_r_edges( edgelist_dsts.begin() + num_valid_edges, raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); - auto potential_closing_edges = allocate_dataframe_buffer>( - prefix_sum_valid.back_element(handle.get_stream()) + prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); + prefix_sum_valid.back_element(handle.get_stream()) + + prefix_sum_invalid.back_element(handle.get_stream()), + handle.get_stream()); auto incoming_edges_to_r = allocate_dataframe_buffer>( - prefix_sum_valid.back_element(handle.get_stream()) + prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); + prefix_sum_valid.back_element(handle.get_stream()) + + prefix_sum_invalid.back_element(handle.get_stream()), + handle.get_stream()); const bool const_is_edge_q_r = is_edge_q_r; thrust::for_each( @@ -220,66 +231,77 @@ void find_unroll_p_r_and_q_r_edges( potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r), const_is_edge_q_r] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; + auto src = invalid_first_src[idx]; + auto dst = invalid_first_dst[idx]; auto dst_array_end_valid = dst_array_begin_valid + num_valid_edges; - auto itr_lower_valid = thrust::lower_bound(thrust::seq, dst_array_begin_valid, dst_array_end_valid, dst); + auto itr_lower_valid = + thrust::lower_bound(thrust::seq, dst_array_begin_valid, dst_array_end_valid, dst); auto idx_lower_valid = thrust::distance( - dst_array_begin_valid, itr_lower_valid); // Need a binary search to find the begining of the range - + dst_array_begin_valid, + itr_lower_valid); // Need a binary search to find the begining of the range + auto invalid_end_dst = invalid_first_dst + num_invalid_edges; - // FIXME: In case of wrong results, investigate lower bound when dst is not part of one partition - auto itr_lower_invalid = thrust::lower_bound(thrust::seq, invalid_first_dst, invalid_end_dst, dst); + // FIXME: In case of wrong results, investigate lower bound when dst is not part of one + // partition + auto itr_lower_invalid = + thrust::lower_bound(thrust::seq, invalid_first_dst, invalid_end_dst, dst); auto idx_lower_invalid = thrust::distance( - invalid_first_dst, itr_lower_invalid); // Need a binary search to find the begining of the range - - auto incoming_edges_to_r_first_valid = - thrust::make_zip_iterator(src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(dst)); - thrust::copy(thrust::seq, - incoming_edges_to_r_first_valid, - incoming_edges_to_r_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), - incoming_edges_to_r + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); - - auto incoming_edges_to_r_first_invalid = - thrust::make_zip_iterator(invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(dst)); - thrust::copy(thrust::seq, - incoming_edges_to_r_first_invalid, - incoming_edges_to_r_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), - // FIXME remove prefix_sum_valid[idx] as it is substracted - incoming_edges_to_r + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); + invalid_first_dst, + itr_lower_invalid); // Need a binary search to find the begining of the range + + auto incoming_edges_to_r_first_valid = thrust::make_zip_iterator( + src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(dst)); + thrust::copy( + thrust::seq, + incoming_edges_to_r_first_valid, + incoming_edges_to_r_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), + incoming_edges_to_r + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); + + auto incoming_edges_to_r_first_invalid = thrust::make_zip_iterator( + invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(dst)); + thrust::copy( + thrust::seq, + incoming_edges_to_r_first_invalid, + incoming_edges_to_r_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), + // FIXME remove prefix_sum_valid[idx] as it is substracted + incoming_edges_to_r + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); if constexpr (const_is_edge_q_r) { auto potential_closing_edges_first_valid = thrust::make_zip_iterator( src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(src)); - thrust::copy(thrust::seq, - potential_closing_edges_first_valid, - potential_closing_edges_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), - potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); - + thrust::copy( + thrust::seq, + potential_closing_edges_first_valid, + potential_closing_edges_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), + potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); + auto potential_closing_edges_first_invalid = thrust::make_zip_iterator( - invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(src)); + invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(src)); thrust::copy(thrust::seq, potential_closing_edges_first_invalid, - potential_closing_edges_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), + potential_closing_edges_first_invalid + + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), potential_closing_edges + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); } else { auto potential_closing_edges_first_valid = thrust::make_zip_iterator( thrust::make_constant_iterator(src), src_array_begin_valid + idx_lower_valid); - thrust::copy(thrust::seq, - potential_closing_edges_first_valid, - potential_closing_edges_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), - potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); - + thrust::copy( + thrust::seq, + potential_closing_edges_first_valid, + potential_closing_edges_first_valid + (prefix_sum_valid[idx + 1] - prefix_sum_valid[idx]), + potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); + auto potential_closing_edges_first_invalid = thrust::make_zip_iterator( thrust::make_constant_iterator(src), invalid_first_src + idx_lower_invalid); - thrust::copy(thrust::seq, - potential_closing_edges_first_invalid, - potential_closing_edges_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), - potential_closing_edges + prefix_sum_invalid[idx] +(prefix_sum_valid[idx + 1])); + thrust::copy( + thrust::seq, + potential_closing_edges_first_invalid, + potential_closing_edges_first_invalid + + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), + potential_closing_edges + prefix_sum_invalid[idx] + (prefix_sum_valid[idx + 1])); } - }); auto edges_exist = graph_view.has_edge( @@ -293,14 +315,14 @@ void find_unroll_p_r_and_q_r_edges( thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), edges_exist.begin()); - + auto has_edge_last = thrust::remove_if(handle.get_thrust_policy(), - edge_to_existance, - edge_to_existance + edges_exist.size(), - [] __device__(auto e) { - auto edge_exists = thrust::get<1>(e); - return edge_exists == 0; - }); + edge_to_existance, + edge_to_existance + edges_exist.size(), + [] __device__(auto e) { + auto edge_exists = thrust::get<1>(e); + return edge_exists == 0; + }); auto num_edge_exists = thrust::distance(edge_to_existance, has_edge_last); @@ -309,98 +331,117 @@ void find_unroll_p_r_and_q_r_edges( resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); - edge_t num_edges_not_overcomp = remove_overcompensating_edges( - handle, - edge_t{num_valid_edges}, - edge_t{num_invalid_edges}, - edge_t{num_edge_exists}, - std::move(potential_closing_edges), - std::move(incoming_edges_to_r), - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()) - ); - - // Extra check for 'incoming_edges_to_r' - if constexpr (!is_edge_q_r) { - // Exchange the arguments (incoming_edges_to_r, num_edges_not_overcomp) order - // To also check if the 'incoming_edges_to_r' belong the the invalid_edgelist - num_edges_not_overcomp = remove_overcompensating_edges( + edge_t num_edges_not_overcomp = + remove_overcompensating_edges( handle, edge_t{num_valid_edges}, edge_t{num_invalid_edges}, - edge_t{num_edges_not_overcomp}, - std::move(incoming_edges_to_r), + edge_t{num_edge_exists}, std::move(potential_closing_edges), + std::move(incoming_edges_to_r), raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()) - ); + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + + // Extra check for 'incoming_edges_to_r' + if constexpr (!is_edge_q_r) { + // Exchange the arguments (incoming_edges_to_r, num_edges_not_overcomp) order + // To also check if the 'incoming_edges_to_r' belong the the invalid_edgelist + num_edges_not_overcomp = + remove_overcompensating_edges( + handle, + edge_t{num_valid_edges}, + edge_t{num_invalid_edges}, + edge_t{num_edges_not_overcomp}, + std::move(incoming_edges_to_r), + std::move(potential_closing_edges), + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); } // FIXME: Combine both 'thrust::for_each' if constexpr (is_edge_q_r) { - thrust::for_each(handle.get_thrust_policy(), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), - [num_triangles = num_triangles.begin(), - num_valid_edges, - invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), - invalid_last = thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end()) - ] __device__(auto potential_or_incoming_e){ - auto potential_e = thrust::get<0>(potential_or_incoming_e); - auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); - auto transposed_invalid_edge = thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<1>(potential_e)); - auto itr = thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); - assert(*itr == transposed_invalid_edge); - auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; - - cuda::atomic_ref atomic_counter(num_triangles[dist]); - auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); - }); - } - else{ - thrust::for_each(handle.get_thrust_policy(), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), - [num_triangles = num_triangles.begin(), - num_valid_edges, - invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), - invalid_last = thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end()) - ] __device__(auto potential_or_incoming_e){ - auto potential_e = thrust::get<0>(potential_or_incoming_e); - auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); - auto transposed_invalid_edge = thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<0>(potential_e)); - auto itr = thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); - assert(*itr == transposed_invalid_edge); - auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; - - cuda::atomic_ref atomic_counter(num_triangles[dist]); - auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); - }); - + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), + get_dataframe_buffer_begin(incoming_edges_to_r)), + thrust::make_zip_iterator( + get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, + get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), + [num_triangles = num_triangles.begin(), + num_valid_edges, + invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, + edgelist_srcs.begin() + num_valid_edges), + invalid_last = thrust::make_zip_iterator( + edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto potential_or_incoming_e) { + auto potential_e = thrust::get<0>(potential_or_incoming_e); + auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); + auto transposed_invalid_edge = + thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<1>(potential_e)); + auto itr = + thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); + assert(*itr == transposed_invalid_edge); + auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; + + cuda::atomic_ref atomic_counter(num_triangles[dist]); + auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); + }); + } else { + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), + get_dataframe_buffer_begin(incoming_edges_to_r)), + thrust::make_zip_iterator( + get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, + get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), + [num_triangles = num_triangles.begin(), + num_valid_edges, + invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, + edgelist_srcs.begin() + num_valid_edges), + invalid_last = thrust::make_zip_iterator( + edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto potential_or_incoming_e) { + auto potential_e = thrust::get<0>(potential_or_incoming_e); + auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); + auto transposed_invalid_edge = + thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<0>(potential_e)); + auto itr = + thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); + assert(*itr == transposed_invalid_edge); + auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; + + cuda::atomic_ref atomic_counter(num_triangles[dist]); + auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); + }); } - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edges_not_overcomp), - unroll_edge{ - edge_t{num_valid_edges}, - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(potential_closing_edges), - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), - thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())}); - - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edges_not_overcomp), - unroll_edge{ - edge_t{num_valid_edges}, - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(incoming_edges_to_r), - thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), - thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, edgelist_srcs.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())}); + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edges_not_overcomp), + unroll_edge{ + edge_t{num_valid_edges}, + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(potential_closing_edges), + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), + thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, + edgelist_srcs.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())}); + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edges_not_overcomp), + unroll_edge{ + edge_t{num_valid_edges}, + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(incoming_edges_to_r), + thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), + thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, + edgelist_srcs.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())}); } namespace { @@ -441,22 +482,17 @@ struct generate_p_r_q_r { raft::device_span invalid_srcs{}; raft::device_span invalid_dsts{}; - - __device__ thrust::tuple operator()(edge_t i) const { auto itr = thrust::upper_bound( thrust::seq, intersection_offsets.begin() + 1, intersection_offsets.end(), i); auto idx = thrust::distance(intersection_offsets.begin() + 1, itr); - if constexpr (generate_p_r) { - return thrust::make_tuple(invalid_srcs[idx], - intersection_indices[i]); - + if constexpr (generate_p_r) { + return thrust::make_tuple(invalid_srcs[idx], intersection_indices[i]); + } else { - return thrust::make_tuple(invalid_dsts[idx], - intersection_indices[i]); - + return thrust::make_tuple(invalid_dsts[idx], intersection_indices[i]); } } }; @@ -540,7 +576,7 @@ std::tuple, rmm::device_uvector> k_truss k_core(handle, cur_graph_view, std::optional>{std::nullopt}, - size_t{k+1}, + size_t{k + 1}, std::make_optional(k_core_degree_type_t::OUT), // Seems like the below argument is required. passing a std::nullopt // create a compiler error @@ -653,7 +689,7 @@ std::tuple, rmm::device_uvector> k_truss std::optional>{std::nullopt}, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - + auto num_triangles = edge_triangle_count( handle, cur_graph_view, @@ -661,12 +697,12 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); // FIXME 'edge_triangle_count' sorts the edges by 'src' but 'k-truss' needs - // the edges to be sorted with 'dst' as key so we need to sort the edges + // the edges to be sorted with 'dst' as key so we need to sort the edges // again. Should 'edge_triangle_count' be implemented edges sorted by 'dst' // instead to avoid resorting? auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - + auto transposed_edge_triangle_count_pair_first = thrust::make_zip_iterator(transposed_edge_first, num_triangles.begin()); @@ -687,7 +723,7 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); - + /* //FIXME: Getting different results than the above auto edges_with_triangle_last = @@ -698,25 +734,26 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles == 0; }); - */ + */ num_edges_with_triangles = static_cast( thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); cugraph::edge_property_t edge_mask(handle, cur_graph_view); - // Set edge property to 'True' for all edges then mask out invalid edges which can be significantly - // smaller than the valid edges + // Set edge property to 'True' for all edges then mask out invalid edges which can be + // significantly smaller than the valid edges cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); - thrust::sort( - handle.get_thrust_policy(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, edgelist_dsts.begin() + num_edges_with_triangles), - thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); + thrust::sort(handle.get_thrust_policy(), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, + edgelist_dsts.begin() + num_edges_with_triangles), + thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); cugraph::edge_bucket_t edges_with_no_triangle(handle); - edges_with_no_triangle.insert( - edgelist_srcs.begin() + num_edges_with_triangles, edgelist_srcs.end(),edgelist_dsts.begin() + num_edges_with_triangles); - + edges_with_no_triangle.insert(edgelist_srcs.begin() + num_edges_with_triangles, + edgelist_srcs.end(), + edgelist_dsts.begin() + num_edges_with_triangles); + // FIXME: Cannot modify an edgemask that is still attached. // This can lead to race conditions cugraph::transform_e( @@ -730,20 +767,24 @@ std::tuple, rmm::device_uvector> k_truss return false; }, edge_mask.mutable_view(), - false); // FIXME: Remove expensive check. This is only here for debugging purposes ********************************** + false); // FIXME: Remove expensive check. This is only here for debugging purposes + // ********************************** cur_graph_view.attach_edge_mask(edge_mask.view()); edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - - thrust::sort_by_key( - handle.get_thrust_policy(), transposed_edge_first, transposed_edge_first + edgelist_srcs.size(), num_triangles.begin()); - // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be removed - // 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end of the edges to be removed - // 'edge_triangle_count_pair_first' marks the begining of the valid edges + thrust::sort_by_key(handle.get_thrust_policy(), + transposed_edge_first, + transposed_edge_first + edgelist_srcs.size(), + num_triangles.begin()); + + // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be + // removed 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end + // of the edges to be removed 'edge_triangle_count_pair_first' marks the begining of the valid + // edges auto invalid_transposed_edge_triangle_count_first = thrust::stable_partition(handle.get_thrust_policy(), transposed_edge_triangle_count_pair_first, @@ -752,27 +793,29 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles >= k - 2; }); - - num_invalid_edges = static_cast(thrust::distance( - invalid_transposed_edge_triangle_count_first, transposed_edge_triangle_count_pair_first + edgelist_srcs.size())); + + num_invalid_edges = static_cast( + thrust::distance(invalid_transposed_edge_triangle_count_first, + transposed_edge_triangle_count_pair_first + edgelist_srcs.size())); if (num_invalid_edges == 0) { break; } - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; // case 3. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) - // FIXME: check if 'invalid_transposed_edge_triangle_count_first' is necessery as I operate on 'vertex_pair_buffer' - // which contains the ordering with the number of triangles. + // FIXME: check if 'invalid_transposed_edge_triangle_count_first' is necessery as I operate on + // 'vertex_pair_buffer' which contains the ordering with the number of triangles. // FIXME: debug this stage. There are edges that have been removed that are still found in nbr // intersection - auto [intersection_offsets, intersection_indices] = - detail::nbr_intersection(handle, - cur_graph_view, - cugraph::edge_dummy_property_t{}.view(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, edgelist_dsts.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end()), - std::array{true, true}, - do_expensive_check); + auto [intersection_offsets, intersection_indices] = detail::nbr_intersection( + handle, + cur_graph_view, + cugraph::edge_dummy_property_t{}.view(), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, + edgelist_dsts.begin() + num_valid_edges), + thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end()), + std::array{true, true}, + do_expensive_check); rmm::device_uvector num_triangles_invalid(num_invalid_edges, handle.get_stream()); @@ -782,14 +825,15 @@ std::tuple, rmm::device_uvector> k_truss intersection_offsets.begin() + 1, intersection_offsets.end(), num_triangles_invalid.begin()); - - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_invalid_edges), - unroll_edge_invalid{ - edge_t{num_valid_edges}, - raft::device_span(num_triangles.data(), num_triangles.size()), - raft::device_span(num_triangles_invalid.data(), num_triangles_invalid.size())}); + + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_invalid_edges), + unroll_edge_invalid{ + edge_t{num_valid_edges}, + raft::device_span(num_triangles.data(), num_triangles.size()), + raft::device_span(num_triangles_invalid.data(), num_triangles_invalid.size())}); size_t accumulate_pair_size = intersection_indices.size(); @@ -797,12 +841,12 @@ std::tuple, rmm::device_uvector> k_truss auto vertex_pair_buffer_p_r_edge_p_q = allocate_dataframe_buffer>(accumulate_pair_size, handle.get_stream()); - + thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r_q_r{ + generate_p_r_q_r{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), @@ -819,9 +863,8 @@ std::tuple, rmm::device_uvector> k_truss get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), transposed_edge_first, transposed_edge_first + num_valid_edges, - transposed_edge_first + edgelist_srcs.size() - }); - + transposed_edge_first + edgelist_srcs.size()}); + auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>(accumulate_pair_size, handle.get_stream()); @@ -830,7 +873,7 @@ std::tuple, rmm::device_uvector> k_truss handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_q_r_edge_p_q), - generate_p_r_q_r{ + generate_p_r_q_r{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), @@ -846,45 +889,39 @@ std::tuple, rmm::device_uvector> k_truss get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), transposed_edge_first, transposed_edge_first + num_valid_edges, - transposed_edge_first + edgelist_srcs.size() - }); - + transposed_edge_first + edgelist_srcs.size()}); + // case 1: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - cugraph::find_unroll_p_r_and_q_r_edges(handle, - cur_graph_view, - num_invalid_edges, - num_valid_edges, - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::move(num_triangles)); - + cugraph::find_unroll_p_r_and_q_r_edges( + handle, + cur_graph_view, + num_invalid_edges, + num_valid_edges, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(num_triangles)); + // case 2: unroll (p, r) - cugraph::find_unroll_p_r_and_q_r_edges(handle, - cur_graph_view, - num_invalid_edges, - num_valid_edges, - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::move(num_triangles)); - + cugraph::find_unroll_p_r_and_q_r_edges( + handle, + cur_graph_view, + num_invalid_edges, + num_valid_edges, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(num_triangles)); + cur_graph_view.clear_edge_mask(); edges_with_no_triangle.clear(); - edge_mask.clear(handle); // masking not working in a loop. - + edge_mask.clear(handle); // masking not working in a loop. } printf("\n*********final results*********\n"); - //raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - //raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - //raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); + // raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + // raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + // raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); printf("\nthe number of edges = %d\n", edgelist_srcs.size()); return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } diff --git a/cpp/src/structure/edge_triangle_count_impl.cuh b/cpp/src/structure/edge_triangle_count_impl.cuh index 11db6b45924..315353b799b 100644 --- a/cpp/src/structure/edge_triangle_count_impl.cuh +++ b/cpp/src/structure/edge_triangle_count_impl.cuh @@ -16,18 +16,16 @@ #pragma once -#include - #include #include #include #include #include #include -#include #include #include + #include #include @@ -37,6 +35,9 @@ #include #include +#include +#include + #include #include #include diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 99f0c068a19..08c96a2ca96 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -14,11 +14,6 @@ * limitations under the License. */ -#include -#include -#include -#include - #include #include #include @@ -27,10 +22,15 @@ #include #include + #include #include #include +#include +#include +#include +#include #include #include @@ -117,5 +117,5 @@ INSTANTIATE_TEST_SUITE_P(file_test, ::testing::Values(cugraph::test::File_Usecase( "/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx")))); -///home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx +/// home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx CUGRAPH_TEST_PROGRAM_MAIN() From 8694f31172b0bdac6cc711cdde3bd3e1fd6db4b3 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 03:02:46 -0700 Subject: [PATCH 072/155] remove unnecessary atomic operation --- cpp/src/community/k_truss_impl.cuh | 109 ++++++++++++++++------------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 8972ad20e6a..50eca5443f9 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -80,21 +80,6 @@ struct unroll_edge { } }; -template -struct unroll_edge_invalid { - edge_t num_valid_edges{}; - raft::device_span num_triangles{}; // FIXME: invalid type for unsigned integers - raft::device_span num_triangles_invalid{}; - - __device__ thrust::tuple operator()(edge_t idx) const - { - cuda::atomic_ref atomic_counter( - num_triangles[idx + num_valid_edges]); - auto r = - atomic_counter.fetch_sub(edge_t{num_triangles_invalid[idx]}, cuda::std::memory_order_relaxed); - } -}; - template rmm::device_uvector prefix_sum_valid_and_invalid_edges( // The edgelist is segmented into 2 partitions (valid and invalid edges) @@ -187,9 +172,9 @@ void find_unroll_p_r_and_q_r_edges( graph_view_t& graph_view, edge_t num_invalid_edges, edge_t num_valid_edges, - rmm::device_uvector&& edgelist_srcs, // FIXME: Use device_span instead - rmm::device_uvector&& edgelist_dsts, - rmm::device_uvector&& num_triangles) + raft::device_span edgelist_srcs, // FIXME: Use device_span instead + raft::device_span edgelist_dsts, + raft::device_span num_triangles) { auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( handle, @@ -558,7 +543,7 @@ std::tuple, rmm::device_uvector> k_truss } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - + /* { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -616,9 +601,12 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } + */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. + + { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; @@ -702,6 +690,9 @@ std::tuple, rmm::device_uvector> k_truss // instead to avoid resorting? auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); + + auto edge_first = + thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); auto transposed_edge_triangle_count_pair_first = thrust::make_zip_iterator(transposed_edge_first, num_triangles.begin()); @@ -719,7 +710,7 @@ std::tuple, rmm::device_uvector> k_truss thrust::stable_partition(handle.get_thrust_policy(), transposed_edge_triangle_count_pair_first, transposed_edge_triangle_count_pair_first + num_triangles.size(), - [k] __device__(auto e) { + [] __device__(auto e) { auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); @@ -736,6 +727,7 @@ std::tuple, rmm::device_uvector> k_truss }); */ + num_edges_with_triangles = static_cast( thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); @@ -748,6 +740,7 @@ std::tuple, rmm::device_uvector> k_truss thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, edgelist_dsts.begin() + num_edges_with_triangles), thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); + cugraph::edge_bucket_t edges_with_no_triangle(handle); edges_with_no_triangle.insert(edgelist_srcs.begin() + num_edges_with_triangles, @@ -807,6 +800,15 @@ std::tuple, rmm::device_uvector> k_truss // 'vertex_pair_buffer' which contains the ordering with the number of triangles. // FIXME: debug this stage. There are edges that have been removed that are still found in nbr // intersection + + + // nbr_intersection requires the edges to be sort by 'src' + // sort the invalid edges by src for nbr intersection + thrust::sort_by_key(handle.get_thrust_policy(), + edge_first + num_valid_edges, + edge_first + edgelist_srcs.size(), + num_triangles.begin() + num_valid_edges); + auto [intersection_offsets, intersection_indices] = detail::nbr_intersection( handle, cur_graph_view, @@ -826,14 +828,14 @@ std::tuple, rmm::device_uvector> k_truss intersection_offsets.end(), num_triangles_invalid.begin()); - thrust::for_each( - handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_invalid_edges), - unroll_edge_invalid{ - edge_t{num_valid_edges}, - raft::device_span(num_triangles.data(), num_triangles.size()), - raft::device_span(num_triangles_invalid.data(), num_triangles_invalid.size())}); + auto pair_first = thrust::make_zip_iterator(num_triangles.begin() + num_valid_edges, num_triangles_invalid.begin()); + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_invalid_edges), + [num_triangles = raft::device_span(num_triangles.data() + num_valid_edges, num_invalid_edges), intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size())]__device__(auto i) { + num_triangles[i] = num_triangles[i] - intersection_offsets[i + 1] - intersection_offsets[i]; + }); size_t accumulate_pair_size = intersection_indices.size(); @@ -853,17 +855,7 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)}); - auto num_edge_exists = accumulate_pair_size; - thrust::for_each(handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), - unroll_edge{ - edge_t{num_valid_edges}, - raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), - transposed_edge_first, - transposed_edge_first + num_valid_edges, - transposed_edge_first + edgelist_srcs.size()}); + auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>(accumulate_pair_size, @@ -880,17 +872,39 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)}); + + // ***************************************************************** + // Unrolling the edges require the edges to be sorted by destination + // re-sort the invalid edges by 'dst' + thrust::sort_by_key(handle.get_thrust_policy(), + transposed_edge_first + num_valid_edges, + transposed_edge_first + edgelist_srcs.size(), + num_triangles.begin() + num_valid_edges); + + + auto num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_edge_exists), unroll_edge{ edge_t{num_valid_edges}, raft::device_span(num_triangles.data(), num_triangles.size()), - get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), transposed_edge_first, transposed_edge_first + num_valid_edges, transposed_edge_first + edgelist_srcs.size()}); + thrust::for_each(handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_edge_exists), + unroll_edge{ + edge_t{num_valid_edges}, + raft::device_span(num_triangles.data(), num_triangles.size()), + get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), + transposed_edge_first, + transposed_edge_first + num_valid_edges, + transposed_edge_first + edgelist_srcs.size()}); + // case 1: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) @@ -899,9 +913,9 @@ std::tuple, rmm::device_uvector> k_truss cur_graph_view, num_invalid_edges, num_valid_edges, - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::move(num_triangles)); + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), + raft::device_span(num_triangles.data(), num_triangles.size())); // case 2: unroll (p, r) cugraph::find_unroll_p_r_and_q_r_edges( @@ -909,9 +923,9 @@ std::tuple, rmm::device_uvector> k_truss cur_graph_view, num_invalid_edges, num_valid_edges, - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::move(num_triangles)); + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), + raft::device_span(num_triangles.data(), num_triangles.size())); cur_graph_view.clear_edge_mask(); edges_with_no_triangle.clear(); @@ -923,7 +937,8 @@ std::tuple, rmm::device_uvector> k_truss // raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); // raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); printf("\nthe number of edges = %d\n", edgelist_srcs.size()); - return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); + + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); // FIXME: return weights as well } } } // namespace cugraph From ccf1b6d706b2c8354afcead724cc17c907d43320 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 03:34:17 -0700 Subject: [PATCH 073/155] sort before and after nbr_intersection --- cpp/src/community/k_truss_impl.cuh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 50eca5443f9..3dd791eac66 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -696,6 +696,11 @@ std::tuple, rmm::device_uvector> k_truss auto transposed_edge_triangle_count_pair_first = thrust::make_zip_iterator(transposed_edge_first, num_triangles.begin()); + + thrust::sort_by_key(handle.get_thrust_policy(), + transposed_edge_first, + transposed_edge_first + edgelist_srcs.size(), + num_triangles.begin()); // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_property_t edge_mask(handle, cur_graph_view); @@ -769,11 +774,6 @@ std::tuple, rmm::device_uvector> k_truss edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - thrust::sort_by_key(handle.get_thrust_policy(), - transposed_edge_first, - transposed_edge_first + edgelist_srcs.size(), - num_triangles.begin()); - // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be // removed 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end // of the edges to be removed 'edge_triangle_count_pair_first' marks the begining of the valid From 9cc76bd8a5d6cf4f7b6f6f02ef12522c1b9f2e27 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 03:38:00 -0700 Subject: [PATCH 074/155] update docstrings --- cpp/src/community/k_truss_impl.cuh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 3dd791eac66..6b13fa01049 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -781,7 +781,7 @@ std::tuple, rmm::device_uvector> k_truss auto invalid_transposed_edge_triangle_count_first = thrust::stable_partition(handle.get_thrust_policy(), transposed_edge_triangle_count_pair_first, - transposed_edge_triangle_count_pair_first + num_triangles.size(), + transposed_edge_triangle_count_pair_first + edgelist_srcs.size(), [k] __device__(auto e) { auto num_triangles = thrust::get<1>(e); return num_triangles >= k - 2; @@ -795,7 +795,7 @@ std::tuple, rmm::device_uvector> k_truss auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - // case 3. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) + // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) // FIXME: check if 'invalid_transposed_edge_triangle_count_first' is necessery as I operate on // 'vertex_pair_buffer' which contains the ordering with the number of triangles. // FIXME: debug this stage. There are edges that have been removed that are still found in nbr @@ -905,7 +905,7 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first + num_valid_edges, transposed_edge_first + edgelist_srcs.size()}); - // case 1: unroll (q, r) + // case 2: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) cugraph::find_unroll_p_r_and_q_r_edges( @@ -917,7 +917,7 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), raft::device_span(num_triangles.data(), num_triangles.size())); - // case 2: unroll (p, r) + // case 3: unroll (p, r) cugraph::find_unroll_p_r_and_q_r_edges( handle, cur_graph_view, From 585c05293a197800bbf23d0dd1ecfb4aa055042f Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 03:45:32 -0700 Subject: [PATCH 075/155] remove unnecessary thrust call --- cpp/src/community/k_truss_impl.cuh | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 6b13fa01049..61d83b14454 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -822,20 +822,14 @@ std::tuple, rmm::device_uvector> k_truss rmm::device_uvector num_triangles_invalid(num_invalid_edges, handle.get_stream()); // Update the number of triangles of each (p, q) edges by looking at their intersection - // size - thrust::adjacent_difference(handle.get_thrust_policy(), - intersection_offsets.begin() + 1, - intersection_offsets.end(), - num_triangles_invalid.begin()); - - auto pair_first = thrust::make_zip_iterator(num_triangles.begin() + num_valid_edges, num_triangles_invalid.begin()); - thrust::for_each( - handle.get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_invalid_edges), - [num_triangles = raft::device_span(num_triangles.data() + num_valid_edges, num_invalid_edges), intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size())]__device__(auto i) { - num_triangles[i] = num_triangles[i] - intersection_offsets[i + 1] - intersection_offsets[i]; - }); + // size. + thrust::for_each( + handle.get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(num_invalid_edges), + [num_triangles = raft::device_span(num_triangles.data() + num_valid_edges, num_invalid_edges), intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size())]__device__(auto i) { + num_triangles[i] = num_triangles[i] - intersection_offsets[i + 1] - intersection_offsets[i]; + }); size_t accumulate_pair_size = intersection_indices.size(); From 8fb2b5b0cbb9efe6016616230511a4ad4f141df5 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 03:52:48 -0700 Subject: [PATCH 076/155] remove unnecessary variables --- cpp/src/community/k_truss_impl.cuh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 61d83b14454..cbfa8989e24 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -831,11 +831,9 @@ std::tuple, rmm::device_uvector> k_truss num_triangles[i] = num_triangles[i] - intersection_offsets[i + 1] - intersection_offsets[i]; }); - size_t accumulate_pair_size = intersection_indices.size(); - // FIXME: Find a way to not have to maintain a dataframe_buffer auto vertex_pair_buffer_p_r_edge_p_q = - allocate_dataframe_buffer>(accumulate_pair_size, + allocate_dataframe_buffer>(intersection_indices.size(), handle.get_stream()); thrust::tabulate( @@ -852,7 +850,7 @@ std::tuple, rmm::device_uvector> k_truss auto vertex_pair_buffer_q_r_edge_p_q = - allocate_dataframe_buffer>(accumulate_pair_size, + allocate_dataframe_buffer>(intersection_indices.size(), handle.get_stream()); thrust::tabulate( @@ -875,11 +873,9 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first + edgelist_srcs.size(), num_triangles.begin() + num_valid_edges); - - auto num_edge_exists = accumulate_pair_size; thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), + thrust::make_counting_iterator(intersection_indices.size()), unroll_edge{ edge_t{num_valid_edges}, raft::device_span(num_triangles.data(), num_triangles.size()), @@ -890,7 +886,7 @@ std::tuple, rmm::device_uvector> k_truss thrust::for_each(handle.get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator(num_edge_exists), + thrust::make_counting_iterator(intersection_indices.size()), unroll_edge{ edge_t{num_valid_edges}, raft::device_span(num_triangles.data(), num_triangles.size()), From 4be2cf6f774a38016c2e267bc684a4b814ed6f17 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 07:52:29 -0700 Subject: [PATCH 077/155] move edge_triangle_count to algorithms.hpp --- cpp/CMakeLists.txt | 2 +- cpp/include/cugraph/algorithms.hpp | 19 ++++++++++++++++++ cpp/include/cugraph/graph_functions.hpp | 17 ---------------- .../edge_triangle_count_impl.cuh | 0 .../edge_triangle_count_sg.cu | 2 +- cpp/src/community/k_truss_impl.cuh | 20 +++++++++---------- 6 files changed, 31 insertions(+), 29 deletions(-) rename cpp/src/{structure => community}/edge_triangle_count_impl.cuh (100%) rename cpp/src/{structure => community}/edge_triangle_count_sg.cu (97%) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e6b61204433..ea3bbf7b1fd 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -199,8 +199,8 @@ set(CUGRAPH_SOURCES src/community/detail/refine_mg.cu src/community/detail/mis_sg.cu src/community/detail/mis_mg.cu + src/community/edge_triangle_count_sg.cu src/detail/utility_wrappers.cu - src/structure/edge_triangle_count_sg.cu src/structure/graph_view_mg.cu src/structure/remove_self_loops.cu src/structure/remove_multi_edges.cu diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 114a2dfd573..79e581a0581 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -2040,6 +2040,24 @@ void triangle_count(raft::handle_t const& handle, raft::device_span counts, bool do_expensive_check = false); +/** + * @brief Count the number of triangles for each edge. + * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. + * @tparam edge_t Type of edge identifiers. Needs to be an integral type. + * + * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and + * handles to various CUDA libraries) to run graph algorithms. + * @param edgelist_srcs List of source vertex ids + * @param edgelist_dsts List of destination vertex ids + * @return triangle count of the edge list + */ +template +rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + /* * @brief Compute K-Truss. * @@ -2053,6 +2071,7 @@ void triangle_count(raft::handle_t const& handle, * @param graph_view Graph view object. * @param k The desired k to be used for extracting the K-Truss subgraph * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). + * @return edge list of the K-Truss subgraph */ template std::tuple, rmm::device_uvector> k_truss( diff --git a/cpp/include/cugraph/graph_functions.hpp b/cpp/include/cugraph/graph_functions.hpp index 0096cce9e6e..90425f86bef 100644 --- a/cpp/include/cugraph/graph_functions.hpp +++ b/cpp/include/cugraph/graph_functions.hpp @@ -1052,21 +1052,4 @@ remove_multi_edges(raft::handle_t const& handle, std::optional>&& edgelist_edge_types, bool keep_min_value_edge = false); -/** - * @brief Count the number of triangles for each edge. - * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. - * @tparam edge_t Type of edge identifiers. Needs to be an integral type. - * - * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and - * handles to various CUDA libraries) to run graph algorithms. - * @param edgelist_srcs List of source vertex ids - * @param edgelist_dsts List of destination vertex ids - */ -template -rmm::device_uvector edge_triangle_count( - raft::handle_t const& handle, - graph_view_t const& graph_view, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts); - } // namespace cugraph diff --git a/cpp/src/structure/edge_triangle_count_impl.cuh b/cpp/src/community/edge_triangle_count_impl.cuh similarity index 100% rename from cpp/src/structure/edge_triangle_count_impl.cuh rename to cpp/src/community/edge_triangle_count_impl.cuh diff --git a/cpp/src/structure/edge_triangle_count_sg.cu b/cpp/src/community/edge_triangle_count_sg.cu similarity index 97% rename from cpp/src/structure/edge_triangle_count_sg.cu rename to cpp/src/community/edge_triangle_count_sg.cu index aa3a0cf72a1..640beee9fcd 100644 --- a/cpp/src/structure/edge_triangle_count_sg.cu +++ b/cpp/src/community/edge_triangle_count_sg.cu @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include +#include namespace cugraph { diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index cbfa8989e24..7bfa4e6a8b7 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -166,8 +166,8 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, return dist; } -template -void find_unroll_p_r_and_q_r_edges( +template +void find_unroll_p_r_or_q_r_edges( raft::handle_t const& handle, graph_view_t& graph_view, edge_t num_invalid_edges, @@ -200,7 +200,7 @@ void find_unroll_p_r_and_q_r_edges( prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); - const bool const_is_edge_q_r = is_edge_q_r; + const bool const_is_q_r_edge = is_q_r_edge; thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -215,7 +215,7 @@ void find_unroll_p_r_and_q_r_edges( prefix_sum_invalid = prefix_sum_invalid.data(), potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r), - const_is_edge_q_r] __device__(auto idx) { + const_is_q_r_edge] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_end_valid = dst_array_begin_valid + num_valid_edges; @@ -252,7 +252,7 @@ void find_unroll_p_r_and_q_r_edges( // FIXME remove prefix_sum_valid[idx] as it is substracted incoming_edges_to_r + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); - if constexpr (const_is_edge_q_r) { + if constexpr (const_is_q_r_edge) { auto potential_closing_edges_first_valid = thrust::make_zip_iterator( src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(src)); thrust::copy( @@ -328,7 +328,7 @@ void find_unroll_p_r_and_q_r_edges( raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); // Extra check for 'incoming_edges_to_r' - if constexpr (!is_edge_q_r) { + if constexpr (!is_q_r_edge) { // Exchange the arguments (incoming_edges_to_r, num_edges_not_overcomp) order // To also check if the 'incoming_edges_to_r' belong the the invalid_edgelist num_edges_not_overcomp = @@ -344,7 +344,7 @@ void find_unroll_p_r_and_q_r_edges( } // FIXME: Combine both 'thrust::for_each' - if constexpr (is_edge_q_r) { + if constexpr (is_q_r_edge) { thrust::for_each( handle.get_thrust_policy(), thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), @@ -898,7 +898,7 @@ std::tuple, rmm::device_uvector> k_truss // case 2: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - cugraph::find_unroll_p_r_and_q_r_edges( + cugraph::find_unroll_p_r_or_q_r_edges( handle, cur_graph_view, num_invalid_edges, @@ -908,7 +908,7 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(num_triangles.data(), num_triangles.size())); // case 3: unroll (p, r) - cugraph::find_unroll_p_r_and_q_r_edges( + cugraph::find_unroll_p_r_or_q_r_edges( handle, cur_graph_view, num_invalid_edges, @@ -919,7 +919,7 @@ std::tuple, rmm::device_uvector> k_truss cur_graph_view.clear_edge_mask(); edges_with_no_triangle.clear(); - edge_mask.clear(handle); // masking not working in a loop. + edge_mask.clear(handle); } printf("\n*********final results*********\n"); From 3ad4c206e9f974be3f44e53da05e59afcb21c00a Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 07:52:58 -0700 Subject: [PATCH 078/155] remove raw pointer --- cpp/src/community/k_truss_impl.cuh | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 7bfa4e6a8b7..b420dcbd051 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -87,16 +87,15 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( // partition. raft::handle_t const& handle, edge_t num_edges, - edge_t num_invalid_edges, - vertex_t* invalid_dst, + raft::device_span invalid_dst, raft::device_span edgelist_dsts) { - rmm::device_uvector prefix_sum(num_invalid_edges + 1, handle.get_stream()); + rmm::device_uvector prefix_sum(invalid_dst.size() + 1, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), - prefix_sum.begin() + num_invalid_edges, + prefix_sum.begin() + invalid_dst.size(), [num_edges, invalid_dst, edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { auto itr_lower_valid = thrust::lower_bound( thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dst[idx]); @@ -179,15 +178,13 @@ void find_unroll_p_r_or_q_r_edges( auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( handle, edge_t{num_valid_edges}, - edge_t{num_invalid_edges}, // num_edges == num_invalid_edges - edgelist_dsts.begin() + num_valid_edges, + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data(), num_valid_edges)); auto prefix_sum_invalid = prefix_sum_valid_and_invalid_edges( handle, edge_t{num_invalid_edges}, - edge_t{num_invalid_edges}, - edgelist_dsts.begin() + num_valid_edges, + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); auto potential_closing_edges = allocate_dataframe_buffer>( @@ -543,7 +540,7 @@ std::tuple, rmm::device_uvector> k_truss } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - /* + { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -601,12 +598,9 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } - */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. - - { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; From d6fd0bff8501290b3ca8bbbfe1a2d0226d985425 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 12 Mar 2024 07:53:50 -0700 Subject: [PATCH 079/155] leverage the new implementation of ktruss in the capi --- cpp/src/c_api/legacy_k_truss.cpp | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/cpp/src/c_api/legacy_k_truss.cpp b/cpp/src/c_api/legacy_k_truss.cpp index a6a1f17f482..53e4f84cf3b 100644 --- a/cpp/src/c_api/legacy_k_truss.cpp +++ b/cpp/src/c_api/legacy_k_truss.cpp @@ -81,27 +81,13 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { auto number_map = reinterpret_cast*>(graph_->number_map_); auto graph_view = graph->view(); - rmm::device_uvector src(0, handle_.get_stream()); - rmm::device_uvector dst(0, handle_.get_stream()); - std::optional> wgt{std::nullopt}; - std::tie(src, dst, wgt, std::ignore) = cugraph::decompress_to_edgelist( + auto [result_src, result_dst] = cugraph::k_truss( handle_, graph_view, - edge_weights ? std::make_optional(edge_weights->view()) : std::nullopt, - std::optional>{std::nullopt}, - std::optional>(std::nullopt), + k_, do_expensive_check_); - auto [result_src, result_dst, result_wgt] = cugraph::k_truss_subgraph( - handle_, - raft::device_span(src.data(), src.size()), - raft::device_span(dst.data(), dst.size()), - wgt ? std::make_optional(raft::device_span(wgt->data(), wgt->size())) - : std::nullopt, - graph_view.number_of_vertices(), - k_); - cugraph::unrenumber_int_vertices( handle_, result_src.data(), @@ -127,9 +113,12 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{ new cugraph::c_api::cugraph_type_erased_device_array_t(result_src, graph_->vertex_type_), new cugraph::c_api::cugraph_type_erased_device_array_t(result_dst, graph_->vertex_type_), + /* wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, graph_->weight_type_) : NULL, + */ + NULL, NULL, NULL, new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, From 31c890266b2ae04396cb907b1e01a55bfdc4b062 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 13 Mar 2024 11:59:19 -0700 Subject: [PATCH 080/155] update edge masking call --- cpp/src/community/k_truss_impl.cuh | 155 +++++++++++++---------------- 1 file changed, 71 insertions(+), 84 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index b420dcbd051..3be4a4014d6 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -99,11 +100,13 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( [num_edges, invalid_dst, edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { auto itr_lower_valid = thrust::lower_bound( thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dst[idx]); + auto itr_upper_valid = thrust::upper_bound( thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dst[idx]); - auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); - return dist_valid; + auto dist = thrust::distance(itr_lower_valid, itr_upper_valid); + + return dist; }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); @@ -143,9 +146,8 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, thrust::seq, invalid_first, invalid_last, - transposed_potential_or_incoming_edge); // very important when using lower or upperbound on - // edges that do not exist. Always make sure to - // compare with the queried edges + transposed_potential_or_incoming_edge); + assert(*itr == transposed_potential_or_incoming_edge); auto dist = thrust::distance(invalid_first, itr); if (*itr != transposed_potential_or_incoming_edge) { return false; } @@ -181,6 +183,7 @@ void find_unroll_p_r_or_q_r_edges( raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data(), num_valid_edges)); + auto prefix_sum_invalid = prefix_sum_valid_and_invalid_edges( handle, edge_t{num_invalid_edges}, @@ -313,6 +316,9 @@ void find_unroll_p_r_or_q_r_edges( resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); + + + edge_t num_edges_not_overcomp = remove_overcompensating_edges( handle, @@ -534,13 +540,13 @@ std::tuple, rmm::device_uvector> k_truss std::nullopt, std::nullopt, cugraph::graph_properties_t{true, graph_view.is_multigraph()}, - true); + do_expensive_check); modified_graph_view = (*modified_graph).view(); } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - + /* { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -598,6 +604,7 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } + */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. @@ -643,7 +650,7 @@ std::tuple, rmm::device_uvector> k_truss std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, - false); //******************FIXME: hardcoded to False + true); modified_graph_view = (*modified_graph).view(); @@ -698,76 +705,12 @@ std::tuple, rmm::device_uvector> k_truss // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_property_t edge_mask(handle, cur_graph_view); + cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); + cur_graph_view.attach_edge_mask(edge_mask.view()); edge_t num_invalid_edges{0}; size_t num_edges_with_triangles{0}; - while (true) { - // Remove edges that have a triangle count of zero. Those should not be accounted - // for during the unroling phase. - auto edges_with_triangle_last = - thrust::stable_partition(handle.get_thrust_policy(), - transposed_edge_triangle_count_pair_first, - transposed_edge_triangle_count_pair_first + num_triangles.size(), - [] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles > 0; - }); - - /* - //FIXME: Getting different results than the above - auto edges_with_triangle_last = - thrust::remove_if(handle.get_thrust_policy(), - transposed_edge_triangle_count_pair_first, - transposed_edge_triangle_count_pair_first + num_triangles.size(), - [] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles == 0; - }); - */ - - - num_edges_with_triangles = static_cast( - thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); - - cugraph::edge_property_t edge_mask(handle, cur_graph_view); - // Set edge property to 'True' for all edges then mask out invalid edges which can be - // significantly smaller than the valid edges - cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); - - thrust::sort(handle.get_thrust_policy(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, - edgelist_dsts.begin() + num_edges_with_triangles), - thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); - - - cugraph::edge_bucket_t edges_with_no_triangle(handle); - edges_with_no_triangle.insert(edgelist_srcs.begin() + num_edges_with_triangles, - edgelist_srcs.end(), - edgelist_dsts.begin() + num_edges_with_triangles); - - // FIXME: Cannot modify an edgemask that is still attached. - // This can lead to race conditions - cugraph::transform_e( - handle, - cur_graph_view, - edges_with_no_triangle, - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - return false; - }, - edge_mask.mutable_view(), - false); // FIXME: Remove expensive check. This is only here for debugging purposes - // ********************************** - - cur_graph_view.attach_edge_mask(edge_mask.view()); - - edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); - edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); - num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be // removed 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end // of the edges to be removed 'edge_triangle_count_pair_first' marks the begining of the valid @@ -786,7 +729,6 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_triangle_count_pair_first + edgelist_srcs.size())); if (num_invalid_edges == 0) { break; } - auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) @@ -795,7 +737,6 @@ std::tuple, rmm::device_uvector> k_truss // FIXME: debug this stage. There are edges that have been removed that are still found in nbr // intersection - // nbr_intersection requires the edges to be sort by 'src' // sort the invalid edges by src for nbr intersection thrust::sort_by_key(handle.get_thrust_policy(), @@ -822,9 +763,15 @@ std::tuple, rmm::device_uvector> k_truss thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_invalid_edges), [num_triangles = raft::device_span(num_triangles.data() + num_valid_edges, num_invalid_edges), intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size())]__device__(auto i) { - num_triangles[i] = num_triangles[i] - intersection_offsets[i + 1] - intersection_offsets[i]; + + num_triangles[i] -= intersection_offsets[i + 1] - intersection_offsets[i]; }); + auto pair_ = thrust::make_tuple(6, 1); + auto itr_ = thrust::find(edge_first + num_valid_edges, edge_first + edgelist_srcs.size(), pair_); + + auto dist_ = thrust::distance(edge_first + num_valid_edges, itr_); + // FIXME: Find a way to not have to maintain a dataframe_buffer auto vertex_pair_buffer_p_r_edge_p_q = allocate_dataframe_buffer>(intersection_indices.size(), @@ -841,8 +788,6 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)}); - - auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>(intersection_indices.size(), handle.get_stream()); @@ -858,8 +803,6 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)}); - - // ***************************************************************** // Unrolling the edges require the edges to be sorted by destination // re-sort the invalid edges by 'dst' thrust::sort_by_key(handle.get_thrust_policy(), @@ -888,7 +831,7 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first, transposed_edge_first + num_valid_edges, transposed_edge_first + edgelist_srcs.size()}); - + // case 2: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) @@ -910,10 +853,54 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), raft::device_span(num_triangles.data(), num_triangles.size())); + + auto edges_with_triangle_last = + thrust::stable_partition(handle.get_thrust_policy(), + transposed_edge_triangle_count_pair_first, + transposed_edge_triangle_count_pair_first + num_triangles.size(), + [] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles > 0; + }); + + num_edges_with_triangles = static_cast( + thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); + //cugraph::edge_property_t edge_mask(handle, cur_graph_view); + // Set edge property to 'True' for all edges then mask out invalid edges which can be + // significantly smaller than the valid edges + //cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); + thrust::sort(handle.get_thrust_policy(), + thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, + edgelist_dsts.begin() + num_edges_with_triangles), + thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); + + + cugraph::edge_bucket_t edges_with_no_triangle(handle); + edges_with_no_triangle.insert(edgelist_srcs.begin() + num_edges_with_triangles, + edgelist_srcs.end(), + edgelist_dsts.begin() + num_edges_with_triangles); + + // FIXME: Cannot modify an edgemask that is still attached. + // This can lead to race conditions cur_graph_view.clear_edge_mask(); - edges_with_no_triangle.clear(); - edge_mask.clear(handle); + cugraph::transform_e( + handle, + cur_graph_view, + edges_with_no_triangle, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + return false; + }, + edge_mask.mutable_view(), + false); + cur_graph_view.attach_edge_mask(edge_mask.view()); + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); + edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); + num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + } printf("\n*********final results*********\n"); From eab194abcda60988cf3f7832298c669ff2ec02c7 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 13 Mar 2024 15:58:28 -0700 Subject: [PATCH 081/155] remove unused code --- cpp/src/community/k_truss_impl.cuh | 27 ++++++++++++++------------- cpp/src/community/k_truss_sg.cu | 18 +++++++++++------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 3be4a4014d6..7f97ebe35c6 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -47,6 +47,14 @@ namespace cugraph { +//FIXME +template +rmm::device_uvector edge_triangle_count( + raft::handle_t const& handle, + graph_view_t const& graph_view, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts); + template struct unroll_edge { edge_t num_valid_edges{}; @@ -316,9 +324,6 @@ void find_unroll_p_r_or_q_r_edges( resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); - - - edge_t num_edges_not_overcomp = remove_overcompensating_edges( handle, @@ -519,6 +524,7 @@ std::tuple, rmm::device_uvector> k_truss edge_src_dummy_property_t{}.view(), edge_dst_dummy_property_t{}.view(), edge_dummy_property_t{}.view(), + //*edge_weight_view, exclude_self_loop_t{}); if constexpr (multi_gpu) { @@ -528,6 +534,7 @@ std::tuple, rmm::device_uvector> k_truss weight_t, int32_t>( handle, std::move(srcs), std::move(dsts), std::nullopt, std::nullopt, std::nullopt); + //std::move(std::make_optional(wgts)) } std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, renumber_map) = @@ -546,7 +553,7 @@ std::tuple, rmm::device_uvector> k_truss } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - /* + { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -604,7 +611,6 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } - */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. @@ -862,6 +868,8 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles > 0; }); + + num_edges_with_triangles = static_cast( thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); @@ -875,7 +883,6 @@ std::tuple, rmm::device_uvector> k_truss edgelist_dsts.begin() + num_edges_with_triangles), thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); - cugraph::edge_bucket_t edges_with_no_triangle(handle); edges_with_no_triangle.insert(edgelist_srcs.begin() + num_edges_with_triangles, edgelist_srcs.end(), @@ -902,14 +909,8 @@ std::tuple, rmm::device_uvector> k_truss num_triangles.resize(num_edges_with_triangles, handle.get_stream()); } - - printf("\n*********final results*********\n"); - // raft::print_device_vector("srcs", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - // raft::print_device_vector("dsts", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - // raft::print_device_vector("n_tr", num_triangles.data(), num_triangles.size(), std::cout); - printf("\nthe number of edges = %d\n", edgelist_srcs.size()); - return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); // FIXME: return weights as well + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } } } // namespace cugraph diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index 571302fefb2..cf0dce74f0a 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -29,12 +29,16 @@ template std::tuple, rmm::device_uvector> int64_t k, bool do_expensive_check); -// FIXME: Add all possible combinations +template std::tuple, rmm::device_uvector> k_truss( + raft::handle_t const& handle, + graph_view_t const& graph_view, + int32_t k, + bool do_expensive_check); + +template std::tuple, rmm::device_uvector> k_truss( + raft::handle_t const& handle, + graph_view_t const& graph_view, + int64_t k, + bool do_expensive_check); -/* -template void ktruss(raft::handle_t const& handle, - graph_view_t const& graph_view, - int64_t k, - bool do_expensive_check); -*/ } // namespace cugraph From d2d6999ae41b3fd45e297edc418169976d9f168c Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 13 Mar 2024 15:59:17 -0700 Subject: [PATCH 082/155] remove edge triangle count from the stable API --- cpp/include/cugraph/algorithms.hpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 79e581a0581..4d705c92c0d 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -2040,24 +2040,6 @@ void triangle_count(raft::handle_t const& handle, raft::device_span counts, bool do_expensive_check = false); -/** - * @brief Count the number of triangles for each edge. - * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. - * @tparam edge_t Type of edge identifiers. Needs to be an integral type. - * - * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and - * handles to various CUDA libraries) to run graph algorithms. - * @param edgelist_srcs List of source vertex ids - * @param edgelist_dsts List of destination vertex ids - * @return triangle count of the edge list - */ -template -rmm::device_uvector edge_triangle_count( - raft::handle_t const& handle, - graph_view_t const& graph_view, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts); - /* * @brief Compute K-Truss. * From 5817b3f5ea3fbe4c83fbf28627a5f19a4951f96b Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Wed, 13 Mar 2024 16:00:13 -0700 Subject: [PATCH 083/155] support more type combination --- cpp/src/community/k_truss_sg.cu | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index cf0dce74f0a..3df5daad1b7 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -30,15 +30,9 @@ template std::tuple, rmm::device_uvector> bool do_expensive_check); template std::tuple, rmm::device_uvector> k_truss( - raft::handle_t const& handle, - graph_view_t const& graph_view, - int32_t k, - bool do_expensive_check); - -template std::tuple, rmm::device_uvector> k_truss( - raft::handle_t const& handle, - graph_view_t const& graph_view, - int64_t k, - bool do_expensive_check); +raft::handle_t const& handle, +graph_view_t const& graph_view, +int64_t k, +bool do_expensive_check); } // namespace cugraph From 8c347c1472e60bc827f6b8838e42827d3211266d Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 14 Mar 2024 10:53:50 -0700 Subject: [PATCH 084/155] remove outdated fixme --- cpp/src/community/k_truss_impl.cuh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 7f97ebe35c6..99ab0c18b5c 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -47,7 +47,7 @@ namespace cugraph { -//FIXME +//FIXME : This will be deleted once edge_triangle_count becomes public template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, @@ -691,10 +691,6 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - // FIXME 'edge_triangle_count' sorts the edges by 'src' but 'k-truss' needs - // the edges to be sorted with 'dst' as key so we need to sort the edges - // again. Should 'edge_triangle_count' be implemented edges sorted by 'dst' - // instead to avoid resorting? auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); @@ -709,7 +705,6 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first + edgelist_srcs.size(), num_triangles.begin()); - // Note: ensure 'edges_with_triangles' and 'cur_graph_view' have the same transpose flag cugraph::edge_property_t edge_mask(handle, cur_graph_view); cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); cur_graph_view.attach_edge_mask(edge_mask.view()); @@ -913,4 +908,4 @@ std::tuple, rmm::device_uvector> k_truss return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); } } -} // namespace cugraph +} // namespace cugraph \ No newline at end of file From 874897eb2e2dc202d083c8548f82217bef837e4c Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 14 Mar 2024 11:00:47 -0700 Subject: [PATCH 085/155] remove unused variables --- cpp/src/community/k_truss_impl.cuh | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 99ab0c18b5c..ada6416cee2 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -710,7 +710,6 @@ std::tuple, rmm::device_uvector> k_truss cur_graph_view.attach_edge_mask(edge_mask.view()); edge_t num_invalid_edges{0}; - size_t num_edges_with_triangles{0}; while (true) { // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be // removed 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end @@ -733,10 +732,6 @@ std::tuple, rmm::device_uvector> k_truss auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) - // FIXME: check if 'invalid_transposed_edge_triangle_count_first' is necessery as I operate on - // 'vertex_pair_buffer' which contains the ordering with the number of triangles. - // FIXME: debug this stage. There are edges that have been removed that are still found in nbr - // intersection // nbr_intersection requires the edges to be sort by 'src' // sort the invalid edges by src for nbr intersection @@ -749,14 +744,11 @@ std::tuple, rmm::device_uvector> k_truss handle, cur_graph_view, cugraph::edge_dummy_property_t{}.view(), - thrust::make_zip_iterator(edgelist_srcs.begin() + num_valid_edges, - edgelist_dsts.begin() + num_valid_edges), - thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end()), + edge_first + num_valid_edges, + edge_first + edgelist_srcs.size(), std::array{true, true}, do_expensive_check); - rmm::device_uvector num_triangles_invalid(num_invalid_edges, handle.get_stream()); - // Update the number of triangles of each (p, q) edges by looking at their intersection // size. thrust::for_each( @@ -768,11 +760,6 @@ std::tuple, rmm::device_uvector> k_truss num_triangles[i] -= intersection_offsets[i + 1] - intersection_offsets[i]; }); - auto pair_ = thrust::make_tuple(6, 1); - auto itr_ = thrust::find(edge_first + num_valid_edges, edge_first + edgelist_srcs.size(), pair_); - - auto dist_ = thrust::distance(edge_first + num_valid_edges, itr_); - // FIXME: Find a way to not have to maintain a dataframe_buffer auto vertex_pair_buffer_p_r_edge_p_q = allocate_dataframe_buffer>(intersection_indices.size(), @@ -866,7 +853,7 @@ std::tuple, rmm::device_uvector> k_truss - num_edges_with_triangles = static_cast( + size_t num_edges_with_triangles = static_cast( thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); //cugraph::edge_property_t edge_mask(handle, cur_graph_view); From 42c8ed83e6be00b2e859f5f41b9752c33db761e4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 14 Mar 2024 11:04:53 -0700 Subject: [PATCH 086/155] rename function unrolling p, q edges --- cpp/src/community/k_truss_impl.cuh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index ada6416cee2..ec0f761885b 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -469,7 +469,7 @@ struct extract_low_to_high_degree_edges_t { }; template -struct generate_p_r_q_r { +struct generate_p_r_and_q_r_from_p_q { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span invalid_srcs{}; @@ -769,7 +769,7 @@ std::tuple, rmm::device_uvector> k_truss handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r_q_r{ + generate_p_r_and_q_r_from_p_q{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), @@ -784,7 +784,7 @@ std::tuple, rmm::device_uvector> k_truss handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_q_r_edge_p_q), - generate_p_r_q_r{ + generate_p_r_and_q_r_from_p_q{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), From 94fa611409fa30d991fe161c3737264022baee55 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Thu, 14 Mar 2024 11:12:22 -0700 Subject: [PATCH 087/155] remove unnecessary sorting --- cpp/src/community/k_truss_impl.cuh | 34 ++++++++---------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index ec0f761885b..1fa7a14d6a8 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -176,13 +176,13 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, } template -void find_unroll_p_r_or_q_r_edges( +void unroll_p_r_or_q_r_edges( raft::handle_t const& handle, graph_view_t& graph_view, edge_t num_invalid_edges, edge_t num_valid_edges, - raft::device_span edgelist_srcs, // FIXME: Use device_span instead - raft::device_span edgelist_dsts, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts, raft::device_span num_triangles) { auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( @@ -823,38 +823,24 @@ std::tuple, rmm::device_uvector> k_truss // case 2: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) - cugraph::find_unroll_p_r_or_q_r_edges( + cugraph::unroll_p_r_or_q_r_edges( handle, cur_graph_view, num_invalid_edges, num_valid_edges, - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), raft::device_span(num_triangles.data(), num_triangles.size())); // case 3: unroll (p, r) - cugraph::find_unroll_p_r_or_q_r_edges( + cugraph::unroll_p_r_or_q_r_edges( handle, cur_graph_view, num_invalid_edges, num_valid_edges, - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), raft::device_span(num_triangles.data(), num_triangles.size())); - - auto edges_with_triangle_last = - thrust::stable_partition(handle.get_thrust_policy(), - transposed_edge_triangle_count_pair_first, - transposed_edge_triangle_count_pair_first + num_triangles.size(), - [] __device__(auto e) { - auto num_triangles = thrust::get<1>(e); - return num_triangles > 0; - }); - - - - size_t num_edges_with_triangles = static_cast( - thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); //cugraph::edge_property_t edge_mask(handle, cur_graph_view); // Set edge property to 'True' for all edges then mask out invalid edges which can be @@ -870,8 +856,6 @@ std::tuple, rmm::device_uvector> k_truss edgelist_srcs.end(), edgelist_dsts.begin() + num_edges_with_triangles); - // FIXME: Cannot modify an edgemask that is still attached. - // This can lead to race conditions cur_graph_view.clear_edge_mask(); cugraph::transform_e( handle, From f30b4fff4591889208bb98c40da1f31d751c773c Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 08:43:53 -0700 Subject: [PATCH 088/155] add weights support --- cpp/include/cugraph/algorithms.hpp | 16 +-- cpp/src/c_api/legacy_k_truss.cpp | 35 +++--- cpp/src/community/k_truss_impl.cuh | 153 ++++++++++++++++----------- cpp/src/community/k_truss_sg.cu | 75 ++++++++++--- cpp/tests/community/k_truss_test.cpp | 12 ++- 5 files changed, 189 insertions(+), 102 deletions(-) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 4d705c92c0d..c01764be665 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -2051,16 +2051,20 @@ void triangle_count(raft::handle_t const& handle, * @param handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, and * handles to various CUDA libraries) to run graph algorithms. * @param graph_view Graph view object. + * @param edge_weight_view Optional view object holding edge weights for @p graph_view. * @param k The desired k to be used for extracting the K-Truss subgraph * @param do_expensive_check A flag to run expensive checks for input arguments (if set to `true`). * @return edge list of the K-Truss subgraph */ -template -std::tuple, rmm::device_uvector> k_truss( - raft::handle_t const& handle, - graph_view_t const& graph_view, - edge_t k, - bool do_expensive_check = false); +template +std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + edge_t k, + bool do_expensive_check = false); /** * @brief Compute Jaccard similarity coefficient diff --git a/cpp/src/c_api/legacy_k_truss.cpp b/cpp/src/c_api/legacy_k_truss.cpp index 53e4f84cf3b..90db9fc133c 100644 --- a/cpp/src/c_api/legacy_k_truss.cpp +++ b/cpp/src/c_api/legacy_k_truss.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024, NVIDIA CORPORATION. + * Copyright (c) 2023, NVIDIA CORPORATION. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "c_api/abstract_functor.hpp" -#include "c_api/graph.hpp" -#include "c_api/induced_subgraph_result.hpp" -#include "c_api/resource_handle.hpp" -#include "c_api/utils.hpp" - #include +#include +#include +#include +#include +#include + #include #include #include @@ -81,13 +81,27 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { auto number_map = reinterpret_cast*>(graph_->number_map_); auto graph_view = graph->view(); + rmm::device_uvector src(0, handle_.get_stream()); + rmm::device_uvector dst(0, handle_.get_stream()); + std::optional> wgt{std::nullopt}; - auto [result_src, result_dst] = cugraph::k_truss( + std::tie(src, dst, wgt, std::ignore) = cugraph::decompress_to_edgelist( handle_, graph_view, - k_, + edge_weights ? std::make_optional(edge_weights->view()) : std::nullopt, + std::optional>{std::nullopt}, + std::optional>(std::nullopt), do_expensive_check_); + auto [result_src, result_dst, result_wgt] = cugraph::k_truss_subgraph( + handle_, + raft::device_span(src.data(), src.size()), + raft::device_span(dst.data(), dst.size()), + wgt ? std::make_optional(raft::device_span(wgt->data(), wgt->size())) + : std::nullopt, + graph_view.number_of_vertices(), + k_); + cugraph::unrenumber_int_vertices( handle_, result_src.data(), @@ -113,12 +127,9 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{ new cugraph::c_api::cugraph_type_erased_device_array_t(result_src, graph_->vertex_type_), new cugraph::c_api::cugraph_type_erased_device_array_t(result_dst, graph_->vertex_type_), - /* wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, graph_->weight_type_) : NULL, - */ - NULL, NULL, NULL, new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 1fa7a14d6a8..af615f3f13b 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -58,8 +58,8 @@ rmm::device_uvector edge_triangle_count( template struct unroll_edge { edge_t num_valid_edges{}; - raft::device_span num_triangles{}; // FIXME: invalid type for unsigned integers - EdgeIterator edge_unrolled{}; + raft::device_span num_triangles{}; + EdgeIterator edge_to_unroll_first{}; EdgeIterator transposed_valid_edge_first{}; EdgeIterator transposed_valid_edge_last{}; EdgeIterator transposed_invalid_edge_last{}; @@ -67,8 +67,8 @@ struct unroll_edge { __device__ thrust::tuple operator()(edge_t i) const { // edges are sorted with destination as key so reverse the edge when looking it - auto pair = thrust::make_tuple(thrust::get<1>(*(edge_unrolled + i)), - thrust::get<0>(*(edge_unrolled + i))); + auto pair = thrust::make_tuple(thrust::get<1>(*(edge_to_unroll_first + i)), + thrust::get<0>(*(edge_to_unroll_first + i))); // Find its position in either partition of the transposed edgelist // An edge can be in found in either of the two partitions (valid or invalid) @@ -89,32 +89,31 @@ struct unroll_edge { } }; -template +template rmm::device_uvector prefix_sum_valid_and_invalid_edges( // The edgelist is segmented into 2 partitions (valid and invalid edges) // and edges to be unrolled can be either in the valid or the invalid edge // partition. raft::handle_t const& handle, - edge_t num_edges, - raft::device_span invalid_dst, - raft::device_span edgelist_dsts) + raft::device_span invalid_dsts, + raft::device_span edgelist_dsts) { - rmm::device_uvector prefix_sum(invalid_dst.size() + 1, handle.get_stream()); + rmm::device_uvector prefix_sum(invalid_dsts.size() + 1, handle.get_stream()); thrust::tabulate( handle.get_thrust_policy(), prefix_sum.begin(), - prefix_sum.begin() + invalid_dst.size(), - [num_edges, invalid_dst, edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { + prefix_sum.begin() + invalid_dsts.size(), + [invalid_dsts, num_edges=edgelist_dsts.size(), edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { auto itr_lower_valid = thrust::lower_bound( - thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dst[idx]); - + thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dsts[idx]); + auto itr_upper_valid = thrust::upper_bound( - thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dst[idx]); + thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dsts[idx]); - auto dist = thrust::distance(itr_lower_valid, itr_upper_valid); + auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); - return dist; + return dist_valid; }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); @@ -129,8 +128,8 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, edge_t dataframe_buffer_size, EdgeIterator&& potential_closing_or_incoming_edges, EdgeIterator&& incoming_or_potential_closing_edges, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts) + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts) { // To avoid over-compensating, check whether the 'potential_closing_edges' // are within the invalid edges. If yes, the was already unrolled @@ -154,8 +153,9 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, thrust::seq, invalid_first, invalid_last, - transposed_potential_or_incoming_edge); - + transposed_potential_or_incoming_edge); // very important when using lower or upperbound on + // edges that do not exist. Always make sure to + // compare with the queried edges assert(*itr == transposed_potential_or_incoming_edge); auto dist = thrust::distance(invalid_first, itr); if (*itr != transposed_potential_or_incoming_edge) { return false; } @@ -187,16 +187,13 @@ void unroll_p_r_or_q_r_edges( { auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( handle, - edge_t{num_valid_edges}, raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges), - raft::device_span(edgelist_dsts.data(), num_valid_edges)); - + raft::device_span(edgelist_dsts.data(), num_valid_edges)); auto prefix_sum_invalid = prefix_sum_valid_and_invalid_edges( handle, - edge_t{num_invalid_edges}, raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges), - raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); auto potential_closing_edges = allocate_dataframe_buffer>( prefix_sum_valid.back_element(handle.get_stream()) + @@ -208,7 +205,7 @@ void unroll_p_r_or_q_r_edges( prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); - const bool const_is_q_r_edge = is_q_r_edge; + //const bool const_is_q_r_edge = is_q_r_edge; thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -222,8 +219,8 @@ void unroll_p_r_or_q_r_edges( prefix_sum_valid = prefix_sum_valid.data(), prefix_sum_invalid = prefix_sum_invalid.data(), potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), - incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r), - const_is_q_r_edge] __device__(auto idx) { + incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r) + ] __device__(auto idx) { auto src = invalid_first_src[idx]; auto dst = invalid_first_dst[idx]; auto dst_array_end_valid = dst_array_begin_valid + num_valid_edges; @@ -235,8 +232,7 @@ void unroll_p_r_or_q_r_edges( itr_lower_valid); // Need a binary search to find the begining of the range auto invalid_end_dst = invalid_first_dst + num_invalid_edges; - // FIXME: In case of wrong results, investigate lower bound when dst is not part of one - // partition + auto itr_lower_invalid = thrust::lower_bound(thrust::seq, invalid_first_dst, invalid_end_dst, dst); auto idx_lower_invalid = thrust::distance( @@ -260,7 +256,7 @@ void unroll_p_r_or_q_r_edges( // FIXME remove prefix_sum_valid[idx] as it is substracted incoming_edges_to_r + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); - if constexpr (const_is_q_r_edge) { + if constexpr (is_q_r_edge) { auto potential_closing_edges_first_valid = thrust::make_zip_iterator( src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(src)); thrust::copy( @@ -332,8 +328,8 @@ void unroll_p_r_or_q_r_edges( edge_t{num_edge_exists}, std::move(potential_closing_edges), std::move(incoming_edges_to_r), - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); // Extra check for 'incoming_edges_to_r' if constexpr (!is_q_r_edge) { @@ -347,8 +343,8 @@ void unroll_p_r_or_q_r_edges( edge_t{num_edges_not_overcomp}, std::move(incoming_edges_to_r), std::move(potential_closing_edges), - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), + raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); } // FIXME: Combine both 'thrust::for_each' @@ -491,14 +487,16 @@ struct generate_p_r_and_q_r_from_p_q { }; } // namespace -template -std::tuple, rmm::device_uvector> k_truss( - raft::handle_t const& handle, - graph_view_t const& graph_view, - edge_t k, - bool do_expensive_check) +template +std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + edge_t k, + bool do_expensive_check) { - using weight_t = float; // dummy // 1. Check input arguments. CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); @@ -524,7 +522,6 @@ std::tuple, rmm::device_uvector> k_truss edge_src_dummy_property_t{}.view(), edge_dst_dummy_property_t{}.view(), edge_dummy_property_t{}.view(), - //*edge_weight_view, exclude_self_loop_t{}); if constexpr (multi_gpu) { @@ -534,7 +531,6 @@ std::tuple, rmm::device_uvector> k_truss weight_t, int32_t>( handle, std::move(srcs), std::move(dsts), std::nullopt, std::nullopt, std::nullopt); - //std::move(std::make_optional(wgts)) } std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, renumber_map) = @@ -547,13 +543,13 @@ std::tuple, rmm::device_uvector> k_truss std::nullopt, std::nullopt, cugraph::graph_properties_t{true, graph_view.is_multigraph()}, - do_expensive_check); + true); modified_graph_view = (*modified_graph).view(); } // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core - + /* { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; auto vertex_partition_range_lasts = @@ -611,6 +607,7 @@ std::tuple, rmm::device_uvector> k_truss } renumber_map = std::move(tmp_renumber_map); } + */ // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. @@ -656,10 +653,9 @@ std::tuple, rmm::device_uvector> k_truss std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, - true); + false); modified_graph_view = (*modified_graph).view(); - if (renumber_map) { // collapse renumber_map unrenumber_int_vertices(handle, (*tmp_renumber_map).data(), @@ -677,6 +673,7 @@ std::tuple, rmm::device_uvector> k_truss auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; rmm::device_uvector edgelist_srcs(0, handle.get_stream()); rmm::device_uvector edgelist_dsts(0, handle.get_stream()); + std::optional> edgelist_wgts{std::nullopt}; std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( handle, @@ -690,7 +687,7 @@ std::tuple, rmm::device_uvector> k_truss cur_graph_view, raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - + auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); @@ -710,11 +707,14 @@ std::tuple, rmm::device_uvector> k_truss cur_graph_view.attach_edge_mask(edge_mask.view()); edge_t num_invalid_edges{0}; + size_t num_edges_with_triangles{0}; + int iteration = 0; while (true) { + // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be // removed 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end // of the edges to be removed 'edge_triangle_count_pair_first' marks the begining of the valid - // edges + // edges. auto invalid_transposed_edge_triangle_count_first = thrust::stable_partition(handle.get_thrust_policy(), transposed_edge_triangle_count_pair_first, @@ -723,15 +723,16 @@ std::tuple, rmm::device_uvector> k_truss auto num_triangles = thrust::get<1>(e); return num_triangles >= k - 2; }); - + size_t num_edges_with_triangles{0}; num_invalid_edges = static_cast( thrust::distance(invalid_transposed_edge_triangle_count_first, transposed_edge_triangle_count_pair_first + edgelist_srcs.size())); if (num_invalid_edges == 0) { break; } + auto num_valid_edges = edgelist_srcs.size() - num_invalid_edges; - // case 1. For the (p, q), find intersection 'r' to create (p, r, -1) and (q, r, -1) + // case 1. For the (p, q), find intersection 'r'. // nbr_intersection requires the edges to be sort by 'src' // sort the invalid edges by src for nbr intersection @@ -747,7 +748,7 @@ std::tuple, rmm::device_uvector> k_truss edge_first + num_valid_edges, edge_first + edgelist_srcs.size(), std::array{true, true}, - do_expensive_check); + do_expensive_check); // Update the number of triangles of each (p, q) edges by looking at their intersection // size. @@ -756,8 +757,9 @@ std::tuple, rmm::device_uvector> k_truss thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_invalid_edges), [num_triangles = raft::device_span(num_triangles.data() + num_valid_edges, num_invalid_edges), intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size())]__device__(auto i) { - + num_triangles[i] -= intersection_offsets[i + 1] - intersection_offsets[i]; + }); // FIXME: Find a way to not have to maintain a dataframe_buffer @@ -779,7 +781,6 @@ std::tuple, rmm::device_uvector> k_truss auto vertex_pair_buffer_q_r_edge_p_q = allocate_dataframe_buffer>(intersection_indices.size(), handle.get_stream()); - thrust::tabulate( handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), @@ -819,7 +820,7 @@ std::tuple, rmm::device_uvector> k_truss transposed_edge_first, transposed_edge_first + num_valid_edges, transposed_edge_first + edgelist_srcs.size()}); - + // case 2: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) @@ -842,15 +843,27 @@ std::tuple, rmm::device_uvector> k_truss raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), raft::device_span(num_triangles.data(), num_triangles.size())); - //cugraph::edge_property_t edge_mask(handle, cur_graph_view); - // Set edge property to 'True' for all edges then mask out invalid edges which can be - // significantly smaller than the valid edges - //cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); + + // Remove edges that have a triangle count of zero. Those should not be accounted + // for during the unroling phase. + auto edges_with_triangle_last = + thrust::stable_partition(handle.get_thrust_policy(), + transposed_edge_triangle_count_pair_first, + transposed_edge_triangle_count_pair_first + num_triangles.size(), + [] __device__(auto e) { + auto num_triangles = thrust::get<1>(e); + return num_triangles > 0; + }); + + num_edges_with_triangles = static_cast( + thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); + thrust::sort(handle.get_thrust_policy(), thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, edgelist_dsts.begin() + num_edges_with_triangles), thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); + cugraph::edge_bucket_t edges_with_no_triangle(handle); edges_with_no_triangle.insert(edgelist_srcs.begin() + num_edges_with_triangles, edgelist_srcs.end(), @@ -870,13 +883,29 @@ std::tuple, rmm::device_uvector> k_truss edge_mask.mutable_view(), false); cur_graph_view.attach_edge_mask(edge_mask.view()); + edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); + iteration = iteration + 1; } + + std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts, std::ignore) = decompress_to_edgelist( + handle, + cur_graph_view, + edge_weight_view, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); - return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts)); + printf("\nthe number of edges after = %d\n", edgelist_srcs.size()); + raft::print_device_vector("src ", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); + raft::print_device_vector("dst ", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); + if (edge_weight_view) { + raft::print_device_vector("wgt ", (*edgelist_wgts).data(), edgelist_dsts.size(), std::cout); + } + + return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_wgts)); } } -} // namespace cugraph \ No newline at end of file +} // namespace cugraph diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index 3df5daad1b7..f6e1b63027a 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -17,22 +17,63 @@ #include namespace cugraph { -template std::tuple, rmm::device_uvector> k_truss( - raft::handle_t const& handle, - graph_view_t const& graph_view, - int32_t k, - bool do_expensive_check); - -template std::tuple, rmm::device_uvector> k_truss( - raft::handle_t const& handle, - graph_view_t const& graph_view, - int64_t k, - bool do_expensive_check); - -template std::tuple, rmm::device_uvector> k_truss( -raft::handle_t const& handle, -graph_view_t const& graph_view, -int64_t k, -bool do_expensive_check); + +// SG instantiation + +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + int32_t k, + bool do_expensive_check); + +/* +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + int32_t k, + bool do_expensive_check); + +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + int64_t k, + bool do_expensive_check); + +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + int64_t k, + bool do_expensive_check); + +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + int64_t k, + bool do_expensive_check); + +template std::tuple, + rmm::device_uvector, + std::optional>> +k_truss(raft::handle_t const& handle, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + int64_t k, + bool do_expensive_check); +*/ } // namespace cugraph diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 08c96a2ca96..9fe03c49177 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -72,9 +72,9 @@ class Tests_KTruss : public ::testing::TestWithParam graph(handle); - std::optional> d_renumber_map_labels{std::nullopt}; - std::tie(graph, std::ignore, d_renumber_map_labels) = + //cugraph::graph_t graph(handle); + //std::optional> d_renumber_map_labels{std::nullopt}; + auto [graph, edge_weights, d_renumber_map_labels] = cugraph::test::construct_graph( handle, input_usecase, false, renumber, false, true); @@ -85,13 +85,15 @@ class Tests_KTruss : public ::testing::TestWithParam(handle, graph_view, k_truss_usecase.k, false); + cugraph::k_truss(handle, graph_view, edge_weight_view, k_truss_usecase.k, false); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement @@ -113,7 +115,7 @@ INSTANTIATE_TEST_SUITE_P(file_test, Tests_KTruss_File, ::testing::Combine( // enable correctness checks - ::testing::Values(KTruss_Usecase{4}), + ::testing::Values(KTruss_Usecase{5}), ::testing::Values(cugraph::test::File_Usecase( "/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx")))); From 7cda92ed7f2f70f3f21d1c7ce3acea4d66b19f1b Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 14:44:36 -0700 Subject: [PATCH 089/155] remove unnecessary arguments --- cpp/src/community/k_truss_impl.cuh | 81 ++++++++++++------------------ 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index af615f3f13b..ac19b2f8225 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -123,13 +123,10 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( template edge_t remove_overcompensating_edges(raft::handle_t const& handle, - edge_t num_valid_edges, - edge_t num_invalid_edges, - edge_t dataframe_buffer_size, EdgeIterator&& potential_closing_or_incoming_edges, EdgeIterator&& incoming_or_potential_closing_edges, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts) + raft::device_span invalid_edgelist_srcs, + raft::device_span invalid_edgelist_dsts) { // To avoid over-compensating, check whether the 'potential_closing_edges' // are within the invalid edges. If yes, the was already unrolled @@ -138,14 +135,13 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), thrust::make_zip_iterator( - get_dataframe_buffer_begin(potential_closing_or_incoming_edges) + dataframe_buffer_size, - get_dataframe_buffer_begin(incoming_or_potential_closing_edges) + dataframe_buffer_size), - [num_invalid_edges, - num_valid_edges, - invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, - edgelist_srcs.begin() + num_valid_edges), + get_dataframe_buffer_begin(potential_closing_or_incoming_edges) + size_dataframe_buffer(potential_closing_or_incoming_edges), + get_dataframe_buffer_begin(incoming_or_potential_closing_edges) + size_dataframe_buffer(incoming_or_potential_closing_edges)), + [num_invalid_edges = invalid_edgelist_dsts.size(), + invalid_first = thrust::make_zip_iterator(invalid_edgelist_dsts.begin(), + invalid_edgelist_srcs.begin()), invalid_last = - thrust::make_zip_iterator(edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto e) { + thrust::make_zip_iterator(invalid_edgelist_dsts.end(), invalid_edgelist_srcs.end())] __device__(auto e) { auto potential_edge = thrust::get<0>(e); auto transposed_potential_or_incoming_edge = thrust::make_tuple(thrust::get<1>(potential_edge), thrust::get<0>(potential_edge)); @@ -153,13 +149,8 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, thrust::seq, invalid_first, invalid_last, - transposed_potential_or_incoming_edge); // very important when using lower or upperbound on - // edges that do not exist. Always make sure to - // compare with the queried edges - assert(*itr == transposed_potential_or_incoming_edge); - auto dist = thrust::distance(invalid_first, itr); - if (*itr != transposed_potential_or_incoming_edge) { return false; } - return dist < num_invalid_edges; + transposed_potential_or_incoming_edge); + return (itr != invalid_last && *itr == transposed_potential_or_incoming_edge); }); auto dist = thrust::distance( @@ -212,35 +203,35 @@ void unroll_p_r_or_q_r_edges( thrust::make_counting_iterator(num_invalid_edges), [num_valid_edges, num_invalid_edges, - invalid_first_dst = edgelist_dsts.begin() + num_valid_edges, - invalid_first_src = edgelist_srcs.begin() + num_valid_edges, - src_array_begin_valid = edgelist_srcs.begin(), - dst_array_begin_valid = edgelist_dsts.begin(), + invalid_dst_first = edgelist_dsts.begin() + num_valid_edges, + invalid_src_first = edgelist_srcs.begin() + num_valid_edges, + valid_src_first = edgelist_srcs.begin(), + valid_dst_first = edgelist_dsts.begin(), prefix_sum_valid = prefix_sum_valid.data(), prefix_sum_invalid = prefix_sum_invalid.data(), potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r) ] __device__(auto idx) { - auto src = invalid_first_src[idx]; - auto dst = invalid_first_dst[idx]; - auto dst_array_end_valid = dst_array_begin_valid + num_valid_edges; + auto src = invalid_src_first[idx]; + auto dst = invalid_dst_first[idx]; + auto dst_array_end_valid = valid_dst_first + num_valid_edges; auto itr_lower_valid = - thrust::lower_bound(thrust::seq, dst_array_begin_valid, dst_array_end_valid, dst); + thrust::lower_bound(thrust::seq, valid_dst_first, dst_array_end_valid, dst); auto idx_lower_valid = thrust::distance( - dst_array_begin_valid, + valid_dst_first, itr_lower_valid); // Need a binary search to find the begining of the range - auto invalid_end_dst = invalid_first_dst + num_invalid_edges; + auto invalid_end_dst = invalid_dst_first + num_invalid_edges; auto itr_lower_invalid = - thrust::lower_bound(thrust::seq, invalid_first_dst, invalid_end_dst, dst); + thrust::lower_bound(thrust::seq, invalid_dst_first, invalid_end_dst, dst); auto idx_lower_invalid = thrust::distance( - invalid_first_dst, + invalid_dst_first, itr_lower_invalid); // Need a binary search to find the begining of the range auto incoming_edges_to_r_first_valid = thrust::make_zip_iterator( - src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(dst)); + valid_src_first + idx_lower_valid, thrust::make_constant_iterator(dst)); thrust::copy( thrust::seq, incoming_edges_to_r_first_valid, @@ -248,17 +239,17 @@ void unroll_p_r_or_q_r_edges( incoming_edges_to_r + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); auto incoming_edges_to_r_first_invalid = thrust::make_zip_iterator( - invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(dst)); + invalid_src_first + idx_lower_invalid, thrust::make_constant_iterator(dst)); thrust::copy( thrust::seq, incoming_edges_to_r_first_invalid, incoming_edges_to_r_first_invalid + (prefix_sum_invalid[idx + 1] - prefix_sum_invalid[idx]), - // FIXME remove prefix_sum_valid[idx] as it is substracted + incoming_edges_to_r + prefix_sum_invalid[idx] + prefix_sum_valid[idx + 1]); if constexpr (is_q_r_edge) { auto potential_closing_edges_first_valid = thrust::make_zip_iterator( - src_array_begin_valid + idx_lower_valid, thrust::make_constant_iterator(src)); + valid_src_first + idx_lower_valid, thrust::make_constant_iterator(src)); thrust::copy( thrust::seq, potential_closing_edges_first_valid, @@ -266,7 +257,7 @@ void unroll_p_r_or_q_r_edges( potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); auto potential_closing_edges_first_invalid = thrust::make_zip_iterator( - invalid_first_src + idx_lower_invalid, thrust::make_constant_iterator(src)); + invalid_src_first + idx_lower_invalid, thrust::make_constant_iterator(src)); thrust::copy(thrust::seq, potential_closing_edges_first_invalid, potential_closing_edges_first_invalid + @@ -275,7 +266,7 @@ void unroll_p_r_or_q_r_edges( } else { auto potential_closing_edges_first_valid = thrust::make_zip_iterator( - thrust::make_constant_iterator(src), src_array_begin_valid + idx_lower_valid); + thrust::make_constant_iterator(src), valid_src_first + idx_lower_valid); thrust::copy( thrust::seq, potential_closing_edges_first_valid, @@ -283,7 +274,7 @@ void unroll_p_r_or_q_r_edges( potential_closing_edges + prefix_sum_valid[idx] + prefix_sum_invalid[idx]); auto potential_closing_edges_first_invalid = thrust::make_zip_iterator( - thrust::make_constant_iterator(src), invalid_first_src + idx_lower_invalid); + thrust::make_constant_iterator(src), invalid_src_first + idx_lower_invalid); thrust::copy( thrust::seq, potential_closing_edges_first_invalid, @@ -323,13 +314,10 @@ void unroll_p_r_or_q_r_edges( edge_t num_edges_not_overcomp = remove_overcompensating_edges( handle, - edge_t{num_valid_edges}, - edge_t{num_invalid_edges}, - edge_t{num_edge_exists}, std::move(potential_closing_edges), std::move(incoming_edges_to_r), - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); // Extra check for 'incoming_edges_to_r' if constexpr (!is_q_r_edge) { @@ -338,13 +326,10 @@ void unroll_p_r_or_q_r_edges( num_edges_not_overcomp = remove_overcompensating_edges( handle, - edge_t{num_valid_edges}, - edge_t{num_invalid_edges}, - edge_t{num_edges_not_overcomp}, std::move(incoming_edges_to_r), std::move(potential_closing_edges), - raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), - raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); } // FIXME: Combine both 'thrust::for_each' From a3f83ef624af88831d6fcf788210044688b9fa55 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 20:33:09 -0700 Subject: [PATCH 090/155] rename variable --- cpp/src/community/k_truss_impl.cuh | 60 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index ac19b2f8225..48edb809c68 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -55,14 +55,14 @@ rmm::device_uvector edge_triangle_count( raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); -template +template struct unroll_edge { edge_t num_valid_edges{}; raft::device_span num_triangles{}; - EdgeIterator edge_to_unroll_first{}; - EdgeIterator transposed_valid_edge_first{}; - EdgeIterator transposed_valid_edge_last{}; - EdgeIterator transposed_invalid_edge_last{}; + EdgeBuffer edge_to_unroll_first{}; + EdgeBuffer transposed_valid_edge_first{}; + EdgeBuffer transposed_valid_edge_last{}; + EdgeBuffer transposed_invalid_edge_last{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -121,10 +121,11 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( return prefix_sum; } -template +template edge_t remove_overcompensating_edges(raft::handle_t const& handle, - EdgeIterator&& potential_closing_or_incoming_edges, - EdgeIterator&& incoming_or_potential_closing_edges, + edge_t buffer_size, + EdgeBuffer potential_closing_or_incoming_edges, + EdgeBuffer incoming_or_potential_closing_edges, raft::device_span invalid_edgelist_srcs, raft::device_span invalid_edgelist_dsts) { @@ -132,11 +133,11 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, // are within the invalid edges. If yes, the was already unrolled auto edges_not_overcomp = thrust::remove_if( handle.get_thrust_policy(), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), - get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), + thrust::make_zip_iterator(potential_closing_or_incoming_edges, + incoming_or_potential_closing_edges), thrust::make_zip_iterator( - get_dataframe_buffer_begin(potential_closing_or_incoming_edges) + size_dataframe_buffer(potential_closing_or_incoming_edges), - get_dataframe_buffer_begin(incoming_or_potential_closing_edges) + size_dataframe_buffer(incoming_or_potential_closing_edges)), + potential_closing_or_incoming_edges + buffer_size, + incoming_or_potential_closing_edges + buffer_size), [num_invalid_edges = invalid_edgelist_dsts.size(), invalid_first = thrust::make_zip_iterator(invalid_edgelist_dsts.begin(), invalid_edgelist_srcs.begin()), @@ -154,15 +155,10 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, }); auto dist = thrust::distance( - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_or_incoming_edges), - get_dataframe_buffer_begin(incoming_or_potential_closing_edges)), + thrust::make_zip_iterator(potential_closing_or_incoming_edges, + incoming_or_potential_closing_edges), edges_not_overcomp); - // After pushing the non-existant edges to the second partition, - // remove them by resizing both vertex pair buffer - resize_dataframe_buffer(potential_closing_or_incoming_edges, dist, handle.get_stream()); - resize_dataframe_buffer(incoming_or_potential_closing_edges, dist, handle.get_stream()); - return dist; } @@ -312,24 +308,34 @@ void unroll_p_r_or_q_r_edges( resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); edge_t num_edges_not_overcomp = - remove_overcompensating_edges( + remove_overcompensating_edges( handle, - std::move(potential_closing_edges), - std::move(incoming_edges_to_r), + edge_t{num_edge_exists}, + get_dataframe_buffer_begin(potential_closing_edges), + get_dataframe_buffer_begin(incoming_edges_to_r), raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); + + // After pushing the non-existant edges to the second partition, + // remove them by resizing both vertex pair buffer + resize_dataframe_buffer(potential_closing_edges, num_edges_not_overcomp, handle.get_stream()); + resize_dataframe_buffer(incoming_edges_to_r, num_edges_not_overcomp, handle.get_stream()); // Extra check for 'incoming_edges_to_r' if constexpr (!is_q_r_edge) { // Exchange the arguments (incoming_edges_to_r, num_edges_not_overcomp) order // To also check if the 'incoming_edges_to_r' belong the the invalid_edgelist num_edges_not_overcomp = - remove_overcompensating_edges( + remove_overcompensating_edges( handle, - std::move(incoming_edges_to_r), - std::move(potential_closing_edges), + edge_t{num_edges_not_overcomp}, + get_dataframe_buffer_begin(incoming_edges_to_r), + get_dataframe_buffer_begin(potential_closing_edges), raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); + + resize_dataframe_buffer(potential_closing_edges, num_edges_not_overcomp, handle.get_stream()); + resize_dataframe_buffer(incoming_edges_to_r, num_edges_not_overcomp, handle.get_stream()); } // FIXME: Combine both 'thrust::for_each' @@ -533,6 +539,7 @@ k_truss(raft::handle_t const& handle, modified_graph_view = (*modified_graph).view(); } + // FIXME: Remove because it yields to incorrect results // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core /* { @@ -693,7 +700,6 @@ k_truss(raft::handle_t const& handle, edge_t num_invalid_edges{0}; size_t num_edges_with_triangles{0}; - int iteration = 0; while (true) { // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be @@ -828,7 +834,6 @@ k_truss(raft::handle_t const& handle, raft::device_span(edgelist_dsts.data(), edgelist_dsts.size()), raft::device_span(num_triangles.data(), num_triangles.size())); - // Remove edges that have a triangle count of zero. Those should not be accounted // for during the unroling phase. auto edges_with_triangle_last = @@ -873,7 +878,6 @@ k_truss(raft::handle_t const& handle, edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - iteration = iteration + 1; } std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts, std::ignore) = decompress_to_edgelist( From 3e03ef36391809a29df9277c8aaebeb870eb7cd4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 20:41:55 -0700 Subject: [PATCH 091/155] reorder variable declaration --- cpp/src/community/k_truss_impl.cuh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 48edb809c68..207bb037a60 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -698,10 +698,7 @@ k_truss(raft::handle_t const& handle, cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); cur_graph_view.attach_edge_mask(edge_mask.view()); - edge_t num_invalid_edges{0}; - size_t num_edges_with_triangles{0}; while (true) { - // 'invalid_transposed_edge_triangle_count_first' marks the beginning of the edges to be // removed 'invalid_transposed_edge_triangle_count_first' + edgelist_srcs.size() marks the end // of the edges to be removed 'edge_triangle_count_pair_first' marks the begining of the valid @@ -715,6 +712,7 @@ k_truss(raft::handle_t const& handle, return num_triangles >= k - 2; }); size_t num_edges_with_triangles{0}; + edge_t num_invalid_edges{0}; num_invalid_edges = static_cast( thrust::distance(invalid_transposed_edge_triangle_count_first, transposed_edge_triangle_count_pair_first + edgelist_srcs.size())); From d23a3ad896e949c3a6fb424861c745b9f40b9cad Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 20:42:26 -0700 Subject: [PATCH 092/155] add suupport for more type combination --- cpp/src/community/k_truss_sg.cu | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index f6e1b63027a..b3f701bc7ab 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, NVIDIA CORPORATION. + * Copyright (c) 2023-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. @@ -29,7 +29,6 @@ k_truss(raft::handle_t const& handle, int32_t k, bool do_expensive_check); -/* template std::tuple, rmm::device_uvector, std::optional>> @@ -74,6 +73,5 @@ k_truss(raft::handle_t const& handle, std::optional> edge_weight_view, int64_t k, bool do_expensive_check); -*/ } // namespace cugraph From 3ce4e31b2fc410a71e41bb9ab7e9f8bba1c95a6e Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 21:16:12 -0700 Subject: [PATCH 093/155] remove legacy k_truss call --- cpp/src/c_api/legacy_k_truss.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/cpp/src/c_api/legacy_k_truss.cpp b/cpp/src/c_api/legacy_k_truss.cpp index 90db9fc133c..6adb8351e56 100644 --- a/cpp/src/c_api/legacy_k_truss.cpp +++ b/cpp/src/c_api/legacy_k_truss.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, NVIDIA CORPORATION. + * Copyright (c) 2023-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. @@ -85,23 +85,13 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { rmm::device_uvector dst(0, handle_.get_stream()); std::optional> wgt{std::nullopt}; - std::tie(src, dst, wgt, std::ignore) = cugraph::decompress_to_edgelist( + auto [result_src, result_dst, result_wgt] = cugraph::k_truss( handle_, graph_view, edge_weights ? std::make_optional(edge_weights->view()) : std::nullopt, - std::optional>{std::nullopt}, - std::optional>(std::nullopt), + k_, do_expensive_check_); - auto [result_src, result_dst, result_wgt] = cugraph::k_truss_subgraph( - handle_, - raft::device_span(src.data(), src.size()), - raft::device_span(dst.data(), dst.size()), - wgt ? std::make_optional(raft::device_span(wgt->data(), wgt->size())) - : std::nullopt, - graph_view.number_of_vertices(), - k_); - cugraph::unrenumber_int_vertices( handle_, result_src.data(), From 264a7a203c3b8568f70263916ef4873a1ae9f537 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 21:17:37 -0700 Subject: [PATCH 094/155] update the C tests to account for the fact that the new implementation returns a directed graph --- cpp/tests/c_api/legacy_k_truss_test.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/tests/c_api/legacy_k_truss_test.c b/cpp/tests/c_api/legacy_k_truss_test.c index bc85f568688..8b753aed78f 100644 --- a/cpp/tests/c_api/legacy_k_truss_test.c +++ b/cpp/tests/c_api/legacy_k_truss_test.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, NVIDIA CORPORATION. + * Copyright (c) 2023-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. @@ -172,11 +172,11 @@ int test_k_truss() weight_t h_wgt[] = { 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_result_src[] = {0, 0, 1, 1, 1, 2, 2, 2, 3, 3}; - vertex_t h_result_dst[] = {1, 2, 0, 2, 3, 0, 1, 3, 1, 2}; - weight_t h_result_wgt[] = {0.1, 5.1, 0.1, 3.1, 2.1, 5.1, 3.1, 4.1, 2.1, 4.1}; - size_t h_result_offsets[] = {0, 10}; - size_t num_expected_edges = 10; + vertex_t h_result_src[] = {0, 0, 2, 2, 3}; + vertex_t h_result_dst[] = {1, 2, 1, 3, 1}; + weight_t h_result_wgt[] = {0.1, 5.1, 0.1, 3.1, 2.1}; + size_t h_result_offsets[] = {0, 5}; + size_t num_expected_edges = 5; size_t num_expected_offsets = 2; return generic_k_truss_test(h_src, @@ -203,10 +203,10 @@ int test_k_truss_no_weights() vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - vertex_t h_result_src[] = {0, 0, 1, 1, 1, 2, 2, 2, 3, 3}; - vertex_t h_result_dst[] = {1, 2, 0, 2, 3, 0, 1, 3, 1, 2}; - size_t h_result_offsets[] = {0, 10}; - size_t num_expected_edges = 10; + vertex_t h_result_src[] = {0, 0, 2, 2, 3}; + vertex_t h_result_dst[] = {1, 2, 1, 3, 1}; + size_t h_result_offsets[] = {0, 5}; + size_t num_expected_edges = 5; size_t num_expected_offsets = 2; return generic_k_truss_test(h_src, From a6783402012c8a836e4348a23a4f6088a425ed4a Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 21:21:48 -0700 Subject: [PATCH 095/155] remove legacy k_truss --- cpp/CMakeLists.txt | 1 - cpp/include/cugraph/algorithms.hpp | 35 ------- cpp/src/c_api/legacy_k_truss.cpp | 143 ----------------------------- 3 files changed, 179 deletions(-) delete mode 100644 cpp/src/c_api/legacy_k_truss.cpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index ea3bbf7b1fd..5dc9f026092 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -451,7 +451,6 @@ add_library(cugraph_c src/c_api/weakly_connected_components.cpp src/c_api/strongly_connected_components.cpp src/c_api/allgather.cpp - src/c_api/legacy_k_truss.cpp ) add_library(cugraph::cugraph_c ALIAS cugraph_c) diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index c01764be665..7024b84f34d 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.hpp @@ -427,41 +427,6 @@ void connected_components(legacy::GraphCSRView const& graph, cugraph_cc_t connectivity_type, VT* labels); -/** - * @brief Compute k truss for a graph ** temporary - * - * K Truss is the maximal subgraph of a graph which contains at least three - * vertices where every edge is incident to at least k-2 triangles. - * - * This version is a temporary solution to clean up python integration through the C API. - * - * This version is only supported SG. - * - * @throws cugraph::logic_error with a custom message when an error - * occurs. - * - * @tparam vertex_t Type of vertex identifiers. Supported value : int (signed, 32-bit) - * @tparam weight_t Type of edge weights. Supported values : float or double. - * - * @param[in] handle Library handle (RAFT). - * @param[in] src Source vertices from COO - * @param[in] dst Destination vertices from COO - * @param[in] wgt Optional edge weights from COO - * @param[in] k The order of the truss - * @return Tuple containing extracted src, dst and optional weights for the - * subgraph - */ -template -std::tuple, - rmm::device_uvector, - std::optional>> -k_truss_subgraph(raft::handle_t const& handle, - raft::device_span src, - raft::device_span dst, - std::optional> wgt, - size_t number_of_vertices, - int k); - /** * @brief Compute Hungarian algorithm on a weighted bipartite graph * diff --git a/cpp/src/c_api/legacy_k_truss.cpp b/cpp/src/c_api/legacy_k_truss.cpp deleted file mode 100644 index 6adb8351e56..00000000000 --- a/cpp/src/c_api/legacy_k_truss.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2023-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 - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace { - -struct k_truss_functor : public cugraph::c_api::abstract_functor { - raft::handle_t const& handle_; - cugraph::c_api::cugraph_graph_t* graph_; - size_t k_; - bool do_expensive_check_; - cugraph::c_api::cugraph_induced_subgraph_result_t* result_{}; - - k_truss_functor(::cugraph_resource_handle_t const* handle, - ::cugraph_graph_t* graph, - size_t k, - bool do_expensive_check) - : abstract_functor(), - handle_(*reinterpret_cast(handle)->handle_), - graph_(reinterpret_cast(graph)), - k_(k), - do_expensive_check_(do_expensive_check) - { - } - - template - void operator()() - { - if constexpr (!cugraph::is_candidate_legacy::value) { - unsupported(); - } else if constexpr (multi_gpu) { - unsupported(); - } else { - // k_truss expects store_transposed == false - if constexpr (store_transposed) { - error_code_ = cugraph::c_api:: - transpose_storage( - handle_, graph_, error_.get()); - if (error_code_ != CUGRAPH_SUCCESS) return; - } - - auto graph = - reinterpret_cast*>(graph_->graph_); - - auto edge_weights = reinterpret_cast< - cugraph::edge_property_t, - weight_t>*>(graph_->edge_weights_); - - auto number_map = reinterpret_cast*>(graph_->number_map_); - - auto graph_view = graph->view(); - rmm::device_uvector src(0, handle_.get_stream()); - rmm::device_uvector dst(0, handle_.get_stream()); - std::optional> wgt{std::nullopt}; - - auto [result_src, result_dst, result_wgt] = cugraph::k_truss( - handle_, - graph_view, - edge_weights ? std::make_optional(edge_weights->view()) : std::nullopt, - k_, - do_expensive_check_); - - cugraph::unrenumber_int_vertices( - handle_, - result_src.data(), - result_src.size(), - number_map->data(), - graph_view.vertex_partition_range_lasts(), - do_expensive_check_); - - cugraph::unrenumber_int_vertices( - handle_, - result_dst.data(), - result_dst.size(), - number_map->data(), - graph_view.vertex_partition_range_lasts(), - do_expensive_check_); - - rmm::device_uvector edge_offsets(2, handle_.get_stream()); - std::vector h_edge_offsets{{0, result_src.size()}}; - raft::update_device( - edge_offsets.data(), h_edge_offsets.data(), h_edge_offsets.size(), handle_.get_stream()); - - // FIXME: Add support for edge_id and edge_type_id. - result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{ - new cugraph::c_api::cugraph_type_erased_device_array_t(result_src, graph_->vertex_type_), - new cugraph::c_api::cugraph_type_erased_device_array_t(result_dst, graph_->vertex_type_), - wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, - graph_->weight_type_) - : NULL, - NULL, - NULL, - new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, - cugraph_data_type_id_t::SIZE_T)}; - } - } -}; - -} // namespace - -extern "C" cugraph_error_code_t cugraph_k_truss_subgraph(const cugraph_resource_handle_t* handle, - cugraph_graph_t* graph, - size_t k, - bool_t do_expensive_check, - cugraph_induced_subgraph_result_t** result, - cugraph_error_t** error) -{ - k_truss_functor functor(handle, graph, k, do_expensive_check); - - return cugraph::c_api::run_algorithm(graph, functor, result, error); -} From dbf220edf038bb124807a1fd928eeb4f0f0db13c Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 21:33:56 -0700 Subject: [PATCH 096/155] remove unused code and invalid type combination --- .../community/edge_triangle_count_impl.cuh | 1 - cpp/src/community/edge_triangle_count_sg.cu | 20 ------------------- 2 files changed, 21 deletions(-) diff --git a/cpp/src/community/edge_triangle_count_impl.cuh b/cpp/src/community/edge_triangle_count_impl.cuh index 315353b799b..69cb928f4aa 100644 --- a/cpp/src/community/edge_triangle_count_impl.cuh +++ b/cpp/src/community/edge_triangle_count_impl.cuh @@ -113,7 +113,6 @@ std::enable_if_t> edge_triangle_count_im std::array{true, true}, false /*FIXME: pass 'do_expensive_check' as argument*/); - // FIXME: invalid type for unsigned integers. rmm::device_uvector num_triangles(edgelist_srcs.size(), handle.get_stream()); // Update the number of triangles of each (p, q) edges by looking at their intersection diff --git a/cpp/src/community/edge_triangle_count_sg.cu b/cpp/src/community/edge_triangle_count_sg.cu index 640beee9fcd..c781b1fe425 100644 --- a/cpp/src/community/edge_triangle_count_sg.cu +++ b/cpp/src/community/edge_triangle_count_sg.cu @@ -36,24 +36,4 @@ template rmm::device_uvector edge_triangle_count( raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); -template rmm::device_uvector edge_triangle_count( - raft::handle_t const& handle, - cugraph::graph_view_t const& graph_view, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts); - -/* -template rmm::device_uvector edge_triangle_count( - raft::handle_t const& handle, - cugraph::graph_view_t const& graph_view, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts); - -template rmm::device_uvector edge_triangle_count( - raft::handle_t const& handle, - cugraph::graph_view_t const& graph_view, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts); -*/ - } // namespace cugraph From 5768ed399bb3f7d5c124dfbc531fdb7083214abc Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 21:43:45 -0700 Subject: [PATCH 097/155] fix style --- cpp/src/c_api/k_truss.cpp | 23 +-- .../community/edge_triangle_count_impl.cuh | 2 +- cpp/src/community/edge_triangle_count_sg.cu | 2 +- cpp/src/community/k_truss_impl.cuh | 147 +++++++++--------- cpp/src/community/k_truss_sg.cu | 4 +- cpp/tests/community/k_truss_test.cpp | 7 +- 6 files changed, 93 insertions(+), 92 deletions(-) diff --git a/cpp/src/c_api/k_truss.cpp b/cpp/src/c_api/k_truss.cpp index 6adb8351e56..ca08dfe3e0f 100644 --- a/cpp/src/c_api/k_truss.cpp +++ b/cpp/src/c_api/k_truss.cpp @@ -16,17 +16,17 @@ #include +#include +#include +#include +#include + #include #include #include #include #include -#include -#include -#include -#include - #include namespace { @@ -85,12 +85,13 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { rmm::device_uvector dst(0, handle_.get_stream()); std::optional> wgt{std::nullopt}; - auto [result_src, result_dst, result_wgt] = cugraph::k_truss( - handle_, - graph_view, - edge_weights ? std::make_optional(edge_weights->view()) : std::nullopt, - k_, - do_expensive_check_); + auto [result_src, result_dst, result_wgt] = + cugraph::k_truss( + handle_, + graph_view, + edge_weights ? std::make_optional(edge_weights->view()) : std::nullopt, + k_, + do_expensive_check_); cugraph::unrenumber_int_vertices( handle_, diff --git a/cpp/src/community/edge_triangle_count_impl.cuh b/cpp/src/community/edge_triangle_count_impl.cuh index 69cb928f4aa..fc42b1ade75 100644 --- a/cpp/src/community/edge_triangle_count_impl.cuh +++ b/cpp/src/community/edge_triangle_count_impl.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2023, NVIDIA CORPORATION. + * Copyright (c) 2020-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. diff --git a/cpp/src/community/edge_triangle_count_sg.cu b/cpp/src/community/edge_triangle_count_sg.cu index c781b1fe425..69ca5233653 100644 --- a/cpp/src/community/edge_triangle_count_sg.cu +++ b/cpp/src/community/edge_triangle_count_sg.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, NVIDIA CORPORATION. + * Copyright (c) 2021-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. diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 207bb037a60..a931c90231e 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -38,16 +38,16 @@ #include #include -#include #include #include #include #include +#include #include namespace cugraph { -//FIXME : This will be deleted once edge_triangle_count becomes public +// FIXME : This will be deleted once edge_triangle_count becomes public template rmm::device_uvector edge_triangle_count( raft::handle_t const& handle, @@ -100,21 +100,22 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( { rmm::device_uvector prefix_sum(invalid_dsts.size() + 1, handle.get_stream()); - thrust::tabulate( - handle.get_thrust_policy(), - prefix_sum.begin(), - prefix_sum.begin() + invalid_dsts.size(), - [invalid_dsts, num_edges=edgelist_dsts.size(), edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { - auto itr_lower_valid = thrust::lower_bound( - thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dsts[idx]); + thrust::tabulate(handle.get_thrust_policy(), + prefix_sum.begin(), + prefix_sum.begin() + invalid_dsts.size(), + [invalid_dsts, + num_edges = edgelist_dsts.size(), + edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { + auto itr_lower_valid = thrust::lower_bound( + thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dsts[idx]); - auto itr_upper_valid = thrust::upper_bound( - thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dsts[idx]); + auto itr_upper_valid = thrust::upper_bound( + thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dsts[idx]); - auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); + auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); - return dist_valid; - }); + return dist_valid; + }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); @@ -135,42 +136,36 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, handle.get_thrust_policy(), thrust::make_zip_iterator(potential_closing_or_incoming_edges, incoming_or_potential_closing_edges), - thrust::make_zip_iterator( - potential_closing_or_incoming_edges + buffer_size, - incoming_or_potential_closing_edges + buffer_size), + thrust::make_zip_iterator(potential_closing_or_incoming_edges + buffer_size, + incoming_or_potential_closing_edges + buffer_size), [num_invalid_edges = invalid_edgelist_dsts.size(), - invalid_first = thrust::make_zip_iterator(invalid_edgelist_dsts.begin(), - invalid_edgelist_srcs.begin()), - invalid_last = - thrust::make_zip_iterator(invalid_edgelist_dsts.end(), invalid_edgelist_srcs.end())] __device__(auto e) { + invalid_first = + thrust::make_zip_iterator(invalid_edgelist_dsts.begin(), invalid_edgelist_srcs.begin()), + invalid_last = thrust::make_zip_iterator(invalid_edgelist_dsts.end(), + invalid_edgelist_srcs.end())] __device__(auto e) { auto potential_edge = thrust::get<0>(e); auto transposed_potential_or_incoming_edge = thrust::make_tuple(thrust::get<1>(potential_edge), thrust::get<0>(potential_edge)); auto itr = thrust::lower_bound( - thrust::seq, - invalid_first, - invalid_last, - transposed_potential_or_incoming_edge); + thrust::seq, invalid_first, invalid_last, transposed_potential_or_incoming_edge); return (itr != invalid_last && *itr == transposed_potential_or_incoming_edge); }); - auto dist = thrust::distance( - thrust::make_zip_iterator(potential_closing_or_incoming_edges, - incoming_or_potential_closing_edges), - edges_not_overcomp); + auto dist = thrust::distance(thrust::make_zip_iterator(potential_closing_or_incoming_edges, + incoming_or_potential_closing_edges), + edges_not_overcomp); return dist; } template -void unroll_p_r_or_q_r_edges( - raft::handle_t const& handle, - graph_view_t& graph_view, - edge_t num_invalid_edges, - edge_t num_valid_edges, - raft::device_span edgelist_srcs, - raft::device_span edgelist_dsts, - raft::device_span num_triangles) +void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, + graph_view_t& graph_view, + edge_t num_invalid_edges, + edge_t num_valid_edges, + raft::device_span edgelist_srcs, + raft::device_span edgelist_dsts, + raft::device_span num_triangles) { auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( handle, @@ -192,7 +187,7 @@ void unroll_p_r_or_q_r_edges( prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); - //const bool const_is_q_r_edge = is_q_r_edge; + // const bool const_is_q_r_edge = is_q_r_edge; thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -201,13 +196,12 @@ void unroll_p_r_or_q_r_edges( num_invalid_edges, invalid_dst_first = edgelist_dsts.begin() + num_valid_edges, invalid_src_first = edgelist_srcs.begin() + num_valid_edges, - valid_src_first = edgelist_srcs.begin(), + valid_src_first = edgelist_srcs.begin(), valid_dst_first = edgelist_dsts.begin(), prefix_sum_valid = prefix_sum_valid.data(), prefix_sum_invalid = prefix_sum_invalid.data(), potential_closing_edges = get_dataframe_buffer_begin(potential_closing_edges), - incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r) - ] __device__(auto idx) { + incoming_edges_to_r = get_dataframe_buffer_begin(incoming_edges_to_r)] __device__(auto idx) { auto src = invalid_src_first[idx]; auto dst = invalid_dst_first[idx]; auto dst_array_end_valid = valid_dst_first + num_valid_edges; @@ -308,14 +302,16 @@ void unroll_p_r_or_q_r_edges( resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); edge_t num_edges_not_overcomp = - remove_overcompensating_edges( + remove_overcompensating_edges( handle, edge_t{num_edge_exists}, get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r), raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); - + // After pushing the non-existant edges to the second partition, // remove them by resizing both vertex pair buffer resize_dataframe_buffer(potential_closing_edges, num_edges_not_overcomp, handle.get_stream()); @@ -326,16 +322,20 @@ void unroll_p_r_or_q_r_edges( // Exchange the arguments (incoming_edges_to_r, num_edges_not_overcomp) order // To also check if the 'incoming_edges_to_r' belong the the invalid_edgelist num_edges_not_overcomp = - remove_overcompensating_edges( + remove_overcompensating_edges( handle, edge_t{num_edges_not_overcomp}, get_dataframe_buffer_begin(incoming_edges_to_r), get_dataframe_buffer_begin(potential_closing_edges), - raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), - raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); - + raft::device_span(edgelist_srcs.data() + num_valid_edges, + num_invalid_edges), + raft::device_span(edgelist_dsts.data() + num_valid_edges, + num_invalid_edges)); + resize_dataframe_buffer(potential_closing_edges, num_edges_not_overcomp, handle.get_stream()); - resize_dataframe_buffer(incoming_edges_to_r, num_edges_not_overcomp, handle.get_stream()); + resize_dataframe_buffer(incoming_edges_to_r, num_edges_not_overcomp, handle.get_stream()); } // FIXME: Combine both 'thrust::for_each' @@ -679,20 +679,19 @@ k_truss(raft::handle_t const& handle, cur_graph_view, raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - + auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); - - auto edge_first = - thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); + + auto edge_first = thrust::make_zip_iterator(edgelist_srcs.begin(), edgelist_dsts.begin()); auto transposed_edge_triangle_count_pair_first = thrust::make_zip_iterator(transposed_edge_first, num_triangles.begin()); - + thrust::sort_by_key(handle.get_thrust_policy(), - transposed_edge_first, - transposed_edge_first + edgelist_srcs.size(), - num_triangles.begin()); + transposed_edge_first, + transposed_edge_first + edgelist_srcs.size(), + num_triangles.begin()); cugraph::edge_property_t edge_mask(handle, cur_graph_view); cugraph::fill_edge_property(handle, cur_graph_view, true, edge_mask); @@ -729,15 +728,15 @@ k_truss(raft::handle_t const& handle, edge_first + num_valid_edges, edge_first + edgelist_srcs.size(), num_triangles.begin() + num_valid_edges); - - auto [intersection_offsets, intersection_indices] = detail::nbr_intersection( - handle, - cur_graph_view, - cugraph::edge_dummy_property_t{}.view(), - edge_first + num_valid_edges, - edge_first + edgelist_srcs.size(), - std::array{true, true}, - do_expensive_check); + + auto [intersection_offsets, intersection_indices] = + detail::nbr_intersection(handle, + cur_graph_view, + cugraph::edge_dummy_property_t{}.view(), + edge_first + num_valid_edges, + edge_first + edgelist_srcs.size(), + std::array{true, true}, + do_expensive_check); // Update the number of triangles of each (p, q) edges by looking at their intersection // size. @@ -745,10 +744,11 @@ k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(num_invalid_edges), - [num_triangles = raft::device_span(num_triangles.data() + num_valid_edges, num_invalid_edges), intersection_offsets = raft::device_span(intersection_offsets.data(), intersection_offsets.size())]__device__(auto i) { - + [num_triangles = + raft::device_span(num_triangles.data() + num_valid_edges, num_invalid_edges), + intersection_offsets = raft::device_span( + intersection_offsets.data(), intersection_offsets.size())] __device__(auto i) { num_triangles[i] -= intersection_offsets[i + 1] - intersection_offsets[i]; - }); // FIXME: Find a way to not have to maintain a dataframe_buffer @@ -809,7 +809,7 @@ k_truss(raft::handle_t const& handle, transposed_edge_first, transposed_edge_first + num_valid_edges, transposed_edge_first + edgelist_srcs.size()}); - + // case 2: unroll (q, r) // For each (q, r) edges to unroll, find the incoming edges to 'r' let's say from 'p' and // create the pair (p, q) @@ -850,7 +850,6 @@ k_truss(raft::handle_t const& handle, thrust::make_zip_iterator(edgelist_srcs.begin() + num_edges_with_triangles, edgelist_dsts.begin() + num_edges_with_triangles), thrust::make_zip_iterator(edgelist_srcs.end(), edgelist_dsts.end())); - cugraph::edge_bucket_t edges_with_no_triangle(handle); edges_with_no_triangle.insert(edgelist_srcs.begin() + num_edges_with_triangles, @@ -869,13 +868,12 @@ k_truss(raft::handle_t const& handle, return false; }, edge_mask.mutable_view(), - false); + false); cur_graph_view.attach_edge_mask(edge_mask.view()); edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); edgelist_dsts.resize(num_edges_with_triangles, handle.get_stream()); num_triangles.resize(num_edges_with_triangles, handle.get_stream()); - } std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts, std::ignore) = decompress_to_edgelist( @@ -884,7 +882,7 @@ k_truss(raft::handle_t const& handle, edge_weight_view, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - + printf("\nthe number of edges after = %d\n", edgelist_srcs.size()); raft::print_device_vector("src ", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); raft::print_device_vector("dst ", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); @@ -892,7 +890,8 @@ k_truss(raft::handle_t const& handle, raft::print_device_vector("wgt ", (*edgelist_wgts).data(), edgelist_dsts.size(), std::cout); } - return std::make_tuple(std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_wgts)); + return std::make_tuple( + std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_wgts)); } } } // namespace cugraph diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index b3f701bc7ab..66306423481 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -20,7 +20,7 @@ namespace cugraph { // SG instantiation -template std::tuple, +template std::tuple, rmm::device_uvector, std::optional>> k_truss(raft::handle_t const& handle, @@ -29,7 +29,7 @@ k_truss(raft::handle_t const& handle, int32_t k, bool do_expensive_check); -template std::tuple, +template std::tuple, rmm::device_uvector, std::optional>> k_truss(raft::handle_t const& handle, diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 9fe03c49177..81cce80b43f 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -72,8 +72,8 @@ class Tests_KTruss : public ::testing::TestWithParam graph(handle); - //std::optional> d_renumber_map_labels{std::nullopt}; + // cugraph::graph_t graph(handle); + // std::optional> d_renumber_map_labels{std::nullopt}; auto [graph, edge_weights, d_renumber_map_labels] = cugraph::test::construct_graph( handle, input_usecase, false, renumber, false, true); @@ -93,7 +93,8 @@ class Tests_KTruss : public ::testing::TestWithParam(handle, graph_view, edge_weight_view, k_truss_usecase.k, false); + cugraph::k_truss( + handle, graph_view, edge_weight_view, k_truss_usecase.k, false); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement From 3187183dcc944553e531d38ce3e7c6bdfa52e921 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 21:56:29 -0700 Subject: [PATCH 098/155] update copyright --- cpp/src/community/edge_triangle_count_impl.cuh | 2 +- cpp/src/community/edge_triangle_count_sg.cu | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/community/edge_triangle_count_impl.cuh b/cpp/src/community/edge_triangle_count_impl.cuh index fc42b1ade75..30d1da8b5aa 100644 --- a/cpp/src/community/edge_triangle_count_impl.cuh +++ b/cpp/src/community/edge_triangle_count_impl.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2024, NVIDIA CORPORATION. + * Copyright (c) 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. diff --git a/cpp/src/community/edge_triangle_count_sg.cu b/cpp/src/community/edge_triangle_count_sg.cu index 69ca5233653..486bd42705e 100644 --- a/cpp/src/community/edge_triangle_count_sg.cu +++ b/cpp/src/community/edge_triangle_count_sg.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024, NVIDIA CORPORATION. + * Copyright (c) 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. From 9c2c19dcd5a220be036ed83756307f17029e0f71 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 23:11:14 -0700 Subject: [PATCH 099/155] rename legacy k-truss tests --- cpp/CMakeLists.txt | 1 + cpp/src/c_api/k_truss.cpp | 2 +- cpp/tests/CMakeLists.txt | 2 +- cpp/tests/c_api/{legacy_k_truss_test.c => k_truss_test.c} | 0 4 files changed, 3 insertions(+), 2 deletions(-) rename cpp/tests/c_api/{legacy_k_truss_test.c => k_truss_test.c} (100%) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index b0dea955389..bcd2582bedf 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -429,6 +429,7 @@ add_library(cugraph_c src/c_api/eigenvector_centrality.cpp src/c_api/betweenness_centrality.cpp src/c_api/core_number.cpp + src/c_api/k_truss.cpp src/c_api/core_result.cpp src/c_api/extract_ego.cpp src/c_api/ecg.cpp diff --git a/cpp/src/c_api/k_truss.cpp b/cpp/src/c_api/k_truss.cpp index ca08dfe3e0f..94c99d4837d 100644 --- a/cpp/src/c_api/k_truss.cpp +++ b/cpp/src/c_api/k_truss.cpp @@ -118,7 +118,7 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { result_ = new cugraph::c_api::cugraph_induced_subgraph_result_t{ new cugraph::c_api::cugraph_type_erased_device_array_t(result_src, graph_->vertex_type_), new cugraph::c_api::cugraph_type_erased_device_array_t(result_dst, graph_->vertex_type_), - wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, + result_wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, graph_->weight_type_) : NULL, NULL, diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index d06d9ae0f85..418b320cba8 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -771,7 +771,7 @@ ConfigureCTest(CAPI_INDUCED_SUBGRAPH_TEST c_api/induced_subgraph_test.c) ConfigureCTest(CAPI_DEGREES c_api/degrees_test.c) ConfigureCTest(CAPI_EGONET_TEST c_api/egonet_test.c) ConfigureCTest(CAPI_TWO_HOP_NEIGHBORS_TEST c_api/two_hop_neighbors_test.c) -ConfigureCTest(CAPI_LEGACY_K_TRUSS_TEST c_api/legacy_k_truss_test.c) +ConfigureCTest(CAPI_LEGACY_K_TRUSS_TEST c_api/k_truss_test.c) if (BUILD_CUGRAPH_MTMG_TESTS) ################################################################################################### diff --git a/cpp/tests/c_api/legacy_k_truss_test.c b/cpp/tests/c_api/k_truss_test.c similarity index 100% rename from cpp/tests/c_api/legacy_k_truss_test.c rename to cpp/tests/c_api/k_truss_test.c From 7c2f4c4803ddd5b0516670a6588f3bd219f3f7f6 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 23:18:15 -0700 Subject: [PATCH 100/155] remove debug prints --- cpp/src/community/k_truss_impl.cuh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index a931c90231e..1b50b76de6d 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -883,13 +883,6 @@ k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - printf("\nthe number of edges after = %d\n", edgelist_srcs.size()); - raft::print_device_vector("src ", edgelist_srcs.data(), edgelist_srcs.size(), std::cout); - raft::print_device_vector("dst ", edgelist_dsts.data(), edgelist_dsts.size(), std::cout); - if (edge_weight_view) { - raft::print_device_vector("wgt ", (*edgelist_wgts).data(), edgelist_dsts.size(), std::cout); - } - return std::make_tuple( std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_wgts)); } From ee1d8c2f114b3056412d2a7548f0e1ac933c970d Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 23:20:16 -0700 Subject: [PATCH 101/155] fix style --- cpp/src/c_api/k_truss.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/c_api/k_truss.cpp b/cpp/src/c_api/k_truss.cpp index 94c99d4837d..46b1f2da9a6 100644 --- a/cpp/src/c_api/k_truss.cpp +++ b/cpp/src/c_api/k_truss.cpp @@ -119,8 +119,8 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { new cugraph::c_api::cugraph_type_erased_device_array_t(result_src, graph_->vertex_type_), new cugraph::c_api::cugraph_type_erased_device_array_t(result_dst, graph_->vertex_type_), result_wgt ? new cugraph::c_api::cugraph_type_erased_device_array_t(*result_wgt, - graph_->weight_type_) - : NULL, + graph_->weight_type_) + : NULL, NULL, NULL, new cugraph::c_api::cugraph_type_erased_device_array_t(edge_offsets, From 813aa83568fe0235c42d19a088f1b5d343a3f9b8 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 23:39:33 -0700 Subject: [PATCH 102/155] update test dataset path --- cpp/tests/community/k_truss_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 81cce80b43f..7877bc8c531 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -118,7 +118,7 @@ INSTANTIATE_TEST_SUITE_P(file_test, // enable correctness checks ::testing::Values(KTruss_Usecase{5}), ::testing::Values(cugraph::test::File_Usecase( - "/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx")))); + "test/datasets/dolphins.mtx")))); /// home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx CUGRAPH_TEST_PROGRAM_MAIN() From 71b89a5beb1c788c7fe61c4280524a933a9f0e26 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 23:51:23 -0700 Subject: [PATCH 103/155] fix style --- cpp/tests/community/k_truss_test.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 7877bc8c531..c01ff72b542 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -117,8 +117,6 @@ INSTANTIATE_TEST_SUITE_P(file_test, ::testing::Combine( // enable correctness checks ::testing::Values(KTruss_Usecase{5}), - ::testing::Values(cugraph::test::File_Usecase( - "test/datasets/dolphins.mtx")))); + ::testing::Values(cugraph::test::File_Usecase("test/datasets/dolphins.mtx")))); -/// home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx CUGRAPH_TEST_PROGRAM_MAIN() From bfca100cf8dd421a6872b21ccb87cb446d9d17fb Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 23:53:48 -0700 Subject: [PATCH 104/155] rename test --- cpp/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 418b320cba8..d36c722db2d 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -771,7 +771,7 @@ ConfigureCTest(CAPI_INDUCED_SUBGRAPH_TEST c_api/induced_subgraph_test.c) ConfigureCTest(CAPI_DEGREES c_api/degrees_test.c) ConfigureCTest(CAPI_EGONET_TEST c_api/egonet_test.c) ConfigureCTest(CAPI_TWO_HOP_NEIGHBORS_TEST c_api/two_hop_neighbors_test.c) -ConfigureCTest(CAPI_LEGACY_K_TRUSS_TEST c_api/k_truss_test.c) +ConfigureCTest(CAPI_K_TRUSS_TEST c_api/k_truss_test.c) if (BUILD_CUGRAPH_MTMG_TESTS) ################################################################################################### From cdf328b8447761125524e894e70b1bc8472044f4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 15 Mar 2024 23:56:51 -0700 Subject: [PATCH 105/155] fix style --- cpp/tests/community/k_truss_test.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index c01ff72b542..9d82bea107f 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -112,11 +112,12 @@ TEST_P(Tests_KTruss_File, CheckInt32Int32) run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); } -INSTANTIATE_TEST_SUITE_P(file_test, - Tests_KTruss_File, - ::testing::Combine( - // enable correctness checks - ::testing::Values(KTruss_Usecase{5}), - ::testing::Values(cugraph::test::File_Usecase("test/datasets/dolphins.mtx")))); +INSTANTIATE_TEST_SUITE_P( + file_test, + Tests_KTruss_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(KTruss_Usecase{5}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/dolphins.mtx")))); CUGRAPH_TEST_PROGRAM_MAIN() From c966eba5762bfc7a2b3bee39814016ce677aabf2 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sat, 16 Mar 2024 12:27:04 -0700 Subject: [PATCH 106/155] symmetrize edgelist to be consistant with the python API --- cpp/src/community/k_truss_impl.cuh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 1b50b76de6d..5acc98caa1e 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -883,6 +883,10 @@ k_truss(raft::handle_t const& handle, std::optional>{std::nullopt}, std::optional>(std::nullopt)); + std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts) = + symmetrize_edgelist( + handle, std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(*edgelist_wgts), false); + return std::make_tuple( std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_wgts)); } From e926faed0d4e0d120c3b74d77e7de44099372ade Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sat, 16 Mar 2024 12:27:42 -0700 Subject: [PATCH 107/155] temporarily update tests --- python/nx-cugraph/nx_cugraph/tests/test_ktruss.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py index 92fe2360688..d3df798f508 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py @@ -25,6 +25,9 @@ def test_k_truss(get_graph): for k in range(6): Hnx = nx.k_truss(Gnx, k) Hcg = nxcg.k_truss(Gcg, k) - assert nx.utils.graphs_equal(Hnx, nxcg.to_networkx(Hcg)) + # FIXME: nx.utils.graphs_equal returns False for dataset + # 'les_miserables_graph' after k = 3 eventhough the number of + # vertices and edges match + assert nx.is_isomorphic(Hnx, nxcg.to_networkx(Hcg)) if Hnx.number_of_edges() == 0: break From 0704ebafaacd42977baca80936e259862a4c13e3 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sat, 16 Mar 2024 12:30:17 -0700 Subject: [PATCH 108/155] fix style --- cpp/src/community/k_truss_impl.cuh | 8 ++++++-- python/nx-cugraph/nx_cugraph/tests/test_ktruss.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 5acc98caa1e..25db847eef4 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -884,8 +884,12 @@ k_truss(raft::handle_t const& handle, std::optional>(std::nullopt)); std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts) = - symmetrize_edgelist( - handle, std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(*edgelist_wgts), false); + symmetrize_edgelist( + handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(*edgelist_wgts), + false); return std::make_tuple( std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_wgts)); diff --git a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py index d3df798f508..b8a1379edf5 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023, NVIDIA CORPORATION. +# Copyright (c) 2023-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 From 4954e240c1ab37adb5343993401e93aa9e2f9975 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 17 Mar 2024 14:19:12 -0700 Subject: [PATCH 109/155] remove unused variables and propertly move edge weights --- cpp/src/c_api/k_truss.cpp | 3 --- cpp/src/community/k_truss_impl.cuh | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/cpp/src/c_api/k_truss.cpp b/cpp/src/c_api/k_truss.cpp index 46b1f2da9a6..08f7001413e 100644 --- a/cpp/src/c_api/k_truss.cpp +++ b/cpp/src/c_api/k_truss.cpp @@ -81,9 +81,6 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { auto number_map = reinterpret_cast*>(graph_->number_map_); auto graph_view = graph->view(); - rmm::device_uvector src(0, handle_.get_stream()); - rmm::device_uvector dst(0, handle_.get_stream()); - std::optional> wgt{std::nullopt}; auto [result_src, result_dst, result_wgt] = cugraph::k_truss( diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 25db847eef4..f451b251bc5 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -888,7 +888,7 @@ k_truss(raft::handle_t const& handle, handle, std::move(edgelist_srcs), std::move(edgelist_dsts), - std::move(*edgelist_wgts), + std::move(edgelist_wgts), false); return std::make_tuple( From 49433a768596a2b677caa90b5bc18a88424ed133 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 18 Mar 2024 08:28:04 -0700 Subject: [PATCH 110/155] update C tests --- cpp/tests/c_api/k_truss_test.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp/tests/c_api/k_truss_test.c b/cpp/tests/c_api/k_truss_test.c index 8b753aed78f..57dd74a56c6 100644 --- a/cpp/tests/c_api/k_truss_test.c +++ b/cpp/tests/c_api/k_truss_test.c @@ -172,11 +172,11 @@ int test_k_truss() weight_t h_wgt[] = { 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_result_src[] = {0, 0, 2, 2, 3}; - vertex_t h_result_dst[] = {1, 2, 1, 3, 1}; - weight_t h_result_wgt[] = {0.1, 5.1, 0.1, 3.1, 2.1}; - size_t h_result_offsets[] = {0, 5}; - size_t num_expected_edges = 5; + vertex_t h_result_src[] = {0, 0, 2, 2, 3, 1, 2, 1, 3, 1}; + vertex_t h_result_dst[] = {1, 2, 1, 3, 1, 0, 0, 2, 2, 3}; + weight_t h_result_wgt[] = {0.1, 5.1, 0.1, 3.1, 2.1, 0.1, 5.1, 0.1, 3.1, 2.1}; + size_t h_result_offsets[] = {0, 10}; + size_t num_expected_edges = 10; size_t num_expected_offsets = 2; return generic_k_truss_test(h_src, @@ -203,10 +203,10 @@ int test_k_truss_no_weights() vertex_t h_src[] = {0, 1, 1, 2, 2, 2, 3, 4, 1, 3, 4, 0, 1, 3, 5, 5}; vertex_t h_dst[] = {1, 3, 4, 0, 1, 3, 5, 5, 0, 1, 1, 2, 2, 2, 3, 4}; - vertex_t h_result_src[] = {0, 0, 2, 2, 3}; - vertex_t h_result_dst[] = {1, 2, 1, 3, 1}; - size_t h_result_offsets[] = {0, 5}; - size_t num_expected_edges = 5; + vertex_t h_result_src[] = {0, 0, 2, 2, 3, 1, 2, 1, 3, 1}; + vertex_t h_result_dst[] = {1, 2, 1, 3, 1, 0, 0, 2, 2, 3}; + size_t h_result_offsets[] = {0, 10}; + size_t num_expected_edges = 10; size_t num_expected_offsets = 2; return generic_k_truss_test(h_src, From 7858b3eae0a046b5fa601ab24f76406cc8398b32 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 18 Mar 2024 08:38:55 -0700 Subject: [PATCH 111/155] fix style --- cpp/tests/c_api/k_truss_test.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/tests/c_api/k_truss_test.c b/cpp/tests/c_api/k_truss_test.c index 57dd74a56c6..42f83fc06b7 100644 --- a/cpp/tests/c_api/k_truss_test.c +++ b/cpp/tests/c_api/k_truss_test.c @@ -120,7 +120,6 @@ int generic_k_truss_test(vertex_t* h_src, TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); } - ret_code = cugraph_type_erased_device_array_view_copy_to_host( resource_handle, (byte_t*)h_result_offsets, offsets, &ret_error); TEST_ASSERT(test_ret_value, ret_code == CUGRAPH_SUCCESS, "copy_to_host failed."); From 302eaa2ca4a78ae3a05099e2a5590cd3d5621063 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 07:50:40 -0700 Subject: [PATCH 112/155] add k_truss c++ tests --- cpp/tests/community/k_truss_test.cpp | 123 --------- cpp/tests/community/k_truss_test.cu | 377 +++++++++++++++++++++++++++ 2 files changed, 377 insertions(+), 123 deletions(-) delete mode 100644 cpp/tests/community/k_truss_test.cpp create mode 100644 cpp/tests/community/k_truss_test.cu diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp deleted file mode 100644 index 9d82bea107f..00000000000 --- a/cpp/tests/community/k_truss_test.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 governin_from_mtxg permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct KTruss_Usecase { - int32_t k{10}; - bool check_correctness{true}; -}; - -template -class Tests_KTruss : public ::testing::TestWithParam> { - public: - Tests_KTruss() {} - - static void SetUpTestCase() {} - static void TearDownTestCase() {} - - virtual void SetUp() {} - virtual void TearDown() {} - - template - void run_current_test(std::tuple const& param) - { - constexpr bool renumber = false; - - using weight_t = float; - - auto [k_truss_usecase, input_usecase] = param; - - raft::handle_t handle{}; - HighResTimer hr_timer{}; - - if (cugraph::test::g_perf) { - RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement - hr_timer.start("MG Construct graph"); - } - - // cugraph::graph_t graph(handle); - // std::optional> d_renumber_map_labels{std::nullopt}; - auto [graph, edge_weights, d_renumber_map_labels] = - cugraph::test::construct_graph( - handle, input_usecase, false, renumber, false, true); - - if (cugraph::test::g_perf) { - RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement - hr_timer.stop(); - hr_timer.display_and_clear(std::cout); - } - - auto graph_view = graph.view(); - auto edge_weight_view = - edge_weights ? std::make_optional((*edge_weights).view()) : std::nullopt; - - if (cugraph::test::g_perf) { - RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement - hr_timer.start("K-truss"); - } - - cugraph::k_truss( - handle, graph_view, edge_weight_view, k_truss_usecase.k, false); - - if (cugraph::test::g_perf) { - RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement - hr_timer.stop(); - hr_timer.display_and_clear(std::cout); - } - } -}; - -using Tests_KTruss_File = Tests_KTruss; - -// FIXME: add tests for type combinations -TEST_P(Tests_KTruss_File, CheckInt32Int32) -{ - run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); -} - -INSTANTIATE_TEST_SUITE_P( - file_test, - Tests_KTruss_File, - ::testing::Combine( - // enable correctness checks - ::testing::Values(KTruss_Usecase{5}), - ::testing::Values(cugraph::test::File_Usecase("test/datasets/dolphins.mtx")))); - -CUGRAPH_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/community/k_truss_test.cu b/cpp/tests/community/k_truss_test.cu new file mode 100644 index 00000000000..ee014fb7ec1 --- /dev/null +++ b/cpp/tests/community/k_truss_test.cu @@ -0,0 +1,377 @@ +/* + * 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 governin_from_mtxg permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct KTruss_Usecase { + int32_t k{3}; + bool check_correctness{true}; + bool test_weighted_{false}; +}; + +template +class Tests_KTruss : public ::testing::TestWithParam> { + public: + Tests_KTruss() {} + + static void SetUpTestCase() {} + static void TearDownTestCase() {} + + virtual void SetUp() {} + virtual void TearDown() {} + + template + std::tuple, + rmm::device_uvector, + std::optional>> + k_truss_reference( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + std::optional> edge_weight_view, + edge_t k) + { + + rmm::device_uvector d_src(0, handle.get_stream()); + rmm::device_uvector d_dst(0, handle.get_stream()); + std::optional> d_wgt{std::nullopt}; + + // Decompress the edgelist - For Rmat generated graph, remove self loop and multi edges + std::tie(d_src, d_dst, d_wgt, std::ignore) = decompress_to_edgelist( + handle, + graph_view, + edge_weight_view? std::make_optional(*edge_weight_view): std::nullopt, + std::optional>{std::nullopt}, + std::optional>(std::nullopt)); + + // get host vectors + + std::vector h_src(d_src.size()); + raft::update_host(h_src.data(), + d_src.data(), + d_src.size(), + handle.get_stream()); + std::vector h_dst(d_src.size()); + raft::update_host(h_dst.data(), + d_dst.data(), + d_dst.size(), + handle.get_stream()); + + std::vector h_wgt(d_src.size()); + if (edge_weight_view){ + raft::update_host(h_wgt.data(), + (*d_wgt).data(), + (*d_wgt).size(), + handle.get_stream()); + } + + std::vector h_src_(h_src.size()); + std::vector h_dst_(h_dst.size()); + // FIXME: Instead extract the vertices from the graph + std::vector dup_vertices(2* h_src.size()); + + thrust::copy(thrust::host, h_src.begin(), h_src.end(), dup_vertices.begin()); + thrust::copy(thrust::host, h_dst.begin(), h_dst.end(), dup_vertices.begin() + h_src.size()); + + std::set vertices(dup_vertices.begin(), dup_vertices.end()); + + auto n_dropped = 1; + + auto edge_first = + thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin())); + + auto weighted_edge_first = + thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin(), h_wgt.begin())); + + thrust::sort(thrust::host, edge_first, edge_first + h_src.size()); + + while (n_dropped > 0) { + n_dropped = 0; + std::set seen; + // Go over all the vertices + for (auto u=vertices.begin(); u!=vertices.end(); ++u) { + // Find all neighbours of u + vertex_t idx_start = 0; + vertex_t idx_end = 0; + auto itr_lower_range = thrust::lower_bound( + thrust::host, h_src.begin(), h_src.end(), *u); + + idx_start = thrust::distance(h_src.begin(), itr_lower_range); + + if (*itr_lower_range == *u) { + auto itr_upper_range = thrust::upper_bound( + thrust::host, itr_lower_range, h_src.end(), *u); + + idx_end = thrust::distance(itr_lower_range, itr_upper_range); + + } + + std::set nbrs_u; + + for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { + nbrs_u.insert(*(h_dst.begin() + i)); + } + + seen.insert(*u); + + std::set new_nbrs; + + std::set_difference(nbrs_u.begin(), nbrs_u.end(), seen.begin(), seen.end(), + std::inserter(new_nbrs, new_nbrs.end())); + + // Finding the neighbors of v + for (auto v=new_nbrs.begin(); v!=new_nbrs.end(); ++v) { + auto itr_lower_range = thrust::lower_bound( + thrust::host, h_src.begin(), h_src.end(), *v); + + idx_start = thrust::distance(h_src.begin(), itr_lower_range); + + if (*itr_lower_range == *v) { + auto itr_upper_range = thrust::upper_bound( + thrust::host, itr_lower_range, h_src.end(), *v); + + idx_end = thrust::distance(itr_lower_range, itr_upper_range); + } + std::set nbrs_v; + + for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { + nbrs_v.insert(*(h_dst.begin() + i)); + } + + std::set nbr_intersection_u_v; + // Find the intersection of nbr_u and nbr_v + std::set_intersection(nbrs_u.begin(), nbrs_u.end(), nbrs_v.begin(), nbrs_v.end(), std::inserter(nbr_intersection_u_v, nbr_intersection_u_v.end())); + + if (nbr_intersection_u_v.size() < (k-2)) { + auto edge = thrust::make_tuple(vertex_t{*u}, vertex_t{*v}); + + if (edge_weight_view){ + auto weighted_edge_last = thrust::remove_if( + thrust::host, + weighted_edge_first, + weighted_edge_first + h_src.size(), + [edge](auto e){ + auto src = thrust::get<0>(e); + auto dst = thrust::get<1>(e); + auto remove_edge = thrust::make_tuple(src, dst); + return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); + } + + ); + + h_src.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); + h_dst.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); + h_wgt.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); + + } + auto edge_last = thrust::remove_if( + thrust::host, + edge_first, + edge_first + h_src.size(), + [edge](auto e){ + auto src = thrust::get<0>(e); + auto dst = thrust::get<1>(e); + auto remove_edge = thrust::make_tuple(src, dst); + return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); + + } + + ); + + h_src.resize(thrust::distance(edge_first, edge_last)); + h_dst.resize(thrust::distance(edge_first, edge_last)); + + n_dropped += 1; + + } + + } + } + } + + // convert back to device_vector + rmm::device_uvector d_reference_src(h_src.size(), + handle.get_stream()); + + raft::update_device(d_reference_src.data(), + h_src.data(), + h_src.size(), + handle.get_stream()); + + rmm::device_uvector d_reference_dst(h_src.size(), + handle.get_stream()); + + raft::update_device(d_reference_dst.data(), + h_dst.data(), + h_dst.size(), + handle.get_stream()); + + auto d_reference_wgt = std::make_optional>( + h_src.size(), handle.get_stream()); + + if (edge_weight_view) { + raft::update_device((*d_reference_wgt).data(), + h_wgt.data(), + h_wgt.size(), + handle.get_stream()); + } + + return std::make_tuple(std::move(d_reference_src), + std::move(d_reference_dst), + edge_weight_view ? std::move(d_reference_wgt): std::nullopt); + } + + template + void run_current_test(std::tuple const& param) + { + constexpr bool renumber = false; + + auto [k_truss_usecase, input_usecase] = param; + + raft::handle_t handle{}; + + HighResTimer hr_timer{}; + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.start("MG Construct graph"); + } + + auto [graph, edge_weights, d_renumber_map_labels] = + cugraph::test::construct_graph( + handle, input_usecase, k_truss_usecase.test_weighted_, renumber, false, true); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + auto graph_view = graph.view(); + + auto edge_weight_view = + edge_weights ? std::make_optional((*edge_weights).view()) : std::nullopt; + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.start("K-truss"); + } + + auto [d_cugraph_src, d_cugraph_dst, d_cugraph_wgt] = cugraph::k_truss( + handle, graph_view, edge_weight_view, k_truss_usecase.k, false); + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + hr_timer.stop(); + hr_timer.display_and_clear(std::cout); + } + + if (k_truss_usecase.check_correctness) { + auto [d_reference_src, d_reference_dst, d_reference_wgt] = k_truss_reference(handle, graph_view, edge_weight_view, k_truss_usecase.k); + if (edge_weight_view) { + auto d_edge_first = + thrust::make_zip_iterator(thrust::make_tuple(d_cugraph_src.begin(), d_cugraph_dst.begin(), (*d_cugraph_wgt).begin())); + + thrust::sort(handle.get_thrust_policy(), d_edge_first, d_edge_first + d_cugraph_src.size()); + } else { + auto d_edge_first = + thrust::make_zip_iterator(thrust::make_tuple(d_cugraph_src.begin(), d_cugraph_dst.begin())); + + thrust::sort(handle.get_thrust_policy(), d_edge_first, d_edge_first + d_cugraph_src.size()); + } + + EXPECT_EQ(d_cugraph_src.size(), d_reference_src.size()); + + ASSERT_TRUE(thrust::equal(handle.get_thrust_policy(), + d_cugraph_src.begin(), + d_cugraph_src.end(), + d_reference_src.begin())); + + ASSERT_TRUE(thrust::equal(handle.get_thrust_policy(), + d_cugraph_dst.begin(), + d_cugraph_dst.end(), + d_reference_dst.begin())); + + if (edge_weight_view) { + auto compare_functor = cugraph::test::device_nearly_equal{ + weight_t{1e-3}, + weight_t{(weight_t{1} / static_cast((*d_cugraph_wgt).size())) * weight_t{1e-3}}}; + EXPECT_TRUE(thrust::equal(handle.get_thrust_policy(), + (*d_cugraph_wgt).begin(), + (*d_cugraph_wgt).end(), + (*d_reference_wgt).begin(), + compare_functor)); + } + } + } + + +}; + +using Tests_KTruss_File = Tests_KTruss; + +TEST_P(Tests_KTruss_File, CheckInt32Int32Float) +{ + run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_KTruss_File, CheckInt64Int64Float) +{ + run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_KTruss_File, CheckInt32Int64Float) +{ + run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +INSTANTIATE_TEST_SUITE_P( + file_test, + Tests_KTruss_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(KTruss_Usecase{5, true, false}, + KTruss_Usecase{4, true, true}, + KTruss_Usecase{9, true, true}, + KTruss_Usecase{7, true, true}), + ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), + cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); + +CUGRAPH_TEST_PROGRAM_MAIN() From bd8ac945493816ddd796fd9498658d1c37f59b25 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 07:59:39 -0700 Subject: [PATCH 113/155] properly handle weights --- cpp/src/community/k_truss_impl.cuh | 106 ++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 26 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index f451b251bc5..0108a1797ee 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -437,6 +437,25 @@ struct exclude_self_loop_t { } }; +template +struct extract_low_to_high_degree_weighted_edges_t { + __device__ thrust::optional> operator()(vertex_t src, + vertex_t dst, + edge_t src_out_degree, + edge_t dst_out_degree, + weight_t wgt) const + { + + return (src_out_degree < dst_out_degree) + ? thrust::optional>{thrust::make_tuple(src, dst, wgt)} + : (((src_out_degree == dst_out_degree) && + (src < dst) /* tie-breaking using vertex ID */) + ? thrust::optional>{thrust::make_tuple(src, + dst, wgt)} + : thrust::nullopt); + } +}; + template struct extract_low_to_high_degree_edges_t { __device__ thrust::optional> operator()(vertex_t src, @@ -488,6 +507,7 @@ k_truss(raft::handle_t const& handle, edge_t k, bool do_expensive_check) { + // 1. Check input arguments. CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); @@ -497,6 +517,7 @@ k_truss(raft::handle_t const& handle, CUGRAPH_EXPECTS(!graph_view.is_multigraph(), "Invalid input arguments: K-truss currently does not support multi-graphs."); + if (do_expensive_check) { // nothing to do } @@ -506,6 +527,7 @@ k_truss(raft::handle_t const& handle, std::optional> modified_graph{std::nullopt}; std::optional> modified_graph_view{std::nullopt}; std::optional> renumber_map{std::nullopt}; + std::optional, weight_t>> edge_weight{std::nullopt}; if (graph_view.count_self_loops(handle) > edge_t{0}) { auto [srcs, dsts] = extract_transform_e(handle, @@ -618,30 +640,45 @@ k_truss(raft::handle_t const& handle, cur_graph_view); update_edge_src_property(handle, cur_graph_view, out_degrees.begin(), edge_src_out_degrees); update_edge_dst_property(handle, cur_graph_view, out_degrees.begin(), edge_dst_out_degrees); - auto [srcs, dsts] = extract_transform_e(handle, - cur_graph_view, - edge_src_out_degrees.view(), - edge_dst_out_degrees.view(), - edge_dummy_property_t{}.view(), - extract_low_to_high_degree_edges_t{}); + + rmm::device_uvector srcs(0, handle.get_stream()); + rmm::device_uvector dsts(0, handle.get_stream()); + std::optional> wgts{std::nullopt}; + if (edge_weight_view) { + std::tie(srcs, dsts, wgts) = extract_transform_e(handle, + cur_graph_view, + edge_src_out_degrees.view(), + edge_dst_out_degrees.view(), + *edge_weight_view, + extract_low_to_high_degree_weighted_edges_t{}); + } else { + std::tie(srcs, dsts) = extract_transform_e(handle, + cur_graph_view, + edge_src_out_degrees.view(), + edge_dst_out_degrees.view(), + edge_dummy_property_t{}.view(), + extract_low_to_high_degree_edges_t{}); + + } if constexpr (multi_gpu) { - std::tie(srcs, dsts, std::ignore, std::ignore, std::ignore) = + std::tie(srcs, dsts, wgts, std::ignore, std::ignore) = detail::shuffle_ext_vertex_pairs_with_values_to_local_gpu_by_edge_partitioning( - handle, std::move(srcs), std::move(dsts), std::nullopt, std::nullopt, std::nullopt); + handle, std::move(srcs), std::move(dsts), std::move(wgts), std::nullopt, std::nullopt); } std::optional> tmp_renumber_map{std::nullopt}; - std::tie(*modified_graph, std::ignore, std::ignore, std::ignore, tmp_renumber_map) = + + std::tie(*modified_graph, edge_weight, std::ignore, std::ignore, tmp_renumber_map) = create_graph_from_edgelist( handle, std::nullopt, std::move(srcs), std::move(dsts), - std::nullopt, + std::move(wgts), std::nullopt, std::nullopt, cugraph::graph_properties_t{false /* now asymmetric */, cur_graph_view.is_multigraph()}, @@ -667,10 +704,11 @@ k_truss(raft::handle_t const& handle, rmm::device_uvector edgelist_dsts(0, handle.get_stream()); std::optional> edgelist_wgts{std::nullopt}; - std::tie(edgelist_srcs, edgelist_dsts, std::ignore, std::ignore) = decompress_to_edgelist( + edge_weight_view = edge_weight ? std::make_optional((*edge_weight).view()) : std::optional>{std::nullopt}; + std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts, std::ignore) = decompress_to_edgelist( handle, cur_graph_view, - std::optional>{std::nullopt}, + edge_weight_view, std::optional>{std::nullopt}, std::optional>(std::nullopt)); @@ -857,18 +895,34 @@ k_truss(raft::handle_t const& handle, edgelist_dsts.begin() + num_edges_with_triangles); cur_graph_view.clear_edge_mask(); - cugraph::transform_e( - handle, - cur_graph_view, - edges_with_no_triangle, - cugraph::edge_src_dummy_property_t{}.view(), - cugraph::edge_dst_dummy_property_t{}.view(), - cugraph::edge_dummy_property_t{}.view(), - [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { - return false; - }, - edge_mask.mutable_view(), - false); + if (edge_weight_view) { + cugraph::transform_e( + handle, + cur_graph_view, + edges_with_no_triangle, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + *edge_weight_view, + [] __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, auto wgt) { + return false; + }, + edge_mask.mutable_view(), + false); + } else { + cugraph::transform_e( + handle, + cur_graph_view, + edges_with_no_triangle, + cugraph::edge_src_dummy_property_t{}.view(), + cugraph::edge_dst_dummy_property_t{}.view(), + cugraph::edge_dummy_property_t{}.view(), + [] + __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + return false; + }, + edge_mask.mutable_view(), + false); + } cur_graph_view.attach_edge_mask(edge_mask.view()); edgelist_srcs.resize(num_edges_with_triangles, handle.get_stream()); @@ -879,12 +933,12 @@ k_truss(raft::handle_t const& handle, std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts, std::ignore) = decompress_to_edgelist( handle, cur_graph_view, - edge_weight_view, + edge_weight_view? std::make_optional(*edge_weight_view): std::nullopt, std::optional>{std::nullopt}, std::optional>(std::nullopt)); std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts) = - symmetrize_edgelist( + symmetrize_edgelist( handle, std::move(edgelist_srcs), std::move(edgelist_dsts), From b470713d5c19db950ea154b1f44a2c1fb7c16685 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 08:00:18 -0700 Subject: [PATCH 114/155] rename c++ tests --- cpp/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index d36c722db2d..95bdacea4e9 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -415,7 +415,7 @@ ConfigureTest(K_CORE_TEST cores/k_core_test.cpp) ############################################################################################### # - K-truss tests -------------------------------------------------------------------------- -ConfigureTest(K_TRUSS_TEST community/k_truss_test.cpp) +ConfigureTest(K_TRUSS_TEST community/k_truss_test.cu) ################################################################################################### # - Triangle Count tests -------------------------------------------------------------------------- From f7a43eccd55d31ca9f5cca928315ca15d39d9c80 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 08:01:08 -0700 Subject: [PATCH 115/155] update c tests --- cpp/tests/c_api/k_truss_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/tests/c_api/k_truss_test.c b/cpp/tests/c_api/k_truss_test.c index 42f83fc06b7..89b2d6df544 100644 --- a/cpp/tests/c_api/k_truss_test.c +++ b/cpp/tests/c_api/k_truss_test.c @@ -171,9 +171,9 @@ int test_k_truss() weight_t h_wgt[] = { 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f, 0.1f, 2.1f, 1.1f, 5.1f, 3.1f, 4.1f, 7.2f, 3.2f}; - vertex_t h_result_src[] = {0, 0, 2, 2, 3, 1, 2, 1, 3, 1}; - vertex_t h_result_dst[] = {1, 2, 1, 3, 1, 0, 0, 2, 2, 3}; - weight_t h_result_wgt[] = {0.1, 5.1, 0.1, 3.1, 2.1, 0.1, 5.1, 0.1, 3.1, 2.1}; + vertex_t h_result_src[] = {1, 2, 2, 3, 3, 0, 0, 1, 1, 2}; + vertex_t h_result_dst[] = {0, 0, 1, 1, 2, 1, 2, 2, 3, 3}; + weight_t h_result_wgt[] = {0.1, 5.1, 3.1, 2.1, 4.1, 0.1, 5.1, 3.1, 2.1, 4.1}; size_t h_result_offsets[] = {0, 10}; size_t num_expected_edges = 10; size_t num_expected_offsets = 2; From 7b80a8dabab0f022c02aa4a884beca5e7ba80379 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 08:08:02 -0700 Subject: [PATCH 116/155] undo changes --- python/nx-cugraph/nx_cugraph/tests/test_ktruss.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py index b8a1379edf5..92fe2360688 100644 --- a/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py +++ b/python/nx-cugraph/nx_cugraph/tests/test_ktruss.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023-2024, NVIDIA CORPORATION. +# Copyright (c) 2023, 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 @@ -25,9 +25,6 @@ def test_k_truss(get_graph): for k in range(6): Hnx = nx.k_truss(Gnx, k) Hcg = nxcg.k_truss(Gcg, k) - # FIXME: nx.utils.graphs_equal returns False for dataset - # 'les_miserables_graph' after k = 3 eventhough the number of - # vertices and edges match - assert nx.is_isomorphic(Hnx, nxcg.to_networkx(Hcg)) + assert nx.utils.graphs_equal(Hnx, nxcg.to_networkx(Hcg)) if Hnx.number_of_edges() == 0: break From 68b97bdd800465ce0bbf7a2ec84d38fe9baff8e8 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 08:11:15 -0700 Subject: [PATCH 117/155] fix style --- cpp/src/community/k_truss_impl.cuh | 69 +++-- cpp/tests/community/k_truss_test.cu | 384 ++++++++++++++-------------- 2 files changed, 219 insertions(+), 234 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 0108a1797ee..bed14bb817a 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -439,19 +439,17 @@ struct exclude_self_loop_t { template struct extract_low_to_high_degree_weighted_edges_t { - __device__ thrust::optional> operator()(vertex_t src, - vertex_t dst, - edge_t src_out_degree, - edge_t dst_out_degree, - weight_t wgt) const + __device__ thrust::optional> operator()( + vertex_t src, vertex_t dst, edge_t src_out_degree, edge_t dst_out_degree, weight_t wgt) const { - return (src_out_degree < dst_out_degree) - ? thrust::optional>{thrust::make_tuple(src, dst, wgt)} + ? thrust::optional>{thrust::make_tuple( + src, dst, wgt)} : (((src_out_degree == dst_out_degree) && (src < dst) /* tie-breaking using vertex ID */) - ? thrust::optional>{thrust::make_tuple(src, - dst, wgt)} + ? thrust::optional< + thrust::tuple>{thrust::make_tuple( + src, dst, wgt)} : thrust::nullopt); } }; @@ -507,7 +505,6 @@ k_truss(raft::handle_t const& handle, edge_t k, bool do_expensive_check) { - // 1. Check input arguments. CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); @@ -517,7 +514,6 @@ k_truss(raft::handle_t const& handle, CUGRAPH_EXPECTS(!graph_view.is_multigraph(), "Invalid input arguments: K-truss currently does not support multi-graphs."); - if (do_expensive_check) { // nothing to do } @@ -527,7 +523,8 @@ k_truss(raft::handle_t const& handle, std::optional> modified_graph{std::nullopt}; std::optional> modified_graph_view{std::nullopt}; std::optional> renumber_map{std::nullopt}; - std::optional, weight_t>> edge_weight{std::nullopt}; + std::optional, weight_t>> + edge_weight{std::nullopt}; if (graph_view.count_self_loops(handle) > edge_t{0}) { auto [srcs, dsts] = extract_transform_e(handle, @@ -645,20 +642,21 @@ k_truss(raft::handle_t const& handle, rmm::device_uvector dsts(0, handle.get_stream()); std::optional> wgts{std::nullopt}; if (edge_weight_view) { - std::tie(srcs, dsts, wgts) = extract_transform_e(handle, - cur_graph_view, - edge_src_out_degrees.view(), - edge_dst_out_degrees.view(), - *edge_weight_view, - extract_low_to_high_degree_weighted_edges_t{}); + std::tie(srcs, dsts, wgts) = extract_transform_e( + handle, + cur_graph_view, + edge_src_out_degrees.view(), + edge_dst_out_degrees.view(), + *edge_weight_view, + extract_low_to_high_degree_weighted_edges_t{}); } else { - std::tie(srcs, dsts) = extract_transform_e(handle, - cur_graph_view, - edge_src_out_degrees.view(), - edge_dst_out_degrees.view(), - edge_dummy_property_t{}.view(), - extract_low_to_high_degree_edges_t{}); - + std::tie(srcs, dsts) = + extract_transform_e(handle, + cur_graph_view, + edge_src_out_degrees.view(), + edge_dst_out_degrees.view(), + edge_dummy_property_t{}.view(), + extract_low_to_high_degree_edges_t{}); } if constexpr (multi_gpu) { @@ -704,7 +702,9 @@ k_truss(raft::handle_t const& handle, rmm::device_uvector edgelist_dsts(0, handle.get_stream()); std::optional> edgelist_wgts{std::nullopt}; - edge_weight_view = edge_weight ? std::make_optional((*edge_weight).view()) : std::optional>{std::nullopt}; + edge_weight_view = + edge_weight ? std::make_optional((*edge_weight).view()) + : std::optional>{std::nullopt}; std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts, std::ignore) = decompress_to_edgelist( handle, cur_graph_view, @@ -916,8 +916,8 @@ k_truss(raft::handle_t const& handle, cugraph::edge_src_dummy_property_t{}.view(), cugraph::edge_dst_dummy_property_t{}.view(), cugraph::edge_dummy_property_t{}.view(), - [] - __device__(auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { + [] __device__( + auto src, auto dst, thrust::nullopt_t, thrust::nullopt_t, thrust::nullopt_t) { return false; }, edge_mask.mutable_view(), @@ -933,17 +933,16 @@ k_truss(raft::handle_t const& handle, std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts, std::ignore) = decompress_to_edgelist( handle, cur_graph_view, - edge_weight_view? std::make_optional(*edge_weight_view): std::nullopt, + edge_weight_view ? std::make_optional(*edge_weight_view) : std::nullopt, std::optional>{std::nullopt}, std::optional>(std::nullopt)); std::tie(edgelist_srcs, edgelist_dsts, edgelist_wgts) = - symmetrize_edgelist( - handle, - std::move(edgelist_srcs), - std::move(edgelist_dsts), - std::move(edgelist_wgts), - false); + symmetrize_edgelist(handle, + std::move(edgelist_srcs), + std::move(edgelist_dsts), + std::move(edgelist_wgts), + false); return std::make_tuple( std::move(edgelist_srcs), std::move(edgelist_dsts), std::move(edgelist_wgts)); diff --git a/cpp/tests/community/k_truss_test.cu b/cpp/tests/community/k_truss_test.cu index ee014fb7ec1..74fddef8a7e 100644 --- a/cpp/tests/community/k_truss_test.cu +++ b/cpp/tests/community/k_truss_test.cu @@ -22,11 +22,12 @@ #include #include -#include #include #include +#include + #include #include #include @@ -38,8 +39,8 @@ #include #include #include -#include #include +#include struct KTruss_Usecase { int32_t k{3}; @@ -63,201 +64,180 @@ class Tests_KTruss : public ::testing::TestWithParam, std::optional>> k_truss_reference( - raft::handle_t const& handle, - cugraph::graph_view_t const& graph_view, - std::optional> edge_weight_view, - edge_t k) - { - - rmm::device_uvector d_src(0, handle.get_stream()); - rmm::device_uvector d_dst(0, handle.get_stream()); - std::optional> d_wgt{std::nullopt}; - - // Decompress the edgelist - For Rmat generated graph, remove self loop and multi edges - std::tie(d_src, d_dst, d_wgt, std::ignore) = decompress_to_edgelist( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + std::optional> edge_weight_view, + edge_t k) + { + rmm::device_uvector d_src(0, handle.get_stream()); + rmm::device_uvector d_dst(0, handle.get_stream()); + std::optional> d_wgt{std::nullopt}; + + // Decompress the edgelist - For Rmat generated graph, remove self loop and multi edges + std::tie(d_src, d_dst, d_wgt, std::ignore) = decompress_to_edgelist( handle, graph_view, - edge_weight_view? std::make_optional(*edge_weight_view): std::nullopt, + edge_weight_view ? std::make_optional(*edge_weight_view) : std::nullopt, std::optional>{std::nullopt}, std::optional>(std::nullopt)); - // get host vectors - - std::vector h_src(d_src.size()); - raft::update_host(h_src.data(), - d_src.data(), - d_src.size(), - handle.get_stream()); - std::vector h_dst(d_src.size()); - raft::update_host(h_dst.data(), - d_dst.data(), - d_dst.size(), - handle.get_stream()); - - std::vector h_wgt(d_src.size()); - if (edge_weight_view){ - raft::update_host(h_wgt.data(), - (*d_wgt).data(), - (*d_wgt).size(), - handle.get_stream()); - } + // get host vectors - std::vector h_src_(h_src.size()); - std::vector h_dst_(h_dst.size()); - // FIXME: Instead extract the vertices from the graph - std::vector dup_vertices(2* h_src.size()); - - thrust::copy(thrust::host, h_src.begin(), h_src.end(), dup_vertices.begin()); - thrust::copy(thrust::host, h_dst.begin(), h_dst.end(), dup_vertices.begin() + h_src.size()); - - std::set vertices(dup_vertices.begin(), dup_vertices.end()); - - auto n_dropped = 1; - - auto edge_first = - thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin())); - - auto weighted_edge_first = - thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin(), h_wgt.begin())); - - thrust::sort(thrust::host, edge_first, edge_first + h_src.size()); - - while (n_dropped > 0) { - n_dropped = 0; - std::set seen; - // Go over all the vertices - for (auto u=vertices.begin(); u!=vertices.end(); ++u) { - // Find all neighbours of u - vertex_t idx_start = 0; - vertex_t idx_end = 0; - auto itr_lower_range = thrust::lower_bound( - thrust::host, h_src.begin(), h_src.end(), *u); - - idx_start = thrust::distance(h_src.begin(), itr_lower_range); - - if (*itr_lower_range == *u) { - auto itr_upper_range = thrust::upper_bound( - thrust::host, itr_lower_range, h_src.end(), *u); - - idx_end = thrust::distance(itr_lower_range, itr_upper_range); - - } - - std::set nbrs_u; + std::vector h_src(d_src.size()); + raft::update_host(h_src.data(), d_src.data(), d_src.size(), handle.get_stream()); + std::vector h_dst(d_src.size()); + raft::update_host(h_dst.data(), d_dst.data(), d_dst.size(), handle.get_stream()); - for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { - nbrs_u.insert(*(h_dst.begin() + i)); - } + std::vector h_wgt(d_src.size()); + if (edge_weight_view) { + raft::update_host(h_wgt.data(), (*d_wgt).data(), (*d_wgt).size(), handle.get_stream()); + } - seen.insert(*u); - - std::set new_nbrs; - - std::set_difference(nbrs_u.begin(), nbrs_u.end(), seen.begin(), seen.end(), - std::inserter(new_nbrs, new_nbrs.end())); - - // Finding the neighbors of v - for (auto v=new_nbrs.begin(); v!=new_nbrs.end(); ++v) { - auto itr_lower_range = thrust::lower_bound( - thrust::host, h_src.begin(), h_src.end(), *v); - - idx_start = thrust::distance(h_src.begin(), itr_lower_range); - - if (*itr_lower_range == *v) { - auto itr_upper_range = thrust::upper_bound( - thrust::host, itr_lower_range, h_src.end(), *v); - - idx_end = thrust::distance(itr_lower_range, itr_upper_range); - } - std::set nbrs_v; + std::vector h_src_(h_src.size()); + std::vector h_dst_(h_dst.size()); + // FIXME: Instead extract the vertices from the graph + std::vector dup_vertices(2 * h_src.size()); - for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { - nbrs_v.insert(*(h_dst.begin() + i)); - } - - std::set nbr_intersection_u_v; - // Find the intersection of nbr_u and nbr_v - std::set_intersection(nbrs_u.begin(), nbrs_u.end(), nbrs_v.begin(), nbrs_v.end(), std::inserter(nbr_intersection_u_v, nbr_intersection_u_v.end())); - - if (nbr_intersection_u_v.size() < (k-2)) { - auto edge = thrust::make_tuple(vertex_t{*u}, vertex_t{*v}); - - if (edge_weight_view){ - auto weighted_edge_last = thrust::remove_if( - thrust::host, - weighted_edge_first, - weighted_edge_first + h_src.size(), - [edge](auto e){ - auto src = thrust::get<0>(e); - auto dst = thrust::get<1>(e); - auto remove_edge = thrust::make_tuple(src, dst); - return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); - } - - ); - - h_src.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); - h_dst.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); - h_wgt.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); - - } - auto edge_last = thrust::remove_if( - thrust::host, - edge_first, - edge_first + h_src.size(), - [edge](auto e){ - auto src = thrust::get<0>(e); - auto dst = thrust::get<1>(e); - auto remove_edge = thrust::make_tuple(src, dst); - return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); - - } - - ); - - h_src.resize(thrust::distance(edge_first, edge_last)); - h_dst.resize(thrust::distance(edge_first, edge_last)); - - n_dropped += 1; - + thrust::copy(thrust::host, h_src.begin(), h_src.end(), dup_vertices.begin()); + thrust::copy(thrust::host, h_dst.begin(), h_dst.end(), dup_vertices.begin() + h_src.size()); + + std::set vertices(dup_vertices.begin(), dup_vertices.end()); + + auto n_dropped = 1; + + auto edge_first = thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin())); + + auto weighted_edge_first = + thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin(), h_wgt.begin())); + + thrust::sort(thrust::host, edge_first, edge_first + h_src.size()); + + while (n_dropped > 0) { + n_dropped = 0; + std::set seen; + // Go over all the vertices + for (auto u = vertices.begin(); u != vertices.end(); ++u) { + // Find all neighbours of u + vertex_t idx_start = 0; + vertex_t idx_end = 0; + auto itr_lower_range = thrust::lower_bound(thrust::host, h_src.begin(), h_src.end(), *u); + + idx_start = thrust::distance(h_src.begin(), itr_lower_range); + + if (*itr_lower_range == *u) { + auto itr_upper_range = + thrust::upper_bound(thrust::host, itr_lower_range, h_src.end(), *u); + + idx_end = thrust::distance(itr_lower_range, itr_upper_range); + } + + std::set nbrs_u; + + for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { + nbrs_u.insert(*(h_dst.begin() + i)); + } + + seen.insert(*u); + + std::set new_nbrs; + + std::set_difference(nbrs_u.begin(), + nbrs_u.end(), + seen.begin(), + seen.end(), + std::inserter(new_nbrs, new_nbrs.end())); + + // Finding the neighbors of v + for (auto v = new_nbrs.begin(); v != new_nbrs.end(); ++v) { + auto itr_lower_range = thrust::lower_bound(thrust::host, h_src.begin(), h_src.end(), *v); + + idx_start = thrust::distance(h_src.begin(), itr_lower_range); + + if (*itr_lower_range == *v) { + auto itr_upper_range = + thrust::upper_bound(thrust::host, itr_lower_range, h_src.end(), *v); + + idx_end = thrust::distance(itr_lower_range, itr_upper_range); + } + std::set nbrs_v; + + for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { + nbrs_v.insert(*(h_dst.begin() + i)); + } + + std::set nbr_intersection_u_v; + // Find the intersection of nbr_u and nbr_v + std::set_intersection(nbrs_u.begin(), + nbrs_u.end(), + nbrs_v.begin(), + nbrs_v.end(), + std::inserter(nbr_intersection_u_v, nbr_intersection_u_v.end())); + + if (nbr_intersection_u_v.size() < (k - 2)) { + auto edge = thrust::make_tuple(vertex_t{*u}, vertex_t{*v}); + + if (edge_weight_view) { + auto weighted_edge_last = thrust::remove_if( + thrust::host, + weighted_edge_first, + weighted_edge_first + h_src.size(), + [edge](auto e) { + auto src = thrust::get<0>(e); + auto dst = thrust::get<1>(e); + auto remove_edge = thrust::make_tuple(src, dst); + return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); } + ); + + h_src.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); + h_dst.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); + h_wgt.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); } + auto edge_last = thrust::remove_if( + thrust::host, + edge_first, + edge_first + h_src.size(), + [edge](auto e) { + auto src = thrust::get<0>(e); + auto dst = thrust::get<1>(e); + auto remove_edge = thrust::make_tuple(src, dst); + return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); + } + + ); + + h_src.resize(thrust::distance(edge_first, edge_last)); + h_dst.resize(thrust::distance(edge_first, edge_last)); + + n_dropped += 1; + } } } + } - // convert back to device_vector - rmm::device_uvector d_reference_src(h_src.size(), - handle.get_stream()); - - raft::update_device(d_reference_src.data(), - h_src.data(), - h_src.size(), - handle.get_stream()); - - rmm::device_uvector d_reference_dst(h_src.size(), - handle.get_stream()); - - raft::update_device(d_reference_dst.data(), - h_dst.data(), - h_dst.size(), - handle.get_stream()); - - auto d_reference_wgt = std::make_optional>( - h_src.size(), handle.get_stream()); + // convert back to device_vector + rmm::device_uvector d_reference_src(h_src.size(), handle.get_stream()); - if (edge_weight_view) { - raft::update_device((*d_reference_wgt).data(), - h_wgt.data(), - h_wgt.size(), - handle.get_stream()); - } - - return std::make_tuple(std::move(d_reference_src), - std::move(d_reference_dst), - edge_weight_view ? std::move(d_reference_wgt): std::nullopt); + raft::update_device(d_reference_src.data(), h_src.data(), h_src.size(), handle.get_stream()); + + rmm::device_uvector d_reference_dst(h_src.size(), handle.get_stream()); + + raft::update_device(d_reference_dst.data(), h_dst.data(), h_dst.size(), handle.get_stream()); + + auto d_reference_wgt = + std::make_optional>(h_src.size(), handle.get_stream()); + + if (edge_weight_view) { + raft::update_device( + (*d_reference_wgt).data(), h_wgt.data(), h_wgt.size(), handle.get_stream()); } + return std::make_tuple(std::move(d_reference_src), + std::move(d_reference_dst), + edge_weight_view ? std::move(d_reference_wgt) : std::nullopt); + } + template void run_current_test(std::tuple const& param) { @@ -285,7 +265,7 @@ class Tests_KTruss : public ::testing::TestWithParam( - handle, graph_view, edge_weight_view, k_truss_usecase.k, false); + auto [d_cugraph_src, d_cugraph_dst, d_cugraph_wgt] = + cugraph::k_truss( + handle, graph_view, edge_weight_view, k_truss_usecase.k, false); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement @@ -304,16 +285,18 @@ class Tests_KTruss : public ::testing::TestWithParam(handle, graph_view, edge_weight_view, k_truss_usecase.k); + auto [d_reference_src, d_reference_dst, d_reference_wgt] = + k_truss_reference( + handle, graph_view, edge_weight_view, k_truss_usecase.k); if (edge_weight_view) { - auto d_edge_first = - thrust::make_zip_iterator(thrust::make_tuple(d_cugraph_src.begin(), d_cugraph_dst.begin(), (*d_cugraph_wgt).begin())); - + auto d_edge_first = thrust::make_zip_iterator(thrust::make_tuple( + d_cugraph_src.begin(), d_cugraph_dst.begin(), (*d_cugraph_wgt).begin())); + thrust::sort(handle.get_thrust_policy(), d_edge_first, d_edge_first + d_cugraph_src.size()); } else { - auto d_edge_first = - thrust::make_zip_iterator(thrust::make_tuple(d_cugraph_src.begin(), d_cugraph_dst.begin())); - + auto d_edge_first = thrust::make_zip_iterator( + thrust::make_tuple(d_cugraph_src.begin(), d_cugraph_dst.begin())); + thrust::sort(handle.get_thrust_policy(), d_edge_first, d_edge_first + d_cugraph_src.size()); } @@ -328,11 +311,12 @@ class Tests_KTruss : public ::testing::TestWithParam{ - weight_t{1e-3}, - weight_t{(weight_t{1} / static_cast((*d_cugraph_wgt).size())) * weight_t{1e-3}}}; + weight_t{1e-3}, + weight_t{(weight_t{1} / static_cast((*d_cugraph_wgt).size())) * + weight_t{1e-3}}}; EXPECT_TRUE(thrust::equal(handle.get_thrust_policy(), (*d_cugraph_wgt).begin(), (*d_cugraph_wgt).end(), @@ -341,25 +325,26 @@ class Tests_KTruss : public ::testing::TestWithParam; TEST_P(Tests_KTruss_File, CheckInt32Int32Float) { - run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); } TEST_P(Tests_KTruss_File, CheckInt64Int64Float) { - run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); } TEST_P(Tests_KTruss_File, CheckInt32Int64Float) { - run_current_test(override_File_Usecase_with_cmd_line_arguments(GetParam())); + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); } INSTANTIATE_TEST_SUITE_P( @@ -371,7 +356,8 @@ INSTANTIATE_TEST_SUITE_P( KTruss_Usecase{4, true, true}, KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), - ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), - cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); + ::testing::Values( + cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), + cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); CUGRAPH_TEST_PROGRAM_MAIN() From 701d87f5e413904e2448ecb396aca18eb1397aa7 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 08:18:53 -0700 Subject: [PATCH 118/155] undo syntax change --- cpp/src/c_api/k_truss.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/src/c_api/k_truss.cpp b/cpp/src/c_api/k_truss.cpp index 08f7001413e..387ff5728d7 100644 --- a/cpp/src/c_api/k_truss.cpp +++ b/cpp/src/c_api/k_truss.cpp @@ -14,6 +14,12 @@ * limitations under the License. */ +#include "c_api/abstract_functor.hpp" +#include "c_api/graph.hpp" +#include "c_api/induced_subgraph_result.hpp" +#include "c_api/resource_handle.hpp" +#include "c_api/utils.hpp" + #include #include @@ -21,12 +27,6 @@ #include #include -#include -#include -#include -#include -#include - #include namespace { From 22527befea8c079ef0033caad1945b10d0bb525a Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 08:41:28 -0700 Subject: [PATCH 119/155] update path to utility functions --- cpp/tests/community/k_truss_test.cu | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cu b/cpp/tests/community/k_truss_test.cu index 74fddef8a7e..373a4463f66 100644 --- a/cpp/tests/community/k_truss_test.cu +++ b/cpp/tests/community/k_truss_test.cu @@ -31,7 +31,8 @@ #include #include #include -#include +#include "utilities/conversion_utilities.hpp" +#include "utilities/check_utilities.hpp" #include #include @@ -360,4 +361,4 @@ INSTANTIATE_TEST_SUITE_P( cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); -CUGRAPH_TEST_PROGRAM_MAIN() +CUGRAPH_TEST_PROGRAM_MAIN() \ No newline at end of file From 2ea7ed219cf430a8409d30fd29e040202d66e270 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 10:01:38 -0700 Subject: [PATCH 120/155] add c++ rmat tests --- cpp/tests/community/k_truss_test.cu | 56 ++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cu b/cpp/tests/community/k_truss_test.cu index 373a4463f66..7d87d5076e7 100644 --- a/cpp/tests/community/k_truss_test.cu +++ b/cpp/tests/community/k_truss_test.cu @@ -255,9 +255,11 @@ class Tests_KTruss : public ::testing::TestWithParam( - handle, input_usecase, k_truss_usecase.test_weighted_, renumber, false, true); + handle, input_usecase, k_truss_usecase.test_weighted_, renumber, true, true); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement @@ -329,6 +331,7 @@ class Tests_KTruss : public ::testing::TestWithParam; +using Tests_KTruss_Rmat = Tests_KTruss; TEST_P(Tests_KTruss_File, CheckInt32Int32Float) { @@ -348,17 +351,60 @@ TEST_P(Tests_KTruss_File, CheckInt32Int64Float) override_File_Usecase_with_cmd_line_arguments(GetParam())); } + +TEST_P(Tests_KTruss_Rmat, CheckInt32Int32Float) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_KTruss_Rmat, CheckInt64Int64Float) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_KTruss_Rmat, CheckInt32Int64Float) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + INSTANTIATE_TEST_SUITE_P( - file_test, + simple_test, Tests_KTruss_File, ::testing::Combine( // enable correctness checks ::testing::Values(KTruss_Usecase{5, true, false}, - KTruss_Usecase{4, true, true}, + KTruss_Usecase{4, true, false}, KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), ::testing::Values( - cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), - cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); + cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), + cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_small_test, + Tests_KTruss_Rmat, + // enable correctness checks + ::testing::Combine( + ::testing::Values(KTruss_Usecase{5, true, false}, + KTruss_Usecase{4, true, false}, + KTruss_Usecase{9, true, true}, + KTruss_Usecase{7, true, true}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, true, false)))); + +INSTANTIATE_TEST_SUITE_P( + rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with + --gtest_filter to select only the rmat_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one Rmat_Usecase that differ only in scale or edge + factor (to avoid running same benchmarks more than once) */ + Tests_KTruss_Rmat, + // disable correctness checks for large graphs + // FIXME: High memory footprint. Perform nbr_intersection in chunks. + ::testing::Combine( + ::testing::Values(KTruss_Usecase{5, false, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(18, 16, 0.57, 0.19, 0.19, 0, true, false)))); CUGRAPH_TEST_PROGRAM_MAIN() \ No newline at end of file From 9bab048f489049a0cd2cf0b3a0968512e295fc34 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Fri, 22 Mar 2024 10:05:58 -0700 Subject: [PATCH 121/155] fix style --- cpp/tests/community/k_truss_test.cu | 34 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cu b/cpp/tests/community/k_truss_test.cu index 7d87d5076e7..934ed789306 100644 --- a/cpp/tests/community/k_truss_test.cu +++ b/cpp/tests/community/k_truss_test.cu @@ -14,6 +14,9 @@ * limitations under the License. */ +#include "utilities/check_utilities.hpp" +#include "utilities/conversion_utilities.hpp" + #include #include #include @@ -31,8 +34,6 @@ #include #include #include -#include "utilities/conversion_utilities.hpp" -#include "utilities/check_utilities.hpp" #include #include @@ -351,7 +352,6 @@ TEST_P(Tests_KTruss_File, CheckInt32Int64Float) override_File_Usecase_with_cmd_line_arguments(GetParam())); } - TEST_P(Tests_KTruss_Rmat, CheckInt32Int32Float) { run_current_test( @@ -379,20 +379,18 @@ INSTANTIATE_TEST_SUITE_P( KTruss_Usecase{4, true, false}, KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), - ::testing::Values( - cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), - cugraph::test::File_Usecase("test/datasets/karate.mtx")))); - -INSTANTIATE_TEST_SUITE_P( - rmat_small_test, - Tests_KTruss_Rmat, - // enable correctness checks - ::testing::Combine( - ::testing::Values(KTruss_Usecase{5, true, false}, - KTruss_Usecase{4, true, false}, - KTruss_Usecase{9, true, true}, - KTruss_Usecase{7, true, true}), - ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, true, false)))); + ::testing::Values(cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), + cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P(rmat_small_test, + Tests_KTruss_Rmat, + // enable correctness checks + ::testing::Combine(::testing::Values(KTruss_Usecase{5, true, false}, + KTruss_Usecase{4, true, false}, + KTruss_Usecase{9, true, true}, + KTruss_Usecase{7, true, true}), + ::testing::Values(cugraph::test::Rmat_Usecase( + 10, 16, 0.57, 0.19, 0.19, 0, true, false)))); INSTANTIATE_TEST_SUITE_P( rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with @@ -407,4 +405,4 @@ INSTANTIATE_TEST_SUITE_P( ::testing::Values(KTruss_Usecase{5, false, false}), ::testing::Values(cugraph::test::Rmat_Usecase(18, 16, 0.57, 0.19, 0.19, 0, true, false)))); -CUGRAPH_TEST_PROGRAM_MAIN() \ No newline at end of file +CUGRAPH_TEST_PROGRAM_MAIN() From a9cdb98c94cd5f9c7f799f98a55b955853fe479c Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 12:21:03 -0700 Subject: [PATCH 122/155] reorder includes --- cpp/src/community/k_truss_impl.cuh | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index bed14bb817a..101f34c1519 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -15,7 +15,15 @@ */ #pragma once -// FIXME: remove all unused imports +#include "prims/edge_bucket.cuh" +#include "prims/extract_transform_e.cuh" +#include "prims/fill_edge_property.cuh" +#include "prims/reduce_op.cuh" +#include "prims/transform_e.cuh" +#include "prims/transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v.cuh" +#include "prims/transform_reduce_e.cuh" +#include "prims/update_edge_src_dst_property.cuh" + #include #include #include @@ -36,15 +44,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include - namespace cugraph { // FIXME : This will be deleted once edge_triangle_count becomes public @@ -620,6 +619,7 @@ k_truss(raft::handle_t const& handle, } */ + // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { @@ -691,6 +691,8 @@ k_truss(raft::handle_t const& handle, *vertex_partition_range_lasts); } renumber_map = std::move(tmp_renumber_map); + + } // 5. Decompress the resulting graph to an edges list and ind intersection of edges endpoints @@ -717,6 +719,8 @@ k_truss(raft::handle_t const& handle, cur_graph_view, raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); + + auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); From 8297ae643b8cb965305a90697ee9a7a3e37d1712 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 12:23:43 -0700 Subject: [PATCH 123/155] pure c++ implementation of reference k_truss and leverage csr format --- cpp/tests/community/k_truss_test.cu | 324 ++++++++++++---------------- 1 file changed, 134 insertions(+), 190 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cu b/cpp/tests/community/k_truss_test.cu index 934ed789306..b48159da66f 100644 --- a/cpp/tests/community/k_truss_test.cu +++ b/cpp/tests/community/k_truss_test.cu @@ -26,15 +26,9 @@ #include #include -#include -#include - -#include - #include #include #include -#include #include #include @@ -61,88 +55,56 @@ class Tests_KTruss : public ::testing::TestWithParam + struct device_nearly_equal { + const type_t threshold_ratio; + const type_t threshold_magnitude; + + bool operator()(type_t lhs, type_t rhs) const + { + return std::abs(lhs - rhs) < + std::max(std::max(lhs, rhs) * threshold_ratio, threshold_magnitude); + } + }; + template - std::tuple, - rmm::device_uvector, - std::optional>> + std::tuple, + std::vector, + std::optional>> k_truss_reference( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, std::optional> edge_weight_view, edge_t k) { - rmm::device_uvector d_src(0, handle.get_stream()); - rmm::device_uvector d_dst(0, handle.get_stream()); - std::optional> d_wgt{std::nullopt}; - - // Decompress the edgelist - For Rmat generated graph, remove self loop and multi edges - std::tie(d_src, d_dst, d_wgt, std::ignore) = decompress_to_edgelist( - handle, - graph_view, - edge_weight_view ? std::make_optional(*edge_weight_view) : std::nullopt, - std::optional>{std::nullopt}, - std::optional>(std::nullopt)); - - // get host vectors - - std::vector h_src(d_src.size()); - raft::update_host(h_src.data(), d_src.data(), d_src.size(), handle.get_stream()); - std::vector h_dst(d_src.size()); - raft::update_host(h_dst.data(), d_dst.data(), d_dst.size(), handle.get_stream()); - - std::vector h_wgt(d_src.size()); - if (edge_weight_view) { - raft::update_host(h_wgt.data(), (*d_wgt).data(), (*d_wgt).size(), handle.get_stream()); - } - - std::vector h_src_(h_src.size()); - std::vector h_dst_(h_dst.size()); - // FIXME: Instead extract the vertices from the graph - std::vector dup_vertices(2 * h_src.size()); - - thrust::copy(thrust::host, h_src.begin(), h_src.end(), dup_vertices.begin()); - thrust::copy(thrust::host, h_dst.begin(), h_dst.end(), dup_vertices.begin() + h_src.size()); - - std::set vertices(dup_vertices.begin(), dup_vertices.end()); + auto [h_offsets, h_indices, h_values] = cugraph::test::graph_to_host_csr( + handle, + graph_view, + edge_weight_view, + std::optional>(std::nullopt)); + + std::vector vertices(h_offsets.size() - 1); + std::iota(vertices.begin(), vertices.end(), 0); auto n_dropped = 1; - auto edge_first = thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin())); - - auto weighted_edge_first = - thrust::make_zip_iterator(thrust::make_tuple(h_src.begin(), h_dst.begin(), h_wgt.begin())); - - thrust::sort(thrust::host, edge_first, edge_first + h_src.size()); - while (n_dropped > 0) { n_dropped = 0; std::set seen; // Go over all the vertices for (auto u = vertices.begin(); u != vertices.end(); ++u) { - // Find all neighbours of u - vertex_t idx_start = 0; - vertex_t idx_end = 0; - auto itr_lower_range = thrust::lower_bound(thrust::host, h_src.begin(), h_src.end(), *u); - - idx_start = thrust::distance(h_src.begin(), itr_lower_range); - - if (*itr_lower_range == *u) { - auto itr_upper_range = - thrust::upper_bound(thrust::host, itr_lower_range, h_src.end(), *u); - - idx_end = thrust::distance(itr_lower_range, itr_upper_range); - } - std::set nbrs_u; + // Find all neighbors of u from the offsets and indices array + auto idx_start = (h_offsets.begin() + (*u)); + auto idx_end = idx_start + 1; - for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { - nbrs_u.insert(*(h_dst.begin() + i)); + for (edge_t i = *idx_start; i < *idx_end; ++i) { + nbrs_u.insert(*(h_indices.begin() + i)); } - + seen.insert(*u); - std::set new_nbrs; - std::set_difference(nbrs_u.begin(), nbrs_u.end(), seen.begin(), @@ -151,21 +113,13 @@ class Tests_KTruss : public ::testing::TestWithParam nbrs_v; - - for (edge_t i = idx_start; i < idx_start + idx_end; ++i) { - nbrs_v.insert(*(h_dst.begin() + i)); - } + // Find all neighbors of v from the offsets and indices array + idx_start = (h_offsets.begin() + (*v)); + idx_end = idx_start + 1; + for (edge_t i = *idx_start; i < *idx_end; ++i) { + nbrs_v.insert(*(h_indices.begin() + i)); + } std::set nbr_intersection_u_v; // Find the intersection of nbr_u and nbr_v @@ -176,89 +130,53 @@ class Tests_KTruss : public ::testing::TestWithParam(e); - auto dst = thrust::get<1>(e); - auto remove_edge = thrust::make_tuple(src, dst); - return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); - } - - ); - - h_src.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); - h_dst.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); - h_wgt.resize(thrust::distance(weighted_edge_first, weighted_edge_last)); + auto del_v = std::find(h_indices.begin() + h_offsets[*u], h_indices.begin() + h_offsets[*u + 1], *v); + + if (edge_weight_view){ + (*h_values).erase((*h_values).begin() + std::distance(h_indices.begin(), del_v)); } - auto edge_last = thrust::remove_if( - thrust::host, - edge_first, - edge_first + h_src.size(), - [edge](auto e) { - auto src = thrust::get<0>(e); - auto dst = thrust::get<1>(e); - auto remove_edge = thrust::make_tuple(src, dst); - return (remove_edge == edge || thrust::make_tuple(dst, src) == edge); - } - - ); - - h_src.resize(thrust::distance(edge_first, edge_last)); - h_dst.resize(thrust::distance(edge_first, edge_last)); + std::transform(std::begin(h_offsets) + (*u) + 1,std::end(h_offsets),std::begin(h_offsets) + (*u) + 1,[](int x){return x-1;}); + h_indices.erase(del_v); + + // Delete edge in both directions + auto del_u = std::find(h_indices.begin() + h_offsets[*v], h_indices.begin() + h_offsets[*v + 1], *u); + + if (edge_weight_view){ + (*h_values).erase((*h_values).begin() + std::distance(h_indices.begin(), del_u)); + } + std::transform(std::begin(h_offsets) + (*v) + 1,std::end(h_offsets),std::begin(h_offsets) + (*v) + 1,[](int x){return x-1;}); + h_indices.erase(del_u); n_dropped += 1; } } } } - // convert back to device_vector - rmm::device_uvector d_reference_src(h_src.size(), handle.get_stream()); + h_offsets.erase(std::unique(h_offsets.begin() + 1, h_offsets.end() ), h_offsets.end()); // CSR start from 0 - raft::update_device(d_reference_src.data(), h_src.data(), h_src.size(), handle.get_stream()); - - rmm::device_uvector d_reference_dst(h_src.size(), handle.get_stream()); - - raft::update_device(d_reference_dst.data(), h_dst.data(), h_dst.size(), handle.get_stream()); - - auto d_reference_wgt = - std::make_optional>(h_src.size(), handle.get_stream()); - - if (edge_weight_view) { - raft::update_device( - (*d_reference_wgt).data(), h_wgt.data(), h_wgt.size(), handle.get_stream()); - } - - return std::make_tuple(std::move(d_reference_src), - std::move(d_reference_dst), - edge_weight_view ? std::move(d_reference_wgt) : std::nullopt); + return std::make_tuple(std::move(h_offsets), + std::move(h_indices), + edge_weight_view ? h_values : std::nullopt); } template void run_current_test(std::tuple const& param) { constexpr bool renumber = false; - auto [k_truss_usecase, input_usecase] = param; - raft::handle_t handle{}; HighResTimer hr_timer{}; if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement - hr_timer.start("MG Construct graph"); + hr_timer.start("SG Construct graph"); } // NX k_truss is not implemented for graph with self loop and isolated // vertices therefore dropped them especially for rmat generated graphs - auto [graph, edge_weights, d_renumber_map_labels] = + auto [graph, edge_weight, d_renumber_map_labels] = cugraph::test::construct_graph( handle, input_usecase, k_truss_usecase.test_weighted_, renumber, true, true); @@ -270,9 +188,6 @@ class Tests_KTruss : public ::testing::TestWithParam( - handle, graph_view, edge_weight_view, k_truss_usecase.k, false); + handle, + graph_view, + edge_weight ? std::make_optional((*edge_weight).view()) : std::nullopt, + k_truss_usecase.k, false); if (cugraph::test::g_perf) { RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement @@ -289,50 +207,69 @@ class Tests_KTruss : public ::testing::TestWithParam> modified_graph{std::nullopt}; + + std::optional, weight_t>> modified_edge_weight{std::nullopt}; + std::tie(*modified_graph, modified_edge_weight, std::ignore, std::ignore, std::ignore) = + cugraph::create_graph_from_edgelist(handle, + std::nullopt, + std::move(d_cugraph_src), + std::move(d_cugraph_dst), + std::move(d_cugraph_wgt), + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{true, false}, + renumber); + + // Convert cugraph results CSR + auto [h_cugraph_offsets, h_cugraph_indices, h_cugraph_values] = cugraph::test::graph_to_host_csr( // FIXME: remove renumber map which is unused ********************* + handle, + (*modified_graph).view(), + modified_edge_weight ? std::make_optional((*modified_edge_weight).view()) : std::nullopt, + std::optional>(std::nullopt)); + + // Remove isolated vertices. + h_cugraph_offsets.erase(std::unique(h_cugraph_offsets.begin() + 1, h_cugraph_offsets.end() ), h_cugraph_offsets.end()); // CSR start from 0 + auto [h_reference_offsets, h_reference_indices, h_reference_values] = k_truss_reference( - handle, graph_view, edge_weight_view, k_truss_usecase.k); - if (edge_weight_view) { - auto d_edge_first = thrust::make_zip_iterator(thrust::make_tuple( - d_cugraph_src.begin(), d_cugraph_dst.begin(), (*d_cugraph_wgt).begin())); - - thrust::sort(handle.get_thrust_policy(), d_edge_first, d_edge_first + d_cugraph_src.size()); - } else { - auto d_edge_first = thrust::make_zip_iterator( - thrust::make_tuple(d_cugraph_src.begin(), d_cugraph_dst.begin())); - - thrust::sort(handle.get_thrust_policy(), d_edge_first, d_edge_first + d_cugraph_src.size()); - } + handle, + graph_view, + edge_weight ? std::make_optional((*edge_weight).view()) : std::nullopt, + k_truss_usecase.k); - EXPECT_EQ(d_cugraph_src.size(), d_reference_src.size()); + EXPECT_EQ(h_cugraph_offsets.size(), h_reference_offsets.size()); - ASSERT_TRUE(thrust::equal(handle.get_thrust_policy(), - d_cugraph_src.begin(), - d_cugraph_src.end(), - d_reference_src.begin())); + ASSERT_TRUE(std::equal(h_cugraph_offsets.begin(), + h_cugraph_offsets.end(), + h_reference_offsets.begin())); - ASSERT_TRUE(thrust::equal(handle.get_thrust_policy(), - d_cugraph_dst.begin(), - d_cugraph_dst.end(), - d_reference_dst.begin())); + ASSERT_TRUE(std::equal(h_cugraph_indices.begin(), + h_cugraph_indices.end(), + h_reference_indices.begin())); - if (edge_weight_view) { - auto compare_functor = cugraph::test::device_nearly_equal{ + if (edge_weight) { + auto compare_functor = device_nearly_equal{ weight_t{1e-3}, - weight_t{(weight_t{1} / static_cast((*d_cugraph_wgt).size())) * + weight_t{(weight_t{1} / static_cast((*h_cugraph_values).size())) * weight_t{1e-3}}}; - EXPECT_TRUE(thrust::equal(handle.get_thrust_policy(), - (*d_cugraph_wgt).begin(), - (*d_cugraph_wgt).end(), - (*d_reference_wgt).begin(), - compare_functor)); + EXPECT_TRUE(std::equal((*h_cugraph_values).begin(), + (*h_cugraph_values).end(), + (*h_reference_values).begin(), + compare_functor)); } + } } }; using Tests_KTruss_File = Tests_KTruss; -using Tests_KTruss_Rmat = Tests_KTruss; +//using Tests_KTruss_Rmat = Tests_KTruss; TEST_P(Tests_KTruss_File, CheckInt32Int32Float) { @@ -340,18 +277,13 @@ TEST_P(Tests_KTruss_File, CheckInt32Int32Float) override_File_Usecase_with_cmd_line_arguments(GetParam())); } +/* TEST_P(Tests_KTruss_File, CheckInt64Int64Float) { run_current_test( override_File_Usecase_with_cmd_line_arguments(GetParam())); } -TEST_P(Tests_KTruss_File, CheckInt32Int64Float) -{ - run_current_test( - override_File_Usecase_with_cmd_line_arguments(GetParam())); -} - TEST_P(Tests_KTruss_Rmat, CheckInt32Int32Float) { run_current_test( @@ -363,13 +295,19 @@ TEST_P(Tests_KTruss_Rmat, CheckInt64Int64Float) run_current_test( override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); } +*/ -TEST_P(Tests_KTruss_Rmat, CheckInt32Int64Float) -{ - run_current_test( - override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); -} +INSTANTIATE_TEST_SUITE_P( + simple_test, + Tests_KTruss_File, + ::testing::Combine( + // enable correctness checks + ::testing::Values(KTruss_Usecase{5, true, false}, + KTruss_Usecase{7, true, true}), + ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), + cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); +/* INSTANTIATE_TEST_SUITE_P( simple_test, Tests_KTruss_File, @@ -381,17 +319,22 @@ INSTANTIATE_TEST_SUITE_P( KTruss_Usecase{7, true, true}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), cugraph::test::File_Usecase("test/datasets/karate.mtx")))); +*/ +/* INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_KTruss_Rmat, // enable correctness checks ::testing::Combine(::testing::Values(KTruss_Usecase{5, true, false}, - KTruss_Usecase{4, true, false}, - KTruss_Usecase{9, true, true}, - KTruss_Usecase{7, true, true}), + //KTruss_Usecase{4, true, false}, + //KTruss_Usecase{9, true, true}, + //KTruss_Usecase{7, true, true} + ), ::testing::Values(cugraph::test::Rmat_Usecase( 10, 16, 0.57, 0.19, 0.19, 0, true, false)))); +*/ +#if 0 INSTANTIATE_TEST_SUITE_P( rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with --gtest_filter to select only the rmat_benchmark_test with a specific @@ -402,7 +345,8 @@ INSTANTIATE_TEST_SUITE_P( // disable correctness checks for large graphs // FIXME: High memory footprint. Perform nbr_intersection in chunks. ::testing::Combine( - ::testing::Values(KTruss_Usecase{5, false, false}), - ::testing::Values(cugraph::test::Rmat_Usecase(18, 16, 0.57, 0.19, 0.19, 0, true, false)))); + ::testing::Values(KTruss_Usecase{17, false, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(17, 16, 0.57, 0.19, 0.19, 0, true, false)))); +#endif CUGRAPH_TEST_PROGRAM_MAIN() From 406a317088a06cba1d777fa93476382c32db126e Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 12:35:32 -0700 Subject: [PATCH 124/155] rename reeference test file --- cpp/tests/CMakeLists.txt | 2 +- .../{k_truss_test.cu => k_truss_test.cpp} | 36 +++++-------------- 2 files changed, 10 insertions(+), 28 deletions(-) rename cpp/tests/community/{k_truss_test.cu => k_truss_test.cpp} (93%) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index f5fa15ed923..32e020ff6f9 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -416,7 +416,7 @@ ConfigureTest(K_CORE_TEST cores/k_core_test.cpp) ############################################################################################### # - K-truss tests -------------------------------------------------------------------------- -ConfigureTest(K_TRUSS_TEST community/k_truss_test.cu) +ConfigureTest(K_TRUSS_TEST community/k_truss_test.cpp) ################################################################################################### # - Triangle Count tests -------------------------------------------------------------------------- diff --git a/cpp/tests/community/k_truss_test.cu b/cpp/tests/community/k_truss_test.cpp similarity index 93% rename from cpp/tests/community/k_truss_test.cu rename to cpp/tests/community/k_truss_test.cpp index b48159da66f..e56cc7e5447 100644 --- a/cpp/tests/community/k_truss_test.cu +++ b/cpp/tests/community/k_truss_test.cpp @@ -55,7 +55,8 @@ class Tests_KTruss : public ::testing::TestWithParam struct device_nearly_equal { const type_t threshold_ratio; @@ -269,7 +270,7 @@ class Tests_KTruss : public ::testing::TestWithParam; -//using Tests_KTruss_Rmat = Tests_KTruss; +using Tests_KTruss_Rmat = Tests_KTruss; TEST_P(Tests_KTruss_File, CheckInt32Int32Float) { @@ -277,7 +278,6 @@ TEST_P(Tests_KTruss_File, CheckInt32Int32Float) override_File_Usecase_with_cmd_line_arguments(GetParam())); } -/* TEST_P(Tests_KTruss_File, CheckInt64Int64Float) { run_current_test( @@ -295,19 +295,7 @@ TEST_P(Tests_KTruss_Rmat, CheckInt64Int64Float) run_current_test( override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); } -*/ - -INSTANTIATE_TEST_SUITE_P( - simple_test, - Tests_KTruss_File, - ::testing::Combine( - // enable correctness checks - ::testing::Values(KTruss_Usecase{5, true, false}, - KTruss_Usecase{7, true, true}), - ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), - cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); -/* INSTANTIATE_TEST_SUITE_P( simple_test, Tests_KTruss_File, @@ -317,24 +305,19 @@ INSTANTIATE_TEST_SUITE_P( KTruss_Usecase{4, true, false}, KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), - ::testing::Values(cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), - cugraph::test::File_Usecase("test/datasets/karate.mtx")))); -*/ + ::testing::Values(cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/dolphins.mtx"), + cugraph::test::File_Usecase("/home/nfs/jnke/ktruss/cugraph/datasets/karate.mtx")))); -/* INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_KTruss_Rmat, // enable correctness checks ::testing::Combine(::testing::Values(KTruss_Usecase{5, true, false}, - //KTruss_Usecase{4, true, false}, - //KTruss_Usecase{9, true, true}, - //KTruss_Usecase{7, true, true} - ), + KTruss_Usecase{4, true, false}, + KTruss_Usecase{9, true, true}, + KTruss_Usecase{7, true, true}), ::testing::Values(cugraph::test::Rmat_Usecase( 10, 16, 0.57, 0.19, 0.19, 0, true, false)))); -*/ -#if 0 INSTANTIATE_TEST_SUITE_P( rmat_benchmark_test, /* note that scale & edge factor can be overridden in benchmarking (with --gtest_filter to select only the rmat_benchmark_test with a specific @@ -345,8 +328,7 @@ INSTANTIATE_TEST_SUITE_P( // disable correctness checks for large graphs // FIXME: High memory footprint. Perform nbr_intersection in chunks. ::testing::Combine( - ::testing::Values(KTruss_Usecase{17, false, false}), + ::testing::Values(KTruss_Usecase{15, false, false}), ::testing::Values(cugraph::test::Rmat_Usecase(17, 16, 0.57, 0.19, 0.19, 0, true, false)))); -#endif CUGRAPH_TEST_PROGRAM_MAIN() From a0a2d6350190835a77360d277e396482da5280a4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 12:38:54 -0700 Subject: [PATCH 125/155] fix style --- cpp/src/community/k_truss_impl.cuh | 5 -- cpp/tests/community/k_truss_test.cpp | 126 ++++++++++++++------------- 2 files changed, 66 insertions(+), 65 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 101f34c1519..42a7ebbe979 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -619,7 +619,6 @@ k_truss(raft::handle_t const& handle, } */ - // 4. Keep only the edges from a low-degree vertex to a high-degree vertex. { @@ -691,8 +690,6 @@ k_truss(raft::handle_t const& handle, *vertex_partition_range_lasts); } renumber_map = std::move(tmp_renumber_map); - - } // 5. Decompress the resulting graph to an edges list and ind intersection of edges endpoints @@ -719,8 +716,6 @@ k_truss(raft::handle_t const& handle, cur_graph_view, raft::device_span(edgelist_srcs.data(), edgelist_srcs.size()), raft::device_span(edgelist_dsts.data(), edgelist_dsts.size())); - - auto transposed_edge_first = thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()); diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index e56cc7e5447..9aca020233a 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -65,14 +65,12 @@ class Tests_KTruss : public ::testing::TestWithParam - std::tuple, - std::vector, - std::optional>> + std::tuple, std::vector, std::optional>> k_truss_reference( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, @@ -80,11 +78,11 @@ class Tests_KTruss : public ::testing::TestWithParam>(std::nullopt)); - + handle, + graph_view, + edge_weight_view, + std::optional>(std::nullopt)); + std::vector vertices(h_offsets.size() - 1); std::iota(vertices.begin(), vertices.end(), 0); @@ -98,12 +96,12 @@ class Tests_KTruss : public ::testing::TestWithParam nbrs_u; // Find all neighbors of u from the offsets and indices array auto idx_start = (h_offsets.begin() + (*u)); - auto idx_end = idx_start + 1; + auto idx_end = idx_start + 1; for (edge_t i = *idx_start; i < *idx_end; ++i) { nbrs_u.insert(*(h_indices.begin() + i)); } - + seen.insert(*u); std::set new_nbrs; std::set_difference(nbrs_u.begin(), @@ -117,10 +115,10 @@ class Tests_KTruss : public ::testing::TestWithParam nbrs_v; // Find all neighbors of v from the offsets and indices array idx_start = (h_offsets.begin() + (*v)); - idx_end = idx_start + 1; + idx_end = idx_start + 1; for (edge_t i = *idx_start; i < *idx_end; ++i) { nbrs_v.insert(*(h_indices.begin() + i)); - } + } std::set nbr_intersection_u_v; // Find the intersection of nbr_u and nbr_v @@ -131,22 +129,30 @@ class Tests_KTruss : public ::testing::TestWithParam void run_current_test(std::tuple const& param) { - constexpr bool renumber = false; + constexpr bool renumber = false; auto [k_truss_usecase, input_usecase] = param; raft::handle_t handle{}; @@ -199,7 +205,8 @@ class Tests_KTruss : public ::testing::TestWithParam> modified_graph{std::nullopt}; - - std::optional, weight_t>> modified_edge_weight{std::nullopt}; - std::tie(*modified_graph, modified_edge_weight, std::ignore, std::ignore, std::ignore) = - cugraph::create_graph_from_edgelist(handle, - std::nullopt, - std::move(d_cugraph_src), - std::move(d_cugraph_dst), - std::move(d_cugraph_wgt), - std::nullopt, - std::nullopt, - cugraph::graph_properties_t{true, false}, - renumber); + std::optional> modified_graph{std::nullopt}; + + std::optional< + cugraph::edge_property_t, weight_t>> + modified_edge_weight{std::nullopt}; + std::tie(*modified_graph, modified_edge_weight, std::ignore, std::ignore, std::ignore) = + cugraph:: + create_graph_from_edgelist( + handle, + std::nullopt, + std::move(d_cugraph_src), + std::move(d_cugraph_dst), + std::move(d_cugraph_wgt), + std::nullopt, + std::nullopt, + cugraph::graph_properties_t{true, false}, + renumber); // Convert cugraph results CSR - auto [h_cugraph_offsets, h_cugraph_indices, h_cugraph_values] = cugraph::test::graph_to_host_csr( // FIXME: remove renumber map which is unused ********************* - handle, - (*modified_graph).view(), - modified_edge_weight ? std::make_optional((*modified_edge_weight).view()) : std::nullopt, - std::optional>(std::nullopt)); + auto [h_cugraph_offsets, h_cugraph_indices, h_cugraph_values] = + cugraph::test::graph_to_host_csr( // FIXME: remove renumber map which is unused + // ********************* + handle, + (*modified_graph).view(), + modified_edge_weight ? std::make_optional((*modified_edge_weight).view()) : std::nullopt, + std::optional>(std::nullopt)); // Remove isolated vertices. - h_cugraph_offsets.erase(std::unique(h_cugraph_offsets.begin() + 1, h_cugraph_offsets.end() ), h_cugraph_offsets.end()); // CSR start from 0 + h_cugraph_offsets.erase(std::unique(h_cugraph_offsets.begin() + 1, h_cugraph_offsets.end()), + h_cugraph_offsets.end()); // CSR start from 0 auto [h_reference_offsets, h_reference_indices, h_reference_values] = k_truss_reference( handle, @@ -246,13 +254,11 @@ class Tests_KTruss : public ::testing::TestWithParam{ @@ -264,7 +270,6 @@ class Tests_KTruss : public ::testing::TestWithParam Date: Sun, 24 Mar 2024 13:13:20 -0700 Subject: [PATCH 126/155] remove unused import --- cpp/src/community/k_truss_impl.cuh | 7 ------- cpp/tests/community/k_truss_test.cpp | 3 +-- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 42a7ebbe979..bf264ed6530 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -18,28 +18,21 @@ #include "prims/edge_bucket.cuh" #include "prims/extract_transform_e.cuh" #include "prims/fill_edge_property.cuh" -#include "prims/reduce_op.cuh" #include "prims/transform_e.cuh" #include "prims/transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v.cuh" -#include "prims/transform_reduce_e.cuh" #include "prims/update_edge_src_dst_property.cuh" #include #include #include #include -#include -#include -#include #include #include #include #include -#include #include #include -#include #include #include #include diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 9aca020233a..3c4cd705a37 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -235,8 +235,7 @@ class Tests_KTruss : public ::testing::TestWithParam Date: Sun, 24 Mar 2024 13:28:10 -0700 Subject: [PATCH 127/155] remove unused import --- .../community/edge_triangle_count_impl.cuh | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/cpp/src/community/edge_triangle_count_impl.cuh b/cpp/src/community/edge_triangle_count_impl.cuh index 30d1da8b5aa..9cbc3844c17 100644 --- a/cpp/src/community/edge_triangle_count_impl.cuh +++ b/cpp/src/community/edge_triangle_count_impl.cuh @@ -16,33 +16,18 @@ #pragma once -#include +#include "detail/graph_partition_utils.cuh" +#include "prims/transform_reduce_dst_nbr_intersection_of_e_endpoints_by_v.cuh" + #include #include -#include #include -#include - -#include -#include - -#include #include -#include -#include #include #include #include -#include -#include - -#include -#include -#include -#include - namespace cugraph { namespace detail { From a87f07a85f802bbc506ac961d2b1fa969664ad41 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 13:29:57 -0700 Subject: [PATCH 128/155] add fixme for chunking --- cpp/src/community/edge_triangle_count_impl.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/community/edge_triangle_count_impl.cuh b/cpp/src/community/edge_triangle_count_impl.cuh index 9cbc3844c17..1370c1a17f2 100644 --- a/cpp/src/community/edge_triangle_count_impl.cuh +++ b/cpp/src/community/edge_triangle_count_impl.cuh @@ -89,6 +89,7 @@ std::enable_if_t> edge_triangle_count_im thrust::sort(handle.get_thrust_policy(), edge_first, edge_first + edgelist_srcs.size()); + // FIXME: Perform 'nbr_intersection' in chunks to reduce peak memory. auto [intersection_offsets, intersection_indices] = detail::nbr_intersection(handle, graph_view, From ecbc2ceb767707286408c0d6d95b6da385a9c3ed Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 13:32:21 -0700 Subject: [PATCH 129/155] reorder member variables --- cpp/tests/community/k_truss_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 3c4cd705a37..4ab64ed75a5 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -40,8 +40,8 @@ struct KTruss_Usecase { int32_t k{3}; - bool check_correctness{true}; bool test_weighted_{false}; + bool check_correctness{true}; }; template @@ -316,8 +316,8 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_KTruss_Rmat, // enable correctness checks - ::testing::Combine(::testing::Values(KTruss_Usecase{5, true, false}, - KTruss_Usecase{4, true, false}, + ::testing::Combine(::testing::Values(KTruss_Usecase{5, false, true}, + KTruss_Usecase{4, false, true}, KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), ::testing::Values(cugraph::test::Rmat_Usecase( From e6e216685fb1ba79e4cde0820a1aacb9f208a421 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 13:50:02 -0700 Subject: [PATCH 130/155] update docstrings --- cpp/tests/community/k_truss_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 4ab64ed75a5..e42239fb698 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -181,8 +181,8 @@ class Tests_KTruss : public ::testing::TestWithParam( handle, input_usecase, k_truss_usecase.test_weighted_, renumber, true, true); @@ -233,7 +233,7 @@ class Tests_KTruss : public ::testing::TestWithParam Date: Sun, 24 Mar 2024 14:01:41 -0700 Subject: [PATCH 131/155] remove outdated code and rename function --- cpp/src/community/k_truss_impl.cuh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index bf264ed6530..207d6fc5a89 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -179,7 +179,6 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, prefix_sum_invalid.back_element(handle.get_stream()), handle.get_stream()); - // const bool const_is_q_r_edge = is_q_r_edge; thrust::for_each( handle.get_thrust_policy(), thrust::make_counting_iterator(0), @@ -465,7 +464,7 @@ struct extract_low_to_high_degree_edges_t { }; template -struct generate_p_r_and_q_r_from_p_q { +struct generate_p_r_or_q_r_from_p_q { raft::device_span intersection_offsets{}; raft::device_span intersection_indices{}; raft::device_span invalid_srcs{}; @@ -790,7 +789,7 @@ k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_p_r_edge_p_q), - generate_p_r_and_q_r_from_p_q{ + generate_p_r_or_q_r_from_p_q{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), @@ -804,7 +803,7 @@ k_truss(raft::handle_t const& handle, handle.get_thrust_policy(), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), get_dataframe_buffer_end(vertex_pair_buffer_q_r_edge_p_q), - generate_p_r_and_q_r_from_p_q{ + generate_p_r_or_q_r_from_p_q{ raft::device_span(intersection_offsets.data(), intersection_offsets.size()), raft::device_span(intersection_indices.data(), intersection_indices.size()), From fbc5681c11aae46a48c183aeaf49e1b54d99c41e Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 14:15:40 -0700 Subject: [PATCH 132/155] update datasets paths --- cpp/tests/community/k_truss_test.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index e42239fb698..a9db6315565 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -39,9 +39,9 @@ #include struct KTruss_Usecase { - int32_t k{3}; + int32_t k_{3}; bool test_weighted_{false}; - bool check_correctness{true}; + bool check_correctness_{true}; }; template @@ -205,7 +205,7 @@ class Tests_KTruss : public ::testing::TestWithParam> modified_graph{std::nullopt}; std::optional< @@ -249,7 +249,7 @@ class Tests_KTruss : public ::testing::TestWithParam Date: Sun, 24 Mar 2024 14:21:18 -0700 Subject: [PATCH 133/155] fix style --- cpp/tests/community/k_truss_test.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index a9db6315565..b89a7050ce6 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -309,10 +309,9 @@ INSTANTIATE_TEST_SUITE_P( KTruss_Usecase{4, true, false}, KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), - ::testing::Values( - cugraph::test::File_Usecase("test/datasets/karate.mtx"), - cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), - cugraph::test::File_Usecase("test/datasets/web-Google.mtx")))); + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), + cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), + cugraph::test::File_Usecase("test/datasets/web-Google.mtx")))); INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_KTruss_Rmat, From 2100e30a72a67bd221b09b6bd4476193dff1e252 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Sun, 24 Mar 2024 15:39:06 -0700 Subject: [PATCH 134/155] remove asymmetric datasets --- cpp/tests/community/k_truss_test.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index b89a7050ce6..cc7fe7f41d1 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -310,8 +310,7 @@ INSTANTIATE_TEST_SUITE_P( KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), - cugraph::test::File_Usecase("test/datasets/dolphins.mtx"), - cugraph::test::File_Usecase("test/datasets/web-Google.mtx")))); + cugraph::test::File_Usecase("test/datasets/dolphins.mtx")))); INSTANTIATE_TEST_SUITE_P(rmat_small_test, Tests_KTruss_Rmat, @@ -333,7 +332,7 @@ INSTANTIATE_TEST_SUITE_P( // disable correctness checks for large graphs // FIXME: High memory footprint. Perform nbr_intersection in chunks. ::testing::Combine( - ::testing::Values(KTruss_Usecase{15, false, false}), - ::testing::Values(cugraph::test::Rmat_Usecase(17, 16, 0.57, 0.19, 0.19, 0, true, false)))); + ::testing::Values(KTruss_Usecase{12, false, false}), + ::testing::Values(cugraph::test::Rmat_Usecase(14, 16, 0.57, 0.19, 0.19, 0, true, false)))); CUGRAPH_TEST_PROGRAM_MAIN() From b66bd1a029f110fbfd63b0413a205ccaaa933db8 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 02:37:15 -0700 Subject: [PATCH 135/155] rename variable --- cpp/src/community/k_truss_impl.cuh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 207d6fc5a89..3677d2ce86d 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -47,14 +47,14 @@ rmm::device_uvector edge_triangle_count( raft::device_span edgelist_srcs, raft::device_span edgelist_dsts); -template +template struct unroll_edge { edge_t num_valid_edges{}; raft::device_span num_triangles{}; - EdgeBuffer edge_to_unroll_first{}; - EdgeBuffer transposed_valid_edge_first{}; - EdgeBuffer transposed_valid_edge_last{}; - EdgeBuffer transposed_invalid_edge_last{}; + EdgeIterator edge_to_unroll_first{}; + EdgeIterator transposed_valid_edge_first{}; + EdgeIterator transposed_valid_edge_last{}; + EdgeIterator transposed_invalid_edge_last{}; __device__ thrust::tuple operator()(edge_t i) const { @@ -114,11 +114,11 @@ rmm::device_uvector prefix_sum_valid_and_invalid_edges( return prefix_sum; } -template +template edge_t remove_overcompensating_edges(raft::handle_t const& handle, edge_t buffer_size, - EdgeBuffer potential_closing_or_incoming_edges, - EdgeBuffer incoming_or_potential_closing_edges, + EdgeIterator potential_closing_or_incoming_edges, + EdgeIterator incoming_or_potential_closing_edges, raft::device_span invalid_edgelist_srcs, raft::device_span invalid_edgelist_dsts) { From 048b408c3740264472a737922e356dec18ec22a7 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 02:49:37 -0700 Subject: [PATCH 136/155] avoid illegal memory access --- cpp/src/community/k_truss_impl.cuh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 3677d2ce86d..4e25d8f72c8 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -63,19 +63,19 @@ struct unroll_edge { thrust::get<0>(*(edge_to_unroll_first + i))); // Find its position in either partition of the transposed edgelist // An edge can be in found in either of the two partitions (valid or invalid) - auto itr = thrust::lower_bound( thrust::seq, transposed_valid_edge_last, transposed_invalid_edge_last, pair); - auto idx = thrust::distance(transposed_valid_edge_last, itr) + num_valid_edges; - - if (*itr != pair) { + size_t idx{}; + if (itr != transposed_invalid_edge_last && *itr == pair) { + idx = static_cast(thrust::distance(transposed_valid_edge_last, itr) + num_valid_edges); + } + else { // The edge must be in the first boundary itr = thrust::lower_bound( thrust::seq, transposed_valid_edge_first, transposed_valid_edge_last, pair); + assert(*itr == pair); idx = thrust::distance(transposed_valid_edge_first, itr); } - - assert(*itr == pair); cuda::atomic_ref atomic_counter(num_triangles[idx]); auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); } From 3e032557b6c6d0cc4f7197b2740939f5b2d510d5 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 07:25:04 -0700 Subject: [PATCH 137/155] rename function for general purpose --- cpp/src/community/k_truss_impl.cuh | 31 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 4e25d8f72c8..858524ec9f2 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -81,28 +81,31 @@ struct unroll_edge { } }; +// FIXME: May re-locate this function as a general utility function for graph algorithm +// implementations. template -rmm::device_uvector prefix_sum_valid_and_invalid_edges( +rmm::device_uvector compute_prefix_sum( // The edgelist is segmented into 2 partitions (valid and invalid edges) // and edges to be unrolled can be either in the valid or the invalid edge // partition. raft::handle_t const& handle, - raft::device_span invalid_dsts, - raft::device_span edgelist_dsts) + raft::device_span sorted_vertices, + raft::device_span query_vertices + ) { - rmm::device_uvector prefix_sum(invalid_dsts.size() + 1, handle.get_stream()); + rmm::device_uvector prefix_sum(query_vertices.size() + 1, handle.get_stream()); thrust::tabulate(handle.get_thrust_policy(), prefix_sum.begin(), - prefix_sum.begin() + invalid_dsts.size(), - [invalid_dsts, - num_edges = edgelist_dsts.size(), - edgelist_dsts = edgelist_dsts.begin()] __device__(auto idx) { + prefix_sum.begin() + query_vertices.size(), + [query_vertices, + num_edges = sorted_vertices.size(), + sorted_vertices = sorted_vertices.begin()] __device__(auto idx) { auto itr_lower_valid = thrust::lower_bound( - thrust::seq, edgelist_dsts, edgelist_dsts + num_edges, invalid_dsts[idx]); + thrust::seq, sorted_vertices, sorted_vertices + num_edges, query_vertices[idx]); auto itr_upper_valid = thrust::upper_bound( - thrust::seq, itr_lower_valid, edgelist_dsts + num_edges, invalid_dsts[idx]); + thrust::seq, itr_lower_valid, sorted_vertices + num_edges, query_vertices[idx]); auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); @@ -159,12 +162,12 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, raft::device_span edgelist_dsts, raft::device_span num_triangles) { - auto prefix_sum_valid = prefix_sum_valid_and_invalid_edges( + auto prefix_sum_valid = compute_prefix_sum( handle, - raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges), - raft::device_span(edgelist_dsts.data(), num_valid_edges)); + raft::device_span(edgelist_dsts.data(), num_valid_edges), + raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); - auto prefix_sum_invalid = prefix_sum_valid_and_invalid_edges( + auto prefix_sum_invalid = compute_prefix_sum( handle, raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges), raft::device_span(edgelist_dsts.data() + num_valid_edges, num_invalid_edges)); From 8f75316f4daaf668373f5da913809a48f4a670b1 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 07:29:16 -0700 Subject: [PATCH 138/155] remove fixme --- cpp/src/community/k_truss_impl.cuh | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 858524ec9f2..86eeca5844e 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -512,8 +512,6 @@ k_truss(raft::handle_t const& handle, // nothing to do } - // 2. Exclude self-loops (FIXME: better mask-out once we add masking support). - std::optional> modified_graph{std::nullopt}; std::optional> modified_graph_view{std::nullopt}; std::optional> renumber_map{std::nullopt}; From 24a6f765d338bbe7a817a199db0ec876215641b2 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 08:12:30 -0700 Subject: [PATCH 139/155] combine similar thrust calls --- cpp/src/community/k_truss_impl.cuh | 35 +++++------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 86eeca5844e..aacd0f58ccb 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -332,9 +332,7 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, resize_dataframe_buffer(incoming_edges_to_r, num_edges_not_overcomp, handle.get_stream()); } - // FIXME: Combine both 'thrust::for_each' - if constexpr (is_q_r_edge) { - thrust::for_each( + thrust::for_each( handle.get_thrust_policy(), thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r)), @@ -349,34 +347,14 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto potential_or_incoming_e) { auto potential_e = thrust::get<0>(potential_or_incoming_e); auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); + //thrust::tuple> transposed_invalid_edge_; auto transposed_invalid_edge = thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<1>(potential_e)); - auto itr = - thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); - assert(*itr == transposed_invalid_edge); - auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; - cuda::atomic_ref atomic_counter(num_triangles[dist]); - auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); - }); - } else { - thrust::for_each( - handle.get_thrust_policy(), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), - get_dataframe_buffer_begin(incoming_edges_to_r)), - thrust::make_zip_iterator( - get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, - get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), - [num_triangles = num_triangles.begin(), - num_valid_edges, - invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, - edgelist_srcs.begin() + num_valid_edges), - invalid_last = thrust::make_zip_iterator( - edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto potential_or_incoming_e) { - auto potential_e = thrust::get<0>(potential_or_incoming_e); - auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); - auto transposed_invalid_edge = - thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<0>(potential_e)); + if constexpr (!is_q_r_edge) { + transposed_invalid_edge = + thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<0>(potential_e)); + } auto itr = thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); assert(*itr == transposed_invalid_edge); @@ -385,7 +363,6 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, cuda::atomic_ref atomic_counter(num_triangles[dist]); auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); }); - } thrust::for_each( handle.get_thrust_policy(), From f95fc13f11f05c0e3e9397a5c88dac050f643ab2 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 08:16:19 -0700 Subject: [PATCH 140/155] fix style --- cpp/src/community/k_truss_impl.cuh | 96 +++++++++++++++--------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index aacd0f58ccb..8ff2093dc99 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -67,9 +67,9 @@ struct unroll_edge { thrust::seq, transposed_valid_edge_last, transposed_invalid_edge_last, pair); size_t idx{}; if (itr != transposed_invalid_edge_last && *itr == pair) { - idx = static_cast(thrust::distance(transposed_valid_edge_last, itr) + num_valid_edges); - } - else { + idx = + static_cast(thrust::distance(transposed_valid_edge_last, itr) + num_valid_edges); + } else { // The edge must be in the first boundary itr = thrust::lower_bound( thrust::seq, transposed_valid_edge_first, transposed_valid_edge_last, pair); @@ -90,27 +90,27 @@ rmm::device_uvector compute_prefix_sum( // partition. raft::handle_t const& handle, raft::device_span sorted_vertices, - raft::device_span query_vertices - ) + raft::device_span query_vertices) { rmm::device_uvector prefix_sum(query_vertices.size() + 1, handle.get_stream()); - thrust::tabulate(handle.get_thrust_policy(), - prefix_sum.begin(), - prefix_sum.begin() + query_vertices.size(), - [query_vertices, - num_edges = sorted_vertices.size(), - sorted_vertices = sorted_vertices.begin()] __device__(auto idx) { - auto itr_lower_valid = thrust::lower_bound( - thrust::seq, sorted_vertices, sorted_vertices + num_edges, query_vertices[idx]); + thrust::tabulate( + handle.get_thrust_policy(), + prefix_sum.begin(), + prefix_sum.begin() + query_vertices.size(), + [query_vertices, + num_edges = sorted_vertices.size(), + sorted_vertices = sorted_vertices.begin()] __device__(auto idx) { + auto itr_lower_valid = thrust::lower_bound( + thrust::seq, sorted_vertices, sorted_vertices + num_edges, query_vertices[idx]); - auto itr_upper_valid = thrust::upper_bound( - thrust::seq, itr_lower_valid, sorted_vertices + num_edges, query_vertices[idx]); + auto itr_upper_valid = thrust::upper_bound( + thrust::seq, itr_lower_valid, sorted_vertices + num_edges, query_vertices[idx]); - auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); + auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); - return dist_valid; - }); + return dist_valid; + }); thrust::exclusive_scan( handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); @@ -333,36 +333,36 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, } thrust::for_each( - handle.get_thrust_policy(), - thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), - get_dataframe_buffer_begin(incoming_edges_to_r)), - thrust::make_zip_iterator( - get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, - get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), - [num_triangles = num_triangles.begin(), - num_valid_edges, - invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, - edgelist_srcs.begin() + num_valid_edges), - invalid_last = thrust::make_zip_iterator( - edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto potential_or_incoming_e) { - auto potential_e = thrust::get<0>(potential_or_incoming_e); - auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); - //thrust::tuple> transposed_invalid_edge_; - auto transposed_invalid_edge = - thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<1>(potential_e)); - - if constexpr (!is_q_r_edge) { - transposed_invalid_edge = - thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<0>(potential_e)); - } - auto itr = - thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); - assert(*itr == transposed_invalid_edge); - auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; - - cuda::atomic_ref atomic_counter(num_triangles[dist]); - auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); - }); + handle.get_thrust_policy(), + thrust::make_zip_iterator(get_dataframe_buffer_begin(potential_closing_edges), + get_dataframe_buffer_begin(incoming_edges_to_r)), + thrust::make_zip_iterator( + get_dataframe_buffer_begin(potential_closing_edges) + num_edges_not_overcomp, + get_dataframe_buffer_begin(incoming_edges_to_r) + num_edges_not_overcomp), + [num_triangles = num_triangles.begin(), + num_valid_edges, + invalid_first = thrust::make_zip_iterator(edgelist_dsts.begin() + num_valid_edges, + edgelist_srcs.begin() + num_valid_edges), + invalid_last = thrust::make_zip_iterator( + edgelist_dsts.end(), edgelist_srcs.end())] __device__(auto potential_or_incoming_e) { + auto potential_e = thrust::get<0>(potential_or_incoming_e); + auto incoming_e_to_r = thrust::get<1>(potential_or_incoming_e); + // thrust::tuple> transposed_invalid_edge_; + auto transposed_invalid_edge = + thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<1>(potential_e)); + + if constexpr (!is_q_r_edge) { + transposed_invalid_edge = + thrust::make_tuple(thrust::get<1>(incoming_e_to_r), thrust::get<0>(potential_e)); + } + auto itr = + thrust::lower_bound(thrust::seq, invalid_first, invalid_last, transposed_invalid_edge); + if (itr != invalid_last) { assert(*itr == transposed_invalid_edge); } + auto dist = thrust::distance(invalid_first, itr) + num_valid_edges; + + cuda::atomic_ref atomic_counter(num_triangles[dist]); + auto r = atomic_counter.fetch_sub(edge_t{1}, cuda::std::memory_order_relaxed); + }); thrust::for_each( handle.get_thrust_policy(), From 3f6928d8dc3ef4a69fcc3c7081cdf8ae08e23361 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 18:23:40 -0700 Subject: [PATCH 141/155] reduce the number of memory accesses and kernel launch --- cpp/src/community/k_truss_impl.cuh | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 8ff2093dc99..0912bd75297 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -85,34 +85,29 @@ struct unroll_edge { // implementations. template rmm::device_uvector compute_prefix_sum( - // The edgelist is segmented into 2 partitions (valid and invalid edges) - // and edges to be unrolled can be either in the valid or the invalid edge - // partition. raft::handle_t const& handle, raft::device_span sorted_vertices, raft::device_span query_vertices) { rmm::device_uvector prefix_sum(query_vertices.size() + 1, handle.get_stream()); - thrust::tabulate( - handle.get_thrust_policy(), - prefix_sum.begin(), - prefix_sum.begin() + query_vertices.size(), + auto count_first = thrust::make_transform_iterator( + thrust::make_counting_iterator(size_t{0}), + cuda::proclaim_return_type( [query_vertices, - num_edges = sorted_vertices.size(), - sorted_vertices = sorted_vertices.begin()] __device__(auto idx) { + num_edges = sorted_vertices.size(), + sorted_vertices = sorted_vertices.begin()]__device__(size_t idx) { auto itr_lower_valid = thrust::lower_bound( thrust::seq, sorted_vertices, sorted_vertices + num_edges, query_vertices[idx]); auto itr_upper_valid = thrust::upper_bound( thrust::seq, itr_lower_valid, sorted_vertices + num_edges, query_vertices[idx]); - - auto dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); + vertex_t dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); return dist_valid; - }); - thrust::exclusive_scan( - handle.get_thrust_policy(), prefix_sum.begin(), prefix_sum.end(), prefix_sum.begin()); + })); + + thrust::exclusive_scan(handle.get_thrust_policy(), count_first, count_first + query_vertices.size() + 1, prefix_sum.begin()); return prefix_sum; } From 56d5010a6ca9e6928dc9e7587c312bde0d93f9d8 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 18:25:05 -0700 Subject: [PATCH 142/155] rename functor --- cpp/tests/community/k_truss_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index cc7fe7f41d1..0bdc12f0413 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -58,7 +58,7 @@ class Tests_KTruss : public ::testing::TestWithParam - struct device_nearly_equal { + struct host_nearly_equal { const type_t threshold_ratio; const type_t threshold_magnitude; @@ -260,7 +260,7 @@ class Tests_KTruss : public ::testing::TestWithParam{ + auto compare_functor = host_nearly_equal{ weight_t{1e-3}, weight_t{(weight_t{1} / static_cast((*h_cugraph_values).size())) * weight_t{1e-3}}}; From 21b7b842bafd6ee8ce4bbeddb7877bf14b0e8914 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 18:28:51 -0700 Subject: [PATCH 143/155] update include paths --- cpp/tests/community/k_truss_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 0bdc12f0413..8cab7bc4afd 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -16,6 +16,8 @@ #include "utilities/check_utilities.hpp" #include "utilities/conversion_utilities.hpp" +#include "utilities/base_fixture.hpp" +#include "utilities/test_graphs.hpp" #include #include @@ -27,8 +29,6 @@ #include #include -#include -#include #include #include From 59546b7b0bcdcb370454a83088fd0df0d10b5d97 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 18:38:14 -0700 Subject: [PATCH 144/155] update include path and rename variables --- cpp/src/community/edge_triangle_count_sg.cu | 2 +- cpp/src/community/k_truss_impl.cuh | 10 +++++----- cpp/src/community/k_truss_sg.cu | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/src/community/edge_triangle_count_sg.cu b/cpp/src/community/edge_triangle_count_sg.cu index 486bd42705e..c4b7e71b967 100644 --- a/cpp/src/community/edge_triangle_count_sg.cu +++ b/cpp/src/community/edge_triangle_count_sg.cu @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include +#include "community/edge_triangle_count_impl.cuh" namespace cugraph { diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 0912bd75297..318d2ad6129 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -97,14 +97,14 @@ rmm::device_uvector compute_prefix_sum( [query_vertices, num_edges = sorted_vertices.size(), sorted_vertices = sorted_vertices.begin()]__device__(size_t idx) { - auto itr_lower_valid = thrust::lower_bound( + auto itr_lower = thrust::lower_bound( thrust::seq, sorted_vertices, sorted_vertices + num_edges, query_vertices[idx]); - auto itr_upper_valid = thrust::upper_bound( - thrust::seq, itr_lower_valid, sorted_vertices + num_edges, query_vertices[idx]); - vertex_t dist_valid = thrust::distance(itr_lower_valid, itr_upper_valid); + auto itr_upper = thrust::upper_bound( + thrust::seq, itr_lower, sorted_vertices + num_edges, query_vertices[idx]); + vertex_t dist = thrust::distance(itr_lower, itr_upper); - return dist_valid; + return dist; })); thrust::exclusive_scan(handle.get_thrust_policy(), count_first, count_first + query_vertices.size() + 1, prefix_sum.begin()); diff --git a/cpp/src/community/k_truss_sg.cu b/cpp/src/community/k_truss_sg.cu index 66306423481..dfea62182f5 100644 --- a/cpp/src/community/k_truss_sg.cu +++ b/cpp/src/community/k_truss_sg.cu @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include "community/k_truss_impl.cuh" namespace cugraph { From b3ba4075bc44a776066f1e975edf4a7bb1ca0e00 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 19:11:44 -0700 Subject: [PATCH 145/155] remove device functions from reference implementation --- cpp/tests/community/k_truss_test.cpp | 33 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 8cab7bc4afd..a0e6b085ccd 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -72,17 +72,11 @@ class Tests_KTruss : public ::testing::TestWithParam std::tuple, std::vector, std::optional>> k_truss_reference( - raft::handle_t const& handle, - cugraph::graph_view_t const& graph_view, - std::optional> edge_weight_view, + std::vector h_offsets, + std::vector h_indices, + std::optional> h_values, edge_t k) { - auto [h_offsets, h_indices, h_values] = cugraph::test::graph_to_host_csr( - handle, - graph_view, - edge_weight_view, - std::optional>(std::nullopt)); - std::vector vertices(h_offsets.size() - 1); std::iota(vertices.begin(), vertices.end(), 0); @@ -132,7 +126,7 @@ class Tests_KTruss : public ::testing::TestWithParam @@ -244,11 +238,18 @@ class Tests_KTruss : public ::testing::TestWithParam>(std::nullopt)); + auto [h_reference_offsets, h_reference_indices, h_reference_values] = k_truss_reference( - handle, - graph_view, - edge_weight ? std::make_optional((*edge_weight).view()) : std::nullopt, + h_offsets, + h_indices, + h_values, k_truss_usecase.k_); EXPECT_EQ(h_cugraph_offsets.size(), h_reference_offsets.size()); @@ -332,7 +333,7 @@ INSTANTIATE_TEST_SUITE_P( // disable correctness checks for large graphs // FIXME: High memory footprint. Perform nbr_intersection in chunks. ::testing::Combine( - ::testing::Values(KTruss_Usecase{12, false, false}), + ::testing::Values(KTruss_Usecase{4, false, false}), ::testing::Values(cugraph::test::Rmat_Usecase(14, 16, 0.57, 0.19, 0.19, 0, true, false)))); CUGRAPH_TEST_PROGRAM_MAIN() From 5dba8b3762640d7b49b73d64d640ea9baac8e6e5 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 19:14:24 -0700 Subject: [PATCH 146/155] drop cuHornet and support all archs --- cpp/CMakeLists.txt | 28 ---- cpp/cmake/thirdparty/get_cuhornet.cmake | 45 ------ cpp/src/community/k_truss_impl.cuh | 12 +- cpp/src/community/legacy/ktruss.cu | 185 ------------------------ 4 files changed, 5 insertions(+), 265 deletions(-) delete mode 100644 cpp/cmake/thirdparty/get_cuhornet.cmake delete mode 100644 cpp/src/community/legacy/ktruss.cu diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index bcd2582bedf..0240e2b892e 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -37,30 +37,6 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND message(FATAL_ERROR "GCC compiler must be at least 9.3") endif() -# Remove the following archs from CMAKE_CUDA_ARCHITECTURES that -# cuhornet currently doesn't support -# -# >= 90 -set(supported_archs "70" "72" "75" "80" "86" "89" "90") -foreach( arch IN LISTS CMAKE_CUDA_ARCHITECTURES) - string(REPLACE "-real" "" arch ${arch}) - if( arch IN_LIST supported_archs ) - list(APPEND usable_arch_values ${arch}) - endif() -endforeach() -# Make sure everything but the 'newest' arch -# is marked as `-real` so we only generate PTX for -# arch > 86 -list(POP_BACK usable_arch_values latest_arch) -list(TRANSFORM usable_arch_values APPEND "-real") -if (usable_arch_values) - list(APPEND usable_arch_values ${latest_arch}) -else() - list(APPEND usable_arch_values ${latest_arch}-real) -endif() - -set(CMAKE_CUDA_ARCHITECTURES ${usable_arch_values}) - # Write the version header rapids_cmake_write_version_file(include/cugraph/version_config.hpp) rapids_cmake_write_version_file(include/cugraph_c/version_config.hpp) @@ -168,7 +144,6 @@ if(USE_CUGRAPH_OPS) endif() include(cmake/thirdparty/get_nccl.cmake) -include(cmake/thirdparty/get_cuhornet.cmake) if (BUILD_CUGRAPH_MTMG_TESTS) include(cmake/thirdparty/get_ucp.cmake) @@ -223,7 +198,6 @@ set(CUGRAPH_SOURCES src/community/ecg_sg.cu src/community/ecg_mg.cu src/community/legacy/louvain.cu - src/community/legacy/ktruss.cu src/community/legacy/ecg.cu src/community/egonet_sg.cu src/community/egonet_mg.cu @@ -393,7 +367,6 @@ if (USE_CUGRAPH_OPS) $<$:raft::raft> $<$:${COMPILED_RAFT_LIB}> cuco::cuco - cugraph::cuHornet NCCL::NCCL ) else() @@ -406,7 +379,6 @@ else() $<$:raft::raft> $<$:${COMPILED_RAFT_LIB}> cuco::cuco - cugraph::cuHornet NCCL::NCCL ) endif() diff --git a/cpp/cmake/thirdparty/get_cuhornet.cmake b/cpp/cmake/thirdparty/get_cuhornet.cmake deleted file mode 100644 index d6dc817d78f..00000000000 --- a/cpp/cmake/thirdparty/get_cuhornet.cmake +++ /dev/null @@ -1,45 +0,0 @@ -#============================================================================= -# Copyright (c) 2021-2023, 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. -#============================================================================= - -function(find_and_configure_cuhornet) - - # We are not using the cuhornet CMake targets, so no need to call `add_subdirectory()`, - # or to use CPM - FetchContent_Declare( - cuhornet - GIT_REPOSITORY https://github.com/rapidsai/cuhornet.git - GIT_TAG 17467c88abe2b76df456614575c02f7e9cbfd02d - SOURCE_SUBDIR hornet - ) - FetchContent_GetProperties(cuhornet) - - if(NOT cuhornet_POPULATED) - FetchContent_Populate(cuhornet) - endif() - - if(NOT TARGET cugraph::cuHornet) - add_library(cugraph::cuHornet IMPORTED INTERFACE GLOBAL) - target_include_directories(cugraph::cuHornet INTERFACE - "${cuhornet_SOURCE_DIR}/hornet/include" - "${cuhornet_SOURCE_DIR}/hornetsnest/include" - "${cuhornet_SOURCE_DIR}/xlib/include" - "${cuhornet_SOURCE_DIR}/primitives" - ) - endif() -endfunction() - - -find_and_configure_cuhornet() diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 318d2ad6129..4d62482185b 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -522,8 +522,8 @@ k_truss(raft::handle_t const& handle, modified_graph_view = (*modified_graph).view(); } - // FIXME: Remove because it yields to incorrect results - // 3. Find (k+1)-core and exclude edges that do not belong to (k+1)-core + // FIXME: Investigate k-1 core failure to yield correct results. + // 3. Find (k-1)-core and exclude edges that do not belong to (k-1)-core /* { auto cur_graph_view = modified_graph_view ? *modified_graph_view : graph_view; @@ -542,7 +542,7 @@ k_truss(raft::handle_t const& handle, k_core(handle, cur_graph_view, std::optional>{std::nullopt}, - size_t{k + 1}, + size_t{k - 1}, std::make_optional(k_core_degree_type_t::OUT), // Seems like the below argument is required. passing a std::nullopt // create a compiler error @@ -712,9 +712,7 @@ k_truss(raft::handle_t const& handle, auto num_triangles = thrust::get<1>(e); return num_triangles >= k - 2; }); - size_t num_edges_with_triangles{0}; - edge_t num_invalid_edges{0}; - num_invalid_edges = static_cast( + auto num_invalid_edges = static_cast( thrust::distance(invalid_transposed_edge_triangle_count_first, transposed_edge_triangle_count_pair_first + edgelist_srcs.size())); @@ -845,7 +843,7 @@ k_truss(raft::handle_t const& handle, return num_triangles > 0; }); - num_edges_with_triangles = static_cast( + auto num_edges_with_triangles = static_cast( thrust::distance(transposed_edge_triangle_count_pair_first, edges_with_triangle_last)); thrust::sort(handle.get_thrust_policy(), diff --git a/cpp/src/community/legacy/ktruss.cu b/cpp/src/community/legacy/ktruss.cu deleted file mode 100644 index 38b68eb1947..00000000000 --- a/cpp/src/community/legacy/ktruss.cu +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2019-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. - */ - -/** - * ---------------------------------------------------------------------------* - * @brief KTruss implementation - * - * @file ktruss.cu - * --------------------------------------------------------------------------*/ - -#include -#include - -#include -#include -#include - -using namespace hornets_nest; - -namespace cugraph { - -namespace detail { - -template -std::tuple, rmm::device_uvector> ktruss_subgraph_impl( - raft::handle_t const& handle, - raft::device_span src, - raft::device_span dst, - size_t number_of_vertices, - int k) -{ - using HornetGraph = hornet::gpu::Hornet; - using UpdatePtr = hornet::BatchUpdatePtr; - using Update = hornet::gpu::BatchUpdate; - - HornetGraph hnt(number_of_vertices + 1); - - // NOTE: Should a constant pointer be passed for @src and @dst - UpdatePtr ptr(static_cast(src.size()), src.data(), dst.data()); - Update batch(ptr); - - hnt.insert(batch); - CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to initialize graph"); - - KTruss kt(hnt); - - kt.init(); - kt.reset(); - kt.createOffSetArray(); - // NOTE : These parameters will become obsolete once we move to the updated - // algorithm (https://ieeexplore.ieee.org/document/8547581) - kt.setInitParameters(4, // Number of threads per block per list intersection - 8, // Number of intersections per block - 2, // log2(Number of threads) - 64000, // Total number of blocks launched - 32); // Thread block dimension - kt.reset(); - kt.sortHornet(); - - kt.runForK(k); - CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to run"); - - rmm::device_uvector result_src(kt.getGraphEdgeCount(), handle.get_stream()); - rmm::device_uvector result_dst(kt.getGraphEdgeCount(), handle.get_stream()); - - kt.copyGraph(result_src.data(), result_dst.data()); - - kt.release(); - CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to release"); - - return std::make_tuple(std::move(result_src), std::move(result_dst)); -} - -template -std::tuple, - rmm::device_uvector, - std::optional>> -weighted_ktruss_subgraph_impl(raft::handle_t const& handle, - raft::device_span src, - raft::device_span dst, - std::optional> wgt, - size_t number_of_vertices, - int k) -{ - using HornetGraph = hornet::gpu::Hornet>; - using UpdatePtr = - hornet::BatchUpdatePtr, hornet::DeviceType::DEVICE>; - using Update = hornet::gpu::BatchUpdate>; - - HornetGraph hnt(number_of_vertices + 1); - - UpdatePtr ptr(static_cast(src.size()), src.data(), dst.data(), wgt->data()); - Update batch(ptr); - - hnt.insert(batch); - CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to initialize graph"); - - KTrussWeighted kt(hnt); - - kt.init(); - kt.reset(); - kt.createOffSetArray(); - // NOTE : These parameters will become obsolete once we move to the updated - // algorithm (https://ieeexplore.ieee.org/document/8547581) - kt.setInitParameters(4, // Number of threads per block per list intersection - 8, // Number of intersections per block - 2, // log2(Number of threads) - 64000, // Total number of blocks launched - 32); // Thread block dimension - kt.reset(); - kt.sortHornet(); - - kt.runForK(k); - CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to run"); - - rmm::device_uvector result_src(kt.getGraphEdgeCount(), handle.get_stream()); - rmm::device_uvector result_dst(kt.getGraphEdgeCount(), handle.get_stream()); - std::optional> result_wgt{std::nullopt}; - - result_wgt = rmm::device_uvector(kt.getGraphEdgeCount(), handle.get_stream()); - kt.copyGraph(result_src.data(), result_dst.data(), result_wgt->data()); - - kt.release(); - CUGRAPH_EXPECTS(cudaPeekAtLastError() == cudaSuccess, "KTruss : Failed to release"); - - return std::make_tuple(std::move(result_src), std::move(result_dst), std::move(result_wgt)); -} - -} // namespace detail - -template -std::tuple, - rmm::device_uvector, - std::optional>> -k_truss_subgraph(raft::handle_t const& handle, - raft::device_span src, - raft::device_span dst, - std::optional> wgt, - size_t number_of_vertices, - int k) -{ - if (wgt.has_value()) { - return detail::weighted_ktruss_subgraph_impl(handle, src, dst, wgt, number_of_vertices, k); - } else { - auto [result_src, result_dst] = - detail::ktruss_subgraph_impl(handle, src, dst, number_of_vertices, k); - std::optional> result_wgt{std::nullopt}; - return std::make_tuple(std::move(result_src), std::move(result_dst), std::move(result_wgt)); - } -} - -template std::tuple, - rmm::device_uvector, - std::optional>> -k_truss_subgraph(raft::handle_t const& handle, - raft::device_span src, - raft::device_span dst, - std::optional> wgt, - size_t number_of_vertices, - int k); - -template std::tuple, - rmm::device_uvector, - std::optional>> -k_truss_subgraph(raft::handle_t const& handle, - raft::device_span src, - raft::device_span dst, - std::optional> wgt, - size_t number_of_vertices, - int k); - -} // namespace cugraph From 8dae62447defffb64923c75f094f037caf7f6236 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 19:18:26 -0700 Subject: [PATCH 147/155] fix style --- cpp/src/community/k_truss_impl.cuh | 38 +++++++++++++++------------- cpp/tests/community/k_truss_test.cpp | 21 ++++++--------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 4d62482185b..229425df23e 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -84,30 +84,32 @@ struct unroll_edge { // FIXME: May re-locate this function as a general utility function for graph algorithm // implementations. template -rmm::device_uvector compute_prefix_sum( - raft::handle_t const& handle, - raft::device_span sorted_vertices, - raft::device_span query_vertices) +rmm::device_uvector compute_prefix_sum(raft::handle_t const& handle, + raft::device_span sorted_vertices, + raft::device_span query_vertices) { rmm::device_uvector prefix_sum(query_vertices.size() + 1, handle.get_stream()); auto count_first = thrust::make_transform_iterator( thrust::make_counting_iterator(size_t{0}), cuda::proclaim_return_type( - [query_vertices, - num_edges = sorted_vertices.size(), - sorted_vertices = sorted_vertices.begin()]__device__(size_t idx) { - auto itr_lower = thrust::lower_bound( - thrust::seq, sorted_vertices, sorted_vertices + num_edges, query_vertices[idx]); - - auto itr_upper = thrust::upper_bound( - thrust::seq, itr_lower, sorted_vertices + num_edges, query_vertices[idx]); - vertex_t dist = thrust::distance(itr_lower, itr_upper); - - return dist; - })); - - thrust::exclusive_scan(handle.get_thrust_policy(), count_first, count_first + query_vertices.size() + 1, prefix_sum.begin()); + [query_vertices, + num_edges = sorted_vertices.size(), + sorted_vertices = sorted_vertices.begin()] __device__(size_t idx) { + auto itr_lower = thrust::lower_bound( + thrust::seq, sorted_vertices, sorted_vertices + num_edges, query_vertices[idx]); + + auto itr_upper = thrust::upper_bound( + thrust::seq, itr_lower, sorted_vertices + num_edges, query_vertices[idx]); + vertex_t dist = thrust::distance(itr_lower, itr_upper); + + return dist; + })); + + thrust::exclusive_scan(handle.get_thrust_policy(), + count_first, + count_first + query_vertices.size() + 1, + prefix_sum.begin()); return prefix_sum; } diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index a0e6b085ccd..0664953749a 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ +#include "utilities/base_fixture.hpp" #include "utilities/check_utilities.hpp" #include "utilities/conversion_utilities.hpp" -#include "utilities/base_fixture.hpp" #include "utilities/test_graphs.hpp" #include @@ -71,11 +71,10 @@ class Tests_KTruss : public ::testing::TestWithParam std::tuple, std::vector, std::optional>> - k_truss_reference( - std::vector h_offsets, - std::vector h_indices, - std::optional> h_values, - edge_t k) + k_truss_reference(std::vector h_offsets, + std::vector h_indices, + std::optional> h_values, + edge_t k) { std::vector vertices(h_offsets.size() - 1); std::iota(vertices.begin(), vertices.end(), 0); @@ -157,8 +156,7 @@ class Tests_KTruss : public ::testing::TestWithParam @@ -247,10 +245,7 @@ class Tests_KTruss : public ::testing::TestWithParam( - h_offsets, - h_indices, - h_values, - k_truss_usecase.k_); + h_offsets, h_indices, h_values, k_truss_usecase.k_); EXPECT_EQ(h_cugraph_offsets.size(), h_reference_offsets.size()); @@ -333,7 +328,7 @@ INSTANTIATE_TEST_SUITE_P( // disable correctness checks for large graphs // FIXME: High memory footprint. Perform nbr_intersection in chunks. ::testing::Combine( - ::testing::Values(KTruss_Usecase{4, false, false}), + ::testing::Values(KTruss_Usecase{12, false, false}), ::testing::Values(cugraph::test::Rmat_Usecase(14, 16, 0.57, 0.19, 0.19, 0, true, false)))); CUGRAPH_TEST_PROGRAM_MAIN() From b5f0d3ad909e378ba6ca6c0b8f6891669c3444f4 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Mon, 25 Mar 2024 19:41:02 -0700 Subject: [PATCH 148/155] fix warning --- cpp/src/community/k_truss_impl.cuh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cpp/src/community/k_truss_impl.cuh b/cpp/src/community/k_truss_impl.cuh index 229425df23e..3db9fd70de2 100644 --- a/cpp/src/community/k_truss_impl.cuh +++ b/cpp/src/community/k_truss_impl.cuh @@ -49,7 +49,7 @@ rmm::device_uvector edge_triangle_count( template struct unroll_edge { - edge_t num_valid_edges{}; + size_t num_valid_edges{}; raft::device_span num_triangles{}; EdgeIterator edge_to_unroll_first{}; EdgeIterator transposed_valid_edge_first{}; @@ -116,7 +116,7 @@ rmm::device_uvector compute_prefix_sum(raft::handle_t const& handle, template edge_t remove_overcompensating_edges(raft::handle_t const& handle, - edge_t buffer_size, + size_t buffer_size, EdgeIterator potential_closing_or_incoming_edges, EdgeIterator incoming_or_potential_closing_edges, raft::device_span invalid_edgelist_srcs, @@ -153,8 +153,8 @@ edge_t remove_overcompensating_edges(raft::handle_t const& handle, template void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, graph_view_t& graph_view, - edge_t num_invalid_edges, - edge_t num_valid_edges, + size_t num_invalid_edges, + size_t num_valid_edges, raft::device_span edgelist_srcs, raft::device_span edgelist_dsts, raft::device_span num_triangles) @@ -292,12 +292,12 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, resize_dataframe_buffer(potential_closing_edges, num_edge_exists, handle.get_stream()); resize_dataframe_buffer(incoming_edges_to_r, num_edge_exists, handle.get_stream()); - edge_t num_edges_not_overcomp = + auto num_edges_not_overcomp = remove_overcompensating_edges( handle, - edge_t{num_edge_exists}, + num_edge_exists, get_dataframe_buffer_begin(potential_closing_edges), get_dataframe_buffer_begin(incoming_edges_to_r), raft::device_span(edgelist_srcs.data() + num_valid_edges, num_invalid_edges), @@ -317,7 +317,7 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, edge_t, decltype(get_dataframe_buffer_begin(potential_closing_edges))>( handle, - edge_t{num_edges_not_overcomp}, + num_edges_not_overcomp, get_dataframe_buffer_begin(incoming_edges_to_r), get_dataframe_buffer_begin(potential_closing_edges), raft::device_span(edgelist_srcs.data() + num_valid_edges, @@ -368,7 +368,7 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, unroll_edge{ - edge_t{num_valid_edges}, + num_valid_edges, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(potential_closing_edges), thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), @@ -383,7 +383,7 @@ void unroll_p_r_or_q_r_edges(raft::handle_t const& handle, unroll_edge{ - edge_t{num_valid_edges}, + num_valid_edges, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(incoming_edges_to_r), thrust::make_zip_iterator(edgelist_dsts.begin(), edgelist_srcs.begin()), @@ -794,7 +794,7 @@ k_truss(raft::handle_t const& handle, thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), unroll_edge{ - edge_t{num_valid_edges}, + num_valid_edges, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_p_r_edge_p_q), transposed_edge_first, @@ -805,7 +805,7 @@ k_truss(raft::handle_t const& handle, thrust::make_counting_iterator(0), thrust::make_counting_iterator(intersection_indices.size()), unroll_edge{ - edge_t{num_valid_edges}, + num_valid_edges, raft::device_span(num_triangles.data(), num_triangles.size()), get_dataframe_buffer_begin(vertex_pair_buffer_q_r_edge_p_q), transposed_edge_first, From c9ada765db97423b466f9d00196d562f8ae3ea3a Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 26 Mar 2024 04:52:53 -0700 Subject: [PATCH 149/155] extend sorting utility function --- cpp/tests/utilities/thrust_wrapper.cu | 100 +++++++++++++++++++++++++ cpp/tests/utilities/thrust_wrapper.hpp | 12 +++ 2 files changed, 112 insertions(+) diff --git a/cpp/tests/utilities/thrust_wrapper.cu b/cpp/tests/utilities/thrust_wrapper.cu index 7d485dd5ab3..91d1b1fc7cd 100644 --- a/cpp/tests/utilities/thrust_wrapper.cu +++ b/cpp/tests/utilities/thrust_wrapper.cu @@ -57,12 +57,51 @@ value_buffer_type sort(raft::handle_t const& handle, value_buffer_type const& va return sorted_values; } +template +std::tuple sort(raft::handle_t const& handle, + value_buffer_type const& first, + value_buffer_type const& second) +{ + auto sorted_first = + cugraph::allocate_dataframe_buffer>( + first.size(), handle.get_stream()); + auto sorted_second = + cugraph::allocate_dataframe_buffer>( + first.size(), handle.get_stream()); + + auto execution_policy = handle.get_thrust_policy(); + thrust::copy(execution_policy, + cugraph::get_dataframe_buffer_begin(first), + cugraph::get_dataframe_buffer_end(first), + cugraph::get_dataframe_buffer_begin(sorted_first)); + thrust::copy(execution_policy, + cugraph::get_dataframe_buffer_begin(second), + cugraph::get_dataframe_buffer_end(second), + cugraph::get_dataframe_buffer_begin(sorted_second)); + thrust::sort(execution_policy, + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first), cugraph::get_dataframe_buffer_begin(sorted_second)), + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first) + first.size(), cugraph::get_dataframe_buffer_begin(sorted_second) + first.size())); + + + return std::make_tuple(std::move(sorted_first), std::move(sorted_second)); +} + template rmm::device_uvector sort(raft::handle_t const& handle, rmm::device_uvector const& values); template rmm::device_uvector sort(raft::handle_t const& handle, rmm::device_uvector const& values); +template std::tuple, rmm::device_uvector> sort( + raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second); + +template std::tuple, rmm::device_uvector> sort( + raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second); + template std::tuple sort_by_key(raft::handle_t const& handle, key_buffer_type const& keys, @@ -143,6 +182,67 @@ template std::tuple, rmm::device_uvector> rmm::device_uvector const& keys, rmm::device_uvector const& values); +template +std::tuple sort_by_key(raft::handle_t const& handle, + key_buffer_type const& first, + key_buffer_type const& second, + value_buffer_type const& values) +{ + auto sorted_first = + cugraph::allocate_dataframe_buffer>( + first.size(), handle.get_stream()); + auto sorted_second = + cugraph::allocate_dataframe_buffer>( + first.size(), handle.get_stream()); + auto sorted_values = + cugraph::allocate_dataframe_buffer>( + first.size(), handle.get_stream()); + + auto execution_policy = handle.get_thrust_policy(); + thrust::copy(execution_policy, + cugraph::get_dataframe_buffer_begin(first), + cugraph::get_dataframe_buffer_end(first), + cugraph::get_dataframe_buffer_begin(sorted_first)); + thrust::copy(execution_policy, + cugraph::get_dataframe_buffer_begin(second), + cugraph::get_dataframe_buffer_end(second), + cugraph::get_dataframe_buffer_begin(sorted_second)); + thrust::copy(execution_policy, + cugraph::get_dataframe_buffer_begin(values), + cugraph::get_dataframe_buffer_end(values), + cugraph::get_dataframe_buffer_begin(sorted_values)); + thrust::sort_by_key(execution_policy, + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first), cugraph::get_dataframe_buffer_begin(sorted_second)), + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first) + first.size(), cugraph::get_dataframe_buffer_begin(sorted_second) + first.size()), + cugraph::get_dataframe_buffer_begin(sorted_values)); + + return std::make_tuple(std::move(sorted_first), std::move(sorted_second), std::move(sorted_values)); +} + +template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( + raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); + +template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( + raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); + +template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( + raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); + +template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( + raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); + template std::tuple, std::tuple, rmm::device_uvector>> sort_by_key(raft::handle_t const& handle, diff --git a/cpp/tests/utilities/thrust_wrapper.hpp b/cpp/tests/utilities/thrust_wrapper.hpp index ae2f5f2fdf7..30a6c7f2fea 100644 --- a/cpp/tests/utilities/thrust_wrapper.hpp +++ b/cpp/tests/utilities/thrust_wrapper.hpp @@ -29,11 +29,23 @@ namespace test { template value_buffer_type sort(raft::handle_t const& handle, value_buffer_type const& values); +template +std::tuple sort(raft::handle_t const& handle, + value_buffer_type const& first, + value_buffer_type const& second); + template std::tuple sort_by_key(raft::handle_t const& handle, key_buffer_type const& keys, value_buffer_type const& values); +template +std::tuple sort_by_key( + raft::handle_t const& handle, + key_buffer_type const& first, + key_buffer_type const& second, + value_buffer_type const& values); + template vertex_t max_element(raft::handle_t const& handle, raft::device_span vertices); From ce8e594be70854105c4b7c38af738cfa43d5a424 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 26 Mar 2024 04:55:36 -0700 Subject: [PATCH 150/155] perform 'coo' comparison instead of 'csr' --- cpp/tests/community/k_truss_test.cpp | 87 +++++++++++++--------------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 0664953749a..94137f59080 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -18,6 +18,7 @@ #include "utilities/check_utilities.hpp" #include "utilities/conversion_utilities.hpp" #include "utilities/test_graphs.hpp" +#include "utilities/thrust_wrapper.hpp" #include #include @@ -152,11 +153,14 @@ class Tests_KTruss : public ::testing::TestWithParam h_srcs(h_indices.size()); + + for (auto i = 0; i < h_offsets.size() - 1; ++i){ + std::fill(h_srcs.begin() + h_offsets[i], h_srcs.begin() + h_offsets[i + 1], i); + } - h_offsets.erase(std::unique(h_offsets.begin() + 1, h_offsets.end()), - h_offsets.end()); // CSR start from 0 - - return std::make_tuple(std::move(h_offsets), std::move(h_indices), std::move(h_values)); + return std::make_tuple(std::move(h_srcs), std::move(h_indices), std::move(h_values)); } template @@ -192,7 +196,7 @@ class Tests_KTruss : public ::testing::TestWithParam( handle, graph_view, @@ -208,63 +212,54 @@ class Tests_KTruss : public ::testing::TestWithParam> modified_graph{std::nullopt}; - - std::optional< - cugraph::edge_property_t, weight_t>> - modified_edge_weight{std::nullopt}; - std::tie(*modified_graph, modified_edge_weight, std::ignore, std::ignore, std::ignore) = - cugraph:: - create_graph_from_edgelist( - handle, - std::nullopt, - std::move(d_cugraph_src), - std::move(d_cugraph_dst), - std::move(d_cugraph_wgt), - std::nullopt, - std::nullopt, - cugraph::graph_properties_t{true, false}, - renumber); - - // Convert cugraph results to CSR - auto [h_cugraph_offsets, h_cugraph_indices, h_cugraph_values] = - cugraph::test::graph_to_host_csr( - handle, - (*modified_graph).view(), - modified_edge_weight ? std::make_optional((*modified_edge_weight).view()) : std::nullopt, - std::optional>(std::nullopt)); - - // Remove isolated vertices. - h_cugraph_offsets.erase(std::unique(h_cugraph_offsets.begin() + 1, h_cugraph_offsets.end()), - h_cugraph_offsets.end()); // CSR start from 0 - auto [h_offsets, h_indices, h_values] = cugraph::test::graph_to_host_csr( handle, graph_view, edge_weight ? std::make_optional((*edge_weight).view()) : std::nullopt, std::optional>(std::nullopt)); - auto [h_reference_offsets, h_reference_indices, h_reference_values] = + rmm::device_uvector d_sorted_cugraph_wgts{0, handle.get_stream()}; + rmm::device_uvector d_sorted_cugraph_srcs{0, handle.get_stream()}; + rmm::device_uvector d_sorted_cugraph_dsts{0, handle.get_stream()}; + + if (edge_weight){ + std::tie(d_sorted_cugraph_srcs, d_sorted_cugraph_dsts, d_sorted_cugraph_wgts) = + cugraph::test::sort_by_key(handle, d_cugraph_srcs, d_cugraph_dsts, *d_cugraph_wgts); + } else { + std::tie(d_sorted_cugraph_srcs, d_sorted_cugraph_dsts) = + cugraph::test::sort(handle, d_cugraph_srcs, d_cugraph_dsts); + } + + auto h_cugraph_srcs = + cugraph::test::to_host(handle, d_sorted_cugraph_srcs); + + auto h_cugraph_dsts = + cugraph::test::to_host(handle, d_sorted_cugraph_dsts); + + auto [h_reference_srcs, h_reference_dsts, h_reference_wgts] = k_truss_reference( h_offsets, h_indices, h_values, k_truss_usecase.k_); - EXPECT_EQ(h_cugraph_offsets.size(), h_reference_offsets.size()); - + EXPECT_EQ(h_cugraph_srcs.size(), h_reference_srcs.size()); ASSERT_TRUE(std::equal( - h_cugraph_offsets.begin(), h_cugraph_offsets.end(), h_reference_offsets.begin())); + h_cugraph_srcs.begin(), h_cugraph_srcs.end(), h_reference_srcs.begin())); ASSERT_TRUE(std::equal( - h_cugraph_indices.begin(), h_cugraph_indices.end(), h_reference_indices.begin())); + h_cugraph_dsts.begin(), h_cugraph_dsts.end(), h_reference_dsts.begin())); if (edge_weight) { + auto h_cugraph_wgts = + cugraph::test::to_host(handle, d_sorted_cugraph_wgts); auto compare_functor = host_nearly_equal{ weight_t{1e-3}, - weight_t{(weight_t{1} / static_cast((*h_cugraph_values).size())) * - weight_t{1e-3}}}; - EXPECT_TRUE(std::equal((*h_cugraph_values).begin(), - (*h_cugraph_values).end(), - (*h_reference_values).begin(), - compare_functor)); + weight_t{(weight_t{1} / static_cast((h_cugraph_wgts).size())) * + weight_t{1e-3}}}; + EXPECT_TRUE(std::equal((h_cugraph_wgts).begin(), + (h_cugraph_wgts).end(), + (*h_reference_wgts).begin(), + compare_functor)); } + } } }; @@ -305,7 +300,7 @@ INSTANTIATE_TEST_SUITE_P( KTruss_Usecase{4, true, false}, KTruss_Usecase{9, true, true}, KTruss_Usecase{7, true, true}), - ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/netscience.mtx"), cugraph::test::File_Usecase("test/datasets/dolphins.mtx")))); INSTANTIATE_TEST_SUITE_P(rmat_small_test, From 9b51e5b68408c3dc5eaca2252e5d00ec873ffe65 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 26 Mar 2024 04:58:58 -0700 Subject: [PATCH 151/155] fix style --- cpp/tests/community/k_truss_test.cpp | 40 ++++++------ cpp/tests/utilities/thrust_wrapper.cu | 85 +++++++++++++++----------- cpp/tests/utilities/thrust_wrapper.hpp | 8 +-- 3 files changed, 70 insertions(+), 63 deletions(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 94137f59080..4faadaeefd2 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -153,10 +153,10 @@ class Tests_KTruss : public ::testing::TestWithParam h_srcs(h_indices.size()); - - for (auto i = 0; i < h_offsets.size() - 1; ++i){ + + for (auto i = 0; i < h_offsets.size() - 1; ++i) { std::fill(h_srcs.begin() + h_offsets[i], h_srcs.begin() + h_offsets[i + 1], i); } @@ -222,44 +222,40 @@ class Tests_KTruss : public ::testing::TestWithParam d_sorted_cugraph_srcs{0, handle.get_stream()}; rmm::device_uvector d_sorted_cugraph_dsts{0, handle.get_stream()}; - if (edge_weight){ + if (edge_weight) { std::tie(d_sorted_cugraph_srcs, d_sorted_cugraph_dsts, d_sorted_cugraph_wgts) = cugraph::test::sort_by_key(handle, d_cugraph_srcs, d_cugraph_dsts, *d_cugraph_wgts); } else { std::tie(d_sorted_cugraph_srcs, d_sorted_cugraph_dsts) = cugraph::test::sort(handle, d_cugraph_srcs, d_cugraph_dsts); } - - auto h_cugraph_srcs = - cugraph::test::to_host(handle, d_sorted_cugraph_srcs); - - auto h_cugraph_dsts = - cugraph::test::to_host(handle, d_sorted_cugraph_dsts); - + + auto h_cugraph_srcs = cugraph::test::to_host(handle, d_sorted_cugraph_srcs); + + auto h_cugraph_dsts = cugraph::test::to_host(handle, d_sorted_cugraph_dsts); + auto [h_reference_srcs, h_reference_dsts, h_reference_wgts] = k_truss_reference( h_offsets, h_indices, h_values, k_truss_usecase.k_); EXPECT_EQ(h_cugraph_srcs.size(), h_reference_srcs.size()); - ASSERT_TRUE(std::equal( - h_cugraph_srcs.begin(), h_cugraph_srcs.end(), h_reference_srcs.begin())); + ASSERT_TRUE( + std::equal(h_cugraph_srcs.begin(), h_cugraph_srcs.end(), h_reference_srcs.begin())); - ASSERT_TRUE(std::equal( - h_cugraph_dsts.begin(), h_cugraph_dsts.end(), h_reference_dsts.begin())); + ASSERT_TRUE( + std::equal(h_cugraph_dsts.begin(), h_cugraph_dsts.end(), h_reference_dsts.begin())); if (edge_weight) { - auto h_cugraph_wgts = - cugraph::test::to_host(handle, d_sorted_cugraph_wgts); + auto h_cugraph_wgts = cugraph::test::to_host(handle, d_sorted_cugraph_wgts); auto compare_functor = host_nearly_equal{ weight_t{1e-3}, weight_t{(weight_t{1} / static_cast((h_cugraph_wgts).size())) * - weight_t{1e-3}}}; + weight_t{1e-3}}}; EXPECT_TRUE(std::equal((h_cugraph_wgts).begin(), - (h_cugraph_wgts).end(), - (*h_reference_wgts).begin(), - compare_functor)); + (h_cugraph_wgts).end(), + (*h_reference_wgts).begin(), + compare_functor)); } - } } }; diff --git a/cpp/tests/utilities/thrust_wrapper.cu b/cpp/tests/utilities/thrust_wrapper.cu index 91d1b1fc7cd..93bb8a04e87 100644 --- a/cpp/tests/utilities/thrust_wrapper.cu +++ b/cpp/tests/utilities/thrust_wrapper.cu @@ -78,10 +78,12 @@ std::tuple sort(raft::handle_t const& hand cugraph::get_dataframe_buffer_begin(second), cugraph::get_dataframe_buffer_end(second), cugraph::get_dataframe_buffer_begin(sorted_second)); - thrust::sort(execution_policy, - thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first), cugraph::get_dataframe_buffer_begin(sorted_second)), - thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first) + first.size(), cugraph::get_dataframe_buffer_begin(sorted_second) + first.size())); - + thrust::sort( + execution_policy, + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first), + cugraph::get_dataframe_buffer_begin(sorted_second)), + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first) + first.size(), + cugraph::get_dataframe_buffer_begin(sorted_second) + first.size())); return std::make_tuple(std::move(sorted_first), std::move(sorted_second)); } @@ -183,10 +185,11 @@ template std::tuple, rmm::device_uvector> rmm::device_uvector const& values); template -std::tuple sort_by_key(raft::handle_t const& handle, - key_buffer_type const& first, - key_buffer_type const& second, - value_buffer_type const& values) +std::tuple sort_by_key( + raft::handle_t const& handle, + key_buffer_type const& first, + key_buffer_type const& second, + value_buffer_type const& values) { auto sorted_first = cugraph::allocate_dataframe_buffer>( @@ -211,37 +214,45 @@ std::tuple sort_by_key(raft cugraph::get_dataframe_buffer_begin(values), cugraph::get_dataframe_buffer_end(values), cugraph::get_dataframe_buffer_begin(sorted_values)); - thrust::sort_by_key(execution_policy, - thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first), cugraph::get_dataframe_buffer_begin(sorted_second)), - thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first) + first.size(), cugraph::get_dataframe_buffer_begin(sorted_second) + first.size()), - cugraph::get_dataframe_buffer_begin(sorted_values)); - - return std::make_tuple(std::move(sorted_first), std::move(sorted_second), std::move(sorted_values)); + thrust::sort_by_key( + execution_policy, + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first), + cugraph::get_dataframe_buffer_begin(sorted_second)), + thrust::make_zip_iterator(cugraph::get_dataframe_buffer_begin(sorted_first) + first.size(), + cugraph::get_dataframe_buffer_begin(sorted_second) + first.size()), + cugraph::get_dataframe_buffer_begin(sorted_values)); + + return std::make_tuple( + std::move(sorted_first), std::move(sorted_second), std::move(sorted_values)); } -template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( - raft::handle_t const& handle, - rmm::device_uvector const& first, - rmm::device_uvector const& second, - rmm::device_uvector const& values); - -template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( - raft::handle_t const& handle, - rmm::device_uvector const& first, - rmm::device_uvector const& second, - rmm::device_uvector const& values); - -template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( - raft::handle_t const& handle, - rmm::device_uvector const& first, - rmm::device_uvector const& second, - rmm::device_uvector const& values); - -template std::tuple, rmm::device_uvector, rmm::device_uvector> sort_by_key( - raft::handle_t const& handle, - rmm::device_uvector const& first, - rmm::device_uvector const& second, - rmm::device_uvector const& values); +template std:: + tuple, rmm::device_uvector, rmm::device_uvector> + sort_by_key(raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); + +template std:: + tuple, rmm::device_uvector, rmm::device_uvector> + sort_by_key(raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); + +template std:: + tuple, rmm::device_uvector, rmm::device_uvector> + sort_by_key(raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); + +template std:: + tuple, rmm::device_uvector, rmm::device_uvector> + sort_by_key(raft::handle_t const& handle, + rmm::device_uvector const& first, + rmm::device_uvector const& second, + rmm::device_uvector const& values); template std::tuple, std::tuple, rmm::device_uvector>> diff --git a/cpp/tests/utilities/thrust_wrapper.hpp b/cpp/tests/utilities/thrust_wrapper.hpp index 30a6c7f2fea..c4b87126f50 100644 --- a/cpp/tests/utilities/thrust_wrapper.hpp +++ b/cpp/tests/utilities/thrust_wrapper.hpp @@ -41,10 +41,10 @@ std::tuple sort_by_key(raft::handle_t const& template std::tuple sort_by_key( - raft::handle_t const& handle, - key_buffer_type const& first, - key_buffer_type const& second, - value_buffer_type const& values); + raft::handle_t const& handle, + key_buffer_type const& first, + key_buffer_type const& second, + value_buffer_type const& values); template vertex_t max_element(raft::handle_t const& handle, raft::device_span vertices); From 65024eaae8d79495281ba9c46b829c5d4bee9847 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 26 Mar 2024 05:56:36 -0700 Subject: [PATCH 152/155] enable 'int64_t' support for K-Truss --- cpp/src/c_api/k_truss.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/c_api/k_truss.cpp b/cpp/src/c_api/k_truss.cpp index 387ff5728d7..18e256b022a 100644 --- a/cpp/src/c_api/k_truss.cpp +++ b/cpp/src/c_api/k_truss.cpp @@ -58,7 +58,7 @@ struct k_truss_functor : public cugraph::c_api::abstract_functor { bool multi_gpu> void operator()() { - if constexpr (!cugraph::is_candidate_legacy::value) { + if constexpr (!cugraph::is_candidate::value) { unsupported(); } else if constexpr (multi_gpu) { unsupported(); From 1d8c5d9f0d42c8d8d1ba03551c1efc2b35a9c9ee Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 26 Mar 2024 06:01:41 -0700 Subject: [PATCH 153/155] update docstring --- cpp/include/cugraph/utilities/graph_traits.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/include/cugraph/utilities/graph_traits.hpp b/cpp/include/cugraph/utilities/graph_traits.hpp index e2737305aed..6905d0013b1 100644 --- a/cpp/include/cugraph/utilities/graph_traits.hpp +++ b/cpp/include/cugraph/utilities/graph_traits.hpp @@ -67,8 +67,8 @@ struct is_candidate { }; // meta-function that constrains -// all 3 template param candidates where vertex_t and edge_t -// are restricted to int32_t: +// vertex_t and edge_t are restricted to int32_t: +// FIXME: Drop this functor as it was only used by legacy K-Truss // template struct is_candidate_legacy { From aa327f920daf110efa038bf3b84b8ad3a851ebb0 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 26 Mar 2024 06:06:15 -0700 Subject: [PATCH 154/155] update copyright --- cpp/include/cugraph/utilities/graph_traits.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/include/cugraph/utilities/graph_traits.hpp b/cpp/include/cugraph/utilities/graph_traits.hpp index 6905d0013b1..bd46c9d4fc1 100644 --- a/cpp/include/cugraph/utilities/graph_traits.hpp +++ b/cpp/include/cugraph/utilities/graph_traits.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, NVIDIA CORPORATION. + * Copyright (c) 2021-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. From 4549f1069dd818f4dab47544389439556f88f284 Mon Sep 17 00:00:00 2001 From: jnke2016 Date: Tue, 26 Mar 2024 06:22:10 -0700 Subject: [PATCH 155/155] update docstring --- cpp/tests/community/k_truss_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/tests/community/k_truss_test.cpp b/cpp/tests/community/k_truss_test.cpp index 4faadaeefd2..c8010422e42 100644 --- a/cpp/tests/community/k_truss_test.cpp +++ b/cpp/tests/community/k_truss_test.cpp @@ -85,7 +85,7 @@ class Tests_KTruss : public ::testing::TestWithParam 0) { n_dropped = 0; std::set seen; - // Go over all the vertices + // Go over all the vertices. for (auto u = vertices.begin(); u != vertices.end(); ++u) { std::set nbrs_u; // Find all neighbors of u from the offsets and indices array