sft_wick.evaluate — Numerical Evaluation Pipeline¶
Numerical evaluation pipeline for DiagramTerm objects.
Provides the 4-step workflow: 1. Coupling coefficients — handled by DiagramTerm.evaluate_coupling() (existing) 2. Spatial structure analysis — analyze_spatial() 3. Propagator evaluation — PropagatorModel + PropagatorCache 4. Contraction & integration — DiagramIntegrand
- class sft_wick.evaluate.SpatialStructure(direction_groups, direction_map, time_orderings, r_propagators, c_propagators, time_integration_vars, direction_integration_vars, external_points, equal_time_aliases=(), r_absorbed_pairs=())[source]¶
Bases:
objectResult of analyzing R-propagator connectivity and time orderings.
R propagators of the form
δ(n−n') Θ(t−t') R_time(t,t')identify directions along R-chains and impose causal time orderings.- direction_groups: tuple[frozenset[str], ...]¶
Groups of spatial points connected by R propagators (share direction).
- time_orderings: tuple[tuple[str, str], ...]¶
(earlier_point, later_point)from R causality.R(a, b)withΘ(t_a − t_b)meanst_b ≤ t_a.- Type:
Time ordering pairs
- c_propagators: tuple[tuple[str, str, str | None, str | None], ...]¶
(spatial_left, spatial_right, index_left, index_right).- Type:
C propagators
- time_integration_vars: tuple[str, ...]¶
Time integration variables, topologically sorted (innermost first). For an
equal_timenon-local vertex, only the canonical representative leg appears here; the other (m-1) legs of that vertex are removed and their time values are read through theequal_time_aliasesmap below.
- direction_integration_vars: tuple[str, ...]¶
Surviving direction integration variables (one per R-component that contains only integration points — typically empty).
- equal_time_aliases: tuple[tuple[str, str], ...] = ()¶
Maps non-representative internal spatial labels to their canonical time representative, propagated from
DiagramTerm.equal_time_aliases. The integration loops look up every leg’s time viaequal_time_aliases.get(label, label)so aliased legs share a single integration variable while keeping independent spatial labels. Empty tuple ⇒ no aliasing (original cross-spacetime cumulant behaviour).
- r_absorbed_pairs: tuple[tuple[str, str], ...] = ()¶
(partner_label, leg_label)pairs identifying R-propagators whose factor has been absorbed into an upstreamNonLocalVertex(already_R_contracted=True)callable. Propagated straight fromDiagramTerm.r_absorbed_pairs. The integrand R-product loop iterates over this set to skip the matching propagators (so the factor becomes 1 rather thanR(t, t) = 0under Itô after the leg time aliases onto the partner’s). The leg’s time / direction collapse onto the partner’s via the accompanyingequal_time_aliasesentries.
- sft_wick.evaluate.analyze_spatial(dt)[source]¶
Analyze a DiagramTerm’s propagator topology.
Determines direction identification groups from R-propagator connectivity, time ordering constraints from R causality, and which integration variables survive after δ-function elimination.
- Parameters:
dt (DiagramTerm)
- Return type:
- class sft_wick.evaluate.PropagatorModel(R_time, kappa2, n_components, iso_R=True, diag_C=False, t_min=0.0, sigma2=None)[source]¶
Bases:
objectPhysical propagator functions for numerical evaluation.
The R propagator has the form:
R_{ab}(n,t; n',t') = δ(n−n') Θ(t−t') R_time(t,t') δ_{ab} [iso_R] R_{ab}(n,t; n',t') = δ(n−n') Θ(t−t') R_time_ab(t,t') [general]The C propagator is computed from R and the 2-point cumulant κ^(2):
C_{ab}(n₁,t₁; n₂,t₂) = ∫∫ R_time(t₁,λ') κ_{ab}(n₁,λ'; n₂,λ'') R_time(t₂,λ'') dλ' dλ''- Variables:
R_time (Callable) – Callable
(t_left, t_right) → scalarwheniso_R=True, or(t_left, t_right) → (N, N) arrayotherwise.kappa2 (Callable) – Callable
(n1, t1, n2, t2) → (N, N) array. The 2-point cumulant.n_components (int) – Number of field components.
iso_R (bool) – Whether R is isotropic (proportional to identity).
diag_C (bool) – Whether C is diagonal in component indices.
t_min (float) – Lower bound for the λ integrals in C computation.
- Parameters:
- class sft_wick.evaluate.PropagatorCache(model, quad_opts=None, homogeneity='translation', interp_method='linear', c_method='dblquad', n_gauss=20)[source]¶
Bases:
objectEvaluates and caches propagator values.
Computes C via double integration of
R · κ · Rand caches results to avoid redundant quadrature.- Parameters:
- R_time(t_left, t_right)[source]¶
Evaluate R_time(t_left, t_right).
Returns scalar if
model.iso_R=True, else(N, N)array. The Θ function is NOT enforced here — the integration domain handles causality.
- R_product(r_pairs, times)[source]¶
Product of R_time over all R propagator pairs.
Only valid when
model.iso_R=True(R is scalar).
- C_value(n1, t1, n2, t2)[source]¶
Compute the full C matrix
C_{ab}(n1, t1; n2, t2).If
precompute_C_table()has been called andt1,t2are within the table range, uses fast spline interpolation. Otherwise falls back toscipy.integrate.dblquad:C_{ab} = ∫_{t_min}^{t1} dλ' ∫_{t_min}^{t2} dλ'' R_time(t1,λ') κ_{ab}(n1,λ'; n2,λ'') R_time(t2,λ'')
- C_diagonal(n, t1, n_prime=None, t2=None)[source]¶
Return diagonal
[C_{00}, C_{11}, …]as a 1D array.If
n_primeandt2are None, uses equal-pointC(n,t; n,t). Uses spline table when available for fast lookup.
- precompute_C_table(t_max, n_grid=100, direction=0)[source]¶
Pre-compute C on a grid and build spline interpolators.
For
iso_R + diag_Cwith direction-independentkappa2,C_{aa}(t1, t2)depends only on(t1, t2). This method evaluates C on ann_grid × n_gridgrid viadblquad, then builds aRectBivariateSplinefor each diagonal component.After calling this,
C_value()andC_diagonal()use fast spline interpolation instead ofdblquad.
- C_diagonal_batch(t1, t2)[source]¶
Evaluate diagonal C at arrays of time pairs.
Requires
precompute_C_table()to have been called.- Parameters:
t1 (ndarray) – Array of shape
(n,)— first time coordinates.t2 (ndarray) – Array of shape
(n,)— second time coordinates.
- Returns:
Array of shape
(n, N)whereNis the number of field components.result[s, a]isC_{aa}(t1[s], t2[s]).- Return type:
ndarray
- R_time_batch(t1, t2)[source]¶
Evaluate R_time at arrays of time pairs (iso_R only).
- Parameters:
t1 (ndarray) – Array of shape
(n,)— left times.t2 (ndarray) – Array of shape
(n,)— right times.
- Returns:
Array of shape
(n,). R(t1, t2) = Θ(t1−t2) × R_time(t1, t2).- Return type:
ndarray
- precompute_C_table_translation(t_max, n_grid_t=60, r_max=None, n_grid_r=None, n_jobs=1, c_method='dblquad', n_gauss=20)[source]¶
Enable spatial support under the translation-invariance assumption:
C(t1, t2; x1, x2)depends only on|x1 − x2|.Two operating modes depending on whether
r_maxandn_grid_rare supplied:- Lazy mode (default, recommended for fixed-point moments)
r_max=Noneandn_grid_r=None. No r-grid is pre-computed. WhenC_at_batch()encounters a newr = |x1−x2|value it builds and caches a 2-D(t1, t2)spline on demand. For a moment calculation at fixed external points(x_1, …, x_n)there are at mostO(n²)distinct r-values, so lazy mode saves an enormous amount of work vs a densely-sampled r-grid — particularly when the underlying_C_value_directisscipy.integrate.dblquad(expensive).- Full-grid mode (right for sweeping r curves)
Both
r_maxandn_grid_rprovided. A 3-D(t1, t2, r)spline is built up-front over the full range, giving O(1) evaluation per query — faster oncen_distinct_r >> n_grid_r. Appropriate when the user will plotCor a derived observable as a function of r over a continuous range.
- Parameters:
t_max (float) – upper bound of the (t1, t2) time grid.
n_grid_t (int) – grid size along each time axis.
r_max (float | None) – upper bound of the r =
|x1-x2|grid (full-grid mode).None⇒ lazy mode.n_grid_r (int | None) – grid size along r (full-grid mode).
None⇒ lazy mode.n_jobs (int) – parallel workers for grid-point
_C_value_directevaluations.1serial,-1all cores viajoblib(lokybackend). Applies in both full-grid mode (fans out across the 3-D(t1, t2, r)grid) and lazy mode (fans out across each on-demand 2-D(t1, t2)build; each new r pays a ~1 s worker startup cost).c_method (str) –
Quadrature method passed to
_C_value_direct()for every grid-point evaluation (full-grid mode only – lazy mode will use the cache instance’sc_methoddefault).'dblquad'(default): backwards-compatible adaptive quadrature.'gauss_legendre': tensor-product GL with diagonal-aware sub-region splitting. 10-1000× faster on smooth κ² with a single|λ1−λ2|cusp. See_C_value_direct_gl().
n_gauss (int) – Per-dimension GL node count when
c_method='gauss_legendre'. Default20is enough for machine precision on demo1/demo2 OU kernels.
- Return type:
None
Requires
self.homogeneity == 'translation'.Side effect: sets either
_c_translation_splines(full) or_lazy_translation(lazy).
- precompute_C_table_rotation(t_max, n_grid_t=60, n_grid_cos=None, n_jobs=1, c_method='dblquad', n_gauss=20)[source]¶
Enable spatial support under the rotation-invariance assumption:
C(t1, t2; x1, x2)depends only onx1 · x2.Appropriate when
xis a unit direction vector (e.g. on the sphere). The effective spatial parameter iscos θ ∈ [−1, 1].Two modes, analogous to
precompute_C_table_translation():Lazy (
n_grid_cos=None): 2-D(t1, t2)splines built on demand per distinctcos θvalue. Best when only a fewcos θvalues arise (e.g. a fixed-set moment calculation).Full-grid (
n_grid_cosinteger): 3-D(t1, t2, cos)spline over the wholecos ∈ [−1, 1]range. Best for angular-correlation sweeps.
Under the hood, the reference evaluation at a specific
cos θuses two representative unit vectorse_1 = (1, 0, …)ande_2chosen so thate_1 · e_2 = cos θ(a 2-D rotation ofe_1).- Parameters:
n_jobs (int) – parallel workers for grid-point
_C_value_directevaluations; applies in both full-grid (fans out across the 3-D(t1, t2, cos)grid) and lazy mode (fans out across each on-demand 2-D build).1serial,-1all cores viajoblib.c_method (str) –
'dblquad'(default) or'gauss_legendre'– forwarded to_C_value_direct()for every full-grid point. Seeprecompute_C_table_translation()for details.n_gauss (int) – Per-dim GL node count for
c_method='gauss_legendre'; default20.t_max (float)
n_grid_t (int)
n_grid_cos (int | None)
- Return type:
None
Requires
self.homogeneity == 'rotation'.
- precompute_C_table_general(t_max, n_grid_t=40, x_max=None, n_grid_x=None, n_jobs=1, c_method='dblquad', n_gauss=20)[source]¶
Pre-compute C with no spatial symmetry assumed.
Two modes:
Lazy (
x_max=Noneorn_grid_x=None): 2-D(t1, t2)splines built on demand per distinct(x1, x2)pair. Best for fixed-point moments where only a handful of pairs occur.Full-grid: 4-D
(t1, t2, x1, x2)interpolator over the pre-allocated grid. Build cost isn_grid_t² · n_grid_x²independent_C_value_directcalls — strongly considern_jobs=-1.
- Parameters:
n_jobs (int) – parallel workers for
_C_value_directevaluations; applies in both full-grid (fans out across the 4-D grid) and lazy mode (fans out across each on-demand 2-D build).1serial,-1all cores viajoblib.c_method (str) –
'dblquad'(default) or'gauss_legendre'– forwarded to_C_value_direct()for every full-grid point. Seeprecompute_C_table_translation()for details.n_gauss (int) – Per-dim GL node count for
c_method='gauss_legendre'; default20.t_max (float)
n_grid_t (int)
x_max (float | None)
n_grid_x (int | None)
- Return type:
None
Requires
self.homogeneity == 'general'.
- C_at_batch(t1, t2, x1, x2)[source]¶
Batch-evaluate C at arbitrary
(t1, t2, x1, x2)arrays.Dispatches on
homogeneity:'translation': C depends only on|x1 − x2|. Uses the 3-D(t1, t2, r)full-grid spline if built viaprecompute_C_table_translation(r_max=..., n_grid_r=...); otherwise uses a lazy per-r 2-D spline cache; otherwise (legacy 2-D spline only) falls back toC_diagonal_batch()— x is ignored in that case.'rotation': C depends only onx1 · x2 / (|x1| |x2|). Uses the 3-D(t1, t2, cos)spline or per-cos lazy cache.'general': 4-D(t1, t2, x1, x2)spline, or a lazy per-pair 2-D cache.
- Parameters:
t1 (ndarray) – Time arrays of shape
(n,).t2 (ndarray) – Time arrays of shape
(n,).x1 (ndarray | float) – Spatial coordinates. Scalar or array of shape
(n,)(broadcast if scalar). # TODO(d-dim): support (n, d).x2 (ndarray | float) – Spatial coordinates. Scalar or array of shape
(n,)(broadcast if scalar). # TODO(d-dim): support (n, d).
- Returns:
Array of shape
(n, N)— diagonal C values per component.- Return type:
ndarray
- class sft_wick.evaluate.DynamicCouplingPromise(diagram_term, static_values, dynamic_values, spatial_args_by_name, fixed_indices)[source]¶
Bases:
objectDefers coupling-tensor materialisation to QMC-sample time.
When any entry in
coupling_valuesis a callable (e.g. a spacetime-dependent non-local vertex such as demo2’sκ^{(3)}(x₁,t₁; x₂,t₂; x₃,t₃)), the usual pre-QMCDiagramTerm.evaluate_coupling()path doesn’t apply — the callable’s output changes with each sample’s QMC time coordinates. This class packages everything needed for the per-sample path:the list of static (
ndarray) symbol values,the list of dynamic (callable) symbol values,
for each dynamic symbol, the ψ-leg spatial-label tuple extracted from the
DiagramTerm’scoupling_sumso we can look up each leg’s time and position per sample.
The per-sample evaluator
evaluate_at()materialises the dynamic tensors using the sample’s(times, positions)and then delegates to the static-pathDiagramTerm.evaluate_coupling()with a fully-numericcoupling_valuesdict — so the contraction code stays the same.- Parameters:
- dynamic_values: dict¶
Dynamic coupling values — mapping
name -> callable(n_list, t_list)returning anndarray.
- spatial_args_by_name: dict¶
Per-dynamic-symbol tuple of ψ-leg spatial labels, as they appear in the diagram’s
coupling_sum.
- evaluate_at(times, positions)[source]¶
Materialise the dynamic callables at this sample’s
(times, positions)and return the per-samplecoupling_array.
- evaluate_at_batch(label_t, label_x, n_samples)[source]¶
Vectorised batch evaluator – returns a
(n_samples,)complex array of the contracted scalar coupling per sample.label_tandlabel_xmap every spatial label appearing in any dynamic symbol’s legs to a(n_samples,)time array / position respectively. Eachlabel_xentry may be:a scalar (the historical / 1-D translation case) — produces a per-leg broadcast of shape
(n_samples,);a
(d,)vector (e.g. a 3-D unit vector underhomogeneity='rotation') — produces a per-leg broadcast of shape(n_samples, d).
The user callable then sees per-leg slices of shape
(m,)or(m, d)respectively (or, in the vectorised contract below,(m, n_samples)/(m, n_samples, d)). All legs of one symbol must have the same position shape; mixing scalar and vector legs raises fromnp.stack.Per-symbol fast path: when the user marks a callable
vectorized=True(e.g. viaNonLocalVertex(coupling_vectorized=True)or by settingfn.vectorized = True), the wrapped callable receives(m_legs, n_samples)arrays — or(m_legs, n_samples, d)for d-dim positions — in a single call and returns a tensor of shape(n_samples,) + (N,)*order. Otherwise we fall back to then_samplesper-sample calls ofevaluate_at().Either way, we still loop in Python over
n_samplesto contract each sample’s tensor down to a scalar viaDiagramTerm.evaluate_coupling(). Vectorising the contraction itself across a sample axis is the deferred prop-indexed-dynamic refactor and is not done here.Raises
NotImplementedErrorif any sample’s contracted coupling is not scalar (the prop-indexed case locked byDC1intests/test_dynamic_coupling.py).
- class sft_wick.evaluate.DiagramIntegrand(diagram_term, spatial, coupling_array, fixed_indices=<factory>, dynamic_coupling=None)[source]¶
Bases:
objectA diagram’s contribution decomposed for numerical integration.
Combines coupling coefficients (Step 1) with spatial analysis (Step 2) for efficient numerical evaluation.
The integrand has the factored form (when
iso_R=True):R_product(times) × Σ_{prop_idx} coeff[idx] × Π_C C_values[idx]- Parameters:
diagram_term (DiagramTerm)
spatial (SpatialStructure)
coupling_array (np.ndarray)
dynamic_coupling (Any)
- diagram_term: DiagramTerm¶
The underlying DiagramTerm.
- spatial: SpatialStructure¶
Spatial analysis result.
- coupling_array: np.ndarray¶
Coupling coefficient array from
evaluate_coupling(). Whendynamic_couplingis set, this is a placeholder zero array and the real coupling value is computed per-sample in the integrator.
- fixed_indices: dict[str, int]¶
Fixed component indices (e.g.
{'a': 0, 'b': 1}). Used by the QMC/GL integrators to resolve C-propagator component indices that are not summation variables.
- dynamic_coupling: Any = None¶
Optional
DynamicCouplingPromisecarrying per-sample coupling materialisation logic for spacetime-dependent (callable) coupling values.None⇒ the fully-static fast path is used (all coupling values were ndarrays).
- evaluate(times, directions, cache)[source]¶
Evaluate the integrand at specific time + direction coordinates.
- Parameters:
times (dict[str, float]) –
{spatial_point: time_value}for ALL spatial points. Aliased (non-representative) legs of equal-time non-local vertices may be absent — their values are filled in from the canonical representatives inside this method.directions (dict[str, Any]) –
{direction_var: value}for independent direction variables (one per R-connected component, keyed by the representative name fromspatial.direction_map).cache (PropagatorCache) – A
PropagatorCachefor propagator evaluation.
- Returns:
Scalar value of the integrand (complex if coupling is complex).
- Return type:
- make_scipy_integrand(external_times, external_directions, cache)[source]¶
Return a callable for
scipy.integrate.nquad.The returned function accepts the integration time variables as positional arguments in the order of
spatial.time_integration_varsand returns the integrand value. External times and directions are baked in.- Parameters:
external_times (dict[str, float]) –
{point_name: time}for external spatial points.external_directions (dict[str, Any]) –
{direction_var: value}for all independent direction variables.cache (PropagatorCache) – A
PropagatorCache.
- Returns:
Callable
f(*time_args) → float.- Return type:
- integration_bounds(external_times, t_min=0.0)[source]¶
Return integration bounds for
scipy.integrate.nquad.Time ordering constraints from R causality translate to variable-dependent bounds. For
nquad, bounds can be callablesf(*earlier_args) → (lo, hi).The variables are ordered as
spatial.time_integration_vars.
- to_latex()[source]¶
Render the decomposed integrand with explicit coordinates.
Shows direction identifications, time-ordered integrals, factored R product, and the component sum over C propagators.
- Return type:
- integrate_moment_qmc(lambda_f, cache, t_min=0.0, direction=0, n_samples=16384, seed=None, positions=None, integrate_over=None)[source]¶
Integrate over all time variables using Quasi-Monte Carlo.
Scalar-loop sibling of
integrate_moment_qmc_vectorized(); see that method’s docstring for theintegrate_overkwarg semantics.
- integrate_moment_qmc_vectorized(lambda_f, cache, t_min=0.0, direction=0, n_samples=16384, seed=None, positions=None, integrate_over=None)[source]¶
Vectorized QMC integration — no Python loop over samples.
Same algorithm as
integrate_moment_qmc()but evaluates all Sobol samples simultaneously using batch propagator lookups. Requiresprecompute_C_tablefor the batch C evaluation.For the
iso_R + diag_Ccase with scalar coupling (no propagator indices), the integrand at each sample is:coupling × prod_R R(t_l, t_r) × prod_C C_diag(t_l, t_r)
where all R and C values are looked up in batch.
- Parameters:
positions (dict[str, float] | None) – Optional mapping
{point_name: spatial_coord}(e.g.{'x': 0.0, 'y': 0.5}). When the cache has a spatial table built (anyhomogeneitykind), the x-coordinate of each propagator endpoint is derived from its direction group’s external-point position and flows throughcache.C_at_batch. Ignored when no spatial table is built (legacy behaviour —C_diagonal_batchhas no x-dependence regardless).integrate_over (Any) –
Controls which external points have their time integrated over
[t_min, lambda_f].None(default, physics observable): all externals held fixed at ``lambda_f`` — this gives the equal-time correlator⟨φ(t_f) φ(t_f)⟩that demo notebooks and MC data compare against."all": all externals integrated — the time-integrated moment⟨∫₀^{t_f}φ(t) dt · ∫₀^{t_f}φ(t') dt'⟩. Useful e.g. for weak-lensing line-of-sight integrals of the Sachs field.Iterable of external point names (subset): those listed are integrated, the rest fixed at
lambda_f. Enables mixed observables such as an integrated source × a detector field.
Internal vertex times are always integrated; their causal parent-map uses the (possibly fixed) external times as upper bounds.
lambda_f (float)
cache (PropagatorCache)
t_min (float)
direction (Any)
n_samples (int)
seed (int | None)
- Returns:
(estimate, std_error)- Return type:
- integrate_moment_gauss_legendre(lambda_f, cache, t_min=0.0, direction=0, n_gauss=8, positions=None, integrate_over=None)[source]¶
Tensor-product Gauss-Legendre quadrature on the causal simplex.
Mirrors
integrate_moment_qmc_vectorized()node-for-node – the SAME causal-simplex mapping (parents → upper bounds → Jacobians) and the SAME vectorised batch path throughPropagatorCache.R_time_batch()andPropagatorCache.C_at_batch()– but replaces the Soboln_samplesquasi-random nodes with the deterministicn_gauss^dtensor product of 1-D Gauss-Legendre nodes (mapped from[-1, 1]to[0, 1]).For smooth integrands (a finite product of exponentials – the typical R/C/κ kernel structure) this gives exponential convergence in n_gauss, vastly outperforming Sobol QMC at modest dimensionality. In particular, demo2’s FK channel (4D smooth integrand on a causal simplex of area
t_f^2 / 2) is dominated at larget_fby a narrow peak band of area~ σ_t/γnear the upper-right corner; Sobol QMC under-resolves it unlessn_samplesis enormous, whilen_gauss=8(4096 nodes for d=4) recovers the notebook’s hand-derived value to ~5 sig figs.Cost trade-off. Tensor-product GL scales as
n_gauss^d– fine ford ≤ 5(demo2 FK), fast atd ≤ 4, painful atd ≥ 7. For high-d integrands prefermethod='qmc_vectorized'with a largen_samplesinstead.- Parameters:
n_gauss (int) – Number of GL nodes per dimension.
n=8is a good default (handles up to degree-15 polynomial exactly).positions (dict[str, float] | None) – Same as
integrate_moment_qmc_vectorized().integrate_over (Any) – Same as
integrate_moment_qmc_vectorized().lambda_f (float)
cache (PropagatorCache)
t_min (float)
direction (Any)
- Returns:
(estimate, 0.0)– GL is deterministic so there is no statistical error to report. The 0.0 mirrors the return shape of the QMC variants for downstream code.- Return type:
- integrate_moment_nquad(lambda_f, cache, t_min=0.0, direction=0, positions=None, integrate_over=None)[source]¶
Integrate over time variables using nested adaptive quadrature.
See
integrate_moment_qmc_vectorized()for theintegrate_overkwarg semantics (external-time partition into integrated vs fixed-at-lambda_f).
- sft_wick.evaluate.integrate_moment(integrand, lambda_f, cache, t_min=0.0, direction=0, method='qmc', n_samples=16384, seed=None, positions=None, integrate_over=None, n_gauss=8)[source]¶
Integrate a diagram’s contribution over all time variables.
Convenience wrapper around
DiagramIntegrand.integrate_moment_qmc(),DiagramIntegrand.integrate_moment_qmc_vectorized(), andDiagramIntegrand.integrate_moment_nquad().Dispatch logic. With
method='qmc'(default) the function auto-selects the fastest QMC path compatible with the supplied cache:If the cache supports batched C evaluation (either
PropagatorCache.precompute_C_tablehas been called, or a custom cache implementsC_diagonal_batchnatively) andcache.model.iso_Ris true →integrate_moment_qmc_vectorized()(~200× faster than the scalar loop on typical workloads).Otherwise →
integrate_moment_qmc()(scalar Python loop).
Users who explicitly want the scalar loop (e.g. for debugging or with a non-batch-capable custom cache) can pass
method='qmc_scalar'.For best performance, call
PropagatorCache.precompute_C_table()before integrating so the vectorised path is selected automatically.- Parameters:
integrand (DiagramIntegrand) – A
DiagramIntegrandbuilt from aDiagramTerm.lambda_f (float) – Upper integration limit for external times.
cache (PropagatorCache) – A
PropagatorCache(or compatible custom cache).t_min (float) – Lower time bound (default 0).
direction (Any) – Direction value for all spatial points.
method (str) –
'qmc'(default, auto-selects vectorised vs scalar),'qmc_scalar'(force scalar loop),'qmc_vectorized'(force vectorised; raises if cache doesn’t support batch), or'nquad'(nested adaptive quadrature).n_samples (int) – Number of Sobol samples (QMC only, should be a power of 2).
seed (int | None) – Random seed for reproducibility (QMC only).
integrate_over (Any)
n_gauss (int)
- Returns:
(estimate, error)tuple.- Return type:
- sft_wick.evaluate.integrate_diagrams(diagram_terms, coupling_values, lambda_f, cache, t_min=0.0, direction=0, method='qmc', n_samples=16384, seed=None, fixed_indices=None, n_jobs=1, positions=None, integrate_over=None, n_gauss=8)[source]¶
Integrate a batch of diagram terms, optionally in parallel.
Builds integrands and evaluates all diagrams, returning the total and per-diagram results. When
n_jobs != 1, diagrams are evaluated in parallel usingjoblib.Note
n_jobsdefaults to1(sequential), matching the L1Expansion.evaluate/Expansion.sweepdefaults. Pass-1to use all CPU cores. Sequential and parallel paths are bit-identical when the QMC seed is fixed.- Parameters:
diagram_terms (list) – List of
DiagramTerm.coupling_values (dict) – Coupling tensor arrays (e.g.
{'F': F_code}).lambda_f (float) – Upper integration limit.
cache (PropagatorCache) – A
PropagatorCache.t_min (float) – Lower time bound (default 0).
direction (Any) – Direction value for all spatial points.
method (str) –
'qmc'or'nquad'.n_samples (int) – Number of Sobol samples (QMC only).
seed (int | None) – Random seed (QMC only).
fixed_indices (dict[str, int] | None) – Optional pinned index values for
build_integrand().n_jobs (int) – Number of parallel jobs.
1= sequential (default, safe under nested-callers),-1= all CPU cores. A built-in guard falls back to sequential forlen(diagram_terms) <= 2to avoid joblib’s ~1 s startup overhead on trivial batches. Requiresjoblibwhenn_jobs != 1.integrate_over (Any)
n_gauss (int)
- Returns:
(total, details)where total is the scalar sum and details is a list of(estimate, error)per diagram.- Return type:
- sft_wick.evaluate.integrate_two_point_qmc(integrands, t_f, positions, cache, t_min=0.0, n_samples=16384, seed=None)[source]¶
Vectorised QMC integration for two-point correlation functions.
Evaluates the sum of diagram integrands at external time t_f with spatial points at the positions given by positions. This is the standard entry point for computing correlators such as
⟨φ(x, t) φ(y, t)⟩where x and y may differ.Unlike
integrate_moment(), which assigns a single direction value to all spatial points, this function supports per-point spatial positions and applies the appropriate spatial factor to each correlation propagator automatically.The spatial factor for a C-propagator connecting points at positions n₁ and n₂ is computed as:
κ²(n₁, t_ref; n₂, t_ref) / κ²(0, t_ref; 0, t_ref)
which is exact for separable kernels (where the factor is time-independent) and a good approximation otherwise.
Requires
PropagatorCache.precompute_C_table()to have been called for the batch C evaluation fast path.- Parameters:
integrands (list[DiagramIntegrand]) – List of
DiagramIntegrandobjects (one per non-vanishing Feynman diagram).t_f (float) – External (observation) time — all external points are set to this time.
positions (dict[str, float]) –
{point_name: position}mapping each spatial point name (e.g."x","y") to a scalar spatial coordinate. Points not in the dict default to 0.cache (PropagatorCache) – A
PropagatorCachewith pre-computed C table.t_min (float) – Lower time bound (default 0).
n_samples (int) – Number of Sobol samples (should be a power of 2).
seed (int | None) – Random seed for the Sobol sequence.
- Returns:
(estimate, std_error)for the summed two-point function.- Return type: