API

The problem

A problem holds functions and nothing else: the residual, and optionally its derivative. The storage of the iterate selects the call form of both, so one problem serves every regime. This name is exported.

GeometricSolvers.NonlinearProblem — Type
NonlinearProblem(F)
NonlinearProblem(F, J)

The residual F of a nonlinear system, and optionally its derivative J. The problem holds functions only — no parameters, no iterate and no flag — so it is isbits when F and J are and a kernel can hold it.

The call form follows the storage of the iterate, and residual!! selects it on the storage alone: with no buffer slot the call is F(x, p), for a Number, an MVector — which counts as a value — and an Array alike. J is the derivative of F, which jvp!! and jacobian!! read for the source nothing; AutoForwardDiff instead differentiates F. A method's own operator is a separate Jacobian source.

julia> prob = NonlinearProblem((x, p) -> x .* x .- 4);

julia> prob.F(3.0, nothing)
5.0

julia> prob.J === nothing
true
source

The storage seam

The storage seam lets an algorithm read each function's result rather than the storage itself, so the storage-specific operations stay at the leaves. The value methods, for a StaticArray or a Number iterate, are the ones this page documents.

These names are not exported. Reach them as GeometricSolvers.name.

GeometricSolvers.buffer — Function
buffer(x)
buffer(x, TF, dims...)

One storage slot for an iterate like x, in the element type TF and with the shape dims. A caller creates slots during initialization; an algorithm consumes their returned values.

A value iterate — a StaticArray or a Number — has no slot: the call returns nothing, so the R1 state carries no buffer and spends no register on one. A static matrix is never an iterate and gets no slot either, which is why this method is written on StaticArray and not on StaticVector.

source
GeometricSolvers.residual!! — Function
residual!!(y, prob, x, p)

The residual F(x, p) of prob at the iterate x, for the parameters p, in the storage of y, and return it.

y is the storage slot the residual is to occupy, and it alone selects the call form of the problem's F: with no buffer slot (nothing) the call is F(x, p), for an SVector, an MVector, a Number and an Array alike, because the iterate's type is read nowhere. There is no inplace flag that could disagree with the iterate, and no hasmethod.

source
GeometricSolvers.update_iterate!! — Function
update_iterate!!(y, x, α, d)

The updated iterate x + α d, in the storage of y, and return it.

This is the one function that moves the iterate, and the one seam a manifold solver extends: a retraction is a second method of it, and no algorithm changes.

source
GeometricSolvers.map!! — Function
map!!(f, y, xs...)

map(f, xs...) in the storage of y, and return it: the elementwise updates that are not an iterate update of update_iterate!! — a negation, a difference, a copy.

source
GeometricSolvers.mul!! — Function
mul!!(C, A, B)

The matrix product A * B, in the storage of C, and return it: the Gram matrix of the Anderson and weighting histories, and a matrix–vector product where no jvp!! applies.

source

Status and stopping test

Every type on this page is isbits, so a kernel can hold it, and every real field is in R = real(T) for the element type T of the iterate.

GeometricSolvers.ReturnCode — Type
ReturnCode

Why a solve or a step ended, stored in one byte so that a status fits a GPU register budget:

  • SUCCESS: the stopping test of Options holds.
  • STALLED: the iterate or the residual stopped improving before the test held.
  • MAXITERS: the iteration cap was reached.
  • SINGULAR: a factorisation found a zero pivot; it reports this and does not throw.
  • NONFINITE: a residual, a step or a norm is not finite.
  • LINESEARCH_FAILED: the globalisation found no acceptable step.
source
GeometricSolvers.SolverStatus — Type
SolverStatus{R <: Real}

The result of a solve. It is isbits, so a kernel can write one per system, and every real field is in R = real(T) for the element type T of the iterate.

Fields

  • code::ReturnCode: why the solve ended.
  • iterations::Int32: the number of steps taken.
  • fnorm::R: $\|F(x)\|$ at return.
  • stepnorm::R: $\|Δx\|$ of the last step.
  • step_failures::Int32: every step-rule failure of the solve, such as a failed line search, in one count.
  • promoted::Bool: a factorisation ran in a higher precision than the factorisation precision of its method, after a stall or as a vendor fallback.

The predicates GeometricSolvers.isconverged and GeometricSolvers.isstalled read the code. They are not exported, because they are generic names that a package doing using GeometricSolvers may want for itself.

source
GeometricSolvers.StepInfo — Type
StepInfo{R <: Real}

The result of one step, which record folds into a SolverStatus.

Fields

  • code::ReturnCode: the outcome of the step.
  • fnorm::R: $\|F(x₊)\|$ at the new iterate, so that the loop needs no second reduction and, on a device, no second synchronisation.
  • stepnorm::R: $\|x₊ - x\|$.
  • failures::Int32: the step-rule failures inside this step, those of its members included.
  • promoted::Bool: the step factorised in a higher precision than its method's.
source
GeometricSolvers.Options — Type
Options{R <: Real}

The stopping test of the loop, and nothing else. It is isbits, and every tolerance is in R = real(T) for the element type T of the iterate, so a Float64 tolerance never reaches a Float32 kernel. The iteration cap of a line search is a field of the line search.

Fields

  • f_abstol::R, f_reltol::R: the residual test $\|F(x)\| ≤ \max(f_{abstol}, f_{reltol} \|F(x₀)\|)$.
  • x_abstol::R, x_reltol::R: the step test $\|Δx\| ≤ \max(x_{abstol}, x_{reltol} \|x\|)$.
  • min_iterations::Int32, max_iterations::Int32: the bounds on the number of steps.
  • max_stalls::Int32: the number of consecutive stalled steps after which the solve stops.

Options(T; kwargs...) builds one from the element type T of the iterate, so the caller never names R, and convert turns an Options of another R into Options{R}. The conversion raises f_reltol to at least √eps(R) and x_reltol to at least 2 eps(R), the defaults of R, because a smaller relative tolerance cannot be met in R.

source
GeometricSolvers.Options — Method
Options(T; f_abstol, f_reltol, x_abstol, x_reltol, min_iterations, max_iterations, max_stalls)

Build the Options of a solve whose iterate has element type T, as Options{real(T)}. The two relative tolerances scale with eps(R):

keyworddefault
f_abstol0
f_reltol√eps(R)
x_abstol0
x_reltol2 eps(R)
min_iterations0
max_iterations100
max_stalls2

The default residual test is relative. The residual cannot fall below about $\mathrm{eps}(T) \|J\| \|x\|$, whatever the precision of the residual (Tisseur 2001, Cor. 2.5), and a default cannot know $\|J\| \|x\|$. So the default f_abstol is 0, which turns the absolute test off, and the relative test $f_{reltol} \|F(x₀)\|$ decides.

The stopping test runs before the first step, and min_iterations is 0. From a start at the limiting accuracy one Newton step makes the result worse (Wilkinson, quoted by Tisseur 2001). The relative test cannot see such a start: before the first step it compares $\|F(x₀)\|$ with a fraction of itself. So a start at the limiting accuracy returns unchanged after 0 iterations only when the caller sets f_abstol at or above its residual floor, as an integrator that knows the scale of F can.

A negative or NaN tolerance, a negative min_iterations, a max_iterations below min_iterations and a max_stalls below 1 raise an ArgumentError.

julia> opt = Options(ComplexF32; f_reltol = 1e-4);

julia> typeof(opt)
Options{Float32}

julia> opt.f_reltol
0.0001f0
source

Linear-solver methods

The factorisation precision TF of a linear-solver method is a type parameter. Nothing selects the working type of the problem, a real float type a lower (or higher) precision, and a marker a vendor tensor-core path.

GeometricSolvers.LUFactorization — Type
LUFactorization()
LUFactorization(TF)

LU factorisation with partial pivoting. LUFactorization() factorises in the working type of the problem, LUFactorization(Float32) in Float32, and LUFactorization(TF32()) carries a tensor-core marker (see TF32).

source
GeometricSolvers.TF32 — Type
TF32()

The factorisation-precision marker for NVIDIA's TensorFloat-32 tensor cores. It is a type only: no factorisation method accepts a marker, so a marker selects no code path.

source

Factorisation and solve

A linear-solver method enters a solve through two functions, factorize!! and ldiv!!. For a static matrix (R1) both return new isbits values, so they run inside a kernel: StaticLU is LU with partial pivoting and Int32 pivots, StaticQR Householder QR without column pivoting. factorize!! states what a singular matrix gives. These names are not exported. Reach them as GeometricSolvers.name.

GeometricSolvers.factorize!! — Function
factorize!!(fac, method, J)

Factorise the matrix J with the linear-solver method method and return the factors.

For a static matrix (R1), fac is nothing or an earlier factor of the same type, whose value is not used, and the result is a new StaticLU or StaticQR. J is converted to the element type of the factors, factor_eltype. Where that is not the element type of J, J is first divided by the power of two 2^e that brings its largest |re| or |im| into [1, 2), or as close as the exponent range allows, and e is kept in the factor, so that the overall scale of J neither overflows nor underflows in the factor type. A zero or non-finite J gives e = 0. A singular J does not throw. LU records the first zero pivot in info, QR the first zero diagonal entry of R, and returncode then gives SINGULAR. QR is not rank-revealing: a singular J whose R has no exact zero on the diagonal gives SUCCESS, and two equal rows, as a rule, give no exact zero.

A precision marker such as TF32 has no method for a static matrix: the call raises a MethodError.

source
GeometricSolvers.ldiv!! — Function
ldiv!!(d, fac, r)

Solve A d = r with the factors fac of A, and return d in the element type of r.

For a static factor (R1), d is nothing and the result is a new SVector. Where the element type of r is the element type of the factors, the solve runs directly in it. Where they differ, a finite, nonzero r is scaled by s = maximum(abs, r), converted to the factor type, solved, converted back and multiplied by s (Kelley 2023, eqs. 2.17–2.18). Normalisation prevents overflow from the overall right-hand-side scale in a lower factor precision; entries small relative to the largest entry can still underflow. The scale takes no squares and overflows only where an entry's absolute value does. In the mixed-type path, a zero r returns a zero step without solving, and an Inf or NaN in r gives a non-finite step. In both paths the step is multiplied by 2^-e, the power of two that factorize!! divided J by.

source
GeometricSolvers.factor_eltype — Function
factor_eltype(method, T)

The element type of the factors of a working element type T: T when the method's factorisation precision is Nothing, the precision TF for a real T, and complex(TF) for a complex T.

source
GeometricSolvers.StaticLU — Type
StaticLU{N, TF, L}

The LU factors with partial pivoting of an N × N static matrix, in element type TF: factors holds U on and above the diagonal and the multipliers of the unit lower L below it, ipiv the row exchanged with row k at step k, info the index of the first zero pivot, or 0, and scale_exponent the exponent e of the power of two 2^e that the matrix was divided by. L = N * N makes the SMatrix field concrete, so the factor is isbits.

source
GeometricSolvers.StaticQR — Type
StaticQR{N, TF, L}

The Householder QR factors of an N × N static matrix, in element type TF: factors holds R on and above the diagonal and below it the reflector vectors w, scaled to w[k] = 1; the reflector of step k is I - τ[k] w wᴴ. info is the index of the first zero diagonal entry of R, or 0, and scale_exponent the exponent e of the power of two 2^e that the matrix was divided by.

source

Line searches

A line search is scalar code: linesearch sees the line only through the merit φ and its derivative φ′, allocates nothing and never throws, so the same search runs on the host and inside a kernel. Its iteration cap, where it has one, is its own field.

GeometricSolvers.Static — Type
Static(α = 1.0)

The line search that takes the fixed step α, bounded by the caller's αmax, and evaluates nothing: the common case inside an integrator. It ignores the trial step and returns SUCCESS with φ = NaN.

source
GeometricSolvers.Backtracking — Type
Backtracking(; c₁ = 1e-4, p = 0.5, maxiter = 100)

The Armijo line search with a safeguarded quadratic and cubic interpolation step. It shrinks the trial step until the sufficient_decrease test

\[φ(α) - φ(0) ≤ \min\bigl(0,\ c_1 α φ′(0) + τ\bigr)\]

holds, where $τ$ is the roundoff of $φ(0)$. Each backtrack takes the minimiser of the model through $φ(0)$, $φ′(0)$ and the last one or two trials, clamped to $[0.1 α, p α]$. A trial whose merit is not finite gives no model; the next trial is then the smaller of $0.1 α$ and the geometric mean of $α$ and the step floor. The search stops at the step floor smallest_step, after two trials at steps below $\sqrt{\mathrm{eps}}$ whose merit equals $φ(0)$ bit for bit, or after maxiter trials.

The slope $φ′(0)$ comes from the step kind: $-2φ(0)$ for an ExactStep, one evaluation for a MeasuredSlope, the number itself for a slope passed in. For an InexactStep with residual $η$ the test is Eisenstat and Walker's $\|r(x + α d)\| ≤ [1 - c_1 (1 - η(α))] \|r\|$ with $η(α) = |1 - α| + α η$, and the model slope is $-2(1 - η) φ(0)$.

The model is built from differences of the merit divided by $|φ′(0)|$, and the cubic step uses the form that does not cancel on a near-quadratic merit, so the trials do not depend on the scale of the merit.

source
GeometricSolvers.Bisection — Type
Bisection(; αmax = 65536.0, maxiter = 100)

The line search that bisects $φ′$ for a minimiser of the merit along the line. It brackets from the anchor: the lower end starts at $0$, where $φ′(0) < 0$, and the upper end at the trial step, doubled while $φ′ < 0$, up to the smaller of its own αmax and the caller's. So the bracket always holds a sign change from $-$ to $+$: a minimum, never a maximum. A $φ′$ that is not finite counts as not descending, so the bracket never grows into a region where the merit is not finite.

It then bisects the bracket. While the bracket spans more than a factor 2, the next trial is the geometric mean of its ends, with a lower end of $0$ taken as the step floor smallest_step; after that it is the middle. It stops when the width is below $\sqrt{\mathrm{eps}}$ times the upper end, or when the lower end is still $0$ and the upper end is within a factor 2 of the floor. So the number of trials grows only with $\log_2 \log_2$ of the ratio of the trial step to the floor, and it stays bounded for αmax = Inf. While the lower end stays at $0$, a search from the trial step $α$ costs at most $3 + ⌈\log_2 \log_2(α / \mathrm{floatmin}(R))⌉$ evaluations, because the floor is at least floatmin(R): 10 in Float32 and 13 in Float64 for $α = 1$.

It returns the lower end of the bracket, where $φ′$ is finite and negative, its upper end if the lower end is $0$, or the ceiling if $φ′$ is still negative there, and classifies it by the merit there: one evaluation of $φ$ after one $φ′$ per trial.

source
GeometricSolvers.StrongWolfe — Type
StrongWolfe(; c₁ = 1e-4, c₂ = 0.9, αmax = 65536.0, maxiter = 100)

The line search for a step with the strong Wolfe conditions

\[φ(α) - φ(0) ≤ c_1 α φ′(0), \qquad |φ′(α)| ≤ c_2 |φ′(0)|,\]

$0 < c_1 < c_2 < 1$: Nocedal and Wright's Algorithms 3.5 and 3.6. The first condition is the sufficient_decrease test without a round-off allowance. The bracketing phase doubles the trial step up to the smaller of αmax and the caller's ceiling until a trial fails the first condition, does not decrease the merit, or has $φ′ ≥ 0$. The zoom phase then takes the minimiser of the cubic through the merit and its derivative at both ends of the bracket, or of the quadratic through both merits and the derivative at the lower end where the upper end has no derivative. The trial is safeguarded to the inner 80 % of the bracket, and it is the middle after a trial that shrinks the bracket by less than a factor 0.66, as in Moré and Thuente. The lower end is the trial with the lowest merit that meets the first condition, up to the roundoff of the larger of two merits: a trial within it of the lower end ties with it, and the sign of $φ′$ at the trial decides the side of the minimiser. A zoom trial that meets both conditions is accepted whatever its merit.

$φ′$ is evaluated only at a trial that meets the first condition and, in the bracketing phase, decreases the merit: a trial that fails costs one $φ$, a trial that passes one $φ$ and one $φ′$.

It returns SUCCESS only for a step that meets both conditions and decreases the merit by more than its round-off. Every other search returns its lowest trial that meets the first condition, or else its last trial. At the ceiling, at a collapsed bracket or at the step floor smallest_step the code is STALLED if the merit is at its round-off floor and LINESEARCH_FAILED otherwise; after maxiter trials it is LINESEARCH_FAILED.

source

Automatic differentiation

In R1, prepare_ad returns the AutoForwardDiff() back end itself for a StaticVector or a Number iterate. jacobian!! and jvp!! run tagged ForwardDiff directly with that source. The source nothing calls the problem's own J; a JacobianFunction calls a method's operator for jacobian!! only. A back end other than AutoForwardDiff has no R1 method, and the call is a host MethodError.

In R3 the AD path is chosen once, by prepare_ad, from the back end and the storage of the iterate: AutoForwardDiff() goes through an own chunked forward mode on every array type, and any other back end goes through DifferentiationInterface on a CPU iterate, so every ADTypes back end works with no line of source here. A device array iterate takes AutoForwardDiff() only, because DifferentiationInterface's jacobian! raises a scalar-indexing error on an MtlArray, a CuArray and a JLArray. jacobian!! and jvp!! then dispatch on the object the preparation returned, so the solver makes no runtime choice and carries no ad keyword.

In R3 the iterate is a vector; another shape raises ArgumentError where the preparation is built. The parameters may be replaced on every call, which is what lets one preparation serve a whole time-stepping loop.

AutoEnzyme reaches the Array path through DifferentiationInterface like any other back end, but it needs runtime activity for the parameter context — AutoEnzyme(; mode = Enzyme.set_runtime_activity(Enzyme.Forward)). A plain AutoEnzyme() raises EnzymeRuntimeActivityError on a residual that broadcasts constant parameters into its result; see KNOWN_ISSUES.md. The mode is the caller's choice, so the package sets none.

These names are not exported. Reach them as GeometricSolvers.name.

GeometricSolvers.prepare_ad — Function
prepare_ad(backend, prob, r, x, p)

Build the AD preparation for the residual of prob at an iterate like x, with a residual buffer like r and a representative parameter set p, and return the object that jacobian!! and jvp!! dispatch on. The values of p, and the parameter object itself, may be replaced on every call.

AutoForwardDiff() gives a ChunkedForwardDiff on every array type. Any other back end gives a DIJacobian on a CPU iterate, and raises ArgumentError on a device array iterate: DifferentiationInterface's jacobian! raises "Scalar indexing is disallowed" on an MtlArray, a CuArray and a JLArray. The iterate must be a vector.

source
GeometricSolvers.DIJacobian — Type
DIJacobian

The AD preparation of the R3 path through DifferentiationInterface: the back end, DI's prepared jacobian! and pushforward!, and a residual buffer. prepare_ad builds it. The parameters are a context and not a differentiated argument, so each call wraps them in DI's Constant.

source
GeometricSolvers.ChunkedForwardDiff — Type
ChunkedForwardDiff{N, Tg}

The AD preparation of the R3 path of AutoForwardDiff: N Jacobian columns per residual evaluation, in one pass of ForwardDiff.Dual{Tg, T, N} arithmetic. Tg is the caller's tag, or this solver's own where the caller named none, and the same tag serves the Jacobian and the JVP.

The four fields are the dual buffers: xdual and rdual carry N partials for jacobian!!, xdual1 and rdual1 one partial for jvp!!. A complex iterate gives Complex{ForwardDiff.Dual{Tg, real(T), N}} buffers, so that a holomorphic residual is differentiated in its complex argument.

source
GeometricSolvers.JacobianFunction — Type
JacobianFunction(f)

The Jacobian source of a method that carries an operator of its own: f(x, p) is that operator, and jacobian!! calls it in place of the problem's J. The method's operator is not the derivative of the residual, so it serves jacobian!! and never jvp!!.

Internal: wrap the operator in this type to pass it as the source of the R1 jacobian!! call. It holds a function and nothing else, so it is isbits exactly when that function is.

source
GeometricSolvers.jacobian!! — Function
jacobian!!(J, prep, prob, x, p)

The Jacobian of the residual of prob at the iterate x, for the parameters p, in the storage of J, and return it.

prep is the Jacobian source. In R1 the output slot J is nothing: nothing calls the problem's J(x, p), an AutoForwardDiff runs tagged ForwardDiff and returns an SMatrix in the element type of a vector iterate or a Number for a Number iterate, and a JacobianFunction calls the method's own function. factorize!! converts the result to the factorisation precision TF. In R3 J is an n × n buffer in TF and prep is a DIJacobian, whose prepared jacobian! runs DifferentiationInterface, or a ChunkedForwardDiff, the own chunked forward mode. The preparation selects the code.

source
GeometricSolvers.jvp!! — Function
jvp!!(Jv, prep, prob, x, v, p)

The Jacobian–vector product J(x, p) * v of the residual of prob, in the storage of Jv, and return it.

With an AD source, one pushforward and exactly one evaluation of the residual make the merit derivative φ′(α) = F(x + αd)ᵀ J(x + αd) d of a line search cost one directional derivative rather than n. With the source nothing, the call evaluates the problem's J instead of F.

prep is the JVP source from the problem, never from a method's operator, and it selects the code exactly as in jacobian!!. In R1 the output slot Jv is nothing and the call returns a new value: nothing gives J(x, p) * v from the problem's J, and an AutoForwardDiff one dual evaluation of F. The R1 direction has the element type and the length of the iterate, as the line-search slope does: a direction of another element type is a host MethodError. In R3 Jv is a buffer and prep is a DIJacobian or a ChunkedForwardDiff.

source

Internals

These names are not exported. Reach them as GeometricSolvers.name.

GeometricSolvers.LineSearch — Type
LineSearch{R}

The supertype of the line searches Static, Backtracking, Bisection and StrongWolfe, whose numbers are of the real type R. A line search is a method: it is isbits, it holds numbers and, except Static, an Int32 cap and nothing else, and linesearch runs it.

Every line search keeps six contracts:

  1. It never throws. A constructor checks its parameters on the host and raises an ArgumentError; linesearch raises nothing.
  2. It returns $α > 0$.
  3. It reports through the code of its LineSearchResult and never logs.
  4. A non-finite or ascending anchor is reported, not searched: NONFINITE and LINESEARCH_FAILED. A stationary anchor, $φ′(0) = 0$, is STALLED. Static is the exception: it reads no anchor and returns its step.
  5. Its number of evaluations does not depend on the scale of the merit.
  6. It returns $α ≤ α_{max}$, for the caller's αmax and for its own.

It never evaluates the merit at $α = 0$: the caller passes $φ(0)$, which is the residual it already has.

source
GeometricSolvers.linesearch — Function
linesearch(ls::LineSearch{R}, lf, step, φ₀::R, α::R, αmax::R = R(Inf))

Run the line search ls along the line function lf from the trial step α, and return a LineSearchResult{R}. φ₀ is the merit at $α = 0$, which the caller has; step is an ExactStep, an InexactStep, a MeasuredSlope or the slope $φ′(0)$ itself; αmax is the caller's ceiling on the step. A trial step that is not positive or not finite is replaced by 1, and one below the step floor smallest_step is raised to it.

A non-positive or NaN αmax is a caller error. The search then evaluates nothing and returns LINESEARCH_FAILED with the trial step, bounded by the ceiling of the method.

This is the one entry point: it applies the ceiling, the trial step and the anchor checks, then runs the loop of the method. Each loop has exactly ls.maxiter trips with a done flag, so that the threads of a warp stay together in a kernel; a trip after the flag is set evaluates nothing. Static has no loop.

source
GeometricSolvers.φ — Function
φ(lf, α)

The merit of the line function lf at the step α, $φ(α) = \|r(x + α d)\|^2$, in the real type of α. A line search sees a line function only through this and φ′.

source
GeometricSolvers.φ′ — Function
φ′(lf, α)

The derivative $dφ/dα$ of the merit of the line function lf at the step α: one Jacobian–vector product.

source
GeometricSolvers.ExactStep — Type
ExactStep()

The step kind of a direction from a fresh Jacobian and an exact linear solve. For the merit $φ = \|r\|^2$ it gives $φ′(0) = -2φ(0)$, so no line search evaluates $φ′(0)$. For $φ(0) > \mathrm{floatmax}(R)/2$ that slope overflows, and the search reports NONFINITE.

source
GeometricSolvers.InexactStep — Type
InexactStep(η)

The step kind of a direction d with the linear residual $\|r + J d\| ≤ η \|r\|$, $0 ≤ η < 1$. It gives only $φ′(0) ≤ -2(1 - η) φ(0)$ (Eisenstat and Walker 1996, (1.2)). Along $α d$ the linear residual is $(1 - α) r + α (r + J d)$, so its bound is $η(α) = |1 - α| + α η$. Backtracking tests $\|r(x + α d)\| ≤ [1 - c_1 (1 - η(α))] \|r\|$ with this $η(α)$, which for $α ≤ 1$ is the update $η ← 1 - θ (1 - η)$ of each backtrack by $θ$, and evaluates no $φ′$. A step with $η(α) ≥ 1$ promises no decrease and is rejected. Bisection and StrongWolfe need the true slope and evaluate $φ′(0)$. The constructor checks nothing, so that solver code can build one in a kernel: a search given an $η$ outside $[0, 1)$ evaluates nothing and returns LINESEARCH_FAILED. Static reads no step kind and returns its step.

source
GeometricSolvers.MeasuredSlope — Type
MeasuredSlope()

The step kind of any other descent direction, such as one from a reused Jacobian: every line search except Static evaluates $φ′(0)$ once. A caller that already has $φ′(0)$ passes the number itself as the step kind, and no search evaluates it.

source
GeometricSolvers.LineSearchResult — Type
LineSearchResult{R <: Real}

The result of one linesearch. It is isbits.

Fields

  • α::R: the step, $0 < α ≤ α_{max}$.
  • φ::R: the merit at α, or NaN if the search did not evaluate it there.
  • code::ReturnCode: SUCCESS for an accepted step that decreases the merit by more than its round-off, and for the step of Static, which evaluates nothing; STALLED for a step that changes the merit by no more than its round-off, or a stationary anchor; NONFINITE for a non-finite anchor; LINESEARCH_FAILED for anything else: an ascending anchor, a spent cap, an increase of the merit, an invalid αmax, or a StrongWolfe step without the curvature condition. φ tells the caller whether the step of a failure still decreases the merit.
  • evaluations::Int32: the evaluations of φ and φ′ together.
source
GeometricSolvers.roundoff — Function
roundoff(φ₀)

The round-off resolution $τ = 4\,\mathrm{eps}(R)\,|φ(0)|$ of the merit, four units of relative round-off. A step that changes the merit by no more than $τ$ is at the round-off floor of the merit. It is proportional to $|φ(0)|$, not a count of ulps of $φ(0)$, so that it and the step floor smallest_step scale with the merit and do not jump by a factor 2 at a power of two. For a subnormal $φ(0)$, where the product underflows, it is at least four of the smallest subnormals, so it is never 0.

source
GeometricSolvers.smallest_step — Function
smallest_step(d₀, τ)

The step floor $τ / |φ′(0)|$ of every search: below it the decrease that the slope predicts, $α |φ′(0)|$, is smaller than the round-off $τ$, so no trial can show it. It is at least floatmin(R), so that no search tries a step of 0 when the quotient underflows.

source
GeometricSolvers.sufficient_decrease — Function
sufficient_decrease(φα, φ₀, demand, τ)

The Armijo test on the difference, $φ(α) - φ(0) ≤ \min(0, \mathrm{demand} + τ)$, where demand is the negative decrease the test demands and $τ$ the round-off allowance. The difference keeps a demand below one ulp of $φ(0)$, which $φ(0) + \mathrm{demand}$ would round away; the min lets $τ$ lower the demand, never accept an increase. StrongWolfe passes $τ = 0$.

source
GeometricSolvers.classify — Function
classify(φα, φ₀, τ)

The code of an accepted step: SUCCESS for a finite decrease by more than $τ$, STALLED for a change by no more than $τ$, and LINESEARCH_FAILED for an increase by more than $τ$ or a merit that is not finite.

source
GeometricSolvers.backtrack_step — Function
backtrack_step(φ₀, d₀, α, φα, αp, φp, p)

The next trial step after the rejected trial α with merit φα; αp and φp are the trial before it, and αp is NaN on the first backtrack. The model of $φ$ is a quadratic through $φ(0)$, $φ′(0) = d_0$ and $φ(α)$, then a cubic through $φ(α_p)$ as well, both in the merit divided by $|d_0|$. The cubic minimiser is $1 / (b + \sqrt{b^2 + 3a})$ for $b > 0$, which does not cancel as $a → 0$. The result is clamped to $[0.1 α, p α]$; a model without a minimiser gives $p α$, and a merit that is not finite at α gives $0.1 α$.

source
GeometricSolvers.zoom_step — Function
zoom_step(a, φa, da, b, φb, db)

The minimiser of the cubic through the merit φa, φb and its derivative da, db at the two ends a and b of a bracket (Nocedal and Wright, (3.59)), clamped to the inner 80 % of the bracket. The discriminant is scaled by the largest of $|θ|$, $|d_a|$, $|d_b|$, as in Moré and Thuente, so it neither overflows nor depends on the scale of the merit. A cubic without a minimiser gives the middle of the bracket. Without a finite db it is the minimiser of the quadratic through φa, da and φb.

source
GeometricSolvers.LinearMethod — Type
LinearMethod{TF}

The supertype of the linear-solver methods. TF is the factorisation precision: Nothing for the working type of the problem, a real float type, or a PrecisionMarker. It is a type parameter and not a field, because a type stored in a field is not isbits, and a method must enter a kernel.

TF is a real type for a complex problem too: LUFactorization(Float32) factorises a ComplexF64 Jacobian in ComplexF32.

source
GeometricSolvers.ToReal — Type
ToReal{R}()

The Adapt rule that converts every AbstractFloat in a method tree to R. Applied on the host with R = real(eltype(x)), it lets a method written with Float64 literals run in a Float32 kernel that never sees a Float64. An integer, a type and a function pass through unchanged.

Each method type that holds a float adds Adapt.@adapt_structure for itself.

julia> GeometricSolvers.Adapt.adapt(GeometricSolvers.ToReal{Float32}(), (0.7, Int32(3), sin))
(0.7f0, 3, sin)
source
GeometricSolvers.record — Function
record(status::SolverStatus{R}, info::StepInfo{R})

Fold the result of one step into the status: count the iteration, take the code and the norms of the step, add its failures, and keep promoted once it is set.

source
GeometricSolvers.converged — Function
converged(options::Options{R}, status::SolverStatus{R}, fnorm₀::R, xnorm::R)

The stopping test of Options at status, for a start with residual norm fnorm₀ and an iterate of norm xnorm. It holds once min_iterations steps are taken and either the residual test or, after the first step, the step test holds. At 0 iterations it is the test before the first step: the relative residual test then holds only for $F(x₀) = 0$, so a start passes it through f_abstol.

source
GeometricSolvers.rdot — Function
rdot(a, b)

The real part of the inner product, $\mathrm{Re} \sum_i \bar{a}_i b_i$, in real(promote_type(eltype(a), eltype(b))). That is the dot product of the real vector space under a complex T, which a merit derivative needs. Arguments with different axes raise a DimensionMismatch.

source