solprop.backends.numba_cpu.dispersive#
SEP 6’s frequency-dependent electronic response on the fused node path.
The leaf responses.dispersive_kerr carries each species’ spectral multipliers on the
fine grid; solprop.nonlinear.responses.dispersive_cubic() is the arithmetic, and
solprop.backends.numpy_ref.passes.dispersive_kerr_polarisation() evaluates it at
every node. This module evaluates the same operator with the multipliers moved off the
nodes and onto the modal columns, where they cost the fewest transforms.
Why the columns. The legacy transverse transform – one mode-averaged node, a fixed
multimode quadrature, the radial Hankel pair – synthesises and projects with real
matrices that do not depend on frequency, and the temporal collar is the same at every
node. A spectral multiplier and the collar therefore commute with both:
P G[w (G E)^3] = G[w P (G E)^3] for a projection P, a filter G and the
collar w. So
the input multipliers act on the modal spectrum the stage combination has already formed, before its inverse transform (no forward transform of the node field), and one batched inverse transform gives every filtered modal field;
the filtered fields are synthesised at the nodes of a chunk by the same real GEMM as the bare field, and the products are formed there, in the leaf’s sector;
each product is projected onto the modal columns (and onto the embedded rule’s, for the quadrature alarm) by the same GEMM as the polarisation;
the collar multiplies the projected products in time, one batched forward transform takes them to frequency, and their output multipliers and the channels’
eps0 gamma0 rho, summed per product, land them in the stage’s own polarisation spectrum, after the stage’s forward transform has made it of every other response.
The collar acts before the output multipliers, G[w (G E)^3] (John Travers,
2026-10-08), so the response has no inverse transform of its own: its output joins the
stage in frequency.
When the response is alone – no constant Kerr term, Raman, plasma or envelope
plasma beside it – the response column and the polarisation’s projection are skipped:
the node field buffer holds the filtered field and its product in place, and the stage’s
forward transform is skipped, the response writing the polarisation spectrum itself. A
symmetric response of one species whose product has the polarisation’s dtype goes
further: its product is projected into the stage’s own projected buffer, the stage’s
forward transform is the product’s, and g finishes it in the stage’s spectrum – the
constant cubic’s transforms exactly – and with no in-stage absorber reading the bare
modal field it filters the stage’s spectrum in place, holding no field-sized buffer at
all. Otherwise the bare field stays where Raman, plasma and Kerr read it, and the
response holds its own buffers (DispersiveWorkspace.memory_ledger()).
Mixtures. The symmetric evaluator forms one product for a whole mixture, under the
filter built from the mixture’s own Kerr diagonal,
g_mix = [sum_s f_s g_s^4 / sum_s f_s]^(1/4) with f_s = eps0 gamma3_0 rho_s
(solprop.nonlinear.responses.mixture_filter()), rebuilt from the channels’
densities whenever they change (StageWeights); a mixture costs what one gas
costs, and takes the lone path when it is alone. The polynomial groups species with
equal leg bounds, which share their derivative fields and add their coefficient tables
density-weighted at every stage (DispersiveTerms): an exact rewriting of the
per-species sum, whose transform count then grows with the number of distinct leg
bounds rather than of species.
The embedded rule’s products are projected at every stage, as the polarisation’s are,
and windowed, transformed and combined only when a step is accepted
(DispersiveWorkspace.finish_coarse()), from the last stage’s buffers, as the
envelope currents’ are.
Module Attributes
The response's rows of the memory ledger, named (at zero) on every problem. |
Functions
|
Add the projected products' spectra under their stage multipliers. |
|
Multiply the response's products by the temporal collar, in place. |
|
Sum the projected products' spectra under their stage multipliers. |
|
Return the product form of a grid kind and a sector. |
|
Lay out a leaf's filtered fields, products and output terms. |
|
Write each input multiplier times the modal spectrum, one block per field. |
|
Compile the polynomial's two derivative products at every node of a chunk. |
|
Compile the product |
|
Write a symmetric mixture's filter rows at a stage; return its coefficient. |
|
Multiply the stage's spectrum, in place, by the one product's multiplier. |
|
Sum the output terms' multipliers that share a product, at this stage. |
Classes
|
How the backend runs the fused node path, as the response needs it. |
|
The backend's taper-scaled transform matrices, rescaled in place every stage. |
|
The response's filtered fields, products and output terms, laid out once. |
|
Preallocate the response's buffers and plans, and run it on the fused path. |
|
The terms' weights at one stage: |
- solprop.backends.numba_cpu.dispersive.DISPERSIVE_LEDGER_ROWS = ('dispersive_filtered_time', 'dispersive_node_fields', 'dispersive_products', 'dispersive_coarse_products', 'dispersive_product_spectra', 'dispersive_chunk_scratch', 'dispersive_synthesis', 'dispersive_multipliers', 'dispersive_fft_plan_heap_growth')#
The response’s rows of the memory ledger, named (at zero) on every problem.
- class solprop.backends.numba_cpu.dispersive.DispersiveLayout(n_modes, n_pol_state, chunk_rows, chunks, alone, embedded, real_synthesis, bare_time_free, parallel)[source]#
Bases:
NamedTupleHow the backend runs the fused node path, as the response needs it.
- Attributes:
- n_modesint
Modal rows of the transform.
- n_pol_stateint
Polarisation components the state carries.
- chunk_rowsint
Rows of the node buffers: chunk nodes times components per node.
- chunksint
Node chunks per stage; more than one needs a scratch to accumulate into.
- alonebool
Whether the dispersive response is the only one, so that the node field buffer, the projected buffer and the polarisation spectrum are the response’s.
- embeddedbool
Whether the quadrature has an embedded rule, projected for the alarm.
- real_synthesisbool
Whether the backend’s scaled synthesis is real; otherwise the response holds a real copy for its real GEMMs.
- bare_time_freebool
Whether nothing reads the bare modal field once the nodes are synthesised – there is no in-stage absorber – so that a lone symmetric response of one species may filter the stage’s own spectrum in place.
- parallelbool
Whether the node loops are
prange.
- class solprop.backends.numba_cpu.dispersive.DispersiveMatrices(synthesis, projection, embedded)[source]#
Bases:
NamedTupleThe backend’s taper-scaled transform matrices, rescaled in place every stage.
- Attributes:
- synthesisnumpy.ndarray
The scaled synthesis,
(n_transform_rows, n_modes); real, or a complex cast of a real matrix that the response replaces with a real copy of its own.- projectionnumpy.ndarray
The scaled projection, real,
(n_modes, n_transform_rows).- embeddednumpy.ndarray or None
The embedded rule’s scaled projection, or
None.
- class solprop.backends.numba_cpu.dispersive.DispersiveTerms(symmetric, groups, keys, cubes, mixtures, input_multipliers, term_rows, term_channels, term_multipliers, pair_constants, quartics, input_scale, output_scale)[source]#
Bases:
NamedTupleThe response’s filtered fields, products and output terms, laid out once.
The channels are grouped as the leaf says (
groups()). The symmetric evaluator forms one filtered field and one product per group: a group of one species takes its owng, fixed, and a mixture – a group of several, the channels that act at the same nodes – the filter of its own Kerr diagonal, whichStageWeightsrebuilds from the channels’ densities (mixture_rows(); SEP 6, section 6, adopted for mixtures on 2026-10-08). The polynomial’s channels are grouped by their leg weight: species whose leg bounds are equal have the samed, so they shareD EandD^2 Eand their four coefficient tables add, density-weighted, at every stage –sum_s N_s gamma0_s (1, A_s, B_s, B'_s)– an exact rewriting of the per-species sum (SEP 6, section 6). The firstn_cubesproducts areE^3, one per key – a single one shared by every channel, but on the general modal route one per node region, whose nodes alone it is formed at; groupgthen owns productsn_cubes + 2 g,T(E, E, D E), andn_cubes + 2 g + 1,c0_g T(E, E, D^2 E) + c1_g T(E, D E, D E)withc0_g = sum_s f_s 3 (b_s + b'_s)andc1_g = sum_s f_s 6 b_sover its channels,f_s = eps0 gamma0_s rho_sat the stage.- Attributes:
- symmetricbool
Whether the evaluator is the symmetric one.
- groupstuple of tuple of int
The channels of each group, in channel order.
- keystuple of int
Each group’s key: its node region on the general modal route, zero otherwise.
- cubestuple of int
Each group’s cube product, for the polynomial; empty for the symmetric evaluator.
- mixturestuple of int
The symmetric groups of more than one channel, whose filter is rebuilt per stage; empty for the polynomial. Mixture
m’s field and product are its group’s, and its coefficient is factorn_channels + 1 + m.- input_multipliersnumpy.ndarray
One row per filtered field:
gper group, ordandd^2per group, timesPi_+on a field grid’s analytic family; a mixture’s row is its stage’s.- term_rowsnumpy.ndarray
Each output term’s product, int64.
- term_channelsnumpy.ndarray
Each term’s factor, int64: its channel;
n_channels, the unit factor, for a product that already carries its channels’ factors;n_channels + 1 + mfor mixturem.- term_multipliersnumpy.ndarray
Each term’s output multiplier times the sector’s
Pi_+and factor, float64; a mixture’s row is its stage’s.- pair_constantsnumpy.ndarray
3 (b + b')and6 bper channel, m^4, shape(n_channels, 2); zero for the symmetric evaluator.- quarticsnumpy.ndarray
g_s^4per channel of a mixture,(n_channels, n_bins);(0, 0)without a mixture.- input_scalenumpy.ndarray
What a symmetric input multiplier carries besides
g:Pi_+on a field grid’s analytic family, one otherwise.- output_scalenumpy.ndarray
The sector’s
Pi_+and factor, which every output multiplier carries.
- property n_cubes: int[source]#
Return the number of cube products.
- Returns:
- int
One per distinct key for the polynomial, none for the symmetric evaluator.
- property n_fields: int[source]#
Return the number of filtered fields.
- Returns:
- int
One per group (symmetric) or two per group (polynomial).
- property n_products: int[source]#
Return the number of products.
- Returns:
- int
One per group (symmetric), or
n_cubes + 2 n_groups(polynomial).
- class solprop.backends.numba_cpu.dispersive.DispersiveWorkspace(problem, plans, nodes, scratch, matrices, layout, *, flag, fftw_threads)[source]#
Bases:
objectPreallocate the response’s buffers and plans, and run it on the fused path.
The stage drives it in five calls:
prepare()weighs the channels at the stage and filters the padded modal spectrum,begin()scales the synthesis,respond_chunk()forms and projects the products of each node chunk,finish()applies the collar to them and transforms them, and after the stage’s own forward transform – skipped whenreplaces_transform–combine()adds their weighted spectra to the polarisation spectrum. The embedded rule’s products are finished and combined when a step is accepted (finish_coarse(),combine_coarse()).- Parameters:
- problemsolprop.translate.problem.KernelProblem
The problem, carrying
responses.dispersive_kerron a legacy transform.- planssolprop.backends.numba_cpu.fft.TransformPlans
The backend’s plans, whose projected buffers and spectra the response adds into and, when it is alone, uses as its own.
- nodesnumpy.ndarray
The backend’s node field buffer,
(chunk_rows, n_to): read as the bare field, and, when the response is alone and symmetric, used as its own.- scratchnumpy.ndarray
The backend’s chunk projection scratch,
(n_columns, n_to), shared between sequential uses when its dtype is the products’.- matricesDispersiveMatrices
The backend’s scaled transform matrices, whose views the response holds.
- layoutDispersiveLayout
How the backend runs the path.
- flagstr
The FFTW planning flag, a key of
solprop.backends.numba_cpu.fft.PLAN_FLAGS.- fftw_threadsint or None
The FFTW pool override;
Nonesizes each batched plan by the backend’s rule.
- begin(bundle, taper)[source]#
Weigh the channels at the stage and scale the real copy of the synthesis.
prepare()has weighed them already on the stage path, and the weights update only when the densities change.- Parameters:
- bundlesolprop.translate.stage.StageScalars
The stage’s scalars, whose channel densities weight the terms and, for the polynomial, its groups’ derivative products.
- taperfloat
The stage’s taper scale.
- combine()[source]#
Land the products’ weighted spectra in the stage’s polarisation spectrum.
After the stage’s forward transform (or instead of it, when
replaces_transform).
- finish()[source]#
Apply the collar to the projected products and transform them.
Before the stage’s forward transform: a borrowed product is the stage’s projected buffer, which that transform takes.
- finish_coarse(bundle)[source]#
Apply the collar to the embedded rule’s products and transform them.
Called when a step is accepted, before the stage’s transform of the embedded projection; the products are the last stage’s, as the projection is, and so are the scalars that weigh them. Once per evaluation: the products hold the collar until another stage projects them again.
- Parameters:
- bundlesolprop.translate.stage.StageScalars
The last stage’s scalars.
- memory_ledger()[source]#
Return the bytes of every buffer the response owns, by name.
Buffers it borrows from the backend – the node field buffer, the chunk scratch, the projected buffers and their spectra – are the backend’s rows.
- Returns:
- dict
Every row of
DISPERSIVE_LEDGER_ROWS.
- prepare(spectrum, bundle)[source]#
Weigh the channels, filter the stage’s spectrum and transform the fields.
Called after the stage combination has padded the modal spectrum and before the stage’s own inverse transform, which destroys it. In place, the filtered spectrum is the stage’s own, and that transform gives the filtered field.
- Parameters:
- spectrumnumpy.ndarray
The padded modal spectrum (doubled positive bins on the analytic family).
- bundlesolprop.translate.stage.StageScalars
The stage’s scalars, whose channel densities weigh the terms and build a symmetric mixture’s filter, which the input multipliers read.
- property replaces_transform: bool[source]#
Return whether the response writes the stage’s polarisation spectrum itself.
Alone with products of its own, nothing else is projected, so the stage’s forward transform of its projected buffer would transform nothing; the response’s
combine()writes the spectrum instead.- Returns:
- bool
True when the stage skips its forward transform.
- class solprop.backends.numba_cpu.dispersive.StageWeights(leaf, terms)[source]#
Bases:
objectThe terms’ weights at one stage:
eps0 gamma0 rhoper channel and per group.It holds the stage’s own copies of the input and output multipliers, whose rows a symmetric mixture rebuilds from the channels’ densities (
mixture_rows()), and updates only when the densities change: a fill of constant composition builds its filter once, and every stage of a run of varying composition its own.- Parameters:
- leafsolprop.translate.responses.DispersiveKerrTerms
The compiled leaf, for its static coefficients.
- termsDispersiveTerms
The layout.
- property nbytes: int[source]#
Return the bytes the weights hold.
- Returns:
- int
The factors, group weights, stage multipliers and the layout’s arrays.
- update(densities)[source]#
Weigh the channels by their densities at a stage, allocating nothing.
A no-op when the densities are those of the last update: everything here is a function of them alone.
- Parameters:
- densitiestuple of float
Each channel’s number density, m^-3, the stage bundle’s
dispersive_kerr_densities.
- solprop.backends.numba_cpu.dispersive.add_spectra(spectra, weights, out)[source]#
Add the projected products’ spectra under their stage multipliers.
out[c, k] += sum_r weights[r, k] spectra[r C + c, k]: the response’s share of the polarisation spectrum, added to what the stage’s forward transform made of every other response’s. The products’ sum is formed first, in product order, and added once, so the result does not depend on what out held.- Parameters:
- spectranumpy.ndarray
The forward transforms of the windowed products, one block of
n_columnsrows per product, at leastn_binslong.- weightsnumpy.ndarray
One multiplier per product (
weigh_terms()).- outnumpy.ndarray
The stage’s polarisation spectrum,
(n_columns, n_bins), added to.
- solprop.backends.numba_cpu.dispersive.apply_window(target, window)[source]#
Multiply the response’s products by the temporal collar, in place.
The collar acts on each product before
Pi_+and the output multipliers:G[w (G E)^3](SEP 6, Phase 2a, open question 1, decided 2026-10-08).- Parameters:
- targetnumpy.ndarray
The products in time, one row per column of each product.
- windownumpy.ndarray
The temporal collar
grids.t_window_fine.
- solprop.backends.numba_cpu.dispersive.combine_spectra(spectra, weights, out)[source]#
Sum the projected products’ spectra under their stage multipliers.
out[c, k] = sum_r weights[r, k] spectra[r C + c, k]. out may be the first block of spectra: the first product writes each bin from itself, and every later product reads a later block.- Parameters:
- spectranumpy.ndarray
The forward transforms of the projected products, one block of
n_columnsrows per product, at leastn_binslong.- weightsnumpy.ndarray
One multiplier per product (
weigh_terms()).- outnumpy.ndarray
Written with the summed spectrum,
(n_columns, n_bins).
- solprop.backends.numba_cpu.dispersive.dispersive_form(kind, sector)[source]#
Return the product form of a grid kind and a sector.
- Parameters:
- kind{‘field’, ‘envelope’}
The spectral layout.
- sector{‘full’, ‘conserving’}
Which quartets the leaf keeps.
- Returns:
- str
"real"(the full cubic of a real field),"conserving"(the number-conserving group of analytic signals or envelopes) or"harmonic"(an envelope’s full cubic: the conserving group and both harmonic groups).
Examples
>>> from solprop.backends.numba_cpu.dispersive import dispersive_form >>> dispersive_form("field", "full"), dispersive_form("envelope", "full") ('real', 'harmonic')
- solprop.backends.numba_cpu.dispersive.dispersive_terms_layout(leaf, kind, *, analytic, keys=None)[source]#
Lay out a leaf’s filtered fields, products and output terms.
Every multiplier carries the sector’s own factor: one on a field grid’s full cubic,
Pi_+ / 8for its conserving sector (the one-sided spectrum of the real part of a quarter of the analytic polarisation),Pi_+ / 4on an envelope grid. No transform normalisation: the products’ spectra land in the stage’s own polarisation spectrum, which its forward transform made unnormalised too.- Parameters:
- leafsolprop.translate.responses.DispersiveKerrTerms
The compiled leaf.
- kind{‘field’, ‘envelope’}
The spectral layout.
- analyticbool
Whether the products are a field grid’s analytic signals.
- keystuple of int or None, optional
A key per channel that must also agree for two channels to share a group: the general modal route’s node region.
Nonetakes the leaf’s own regions, a single key where it has none.
- Returns:
- DispersiveTerms
The layout.
- solprop.backends.numba_cpu.dispersive.filter_spectra(spectrum, multipliers, out)[source]#
Write each input multiplier times the modal spectrum, one block per field.
Row
field * n_columns + columnof out ismultipliers[field] * spectrum[column]on the firstn_weightedbins and zero beyond, which is where an analytic family’s full-length spectrum holds nothing.- Parameters:
- spectrumnumpy.ndarray
The padded modal spectrum the stage’s inverse transform reads, complex.
- multipliersnumpy.ndarray
The input multipliers on the leaf’s spectral layout, float64.
- outnumpy.ndarray
Written whole, shape
(n_fields * n_columns, n_bins): a complex-to-real plan destroys its input, so nothing written once can be relied on.
- solprop.backends.numba_cpu.dispersive.make_dispersive_pairs(form, n_pol, *, parallel, shifted=False)[source]#
Compile the polynomial’s two derivative products at every node of a chunk.
With
Ethe bare field,F = D EandH = D^2 E, it writesT(E, E, F)overFandc0 T(E, E, H) + c1 T(E, F, F)overH, in place: the grouped polynomial’s second and third terms, whose output multipliers are3 a + 6 b dand one (SEP 6, section 4.5 and the Phase 0 report).- Parameters:
- form{‘real’, ‘conserving’, ‘harmonic’}
The product form (
dispersive_form()).- n_polint
Components at one node, 1 or 2.
- parallelbool
Whether the node loop is a
prange.- shiftedbool, optional
Whether the bare field’s zero-frequency constant is subtracted per row as it is read (
make_dispersive_self()).
- Returns:
- numba.core.dispatcher.Dispatcher
The compiled kernel
(field, first, second, c0, c1, carrier, offset).
- solprop.backends.numba_cpu.dispersive.make_dispersive_self(form, n_pol, *, parallel, shifted=False)[source]#
Compile the product
(u, u, u)of one field at every node of a chunk.The symmetric evaluator’s only product, of the filtered field, and the polynomial’s
E^3. It may run in place: each sample reads every component before it writes.- Parameters:
- form{‘real’, ‘conserving’, ‘harmonic’}
The product form (
dispersive_form()).- n_polint
Components at one node, 1 or 2.
- parallelbool
Whether the node loop is a
prange.- shiftedbool, optional
Whether a constant per row is subtracted from the source as it is read: the zero-frequency bin of a field grid’s bare analytic signal, which
Pi_+excludes and the backend’s analytic family keeps.
- Returns:
- numba.core.dispatcher.Dispatcher
The compiled kernel
(source, target, carrier, offset).
- solprop.backends.numba_cpu.dispersive.mixture_rows(quartics, members, factors, input_scale, output_scale, inputs, outputs)[source]#
Write a symmetric mixture’s filter rows at a stage; return its coefficient.
g_mix = [sum_s f_s K_s / sum_s f_s]^(1/4)over the group’s channels, withK_s = g_s^4andf_sthe channel’seps0 gamma3_0 rhoat the stage: the arithmetic ofsolprop.nonlinear.responses.mixture_filter()– the weighted species sum, the fourth root as two square roots – within its rounding bound (a sum over a few channels, SEP 25), not to its bits: the order of the sum is free and the bin loop may vectorise. At a vacuum (no positive total) the filter is one and the coefficient zero.- Parameters:
- quarticsnumpy.ndarray
K_s = (g_s^2)^2per channel on the leaf’s layout.- membersnumpy.ndarray
The group’s channels, int64.
- factorsnumpy.ndarray
f_sper channel at the stage (at least the members’).- input_scalenumpy.ndarray
What the input multiplier carries besides
g_mix:Pi_+on a field grid’s analytic family, one otherwise.- output_scalenumpy.ndarray
The sector’s
Pi_+and factor, which the output multiplier carries.- inputs, outputsnumpy.ndarray
Written with
g_mix input_scaleandg_mix output_scale.
- Returns:
- float
sum_s f_s, the mixture’s static coefficient at the stage.
- solprop.backends.numba_cpu.dispersive.weigh_spectrum(weights, spectrum)[source]#
Multiply the stage’s spectrum, in place, by the one product’s multiplier.
The lone symmetric response of one species: its windowed product is the stage’s projected polarisation, the stage’s forward transform has made its spectrum, and the output multiplier
gwitheps0 gamma3_0 rhofinishes it there.- Parameters:
- weightsnumpy.ndarray
The stage multipliers (
weigh_terms()); row zero is read.- spectrumnumpy.ndarray
The stage’s polarisation spectrum, modified in place.
- solprop.backends.numba_cpu.dispersive.weigh_terms(rows, channels, multipliers, factors, out)[source]#
Sum the output terms’ multipliers that share a product, at this stage.
out[r] = sum_{t: row_t = r} factors[channel_t] multipliers[t]: each term one product of one channel, its multiplier carrying the sector’sPi_+and factor, weighted by the channel’seps0 gamma3_0 rho. Products shared by channels – the polynomial’sE^3and a group’sT(E, E, D E)– get one multiplier.- Parameters:
- rowsnumpy.ndarray
Each term’s product, int64.
- channelsnumpy.ndarray
Each term’s channel, int64.
- multipliersnumpy.ndarray
Each term’s output multiplier, float64.
- factorsnumpy.ndarray
eps0 gamma3_0 rhoper channel at this stage.- outnumpy.ndarray
Written with one multiplier per product.