diff --git a/core/test/gtest/ginkgo_mpi_main.cpp b/core/test/gtest/ginkgo_mpi_main.cpp index 07a1c2c343d..76f4546c4ed 100644 --- a/core/test/gtest/ginkgo_mpi_main.cpp +++ b/core/test/gtest/ginkgo_mpi_main.cpp @@ -356,7 +356,13 @@ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - MPI_Init(&argc, &argv); + int provided_thread_support; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, + &provided_thread_support); + if (provided_thread_support != MPI_THREAD_MULTIPLE) { + throw std::runtime_error( + "This test requires an thread compliant MPI implementation."); + } MPI_Comm comm(MPI_COMM_WORLD); int rank; int size; diff --git a/core/test/mpi/distributed/CMakeLists.txt b/core/test/mpi/distributed/CMakeLists.txt index 9c7b43cf44b..ab676c5cfd1 100644 --- a/core/test/mpi/distributed/CMakeLists.txt +++ b/core/test/mpi/distributed/CMakeLists.txt @@ -1,6 +1,7 @@ ginkgo_create_test(helpers MPI_SIZE 1) ginkgo_create_test(matrix MPI_SIZE 1) ginkgo_create_test(dense_communicator MPI_SIZE 6) +ginkgo_create_test(row_gatherer MPI_SIZE 6) if(NOT GINKGO_HAVE_OPENMPI_PRE_4_1_X) ginkgo_create_test(neighborhood_communicator MPI_SIZE 6) diff --git a/core/test/mpi/distributed/row_gatherer.cpp b/core/test/mpi/distributed/row_gatherer.cpp new file mode 100644 index 00000000000..c2b8a8ab8e9 --- /dev/null +++ b/core/test/mpi/distributed/row_gatherer.cpp @@ -0,0 +1,266 @@ +// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors +// +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#include +#include +#include + +#include "core/test/utils.hpp" +#include "core/test/utils/assertions.hpp" + + +template +class RowGatherer : public ::testing::Test { +protected: + using index_type = IndexType; + using part_type = + gko::experimental::distributed::Partition; + using map_type = + gko::experimental::distributed::index_map; + using row_gatherer_type = + gko::experimental::distributed::RowGatherer; + + RowGatherer() + { + int rank = this->comm.rank(); + auto part = gko::share(part_type::build_from_global_size_uniform( + this->ref, this->comm.size(), this->comm.size() * 3)); + auto recv_connections = + this->template create_recv_connections()[rank]; + auto imap = + map_type{this->ref, part, this->comm.rank(), recv_connections}; + auto coll_comm = + std::make_shared( + this->comm, imap); + rg = row_gatherer_type::create(ref, coll_comm, imap); + } + + void SetUp() override { ASSERT_EQ(comm.size(), 6); } + + template + std::array, 6> create_recv_connections() + { + return {gko::array{ref, {3, 5, 10, 11}}, + gko::array{ref, {0, 1, 7, 12, 13}}, + gko::array{ref, {3, 4, 17}}, + gko::array{ref, {1, 2, 12, 14}}, + gko::array{ref, {4, 5, 9, 10, 15, 16}}, + gko::array{ref, {8, 12, 13, 14}}}; + } + + std::shared_ptr ref = gko::ReferenceExecutor::create(); + gko::experimental::mpi::communicator comm = MPI_COMM_WORLD; + std::shared_ptr rg; +}; + +TYPED_TEST_SUITE(RowGatherer, gko::test::IndexTypes, TypenameNameGenerator); + +TYPED_TEST(RowGatherer, CanDefaultConstruct) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + + auto rg = RowGatherer::create(this->ref, this->comm); + + GKO_ASSERT_EQUAL_DIMENSIONS(rg, gko::dim<2>()); +} + + +TYPED_TEST(RowGatherer, CanConstructWithEmptCollectiveCommAndIndexMap) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + using IndexMap = typename TestFixture::map_type; + auto coll_comm = + std::make_shared( + this->comm); + auto map = IndexMap{this->ref}; + + auto rg = RowGatherer::create(this->ref, coll_comm, map); + + GKO_ASSERT_EQUAL_DIMENSIONS(rg, gko::dim<2>()); +} + + +TYPED_TEST(RowGatherer, CanConstructFromCollectiveCommAndIndexMap) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + using Part = typename TestFixture::part_type; + using IndexMap = typename TestFixture::map_type; + int rank = this->comm.rank(); + auto part = gko::share(Part::build_from_global_size_uniform( + this->ref, this->comm.size(), this->comm.size() * 3)); + auto recv_connections = + this->template create_recv_connections()[rank]; + auto imap = IndexMap{this->ref, part, this->comm.rank(), recv_connections}; + auto coll_comm = + std::make_shared( + this->comm, imap); + + auto rg = RowGatherer::create(this->ref, coll_comm, imap); + + gko::dim<2> size{recv_connections.get_size(), 18}; + GKO_ASSERT_EQUAL_DIMENSIONS(rg, size); +} + + +TYPED_TEST(RowGatherer, CanApply) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + + this->rg->apply(b, x); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, CanApplyAsync) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + + auto req = this->rg->apply_async(b, x); + req.wait(); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, CanApplyAsyncConsequetively) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + + this->rg->apply_async(b, x).wait(); + this->rg->apply_async(b, x).wait(); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, CanApplyAsyncWithWorkspace) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + gko::array workspace(this->ref); + + auto req = this->rg->apply_async(b, x, workspace); + req.wait(); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, CanApplyAsyncMultipleTimesWithWorkspace) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b1 = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 1}, + gko::initialize({offset, offset + 1, offset + 2}, this->ref)); + auto b2 = gko::clone(b1); + b2->scale(gko::initialize({-1}, this->ref)); + auto x1 = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 1}); + auto x2 = gko::clone(x1); + gko::array workspace1(this->ref); + gko::array workspace2(this->ref); + + auto req1 = this->rg->apply_async(b1, x1, workspace1); + auto req2 = this->rg->apply_async(b2, x2, workspace2); + req1.wait(); + req2.wait(); + + auto expected = this->template create_recv_connections()[rank]; + auto expected_vec1 = Dense::create( + this->ref, gko::dim<2>{expected.get_size(), 1}, expected, 1); + auto expected_vec2 = gko::clone(expected_vec1); + expected_vec2->scale(gko::initialize({-1}, this->ref)); + GKO_ASSERT_MTX_NEAR(x1, expected_vec1, 0.0); + GKO_ASSERT_MTX_NEAR(x2, expected_vec2, 0.0); +} + + +TYPED_TEST(RowGatherer, CanApplyAsyncWithMultipleColumns) +{ + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + int rank = this->comm.rank(); + auto offset = static_cast(rank * 3); + auto b = Vector::create( + this->ref, this->comm, gko::dim<2>{18, 2}, + gko::initialize({{offset, offset * offset}, + {offset + 1, offset * offset + 1}, + {offset + 2, offset * offset + 2}}, + this->ref)); + auto x = Dense::create(this->ref, gko::dim<2>{this->rg->get_size()[0], 2}); + + this->rg->apply_async(b, x).wait(); + + gko::array expected[] = { + gko::array{this->ref, {3, 9, 5, 11, 10, 82, 11, 83}}, + gko::array{this->ref, {0, 0, 1, 1, 7, 37, 12, 144, 13, 145}}, + gko::array{this->ref, {3, 9, 4, 10, 17, 227}}, + gko::array{this->ref, {1, 1, 2, 2, 12, 144, 14, 146}}, + gko::array{this->ref, + {4, 10, 5, 11, 9, 81, 10, 82, 15, 225, 16, 226}}, + gko::array{this->ref, {8, 38, 12, 144, 13, 145, 14, 146}}}; + auto expected_vec = + Dense::create(this->ref, gko::dim<2>{expected[rank].get_size() / 2, 2}, + expected[rank], 2); + GKO_ASSERT_MTX_NEAR(x, expected_vec, 0.0); +} + + +TYPED_TEST(RowGatherer, ThrowsOnAdvancedApply) +{ + using RowGatherer = typename TestFixture::row_gatherer_type; + using Dense = gko::matrix::Dense; + using Vector = gko::experimental::distributed::Vector; + auto rg = RowGatherer::create(this->ref, this->comm); + auto b = Vector::create(this->ref, this->comm); + auto x = Dense::create(this->ref); + auto alpha = Dense::create(this->ref, gko::dim<2>{1, 1}); + auto beta = Dense::create(this->ref, gko::dim<2>{1, 1}); + + ASSERT_THROW(rg->apply(alpha, b, beta, x), gko::NotImplemented); +}