Skip to content

Commit 291aa7f

Browse files
Surface code demo 1 refactor (#685)
This PR updates the surface code demo (/libs/qec/unittests/realtime/app_examples/surface_code-1.cpp) to use the new syndrome format and decoder context objects. Semantically, the demo should remain very similar to the original version, but I have aimed to remove the hand-rolled sections and replace them with CUDA-QX features as much as possible. I have also refactored for readability. --------- Signed-off-by: Eliot Heinrich <eheinrich@nvidia.com> Signed-off-by: Ben Howe <bhowe@nvidia.com> Co-authored-by: Ben Howe <bhowe@nvidia.com>
1 parent 4227924 commit 291aa7f

6 files changed

Lines changed: 763 additions & 1033 deletions

File tree

docs/sphinx/examples_rst/qec/realtime_decoding.rst

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -583,38 +583,39 @@ Given that the user follows the structure of the examples provided, where each e
583583
# This is done once per code/distance/noise configuration
584584
585585
## C++
586-
./surface_code-1 --distance 3 --num_shots 1000 --p_spam 0.01 \
587-
--save_dem config_d3.yaml --num_rounds 12 --decoder_window 6
588-
586+
./surface_code-1 --distance 3 --num_shots 1000 --p_cnot 0.001 \
587+
--save_dem config_d3.yaml --num_rounds 12
588+
589589
## Python
590590
python surface_code-1.py --distance 3 --num_shots 1000 --p_spam 0.01 \
591591
--save_dem config_d3.yaml --num_rounds 12 --decoder_window 6
592-
592+
593593
# Phase 2: Run with Real-Time Decoding
594594
# Use the saved DEM configuration
595-
595+
596596
## Simulation
597597
./surface_code-1 --distance 3 --num_shots 1000 --load_dem config_d3.yaml \
598-
--num_rounds 12 --decoder_window 6
599-
598+
--num_rounds 12
599+
600600
## Quantinuum Emulation
601601
./surface_code-1-quantinuum-emulate --distance 3 --num_shots 1000 --load_dem config_d3.yaml \
602-
--num_rounds 12 --decoder_window 6
603-
602+
--num_rounds 12
603+
604604
## Quantinuum Hardware
605605
export CUDAQ_QUANTINUUM_CREDENTIALS=credentials.json
606606
./surface_code-1-quantinuum-hardware --distance 3 --num_shots 100 --load_dem config_d3.yaml \
607-
--num_rounds 12 --decoder_window 6
607+
--num_rounds 12
608608
609609
**Application Parameters:**
610610

611611
- ``--distance``: Code distance (3, 5, 7, etc.)
612612
- ``--num_shots``: Number of circuit repetitions
613-
- ``--p_spam``: Physical error rate for noise model (DEM generation only)
613+
- ``--p_cnot``: Two-qubit depolarizing rate on CNOT gates for DEM generation (C++ binary)
614+
- ``--p_spam``: Single-qubit SPAM error rate for DEM generation (Python script)
614615
- ``--save_dem``: Generate and save DEM configuration to file
615616
- ``--load_dem``: Load existing DEM configuration from file
616617
- ``--num_rounds``: Total number of syndrome measurement rounds
617-
- ``--decoder_window``: Number of rounds processed per decoding window
618+
- ``--decoder_window``: Number of rounds processed per decoding window (Python script only)
618619

619620
Debugging and Environment Variables
620621
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

docs/sphinx/examples_rst/qec/realtime_relay_bp.rst

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ Surface Code Test (Relay BP)
375375

376376
The ``surface_code-1-local`` app example drives the device-graph scheduler
377377
through the in-process RPC path (``CUDAQ_QEC_REALTIME_MODE=inproc_rpc``) with
378-
the nv-qldpc-decoder configured for Relay BP (``--use-relay-bp``). It simulates
378+
the nv-qldpc-decoder configured for Relay BP. It simulates
379379
a surface code with ``stim`` and generates syndromes on the fly, so -- unlike
380380
the fixed-fixture CI unit test -- it can run an arbitrary number of shots.
381381

@@ -398,20 +398,19 @@ loop through the scheduler:
398398
APP=./libs/qec/unittests/realtime/app_examples/surface_code-1-local
399399
400400
# 1. Generate the Relay BP decoder config (DEM) for a distance-3 surface code
401-
"$APP" --distance 3 --num_rounds 12 --decoder_window 6 \
402-
--decoder_type nv-qldpc-decoder --use-relay-bp \
401+
"$APP" --distance 3 --num_rounds 12 \
402+
--decoder_type nv-qldpc-decoder \
403403
--num_shots 1000 --save_dem config.yml
404404
405405
# 2. Run the decode loop through the device-graph scheduler
406-
"$APP" --distance 3 --num_rounds 12 --decoder_window 6 \
407-
--decoder_type nv-qldpc-decoder --use-relay-bp \
406+
"$APP" --distance 3 --num_rounds 12 \
407+
--decoder_type nv-qldpc-decoder \
408408
--num_shots 1000 --load_dem config.yml
409409
410410
A clean run exits ``0`` and reports a small number of non-zero syndrome
411411
measurements alongside a larger number of corrections found. The
412412
``app_examples`` CTest ``surface_code-1-local-test-distance-3-inproc-rpc``
413-
wraps this flow (it sets ``CUDAQ_QEC_REALTIME_MODE=inproc_rpc`` and
414-
``EXTRA_CLI_ARGS=--use-relay-bp``).
413+
wraps this flow (it sets ``CUDAQ_QEC_REALTIME_MODE=inproc_rpc``).
415414

416415
Emulated End-to-End Test
417416
------------------------

libs/qec/unittests/realtime/app_examples/CMakeLists.txt

Lines changed: 93 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ add_test(
7878
bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-test.sh"
7979
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
8080
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
81-
3 40 40 NULL 12 6
81+
3 60 30 NULL 12
8282
${CMAKE_BINARY_DIR}/lib
8383
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
8484
)
@@ -91,15 +91,15 @@ if(_APP_CUDAQ_REALTIME_DISPATCH_LIB
9191
bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-test.sh"
9292
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
9393
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
94-
3 60 40 NULL 12 6
94+
3 60 30 NULL 12
9595
${CMAKE_BINARY_DIR}/lib
9696
nv-qldpc-decoder
9797
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
9898
)
9999
set_tests_properties(
100100
app_examples.surface_code-1-local-test-distance-3-inproc-rpc
101101
PROPERTIES ENVIRONMENT
102-
"CUDAQ_QEC_REALTIME_MODE=inproc_rpc;EXTRA_CLI_ARGS=--use-relay-bp"
102+
"CUDAQ_QEC_REALTIME_MODE=inproc_rpc"
103103
# The wrapper exits 77 on GPUs below compute capability 9.0 (the
104104
# device-graph scheduler needs device-side graph launch); report that
105105
# as SKIPPED rather than FAILED.
@@ -113,7 +113,7 @@ add_test(
113113
bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-test.sh"
114114
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
115115
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
116-
3 40 40 NULL 12 6
116+
3 60 30 NULL 12
117117
${CMAKE_BINARY_DIR}/lib
118118
sliding_window 6 1
119119
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
@@ -128,7 +128,7 @@ add_test(
128128
# bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-test.sh"
129129
# ${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
130130
# ${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
131-
# 5 40 40 NULL 20 10
131+
# 5 40 40 NULL 20
132132
# ${CMAKE_BINARY_DIR}/lib
133133
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
134134
# )
@@ -141,6 +141,80 @@ add_test(
141141
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
142142
)
143143

144+
# --------------------------------------------------------------------------- #
145+
# surface_code-1 negative / error-path tests
146+
#
147+
# These drive the three throw sites added to surface_code-1.cpp so that every
148+
# error path is exercised by at least one ctest. Each test asserts BOTH a
149+
# nonzero exit AND the expected diagnostic substring.
150+
# --------------------------------------------------------------------------- #
151+
set(_sc1_exe ${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local)
152+
set(_sc1_neg_dir ${CMAKE_CURRENT_BINARY_DIR}/sc1_negative)
153+
file(MAKE_DIRECTORY ${_sc1_neg_dir})
154+
set(_sc1_neg_env "CUDAQ_DEFAULT_SIMULATOR=stim")
155+
156+
# Helper: command must exit nonzero AND print the expected diagnostic.
157+
function(_sc1_reject name diagnostic)
158+
add_test(NAME app_examples.surface_code-1-negative-${name}
159+
COMMAND bash -c [==[
160+
diag=$1; shift
161+
if out=$("$@" 2>&1); then
162+
printf '%s\n' "$out"
163+
echo "NEGATIVE TEST FAIL: command returned 0; expected a nonzero exit"
164+
exit 1
165+
fi
166+
printf '%s\n' "$out"
167+
printf '%s' "$out" | grep -qF -- "$diag" || {
168+
echo "NEGATIVE TEST FAIL: missing expected diagnostic: $diag"
169+
exit 1
170+
}
171+
]==] _ "${diagnostic}" ${_sc1_exe} ${ARGN}
172+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
173+
set_tests_properties(app_examples.surface_code-1-negative-${name}
174+
PROPERTIES ENVIRONMENT "${_sc1_neg_env}")
175+
endfunction()
176+
177+
# 1. --load_dem with a nonexistent file → setup_decoders throws.
178+
_sc1_reject(load-dem-missing-file "Could not open dem config file"
179+
--load_dem ${_sc1_neg_dir}/nonexistent.yml
180+
--distance 3 --num_rounds 3)
181+
182+
# 2. --save_syndrome to an unwriteable path → begin_syndrome_capture throws.
183+
_sc1_reject(save-syndrome-bad-path "Could not open syndrome file for writing"
184+
--distance 3 --num_rounds 3 --num_shots 2
185+
--save_syndrome ${_sc1_neg_dir}/no_such_dir/out.txt)
186+
187+
# 3. --load_syndrome with a nonexistent file → replay_syndrome_file throws.
188+
_sc1_reject(load-syndrome-missing-file "Could not open syndrome file"
189+
--distance 3 --num_rounds 3
190+
--load_syndrome ${_sc1_neg_dir}/nonexistent.txt)
191+
192+
# Fixture: capture a valid d=3 syndrome file, then derive a
193+
# corrections-flipped copy (0↔1 in the CORRECTIONS block).
194+
add_test(NAME app_examples.surface_code-1-negative-replay-fixtures
195+
COMMAND bash -c "set -e
196+
'${_sc1_exe}' --distance 3 --num_rounds 3 --num_shots 5 \
197+
--save_syndrome '${_sc1_neg_dir}/cap.txt'
198+
sed '/CORRECTIONS_START/,/CORRECTIONS_END/{s/^0$/X/;s/^1$/0/;s/^X$/1/}' \
199+
'${_sc1_neg_dir}/cap.txt' > '${_sc1_neg_dir}/cap_mismatch.txt'"
200+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
201+
set_tests_properties(app_examples.surface_code-1-negative-replay-fixtures
202+
PROPERTIES FIXTURES_SETUP sc1_replay ENVIRONMENT "${_sc1_neg_env}")
203+
204+
# 4. Replay d=3 capture against d=5 experiment → num_data mismatch (9 vs 25).
205+
_sc1_reject(replay-geometry-mismatch "Syndrome file mismatch"
206+
--distance 5 --num_rounds 5
207+
--load_syndrome ${_sc1_neg_dir}/cap.txt)
208+
set_tests_properties(app_examples.surface_code-1-negative-replay-geometry-mismatch
209+
PROPERTIES FIXTURES_REQUIRED sc1_replay)
210+
211+
# 5. Replay with flipped corrections → correction mismatch throw.
212+
_sc1_reject(replay-correction-mismatch "Replay correction mismatch"
213+
--distance 3 --num_rounds 3
214+
--load_syndrome ${_sc1_neg_dir}/cap_mismatch.txt)
215+
set_tests_properties(app_examples.surface_code-1-negative-replay-correction-mismatch
216+
PROPERTIES FIXTURES_REQUIRED sc1_replay)
217+
144218
# End of surface code 1, fully local, regular Stim simulator
145219
#------------------------------------------------------------------------------#
146220

@@ -183,19 +257,7 @@ if(TARGET cudaq-qec-realtime-decoding-server-cqr)
183257
nvqir nvqir-stim)
184258
qec_realtime_app_link_options(surface_code-1-cqr)
185259

186-
# Enable Relay-BP only when this build provides nv-qldpc support.
187-
set(_surface_code_1_cqr_has_nv_qldpc_decoder FALSE)
188-
if(TARGET cudaq-qec-nv-qldpc-decoder
189-
OR QEC_EXTERNAL_DECODERS
190-
OR DEFINED ENV{QEC_EXTERNAL_DECODERS})
191-
set(_surface_code_1_cqr_has_nv_qldpc_decoder TRUE)
192-
endif()
193-
194260
set(_surface_code_1_cqr_env CUDAQ_DEVICE_CALL_CHANNEL=host_dispatch)
195-
if(TARGET cudaq-qec-realtime-cudevice-proprietary
196-
AND _surface_code_1_cqr_has_nv_qldpc_decoder)
197-
list(APPEND _surface_code_1_cqr_env EXTRA_CLI_ARGS=--use-relay-bp)
198-
endif()
199261

200262
add_test(
201263
NAME app_examples.surface_code-1-cqr-test-distance-3
@@ -204,7 +266,7 @@ if(TARGET cudaq-qec-realtime-decoding-server-cqr)
204266
bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-test.sh"
205267
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-cqr
206268
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-cqr
207-
3 60 40 NULL 12 6
269+
3 60 30 NULL 12
208270
${CMAKE_BINARY_DIR}/lib
209271
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
210272
)
@@ -217,14 +279,16 @@ if(TARGET cudaq-qec-realtime-decoding-server-cqr)
217279
add_test(
218280
NAME app_examples.surface_code-1-cqr-two-process-test-distance-3
219281
COMMAND
220-
# Non-zero threshold 60 (vs the -local test's 40): unseeded runs at
221-
# 1000 shots land ~35-45, so 40 flakes; a broken decode path produces
222-
# ~150+, so 60 still discriminates.
282+
# The app seeds the simulator (--seed, default 42), so the run is
283+
# deterministic: ~10 non-zero and ~67 corrections at 1000 shots. The
284+
# thresholds (non-zero <= 60, corrections >= 30) leave margin while
285+
# still catching a broken decode path (which applies ~0 corrections,
286+
# leaving all ~70 raw logical errors uncorrected).
223287
bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-cqr-two-process-test.sh"
224288
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-cqr
225-
3 60 40
289+
3 60 30
226290
$<TARGET_FILE:decoding_server>
227-
12 6
291+
12
228292
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
229293
)
230294
endif()
@@ -245,7 +309,7 @@ cudaqx_add_device_code(surface_code-1-quantinuum-emulate
245309
SOURCES
246310
surface_code-1.cpp
247311
COMPILER_FLAGS
248-
--target quantinuum --emulate --quantinuum-machine Helios-Fake -DMANUALLY_INJECT_ERRORS=1
312+
--target quantinuum --emulate --quantinuum-machine Helios-Fake
249313
DEPENDS_ON
250314
# Must wait for the local executable to be built because it has the same
251315
# surface_code-1.cpp file, and the nvq++ compiler will use the same filename
@@ -274,7 +338,10 @@ add_test(
274338
bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-test.sh"
275339
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
276340
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-quantinuum-emulate
277-
3 0 1000 NULL 12 6
341+
# QIR emulation applies no noise, so the live generate-and-decode run
342+
# finds 0 errors / 0 corrections: this is a lowering + run smoke test
343+
# (matches the distance-5 variant's 0/0 thresholds).
344+
3 0 0 NULL 12
278345
${CMAKE_BINARY_DIR}/lib
279346
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
280347
)
@@ -285,7 +352,7 @@ add_test(
285352
bash "${CMAKE_CURRENT_SOURCE_DIR}/surface_code-1-test.sh"
286353
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-local
287354
${CMAKE_CURRENT_BINARY_DIR}/surface_code-1-quantinuum-emulate
288-
5 0 0 NULL 20 10
355+
5 0 0 NULL 5
289356
${CMAKE_BINARY_DIR}/lib
290357
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
291358
)

libs/qec/unittests/realtime/app_examples/surface_code-1-cqr-two-process-test.sh

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@
2828
# 4: number_of_corrections_decoder_threshold
2929
# 5: path to decoding_server executable
3030
# 6: num_rounds
31-
# 7: decoder_window
32-
# 8: decoder_type (optional, defaults to multi_error_lut)
31+
# 7: decoder_type (optional, defaults to multi_error_lut)
3332

3433
set -e
3534

3635
return_code=0
3736

38-
if [[ $# -lt 7 ]]; then
39-
echo "Error: Expected at least 7 arguments (got $#)"
37+
if [[ $# -lt 6 ]]; then
38+
echo "Error: Expected at least 6 arguments (got $#)"
4039
exit 1
4140
fi
4241

@@ -46,8 +45,7 @@ number_of_non_zero_values_threshold=$3
4645
number_of_corrections_decoder_threshold=$4
4746
SERVER_PATH=$5
4847
NUM_ROUNDS=$6
49-
DECODER_WINDOW=$7
50-
DECODER_TYPE=${8:-multi_error_lut}
48+
DECODER_TYPE=${7:-multi_error_lut}
5149

5250
export CUDAQ_DEFAULT_SIMULATOR=stim
5351

@@ -63,7 +61,7 @@ APP_LOG=load_dem-2proc-${FULL_SUFFIX}.log
6361

6462
# [1] Generate the decoder config (no realtime channel needed for this pass).
6563
$EXE_PATH --distance $DISTANCE --num_rounds $NUM_ROUNDS --num_shots $NUM_SHOTS \
66-
--save_dem $CONFIG_FILE --decoder_window $DECODER_WINDOW \
64+
--save_dem $CONFIG_FILE \
6765
--decoder_type $DECODER_TYPE | tee save_dem-2proc-$FULL_SUFFIX.log
6866

6967
# [2] Start the decoding server on an ephemeral port with that config.
@@ -105,7 +103,7 @@ echo "Decoding server ready on $TRANSPORT port $SERVER_PORT"
105103
QEC_DECODING_SERVER_PORT=$SERVER_PORT \
106104
$EXE_PATH --distance $DISTANCE --num_shots $NUM_SHOTS \
107105
--load_dem $CONFIG_FILE --num_rounds $NUM_ROUNDS \
108-
--decoder_window $DECODER_WINDOW --decoder_type $DECODER_TYPE \
106+
--decoder_type $DECODER_TYPE \
109107
|& tee $APP_LOG
110108

111109
# [4] Stop the server and collect its dispatch count.

0 commit comments

Comments
 (0)