Repository navigation
PALC: optional step control by the corrector's quality and orientation check (off by default) - #347
ParadaCarleton wants to merge 27 commits into
Conversation
cfe0341 to
7f26c34
Compare
…rQuality) and orientation check, off by default
7f26c34 to
3792c71
Compare
cc200dd to
3792c71
Compare
… makes an angle with the tangent of more than max_angle (off by default)
…Bordered tangent at the new point is computed for the test and reused by the next predictor); max_angle = :derived takes 2 max_distance / |ds| up to max_angle_cap
…ngent is oriented by the determinant of the bordered matrix and a step is rejected when the new tangent has a negative dot product with the previous one
…to the Newton iteration count; a debug record tells whether max_growth capped the growth of ds
… the bound by Newton's method with the corrector quality tests and the orientation test
…son, so a Float type without Int conversion of sign (not Float32) works
| # we also extend this for ContIterable | ||
| getdot(it::ContIterable) = getdot(it.alg) | ||
| getθ(it::ContIterable) = getθ(it.alg) | ||
| getθ(it::ContIterable) = convert(eltype(it), getθ(it.alg)) |
There was a problem hiding this comment.
Done in d290a4e: getθ(it::ContIterable) = oftype(it.contparams.ds, getθ(it.alg)) (Palc.jl:114).
There was a problem hiding this comment.
Done in d290a4e: getθ(it::ContIterable) = oftype(it.contparams.ds, getθ(it.alg)) (Palc.jl:114).
| # we want to start at (u0, p0), not at (u1, p1) | ||
| _copyto!(state.z, state.z_old) | ||
| if alg.orientation_check | ||
| initial_orientation!(state, iter, alg) |
There was a problem hiding this comment.
name too generic, only called when orientation check
There was a problem hiding this comment.
Renamed init_orientation_check! in d290a4e. It runs only when tracks_turn(alg) holds (orientation_check or a max_turn).
|
|
||
| The corrector of a step whose predictor lies beyond `p_min` or `p_max`: the predictor is put on the bound and Newton's method solves `F(x, p) = 0` there (the `Natural` corrector), with the tests of `control` on the lengths of its steps, taken in the norm of the arc length constraint with no parameter component. Returns the solution and its quality like `_newton_palc`. | ||
| """ | ||
| function _newton_at_bound(iter::AbstractContinuationIterable, |
There was a problem hiding this comment.
I'd say this function is not need. A callback to newton would suffice
There was a problem hiding this comment.
You are right. _newton_at_bound is deleted in d290a4e. The corrector quality tests and the orientation test run in a Newton callback that wraps callback_newton, so newton_palc is unchanged by them.
| stepsizecontrol::Bool = true | ||
| "factor by which step size control multiplies `ds` after the current step. Set by the corrector when the algorithm decides it (for example `PALC` with `step_control`), `nothing` otherwise." | ||
| step_factor::Union{Nothing, T} = nothing | ||
| "tangent at the new solution, which the corrector computed to measure the turn of the tangent (`PALC` with `max_angle` and a `Bordered` predictor). The next predictor takes it instead of computing it again. `nothing` otherwise." |
There was a problem hiding this comment.
this seems too specific to PALC except for
next_tangent
Proposal:- add a field
cache::Tcachedefaulting tonothingto ContState - define a cache with
step_factor, orientation
There was a problem hiding this comment.
Done in d290a4e as you proposed. ContState has cache::Tcache = nothing; PALC fills it with a PALCCache (step_factor, orientation, and the data of the corrector quality tests). next_tangent stays a ContState field since it is not specific to PALC. Multiple and AutoSwitch carry the inner PALC cache (6690142).
|
Hi, Thank you for this production!
On a side note:
|
…outside stops the corrector, not converged, before F is evaluated there, so no clamped point is reported as converged
|
Pushed 479fb7a. F(u, p) = @. u^3 - u - (p.λ - 1)
prob = BifurcationProblem(F, [-1.3247179], (λ = 0.0,), (@optic _.λ))
opts = ContinuationPar(ds = 0.05, dsmin = 1e-6, dsmax = 0.5, a = 0.5, p_min = 0.0, p_max = 2.0, η = 150.0, tol_stability = 1e-10, dsmin_bisection = 1e-16, tol_bisection_eigenvalue = 1e-16, tol_param_bisection_event = 1e-16, max_steps = 200, detect_fold = true, detect_bifurcation = 0, newton_options = NewtonPar(tol = sqrt(eps())))
br = continuation(prob, PALC(), opts)
any(point -> abs(point.x[1]) < 1 / sqrt(3), br.sol) # false before 479fb7ad: the branch never enters |u| < 1/√3 |
…tely typed and the corrector's keyword call dispatches statically
|
PALC's |
| state.itlinear = sol.itlineartot | ||
| # record previous solution | ||
| if converged(sol) | ||
| if converged(state) |
There was a problem hiding this comment.
That change is gone. It was there so that a step refused by the orientation test (converged corrector, tangent pointing back) was recorded as not converged and kept state.z_old. Since d290a4e the orientation test runs in the Newton callback: a refusal stops Newton with converged = false, so sol.converged already says it and _update_field_but_not_solution! is the same as on master. Contbase.jl now differs from master by _step_growth (and the step_size_control! signature), and _step_growth forms the Int ratio (Nmax - itnewton) / Nmax in the type of ds, because the Int quotient is a Float64 and promoted the step of a Float32 continuation.
|
Thank you!
|
…ewton callback wrapping callback_newton _newton and newton_palc return a plain NonLinearSolution again. PALCStepCheck, built per corrector, rejects a step by returning false and leaves the corrector's quality in the PALCCache that ContState.cache carries. Docstrings state the tests and cite Allgower and Georg (6.1.10), Program 1 and (2.1.7); the determinant's O(n^3) cost is documented.
…e an iterate outside [p_min, p_max] refuses the long step that would jump to the upper sheet
…te after a failed line search and stops at an iterate outside [p_min, p_max]; the Bordered tangent takes dFdl from R01 at its point
…rg's step length adaptation (6.1.10): a step whose corrected tangent turns by max_turn is refused and a smaller turn slows the next step
|
…arch halves a trial past a bound; test wrappers and Float32 Bordered orientation Claude-Session: https://claude.ai/code/session_0195j23m7MRde8V6ApNmKjBR
With `by_determinant = true` and a `MatrixBLS`, `bordered_tangent` builds the bordered matrix once (`bordered_matrix`, shared with `MatrixBLS`), factorises it with `lu`, solves for the tangent and reads the determinant sign from the same factorisation (pivot signs times permutation parity). A sparse matrix takes the sign from its UMFPACK factorisation through `logabsdet`. The orientation test of a `Bordered` tangent then costs no factorisation beyond the tangent's own.
|
You were right that This is one commit ( Why the determinant stays: orienting by the dot product with the previous tangent cannot see a single step across a turn of more than π/2 when the chord still looks forward; the determinant sign does. A chord-angle |
|
Pushed the single-LU orientation as fab2994 and added its cost to the PR description. |
…to tol The loops stop at res <= tol but the verdict required res < tol, so a solve that stopped exactly at tol (including tol = 0 at an exact root) was reported not converged.
… deceleration but rejects no step on it For a Newton callback that judges contraction itself, in its own norm. Claude-Session: https://claude.ai/code/session_014FMUtEXDn4dox6driwcGkm
…n, turn or tangent cosine refuses the step Each test compared with >=, > or <, which a NaN fails, so a NaN passed as an accepted step. Claude-Session: https://claude.ai/code/session_013WmUuZon4D1nexEEjGV1WM
…of reaching the orientation test NaN >= tol is false, so the closing check took a NaN corrector as converged and computed its bordered tangent; LAPACK then threw on the NaN Jacobian. Claude-Session: https://claude.ai/code/session_013WmUuZon4D1nexEEjGV1WM
… stops continuation With tol = 0 two Newton steps of length zero gave the contraction rate 0 / 0. With rejects_contraction = false nothing rejected it, so the deceleration and then ds went NaN. abs(NaN) <= dsmin is false, so every failed step halved ds forever. A step of length zero now has rate zero, and the dsmin test is written as a negation so that a NaN ds stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0195j23kWb8rVLQf4ZxZ1o1R
… tol newton_palc accepts a point with `residual <= tol`, but PALCStepCheck ran the orientation test only when `residual < tol`. At tol = 0, a corrector that lands exactly on a root was accepted without the test, so a step onto a reversed tangent passed. The `<` dates from 5b0f67c (as `>= tol` returns early); af75aa3 kept it while refusing NaN. The guard now uses Newton's own predicate `_converged(residual <= tol, verdict)`, which still skips a NaN residual. Claude-Session: https://claude.ai/code/session_014FMUtEXDn4dox6driwcGkm
This adds two optional
PALCsettings for step control, following Allgower and Georg:step_control = CorrectorQuality(; max_distance = nothing, max_contraction = 0.6, max_growth = 1.1, η = 0.1), which, following Allgower and Georg (Numerical Continuation Methods, 1990) and Gambit's logit tracer, rejects a step (retried at halfds) when the first Newton step is longer thanmax_distanceor a later one contracts by more thanmax_contraction, and growsdsafter an accepted step by a factor set by those same ratios instead of by the Newton iteration count, withnothingswitching a test off.orientation_check = truerejects a step whose new tangent has a negative dot product with the previous tangent (Allgower and Georg 6.1.10), testing that the continuation has not turned back on itself or taken a step so long that the branch bends past a right angle. The distance threshold is in the state's units, which is why that test isnothingunless the caller supplies one, while the contraction rate and the orientation have no units.test/continuation/palc_step_control.jlcovers the rules, both settings through the folds of a cubic with theSecantandBorderedpredictors inFloat32andFloat64, and the orientation test, andNews.mdhas an entry.Cost of the orientation test (both settings are off by default):
Secanttangent: the chord, two norms and one dot product per step, no factorization.orientation_checkormax_turnset, aBorderedtangent is oriented by the sign of the determinant of the bordered matrix (Allgower and Georg, (2.1.7)). The sign comes from the LU factorization that solves for the tangent, atO(n)extra, so a step adds no factorization; the start computes one extraBorderedtangent.MatrixBLSsolver; with a matrix-free or iterative solver the continuation throws at initialization.