Nonlinear Solvers - General Info

Defined in module: pressio4py.solvers

Import as:       from pressio4py import solvers


At a high level, a nonlinear solver can be seen as a process that repeatedly updates a given state until a certain stopping criterion is met. This forms the basis of our design approach, and if you keep this in mind, the details below will (hopefully) be very clear and intuitive.


Step by step guide

Using the pressio4py nonlinear solvers involves four main steps:

  1. define your problem in the form of a class with a specific API
  2. instantiate a nonlinear problem object
  3. set/change (if needed) the convergence and updating criteria
  4. invoke the solve operation

1. Your problem class

The problem is the object defining your math system to solve, and is what you need to implement and provide to pressio4py to compute the needed operators to operate on. The problem must be an instance of a class meeting what we call the "residual-jacobian" API:

class Problem:
  def createResidual():
    # return a copy of the rank-1 residual
    return np.zeros(...)

  def createJacobian():
    # return a copy of the rank-2 jacobian
    return np.zeros(...)

  def residual(state, R):
    # given current state, compute residual, R

  def jacobian(state, J):
    # given current state, compute jacobian, J

Currently, we only support dense Jacobians. This is because pybind11 does not yet support passing sparse types by reference. Note, however, that this is not critical for the main purpose of this library because ROMs are inherently dense.


2. Instantiating a solver

We currently support the following methods:

NameDocPurpose:
Newton-RaphsonLinkSystems of nonlinear equations (see e.g. link, link )
Gauss-NewtonLinkNonlinear least-squares problem (see link )
Levenberg–MarquardtLinkNonlinear least-squares problem (see link )

To instantiate a solver, you can use specific factory functions as follows:

solver = solvers.create_newton_raphson(problem, state, ...);
solver = solvers.create_gauss_newton(problem, state, ...);
solver = solvers.create_levenber_marquardt(problem, state, ...);

Note that the first and second arguments are your problem object and the state. These are needed at construction because presssio4py uses them to initialize all data structures needed. Please refer to each method's documentation for the details on the other arguments to pass.


3. Setting convergence and updating criteria

The solver class exposes the following methods:

class Solver:

  # set stopping criterion
  def setStoppingCriterion(value) # choose value is from the 'stop' enum

  # query stopping criterion
  def stoppingCriterion():
    return # the stored stopping criterion

  # set update criterion
  def setUpdatingCriterion(value) # choose value is from the 'update' enum

  # query update criterion
  def updatingCriterion():
    return # the stored update criterion

  # set max number of iterations
  def setMaxIterations(integer)
  # query max number of iterations
  def maxIterations():
    return # the current max num of iterations

  # this is used to set a single tol for all
  def setTolerance(float)

  # finer-grained methods for setting tolerances
  def setCorrectionAbsoluteTolerance(float)
  def setCorrectionRelativeTolerance(float)
  def setResidualAbsoluteTolerance(float)
  def setResidualRelativeTolerance(float)
  def setGradientAbsoluteTolerance(float)
  def setGradientRelativeTolerance(float)

  # querying tolerances
  def correctionAbsoluteTolerance(): return #...
  def correctionRelativeTolerance(): return #...
  def residualAbsoluteTolerance()  : return #...
  def residualRelativeTolerance()  : return #...
  def gradientAbsoluteTolerance()  : return #...
  def gradientRelativeTolerance()  : return #...
};

The convergence criterion and associated tolerance are used to decide why and when the solver needs to terminate. We currently support these termination criteria:

Enum valueDescriptionCurrently supported for:
stop.AfterMaxItersself-explanatoryall algorithms
stop.WhenCorrectionAbsoluteNormBelowToleranceself-explanatoryall algorithms
stop.WhenCorrectionRelativeNormBelowToleranceself-explanatoryall algorithms
stop.WhenResidualAbsoluteNormBelowToleranceself-explanatoryall algorithms
stop.WhenResidualRelativeNormBelowToleranceself-explanatoryall algorithms
stop.WhenGradientAbsoluteNormBelowToleranceself-explanatoryleast-squares solvers
stop.WhenGradientRelativeNormBelowToleranceself-explanatoryleast-squares solvers

The update stage represents the how the current correction term is combined with state to update the latter. We currently support the following:

NameEnum valueDescriptionCurrently supported for:
Defaultupdate.Standard $x_{n+1} = x_{n} + \lambda_{n}$ all algorithms
Armijoupdate.ArmijotodoGauss-Newton
LM-schedule1update.LMSchedule1todoLevenberg–Marquardt
LM-schedule2update.LMSchedule2todoLevenberg–Marquardt

where $\lambda_{n}$ is the correction computed at the n-th iteration of the solver.


4. Invoking the solve

This is best explained via a simple snippet:

from pressio4py import solvers

# assuming problem is already defined

state = np.array( # whatever initial condition )
solver = solvers.create_gauss_newton(problem, state, ...)
solver.solve(problem, state)