diff --git a/cpp/src/community/legacy/louvain.cu b/cpp/src/community/legacy/louvain.cu index 1f1e09461b5..6ec04402a2e 100644 --- a/cpp/src/community/legacy/louvain.cu +++ b/cpp/src/community/legacy/louvain.cu @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, NVIDIA CORPORATION. + * Copyright (c) 2021-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. @@ -95,6 +95,7 @@ std::pair louvain( using vertex_t = typename graph_view_t::vertex_type; using weight_t = typename graph_view_t::weight_type; + CUGRAPH_EXPECTS(graph_view.has_data(), "Graph must be weighted"); detail::check_clustering(graph_view, clustering); std::unique_ptr> dendrogram; diff --git a/cpp/src/community/louvain_impl.cuh b/cpp/src/community/louvain_impl.cuh index 124ee6f12b4..cfdeddfcf62 100644 --- a/cpp/src/community/louvain_impl.cuh +++ b/cpp/src/community/louvain_impl.cuh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021, NVIDIA CORPORATION. + * 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. @@ -103,6 +103,7 @@ std::pair louvain( using vertex_t = typename graph_view_t::vertex_type; using weight_t = typename graph_view_t::weight_type; + CUGRAPH_EXPECTS(graph_view.is_weighted(), "Graph must be weighted"); detail::check_clustering(graph_view, clustering); std::unique_ptr> dendrogram; diff --git a/cpp/tests/community/louvain_test.cpp b/cpp/tests/community/louvain_test.cpp index 37979a78414..b86cfdee5c6 100644 --- a/cpp/tests/community/louvain_test.cpp +++ b/cpp/tests/community/louvain_test.cpp @@ -29,7 +29,8 @@ #include struct Louvain_Usecase { - bool test_weighted_{false}; + size_t max_level_{100}; + double resolution_{1}; bool check_correctness_{false}; int expected_level_{0}; float expected_modularity_{0}; @@ -47,8 +48,10 @@ class Tests_Louvain virtual void TearDown() {} template - void run_legacy_test(Louvain_Usecase const& louvain_usecase, input_usecase_t const& input_usecase) + void run_legacy_test(std::tuple const& param) { + auto [louvain_usecase, input_usecase] = param; + raft::handle_t handle{}; bool directed{false}; @@ -82,9 +85,10 @@ class Tests_Louvain } template - void run_current_test(Louvain_Usecase const& louvain_usecase, - input_usecase_t const& input_usecase) + void run_current_test(std::tuple const& param) { + auto [louvain_usecase, input_usecase] = param; + raft::handle_t handle{}; // Can't currently check correctness if we renumber @@ -93,7 +97,7 @@ class Tests_Louvain auto [graph, d_renumber_map_labels] = cugraph::test::construct_graph( - handle, input_usecase, louvain_usecase.test_weighted_, renumber); + handle, input_usecase, true, renumber); auto graph_view = graph.view(); @@ -296,65 +300,126 @@ TEST(louvain_legacy_renumbered, success) } } -using Tests_Louvain_File = Tests_Louvain; -using Tests_Louvain_Rmat = Tests_Louvain; +using Tests_Louvain_File = Tests_Louvain; +using Tests_Louvain_File32 = Tests_Louvain; +using Tests_Louvain_File64 = Tests_Louvain; +using Tests_Louvain_Rmat = Tests_Louvain; +using Tests_Louvain_Rmat32 = Tests_Louvain; +using Tests_Louvain_Rmat64 = Tests_Louvain; TEST_P(Tests_Louvain_File, CheckInt32Int32FloatFloatLegacy) { - auto param = GetParam(); - run_legacy_test(std::get<0>(param), std::get<1>(param)); + run_legacy_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); } TEST_P(Tests_Louvain_File, CheckInt32Int32FloatFloat) { - auto param = GetParam(); - run_current_test(std::get<0>(param), std::get<1>(param)); + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); } TEST_P(Tests_Louvain_File, CheckInt64Int64FloatFloat) { - auto param = GetParam(); - run_current_test(std::get<0>(param), std::get<1>(param)); + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_Louvain_File32, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_Louvain_File64, CheckInt64Int64FloatFloat) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); } TEST_P(Tests_Louvain_Rmat, CheckInt32Int32FloatFloatLegacy) { - auto param = GetParam(); run_legacy_test( - std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); } TEST_P(Tests_Louvain_Rmat, CheckInt32Int32FloatFloat) { - auto param = GetParam(); run_current_test( - std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); } TEST_P(Tests_Louvain_Rmat, CheckInt64Int64FloatFloat) { - auto param = GetParam(); run_current_test( - std::get<0>(param), override_Rmat_Usecase_with_cmd_line_arguments(std::get<1>(param))); + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_Louvain_Rmat32, CheckInt32Int32FloatFloat) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_Louvain_Rmat64, CheckInt64Int64FloatFloat) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); } // FIXME: Expand testing once we evaluate RMM memory use INSTANTIATE_TEST_SUITE_P( simple_test, Tests_Louvain_File, - ::testing::Combine(::testing::Values(Louvain_Usecase{true, true, 3, 0.408695}), + ::testing::Combine(::testing::Values(Louvain_Usecase{100, 1, true, 3, 0.408695}), ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); +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_Louvain_File32, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Louvain_Usecase{}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + file64_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_Louvain_File64, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Louvain_Usecase{}), + ::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_Louvain_Rmat, + Tests_Louvain_Rmat32, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Louvain_Usecase{}), + ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, true, false)))); + +INSTANTIATE_TEST_SUITE_P( + rmat64_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_Louvain_Rmat64, ::testing::Combine( // disable correctness checks for large graphs - ::testing::Values(Louvain_Usecase{true, false}), + ::testing::Values(Louvain_Usecase{}), ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, true, false)))); CUGRAPH_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/community/mg_louvain_helper.cu b/cpp/tests/community/mg_louvain_helper.cu index a2cfe649f99..1c37851bad7 100644 --- a/cpp/tests/community/mg_louvain_helper.cu +++ b/cpp/tests/community/mg_louvain_helper.cu @@ -264,14 +264,25 @@ coarsen_graph( template void single_gpu_renumber_edgelist_given_number_map( raft::handle_t const& handle, - rmm::device_uvector& d_edgelist_rows, - rmm::device_uvector& d_edgelist_cols, - rmm::device_uvector& d_renumber_map_gathered_v); + rmm::device_uvector& d_edgelist_rows, + rmm::device_uvector& d_edgelist_cols, + rmm::device_uvector& d_renumber_map_gathered_v); + +template void single_gpu_renumber_edgelist_given_number_map( + raft::handle_t const& handle, + rmm::device_uvector& d_edgelist_rows, + rmm::device_uvector& d_edgelist_cols, + rmm::device_uvector& d_renumber_map_gathered_v); template std::unique_ptr> coarsen_graph( raft::handle_t const& handle, cugraph::graph_view_t const& graph_view, int32_t const* labels); +template std::unique_ptr> coarsen_graph( + raft::handle_t const& handle, + cugraph::graph_view_t const& graph_view, + int64_t const* labels); + } // namespace test } // namespace cugraph diff --git a/cpp/tests/community/mg_louvain_test.cpp b/cpp/tests/community/mg_louvain_test.cpp index 9acca9a88ae..feb0b91d06a 100644 --- a/cpp/tests/community/mg_louvain_test.cpp +++ b/cpp/tests/community/mg_louvain_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, NVIDIA CORPORATION. + * Copyright (c) 2021-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. @@ -49,26 +49,9 @@ void compare(double mg_modularity, double sg_modularity) // INSTANTIATE_TEST_SUITE_P() // struct Louvain_Usecase { - std::string graph_file_full_path{}; - bool weighted{false}; - size_t max_level; - double resolution; - - // FIXME: We really should have a Graph_Testparms_Base class or something - // like that which can handle this graph_full_path thing. - // - Louvain_Usecase(std::string const& graph_file_path, - bool weighted, - size_t max_level, - double resolution) - : weighted(weighted), max_level(max_level), resolution(resolution) - { - if ((graph_file_path.length() > 0) && (graph_file_path[0] != '/')) { - graph_file_full_path = cugraph::test::get_rapids_dataset_root_dir() + "/" + graph_file_path; - } else { - graph_file_full_path = graph_file_path; - } - }; + size_t max_level_{100}; + double resolution_{1}; + bool check_correctness_{false}; }; //////////////////////////////////////////////////////////////////////////////// @@ -77,7 +60,9 @@ struct Louvain_Usecase { // 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. // -class Louvain_MG_Testfixture : public ::testing::TestWithParam { +template +class Tests_MG_Louvain + : public ::testing::TestWithParam> { public: static void SetUpTestCase() {} static void TearDownTestCase() {} @@ -91,7 +76,7 @@ class Louvain_MG_Testfixture : public ::testing::TestWithParam // on the MNMG renumbering. template void compare_sg_results(raft::handle_t const& handle, - std::string const& graph_filename, + input_usecase_t const& input_usecase, rmm::device_uvector& d_renumber_map_gathered_v, cugraph::Dendrogram const& dendrogram, weight_t resolution, @@ -112,8 +97,8 @@ class Louvain_MG_Testfixture : public ::testing::TestWithParam d_vertices, number_of_vertices, is_symmetric] = - cugraph::test::read_edgelist_from_matrix_market_file( - handle, graph_filename, true); + input_usecase.template construct_edgelist(handle, + true); d_clustering_v.resize(d_vertices.size(), handle.get_stream()); @@ -165,8 +150,10 @@ class Louvain_MG_Testfixture : public ::testing::TestWithParam // iteration of the outer loop. Renumbering of the partitions when coarsening // the graph is a function of the number of GPUs in the GPU cluster. template - void run_test(const Louvain_Usecase& param) + void run_current_test(std::tuple const& param) { + auto [louvain_usecase, input_usecase] = param; + raft::handle_t handle; raft::comms::initialize_mpi_comms(&handle, MPI_COMM_WORLD); @@ -185,42 +172,127 @@ class Louvain_MG_Testfixture : public ::testing::TestWithParam cudaStream_t stream = handle.get_stream(); auto [mg_graph, d_renumber_map_labels] = - cugraph::test::read_graph_from_matrix_market_file( - handle, param.graph_file_full_path, true, true); + cugraph::test::construct_graph( + handle, input_usecase, true, true); auto mg_graph_view = mg_graph.view(); std::unique_ptr> dendrogram; weight_t mg_modularity; - std::tie(dendrogram, mg_modularity) = - cugraph::louvain(handle, mg_graph_view, param.max_level, param.resolution); + std::tie(dendrogram, mg_modularity) = cugraph::louvain( + handle, mg_graph_view, louvain_usecase.max_level_, louvain_usecase.resolution_); - SCOPED_TRACE("compare modularity input: " + param.graph_file_full_path); + if (louvain_usecase.check_correctness_) { + SCOPED_TRACE("compare modularity input"); - auto d_renumber_map_gathered_v = cugraph::test::device_gatherv( - handle, (*d_renumber_map_labels).data(), (*d_renumber_map_labels).size()); + auto d_renumber_map_gathered_v = cugraph::test::device_gatherv( + handle, (*d_renumber_map_labels).data(), (*d_renumber_map_labels).size()); - compare_sg_results(handle, - param.graph_file_full_path, - d_renumber_map_gathered_v, - *dendrogram, - param.resolution, - comm_rank, - mg_modularity); + compare_sg_results(handle, + input_usecase, + d_renumber_map_gathered_v, + *dendrogram, + louvain_usecase.resolution_, + comm_rank, + mg_modularity); + } } }; //////////////////////////////////////////////////////////////////////////////// -TEST_P(Louvain_MG_Testfixture, CheckInt32Int32Float) +using Tests_MG_Louvain_File = Tests_MG_Louvain; +using Tests_MG_Louvain_File64 = Tests_MG_Louvain; +using Tests_MG_Louvain_Rmat = Tests_MG_Louvain; +using Tests_MG_Louvain_Rmat64 = Tests_MG_Louvain; + +TEST_P(Tests_MG_Louvain_File, CheckInt32Int32Float) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_MG_Louvain_File64, CheckInt64Int64Float) +{ + run_current_test( + override_File_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_MG_Louvain_Rmat, CheckInt32Int32Float) +{ + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); +} + +TEST_P(Tests_MG_Louvain_Rmat64, CheckInt64Int64Float) { - run_test(GetParam()); + run_current_test( + override_Rmat_Usecase_with_cmd_line_arguments(GetParam())); } INSTANTIATE_TEST_SUITE_P( - simple_test, - Louvain_MG_Testfixture, - ::testing::Values(Louvain_Usecase("test/datasets/karate.mtx", true, 100, 1), - Louvain_Usecase("test/datasets/dolphins.mtx", true, 100, 1))); + simple_file_test, + Tests_MG_Louvain_File, + ::testing::Combine( + // enable correctness checks for small graphs + ::testing::Values(Louvain_Usecase{100, 1, true}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx"), + cugraph::test::File_Usecase("test/datasets/dolphins.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + simple_rmat_test, + Tests_MG_Louvain_Rmat, + ::testing::Combine( + // enable correctness checks for small graphs + ::testing::Values(Louvain_Usecase{}), + ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 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_MG_Louvain_File, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Louvain_Usecase{}), + ::testing::Values(cugraph::test::File_Usecase("test/datasets/karate.mtx")))); + +INSTANTIATE_TEST_SUITE_P( + file64_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_MG_Louvain_File64, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Louvain_Usecase{}), + ::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_MG_Louvain_Rmat, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Louvain_Usecase{}), + ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, true, false)))); + +INSTANTIATE_TEST_SUITE_P( + rmat64_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_MG_Louvain_Rmat64, + ::testing::Combine( + // disable correctness checks for large graphs + ::testing::Values(Louvain_Usecase{}), + ::testing::Values(cugraph::test::Rmat_Usecase(20, 32, 0.57, 0.19, 0.19, 0, true, false)))); CUGRAPH_MG_TEST_PROGRAM_MAIN() diff --git a/cpp/tests/utilities/test_utilities.hpp b/cpp/tests/utilities/test_utilities.hpp index 62c32e6a667..2e0d8187e93 100644 --- a/cpp/tests/utilities/test_utilities.hpp +++ b/cpp/tests/utilities/test_utilities.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. + * Copyright (c) 2019-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. @@ -331,22 +331,6 @@ bool renumbered_vectors_same(raft::handle_t const& handle, return (error_count == 0); } -template -bool renumbered_vectors_same(raft::handle_t const& handle, - rmm::device_uvector const& v1, - rmm::device_uvector const& v2) -{ - if (v1.size() != v2.size()) return false; - - std::vector h_v1(v1.size()); - std::vector h_v2(v1.size()); - - raft::update_host(h_v1.data(), v1.data(), v1.size(), handle.get_stream()); - raft::update_host(h_v2.data(), v2.data(), v2.size(), handle.get_stream()); - - return renumbered_vectors_same(handle, h_v1, h_v2); -} - template std::vector to_host(raft::handle_t const& handle, T const* data, L size) { @@ -356,6 +340,17 @@ std::vector to_host(raft::handle_t const& handle, T const* data, L size) return h_data; } +template +bool renumbered_vectors_same(raft::handle_t const& handle, + rmm::device_uvector const& v1, + rmm::device_uvector const& v2) +{ + if (v1.size() != v2.size()) return false; + + return renumbered_vectors_same( + handle, to_host(handle, v1.data(), v1.size()), to_host(handle, v2.data(), v2.size())); +} + template std::vector random_vector(L size, unsigned seed = 0) {