API
Index
The global API index is as follows:
NLS_Solver.AbstractNLSNLS_Solver.Abstract_BC_QuadSolver_ResultNLS_Solver.Abstract_BC_Solver_ConfNLS_Solver.Abstract_BC_Solver_ResultNLS_Solver.Abstract_Solver_ConfNLS_Solver.Abstract_Solver_ResultNLS_Solver.BoundConstraint_EnumNLS_Solver.BoundConstraintsNLS_Solver.LevenbergMarquardt_BC_ConfNLS_Solver.LevenbergMarquardt_BC_ResultNLS_Solver.LevenbergMarquardt_ConfNLS_Solver.LevenbergMarquardt_ResultNLS_Solver.NLS_ForwardDiffNLS_Solver.check_first_orderNLS_Solver.clean_τ!NLS_Solver.compute_δL_constrainedNLS_Solver.compute_δL_unconstrainedNLS_Solver.compute_δfNLS_Solver.convergedNLS_Solver.convergedNLS_Solver.create_NLS_problem_using_ForwardDiffNLS_Solver.eval_nls_fobjNLS_Solver.eval_nls_∇fobjNLS_Solver.eval_nls_∇∇fobjNLS_Solver.eval_rNLS_Solver.eval_r_JNLS_Solver.initialize_x_ZNLS_Solver.iteration_countNLS_Solver.iteration_countNLS_Solver.lower_boundNLS_Solver.multiplier_τNLS_Solver.objective_valueNLS_Solver.objective_valueNLS_Solver.parameter_sizeNLS_Solver.project!NLS_Solver.quadratic_subproblemNLS_Solver.residue_sizeNLS_Solver.restrict_to_inactive!NLS_Solver.set_max_iteration!NLS_Solver.set_max_iteration!NLS_Solver.set_ε_grad_Inf_norm!NLS_Solver.set_ε_grad_Inf_norm!NLS_Solver.set_ε_step_Inf_norm!NLS_Solver.set_ε_step_Inf_norm!NLS_Solver.solutionNLS_Solver.solutionNLS_Solver.solveNLS_Solver.solveNLS_Solver.update_Z!NLS_Solver.update_x!NLS_Solver.upper_bound
Documentation
Public
NLS_Solver.AbstractNLS — Typeabstract type AbstractNLS end Defines an abstract non-linear least squares problem (NLS). In our context such problem is essentially a differentiable function $\mathbf{r}$:
\[\mathbf{r}: \theta\in\mathbb{R}^{n_θ}\mapsto \mathbf{r}(\mathbf{\theta})\in\mathbb{R}^{n_S}\]
where:
- $\mathbf{r}(\mathbf{θ})∈\mathbb{R}^{n_S}$ is the residue vector,
- $\mathbf{θ}∈\mathbb{R}^{n_θ}$ is the parameter vector to be optimized
The objective function to minimize is:
\[f(θ)=\frac{1}{2}\| \mathbf{r}(θ) \|^2\]
The classical approach uses a linear approximation of $\mathbf{r}$:
\[\mathbf{r}(\mathbf{θ}+δ\mathbf{θ})\approx \mathbf{r}(\mathbf{θ}) + \mathbf{J}(\mathbf{θ})\cdot δ\mathbf{θ}\]
where $\mathbf{J}$ is the Jacobian:
\[\mathbf{J}_{i,j}=\partial_j r^i(\mathbf{θ}),\ i\in[1,n_S],\ j\in[1,n_θ]\]
This leads to
\[f(\mathbf{θ}+δ\mathbf{θ})\approx f(\mathbf{θ}) + \langle \nabla f, δ\mathbf{θ} \rangle + \frac{1}{2} \langle \nabla^2 f \cdot δ\mathbf{θ}, δ\mathbf{θ} \rangle\]
Where the gradient $\nabla f$ is $\mathbf{J}^t \mathbf{r}$ and the (approximate) Hessian $\nabla^2 f$ is $\mathbf{J}^t \mathbf{J}$.
To implement such model, you must define the following functions:
parameter_size: returns $n_θ$residue_size: returns $n_S$eval_r: compute $\mathbf{r}$eval_r_J: compute $(\mathbf{r}, \mathbf{J})$
NLS_Solver.Abstract_BC_Solver_Conf — Typeabstract type Abstract_BC_Solver_Conf endAbstract solver configuration. These are the solvers to be used to solve bound constrained nonlinear least squares:
\[\min\limits_{\theta_l \le \theta \le \theta_u } \frac{1}{2}\|r(\theta)\|^2\]
Implementations:
NLS_Solver.Abstract_BC_Solver_Result — TypeThe structure returned by solve when using the LevenbergMarquardt_BC_Conf method.
NLS_Solver.Abstract_Solver_Conf — Typeabstract type Abstract_Solver_Conf endAbstract solver configuration. These are the solvers to be used to solve unconstrained nonlinear least squares:
\[\min\limits_\theta \frac{1}{2}\|r(\theta)\|^2\]
Implementations:
NLS_Solver.Abstract_Solver_Result — Typeabstract type Abstract_Solver_Result endThis is the base type returned by the solve method. It contains the information related to the founded solution.
Interface
Implementations
NLS_Solver.BoundConstraints — TypeStore bound constraints $[l,u]$
Presence of NaN component and the $l\le u$ condition is checked at construction time. Note however that some components can be infinite.
Constructors
The following constructors are available:
- Construct $[0.0,1.0]^n$
BoundConstraints(n)- Construct $[T(0),T(1)]^n$ where components are of type
T
BoundConstraints(T,n)- Construct $[l,u]$ where
landuare lower and upper bound vectors
BoundConstraints(l,u)Related functions
Base.eltype(bc::BoundConstraints{ELT}) where ELTBase.axes(bc::BoundConstraints)Base.length(bc::BoundConstraints)Base.size(bc::BoundConstraints)in(x::AbstractArray{<:Real,N},bc::BoundConstraints{<:Real,N}) where Nlower_bound(bc::BoundConstraints)upper_bound(bc::BoundConstraints)project!(x::AbstractArray{<:Real,N},bc::BoundConstraints{<:Real,N}) where NBase.:-(bc::BoundConstraints,τ::AbstractArray)Base.:+(bc::BoundConstraints,τ::AbstractArray)
NLS_Solver.LevenbergMarquardt_BC_Conf — Typemutable struct LevenbergMarquardt_BC_Conf <: Abstract_Solver_Conf
...
endUse this constructor
LevenbergMarquardt_BC_Conf()to initialize the bound constrained Levenberg-Marquardt solver default configuration parameters.
To solve a problem with this method, you must then call solve(nls::AbstractNLS, θ_init::AbstractVector, bc::BoundConstraints, conf::Abstract_BC_Solver_Conf)
See:
NLS_Solver.LevenbergMarquardt_BC_Result — Typestruct LevenbergMarquardt_BC_Result{T<:Real} <: Abstract_BC_Solver_Result
...
end This structure subtypes Abstract_BC_Solver_Result
NLS_Solver.LevenbergMarquardt_Conf — Typemutable struct LevenbergMarquardt_Conf <: Abstract_Solver_Conf
...
endUse this constructor
LevenbergMarquardt_Conf()to initialize the Levenberg-Marquardt solver default configuration parameters.
To solve a problem with this method, you must then call solve(nls::AbstractNLS, θ_init::AbstractVector, conf::Abstract_Solver_Conf)
See:
NLS_Solver.LevenbergMarquardt_Result — Typestruct LevenbergMarquardt_Result{T<:Real} <: Abstract_Solver_Result
...
endThis structure subtypes Abstract_Solver_Result
NLS_Solver.NLS_ForwardDiff — Typestruct NLS_ForwardDiff <: AbstractNLS
...
end
A specialization that uses the ForwardDiff package to compute the Jacobian.
By comparison with AbstractNLS you only have to define these functions:
parameter_size: returns $n_θ$residue_size: returns $n_S$eval_r: computation of $\mathbf{r}$
NLS_Solver.converged — MethodNLS_Solver.converged — Methodconverged(::Abstract_BC_QuadSolver_Result)Return true if the solver converged
NLS_Solver.create_NLS_problem_using_ForwardDiff — Methodcreate_NLS_problem_using_ForwardDiff(r::Function;domain_image_dim::Pair{Int,Int})Creates an AbstractNLS specialized instance where the eval_r_J function is automatically defined using automatic differentiation.
ris a function that maps a parameter vector θ to its residue. The Jacobian matrix is computed using theForwardDiffpackage.domain_image_dimis a pair of the formθ length => r lengththat defines domain and codomain dimensions.
Usage example
An example defining the Rosenbrock function
\[\frac{1}{2}\|r(\theta)\|^2\text{ where }r = \sqrt{2} \left( \begin{array}{c} 1-\theta_1 \\ 10(\theta_2-\theta_1^2) \end{array} \right)\]
nls = create_NLS_problem_using_ForwardDiff(2 => 2) do θ
sqrt(2) sqrt(2)* [ 1-θ[1], 10*(θ[2]-θ[1]^2) ]* [ 1-θ[1], 10*(θ[2]-θ[1]^2) ]
endNLS_Solver.eval_nls_fobj — Methodeval_nls_fobj(r::AbstractVector{T}) -> f(θ)Compute $f(θ)=\frac{1}{2}\| \mathbf{r}(\mathbf{θ}) \|^2$
NLS_Solver.eval_nls_∇fobj — Methodeval_nls_∇fobj(r,J) -> ∇fobjCompute the gradient: $\nabla f(\mathbf{θ}) = \mathbf{J}^t\mathbf{r}$
NLS_Solver.eval_nls_∇∇fobj — Methodeval_nls_∇∇fobj(J) -> ∇∇fobjCompute the (approximate) Hessian: $\nabla^2 f(\mathbf{θ}) = \mathbf{J}^t\mathbf{J}$
NLS_Solver.eval_r — Methodeval_r(nls::AbstractNLS,
θ::AbstractVector) -> rCompte the residual vector $\mathbf{r}$
NLS_Solver.eval_r_J — Methodeval_r_J(nls::AbstractNLS,θ::AbstractVector) -> (r,J)Compute the residual the vector $\mathbf{r}$ and its Jacobian $\mathbf{J}$
NLS_Solver.iteration_count — Methoditeration_count(::Abstract_Solver_Result)Return the number of consumed iteration
NLS_Solver.iteration_count — Methoditeration_count(::Abstract_BC_QuadSolver_Result)Return the number of consumed iteration
NLS_Solver.lower_bound — MethodNLS_Solver.objective_value — Methodobjective_value(::Abstract_Solver_Result)Returns objective value at the point solution.
NLS_Solver.objective_value — Methodobjective_value(::Abstract_BC_QuadSolver_Result)Returns objective value at the point solution.
NLS_Solver.parameter_size — Methodparameter_size(nls::AbstractNLS)Return the dimension $n_θ$ of the parameter vector $θ$.
NLS_Solver.project! — Methodproject!(x::AbstractArray{<:Real,N},bc::BoundConstraints{<:Real,N})Project x such that $x \in [l,u]$ is fullfiled.
See: BoundConstraints
NLS_Solver.residue_size — Methodsample_size(nls::AbstractNLS)Return the dimension $n_S$ of the residue vector $r$.
NLS_Solver.set_max_iteration! — Methodset_max_iteration!(conf::LevenbergMarquardt_BC_Conf,
max_iter::Int)Modify the maximum number of iterations
NLS_Solver.set_max_iteration! — Methodset_max_iteration!(conf::LevenbergMarquardt_Conf,
max_iter::Int)Modify the maximum number of iterations
NLS_Solver.set_ε_grad_Inf_norm! — Methodset_ε_grad_Inf_norm!(conf::LevenbergMarquardt_BC_Conf,
ε_grad_Inf_norm::Float64)Modify the stopping criterion $|\nabla f|_\infty\le\epsilon$
NLS_Solver.set_ε_grad_Inf_norm! — Methodset_ε_grad_Inf_norm!(conf::LevenbergMarquardt_Conf,
ε_grad_Inf_norm::Float64)Modify the stopping criterion $|\nabla f|_\infty\le\epsilon$
NLS_Solver.set_ε_step_Inf_norm! — Methodset_ε_step_Inf_norm!(conf::LevenbergMarquardt_BC_Conf,
ε_step_Inf_norm::Float64)Modify the stopping criterion $|\delta x|_\infty\le\epsilon$
NLS_Solver.set_ε_step_Inf_norm! — Methodset_ε_step_Inf_norm!(conf::LevenbergMarquardt_Conf,
ε_step_Inf_norm::Float64)Modify the stopping criterion $|\delta x|_\infty\le\epsilon$
NLS_Solver.solution — MethodNLS_Solver.solution — Methodsolution(::Abstract_BC_QuadSolver_Result)Returns the founded solution
NLS_Solver.solve — Methodsolve(nls::AbstractNLS,
θ_init::AbstractVector,
conf::Abstract_Solver_Conf)::Abstract_Solver_ResultGeneric interface to solve an AbstractNLS problem.
The used algorithm is defined through Abstract_Solver_Conf specializations.
The method returns a Abstract_Solver_Result specialization.
NLS_Solver.solve — Methodsolve(nls::AbstractNLS,
θ_init::AbstractVector,
bc::BoundConstraints,
conf::Abstract_BC_Solver_Conf) -> Abstract_Solver_ResultGeneric interface to solve a AbstractNLS problem with bound constraints.
The used algorithm is defined through Abstract_BC_Solver_Conf specializations.
The method returns a Abstract_BC_Solver_Result specialization.
NLS_Solver.upper_bound — MethodPrivate
NLS_Solver.Abstract_BC_QuadSolver_Result — Typeabstract type Abstract_BC_QuadSolver_Result endQuadratic solver result abstraction
NLS_Solver.BoundConstraint_Enum — Type@enum(BoundConstraint_Enum,
ACTIVE_LB = -1,
INACTIVE_BC = 0,
ACTIVE_UB = +1)An enum to store bound constraint state.
Base.:+ — MethodBase.:+(bc::BoundConstraints,τ::AbstractArray)Translate bound constraints
\[[a+τ,b+τ] = [a,b]+τ\]
See: BoundConstraints
Base.:- — MethodBase.:-(bc::BoundConstraints,τ::AbstractArray)Translate bound constraints
\[[a-τ,b-τ] = [a,b]-τ\]
See: BoundConstraints
Base.axes — MethodBase.eltype — MethodBase.in — MethodBase.length — MethodBase.size — MethodNLS_Solver.check_first_order — Methodcheck_first_order(∇f::AbstractVector{<:Real},
xstar::AbstractVector{<:Real},
bc::BoundConstraints{<:Real,1})
check_first_order(Q::Symmetric{<:Real},
q::AbstractVector{<:Real},
xstar::AbstractVector{<:Real},
bc::BoundConstraints{<:Real,1})Check First-Order Conditions (see Bound Constrained Optimization slides)
If $x^\star=\arg\min f(x), x\in[l,u]$ then:
\[\partial_i f(x^\star) = \left\{\begin{array}{ll} \ge 0, & \text{if } x^\star[i] = l[i] \\ = 0, & \text{if } l[i] \le x^\star[i] \le u[i] \\ \le 0, & \text{if } x^\star[i] = u[i] \\ \end{array} \right.\]
This is equivalent to:
\[x^\star = P_{[l,u]}(x^\star-\nabla f(x^\star))\]
According to the previous result, this function returns:
\[\max \mid x^\star - P_{[l,u]}(x^\star-(Q.x^\star+q)) \mid\]
For a local stationary point this quantity must be null
The second function is a wrapper that computes $∇f=Q.x^\star+q$
NLS_Solver.clean_τ! — Methodclean_τ!(τ::AbstractArray{<:Real},
Z::AbstractArray{BoundConstraint_Enum})By definition τ=-Qx-q. If the algorithm converged, then one must have τ[i]=0 when the constraint is inactive.
This function updates τ by overwriting τ[i]=0 when Z[i]=inactive.
NLS_Solver.compute_δL_constrained — MethodSame idea than compute_δL_unconstrained, however when bound constraints are present $h$ is such that:
\[(\nabla^2 f + \mu I)h + \nabla f + \tau = 0\]
it follows that:
\[δL = L(0)-L(h) = \frac{1}{2} \langle h, \mu h + \tau - \nabla f \rangle\]
NLS_Solver.compute_δL_unconstrained — MethodCompute δL = L(0)-L(h) where L is the quadratic model
\[L(h)=f(\theta) + \langle \nabla f, h \rangle + \frac{1}{2}\langle \nabla^2 f h, h \rangle\]
with $f(\theta)=\frac{1}{2}\| r(θ) \|_2^2$, $\nabla f = J^t r$ and $\nabla^2 f = J^t J$
A direct computation gives:
\[δL = L(0)-L(h) = -\left( \langle J^tr, h \rangle + \frac{1}{2}\langle \nabla^2 f h, h \rangle \right)\]
However one can avoid the computation of $\nabla^2 f h$ if one uses the fact that $h$ is solution of:
\[(\nabla^2 f + \mu I)h + \nabla f = 0\]
With this hypothesis, one gets:
\[δL = L(0)-L(h) = \frac{1}{2} \langle h, \mu h - \nabla f \rangle\]
NLS_Solver.compute_δf — MethodCompute true variation of the real model: $δf = \frac{1}{2}(r^t(θ)r(θ)-r^t(θ+h)r(θ+h))$
Contrary to $δL$ things are simpler. However a trick is to use an equivalent formulation:
\[δf = \frac{1}{2}(r^t(θ)r(θ)-r^t(θ+h)r(θ+h)) = \frac{1}{2}(r(θ)-r(θ+h))^t(r(θ)+r(θ+h))\]
that has a better numerical behavior.
NLS_Solver.initialize_x_Z — Methodinitialize_x_Z(x_init::AbstractArray,
bc::BoundConstraints)Create (x,Z) from initial guess x_init and bound constraints bc
Z is created by recording how x_init fulfils the bound constraints bc.
x is the projection of x_init on the bounded domain [l,b].
NLS_Solver.multiplier_τ — Methodmultiplier_τ(::Abstract_BC_QuadSolver_Result)Returns the multipliers stored in a compact form (see τ definition, TODO)
NLS_Solver.quadratic_subproblem — MethodSolve
\[\min\limits_{h∈[θ^l-θ,θ^u-θ]}\frac{1}{2}h^t.(H+μI).h + ∇f^t.h\]
We use the quadratic model of $f$, the bound contraints are such that the step $h$ makes the update $x+h$ falls in the $[θ^l,θ^u]$ bound.
NLS_Solver.restrict_to_inactive! — Methodrestrict_to_inactive!(Q::Symmetric,
q::AbstractVector,
Z::AbstractVector{BoundConstraint_Enum},
lb::AbstractVector{<:Real},
ub::AbstractVector{<:Real})function restrict_to_inactive!(Q::Symmetric,
q::AbstractVector,
Z::AbstractVector{BoundConstraint_Enum},
bc::BoundConstraints{<:Real,1})In-place modification of $(Q,q)$ that produces $(\tilde{Q},\tilde{q})$ such that the initial optimization problem:
\[\tilde{x} = \arg\min \frac{1}{2} x^t Q x + q^tx\]
under these constraints:
\[x[i] = \left\{\begin{array}{ll} l[i], & \text{if } Z[i] = -1 \\ u[i], & \text{if } Z[i] = +1 \end{array}\right.\]
is transformed into this linear system:
\[\tilde{Q}\tilde{x}+\tilde{q}=0\]
NLS_Solver.update_Z! — Methodupdate_Z!(x::AbstractVector,
τ::AbstractVector,
Z::AbstractVector{BoundConstraint_Enum},
lb::AbstractVector,
ub::AbstractVector)update_Z!(x::AbstractVector,
τ::AbstractVector,
Z::AbstractVector{BoundConstraint_Enum},
bc::BoundConstraints{<:Real,1})This function updates Z according to x, τ and bounds lb, ub values.
It also count how many changes have be done during this update.
No change means that the algorithm has converged.
Note: this function only modifies Z and return the number of bad hypothesis.
NLS_Solver.update_x! — Methodupdate_x!(x::AbstractArray,
Z::AbstractArray{BoundConstraint_Enum},
bc::BoundConstraints)Update x value such that:
\[x[i] = \left\{\begin{array}{ll} l[i], & \text{if } Z[i] = -1 \\ u[i], & \text{if } Z[i] = +1 \end{array}\right.\]
When $Z[i]=0$ the $x[i]$ value is unaffected.