Concepts
Numerics
GSPICE computes Gaussian conditional predictions from the precision matrix , avoiding full-spectrum covariance factorizations on each pixel mask. The sections below state the linear algebra behind Prediction and Detection, the operation counts, the numerical condition checks, and measured runtimes.
The precision form
Section titled “The precision form”The conditional distribution of The model needs the inverse of , the covariance of the conditioning pixels. That set changes with every prediction and is nearly all of the pixels, so each factorization would cost about npix³/3 flops. GSPICE uses the precision matrix instead. The density of is proportional to . As a function of , it is proportional to
so
The two forms are equal in exact arithmetic. The precision form reads only the rows of : it factors and multiplies by . Each prediction has its own :
Pixelwise: G is the guard window of pixel i
Infill: G holds the replaced, ignored and bad pixels
Block scan: G is the block and its guard window
- PGG, factored
- PGO, multiplied by xO
- not read
- xG, not read
- xO
The three panels compare pixelwise, infill, and block-scan predictions on 24 pixels. Each row and column of P is one pixel. A prediction reads the rows G of P and the values of x outside G.
The precision form has three advantages:
- Cost. The model computes once for each S/N bin, in npix³ flops, and keeps it. Each pixel set then needs a Cholesky factor of , in flops. is usually small, a guard window or a few bad pixels.
- No values from G. In
gspice.infill(), the mean reads no value in , so an outlier in cannot change it. - Accuracy. A Cholesky solve with a matrix of condition number has a relative error of order , with (Higham, 2002). The 2022 code instead updates a precomputed inverse (paper Appendix A). Where the guard window is coupled to the conditioning pixels, that update subtracts nearly equal terms, and its error has no such bound. The legacy interface keeps the 2022 arithmetic.
The tests compare each fast path with a direct solve, at condition numbers up to 10⁸.
A call pays for once per model and bin, then a cost per pixel set and a cost per
spectrum. Spectra of one S/N bin with equal pixel sets , and
form a pattern. The spectra of a pattern
in one chunk of max(1, 2**24 // npix) rows share one factorization. An infill is
therefore fast where many spectra share their masks. With the width of a guard window, the main costs in flops are:
| Work | Once per model and bin | Per pixel set | Per spectrum |
|---|---|---|---|
| of a dense covariance | npix³ | ||
pixelwise | npix ( + 2 npix ) for the guard weights | 2 npix² | |
infill | per pattern and chunk | 2 (npix + ) | |
block scan of fit_blocks and repair | per block | 2 npix² + | |
fit, per clipping pass | npix³, and the guard weights | npix² for the covariance, 2 npix² for z |
The model keeps and the guard weights of the latest nguard of each bin, so the
“once” column is paid at the first use. pixelwise predicts every pixel by one
fixed linear function of . It therefore applies the weights to all the spectra of a
bin as one matrix product. The docstring of each function states its full cost,
and Memory lists the large arrays.
A noise-aware model has its own covariance for each spectrum. Its pixelwise
therefore pays the “once” column for every spectrum: about npix³ flops with a dense
covariance, or npix² with rank=K. Its infill forms no
(Other routes), but pays a factorization per spectrum.
Condition checks
Section titled “Condition checks”A covariance can be positive definite and still too close to singular for a reliable
. Every covariance therefore passes a check before its first use, and
numpy.linalg.LinAlgError reports a failure:
- A dense covariance fails where its Cholesky factorization fails, or where its reciprocal condition number is below 10⁻¹³ after scaling the covariance to unit diagonal. LAPACK estimates that number in the 1-norm from the factor.
- A
gspice.LowRankcovariance fails where the bound of its docstring exceeds 10¹³. bounds the condition number of the covariance scaled to unit diagonal.
A default model near the minimum of training spectra, close to npix, can fail the check. Troubleshooting gives the remedies.
Other routes
Section titled “Other routes”Four computations do not factor of a dense :
-
A low-rank model, , computes by the Woodbury identity (Woodbury, 1950), in npix² flops, without forming :
-
A dense noise-aware model has its own covariance for each spectrum.
infillsolves with the Cholesky factor of and forms no . -
A low-rank noise-aware model forms no either. Its
infillsolves a system of size for each spectrum. -
In a dense model without noise, some infills compute the conditional mean alone: those inside
gspice.repair()andgspice.fit_blocks(). They factor where holds more than half the pixels. That factor is then the smaller one.
Benchmarks
Section titled “Benchmarks”The times below come from one run of benchmarks/run.py, which reports the minimum
and the median of 5 timed repeats after a warm-up run. The table gives the minimum. The
covariance of every case is a squared-exponential kernel of length 15 pixels, plus a
rank-30 term, plus the identity, at npix 2400, with nguard=20.
| Case | Spectra | Minimum | Per spectrum, computed |
|---|---|---|---|
| of the covariance | 0.314 s | ||
| Guard weights | 0.075 s | ||
pixelwise, after and the weights | 100,000 | 36.3 s | 36.3 s / 100,000 = 0.36 ms |
infill, 30 pixels, each spectrum its own pattern | 1000 | 0.218 s | 0.218 s / 1000 = 0.22 ms |
infill, 30 pixels, one pattern | 6990 | 0.465 s | 0.465 s / 6990 = 0.067 ms |
infill, 1200 pixels, each spectrum its own pattern | 20 | 1.18 s | 1.18 s / 20 = 59 ms |
| Block scan, 31 blocks | 4000 | 1.27 s | 1.27 s / 4000 = 0.32 ms |
fit_blocks | 4000 | 1.87 s | 1.87 s / 4000 = 0.47 ms |
repair, Gaussian spectra | 2000 | 1.93 s | 1.93 s / 2000 = 0.96 ms |
repair, gspice.simulate with artifacts=0.4 | 500 | 2.86 s | 2.86 s / 500 = 5.7 ms |
fit, three clipping passes | 20,000 | 26.9 s | |
Noise-aware pixelwise, dense | 8 | 3.56 s | 3.56 s / 8 = 0.45 s |
Noise-aware infill, dense, 30 pixels | 8 | 1.50 s | 1.50 s / 8 = 0.19 s |
Noise-aware pixelwise, rank=50 | 8 | 0.484 s | 0.484 s / 8 = 61 ms |
Noise-aware infill, rank=50, 30 pixels | 8 | 0.0151 s | 0.0151 s / 8 = 1.9 ms |
Two cases compare with the 2022 arithmetic:
- The 2022
gp_interptook 18.5 s for 4000 spectra.pixelwisetook 1.31 s for the same work, and the guard weights included. - The 2022 update of a precomputed inverse took 1.45 s for 20 pixel sets of 190 pixels, one spectrum each, with the covariance. The precision form took 0.039 s.
The setup of the run:
- Commit
10350faofmain, with no local changes tosrc/gspiceorbenchmarks/run.py. - Intel Xeon Processor @ 2.10 GHz, 4 cores, 16 GB, in a cloud machine. The load average
over one minute was 0.49 before the run and 1.00 after it, as
benchmarks/run.pyrecords. OPENBLAS_NUM_THREADS=1.- Python 3.13.17, numpy 2.5.4 on OpenBLAS 0.3.34, scipy 1.18.1 on OpenBLAS 0.3.31.dev, gspice 2.0.0.
Times from another machine or another thread setting are not comparable with these.
precision matrix
The inverse of the covariance, . GSPICE computes the conditional distribution from its blocks and .
pattern
The pixel sets , and of an infill. Spectra of one S/N bin with one pattern share one factorization within each chunk of spectra.
low-rank model
A model whose covariance is : components and a
diagonal (fit(rank=K), gspice.LowRank).