quadsv.kernels.nufft#
Non-uniform FFT (NUFFT) spectra, kernel and spatial tests for irregular data
When data sit on a regular grid (e.g., a rasterized Visium slide),
quadsv.power_spectrum_2d() computes \(|\hat{x}(k)|^2\) with a plain
2D FFT. For data whose spatial coordinates are irregular — e.g.,
imaging-based in situ platforms, Slide-seq, or a Visium slide read straight
from adata.obsm['spatial'] without rasterization — power_spectrum_2d_nufft()
evaluates the type-1 NUFFT
on the same uniform (ny, nx) k-space grid that power_spectrum_2d()
would produce for a rasterized input of the same physical extent, and returns
\(|\hat c|^2\) in the scipy FFT layout (DC at [0, 0]). Anything
downstream — quadsv.comparators.multisample.radial_bin_spectrum(),
quadsv.ComparatorIrregular — works identically.
Classes#
Spatial kernel over irregular 2D coordinates evaluated via NUFFTs. |
Functions#
|
Compute the 2D power spectrum via type-1 NUFFT |
Module Contents#
- class quadsv.kernels.nufft.NUFFTKernel(coords, grid_shape=None, spacing=None, method='matern', unit_scale=1.0, oversample=2.0, eps=1e-06, workers=None, *, centering=True, **kwargs)[source]#
Bases:
quadsv.kernels.base.KernelSpatial kernel over irregular 2D coordinates evaluated via NUFFTs.
Parallels
quadsv.fft.FFTKernel(which requires a regular grid) and implements theKernelinterface so it plugs intoquadsv.statistics.spatial_q_test()/quadsv.statistics.spatial_r_test()the same way.The band-limited approximation of the
n × nirregular-point operator isK ≈ (1/n') · U · diag(λ) · Uᴴ, whereUis then × n'type-2 NUFFT matrix andλ = F(K')is the grid kernel’s spectrum. Under this approximation, Parseval’s identity gives the fast quadratic formxᵀ K x = (1/n') Σ_k λ(k) |x̂(k)|²withx̂ = Uᴴ x(a single type-1 NUFFT). The matrix-vector primitiveKx()uses the companion two-shot NUFFTK z = (1/n') · U · (λ ⊙ Uᴴ z)and backs the Hutchinson-based cumulant estimator (quadsv.statistics._hutchinson_cumulants()) and the bipartite R-test cross matrix.quadsv.spatial_q_test()always uses the k-space Parseval path (xtKx());quadsv.spatial_r_test()dispatches on shape — paired diagonal forM_x == M_yviaxtKy(), full(M_x, M_y)cross matrix viaKx()otherwise. The matmul counterparts (xtKx_matmul()/xtKy_matmul()) are exposed for callers that want to compute the same form directly at thenirregular points — they agree with the spectral path to NUFFT precision (eps) on both regular and irregular coordinates.- Parameters:
coords (np.ndarray) – Spot coordinates of shape
(n, 2)in order(y, x).grid_shape (tuple[int, int], optional) –
(ny, nx)of the internal uniform k-grid. IfNone(default), auto-inferred fromcoords: the grid is sized to cover the bounding box and to resolve the sampling Nyquist set by the median nearest-neighbor distance (fully coordinate-driven, kernel-agnostic). Override only when you know you need a finer or coarser grid.spacing (tuple[float, float], optional) –
(dy, dx)physical spacing per k-grid cell (same unit ascoordsafterunit_scale). IfNone(default), auto-inferred alongsidegrid_shape. When both are supplied, users are responsible for ensuringny · dy,nx · dxcovers the coordinate extent.method (str, default
'matern') – Kernel method forwarded toFFTKernel. One of'gaussian','matern','moran','graph_laplacian','car'.unit_scale (float, default 1.0) – Multiplier applied to
coordsso they share the same unit asspacing(e.g.0.35if coords are in pixels at 0.35 μm/pixel).oversample (float, default 2.0) – Auto-grid oversampling factor above the sampling Nyquist. Used only when
grid_shape/spacingare auto-derived. Larger values give a finer k-grid (more accurate, slower); 2.0 is safe for all tested kernels.eps (float, default 1e-6) – NUFFT tolerance forwarded to finufft.
workers (int, optional) – Forwarded to
scipy.fft(used byKx_grid()) and reserved for future finufft parallelism.Noneuses the SciPy default.**kwargs – Method-specific kernel hyperparameters forwarded to the internal
FFTKernel.bandwidth/nuforgaussian/matern;rhoforcar; plus a coord-awarek_neighborsfor the graph methods (moran/graph_laplacian/car). Note thek_neighbors-to-neighbor_degreemapping differs from that ofFFTKerneldue to an oversampled internal grid.neighbor_degreeis chosen so the internal-grid-ring cutoff matches the median k-th nearest-neighbor distance among the coords — matchingMatrixKernel’s mutual-k-NN graph semantic up to the band-limit of the internal grid. See_resolve_k_neighbors_on_coords()for the mapping detail. Passneighbor_degreedirectly to bypass this and use the raw grid-ring semantic as inFFTKernel.centering (bool)
- Variables:
coords (np.ndarray) – Original
(n, 2)coordinates.n (int) – Number of spots
n.grid_shape (tuple[int, int]) – Internal k-grid shape
(ny, nx).spacing (tuple[float, float]) – Physical spacing per k-grid cell
(dy, dx).method (str) – Kernel method name.
params (dict) – Resolved kernel hyperparameters (snapshot of the internal FFT kernel).
workers (int or None) – scipy.fft worker count used by
Kx_grid().stores_precision (bool) – Always
False— NUFFTKernel never holds ann × nmatrix.
- Kx(z)[source]#
Matrix–vector product
K zat thenirregular coordinates.Implements the band-limited apply
\[K z \;\approx\; \tfrac{1}{n'} \, U \bigl(\lambda \odot U^{\mathsf H} z\bigr),\]evaluated as type-1 NUFFT → elementwise multiply by
λ(k) / n'→ type-2 NUFFT. Output lengthn, same shape asz. Base primitive forxtKx_matmul(),xtKy_matmul(), the Hutchinson cumulant estimator used by Liu’s null approximation, and the bipartite R-test inDetectorIrregular.- Parameters:
z (np.ndarray) –
(n,)or(n, M).- Returns:
Same shape as
z.- Return type:
np.ndarray
- Kx_grid(x)[source]#
Grid-domain companion of
Kx()—(ny, nx)spatial output.Whereas
Kx()returns the length-napply at the irregular coordinates,Kx_grid()returns the apply evaluated on the internal uniform grid. Pipeline: type-1 NUFFT → undo the coordinate-centering phase (needed here because we keep complex coefficients; the square-magnitude and adjoint-round-trip paths ofxtKx()/Kx()absorb it automatically) → multiply byλ(k)→ifftshift→ifft2→ real.- Parameters:
x (np.ndarray) –
(n,)or(n, M).- Returns:
Real
(ny, nx)or(ny, nx, M)in the scipy FFT layout (DC at[0, 0]).- Return type:
np.ndarray
- eigenvalues(k=None, return_full_layout=False)[source]#
Eigenvalues of the
n × nirregular-point operator.Returns the spectrum of the realized operator
K_n(raw) orH K_n H(centered) at the irregular coordinates — not the internal FFT-grid spectrum. Purely matrix-free: no densen × nconstruction.Two paths:
``k`` given — Lanczos top-k. Wrap
Kx()as aLinearOperatorand calleigsh(which='LM')()for the topkeigenvalues by magnitude. Cost:O(k × nufft). Not cached.``k=None`` — Toeplitz-M (analytic). See
_eigvals_toeplitz_M(). Applies whenΛis PSD and the support sizer = #{k : |λ(k)| > 10⁻⁵ · max|λ|}is below_TOEPLITZ_R_THRESHOLD(default 2000). Exact to NUFFTeps; cost dominated byO(r³)eigvalsh on a denser × rreduced matrix. Cached percenteringmode.
Full-spectrum requests that fall outside Toeplitz-M’s reach (indefinite
Λlike Moran, or broad-support kernels like CAR at strong coupling) raiseNotImplementedErrorrather than falling back to an approximate density reconstruction. For Liu’s method, usecompute_null_params(..., method='liu', liu_n_probes=...)()to get cumulant-based Liu directly from \(2m\) matvecs.- Parameters:
k (int, optional) – Top-
keigenvalues by magnitude (Lanczos).Nonereturns the full spectrum via Toeplitz-M.return_full_layout (bool, default False) – Kept for API compatibility with
FFTKernel.eigenvalues(); ignored.
- Returns:
Eigenvalues in descending order. Length
kwhenkis given,nfor the full-spectrum path.- Return type:
np.ndarray
- square_trace()[source]#
trace(K²)(raw) ortrace((HKH)²)(centered).Closed-form
(n²/n'²) · λᵀ Ψ λwith ToeplitzΨ_{k,k'} = |φ(k'-k)|², evaluated as a linear (non-circular) 2D convolution of|φ|²withλvia a doubled-grid FFT inO(n' log n').φ(j) = (1/n) Σ_i exp(-ij·y_i)is evaluated on a(2·ny, 2·nx)mode grid by a separate type-1 NUFFT of the ones vector (see_coord_power_spectrum_doubled()), andλis zero-padded to the same doubled layout so the FFT convolution does not wrap values of|φ|²across then'-period — a silent bias of up to ~45% on broad-spectrum kernels (CAR, graph_laplacian) on the typical oversampled NUFFT grid. On a regular grid where coords coincide with the k-grid,|φ|² = δand the formula collapses to(n/n')² · Σ_k λ(k)². Adjusts by-2·s₂/n + s₁²/n²whencentering=True.Observed band-limit residuals vs. explicit
Kx(I)truth: ≲1e-7on Gaussian / Matern,~0.1 %on CAR,~1 %on graph_laplacian, and~0.05–1.2 %on Moran (indefiniteΛ) — accurate across regular, irregular, and clustered coord layouts.- Return type:
float
- trace()[source]#
trace(K)(raw) ortrace(HKH)(centered).Closed-form
(n/n') · Σ_k λ(k)— exact because the diagonal ofG = UᴴUisnregardless of coord arrangement, sotrace(K_n) = (n/n') · trace(K_grid)is independent of the coord layout. Adjusts by-s₁/nwhencentering=True(s₁ = 𝟏ᵀ K 𝟏via a singleK·𝟏apply inKernel._ones_stats()).- Return type:
float
- xtKx(x)[source]#
Quadratic form
xᵀ K xvia k-space Parseval.Implements the default path:
\[x^T K x \;=\; \frac{1}{n'} \sum_k \lambda(k) \, |\hat x(k)|^{2}\]using one type-1 NUFFT of
xand an elementwise Parseval sum. Only the real power spectrum|x̂|²of shape(ny, nx)is materialized — noifft2, no spatial-grid copy ofx.- Parameters:
x (np.ndarray) –
(n,)for one feature or(n, M)forMfeatures.- Returns:
Scalar if
xis 1-D, shape(M,)otherwise.- Return type:
float or np.ndarray
See also
xtKx_matmulCompute
xᵀ · Kxvia the length-nmatrix product.
- xtKx_matmul(x)[source]#
Quadratic form
xᵀ K xvia direct matmul.Computes
Q_B = xᵀ · self.Kx(x)end-to-end at thenirregular points. Sparse-aware onx(x.multiply(Kx).sum). ~2× the NUFFT work ofxtKx()per feature; agrees with it to NUFFT precision on regular grids and to the torus-BC band (~1–2 %) on irregular ones.- Parameters:
x (np.ndarray or scipy.sparse matrix) –
(n,)or(n, M).- Returns:
Scalar for 1-D input;
(M,)for batched.- Return type:
float or np.ndarray
- xtKy(x, y)[source]#
Bilinear form
xᵀ K yvia cross Parseval.Implements the default path:
\[x^T K y \;=\; \frac{1}{n'} \sum_k \lambda(k) \, \overline{\hat x(k)}\, \hat y(k).\]Paired same-
Mconvention — returns the diagonal ofXᵀ K Y(shape(M,)) for batched inputs, scalar for 1-D inputs. For the bipartite(M_x, M_y)cross matrix usextKy_matmul()(or build it explicitly viaX.T @ self.Kx(Y)).- Parameters:
x (np.ndarray) –
(n,)or(n, M)— must share shape.y (np.ndarray) –
(n,)or(n, M)— must share shape.
- Returns:
Scalar for 1-D inputs;
(M,)for batched.- Return type:
float or np.ndarray
- xtKy_matmul(x, y)[source]#
Bilinear form
xᵀ K yvia direct matmul.Returns the paired
(M,)diagonal ofXᵀ K Y(sparse-aware onx). For the full(M_x, M_y)bipartite cross matrix build it explicitly asX.T @ self.Kx(Y)— that’s whatDetectorIrregulardoes forcompute_rstatwhen the two feature blocks have different widths.- Parameters:
x (np.ndarray or scipy.sparse matrix) –
(n,)or(n, M).y (np.ndarray or scipy.sparse matrix) –
(n,)or(n, M).
- Returns:
Scalar for 1-D inputs;
(M,)for batched.- Return type:
float or np.ndarray
- quadsv.kernels.nufft.power_spectrum_2d_nufft(coords, values, grid_shape, spacing, unit_scale=1.0, eps=1e-06, center_coords=True)[source]#
Compute the 2D power spectrum via type-1 NUFFT
This function computes the power spectrum \(P(k) = |\hat{c}(k)|^2\) of one or more non-uniform spatial signals via a type-1 NUFFT. The output has the same
(ny, nx)layout asquadsv.kernels.fft.power_spectrum_2d()withfft_solver='fft2': DC at[0, 0], Nyquist at[ny/2, nx/2](when dimensions are even).- Parameters:
coords (np.ndarray) – Non-uniform spatial coordinates, shape
(n, 2)in the order(y, x). Values outside the physical domain implied bygrid_shapeandspacingare folded into[-π, π)by finufft.values (np.ndarray) – Signal strengths at each coordinate. Shape
(n,)for a single feature, or(n, M)forMstacked features (e.g., genes) on the same coordinates. Real-valued; promoted to complex internally.grid_shape (tuple[int, int]) –
(ny, nx)of the target uniform k-space grid. Match whatever grid you use for rasterized samples so the two paths produce comparable spectra.spacing (tuple[float, float]) –
(dy, dx)physical spacing per cell of the target grid, in the same unit asunit_scale * coords. Together withgrid_shapethis defines the physical domain extent(ny · dy, nx · dx).unit_scale (float, default 1.0) – Multiplier applied to
coordsbefore scaling into[-π, π). Use this to convert per-sample coordinate units into the common unit ofspacing(e.g., 0.35 ifcoordsare in Visium full-res pixels at 0.35 μm/pixel andspacingis in μm).eps (float, default 1e-6) – NUFFT tolerance forwarded to finufft.
center_coords (bool, default True) – If True, subtract the mean of
coordsbefore scaling — avoids wrapping artefacts when coordinates are stored with an arbitrary origin offset (e.g., Visium pixel coordinates start at a few thousand). Power spectra are translation-invariant so recentering does not change the result.
- Returns:
Power spectrum. Shape
(ny, nx)for 1Dvaluesor(ny, nx, M)for 2Dvalues, with DC at index[0, 0].- Return type:
np.ndarray
- Raises:
ImportError – If
finufftis not installed.ValueError – If input shapes are inconsistent.
Examples
>>> import numpy as np >>> coords = np.random.default_rng(0).uniform(0, 100, size=(500, 2)) >>> vals = np.random.default_rng(1).standard_normal(500) >>> P = power_spectrum_2d_nufft(coords, vals, grid_shape=(32, 32), spacing=(4.0, 4.0)) >>> P.shape (32, 32)