Skip to content

Commit

Permalink
Merge: Add more data to the convergence logger
Browse files Browse the repository at this point in the history
This PR adds the implicit res norm getter to the ConvergenceLogger.
It also adds a `has_converged` check which is true only when all the solutions have converged (from a stopping criterion perspective)
Additionally, a reset_convergence_status function has been added to the logger to allow resetting of the convergence status variable for subsequent solves. 

Related PR: #728
  • Loading branch information
pratikvn authored Apr 1, 2021
2 parents 40de7dc + b2b9bb6 commit 94e2361
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 8 deletions.
36 changes: 32 additions & 4 deletions core/log/convergence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,30 @@ namespace log {
template <typename ValueType>
void Convergence<ValueType>::on_criterion_check_completed(
const stop::Criterion *criterion, const size_type &num_iterations,
const LinOp *residual, const LinOp *residual_norm, const LinOp *solution,
const LinOp *residual, const LinOp *residual_norm,
const LinOp *implicit_sq_resnorm, const LinOp *solution,
const uint8 &stopping_id, const bool &set_finalized,
const Array<stopping_status> *status, const bool &oneChanged,
const bool &converged) const
const Array<stopping_status> *status, const bool &one_changed,
const bool &stopped) const
{
if (converged) {
if (stopped) {
Array<stopping_status> tmp(status->get_executor()->get_master(),
*status);
this->convergence_status_ = true;
for (int i = 0; i < status->get_num_elems(); i++) {
if (!tmp.get_data()[i].has_converged()) {
this->convergence_status_ = false;
break;
}
}
this->num_iterations_ = num_iterations;
if (residual != nullptr) {
this->residual_.reset(residual->clone().release());
}
if (implicit_sq_resnorm != nullptr) {
this->implicit_sq_resnorm_.reset(
implicit_sq_resnorm->clone().release());
}
if (residual_norm != nullptr) {
this->residual_norm_.reset(residual_norm->clone().release());
} else if (residual != nullptr) {
Expand All @@ -70,6 +84,20 @@ void Convergence<ValueType>::on_criterion_check_completed(
}


template <typename ValueType>
void Convergence<ValueType>::on_criterion_check_completed(
const stop::Criterion *criterion, const size_type &num_iterations,
const LinOp *residual, const LinOp *residual_norm, const LinOp *solution,
const uint8 &stopping_id, const bool &set_finalized,
const Array<stopping_status> *status, const bool &one_changed,
const bool &stopped) const
{
this->on_criterion_check_completed(
criterion, num_iterations, residual, residual_norm, nullptr, solution,
stopping_id, set_finalized, status, one_changed, stopped);
}


#define GKO_DECLARE_CONVERGENCE(_type) class Convergence<_type>
GKO_INSTANTIATE_FOR_EACH_VALUE_TYPE(GKO_DECLARE_CONVERGENCE);

Expand Down
1 change: 1 addition & 0 deletions core/test/log/convergence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ TYPED_TEST(Convergence, CanGetData)
auto logger = gko::log::Convergence<TypeParam>::create(
exec, gko::log::Logger::iteration_complete_mask);

ASSERT_EQ(logger->has_converged(), false);
ASSERT_EQ(logger->get_num_iterations(), 0);
ASSERT_EQ(logger->get_residual(), nullptr);
ASSERT_EQ(logger->get_residual_norm(), nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ int main(int argc, char *argv[])
.on(exec))
.on(exec);
// Create solver
solver_gen->add_logger(logger);
auto solver = solver_gen->generate(A);


Expand All @@ -148,11 +149,14 @@ int main(int argc, char *argv[])
auto res = gko::initialize<real_vec>({0.0}, exec);
A->apply(lend(one), lend(x), lend(neg_one), lend(b));
b->compute_norm2(lend(res));
auto impl_res = gko::as<real_vec>(logger->get_implicit_sq_resnorm());

std::cout << "Initial residual norm sqrt(r^T r):\n";
write(std::cout, lend(initres));
std::cout << "Final residual norm sqrt(r^T r):\n";
write(std::cout, lend(res));
std::cout << "Implicit residual norm squared (r^2):\n";
write(std::cout, lend(impl_res));

// Print solver statistics
std::cout << "CG iteration count: " << logger->get_num_iterations()
Expand Down
4 changes: 4 additions & 0 deletions examples/adaptiveprecision-blockjacobi/doc/results.dox
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Final residual norm sqrt(r^T r):
%%MatrixMarket matrix array real general
1 1
5.69384e-06
Implicit residual norm squared (r^2):
%%MatrixMarket matrix array real general
1 1
1.27043e-15
CG iteration count: 5
CG execution time [ms]: 0.080041

Expand Down
36 changes: 34 additions & 2 deletions include/ginkgo/core/log/convergence.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace log {
/**
* Convergence is a Logger which logs data strictly from the
* `criterion_check_completed` event. The purpose of this logger is to give a
* simple access to standard data generated by the solver once it has converged
* simple access to standard data generated by the solver once it has stopped
* with minimal overhead.
*
* This logger also computes the residual norm from the residual when the
Expand All @@ -70,7 +70,15 @@ class Convergence : public Logger {
const LinOp *residual, const LinOp *residual_norm,
const LinOp *solution, const uint8 &stopping_id,
const bool &set_finalized, const Array<stopping_status> *status,
const bool &one_changed, const bool &all_converged) const override;
const bool &one_changed, const bool &all_stopped) const override;

void on_criterion_check_completed(
const stop::Criterion *criterion, const size_type &num_iterations,
const LinOp *residual, const LinOp *residual_norm,
const LinOp *implicit_sq_resnorm, const LinOp *solution,
const uint8 &stopping_id, const bool &set_finalized,
const Array<stopping_status> *status, const bool &one_changed,
const bool &all_stopped) const override;

/**
* Creates a convergence logger. This dynamically allocates the memory,
Expand All @@ -94,6 +102,18 @@ class Convergence : public Logger {
new Convergence(exec, enabled_events));
}

/**
* Returns true if the solver has converged.
*
* @return the bool flag for convergence status
*/
bool has_converged() const noexcept { return convergence_status_; }

/**
* Resets the convergence status to false.
*/
void reset_convergence_status() { this->convergence_status_ = false; }

/**
* Returns the number of iterations
*
Expand Down Expand Up @@ -121,6 +141,16 @@ class Convergence : public Logger {
return residual_norm_.get();
}

/**
* Returns the implicit squared residual norm
*
* @return the implicit squared residual norm
*/
const LinOp *get_implicit_sq_resnorm() const noexcept
{
return implicit_sq_resnorm_.get();
}

protected:
/**
* Creates a Convergence logger.
Expand All @@ -136,9 +166,11 @@ class Convergence : public Logger {
{}

private:
mutable bool convergence_status_{false};
mutable size_type num_iterations_{};
mutable std::unique_ptr<LinOp> residual_{};
mutable std::unique_ptr<LinOp> residual_norm_{};
mutable std::unique_ptr<LinOp> implicit_sq_resnorm_{};
};


Expand Down
47 changes: 47 additions & 0 deletions include/ginkgo/core/log/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,13 +381,46 @@ public: \
* @param status the stopping status of the right hand sides
* @param one_changed whether at least one right hand side converged or not
* @param all_converged whether all right hand sides
*
* @note The on_criterion_check_completed function that this macro declares
* is deprecated. Please use the one with the additional implicit_tau_sq
* parameter as below.
*/
GKO_LOGGER_REGISTER_EVENT(
20, criterion_check_completed, const stop::Criterion *criterion,
const size_type &it, const LinOp *r, const LinOp *tau, const LinOp *x,
const uint8 &stopping_id, const bool &set_finalized,
const Array<stopping_status> *status, const bool &one_changed,
const bool &all_converged)
protected:
/**
* stop::Criterion's check completed event. Parameters are the Criterion,
* the stoppingId, the finalized boolean, the stopping status, plus the
* output one_changed boolean and output all_converged boolean.
*
* @param criterion the criterion used
* @param it the current iteration count
* @param r the residual
* @param tau the residual norm
* @param implicit_tau_sq the implicit residual norm squared
* @param x the solution
* @param stopping_id the id of the stopping criterion
* @param set_finalized whether this finalizes the iteration
* @param status the stopping status of the right hand sides
* @param one_changed whether at least one right hand side converged or not
* @param all_converged whether all right hand sides
*/
virtual void on_criterion_check_completed(
const stop::Criterion *criterion, const size_type &it, const LinOp *r,
const LinOp *tau, const LinOp *implicit_tau_sq, const LinOp *x,
const uint8 &stopping_id, const bool &set_finalized,
const Array<stopping_status> *status, const bool &one_changed,
const bool &all_converged) const
{
this->on_criterion_check_completed(criterion, it, r, x, tau, x,
stopping_id, set_finalized, status,
one_changed, all_converged);
}

/**
* Register the `iteration_complete` event which logs every completed
Expand All @@ -397,12 +430,26 @@ public: \
* @param r the residual
* @param x the solution vector (optional)
* @param tau the residual norm (optional)
*
* @note The on_iteration_complete function that this macro declares is
* deprecated. Please use the one with the additional implicit_tau_sq
* parameter as below.
*/
GKO_LOGGER_REGISTER_EVENT(21, iteration_complete, const LinOp *solver,
const size_type &it, const LinOp *r,
const LinOp *x = nullptr,
const LinOp *tau = nullptr)
protected:
/**
* Register the `iteration_complete` event which logs every completed
* iterations.
*
* @param it the current iteration count
* @param r the residual
* @param x the solution vector (optional)
* @param tau the residual norm (optional)
* @param implicit_tau_sq the implicit residual norm squared (optional)
*/
virtual void on_iteration_complete(const LinOp *solver, const size_type &it,
const LinOp *r, const LinOp *x,
const LinOp *tau,
Expand Down
5 changes: 3 additions & 2 deletions include/ginkgo/core/stop/criterion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ class Criterion : public EnableAbstractPolymorphicObject<Criterion> {
stopping_id, set_finalized, stop_status, one_changed, updater);
this->template log<log::Logger::criterion_check_completed>(
this, updater.num_iterations_, updater.residual_,
updater.residual_norm_, updater.solution_, stopping_id,
set_finalized, stop_status, *one_changed, all_converged);
updater.residual_norm_, updater.implicit_sq_residual_norm_,
updater.solution_, stopping_id, set_finalized, stop_status,
*one_changed, all_converged);
return all_converged;
}

Expand Down
119 changes: 119 additions & 0 deletions reference/test/log/convergence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ TYPED_TEST(Convergence, CatchesCriterionCheckCompleted)
nullptr, nullptr, nullptr);
constexpr gko::uint8 RelativeStoppingId{42};
gko::Array<gko::stopping_status> stop_status(exec, 1);
stop_status.get_data()[0].reset();
using Mtx = gko::matrix::Dense<TypeParam>;
using NormVector = gko::matrix::Dense<gko::remove_complex<TypeParam>>;
auto residual = gko::initialize<Mtx>({1.0, 2.0, 2.0}, exec);
Expand All @@ -73,6 +74,124 @@ TYPED_TEST(Convergence, CatchesCriterionCheckCompleted)
RelativeStoppingId, true, &stop_status, true, true);

ASSERT_EQ(logger->get_num_iterations(), 1);
ASSERT_EQ(logger->has_converged(), false);
GKO_ASSERT_MTX_NEAR(gko::as<Mtx>(logger->get_residual()),
l({1.0, 2.0, 2.0}), 0.0);
GKO_ASSERT_MTX_NEAR(gko::as<NormVector>(logger->get_residual_norm()),
l({3.0}), 0.0);
}


TYPED_TEST(Convergence, CatchesCriterionCheckCompletedWithConvCheck)
{
auto exec = gko::ReferenceExecutor::create();
auto logger = gko::log::Convergence<TypeParam>::create(
exec, gko::log::Logger::criterion_check_completed_mask);
auto criterion =
gko::stop::Iteration::build().with_max_iters(3u).on(exec)->generate(
nullptr, nullptr, nullptr);
constexpr gko::uint8 RelativeStoppingId{42};
gko::Array<gko::stopping_status> stop_status(exec, 1);
stop_status.get_data()[0].reset();
stop_status.get_data()[0].converge(0);
using Mtx = gko::matrix::Dense<TypeParam>;
using NormVector = gko::matrix::Dense<gko::remove_complex<TypeParam>>;
auto residual = gko::initialize<Mtx>({1.0, 2.0, 2.0}, exec);

logger->template on<gko::log::Logger::criterion_check_completed>(
criterion.get(), 3, residual.get(), nullptr, nullptr,
RelativeStoppingId, true, &stop_status, true, true);

ASSERT_EQ(logger->get_num_iterations(), 3);
ASSERT_EQ(logger->has_converged(), true);
GKO_ASSERT_MTX_NEAR(gko::as<Mtx>(logger->get_residual()),
l({1.0, 2.0, 2.0}), 0.0);
GKO_ASSERT_MTX_NEAR(gko::as<NormVector>(logger->get_residual_norm()),
l({3.0}), 0.0);
}


TYPED_TEST(Convergence, CatchesCriterionCheckCompletedWithStopCheck)
{
auto exec = gko::ReferenceExecutor::create();
auto logger = gko::log::Convergence<TypeParam>::create(
exec, gko::log::Logger::criterion_check_completed_mask);
auto criterion =
gko::stop::Iteration::build().with_max_iters(3u).on(exec)->generate(
nullptr, nullptr, nullptr);
constexpr gko::uint8 RelativeStoppingId{42};
gko::Array<gko::stopping_status> stop_status(exec, 1);
stop_status.get_data()[0].reset();
stop_status.get_data()[0].stop(0);
using Mtx = gko::matrix::Dense<TypeParam>;
using NormVector = gko::matrix::Dense<gko::remove_complex<TypeParam>>;
auto residual = gko::initialize<Mtx>({1.0, 2.0, 2.0}, exec);

logger->template on<gko::log::Logger::criterion_check_completed>(
criterion.get(), 3, residual.get(), nullptr, nullptr,
RelativeStoppingId, true, &stop_status, true, true);

ASSERT_EQ(logger->get_num_iterations(), 3);
ASSERT_EQ(logger->has_converged(), false);
GKO_ASSERT_MTX_NEAR(gko::as<Mtx>(logger->get_residual()),
l({1.0, 2.0, 2.0}), 0.0);
GKO_ASSERT_MTX_NEAR(gko::as<NormVector>(logger->get_residual_norm()),
l({3.0}), 0.0);
}


TYPED_TEST(Convergence, CanResetConvergenceStatus)
{
auto exec = gko::ReferenceExecutor::create();
auto logger = gko::log::Convergence<TypeParam>::create(
exec, gko::log::Logger::criterion_check_completed_mask);
auto criterion =
gko::stop::Iteration::build().with_max_iters(3u).on(exec)->generate(
nullptr, nullptr, nullptr);
constexpr gko::uint8 RelativeStoppingId{42};
gko::Array<gko::stopping_status> stop_status(exec, 1);
stop_status.get_data()[0].reset();
stop_status.get_data()[0].converge(0);
using Mtx = gko::matrix::Dense<TypeParam>;
using NormVector = gko::matrix::Dense<gko::remove_complex<TypeParam>>;
auto residual = gko::initialize<Mtx>({1.0, 2.0, 2.0}, exec);

logger->template on<gko::log::Logger::criterion_check_completed>(
criterion.get(), 3, residual.get(), nullptr, nullptr,
RelativeStoppingId, true, &stop_status, true, true);
ASSERT_EQ(logger->get_num_iterations(), 3);
ASSERT_EQ(logger->has_converged(), true);

logger->reset_convergence_status();

ASSERT_EQ(logger->has_converged(), false);
}


TYPED_TEST(Convergence, CatchesCriterionCheckCompletedWithImplicitNorm)
{
auto exec = gko::ReferenceExecutor::create();
auto logger = gko::log::Convergence<TypeParam>::create(
exec, gko::log::Logger::criterion_check_completed_mask);
auto criterion =
gko::stop::Iteration::build().with_max_iters(3u).on(exec)->generate(
nullptr, nullptr, nullptr);
constexpr gko::uint8 RelativeStoppingId{42};
gko::Array<gko::stopping_status> stop_status(exec, 1);
stop_status.get_data()[0].reset();
using Mtx = gko::matrix::Dense<TypeParam>;
using NormVector = gko::matrix::Dense<gko::remove_complex<TypeParam>>;
auto residual = gko::initialize<Mtx>({1.0, 2.0, 2.0}, exec);
auto implicit_sq_resnorm = gko::initialize<Mtx>({4.0}, exec);

logger->template on<gko::log::Logger::criterion_check_completed>(
criterion.get(), 1, residual.get(), nullptr, implicit_sq_resnorm.get(),
nullptr, RelativeStoppingId, true, &stop_status, true, true);

ASSERT_EQ(logger->get_num_iterations(), 1);
ASSERT_EQ(logger->has_converged(), false);
GKO_ASSERT_MTX_NEAR(gko::as<Mtx>(logger->get_implicit_sq_resnorm()),
l({4.0}), 0.0);
GKO_ASSERT_MTX_NEAR(gko::as<Mtx>(logger->get_residual()),
l({1.0, 2.0, 2.0}), 0.0);
GKO_ASSERT_MTX_NEAR(gko::as<NormVector>(logger->get_residual_norm()),
Expand Down

0 comments on commit 94e2361

Please sign in to comment.