Backend layout and parity contract#
quantem.gpu is organized by scientific domain first and accelerator second.
This is deliberate: IO, detector reductions, DPC, display, and SSB each expose
one scientific contract, while CUDA, Python MPS, native Swift/Metal, and WebGPU
provide implementations of that contract. A backend must not invent a second
public workflow or result type.
Ownership rules#
Public Python callers enter through
quantem.gpu.io,quantem.gpu.detector,quantem.gpu.dpc,quantem.gpu.display, andquantem.gpu.direct_ptycho.Backend modules implement those contracts and are not consumer APIs.
Native clients consume the repository-root Swift package. Swift and Swift-only Metal sources live in
native/swift, outside the Python wheel; Metal sources and data Python also reads live once in the Python package, andPackage.swiftcopies them into the resource bundles.Browser consumers import the domain-owned TypeScript sources listed in
src/quantem/gpu/webgpu/sources.json; there is no separate browser entry module.Browser kernels remain beside their scientific domain. WebGPU is a browser runtime, not a Python device name and not a synonym for Metal.
CPU/NumPy is an explicit reference backend for tests. Production scientific work must never silently fall back to it.
UI, view state, cache scheduling, and resource-policy choices remain in the consuming application. Reusable math, kernels, resource estimation, and scientific provenance remain in
quantem.gpu.
IO representation layout#
The migration separates representation-specific implementation names without changing the public Python verbs, load defaults, metadata, or kernels. The Python files below were moved, not copied.
Backend |
HDF5 decode implementation |
|---|---|
Python CUDA |
|
Python MPS |
|
Explicit CPU reference |
|
WebGPU |
|
Python callers continue to use io.load(..., representation=...).
The representation contract remains authoritative
for supported formats and operations. Representation names do not imply that
every backend accepts every source or offers every operation.
io/hdf5/webgpu/local-h5.ts exports setShow4DSTEMLocalFiles,
loadShow4DSTEMLocalH5Master, and loadShow4DSTEMLocalH5MaskedSum;
detector/webgpu/backend.ts exports DetectorCompute; browser display is
quantem.widget’s. Register the selected HDF5 master/chunk files with
setShow4DSTEMLocalFiles before using the dense loader. Each registration
replaces the previous file registry; it does not release GPU buffers.
Python backend imports use the canonical modules above. The old
cpu.reference and mps.decoder compatibility modules are removed, and the
lossless-pack readers (cuda.packed, mps.packed) were deleted with their
container format. Mutable backend state has one owner; tests
that inject allocation, cache, or device state target that canonical module.
Browser build scripts must export the complete dependency graph.
The loader separates result/ownership types (io/dataset.py), bounded
region reads (io/read.py), header readers (formats/hdf5/master.py) and
pinned staging ownership (device/cuda_runtime.py).
load.py keeps the public route choice. Bounded HDF5-to-ANS streaming lives
in io/encoded.py, the CUDA paired-count layout in io/paired.py, scaled
precision storage in io/precision.py, and the dense CPU reference in
io/hdf5/cpu.py; GPU dense loading is not a route. Further decomposition must
preserve source-range, cancellation, and failure behavior.
Current source tree#
Only implemented directories are shown. A directory does not imply every operation is supported by every backend:
src/quantem/gpu/ # layers import downward only:
# device < formats < resident < io < detector, geometry
# < dpc, parallax < screening < ssb < remote < cli
device/ # device detection, CUDA and Metal runtime plumbing
formats/ # files on disk, GPU-free: hdf5/, qem/, emd, empad, ...
resident/ # acquisitions held on the GPU and their exact reductions
cuda/, mps/ # backend residents; kernels/ beside the Python that reads them
io/
__init__.py # public API only
load.py # public load and route choice
dataset.py # loaded data and ownership contract
representation.py # representation is independent of dtype
hdf5/{cpu.py,cuda,mps,webgpu} # bitshuffle+LZ4 codecs and browser sources
detector/
__init__.py # public BF/ADF/DF/mean-DP API
cuda/, mps/, webgpu/
dpc/
__init__.py # public CoM, rotation, and iDPC API
results.py
webgpu/
display/
__init__.py # shared display-math contract
colormaps.json # colormap control points for every backend
goldens/parity.json # shared display goldens
metal/display.metal # native Metal display shader
ssb/
__init__.py # one SSB workflow and result contract
contract.py, cuda/, mps/, webgpu/
geometry/cuda.py
parallax/cuda/
optics/physics.py
movie/{export,cuda,mps}.py
remote/ # transport of exact scientific arrays
webgpu/ # the sources.json build manifest
native/ # not part of the Python wheel
swift/
Sources/ # SwiftPM products mirroring the domains
Tests/
Benchmarks/
Vendor/ # CHDF5.xcframework
tests/
contracts/ # public API, provenance, and failure rules
parity/
backend_matrix.json # machine-readable required coverage
fixtures/ # retained frozen scientific evidence
webgpu/ # executable TypeScript checks
hardware/{cuda,mps,metal}/ # device-dependent gates
e2e/ # consumer-local override and real-data gates
infrastructure/ # docs, packaging, benchmark checks
mps remains the Python backend selector for compatibility. Its accelerated
implementation may use MLX or Metal. Native Swift products use Metal in their
names because they expose Metal buffers and command encoding directly.
Canonical owners#
Implementations have one canonical owner. Backend code lives in a cuda/,
mps/ or webgpu/ directory (or one cuda.py, mps.py or cpu.py module)
directly under the package that owns the science; there is no backends/ or
compute/ level, and no compatibility shims.
Keep one support matrix, tests/parity/backend_matrix.json, and keep
historical result paths and trial IDs intact. Moving source code does not make
pending hardware cells pass.
Browser build integration#
Do not maintain a consumer-side list of individual kernel files. The package
ships webgpu/sources.json, the list of package-owned browser sources as
paths relative to quantem.gpu. Build scripts read that manifest (for example
through importlib.resources.files("quantem.gpu")), copy the listed files into
a generated directory, and import the modules they need. Qualify the complete
consumer bundle against an
exact package revision before adoption. Manifest completeness alone is not
application compatibility.
Reproduce the layout checks#
Run from the repository root. These checks validate imports, numerical regressions, package resources, native contracts, and documentation. They do not replace real-device timing or app acceptance.
# Python: imports, lazy accelerators, dense and encoded behavior.
python scripts/run_tests.py --list
python scripts/run_tests.py contracts parity infrastructure -q
python scripts/run_tests.py hardware/cuda -q
python scripts/run_tests.py hardware/mps -q
python scripts/run_tests.py e2e -q
# Suite names map to test directories; pytest paths and node IDs pass through.
python scripts/run_tests.py tests/contracts/io/test_load.py -q
# Browser: install the pinned development tools, then check the source manifest.
npm ci
python scripts/run_tests.py tests/contracts/test_webgpu_export.py -q
# Every source in webgpu/sources.json, with quantem.widget's compiler options.
npm run typecheck
# Native Apple: unchanged SwiftPM entry.
swift test -j 4
jupyter-book build docs --warningiserror --nitpick
python scripts/check_docs_links.py --html-root docs/_build/html
The browser contract tests read their tools from node_modules/ in the
repository root. They are skipped when Node.js is not installed (the
logical-pixel hash contract also needs Node.js 22 or newer), and a skip is not
a passing WebGPU contract; a missing tool fails with the npm ci instruction.
One cross-language scientific contract#
Every backend result bundle must record:
source identity or fixture hash;
source scan and detector shape and dtype;
half-open scan and detector regions;
scan bin, detector bin, output shape, output dtype, and accumulation dtype;
bad-pixel policy and detector-mask definition;
(row, column) ≡ (r, c)coordinate convention;backend, device, source revision, and kernel revision;
whether the result is native resolution, explicitly binned, or explicitly cropped; and
output array hashes plus the parity metric used.
Real-space crop is never an implicit memory or speed policy. Detector binning must be explicit and count-preserving, including incomplete edge bins. A binned array must never be described as native-resolution evidence.
Parity layers#
Parity is cumulative. A source-presence or compile test does not replace a hardware or real-data gate.
Contract: imports, signatures, shapes, dtypes, coordinate order, provenance, and honest unsupported/failure behavior.
Synthetic numerical: small odd and rectangular arrays; partial edge bins; masks; nonfinite display inputs; deterministic seeds.
Frozen cross-backend: every backend reads the same input fixture and writes the same versioned result bundle. Integer decode, bin, masks, sums, and RGBA/histogram outputs are byte-exact.
Real-data: full source shape and dtype, no unreported crop/bin/precision change, exact source hashes, peak memory, and output hashes.
Physical end-to-end: the real consumer uses a local package override or exact revision pin; cold first-source, warm source, and saved-result reopen are reported separately.
Floating-point operations use a frozen, operation-specific metric. CoM, DPC, FFT, and SSB must report maximum and high-percentile error as applicable; a tolerance may not be widened to make a new backend pass. Goldens are generated only by an explicit recapture command and never by the backend being adjudicated.
Migration gate#
Move one domain at a time in this order: contract/fixtures, IO, detector/DPC, display, then SSB. For each move:
freeze the old output bundles;
move the module and update every caller in the same coordinated change: this repository, quantem.widget, quantem.live, and denoise. No import-only compatibility shim or alias keeps the old path;
run CPU-reference, CUDA, MPS/Metal, Swift, and WebGPU gates listed in
tests/parity/backend_matrix.json;test supported native and browser clients through a local package override; and
commit the move and pin consumers to that exact revision.
No folder cleanup is complete merely because unit tests pass on one host.