From 0b8348d2dfbb42a1f54cd19de742a85a3124e1de Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Sun, 8 Oct 2023 23:26:29 +0200 Subject: [PATCH 01/21] multilevel schwarz WIP --- core/distributed/preconditioner/schwarz.cpp | 9 +++++++ .../distributed/preconditioner/schwarz.cpp | 27 +++++++++++++++++-- .../distributed/preconditioner/schwarz.hpp | 10 +++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index 1ba3a09861b..fad134b317d 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -183,6 +183,15 @@ void Schwarz::generate( this->set_solver( gko::share(parameters_.local_solver->generate(local_matrix))); } + + auto dist_mat = + as>(system_matrix); + + if (parameters_.coarse_solver_factory) { + this->coarse_solver_ = as( + share(parameters_.coarse_solver_factory->generate(dist_mat))); + } } diff --git a/core/test/mpi/distributed/preconditioner/schwarz.cpp b/core/test/mpi/distributed/preconditioner/schwarz.cpp index 1cf3f04d311..c8592223020 100644 --- a/core/test/mpi/distributed/preconditioner/schwarz.cpp +++ b/core/test/mpi/distributed/preconditioner/schwarz.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ class SchwarzFactory : public ::testing::Test { using Schwarz = gko::experimental::distributed::preconditioner::Schwarz< value_type, local_index_type, global_index_type>; using Jacobi = gko::preconditioner::Jacobi; + using Pgm = gko::multigrid::Pgm; using Mtx = gko::experimental::distributed::Matrix; @@ -36,10 +38,12 @@ class SchwarzFactory : public ::testing::Test { SchwarzFactory() : exec(gko::ReferenceExecutor::create()), jacobi_factory(Jacobi::build().on(exec)), + pgm_factory(Pgm::build().on(exec)), mtx(Mtx::create(exec, MPI_COMM_WORLD)) { schwarz = Schwarz::build() .with_local_solver(jacobi_factory) + .with_coarse_solver_factory(pgm_factory) .on(exec) ->generate(mtx); } @@ -57,11 +61,14 @@ class SchwarzFactory : public ::testing::Test { ASSERT_EQ(a->get_size(), b->get_size()); ASSERT_EQ(a->get_parameters().local_solver, b->get_parameters().local_solver); + ASSERT_EQ(a->get_parameters().coarse_solver_factory, + b->get_parameters().coarse_solver_factory); } std::shared_ptr exec; std::unique_ptr schwarz; std::shared_ptr jacobi_factory; + std::shared_ptr pgm_factory; std::shared_ptr mtx; }; @@ -82,6 +89,13 @@ TYPED_TEST(SchwarzFactory, CanSetLocalFactory) } +TYPED_TEST(SchwarzFactory, CanSetCoarseSolverFactory) +{ + ASSERT_EQ(this->schwarz->get_parameters().coarse_solver_factory, + this->pgm_factory); +} + + TYPED_TEST(SchwarzFactory, CanBeCloned) { auto schwarz_clone = clone(this->schwarz); @@ -93,10 +107,14 @@ TYPED_TEST(SchwarzFactory, CanBeCloned) TYPED_TEST(SchwarzFactory, CanBeCopied) { using Jacobi = typename TestFixture::Jacobi; + using Pgm = typename TestFixture::Pgm; using Schwarz = typename TestFixture::Schwarz; using Mtx = typename TestFixture::Mtx; + auto bj = gko::share(Jacobi::build().on(this->exec)); + auto pgm = gko::share(Pgm::build().on(this->exec)); auto copy = Schwarz::build() - .with_local_solver(Jacobi::build()) + .with_local_solver(bj) + .with_coarse_solver_factory(pgm) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -109,11 +127,15 @@ TYPED_TEST(SchwarzFactory, CanBeCopied) TYPED_TEST(SchwarzFactory, CanBeMoved) { using Jacobi = typename TestFixture::Jacobi; + using Pgm = typename TestFixture::Pgm; using Schwarz = typename TestFixture::Schwarz; using Mtx = typename TestFixture::Mtx; auto tmp = clone(this->schwarz); + auto bj = gko::share(Jacobi::build().on(this->exec)); + auto pgm = gko::share(Pgm::build().on(this->exec)); auto copy = Schwarz::build() - .with_local_solver(Jacobi::build()) + .with_local_solver(bj) + .with_coarse_solver_factory(pgm) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -129,6 +151,7 @@ TYPED_TEST(SchwarzFactory, CanBeCleared) ASSERT_EQ(this->schwarz->get_size(), gko::dim<2>(0, 0)); ASSERT_EQ(this->schwarz->get_parameters().local_solver, nullptr); + ASSERT_EQ(this->schwarz->get_parameters().coarse_solver_factory, nullptr); } diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 9578aa27260..ec5506e6477 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace gko { @@ -99,6 +100,12 @@ class Schwarz * local solver. */ bool GKO_FACTORY_PARAMETER_SCALAR(l1_smoother, false); + + /** + * Coarse solver factory. + */ + std::shared_ptr GKO_FACTORY_PARAMETER_SCALAR( + coarse_solver_factory, nullptr); }; GKO_ENABLE_LIN_OP_FACTORY(Schwarz, parameters, Factory); GKO_ENABLE_BUILD_METHOD(Factory); @@ -131,6 +138,7 @@ class Schwarz */ explicit Schwarz(std::shared_ptr exec) : EnableLinOp(std::move(exec)) + {} /** @@ -173,6 +181,8 @@ class Schwarz std::shared_ptr local_solver_; detail::VectorCache cache_; + + std::shared_ptr coarse_solver_; }; From 03f49b5ad5ff006323f411e956c6ccc786358bb3 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Mon, 9 Oct 2023 17:33:16 +0200 Subject: [PATCH 02/21] Additive Schwarz coarse solver --- core/distributed/preconditioner/schwarz.cpp | 43 +++++++++++++++++-- .../distributed/preconditioner/schwarz.cpp | 26 +++++++++-- .../distributed-solver/distributed-solver.cpp | 41 ++++++++++++------ .../distributed/preconditioner/schwarz.hpp | 8 ++++ 4 files changed, 100 insertions(+), 18 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index fad134b317d..03494563188 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -87,11 +87,38 @@ template void Schwarz::apply_dense_impl( const VectorType* dense_b, VectorType* dense_x) const { + using Vector = matrix::Dense; + using dist_vec = experimental::distributed::Vector; auto exec = this->get_executor(); + if (this->local_solver_ != nullptr) { this->local_solver_->apply(gko::detail::get_local(dense_b), gko::detail::get_local(dense_x)); } + + if (this->coarse_solver_ != nullptr && this->galerkin_ops_ != nullptr) { + auto restrict = this->galerkin_ops_->get_restrict_op(); + auto prolong = this->galerkin_ops_->get_prolong_op(); + auto coarse = + as>( + this->galerkin_ops_->get_coarse_op()); + auto comm = coarse->get_communicator(); + + auto cs_ncols = dense_x->get_size()[1]; + auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; + auto cs_global_nrows = coarse->get_size()[0]; + auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); + auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); + auto csol = dist_vec::create(exec, comm, cs_global_size, cs_local_size, + dense_x->get_stride()); + restrict->apply(dense_b, csol); + auto tmp = csol->clone(); + this->coarse_solver_->apply(csol, tmp); + auto one = gko::initialize({0.5}, exec); + auto zero = gko::initialize({0.5}, exec); + prolong->apply(one, tmp, zero, dense_x); + } } @@ -188,9 +215,19 @@ void Schwarz::generate( as>(system_matrix); - if (parameters_.coarse_solver_factory) { - this->coarse_solver_ = as( - share(parameters_.coarse_solver_factory->generate(dist_mat))); + if (parameters_.local_solver) { + this->set_solver(gko::share( + parameters_.local_solver->generate(dist_mat->get_local_matrix()))); + } else { + this->set_solver(parameters_.generated_local_solver); + } + + + if (parameters_.galerkin_ops_factory && parameters_.coarse_solver_factory) { + this->galerkin_ops_ = as( + share(parameters_.galerkin_ops_factory->generate(dist_mat))); + this->coarse_solver_ = parameters_.coarse_solver_factory->generate( + this->galerkin_ops_->get_coarse_op()); } } diff --git a/core/test/mpi/distributed/preconditioner/schwarz.cpp b/core/test/mpi/distributed/preconditioner/schwarz.cpp index c8592223020..7576b82da4d 100644 --- a/core/test/mpi/distributed/preconditioner/schwarz.cpp +++ b/core/test/mpi/distributed/preconditioner/schwarz.cpp @@ -31,6 +31,7 @@ class SchwarzFactory : public ::testing::Test { value_type, local_index_type, global_index_type>; using Jacobi = gko::preconditioner::Jacobi; using Pgm = gko::multigrid::Pgm; + using Cg = gko::solver::Cg; using Mtx = gko::experimental::distributed::Matrix; @@ -39,10 +40,12 @@ class SchwarzFactory : public ::testing::Test { : exec(gko::ReferenceExecutor::create()), jacobi_factory(Jacobi::build().on(exec)), pgm_factory(Pgm::build().on(exec)), + cg_factory(Cg::build().on(exec)), mtx(Mtx::create(exec, MPI_COMM_WORLD)) { schwarz = Schwarz::build() .with_local_solver(jacobi_factory) + .with_galerkin_ops_factory(pgm_factory) .with_coarse_solver_factory(pgm_factory) .on(exec) ->generate(mtx); @@ -61,6 +64,8 @@ class SchwarzFactory : public ::testing::Test { ASSERT_EQ(a->get_size(), b->get_size()); ASSERT_EQ(a->get_parameters().local_solver, b->get_parameters().local_solver); + ASSERT_EQ(a->get_parameters().galerkin_ops_factory, + b->get_parameters().galerkin_ops_factory); ASSERT_EQ(a->get_parameters().coarse_solver_factory, b->get_parameters().coarse_solver_factory); } @@ -69,6 +74,7 @@ class SchwarzFactory : public ::testing::Test { std::unique_ptr schwarz; std::shared_ptr jacobi_factory; std::shared_ptr pgm_factory; + std::shared_ptr cg_factory; std::shared_ptr mtx; }; @@ -89,10 +95,17 @@ TYPED_TEST(SchwarzFactory, CanSetLocalFactory) } +TYPED_TEST(SchwarzFactory, CanSetGalerkinOpsFactory) +{ + ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops_factory, + this->pgm_factory); +} + + TYPED_TEST(SchwarzFactory, CanSetCoarseSolverFactory) { ASSERT_EQ(this->schwarz->get_parameters().coarse_solver_factory, - this->pgm_factory); + this->cg_factory); } @@ -108,13 +121,16 @@ TYPED_TEST(SchwarzFactory, CanBeCopied) { using Jacobi = typename TestFixture::Jacobi; using Pgm = typename TestFixture::Pgm; + using Cg = typename TestFixture::Cg; using Schwarz = typename TestFixture::Schwarz; using Mtx = typename TestFixture::Mtx; auto bj = gko::share(Jacobi::build().on(this->exec)); auto pgm = gko::share(Pgm::build().on(this->exec)); + auto cg = gko::share(Cg::build().on(this->exec)); auto copy = Schwarz::build() .with_local_solver(bj) - .with_coarse_solver_factory(pgm) + .with_galerkin_ops_factory(pgm) + .with_coarse_solver_factory(cg) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -128,14 +144,17 @@ TYPED_TEST(SchwarzFactory, CanBeMoved) { using Jacobi = typename TestFixture::Jacobi; using Pgm = typename TestFixture::Pgm; + using Cg = typename TestFixture::Cg; using Schwarz = typename TestFixture::Schwarz; using Mtx = typename TestFixture::Mtx; auto tmp = clone(this->schwarz); auto bj = gko::share(Jacobi::build().on(this->exec)); auto pgm = gko::share(Pgm::build().on(this->exec)); + auto cg = gko::share(Cg::build().on(this->exec)); auto copy = Schwarz::build() .with_local_solver(bj) - .with_coarse_solver_factory(pgm) + .with_galerkin_ops_factory(pgm) + .with_coarse_solver_factory(cg) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -151,6 +170,7 @@ TYPED_TEST(SchwarzFactory, CanBeCleared) ASSERT_EQ(this->schwarz->get_size(), gko::dim<2>(0, 0)); ASSERT_EQ(this->schwarz->get_parameters().local_solver, nullptr); + ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops_factory, nullptr); ASSERT_EQ(this->schwarz->get_parameters().coarse_solver_factory, nullptr); } diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index 82eaef91ffc..820aae6b309 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -53,6 +53,7 @@ int main(int argc, char* argv[]) using schwarz = gko::experimental::distributed::preconditioner::Schwarz< ValueType, LocalIndexType, GlobalIndexType>; using bj = gko::preconditioner::Jacobi; + using pgm = gko::multigrid::Pgm; // Create an MPI communicator get the rank of the calling process. const auto comm = gko::experimental::mpi::communicator(MPI_COMM_WORLD); @@ -188,20 +189,36 @@ int main(int argc, char* argv[]) // Setup the local block diagonal solver factory. auto local_solver = gko::share(bj::build().on(exec)); + auto coarse_solver = gko::share( + solver::build() + .with_criteria( + gko::stop::Iteration::build().with_max_iters(100).on(exec), + gko::stop::ResidualNorm::build() + .with_reduction_factor(1e-6) + .on(exec)) + .on(exec)); + auto pgm_fac = gko::share(pgm::build().on(exec)); // Setup the stopping criterion and logger const gko::remove_complex reduction_factor{1e-8}; std::shared_ptr> logger = gko::log::Convergence::create(); - auto Ainv = solver::build() - .with_preconditioner( - schwarz::build().with_local_solver(local_solver)) - .with_criteria( - gko::stop::Iteration::build().with_max_iters(num_iters), - gko::stop::ResidualNorm::build() - .with_reduction_factor(reduction_factor)) - .on(exec) - ->generate(A); + auto Ainv = + solver::build() + .with_preconditioner( + schwarz::build() + .with_local_solver_factory(local_solver) + // .with_galerkin_ops_factory(pgm_fac) + // .with_coarse_solver_factory(coarse_solver) + .on(exec)) + .with_criteria( + gko::stop::Iteration::build().with_max_iters(num_iters).on( + exec), + gko::stop::ResidualNorm::build() + .with_reduction_factor(reduction_factor) + .on(exec)) + .on(exec) + ->generate(A); // Add logger to the generated solver to log the iteration count and // residual norm Ainv->add_logger(logger); @@ -219,8 +236,8 @@ int main(int argc, char* argv[]) ValueType t_end = gko::experimental::mpi::get_walltime(); // Get the residual. - auto res_norm = gko::clone(exec->get_master(), - gko::as(logger->get_residual_norm())); + auto res_norm = gko::as(logger->get_residual_norm()); + auto host_res = gko::make_temporary_clone(exec->get_master(), res_norm); // @sect3{Printing Results} // Print the achieved residual norm and timings on rank 0. @@ -228,7 +245,7 @@ int main(int argc, char* argv[]) // clang-format off std::cout << "\nNum rows in matrix: " << num_rows << "\nNum ranks: " << comm.size() - << "\nFinal Res norm: " << res_norm->at(0, 0) + << "\nFinal Res norm: " << *host_res->get_const_values() << "\nIteration count: " << logger->get_num_iterations() << "\nInit time: " << t_init_end - t_init << "\nRead time: " << t_read_setup_end - t_init diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index ec5506e6477..861638f7945 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -101,6 +101,13 @@ class Schwarz */ bool GKO_FACTORY_PARAMETER_SCALAR(l1_smoother, false); + /** + * Operator factory to generate the triplet (prolong_op, coarse_op, + * restrict_op). + */ + std::shared_ptr GKO_FACTORY_PARAMETER_SCALAR( + galerkin_ops_factory, nullptr); + /** * Coarse solver factory. */ @@ -183,6 +190,7 @@ class Schwarz detail::VectorCache cache_; std::shared_ptr coarse_solver_; + std::shared_ptr galerkin_ops_; }; From 8f995477c4b5dfe8528db1767367b012e8badf9f Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Sat, 14 Oct 2023 23:39:11 +0200 Subject: [PATCH 03/21] Move allocs to schwarz generate --- core/distributed/preconditioner/schwarz.cpp | 45 +++++++------- .../distributed-solver/distributed-solver.cpp | 58 +++++++++++++------ .../distributed/preconditioner/schwarz.hpp | 5 +- 3 files changed, 69 insertions(+), 39 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index 03494563188..fc90f4e6ad8 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -99,25 +99,11 @@ void Schwarz::apply_dense_impl( if (this->coarse_solver_ != nullptr && this->galerkin_ops_ != nullptr) { auto restrict = this->galerkin_ops_->get_restrict_op(); auto prolong = this->galerkin_ops_->get_prolong_op(); - auto coarse = - as>( - this->galerkin_ops_->get_coarse_op()); - auto comm = coarse->get_communicator(); - auto cs_ncols = dense_x->get_size()[1]; - auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; - auto cs_global_nrows = coarse->get_size()[0]; - auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); - auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); - auto csol = dist_vec::create(exec, comm, cs_global_size, cs_local_size, - dense_x->get_stride()); - restrict->apply(dense_b, csol); - auto tmp = csol->clone(); - this->coarse_solver_->apply(csol, tmp); - auto one = gko::initialize({0.5}, exec); - auto zero = gko::initialize({0.5}, exec); - prolong->apply(one, tmp, zero, dense_x); + restrict->apply(dense_b, this->csol_); + this->coarse_solver_->apply(this->csol_, this->csol_); + prolong->apply(this->half_.get(), this->csol_.get(), this->half_.get(), + dense_x); } } @@ -157,6 +143,8 @@ template void Schwarz::generate( std::shared_ptr system_matrix) { + using Vector = matrix::Dense; + using dist_vec = experimental::distributed::Vector; if (parameters_.local_solver && parameters_.generated_local_solver) { GKO_INVALID_STATE( "Provided both a generated solver and a solver factory"); @@ -226,8 +214,25 @@ void Schwarz::generate( if (parameters_.galerkin_ops_factory && parameters_.coarse_solver_factory) { this->galerkin_ops_ = as( share(parameters_.galerkin_ops_factory->generate(dist_mat))); - this->coarse_solver_ = parameters_.coarse_solver_factory->generate( - this->galerkin_ops_->get_coarse_op()); + auto coarse = + as>( + this->galerkin_ops_->get_coarse_op()); + auto exec = coarse->get_executor(); + auto comm = coarse->get_communicator(); + this->coarse_solver_ = + parameters_.coarse_solver_factory->generate(coarse); + // TODO: Set correct rhs and stride. + auto cs_ncols = 1; // dense_x->get_size()[1]; + auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; + auto cs_global_nrows = coarse->get_size()[0]; + auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); + auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); + this->csol_ = gko::share(dist_vec::create(exec, comm, cs_global_size, + cs_local_size, + 1 /*dense_x->get_stride()*/)); + // this->temp_ = this->csol->clone(); + this->half_ = gko::share(gko::initialize({0.5}, exec)); } } diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index 820aae6b309..dd2f102b738 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -80,6 +80,7 @@ int main(int argc, char* argv[]) static_cast(argc >= 3 ? std::atoi(argv[2]) : 100); const auto num_iters = static_cast(argc >= 4 ? std::atoi(argv[3]) : 1000); + std::string schw_type = argc >= 5 ? argv[4] : "multi-level"; const std::map(MPI_Comm)>> @@ -194,31 +195,53 @@ int main(int argc, char* argv[]) .with_criteria( gko::stop::Iteration::build().with_max_iters(100).on(exec), gko::stop::ResidualNorm::build() - .with_reduction_factor(1e-6) + .with_reduction_factor(1e-3) .on(exec)) .on(exec)); + auto pgm_fac = gko::share(pgm::build().on(exec)); // Setup the stopping criterion and logger const gko::remove_complex reduction_factor{1e-8}; std::shared_ptr> logger = gko::log::Convergence::create(); - auto Ainv = - solver::build() - .with_preconditioner( - schwarz::build() - .with_local_solver_factory(local_solver) - // .with_galerkin_ops_factory(pgm_fac) - // .with_coarse_solver_factory(coarse_solver) - .on(exec)) - .with_criteria( - gko::stop::Iteration::build().with_max_iters(num_iters).on( - exec), - gko::stop::ResidualNorm::build() - .with_reduction_factor(reduction_factor) - .on(exec)) - .on(exec) - ->generate(A); + std::shared_ptr Ainv{}; + if (schw_type == "multi-level") { + Ainv = + solver::build() + .with_preconditioner( + schwarz::build() + .with_local_solver_factory(local_solver) + .with_galerkin_ops_factory(pgm_fac) + .with_coarse_solver_factory(coarse_solver) + .on(exec)) + .with_criteria( + gko::stop::Iteration::build().with_max_iters(num_iters).on( + exec), + gko::stop::ResidualNorm::build() + .with_reduction_factor(reduction_factor) + .on(exec)) + .on(exec) + ->generate(A); + } else { + schw_type = "one-level"; + Ainv = + solver::build() + .with_preconditioner( + schwarz::build() + .with_local_solver_factory(local_solver) + .with_galerkin_ops_factory(pgm_fac) + .with_coarse_solver_factory(coarse_solver) + .on(exec)) + .with_criteria( + gko::stop::Iteration::build().with_max_iters(num_iters).on( + exec), + gko::stop::ResidualNorm::build() + .with_reduction_factor(reduction_factor) + .on(exec)) + .on(exec) + ->generate(A); + } // Add logger to the generated solver to log the iteration count and // residual norm Ainv->add_logger(logger); @@ -245,6 +268,7 @@ int main(int argc, char* argv[]) // clang-format off std::cout << "\nNum rows in matrix: " << num_rows << "\nNum ranks: " << comm.size() + << "\nPrecond type: " << schw_type << "\nFinal Res norm: " << *host_res->get_const_values() << "\nIteration count: " << logger->get_num_iterations() << "\nInit time: " << t_init_end - t_init diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 861638f7945..5d937bcf384 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -189,8 +189,9 @@ class Schwarz detail::VectorCache cache_; - std::shared_ptr coarse_solver_; - std::shared_ptr galerkin_ops_; + std::shared_ptr coarse_solver_; + std::shared_ptr csol_; + std::shared_ptr half_; }; From 06f52d897a4488b59ba17f03ab0814f737070cfe Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Sun, 15 Oct 2023 11:50:15 +0200 Subject: [PATCH 04/21] Update example --- core/distributed/preconditioner/schwarz.cpp | 3 +-- examples/distributed-solver/distributed-solver.cpp | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index fc90f4e6ad8..2841156c06e 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -102,8 +102,7 @@ void Schwarz::apply_dense_impl( restrict->apply(dense_b, this->csol_); this->coarse_solver_->apply(this->csol_, this->csol_); - prolong->apply(this->half_.get(), this->csol_.get(), this->half_.get(), - dense_x); + prolong->apply(this->half_, this->csol_, this->half_, dense_x); } } diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index dd2f102b738..acda7a52f8c 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -211,7 +211,7 @@ int main(int argc, char* argv[]) solver::build() .with_preconditioner( schwarz::build() - .with_local_solver_factory(local_solver) + .with_local_solver(local_solver) .with_galerkin_ops_factory(pgm_fac) .with_coarse_solver_factory(coarse_solver) .on(exec)) @@ -228,11 +228,7 @@ int main(int argc, char* argv[]) Ainv = solver::build() .with_preconditioner( - schwarz::build() - .with_local_solver_factory(local_solver) - .with_galerkin_ops_factory(pgm_fac) - .with_coarse_solver_factory(coarse_solver) - .on(exec)) + schwarz::build().with_local_solver(local_solver).on(exec)) .with_criteria( gko::stop::Iteration::build().with_max_iters(num_iters).on( exec), From 30ba1b483d1bc0277bb5d35c0cfa502dccc09b9f Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Mon, 12 Aug 2024 16:43:26 +0200 Subject: [PATCH 05/21] Move to deferred factory, fix issues --- core/distributed/preconditioner/schwarz.cpp | 55 +++++++++++-------- .../distributed/preconditioner/schwarz.cpp | 36 +++++------- .../distributed-solver/distributed-solver.cpp | 11 ++-- .../distributed/preconditioner/schwarz.hpp | 10 ++-- test/mpi/preconditioner/schwarz.cpp | 28 ++++++++++ 5 files changed, 84 insertions(+), 56 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index 2841156c06e..f49313e521e 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -97,8 +97,11 @@ void Schwarz::apply_dense_impl( } if (this->coarse_solver_ != nullptr && this->galerkin_ops_ != nullptr) { - auto restrict = this->galerkin_ops_->get_restrict_op(); - auto prolong = this->galerkin_ops_->get_prolong_op(); + auto restrict = as(this->galerkin_ops_) + ->get_restrict_op(); + auto prolong = as(this->galerkin_ops_) + ->get_prolong_op(); + GKO_ASSERT(this->half_ != nullptr); restrict->apply(dense_b, this->csol_); this->coarse_solver_->apply(this->csol_, this->csol_); @@ -210,28 +213,32 @@ void Schwarz::generate( } - if (parameters_.galerkin_ops_factory && parameters_.coarse_solver_factory) { - this->galerkin_ops_ = as( - share(parameters_.galerkin_ops_factory->generate(dist_mat))); - auto coarse = - as>( - this->galerkin_ops_->get_coarse_op()); - auto exec = coarse->get_executor(); - auto comm = coarse->get_communicator(); - this->coarse_solver_ = - parameters_.coarse_solver_factory->generate(coarse); - // TODO: Set correct rhs and stride. - auto cs_ncols = 1; // dense_x->get_size()[1]; - auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; - auto cs_global_nrows = coarse->get_size()[0]; - auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); - auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); - this->csol_ = gko::share(dist_vec::create(exec, comm, cs_global_size, - cs_local_size, - 1 /*dense_x->get_stride()*/)); - // this->temp_ = this->csol->clone(); - this->half_ = gko::share(gko::initialize({0.5}, exec)); + if (parameters_.galerkin_ops && parameters_.coarse_solver) { + this->galerkin_ops_ = + share(parameters_.galerkin_ops->generate(system_matrix)); + if (as(this->galerkin_ops_) + ->get_coarse_op()) { + auto coarse = + as>( + as(this->galerkin_ops_) + ->get_coarse_op()); + auto exec = coarse->get_executor(); + auto comm = coarse->get_communicator(); + this->coarse_solver_ = + share(parameters_.coarse_solver->generate(coarse)); + // TODO: Set correct rhs and stride. + auto cs_ncols = 1; // dense_x->get_size()[1]; + auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; + auto cs_global_nrows = coarse->get_size()[0]; + auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); + auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); + this->csol_ = gko::share( + dist_vec::create(exec, comm, cs_global_size, cs_local_size, + 1 /*dense_x->get_stride()*/)); + // this->temp_ = this->csol->clone(); + this->half_ = gko::share(gko::initialize({0.5}, exec)); + } } } diff --git a/core/test/mpi/distributed/preconditioner/schwarz.cpp b/core/test/mpi/distributed/preconditioner/schwarz.cpp index 7576b82da4d..22300f79ece 100644 --- a/core/test/mpi/distributed/preconditioner/schwarz.cpp +++ b/core/test/mpi/distributed/preconditioner/schwarz.cpp @@ -15,9 +15,6 @@ #include "core/test/utils.hpp" -namespace { - - template class SchwarzFactory : public ::testing::Test { protected: @@ -45,8 +42,8 @@ class SchwarzFactory : public ::testing::Test { { schwarz = Schwarz::build() .with_local_solver(jacobi_factory) - .with_galerkin_ops_factory(pgm_factory) - .with_coarse_solver_factory(pgm_factory) + .with_galerkin_ops(pgm_factory) + .with_coarse_solver(cg_factory) .on(exec) ->generate(mtx); } @@ -64,10 +61,10 @@ class SchwarzFactory : public ::testing::Test { ASSERT_EQ(a->get_size(), b->get_size()); ASSERT_EQ(a->get_parameters().local_solver, b->get_parameters().local_solver); - ASSERT_EQ(a->get_parameters().galerkin_ops_factory, - b->get_parameters().galerkin_ops_factory); - ASSERT_EQ(a->get_parameters().coarse_solver_factory, - b->get_parameters().coarse_solver_factory); + ASSERT_EQ(a->get_parameters().galerkin_ops, + b->get_parameters().galerkin_ops); + ASSERT_EQ(a->get_parameters().coarse_solver, + b->get_parameters().coarse_solver); } std::shared_ptr exec; @@ -97,15 +94,13 @@ TYPED_TEST(SchwarzFactory, CanSetLocalFactory) TYPED_TEST(SchwarzFactory, CanSetGalerkinOpsFactory) { - ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops_factory, - this->pgm_factory); + ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops, this->pgm_factory); } TYPED_TEST(SchwarzFactory, CanSetCoarseSolverFactory) { - ASSERT_EQ(this->schwarz->get_parameters().coarse_solver_factory, - this->cg_factory); + ASSERT_EQ(this->schwarz->get_parameters().coarse_solver, this->cg_factory); } @@ -129,8 +124,8 @@ TYPED_TEST(SchwarzFactory, CanBeCopied) auto cg = gko::share(Cg::build().on(this->exec)); auto copy = Schwarz::build() .with_local_solver(bj) - .with_galerkin_ops_factory(pgm) - .with_coarse_solver_factory(cg) + .with_galerkin_ops(pgm) + .with_coarse_solver(cg) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -153,8 +148,8 @@ TYPED_TEST(SchwarzFactory, CanBeMoved) auto cg = gko::share(Cg::build().on(this->exec)); auto copy = Schwarz::build() .with_local_solver(bj) - .with_galerkin_ops_factory(pgm) - .with_coarse_solver_factory(cg) + .with_galerkin_ops(pgm) + .with_coarse_solver(cg) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -170,8 +165,8 @@ TYPED_TEST(SchwarzFactory, CanBeCleared) ASSERT_EQ(this->schwarz->get_size(), gko::dim<2>(0, 0)); ASSERT_EQ(this->schwarz->get_parameters().local_solver, nullptr); - ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops_factory, nullptr); - ASSERT_EQ(this->schwarz->get_parameters().coarse_solver_factory, nullptr); + ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops, nullptr); + ASSERT_EQ(this->schwarz->get_parameters().coarse_solver, nullptr); } @@ -209,6 +204,3 @@ TYPED_TEST(SchwarzFactory, ApplyUsesInitialGuessAsLocalSolver) ASSERT_EQ(schwarz_with_jacobi->apply_uses_initial_guess(), false); ASSERT_EQ(schwarz_with_cg->apply_uses_initial_guess(), true); } - - -} // namespace diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index acda7a52f8c..427c8a97f4a 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -209,12 +209,11 @@ int main(int argc, char* argv[]) if (schw_type == "multi-level") { Ainv = solver::build() - .with_preconditioner( - schwarz::build() - .with_local_solver(local_solver) - .with_galerkin_ops_factory(pgm_fac) - .with_coarse_solver_factory(coarse_solver) - .on(exec)) + .with_preconditioner(schwarz::build() + .with_local_solver(local_solver) + .with_galerkin_ops(pgm_fac) + .with_coarse_solver(coarse_solver) + .on(exec)) .with_criteria( gko::stop::Iteration::build().with_max_iters(num_iters).on( exec), diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 5d937bcf384..4b2540fc800 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -21,6 +21,7 @@ #include #include #include +#include namespace gko { @@ -105,14 +106,14 @@ class Schwarz * Operator factory to generate the triplet (prolong_op, coarse_op, * restrict_op). */ - std::shared_ptr GKO_FACTORY_PARAMETER_SCALAR( - galerkin_ops_factory, nullptr); + std::shared_ptr GKO_DEFERRED_FACTORY_PARAMETER( + galerkin_ops); /** * Coarse solver factory. */ - std::shared_ptr GKO_FACTORY_PARAMETER_SCALAR( - coarse_solver_factory, nullptr); + std::shared_ptr GKO_DEFERRED_FACTORY_PARAMETER( + coarse_solver); }; GKO_ENABLE_LIN_OP_FACTORY(Schwarz, parameters, Factory); GKO_ENABLE_BUILD_METHOD(Factory); @@ -189,6 +190,7 @@ class Schwarz detail::VectorCache cache_; + std::shared_ptr galerkin_ops_; std::shared_ptr coarse_solver_; std::shared_ptr csol_; std::shared_ptr half_; diff --git a/test/mpi/preconditioner/schwarz.cpp b/test/mpi/preconditioner/schwarz.cpp index 912c9e9a652..91b6f236314 100644 --- a/test/mpi/preconditioner/schwarz.cpp +++ b/test/mpi/preconditioner/schwarz.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,9 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { using solver_type = gko::solver::Bicgstab; using local_prec_type = gko::preconditioner::Jacobi; + using coarse_solver_type = + gko::preconditioner::Jacobi; + using galerkin_ops_type = gko::multigrid::Pgm; using local_matrix_type = gko::matrix::Csr; using non_dist_matrix_type = gko::matrix::Csr; @@ -118,6 +122,8 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { std::shared_ptr non_dist_solver_factory; std::shared_ptr dist_solver_factory; std::shared_ptr local_solver_factory; + std::shared_ptr pgm_factory; + std::shared_ptr coarse_solver_factory; void assert_equal_to_non_distributed_vector( std::shared_ptr dist_vec, @@ -264,6 +270,28 @@ TYPED_TEST(SchwarzPreconditioner, CanApplyPreconditioner) } +TYPED_TEST(SchwarzPreconditioner, CanApplyMultilevelPreconditioner) +{ + using value_type = typename TestFixture::value_type; + using prec = typename TestFixture::dist_prec_type; + + auto precond_factory = prec::build() + .with_local_solver(this->local_solver_factory) + .with_coarse_solver(this->coarse_solver_factory) + .with_galerkin_ops(this->pgm_factory) + .on(this->exec); + auto local_precond = + this->local_solver_factory->generate(this->non_dist_mat); + auto precond = precond_factory->generate(this->dist_mat); + + precond->apply(this->dist_b.get(), this->dist_x.get()); + local_precond->apply(this->non_dist_b.get(), this->non_dist_x.get()); + + this->assert_equal_to_non_distributed_vector(this->dist_x, + this->non_dist_x); +} + + TYPED_TEST(SchwarzPreconditioner, CanAdvancedApplyPreconditioner) { using value_type = typename TestFixture::value_type; From cbb4e5b14346223609d9a96adaa1432d16e5fa64 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Fri, 31 Jan 2025 13:32:36 +0100 Subject: [PATCH 06/21] [config] update config --- core/distributed/preconditioner/schwarz.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index f49313e521e..41c1adeac27 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -59,6 +59,17 @@ Schwarz::parse( if (auto& obj = config.get("l1_smoother")) { params.with_l1_smoother(obj.get_boolean()); } + if (auto& obj = config.get("galerkin_ops")) { + params.with_galerkin_ops( + gko::config::parse_or_get_factory( + obj, context, td_for_child)); + } + if (auto& obj = config.get("coarse_solver")) { + params.with_coarse_solver( + gko::config::parse_or_get_factory( + obj, context, td_for_child)); + } + return params; } From 2505bf54fbf0a109212892af235a6d070e6a8a30 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Fri, 31 Jan 2025 13:55:07 +0100 Subject: [PATCH 07/21] [precond] move csol to a VectorCache --- core/distributed/preconditioner/schwarz.cpp | 31 +++++++++++-------- .../distributed/preconditioner/schwarz.hpp | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index 41c1adeac27..eb4676a2404 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -112,11 +112,26 @@ void Schwarz::apply_dense_impl( ->get_restrict_op(); auto prolong = as(this->galerkin_ops_) ->get_prolong_op(); + auto coarse = + as>( + as(this->galerkin_ops_) + ->get_coarse_op()); GKO_ASSERT(this->half_ != nullptr); - restrict->apply(dense_b, this->csol_); - this->coarse_solver_->apply(this->csol_, this->csol_); - prolong->apply(this->half_, this->csol_, this->half_, dense_x); + // Coarse solve vector cache init + // Should allocare only in the first apply call. + auto cs_ncols = dense_x->get_size()[1]; + auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; + auto cs_global_nrows = coarse->get_size()[0]; + auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); + auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); + auto comm = coarse->get_communicator(); + csol_cache_.init(exec, comm, cs_global_size, cs_local_size); + + restrict->apply(dense_b, csol_cache_.get()); + this->coarse_solver_->apply(csol_cache_.get(), csol_cache_.get()); + prolong->apply(this->half_, csol_cache_.get(), this->half_, dense_x); } } @@ -238,16 +253,6 @@ void Schwarz::generate( auto comm = coarse->get_communicator(); this->coarse_solver_ = share(parameters_.coarse_solver->generate(coarse)); - // TODO: Set correct rhs and stride. - auto cs_ncols = 1; // dense_x->get_size()[1]; - auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; - auto cs_global_nrows = coarse->get_size()[0]; - auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); - auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); - this->csol_ = gko::share( - dist_vec::create(exec, comm, cs_global_size, cs_local_size, - 1 /*dense_x->get_stride()*/)); - // this->temp_ = this->csol->clone(); this->half_ = gko::share(gko::initialize({0.5}, exec)); } } diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 4b2540fc800..baa6d426eb1 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -189,10 +189,10 @@ class Schwarz std::shared_ptr local_solver_; detail::VectorCache cache_; + detail::VectorCache csol_cache_; std::shared_ptr galerkin_ops_; std::shared_ptr coarse_solver_; - std::shared_ptr csol_; std::shared_ptr half_; }; From 19c26085c0a12709f8dff3cf2798d5e4aabb0d94 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Fri, 14 Feb 2025 10:24:58 +0100 Subject: [PATCH 08/21] update name and doc --- core/distributed/preconditioner/schwarz.cpp | 33 +++++++++++-------- .../distributed/preconditioner/schwarz.cpp | 14 ++++---- .../distributed-solver/distributed-solver.cpp | 4 +-- .../distributed/preconditioner/schwarz.hpp | 22 ++++++++++--- test/mpi/preconditioner/schwarz.cpp | 4 +-- 5 files changed, 47 insertions(+), 30 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index eb4676a2404..ba403ac74d7 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -59,8 +59,8 @@ Schwarz::parse( if (auto& obj = config.get("l1_smoother")) { params.with_l1_smoother(obj.get_boolean()); } - if (auto& obj = config.get("galerkin_ops")) { - params.with_galerkin_ops( + if (auto& obj = config.get("coarse_level")) { + params.with_coarse_level( gko::config::parse_or_get_factory( obj, context, td_for_child)); } @@ -107,17 +107,16 @@ void Schwarz::apply_dense_impl( gko::detail::get_local(dense_x)); } - if (this->coarse_solver_ != nullptr && this->galerkin_ops_ != nullptr) { - auto restrict = as(this->galerkin_ops_) + if (this->coarse_solver_ != nullptr && this->coarse_level_ != nullptr) { + auto restrict = as(this->coarse_level_) ->get_restrict_op(); - auto prolong = as(this->galerkin_ops_) + auto prolong = as(this->coarse_level_) ->get_prolong_op(); auto coarse = as>( - as(this->galerkin_ops_) + as(this->coarse_level_) ->get_coarse_op()); - GKO_ASSERT(this->half_ != nullptr); // Coarse solve vector cache init // Should allocare only in the first apply call. @@ -131,7 +130,8 @@ void Schwarz::apply_dense_impl( restrict->apply(dense_b, csol_cache_.get()); this->coarse_solver_->apply(csol_cache_.get(), csol_cache_.get()); - prolong->apply(this->half_, csol_cache_.get(), this->half_, dense_x); + prolong->apply(this->coarse_weight_, csol_cache_.get(), + this->local_weight_, dense_x); } } @@ -230,6 +230,10 @@ void Schwarz::generate( auto dist_mat = as>(system_matrix); + this->local_weight_ = gko::initialize>( + {ValueType{1} - parameters_.coarse_weight}, this->get_executor()); + this->coarse_weight_ = gko::initialize>( + {parameters_.coarse_weight}, this->get_executor()); if (parameters_.local_solver) { this->set_solver(gko::share( @@ -239,21 +243,22 @@ void Schwarz::generate( } - if (parameters_.galerkin_ops && parameters_.coarse_solver) { - this->galerkin_ops_ = - share(parameters_.galerkin_ops->generate(system_matrix)); - if (as(this->galerkin_ops_) + if (parameters_.coarse_level && parameters_.coarse_solver) { + this->coarse_level_ = + share(parameters_.coarse_level->generate(system_matrix)); + if (as(this->coarse_level_) ->get_coarse_op()) { auto coarse = as>( - as(this->galerkin_ops_) + as(this->coarse_level_) ->get_coarse_op()); auto exec = coarse->get_executor(); auto comm = coarse->get_communicator(); this->coarse_solver_ = share(parameters_.coarse_solver->generate(coarse)); - this->half_ = gko::share(gko::initialize({0.5}, exec)); + } else { + GKO_NOT_SUPPORTED(this->coarse_level_); } } } diff --git a/core/test/mpi/distributed/preconditioner/schwarz.cpp b/core/test/mpi/distributed/preconditioner/schwarz.cpp index 22300f79ece..ad754564f9e 100644 --- a/core/test/mpi/distributed/preconditioner/schwarz.cpp +++ b/core/test/mpi/distributed/preconditioner/schwarz.cpp @@ -42,7 +42,7 @@ class SchwarzFactory : public ::testing::Test { { schwarz = Schwarz::build() .with_local_solver(jacobi_factory) - .with_galerkin_ops(pgm_factory) + .with_coarse_level(pgm_factory) .with_coarse_solver(cg_factory) .on(exec) ->generate(mtx); @@ -61,8 +61,8 @@ class SchwarzFactory : public ::testing::Test { ASSERT_EQ(a->get_size(), b->get_size()); ASSERT_EQ(a->get_parameters().local_solver, b->get_parameters().local_solver); - ASSERT_EQ(a->get_parameters().galerkin_ops, - b->get_parameters().galerkin_ops); + ASSERT_EQ(a->get_parameters().coarse_level, + b->get_parameters().coarse_level); ASSERT_EQ(a->get_parameters().coarse_solver, b->get_parameters().coarse_solver); } @@ -94,7 +94,7 @@ TYPED_TEST(SchwarzFactory, CanSetLocalFactory) TYPED_TEST(SchwarzFactory, CanSetGalerkinOpsFactory) { - ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops, this->pgm_factory); + ASSERT_EQ(this->schwarz->get_parameters().coarse_level, this->pgm_factory); } @@ -124,7 +124,7 @@ TYPED_TEST(SchwarzFactory, CanBeCopied) auto cg = gko::share(Cg::build().on(this->exec)); auto copy = Schwarz::build() .with_local_solver(bj) - .with_galerkin_ops(pgm) + .with_coarse_level(pgm) .with_coarse_solver(cg) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -148,7 +148,7 @@ TYPED_TEST(SchwarzFactory, CanBeMoved) auto cg = gko::share(Cg::build().on(this->exec)); auto copy = Schwarz::build() .with_local_solver(bj) - .with_galerkin_ops(pgm) + .with_coarse_level(pgm) .with_coarse_solver(cg) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -165,7 +165,7 @@ TYPED_TEST(SchwarzFactory, CanBeCleared) ASSERT_EQ(this->schwarz->get_size(), gko::dim<2>(0, 0)); ASSERT_EQ(this->schwarz->get_parameters().local_solver, nullptr); - ASSERT_EQ(this->schwarz->get_parameters().galerkin_ops, nullptr); + ASSERT_EQ(this->schwarz->get_parameters().coarse_level, nullptr); ASSERT_EQ(this->schwarz->get_parameters().coarse_solver, nullptr); } diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index 427c8a97f4a..a85edf8de6c 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors +// SPDX-FileCopyrightText: 2017 - 2025 The Ginkgo authors // // SPDX-License-Identifier: BSD-3-Clause @@ -211,7 +211,7 @@ int main(int argc, char* argv[]) solver::build() .with_preconditioner(schwarz::build() .with_local_solver(local_solver) - .with_galerkin_ops(pgm_fac) + .with_coarse_level(pgm_fac) .with_coarse_solver(coarse_solver) .on(exec)) .with_criteria( diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index baa6d426eb1..6a2fbc309a6 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -103,14 +103,25 @@ class Schwarz bool GKO_FACTORY_PARAMETER_SCALAR(l1_smoother, false); /** - * Operator factory to generate the triplet (prolong_op, coarse_op, - * restrict_op). + * Operator factory list to generate the triplet (prolong_op, coarse_op, + * restrict_op), `A_c = R * A * P` + * + * Note: The linop factory must generate the triplet (R, A_c, P). For + * example, any coarse level generator from multigrid::MultigridLevel + * can be used. */ std::shared_ptr GKO_DEFERRED_FACTORY_PARAMETER( - galerkin_ops); + coarse_level); + + /** + * Weighting for the coarse solution + */ + ValueType GKO_FACTORY_PARAMETER(coarse_weight, value_type{0.5}); /** * Coarse solver factory. + * + * TODO: Set default */ std::shared_ptr GKO_DEFERRED_FACTORY_PARAMETER( coarse_solver); @@ -191,9 +202,10 @@ class Schwarz detail::VectorCache cache_; detail::VectorCache csol_cache_; - std::shared_ptr galerkin_ops_; + std::shared_ptr coarse_level_; std::shared_ptr coarse_solver_; - std::shared_ptr half_; + std::shared_ptr> coarse_weight_; + std::shared_ptr> local_weight_; }; diff --git a/test/mpi/preconditioner/schwarz.cpp b/test/mpi/preconditioner/schwarz.cpp index 91b6f236314..ac88046b114 100644 --- a/test/mpi/preconditioner/schwarz.cpp +++ b/test/mpi/preconditioner/schwarz.cpp @@ -55,7 +55,7 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { gko::preconditioner::Jacobi; using coarse_solver_type = gko::preconditioner::Jacobi; - using galerkin_ops_type = gko::multigrid::Pgm; + using coarse_level_type = gko::multigrid::Pgm; using local_matrix_type = gko::matrix::Csr; using non_dist_matrix_type = gko::matrix::Csr; @@ -278,7 +278,7 @@ TYPED_TEST(SchwarzPreconditioner, CanApplyMultilevelPreconditioner) auto precond_factory = prec::build() .with_local_solver(this->local_solver_factory) .with_coarse_solver(this->coarse_solver_factory) - .with_galerkin_ops(this->pgm_factory) + .with_coarse_level(this->pgm_factory) .on(this->exec); auto local_precond = this->local_solver_factory->generate(this->non_dist_mat); From fe0994441836374690f51c597d8a5aae0c1d5b27 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Tue, 18 Feb 2025 14:54:11 +0100 Subject: [PATCH 09/21] add logic for multiplic/additive --- core/distributed/preconditioner/schwarz.cpp | 93 +++++++++++-------- .../distributed/preconditioner/schwarz.cpp | 42 ++++++++- .../distributed-solver/distributed-solver.cpp | 30 ++++-- .../distributed/preconditioner/schwarz.hpp | 33 ++++++- 4 files changed, 146 insertions(+), 52 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index ba403ac74d7..d2e76d46dfb 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -107,31 +107,41 @@ void Schwarz::apply_dense_impl( gko::detail::get_local(dense_x)); } - if (this->coarse_solver_ != nullptr && this->coarse_level_ != nullptr) { - auto restrict = as(this->coarse_level_) - ->get_restrict_op(); - auto prolong = as(this->coarse_level_) - ->get_prolong_op(); - auto coarse = - as>( + if (parameters_.coarse_correction.mode == + coarse_correction_mode::additive) { + if (this->coarse_solver_ != nullptr && this->coarse_level_ != nullptr) { + auto restrict = as(this->coarse_level_) - ->get_coarse_op()); - - // Coarse solve vector cache init - // Should allocare only in the first apply call. - auto cs_ncols = dense_x->get_size()[1]; - auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; - auto cs_global_nrows = coarse->get_size()[0]; - auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); - auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); - auto comm = coarse->get_communicator(); - csol_cache_.init(exec, comm, cs_global_size, cs_local_size); - - restrict->apply(dense_b, csol_cache_.get()); - this->coarse_solver_->apply(csol_cache_.get(), csol_cache_.get()); - prolong->apply(this->coarse_weight_, csol_cache_.get(), - this->local_weight_, dense_x); + ->get_restrict_op(); + auto prolong = + as(this->coarse_level_) + ->get_prolong_op(); + auto coarse = + as>( + as(this->coarse_level_) + ->get_coarse_op()); + + // Coarse solve vector cache init + // Should allocare only in the first apply call. + auto cs_ncols = dense_x->get_size()[1]; + auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; + auto cs_global_nrows = coarse->get_size()[0]; + auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); + auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); + auto comm = coarse->get_communicator(); + csol_cache_.init(exec, comm, cs_global_size, cs_local_size); + + restrict->apply(dense_b, csol_cache_.get()); + this->coarse_solver_->apply(csol_cache_.get(), csol_cache_.get()); + prolong->apply(this->coarse_weight_, csol_cache_.get(), + this->local_weight_, dense_x); + } + } else if (parameters_.coarse_correction.mode == + coarse_correction_mode::multiplicative) { + GKO_NOT_IMPLEMENTED; + } else { + GKO_NOT_IMPLEMENTED; } } @@ -227,13 +237,22 @@ void Schwarz::generate( gko::share(parameters_.local_solver->generate(local_matrix))); } + if ((parameters_.coarse_level && !parameters_.coarse_solver) || + (!parameters_.coarse_level && parameters_.coarse_solver)) { + GKO_INVALID_STATE( + "Requires both coarse solver and coarse level to be set."); + } auto dist_mat = as>(system_matrix); + // this->local_weight_ = gko::initialize>( + // {ValueType{1} - parameters_.coarse_weight}, this->get_executor()); + // this->coarse_weight_ = gko::initialize>( + // {parameters_.coarse_weight}, this->get_executor()); this->local_weight_ = gko::initialize>( - {ValueType{1} - parameters_.coarse_weight}, this->get_executor()); + {ValueType{1}}, this->get_executor()); this->coarse_weight_ = gko::initialize>( - {parameters_.coarse_weight}, this->get_executor()); + {ValueType{1}}, this->get_executor()); if (parameters_.local_solver) { this->set_solver(gko::share( @@ -246,17 +265,17 @@ void Schwarz::generate( if (parameters_.coarse_level && parameters_.coarse_solver) { this->coarse_level_ = share(parameters_.coarse_level->generate(system_matrix)); - if (as(this->coarse_level_) - ->get_coarse_op()) { - auto coarse = - as>( - as(this->coarse_level_) - ->get_coarse_op()); - auto exec = coarse->get_executor(); - auto comm = coarse->get_communicator(); - this->coarse_solver_ = - share(parameters_.coarse_solver->generate(coarse)); + if (as(this->coarse_level_)) { + auto coarse = as(this->coarse_level_) + ->get_coarse_op(); + // Cannot guarantee that coarse op is not nullptr + if (coarse != nullptr) { + this->coarse_solver_ = + share(parameters_.coarse_solver->generate( + as>( + coarse))); + } } else { GKO_NOT_SUPPORTED(this->coarse_level_); } diff --git a/core/test/mpi/distributed/preconditioner/schwarz.cpp b/core/test/mpi/distributed/preconditioner/schwarz.cpp index ad754564f9e..22200601b91 100644 --- a/core/test/mpi/distributed/preconditioner/schwarz.cpp +++ b/core/test/mpi/distributed/preconditioner/schwarz.cpp @@ -65,6 +65,10 @@ class SchwarzFactory : public ::testing::Test { b->get_parameters().coarse_level); ASSERT_EQ(a->get_parameters().coarse_solver, b->get_parameters().coarse_solver); + ASSERT_EQ(a->get_parameters().coarse_correction.mode, + b->get_parameters().coarse_correction.mode); + ASSERT_EQ(a->get_parameters().coarse_correction.weight, + b->get_parameters().coarse_correction.weight); } std::shared_ptr exec; @@ -92,7 +96,7 @@ TYPED_TEST(SchwarzFactory, CanSetLocalFactory) } -TYPED_TEST(SchwarzFactory, CanSetGalerkinOpsFactory) +TYPED_TEST(SchwarzFactory, CanSetCoarseLevelFactory) { ASSERT_EQ(this->schwarz->get_parameters().coarse_level, this->pgm_factory); } @@ -104,6 +108,42 @@ TYPED_TEST(SchwarzFactory, CanSetCoarseSolverFactory) } +TYPED_TEST(SchwarzFactory, CanSetAdditiveCorrectionType) +{ + using vtype = typename TestFixture::value_type; + + ASSERT_EQ(this->schwarz->get_parameters().coarse_correction.mode, + gko::experimental::distributed::preconditioner:: + coarse_correction_mode::additive); + ASSERT_EQ(this->schwarz->get_parameters().coarse_correction.weight, 1.0); +} + + +TYPED_TEST(SchwarzFactory, CanSetMultiplicativeCorrectionType) +{ + using Schwarz = typename TestFixture::Schwarz; + using vtype = typename TestFixture::value_type; + auto schwarz = + Schwarz::build() + .with_local_solver(this->jacobi_factory) + .with_coarse_correction( + gko::experimental::distributed::preconditioner:: + coarse_correction_type( + gko::experimental::distributed::preconditioner:: + coarse_correction_mode::multiplicative, + 0.4)) + .with_coarse_level(this->pgm_factory) + .with_coarse_solver(this->cg_factory) + .on(this->exec) + ->generate(this->mtx); + + ASSERT_EQ(schwarz->get_parameters().coarse_correction.mode, + gko::experimental::distributed::preconditioner:: + coarse_correction_mode::multiplicative); + ASSERT_EQ(schwarz->get_parameters().coarse_correction.weight, 0.4); +} + + TYPED_TEST(SchwarzFactory, CanBeCloned) { auto schwarz_clone = clone(this->schwarz); diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index a85edf8de6c..10a969add23 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -64,10 +64,12 @@ int main(int argc, char* argv[]) // - The executor, defaults to reference. // - The number of grid points, defaults to 100. // - The number of iterations, defaults to 1000. + // - One-level or two-level preconditioner, defaults to one-level. if (argc == 2 && (std::string(argv[1]) == "--help")) { if (rank == 0) { std::cerr << "Usage: " << argv[0] << " [executor] [num_grid_points] [num_iterations] " + "[schwarz_prec_type] " << std::endl; } std::exit(-1); @@ -80,7 +82,7 @@ int main(int argc, char* argv[]) static_cast(argc >= 3 ? std::atoi(argv[2]) : 100); const auto num_iters = static_cast(argc >= 4 ? std::atoi(argv[3]) : 1000); - std::string schw_type = argc >= 5 ? argv[4] : "multi-level"; + std::string schw_type = argc >= 5 ? argv[4] : "one-level"; const std::map(MPI_Comm)>> @@ -190,12 +192,18 @@ int main(int argc, char* argv[]) // Setup the local block diagonal solver factory. auto local_solver = gko::share(bj::build().on(exec)); + // Setup the coarse solver. If it is more accurate, then the outer + // iterations will reduce, but the cost of the coarse solve increases. + // The coarse solver can in turn have another Schwarz preconditioner if + // needed. auto coarse_solver = gko::share( solver::build() + .with_preconditioner( + schwarz::build().with_local_solver(local_solver).on(exec)) .with_criteria( - gko::stop::Iteration::build().with_max_iters(100).on(exec), + gko::stop::Iteration::build().with_max_iters(1000u).on(exec), gko::stop::ResidualNorm::build() - .with_reduction_factor(1e-3) + .with_reduction_factor(1e-7) .on(exec)) .on(exec)); @@ -206,14 +214,18 @@ int main(int argc, char* argv[]) std::shared_ptr> logger = gko::log::Convergence::create(); std::shared_ptr Ainv{}; - if (schw_type == "multi-level") { + if (schw_type == "two-level") { Ainv = solver::build() - .with_preconditioner(schwarz::build() - .with_local_solver(local_solver) - .with_coarse_level(pgm_fac) - .with_coarse_solver(coarse_solver) - .on(exec)) + .with_preconditioner( + schwarz::build() + .with_local_solver(local_solver) + .with_coarse_level(pgm_fac) + .with_coarse_correction( + gko::experimental::distributed::preconditioner:: + coarse_correction_mode::multiplicative) + .with_coarse_solver(coarse_solver) + .on(exec)) .with_criteria( gko::stop::Iteration::build().with_max_iters(num_iters).on( exec), diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 6a2fbc309a6..3371985e119 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -34,6 +34,26 @@ namespace distributed { */ namespace preconditioner { +/** + * The mode for the multi-level coarse correction + * + * - additive: Additively incorporate the coarse level correction. + * - multiplicative: Multiplicatively incorporate the coarse level correction. + * + * See Domain Decomposition, Smith, Bjorstad, Gropp, Cambridge University Press, + * 1996. + */ +enum class coarse_correction_mode { additive, multiplicative }; + +struct coarse_correction_type { + explicit coarse_correction_type(coarse_correction_mode m, double w) + : mode(m), weight(w) + {} + + coarse_correction_mode mode; + double weight; +}; + /** * A Schwarz preconditioner is a simple domain decomposition preconditioner that @@ -113,11 +133,6 @@ class Schwarz std::shared_ptr GKO_DEFERRED_FACTORY_PARAMETER( coarse_level); - /** - * Weighting for the coarse solution - */ - ValueType GKO_FACTORY_PARAMETER(coarse_weight, value_type{0.5}); - /** * Coarse solver factory. * @@ -125,6 +140,14 @@ class Schwarz */ std::shared_ptr GKO_DEFERRED_FACTORY_PARAMETER( coarse_solver); + + /** + * Coarse correction type: Additive or multiplicative and their + * respective weights. + */ + coarse_correction_type GKO_FACTORY_PARAMETER_SCALAR( + coarse_correction, + coarse_correction_type(coarse_correction_mode::additive, 1.0)); }; GKO_ENABLE_LIN_OP_FACTORY(Schwarz, parameters, Factory); GKO_ENABLE_BUILD_METHOD(Factory); From 713d009a311eb8266b7c980105ed18a564de344e Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Sun, 23 Feb 2025 15:36:42 +0100 Subject: [PATCH 10/21] use only additive with weights --- core/distributed/preconditioner/schwarz.cpp | 89 ++++++++++--------- .../distributed/preconditioner/schwarz.cpp | 44 ++------- .../distributed-solver/distributed-solver.cpp | 29 +++--- .../distributed/preconditioner/schwarz.hpp | 54 +++++------ 4 files changed, 93 insertions(+), 123 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index d2e76d46dfb..e4974fc5e78 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -102,46 +102,43 @@ void Schwarz::apply_dense_impl( using dist_vec = experimental::distributed::Vector; auto exec = this->get_executor(); - if (this->local_solver_ != nullptr) { + // Two-level + if (this->coarse_solver_ != nullptr && this->coarse_level_ != nullptr) { this->local_solver_->apply(gko::detail::get_local(dense_b), gko::detail::get_local(dense_x)); - } - - if (parameters_.coarse_correction.mode == - coarse_correction_mode::additive) { - if (this->coarse_solver_ != nullptr && this->coarse_level_ != nullptr) { - auto restrict = + auto restrict_op = + as(this->coarse_level_) + ->get_restrict_op(); + auto prolong_op = + as(this->coarse_level_) + ->get_prolong_op(); + auto coarse_op = + as>( as(this->coarse_level_) - ->get_restrict_op(); - auto prolong = - as(this->coarse_level_) - ->get_prolong_op(); - auto coarse = - as>( - as(this->coarse_level_) - ->get_coarse_op()); - - // Coarse solve vector cache init - // Should allocare only in the first apply call. - auto cs_ncols = dense_x->get_size()[1]; - auto cs_local_nrows = coarse->get_local_matrix()->get_size()[0]; - auto cs_global_nrows = coarse->get_size()[0]; - auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); - auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); - auto comm = coarse->get_communicator(); - csol_cache_.init(exec, comm, cs_global_size, cs_local_size); - - restrict->apply(dense_b, csol_cache_.get()); - this->coarse_solver_->apply(csol_cache_.get(), csol_cache_.get()); - prolong->apply(this->coarse_weight_, csol_cache_.get(), - this->local_weight_, dense_x); - } - } else if (parameters_.coarse_correction.mode == - coarse_correction_mode::multiplicative) { - GKO_NOT_IMPLEMENTED; + ->get_coarse_op()); + + // Coarse solve vector cache init + // Should allocate only in the first apply call. + auto cs_ncols = dense_x->get_size()[1]; + auto cs_local_nrows = coarse_op->get_local_matrix()->get_size()[0]; + auto cs_global_nrows = coarse_op->get_size()[0]; + auto cs_local_size = dim<2>(cs_local_nrows, cs_ncols); + auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); + auto comm = coarse_op->get_communicator(); + csol_cache_.init(exec, comm, cs_global_size, cs_local_size); + + // Additive apply of coarse correction + restrict_op->apply(dense_b, csol_cache_.get()); + this->coarse_solver_->apply(csol_cache_.get(), csol_cache_.get()); + prolong_op->apply(this->coarse_weight_, csol_cache_.get(), + this->local_weight_, dense_x); } else { - GKO_NOT_IMPLEMENTED; + // One-level + if (this->local_solver_ != nullptr) { + this->local_solver_->apply(gko::detail::get_local(dense_b), + gko::detail::get_local(dense_x)); + } } } @@ -245,14 +242,18 @@ void Schwarz::generate( auto dist_mat = as>(system_matrix); - // this->local_weight_ = gko::initialize>( - // {ValueType{1} - parameters_.coarse_weight}, this->get_executor()); - // this->coarse_weight_ = gko::initialize>( - // {parameters_.coarse_weight}, this->get_executor()); - this->local_weight_ = gko::initialize>( - {ValueType{1}}, this->get_executor()); - this->coarse_weight_ = gko::initialize>( - {ValueType{1}}, this->get_executor()); + if (parameters_.coarse_weight > 0) { + this->local_weight_ = gko::initialize>( + {ValueType{1} - ValueType{parameters_.coarse_weight}}, + this->get_executor()); + this->coarse_weight_ = gko::initialize>( + {ValueType{parameters_.coarse_weight}}, this->get_executor()); + } else { + this->local_weight_ = gko::initialize>( + {ValueType{1.0}}, this->get_executor()); + this->coarse_weight_ = gko::initialize>( + {ValueType{1.0}}, this->get_executor()); + } if (parameters_.local_solver) { this->set_solver(gko::share( diff --git a/core/test/mpi/distributed/preconditioner/schwarz.cpp b/core/test/mpi/distributed/preconditioner/schwarz.cpp index 22200601b91..4bbcc65e41b 100644 --- a/core/test/mpi/distributed/preconditioner/schwarz.cpp +++ b/core/test/mpi/distributed/preconditioner/schwarz.cpp @@ -44,6 +44,7 @@ class SchwarzFactory : public ::testing::Test { .with_local_solver(jacobi_factory) .with_coarse_level(pgm_factory) .with_coarse_solver(cg_factory) + .with_coarse_weight(0.4) .on(exec) ->generate(mtx); } @@ -65,10 +66,8 @@ class SchwarzFactory : public ::testing::Test { b->get_parameters().coarse_level); ASSERT_EQ(a->get_parameters().coarse_solver, b->get_parameters().coarse_solver); - ASSERT_EQ(a->get_parameters().coarse_correction.mode, - b->get_parameters().coarse_correction.mode); - ASSERT_EQ(a->get_parameters().coarse_correction.weight, - b->get_parameters().coarse_correction.weight); + ASSERT_EQ(a->get_parameters().coarse_weight, + b->get_parameters().coarse_weight); } std::shared_ptr exec; @@ -108,39 +107,9 @@ TYPED_TEST(SchwarzFactory, CanSetCoarseSolverFactory) } -TYPED_TEST(SchwarzFactory, CanSetAdditiveCorrectionType) +TYPED_TEST(SchwarzFactory, CanSetCoarseWeight) { - using vtype = typename TestFixture::value_type; - - ASSERT_EQ(this->schwarz->get_parameters().coarse_correction.mode, - gko::experimental::distributed::preconditioner:: - coarse_correction_mode::additive); - ASSERT_EQ(this->schwarz->get_parameters().coarse_correction.weight, 1.0); -} - - -TYPED_TEST(SchwarzFactory, CanSetMultiplicativeCorrectionType) -{ - using Schwarz = typename TestFixture::Schwarz; - using vtype = typename TestFixture::value_type; - auto schwarz = - Schwarz::build() - .with_local_solver(this->jacobi_factory) - .with_coarse_correction( - gko::experimental::distributed::preconditioner:: - coarse_correction_type( - gko::experimental::distributed::preconditioner:: - coarse_correction_mode::multiplicative, - 0.4)) - .with_coarse_level(this->pgm_factory) - .with_coarse_solver(this->cg_factory) - .on(this->exec) - ->generate(this->mtx); - - ASSERT_EQ(schwarz->get_parameters().coarse_correction.mode, - gko::experimental::distributed::preconditioner:: - coarse_correction_mode::multiplicative); - ASSERT_EQ(schwarz->get_parameters().coarse_correction.weight, 0.4); + ASSERT_EQ(this->schwarz->get_parameters().coarse_weight, 0.4); } @@ -166,6 +135,7 @@ TYPED_TEST(SchwarzFactory, CanBeCopied) .with_local_solver(bj) .with_coarse_level(pgm) .with_coarse_solver(cg) + .with_coarse_weight(0.4) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -190,6 +160,7 @@ TYPED_TEST(SchwarzFactory, CanBeMoved) .with_local_solver(bj) .with_coarse_level(pgm) .with_coarse_solver(cg) + .with_coarse_weight(0.4) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -207,6 +178,7 @@ TYPED_TEST(SchwarzFactory, CanBeCleared) ASSERT_EQ(this->schwarz->get_parameters().local_solver, nullptr); ASSERT_EQ(this->schwarz->get_parameters().coarse_level, nullptr); ASSERT_EQ(this->schwarz->get_parameters().coarse_solver, nullptr); + ASSERT_EQ(this->schwarz->get_parameters().coarse_weight, -1); } diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index 10a969add23..577e8279664 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -217,15 +217,11 @@ int main(int argc, char* argv[]) if (schw_type == "two-level") { Ainv = solver::build() - .with_preconditioner( - schwarz::build() - .with_local_solver(local_solver) - .with_coarse_level(pgm_fac) - .with_coarse_correction( - gko::experimental::distributed::preconditioner:: - coarse_correction_mode::multiplicative) - .with_coarse_solver(coarse_solver) - .on(exec)) + .with_preconditioner(schwarz::build() + .with_local_solver(local_solver) + .with_coarse_level(pgm_fac) + .with_coarse_solver(coarse_solver) + .on(exec)) .with_criteria( gko::stop::Iteration::build().with_max_iters(num_iters).on( exec), @@ -234,8 +230,7 @@ int main(int argc, char* argv[]) .on(exec)) .on(exec) ->generate(A); - } else { - schw_type = "one-level"; + } else if (schw_type == "one-level") { Ainv = solver::build() .with_preconditioner( @@ -248,6 +243,18 @@ int main(int argc, char* argv[]) .on(exec)) .on(exec) ->generate(A); + } else { + schw_type = "no-precond"; + Ainv = + solver::build() + .with_criteria( + gko::stop::Iteration::build().with_max_iters(num_iters).on( + exec), + gko::stop::ResidualNorm::build() + .with_reduction_factor(reduction_factor) + .on(exec)) + .on(exec) + ->generate(A); } // Add logger to the generated solver to log the iteration count and // residual norm diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 3371985e119..2017f2d61a5 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -34,26 +34,6 @@ namespace distributed { */ namespace preconditioner { -/** - * The mode for the multi-level coarse correction - * - * - additive: Additively incorporate the coarse level correction. - * - multiplicative: Multiplicatively incorporate the coarse level correction. - * - * See Domain Decomposition, Smith, Bjorstad, Gropp, Cambridge University Press, - * 1996. - */ -enum class coarse_correction_mode { additive, multiplicative }; - -struct coarse_correction_type { - explicit coarse_correction_type(coarse_correction_mode m, double w) - : mode(m), weight(w) - {} - - coarse_correction_mode mode; - double weight; -}; - /** * A Schwarz preconditioner is a simple domain decomposition preconditioner that @@ -63,7 +43,14 @@ struct coarse_correction_type { * See Iterative Methods for Sparse Linear Systems (Y. Saad) for a general * treatment and variations of the method. * - * @note Currently overlap and coarse grid correction are not supported (TODO). + * A Two-level variant is also available. To enable two-level preconditioning, + * you need to specify a LinOpFactory that can generate a + * multigrid::MultigridLevel. Currently, only multiplicative coarse correction + * is supported. + * - See Smith, Bjorstad, Gropp, Domain Decomposition, 1996, Cambridge + * University Press. + * + * @note Currently overlap is not supported (TODO). * * @tparam ValueType precision of matrix element * @tparam LocalIndexType local integer type of the matrix @@ -122,6 +109,15 @@ class Schwarz */ bool GKO_FACTORY_PARAMETER_SCALAR(l1_smoother, false); + /** + * Coarse weighting. + * + * By default the coarse and the local solutions are added together. A + * weighting can instead be provided if the coarse solution tends to + * over-correct. + */ + double GKO_FACTORY_PARAMETER_SCALAR(coarse_weight, double{-1.0}); + /** * Operator factory list to generate the triplet (prolong_op, coarse_op, * restrict_op), `A_c = R * A * P` @@ -135,19 +131,9 @@ class Schwarz /** * Coarse solver factory. - * - * TODO: Set default */ std::shared_ptr GKO_DEFERRED_FACTORY_PARAMETER( coarse_solver); - - /** - * Coarse correction type: Additive or multiplicative and their - * respective weights. - */ - coarse_correction_type GKO_FACTORY_PARAMETER_SCALAR( - coarse_correction, - coarse_correction_type(coarse_correction_mode::additive, 1.0)); }; GKO_ENABLE_LIN_OP_FACTORY(Schwarz, parameters, Factory); GKO_ENABLE_BUILD_METHOD(Factory); @@ -194,7 +180,8 @@ class Schwarz std::shared_ptr system_matrix) : EnableLinOp(factory->get_executor(), gko::transpose(system_matrix->get_size())), - parameters_{factory->get_parameters()} + parameters_{factory->get_parameters()}, + system_matrix_{system_matrix} { this->generate(system_matrix); } @@ -221,8 +208,11 @@ class Schwarz void set_solver(std::shared_ptr new_solver); std::shared_ptr local_solver_; + std::shared_ptr system_matrix_; + // Used for advanced apply detail::VectorCache cache_; + // Used in apply for two-level method detail::VectorCache csol_cache_; std::shared_ptr coarse_level_; From e5f176e5626cdb92ea8a687d107e663d7c1499d6 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Tue, 18 Mar 2025 11:01:23 +0100 Subject: [PATCH 11/21] review updates Co-authored-by: Marcel Koch --- core/distributed/preconditioner/schwarz.cpp | 7 +++++-- .../ginkgo/core/distributed/preconditioner/schwarz.hpp | 8 +++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index e4974fc5e78..c3479e22a17 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "core/base/utils.hpp" #include "core/config/config_helper.hpp" @@ -69,6 +70,9 @@ Schwarz::parse( gko::config::parse_or_get_factory( obj, context, td_for_child)); } + if (auto& obj = config.get("coarse_weight")) { + params.with_coarse_weight(gko::config::get_value(obj)); + } return params; } @@ -242,7 +246,7 @@ void Schwarz::generate( auto dist_mat = as>(system_matrix); - if (parameters_.coarse_weight > 0) { + if (parameters_.coarse_weight > 0 && parameters_.coarse_weight <= 1) { this->local_weight_ = gko::initialize>( {ValueType{1} - ValueType{parameters_.coarse_weight}}, this->get_executor()); @@ -262,7 +266,6 @@ void Schwarz::generate( this->set_solver(parameters_.generated_local_solver); } - if (parameters_.coarse_level && parameters_.coarse_solver) { this->coarse_level_ = share(parameters_.coarse_level->generate(system_matrix)); diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 2017f2d61a5..6466fd162ff 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -20,7 +20,6 @@ #include #include #include -#include #include @@ -112,9 +111,9 @@ class Schwarz /** * Coarse weighting. * - * By default the coarse and the local solutions are added together. A - * weighting can instead be provided if the coarse solution tends to - * over-correct. + * By default the coarse and the local solutions are added together + * (when the coarse weight is < 0). A weighting can instead be provided + * if the coarse solution tends to over-correct. */ double GKO_FACTORY_PARAMETER_SCALAR(coarse_weight, double{-1.0}); @@ -166,7 +165,6 @@ class Schwarz */ explicit Schwarz(std::shared_ptr exec) : EnableLinOp(std::move(exec)) - {} /** From ad1d8d3512674e6010cb85887a6267a8f135838d Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Tue, 18 Mar 2025 13:17:59 +0100 Subject: [PATCH 12/21] [config] add config tests --- core/test/config/preconditioner.cpp | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/core/test/config/preconditioner.cpp b/core/test/config/preconditioner.cpp index 18e771296e9..84e1a4048ca 100644 --- a/core/test/config/preconditioner.cpp +++ b/core/test/config/preconditioner.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -402,6 +403,7 @@ struct GaussSeidel #if GINKGO_BUILD_MPI +using DummyMgLevel = gko::multigrid::Pgm; struct Schwarz : PreconditionerConfigTest< @@ -428,17 +430,34 @@ struct Schwarz param.with_local_solver( detail::registry_accessor::get_data( reg, "solver")); + config_map["coarse_solver"] = pnode{"solver"}; + param.with_coarse_solver( + detail::registry_accessor::get_data( + reg, "solver")); + config_map["coarse_level"] = pnode{"c_level"}; + param.with_coarse_level( + detail::registry_accessor::get_data( + reg, "c_level")); } else { config_map["local_solver"] = pnode{{{"type", pnode{"solver::Ir"}}, {"value_type", pnode{"float32"}}}}; param.with_local_solver(DummyIr::build().on(exec)); + config_map["coarse_solver"] = + pnode{{{"type", pnode{"solver::Ir"}}, + {"value_type", pnode{"float32"}}}}; + param.with_coarse_solver(DummyIr::build().on(exec)); + config_map["coarse_level"] = + pnode{{{"type", pnode{"multigrid::Pgm"}}}}; + param.with_coarse_level(DummyMgLevel::build().on(exec)); } config_map["generated_local_solver"] = pnode{"linop"}; param.with_generated_local_solver( detail::registry_accessor::get_data(reg, "linop")); config_map["l1_smoother"] = pnode{true}; param.with_l1_smoother(true); + config_map["coarse_weight"] = pnode{0.1}; + param.with_coarse_weight(0.1); } template @@ -449,14 +468,25 @@ struct Schwarz if (from_reg) { ASSERT_EQ(res_param.local_solver, ans_param.local_solver); + ASSERT_EQ(res_param.coarse_solver, ans_param.coarse_solver); + ASSERT_EQ(res_param.coarse_level, ans_param.coarse_level); } else { ASSERT_NE( std::dynamic_pointer_cast( res_param.local_solver), nullptr); + ASSERT_NE( + std::dynamic_pointer_cast( + res_param.coarse_solver), + nullptr); + ASSERT_NE( + std::dynamic_pointer_cast( + res_param.coarse_level), + nullptr); } ASSERT_EQ(res_param.generated_local_solver, ans_param.generated_local_solver); + ASSERT_EQ(res_param.coarse_weight, ans_param.coarse_weight); } }; @@ -477,9 +507,11 @@ class Preconditioner : public ::testing::Test { u_solver(DummyIr::build().on(exec)), factorization(DummyIr::build().on(exec)), linop(gko::matrix::Dense<>::create(exec)), + coarse_level(DummyMgLevel::build().on(exec)), reg() { reg.emplace("solver", solver_factory); + reg.emplace("c_level", coarse_level); reg.emplace("l_solver", l_solver); reg.emplace("u_solver", u_solver); reg.emplace("factorization", factorization); @@ -492,6 +524,7 @@ class Preconditioner : public ::testing::Test { std::shared_ptr l_solver; std::shared_ptr u_solver; std::shared_ptr factorization; + std::shared_ptr coarse_level; std::shared_ptr linop; registry reg; }; From 057fbccab776d5e62d21a7e6fe372231478e28ef Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Mon, 24 Mar 2025 14:32:36 +0100 Subject: [PATCH 13/21] review updates Co-authored-by: Yu-Hsiang Tsai --- core/distributed/preconditioner/schwarz.cpp | 29 ++++++++++--------- core/test/config/preconditioner.cpp | 2 +- .../distributed/preconditioner/schwarz.cpp | 15 ++++++---- .../distributed/preconditioner/schwarz.hpp | 7 +++-- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index c3479e22a17..eb0b666f33a 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -71,7 +71,7 @@ Schwarz::parse( obj, context, td_for_child)); } if (auto& obj = config.get("coarse_weight")) { - params.with_coarse_weight(gko::config::get_value(obj)); + params.with_coarse_weight(gko::config::get_value(obj)); } return params; @@ -110,20 +110,18 @@ void Schwarz::apply_dense_impl( if (this->coarse_solver_ != nullptr && this->coarse_level_ != nullptr) { this->local_solver_->apply(gko::detail::get_local(dense_b), gko::detail::get_local(dense_x)); - auto restrict_op = - as(this->coarse_level_) - ->get_restrict_op(); - auto prolong_op = - as(this->coarse_level_) - ->get_prolong_op(); + auto coarse_level = + as(this->coarse_level_); + auto restrict_op = coarse_level->get_restrict_op(); + auto prolong_op = coarse_level->get_prolong_op(); auto coarse_op = as>( - as(this->coarse_level_) - ->get_coarse_op()); + coarse_level->get_coarse_op()); // Coarse solve vector cache init - // Should allocate only in the first apply call. + // Should allocate only in the first apply call if the number of rhs is + // unchanged. auto cs_ncols = dense_x->get_size()[1]; auto cs_local_nrows = coarse_op->get_local_matrix()->get_size()[0]; auto cs_global_nrows = coarse_op->get_size()[0]; @@ -131,10 +129,13 @@ void Schwarz::apply_dense_impl( auto cs_global_size = dim<2>(cs_global_nrows, cs_ncols); auto comm = coarse_op->get_communicator(); csol_cache_.init(exec, comm, cs_global_size, cs_local_size); + crhs_cache_.init(exec, comm, cs_global_size, cs_local_size); // Additive apply of coarse correction - restrict_op->apply(dense_b, csol_cache_.get()); - this->coarse_solver_->apply(csol_cache_.get(), csol_cache_.get()); + restrict_op->apply(dense_b, crhs_cache_.get()); + // TODO: Does it make sense to restrict dense_x (to csol_cache) to + // provide a good initial guess for the coarse solver ? + this->coarse_solver_->apply(crhs_cache_.get(), csol_cache_.get()); prolong_op->apply(this->coarse_weight_, csol_cache_.get(), this->local_weight_, dense_x); } else { @@ -246,7 +247,9 @@ void Schwarz::generate( auto dist_mat = as>(system_matrix); - if (parameters_.coarse_weight > 0 && parameters_.coarse_weight <= 1) { + gko::remove_complex cweight = + gko::detail::real_impl(parameters_.coarse_weight); + if (cweight > 0.0 && cweight <= 1.0) { this->local_weight_ = gko::initialize>( {ValueType{1} - ValueType{parameters_.coarse_weight}}, this->get_executor()); diff --git a/core/test/config/preconditioner.cpp b/core/test/config/preconditioner.cpp index 84e1a4048ca..40482170f74 100644 --- a/core/test/config/preconditioner.cpp +++ b/core/test/config/preconditioner.cpp @@ -457,7 +457,7 @@ struct Schwarz config_map["l1_smoother"] = pnode{true}; param.with_l1_smoother(true); config_map["coarse_weight"] = pnode{0.1}; - param.with_coarse_weight(0.1); + param.with_coarse_weight(decltype(param.coarse_weight){0.1}); } template diff --git a/core/test/mpi/distributed/preconditioner/schwarz.cpp b/core/test/mpi/distributed/preconditioner/schwarz.cpp index 4bbcc65e41b..f06ec811576 100644 --- a/core/test/mpi/distributed/preconditioner/schwarz.cpp +++ b/core/test/mpi/distributed/preconditioner/schwarz.cpp @@ -44,7 +44,7 @@ class SchwarzFactory : public ::testing::Test { .with_local_solver(jacobi_factory) .with_coarse_level(pgm_factory) .with_coarse_solver(cg_factory) - .with_coarse_weight(0.4) + .with_coarse_weight(value_type{0.4}) .on(exec) ->generate(mtx); } @@ -109,7 +109,9 @@ TYPED_TEST(SchwarzFactory, CanSetCoarseSolverFactory) TYPED_TEST(SchwarzFactory, CanSetCoarseWeight) { - ASSERT_EQ(this->schwarz->get_parameters().coarse_weight, 0.4); + using value_type = typename TestFixture::value_type; + + ASSERT_EQ(this->schwarz->get_parameters().coarse_weight, value_type{0.4}); } @@ -123,6 +125,7 @@ TYPED_TEST(SchwarzFactory, CanBeCloned) TYPED_TEST(SchwarzFactory, CanBeCopied) { + using value_type = typename TestFixture::value_type; using Jacobi = typename TestFixture::Jacobi; using Pgm = typename TestFixture::Pgm; using Cg = typename TestFixture::Cg; @@ -135,7 +138,7 @@ TYPED_TEST(SchwarzFactory, CanBeCopied) .with_local_solver(bj) .with_coarse_level(pgm) .with_coarse_solver(cg) - .with_coarse_weight(0.4) + .with_coarse_weight(value_type{0.4}) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -147,6 +150,7 @@ TYPED_TEST(SchwarzFactory, CanBeCopied) TYPED_TEST(SchwarzFactory, CanBeMoved) { + using value_type = typename TestFixture::value_type; using Jacobi = typename TestFixture::Jacobi; using Pgm = typename TestFixture::Pgm; using Cg = typename TestFixture::Cg; @@ -160,7 +164,7 @@ TYPED_TEST(SchwarzFactory, CanBeMoved) .with_local_solver(bj) .with_coarse_level(pgm) .with_coarse_solver(cg) - .with_coarse_weight(0.4) + .with_coarse_weight(value_type{0.4}) .on(this->exec) ->generate(Mtx::create(this->exec, MPI_COMM_WORLD)); @@ -172,13 +176,14 @@ TYPED_TEST(SchwarzFactory, CanBeMoved) TYPED_TEST(SchwarzFactory, CanBeCleared) { + using value_type = typename TestFixture::value_type; this->schwarz->clear(); ASSERT_EQ(this->schwarz->get_size(), gko::dim<2>(0, 0)); ASSERT_EQ(this->schwarz->get_parameters().local_solver, nullptr); ASSERT_EQ(this->schwarz->get_parameters().coarse_level, nullptr); ASSERT_EQ(this->schwarz->get_parameters().coarse_solver, nullptr); - ASSERT_EQ(this->schwarz->get_parameters().coarse_weight, -1); + ASSERT_EQ(this->schwarz->get_parameters().coarse_weight, value_type{-1}); } diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 6466fd162ff..ff233503e08 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -112,10 +112,10 @@ class Schwarz * Coarse weighting. * * By default the coarse and the local solutions are added together - * (when the coarse weight is < 0). A weighting can instead be provided - * if the coarse solution tends to over-correct. + * (when the coarse weight is < 0 or > 1). A weighting can instead be + * provided if the coarse solution tends to over-correct. */ - double GKO_FACTORY_PARAMETER_SCALAR(coarse_weight, double{-1.0}); + ValueType GKO_FACTORY_PARAMETER_SCALAR(coarse_weight, ValueType{-1.0}); /** * Operator factory list to generate the triplet (prolong_op, coarse_op, @@ -212,6 +212,7 @@ class Schwarz detail::VectorCache cache_; // Used in apply for two-level method detail::VectorCache csol_cache_; + detail::VectorCache crhs_cache_; std::shared_ptr coarse_level_; std::shared_ptr coarse_solver_; From 973ea02982e183df583f7dcd934a2a6a27c08a55 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Mon, 24 Mar 2025 14:43:21 +0100 Subject: [PATCH 14/21] [examples] update distributed solver example --- examples/distributed-solver/distributed-solver.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/distributed-solver/distributed-solver.cpp b/examples/distributed-solver/distributed-solver.cpp index 577e8279664..61f23391703 100644 --- a/examples/distributed-solver/distributed-solver.cpp +++ b/examples/distributed-solver/distributed-solver.cpp @@ -64,7 +64,8 @@ int main(int argc, char* argv[]) // - The executor, defaults to reference. // - The number of grid points, defaults to 100. // - The number of iterations, defaults to 1000. - // - One-level or two-level preconditioner, defaults to one-level. + // - One-level, two-level preconditioner, and no preconditioner, defaults to + // one-level. if (argc == 2 && (std::string(argv[1]) == "--help")) { if (rank == 0) { std::cerr << "Usage: " << argv[0] @@ -214,6 +215,9 @@ int main(int argc, char* argv[]) std::shared_ptr> logger = gko::log::Convergence::create(); std::shared_ptr Ainv{}; + // The benefit of the two-level Schwarz preconditioner is generally + // observable (in runtime and in number of iterations) usually for larger + // problem sizes and for larger number of ranks. if (schw_type == "two-level") { Ainv = solver::build() From 1b4a421f5a28b2c2f36814cb197bdc854ebbeca9 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Mon, 24 Mar 2025 16:08:23 +0100 Subject: [PATCH 15/21] fix tests and rebase --- core/distributed/preconditioner/schwarz.cpp | 31 ++++++--------------- test/mpi/preconditioner/schwarz.cpp | 14 +++++----- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index eb0b666f33a..097fcc2d61c 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -189,20 +189,23 @@ void Schwarz::generate( GKO_INVALID_STATE( "Provided both a generated solver and a solver factory"); } - if (!parameters_.local_solver && !parameters_.generated_local_solver) { GKO_INVALID_STATE( "Requires either a generated solver or an solver factory"); } - if (parameters_.generated_local_solver) { this->set_solver(parameters_.generated_local_solver); return; } + if ((parameters_.coarse_level && !parameters_.coarse_solver) || + (!parameters_.coarse_level && parameters_.coarse_solver)) { + GKO_INVALID_STATE( + "Requires both coarse solver and coarse level to be set."); + } - auto local_matrix = - as>(system_matrix) - ->get_local_matrix(); + auto dist_mat = + as>(system_matrix); + auto local_matrix = dist_mat->get_local_matrix(); if (parameters_.l1_smoother) { auto exec = this->get_executor(); @@ -239,14 +242,6 @@ void Schwarz::generate( gko::share(parameters_.local_solver->generate(local_matrix))); } - if ((parameters_.coarse_level && !parameters_.coarse_solver) || - (!parameters_.coarse_level && parameters_.coarse_solver)) { - GKO_INVALID_STATE( - "Requires both coarse solver and coarse level to be set."); - } - auto dist_mat = - as>(system_matrix); gko::remove_complex cweight = gko::detail::real_impl(parameters_.coarse_weight); if (cweight > 0.0 && cweight <= 1.0) { @@ -262,13 +257,6 @@ void Schwarz::generate( {ValueType{1.0}}, this->get_executor()); } - if (parameters_.local_solver) { - this->set_solver(gko::share( - parameters_.local_solver->generate(dist_mat->get_local_matrix()))); - } else { - this->set_solver(parameters_.generated_local_solver); - } - if (parameters_.coarse_level && parameters_.coarse_solver) { this->coarse_level_ = share(parameters_.coarse_level->generate(system_matrix)); @@ -279,8 +267,7 @@ void Schwarz::generate( if (coarse != nullptr) { this->coarse_solver_ = share(parameters_.coarse_solver->generate( - as>( + as>( coarse))); } } else { diff --git a/test/mpi/preconditioner/schwarz.cpp b/test/mpi/preconditioner/schwarz.cpp index ac88046b114..bae01f2a420 100644 --- a/test/mpi/preconditioner/schwarz.cpp +++ b/test/mpi/preconditioner/schwarz.cpp @@ -104,6 +104,12 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { local_solver_factory = local_prec_type::build().with_max_block_size(1u).on(exec); + coarse_solver_factory = + solver_type::build() + .with_criteria( + gko::stop::Iteration::build().with_max_iters(5u).on(exec)) + .on(exec); + pgm_factory = coarse_level_type::build().on(exec); } void SetUp() override { ASSERT_EQ(comm.size(), 3); } @@ -280,15 +286,9 @@ TYPED_TEST(SchwarzPreconditioner, CanApplyMultilevelPreconditioner) .with_coarse_solver(this->coarse_solver_factory) .with_coarse_level(this->pgm_factory) .on(this->exec); - auto local_precond = - this->local_solver_factory->generate(this->non_dist_mat); auto precond = precond_factory->generate(this->dist_mat); - precond->apply(this->dist_b.get(), this->dist_x.get()); - local_precond->apply(this->non_dist_b.get(), this->non_dist_x.get()); - - this->assert_equal_to_non_distributed_vector(this->dist_x, - this->non_dist_x); + ASSERT_NO_THROW(precond->apply(this->dist_b.get(), this->dist_x.get())); } From a70c5b4180d531345321a8927d916328814cfe97 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Mon, 24 Mar 2025 20:42:13 +0100 Subject: [PATCH 16/21] fix missing using error --- core/test/config/preconditioner.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/test/config/preconditioner.cpp b/core/test/config/preconditioner.cpp index 40482170f74..447ec5aefc9 100644 --- a/core/test/config/preconditioner.cpp +++ b/core/test/config/preconditioner.cpp @@ -31,6 +31,8 @@ using namespace gko::config; using DummyIr = gko::solver::Ir; +using DummyMgLevel = gko::multigrid::Pgm; + template struct PreconditionerConfigTest { @@ -403,8 +405,6 @@ struct GaussSeidel #if GINKGO_BUILD_MPI -using DummyMgLevel = gko::multigrid::Pgm; - struct Schwarz : PreconditionerConfigTest< ::gko::experimental::distributed::preconditioner::Schwarz Date: Mon, 7 Apr 2025 22:21:21 +0200 Subject: [PATCH 17/21] review updates --- core/distributed/preconditioner/schwarz.cpp | 39 +++++++++------------ 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index 097fcc2d61c..a047dbc2fae 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -135,15 +135,15 @@ void Schwarz::apply_dense_impl( restrict_op->apply(dense_b, crhs_cache_.get()); // TODO: Does it make sense to restrict dense_x (to csol_cache) to // provide a good initial guess for the coarse solver ? + if (this->coarse_solver_->apply_uses_initial_guess()) { + csol_cache_->copy_from(crhs_cache_.get()); + } this->coarse_solver_->apply(crhs_cache_.get(), csol_cache_.get()); prolong_op->apply(this->coarse_weight_, csol_cache_.get(), this->local_weight_, dense_x); - } else { - // One-level - if (this->local_solver_ != nullptr) { - this->local_solver_->apply(gko::detail::get_local(dense_b), - gko::detail::get_local(dense_x)); - } + } else if (this->local_solver_ != nullptr) { + this->local_solver_->apply(gko::detail::get_local(dense_b), + gko::detail::get_local(dense_x)); } } @@ -246,32 +246,27 @@ void Schwarz::generate( gko::detail::real_impl(parameters_.coarse_weight); if (cweight > 0.0 && cweight <= 1.0) { this->local_weight_ = gko::initialize>( - {ValueType{1} - ValueType{parameters_.coarse_weight}}, + {one() - + static_cast(parameters_.coarse_weight)}, this->get_executor()); this->coarse_weight_ = gko::initialize>( - {ValueType{parameters_.coarse_weight}}, this->get_executor()); + {static_cast(parameters_.coarse_weight)}, + this->get_executor()); } else { this->local_weight_ = gko::initialize>( - {ValueType{1.0}}, this->get_executor()); + {one()}, this->get_executor()); this->coarse_weight_ = gko::initialize>( - {ValueType{1.0}}, this->get_executor()); + {one()}, this->get_executor()); } if (parameters_.coarse_level && parameters_.coarse_solver) { this->coarse_level_ = share(parameters_.coarse_level->generate(system_matrix)); - if (as(this->coarse_level_)) { - auto coarse = as(this->coarse_level_) - ->get_coarse_op(); - // Cannot guarantee that coarse op is not nullptr - if (coarse != nullptr) { - this->coarse_solver_ = - share(parameters_.coarse_solver->generate( - as>( - coarse))); - } - } else { - GKO_NOT_SUPPORTED(this->coarse_level_); + if (auto coarse = as(this->coarse_level_) + ->get_coarse_op()) { + this->coarse_solver_ = share(parameters_.coarse_solver->generate( + as>( + coarse))); } } } From 9d9b8292c70314d08e320c4c54f97e3549efcad7 Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Wed, 9 Apr 2025 16:57:00 +0200 Subject: [PATCH 18/21] update two-level test --- test/mpi/preconditioner/schwarz.cpp | 41 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/test/mpi/preconditioner/schwarz.cpp b/test/mpi/preconditioner/schwarz.cpp index bae01f2a420..7fdc333f22c 100644 --- a/test/mpi/preconditioner/schwarz.cpp +++ b/test/mpi/preconditioner/schwarz.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,7 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { gko::experimental::distributed::preconditioner::Schwarz< value_type, local_index_type, global_index_type>; using solver_type = gko::solver::Bicgstab; + using mg_prec_type = gko::solver::Multigrid; using local_prec_type = gko::preconditioner::Jacobi; using coarse_solver_type = @@ -96,6 +98,8 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { dist_b->fill(-gko::one()); dist_x = gko::share(gko::clone(exec, dist_result)); dist_x->fill(gko::zero()); + dist_x2 = gko::share(gko::clone(exec, dist_result)); + dist_x2->fill(gko::zero()); auto non_dist_result = local_vec_type::create(ref, global_size, nrhs); non_dist_result->fill(-gko::one()); non_dist_b = gko::share(gko::clone(exec, non_dist_result)); @@ -106,10 +110,11 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { local_prec_type::build().with_max_block_size(1u).on(exec); coarse_solver_factory = solver_type::build() - .with_criteria( - gko::stop::Iteration::build().with_max_iters(5u).on(exec)) + .with_criteria(gko::stop::Iteration::build().with_max_iters(5u)) .on(exec); pgm_factory = coarse_level_type::build().on(exec); + one_ = gko::initialize({gko::one()}, + this->exec); } void SetUp() override { ASSERT_EQ(comm.size(), 3); } @@ -122,9 +127,11 @@ class SchwarzPreconditioner : public CommonMpiTestFixture { std::shared_ptr dist_mat; std::shared_ptr dist_b; std::shared_ptr dist_x; + std::shared_ptr dist_x2; std::shared_ptr non_dist_mat; std::shared_ptr non_dist_b; std::shared_ptr non_dist_x; + std::shared_ptr one_; std::shared_ptr non_dist_solver_factory; std::shared_ptr dist_solver_factory; std::shared_ptr local_solver_factory; @@ -276,19 +283,45 @@ TYPED_TEST(SchwarzPreconditioner, CanApplyPreconditioner) } -TYPED_TEST(SchwarzPreconditioner, CanApplyMultilevelPreconditioner) +TYPED_TEST(SchwarzPreconditioner, CanApplyTwolevelPreconditioner) { using value_type = typename TestFixture::value_type; using prec = typename TestFixture::dist_prec_type; + using mg_prec = typename TestFixture::mg_prec_type; + using dist_vec = typename TestFixture::dist_vec_type; auto precond_factory = prec::build() .with_local_solver(this->local_solver_factory) .with_coarse_solver(this->coarse_solver_factory) .with_coarse_level(this->pgm_factory) .on(this->exec); + auto local_precond_factory = + prec::build() + .with_local_solver(this->local_solver_factory) + .on(this->exec); + auto mg_precond_factory = + mg_prec::build() + .with_coarsest_solver(this->coarse_solver_factory) + .with_mg_level(this->pgm_factory) + .with_post_smoother(nullptr) + .with_pre_smoother(nullptr) + .with_max_levels(1u) + .with_min_coarse_rows(1u) + .with_criteria(gko::stop::Iteration::build().with_max_iters(1u)) + .on(this->exec); auto precond = precond_factory->generate(this->dist_mat); + auto mg_precond = mg_precond_factory->generate(this->dist_mat); + auto local_precond = local_precond_factory->generate(this->dist_mat); + auto local_dist_x = gko::clone(this->dist_x); + local_precond->apply(this->dist_b.get(), local_dist_x.get()); + mg_precond->apply(this->dist_b.get(), this->dist_x2.get()); + this->dist_x2->add_scaled(this->one_, local_dist_x); - ASSERT_NO_THROW(precond->apply(this->dist_b.get(), this->dist_x.get())); + precond->apply(this->dist_b.get(), this->dist_x.get()); + + GKO_ASSERT_MTX_NEAR(this->dist_x->get_local_vector(), + this->dist_x2->get_local_vector(), + r::value); } From 01262e8e083ca2812f15d86266db6d16fd83f0cb Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Thu, 10 Apr 2025 10:43:37 +0200 Subject: [PATCH 19/21] [doc] update doc --- include/ginkgo/core/distributed/preconditioner/schwarz.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index ff233503e08..988093e41f8 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -105,6 +105,9 @@ class Schwarz * sum of the non-local matrix entries. The diagonal matrix * is then added to the system matrix when generating the * local solver. + * + * Note: The L1 smoother will not be used for the coarse level matrix + * generation, and the original system matrix will still be used. */ bool GKO_FACTORY_PARAMETER_SCALAR(l1_smoother, false); From abb85842172976af19f1fe2ed6b745044f51b4bd Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Thu, 10 Apr 2025 14:12:20 +0200 Subject: [PATCH 20/21] review updates --- core/distributed/preconditioner/schwarz.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/distributed/preconditioner/schwarz.cpp b/core/distributed/preconditioner/schwarz.cpp index a047dbc2fae..da357784a67 100644 --- a/core/distributed/preconditioner/schwarz.cpp +++ b/core/distributed/preconditioner/schwarz.cpp @@ -108,8 +108,10 @@ void Schwarz::apply_dense_impl( // Two-level if (this->coarse_solver_ != nullptr && this->coarse_level_ != nullptr) { - this->local_solver_->apply(gko::detail::get_local(dense_b), - gko::detail::get_local(dense_x)); + if (this->local_solver_) { + this->local_solver_->apply(gko::detail::get_local(dense_b), + gko::detail::get_local(dense_x)); + } auto coarse_level = as(this->coarse_level_); auto restrict_op = coarse_level->get_restrict_op(); @@ -244,7 +246,7 @@ void Schwarz::generate( gko::remove_complex cweight = gko::detail::real_impl(parameters_.coarse_weight); - if (cweight > 0.0 && cweight <= 1.0) { + if (cweight >= 0.0 && cweight <= 1.0) { this->local_weight_ = gko::initialize>( {one() - static_cast(parameters_.coarse_weight)}, @@ -262,11 +264,17 @@ void Schwarz::generate( if (parameters_.coarse_level && parameters_.coarse_solver) { this->coarse_level_ = share(parameters_.coarse_level->generate(system_matrix)); + if (this->coarse_level_ == nullptr) { + GKO_NOT_SUPPORTED(this->coarse_level_); + } if (auto coarse = as(this->coarse_level_) ->get_coarse_op()) { this->coarse_solver_ = share(parameters_.coarse_solver->generate( as>( coarse))); + if (this->coarse_solver_ == nullptr) { + GKO_NOT_SUPPORTED(this->coarse_solver_); + } } } } From d6c3ebc1156df5f1b72cb4eb666c0985893e8a6b Mon Sep 17 00:00:00 2001 From: Pratik Nayak Date: Thu, 10 Apr 2025 17:34:17 +0200 Subject: [PATCH 21/21] [doc] doc updates --- .../ginkgo/core/distributed/preconditioner/schwarz.hpp | 9 +++++++-- include/ginkgo/core/distributed/vector.hpp | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp index 988093e41f8..66ae800103c 100644 --- a/include/ginkgo/core/distributed/preconditioner/schwarz.hpp +++ b/include/ginkgo/core/distributed/preconditioner/schwarz.hpp @@ -39,13 +39,18 @@ namespace preconditioner { * generalizes the Block Jacobi preconditioner, incorporating options for * different local subdomain solvers and overlaps between the subdomains. * + * A L1 smoother variant is also available, which updates the local matrix with + * the sums of the non-local matrix row sums. + * * See Iterative Methods for Sparse Linear Systems (Y. Saad) for a general * treatment and variations of the method. * * A Two-level variant is also available. To enable two-level preconditioning, * you need to specify a LinOpFactory that can generate a - * multigrid::MultigridLevel. Currently, only multiplicative coarse correction - * is supported. + * multigrid::MultigridLevel and a solver for the coarse level solution. + * Currently, only additive coarse correction is supported with an optional + * weighting between the local and the coarse solutions, for cases when the + * coarse solutions might tend to overcorrect. * - See Smith, Bjorstad, Gropp, Domain Decomposition, 1996, Cambridge * University Press. * diff --git a/include/ginkgo/core/distributed/vector.hpp b/include/ginkgo/core/distributed/vector.hpp index dc278459fd4..eefdebe39b9 100644 --- a/include/ginkgo/core/distributed/vector.hpp +++ b/include/ginkgo/core/distributed/vector.hpp @@ -538,7 +538,7 @@ class Vector * @note The data form the local_vector will be moved into the new * distributed vector. You could either move in a std::unique_ptr * directly, copy a local vector with gko::clone, or create a - * unique non-owining view of a given local vector with + * unique non-owning view of a given local vector with * gko::make_dense_view. * * @param exec Executor associated with this vector @@ -561,7 +561,7 @@ class Vector * @note The data form the local_vector will be moved into the new * distributed vector. You could either move in a std::unique_ptr * directly, copy a local vector with gko::clone, or create a - * unique non-owining view of a given local vector with + * unique non-owning view of a given local vector with * gko::make_dense_view. * * @param exec Executor associated with this vector