Skip to content
Merged
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: 1 addition & 2 deletions libs/qec/include/cudaq/qec/realtime/decoding_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,9 @@ struct decoder_config {
std::string type;
uint64_t block_size = 0;
uint64_t syndrome_size = 0;
uint64_t num_syndromes_per_round = 0;
std::vector<std::int64_t> H_sparse;
std::vector<std::int64_t> O_sparse;
std::optional<std::vector<std::int64_t>> D_sparse;
std::vector<std::int64_t> D_sparse;
std::variant<single_error_lut_config, multi_error_lut_config,
nv_qldpc_decoder_config, sliding_window_config>
decoder_custom_args;
Expand Down
24 changes: 5 additions & 19 deletions libs/qec/lib/realtime/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,9 @@ struct MappingTraits<cudaq::qec::decoding::config::decoder_config> {
io.mapRequired("type", config.type);
io.mapRequired("block_size", config.block_size);
io.mapRequired("syndrome_size", config.syndrome_size);
io.mapRequired("num_syndromes_per_round", config.num_syndromes_per_round);
io.mapRequired("H_sparse", config.H_sparse);
io.mapRequired("O_sparse", config.O_sparse);
io.mapOptional("D_sparse", config.D_sparse);
io.mapRequired("D_sparse", config.D_sparse);

// Validate that the number of rows in the H_sparse vector is equal to
// syndrome_size.
Expand Down Expand Up @@ -384,9 +383,9 @@ struct MappingTraits<cudaq::qec::decoding::config::decoder_config> {
// Validate that if the D_sparse is provided, it is a valid D matrix. That
// means that the number of rows in the D_sparse matrix should be equal to
// the number of rows in the H_sparse matrix, and no row should be empty.
if (config.D_sparse.has_value()) {
if (!config.D_sparse.empty()) {
auto num_D_rows =
std::count(config.D_sparse->begin(), config.D_sparse->end(), -1);
std::count(config.D_sparse.begin(), config.D_sparse.end(), -1);
if (num_D_rows != config.syndrome_size) {
throw std::runtime_error("Number of rows in D_sparse vector is not "
"equal to syndrome_size: " +
Expand All @@ -395,14 +394,13 @@ struct MappingTraits<cudaq::qec::decoding::config::decoder_config> {
}
// No row should be empty, which means that there should be no
// back-to-back -1 values.
for (std::size_t i = 0; i < config.D_sparse->size() - 1; ++i) {
if (config.D_sparse->at(i) == -1 && config.D_sparse->at(i + 1) == -1) {
for (std::size_t i = 0; i < config.D_sparse.size() - 1; ++i) {
if (config.D_sparse.at(i) == -1 && config.D_sparse.at(i + 1) == -1) {
throw std::runtime_error("D_sparse row is empty for decoder " +
std::to_string(config.id));
}
}
}

#define INIT_AND_MAP_DECODER_CUSTOM_ARGS(type) \
do { \
if (!std::holds_alternative<type>(config.decoder_custom_args)) { \
Expand All @@ -425,18 +423,6 @@ struct MappingTraits<cudaq::qec::decoding::config::decoder_config> {
INIT_AND_MAP_DECODER_CUSTOM_ARGS(
cudaq::qec::decoding::config::sliding_window_config);
}

// Validate that the number of syndromes per round is less than or equal to
// the syndrome size and >= 0.
if (config.num_syndromes_per_round < 0 ||
config.num_syndromes_per_round > config.syndrome_size) {
throw std::runtime_error(
"num_syndromes_per_round (" +
std::to_string(config.num_syndromes_per_round) +
") is not greater than 0 and less than or equal to syndrome_size (" +
std::to_string(config.syndrome_size) + ") for decoder " +
std::to_string(config.id));
}
}
};

Expand Down
18 changes: 4 additions & 14 deletions libs/qec/lib/realtime/realtime_decoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,11 @@ int configure_decoders(
auto observable_matrix = cudaq::qec::pcm_from_sparse_vec(
decoder_config.O_sparse, num_observables, decoder_config.block_size);
new_decoder->set_O_sparse(decoder_config.O_sparse);
if (decoder_config.D_sparse.has_value()) {
new_decoder->set_D_sparse(*decoder_config.D_sparse);
if (!decoder_config.D_sparse.empty()) {
new_decoder->set_D_sparse(decoder_config.D_sparse);
} else {
// Auto-generate a hard-wired D_sparse.
auto num_rounds = decoder_config.syndrome_size /
decoder_config.num_syndromes_per_round;
CUDAQ_INFO("Auto-generating D_sparse for decoder {} with "
"num_syndromes_per_round {} and num_rounds {}",
decoder_config.id, decoder_config.num_syndromes_per_round,
num_rounds);
std::vector<std::int64_t> D_sparse =
cudaq::qec::generate_timelike_sparse_detector_matrix(
decoder_config.num_syndromes_per_round, num_rounds,
/*include_first_round=*/false);
new_decoder->set_D_sparse(D_sparse);
throw std::runtime_error(
"D_sparse must be provided in decoder configuration");
}

// Invoke a dummy decoding operation to force the decoder to be
Expand Down
2 changes: 0 additions & 2 deletions libs/qec/python/bindings/py_decoding_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ void bindDecodingConfig(py::module &mod) {
.def_readwrite("type", &decoder_config::type)
.def_readwrite("block_size", &decoder_config::block_size)
.def_readwrite("syndrome_size", &decoder_config::syndrome_size)
.def_readwrite("num_syndromes_per_round",
&decoder_config::num_syndromes_per_round)
.def_readwrite("H_sparse", &decoder_config::H_sparse)
.def_readwrite("O_sparse", &decoder_config::O_sparse)
.def_readwrite("D_sparse", &decoder_config::D_sparse)
Expand Down
9 changes: 3 additions & 6 deletions libs/qec/python/tests/test_decoders_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def create_test_empty_decoder_config(decoder_id):
config.type = "single_error_lut"
config.block_size = 20
config.syndrome_size = 10
config.num_syndromes_per_round = config.syndrome_size

# Create sparse H matrix representation from a zero matrix
H = np.zeros((config.syndrome_size, config.block_size), dtype=np.uint8)
Expand All @@ -80,8 +79,7 @@ def create_test_empty_decoder_config(decoder_id):

# Generate timelike sparse detector matrix
config.D_sparse = qec.generate_timelike_sparse_detector_matrix(
config.num_syndromes_per_round, 2, include_first_round=False)

config.syndrome_size, 2, include_first_round=False)
return config


Expand Down Expand Up @@ -219,7 +217,6 @@ def test_sliding_window_decoder():
config.type = "sliding_window"
config.block_size = n_cols
config.syndrome_size = n_rows
config.num_syndromes_per_round = n_syndromes_per_round

# Convert PCM to sparse representation
config.H_sparse = qec.pcm_to_sparse_vec(pcm)
Expand All @@ -228,8 +225,8 @@ def test_sliding_window_decoder():
O = np.zeros((2, n_cols), dtype=np.uint8)
config.O_sparse = qec.pcm_to_sparse_vec(O)

# Reset D_sparse for sliding window (set to None to indicate it's not used)
config.D_sparse = None
config.D_sparse = qec.generate_timelike_sparse_detector_matrix(
config.syndrome_size, 2, include_first_round=False)

# Sliding window config
sw_config = qec.qecrt.config.sliding_window_config()
Expand Down
12 changes: 4 additions & 8 deletions libs/qec/python/tests/test_decoding_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,9 @@ def test_configure_valid_multi_error_lut_decoders():
dc.type = "multi_error_lut"
dc.block_size = 10
dc.syndrome_size = 3
dc.num_syndromes_per_round = 3
dc.H_sparse = [1, 2, 3, -1, 6, 7, 8, -1, -1]
dc.D_sparse = qec.generate_timelike_sparse_detector_matrix(
dc.syndrome_size, 2, include_first_round=False)
dc.set_decoder_custom_args(nv)

mdc = qec.multi_decoder_config()
Expand All @@ -195,7 +196,6 @@ def test_decoder_config_yaml_roundtrip_and_custom_args():
dc.type = "nv-qldpc-decoder"
dc.block_size = 10
dc.syndrome_size = 3
dc.num_syndromes_per_round = 3
dc.H_sparse = [1, 2, 3, -1, 6, 7, 8, -1, -1]
dc.set_decoder_custom_args(nv)

Expand All @@ -210,7 +210,6 @@ def test_decoder_config_yaml_roundtrip_and_custom_args():
assert dc2.type == "nv-qldpc-decoder"
assert dc2.block_size == 10
assert dc2.syndrome_size == 3
assert dc2.num_syndromes_per_round == 3

# Recover NV config from decoder_custom_args (it's already the config object)
nv2 = dc2.decoder_custom_args
Expand All @@ -236,7 +235,6 @@ def test_multi_decoder_config_yaml_roundtrip():
d1.type = "nv-qldpc-decoder"
d1.block_size = 10
d1.syndrome_size = 3
d1.num_syndromes_per_round = 3
d1.H_sparse = [1, 2, 3, -1, 6, 7, 8, -1, -1]
d1.set_decoder_custom_args(nv)

Expand All @@ -248,7 +246,6 @@ def test_multi_decoder_config_yaml_roundtrip():
d2.type = "multi_error_lut"
d2.block_size = 10
d2.syndrome_size = 3
d2.num_syndromes_per_round = 3
d2.H_sparse = [1, 2, 3, -1, 6, 7, 8, -1, -1]
d2.set_decoder_custom_args(lut_config)

Expand Down Expand Up @@ -280,7 +277,6 @@ def test_configure_decoders_from_str_smoke():
decoder_config.type = "nv-qldpc-decoder"
decoder_config.block_size = 10
decoder_config.syndrome_size = 3
decoder_config.num_syndromes_per_round = 3
decoder_config.H_sparse = [1, 2, 3, -1, 6, 7, 8, -1, -1]
decoder_config.set_decoder_custom_args(nv)
yaml_str = decoder_config.to_yaml_str()
Expand Down Expand Up @@ -310,8 +306,9 @@ def test_configure_valid_decoders():
dc.type = "multi_error_lut"
dc.block_size = 10
dc.syndrome_size = 3
dc.num_syndromes_per_round = 3
dc.H_sparse = [1, 2, 3, -1, 6, 7, 8, -1, -1]
dc.D_sparse = qec.generate_timelike_sparse_detector_matrix(
dc.syndrome_size, 2, include_first_round=False)
lut_config = qec.multi_error_lut_config()
lut_config.lut_error_depth = 2
dc.set_decoder_custom_args(lut_config)
Expand All @@ -336,7 +333,6 @@ def test_configure_invalid_decoders():
decoder_config.type = "invalid-decoder"
decoder_config.block_size = 10
decoder_config.syndrome_size = 3
decoder_config.num_syndromes_per_round = 3
decoder_config.H_sparse = [1, 2, 3, -1, 6, 7, 8, -1, -1]
decoder_config.set_decoder_custom_args(nv)

Expand Down
10 changes: 3 additions & 7 deletions libs/qec/unittests/realtime/app_examples/surface_code-1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,14 @@ void save_dem_to_file(const cudaq::qec::detector_error_model &dem,
config.type = "multi_error_lut";
config.block_size = dem.num_error_mechanisms();
config.syndrome_size = dem.num_detectors();
config.num_syndromes_per_round = numSyndromesPerRound;
config.H_sparse = cudaq::qec::pcm_to_sparse_vec(dem.detector_error_matrix);
config.O_sparse =
cudaq::qec::pcm_to_sparse_vec(dem.observables_flips_matrix);
config.D_sparse = cudaq::qec::generate_timelike_sparse_detector_matrix(
numSyndromesPerRound, numRounds, /*include_first_round=*/false);
config.decoder_custom_args =
cudaq::qec::decoding::config::multi_error_lut_config();
auto &multi_error_lut_config =
std::get<cudaq::qec::decoding::config::multi_error_lut_config>(
config.decoder_custom_args);
multi_error_lut_config.lut_error_depth = 2;
cudaq::qec::decoding::config::multi_error_lut_config lut_config;
lut_config.lut_error_depth = 2;
config.decoder_custom_args = lut_config;
multi_config.decoders.push_back(config);
}
std::string config_str = multi_config.to_yaml_str(200);
Expand Down
10 changes: 3 additions & 7 deletions libs/qec/unittests/realtime/app_examples/surface_code-2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,13 @@ void save_dem_to_file(const cudaq::qec::detector_error_model &dem,
config.type = "multi_error_lut";
config.block_size = dem.num_error_mechanisms();
config.syndrome_size = dem.num_detectors();
config.num_syndromes_per_round = numSyndromesPerRound;
config.H_sparse = cudaq::qec::pcm_to_sparse_vec(dem.detector_error_matrix);
config.O_sparse =
cudaq::qec::pcm_to_sparse_vec(dem.observables_flips_matrix);
config.D_sparse = std::vector<int64_t>(det_mat);
config.decoder_custom_args =
cudaq::qec::decoding::config::multi_error_lut_config();
auto &multi_error_lut_config =
std::get<cudaq::qec::decoding::config::multi_error_lut_config>(
config.decoder_custom_args);
multi_error_lut_config.lut_error_depth = 2;
cudaq::qec::decoding::config::multi_error_lut_config lut_config;
lut_config.lut_error_depth = 2;
config.decoder_custom_args = lut_config;
multi_config.decoders.push_back(config);
}
std::string config_str = multi_config.to_yaml_str(200);
Expand Down
10 changes: 3 additions & 7 deletions libs/qec/unittests/realtime/app_examples/surface_code-3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,12 @@ create_decoder_config(uint64_t id, const cudaq::qec::detector_error_model &dem,
config.type = "multi_error_lut";
config.block_size = dem.num_error_mechanisms();
config.syndrome_size = dem.num_detectors();
config.num_syndromes_per_round = numSyndromesPerRound;
config.H_sparse = cudaq::qec::pcm_to_sparse_vec(dem.detector_error_matrix);
config.O_sparse = cudaq::qec::pcm_to_sparse_vec(dem.observables_flips_matrix);
config.D_sparse = det_mat;
config.decoder_custom_args =
cudaq::qec::decoding::config::multi_error_lut_config();
auto &multi_error_lut_config =
std::get<cudaq::qec::decoding::config::multi_error_lut_config>(
config.decoder_custom_args);
multi_error_lut_config.lut_error_depth = 2;
cudaq::qec::decoding::config::multi_error_lut_config lut_config;
lut_config.lut_error_depth = 2;
config.decoder_custom_args = lut_config;
return config;
}

Expand Down
1 change: 0 additions & 1 deletion libs/qec/unittests/realtime/app_examples/surface_code_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ def save_dem_to_file(dem, dem_filename, numSyndromesPerRound, num_logical):
config.type = "multi_error_lut"
config.block_size = dem.num_error_mechanisms()
config.syndrome_size = dem.num_detectors()
config.num_syndromes_per_round = numSyndromesPerRound
config.H_sparse = qec.pcm_to_sparse_vec(dem.detector_error_matrix)
config.O_sparse = qec.pcm_to_sparse_vec(dem.observables_flips_matrix)
config.D_sparse = qec.generate_timelike_sparse_detector_matrix(
Expand Down
7 changes: 3 additions & 4 deletions libs/qec/unittests/test_decoders_yaml.cpp
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,12 @@ create_test_empty_decoder_config(int id) {
config.type = "single_error_lut";
config.block_size = 20;
config.syndrome_size = 10;
config.num_syndromes_per_round = config.syndrome_size;
cudaqx::tensor<uint8_t> H({config.syndrome_size, config.block_size});
cudaqx::tensor<uint8_t> O({2, config.block_size});
config.H_sparse = cudaq::qec::pcm_to_sparse_vec(H);
config.O_sparse = cudaq::qec::pcm_to_sparse_vec(O);
config.D_sparse = cudaq::qec::generate_timelike_sparse_detector_matrix(
config.num_syndromes_per_round, 2, /*include_first_round=*/false);
config.syndrome_size, 2, /*include_first_round=*/false);
return config;
}

Expand Down Expand Up @@ -208,7 +207,6 @@ TEST(DecoderYAMLTest, SlidingWindowDecoder) {
config.type = "sliding_window";
config.block_size = n_cols;
config.syndrome_size = n_rows;
config.num_syndromes_per_round = n_syndromes_per_round;

// Sliding window config
config.decoder_custom_args =
Expand All @@ -219,7 +217,8 @@ TEST(DecoderYAMLTest, SlidingWindowDecoder) {
config.H_sparse = cudaq::qec::pcm_to_sparse_vec(pcm);
config.O_sparse =
cudaq::qec::pcm_to_sparse_vec(cudaqx::tensor<uint8_t>({2, n_cols}));
config.D_sparse.reset();
config.D_sparse = cudaq::qec::generate_timelike_sparse_detector_matrix(
config.syndrome_size, 2, /*include_first_round=*/false);
sw_config.window_size = 1;
sw_config.step_size = 1;
sw_config.num_syndromes_per_round = n_syndromes_per_round;
Expand Down