Thyra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Thyra_DampenedNewtonNonlinearSolver.hpp
1 // @HEADER
2 // *****************************************************************************
3 // Thyra: Interfaces and Support for Abstract Numerical Algorithms
4 //
5 // Copyright 2004 NTESS and the Thyra contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef THYRA_DAMPENED_NEWTON_NONLINEAR_SOLVER_HPP
11 #define THYRA_DAMPENED_NEWTON_NONLINEAR_SOLVER_HPP
12 
13 #include "Thyra_NonlinearSolverBase.hpp"
14 #include "Thyra_ModelEvaluatorHelpers.hpp"
15 #include "Thyra_TestingTools.hpp"
16 #include "Teuchos_StandardMemberCompositionMacros.hpp"
17 #include "Teuchos_StandardCompositionMacros.hpp"
18 #include "Teuchos_VerboseObject.hpp"
19 #include "Teuchos_VerboseObjectParameterListHelpers.hpp"
20 #include "Teuchos_StandardParameterEntryValidators.hpp"
21 #include "Teuchos_as.hpp"
22 
23 
24 namespace Thyra {
25 
26 
47 template <class Scalar>
49 public:
50 
54  typedef typename ST::magnitudeType ScalarMag;
57 
60 
62  STANDARD_MEMBER_COMPOSITION_MEMBERS( int, defaultMaxNewtonIterations );
63 
65  STANDARD_MEMBER_COMPOSITION_MEMBERS( bool, useDampenedLineSearch );
66 
68  STANDARD_MEMBER_COMPOSITION_MEMBERS( Scalar, armijoConstant );
69 
71  STANDARD_MEMBER_COMPOSITION_MEMBERS( int, maxLineSearchIterations );
72 
75  const ScalarMag defaultTol = 1e-2
76  ,const int defaultMaxNewtonIterations = 1000
77  ,const bool useDampenedLineSearch = true
78  ,const Scalar armijoConstant = 1e-4
79  ,const int maxLineSearchIterations = 20
80  );
81 
85 
88 
90  void setParameterList(RCP<Teuchos::ParameterList> const& paramList);
99 
101 
104 
106  void setModel(
107  const RCP<const ModelEvaluator<Scalar> > &model
108  );
114  const SolveCriteria<Scalar> *solveCriteria,
115  VectorBase<Scalar> *delta
116  );
120  bool is_W_current() const;
122  RCP<LinearOpWithSolveBase<Scalar> > get_nonconst_W(const bool forceUpToDate);
126  void set_W_is_current(bool W_is_current);
127 
129 
130 private:
131 
132  RCP<Teuchos::ParameterList> paramList_;
135  RCP<VectorBase<Scalar> > current_x_;
136  bool J_is_current_;
137 
138 };
139 
140 // ////////////////////////
141 // Defintions
142 
143 template <class Scalar>
145  const ScalarMag my_defaultTol
146  ,const int my_defaultMaxNewtonIterations
147  ,const bool my_useDampenedLineSearch
148  ,const Scalar my_armijoConstant
149  ,const int my_maxLineSearchIterations
150  )
151  :defaultTol_(my_defaultTol)
152  ,defaultMaxNewtonIterations_(my_defaultMaxNewtonIterations)
153  ,useDampenedLineSearch_(my_useDampenedLineSearch)
154  ,armijoConstant_(my_armijoConstant)
155  ,maxLineSearchIterations_(my_maxLineSearchIterations)
156  ,J_is_current_(false)
157 {}
158 
159 template <class Scalar>
162 {
163  static RCP<const Teuchos::ParameterList> validSolveCriteriaExtraParameters;
164  if(!validSolveCriteriaExtraParameters.get()) {
166  paramList = Teuchos::rcp(new Teuchos::ParameterList);
167  paramList->set("Max Iters",int(1000));
168  validSolveCriteriaExtraParameters = paramList;
169  }
170  return validSolveCriteriaExtraParameters;
171 }
172 
173 // Overridden from Teuchos::ParameterListAcceptor
174 
175 template<class Scalar>
177  RCP<Teuchos::ParameterList> const& paramList
178  )
179 {
180  using Teuchos::get;
181  TEUCHOS_TEST_FOR_EXCEPT(is_null(paramList));
182  paramList->validateParametersAndSetDefaults(*getValidParameters(),0);
183  paramList_ = paramList;
184  TEUCHOS_TEST_FOR_EXCEPT("ToDo: Implement!");
185  Teuchos::readVerboseObjectSublist(&*paramList_,this);
186 #ifdef TEUCHOS_DEBUG
187  paramList_->validateParameters(*getValidParameters(),0);
188 #endif // TEUCHOS_DEBUG
189 }
190 
191 template<class Scalar>
194 {
195  return paramList_;
196 }
197 
198 template<class Scalar>
201 {
202  RCP<Teuchos::ParameterList> _paramList = paramList_;
203  paramList_ = Teuchos::null;
204  return _paramList;
205 }
206 
207 template<class Scalar>
210 {
211  return paramList_;
212 }
213 
214 template<class Scalar>
217 {
218  using Teuchos::setDoubleParameter; using Teuchos::setIntParameter;
219  static RCP<const Teuchos::ParameterList> validPL;
220  if (is_null(validPL)) {
222  pl = Teuchos::parameterList();
223  TEUCHOS_TEST_FOR_EXCEPT("ToDo: Implement!");
224  Teuchos::setupVerboseObjectSublist(&*pl);
225  validPL = pl;
226  }
227  return validPL;
228 }
229 
230 // Overridden from NonlinearSolverBase
231 
232 template <class Scalar>
234  const RCP<const ModelEvaluator<Scalar> > &model
235  )
236 {
237  TEUCHOS_TEST_FOR_EXCEPT(model.get()==NULL);
238  model_ = model;
239  J_ = Teuchos::null;
240  current_x_ = Teuchos::null;
241  J_is_current_ = false;
242 }
243 
244 template <class Scalar>
247 {
248  return model_;
249 }
250 
251 template <class Scalar>
254  VectorBase<Scalar> *x_inout
255  ,const SolveCriteria<Scalar> *solveCriteria
256  ,VectorBase<Scalar> *delta
257  )
258 {
259 
260  using std::endl;
261  using Teuchos::as;
262 
263  // Validate input
264 #ifdef TEUCHOS_DEBUG
265  TEUCHOS_TEST_FOR_EXCEPT(0==x_inout);
267  "DampenedNewtonNonlinearSolver<Scalar>::solve(...)",
268  *x_inout->space(), *model_->get_x_space() );
269 #endif
270 
271  // Get the output stream and verbosity level
272  const RCP<Teuchos::FancyOStream> out = this->getOStream();
273  const Teuchos::EVerbosityLevel verbLevel = this->getVerbLevel();
274  const bool showNewtonIters = (verbLevel==Teuchos::VERB_LOW);
275  const bool showLineSearchIters = (as<int>(verbLevel) >= as<int>(Teuchos::VERB_MEDIUM));
276  const bool showNewtonDetails = (as<int>(verbLevel) >= as<int>(Teuchos::VERB_HIGH));
277  const bool dumpAll = (as<int>(verbLevel) == as<int>(Teuchos::VERB_EXTREME));
279  if(out.get() && showNewtonIters) {
280  *out << "\nBeginning dampended Newton solve of model = " << model_->description() << "\n\n";
281  if (!useDampenedLineSearch())
282  *out << "\nDoing undampened newton ...\n\n";
283  }
284 
285  // Initialize storage for algorithm
286  if(!J_.get()) J_ = model_->create_W();
287  RCP<VectorBase<Scalar> > f = createMember(model_->get_f_space());
288  RCP<VectorBase<Scalar> > x = Teuchos::rcp(x_inout,false);
289  RCP<VectorBase<Scalar> > dx = createMember(model_->get_x_space());
290  RCP<VectorBase<Scalar> > x_new = createMember(model_->get_x_space());
291  RCP<VectorBase<Scalar> > ee = createMember(model_->get_x_space());
292  V_S(ee.ptr(),ST::zero());
293 
294  // Get convergence criteria
295  ScalarMag tol = this->defaultTol();
296  int maxIters = this->defaultMaxNewtonIterations();
297  if(solveCriteria && !solveCriteria->solveMeasureType.useDefault()) {
300  ,"DampenedNewtonNonlinearSolver<Scalar>::solve(...): Error, can only support resudual-based"
301  " convergence criteria!");
302  tol = solveCriteria->requestedTol;
303  if(solveCriteria->extraParameters.get()) {
304  solveCriteria->extraParameters->validateParameters(*getValidSolveCriteriaExtraParameters());
305  maxIters = solveCriteria->extraParameters->get("Max Iters",int(maxIters));
306  }
307  }
308 
309  if(out.get() && showNewtonDetails)
310  *out << "\nCompute the initial starting point ...\n";
311 
312  eval_f_W( *model_, *x, &*f, &*J_ );
313  if(out.get() && dumpAll) {
314  *out << "\nInitial starting point:\n";
315  *out << "\nx =\n" << *x;
316  *out << "\nf =\n" << *f;
317  *out << "\nJ =\n" << *J_;
318  }
319 
320  // Peform the Newton iterations
321  int newtonIter, num_residual_evals = 1;
322  SolveStatus<Scalar> solveStatus;
324 
325  for( newtonIter = 1; newtonIter <= maxIters; ++newtonIter ) {
326 
327  if(out.get() && showNewtonDetails) *out << "\n*** newtonIter = " << newtonIter << endl;
328 
329  // Check convergence
330  if(out.get() && showNewtonDetails) *out << "\nChecking for convergence ... : ";
331  const Scalar phi = scalarProd(*f,*f), sqrt_phi = ST::squareroot(phi); // merit function: phi(f) = <f,f>
332  solveStatus.achievedTol = sqrt_phi;
333  const bool isConverged = sqrt_phi <= tol;
334  if(out.get() && showNewtonDetails) *out
335  << "sqrt(phi) = sqrt(<f,f>) = ||f|| = " << sqrt_phi << ( isConverged ? " <= " : " > " ) << "tol = " << tol << endl;
336  if(out.get() && showNewtonIters) *out
337  << "newton_iter="<<newtonIter<<": Check convergence: ||f|| = "
338  << sqrt_phi << ( isConverged ? " <= " : " > " ) << "tol = " << tol << ( isConverged ? ", Converged!!!" : "" ) << endl;
339  if(isConverged) {
340  if(x_inout != x.get()) assign( ptr(x_inout), *x ); // Assign the solution if we have to
341  if(out.get() && showNewtonDetails) {
342  *out << "\nWe have converged :-)\n"
343  << "\n||x||inf = " << norm_inf(*x) << endl;
344  if(dumpAll) *out << "\nx =\n" << *x;
345  *out << "\nExiting SimpleNewtonSolver::solve(...)\n";
346  }
347  std::ostringstream oss;
348  oss << "Converged! ||f|| = " << sqrt_phi << ", num_newton_iters="<<newtonIter<<", num_residual_evals="<<num_residual_evals<<".";
349  solveStatus.solveStatus = SOLVE_STATUS_CONVERGED;
350  solveStatus.message = oss.str();
351  break;
352  }
353  if(out.get() && showNewtonDetails) *out << "\nWe have to keep going :-(\n";
354 
355  // Compute the Jacobian if we have not already
356  if(newtonIter > 1) {
357  if(out.get() && showNewtonDetails) *out << "\nComputing the Jacobian J_ at current point ...\n";
358  eval_f_W<Scalar>( *model_, *x, NULL, &*J_ );
359  if(out.get() && dumpAll) *out << "\nJ =\n" << *J_;
360  }
361 
362  // Compute the newton step: dx = -inv(J)*f
363  if(out.get() && showNewtonDetails) *out << "\nComputing the Newton step: dx = - inv(J)*f ...\n";
364  if(out.get() && showNewtonIters) *out << "newton_iter="<<newtonIter<<": Computing Newton step ...\n";
365  assign( dx.ptr(), ST::zero() ); // Initial guess for the linear solve
366  J_->solve(NOTRANS,*f,dx.ptr()); // Solve: J*dx = f
367  Vt_S( dx.ptr(), Scalar(-ST::one()) ); // dx *= -1.0
368  Vp_V( ee.ptr(), *dx); // ee += dx
369  if(out.get() && showNewtonDetails) *out << "\n||dx||inf = " << norm_inf(*dx) << endl;
370  if(out.get() && dumpAll) *out << "\ndy =\n" << *dx;
371 
372  // Perform backtracking armijo line search
373  if(out.get() && showNewtonDetails) *out << "\nStarting backtracking line search iterations ...\n";
374  if(out.get() && showNewtonIters) *out << "newton_iter="<<newtonIter<<": Starting backtracking line search ...\n";
375  const Scalar Dphi = -2.0*phi; // D(phi(x+alpha*dx))/D(alpha) at alpha=0.0 => -2.0*<f,c>: where dx = -inv(J)*f
376  Scalar alpha = 1.0; // Try a full step initially since it will eventually be accepted near solution
377  int lineSearchIter;
378  ++num_residual_evals;
379  for( lineSearchIter = 1; lineSearchIter <= maxLineSearchIterations(); ++lineSearchIter, ++num_residual_evals ) {
380  TEUCHOS_OSTAB_DIFF(lineSearchIter);
381  if(out.get() && showNewtonDetails) *out << "\n*** lineSearchIter = " << lineSearchIter << endl;
382  // x_new = x + alpha*dx
383  assign( x_new.ptr(), *x ); Vp_StV( x_new.ptr(), alpha, *dx );
384  if(out.get() && showNewtonDetails) *out << "\n||x_new||inf = " << norm_inf(*x_new) << endl;
385  if(out.get() && dumpAll) *out << "\nx_new =\n" << *x_new;
386  // Compute the residual at the updated point
387  eval_f(*model_,*x_new,&*f);
388  if(out.get() && dumpAll) *out << "\nf_new =\n" << *f;
389  const Scalar phi_new = scalarProd(*f,*f), phi_frac = phi + alpha * armijoConstant() * Dphi;
390  if(out.get() && showNewtonDetails) *out << "\nphi_new = <f_new,f_new> = " << phi_new << endl;
392  if(out.get() && showNewtonDetails) *out << "\nphi_new is not a valid number, backtracking (alpha = 0.1*alpha) ...\n";
393  alpha *= 0.1;
394  continue;
395  }
396  const bool acceptPoint = (phi_new <= phi_frac);
397  if(out.get() && showNewtonDetails) *out
398  << "\nphi_new = " << phi_new << ( acceptPoint ? " <= " : " > " )
399  << "phi + alpha * eta * Dphi = " << phi << " + " << alpha << " * " << armijoConstant() << " * " << Dphi
400  << " = " << phi_frac << endl;
401  if(out.get() && (showLineSearchIters || (showNewtonIters && acceptPoint))) *out
402  << "newton_iter="<<newtonIter<<", ls_iter="<<lineSearchIter<<" : "
403  << "phi(alpha="<<alpha<<") = "<<phi_new<<(acceptPoint ? " <=" : " >")<<" armijo_cord = " << phi_frac << endl;
404  if (out.get() && showNewtonDetails && !useDampenedLineSearch())
405  *out << "\nUndamped newton, always accpeting the point!\n";
406  if( acceptPoint || !useDampenedLineSearch() ) {
407  if(out.get() && showNewtonDetails) *out << "\nAccepting the current step with step length alpha = " << alpha << "!\n";
408  break;
409  }
410  if(out.get() && showNewtonDetails) *out << "\nBacktracking (alpha = 0.5*alpha) ...\n";
411  alpha *= 0.5;
412  }
413 
414  // Check for line search failure
415  if( lineSearchIter > maxLineSearchIterations() ) {
416  std::ostringstream oss;
417  oss
418  << "lineSearchIter = " << lineSearchIter << " > maxLineSearchIterations = " << maxLineSearchIterations()
419  << ": Linear search failure! Algorithm terminated!";
420  solveStatus.message = oss.str();
421  if(out.get() && (showNewtonIters || showNewtonDetails)) *out << endl << oss.str() << endl;
422  goto exit;
423  }
424 
425  // Take the Newton step
426  std::swap<RCP<VectorBase<Scalar> > >( x_new, x ); // Now x is current point!
427 
428  }
429 
430 exit:
431 
432  if(out.get() && showNewtonIters) *out
433  << "\n[Final] newton_iters="<<newtonIter<<", num_residual_evals="<<num_residual_evals<<"\n";
434 
435  if(newtonIter > maxIters) {
436  std::ostringstream oss;
437  oss
438  << "newton_iter = " << newtonIter << " > maxIters = " << maxIters
439  << ": Newton algorithm terminated!";
440  solveStatus.message = oss.str();
441  if( out.get() && (showNewtonIters || showNewtonDetails)) *out << endl << oss.str() << endl;
442  }
443 
444  if(x_inout != x.get()) assign( ptr(x_inout), *x ); // Assign the final point
445  if(delta != NULL) assign( ptr(delta), *ee );
446  current_x_ = x_inout->clone_v(); // Remember the final point
447  J_is_current_ = newtonIter==1; // J is only current with x if initial point was converged!
448 
449  if(out.get() && showNewtonDetails) *out
450  << "\n*** Ending dampended Newton solve." << endl;
451 
452  return solveStatus;
453 
454 }
455 
456 template <class Scalar>
459 {
460  return current_x_;
461 }
462 
463 template <class Scalar>
465 {
466  return J_is_current_;
467 }
468 
469 template <class Scalar>
472 {
473  if (forceUpToDate) {
474  TEUCHOS_TEST_FOR_EXCEPT(forceUpToDate);
475  }
476  return J_;
477 }
478 
479 template <class Scalar>
482 {
483  return J_;
484 }
485 
486 template <class Scalar>
488 {
489  J_is_current_ = W_is_current;
490 }
491 
492 
493 } // namespace Thyra
494 
495 
496 #endif // THYRA_DAMPENED_NEWTON_NONLINEAR_SOLVER_HPP
virtual RCP< const VectorSpaceBase< Scalar > > space() const =0
Return a smart pointer to the vector space that this vector belongs to.
Pure abstract base interface for evaluating a stateless &quot;model&quot; that can be mapped into a number of d...
SolveStatus< Scalar > solve(VectorBase< Scalar > *x, const SolveCriteria< Scalar > *solveCriteria, VectorBase< Scalar > *delta)
#define THYRA_ASSERT_VEC_SPACES(FUNC_NAME, VS1, VS2)
This is a very useful macro that should be used to validate that two vector spaces are compatible...
ScalarMag requestedTol
The requested solve tolerance (what the client would like to see). Only significant if !this-&gt;solveMe...
bool is_null(const boost::shared_ptr< T > &p)
bool useDefault() const
Return if this is a default solve measure (default constructed).
Simple dampended Newton solver using a Armijo line search :-)
std::string message
A simple one-line message (i.e. no newlines) returned from the solver.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Use the non-transposed operator.
Base class for all nonlinear equation solvers.
T * get() const
ParameterList & set(std::string const &name, T &&value, std::string const &docString="", RCP< const ParameterEntryValidator > const &validator=null)
void setParameterList(RCP< Teuchos::ParameterList > const &paramList)
DampenedNewtonNonlinearSolver(const ScalarMag defaultTol=1e-2, const int defaultMaxNewtonIterations=1000, const bool useDampenedLineSearch=true, const Scalar armijoConstant=1e-4, const int maxLineSearchIterations=20)
#define TEUCHOS_OSTAB_DIFF(DIFF)
RCP< const ModelEvaluator< Scalar > > getModel() const
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
RCP< const VectorBase< Scalar > > get_current_x() const
Ptr< T > ptr() const
Abstract interface for finite-dimensional dense vectors.
void validateParametersAndSetDefaults(ParameterList const &validParamList, int const depth=1000)
Simple struct for the return status from a solve.
Norm of the right-hand side (i.e. ||b||)
RCP< const Teuchos::ParameterList > getValidParameters() const
ESolveStatus solveStatus
The return status of the solve.
#define TEUCHOS_OSTAB
ScalarMag achievedTol
The maximum final tolerance actually achieved by the (block) linear solve. A value of unknownToleranc...
RCP< ParameterList > extraParameters
Any extra control parameters (e.g. max iterations).
void setModel(const RCP< const ModelEvaluator< Scalar > > &model)
TypeTo as(const TypeFrom &t)
STANDARD_MEMBER_COMPOSITION_MEMBERS(ScalarMag, defaultTol)
The default solution tolerance.
static RCP< const Teuchos::ParameterList > getValidSolveCriteriaExtraParameters()
RCP< const Teuchos::ParameterList > getParameterList() const
SolveMeasureType solveMeasureType
The type of solve tolerance requested as given in this-&gt;requestedTol.
The requested solution criteria has likely been achieved.
Exception type thrown on an catastrophic solve failure.
RCP< const LinearOpWithSolveBase< Scalar > > get_W() const
The requested solution criteria has likely not been achieved.
Simple struct that defines the requested solution criteria for a solve.
RCP< LinearOpWithSolveBase< Scalar > > get_nonconst_W(const bool forceUpToDate)
virtual RCP< VectorBase< Scalar > > clone_v() const =0
Returns a seprate cloned copy of *this vector with the same values but different storage.
#define TEUCHOS_TEST_FOR_EXCEPT(throw_exception_test)
Norm of the current residual vector (i.e. ||A*x-b||)