solprop.backends.host.driver#

The reference’s step loop, with the kernels left abstract (SEP 47).

March, replay, landing, controller bookkeeping, history, the noise model and the seam’s set_initial/propagate_to/observe/close do not depend on how a stage is evaluated: they touch the state and HostDriver._trial_step and nothing else. Written once here, they are the step control of every NumPy reference backend.

The reference owns this loop since SEP 47 phase 2: HostDriver is a copy of the inherited driver the native bridge still runs on (solprop.backends.driver.StepDriver), so that the reference shares stateless numerical modules – the controller cores, tableaux, coefficient evaluation and stage tables – with that bridge, and transitionally the session lifecycle and the host engines (the exceptions solprop.backends.host records). The copies started identical; their decisions on identical inputs stay bit-identical (the decision traces of tests/backends). What a backend supplies is _trial_step, _apply_mask and stats, plus _finish when it has boundary treatments of its own; two optional hooks, _begin_plane and _on_accepted_step, are where a backend scores whatever a plane or an accepted step leaves behind. The march owns the periodic statistics schedule. Its default storage hooks call stats and retain host rows.

Module Attributes

INITIAL_STEP_STATS_CAPACITY

Rows a plane's statistics buffer holds before it first has to grow.

Functions

reached(position, target)

Return whether a march at position has arrived at target.

replay_tolerance(schedule, position)

Return how far a replayed schedule's edge may sit from a target it lands on, m.

Classes

HostDriver(problem, request, *[, grid, ...])

The interaction-picture step loop, with the stage evaluation left to a subclass.

class solprop.backends.host.driver.HostDriver(problem, request, *, grid=None, modes=None, profiles=None, options=None)[source]#

Bases: ABC, Generic

The interaction-picture step loop, with the stage evaluation left to a subclass.

A subclass supplies _trial_step and _apply_mask and gets the whole of solprop.backends.protocol.Backend’s marching behaviour: adaptive stepping with the controller of docs/dev/stepper.md section 3, replay of a contract’s fixed schedule, landing exactly on save planes and hard kinks, the step history, and both noise models, input and modified.

The controller’s state is its own proposal, never the clipped or stretched step that was executed, and what the history and a rejection see is the norm the proposal would have scored while what decides acceptance is the error the executed step committed. Both are section 3’s, and both matter: the first costs a run hundreds of steps if it is got wrong, the second throws away short landing steps that were accurate.

Parameters:
problemsolprop.translate.problem.PropagationProblem

The validated problem to step. The subclass refuses what it cannot execute before calling this constructor.

requestsolprop.translate.run_request.RunRequest

The run’s policies: the method, the error control, the itinerary, the planes and the statistics (SEP 47 section 5.2). The subclass checks it with the problem (solprop.translate.problem.validate_run()) before calling this constructor; a session started on this backend serves a request that differs from it only in one run’s start, retention, monitor and history (for_run()).

gridsolprop.grid.TimeGrid, optional

The grid the state lives on, which the statistics need and the contract does not carry.

modescollections.abc.Sequence, optional

The propagated modes, in state order, for the statistics that need them.

profilescollections.abc.Mapping, optional

The z-profiles keyed by statistic name.

optionssolprop.backends.session.ExecutionOptions, optional

The run’s execution options: an explicit node budget or an exact node chunk, from which the node chunk is planned here, before anything is allocated (solprop.backends.node_chunk.plan_node_chunk()). None for the automatic choice.

Attributes:
namestr

The registry name, set by the subclass and used in every log line.

max_batchint

How many problems the backend steps at once.

propagator_default{‘tabulate’, ‘recompute’}

What this backend resolves the contract’s "auto" propagator mode to. The default suits a backend that evaluates two complex exponentials per stage; one that takes both signs from a single exp and sincos overrides it.

helperssolprop._helpers.Helpers

The implementation of the per-stage and per-step scalar evaluations – the stage tables’ Clenshaw rows and the Adams basis – which the driver, march and engines pass down rather than choosing one (SEP 29 section 6.6): PLAIN, the uncompiled forms, for the NumPy reference.

observes_fresh_fieldbool

Whether _observed_field makes a new array at every call and keeps no reference to it, so that observe() can say its field is owned (solprop.backends.protocol.Observation.field_owned). False by default: the array-backed state is observed as it is, frozen, and left behind by the next step.

attach_monitor(monitor)[source]#

Watch the time window’s edges from here on, starting with the current state.

The launch is recorded now; every committed step is then accounted at the same point as the periodic statistics rows (_take_step_stats), on every march, after the state is finished and the position advanced.

Parameters:
monitorsolprop.monitor.BoundaryMonitor

A monitor with nothing recorded.

Raises:
BackendError

If the monitor’s weights do not match the grid, or its columns the state.

available_device_memory()[source]#

Return None for host execution; device backends report free bytes.

Returns:
int or None

None for the shared host implementation.

property chunk_plan: NodeChunkPlan[source]#

Return the node-chunk plan and what chose it.

Returns:
solprop.backends.node_chunk.NodeChunkPlan

The plan made at construction.

clear_halt()[source]#

Withdraw a halt requested with request_halt(), once it is serviced.

property closed: bool[source]#

Return whether the backend has released its buffers, as a failure may.

Returns:
bool

True when it can run no other input.

drain_attempts()[source]#

Drain the attempts recorded since the last observation, with no field.

For a state left in flight, which observe() refuses: the attempts and periodic rows recorded before the abandoned step, drained once, as an observation drains them.

Returns:
solprop.backends.protocol.Observation

The attempts and rows at position, with no field.

edge_memory(space)[source]#

Return what reading the edge holds in the state’s memory space.

What solprop.driver.estimate.estimate() adds: it attaches no monitor.

Parameters:
spaceobject

The run’s transverse grid.

Returns:
int

Zero by default: a read on the host holds nothing beside the state there.

halt_ahead()[source]#

Halt the next march at the endpoint a dense observation held, if short.

The execution session’s snapshot latch beside interpolated planes (SEP 47 section 5.5): the march stands ahead of the plane it observed, at the end of the step that crossed it. When the next march restores that endpoint and the latch is set, it returns there at once, unless a step plane is owed there first or the target is another plane the step crossed, so that the snapshot is taken at that endpoint rather than wait for a step that crosses no plane.

property halted: bool[source]#

Return whether the last march stopped at a requested halt short of its end.

Returns:
bool

True while the halt latch of request_halt() or the yield check of set_halt_check() stopped it.

helpers: Helpers = Helpers(name='plain')#
property holding_plane: bool[source]#

Return whether a dense observation holds the state between endpoints.

Returns:
bool

True while this driver’s own (a Runge-Kutta march’s) or its Adams march’s observation holds an interpolated plane, the march ahead of it.

property in_flight: tuple[float, float] | None[source]#

Return the low-storage step a failure abandoned in flight, or None.

Returns:
tuple of float or None

The step’s start and length, m, while the state is neither of its ends (solprop.backends.low_storage).

march_export_refusal()[source]#

Return why this backend’s Adams march cannot carry its history, or None.

Returns:
str or None

The reason (solprop.backends.host.adams.HostAdamsMarch.export_refusal()), or None for a march that exports and installs its history and for the Runge-Kutta and low-storage marches, which keep none.

march_snapshot()[source]#

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

Returns:
solprop.output.schema.MarchSnapshot or None

The snapshot, or None for the Runge-Kutta and low-storage marches and wherever the Adams march has nothing a resume could continue (solprop.backends.host.adams.HostAdamsMarch.snapshot()).

Raises:
BackendError

If a low-storage step was abandoned in flight.

march_snapshot_nbytes(z)[source]#

Return the bytes march_snapshot() would copy at z, copying nothing.

Parameters:
zfloat

The position the snapshot is wanted at, m.

Returns:
int or None

The state and every engine row, or None where a snapshot would be None or stand somewhere other than z.

Raises:
BackendError

If a low-storage step was abandoned in flight.

max_batch: int = 1#
metadata()[source]#

Return what this execution was, for the run’s record.

A saved run has to say what produced it: the thread configuration alone was measured to move one step by a factor of nine, so a result whose execution is not recorded is not reproducible. The base record is the name and the resolved propagator mode and, for Adams runs, cumulative logical source calls; the low-storage march’s evaluations are physical, source_evaluations, never response_calls (SEP 24 section 5); a backend with pools, plans or wisdom adds them.

Returns:
dict

JSON-serialisable scalars, keyed by name.

name: str = 'driver'#
property node_chunk: int[source]#

Return the nodes a response pass of this backend holds at once.

Returns:
int

The planned chunk (chunk_plan).

observe(*, field=True)[source]#

Return the state at the current position, as host arrays.

The field is in the lab frame and the FFT convention (spectral_convention = "fft"): the output layer conjugates it on the way to disk and the processing accessors on the way to a user. The step history covers everything since the previous call.

With field=False (SEP 22 section 5.1) neither the field nor the noise phase crosses to the host – a device backend copies nothing – and the observation carries None for both. Nothing scans the field for finiteness then: the step loop accepts only finite error norms and a replay raises on a non-finite one. The spatial collar diagnostic, which reads the field, is not evaluated; the backend counts such planes in metadata() and logs the first.

Parameters:
fieldbool, optional

Whether the field and the noise phase cross to the host. Default True.

Returns:
Observation

The field, the noise phase and the step history.

Raises:
BackendError

If the backend is closed or no initial state was loaded.

observes_fresh_field: ClassVar[bool] = False#
poll_halt()[source]#

Latch a halt when the yield check says one is due.

property position: float[source]#

Return where the march stands, m.

Returns:
float

The committed state’s position, or the end of a step a group took but neither validated nor recorded, which leaves the state in flight (in_flight).

prepare(problem, request)[source]#

Prepare a run of this backend for one problem and request (SEP 47 5.2).

The backend lowered the method, the error control, the itinerary, the planes and the statistics where it was built; preparing binds the problem and the request a run serves – this backend’s own, or one run’s adaptation of it (for_run()) – and each start of the result opens the reference’s own session (HostSession).

Parameters:
problemsolprop.translate.problem.PropagationProblem

The translated problem: this backend’s, or one derived from it with another input (solprop.driver.simulation.check_derived_problem()).

requestsolprop.translate.run_request.RunRequest

The run’s request.

Returns:
solprop.backends.session.PreparedExecution

The prepared execution.

prepare_group(members, problems, requests)[source]#

Prepare a group of compatible members of this backend (SEP 47 section 6.7).

The reference’s own group (HostGroupSession): its members advance in lockstep through one fixed schedule on the reference’s group runner (HostGroup or NumpyBatchBackend, chosen by solprop.backends.registry.select_batch()).

Parameters:
memberssequence of solprop.backends.protocol.Backend

One constructed backend per member, this one first.

problemssequence of solprop.translate.problem.PropagationProblem

Each member’s independently translated problem.

requestssequence of solprop.translate.run_request.RunRequest

Each member’s run request.

Returns:
solprop.backends.session.PreparedGroup

The prepared group.

propagate_to(z)[source]#

Step the state to a position, landing on it exactly.

Adaptive by default and a replay when the contract carries a schedule. Either way the embedded estimate is computed and recorded every step, so a schedule too coarse for the tolerance it is scaled by is reported rather than silent (docs/dev/stepper.md section 8). The low-storage march has no estimate: it replays its schedule and records 0.0, “not estimated” (SEP 24 section 6.3).

Parameters:
zfloat

The save plane to reach, m, at or ahead of the current position and inside the operator table.

Returns:
StepSummary

What the call cost and achieved.

Raises:
BackendError

If the backend is closed, no initial state was loaded, a low-storage step was abandoned in flight, or z is behind the current position or outside the table.

ConvergenceError

If the controller asks for a step below the contract’s dz_min, or a replayed step’s error norm, or the low-storage march’s state, is not finite.

propagator_default: Literal['auto', 'tabulate', 'recompute'] = 'tabulate'#
reads_transverse_edge: bool = False#

Whether this backend reads a free-space run’s transverse edge on the host (SEP 40 section 5.3). A backend that does not leaves the run unwatched, and says so before the first step.

property request: RunRequest[source]#

Return the request this backend was built for.

Returns:
solprop.translate.run_request.RunRequest

Its method, error control, itinerary, planes and statistics; a run adapts it with for_run().

request_halt()[source]#

Stop the march at the next accepted endpoint short of its target.

The execution session’s snapshot latch (SEP 47 section 5.5): propagate_to returns at the end of the next accepted step, as at a natural stop, without counting it as one, so the session can service a snapshot there. An endpoint on the target itself is observed as the plane, and the latch stays set for the session to find.

restore_call_ledgers(logical, atomic=None)[source]#

Restore shared Adams call counts without restoring numerical history.

Non-TDSE backends inherit logical budget persistence while refusing atomic counts. The caller first loads the saved field, which resets Adams history; the next source evaluation charges startup against these cumulative counts. A Runge-Kutta or low-storage march keeps no logical ledger, so nothing is installed for it.

Parameters:
logicalmapping

Persisted logical response counts; empty on ordinary RK runs.

atomicmapping or None, optional

Persisted atomic counts; absent on non-TDSE runs.

Raises:
BackendError

If this backend is handed counters it does not implement.

restore_march(snapshot)[source]#

Continue the Adams march from a snapshot at the loaded position.

Called after set_initial() at the snapshot’s position. The state is replaced by the snapshot’s own, exactly as the interrupted run held it.

Parameters:
snapshotsolprop.output.schema.MarchSnapshot

The snapshot a file holds.

Returns:
str or None

None when the history was continued, otherwise why the march starts afresh, as a resume did before snapshots were kept.

set_halt_check(check)[source]#

Ask check at every accepted endpoint whether to halt there.

The execution session’s bounded yield (SEP 47 section 5.1): when check returns True the halt latch of request_halt() is set, so the march returns at that endpoint. It is asked only at safe boundaries and changes nothing the march computes.

Parameters:
checkcallable or None

Returns whether a yield is due; None asks nothing.

set_initial(field, noise, *, z=0.0, resume=False)[source]#

Load a state and the noise realisation at a position.

The state is masked to the active band once here, as the scaffold does: the operator and the prefactor are exactly zero outside it, so out-of-band content would neither propagate nor decay and would sit in the error norm for the whole run. The traditional input noise model adds its realisation to the state at the run’s launch position and never again; the modified model keeps it apart and adds it at every stage, so the propagating field never carries it.

A resume loads a state from a plane the run already saved, so it already carries whatever the input model added at the launch and must not be given a second realisation, and a replayed schedule continues from the edge that plane sits on. A z past the launch is always one. Position cannot tell for a file holding only its launch plane, which comes back at the launch itself: its caller says so with resume. The launch is the entrance unless the run starts inside the guide (SEP 37: an interior launch with input noise takes its draw there). The controller starts afresh from dz_init either way (docs/dev/stepper.md section 3).

Everything else a march reads is restored too, so that a backend loaded again after a run – a SimulationRunner’s, or after a failed run – repeats a fresh backend’s run (SEP 22 section 4.7): a dense observation’s look-ahead, an Adams march’s call ledger and last-rejection flag, a low-storage march’s source-evaluation count and the boundary monitor, with the collar it may have rested, here, and the quadrature alarm, whose value the entrance plane’s quadrature_error reads before any march, in the overrides of the backends that keep one. A resume restores its ledger afterwards (restore_call_ledgers()).

Loading is the one entry that clears a low-storage step abandoned in flight (SEP 24 section 6.2): the state it holds is replaced, and the march’s flow table is rebuilt from the next step.

Parameters:
fieldnumpy.ndarray

The state in the canonical layout, complex, shape (n_modes, n_pol, n_omega), in the square root of watts and the FFT convention.

noisesolprop.noise.NoiseRealisation or None

The realisation to carry along the run, or None to take the one the problem was assembled with.

zfloat, optional

The position the state belongs to, m. Zero, the entrance, by default.

resumebool, optional

Whether field came back from a saved plane of this run, and so never takes the input draw, whatever z. False by default.

Raises:
BackendError

If the backend is closed, the field does not match the contract’s shape, z lies outside the operator table, or a schedule is being replayed and z is not one of its edges.

set_progress(reporter)[source]#

Attach the reporter every attempted step is reported to.

The next call to propagate_to() announces the run to it. Attaching a reporter changes nothing the propagation computes – the calls are observers, made after the step is decided – so a run with one is bitwise the run without.

Parameters:
reportersolprop.log.ProgressReporter

Where the step events go; solprop.log.NullReporter turns them off.

set_stop(flag)[source]#

Attach the flag the march polls, or detach it with None.

A run polls its own flag before each trial (SEP 41, S17), where the state is the last accepted one, so a stop abandons nothing but the trial not yet taken. Polling changes nothing the propagation computes.

Parameters:
flagsolprop.log.StopFlag or None

Any object with an is_set() method, such as a threading.Event.

abstractmethod stats(spec)[source]#

Compute the requested statistics in one fused pass.

Declared here, not only on solprop.backends.protocol.Backend, because the shared step loop calls it through the default row-storage hook. A device backend shares its statistic evaluator between this method and its device row-storage hook.

Parameters:
specsolprop.stats.StatsSpec

Which statistics and which windows.

Returns:
dict

Small host arrays keyed by statistic name, without a batch axis.

timing_probe(start)[source]#

Load a start and return single trials of this run’s march from it.

The start’s field, noise, position and resume flag are loaded as by set_initial(); its method history, ledgers and monitor continuation are not read. A Runge-Kutta trial leaves the loaded state alone; a low-storage march’s step advances the discarded registers in place, so a backend probed so is closed afterwards (SEP 47 section 6.6).

Parameters:
startsolprop.backends.session.Start

Where the trials start; its field is taken.

Returns:
solprop.backends.protocol.TimingProbe

The probe.

solprop.backends.host.driver.INITIAL_STEP_STATS_CAPACITY = 64#

Rows a plane’s statistics buffer holds before it first has to grow.

solprop.backends.host.driver.reached(position, target)[source]#

Return whether a march at position has arrived at target.

Parameters:
positionfloat

Current position, m.

targetfloat

Position being marched to, m.

Returns:
bool

True when no further step is due.

Examples

>>> from solprop.backends.host.driver import reached
>>> reached(0.5, 1.0), reached(1.0, 1.0)
(False, True)
solprop.backends.host.driver.replay_tolerance(schedule, position)[source]#

Return how far a replayed schedule’s edge may sit from a target it lands on, m.

A schedule’s edges are the running sum of its steps, and a sum of n rounded steps can drift from the position they were chosen to reach by about n ulps: FixedSchedule(np.full(160, L / 160)) ends 12 ulps short of L. The slack is therefore LANDING_ULPS or the step count, whichever is larger, in ulps of the larger of the position and the schedule’s length – still far inside the 1e-9 of the length that validate() allows an edge to miss a save plane by, so no step is ever skipped.

Parameters:
schedulesolprop.stepper.schedule.FixedSchedule

The schedule being replayed.

positionfloat

The position the edge is compared with, m.

Returns:
float

The slack, m.

Examples

>>> import numpy as np
>>> from solprop.backends.host.driver import replay_tolerance
>>> from solprop.stepper.schedule import FixedSchedule
>>> schedule = FixedSchedule(np.full(160, 1e-3 / 160))
>>> bool(1e-3 - schedule.z_end <= replay_tolerance(schedule, 1e-3))
True