12#ifndef DUMUX_NONLINEAR_LEASTSQUARES_HH
13#define DUMUX_NONLINEAR_LEASTSQUARES_HH
19#include <dune/common/exceptions.hh>
20#include <dune/istl/bvector.hh>
21#include <dune/istl/matrix.hh>
22#if HAVE_SUITESPARSE_CHOLMOD
23#include <dune/istl/cholmod.hh>
25#include <dune/istl/io.hh>
35template<
class Variables>
40 virtual bool apply(Variables& vars) = 0;
49template<
class T,
class F>
54 using JacobianMatrix = Dune::Matrix<T>;
55 using ResidualType = Dune::BlockVector<T>;
56 using SolutionVector = Dune::BlockVector<T>;
57 using Variables = Dune::BlockVector<T>;
65 Assembler(
const F& f,
const SolutionVector& x0, std::size_t residualSize)
66 : prevSol_(x0), f_(f), solSize_(x0.size()), residualSize_(residualSize)
67 , JT_(solSize_, residualSize_), regularizedJTJ_(solSize_, solSize_)
68 , residual_(residualSize_), projectedResidual_(solSize_)
70 printMatrix_ = getParamFromGroup<bool>(
"LevenbergMarquardt",
"PrintMatrix",
false);
71 baseEpsilon_ = getParamFromGroup<Scalar>(
"LevenbergMarquardt",
"BaseEpsilon", 1e-1);
75 void setLinearSystem()
77 std::cout <<
"Setting up linear system with " << solSize_ <<
" variables and "
78 << residualSize_ <<
" residuals." << std::endl;
81 regularizedJTJ_ = 0.0;
83 projectedResidual_ = 0.0;
87 JacobianMatrix& jacobian() {
return regularizedJTJ_; }
90 ResidualType& residual() {
return projectedResidual_; }
93 void setLambda(
const T lambda) { lambda_ = lambda; }
94 T lambda() {
return lambda_; }
97 void assembleResidual(
const SolutionVector& x)
99 projectedResidual_ = 0.0;
103 for (
auto rowIt = JT_.begin(); rowIt != JT_.end(); ++rowIt)
105 const auto paramIdx = rowIt.index();
106 const auto residual = f_(x);
108 const auto eps = baseEpsilon_;
109 p[paramIdx] = x[paramIdx] + eps;
110 auto deflectedResidual = f_(p);
111 deflectedResidual -= residual;
112 deflectedResidual /= eps;
113 for (
auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt)
114 *colIt += deflectedResidual[colIt.index()];
116 projectedResidual_[paramIdx] = residual * deflectedResidual;
121 std::cout << std::endl <<
"J^T = " << std::endl;
122 Dune::printmatrix(std::cout, JT_,
"",
"");
127 void assembleJacobianAndResidual(
const SolutionVector& x)
131 regularizedJTJ_ = 0.0;
132 for (
auto rowIt = regularizedJTJ_.begin(); rowIt != regularizedJTJ_.end(); ++rowIt)
134 for (
auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt)
136 for (
int j = 0; j < residualSize_; ++j)
137 *colIt += JT_[rowIt.index()][j]*JT_[colIt.index()][j];
139 if (rowIt.index() == colIt.index())
141 *colIt += lambda_*(*colIt);
144 *colIt = 1e-6*lambda_;
151 std::cout << std::endl <<
"J^T J + λ diag(J^T J) = " << std::endl;
152 Dune::printmatrix(std::cout, regularizedJTJ_,
"",
"");
157 T computeMeanSquaredError(
const SolutionVector& x)
const
158 {
return f_(x).two_norm2(); }
161 const SolutionVector& prevSol()
const
165 SolutionVector prevSol_;
167 std::size_t solSize_, residualSize_;
170 JacobianMatrix regularizedJTJ_;
171 ResidualType residual_;
172 ResidualType projectedResidual_;
174 Scalar lambda_ = 0.0;
177 bool printMatrix_ =
false;
180#if HAVE_SUITESPARSE_CHOLMOD
181template<
class Matrix,
class Vector>
182class CholmodLinearSolver
185 void setResidualReduction(
double residualReduction) {}
187 bool solve(
const Matrix& A, Vector& x,
const Vector& b)
const
189 Dune::Cholmod<Vector> solver;
191 Dune::InverseOperatorResult r;
194 solver.apply(xCopy, bCopy, r);
195 checkResult_(xCopy, r);
197 DUNE_THROW(NumericalProblem,
"Linear solver did not converge.");
202 auto norm(
const Vector& residual)
const
203 {
return residual.two_norm(); }
206 void checkResult_(Vector& x, Dune::InverseOperatorResult& result)
const
208 flatVectorForEach(x, [&](
auto&& entry, std::size_t){
209 using std::isnan, std::isinf;
210 if (isnan(entry) || isinf(entry))
211 result.converged =
false;
229template<
class Assembler,
class LinearSolver>
233 using Scalar =
typename Assembler::Scalar;
234 using Variables =
typename Assembler::Variables;
235 using SolutionVector =
typename Assembler::SolutionVector;
236 using ResidualVector =
typename Assembler::ResidualType;
247 maxLambdaDivisions_ = getParamFromGroup<std::size_t>(
ParentType::paramGroup(),
"LevenbergMarquardt.MaxLambdaDivisions", 10);
262 bool apply(Variables& vars)
override
265 for (std::size_t i = 0; i <= maxLambdaDivisions_; ++i)
273 else if (!converged && i < maxLambdaDivisions_)
276 std::cout << Fmt::format(
"LevenbergMarquardt solver did not converge with λ = {:.2e}. ",
ParentType::assembler().lambda())
288 std::cout << Fmt::format(
"Choose best solution so far with a MSE of {:.4e}", minResidual_) << std::endl;
298 void newtonEndStep(Variables &vars,
const SolutionVector &uLastIter)
override
339 bestSol_ = uLastIter;
343 void lineSearchUpdate_(Variables& vars,
344 const SolutionVector& uLastIter,
345 const ResidualVector& deltaU)
override
348 auto uCurrentIter = uLastIter;
352 Backend::axpy(-alpha_, deltaU, uCurrentIter);
368 uCurrentIter = uLastIter;
372 Scalar minResidual_ = std::numeric_limits<Scalar>::max();
373 SolutionVector bestSol_;
374 std::size_t maxLambdaDivisions_ = 10;
378#if HAVE_SUITESPARSE_CHOLMOD
379template<
class T,
class F>
380class NonlinearLeastSquaresSolver :
public Solver<typename Optimization::Detail::Assembler<T, F>::Variables>
382 using Assembler = Optimization::Detail::Assembler<T, F>;
383 using LinearSolver = Optimization::Detail::CholmodLinearSolver<typename Assembler::JacobianMatrix, typename Assembler::ResidualType>;
384 using Optimizer = Optimization::Detail::LevenbergMarquardt<Assembler, LinearSolver>;
386 using Variables =
typename Assembler::Variables;
388 NonlinearLeastSquaresSolver(
const F& f,
const Dune::BlockVector<T>& x0, std::size_t size)
389 : solver_(std::make_unique<Optimizer>(std::make_shared<Assembler>(f, x0, size), std::make_shared<LinearSolver>(), 2))
392 bool apply(Variables& vars)
override
393 {
return solver_->apply(vars); }
396 std::unique_ptr<Optimizer> solver_;
404#if HAVE_SUITESPARSE_CHOLMOD
416template<
class T,
class F>
417auto makeNonlinearLeastSquaresSolver(
const F& f,
const Dune::BlockVector<T>& x0, std::size_t size)
418-> std::unique_ptr<Solver<Dune::BlockVector<T>>>
419{
return std::make_unique<Optimization::Detail::NonlinearLeastSquaresSolver<T, F>>(f, x0, size); }
An implementation of a Newton solver.
Definition: nonlinear/newtonsolver.hh:181
const std::string & paramGroup() const
Returns the parameter group.
Definition: nonlinear/newtonsolver.hh:821
Scalar reduction_
Definition: nonlinear/newtonsolver.hh:890
int numSteps_
actual number of steps done so far
Definition: nonlinear/newtonsolver.hh:887
int verbosity() const
Return the verbosity level.
Definition: nonlinear/newtonsolver.hh:803
virtual void newtonEndStep(Variables &vars, const SolutionVector &uLastIter)
Indicates that one Newton iteration was finished.
Definition: nonlinear/newtonsolver.hh:608
virtual void solutionChanged_(Variables &vars, const SolutionVector &uCurrentIter)
Update solution-dependent quantities like grid variables after the solution has changed.
Definition: nonlinear/newtonsolver.hh:855
bool apply(Variables &vars) override
Run the Newton method to solve a non-linear system. The solver is responsible for all the strategic d...
Definition: nonlinear/newtonsolver.hh:377
Scalar initialResidual_
Definition: nonlinear/newtonsolver.hh:893
Scalar lastReduction_
Definition: nonlinear/newtonsolver.hh:892
void setUseLineSearch(bool val=true)
Specify whether line search is enabled or not.
Definition: nonlinear/newtonsolver.hh:809
std::ostringstream endIterMsgStream_
message stream to be displayed at the end of iterations
Definition: nonlinear/newtonsolver.hh:900
VariablesBackend< typename ParentType::Variables > Backend
Definition: nonlinear/newtonsolver.hh:185
void setMaxRelativeShift(Scalar tolerance)
Set the maximum acceptable difference of any primary variable between two iterations for declaring co...
Definition: nonlinear/newtonsolver.hh:248
Scalar residualNorm_
Definition: nonlinear/newtonsolver.hh:891
A nonlinear least squares solver with model parameters and observations.
Definition: leastsquares.hh:231
bool apply(Variables &vars) override
Solve the nonlinear least squares problem.
Definition: leastsquares.hh:262
LevenbergMarquardt(std::shared_ptr< Assembler > assembler, std::shared_ptr< LinearSolver > linearSolver, int verbosity=0)
Definition: leastsquares.hh:241
a solver base class
Definition: leastsquares.hh:37
virtual ~Solver()=default
virtual bool apply(Variables &vars)=0
const LinearSolver & linearSolver() const
Access the linear solver.
Definition: common/pdesolver.hh:133
const Assembler & assembler() const
Access the assembler.
Definition: common/pdesolver.hh:121
Some exceptions thrown in DuMux
Definition: leastsquares.hh:220
Definition: leastsquares.hh:32
Reference implementation of a Newton solver.
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.