18#include <ginkgo/ginkgo.hpp>
19#include <ginkgo/core/multigrid/pgm.hpp>
20#include <ginkgo/core/multigrid/multigrid_level.hpp>
21#include <ginkgo/core/matrix/csr.hpp>
22#include <ginkgo/core/matrix/row_gatherer.hpp>
23#include <ginkgo/core/solver/multigrid.hpp>
28#ifdef NF_WITH_MPI_SUPPORT
29#include <ginkgo/core/distributed/base.hpp>
30#include <ginkgo/core/distributed/matrix.hpp>
31#include <ginkgo/core/distributed/vector.hpp>
34namespace NeoN::la::ginkgo
44struct ScaleCorrectionState
46 const gko::LinOp* residual =
nullptr;
53template<
typename Kernel>
54void forEachOn(
const gko::Executor& exec, gko::size_type n,
const Kernel& kernel)
57 if (exec.get_master().get() != &exec)
63 for (localIdx i = 0; i < static_cast<localIdx>(n); ++i)
72template<
typename ValueType>
74 const gko::matrix::Dense<ValueType>* num,
75 const gko::matrix::Dense<ValueType>* den,
76 gko::matrix::Dense<ValueType>* sf
79 const auto* n = num->get_const_values();
80 const auto* d = den->get_const_values();
81 auto* s = sf->get_values();
85 NEON_LAMBDA(
const localIdx c) { s[c] = d[c] != ValueType {} ? n[c] / d[c] : ValueType {1}; }
90class RecordingRestriction :
public gko::LinOp
95 std::shared_ptr<const gko::LinOp> restrictOp, std::shared_ptr<ScaleCorrectionState> state
97 : gko::LinOp(restrictOp->get_executor(), restrictOp->get_size()),
98 restrict_ {std::move(restrictOp)}, state_ {std::move(state)}
103 void apply_impl(
const gko::LinOp* b, gko::LinOp* x)
const override
105 state_->residual = b;
106 restrict_->apply(b, x);
110 const gko::LinOp* alpha,
const gko::LinOp* b,
const gko::LinOp* beta, gko::LinOp* x
113 state_->residual =
nullptr;
114 restrict_->apply(alpha, b, beta, x);
119 std::shared_ptr<const gko::LinOp> restrict_;
120 std::shared_ptr<ScaleCorrectionState> state_;
134template<
typename ValueType,
typename IndexType>
135class ScaledProlongation :
public gko::LinOp
137 using dense = gko::matrix::Dense<ValueType>;
138 using diagonal = gko::matrix::Diagonal<ValueType>;
145 std::shared_ptr<const gko::LinOp> prolongOp,
146 std::shared_ptr<const gko::LinOp> fineOp,
147 std::shared_ptr<const diagonal> invDiag,
148 std::shared_ptr<ScaleCorrectionState> state
150 : gko::LinOp(prolongOp->get_executor(), prolongOp->get_size()),
151 prolong_ {std::move(prolongOp)}, fine_ {std::move(fineOp)}, invDiag_ {std::move(invDiag)},
152 state_ {std::move(state)}
157 void apply_impl(
const gko::LinOp* b, gko::LinOp* x)
const override { prolong_->apply(b, x); }
160 const gko::LinOp* alpha,
const gko::LinOp* b,
const gko::LinOp* beta, gko::LinOp* x
163 const auto* r = state_->residual;
164 state_->residual =
nullptr;
165 if (r ==
nullptr || r->get_size() != x->get_size())
167 prolong_->apply(alpha, b, beta, x);
170#ifdef NF_WITH_MPI_SUPPORT
171 using dist_vec = gko::experimental::distributed::Vector<ValueType>;
172 if (
auto distX =
dynamic_cast<dist_vec*
>(x))
174 scaled(alpha, b, beta, distX, gko::as<const dist_vec>(r));
178 scaled(alpha, b, beta, gko::as<dense>(x), gko::as<const dense>(r));
183 static dense* local(dense* v) {
return v; }
184 static const dense* local(
const dense* v) {
return v; }
185#ifdef NF_WITH_MPI_SUPPORT
186 static const dense* local(
const gko::experimental::distributed::Vector<ValueType>* v)
188 return v->get_local_vector();
191 static std::unique_ptr<dense> local(gko::experimental::distributed::Vector<ValueType>* v)
193 const auto* l = v->get_local_vector();
194 return dense::create(
197 gko::make_array_view(
198 l->get_executor(), l->get_size()[0] * l->get_stride(), v->get_local_values()
205 template<
typename VecType>
206 VecType* workspace(std::unique_ptr<gko::LinOp>& slot,
const VecType* like)
const
208 auto ws =
dynamic_cast<VecType*
>(slot.get());
209 if (ws ==
nullptr || ws->get_size() != like->get_size())
211 slot = VecType::create_with_config_of(like);
212 ws = gko::as<VecType>(slot.get());
217 template<
typename VecType>
219 const gko::LinOp* alpha,
221 const gko::LinOp* beta,
226 auto exec = this->get_executor();
227 const auto ncols = x->get_size()[1];
228 auto* d = workspace(delta_, x);
229 auto* ad = workspace(aDelta_, x);
230 auto* t = workspace(tmp_, x);
232 prolong_->apply(e, d);
235 auto num = dense::create(exec, gko::dim<2> {1, ncols});
236 auto den = dense::create(exec, gko::dim<2> {1, ncols});
237 d->compute_dot(r, num);
238 d->compute_dot(ad, den);
241 auto sf = dense::create(exec, gko::dim<2> {1, ncols});
242 rayleighScale(num.get(), den.get(), sf.get());
244 auto one = gko::initialize<dense>({gko::one<ValueType>()}, exec);
247 t->sub_scaled(one, ad);
249 invDiag_->apply(one, local(
static_cast<const VecType*
>(t)), one, local(d));
252 x->add_scaled(alpha, d);
255 std::shared_ptr<const gko::LinOp> prolong_;
256 std::shared_ptr<const gko::LinOp> fine_;
257 std::shared_ptr<const diagonal> invDiag_;
258 std::shared_ptr<ScaleCorrectionState> state_;
259 mutable std::unique_ptr<gko::LinOp> delta_;
260 mutable std::unique_ptr<gko::LinOp> aDelta_;
261 mutable std::unique_ptr<gko::LinOp> tmp_;
265template<
typename ValueType,
typename IndexType>
266std::shared_ptr<const gko::matrix::Diagonal<ValueType>>
267invertedDiagonal(
const gko::matrix::Csr<ValueType, IndexType>* mtx)
270 auto diag = gko::share(mtx->extract_diagonal());
271 auto* v = diag->get_values();
273 *diag->get_executor(),
276 v[i] = v[i] != ValueType {} ? ValueType {1} / v[i] : ValueType {};
314template<
typename ValueType = gko::default_precision,
typename IndexType = gko::
int32>
315class MergedPgm :
public gko::LinOp,
public gko::multigrid::EnableMultigridLevel<ValueType>
319 using value_type = ValueType;
320 using index_type = IndexType;
321 using csr = gko::matrix::Csr<ValueType, IndexType>;
322 using pgm = gko::multigrid::Pgm<ValueType, IndexType>;
323 using row_gatherer = gko::matrix::RowGatherer<IndexType>;
324#ifdef NF_WITH_MPI_SUPPORT
328 using dist_mtx = gko::experimental::distributed::Matrix<ValueType, IndexType, gko::int64>;
331 std::shared_ptr<const gko::LinOp> get_system_matrix()
const {
return system_matrix_; }
333 GKO_CREATE_FACTORY_PARAMETERS(parameters, Factory)
336 unsigned GKO_FACTORY_PARAMETER_SCALAR(merge_levels, 2u);
338 unsigned GKO_FACTORY_PARAMETER_SCALAR(max_iterations, 15u);
339 double GKO_FACTORY_PARAMETER_SCALAR(max_unassigned_ratio, 0.05);
340 bool GKO_FACTORY_PARAMETER_SCALAR(deterministic,
false);
341 bool GKO_FACTORY_PARAMETER_SCALAR(skip_sorting,
false);
343 bool GKO_FACTORY_PARAMETER_SCALAR(scale_correction,
false);
345 class reuse_data_type;
346 GKO_ENABLE_LIN_OP_FACTORY_WITH_REUSE(MergedPgm, parameters, Factory, reuse_data_type);
347 GKO_ENABLE_BUILD_METHOD(Factory);
355 class reuse_data_type :
public gko::LinOpFactory::ReuseData
357 friend class MergedPgm;
359 bool initialized_ =
false;
360 bool distributed_ =
false;
361 gko::dim<2> size_ {};
362 std::vector<std::unique_ptr<gko::LinOpFactory::ReuseData>> levels_;
363 std::shared_ptr<const gko::LinOp> prolong_;
364 std::shared_ptr<const gko::LinOp> restrict_;
369 void apply_impl(
const gko::LinOp* b, gko::LinOp* x)
const override
371 this->get_composition()->apply(b, x);
374 const gko::LinOp* alpha,
const gko::LinOp* b,
const gko::LinOp* beta, gko::LinOp* x
377 this->get_composition()->apply(alpha, b, beta, x);
380 explicit MergedPgm(std::shared_ptr<const gko::Executor> exec) : gko::LinOp(std::move(exec)) {}
382 MergedPgm(
const Factory* factory, std::shared_ptr<const gko::LinOp> system_matrix)
383 : MergedPgm(factory, std::move(system_matrix), nullptr)
387 const Factory* factory,
388 std::shared_ptr<const gko::LinOp> system_matrix,
389 reuse_data_type& reuse_data
391 : MergedPgm(factory, std::move(system_matrix), &reuse_data)
395 const Factory* factory,
396 std::shared_ptr<const gko::LinOp> system_matrix,
397 reuse_data_type* reuse_data
399 : gko::LinOp(factory->get_executor(), system_matrix->get_size()),
400 gko::multigrid::EnableMultigridLevel<ValueType>(system_matrix),
401 parameters_ {factory->get_parameters()}, system_matrix_ {system_matrix}
403 GKO_ASSERT(parameters_.merge_levels >= 1u);
404 if (system_matrix_->get_size()[0] != 0)
406 if (is_distributed(system_matrix_.get()))
408 generateDistributed(reuse_data);
412 generateLocal(reuse_data);
418 static void check_reuse_consistent(
419 const Factory*,
const gko::LinOp* input,
const reuse_data_type& reuse_data
422 if (!reuse_data.initialized_)
return;
423 GKO_ASSERT_EQUAL_DIMENSIONS(input, reuse_data.size_);
424 if (is_distributed(input) != reuse_data.distributed_)
427 "generate_reuse needs a distributed matrix exactly if the reuse data was "
428 "initialized with one"
434 void generateLocal(reuse_data_type* reuseData)
436 auto A = as_csr(system_matrix_);
437 const auto fineRows = A->get_size()[0];
438 const bool reuseMerged = reuseData && reuseData->initialized_;
448 auto pgmFactory = makePgm();
449 std::vector<std::unique_ptr<gko::LinOpFactory::ReuseData>> newSlots;
450 std::shared_ptr<const csr> coarse = A;
451 std::vector<IndexType> mergedAgg;
452 gko::size_type coarseRows = 0;
453 for (
unsigned i = 0; i < parameters_.merge_levels; ++i)
455 auto level = generateLevel(pgmFactory.get(), coarse, i, reuseData, newSlots);
458 auto aggThis = gather_agg(level.get());
459 coarseRows = level->get_prolong_op()->get_size()[1];
460 composeAgg(mergedAgg, aggThis, fineRows, i);
462 coarse = as_csr(level->get_coarse_op());
467 prolong_ = reuseData->prolong_;
468 restrict_ = reuseData->restrict_;
472 auto pCsr = make_injection(mergedAgg, fineRows, coarseRows);
474 restrict_ = gko::share(gko::as<csr>(pCsr->transpose()));
477 setLevel(A, A.get(), coarse);
478 recordReuse(reuseData, std::move(newSlots),
false);
481#ifdef NF_WITH_MPI_SUPPORT
483 void generateDistributed(reuse_data_type* reuseData)
485 auto exec = this->get_executor();
486 auto distFine = gko::as<const dist_mtx>(system_matrix_);
487 auto comm = gko::as<const gko::experimental::distributed::DistributedBase>(system_matrix_)
488 ->get_communicator();
489 const auto fineGlobalRows = system_matrix_->get_size()[0];
492 const auto localFineRows = distFine->get_diag_matrix()->get_size()[0];
493 const bool reuseMerged = reuseData && reuseData->initialized_;
495 auto pgmFactory = makePgm();
496 std::vector<std::unique_ptr<gko::LinOpFactory::ReuseData>> newSlots;
497 std::shared_ptr<const gko::LinOp> coarse = system_matrix_;
498 std::vector<IndexType> mergedAgg;
499 gko::size_type localCoarseRows = 0;
500 for (
unsigned i = 0; i < parameters_.merge_levels; ++i)
502 auto level = generateLevel(pgmFactory.get(), coarse, i, reuseData, newSlots);
507 gko::as<const dist_mtx>(level->get_prolong_op())->get_diag_matrix();
509 agg_from_rowgatherer(prolongDiag.get());
511 localCoarseRows = gko::as<const dist_mtx>(level->get_coarse_op())
514 composeAgg(mergedAgg, aggThis, localFineRows, i);
516 coarse = level->get_coarse_op();
521 prolong_ = reuseData->prolong_;
522 restrict_ = reuseData->restrict_;
526 const auto coarseGlobalRows = coarse->get_size()[0];
531 std::shared_ptr<csr> pLocal = make_injection(mergedAgg, localFineRows, localCoarseRows);
532 std::shared_ptr<gko::LinOp> rLocal = gko::share(gko::as<csr>(pLocal->transpose()));
533 prolong_ = gko::share(
534 dist_mtx::create(exec, comm, gko::dim<2> {fineGlobalRows, coarseGlobalRows}, pLocal)
536 restrict_ = gko::share(
537 dist_mtx::create(exec, comm, gko::dim<2> {coarseGlobalRows, fineGlobalRows}, rLocal)
541 setLevel(system_matrix_, gko::as<csr>(distFine->get_diag_matrix().get()), coarse);
542 recordReuse(reuseData, std::move(newSlots),
true);
545 void generateDistributed(reuse_data_type*) { GKO_NOT_IMPLEMENTED; }
554 std::shared_ptr<const gko::LinOp> fineOp,
555 const csr* diagBlock,
556 std::shared_ptr<const gko::LinOp> coarse
559 if (!parameters_.scale_correction)
561 this->set_multigrid_level(prolong_, std::move(coarse), restrict_);
564 auto state = std::make_shared<detail::ScaleCorrectionState>();
565 this->set_multigrid_level(
566 std::make_shared<detail::ScaledProlongation<ValueType, IndexType>>(
567 prolong_, std::move(fineOp), detail::invertedDiagonal(diagBlock), state
570 std::make_shared<detail::RecordingRestriction>(restrict_, state)
576 static std::unique_ptr<pgm> generateLevel(
577 const typename pgm::Factory* pgmFactory,
578 std::shared_ptr<const gko::LinOp> fine,
580 reuse_data_type* reuseData,
581 std::vector<std::unique_ptr<gko::LinOpFactory::ReuseData>>& newSlots
584 if (!reuseData)
return pgmFactory->generate(std::move(fine));
585 if (reuseData->initialized_)
587 return pgmFactory->generate_reuse(std::move(fine), *reuseData->levels_.at(i));
589 newSlots.push_back(pgmFactory->create_empty_reuse_data());
590 return pgmFactory->generate_reuse(std::move(fine), *newSlots.back());
596 reuse_data_type* reuseData,
597 std::vector<std::unique_ptr<gko::LinOpFactory::ReuseData>> newSlots,
601 if (!reuseData || reuseData->initialized_)
return;
602 reuseData->levels_ = std::move(newSlots);
603 reuseData->prolong_ = prolong_;
604 reuseData->restrict_ = restrict_;
605 reuseData->size_ = system_matrix_->get_size();
606 reuseData->distributed_ = distributed;
607 reuseData->initialized_ =
true;
610 static bool is_distributed(
const gko::LinOp* op)
612#ifdef NF_WITH_MPI_SUPPORT
613 return dynamic_cast<const gko::experimental::distributed::DistributedBase*
>(op) !=
nullptr;
615 static_cast<void>(op);
620 std::unique_ptr<typename pgm::Factory> makePgm()
const
623 .with_max_iterations(parameters_.max_iterations)
624 .with_max_unassigned_ratio(parameters_.max_unassigned_ratio)
625 .with_deterministic(parameters_.deterministic)
626 .with_skip_sorting(parameters_.skip_sorting)
627 .on(this->get_executor());
633 static void composeAgg(
634 std::vector<IndexType>& mergedAgg,
635 const gko::array<IndexType>& aggThis,
636 gko::size_type fineRows,
642 mergedAgg.assign(aggThis.get_const_data(), aggThis.get_const_data() + fineRows);
646 for (gko::size_type k = 0; k < fineRows; ++k)
648 mergedAgg[k] = aggThis.get_const_data()[mergedAgg[k]];
653 static std::shared_ptr<const csr> as_csr(std::shared_ptr<const gko::LinOp> op)
655 if (
auto c = std::dynamic_pointer_cast<const csr>(op))
return c;
656 auto exec = op->get_executor();
657 auto out = csr::create(exec);
658 gko::as<const gko::ConvertibleTo<csr>>(op.get())->convert_to(out);
659 return std::move(out);
664 gko::array<IndexType> agg_from_rowgatherer(
const gko::LinOp* rgOp)
const
666 auto exec = this->get_executor();
667 auto host = exec->get_master();
668 auto rg = gko::as<const row_gatherer>(rgOp);
669 auto fineRows = rg->get_size()[0];
670 gko::array<IndexType> agg(host, fineRows);
671 host->copy_from(exec, fineRows, rg->get_const_row_idxs(), agg.get_data());
676 gko::array<IndexType> gather_agg(
const gko::multigrid::MultigridLevel* level)
const
678 return agg_from_rowgatherer(level->get_prolong_op().get());
685 std::shared_ptr<csr> make_injection(
686 const std::vector<IndexType>& mergedAgg, gko::size_type fineRows, gko::size_type coarseRows
689 auto exec = this->get_executor();
690 auto host = exec->get_master();
691 gko::array<IndexType> cols(host, fineRows);
692 std::copy(mergedAgg.begin(), mergedAgg.end(), cols.get_data());
693 gko::array<IndexType> rowPtrs(host, fineRows + 1);
694 std::iota(rowPtrs.get_data(), rowPtrs.get_data() + fineRows + 1, IndexType {0});
695 gko::array<ValueType> vals(host, fineRows);
696 std::fill_n(vals.get_data(), fineRows, gko::one<ValueType>());
698 auto pHost = csr::create(
700 gko::dim<2> {fineRows, coarseRows},
705 return gko::share(gko::clone(exec, pHost));
709 std::shared_ptr<const gko::LinOp> system_matrix_;
712 std::shared_ptr<const gko::LinOp> prolong_;
713 std::shared_ptr<const gko::LinOp> restrict_;
724template<
typename ValueType = gko::default_precision,
typename IndexType = gko::
int32>
725inline std::shared_ptr<typename MergedPgm<ValueType, IndexType>::Factory> makeMergedPgmFactory(
726 std::shared_ptr<const gko::Executor> exec,
727 unsigned mergeLevels,
728 bool deterministic =
true,
729 bool scaleCorrection =
false
732 return MergedPgm<ValueType, IndexType>::build()
733 .with_merge_levels(mergeLevels)
734 .with_deterministic(deterministic)
735 .with_scale_correction(scaleCorrection)
736 .on(std::move(exec));
744template<
typename ValueType = gko::default_precision,
typename IndexType = gko::
int32>
745inline gko::config::configuration_map mergedPgmConfigMap()
749 [](
const gko::config::pnode& config,
750 const gko::config::registry&,
751 gko::config::type_descriptor) -> gko::deferred_factory_parameter<gko::LinOpFactory>
753 auto params = MergedPgm<ValueType, IndexType>::build();
754 auto toUnsigned = [](
const gko::config::pnode& node,
const std::string& key)
756 const auto v = node.get_integer();
759 throw std::invalid_argument(
"neon::MergedPgm: " + key +
" must be >= 0");
761 return static_cast<unsigned>(v);
763 for (
const auto& [key, value] : config.get_map())
765 if (key ==
"type" || key ==
"value_type")
continue;
766 if (key ==
"merge_levels") params.with_merge_levels(toUnsigned(value, key));
767 else if (key ==
"max_iterations")
768 params.with_max_iterations(toUnsigned(value, key));
769 else if (key ==
"max_unassigned_ratio")
770 params.with_max_unassigned_ratio(value.get_real());
771 else if (key ==
"deterministic")
772 params.with_deterministic(value.get_boolean());
773 else if (key ==
"skip_sorting")
774 params.with_skip_sorting(value.get_boolean());
775 else if (key ==
"scale_correction")
776 params.with_scale_correction(value.get_boolean());
778 throw std::invalid_argument(
"neon::MergedPgm: unknown key '" + key +
"'");
KOKKOS_INLINE_FUNCTION T one()
void parallelFor(const ExecutorType &, std::pair< localIdx, localIdx > range, const Kernel &kernel, std::string name)