Skip to content

Commit 5f92ae5

Browse files
committed
basic code examples
Signed-off-by: Kevin Mato <[email protected]>
1 parent b2e47f9 commit 5f92ae5

File tree

2 files changed

+214
-78
lines changed

2 files changed

+214
-78
lines changed

docs/sphinx/examples/qec/cpp/real_time_complete.cpp

Lines changed: 115 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,50 +7,133 @@
77
******************************************************************************/
88
// [Begin Documentation]
99

10+
// Simple 3-qubit repetition code with real-time decoding
11+
// This is the most basic QEC example possible
12+
13+
#include "cudaq.h"
14+
#include "cudaq/qec/code.h"
15+
#include "cudaq/qec/experiments.h"
16+
#include "cudaq/qec/pcm_utils.h"
1017
#include "cudaq/qec/realtime/decoding.h"
1118
#include "cudaq/qec/realtime/decoding_config.h"
19+
#include <common/NoiseModel.h>
20+
#include <fstream>
1221

13-
// Configure decoder before circuit execution
14-
cudaq::qec::decoding::config::configure_decoders_from_file(
15-
"decoder_config.yaml");
22+
// Save decoder configuration to YAML file
23+
void save_dem(const cudaq::qec::detector_error_model &dem,
24+
const std::string &filename) {
25+
// Create decoder config
26+
cudaq::qec::decoding::config::decoder_config config;
27+
config.id = 0;
28+
config.type = "multi_error_lut";
29+
config.block_size = dem.num_error_mechanisms();
30+
config.syndrome_size = dem.num_detectors();
31+
config.H_sparse = cudaq::qec::pcm_to_sparse_vec(dem.detector_error_matrix);
32+
config.O_sparse = cudaq::qec::pcm_to_sparse_vec(dem.observables_flips_matrix);
33+
34+
// Calculate numRounds from DEM (we send 1 additional round, so add 1)
35+
uint64_t numSyndromesPerRound = 2; // Z0Z1 and Z1Z2
36+
auto numRounds = dem.num_detectors() / numSyndromesPerRound + 1;
37+
config.D_sparse = cudaq::qec::generate_timelike_sparse_detector_matrix(
38+
numSyndromesPerRound, numRounds, false);
39+
40+
cudaq::qec::decoding::config::multi_error_lut_config lut_config;
41+
lut_config.lut_error_depth = 2;
42+
config.decoder_custom_args = lut_config;
43+
44+
cudaq::qec::decoding::config::multi_decoder_config multi_config;
45+
multi_config.decoders.push_back(config);
46+
47+
std::ofstream file(filename);
48+
file << multi_config.to_yaml_str(200);
49+
file.close();
50+
printf("Saved config to %s\n", filename.c_str());
51+
}
1652

17-
__qpu__ void prep0(cudaq::qec::patch logical) {
18-
// Your state preparation logic
19-
continue;
53+
// Load decoder configuration from YAML file
54+
void load_dem(const std::string &filename) {
55+
std::ifstream file(filename);
56+
std::string yaml((std::istreambuf_iterator<char>(file)),
57+
std::istreambuf_iterator<char>());
58+
auto config = cudaq::qec::decoding::config::multi_decoder_config::from_yaml_str(yaml);
59+
cudaq::qec::decoding::config::configure_decoders(config);
60+
printf("Loaded config from %s\n", filename.c_str());
2061
}
21-
__qpu__ std::vector<bool> measure_stabilizers(cudaq::qec::patch logical) {
22-
// Your stabilizer measurement logic
23-
return std::vector<bool>(12, false);
62+
63+
// Prepare logical |0⟩
64+
__qpu__ void prep0(cudaq::qec::patch logical) {
65+
for (std::size_t i = 0; i < logical.data.size(); ++i) {
66+
cudaq::reset(logical.data[i]);
67+
}
2468
}
2569

26-
// Quantum kernel with real-time decoding
27-
__qpu__ void qec_circuit(int decoder_id, int num_rounds) {
28-
// Reset decoder state
29-
cudaq::qec::decoding::reset_decoder(decoder_id);
70+
// Measure ZZ stabilizers for 3-qubit repetition code
71+
__qpu__ std::vector<cudaq::measure_result> measure_stabilizers(
72+
cudaq::qec::patch logical) {
73+
for (std::size_t i = 0; i < logical.ancz.size(); ++i) {
74+
cudaq::reset(logical.ancz[i]);
75+
}
76+
77+
// Z0Z1 stabilizer
78+
cudaq::x<cudaq::ctrl>(logical.data[0], logical.ancz[0]);
79+
cudaq::x<cudaq::ctrl>(logical.data[1], logical.ancz[0]);
80+
81+
// Z1Z2 stabilizer
82+
cudaq::x<cudaq::ctrl>(logical.data[1], logical.ancz[1]);
83+
cudaq::x<cudaq::ctrl>(logical.data[2], logical.ancz[1]);
84+
85+
return {mz(logical.ancz[0]), mz(logical.ancz[1])};
86+
}
3087

31-
// Allocate qubits
32-
cudaq::qvector data(25), ancx(12), ancz(12);
88+
// QEC circuit with real-time decoding
89+
__qpu__ int64_t qec_circuit() {
90+
cudaq::qec::decoding::reset_decoder(0);
91+
92+
cudaq::qvector data(3);
93+
cudaq::qvector ancz(2);
94+
cudaq::qvector ancx; // Empty for repetition code
3395
cudaq::qec::patch logical(data, ancx, ancz);
34-
35-
// Prepare logical state
96+
3697
prep0(logical);
37-
38-
// Syndrome extraction with real-time decoding
39-
for (int round = 0; round < num_rounds; ++round) {
98+
99+
// 3 rounds of syndrome measurement
100+
for (int round = 0; round < 3; ++round) {
40101
auto syndromes = measure_stabilizers(logical);
41-
cudaq::qec::decoding::enqueue_syndromes(decoder_id, syndromes);
102+
cudaq::qec::decoding::enqueue_syndromes(0, syndromes);
42103
}
43-
44-
// Get and apply corrections
45-
auto corrections =
46-
cudaq::qec::decoding::get_corrections(decoder_id, 1, false);
47-
if (corrections[0]) {
48-
cudaq::x(data); // Apply correction
104+
105+
// Get corrections and apply them
106+
auto corrections = cudaq::qec::decoding::get_corrections(0, 3);
107+
for (std::size_t i = 0; i < 3; ++i) {
108+
if (corrections[i])
109+
cudaq::x(data[i]);
49110
}
50-
51-
// Measure logical observable
52-
auto result = mz(data);
111+
112+
return cudaq::to_integer(mz(data));
53113
}
54114

55-
// Clean up
56-
cudaq::qec::decoding::config::finalize_decoders();
115+
int main() {
116+
auto code = cudaq::qec::get_code("repetition",
117+
cudaqx::heterogeneous_map{{"distance", 3}});
118+
119+
// Step 1: Generate detector error model
120+
printf("Step 1: Generating DEM...\n");
121+
cudaq::noise_model noise;
122+
noise.add_all_qubit_channel("x", cudaq::depolarization2(0.01), 1);
123+
124+
auto dem = cudaq::qec::z_dem_from_memory_circuit(
125+
*code, cudaq::qec::operation::prep0, 3, noise);
126+
127+
save_dem(dem, "config.yaml");
128+
129+
// Step 2: Load config and run circuit
130+
printf("\nStep 2: Running circuit with decoding...\n");
131+
load_dem("config.yaml");
132+
133+
cudaq::run(10, qec_circuit);
134+
printf("Ran 10 shots\n");
135+
136+
cudaq::qec::decoding::config::finalize_decoders();
137+
printf("\nDone!\n");
138+
return 0;
139+
}

docs/sphinx/examples/qec/python/real_time_complete.py

Lines changed: 99 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,58 +8,111 @@
88

99
# [Begin Documentation]
1010

11-
import cudaq
12-
import cudaq_qec as qec
13-
from cudaq_qec import patch
11+
#!/usr/bin/env python3
12+
"""
13+
Simple 3-qubit repetition code with real-time decoding.
14+
This is the most basic QEC example possible.
15+
"""
1416

15-
# Configure decoder before circuit execution (host-side)
16-
qec.configure_decoders_from_file("decoder_config.yaml")
17+
import os
18+
os.environ["CUDAQ_DEFAULT_SIMULATOR"] = "stim"
1719

20+
import cudaq
21+
import cudaq_qec as qec
1822

19-
# Define helper operations
23+
# Prepare logical |0⟩
2024
@cudaq.kernel
21-
def prep0(logical: patch):
22-
# Your state preparation logic
23-
return
24-
25+
def prep0(logical: qec.patch):
26+
for i in range(logical.data.size()):
27+
cudaq.reset(logical.data[i])
2528

29+
# Measure ZZ stabilizers for 3-qubit repetition code
2630
@cudaq.kernel
27-
def measure_stabilizers(logical: patch) -> list[bool]:
28-
# Your stabilizer measurement logic
29-
return [False] * 12 # 12 stabilizers for the surface code
30-
31-
32-
# Quantum kernel with real-time decoding (device-side)
31+
def measure_stabilizers(logical: qec.patch):
32+
for i in range(logical.ancz.size()):
33+
cudaq.reset(logical.ancz[i])
34+
35+
# Z0Z1 stabilizer
36+
cudaq.cx(logical.data[0], logical.ancz[0])
37+
cudaq.cx(logical.data[1], logical.ancz[0])
38+
39+
# Z1Z2 stabilizer
40+
cudaq.cx(logical.data[1], logical.ancz[1])
41+
cudaq.cx(logical.data[2], logical.ancz[1])
42+
43+
return [cudaq.mz(logical.ancz[0]), cudaq.mz(logical.ancz[1])]
44+
45+
# QEC circuit with real-time decoding
3346
@cudaq.kernel
34-
def qec_circuit(decoder_id: int, num_rounds: int):
35-
# Reset decoder state
36-
qec.reset_decoder(decoder_id)
37-
38-
# Allocate qubits
39-
data = cudaq.qvector(25)
40-
ancx = cudaq.qvector(12)
41-
ancz = cudaq.qvector(12)
42-
logical = patch(data, ancx, ancz)
43-
44-
# Prepare logical state
47+
def qec_circuit():
48+
qec.reset_decoder(0)
49+
50+
data = cudaq.qvector(3)
51+
ancz = cudaq.qvector(2)
52+
ancx = cudaq.qvector(0)
53+
logical = qec.patch(data, ancx, ancz)
54+
4555
prep0(logical)
46-
47-
# Syndrome extraction with real-time decoding
48-
for round in range(num_rounds):
56+
57+
# 3 rounds of syndrome measurement
58+
for _ in range(3):
4959
syndromes = measure_stabilizers(logical)
50-
qec.enqueue_syndromes(decoder_id, syndromes)
51-
52-
# Get and apply corrections
53-
corrections = qec.get_corrections(decoder_id, 1, False)
54-
if corrections[0]:
55-
x(data) # Apply correction
56-
57-
# Measure logical observable
58-
result = mz(data)
59-
60-
61-
# Execute
62-
qec_circuit(0, 10)
63-
64-
# Clean up (host-side)
65-
qec.finalize_decoders()
60+
qec.enqueue_syndromes(0, syndromes)
61+
62+
# Get corrections and apply them
63+
corrections = qec.get_corrections(0, 3, False)
64+
for i in range(3):
65+
if corrections[i]:
66+
cudaq.x(data[i])
67+
68+
cudaq.mz(data)
69+
70+
def main():
71+
# Get 3-qubit repetition code
72+
code = qec.get_code("repetition", distance=3)
73+
74+
# Step 1: Generate detector error model
75+
print("Step 1: Generating DEM...")
76+
cudaq.set_target("stim")
77+
78+
noise = cudaq.NoiseModel()
79+
noise.add_all_qubit_channel("x", cudaq.Depolarization2(0.01), 1)
80+
81+
dem = qec.z_dem_from_memory_circuit(
82+
code, qec.operation.prep0, 3, noise)
83+
84+
# Save decoder config
85+
config = qec.DecoderConfig()
86+
config.id = 0
87+
config.type = "multi_error_lut"
88+
config.block_size = dem.detector_error_matrix.shape[1]
89+
config.syndrome_size = dem.detector_error_matrix.shape[0]
90+
config.H_sparse = qec.pcm_to_sparse_vec(dem.detector_error_matrix)
91+
config.O_sparse = qec.pcm_to_sparse_vec(dem.observables_flips_matrix)
92+
93+
# Calculate numRounds from DEM (we send 1 additional round, so add 1)
94+
num_syndromes_per_round = 2 # Z0Z1 and Z1Z2
95+
num_rounds = dem.detector_error_matrix.shape[0] // num_syndromes_per_round + 1
96+
config.D_sparse = qec.generate_timelike_sparse_detector_matrix(
97+
num_syndromes_per_round, num_rounds, False)
98+
config.decoder_custom_args = {"lut_error_depth": 2}
99+
100+
multi_config = qec.MultiDecoderConfig()
101+
multi_config.decoders = [config]
102+
103+
with open("config.yaml", 'w') as f:
104+
f.write(multi_config.to_yaml_str(200))
105+
print("Saved config to config.yaml")
106+
107+
# Step 2: Load config and run circuit
108+
print("\nStep 2: Running circuit with decoding...")
109+
qec.configure_decoders_from_file("config.yaml")
110+
111+
cudaq.sample(qec_circuit, shots_count=10)
112+
print("Ran 10 shots")
113+
114+
qec.finalize_decoders()
115+
print("\nDone!")
116+
117+
if __name__ == "__main__":
118+
main()

0 commit comments

Comments
 (0)