nonlinear solvers: tutorial 2#

Solve one variant of the Rosenbrock problem via Gauss-Newton and Levenberg-Marquardt.

#include "pressio/solvers_linear.hpp"
#include "pressio/solvers_nonlinear.hpp"
#include <Eigen/Core>
#include <optional>

struct MyRosenbrockSystem
{
  using scalar_type   = double;
  using state_type    = Eigen::VectorXd;
  using residual_type = state_type;
  using jacobian_type = Eigen::MatrixXd;

  state_type createState() const{
    auto s = state_type(4);
    s.setZero();
    return s;
  };

  residual_type createResidual() const{
    auto r = residual_type(6);
    r.setZero();
    return r;
  }

  jacobian_type createJacobian() const{
    auto j = jacobian_type(6, 4);
    j.setZero();
    return j;
  }

  void residualAndJacobian(const state_type& x,
			   residual_type& res,
			   std::optional<jacobian_type*> Jin) const
  {
    auto x1 = x(0);
    auto x2 = x(1);
    auto x3 = x(2);
    auto x4 = x(3);

    res(0) = 10.*(x4 - x3*x3);
    res(1) = 10.*(x3 - x2*x2);
    res(2) = 10.*(x2 - x1*x1);
    res(3) = (1.-x1);
    res(4) = (1.-x2);
    res(5) = (1.-x3);

    if (Jin){
      auto & J = *Jin.value();
      J.setZero();
      J(0,2) = -20.*x3; J(0,3) = 10.; // first row
      J(1,1) = -20.*x2; J(1,2) = 10.; // second row
      J(2,0) = -20.*x1; J(2,1) = 10.; // etc
      J(3,0) = -1.;
      J(4,1) = -1.;
      J(5,2) = -1.;
    }
  }
};

int main()
{
  namespace plog   = pressio::log;
  namespace pls    = pressio::linearsolvers;
  namespace pnonls = pressio::nonlinearsolvers;

  plog::initialize(pressio::logto::terminal);
  plog::setVerbosity({plog::level::info});

  MyRosenbrockSystem problem;

  using hessian_t = Eigen::MatrixXd;
  using linear_solver_tag = pls::direct::HouseholderQR;
  using linear_solver_t   = pls::Solver<linear_solver_tag, hessian_t>;
  linear_solver_t linearSolver;

  auto x0 = problem.createState();
  x0(0) = 0.0; x0(1) = 1.1; x0(2) = 1.2; x0(3) = 1.5;

  {
    auto solver = pressio::create_gauss_newton_solver(problem, linearSolver);
    solver.setStopTolerance(1e-5);

    auto x = pressio::ops::clone(x0);
    solver.solve(x);

    // check solution
    std::cout << "Computed solution: \n "
	      << "["
	      << std::setprecision(14)
	      << x(0) << " "
	      << x(1) << " "
	      << x(2) << " "
	      << x(3) << " " << "] \n"
	      << "exact minimum at: \n "
	      << "[1.0, 1.0, 1.0, 1.0] \n";
  }

  {
    auto solver = pressio::create_levenberg_marquardt_solver(problem, linearSolver);
    solver.setStopTolerance(1e-5);
    solver.setUpdateCriterion(pnonls::Update::LMSchedule2);

    auto x = pressio::ops::clone(x0);
    solver.solve(x);

    // check solution
    std::cout << "Computed solution: \n "
	      << "["
	      << std::setprecision(14)
	      << x(0) << " "
	      << x(1) << " "
	      << x(2) << " "
	      << x(3) << " " << "] \n"
	      << "exact minimum at: \n "
	      << "[1.0, 1.0, 1.0, 1.0] \n";
  }

  plog::finalize();
  return 0;
}