examples: SDSS spectral reconstruction (spPlate, train in place, no reshard)#17
examples: SDSS spectral reconstruction (spPlate, train in place, no reshard)#17emfdavid wants to merge 4 commits into
Conversation
The SDSS/spectra astronomy example reads FITS binary tables (structured
dtypes) end-to-end, which needs two upstream fixes not yet in a release:
- zarr struct-dtype byte-order handling in the bytes codec
(zarr-developers/zarr-python#4142, merged, past v3.2.1)
- VirtualiZarr kerchunk struct-dtype refs for FITS binary tables
(zarr-developers/VirtualiZarr#1037, open on the emfdavid fork)
Pin both to git commits via [tool.uv.sources]. These are dev-environment
only -- uv strips them from the published wheel, so the abstract zarr>=3.0
requirement downstream is unchanged. Drop each pin once a release ships.
Add an `astronomy` optional-dependency extra (virtualizarr/kerchunk/astropy/
s3fs/icechunk) that formalizes the FITS build-time index stack for both the
Hubble and SDSS examples, replacing the ad-hoc `uv run --with ...` install;
point the Hubble README at it.
Verified: uv sync resolves, the struct-dtype endianness repro passes on the
pinned env, and the full suite is green (168 passed / 10 skipped) with the
git-pinned dev-zarr.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eshard) An astroML cross-domain companion to the Hubble example, and the decode- amortization counterpart to it. astroML's spectral-PCA workflow first downloads the raw archive and resamples every spectrum onto a common grid into one spec4000.npz -- the download-and-reshard step insitubatch argues against. examples/sdss indexes a real SDSS spPlate frame on data.sdss.org (HTTPS) as virtual references: one plate holds all ~640 fibers on a common log-wavelength grid as a single (fiber, wave) image, which _rechunk_fibers splits along the fiber axis into contiguous virtual chunks by pure byte arithmetic -- no pixels moved. sample_axis=0 makes each fiber one sample and, because a chunk is many fibers, this is the O(chunks) decode-amortization regime (contrast Hubble's one image per chunk). Verified byte-identical to astropy. The task mirrors astroML's spectral reconstruction: a 1-D conv autoencoder trained by SGD over the streamed mini-batches beats a PCA baseline at the same latent dim, because galaxy spectra are translation-structured (redshift shifts the lines, which a fixed-dim linear basis reconstructs poorly). The offline synthetic default (redshift-shifted spectra) carries the beats-PCA claim and backs tests/test_sdss.py; the real run is a same-pipeline, real-archive demo. Only one plate per store: plates cover slightly different wavelength ranges, so concatenating them would require resampling -- the reshard this example avoids. SDSS spectra also ship as FITS binary tables (structured big-endian dtypes). tests/test_fits_bintable.py validates that path end-to-end through insitubatch (kerchunk -> VirtualiZarr -> Icechunk -> insitubatch) against astropy, and pins the VirtualiZarr fix it needs (zarr-developers/VirtualiZarr#1037) -- released VirtualiZarr raises on the list-valued kerchunk field specs. Endianness is dropped in the VZ/icechunk struct metadata, so consumers reinterpret each field with .view('>f4') at the numpy boundary (the zarr #4142 codec fix does not engage in this path, so it is not pinned). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ve scale)
One spPlate (~640 spectra, ~10 MB) fits in memory, so a single-plate store is a
demo, not a streaming workload. build_store now takes a list of plates and picks
one of two modes, both of which rewrite only the chunk manifest (no pixels moved):
* one plate -> _single_plate_fiber_chunks: full wave width, many fibers per
chunk (the decode-amortization regime).
* many plates -> _many_plates_common_grid: every plate uses the same dloglam
and their COEFF0 start wavelengths differ by whole bins, so cropping each to
the shared window aligns them onto one grid exactly (no resampling) and folds
(plate, fiber) into one flat sample axis, one fiber per chunk -- the streaming
regime, and the only no-reshard way to concat the ragged-width plates. Scales
to the full archive (thousands of plates, tens of GB).
The two modes are mutually exclusive by construction: cropping to the common
window breaks the multi-fiber contiguity the single-plate mode rides on, so
decode-amortization and corpus scale cannot both hold -- an honest boundary of
the FITS byte layout. A `--plates N` CLI flag selects the count.
tests/test_sdss_build.py guards both modes offline with synthetic big-endian
FITS images (byte-identical reads, exact cross-plate alignment, and a fail-fast
on non-bin-aligned grids). Verified end-to-end on real plates: 3 plates -> 1920
aligned spectra, byte-identical to astropy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The kerchunk struct-dtype fix for FITS binary tables merged upstream as zarr-developers/VirtualiZarr#1037, so drop the emfdavid fork pin. That rev (8a1563f) was never pushed to the fork remote and only ever resolved from uv's local cache -- it was unresolvable from a clean checkout. Pin to zarr-developers main (f46902d), verified to carry the merged fix as an ancestor. The fix ships in v2.7.1, so a git pin is still required; it becomes a plain virtualizarr>=2.7.1 once that releases. Moving to current upstream deprecated two APIs the astronomy build-time path uses: - .virtualize accessor -> .vz - virtualizarr.registry.ObjectStoreRegistry -> obspec_utils.registry Both are confined to the four FITS index-build sites (two tests, two examples); nothing in src/ or on the hot path. obspec-utils was already present as a VirtualiZarr dependency, so declaring it in [astronomy] adds no package (uv still resolves 202) -- it just stops us importing through a re-export scheduled for removal. Astronomy tests pass under -W error::DeprecationWarning. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9fc7ea8 to
ec43f84
Compare
|
Since you pick a concrete example with real data, this is clearly what at least some astronomers do :) Is there an intermediate representation of the chunk/manifest that can be persisted in a public place? If the workflow were structured as 1) scan 2) use, then interested people could just do the latter with any benchmarking they care to do. I understand its not quite just a vzarr manifest if you need to do extra work like numpy .view() calls. Will you make this a published blog or something? |
|
Good question, and it's the right shape — scan-once / use-many is already how the example is structured, and Icechunk is the persistence layer that makes the "scan" output publishable. The one thing I want to keep central is that this stays anchored to the tools astronomers already validate with. astropy is the ground truth here: the build checks byte-identical against On persistence itself: the committed Icechunk store isn't an extra step — it's a one-time side effect of the first build (kerchunk → VirtualiZarr → Icechunk → insitubatch). The scan writes it once — virtual refs, no pixels moved — and everything downstream already reads from it. So "publish the manifest" just means dropping that artifact in a public bucket: a consumer opens it and trains/benchmarks with no re-scan and no astropy in the loop. The raw FITS is HTTPS-only (no S3 mirror), but the manifest is tiny and location-independent. Where it's headed makes the "what for" concrete: the eventual target is a PR to astroML adding insitubatch recipes that hook into its existing SDSS workflow ( And yes — blog post committed, kicking off with a Pangeo Showcase talk (~September), with this SDSS case as one of the cross-domain examples. |
An astroML cross-domain example: reconstruct SDSS galaxy spectra streamed in place from real
spPlateFITS, with no reshard. The decode-amortization/streaming companion to the Hubble example.What it does
astroML's spectral-PCA workflow (
fetch_sdss_corrected_spectra→compute_sdss_pca) firstdownloads the raw archive and resamples every spectrum onto a common grid into one
spec4000.npz—the download-and-reshard step insitubatch argues against.
examples/sdssinstead indexes realspPlateframes ondata.sdss.org(HTTPS) as virtual references: one plate holds all ~640fibers on a common log-wavelength grid as a single
(fiber, wave)image, which is virtuallyre-chunked along the fiber axis by pure byte arithmetic — no pixels moved.
The task mirrors astroML's spectral reconstruction: a 1-D convolutional autoencoder, trained by
SGD over the streamed mini-batches, beats a PCA baseline at the same latent dim — because galaxy
spectra are translation-structured (varying redshift shifts the lines, which a fixed-dim linear
basis reconstructs poorly). The offline synthetic default (redshift-shifted spectra) carries the
beats-PCA claim and backs the drift test; the real run is a same-pipeline, real-archive demo.
Two build modes (
--plates N) — both rewrite only the chunk manifest, no pixels moveddloglamand theirCOEFF0start wavelengths differ bywhole bins, so cropping each to the shared window aligns them onto one grid exactly (no
resampling) and folds
(plate, fiber)into one flat sample axis, one fiber per chunk (thestreaming regime — the only no-reshard way to concat the ragged-width plates, scaling to the
full ~2800-plate archive). Verified: 3 plates → 1920 spectra byte-identical to astropy.
The modes are mutually exclusive by construction: cropping to the common window breaks the
multi-fiber contiguity the single-plate mode rides on — an honest boundary of the FITS byte layout.
FITS binary tables
SDSS spectra also ship as FITS binary tables (structured big-endian dtypes).
tests/test_fits_bintable.pyvalidates that path end-to-end through insitubatch (kerchunk →VirtualiZarr → Icechunk → insitubatch) against astropy, and the
astronomyextra pins theVirtualiZarr fix it needs (VirtualiZarr#1037,
open on a fork) — released VirtualiZarr raises on the list-valued kerchunk field specs. Endianness
is dropped in the VZ/icechunk struct metadata, so consumers reinterpret each field with
.view('>f4')at the numpy boundary (the zarr #4142 codec fix does not engage in this path, so it is not pinned).
Tests / gates
tests/test_sdss.py(synthetic drift guard, AE beats PCA),tests/test_sdss_build.py(both buildmodes offline, exact cross-plate alignment, fail-fast on non-bin-aligned grids), and
tests/test_fits_bintable.py. Full suite 178 passed / 10 skipped; ruff + format + mypy clean.🤖 Generated with Claude Code