Reference
Changelog
The changes of each release since the 2022 package. The build copies this page from
CHANGELOG.md.
Unreleased
Section titled “Unreleased”Changed
Section titled “Changed”README.md, the home page and the Install page givepip install pygspiceas the install command.
2.0.0 - 2026-10-29
Section titled “2.0.0 - 2026-10-29”- Golden tests in
tests/golden/: the outputs of every public function of the 2022 package (commit1fbab9f) on synthetic inputs, and tests that compare an implementation with them. - Continuous integration on Linux with Python 3.11 to 3.15, on macOS and Windows with Python 3.11 and 3.15, and on the oldest supported numpy and scipy.
- A CI job that builds the wheel and the sdist, checks that the sdist holds the sources and tests and that the wheel holds
py.typedand no tests, and runs a smoke test of the wheel in a clean environment. - The package is on PyPI as
pygspice, because PyPI does not allow the namegspice:pip install pygspice. The import name isgspice. Before you install it, runpip uninstall gspiceif you installed GSPICE from GitHub, because both packages write the same files. - A workflow that, for each tag
vX.Y.Z, runs CI, uploads the wheel and the sdist to PyPI once the lead approves, and publishes a GitHub release with them and the CHANGELOG section of X.Y.Z. It checks that the tag names the version of the built package. pyproject.toml:pip install .installs the package. It requires Python 3.11 or later, numpy 1.26 or later and scipy 1.11 or later.gspice.__version__, which is2.0.0.CITATION.cff, the citation metadata for the software and the paper, which the sdist holds.- The license is declared as GPL-3.0-or-later, the GPL v3 of the 2022 code in its “or later” form.
- Type checks with mypy in strict mode for the code outside
gspice.legacy. - A test coverage floor of 90% for the code outside
gspice.legacy. - pre-commit hooks that run ruff.
- Log records on the
gspicelogger: DEBUG lines on the time and size of the work of the main functions, INFO lines on the clipping passes offit, and a WARNING fromfitfor each clipping pass whose threshold exceeds the in-sample ceiling of |z|. The package adds alogging.NullHandlerto the logger, so the application decides where the records go. benchmarks/run.py, which timespixelwise,infill,fit_blocks,repair,fit, the noise models, the 2022gp_interpand the 2022 Schur route againstsolve_pattern, and prints the timings and the setup as JSON.gspice.scale,gspice.unscaleandgspice.snr.scaleinterpolates each spectrum over its bad pixels and scales it to unit mean noise,unscalereturns scaled values and variances to flux units, andsnrgives the median signal-to-noise ratio of each spectrum.gspice.Model, which holds the mean, the covariance and the training count of each S/N bin, andgspice.saveandgspice.load, which write and read a model as one .npz file.gspice.pixelwise, which predicts each pixel of each spectrum from the pixels farther thannguardfrom it, and its resultgspice.Prediction. A model keeps the precision matrix and the guard weights of each bin after their first use.gspice.simulate, which draws synthetic continuum-normalized spectra for tests and examples, and its resultgspice.Simulation. The spectra have absorption lines whose depths follow two latent parameters in two populations, Gaussian noise, and optional cosmic rays, bad columns and shifted blocks. Withgaussian=Trueand no artifacts they are exactly Gaussian.gspice.infill, which replaces chosen pixels of each spectrum by their Gaussian conditional mean given the other pixels, and its resultgspice.Repaired. It reports the replaced and ignored pixels and χ², withcovariance=Truethe conditional covariance of the replaced pixels of each spectrum, and with anumpy.random.Generatorasrnga draw from the conditional distribution inRepaired.draws. Withoutrng, spectra of one S/N bin with the same replaced pixels share one factorization of the precision matrix at the pixels that all of them exclude, also where each spectrum has a few bad pixels of its own;repairandcalibrationtake the same route where their patterns allow it.gspice.fit, which fits a model to training spectra with the iterative outlier clipping of paper Alg. 4, and returns the model and the final training mask.gspice.finite_sample_factor, which gives f_s(N_s, N_k, N_), the mean χ² per predicted pixel of a held-out Gaussian spectrum when a model fitted to N_s spectra predicts N_ pixels from N_k others.- The option
inflateofgspice.pixelwiseandgspice.infill, which multiplies the variances and covariances of a mean-centered, unweighted model by f_s(N_s, N_k, 1). The fieldRepaired.fsholds the factor of each spectrum by whichchi2 / dofis divided, so thatchi2 / (dof * fs)has the mean 1 for held-out Gaussian spectra of such a model, with or withoutinflate. gspice/py.typed, the PEP 561 marker, so that type checkers read the annotations of an installedgspice. The functions ofgspice.legacykeep their 2022 signatures without annotations.gspice.fit_blocks, which computes from spectra without artifacts a χ² threshold for each block of consecutive pixels and each S/N bin (paper section 3.2), and returns the model with them.gspice.Blocksholds the blocks and their thresholds in the new fieldModel.blocks, whichsaveandloadkeep.gspice.noise_audit, which compares the residuals ofgspice.pixelwisewith the reported noise at each pixel. It returns rho, the root mean of (f − f̂)² ivar over the spectra that are good at the pixel; on spectra not used in the fit and away from bad runs longer than nguard, rho² has a mean above 1 even where ivar is right.gspice.calibration, which predicts a run or a random set of n good pixels of each held-out spectrum from its other good pixels, for each of several sizes n, with masks drawn independently of the data, and its resultgspice.Calibration.Calibrationholds χ², n and the finite-sample factor f_s of each prediction, and also takes the results ofinfillandpixelwise. Its methods give the mean of χ²/(n·f_s) and its standard error, the fraction of χ²/f_s above a quantile of the χ² distribution with n degrees of freedom, and the normal scores of a QQ plot, for all spectra or a selection of them. An error of a prediction names the row of its spectrum influx.gspice.repair, which detects and replaces bad pixels with the procedure that the paper validates on simulated spectra: two pixelwise passes, a block pass with the thresholds offit_blocks, an exclusion of the pixels around extended artifacts, and a finalinfill. The second pixelwise pass tests again each pixel that the first one flagged. The fieldRepaired.blocksholds the blocks that the block pass flagged in each spectrum.- The option
rankofgspice.fit, which fits a low-rank model: the covariance C = V diag(values) Vᵀ + diag(d) holds the K leading eigenvectors V and eigenvalues of the sample covariance, and d keeps its diagonal. Where K ≤ npix/80, Lanczos finds them, with a check that falls back to the eigendecomposition of the K pairs and logs it at INFO. The clipping passes use the same rank, so they also run with fewer spectra than pixels.gspice.LowRankholds V, the values and d, andsaveandloadkeep it. A model computes the precision matrix of a low-rank covariance by the Woodbury identity, without forming C, and raisesnumpy.linalg.LinAlgErrorwhere its condition bound κ_W exceeds 1e13.inflateneeds a dense model, andRepaired.fsandCalibration.fsare 1 for a low-rank one. - The option
snr_binsofgspice.fit, which fits a model of several S/N bins: either their number n, with edges at the quantiles that give each bin the same count of the spectra thatfituses before clipping, or the interior edges. Each bin runs the clipping passes and the final fit on its own spectra, so it gets its own mask, mean, covariance and training count, asfitgives for those spectra alone. - The option
noiseofgspice.fit, which fits a noise model in flux units. Its covariance is that of the noise-free flux, from an estimator that is unbiased with per-pixel masks, weights and the ivar of each spectrum, with its negative eigenvalues set to 0; withrank=K, its K leading eigenpairs plus the diagonal of the other eigenpairs, with their negative eigenvalues set to 0. The clipping passes run as withoutnoise. The new fieldModel.noisemarks such a model, andsaveandloadkeep it.pixelwise,infill,repair,fit_blocks,calibrationandnoise_auditcondition each spectrum on that covariance plus its own noise variances, 1/ivar at good pixels and 1/a_s² at bad pixels, with a_s² the mean ivar of the good pixels, and form its precision matrix once per call, or, for a dense noise model in a call that forms no precision matrix for other steps, such asinfillandcalibration, only its Cholesky factor.pixelwiseof a noise model computes each mean from its precision matrix times the spectrum, so an outlier within nguard of a pixel cancels in its mean only to rounding, linearly in the outlier: 9.3e-10 in z at 1e6σ in the review of PR #103. Their infills of a noise model withrank=Kuse the K eigenpairs and the diagonal plus the noise variances instead, at about npix K² flops per pattern, without the precision matrix; they raise where κ_K = 1 + λ_max(Λ^½ Vᵀ D_s⁻¹ V Λ^½) exceeds 1e13, which accepts every C_s that κ_W accepts.noisetakes nocenterorsnr_bins,inflateneeds a model without noise, andRepaired.fsandCalibration.fsare 1 for a noise model.
Changed
Section titled “Changed”- The documentation site is built with Astro Starlight instead of Sphinx. Its API pages come from the docstrings, as before. Page addresses end in
/instead of.html, for examplehttps://joshspeagle.github.io/GSPICE/start/install/, and the anchors of the API and glossary entries are unchanged. - The documentation site has the visual design of
docs/brand.md: dark and light themes on a navy header, the Archivo and IBM Plex Mono fonts, square corners, a new logo and favicon drawn in pixel cells, output blocks joined to their code, and framed figures. - The documentation site opens with a Getting started group: Is GSPICE right for my data?, Install, Quickstart, Prepare your spectra, and What you get back. The home page has an interactive figure that predicts the pixels under a block that you drag over a spectrum. The installation and quickstart pages moved to
start/install/andstart/quickstart/, and their old addresses redirect there. - The documentation site has a Guides group of seven task pages on simulated spectra, which need no download: Fill known bad pixels, Find outliers, Find and repair artifacts, Choose a model, Check your model, Save and load, and Troubleshooting. They replace the tutorials on pixelwise prediction, infilling, repair, calibration, the low-rank and noise-aware models, and saving, whose old addresses redirect to them. The glossary adds artifact, clean spectrum, flagged pixel, outlier, scaled model and shifted segment.
- The docstrings, and so the API pages, use the terms of the glossary and show their equations as rendered maths.
README.mdhas a figure of the quickstart, an example, badges and links to the pages of the site. - The documentation site has a Concepts group of eight pages in place of the six Background pages: The model, Fitting, Prediction, Held-out uncertainty, Detection, Diagnostics, Numerics and Glossary. They hold the ideas, the equations and their limits, with new figures drawn from
gspice.simulatebydocs/scripts/concepts.py, and no code to run. Held-out uncertainty and Numerics are new, Numerics with benchmark times. The glossary adds a symbol table and terms such as conditional mean, covariance, ivar and S/N, and keeps its anchors. The old Background addresses redirect to the new pages. - The documentation site has interactive explainers: on The model, a covariance explorer, a two-pixel explainer and the full mask explainer; in Find outliers, the guard window; and in Find and repair artifacts, the steps of
repair. A link to a glossary entry shows the entry in a card on hover or keyboard focus. - The documentation site replaces its two tutorials with four pages. A survey end to end runs GSPICE on the 10000 simulated LAMOST-like spectra of
docs/scripts/spectra.py, with figures in the style of the site. Migrate from the 2022 package gives in one table the new function, the outputs and the units for each 2022 function, then the deprecation timeline. Cite GSPICE gives the paper,CITATION.cffand the data of the worked example, and the Changelog page shows this file. The old tutorial addresses redirect to the new pages.
Deprecated
Section titled “Deprecated”- An access to one of the 11 functions of the 2022 package through
gspice, asgspice.<name>orfrom gspice import <name>, emits aDeprecationWarning. It names the replacement and the removal of the name in 3.0.0. The access returnsgspice.legacy.<name>, which keeps the 2022 results, except for the behavior changes below, and does not warn. Type checkers reportgspice.<name>as an unknown attribute. gspice.standard_scale: usegspice.scale.gspice.covarandgspice.covar_iter_mask: usegspice.fit.gspice.get_chimaskandgspice.gp_interp: usegspice.pixelwise.gspice.gaussian_estimate: usegspice.infill.gspice.cholesky_inv,gspice.submatrix_inv,gspice.submatrix_inv_mult,gspice.maskinterp1andgspice.maskinterp: usegspice.legacy.<name>.
Removed
Section titled “Removed”- The IDL code in
pro/and the Julia code injulia/. - The notebooks and notes of 2021 and 2022 in
notebooks/andnotes/, and the 2021 draft of the paper inpaper/. setup.py, whichpyproject.tomlreplaces.- The egg in
dist/and the 2022 tests ingspice/tests/, which could not run and which the golden tests replace. - The import of astropy, which the 2022 code did not use.
- The 10 prints of
gspice.legacy, which reported progress, timings, shapes and notices such as “imask does not remove any rows or columns.”covar(checkmean=True)still prints its check of the column means. - The LAPACK message that
cholesky_invprinted for a 0 × 0 matrix.
Behavior changes
Section titled “Behavior changes”- The second pixelwise pass of
repairtests each pixel again against its prediction with the replaced pixels infilled. A pixel abovensigma_replacein the first pass stays flagged only while it exceedsnsigma_ignorethere, and the exclusion of step 4 counts the new sets. The paper and r26gp_repairkeep every such pixel, which replaced artifact-free pixels whose first-pass prediction a strong artifact had disturbed; design decision A30 gives the measurements. - Step 5 of
repairon a dense noise model agrees withinfillto rounding, not bit for bit, where steps 1 to 4 form the precision matrix of the spectrum:infillthen uses its Cholesky factor. In the test of the steps, flux differed by 1.4e-14σ. - The block χ² of
fit_blocksandrepairreads the guard pixels of each block, which the earlier unreleased code never read. An outlier in a guard cancels only to rounding, so χ²_A changes linearly with its size: by up to 6e-8 at κ 1e8 and 1e4σ, and by 8.6 at κ 1e10 and 1e10σ (measured in PR #99). - The golden tests compare 11 outputs at a relative tolerance looser than 1e-10: 1e-9 for 7 outputs and 1e-8 for 4. These outputs come from ill-conditioned inputs, on which the 2022 results differ from a direct Cholesky solve by up to 7.6e-10.
- The 2022 modules
gspice.specUtils,gspice.matrixUtilsandgspice.djs_maskinterpmoved togspice.legacy.specUtils,gspice.legacy.matrixUtilsandgspice.legacy.djs_maskinterp, and the unusedgspice.gspiceMainis deleted. Imports of the old module paths fail, and pickles that name the old paths no longer load. To migrate, insertlegacy.into the module path, for examplefrom gspice.legacy.specUtils import standard_scale. gspice.__all__holds the new public names andlegacy, sofrom gspice import *binds them. It no longer binds the 11 functions and the 3 modules of the 2022 package. It can shadow names such asloadandsaveof an earlierfrom numpy import *. To migrate, usefrom gspice.legacy import *, which binds the same 14 names as the 2022 star import, without a warning.- The 2022 asserts of
gspice.legacyraise_LegacyError, a subclass ofAssertionErrorandValueError, with a message that names the argument. Anexcept ValueErrorclause now catches them, and the checks also run underpython -O. gp_interpacceptsirangeas a list, a tuple or an array of at least two integers of any integer type, and raises unless 0 <=irange[0]<=irange[1]<= npix - 1. The 2022 code rejected a 2-elementirangeand a uint64 array, and for a negative start withirange[1]= npix - 1 it returned leading columns of zeros. A numpy bool inirange[0]orirange[1]raisesTypeError, and spectra without pixels raise.gp_interpraises for a negativenguard, where the 2022 code predicted each pixel from itself. Under numpy 2, fixed-width numpy integers now work fornguard, where the 2022 code gave wrong windows or raised OverflowError or TypeError. A numpy bool or an array of one or more dimensions raisesTypeErrorfornguard.- When
bruteforceis False andimaskkeeps every row,submatrix_invandsubmatrix_inv_multreturn a copy ofMinvandMinvY. The 2022 code returned the argument itself, so a later change to the result changed the caller’s array. gp_interpwithbruteforce=Truereturnspredandpredvarin the layout of the default path, where the 2022 code raised ValueError. The golden tests compare them with the 2022 outputs of the default path at the tolerances of those outputs, which are 1e-8 for 4 of them.- In an array with one good entry,
maskinterp1sets a non-finite value at a bad entry to the good value, and so domaskinterpanddjs_maskinterp.average_bilinear, which call it. The 2022 code gave NaN there.standard_scalespread that NaN to a whole column of Dvec, andget_chimaskandcovar_iter_maskthen raised ValueError. standard_scaleignores a non-finite ivar at a bad pixel. The 2022 code made the refscale of that spectrum NaN, and with it every value of Dvec and refmean, soget_chimaskandcovar_iter_maskraised ValueError.standard_scaleandget_chimaskraise_LegacyErrorfor a spectrum without a good pixel, and name its row.covar_iter_maskraises it for a kept spectrum that its mask or the clipping masks completely, and names its row influx. The 2022 code returned NaN or raised another error.covar_iter_maskacceptsmask=None, which marks the pixels where ivar == 0, also formaxbadpix. The 2022 code raised numpy’s AxisError.scaleraises ValueError for a non-finite flux at a good pixel, a non-finite or negative ivar at a pixel not inmask, and zero spectra withoutrefmean. For such inputs, the 2022standard_scalereturned NaN or a wrong refscale.scalecounts a pixel with ivar = 0 as bad also wheremaskis given. It interpolates the flux of such a pixel and leaves the pixel out of refscale. The 2022standard_scalewith a mask used that flux and counted the 0 in the mean ivar.scalecasts flux to float64 before it interpolates. The 2022standard_scaleinterpolated in the dtype of the flux, so it truncated the interpolated values of integer flux and rounded those of float32 flux to float32.scalechecks and computes a long double flux or ivar as float64. It raises ValueError for a long double beyond the float64 range at a good pixel, and an ivar that rounds to 0 in float64 marks a bad pixel. The 2022standard_scalecomputed in long double and returned a finite result for such values.scaleandsnrraise ValueError for a spectrum without a good pixel, and name its row. For such a spectrum, the 2022standard_scalereturned NaN in every value of Dvec and refmean.pixelwisecomputes the conditional variances through the precision matrix, where the 2022gp_interpused the Schur updates of a precomputed inverse. On the golden data set D3, whose covariance has the condition number 8.6e4, the 2022 predvar differs from a 60-digit reference by up to 7.6e-10 at nguard 5 and 2.7e-10 at nguard 20, relative.pixelwisediffers by at most 1.7e-13 and 8.7e-14.infillcomputes the conditional covariance through the precision matrix, where the 2022gaussian_estimateused the Schur updates of a precomputed inverse. On D3, the 2022 predcovar differs from a 60-digit reference by up to 3.0e-10 relative on the diagonal, andinfillby at most 8.8e-14.fitmasks the flags of every clipping pass, as paper Alg. 4 does. The 2022covar_iter_maskmasked only the flags of the last two passes;fit(cumulative=False)keeps that rule. On the golden data sets D1 to D4, the final mask of the defaultfitdiffers from the 2022 mask in 157, 152, 106 and 8 entries.fitflags a pixel whose |z| exceeds the threshold. The 2022get_chimaskalso flagged a pixel whose |z| equals it.fitdrops a spectrum that has no good pixel, or that a clipping mask covers completely. For such a spectrum, the 2022covar_iter_maskreturned a covariance of NaN or raised an error.fitneeds at least npix + 1 spectra for a mean-centered model, and a good pixel in one of them at every pixel, and raises ValueError otherwise. The 2022covar_iter_maskaccepted npix spectra, whose covariance is singular, and then raised LinAlgError or flagged most pixels. It filled a pixel that is bad in every spectrum by interpolation, which also makes the covariance singular; on D1 it then raised LinAlgError.fitcounts a pixel with ivar = 0 as bad, also againstmaxbadpix. The 2022covar_iter_maskcounted only the pixels in its mask.