Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ benches/results/**
# devcontainer files
.devcontainer/devcontainer-lock.json

# `just capture-baseline` / `just diff-baseline` output (tools/capture-baseline.py)
.baseline-capture/**

# Useful when running in clusters
logs/

Expand Down
28 changes: 28 additions & 0 deletions docs/content/docs/testing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,34 @@ one entry per rank count rather than per case, because the ranks have to reach
the same collectives. Select either group with
`ctest --test-dir build/editable/Release -L serial` (or `-L mpi-2`).

### Golden baselines

A refactor that is meant to change no result is checked against a captured
baseline rather than by reading the diff. `tools/capture-baseline.py` propagates
every `tests/data/*.msgpack` fixture at a spread of cutoffs and cutoff types,
plus a few hand-written qubit problems, and dumps each run's term count, its
full `(monomial indices, coefficient)` set and its expectation value:

```bash
just capture-baseline golden # seed, once, on an unmodified tree
just diff-baseline # rebuild, re-capture, diff against the golden
```

Terms are dumped in the engine's own returned order, not sorted, and
`diff-baseline` is a byte-wise `diff`. That is deliberate: the order runs from
`SplitmixHash` through probe order, MPI owner routing and insertion order into
floating-point accumulation order, so a silent reorder is a regression, not an
implementation detail.

For a change that reorders terms *on purpose*, `--compare REF CAND --tol` holds
the term set and the term count exactly and compares coefficients and energies to
a relative tolerance instead. Reach for it only when the reordering is the point;
it is not a way to wave through a diff that was supposed to be stable.

The capture is rank-local, so under `mpiexec` each rank writes its own
`manifest-rank<r>.json`. Output lands in `.baseline-capture/`, which is
gitignored.


## Adding tests

Expand Down
17 changes: 17 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ test:
uv run python -m pytest -m "not mpi"
ctest --test-dir build/editable/Release --output-on-failure

# Output directory for `capture-baseline` / `diff-baseline` (gitignored).
baseline_dir := ".baseline-capture"

# Capture a golden baseline snapshot into `.baseline-capture/LABEL` (default "golden") via
# tools/capture-baseline.py. Run once on an unmodified tree to seed the golden baseline, then
# `just diff-baseline` after any change that must not move a term or a coefficient.
capture-baseline LABEL='golden':
uv run --no-sync python tools/capture-baseline.py --out "{{ baseline_dir }}/{{ LABEL }}"

# Rebuild monoprop and diff a fresh capture against a stored one (default "golden"). Byte-identical
# is the bar: term order is a regression signal, not an implementation detail.
diff-baseline AGAINST='golden':
uv sync --all-extras --group test --reinstall-package monoprop --no-cache -v
rm -rf "{{ baseline_dir }}/candidate"
just capture-baseline candidate
diff -rq "{{ baseline_dir }}/{{ AGAINST }}" "{{ baseline_dir }}/candidate"

# MPI is off by default in source builds, so build an MPI-enabled editable install
# first, then run the suite under mpiexec with --no-sync (avoids a per-rank resync).
# Pass RANKS as either a single integer or a semicolon-separated list (e.g. "1;2;4").
Expand Down
Loading
Loading