Thyra Version of the Day
Loading...
Searching...
No Matches
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
24namespace Thyra {
25
26
47template <class Scalar>
49public:
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
130private:
131
135 RCP<VectorBase<Scalar> > current_x_;
136 bool J_is_current_;
137
138};
139
140// ////////////////////////
141// Defintions
142
143template <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
159template <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
175template<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
191template<class Scalar>
197
198template<class Scalar>
201{
202 RCP<Teuchos::ParameterList> _paramList = paramList_;
203 paramList_ = Teuchos::null;
204 return _paramList;
205}
206
207template<class Scalar>
210{
211 return paramList_;
212}
213
214template<class Scalar>
217{
218 using Teuchos::setDoubleParameter; using Teuchos::setIntParameter;
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
232template <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
244template <class Scalar>
247{
248 return model_;
249}
250
251template <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<<".";
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
430exit:
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
456template <class Scalar>
459{
460 return current_x_;
461}
462
463template <class Scalar>
465{
466 return J_is_current_;
467}
468
469template <class Scalar>
472{
473 if (forceUpToDate) {
474 TEUCHOS_TEST_FOR_EXCEPT(forceUpToDate);
475 }
476 return J_;
477}
478
479template <class Scalar>
482{
483 return J_;
484}
485
486template <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
Ptr< T > ptr() const
T * get() const
Exception type thrown on an catastrophic solve failure.
Simple dampended Newton solver using a Armijo line search :-)
void setParameterList(RCP< Teuchos::ParameterList > const &paramList)
STANDARD_MEMBER_COMPOSITION_MEMBERS(int, maxLineSearchIterations)
Set the maximum number of backtracking line search iterations to take.
DampenedNewtonNonlinearSolver(const ScalarMag defaultTol=1e-2, const int defaultMaxNewtonIterations=1000, const bool useDampenedLineSearch=true, const Scalar armijoConstant=1e-4, const int maxLineSearchIterations=20)
STANDARD_MEMBER_COMPOSITION_MEMBERS(bool, useDampenedLineSearch)
The default maximum number of iterations.
RCP< const VectorBase< Scalar > > get_current_x() const
RCP< const Teuchos::ParameterList > getValidParameters() const
void setModel(const RCP< const ModelEvaluator< Scalar > > &model)
SolveStatus< Scalar > solve(VectorBase< Scalar > *x, const SolveCriteria< Scalar > *solveCriteria, VectorBase< Scalar > *delta)
RCP< LinearOpWithSolveBase< Scalar > > get_nonconst_W(const bool forceUpToDate)
STANDARD_MEMBER_COMPOSITION_MEMBERS(Scalar, armijoConstant)
Set the armijo constant for the line search.
RCP< const ModelEvaluator< Scalar > > getModel() const
static RCP< const Teuchos::ParameterList > getValidSolveCriteriaExtraParameters()
STANDARD_MEMBER_COMPOSITION_MEMBERS(int, defaultMaxNewtonIterations)
The default maximum number of iterations.
STANDARD_MEMBER_COMPOSITION_MEMBERS(ScalarMag, defaultTol)
The default solution tolerance.
RCP< const LinearOpWithSolveBase< Scalar > > get_W() const
RCP< const Teuchos::ParameterList > getParameterList() const
Pure abstract base interface for evaluating a stateless "model" that can be mapped into a number of d...
Base class for all nonlinear equation solvers.
Abstract interface for finite-dimensional dense vectors.
virtual RCP< VectorBase< Scalar > > clone_v() const =0
Returns a seprate cloned copy of *this vector with the same values but different storage.
virtual RCP< const VectorSpaceBase< Scalar > > space() const =0
Return a smart pointer to the vector space that this vector belongs to.
#define TEUCHOS_TEST_FOR_EXCEPT(throw_exception_test)
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
@ SOLVE_STATUS_UNCONVERGED
The requested solution criteria has likely not been achieved.
@ SOLVE_STATUS_CONVERGED
The requested solution criteria has likely been achieved.
@ SOLVE_MEASURE_NORM_RESIDUAL
Norm of the current residual vector (i.e. ||A*x-b||)
@ SOLVE_MEASURE_NORM_RHS
Norm of the right-hand side (i.e. ||b||)
#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.
@ NOTRANS
Use the non-transposed operator.
TypeTo as(const TypeFrom &t)
T_To & dyn_cast(T_From &from)
#define TEUCHOS_OSTAB_DIFF(DIFF)
#define TEUCHOS_OSTAB
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Simple struct that defines the requested solution criteria for a solve.
ScalarMag requestedTol
The requested solve tolerance (what the client would like to see). Only significant if !...
RCP< ParameterList > extraParameters
Any extra control parameters (e.g. max iterations).
SolveMeasureType solveMeasureType
The type of solve tolerance requested as given in this->requestedTol.
bool useDefault() const
Return if this is a default solve measure (default constructed).
Simple struct for the return status from a solve.
std::string message
A simple one-line message (i.e. no newlines) returned from the solver.
ESolveStatus solveStatus
The return status of the solve.
ScalarMag achievedTol
The maximum final tolerance actually achieved by the (block) linear solve. A value of unknownToleranc...