Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d7ac83a
Merge pull request #1 from polyfem/main
maxpaik16 Nov 8, 2024
1a81319
Merge pull request #4 from polyfem/main
maxpaik16 Jun 2, 2025
f6a8b39
Merge pull request #6 from polyfem/main
maxpaik16 Mar 13, 2026
8faac49
implement rescaled and custom norms in nonlinear solve
maxpaik16 Nov 10, 2025
cce2e67
print out all criteria
maxpaik16 Nov 10, 2025
97e7bc5
debugging relative norms
maxpaik16 Nov 10, 2025
cc1866f
fix typo
maxpaik16 Nov 10, 2025
f4bf904
rename tols
maxpaik16 Nov 11, 2025
2e54add
use proper norms for linear solve residual tolerance in Newton
maxpaik16 Nov 11, 2025
fb0608d
add new criteria to pre-update-direction-computation exemption
maxpaik16 Nov 11, 2025
3341c1f
line search printouts
maxpaik16 Nov 12, 2025
a08693f
fix scaling for newton dec
maxpaik16 Nov 19, 2025
00467f2
fix newton dec logic
maxpaik16 Nov 19, 2025
9eba0de
fix flipped sign in newton dec
maxpaik16 Nov 19, 2025
a867a48
add option gradient-interpolation-based step size selection to line s…
maxpaik16 Nov 19, 2025
aa14ba7
improve interp check
maxpaik16 Nov 19, 2025
37d35f1
rescale interpolation tolerance (hack)
maxpaik16 Nov 19, 2025
99ce90c
add not yet tested/implemented messages
maxpaik16 Feb 16, 2026
dd06af1
axe adaptive newton tol for now
maxpaik16 Feb 26, 2026
4516d2b
add proper norms to line search
maxpaik16 Feb 26, 2026
a24aa73
pass all tests
maxpaik16 Feb 26, 2026
f842fd4
rescale grad norm tol in line search
maxpaik16 Feb 26, 2026
75d441d
get rid of interpolating step
maxpaik16 Mar 14, 2026
07690ae
use NormType enum
maxpaik16 Mar 14, 2026
841d481
set line search norm type
maxpaik16 Mar 14, 2026
b1e9a40
put newton decrement in try/catch
maxpaik16 Mar 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions nonlinear-solver-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
"type": "object",
"optional": [
"solver",
"x_delta",
"grad_norm",
"x_delta_tol",
"grad_norm_tol",
"rel_grad_norm_tol",
"newton_decrement_tol",
"rel_x_delta_tol",
"first_grad_norm_tol",
"norm_type",
"max_iterations",
"iterations_per_strategy",
"line_search",
Expand Down Expand Up @@ -41,14 +45,35 @@
"doc": "Nonlinear solver type"
},
{
"pointer": "/x_delta",
"pointer": "/x_delta_tol",
"default": 0,
"type": "float",
"min": 0,
"doc": "Stopping criterion: minimal change of the variables x for the iterations to continue. Computed as the L2 norm of x divide by the time step."
},
{
"pointer": "/grad_norm",
"pointer": "/rel_x_delta_tol",
"default": 0,
"type": "float",
"min": 0,
"doc": "Stopping criterion: minimal change of the variables x for the iterations to continue relative to first step in nonlinear solve."
},
{
"pointer": "/rel_grad_norm_tol",
"default": 0,
"type": "float",
"min": 0,
"doc": "Stopping criterion: minimal gradient for the iterations to continue relative to first step in nonlinear solve."
},
{
"pointer": "/newton_decrement_tol",
"default": 0,
"type": "float",
"min": 0,
"doc": "Stopping criterion: minimal change of energy (as estimated by Newton decrement) for the iterations to continue."
},
{
"pointer": "/grad_norm_tol",
"default": 1e-08,
"type": "float",
"min": 0,
Expand All @@ -60,6 +85,17 @@
"type": "float",
"doc": "Minimal gradient norm for the iterations to not start, assume we already are at a minimum."
},
{
"pointer": "/norm_type",
"default": "L2",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use options to enumearte all. in the code is called euclidean

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

"type": "string",
"doc": "Norm to use when computing stopping criteria in nonlinear solve.",
"options": [
"Euclidean",
"L2",
"Linf"
]
},
{
"pointer": "/max_iterations",
"default": 500,
Expand Down Expand Up @@ -692,7 +728,7 @@
"default": null,
"type": "object",
"optional": [
"f_delta",
"f_delta_tol",
"f_delta_step_tol",
"derivative_along_delta_x_tol",
"apply_gradient_fd",
Expand All @@ -701,7 +737,7 @@
"doc": "Nonlinear solver advanced options"
},
{
"pointer": "/advanced/f_delta",
"pointer": "/advanced/f_delta_tol",
"default": 0,
"min": 0,
"type": "float",
Expand Down
2 changes: 2 additions & 0 deletions src/polysolve/linear/AMGCL.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ namespace polysolve::linear
// Solve the linear system Ax = b
virtual void solve(const Ref<const VectorXd> b, Ref<VectorXd> x) override;

virtual void set_tolerance(const double tol) override {residual_error_ = tol;}

// Name of the solver type (for debugging purposes)
virtual std::string name() const override { return "AMGCL_Block" + std::to_string(BLOCK_SIZE); }

Expand Down
2 changes: 2 additions & 0 deletions src/polysolve/linear/EigenSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ namespace polysolve::linear
// Constructor requires a solver name used for finding parameters in the json file passed to set_parameters
EigenIterative(const std::string &name) { m_Name = name; }

virtual void set_tolerance(const double tol) override {m_Solver.setTolerance(tol);}

public:
// Set solver parameters
virtual void set_parameters(const json &params) override;
Expand Down
2 changes: 2 additions & 0 deletions src/polysolve/linear/HypreSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ namespace polysolve::linear
// Name of the solver type (for debugging purposes)
virtual std::string name() const override { return "Hypre"; }

virtual void set_tolerance(const double tol) override {conv_tol_ = tol;}

protected:
int dimension_ = 1; // 1 = scalar (Laplace), 2 or 3 = vector (Elasticity)
int max_iter_ = 1000;
Expand Down
3 changes: 3 additions & 0 deletions src/polysolve/linear/Solver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ namespace polysolve::linear
/// If the problem is nullspace for multigrid solvers
virtual void set_is_nullspace(const VectorXd &x) {}

/// Set solver tolerance
virtual void set_tolerance(const double tol) {}

///
/// @brief { Solve the linear system Ax = b }
///
Expand Down
3 changes: 2 additions & 1 deletion src/polysolve/nonlinear/BoxConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ namespace polysolve::nonlinear
direction);
}

double BoxConstraintSolver::compute_grad_norm(const Eigen::VectorXd &x,
double BoxConstraintSolver::compute_grad_norm(const Problem &objFunc,
const Eigen::VectorXd &x,
const Eigen::VectorXd &grad) const
{
auto min = get_lower_bound(x, false);
Expand Down
2 changes: 1 addition & 1 deletion src/polysolve/nonlinear/BoxConstraintSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace polysolve::nonlinear
const double characteristic_length,
spdlog::logger &logger);

double compute_grad_norm(const Eigen::VectorXd &x, const Eigen::VectorXd &grad) const override;
double compute_grad_norm(const Problem &objFunc, const Eigen::VectorXd &x, const Eigen::VectorXd &grad) const override;
Eigen::VectorXd get_lower_bound(const Eigen::VectorXd &x, bool consider_max_change = true) const;
Eigen::VectorXd get_upper_bound(const Eigen::VectorXd &x, bool consider_max_change = true) const;
Eigen::VectorXd get_max_change(const Eigen::VectorXd &x) const;
Expand Down
31 changes: 28 additions & 3 deletions src/polysolve/nonlinear/Criteria.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace polysolve::nonlinear
{
bool is_converged_status(const Status s)
{
return s == Status::XDeltaTolerance || s == Status::FDeltaTolerance || s == Status::GradNormTolerance;
return s == Status::XDeltaTolerance || s == Status::FDeltaTolerance || s == Status::GradNormTolerance || s == Status::NewtonDecrementTolerance || s == Status::RelGradNormTolerance || s == Status::RelXDeltaTolerance;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why newton here? this interface is more generic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Newton decrement is a generic estimate of how close you are to the optimum and applies for non-Newton methods.

}

Criteria::Criteria()
Expand All @@ -26,6 +26,9 @@ namespace polysolve::nonlinear
firstGradNorm = 0;
fDeltaCount = 0;
xDeltaDotGrad = 0;
relGradNorm = 0;
relXDelta = 0;
newtonDecrement = 0;
}

void Criteria::print(std::ostream &os) const
Expand All @@ -34,12 +37,16 @@ namespace polysolve::nonlinear
}
std::string Criteria::print_message() const {
return fmt::format(
"iters={:d} {}={:g} {}={:g} {}={:g} {}={:g}",
"iters={:d} {}={:g} {}={:g} {}_rel={:g} {}={:g} {}_rel={:g} {}={:g} {}={:g}",
iterations,
log::delta("f"), fDelta,
log::norm(log::grad("f")), gradNorm,
log::norm(log::grad("f")), relGradNorm,
log::norm(log::delta("x")), xDelta,
log::delta("x") + log::dot() + log::grad("f(x)"), xDeltaDotGrad);
log::norm(log::delta("x")), relXDelta,
log::delta("x") + log::dot() + log::grad("f(x)"), xDeltaDotGrad,
"1/2" + log::delta("x") + "^TH" + log::delta("x"), newtonDecrement
);
}

Status checkConvergence(const Criteria &stop, const Criteria &current)
Expand All @@ -53,6 +60,18 @@ namespace polysolve::nonlinear
{
return Status::GradNormTolerance;
}
if (stop.relXDelta > 0 && current.relXDelta < stop.relXDelta)
{
return Status::RelXDeltaTolerance;
}
if (stop.relGradNorm > 0 && current.relGradNorm < stop.relGradNorm)
{
return Status::RelGradNormTolerance;
}
if (stop.newtonDecrement > 0 && current.newtonDecrement < stop.newtonDecrement)
{
return Status::NewtonDecrementTolerance;
}
if (stop.xDelta > 0 && current.xDelta < stop.xDelta)
{
return Status::XDeltaTolerance;
Expand Down Expand Up @@ -84,6 +103,12 @@ namespace polysolve::nonlinear
return "Change in cost function value too small";
case Status::GradNormTolerance:
return "Gradient vector norm too small";
case Status::RelGradNormTolerance:
return "Relative gradient vector too small";
case Status::RelXDeltaTolerance:
return "Relative change in parameter vector too small";
case Status::NewtonDecrementTolerance:
return "Newton decrement too small";
case Status::ObjectiveCustomStop:
return "Objective function specified to stop";
case Status::NanEncountered:
Expand Down
6 changes: 6 additions & 0 deletions src/polysolve/nonlinear/Criteria.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ namespace polysolve::nonlinear
// Success cases
IterationLimit, ///< The maximum number of iterations has been reached
XDeltaTolerance, ///< The change in the parameter vector is below the tolerance
RelXDeltaTolerance,
FDeltaTolerance, ///< The change in the cost function is below the tolerance
GradNormTolerance, ///< The norm of the gradient vector is below the tolerance
RelGradNormTolerance,
NewtonDecrementTolerance,
ObjectiveCustomStop, ///< The objective function specified to stop
// Failure cases
NanEncountered, ///< The objective function returned NaN
Expand All @@ -37,6 +40,9 @@ namespace polysolve::nonlinear
double gradNorm; ///< Minimum norm of gradient vector
double firstGradNorm; ///< Initial norm of gradient vector
double xDeltaDotGrad; ///< Dot product of parameter vector and gradient vector
double relXDelta;
double relGradNorm;
double newtonDecrement;
unsigned fDeltaCount; ///< Number of steps where fDelta is satisfied

Criteria();
Expand Down
15 changes: 15 additions & 0 deletions src/polysolve/nonlinear/Problem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@

namespace polysolve::nonlinear
{

enum class NormType
{
EUCLIDEAN = 0,
L2 = 1,
Linf = 2
};

/// @brief Class defining optimization problem to be solved. To be defined by user code
class Problem
{
Expand Down Expand Up @@ -103,6 +111,13 @@ namespace polysolve::nonlinear
/// @return True if the solver should stop, false otherwise.
virtual bool stop(const TVector &x) { return false; }

virtual double grad_norm_rescaling(const NormType norm_type) const {return 1;}
virtual double step_norm_rescaling(const NormType norm_type) const {return 1;}
virtual double energy_norm_rescaling(const NormType norm_type) const {return 1;}

virtual double grad_norm(const TVector &grad, const NormType norm_type) const {return grad.norm();}
virtual double step_norm(const TVector &x, const NormType norm_type) const {return x.norm();}

/// --- Misc ----------------------------------------------------------

/// @brief Sample the function along a direction.
Expand Down
Loading
Loading