solprop.backends.numpy_ref.single#

The single-precision path of the reference backend: SEP 33’s oracle.

NumpySingleBackend is NumpyRefBackend with the propagated state, the stage values, every transform and the responses held in IEEE binary32, under the contract of docs/dev/seps/archive/sep-0033-single-precision-mlx.md section 4.2. It is the target the mlx backend is compared against, so it keeps the reference’s one-function-per-pass shape and states every order it sums in.

What is binary32 and what is not:

  • The state is held as a / s, s the launch’s exact power-of-two scale (solprop.translate.precision.state_scale()). observe and the statistics read s times the state, widened to binary64, which is exact.

  • The tables a source evaluation multiplies by are narrowed once at construction (_upload). Each has its block exponent (solprop.translate.precision.block_exponent()); where narrowing the table directly keeps every entry the block-exponent form keeps (the table’s largest magnitude at least one, or no entry below binary32’s smallest normal), the exponent is applied at upload, exactly, and otherwise the table is refused unless its pass carries the exponent itself, as the Raman kernel’s does.

  • The propagator exp(dPhi) is formed from the binary64 exponent in binary64 and rounded once (section 4.1, item 3); every other z-dependent array a pass multiplies by – the prefactor, a z-table’s value, the spatial collar’s rate – is evaluated on the host in binary64 and rounded once.

  • The responses run in binary32 on the scaled field. Each takes its homogeneity factor (section 4.3, rule 4) in one binary64 coefficient combined on the host (solprop.translate.precision.combine()): s**2 for the cubic terms; the plasma is evaluated at the physical field s E, an exact multiplication, and its current divided by s. The field-grid plasma uses the stable population forms of section 4.4 and blocked running integrals (solprop.translate.precision.prefix_sum()).

  • Host evaluations of the oracle. The ionisation rate’s table lookup, the envelope-grid plasma’s cycle moments and populations, and the statistics are evaluated in binary64 from the binary32 field and rounded (or, for the statistics, returned) once: the oracle defines the reference these comparisons are made against.

  • The controller reads a norm reduced as a scaled sum of squares (solprop.translate.precision.scaled_square_sum()), against an absolute tolerance divided by s.

Functions

analytic_single(values)

Return the analytic signal of a real binary32 array, in binary32.

kerr_single(field_t, kerr, factor, carrier)

Evaluate the Kerr polarisation of passes.kerr_polarisation() in binary32.

plasma_current_single(field, channels, ...)

Return the field-grid plasma's ungated current density in binary32.

raman_single(field_t, spectrum, factor, *, ...)

Evaluate the delayed Raman polarisation in binary32.

running_integral(values, dt, rule)

Return a running integral in binary32, in the oracle's blocked order.

single_sources(field_t, responses, t_window, ...)

Evaluate the responses of passes.nonlinear_sources() in binary32.

stable_population(exposure, preionisation)

Return the ionised fraction and the neutral survival in their stable forms.

Classes

NumpySingleBackend(problem, *[, grid, ...])

Propagate a single-precision problem: the reference backend in binary32.

class solprop.backends.numpy_ref.single.NumpySingleBackend(problem, *, grid=None, modes=None, profiles=None, tdse_binding=None)[source]#

Bases: NumpyRefBackend

Propagate a single-precision problem: the reference backend in binary32.

Constructed by the registry for backend="numpy_ref" when the problem’s precision is single. See the module docstring for what is held in which precision.

Parameters:
problemsolprop.translate.problem.KernelProblem

A validated problem with precision.single.

gridsolprop.grid.TimeGrid, optional

The grid the state lives on, needed only by stats().

modescollections.abc.Sequence, optional

The propagated modes, needed only by the zdw statistic.

profilescollections.abc.Mapping, optional

The z-profiles keyed by statistic name.

tdse_bindingsolprop.translate.tdse.TDSEBinding or None, optional

Must be None: TDSE responses are refused under single precision.

Raises:
BackendError

If the problem is not single precision, or a table cannot be narrowed without losing range that its pass does not carry.

block_exponents: dict[str, int]#
intermediate_ranges: dict[str, tuple[float, float]] | None#
property scale: float[source]#

Return the state’s exact power-of-two scale s.

Returns:
float

The state is held as a / s.

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

Load a state, holding it at the launch’s scale in binary32.

Parameters:
fieldnumpy.ndarray

The state in the canonical layout, physical units.

noiseobject

Must be None; noise models are refused under single precision.

zfloat, optional

The position the state belongs to, m.

stats(spec)[source]#

Collect statistics from the physical state widened to binary64.

The oracle’s statistics are binary64 functions of the exactly widened binary32 state, so they carry the state’s rounding and none of their own.

Parameters:
specsolprop.stats.StatsSpec

The statistics to collect.

Returns:
dict

As solprop.backends.numpy_ref.backend.NumpyRefBackend.stats().

solprop.backends.numpy_ref.single.analytic_single(values)[source]#

Return the analytic signal of a real binary32 array, in binary32.

The same construction as solprop.fourier.analytic_signal() (doubling the positive frequencies, keeping zero and Nyquist), with the multiplier held in the values’ precision.

Parameters:
valuesnumpy.ndarray

Real values along the last axis.

Returns:
numpy.ndarray

The complex64 analytic signal.

solprop.backends.numpy_ref.single.kerr_single(field_t, kerr, factor, carrier)[source]#

Evaluate the Kerr polarisation of passes.kerr_polarisation() in binary32.

Parameters:
field_tnumpy.ndarray

The scaled field, binary32.

kerrsolprop.translate.responses.KerrCoefficients

The compiled coefficients.

factorfloat

coefficient * density * s**2, combined on the host and rounded once.

carriernumpy.ndarray or None

The narrowed harmonic carrier, when the envelope grid radiates the harmonic.

Returns:
numpy.ndarray

The polarisation divided by s, binary32.

solprop.backends.numpy_ref.single.plasma_current_single(field, channels, densities, preionisation, dt, rule)[source]#

Return the field-grid plasma’s ungated current density in binary32.

The current of passes.nonlinear_sources() (one channel) and of its channel sum (several), with the stable populations of stable_population(): the free current integrates sum_c rho_c f_c (e^2/m) n_c E and the loss current is sum_c rho_c I_c W_c sigma_c E / |E|^2, its division masked where the rate or the field is zero. The rate’s table lookup is evaluated in binary64 from the binary32 magnitude and rounded once.

Parameters:
fieldnumpy.ndarray

The physical field s E, V/m, binary32.

channelstuple of solprop.translate.responses.PlasmaChannel

The compiled channels.

densitiessequence of float

Each channel’s density at the stage, m^-3.

preionisationsequence of float

Each channel’s pre-ionised fraction at the stage.

dtfloat

The sample spacing, s.

rulesolprop.nonlinear.ionisation.RunningIntegral

The running-integral rule.

Returns:
numpy.ndarray

The current density, A/m^2, binary32.

solprop.backends.numpy_ref.single.raman_single(field_t, spectrum, factor, *, kind, thg)[source]#

Evaluate the delayed Raman polarisation in binary32.

The convolution of passes.raman_polarisation() with the narrowed kernel; the coefficient carries eps0, the density, s**2 and the kernel’s exponent, and is applied once to the product with the field.

Parameters:
field_tnumpy.ndarray

The scaled field, binary32.

spectrumnumpy.ndarray

The narrowed kernel spectrum, (n_to + 1,) scalar or (1 | 2, n_to + 1) tensor.

factorfloat

The combined coefficient, rounded once.

kind{‘field’, ‘envelope’}

The spectral layout.

thgbool

Whether the drive keeps the harmonic.

Returns:
numpy.ndarray

The polarisation divided by s.

Raises:
BackendError

If the field has neither one nor two components.

solprop.backends.numpy_ref.single.running_integral(values, dt, rule)[source]#

Return a running integral in binary32, in the oracle’s blocked order.

The rule of solprop.nonlinear.ionisation.RunningIntegral.integrate(): the band rule’s filter (its taps rounded once, the pairs added in the same order), then the cumulative trapezoid, whose increments are summed by solprop.translate.precision.prefix_sum(). Each prefix is a sum of depth at most ceil(log2 n), where a sequential binary32 cumulative sum of n terms would carry an error growing with n.

Parameters:
valuesnumpy.ndarray

The integrand along the last axis, binary32.

dtfloat

The sample spacing, s.

rulesolprop.nonlinear.ionisation.RunningIntegral

The problem’s rule.

Returns:
numpy.ndarray

The running integral, zero at the first sample, binary32.

solprop.backends.numpy_ref.single.single_sources(field_t, responses, t_window, *, kind, scale_exponent, dt, scalars, carrier=None, raman_spectrum=None, raman_exponent=0)[source]#

Evaluate the responses of passes.nonlinear_sources() in binary32.

The field is the scaled one, E / s with s = 2**k. The result is in the scaled state’s units: the polarisation and the current divided by s. Each response takes its homogeneity factor (SEP 33 section 4.3, rule 4) in one binary64 coefficient: the cubic terms s**2 with the density, the Raman term also the kernel’s block exponent; the plasma is evaluated at the physical field s E (exact) and its current divided by s (exact).

Parameters:
field_tnumpy.ndarray

The scaled field at the chunk’s nodes, binary32.

responsessolprop.translate.responses.CompiledResponses

The compiled responses.

t_windownumpy.ndarray

The narrowed temporal collar profile.

kind{‘field’, ‘envelope’}

The spectral layout.

scale_exponentint

k with s = 2**k.

dtfloat

The fine time grid’s spacing, s.

scalarssolprop.translate.stage.StageScalars

The stage’s host scalars: densities and preionisation.

carriernumpy.ndarray or None, optional

The narrowed harmonic carrier of an envelope grid.

raman_spectrumnumpy.ndarray or None, optional

The narrowed Raman kernel spectrum.

raman_exponentint, optional

Its block exponent.

Returns:
solprop.backends.numpy_ref.passes.NonlinearSources

The windowed polarisation and the ungated current, in the scaled units.

Raises:
BackendError

For a response the single-precision path does not run.

solprop.backends.numpy_ref.single.stable_population(exposure, preionisation)[source]#

Return the ionised fraction and the neutral survival in their stable forms.

SEP 33 section 4.4: f = p + (1 - p) (-expm1(-X)) keeps relative accuracy for a small exposure X, and sigma = (1 - p) exp(-X) for a large one, where f rounds to one. The loss current and the ionisation work are formed from sigma, never from 1 - f.

Parameters:
exposurenumpy.ndarray

X, the running integral of the rate, non-negative, binary32.

preionisationfloat

p in [0, 1].

Returns:
fractionnumpy.ndarray

f, binary32.

survivalnumpy.ndarray

sigma, binary32.

Examples

>>> import numpy as np
>>> from solprop.backends.numpy_ref.single import stable_population
>>> f, sigma = stable_population(np.array([1e-8, 20.0], np.float32), 0.0)
>>> bool(f[0] > 0), bool(sigma[1] > 0)
(True, True)