Skip to content

PALC: pass the predictor z_pred and the step length ds to every corrector callback - #368

Open
ParadaCarleton wants to merge 2 commits into
bifurcationkit:masterfrom
ParadaCarleton:palc-corrector-zpred
Open

ParadaCarleton wants to merge 2 commits into
bifurcationkit:masterfrom
ParadaCarleton:palc-corrector-zpred

Conversation

@ParadaCarleton

Copy link
Copy Markdown
Contributor

The Newton callback of newton_palc sees the predictor only as x in its first call, and its closing call gets neither the predictor nor the step length, so a callback_newton that compares iterates with the predictor (for example, to refuse a corrector that lands far from it) has to copy them into mutable state. This adds z_pred and ds to the named tuple of all three PALC calls and changes nothing else; existing callbacks ignore the extra fields. A test in test/continuation/simple_continuation.jl checks that every PALC call carries both, with x == z_pred at step 0, and it fails without the change.

…ctor callback

A Newton callback could see the predictor only as x at step 0 and not at
all in the closing call, so a callback comparing iterates to the predictor
had to copy it into mutable state.

Claude-Session: https://claude.ai/code/session_01BEoUbvf41HZqywHoqybG7q
@rveltz

rveltz commented Oct 29, 2026

Copy link
Copy Markdown
Member

OK, that's a nice suggestion.
I don't like the callback arguments, even before your changes.
To me, the best is to pass iter, state and with this the user has all it needs.
Do you agree?

@ParadaCarleton

Copy link
Copy Markdown
Contributor Author

I agree that the callback arguments are awkward, but iter, state alone cannot replace them. Inside a corrector the iterate x, p, res_f, J, residuals and step live in local variables of newton_palc; state holds none of them during the corrector (state.z_pred is the predictor; the return records convergence, the iteration counts and the previous solution). A callback that stops or damps a Newton step needs the current iterate, so state would have to grow to hold it.

Proposal: keep the NamedTuple and add iter and state to it ((; x, res_f, J, residual, step, residuals, iter, state, ...)). The NamedTuple is the public callback API since v0.2.0 (4d74943, 5235333): user callbacks read its fields by name, so an iter, state-only signature would break long-standing user code, and newton shares the convention. Adding fields cannot break a callback that reads fields by name (a callback that dispatches on the exact NamedTuple type would see a new type), and a callback then reads contparams, z0 (getsolution(state)), z_pred and ds from state and iter.

The same need comes from downstream: a step-tracking callback wants the step's start z0, the predictor z_pred, ds and to know which call is the last one. The first and the last call of a corrector have no J, so a closing = true field marks the last one. The fields then are the Newton iterate and its history, iter, state, and closing; the fields this PR adds (z_pred, ds) and the older z0 (present in the first and per-iteration calls, absent from the last) and contparams stay for compatibility.

I have not changed this PR. If you agree, I will push iter, state and closing as a commit.

2 similar comments
@ParadaCarleton

Copy link
Copy Markdown
Contributor Author

I agree that the callback arguments are awkward, but iter, state alone cannot replace them. Inside a corrector the iterate x, p, res_f, J, residuals and step live in local variables of newton_palc; state holds none of them during the corrector (state.z_pred is the predictor; the return records convergence, the iteration counts and the previous solution). A callback that stops or damps a Newton step needs the current iterate, so state would have to grow to hold it.

Proposal: keep the NamedTuple and add iter and state to it ((; x, res_f, J, residual, step, residuals, iter, state, ...)). The NamedTuple is the public callback API since v0.2.0 (4d74943, 5235333): user callbacks read its fields by name, so an iter, state-only signature would break long-standing user code, and newton shares the convention. Adding fields cannot break a callback that reads fields by name (a callback that dispatches on the exact NamedTuple type would see a new type), and a callback then reads contparams, z0 (getsolution(state)), z_pred and ds from state and iter.

The same need comes from downstream: a step-tracking callback wants the step's start z0, the predictor z_pred, ds and to know which call is the last one. The first and the last call of a corrector have no J, so a closing = true field marks the last one. The fields then are the Newton iterate and its history, iter, state, and closing; the fields this PR adds (z_pred, ds) and the older z0 (present in the first and per-iteration calls, absent from the last) and contparams stay for compatibility.

I have not changed this PR. If you agree, I will push iter, state and closing as a commit.

@ParadaCarleton

Copy link
Copy Markdown
Contributor Author

I agree that the callback arguments are awkward, but iter, state alone cannot replace them. Inside a corrector the iterate x, p, res_f, J, residuals and step live in local variables of newton_palc; state holds none of them during the corrector (state.z_pred is the predictor; the return records convergence, the iteration counts and the previous solution). A callback that stops or damps a Newton step needs the current iterate, so state would have to grow to hold it.

Proposal: keep the NamedTuple and add iter and state to it ((; x, res_f, J, residual, step, residuals, iter, state, ...)). The NamedTuple is the public callback API since v0.2.0 (4d74943, 5235333): user callbacks read its fields by name, so an iter, state-only signature would break long-standing user code, and newton shares the convention. Adding fields cannot break a callback that reads fields by name (a callback that dispatches on the exact NamedTuple type would see a new type), and a callback then reads contparams, z0 (getsolution(state)), z_pred and ds from state and iter.

The same need comes from downstream: a step-tracking callback wants the step's start z0, the predictor z_pred, ds and to know which call is the last one. The first and the last call of a corrector have no J, so a closing = true field marks the last one. The fields then are the Newton iterate and its history, iter, state, and closing; the fields this PR adds (z_pred, ds) and the older z0 (present in the first and per-iteration calls, absent from the last) and contparams stay for compatibility.

I have not changed this PR. If you agree, I will push iter, state and closing as a commit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants