driver.hpp memory vector serif/solver/continuation/options.hpp src/include/serif/solver/continuation/continuation.hpp src/include/serif/solver/continuation/equilibrium.hpp serif::solver::continuation::CorrectionControls serif::solver::continuation::StageRecord serif::solver::continuation::Result serif::solver::continuation::StartingStar serif serif::solver serif::solver::continuation serif::solver::continuation::detail #pragmaonce #include<memory> #include<vector> #include"serif/solver/continuation/options.hpp" namespaceserif::solver::continuation{ enumclassStatus{target_reached,starting_star_failed,attempt_limit,work_limit,no_progress,invalid_model}; enumclassStageDisposition{converged,not_converged,invalid_model}; structCorrectionControlsfinal{ doubleabsolute_tolerance{}; std::uint64_tmaximum_work{}; }; template<std::size_tCount> structStageRecordfinal{ std::array<double,Count>coordinates{}; std::optional<std::size_t>changed_axis; boolstarting_star{false}; StageDispositiondisposition{StageDisposition::not_converged}; std::uint64_twork{}; std::optional<double>residual_norm; std::stringmessage; }; template<typenameReport,std::size_tCount> structResultfinal{ Statusstatus{Status::starting_star_failed}; std::stringmessage; std::array<double,Count>target{}; std::optional<std::array<double,Count>>accepted_coordinates; std::unique_ptr<Report>accepted; std::unique_ptr<Report>failed; std::vector<StageRecord<Count>>stages; CoordinateCounterscounters{}; std::uint64_tstarting_attempts{}; std::uint64_tstarting_work{}; std::uint64_tomitted_records{}; doubleabsolute_tolerance{}; [[nodiscard]]boolconverged()constnoexcept{returnstatus==Status::target_reached;} [[nodiscard]]constReport*last_converged()constnoexcept{returnaccepted.get();} [[nodiscard]]std::uint64_ttotal_attempts()constnoexcept{returnstarting_attempts+counters.attempts;} [[nodiscard]]std::uint64_ttotal_work()constnoexcept{returnstarting_work+counters.work;} }; template<typenameModel,typenameState> structStartingStarfinal{ Modelmodel; Statestate; }; namespacedetail{ template<typenameFamily> [[nodiscard]]std::vector<typenameFamily::Coordinates>starting_candidates(constFamily&family,constOptions<Family>&options){ std::vector<typenameFamily::Coordinates>result{options.start}; if(!options.explicit_start){ constautotarget=family.target_coordinates(); for(std::size_taxis=0;axis<Family::coordinate_count;++axis){ autocandidate=options.start; candidate[axis]=target[axis]; if(std::ranges::find(result,candidate)==result.end()){ result.push_back(candidate); } } if(std::ranges::find(result,target)==result.end()){ result.push_back(target); } } returnresult; } template<typenameReport,std::size_tCount> voidrecord(Result<Report, Count>&result,StageRecord<Count>stage,std::size_tmaximum){ if(maximum==0){ ++result.omitted_records; }elseif(result.stages.size()<maximum){ result.stages.push_back(std::move(stage)); }else{ result.stages.back()=std::move(stage); ++result.omitted_records; } } } template<typenameFamily,typenameState,typenameCorrector> [[nodiscard]]autorun(constFamily&family,constState&initial_state,Corrector&&correct,Options<Family>options,doubleabsolute_tolerance){ usingnamespaceutils::errors; usingReport=std::remove_cvref_t<std::invoke_result_t<Corrector&,typenameFamily::ModelType,State,CorrectionControls>>; usingRecord=StageRecord<Family::coordinate_count>; constexprstd::size_tcount=Family::coordinate_count; serif_ensure<SERiFErrorCode::INVALID_ARGUMENT,std::invalid_argument>(std::isfinite(absolute_tolerance)&&absolute_tolerance>0.0&&options.maximum_work>0, "Continuationrequiresafixedpositiveabsolutetoleranceandapositivetotalworkbudget."); constCoordinateController<count>validation(options.start,family.target_coordinates(),options.steps); Result<Report, count>result{.target=family.target_coordinates(),.absolute_tolerance=absolute_tolerance}; result.stages.reserve(std::min<std::uint64_t>(options.maximum_records,options.steps.maximum_attempts)); //Bothinitialqualificationandlatertrialsusethissamecertificationruleandphysical-stateboundary. constautoattempt=[&](consttypenameFamily::Coordinates&coordinates,constState&state,boolstarting,std::optional<std::size_t>axis){ Recordstage{.coordinates=coordinates,.changed_axis=axis,.starting_star=starting}; automodel=family.try_model_at(coordinates); std::unique_ptr<Report>report; if(!model){ stage.disposition=StageDisposition::invalid_model; stage.message=model.error().message; }else{ conststd::uint64_tremaining=options.maximum_work-result.total_work(); report=std::make_unique<Report>(std::invoke(correct,std::move(*model),State(state),CorrectionControls{absolute_tolerance,remaining})); stage.work=static_cast<std::uint64_t>(report->diagnostics().attempted_iterations); serif_ensure<SERiFErrorCode::INVALID_ARGUMENT>(stage.work<=remaining,"Acontinuationcorrectorexceededitssuppliedworkbudget."); stage.residual_norm=report->diagnostics().final_residual_norm; constboolcertified=report->converged()&&report->has_state()&&stage.residual_norm&& std::isfinite(*stage.residual_norm)&&*stage.residual_norm>=0.0&&*stage.residual_norm<=absolute_tolerance; stage.disposition=certified?StageDisposition::converged:StageDisposition::not_converged; stage.message=report->message(); } returnstd::pair{std::move(report),std::move(stage)}; }; for(constauto&coordinates:detail::starting_candidates(family,options)){ if(result.starting_attempts==options.steps.maximum_attempts||result.total_work()==options.maximum_work){ break; } ++result.starting_attempts; auto[report,stage]=attempt(coordinates,initial_state,true,std::nullopt); result.starting_work+=stage.work; constStageDispositiondisposition=stage.disposition; conststd::stringmessage=stage.message; detail::record(result,std::move(stage),options.maximum_records); if(disposition==StageDisposition::invalid_model){ result.status=Status::invalid_model; result.message=message; returnresult; } if(disposition==StageDisposition::converged){ result.accepted.swap(report); result.accepted_coordinates=coordinates; break; } result.failed.swap(report); } if(!result.accepted){ result.status=Status::starting_star_failed; result.message="Nopermittedstartingmodelproducedastarcertifiedatthefixedcontinuationtolerance."; returnresult; } if(*result.accepted_coordinates==result.target){ result.status=Status::target_reached; returnresult; } if(result.total_attempts()==options.steps.maximum_attempts){ result.status=Status::attempt_limit; returnresult; } autosteps=options.steps; steps.maximum_attempts-=result.starting_attempts; CoordinateController<count>controller(*result.accepted_coordinates,result.target,steps); while(result.total_work()<options.maximum_work){ constautoproposal=controller.propose(); if(!proposal){ break; } auto[report,stage]=attempt(proposal->coordinates,result.accepted->state(),false,proposal->axis); constStageDispositiondisposition=stage.disposition; AttemptOutcomeoutcome=AttemptOutcome::recoverable_rejection; if(disposition==StageDisposition::converged){ outcome=AttemptOutcome::success; }elseif(disposition==StageDisposition::invalid_model){ outcome=AttemptOutcome::fatal_failure; } conststd::uint64_twork=stage.work; conststd::stringmessage=stage.message; controller.validate_resolution(proposal->id,outcome,work); detail::record(result,std::move(stage),options.maximum_records); controller.resolve(proposal->id,outcome,work); if(outcome==AttemptOutcome::success){ result.accepted.swap(report); result.accepted_coordinates=controller.accepted(); }else{ result.failed.swap(report); } result.counters=controller.counters(); if(outcome==AttemptOutcome::fatal_failure){ result.status=Status::invalid_model; result.message=message; returnresult; } if(controller.status()==ControllerStatus::complete){ result.status=Status::target_reached; returnresult; } } result.counters=controller.counters(); if(result.total_work()==options.maximum_work){ result.status=Status::work_limit; }elseif(controller.status()==ControllerStatus::attempt_limit){ result.status=Status::attempt_limit; }else{ result.status=Status::no_progress; } returnresult; } template<typenameFamily,typenameModel,typenameState,typenameCorrector> [[nodiscard]]autorun(constFamily&family,constStartingStar<Model, State>&initial,Corrector&&correct, Options<Family>options,doubleabsolute_tolerance){ utils::errors::serif_ensure<utils::errors::SERiFErrorCode::INVALID_ARGUMENT, std::invalid_argument>(!options.explicit_start, "Asuppliedstartingstardeterminesitsownphysicalcoordinates.Removestarting_atsettingswhencontinuingfromthatstar."); family.validate_member(initial.model); options.start=family.coordinates_of(initial.model); options.explicit_start=true; returnrun(family,initial.state,std::forward<Corrector>(correct),std::move(options),absolute_tolerance); } }