steps.hpp limits serif/solver/nonlinear/qualification.hpp src/include/serif/solver/nonlinear/newton.hpp serif::solver::detail::InitialEvaluation serif::solver::detail::QualifiedStep serif serif::solver serif::solver::detail #pragmaonce #include<limits> #include"serif/solver/nonlinear/qualification.hpp" namespaceserif::solver::detail{ voidvalidate_start(constauto&options,conststd::optional<NewtonProgress>&restart,std::size_tsize){ usingnamespaceutils::errors; options.method.validate(); options.line_search.validate(); ifconstexpr(requires{options.linear_solver.validate();}){ options.linear_solver.validate(); } if(!restart){ return; } serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(checkpointable_options<std::remove_cvref_t<decltype(options)>>, "NewtonrestartrequiresStandardNewton,NormalizedResidualMetricandFGMRES.Custompoliciesrequirerestorationoftheirnumericalhistory."); if(constautoerror=validate_newton_progress(*restart,size)){ serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(false,"InvalidNewtoncheckpoint:{}",*error); } serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(restart->next_step<=options.line_search.initial_step&& *restart->diagnostics.convergence_threshold==options.method.convergence_threshold(*restart->diagnostics.initial_residual_norm), "TherestartchangesNewton'soriginaltoleranceortrial-stepbound.Restorethearchivednumericaloptionsbeforecontinuing."); } template<typenameSystem> structInitialEvaluation{ std::shared_ptr<constSystem>system; MetricEvaluationmetric; }; template<typenameProblem> [[nodiscard]]std::expected<InitialEvaluation<typenameProblem::System>,NewtonFailure>evaluate_start( Problem&problem,auto&state,auto&options,conststd::optional<NewtonProgress>&restart,SolveDiagnostics&diagnostics ){ usingnamespaceutils::errors; autoevaluation=problem.evaluate(state); TrialAssessmentinitialized; if(evaluation&&!restart){ initialized=options.strategies.initialize(problem,state,evaluation,options); } constautometric=qualify_candidate(problem,state,evaluation,options.metric,options.strategies,initialized); if(!metric){ returnstd::unexpected(metric.error()); } if(restart){ constdoubleallowance=256.0*std::numeric_limits<double>::epsilon()*std::max(1.0,restart->accepted_metric.residual_norm); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(std::abs(metric->residual_norm-restart->accepted_metric.residual_norm)<=allowance&& std::abs(metric->merit-restart->accepted_metric.merit)<=allowance*std::max(1.0,restart->accepted_metric.residual_norm), "Therebuiltstardoesnotreproducethecheckpoint'sacceptedresidualmetric.Restorethesamemodel,discretization,normalization,prescribedinputsandmetric."); }else{ diagnostics.initial_residual_norm=metric->residual_norm; diagnostics.convergence_threshold=options.method.convergence_threshold(metric->residual_norm); } diagnostics.final_residual_norm=metric->residual_norm; returnInitialEvaluation<typename Problem::System>{.system=std::move(*evaluation),.metric=*metric}; } [[nodiscard]]std::expected<DirectionAssessment,NewtonFailure>compute_direction( auto&problem,constauto&accepted,constauto&prepared,auto&options,SolveDiagnostics&diagnostics, auto&rhs,auto&correction,std::span<double>physical_direction ){ constintsize=static_cast<int>(physical_direction.size()); utils::errors::serif_ensure<utils::errors::SERiFErrorCode::INVALID_DOF_COUNT>( std::ranges::size(rhs)==accepted.system()->residual().size(), "TheresidualchangedsizeduringNewton.Preservetheequationlayoutthroughoutonesolve."); std::ranges::transform(accepted.system()->residual(),rhs.begin(),[](doublevalue){return-value;}); constlinear::Reportreport=options.strategies.solve(problem,accepted.state(),prepared,rhs,correction,options); diagnostics.last_linear_solve=report; diagnostics.total_linear_iterations+=report.iterations; if(!finite_correction(std::span(correction),size)||!report.converged()){ returnstd::unexpected(NewtonFailure{.status=SolveStatus::linear_solve_failure, .message="TheNewtonlinearsolvedidnotproduceafinitecorrectionmeetingitstrue-residualtolerance."}); } DirectionAssessmentdirection=options.strategies.direction(problem,accepted.state(),prepared,rhs,correction,options); if(!direction.usable||!finite_correction(std::span(correction),size)|| (direction.merit_slope&&(!std::isfinite(*direction.merit_slope)||*direction.merit_slope>=0.0))){ returnstd::unexpected(NewtonFailure{.status=SolveStatus::globalization_failure, .message=direction.message.empty()?"Thenumericaldirectionpolicyfoundnofinitedecreasingdirection.":direction.message}); } accepted.system()->coordinates().denormalize_state(std::span(correction),physical_direction); returndirection; } template<typenameState,typenameSystem,typenamePrepared> structQualifiedStep{ Statestate; std::shared_ptr<constSystem>system; MetricEvaluationmetric; std::optional<Prepared>preparation; doublestep; inttrial; }; template<typenameProblem,typenameState,typenameSystem,typenameModel> [[nodiscard]]autofind_acceptable_step( Problem&problem,constSolveResult<State, System, Model>&accepted,MetricEvaluationaccepted_metric, constauto&correction,std::span<constdouble>physical_direction,constDirectionAssessment&direction, doublestep,auto&options,SolveDiagnostics&diagnostics,auto&observation ){ usingPrepared=typenamedecltype(problem.prepare(accepted.state()))::value_type; usingStep=QualifiedStep<State, System, Prepared>; usingResult=std::expected<Step,NewtonFailure>; constintiteration=diagnostics.attempted_iterations-1; ifconstexpr(requires{problem.geometry_limit(accepted.state(),physical_direction,step, options.line_search.fraction_to_boundary,options.line_search.geometry_preflight);}){ constautolimit=problem.geometry_limit(accepted.state(),physical_direction,step, options.line_search.fraction_to_boundary,options.line_search.geometry_preflight); if(!limit){ returnResult(std::unexpected(NewtonFailure{.status=SolveStatus::globalization_failure, .message=problem.failure_message(limit.error())})); } diagnostics.last_geometry_preflight=*limit; if(options.line_search.geometry_preflight){ step=limit->step; diagnostics.geometry_limited_iterations+=limit->limited_by_geometry; } } std::stringrejection="Backtrackingexhausteditstrialorstep-lengthbudget."; for(inttrial=0;trial<options.line_search.maximum_trials&&step>=options.line_search.minimum_step;++trial){ ++diagnostics.line_search_trials; Statecandidate=accepted.state(); for(std::size_tindex=0;index<physical_direction.size();++index){ candidate.values()[index]+=step*physical_direction[index]; } doublerequired_merit=(1.0-options.line_search.sufficient_decrease*step)*accepted_metric.merit; if(direction.merit_slope){ required_merit=accepted_metric.merit+options.line_search.sufficient_decrease*step**direction.merit_slope; } autoevaluation=problem.evaluate(candidate); TrialAssessmentassessment; if(evaluation){ assessment=options.strategies.trial(problem,accepted.state(),accepted.system(),correction,step, candidate,evaluation,options,required_merit); } constautometric=qualify_candidate(problem,candidate,evaluation,options.metric,options.strategies,std::move(assessment)); if(metric&&metric->merit<=required_merit){ std::optional<Prepared>preparation; if(metric->residual_norm>*diagnostics.convergence_threshold){ autoprepared=problem.prepare(candidate); if(constautofailure=preparation_rejection(problem,prepared)){ returnResult(std::unexpected(NewtonFailure{.status=SolveStatus::preparation_failure,.message=*failure})); } preparation=std::move(*prepared); } returnResult(Step{.state=std::move(candidate),.system=std::move(*evaluation),.metric=*metric, .preparation=std::move(preparation),.step=step,.trial=trial}); } rejection=metric?"Thetrialdidnotsufficientlyreducethecompleteresidualmerit.":metric.error().message; ++diagnostics.rejected_trials; observation.notify(EventKind::trial_rejected,candidate,evaluation?*evaluation:nullptr,iteration,trial,step, metric?std::optional(*metric):std::nullopt,rejection); doublefollowing=step*options.line_search.contraction; ifconstexpr(requires{problem.next_step(step,options.line_search,evaluation);}){ if(!evaluation){ following=problem.next_step(step,options.line_search,evaluation); } } step=following; } returnResult(std::unexpected(NewtonFailure{.status=SolveStatus::globalization_failure,.message=std::move(rejection)})); } voidsave_progress(auto&result,auto&options,SolveDiagnostics&diagnostics,MetricEvaluationmetric, std::span<constdouble>correction,doublenext_step){ ifconstexpr(checkpointable_options<std::remove_cvref_t<decltype(options)>>){ options.strategies.contribute_diagnostics(diagnostics); result.checkpoint(NewtonProgress{.accepted_metric=metric,.correction={correction.begin(),correction.end()}, .next_step=next_step,.diagnostics=diagnostics}); } } }