diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index d8f359b5bcb..ecc2ebf06d3 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,5 +1,5 @@ #============================================================================= -# Copyright (c) 2018-2023, NVIDIA CORPORATION. +# Copyright (c) 2018-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. @@ -187,6 +187,7 @@ endif() set(CUGRAPH_SOURCES src/detail/shuffle_vertices.cu + src/detail/permute_range.cu src/detail/shuffle_vertex_pairs.cu src/detail/collect_local_vertex_values.cu src/detail/groupby_and_count.cu @@ -218,6 +219,8 @@ set(CUGRAPH_SOURCES src/community/louvain_mg.cu src/community/leiden_sg.cu src/community/leiden_mg.cu + 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 diff --git a/cpp/include/cugraph/algorithms.hpp b/cpp/include/cugraph/algorithms.hpp index 8501eedce5c..bb721468106 100644 --- a/cpp/include/cugraph/algorithms.hpp +++ b/cpp/include/cugraph/algorithms.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. @@ -541,30 +541,37 @@ weight_t hungarian(raft::handle_t const& handle, * community hierarchies in large networks, J Stat Mech P10008 (2008), * http://arxiv.org/abs/0803.0476 * - * @throws cugraph::logic_error when an error occurs. - * - * @tparam graph_view_t Type of graph + * @throws cugraph::logic_error when an error occurs. * - * @param[in] handle Library handle (RAFT). If a communicator is set in the handle, - * @param[in] graph input graph object - * @param[out] clustering Pointer to device array where the clustering should be stored - * @param[in] max_level (optional) maximum number of levels to run (default 100) - * @param[in] threshold (optional) threshold for convergence at each level (default - * 1e-7) - * @param[in] resolution (optional) The value of the resolution parameter to use. - * Called gamma in the modularity formula, this changes the size - * of the communities. Higher resolutions lead to more smaller - * communities, lower resolutions lead to fewer larger - * communities. (default 1) + * @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 weight_t Type of edge weights. Needs to be a floating point type. + * @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false) * - * @return a pair containing: - * 1) number of levels of the returned clustering - * 2) modularity of the returned clustering + * @param[in] handle Library handle (RAFT). If a communicator is set in the handle, + * @param[in] rng_state The RngState instance holding pseudo-random number generator state. + * @param[in] graph_view Input graph view object. + * @param[in] edge_weight_view Optional view object holding edge weights for @p graph_view. + * If @pedge_weight_view.has_value() == false, edge weights + * are assumed to be 1.0. + @param[out] clustering Pointer to device array where the clustering should be stored + * @param[in] max_level (optional) maximum number of levels to run (default 100) + * @param[in] threshold (optional) threshold for convergence at each level (default 1e-7) + * @param[in] resolution (optional) The value of the resolution parameter to use. + * Called gamma in the modularity formula, this changes the size + * of the communities. Higher resolutions lead to more smaller + * communities, lower resolutions lead to fewer larger + * communities. (default 1) + * + * @return a pair containing: + * 1) number of levels of the returned clustering + * 2) modularity of the returned clustering * */ template std::pair louvain( raft::handle_t const& handle, + std::optional> rng_state, graph_view_t const& graph_view, std::optional> edge_weight_view, vertex_t* clustering, @@ -593,25 +600,33 @@ std::pair louvain( * * @throws cugraph::logic_error when an error occurs. * - * @tparam graph_view_t Type of graph - * - * @param[in] handle Library handle (RAFT) - * @param[in] graph_view Input graph view object - * @param[in] max_level (optional) maximum number of levels to run (default 100) - * @param[in] resolution (optional) The value of the resolution parameter to use. - * Called gamma in the modularity formula, this changes the size - * of the communities. Higher resolutions lead to more smaller - * communities, lower resolutions lead to fewer larger - * communities. (default 1) + * @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 weight_t Type of edge weights. Needs to be a floating point type. + * @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false) * - * @return a pair containing: - * 1) unique pointer to dendrogram - * 2) modularity of the returned clustering + * @param[in] handle Library handle (RAFT). If a communicator is set in the handle, + * @param[in] rng_state The RngState instance holding pseudo-random number generator state. + * @param[in] graph_view Input graph view object. + * @param[in] edge_weight_view Optional view object holding edge weights for @p graph_view. + * If @pedge_weight_view.has_value() == false, edge weights + * are assumed to be 1.0. + * @param[in] max_level (optional) maximum number of levels to run (default 100) + * @param[in] threshold (optional) threshold for convergence at each level (default 1e-7) + * @param[in] resolution (optional) The value of the resolution parameter to use. + * Called gamma in the modularity formula, this changes the size + * of the communities. Higher resolutions lead to more smaller + * communities, lower resolutions lead to fewer larger + * communities. (default 1) + * @return a pair containing: + * 1) unique pointer to dendrogram + * 2) modularity of the returned clustering * */ template std::pair>, weight_t> louvain( raft::handle_t const& handle, + std::optional> rng_state, graph_view_t const& graph_view, std::optional> edge_weight_view, size_t max_level = 100, @@ -779,6 +794,55 @@ void ecg(raft::handle_t const& handle, vertex_t ensemble_size, vertex_t* clustering); +/** + * @brief Computes the ecg clustering of the given graph. + * + * ECG runs truncated Louvain on an ensemble of permutations of the input graph, + * then uses the ensemble partitions to determine weights for the input graph. + * The final result is found by running full Louvain on the input graph using + * the determined weights. See https://arxiv.org/abs/1809.05578 for further + * information. + * + * @throws cugraph::logic_error when an error occurs. + * + * @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 weight_t Type of edge weights. Needs to be a floating point type. + * @tparam multi_gpu Flag indicating whether template instantiation should target single-GPU (false) + * + * @param[in] handle Library handle (RAFT). If a communicator is set in the handle, + * @param[in] rng_state The RngState instance holding pseudo-random number generator state. + * @param[in] graph_view Input graph view object + * @param[in] edge_weight_view View object holding edge weights for @p graph_view. + * @param[in] min_weight Minimum edge weight to use in the final call of the clustering + * algorithm if an edge does not appear in any of the ensemble runs. + * @param[in] ensemble_size The ensemble size parameter + * @param[in] max_level (optional) maximum number of levels to run (default 100) + * @param[in] threshold (optional) threshold for convergence at each level (default 1e-7) + * @param[in] resolution (optional) The value of the resolution parameter to use. + * Called gamma in the modularity formula, this changes the size + * of the communities. Higher resolutions lead to more smaller + * communities, lower resolutions lead to fewer larger + * communities. (default 1) + * + * @return a tuple containing: + * 1) Device vector containing clustering result + * 2) number of levels of the returned clustering + * 3) modularity of the returned clustering + * + */ +template +std::tuple, size_t, weight_t> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + weight_t min_weight, + size_t ensemble_size, + size_t max_level = 100, + weight_t threshold = weight_t{1e-7}, + weight_t resolution = weight_t{1}); + /** * @brief Generate edges in a minimum spanning forest of an undirected weighted graph. * diff --git a/cpp/include/cugraph/detail/collect_comm_wrapper.hpp b/cpp/include/cugraph/detail/collect_comm_wrapper.hpp index b791c593f41..4a2f5d7c44e 100644 --- a/cpp/include/cugraph/detail/collect_comm_wrapper.hpp +++ b/cpp/include/cugraph/detail/collect_comm_wrapper.hpp @@ -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. @@ -15,6 +15,7 @@ */ #pragma once +#include #include #include diff --git a/cpp/include/cugraph/detail/shuffle_wrappers.hpp b/cpp/include/cugraph/detail/shuffle_wrappers.hpp index 55ea6a0e355..c77ecb7aa01 100644 --- a/cpp/include/cugraph/detail/shuffle_wrappers.hpp +++ b/cpp/include/cugraph/detail/shuffle_wrappers.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. @@ -16,6 +16,7 @@ #pragma once #include +#include #include #include @@ -138,6 +139,28 @@ shuffle_ext_vertex_value_pairs_to_local_gpu_by_vertex_partitioning( rmm::device_uvector&& vertices, rmm::device_uvector&& values); +/** + * @brief Permute a range. + * + * @tparam vertex_t Type of vertex identifiers. Needs to be an integral type. + * + * @param[in] handle RAFT handle object to encapsulate resources (e.g. CUDA stream, communicator, + * and handles to various CUDA libraries) to run graph algorithms. + * @param[in] rng_state The RngState instance holding pseudo-random number generator state. + * @param[in] local_range_size Size of local range assigned to this process. + * @param[in] local_start Start of local range assigned to this process. + * + * @return permuted range. + */ + +template +rmm::device_uvector permute_range(raft::handle_t const& handle, + raft::random::RngState& rng_state, + vertex_t local_start, + vertex_t local_range_size, + bool multi_gpu = false, + bool do_expensive_check = false); + /** * @brief Shuffle internal (i.e. renumbered) vertices to their local GPUs based on vertex * partitioning. diff --git a/cpp/src/c_api/louvain.cpp b/cpp/src/c_api/louvain.cpp index 0e48b29388a..a131ee6a3ad 100644 --- a/cpp/src/c_api/louvain.cpp +++ b/cpp/src/c_api/louvain.cpp @@ -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. @@ -95,18 +95,19 @@ struct louvain_functor : public cugraph::c_api::abstract_functor { // could add support in Louvain for std::nullopt as the edge weights behaving // as desired and only instantiating a real edge_property_view_t for the // coarsened graphs. - auto [level, modularity] = - cugraph::louvain(handle_, - graph_view, - (edge_weights != nullptr) - ? std::make_optional(edge_weights->view()) - : std::make_optional(cugraph::c_api::create_constant_edge_property( - handle_, graph_view, weight_t{1}) - .view()), - clusters.data(), - max_level_, - static_cast(threshold_), - static_cast(resolution_)); + auto [level, modularity] = cugraph::louvain( + handle_, + std::optional>{std::nullopt}, + graph_view, + (edge_weights != nullptr) + ? std::make_optional(edge_weights->view()) + : std::make_optional( + cugraph::c_api::create_constant_edge_property(handle_, graph_view, weight_t{1}) + .view()), + clusters.data(), + max_level_, + static_cast(threshold_), + static_cast(resolution_)); rmm::device_uvector vertices(graph_view.local_vertex_partition_range_size(), handle_.get_stream()); diff --git a/cpp/src/community/ecg_impl.cuh b/cpp/src/community/ecg_impl.cuh new file mode 100644 index 00000000000..f885952dfe6 --- /dev/null +++ b/cpp/src/community/ecg_impl.cuh @@ -0,0 +1,176 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace cugraph { + +namespace detail { + +template +std::tuple, size_t, weight_t> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + weight_t min_weight, + size_t ensemble_size, + size_t max_level, + weight_t threshold, + weight_t resolution) +{ + using graph_view_t = cugraph::graph_view_t; + + CUGRAPH_EXPECTS(min_weight >= weight_t{0.0}, + "Invalid input arguments: min_weight must be positive"); + CUGRAPH_EXPECTS(ensemble_size >= 1, + "Invalid input arguments: ensemble_size must be a non-zero integer"); + CUGRAPH_EXPECTS( + threshold > 0.0 && threshold <= 1.0, + "Invalid input arguments: threshold must be a positive number in range (0.0, 1.0]"); + CUGRAPH_EXPECTS( + resolution > 0.0 && resolution <= 1.0, + "Invalid input arguments: resolution must be a positive number in range (0.0, 1.0]"); + + edge_src_property_t src_cluster_assignments(handle, graph_view); + edge_dst_property_t dst_cluster_assignments(handle, graph_view); + edge_property_t modified_edge_weights(handle, graph_view); + + cugraph::fill_edge_property(handle, graph_view, weight_t{0}, modified_edge_weights); + + weight_t modularity = -1.0; + rmm::device_uvector cluster_assignments(graph_view.local_vertex_partition_range_size(), + handle.get_stream()); + + for (size_t i = 0; i < ensemble_size; i++) { + std::tie(std::ignore, modularity) = cugraph::louvain( + handle, + std::make_optional(std::reference_wrapper(rng_state)), + graph_view, + edge_weight_view, + cluster_assignments.data(), + size_t{1}, + threshold, + resolution); + + cugraph::update_edge_src_property( + handle, graph_view, cluster_assignments.begin(), src_cluster_assignments); + cugraph::update_edge_dst_property( + handle, graph_view, cluster_assignments.begin(), dst_cluster_assignments); + + cugraph::transform_e( + handle, + graph_view, + src_cluster_assignments.view(), + dst_cluster_assignments.view(), + modified_edge_weights.view(), + [] __device__(auto, auto, auto src_property, auto dst_property, auto edge_property) { + return edge_property + (src_property == dst_property); + }, + modified_edge_weights.mutable_view()); + } + + cugraph::transform_e( + handle, + graph_view, + edge_src_dummy_property_t{}.view(), + edge_dst_dummy_property_t{}.view(), + view_concat(*edge_weight_view, modified_edge_weights.view()), + [min_weight, ensemble_size = static_cast(ensemble_size)] __device__( + auto, auto, thrust::nullopt_t, thrust::nullopt_t, auto edge_properties) { + auto e_weight = thrust::get<0>(edge_properties); + auto e_frequency = thrust::get<1>(edge_properties); + return min_weight + (e_weight - min_weight) * e_frequency / ensemble_size; + }, + modified_edge_weights.mutable_view()); + + std::tie(max_level, modularity) = + cugraph::louvain(handle, + std::make_optional(std::reference_wrapper(rng_state)), + graph_view, + std::make_optional(modified_edge_weights.view()), + cluster_assignments.data(), + max_level, + threshold, + resolution); + + // Compute final modularity using original edge weights + weight_t total_edge_weight = + cugraph::compute_total_edge_weight(handle, graph_view, *edge_weight_view); + + if constexpr (multi_gpu) { + cugraph::update_edge_src_property( + handle, graph_view, cluster_assignments.begin(), src_cluster_assignments); + cugraph::update_edge_dst_property( + handle, graph_view, cluster_assignments.begin(), dst_cluster_assignments); + } + + auto [cluster_keys, cluster_weights] = cugraph::detail::compute_cluster_keys_and_values( + handle, graph_view, edge_weight_view, cluster_assignments, src_cluster_assignments); + + modularity = detail::compute_modularity(handle, + graph_view, + edge_weight_view, + src_cluster_assignments, + dst_cluster_assignments, + cluster_assignments, + cluster_weights, + total_edge_weight, + resolution); + + return std::make_tuple(std::move(cluster_assignments), max_level, modularity); +} + +} // namespace detail + +template +std::tuple, size_t, weight_t> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + weight_t min_weight, + size_t ensemble_size, + size_t max_level, + weight_t threshold, + weight_t resolution) +{ + return detail::ecg(handle, + rng_state, + graph_view, + edge_weight_view, + min_weight, + ensemble_size, + max_level, + threshold, + resolution); +} + +} // namespace cugraph diff --git a/cpp/src/community/ecg_mg.cu b/cpp/src/community/ecg_mg.cu new file mode 100644 index 00000000000..9c910c70739 --- /dev/null +++ b/cpp/src/community/ecg_mg.cu @@ -0,0 +1,92 @@ +/* + * 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 + +namespace cugraph { +template std::tuple, size_t, float> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + float min_weight, + size_t ensemble_size, + size_t max_level, + float threshold, + float resolution); + +template std::tuple, size_t, float> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + float min_weight, + size_t ensemble_size, + size_t max_level, + float threshold, + float resolution); + +template std::tuple, size_t, float> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + float min_weight, + size_t ensemble_size, + size_t max_level, + float threshold, + float resolution); + +template std::tuple, size_t, double> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution); + +template std::tuple, size_t, double> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution); + +template std::tuple, size_t, double> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution); + +} // namespace cugraph diff --git a/cpp/src/community/ecg_sg.cu b/cpp/src/community/ecg_sg.cu new file mode 100644 index 00000000000..530fb035ed5 --- /dev/null +++ b/cpp/src/community/ecg_sg.cu @@ -0,0 +1,92 @@ +/* + * 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 + +namespace cugraph { +template std::tuple, size_t, float> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + float min_weight, + size_t ensemble_size, + size_t max_level, + float threshold, + float resolution); + +template std::tuple, size_t, float> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + float min_weight, + size_t ensemble_size, + size_t max_level, + float threshold, + float resolution); + +template std::tuple, size_t, float> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + float min_weight, + size_t ensemble_size, + size_t max_level, + float threshold, + float resolution); + +template std::tuple, size_t, double> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution); + +template std::tuple, size_t, double> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution); + +template std::tuple, size_t, double> ecg( + raft::handle_t const& handle, + raft::random::RngState& rng_state, + graph_view_t const& graph_view, + std::optional> edge_weight_view, + + double min_weight, + size_t ensemble_size, + size_t max_level, + double threshold, + double resolution); + +} // namespace cugraph diff --git a/cpp/src/community/louvain_impl.cuh b/cpp/src/community/louvain_impl.cuh index 7777921a091..4919dda5a75 100644 --- a/cpp/src/community/louvain_impl.cuh +++ b/cpp/src/community/louvain_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,15 +18,18 @@ // #define TIMING +// FIXME: Only outstanding items preventing this becoming a .hpp file +#include + #include #include -#include -// FIXME: Only outstanding items preventing this becoming a .hpp file +#include #include #include #include #include +#include #include namespace cugraph { @@ -44,6 +47,7 @@ void check_clustering(graph_view_t const& gr template std::pair>, weight_t> louvain( raft::handle_t const& handle, + std::optional> rng_state, graph_view_t const& graph_view, std::optional> edge_weight_view, size_t max_level, @@ -82,11 +86,25 @@ std::pair>, weight_t> louvain( current_graph_view.local_vertex_partition_range_size(), handle.get_stream()); - detail::sequence_fill(handle.get_stream(), - dendrogram->current_level_begin(), - dendrogram->current_level_size(), - current_graph_view.local_vertex_partition_range_first()); - + if (rng_state) { + auto random_cluster_assignments = cugraph::detail::permute_range( + handle, + *rng_state, + current_graph_view.local_vertex_partition_range_first(), + current_graph_view.local_vertex_partition_range_size(), + multi_gpu); + + raft::copy(dendrogram->current_level_begin(), + random_cluster_assignments.begin(), + random_cluster_assignments.size(), + handle.get_stream()); + + } else { + detail::sequence_fill(handle.get_stream(), + dendrogram->current_level_begin(), + dendrogram->current_level_size(), + current_graph_view.local_vertex_partition_range_first()); + } // // Compute the vertex and cluster weights, these are different for each // graph in the hierarchical decomposition @@ -289,6 +307,7 @@ void flatten_dendrogram(raft::handle_t const& handle, template std::pair>, weight_t> louvain( raft::handle_t const& handle, + std::optional> rng_state, graph_view_t const& graph_view, std::optional> edge_weight_view, size_t max_level, @@ -298,7 +317,9 @@ std::pair>, weight_t> louvain( CUGRAPH_EXPECTS(!graph_view.has_edge_mask(), "unimplemented."); CUGRAPH_EXPECTS(edge_weight_view.has_value(), "Graph must be weighted"); - return detail::louvain(handle, graph_view, edge_weight_view, max_level, threshold, resolution); + + return detail::louvain( + handle, rng_state, graph_view, edge_weight_view, max_level, threshold, resolution); } template @@ -315,6 +336,7 @@ void flatten_dendrogram(raft::handle_t const& handle, template std::pair louvain( raft::handle_t const& handle, + std::optional> rng_state, graph_view_t const& graph_view, std::optional> edge_weight_view, vertex_t* clustering, @@ -330,8 +352,8 @@ std::pair louvain( std::unique_ptr> dendrogram; weight_t modularity; - std::tie(dendrogram, modularity) = - detail::louvain(handle, graph_view, edge_weight_view, max_level, threshold, resolution); + std::tie(dendrogram, modularity) = detail::louvain( + handle, rng_state, graph_view, edge_weight_view, max_level, threshold, resolution); detail::flatten_dendrogram(handle, graph_view, *dendrogram, clustering); diff --git a/cpp/src/community/louvain_mg.cu b/cpp/src/community/louvain_mg.cu index 0be32ed049f..51fb5e3d93d 100644 --- a/cpp/src/community/louvain_mg.cu +++ b/cpp/src/community/louvain_mg.cu @@ -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. @@ -22,6 +22,7 @@ namespace cugraph { template std::pair>, float> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -29,6 +30,7 @@ template std::pair>, float> louvain( float); template std::pair>, float> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -36,6 +38,7 @@ template std::pair>, float> louvain( float); template std::pair>, float> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -43,6 +46,7 @@ template std::pair>, float> louvain( float); template std::pair>, double> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -50,6 +54,7 @@ template std::pair>, double> louvain( double); template std::pair>, double> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -57,6 +62,7 @@ template std::pair>, double> louvain( double); template std::pair>, double> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -65,6 +71,7 @@ template std::pair>, double> louvain( template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -73,6 +80,7 @@ template std::pair louvain( float); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -81,6 +89,7 @@ template std::pair louvain( double); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -89,6 +98,7 @@ template std::pair louvain( float); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -97,6 +107,7 @@ template std::pair louvain( double); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int64_t*, @@ -105,6 +116,7 @@ template std::pair louvain( float); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int64_t*, diff --git a/cpp/src/community/louvain_sg.cu b/cpp/src/community/louvain_sg.cu index 3fc0ffab928..557c219d424 100644 --- a/cpp/src/community/louvain_sg.cu +++ b/cpp/src/community/louvain_sg.cu @@ -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. @@ -22,6 +22,7 @@ namespace cugraph { template std::pair>, float> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -29,6 +30,7 @@ template std::pair>, float> louvain( float); template std::pair>, float> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -36,6 +38,7 @@ template std::pair>, float> louvain( float); template std::pair>, float> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -43,6 +46,7 @@ template std::pair>, float> louvain( float); template std::pair>, double> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -50,6 +54,7 @@ template std::pair>, double> louvain( double); template std::pair>, double> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -57,6 +62,7 @@ template std::pair>, double> louvain( double); template std::pair>, double> louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, size_t, @@ -65,6 +71,7 @@ template std::pair>, double> louvain( template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -73,6 +80,7 @@ template std::pair louvain( float); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -81,6 +89,7 @@ template std::pair louvain( double); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -89,6 +98,7 @@ template std::pair louvain( float); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int32_t*, @@ -97,6 +107,7 @@ template std::pair louvain( double); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int64_t*, @@ -105,6 +116,7 @@ template std::pair louvain( float); template std::pair louvain( raft::handle_t const&, + std::optional>, graph_view_t const&, std::optional>, int64_t*, diff --git a/cpp/src/detail/permute_range.cu b/cpp/src/detail/permute_range.cu new file mode 100644 index 00000000000..cc77f022616 --- /dev/null +++ b/cpp/src/detail/permute_range.cu @@ -0,0 +1,187 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace cugraph { + +namespace detail { + +template +rmm::device_uvector permute_range(raft::handle_t const& handle, + raft::random::RngState& rng_state, + vertex_t local_range_start, + vertex_t local_range_size, + bool multi_gpu, + bool do_expensive_check) +{ + if (do_expensive_check && multi_gpu) { + auto& comm = handle.get_comms(); + auto const comm_size = comm.get_size(); + auto const comm_rank = comm.get_rank(); + + auto global_start = + cugraph::host_scalar_bcast(handle.get_comms(), local_range_start, 0, handle.get_stream()); + auto sub_range_sizes = + cugraph::host_scalar_allgather(handle.get_comms(), local_range_size, handle.get_stream()); + std::exclusive_scan( + sub_range_sizes.begin(), sub_range_sizes.end(), sub_range_sizes.begin(), global_start); + CUGRAPH_EXPECTS( + sub_range_sizes[comm_rank] == local_range_start, + "Invalid input arguments: a rage must have contiguous and non-overlapping values"); + } + rmm::device_uvector permuted_integers(local_range_size, handle.get_stream()); + + // generate as many integers as #local_range_size on each GPU + detail::sequence_fill( + handle.get_stream(), permuted_integers.begin(), permuted_integers.size(), local_range_start); + + if (multi_gpu) { + // randomly distribute integers to all GPUs + auto& comm = handle.get_comms(); + auto const comm_size = comm.get_size(); + auto const comm_rank = comm.get_rank(); + + std::vector tx_value_counts(comm_size, 0); + + { + rmm::device_uvector d_target_ranks(permuted_integers.size(), handle.get_stream()); + + cugraph::detail::uniform_random_fill(handle.get_stream(), + d_target_ranks.data(), + d_target_ranks.size(), + vertex_t{0}, + vertex_t{comm_size}, + rng_state); + + thrust::sort_by_key(handle.get_thrust_policy(), + d_target_ranks.begin(), + d_target_ranks.end(), + permuted_integers.begin()); + + rmm::device_uvector d_reduced_ranks(comm_size, handle.get_stream()); + rmm::device_uvector d_reduced_counts(comm_size, handle.get_stream()); + + auto output_end = thrust::reduce_by_key(handle.get_thrust_policy(), + d_target_ranks.begin(), + d_target_ranks.end(), + thrust::make_constant_iterator(1), + d_reduced_ranks.begin(), + d_reduced_counts.begin(), + thrust::equal_to()); + + auto nr_output_pairs = + static_cast(thrust::distance(d_reduced_ranks.begin(), output_end.first)); + + std::vector h_reduced_ranks(comm_size); + std::vector h_reduced_counts(comm_size); + + raft::update_host( + h_reduced_ranks.data(), d_reduced_ranks.data(), nr_output_pairs, handle.get_stream()); + + raft::update_host( + h_reduced_counts.data(), d_reduced_counts.data(), nr_output_pairs, handle.get_stream()); + + for (int i = 0; i < static_cast(nr_output_pairs); i++) { + tx_value_counts[h_reduced_ranks[i]] = static_cast(h_reduced_counts[i]); + } + } + + std::tie(permuted_integers, std::ignore) = cugraph::shuffle_values( + handle.get_comms(), permuted_integers.begin(), tx_value_counts, handle.get_stream()); + } + + // permute locally + rmm::device_uvector fractional_random_numbers(permuted_integers.size(), + handle.get_stream()); + + cugraph::detail::uniform_random_fill(handle.get_stream(), + fractional_random_numbers.data(), + fractional_random_numbers.size(), + float{0.0}, + float{1.0}, + rng_state); + thrust::sort_by_key(handle.get_thrust_policy(), + fractional_random_numbers.begin(), + fractional_random_numbers.end(), + permuted_integers.begin()); + + if (multi_gpu) { + // take care of deficits and extras numbers + auto& comm = handle.get_comms(); + auto const comm_rank = comm.get_rank(); + + size_t nr_extras{0}; + size_t nr_deficits{0}; + if (permuted_integers.size() > static_cast(local_range_size)) { + nr_extras = permuted_integers.size() - static_cast(local_range_size); + } else { + nr_deficits = static_cast(local_range_size) - permuted_integers.size(); + } + + auto extra_cluster_ids = cugraph::detail::device_allgatherv( + handle, + comm, + raft::device_span(permuted_integers.data() + local_range_size, + nr_extras > 0 ? nr_extras : 0)); + + permuted_integers.resize(local_range_size, handle.get_stream()); + auto deficits = + cugraph::host_scalar_allgather(handle.get_comms(), nr_deficits, handle.get_stream()); + + std::exclusive_scan(deficits.begin(), deficits.end(), deficits.begin(), vertex_t{0}); + + raft::copy(permuted_integers.data() + local_range_size - nr_deficits, + extra_cluster_ids.begin() + deficits[comm_rank], + nr_deficits, + handle.get_stream()); + } + + assert(permuted_integers.size() == local_range_size); + return permuted_integers; +} + +template rmm::device_uvector permute_range(raft::handle_t const& handle, + raft::random::RngState& rng_state, + int32_t local_range_start, + int32_t local_range_size, + bool multi_gpu, + bool do_expensive_check); + +template rmm::device_uvector permute_range(raft::handle_t const& handle, + raft::random::RngState& rng_state, + int64_t local_range_start, + int64_t local_range_size, + bool multi_gpu, + bool do_expensive_check); + +} // namespace detail +} // namespace cugraph diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index e9c6dc446af..d9d2f677abc 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -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. @@ -521,6 +521,10 @@ if(BUILD_CUGRAPH_MG_TESTS) # - MG LEIDEN tests -------------------------------------------------------------------------- ConfigureTestMG(MG_LEIDEN_TEST community/mg_leiden_test.cpp) + ############################################################################################### + # - MG ECG tests -------------------------------------------------------------------------- + ConfigureTestMG(MG_ECG_TEST community/mg_ecg_test.cpp) + ############################################################################################### # - MG MIS tests ------------------------------------------------------------------------------ ConfigureTestMG(MG_MIS_TEST community/mg_mis_test.cu) diff --git a/cpp/tests/community/louvain_test.cpp b/cpp/tests/community/louvain_test.cpp index 8de9cdaf4a8..a39793994d1 100644 --- a/cpp/tests/community/louvain_test.cpp +++ b/cpp/tests/community/louvain_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2024, NVIDIA CORPORATION. All rights reserved. * * NVIDIA CORPORATION and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation @@ -174,27 +174,39 @@ class Tests_Louvain weight_t modularity; if (resolution) { - std::tie(level, modularity) = - cugraph::louvain(handle, - graph_view, - edge_weight_view, - clustering_v.data(), - max_level ? *max_level : size_t{100}, - threshold ? static_cast(*threshold) : weight_t{1e-7}, - static_cast(*resolution)); + std::tie(level, modularity) = cugraph::louvain( + handle, + std::optional>{std::nullopt}, + graph_view, + edge_weight_view, + clustering_v.data(), + max_level ? *max_level : size_t{100}, + threshold ? static_cast(*threshold) : weight_t{1e-7}, + static_cast(*resolution)); } else if (threshold) { - std::tie(level, modularity) = cugraph::louvain(handle, - graph_view, - edge_weight_view, - clustering_v.data(), - max_level ? *max_level : size_t{100}, - static_cast(*threshold)); + std::tie(level, modularity) = cugraph::louvain( + handle, + std::optional>{std::nullopt}, + graph_view, + edge_weight_view, + clustering_v.data(), + max_level ? *max_level : size_t{100}, + static_cast(*threshold)); } else if (max_level) { - std::tie(level, modularity) = - cugraph::louvain(handle, graph_view, edge_weight_view, clustering_v.data(), *max_level); + std::tie(level, modularity) = cugraph::louvain( + handle, + std::optional>{std::nullopt}, + graph_view, + edge_weight_view, + clustering_v.data(), + *max_level); } else { - std::tie(level, modularity) = - cugraph::louvain(handle, graph_view, edge_weight_view, clustering_v.data()); + std::tie(level, modularity) = cugraph::louvain( + handle, + std::optional>{std::nullopt}, + graph_view, + edge_weight_view, + clustering_v.data()); } RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement diff --git a/cpp/tests/community/mg_ecg_test.cpp b/cpp/tests/community/mg_ecg_test.cpp new file mode 100644 index 00000000000..81cee1370f0 --- /dev/null +++ b/cpp/tests/community/mg_ecg_test.cpp @@ -0,0 +1,233 @@ +/* + * 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 + +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// Test param object. This defines the input and expected output for a test, and +// will be instantiated as the parameter to the tests defined below using +// INSTANTIATE_TEST_SUITE_P() +// +struct Ecg_Usecase { + double min_weight_{0.1}; + size_t ensemble_size_{10}; + size_t max_level_{100}; + double threshold_{1e-7}; + double resolution_{1.0}; + bool check_correctness_{true}; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Parameterized test fixture, to be used with TEST_P(). This defines common +// setup and teardown steps as well as common utilities used by each E2E MG +// test. In this case, each test is identical except for the inputs and +// expected outputs, so the entire test is defined in the run_test() method. +// +template +class Tests_MGEcg : public ::testing::TestWithParam> { + public: + static void SetUpTestCase() { handle_ = cugraph::test::initialize_mg_handle(); } + + static void TearDownTestCase() { handle_.reset(); } + + // Run once for each test instance + virtual void SetUp() {} + virtual void TearDown() {} + + template + void run_current_test(std::tuple const& param) + { + auto [ecg_usecase, input_usecase] = param; + + HighResTimer hr_timer{}; + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.start("MG Construct graph"); + } + + auto [mg_graph, mg_edge_weights, d_renumber_map_labels] = + cugraph::test::construct_graph( + *handle_, input_usecase, true, 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(); + auto mg_edge_weight_view = + mg_edge_weights ? std::make_optional((*mg_edge_weights).view()) : std::nullopt; + + if (cugraph::test::g_perf) { + RAFT_CUDA_TRY(cudaDeviceSynchronize()); // for consistent performance measurement + handle_->get_comms().barrier(); + hr_timer.start("MG ECG"); + } + + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + raft::random::RngState rng_state(seed); + + cugraph::ecg(*handle_, + rng_state, + mg_graph_view, + mg_edge_weight_view, + ecg_usecase.min_weight_, + ecg_usecase.ensemble_size_, + ecg_usecase.max_level_, + ecg_usecase.threshold_, + ecg_usecase.resolution_); + + 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); + } + // Louvain and detail::permute_range are both tested, here we only make + // sure that SG and MG ECG calls work expected. + + cugraph::graph_t sg_graph(*handle_); + std::optional< + cugraph::edge_property_t, weight_t>> + sg_edge_weights{std::nullopt}; + std::tie(sg_graph, sg_edge_weights, std::ignore) = cugraph::test::mg_graph_to_sg_graph( + *handle_, + mg_graph_view, + mg_edge_weight_view, + std::optional>{std::nullopt}, + false); // crate a SG graph with MG graph vertex IDs + + auto const comm_rank = handle_->get_comms().get_rank(); + if (comm_rank == 0) { + auto sg_graph_view = sg_graph.view(); + auto sg_edge_weight_view = + sg_edge_weights ? std::make_optional((*sg_edge_weights).view()) : std::nullopt; + + cugraph::ecg(*handle_, + rng_state, + sg_graph_view, + sg_edge_weight_view, + ecg_usecase.min_weight_, + ecg_usecase.ensemble_size_, + ecg_usecase.max_level_, + ecg_usecase.threshold_, + ecg_usecase.resolution_); + } + } + + private: + static std::unique_ptr handle_; +}; + +template +std::unique_ptr Tests_MGEcg::handle_ = nullptr; + +using Tests_MGEcg_File = Tests_MGEcg; +using Tests_MGEcg_Rmat = Tests_MGEcg; + +TEST_P(Tests_MGEcg_File, CheckInt32Int32Float) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_MGEcg_File, CheckInt64Int64Float) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_MGEcg_Rmat, CheckInt32Int32Float) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_MGEcg_Rmat, CheckInt32Int64Float) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_MGEcg_Rmat, CheckInt64Int64Float) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +INSTANTIATE_TEST_SUITE_P( + file_tests, + Tests_MGEcg_File, + ::testing::Combine( + // enable correctness checks for small graphs + ::testing::Values(Ecg_Usecase{0.1, 10, 100, 1e-7, 1.0, true}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + rmat_small_tests, + Tests_MGEcg_Rmat, + ::testing::Combine( + ::testing::Values(Ecg_Usecase{0.1, 10, 100, 1e-7, 1.0, true}), + ::testing::Values(cugraph::test::Rmat_Usecase(10, 16, 0.57, 0.19, 0.19, 0, true, false)))); + +INSTANTIATE_TEST_SUITE_P( + file_benchmark_test, /* note that the test filename can be overridden in benchmarking (with + --gtest_filter to select only the file_benchmark_test with a specific + vertex & edge type combination) by command line arguments and do not + include more than one File_Usecase that differ only in filename + (to avoid running same benchmarks more than once) */ + Tests_MGEcg_File, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Ecg_Usecase{0.1, 10, 100, 1e-7, 1.0, true}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +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_MGEcg_Rmat, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Ecg_Usecase{0.1, 10, 100, 1e-7, 1.0, true}), + ::testing::Values(cugraph::test::Rmat_Usecase(12, 32, 0.57, 0.19, 0.19, 0, true, false)))); + +CUGRAPH_MG_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/community/mg_louvain_test.cpp b/cpp/tests/community/mg_louvain_test.cpp index 41339e32d77..011426606fd 100644 --- a/cpp/tests/community/mg_louvain_test.cpp +++ b/cpp/tests/community/mg_louvain_test.cpp @@ -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. @@ -126,13 +126,15 @@ class Tests_MGLouvain rmm::device_uvector d_sg_cluster_v(sg_graph_view.number_of_vertices(), handle_->get_stream()); - std::tie(std::ignore, sg_modularity) = cugraph::louvain(handle, - sg_graph_view, - sg_edge_weight_view, - d_sg_cluster_v.data(), - size_t{1}, - threshold, - resolution); + std::tie(std::ignore, sg_modularity) = cugraph::louvain( + handle, + std::optional>{std::nullopt}, + sg_graph_view, + sg_edge_weight_view, + d_sg_cluster_v.data(), + size_t{1}, + threshold, + resolution); EXPECT_TRUE(cugraph::test::check_invertible( handle, @@ -191,6 +193,7 @@ class Tests_MGLouvain auto [dendrogram, mg_modularity] = cugraph::louvain( *handle_, + std::optional>{std::nullopt}, mg_graph_view, mg_edge_weight_view, louvain_usecase.max_level_,