#include "serif/solver/nonlinear/options.hpp" #include #include "serif/utils/error/errors.hpp" namespace serif::solver { void Newton::validate() const { using namespace utils::errors; serif_ensure(std::isfinite(relative_tolerance) && relative_tolerance >= 0.0 && std::isfinite(absolute_tolerance) && absolute_tolerance >= 0.0 && maximum_iterations >= 0, "Newton tolerances must be finite and nonnegative, and its iteration budget must be nonnegative."); linear_solve.validate(); } double Newton::convergence_threshold(const double initial_norm) const { using namespace utils::errors; validate(); const double relative = relative_tolerance * initial_norm; serif_ensure(std::isfinite(initial_norm) && initial_norm >= 0.0 && std::isfinite(relative), "Newton requires a finite initial norm and representable relative convergence threshold."); return std::max(absolute_tolerance, relative); } void Backtracking::validate() const { using namespace utils::errors; serif_ensure(std::isfinite(initial_step) && initial_step > 0.0 && initial_step <= 1.0 && std::isfinite(contraction) && contraction > 0.0 && contraction < 1.0 && std::isfinite(fraction_to_boundary) && fraction_to_boundary > 0.0 && fraction_to_boundary < 1.0 && std::isfinite(sufficient_decrease) && sufficient_decrease > 0.0 && sufficient_decrease < 1.0 && std::isfinite(minimum_step) && minimum_step > 0.0 && minimum_step <= initial_step && maximum_trials > 0, "Backtracking needs 0 < minimum step <= initial step <= 1, contraction, boundary safety and sufficient decrease in (0,1), and a positive trial budget."); } MetricEvaluation NormalizedResidualMetric::operator()(std::span residual) const { double norm = 0.0; for (const double value : residual) { norm = std::hypot(norm, value); } return {.residual_norm = norm, .merit = 0.5 * norm * norm}; } double next_backtracking_step(const double rejected_step, const double accepted_determinant, const double rejected_determinant, const bool inverted, const Backtracking& options) noexcept { const double contracted = rejected_step * options.contraction; if (!inverted || !std::isfinite(accepted_determinant) || accepted_determinant <= 0.0 || !std::isfinite(rejected_determinant) || rejected_determinant > 0.0) { return contracted; } const double change = accepted_determinant - rejected_determinant; if (!std::isfinite(change) || change <= 0.0) { return contracted; } const double safe_step = options.fraction_to_boundary * rejected_step * accepted_determinant / change; if (!std::isfinite(safe_step) || safe_step <= 0.0 || contracted <= safe_step) { return contracted; } const double rung = std::ceil(std::log(safe_step / rejected_step) / std::log(options.contraction)); double skipped = rejected_step * std::pow(options.contraction, rung); if (!std::isfinite(skipped) || skipped <= 0.0 || skipped >= rejected_step) { return contracted; } if (skipped > safe_step) { skipped *= options.contraction; } return skipped; } }