solprop.backends.multistep#

The interaction-picture Adams march every CPU backend steps with (SEP 13, SEP 14).

One accepted step of a mature method costs one expensive source evaluation (explicit Adams-Bashforth and PEC) or two (PECE), against seven for the reference backend’s IP-DOPRI5. The march keeps a short history of transported derivatives, re-anchored at every accepted step, and decides each step on a local-defect estimate formed from that history without another evaluation (SEP 13 sections 4.1–4.3).

The interaction picture is anchored at the start of each step, z_n. With U(z_2, z_1) = exp(Phi(z_2) - Phi(z_1)), the variable v(z) = U(z_n, z) a(z) obeys v' = g(z) = U(z_n, z) N(U(z, z_n) v, z). A history derivative is stored in the current anchor’s frame; accepting a step re-anchors every one of them by the step’s own propagator, which is the multiplication the Runge-Kutta march already performs on its state, so no table is queried backwards. Every derivative keeps its position and the identity of the field it was evaluated at – the accepted field, or the predicted one PEC stores in its place.

The march is shared: a backend supplies two hooks of the step driver, _adams_source (one source evaluation at a lab-frame field, returned in the step’s anchor frame) and _adams_propagator, and the driver’s own state, landing and recording do the rest. numpy_ref evaluates through its reference passes and numba_cpu through its fused stage kernels (SEP 14 phase 2).

Lifecycle, owned here and not by the shared driver’s Runge-Kutta march (SEP 13 section 6.1):

  • a trial computes its predictor, its single source call and its estimate without touching the history; only an accepted step commits, so a rejection leaves the accepted history exactly as it was;

  • PECE decides acceptance before its second call, so a rejection costs one call;

  • explicit Adams-Bashforth rejects for free on a history estimate before the call, and checks the step again afterwards with the new derivative (section 4.3), at the cost of the call it has made;

  • a hard kink is landed on and the history restarted on its far side; a save plane is a pure observation and keeps the history;

  • a load or a resume starts afresh, at order one, and its startup calls are counted.

Module Attributes

RESTART_AFTER

Consecutive rejections after which the history is restarted at order one.

ORDER_HYSTERESIS

Factor by which a neighbouring order's predicted step must win to be adopted.

COLLAR_STABILITY

Share of an order's negative-real stability interval the order choice allows.

Functions

compensation_tables(nodes, order, orders, *, ...)

Return a trial's transported-compensation tables, for a resident engine.

coupled_moments(exponent, count, block, dz, *)

Return a coupled trial's flow and moments k! phi_{k+1}(h B), k < count.

exponential_tables(nodes, order, orders, *, ...)

Return an exponential trial's coefficient tables, for a resident engine.

factored_defect_rows(nodes, orders, width)

Return each exponential defect factored: a divided difference and a polynomial.

fused_tables(nodes, order, orders, *, helpers)

Return a fused trial's coefficient tables, for an engine that applies them.

source_absent(problem)

Return whether a problem's complete source is structurally zero.

Classes

AdamsEngine(*args, **kwargs)

What AdamsMarch asks of the engine that holds its history (SEP 16).

AdamsKernels(*[, helpers])

The field arithmetic of a fused Adams step, in NumPy (SEP 14 phase 2).

AdamsMarch(backend, method[, kernels, ...])

March a reference backend's state with an interaction-picture Adams method.

CallLedger([startup, predictor, corrected, ...])

Expensive source evaluations of one run, by purpose (SEP 13 section 5).

CoupledBlock(active, vectors, triangular, ...)

A step's frozen coupled block on the bins that carry one (SEP 16 phase 5).

CoupledExponentialKernels(*[, helpers])

The reference passes beside a coupled flow (SEP 16 phase 5).

ExponentialKernels(*[, helpers])

The field arithmetic of an exponential Adams step, in NumPy (SEP 16 phase 2).

HostHistory(backend, method, kernels, ...)

The Adams history and its field arithmetic, on host arrays (SEP 16 phase 3b).

class solprop.backends.multistep.AdamsEngine(*args, **kwargs)[source]#

Bases: Protocol

What AdamsMarch asks of the engine that holds its history (SEP 16).

The march keeps the controller; an engine keeps the history’s rows and does every state-sized operation, so that a backend can keep them where its state lives. HostHistory is the reference; docs/dev/stepper.md states the contract.

compensated: bool#

Whether trials carry a varying operator’s linear compensation (SEP 16 4.2).

dense: Any#

The general path’s last accepted step (a _DenseStep or None), which the march sets for observation.

exponential_accept(keep, free, *, interpolate)[source]#

Adopt the held exponential trial.

Parameters:
keepnumpy.ndarray

The history rows kept, int64.

freeint

The row the new sample takes.

interpolatebool

Whether the step’s interpolant is kept for dense observation.

exponential_trial(z, dz, nodes, slots, order, orders)[source]#

Form an exponential trial; return its scored norms.

Parameters:
z, dzfloat

The anchor and the step, m.

nodeslist of float

The history’s normalised positions, newest first.

slotsnumpy.ndarray

Their store rows, int64.

orderint

The step’s order.

orderslist of int

The orders scored, the step’s own first.

Returns:
numpy.ndarray

One scaled norm per scored order.

fused_accept(keep, free, *, interpolate)[source]#

Adopt the held interaction-formulation trial.

Parameters:
keepnumpy.ndarray

The history rows kept, int64.

freeint

The row the new sample takes.

interpolatebool

Whether the step’s interpolant is kept for dense observation.

fused_trial(z, dz, nodes, slots, order, orders)[source]#

Form an interaction-formulation trial; return its scored norms.

Parameters:
z, dzfloat

The anchor and the step, m.

nodeslist of float

The history’s normalised positions, newest first.

slotsnumpy.ndarray

Their store rows, int64.

orderint

The step’s order.

orderslist of int

The orders scored, the step’s own first.

Returns:
numpy.ndarray

One scaled norm per scored order.

hold()[source]#

Return the accepted state, to restore after a dense observation.

Returns:
object

A handle restore() takes.

load_rows(rows, shape)[source]#

Install rows snapshot_rows() returned, refusing rows it does not keep.

Parameters:
rowscollections.abc.Mapping

The rows, by name.

shapetuple of int

The state’s shape.

Raises:
BackendError

If the rows are another engine’s, missing, or of the wrong shape; nothing is installed then.

property n_slots: int[source]#

The history store’s rows.

observe(position)[source]#

Return the masked field at position inside the last accepted step.

Parameters:
positionfloat

The observation, m.

Returns:
object

The field, as the backend holds a state.

reset()[source]#

Forget the last accepted step and any trial in flight.

restore(z, held)[source]#

Make a held accepted state current again at z.

Parameters:
zfloat

Its position, m.

heldobject

What hold() returned.

snapshot_rows()[source]#

Return host copies of the rows a resume needs to continue the history.

Returns:
dict

The rows by name, each (n_slots, *shape), in the engine’s own names (HostHistory.snapshot_rows states the host engine’s).

start(z, *, stored)[source]#

Evaluate the history’s first sample at the accepted state.

Parameters:
zfloat

The accepted position, m.

storedbool

Whether the sample goes to the store’s row zero.

Returns:
numpy.ndarray or None

The sample on the host, or None when the rows are resident.

value(slot)[source]#

Return a store row on the host, or None when the rows are resident.

Parameters:
slotint

The row.

Returns:
numpy.ndarray or None

The row, or None.

class solprop.backends.multistep.AdamsKernels(*, helpers=Helpers(name='numba'))[source]#

Bases: object

The field arithmetic of a fused Adams step, in NumPy (SEP 14 phase 2).

Three passes serve a step of the one-order-higher PEC family: the predictor’s lab-frame field, the corrected field with the defects of the current and the neighbouring orders, and the history’s transport to the new anchor. The history is a store of rows, newest first by slots. numba_cpu compiles each pass into one traversal (solprop.backends.numba_cpu.multistep); this is the reference the compiled form is checked against.

Parameters:
helperssolprop._helpers.Helpers, optional

The owning backend’s implementation of the Adams basis (SEP 29 section 6.6); the shared Numba one by default.

correct(state, propagator, source, store, slots, corrector, rows, dz, mask, divisor)[source]#

Return the corrected lab-frame field and the scaled norms of the defects.

With X = (source, store[slots[0]], ...), the field is U (a_n + dz sum_t c_t X_t) and each norm is the weak norm of U dz sum_t rows[r, t] X_t over the in-band bins.

Parameters:
state, propagatornumpy.ndarray

As for predict().

sourcenumpy.ndarray

The source at the predicted field, in the anchor’s frame.

storenumpy.ndarray

The history rows.

slotsnumpy.ndarray

The rows after the source, newest first, int64.

correctornumpy.ndarray

The corrector’s weights over X, zero past its own nodes.

rowsnumpy.ndarray

One defect’s weights over X per row, shape (n_rows, 1 + n_slots).

dzfloat

The step, m.

masknumpy.ndarray

The in-band bins, Boolean.

divisornumpy.ndarray

The norm’s scale per bin.

Returns:
tuple

The corrected field and one norm per row.

predict(state, propagator, store, slots, weights, dz)[source]#

Return the predictor’s lab-frame field U (a_n + dz sum_t w_t g_t).

Parameters:
statenumpy.ndarray

The accepted state at the anchor.

propagatornumpy.ndarray

U(z_n + dz, z_n).

storenumpy.ndarray

The history rows.

slotsnumpy.ndarray

The rows the predictor combines, newest first, int64.

weightsnumpy.ndarray

One integrated weight per row.

dzfloat

The step, m.

Returns:
numpy.ndarray

The predicted field at the end of the step.

transport(store, slots, propagator, new_slot, source)[source]#

Re-anchor the history at the step’s end and store the new derivative.

Parameters:
storenumpy.ndarray

The history rows, updated in place.

slotsnumpy.ndarray

The rows of the kept history, int64.

propagatornumpy.ndarray

U(z_n + dz, z_n).

new_slotint

The free row the new derivative goes to.

sourcenumpy.ndarray

The new derivative in the step’s anchor frame.

weights(nodes, upper=1.0)[source]#

Return the integrated weights of an interpolant on distinct nodes.

Parameters:
nodeslist of float or numpy.ndarray

The normalised nodes, accepted positions and so distinct.

upperfloat, optional

The integral’s upper limit.

Returns:
numpy.ndarray

One weight per node, as integrated_weights gives them.

class solprop.backends.multistep.AdamsMarch(backend, method, kernels=None, exponential=None, *, compensation='transported')[source]#

Bases: object

March a reference backend’s state with an interaction-picture Adams method.

Parameters:
backendsolprop.backends.driver.StepDriver

The backend whose state, source evaluation and propagators are used. It owns the step history and the statistics rows; this object owns the derivative history and the call ledger.

methodsolprop.stepper.multistep.Multistep

The method.

kernelsAdamsKernels or None, optional

The field arithmetic of the fused path; None takes the NumPy reference.

exponentialExponentialKernels or None, optional

The exponential formulation’s field arithmetic; None takes the NumPy reference. Used only when the method’s formulation is "exponential".

compensation{‘transported’, ‘untransported’}, optional

How a varying operator’s linear compensation is integrated (SEP 16 Q8): "transported", the delivered treatment, or "untransported", kept as a test witness only.

begin_run()[source]#

Start a run’s accounting afresh: the call ledger and the last rejection.

Called by set_initial(), so that a backend loaded for another run counts that run’s calls alone and chooses its first order as a fresh backend would (SEP 22 section 4.7). A resume restores its persisted ledger afterwards.

export_refusal()[source]#

Return why this march cannot carry its history across a resume, or None.

The fused and exponential paths keep their history in an engine, and every engine exports and installs its rows (AdamsEngine, SEP 29 section 6.6). The general path combines a list of host arrays one product at a time and keeps no store a snapshot could hold.

Returns:
str or None

The reason, or None when the march exports and installs its history.

load(snapshot)[source]#

Continue from a snapshot taken where the backend’s state now stands.

The backend has been loaded at the snapshot’s position; this installs the accepted state exactly as it was held, the history, the order, the rejection count and the proposed step, so that the march goes on as the uninterrupted one would have. Nothing is changed when the snapshot cannot be used.

Parameters:
snapshotsolprop.output.schema.MarchSnapshot

What snapshot() returned, possibly read back from a file.

Returns:
str or None

None when the history was continued, otherwise why it starts afresh.

march_to(target)[source]#

Step the backend’s state adaptively to target, landing on it exactly.

Parameters:
targetfloat

Position to reach, m.

Raises:
ConvergenceError

If the proposal falls below the contract’s dz_min.

replay_step(dz, end, slack)[source]#

Take one prescribed step from the backend’s position and accept it.

The unit a lockstep batch advances each member by (solprop.backends.batch.LockstepRunner): the budget, the startup of an empty history, the step and, when end is a hard kink, the restart.

Parameters:
dzfloat

The step, m.

endfloat

The schedule’s edge it ends on, m.

slackfloat

How far an edge may sit from a kink it lands on, m.

Returns:
float

The step’s scaled error norm, recorded and not acted on.

Raises:
ConvergenceError

If the norm is not finite: the field has diverged, and a replay cannot reject the step.

reset()[source]#

Forget the history: the state was loaded or changed outside the march.

snapshot()[source]#

Return the march’s state at its accepted position, for a resume.

Returns:
solprop.output.schema.MarchSnapshot or None

The snapshot, or None where there is nothing a resume could continue: no history yet, the march ahead of an interpolated observation, or a march that cannot carry its history (export_refusal()).

solprop.backends.multistep.COLLAR_STABILITY = 1.0#

Share of an order’s negative-real stability interval the order choice allows.

class solprop.backends.multistep.CallLedger(startup=0, predictor=0, corrected=0, rejected=0, accepted_steps=0, rejected_steps=0, restarts=0, failure_restarts=0, dense_observations=0, orders=<factory>)[source]#

Bases: object

Expensive source evaluations of one run, by purpose (SEP 13 section 5).

Attributes:
startupint

Evaluations at an accepted field to start or restart the history.

predictorint

Evaluations at a predicted field, accepted or not.

correctedint

PECE’s evaluations at the corrected, accepted field.

rejectedint

Evaluations spent on steps that were then rejected (included in the above).

accepted_steps, rejected_stepsint

Step counts.

restartsint

History restarts at hard kinks.

failure_restartsint

History restarts after RESTART_AFTER consecutive rejections.

dense_observationsint

Save planes observed by interpolation instead of a landing; none costs a call.

orderslist of int

The predictor order of every accepted step.

accepted_steps: int = 0#
corrected: int = 0#
dense_observations: int = 0#
failure_restarts: int = 0#
classmethod from_record(record)[source]#

Restore validated cumulative counts without restoring numerical history.

Parameters:
recordmapping

Persisted counter fields and accepted predictor orders.

Returns:
CallLedger

Independently owned counters.

Raises:
BackendError

If a counter is absent, unknown, negative or not an integer.

orders: list[int]#
predictor: int = 0#
rejected: int = 0#
rejected_steps: int = 0#
restarts: int = 0#
startup: int = 0#
property total: int[source]#

Return every charged source evaluation.

Returns:
int

Startup, predictor and corrected evaluations together.

class solprop.backends.multistep.CoupledBlock(active, vectors, triangular, section, end_weights=None, residual=None, basis=None)[source]#

Bases: object

A step’s frozen coupled block on the bins that carry one (SEP 16 phase 5).

Beside a coupled flow the cumulative diagonal Phi is a multiple of the identity in the channel axis (docs/dev/kernel-contract.md, coupled_flow), so the step’s reference is B = (Delta Phi / h) I + G_s with G_s = Z T Z^H the frozen block of the section the step is anchored in, and the two parts commute.

Attributes:
activenumpy.ndarray

Which bins carry a block, bool, shape (n_omega,).

vectors, triangularnumpy.ndarray

Z and T of the section’s block, 1/m for T, shape (n_active, n_modes, n_modes).

sectionint

The section the step is anchored in.

end_weightsnumpy.ndarray or None

The flow’s term weights at the step’s end, read on the step’s side of a section edge (StageScalars.flow_weights); None without a residual.

residualcallable or None

(state, weights) -> R_s(z) state for a frozen-residual flow: the generator at the position the weights belong to less this section’s frozen block, on the active bins the error norm scores and zero elsewhere, as the Runge-Kutta stage applies it; None for a constant-Schur flow.

basistuple or None

The section’s eigenbasis (W, Y, lambda, route), as solprop.translate.coupled.schur_eigenbasis() returns it: the bins on the route take their matrix functions as W f(x + h lambda) Y, the compiled backends’ evaluation. None, the reference’s, takes every bin from the augmented exponential.

active: ndarray#
basis: tuple[ndarray, ndarray, ndarray, ndarray] | None = None#
end_weights: ndarray | None = None#
residual: Callable[[ndarray, ndarray], ndarray] | None = None#
section: int#
triangular: ndarray#
vectors: ndarray#
class solprop.backends.multistep.CoupledExponentialKernels(*, helpers=Helpers(name='numba'))[source]#

Bases: ExponentialKernels

The reference passes beside a coupled flow (SEP 16 phase 5).

A coupled trial’s flow and moments are _MatrixMoments (coupled_moments()): matrices in the channel axis on the bins that carry a block, elementwise elsewhere. The passes are ExponentialKernels’ with those products.

carry_coupled(exponent, dz, rows, slots, spans, block)[source]#

Return each sample’s compensation carried to the step’s end (SEP 16 4.5).

C_j = (D(z_j) - x / h) u_j + R_s(z_j) u_j: the diagonal chord’s difference of rates and the frozen-residual’s residual relative to the step’s section, formed from the sample’s own flow weights – so that a step in a new section rebuilds the history’s residual against its block – and carried by the reference’s forward flow exp((1 - theta_j) h B).

Parameters:
exponentnumpy.ndarray

x = Delta Phi over the step.

dzfloat

The step, m.

rowstuple

The history’s rates and states, by slot, and each slot’s flow weights.

slotsnumpy.ndarray

The samples carried, newest first.

spansnumpy.ndarray

1 - theta_j per sample.

blockCoupledBlock

The step’s frozen block.

Returns:
numpy.ndarray

One carried row per sample.

correct(state, flow, moments, source, store, slots, corrector, rows, dz, mask, divisor, extra=None)[source]#

Return the corrected field and the defects’ norms through the block.

Parameters:
statenumpy.ndarray

The accepted state at the anchor.

flow, moments_MatrixMoments or numpy.ndarray

The coupled trial’s flow and moments.

sourcenumpy.ndarray

The untransported source at the predicted field.

storenumpy.ndarray

The history rows.

slotsnumpy.ndarray

The rows after the source, newest first, int64.

correctornumpy.ndarray

The corrector’s coefficients.

rowsnumpy.ndarray

One defect’s coefficients per row.

dzfloat

The step, m.

masknumpy.ndarray

The in-band bins, Boolean.

divisornumpy.ndarray

The norm’s scale per bin.

extratuple or None, optional

A varying operator’s compensation terms.

Returns:
tuple

The corrected field and one norm per row.

coupled()[source]#

Return these passes.

Returns:
CoupledExponentialKernels

This object.

coupled_moments(exponent, count, block, dz)[source]#

Return a coupled trial’s flow and moments (coupled_moments()).

Parameters:
exponentnumpy.ndarray

Delta Phi over the step.

countint

The moments formed.

blockCoupledBlock

The step’s frozen block.

dzfloat

The step, m.

Returns:
_MatrixMoments

The flow and the moments.

predict(state, flow, moments, store, slots, coefficients, dz)[source]#

Return the predicted field through the trial’s block.

Parameters:
statenumpy.ndarray

The accepted state at the anchor.

flow, moments_MatrixMoments or numpy.ndarray

The coupled trial’s flow and moments.

storenumpy.ndarray

The history rows.

slotsnumpy.ndarray

The rows the predictor combines, newest first, int64.

coefficientsnumpy.ndarray

The predictor’s Lagrange coefficients.

dzfloat

The step, m.

Returns:
numpy.ndarray

The predicted lab-frame field at the end of the step.

class solprop.backends.multistep.ExponentialKernels(*, helpers=Helpers(name='numba'))[source]#

Bases: object

The field arithmetic of an exponential Adams step, in NumPy (SEP 16 phase 2).

The history is a store of untransported source rows, newest first by slots. A trial forms M_k = k! phi_{k+1}(x), x = h D per element, once, and applies every formula it needs in the monomial basis of its interpolant, h sum_k M_k (sum_j c_jk F_j) (solprop.backends.exponential), so no per-node weight volume is formed. Accepting a step re-anchors nothing. The compiled backends’ kernels are checked against these passes.

Parameters:
helperssolprop._helpers.Helpers, optional

The owning backend’s implementation of the Adams basis (SEP 29 section 6.6); the shared Numba one by default.

apply(moments, coefficients, values, dz)[source]#

Return dz sum_k M_k (sum_j c_jk X_j) over the given rows.

Parameters:
momentsnumpy.ndarray

M_k from moments().

coefficientsnumpy.ndarray

c_jk, shape (len(values), n_k) with n_k <= len(moments).

valueslist of numpy.ndarray

The samples X_j.

dzfloat

The step, m.

Returns:
numpy.ndarray

The increment.

carry(exponent, dz, rates, states, slots, spans)[source]#

Return each history sample’s compensation carried to the step’s end.

exp(s_j x) (D(z_j) - x / dz) u_j with s_j = 1 - theta_j, the difference of rates formed before the state multiplies it (SEP 16 4.2).

Parameters:
exponentnumpy.ndarray

x = Phi(z + h) - Phi(z).

dzfloat

The step, m.

rates, statesnumpy.ndarray

The history’s rates and states, by slot.

slotsnumpy.ndarray

The samples carried, newest first, int64.

spansnumpy.ndarray

1 - theta_j per sample: the flow’s length in steps.

Returns:
numpy.ndarray

One carried row per sample.

correct(state, flow, moments, source, store, slots, corrector, rows, dz, mask, divisor, extra=None)[source]#

Return the corrected field and the scaled norms of the defects.

With X = (source, store[slots[0]], ...), the field is exp(x) u_n + dz sum_k M_k sum_j corrector[j, k] X_j and each norm is the weak norm of dz sum_k M_k sum_j rows[r, j, k] X_j over the in-band bins; both are already at the end of the step. A varying operator’s compensation adds its own corrector term to the field and one term to each defect.

Parameters:
state, flow, momentsnumpy.ndarray

As for predict().

sourcenumpy.ndarray

The untransported source at the predicted field.

storenumpy.ndarray

The history rows.

slotsnumpy.ndarray

The rows after the source, newest first, int64.

correctornumpy.ndarray

The corrector’s coefficients over X, shape (width, n_k), zero past its own nodes.

rowsnumpy.ndarray

One defect’s coefficients over X per row, shape (n_rows, width, n_k).

dzfloat

The step, m.

masknumpy.ndarray

The in-band bins, Boolean.

divisornumpy.ndarray

The norm’s scale per bin.

extratuple or None, optional

The compensation’s corrector term and its term in each defect, or None for a constant operator.

Returns:
tuple

The corrected field and one norm per row.

coupled()[source]#

Return the passes this backend takes beside a coupled flow.

Returns:
CoupledExponentialKernels

The reference’s; a compiled backend returns its own.

defect_rows(nodes, orders, width)[source]#

Return the defects’ coefficients in the form correct() takes.

Parameters:
nodeslist of float

The history’s normalised positions, newest first.

orderslist of int

The orders scored, the step’s own first.

widthint

1 + max(orders).

Returns:
numpy.ndarray

The monomial rows of _exponential_rows, shape (len(orders), width, width).

flow(exponent)[source]#

Return the step’s exact linear flow exp(x).

Parameters:
exponentnumpy.ndarray

x = Phi(z + h) - Phi(z).

Returns:
numpy.ndarray

exp(x), the propagator the interaction formulation would apply.

moments(exponent, count)[source]#

Return k! phi_{k+1}(x) for k < count along a new leading axis.

Parameters:
exponentnumpy.ndarray

x = Phi(z + h) - Phi(z), in the canonical layout.

countint

The most interpolation nodes any formula of the trial has.

Returns:
numpy.ndarray

Shape (count, *x.shape).

predict(state, flow, moments, store, slots, coefficients, dz)[source]#

Return the predicted field exp(x) u_n + dz sum_k M_k sum_j c_jk F_j.

Parameters:
statenumpy.ndarray

The accepted state at the anchor.

flownumpy.ndarray

exp(x), the step’s exact linear flow.

momentsnumpy.ndarray

M_k from moments().

storenumpy.ndarray

The history rows.

slotsnumpy.ndarray

The rows the predictor combines, newest first, int64.

coefficientsnumpy.ndarray

The predictor’s Lagrange coefficients, shape (len(slots), len(slots)).

dzfloat

The step, m.

Returns:
numpy.ndarray

The predicted lab-frame field at the end of the step.

class solprop.backends.multistep.HostHistory(backend, method, kernels, exponential, compensation)[source]#

Bases: object

The Adams history and its field arithmetic, on host arrays (SEP 16 phase 3b).

AdamsMarch keeps the controller – the orders, the slots, rejection, restarts, the budget and the ledger – and hands every state-sized operation to an engine: the startup sample, a trial’s predictor, source, corrector and defect norms, the adoption and transport of an accepted step, and dense observation. This engine holds the history in a NumPy store and applies the given kernels (NumPy, or numba_cpu’s compiled passes); a backend may supply its own through solprop.backends.driver.StepDriver._adams_engine, as Kokkos does to keep the history resident in its engine. Between a trial and its acceptance the engine holds the trial; nothing else is carried across calls but the store and the last accepted step for dense observation.

Parameters:
backendsolprop.backends.driver.StepDriver

The backend whose state, source evaluation and propagators are used.

methodsolprop.stepper.multistep.Multistep

The method.

kernelsAdamsKernels

The interaction formulation’s fused arithmetic.

exponentialExponentialKernels or None

The exponential formulation’s arithmetic, None for the interaction one.

compensation{‘transported’, ‘untransported’}

A varying operator’s compensation treatment (SEP 16 Q8).

dense: Any#
exponential_accept(keep, free, *, interpolate)[source]#

Adopt the held exponential trial; the new sample takes row free.

Parameters:
keepnumpy.ndarray

The history rows kept, int64; nothing moves in this formulation.

freeint

The row the new sample takes.

interpolatebool

Whether the step’s interpolant is kept for dense observation.

exponential_trial(z, dz, nodes, slots, order, orders)[source]#

Form one exponential trial and return the scaled norms of its defects.

The arithmetic of fused_trial() with untransported samples: the predictor and the corrector integrate their interpolants exactly against the step’s flow exp(x), x = Phi(z + h) - Phi(z); the defects of the neighbouring orders are scored from the same moments. A varying operator adds the compensation of its samples (SEP 16 section 4.2). The trial is held for exponential_accept().

Parameters:
z, dzfloat

The anchor and the step, m.

nodeslist of float

The history’s normalised positions, newest first.

slotsnumpy.ndarray

Their store rows, int64.

orderint

The step’s order.

orderslist of int

The orders scored, the step’s own first.

Returns:
numpy.ndarray

One scaled norm per scored order.

fused_accept(keep, free, *, interpolate)[source]#

Adopt the held fused trial: the state, its interpolant, the new sample.

Parameters:
keepnumpy.ndarray

The history rows kept, int64; each is re-anchored at the step’s end.

freeint

The row the new sample takes.

interpolatebool

Whether the step’s interpolant is kept for dense observation.

fused_trial(z, dz, nodes, slots, order, orders)[source]#

Form one fused trial and return the scaled norms of its scored defects.

The predictor’s lab-frame field, the source there (one call, labelled a predictor), and in one pass the corrected field and each scored order’s defect: its k + 1-node corrector through the new source less its k-node predictor. The trial is held for fused_accept().

Parameters:
z, dzfloat

The anchor and the step, m.

nodeslist of float

The history’s normalised positions, newest first.

slotsnumpy.ndarray

Their store rows, int64.

orderint

The step’s order.

orderslist of int

The orders scored, the step’s own first.

Returns:
numpy.ndarray

One scaled norm per scored order.

hold()[source]#

Return the accepted state, to restore after a dense observation.

Returns:
object

The backend’s host copy of the accepted state.

load_rows(rows, shape)[source]#

Install rows snapshot_rows() returned, for a state of shape.

Parameters:
rowscollections.abc.Mapping

The rows, by name.

shapetuple of int

The state’s shape.

Raises:
BackendError

If a row is missing, does not have the store’s shape, or is one this history does not keep – the native engine’s applied frozen-residual generators ("applied") among them.

property n_slots: int[source]#

The history store’s rows: the method’s order and two more.

Returns:
int

order + 2.

observe(position)[source]#

Return the field at position inside the last accepted step.

Parameters:
positionfloat

The observation, m, inside the last accepted step.

Returns:
object

The masked lab-frame field at position, as the backend holds a state: a host array, or Kokkos’s native state loaded with it.

reset()[source]#

Forget the last accepted step and any trial in flight.

restore(z, held)[source]#

Make a held accepted state current again at z.

Parameters:
zfloat

Its position, m.

heldobject

What hold() returned.

snapshot_rows()[source]#

Return copies of the rows a resume needs to continue the history.

Returns:
dict

"store", the samples; for a compensated march also "states" and "rates", and "weights_<row>" for each row a frozen-residual flow weights.

start(z, *, stored)[source]#

Evaluate the source at the accepted state, the history’s first sample.

Parameters:
zfloat

The accepted position, m.

storedbool

Whether the sample goes to the store’s row zero (the fused and exponential paths) or is returned alone (the general path).

Returns:
numpy.ndarray

The sample: the transported source in the interaction formulation, the untransported one in the exponential formulation.

value(slot)[source]#

Return a store row, for the history entry that names it.

Parameters:
slotint

The row.

Returns:
numpy.ndarray

The row itself, not a copy.

solprop.backends.multistep.ORDER_HYSTERESIS = 1.2#

Factor by which a neighbouring order’s predicted step must win to be adopted.

solprop.backends.multistep.RESTART_AFTER = 3#

Consecutive rejections after which the history is restarted at order one.

solprop.backends.multistep.compensation_tables(nodes, order, orders, *, helpers)[source]#

Return a trial’s transported-compensation tables, for a resident engine.

The weights _Compensation applies (SEP 16 section 4.2, ADR-042), over the carried rows exp((1 - theta_j) x) (D(z_j) - x / h) u_j of the history samples and the endpoint term (D(z + h) - x / h) u_pred, each sum still to be multiplied by the step.

Parameters:
nodeslist of float

The history’s normalised positions, newest first.

orderint

The step’s order.

orderslist of int

The orders scored, the step’s own first.

helperssolprop._helpers.Helpers

The engine’s backend’s implementation of the Adams basis.

Returns:
tuple

The spans 1 - theta_j of the width - 1 carried samples; the predictor’s weights over the order newest of them; the corrector’s over (endpoint, newest, ...); and each scored order’s defect weights over the same, shape (len(orders), width), with width = 1 + max(orders).

solprop.backends.multistep.coupled_moments(exponent, count, block, dz, *, eigen=<function _eigen_functions>, diagonal=None)[source]#

Return a coupled trial’s flow and moments k! phi_{k+1}(h B), k < count.

The reference’s evaluation (SEP 16 section 4.5): on each bin of the block the functions of X = Delta Phi I + h T, upper triangular, are read off the first block row of the exponential of the augmented matrix [[X, I, 0, ...], [0, 0, I, ...], ..., [0, ..., 0]] – exp(X), phi_1(X), …, phi_count(X) – and carried back by Z; off the block the diagonal functions of ExponentialKernels. With the block’s eigenbasis (CoupledBlock.basis), the bins on its route instead take W f(Delta Phi + h lambda) Y, every bin in one pass: the route and its gate are the compiled flow actions’ (solprop.translate.coupled.eigenbasis_limit()), and the bins off it keep the augmented exponential.

Parameters:
exponentnumpy.ndarray

Delta Phi over the step, the same in every channel.

countint

The moments formed.

blockCoupledBlock

The step’s frozen block.

dzfloat

The step, m.

eigencallable, optional

Writes the eigenbasis route’s bins, (shift, dz, count, basis, matrices, flow_matrix): _eigen_functions, or a compiled backend’s kernel.

diagonalcallable or None, optional

(exponent, count) -> moments off the block; None for the reference’s ExponentialKernels.moments().

Returns:
_MatrixMoments

The flow and the moments.

solprop.backends.multistep.exponential_tables(nodes, order, orders, *, helpers)[source]#

Return an exponential trial’s coefficient tables, for a resident engine.

Parameters:
nodeslist of float

The history’s normalised positions, newest first.

orderint

The step’s order.

orderslist of int

The orders scored, the step’s own first.

helperssolprop._helpers.Helpers

The engine’s backend’s implementation of the Adams basis.

Returns:
tuple

The predictor’s Lagrange coefficients over the order newest rows, shape (order, order); the corrector’s over (source, newest, ...), shape (width, width) with width = 1 + max(orders); and the factored defect rows of factored_defect_rows().

solprop.backends.multistep.factored_defect_rows(nodes, orders, width)[source]#

Return each exponential defect factored: a divided difference and a polynomial.

Order k’s defect, its k + 1-node corrector through the new sample less its k-node predictor, vanishes at the predictor’s nodes, so it is f[1, theta_0, ..., theta_{k-1}] prod_{j<k} (theta - theta_j): one divided difference of the samples times one polynomial, whose integral against the flow is one combination of the moments. That is 2 (k + 1) products an element instead of (k + 1) n_k, the monomial rows of _exponential_rows.

Parameters:
nodeslist of float

The history’s normalised positions, newest first.

orderslist of int

The orders scored, the step’s own first.

widthint

1 + max(orders): the samples and the moments of the trial.

Returns:
numpy.ndarray

Shape (len(orders), 2 width): the divided-difference coefficients over (source, newest, ...), then the node polynomial’s ascending monomial coefficients.

solprop.backends.multistep.fused_tables(nodes, order, orders, *, helpers)[source]#

Return a fused trial’s coefficient tables, for an engine that applies them.

The interaction formulation’s tables of HostHistory.fused_trial(), which a resident engine (ADR-043) receives instead of forming them.

Parameters:
nodeslist of float

The history’s normalised positions, newest first.

orderint

The step’s order.

orderslist of int

The orders scored, the step’s own first.

helperssolprop._helpers.Helpers

The engine’s backend’s implementation of the Adams basis.

Returns:
tuple

The predictor’s integrated weights over the order newest rows; the corrector’s weights over (source, newest, ...), zero past its nodes; and one defect row per scored order over the same samples.

solprop.backends.multistep.source_absent(problem)[source]#

Return whether a problem’s complete source is structurally zero.

No response is compiled beyond the accumulation, and no in-stage absorber, no atomic response and no general or spatial pipeline adds a term: SEP 16 section 4.2 then requires the exact chord rather than an interpolated compensation, which need not cancel even at the endpoint. Decided from the problem’s structure, never from a sample that happens to vanish.

Parameters:
problemsolprop.translate.problem.KernelProblem

The problem.

Returns:
bool

Whether every source evaluation is identically zero.