Byte-exact verification that the multiscale pyramids in the Vesuvius Challenge open data are what they claim to be.
Every published volume ships a resolution pyramid. Downstream work — surface prediction, segmentation review, any browsing UI — reads the reduced levels rather than level 0, because level 0 is too large to touch. Nothing checks that those reduced levels are actually a faithful reduction of the data underneath them. A pyramid that silently disagrees with its own level 0 corrupts every result computed from it, and does so invisibly, because the reduced level still looks like plausible scroll data.
This tool checks it, over the whole published catalogue, without credentials.
| volumes audited | 71 |
| volumes clean | 70 |
| volumes with a contract finding | 1 |
| chunk reductions compared byte-for-byte | 1,410 |
| of those that disagreed with the stated rule | 0 |
| chunks legitimately absent (sparse storage) | 706 |
| chunks not reachable after 4 retries | 0 |
| wall-clock for the full catalogue | 574 s |
Every level of every published pyramid satisfies, exactly:
level[N+1][z,y,x] == round_half_even( mean( level[N][2z:2z+2, 2y:2y+2, 2x:2x+2] ) )
with ceil for odd extents, and with a trailing partial block averaged over the
voxels that actually exist rather than zero-padded.
That held for 1,410 of 1,410 compared reductions (100.00%). The rounding rule matters: it is round-half-to-even, not round-half-away-from-zero. Assuming the schoolbook rule instead disagrees on 6.25% of voxels by exactly 1 — which is the fraction of block means that land on a half. Anyone regenerating a level, or writing a reader that reduces on the fly, needs this to reproduce the published bytes.
The tool does not assume a rule. It fits one per store from
{half-even, half-up, trunc}, reports which fits, and raises a contract
violation only when no standard rule fits. It also flags a store that uses
more than one rule internally.
| check | class | what it means |
|---|---|---|
downsample |
contract | a child chunk is the exact reduction of its parent region |
catalog_shape |
contract | the catalogue's declared volume shape equals the level-0 array shape |
catalog_dtype |
contract | the catalogue's declared data_format matches the array dtype |
shape_relation |
contract | each level's extent follows from its parent and the declared scale |
declared_scale |
contract | OME coordinateTransformations agree with real extents |
uniformity |
contract | dtype, chunking, order, fill value and codec agree across levels |
sparsity_coherence |
contract | a child chunk holding data has at least one present parent chunk |
rounding_consistency |
contract | one store does not mix rounding rules |
coverage |
advisory | what could not be verified, and why |
A contract finding is machine-checkable and safe to gate automation on; the CLI exits 2. An advisory never fails a run.
1 contract finding(s):
catalog_shape — catalog declares shape [14369, 7888, 8096] but level 0 array is [14258, 7888, 8096]. The same record's creation.metadata gives z_crop_start=0, z_crop_end=14257, which yields exactly 14258 planes (inclusive) and matches the array. The declared shape is the pre-crop extent and was not updated after the crop; a consumer sizing work from it overruns the array by 111 planes.
pip install numpy numcodecs # numcodecs only for blosc-compressed stores
python -m pyramid_audit scan --samples 6 --workers 6 --out reports/scan.json
python -m pyramid_audit check PHerc0800/volumes/20250521135224-8.640um-1.2m-116keV-masked.zarr/
python -m tests.test_checks # self-checks, no pytest neededExit status 0 clean, 2 contract violation, 1 tool error.
The uncompressed stores are C-ordered, so a range of leading-axis planes is one
contiguous byte range. The tool verifies a 16-plane slab per chunk with an HTTP
Range request, moving an eighth of the bytes of a full 128³ chunk. Because
child chunk origins are multiples of 128, the corresponding parent range falls
inside a single parent z-chunk, which halves the parent fetches as well. The
comparison over the covered planes is exact; this reduces coverage, not rigour,
and the covered plane count is recorded in every finding.
The whole catalogue audits in 574 seconds.
- It samples chunks; it does not compare every voxel of every level. Counts of
what was compared are reported so the claim is bounded.
--samplesraises it. - A pyramid that is internally consistent can still be a faithful reduction of the wrong source. This tool checks self-consistency, not provenance.
- It does not check ink detection, segmentation, meshes, or anything derived.
- Whether the one catalogue-shape defect below has affected any published result has not been measured. It is reported because it is provable from the catalogue's own record, not because downstream damage is demonstrated.
- Absent chunks are treated as fill, matching Zarr semantics. The tool does not attempt to distinguish "legitimately empty" from "lost".
- It cannot see an oversized chunk. Reads use HTTP Range to fetch a slab of
leading planes, so an object that is larger than its declared chunk size
still returns exactly the expected slab and reshapes cleanly. A volume with
chunks stored at 8x or 64x their declared size passes this audit untouched;
that defect class is only visible from listing metadata, which is what
fill-censuschecks. Found the hard way:PHerc0343P/20250521134555has 555 such chunks and audits clean here at 60 samples per level pair.
README.md is generated from reports/scan-full.json by make_readme.py. It
cannot be edited into disagreement with the evidence:
python make_readme.py > README.mdRead-only throughout: anonymous HTTPS GETs, no credentials, no writes. MIT licensed.