solprop.backends.numba_cpu.general_modal#
The general modal guide’s nonlinear source, compiled, on the CPU backend.
The second execution path of
solprop.backends.numpy_ref.general_modal, with the same four steps of one
stage – scale, synthesise, respond, project – and the same maps, derived by the
reference’s own solprop.backends.numpy_ref.backend._general_maps so that the
two backends cannot read a record differently. What is this backend’s own is the
execution: a fourth FFT plan family
(solprop.backends.numba_cpu.fft.GeneralPlans) bound to buffers allocated
once, compiled kernels in place of einsum, and nothing allocated inside a stage.
Parity is a rounding statement (SEP 25): this backend’s source is within the
bound of tests/general_parity.py, fixed from the contractions’ term counts, the
transforms’ lengths and the maps’ conditioning, of the reference’s – not equal to it.
The contractions – the synthesis over channels, the real cubic’s symmetric
contraction (each node’s coefficients, then the ten monomials of a sample), the
analytic form’s contraction over k and l and then over j, the delayed
contraction over j, k, l and the projection over nodes and components – are
written for locality through the per-element bodies of
solprop.backends.bodies.define_bodies(), and their order is this backend’s own.
Each is diagonal in the bin (or the sample), a batch of tiny matrix-vector products
with a different matrix per bin, not a GEMM, so BLAS has nothing to take: the loops
run the bin (or the sample) innermost over contiguous rows. FFTW’s transforms differ
from pocketfft’s by their own rounding, which the same bound covers.
Normalisation. FFTW applies none. The inverse transform’s 1 / n_to is folded
into the per-bin scale of step 1 with the pad scale and the analytic doubling, and the
doubled axis’s 1 / (2 n_to) into the delayed contraction; both are powers of two
on every grid this package builds, so the folding is exact.
Chunks. The node loop runs in the reference’s node chunks, which size the plans and the per-chunk buffers; the projection accumulates each chunk’s partial into the source. A partial last chunk executes its own plans on prefix views of the same buffers.
A moving guide. Its maps are n_intervals * n_deg
Chebyshev coefficients of each map, the reference’s GeneralMaps.synthesis_terms
and projection_terms (views of the record’s own arrays, never copied), and a
stage evaluates them at its position from the bundle’s transform_weights (kernel
schema 14). The combination is one compiled kernel, combine, run once per map
into two stage buffers allocated once: every coefficient is flattened to one axis,
the axis is cut into fixed blocks that the threads share (prange), and each block
is zeroed and then accumulates w_t c_t term by term in the terms’ order, skipping
a term of zero weight – every section but the stage’s own – so that a stage reads
n_deg of the coefficients and not all of them (a BLAS product over every term, as
the reference’s numpy.tensordot is, would read all of them). The reference’s sum
and this one differ by a rounding of n_deg terms, bounded in the backend parity
tests. A constant guide’s maps are the reference’s arrays read directly.
Module Attributes
The ledger rows of |
|
Map coefficients one thread of the per-stage combination takes at a time. |
Functions
|
Compile the general route's kernels for one form, delay and threading regime. |
Classes
|
The compiled kernels of one general modal problem. |
|
The general modal route's buffers, plans and kernels, allocated once. |
- solprop.backends.numba_cpu.general_modal.GENERAL_LEDGER_ROWS = ('general_synthesis', 'general_projection', 'general_stage_maps', 'general_tensor', 'general_electric', 'general_magnetic', 'general_backward_electric', 'general_backward_magnetic', 'general_reciprocal_gram', 'general_coordinate_dual', 'general_scale', 'general_lab', 'general_scaled_state', 'general_source', 'general_source_partial', 'general_output', 'general_node_spectrum', 'general_node_field', 'general_node_polarisation', 'general_node_source', 'general_node_drive', 'general_node_drive_spectrum', 'general_node_product', 'general_node_histories', 'general_node_delayed', 'general_delayed_tensor', 'general_delayed_spectrum', 'general_fft_plan_heap_growth')#
The ledger rows of
numba_cpu’s general modal route, named on every route.The reference’s names where the two backends hold the same buffer – on a moving guide
general_synthesisandgeneral_projectionare every coefficient of the maps andgeneral_stage_mapsthe two maps a stage evaluates into, zero otherwise – and the rest are this backend’s own persistent buffers – the per-bin scale, the combined stage input, the source, its per-chunk partial and the scaled output, the doubled axis’s product buffer – and the allocator’s growth over planning the fourth family. Zero on every other route.
- class solprop.backends.numba_cpu.general_modal.GeneralKernels(synthesise, respond, product, contract, add_delayed, project, combine)[source]#
Bases:
NamedTupleThe compiled kernels of one general modal problem.
- Attributes:
- synthesisenumba.core.dispatcher.Dispatcher
Step 2:
E(q, c, w) = sum_m S ainto the chunk’s fine spectrum.- respondnumba.core.dispatcher.Dispatcher
Step 3: the instantaneous polarisation, and the pair drive when delayed.
- productnumba.core.dispatcher.Dispatcher
One response and channel’s kernel spectrum times the drive spectrum.
- contractnumba.core.dispatcher.Dispatcher
The delayed contraction of one response and channel, accumulated.
- add_delayednumba.core.dispatcher.Dispatcher
P += eps0 * totalover the chunk’s rows.- projectnumba.core.dispatcher.Dispatcher
Step 4: the chunk’s projection, accumulated into the source.
- combinenumba.core.dispatcher.Dispatcher
A moving guide’s map at a stage:
sum_t w_t c_tover flattened coefficients.
- class solprop.backends.numba_cpu.general_modal.GeneralWorkspace(problem, *, flag, fftw_threads, parallel)[source]#
Bases:
objectThe general modal route’s buffers, plans and kernels, allocated once.
- Parameters:
- problemsolprop.translate.problem.KernelProblem
A problem the backend has admitted, carrying
transform.modal_tables.- flagstr
The FFTW planning flag’s key.
- fftw_threadsint or None
Override for FFTW’s pool for the fourth family;
Nonesizes it from the chunk’s transform by the backend’s own rule (solprop.backends.numba_cpu.fft.choose_pools()).- parallelbool
Whether the node loops are a
prange.
- memory_ledger()[source]#
Return the bytes the route holds, one row per named buffer.
- Returns:
- dict
Every row of
GENERAL_LEDGER_ROWS: the maps and the record’s resident reference, counted as the reference backend counts them, and this backend’s persistent buffers and plan heap growth.
- move(weights)[source]#
Evaluate a moving guide’s maps at a stage, from its
transform_weights.The reference’s
GeneralMaps.aton this backend’s buffers: nothing for a constant guide, and for a moving one the two weighted sums of the coefficients bymake_general_kernels()’combine, allocating nothing.- Parameters:
- weightsnumpy.ndarray or None
The stage bundle’s
transform_weights, float64, one per coefficient;Nonefor maps that do not move.
- Raises:
- BackendError
If moving maps are asked for with no weights, or constant ones with some, in the reference’s words.
- source(transform_weights=None, dispersive_densities=())[source]#
Evaluate one stage’s modal source from
labintooutput.The reference’s
solprop.backends.numpy_ref.general_modal.general_source()on this backend’s buffers, allocating nothing. SEP 6’s dispersive channels filter each chunk’s synthesised spectrum and land their products’ spectra in its polarisation spectrum, before the projection (GeneralDispersive).- Parameters:
- transform_weightsnumpy.ndarray or None, optional
The stage bundle’s weights of a moving guide’s maps (
move());Nonefor maps that do not move.- dispersive_densitiestuple of float, optional
Each dispersive channel’s number density at the stage, m^-3: the stage bundle’s
dispersive_kerr_densities.
- Returns:
- numpy.ndarray
output: the projected polarisation times-i omega, shape(n_modes, n_omega), in sqrt(W)/m before the crop scale.
- solprop.backends.numba_cpu.general_modal.MAP_BLOCK = 4096#
Map coefficients one thread of the per-stage combination takes at a time.
64 KiB of complex128 in the output block and as much per term read, so that a block’s running sum stays in a core’s level-2 cache while its
n_degterms stream past; a map of the graded-index taper has about 300 of them. Each element’s sum runs over its terms whatever the blocking, so the value does not depend on it.
- solprop.backends.numba_cpu.general_modal.make_general_kernels(*, analytic, delayed, parallel, contiguous=False)[source]#
Compile the general route’s kernels for one form, delay and threading regime.
Every flag is a closure constant, so a problem without a delayed response pays nothing for it and the serial and parallel variants come from one body (
docs/dev/backends.mdsection 3.2). The node loops are independent per node and the projection’s reduction is per output bin, so the result does not depend on how many threads ran either.With contiguous the coarse bins are the leading bins of the fine spectrum (
cropisarange(n_omega), as on every field grid), and the synthesis and the projection index the fine spectrum directly instead of throughcrop: the loads are then contiguous and the bin loops vectorise. Measured on one thread of an Apple-silicon laptop on the benchmark case’s 576 nodes, 2 channels and 513 bins: the synthesis 1.8 -> 1.35 ms a stage and the projection 1.4 to 1.6 -> 1.0 ms.- Parameters:
- analyticbool
Whether the cubic is the analytic-signal form, which drives the delayed response with
Re(E_k conj(E_l)) / 2; otherwise the real field’sE_k E_l.- delayedbool
Whether the respond kernel writes the delayed response’s pair drive.
- parallelbool
Whether the node loops, and the projection’s channel loop, are a
prange.- contiguousbool, optional
Whether
cropisarange(n_omega), which the caller has checked.
- Returns:
- GeneralKernels
The compiled kernels.