solprop.translate.stage#
The host scalars of one stage, and the device form they are the other half of.
A stage of the interaction-picture step needs three z-dependent quantities that are
arrays – the propagator’s exponent dPhi, the response prefactor and the noise’s
unitary phase – and a handful that are scalars: the number density, the
pre-ionisation, the mode set’s taper, the Raman decay rates and, since schema 13, the
frozen section and the residual weights of a coupled flow, and since schema 14
the weights of a moving guide’s maps. Until this module the
backends fetched all seven for themselves, each through the function that hides the
representation (solprop.translate.linop.delta_phi(),
solprop.translate.linop.unitary_phase(),
solprop.translate.responses.prefactor_at()) or straight off a
PiecewiseChebyshev. That works on a host and
cannot work on a device: a Clenshaw pass over a Python object is not something a
compiled step loop can call, and the z table it walks is an array of the state’s size
times the degree – 1.55 GB at the comb-to-continuum size.
The seam’s evaluation form is therefore split in two:
terms, the fixed arrays, uploaded once per run (
terms()). A constant operator is one term; an expansion isn_termsof them.scalars, a small frozen record of host floats per stage (
scalars()). The weights that multiply the terms, and every scalar path value the responses read.
A backend evaluates dPhi as one linear combination of the terms it holds over the
bundle’s floats (combine()) and knows nothing else about the representation. The
z table has no such split: solprop.translate.problem.KernelProblem.device_ready
is the predicate, the host backends evaluate the table, the native backend’s CPU build
evaluates it natively into term slots it then weighs by +-1 (SEP 29 section 6.6), and
require_device_ready() is the refusal an accelerator build raises.
The split costs nothing in accuracy because it is the arithmetic that was already
there: delta() is
tensordot(weights(z2) - weights(z1), terms), and the constant path is
D (z2 - z1), so a backend that goes through this module reproduces
delta_phi() to the last bit.
Functions
|
Return the weights a step's stages all subtract, evaluated once. |
|
Return the weighted sum of the terms, the one arithmetic a backend does. |
|
Refuse a problem whose z quantities an accelerator would hold as a z table. |
|
Return the host scalars of one stage, from the step's anchor to the node. |
|
Return the host scalars of several stages of one step, each table read once. |
|
Return the fixed arrays of a problem's two array-valued z quantities. |
Classes
|
Everything about one stage that is a host scalar. |
|
The fixed arrays a backend holds for the whole run. |
|
The weights of |
- class solprop.translate.stage.StageScalars(z0, z1, phi_delta, phi_at, prefactor, density, taper_scale, raman_rates, kerr_density, plasma_densities, plasma_preionisation, raman_densities, flow_section=-1, flow_weights=None, transform_weights=None, dispersive_kerr_densities=())[source]#
Bases:
objectEverything about one stage that is a host scalar.
One stage runs from the step’s anchor z0 to the node position z1; the weights for
Phiare the difference between the two, becausePhiis cumulative and the step needs its increment, while everything else is a value at z1.- Attributes:
- z0float
The step’s anchor, m.
- z1float
The stage’s position, m,
z0 + c_i h.- phi_deltanumpy.ndarray or None
Weights of
Phi(z1) - Phi(z0), float64, shape(n_phi_terms,);NonewhenPhiis a z table.- phi_atnumpy.ndarray or None
Weights of
Phi(z1)itself, float64, shape(n_phi_terms,), which the noise’s unitary phase needs;Noneon the same condition.- prefactornumpy.ndarray or None
Weights of the prefactor at z1, float64, shape
(n_pre_terms,);Nonewhen the prefactor is a z table.- densityfloat
Number density at z1, m^-3, or dimensionless 1 for an absolute
chi3.- taper_scalefloat
The mode set’s scalar rescaling at z1, dimensionless; 1.0 with no taper.
- raman_ratestuple of float
The decay rate of each Raman term at z1, s^-1, in the order of undamped. Empty with no Raman response. Hashable, because it is what a backend memoises its transformed kernel spectrum on.
- kerr_densityfloat
The Kerr leaf’s own scalar scale at z1. With one component it is that component’s number density in m^-3 (bitwise the density above) and the leaf’s coefficient carries
eps0 * gamma3; with several it is the compositesum(rho_s * gamma3_s)in m^2/V^2 and the coefficient iseps0alone, so the product is the same quantity in both cases and only the split differs (the single-gas bits are kept by construction).- plasma_densitiestuple of float
Channel number densities at z1 in m^-3, in compiled channel order.
- plasma_preionisationtuple of float
Channel initial ionised fractions at z1, dimensionless in [0, 1].
- raman_densitiestuple of float
Raman component amplitude densities at z1 in m^-3. Multiple components add these amplitudes to the transformed-kernel cache key beside raman_rates.
- flow_sectionint
Which section of
linear.coupled_flowthe step’s anchor z0 lies in, so that every stage of one step names the same frozen block;-1for a problem carrying no coupled flow. Schema 13.- flow_weightsnumpy.ndarray or None
Weights of the coupled flow’s
(n_intervals, n_deg)coefficient block at z1, float64, shape(n_intervals * n_deg,), flattened in that order;Nonewithout a coupled flow. Zero outside the section the stage is evaluated on, which is the step’s own whenever z1 lies in it – a stage landing exactly on a section edge reads the closing side of that kink and not the opening one – and z1’s own otherwise, so no expansion is ever extrapolated. The generator and the connection share the axes and are combined over the same weights (solprop.translate.coupled.flow_weights()). Schema 13.- transform_weightsnumpy.ndarray or None
Weights of
transform.modal_tables’ moving maps – the(n_intervals, n_deg)coefficient axes ofsynthesisandprojection, flattened in that order – at z1, float64, shape(n_intervals * n_deg,): a backend evaluatesS(z1)andQ(z1)as that combination, zero outside the section z1 lies in (solprop.translate.modal_path.transform_weights()).Nonefor a problem whose maps do not move – no general modal tables, or maps of one coefficient – so such a problem’s bundle carries nothing new. The transform’s sections are its own and need not be the coupled flow’s. Schema 14.- dispersive_kerr_densitiestuple of float
Each channel of
responses.dispersive_kerrits number density at z1, in m^-3, in the leaf’s channel order; empty without the leaf. Schema 24.
Examples
>>> import numpy as np >>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import problem_from >>> from solprop.pulses import SechPulse >>> from solprop.stepper import PIController >>> from solprop.translate.problem import assemble >>> from solprop.translate.stage import scalars >>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (500e-9, 2e-6), 4e-12) >>> gnlse = problem_from([0.0, 0.0, -1.2e-26], omega_ref=grid.omega0, gamma=0.11, ... shock=False) >>> start = SechPulse(wavelength=835e-9, width=1e-13, peak_power=1.0).build(grid) >>> problem = assemble(grid, gnlse.mode, [gnlse.kerr], start, ... controller=PIController(rtol=1e-8, atol=1e-14), ... save_planes=[1.0], dz_max=0.1, dz_init=1e-3, ... shock=False, prefactor_index=1.0, density=1.0) >>> bundle = scalars(problem, 0.25, 0.75) >>> bundle.phi_delta.tolist(), bundle.prefactor.tolist() ([0.5], [1.0])
- class solprop.translate.stage.StageTerms(phi, prefactor, separable=None)[source]#
Bases:
objectThe fixed arrays a backend holds for the whole run.
Each shape annotation carries its own axis name because the two members are independent builds with their own term counts; nothing here ties them together.
- Attributes:
- phinumpy.ndarray or None
The terms of
Phi, complex128, shape(n_phi_terms, n_batch_or_1, n_modes, n_pol, n_omega). One term for a constant operator,n_termsfor an expansion, andNonefor the z table, which has no such form.- prefactornumpy.ndarray or None
The terms of the prefactor, complex128, shape
(n_pre_terms, n_modes, n_pol, n_omega), on the same three cases.- separablesolprop.translate.spatial.SeparableOperator or None
Shared constant spatial coefficients. When present,
phiandprefactorare absent; a backend evaluates the operator and prefactor per element.
Examples
>>> import numpy as np >>> from solprop.translate.linop import constant_phi >>> from solprop.translate.stage import StageTerms >>> table = constant_phi(np.array([-1j, -2j]), 1.0) >>> StageTerms(phi=table.phi_constant[np.newaxis], prefactor=None).phi.shape (1, 1, 1, 1, 2)
- property device_ready: bool[source]#
Return whether both quantities are a weighted sum of fixed arrays.
- Returns:
- bool
True when neither member is
None.
- separable: SeparableOperator | None = None#
- class solprop.translate.stage.StepAnchor(z, phi)[source]#
Bases:
objectThe weights of
Phiat the start of a step, hoisted for all of its stages.Phiis cumulative and the stage needs its increment, so every stage of a step subtracts the weights at the same anchor. Evaluating them once rather than once per stage is worth a sixth of the whole per-step scalar cost on the RDW gradient, where a Clenshaw pass over the scalar tables is the largest part of what the host does.- Attributes:
- zfloat
The step’s anchor, m.
- phinumpy.ndarray or None
Weights of
Phi(z), float64, shape(n_phi_terms,);NonewhenPhiis a z table and has no such form.
Examples
>>> from solprop.translate.stage import StepAnchor >>> StepAnchor(0.5, None).z 0.5
- solprop.translate.stage.anchor(problem, z0, *, helpers=Helpers(name='numba'))[source]#
Return the weights a step’s stages all subtract, evaluated once.
- Parameters:
- problemsolprop.translate.problem.KernelProblem
The validated contract.
- z0float
The step’s anchor, m, inside the tabulated range.
- helperssolprop._helpers.Helpers, optional
The implementation of the tables’ Clenshaw pass: the backend’s that runs the step (
StepDriver.helpers, SEP 29 section 6.6), or the shared Numba one by default.
- Returns:
- StepAnchor
The weights of
Phi(z0), orNonefor the z table.
- Raises:
- solprop.exceptions.TranslationError
If the position lies outside the tabulated range.
- solprop.translate.stage.combine(weights, fixed)[source]#
Return the weighted sum of the terms, the one arithmetic a backend does.
A single term is multiplied rather than contracted, so that a constant operator’s
D (z2 - z1)stays exact to the last bit – the propertynumpy_ref’s P0 rung rests on – and is not routed through a BLAS call for one product. A single term weighted by exactly one is the stored array itself, assolprop.translate.responses.prefactor_at()returns it on the same fast path: the alternative is a full array multiply and an allocation per stage for a factor of one, which on the comb-to-continuum prefactor is 1.6 MB eight times a step. The array is read-only, as everything crossing the seam is.- Parameters:
- weightsnumpy.ndarray
Host scalars, float64, shape
(n_terms,).- fixednumpy.ndarray
The terms, shape
(n_terms, *value_shape).
- Returns:
- numpy.ndarray
The combination, of the terms’ value shape.
- Raises:
- BackendError
If the counts disagree.
Examples
>>> import numpy as np >>> from solprop.translate.stage import combine >>> combine(np.array([0.5]), np.array([[2.0, 4.0]])).tolist() [1.0, 2.0] >>> fixed = np.array([[2.0, 4.0]]) >>> bool(np.shares_memory(combine(np.array([1.0]), fixed), fixed)) True
- solprop.translate.stage.require_device_ready(problem, name)[source]#
Refuse a problem whose z quantities an accelerator would hold as a z table.
A backend whose step loop runs on an accelerator calls this at construction: the native backend’s device builds. The z table is a Chebyshev table of the state’s own size times its degree and intervals, which the device would have to hold resident; the translation layer falls back to it for a mode set that declares no scalar parameter paths, that disagrees about their values, or whose operator kinks in a parameter (
docs/dev/kernel-contract.mdsection 3). Every CPU backend runs it:numpy_refandnumba_cpuevaluate it on the host, and the native backend’s CPU build evaluates it natively (SEP 29 section 6.6).- Parameters:
- problemsolprop.translate.problem.KernelProblem
The validated contract.
- namestr
The backend’s registry name, for the message.
- Raises:
- solprop.exceptions.BackendError
If either quantity is a z table, naming which, its size and the backends that run it.
Examples
>>> from solprop.translate.stage import require_device_ready >>> require_device_ready.__name__ 'require_device_ready'
- solprop.translate.stage.scalars(problem, z0, z1, *, at=None, helpers=Helpers(name='numba'))[source]#
Return the host scalars of one stage, from the step’s anchor to the node.
- Parameters:
- problemsolprop.translate.problem.KernelProblem
The validated contract.
- z0float
The step’s anchor, m, inside the tabulated range.
- z1float
The stage’s position, m, inside the tabulated range.
- atStepAnchor or None, optional
The anchor’s weights from
anchor(), hoisted by a caller that takes every stage of one step.Noneevaluates them here, which is one Clenshaw pass per stage that the step did not need. Must be the anchor of z0.- helperssolprop._helpers.Helpers, optional
The implementation of the tables’ Clenshaw pass: the backend’s that runs the stage (
StepDriver.helpers, SEP 29 section 6.6), or the shared Numba one by default.
- Returns:
- StageScalars
The weights and the scalar path values, all host floats.
- Raises:
- solprop.exceptions.TranslationError
If a position lies outside the tabulated range.
- solprop.exceptions.BackendError
If at is the anchor of a different position.
- solprop.translate.stage.scalars_at(problem, z0, positions, *, at=None, helpers=Helpers(name='numba'))[source]#
Return the host scalars of several stages of one step, each table read once.
A trial knows every position it will evaluate before its first stage – the step’s end and
z0 + c_i hfor each node – andscalars()reads every scalar table once per position, a Python call and a compiled Clenshaw pass apiece. Here each table is read once over all the positions (at_positions()), which keeps every operation of the per-position pass, so bundleiequalsscalars(problem, z0, positions[i], at=at)field for field and bit for bit, with the same interval choice at a breakpoint and the same refusal outside the range. The increment ofPhiis formed as there:z1 - z0for a constant operator, the difference of the integrated weights for an expansion.The coupled flow’s and the moving guide’s weights are a basis row per position rather than a table, and are formed one position at a time.
- Parameters:
- problemsolprop.translate.problem.KernelProblem
The validated contract.
- z0float
The step’s anchor, m, inside the tabulated range.
- positionssequence of float
The stages’ positions, m, inside the tabulated range, in the order the bundles are returned.
- atStepAnchor or None, optional
The anchor’s weights from
anchor(); must be the anchor of z0.- helperssolprop._helpers.Helpers, optional
The implementation of the tables’ Clenshaw pass: the backend’s that runs the stages (
StepDriver.helpers, SEP 29 section 6.6), or the shared Numba one by default, for callers outside a backend.
- Returns:
- tuple of StageScalars
One bundle per position, in order.
- Raises:
- solprop.exceptions.TranslationError
If a position lies outside the tabulated range.
- solprop.exceptions.BackendError
If at is the anchor of a different position.
Examples
>>> import numpy as np >>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import problem_from >>> from solprop.pulses import SechPulse >>> from solprop.stepper import PIController >>> from solprop.translate.problem import assemble >>> from solprop.translate.stage import scalars_at >>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (500e-9, 2e-6), 4e-12) >>> gnlse = problem_from([0.0, 0.0, -1.2e-26], omega_ref=grid.omega0, gamma=0.11, ... shock=False) >>> start = SechPulse(wavelength=835e-9, width=1e-13, peak_power=1.0).build(grid) >>> problem = assemble(grid, gnlse.mode, [gnlse.kerr], start, ... controller=PIController(rtol=1e-8, atol=1e-14), ... save_planes=[1.0], dz_max=0.1, dz_init=1e-3, ... shock=False, prefactor_index=1.0, density=1.0) >>> [bundle.phi_delta.tolist() for bundle in scalars_at(problem, 0.25, [0.5, 0.75])] [[0.25], [0.5]]
- solprop.translate.stage.terms(problem)[source]#
Return the fixed arrays of a problem’s two array-valued z quantities.
Built once per run and uploaded once by a device backend. The constant fast path is the one-term case rather than a case of its own, so a backend has one code path (
docs/dev/kernel-contract.mdsections 3 and 5).- Parameters:
- problemsolprop.translate.problem.KernelProblem
The validated contract.
- Returns:
- StageTerms
The terms of
Phiand of the prefactor, eachNonewhere the z table carries the quantity instead.
Examples
>>> import numpy as np >>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import problem_from >>> from solprop.pulses import SechPulse >>> from solprop.stepper import PIController >>> from solprop.translate.problem import assemble >>> from solprop.translate.stage import terms >>> grid = EnvelopeGrid.from_limits(1.0, 835e-9, (500e-9, 2e-6), 4e-12) >>> gnlse = problem_from([0.0, 0.0, -1.2e-26], omega_ref=grid.omega0, gamma=0.11, ... shock=False) >>> start = SechPulse(wavelength=835e-9, width=1e-13, peak_power=1.0).build(grid) >>> problem = assemble(grid, gnlse.mode, [gnlse.kerr], start, ... controller=PIController(rtol=1e-8, atol=1e-14), ... save_planes=[1.0], dz_max=0.1, dz_init=1e-3, ... shock=False, prefactor_index=1.0, density=1.0) >>> held = terms(problem) >>> held.phi.shape[0], held.prefactor.shape[0], held.device_ready (1, 1, True)