diff --git a/benchmark/solver/solver.cpp b/benchmark/solver/solver.cpp index d73ae9151b6..e90bc738f4c 100644 --- a/benchmark/solver/solver.cpp +++ b/benchmark/solver/solver.cpp @@ -382,6 +382,8 @@ void solve_system(const std::string &solver_name, rapidjson::Value(rapidjson::kArrayType), allocator); add_or_set_member(solver_json, "true_residuals", rapidjson::Value(rapidjson::kArrayType), allocator); + add_or_set_member(solver_json, "implicit_residuals", + rapidjson::Value(rapidjson::kArrayType), allocator); add_or_set_member(solver_json, "iteration_timestamps", rapidjson::Value(rapidjson::kArrayType), allocator); if (b->get_size()[1] == 1 && !FLAGS_overhead) { @@ -457,9 +459,13 @@ void solve_system(const std::string &solver_name, exec, lend(system_matrix), b, solver_json["recurrent_residuals"], solver_json["true_residuals"], + solver_json["implicit_residuals"], solver_json["iteration_timestamps"], allocator); solver->add_logger(res_logger); solver->apply(lend(b), lend(x_clone)); + if (!res_logger->has_implicit_res_norms()) { + solver_json.RemoveMember("implicit_residuals"); + } } exec->synchronize(); } diff --git a/benchmark/utils/loggers.hpp b/benchmark/utils/loggers.hpp index 5edf8f96b69..983cd2951fb 100644 --- a/benchmark/utils/loggers.hpp +++ b/benchmark/utils/loggers.hpp @@ -38,6 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include #include #include #include @@ -202,10 +203,21 @@ template struct ResidualLogger : gko::log::Logger { using rc_vtype = gko::remove_complex; - void on_iteration_complete(const gko::LinOp *, const gko::size_type &, + // TODO2.0: Remove when deprecating simple overload + void on_iteration_complete(const gko::LinOp *solver, + const gko::size_type &it, const gko::LinOp *residual, const gko::LinOp *solution, const gko::LinOp *residual_norm) const override + { + on_iteration_complete(solver, it, residual, solution, residual_norm, + nullptr); + } + + void on_iteration_complete( + const gko::LinOp *, const gko::size_type &, const gko::LinOp *residual, + const gko::LinOp *solution, const gko::LinOp *residual_norm, + const gko::LinOp *implicit_sq_residual_norm) const override { timestamps.PushBack(std::chrono::duration( std::chrono::steady_clock::now() - start) @@ -226,12 +238,22 @@ struct ResidualLogger : gko::log::Logger { } else { true_res_norms.PushBack(-1.0, alloc); } + if (implicit_sq_residual_norm) { + implicit_res_norms.PushBack( + std::sqrt(get_norm( + gko::as>(implicit_sq_residual_norm))), + alloc); + has_implicit_res_norm = true; + } else { + implicit_res_norms.PushBack(-1.0, alloc); + } } ResidualLogger(std::shared_ptr exec, const gko::LinOp *matrix, const vec *b, rapidjson::Value &rec_res_norms, rapidjson::Value &true_res_norms, + rapidjson::Value &implicit_res_norms, rapidjson::Value ×tamps, rapidjson::MemoryPoolAllocator<> &alloc) : gko::log::Logger(exec, gko::log::Logger::iteration_complete_mask), @@ -240,16 +262,22 @@ struct ResidualLogger : gko::log::Logger { start{std::chrono::steady_clock::now()}, rec_res_norms{rec_res_norms}, true_res_norms{true_res_norms}, + has_implicit_res_norm{}, + implicit_res_norms{implicit_res_norms}, timestamps{timestamps}, alloc{alloc} {} + bool has_implicit_res_norms() const { return has_implicit_res_norm; } + private: const gko::LinOp *matrix; const vec *b; std::chrono::steady_clock::time_point start; rapidjson::Value &rec_res_norms; rapidjson::Value &true_res_norms; + mutable bool has_implicit_res_norm; + rapidjson::Value &implicit_res_norms; rapidjson::Value ×tamps; rapidjson::MemoryPoolAllocator<> &alloc; }; diff --git a/core/log/papi.cpp b/core/log/papi.cpp index 0ae7d334333..bfb5a16d00b 100644 --- a/core/log/papi.cpp +++ b/core/log/papi.cpp @@ -256,6 +256,17 @@ void Papi::on_iteration_complete(const LinOp *solver, const LinOp *residual, const LinOp *solution, const LinOp *residual_norm) const +{ + this->on_iteration_complete(solver, num_iterations, residual, solution, + residual_norm, nullptr); +} + + +template +void Papi::on_iteration_complete( + const LinOp *solver, const size_type &num_iterations, const LinOp *residual, + const LinOp *solution, const LinOp *residual_norm, + const LinOp *implicit_sq_residual_norm) const { iteration_complete.get_counter(solver) = num_iterations; } diff --git a/core/log/record.cpp b/core/log/record.cpp index c4543e3e4fc..32be42931c9 100644 --- a/core/log/record.cpp +++ b/core/log/record.cpp @@ -261,11 +261,23 @@ void Record::on_iteration_complete(const LinOp *solver, const size_type &num_iterations, const LinOp *residual, const LinOp *solution, const LinOp *residual_norm) const +{ + this->on_iteration_complete(solver, num_iterations, residual, solution, + residual_norm, nullptr); +} + + +void Record::on_iteration_complete(const LinOp *solver, + const size_type &num_iterations, + const LinOp *residual, const LinOp *solution, + const LinOp *residual_norm, + const LinOp *implicit_sq_residual_norm) const { append_deque( data_.iteration_completed, (std::unique_ptr(new iteration_complete_data{ - solver, num_iterations, residual, solution, residual_norm}))); + solver, num_iterations, residual, solution, residual_norm, + implicit_sq_residual_norm}))); } diff --git a/core/log/stream.cpp b/core/log/stream.cpp index 92e3f51da64..bc0c32a7c48 100644 --- a/core/log/stream.cpp +++ b/core/log/stream.cpp @@ -428,12 +428,24 @@ void Stream::on_iteration_complete(const LinOp *solver, const LinOp *residual, const LinOp *solution, const LinOp *residual_norm) const +{ + this->on_iteration_complete(solver, num_iterations, residual, solution, + residual_norm, nullptr); +} + + +template +void Stream::on_iteration_complete( + const LinOp *solver, const size_type &num_iterations, const LinOp *residual, + const LinOp *solution, const LinOp *residual_norm, + const LinOp *implicit_sq_residual_norm) const { os_ << prefix_ << "iteration " << num_iterations << " completed with solver " << demangle_name(solver) << " with residual " << demangle_name(residual) << ", solution " - << demangle_name(solution) << " and residual_norm " - << demangle_name(residual_norm) << std::endl; + << demangle_name(solution) << ", residual_norm " + << demangle_name(residual_norm) << " and implicit_sq_residual_norm " + << demangle_name(implicit_sq_residual_norm) << std::endl; if (verbose_) { os_ << demangle_name(residual) << as>(residual) << std::endl; @@ -446,6 +458,11 @@ void Stream::on_iteration_complete(const LinOp *solver, << as>(residual_norm) << std::endl; } + if (implicit_sq_residual_norm != nullptr) { + os_ << demangle_name(implicit_sq_residual_norm) + << as>(implicit_sq_residual_norm) + << std::endl; + } } } diff --git a/core/solver/bicg.cpp b/core/solver/bicg.cpp index 7cb51f2f404..300270fd106 100644 --- a/core/solver/bicg.cpp +++ b/core/solver/bicg.cpp @@ -199,8 +199,8 @@ void Bicg::apply_impl(const LinOp *b, LinOp *x) const z->compute_dot(r2.get(), rho.get()); ++iter; - this->template log(this, iter, r.get(), - dense_x); + this->template log( + this, iter, r.get(), dense_x, nullptr, rho.get()); if (stop_criterion->update() .num_iterations(iter) .residual(r.get()) diff --git a/core/solver/bicgstab.cpp b/core/solver/bicgstab.cpp index f3828797fdb..640af69ad45 100644 --- a/core/solver/bicgstab.cpp +++ b/core/solver/bicgstab.cpp @@ -151,8 +151,8 @@ void Bicgstab::apply_impl(const LinOp *b, LinOp *x) const */ while (true) { ++iter; - this->template log(this, iter, r.get(), - dense_x); + this->template log( + this, iter, r.get(), dense_x, nullptr, rho.get()); rr->compute_dot(r.get(), rho.get()); if (stop_criterion->update() diff --git a/core/solver/cg.cpp b/core/solver/cg.cpp index 5a76392abff..ba19aa1b1d0 100644 --- a/core/solver/cg.cpp +++ b/core/solver/cg.cpp @@ -142,8 +142,8 @@ void Cg::apply_impl(const LinOp *b, LinOp *x) const r->compute_dot(z.get(), rho.get()); ++iter; - this->template log(this, iter, r.get(), - dense_x); + this->template log( + this, iter, r.get(), dense_x, nullptr, rho.get()); if (stop_criterion->update() .num_iterations(iter) .residual(r.get()) diff --git a/core/solver/cgs.cpp b/core/solver/cgs.cpp index 99438ba4a1f..ffcf800b9d2 100644 --- a/core/solver/cgs.cpp +++ b/core/solver/cgs.cpp @@ -174,8 +174,8 @@ void Cgs::apply_impl(const LinOp *b, LinOp *x) const alpha.get(), &stop_status)); ++iter; - this->template log(this, iter, r.get(), - dense_x); + this->template log( + this, iter, r.get(), dense_x, nullptr, rho.get()); if (stop_criterion->update() .num_iterations(iter) .residual(r.get()) diff --git a/core/solver/fcg.cpp b/core/solver/fcg.cpp index 1e38cdae02d..bc41dfeca58 100644 --- a/core/solver/fcg.cpp +++ b/core/solver/fcg.cpp @@ -145,8 +145,8 @@ void Fcg::apply_impl(const LinOp *b, LinOp *x) const t->compute_dot(z.get(), rho_t.get()); ++iter; - this->template log(this, iter, r.get(), - dense_x); + this->template log( + this, iter, r.get(), dense_x, nullptr, rho.get()); if (stop_criterion->update() .num_iterations(iter) .residual(r.get()) diff --git a/core/test/log/papi.cpp b/core/test/log/papi.cpp index 9c72b8233e7..c5fb0d98f95 100644 --- a/core/test/log/papi.cpp +++ b/core/test/log/papi.cpp @@ -482,7 +482,7 @@ TYPED_TEST(Papi, CatchesIterationComplete) this->start(); this->logger->template on( - A.get(), 42, nullptr, nullptr, nullptr); + A.get(), 42, nullptr, nullptr, nullptr, nullptr); long long int value = 0; this->stop(&value); diff --git a/core/test/log/record.cpp b/core/test/log/record.cpp index 647691d517d..ffde5bb0e23 100644 --- a/core/test/log/record.cpp +++ b/core/test/log/record.cpp @@ -509,11 +509,12 @@ TEST(Record, CatchesIterations) auto residual = gko::initialize({-4.4}, exec); auto solution = gko::initialize({-2.2}, exec); auto residual_norm = gko::initialize({-3.3}, exec); + auto implicit_sq_residual_norm = gko::initialize({-3.5}, exec); logger->on( solver.get(), num_iters, residual.get(), solution.get(), - residual_norm.get()); + residual_norm.get(), implicit_sq_residual_norm.get()); auto &data = logger->get().iteration_completed.back(); ASSERT_NE(data->solver.get(), nullptr); @@ -522,6 +523,8 @@ TEST(Record, CatchesIterations) GKO_ASSERT_MTX_NEAR(gko::as(data->solution.get()), solution, 0); GKO_ASSERT_MTX_NEAR(gko::as(data->residual_norm.get()), residual_norm, 0); + GKO_ASSERT_MTX_NEAR(gko::as(data->implicit_sq_residual_norm.get()), + implicit_sq_residual_norm, 0); } diff --git a/core/test/log/stream.cpp b/core/test/log/stream.cpp index 3ea1fc8b5b9..42e5a665417 100644 --- a/core/test/log/stream.cpp +++ b/core/test/log/stream.cpp @@ -708,13 +708,15 @@ TYPED_TEST(Stream, CatchesIterations) auto residual = Dense::create(exec); auto solution = Dense::create(exec); auto residual_norm = Dense::create(exec); + auto implicit_sq_residual_norm = Dense::create(exec); std::stringstream ptrstream_solver; ptrstream_solver << solver.get(); std::stringstream ptrstream_residual; ptrstream_residual << residual.get(); logger->template on( - solver.get(), num_iters, residual.get()); + solver.get(), num_iters, residual.get(), solution.get(), + residual_norm.get(), implicit_sq_residual_norm.get()); GKO_ASSERT_STR_CONTAINS(out.str(), "iteration " + std::to_string(num_iters)); diff --git a/examples/custom-logger/custom-logger.cpp b/examples/custom-logger/custom-logger.cpp index 1caa5911dab..39fcb440eb1 100644 --- a/examples/custom-logger/custom-logger.cpp +++ b/examples/custom-logger/custom-logger.cpp @@ -90,21 +90,24 @@ struct ResidualLogger : gko::log::Logger { void write() const { // Print a header for the table - std::cout << "Recurrent vs true residual norm:" << std::endl; + std::cout << "Recurrent vs true vs implicit residual norm:" + << std::endl; std::cout << '|' << std::setw(10) << "Iteration" << '|' << std::setw(25) << "Recurrent Residual Norm" << '|' << std::setw(25) - << "True Residual Norm" << '|' << std::endl; + << "True Residual Norm" << '|' << std::setw(25) + << "Implicit Residual Norm" << '|' << std::endl; // Print a separation line. Note that for creating `10` characters // `std::setw()` should be set to `11`. std::cout << '|' << std::setfill('-') << std::setw(11) << '|' << std::setw(26) << '|' << std::setw(26) << '|' - << std::setfill(' ') << std::endl; + << std::setw(26) << '|' << std::setfill(' ') << std::endl; // Print the data one by one in the form std::cout << std::scientific; for (std::size_t i = 0; i < iterations.size(); i++) { std::cout << '|' << std::setw(10) << iterations[i] << '|' << std::setw(25) << recurrent_norms[i] << '|' - << std::setw(25) << real_norms[i] << '|' << std::endl; + << std::setw(25) << real_norms[i] << '|' << std::setw(25) + << implicit_norms[i] << '|' << std::endl; } // std::defaultfloat could be used here but some compilers // do not support it properly, e.g. the Intel compiler @@ -112,19 +115,30 @@ struct ResidualLogger : gko::log::Logger { // Print a separation line std::cout << '|' << std::setfill('-') << std::setw(11) << '|' << std::setw(26) << '|' << std::setw(26) << '|' - << std::setfill(' ') << std::endl; + << std::setw(26) << '|' << std::setfill(' ') << std::endl; } using gko_dense = gko::matrix::Dense; using gko_real_dense = gko::matrix::Dense; - // Customize the logging hook which is called everytime an iteration is - // completed - void on_iteration_complete(const gko::LinOp *, + // This overload is necessary to avoid interface breaks for Ginkgo 2.0 + void on_iteration_complete(const gko::LinOp *solver, const gko::size_type &iteration, const gko::LinOp *residual, const gko::LinOp *solution, const gko::LinOp *residual_norm) const override + { + this->on_iteration_complete(solver, iteration, residual, solution, + residual_norm, nullptr); + } + + // Customize the logging hook which is called everytime an iteration is + // completed + void on_iteration_complete( + const gko::LinOp *, const gko::size_type &iteration, + const gko::LinOp *residual, const gko::LinOp *solution, + const gko::LinOp *residual_norm, + const gko::LinOp *implicit_sq_residual_norm) const override { // If the solver shares a residual norm, log its value if (residual_norm) { @@ -164,6 +178,18 @@ struct ResidualLogger : gko::log::Logger { real_norms.push_back(-1.0); } + if (implicit_sq_residual_norm) { + auto dense_norm = + gko::as(implicit_sq_residual_norm); + // Add the norm to the `implicit_norms` vector + implicit_norms.push_back( + std::sqrt(get_first_element(gko::lend(dense_norm)))); + } else { + // Add to the `implicit_norms` vector the value -1.0 if it could not + // be computed + implicit_norms.push_back(-1.0); + } + // Add the current iteration number to the `iterations` vector iterations.push_back(iteration); } @@ -185,6 +211,8 @@ struct ResidualLogger : gko::log::Logger { mutable std::vector recurrent_norms{}; // Vector which stores all the real residual norms mutable std::vector real_norms{}; + // Vector which stores all the implicit residual norms + mutable std::vector implicit_norms{}; // Vector which stores all the iteration numbers mutable std::vector iterations{}; }; @@ -192,36 +220,36 @@ struct ResidualLogger : gko::log::Logger { int main(int argc, char *argv[]) { - // Use some shortcuts. In Ginkgo, vectors are seen as a gko::matrix::Dense - // with one column/one row. The advantage of this concept is that using - // multiple vectors is a now a natural extension of adding columns/rows are - // necessary. + // Use some shortcuts. In Ginkgo, vectors are seen as a + // gko::matrix::Dense with one column/one row. The advantage of this + // concept is that using multiple vectors is a now a natural extension + // of adding columns/rows are necessary. using ValueType = double; using RealValueType = gko::remove_complex; using IndexType = int; using vec = gko::matrix::Dense; using real_vec = gko::matrix::Dense; - // The gko::matrix::Csr class is used here, but any other matrix class such - // as gko::matrix::Coo, gko::matrix::Hybrid, gko::matrix::Ell or + // The gko::matrix::Csr class is used here, but any other matrix class + // such as gko::matrix::Coo, gko::matrix::Hybrid, gko::matrix::Ell or // gko::matrix::Sellp could also be used. using mtx = gko::matrix::Csr; - // The gko::solver::Cg is used here, but any other solver class can also be - // used. + // The gko::solver::Cg is used here, but any other solver class can also + // be used. using cg = gko::solver::Cg; // Print the ginkgo version information. std::cout << gko::version_info::get() << std::endl; // @sect3{Where do you want to run your solver ?} - // The gko::Executor class is one of the cornerstones of Ginkgo. Currently, - // we have support for - // an gko::OmpExecutor, which uses OpenMP multi-threading in most of its - // kernels, a gko::ReferenceExecutor, a single threaded specialization of - // the OpenMP executor and a gko::CudaExecutor which runs the code on a - // NVIDIA GPU if available. - // @note With the help of C++, you see that you only ever need to change the - // executor and all the other functions/ routines within Ginkgo should - // automatically work and run on the executor with any other changes. + // The gko::Executor class is one of the cornerstones of Ginkgo. + // Currently, we have support for an gko::OmpExecutor, which uses OpenMP + // multi-threading in most of its kernels, a gko::ReferenceExecutor, a + // single threaded specialization of the OpenMP executor and a + // gko::CudaExecutor which runs the code on a NVIDIA GPU if available. + // @note With the help of C++, you see that you only ever need to change + // the executor and all the other functions/ routines within Ginkgo + // should automatically work and run on the executor with any other + // changes. if (argc == 2 && (std::string(argv[1]) == "--help")) { std::cerr << "Usage: " << argv[0] << " [executor]" << std::endl; std::exit(-1); @@ -253,27 +281,27 @@ int main(int argc, char *argv[]) const auto exec = exec_map.at(executor_string)(); // throws if not valid // @sect3{Reading your data and transfer to the proper device.} - // Read the matrix, right hand side and the initial solution using the @ref - // read function. - // @note Ginkgo uses C++ smart pointers to automatically manage memory. To - // this end, we use our own object ownership transfer functions that under - // the hood call the required smart pointer functions to manage object - // ownership. The gko::share , gko::give and gko::lend are the functions - // that you would need to use. + // Read the matrix, right hand side and the initial solution using the + // @ref read function. + // @note Ginkgo uses C++ smart pointers to automatically manage memory. + // To this end, we use our own object ownership transfer functions that + // under the hood call the required smart pointer functions to manage + // object ownership. The gko::share , gko::give and gko::lend are the + // functions that you would need to use. auto A = share(gko::read(std::ifstream("data/A.mtx"), exec)); auto b = gko::read(std::ifstream("data/b.mtx"), exec); auto x = gko::read(std::ifstream("data/x0.mtx"), exec); const RealValueType reduction_factor = 1e-7; // @sect3{Creating the solver} - // Generate the gko::solver factory. Ginkgo uses the concept of Factories to - // build solvers with certain - // properties. Observe the Fluent interface used here. Here a cg solver is - // generated with a stopping criteria of maximum iterations of 20 and a - // residual norm reduction of 1e-15. You also observe that the stopping - // criteria(gko::stop) are also generated from factories using their build - // methods. You need to specify the executors which each of the object needs - // to be built on. + // Generate the gko::solver factory. Ginkgo uses the concept of + // Factories to build solvers with certain properties. Observe the + // Fluent interface used here. Here a cg solver is generated with a + // stopping criteria of maximum iterations of 20 and a residual norm + // reduction of 1e-15. You also observe that the stopping + // criteria(gko::stop) are also generated from factories using their + // build methods. You need to specify the executors which each of the + // object needs to be built on. auto solver_gen = cg::build() .with_criteria( @@ -287,8 +315,9 @@ int main(int argc, char *argv[]) auto logger = std::make_shared>( exec, gko::lend(A), gko::lend(b)); - // Add the previously created logger to the solver factory. The logger will - // be automatically propagated to all solvers created from this factory. + // Add the previously created logger to the solver factory. The logger + // will be automatically propagated to all solvers created from this + // factory. solver_gen->add_logger(logger); // Generate the solver from the matrix. The solver factory built in the @@ -302,9 +331,8 @@ int main(int argc, char *argv[]) auto solver = solver_gen->generate(A); - // Finally, solve the system. The solver, being a gko::LinOp, can be applied - // to a right hand side, b to - // obtain the solution, x. + // Finally, solve the system. The solver, being a gko::LinOp, can be + // applied to a right hand side, b to obtain the solution, x. solver->apply(gko::lend(b), gko::lend(x)); // Print the solution to the command line. @@ -314,13 +342,13 @@ int main(int argc, char *argv[]) // Print the table of the residuals obtained from the logger logger->write(); - // To measure if your solution has actually converged, you can measure the - // error of the solution. - // one, neg_one are objects that represent the numbers which allow for a - // uniform interface when computing on any device. To compute the residual, - // all you need to do is call the apply method, which in this case is an - // spmv and equivalent to the LAPACK z_spmv routine. Finally, you compute - // the euclidean 2-norm with the compute_norm2 function. + // To measure if your solution has actually converged, you can measure + // the error of the solution. one, neg_one are objects that represent + // the numbers which allow for a uniform interface when computing on any + // device. To compute the residual, all you need to do is call the apply + // method, which in this case is an spmv and equivalent to the LAPACK + // z_spmv routine. Finally, you compute the euclidean 2-norm with the + // compute_norm2 function. auto one = gko::initialize({1.0}, exec); auto neg_one = gko::initialize({-1.0}, exec); auto res = gko::initialize({0.0}, exec); diff --git a/examples/custom-logger/doc/results.dox b/examples/custom-logger/doc/results.dox index 13ef8f5b746..2d8185091a7 100644 --- a/examples/custom-logger/doc/results.dox +++ b/examples/custom-logger/doc/results.dox @@ -25,30 +25,30 @@ Solution (x): 0.0107016 0.0121141 0.0123025 -Recurrent vs true residual norm: -| Iteration| Recurrent Residual Norm| True Residual Norm| -|----------|-------------------------|-------------------------| -| 0| 4.358899e+00| 4.358899e+00| -| 1| 2.304548e+00| 2.304548e+00| -| 2| 1.467706e+00| 1.467706e+00| -| 3| 9.848751e-01| 9.848751e-01| -| 4| 7.418330e-01| 7.418330e-01| -| 5| 5.136231e-01| 5.136231e-01| -| 6| 3.841650e-01| 3.841650e-01| -| 7| 3.164394e-01| 3.164394e-01| -| 8| 2.277088e-01| 2.277088e-01| -| 9| 1.703121e-01| 1.703121e-01| -| 10| 9.737220e-02| 9.737220e-02| -| 11| 6.168306e-02| 6.168306e-02| -| 12| 4.541231e-02| 4.541231e-02| -| 13| 3.195304e-02| 3.195304e-02| -| 14| 1.616058e-02| 1.616058e-02| -| 15| 6.570152e-03| 6.570152e-03| -| 16| 2.643669e-03| 2.643669e-03| -| 17| 8.588089e-04| 8.588089e-04| -| 18| 2.864613e-04| 2.864613e-04| -| 19| 1.641952e-15| 2.107881e-15| -|----------|-------------------------|-------------------------| +Recurrent vs true vs implicit residual norm: +| Iteration| Recurrent Residual Norm| True Residual Norm| Implicit Residual Norm| +|----------|-------------------------|-------------------------|-------------------------| +| 0| 4.358899e+00| 4.358899e+00| 4.358899e+00| +| 1| 2.304548e+00| 2.304548e+00| 2.304548e+00| +| 2| 1.467706e+00| 1.467706e+00| 1.467706e+00| +| 3| 9.848751e-01| 9.848751e-01| 9.848751e-01| +| 4| 7.418330e-01| 7.418330e-01| 7.418330e-01| +| 5| 5.136231e-01| 5.136231e-01| 5.136231e-01| +| 6| 3.841650e-01| 3.841650e-01| 3.841650e-01| +| 7| 3.164394e-01| 3.164394e-01| 3.164394e-01| +| 8| 2.277088e-01| 2.277088e-01| 2.277088e-01| +| 9| 1.703121e-01| 1.703121e-01| 1.703121e-01| +| 10| 9.737220e-02| 9.737220e-02| 9.737220e-02| +| 11| 6.168306e-02| 6.168306e-02| 6.168306e-02| +| 12| 4.541231e-02| 4.541231e-02| 4.541231e-02| +| 13| 3.195304e-02| 3.195304e-02| 3.195304e-02| +| 14| 1.616058e-02| 1.616058e-02| 1.616058e-02| +| 15| 6.570152e-03| 6.570152e-03| 6.570152e-03| +| 16| 2.643669e-03| 2.643669e-03| 2.643669e-03| +| 17| 8.588089e-04| 8.588089e-04| 8.588089e-04| +| 18| 2.864613e-04| 2.864613e-04| 2.864613e-04| +| 19| 1.641952e-15| 2.107881e-15| 1.641952e-15| +|----------|-------------------------|-------------------------|-------------------------| Residual norm sqrt(r^T r): %%MatrixMarket matrix array real general 1 1 diff --git a/include/ginkgo/core/log/papi.hpp b/include/ginkgo/core/log/papi.hpp index 1f470dd1bb4..436dc59d1ef 100644 --- a/include/ginkgo/core/log/papi.hpp +++ b/include/ginkgo/core/log/papi.hpp @@ -177,6 +177,12 @@ class Papi : public Logger { const LinOp *residual, const LinOp *solution = nullptr, const LinOp *residual_norm = nullptr) const override; + void on_iteration_complete( + const LinOp *solver, const size_type &num_iterations, + const LinOp *residual, const LinOp *solution, + const LinOp *residual_norm, + const LinOp *implicit_sq_residual_norm) const override; + /** * Creates a Papi Logger. * diff --git a/include/ginkgo/core/log/record.hpp b/include/ginkgo/core/log/record.hpp index 2fe1d4295c8..eb20ae0a40a 100644 --- a/include/ginkgo/core/log/record.hpp +++ b/include/ginkgo/core/log/record.hpp @@ -62,16 +62,19 @@ struct iteration_complete_data { std::unique_ptr residual; std::unique_ptr solution; std::unique_ptr residual_norm; + std::unique_ptr implicit_sq_residual_norm; iteration_complete_data(const LinOp *solver, const size_type num_iterations, const LinOp *residual = nullptr, const LinOp *solution = nullptr, - const LinOp *residual_norm = nullptr) + const LinOp *residual_norm = nullptr, + const LinOp *implicit_sq_residual_norm = nullptr) : solver{nullptr}, num_iterations{num_iterations}, residual{nullptr}, solution{nullptr}, - residual_norm{nullptr} + residual_norm{nullptr}, + implicit_sq_residual_norm{nullptr} { this->solver = solver->clone(); if (residual != nullptr) { @@ -83,6 +86,10 @@ struct iteration_complete_data { if (residual_norm != nullptr) { this->residual_norm = residual_norm->clone(); } + if (implicit_sq_residual_norm != nullptr) { + this->implicit_sq_residual_norm = + implicit_sq_residual_norm->clone(); + } } }; @@ -369,6 +376,11 @@ class Record : public Logger { const LinOp *residual, const LinOp *solution = nullptr, const LinOp *residual_norm = nullptr) const override; + void on_iteration_complete( + const LinOp *solver, const size_type &num_iterations, + const LinOp *residual, const LinOp *solution, + const LinOp *residual_norm, + const LinOp *implicit_sq_residual_norm) const override; /** * Creates a Record logger. This dynamically allocates the memory, diff --git a/include/ginkgo/core/log/stream.hpp b/include/ginkgo/core/log/stream.hpp index 634ba7e2a9b..664ee5a47e4 100644 --- a/include/ginkgo/core/log/stream.hpp +++ b/include/ginkgo/core/log/stream.hpp @@ -153,6 +153,12 @@ class Stream : public Logger { const LinOp *residual, const LinOp *solution = nullptr, const LinOp *residual_norm = nullptr) const override; + void on_iteration_complete( + const LinOp *solver, const size_type &num_iterations, + const LinOp *residual, const LinOp *solution, + const LinOp *residual_norm, + const LinOp *implicit_sq_residual_norm) const override; + /** * Creates a Stream logger. This dynamically allocates the memory, * constructs the object and returns an std::unique_ptr to this object.