Skip to content

Add a surface code example to demonstrate NVQLink#647

Open
melody-ren wants to merge 13 commits into
NVIDIA:mainfrom
melody-ren:melodyr/surface-code-realtime-yaml-ising
Open

Add a surface code example to demonstrate NVQLink#647
melody-ren wants to merge 13 commits into
NVIDIA:mainfrom
melody-ren:melodyr/surface-code-realtime-yaml-ising

Conversation

@melody-ren

@melody-ren melody-ren commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds a YAML-configurable realtime surface-code example that streams CUDA-QX measurement data.

It supports:

  • PyMatching.
  • TensorRT CNN predecoder plus PyMatching.
  • Conditionally configured NV-Q-LDPC/Relay-BP.

The TensorRT path interoperates with the Ising basis-Z, XV surface-code predecoder by aligning the surface-code geometry, measurement order, detector layout, and logical observable convention.

Depends on #637 for surface code orientation.

Ising predecoder interoperability

The Ising bundle is an external asset and is not committed. When the TensorRT+Ising path is requested without it, the example prints the exact local-generation recipe and exits.

This PR has no runtime impact.

Justification for > 1k lines PR: the example requires its own .cpp file, driver script and helper python script. Additionally, it also carries the unmerged content from #637 .

…erop

Bring in PR NVIDIA#637's C++ surface-code orientation support (sc_orientation
XV/XH/ZV/ZH; orientation-aware role and observable geometry) and its unit
tests, so the realtime example can build the surface code at code_rotation XV
to match the Ising-trained predecoder. Vendored verbatim; temporary until NVIDIA#637
merges upstream. Python bindings/exports omitted (this branch's consumer is the
C++ example).

Signed-off-by: Melody Ren <melodyr@nvidia.com>
Add surface_code-4-yaml, a YAML-switchable realtime surface-code decoder example (pymatching / trt+pymatching / nv-qldpc relay-bp over inproc_rpc), and build the code at code_rotation XV for both the stabilizer CNOT schedule and the logical observables.

Over inproc_rpc the trt path loads an Ising-trained d7/T7 CNN predecoder ONNX and runs end-to-end; pymatching decodes at 0 LER and the example ctests pass at XV.

Signed-off-by: Melody Ren <melodyr@nvidia.com>
Rework the surface_code-4-yaml DEM-generation kernel to emit the detector layout an Ising-trained predecoder expects (prep-basis single-term detectors + paired rounds + data-derived boundary detectors, X-then-Z), so the Ising CNN model decodes cudaqx's live syndromes.

The trt+Ising config loads H/O/priors from the Ising bundle (Ising detector order) and a D_sparse that maps the cudaqx live measurement buffer onto Ising's detector rows (cudaqx's m2d with the Z-ancillas permuted to Ising order; X-ancillas and data identity); the final data measurements are enqueued so the boundary detectors close. End-to-end over inproc_rpc, trt + the Ising d7/T7 model decodes at 0/200 residual; pymatching and nv-qldpc keep cudaqx's own DEM (D = m2d).

The example is single-volume (decoding one num_rounds-round volume; the sliding-window machinery is removed). --save_syndrome and --load_syndrome record the heterogeneous final data round so replay round-trips exactly. gen_dsparse.py prepares the bundle's D_sparse.txt from a local Ising checkout (--ising-repo); the trt+Ising path needs that external model and bundle and is not exercised in CI. The TensorRT predecoder runs with its internal CUDA graph enabled.

Signed-off-by: Melody Ren <melodyr@nvidia.com>
…ance

Bind a loaded YAML config to the experiment geometry: the load path validates the detector count and the measurement-buffer span (max(D_sparse)+1) against the run, which together pin distance and num_rounds; it also checks the observable count, and the trt+Ising path reads and enforces the bundle's metadata.txt (basis=Z, code_rotation=XV, distance, n_rounds).

--load_syndrome now requires --yaml, and configure_decoders' return status is checked. Drop the num_rounds-multiple-of-distance constraint (single-volume decoding needs only num_rounds >= 1); num_rounds < distance warns rather than errors. Reject even or below-3 distance up front. Validation failures surface as a clean 'Error: ...' with a nonzero exit.

Signed-off-by: Melody Ren <melodyr@nvidia.com>
Validate every decoder config in a loaded YAML (one observable each, consistent detector count and measurement-buffer span), supporting num_logical > 1 independent patches.

Replay requires a structurally complete capture -- a present CORRECTIONS_START/END footer, one correction per shot, and per-shot round counts and widths matching the geometry -- and exits nonzero on a missing, truncated, or correction-mismatched file.

Add native ctests covering the validation and boundary behavior: distance and round guards, geometry-bound YAML loading, X-basis bundle rejection, replay guards, and multi-logical generate+reload. Each negative case asserts both a nonzero exit and the expected diagnostic.

Signed-off-by: Melody Ren <melodyr@nvidia.com>
- Neutralize internal-note and "for-Ising" comment framing; fix copyright year.
- Rename gen_dsparse.py -> gen_dsparse_from_memory_circuit.py.
- trt+Ising path: when the locally-generated predecoder bundle is absent,
  print the exact generation recipe and exit rather than failing deeper in.
- Order the example after surface_code-3 in CMake; drop cross-example comments.

Signed-off-by: Melody Ren <melodyr@nvidia.com>
Brings in NVIDIA#637 (surface-code orientation XV/XH/ZV/ZH). The stale vendored C++-only copy is replaced by upstream's version, which also adds the Python bindings the vendored copy omitted.

Signed-off-by: Melody Ren <melodyr@nvidia.com>
Fold the d5t6 and multilogical generate/reload pairs into single driver runs (the driver already does generate + reload + decode and adds a residual-LER check), and drop the undersized-distance case (same guard and diagnostic as the even-distance case). 18 -> 15 tests, all passing; coverage unchanged.

Signed-off-by: Melody Ren <melodyr@nvidia.com>
@melody-ren melody-ren marked this pull request as ready for review July 2, 2026 22:55

@kvmto kvmto left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice example and the negative-test harness is careful. Two blocking questions on what the positive tests can actually detect, plus four smaller inline comments.

Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml-test.sh Outdated
Comment thread libs/qec/unittests/realtime/app_examples/CMakeLists.txt
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp Outdated
Signed-off-by: Melody Ren <melodyr@nvidia.com>
…eplay test, flag/arg guards, dead debug)

Signed-off-by: Melody Ren <melodyr@nvidia.com>

@eliotheinrich eliotheinrich left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice, but it looks like there are a few spots which are hand-rolling sections that we should be able to support using existing components (and since this is a demo, it seems prudent to use our nice features). I could be missing some context, please let me know if that's the case.

Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp Outdated
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp Outdated
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-4-yaml.cpp
@melody-ren melody-ren requested a review from kvmto July 6, 2026 18:34
…_*_syndrome

Signed-off-by: Melody Ren <melodyr@nvidia.com>
}
}

__qpu__ std::int64_t

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be uint64_t to match ret below?

if (numLogical != config.decoders.size()) {
printf("ERROR: numLogical [%ld] != config.decoders.size() [%ld]\n",
numLogical, config.decoders.size());
exit(1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to use exit(), or throw exceptions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants