Skip to content

Commit 14542d1

Browse files
authored
Merge pull request #4 from noajshu/06npdl-codex/modify-dem_from_counts-in-src/common.cc
Fix zero-probability handling
2 parents 05fbd87 + c9f3c52 commit 14542d1

11 files changed

Lines changed: 159 additions & 13 deletions

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ We tested the Tesseract decoder for:
3737
* **Stim and DEM Support:** processes [Stim](https://github.com/quantumlib/stim) circuit files and
3838
[Detector Error Model
3939
(DEM)](https://github.com/quantumlib/Stim/blob/main/doc/file_format_dem_detector_error_model.md)
40-
files with arbitrary error models.
40+
files with arbitrary error models. Zero-probability error instructions are
41+
automatically removed when a DEM is loaded.
4142
* **Parallel Decoding:** uses multithreading to accelerate the decoding process, making it
4243
suitable for large-scale simulations.
4344
* **Efficient Beam Search:** implements a [beam search](https://en.wikipedia.org/wiki/Beam_search)

src/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ cc_test(
139139
cc_test(
140140
name = "common_tests",
141141
srcs = ["common.test.cc"],
142+
copts = OPT_COPTS,
143+
linkopts = OPT_LINKOPTS,
142144
linkstatic = True,
143145
deps = [
144146
":libcommon",

src/common.cc

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,29 @@ stim::DetectorErrorModel common::merge_identical_errors(
113113
return out_dem;
114114
}
115115

116+
stim::DetectorErrorModel common::remove_zero_probability_errors(
117+
const stim::DetectorErrorModel& dem) {
118+
stim::DetectorErrorModel out_dem;
119+
for (const stim::DemInstruction& instruction : dem.flattened().instructions) {
120+
switch (instruction.type) {
121+
case stim::DemInstructionType::DEM_SHIFT_DETECTORS:
122+
assert(false && "unreachable");
123+
break;
124+
case stim::DemInstructionType::DEM_ERROR:
125+
if (instruction.arg_data[0] > 0) {
126+
out_dem.append_dem_instruction(instruction);
127+
}
128+
break;
129+
case stim::DemInstructionType::DEM_DETECTOR:
130+
out_dem.append_dem_instruction(instruction);
131+
break;
132+
default:
133+
assert(false && "unreachable");
134+
}
135+
}
136+
return out_dem;
137+
}
138+
116139
stim::DetectorErrorModel common::dem_from_counts(
117140
stim::DetectorErrorModel& orig_dem, const std::vector<size_t>& error_counts,
118141
size_t num_shots) {
@@ -121,6 +144,17 @@ stim::DetectorErrorModel common::dem_from_counts(
121144
"Error hits array must be the same size as the number of errors in the "
122145
"original DEM.");
123146
}
147+
148+
for (const stim::DemInstruction& instruction :
149+
orig_dem.flattened().instructions) {
150+
if (instruction.type == stim::DemInstructionType::DEM_ERROR &&
151+
instruction.arg_data[0] == 0) {
152+
throw std::invalid_argument(
153+
"dem_from_counts requires DEMs without zero-probability errors. Use"
154+
" remove_zero_probability_errors first.");
155+
}
156+
}
157+
124158
stim::DetectorErrorModel out_dem;
125159
size_t ei = 0;
126160
for (const stim::DemInstruction& instruction :
@@ -130,14 +164,11 @@ stim::DetectorErrorModel common::dem_from_counts(
130164
assert(false && "unreachable");
131165
break;
132166
case stim::DemInstructionType::DEM_ERROR: {
133-
// Ignore zero-probability errors
134-
if (instruction.arg_data[0] > 0) {
135-
double est_probability =
136-
double(error_counts.at(ei)) / double(num_shots);
137-
out_dem.append_error_instruction(est_probability,
138-
instruction.target_data, /*tag=*/"");
139-
++ei;
140-
}
167+
double est_probability =
168+
double(error_counts.at(ei)) / double(num_shots);
169+
out_dem.append_error_instruction(est_probability,
170+
instruction.target_data, /*tag=*/"");
171+
++ei;
141172
break;
142173
}
143174
case stim::DemInstructionType::DEM_DETECTOR: {

src/common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,15 @@ struct Error {
7171
stim::DetectorErrorModel merge_identical_errors(
7272
const stim::DetectorErrorModel& dem);
7373

74+
// Returns a copy of the given error model with any zero-probability DEM_ERROR
75+
// instructions removed.
76+
stim::DetectorErrorModel remove_zero_probability_errors(
77+
const stim::DetectorErrorModel& dem);
78+
7479
// Makes a new dem where the probabilities of errors are estimated from the
7580
// fraction of shots they were used in.
81+
// Throws std::invalid_argument if `orig_dem` contains zero-probability errors;
82+
// call remove_zero_probability_errors first.
7683
stim::DetectorErrorModel dem_from_counts(
7784
stim::DetectorErrorModel& orig_dem, const std::vector<size_t>& error_counts,
7885
size_t num_shots);

src/common.test.cc

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,83 @@ TEST(common, ErrorsStructFromDemInstruction) {
2525
EXPECT_EQ(ES.symptom.detectors, std::vector<int>{1});
2626
EXPECT_EQ(ES.symptom.observables, 0b01);
2727
}
28+
29+
TEST(common, DemFromCountsRejectsZeroProbabilityErrors) {
30+
stim::DetectorErrorModel dem(R"DEM(
31+
error(0.1) D0
32+
error(0) D1
33+
error(0.2) D2
34+
detector(0, 0, 0) D0
35+
detector(0, 0, 0) D1
36+
detector(0, 0, 0) D2
37+
)DEM");
38+
39+
std::vector<size_t> counts{1, 7, 4};
40+
size_t num_shots = 10;
41+
EXPECT_THROW({
42+
common::dem_from_counts(dem, counts, num_shots);
43+
}, std::invalid_argument);
44+
45+
stim::DetectorErrorModel cleaned = common::remove_zero_probability_errors(dem);
46+
stim::DetectorErrorModel out_dem =
47+
common::dem_from_counts(cleaned, std::vector<size_t>{1, 4}, num_shots);
48+
49+
auto flat = out_dem.flattened();
50+
ASSERT_EQ(out_dem.count_errors(), 2);
51+
ASSERT_GE(flat.instructions.size(), 2);
52+
53+
EXPECT_EQ(flat.instructions[0].type,
54+
stim::DemInstructionType::DEM_ERROR);
55+
EXPECT_NEAR(flat.instructions[0].arg_data[0], 0.1, 1e-9);
56+
ASSERT_EQ(flat.instructions[1].type,
57+
stim::DemInstructionType::DEM_ERROR);
58+
EXPECT_NEAR(flat.instructions[1].arg_data[0], 0.4, 1e-9);
59+
}
60+
61+
TEST(common, DemFromCountsSimpleTwoErrors) {
62+
stim::DetectorErrorModel dem(R"DEM(
63+
error(0.25) D0
64+
error(0.35) D1
65+
detector(0, 0, 0) D0
66+
detector(0, 0, 0) D1
67+
)DEM");
68+
69+
std::vector<size_t> counts{5, 7};
70+
size_t num_shots = 20;
71+
stim::DetectorErrorModel out_dem =
72+
common::dem_from_counts(dem, counts, num_shots);
73+
74+
auto flat = out_dem.flattened();
75+
ASSERT_EQ(out_dem.count_errors(), 2);
76+
77+
ASSERT_GE(flat.instructions.size(), 2);
78+
EXPECT_EQ(flat.instructions[0].type,
79+
stim::DemInstructionType::DEM_ERROR);
80+
EXPECT_NEAR(flat.instructions[0].arg_data[0], 0.25, 1e-9);
81+
EXPECT_EQ(flat.instructions[1].type,
82+
stim::DemInstructionType::DEM_ERROR);
83+
EXPECT_NEAR(flat.instructions[1].arg_data[0], 0.35, 1e-9);
84+
}
85+
86+
TEST(common, RemoveZeroProbabilityErrors) {
87+
stim::DetectorErrorModel dem(R"DEM(
88+
error(0.1) D0
89+
error(0) D1
90+
error(0.2) D2
91+
detector(0, 0, 0) D0
92+
detector(0, 0, 0) D1
93+
detector(0, 0, 0) D2
94+
)DEM");
95+
96+
stim::DetectorErrorModel cleaned =
97+
common::remove_zero_probability_errors(dem);
98+
99+
EXPECT_EQ(cleaned.count_errors(), 2);
100+
auto flat = cleaned.flattened();
101+
ASSERT_EQ(flat.instructions[0].type,
102+
stim::DemInstructionType::DEM_ERROR);
103+
EXPECT_NEAR(flat.instructions[0].arg_data[0], 0.1, 1e-9);
104+
ASSERT_EQ(flat.instructions[1].type,
105+
stim::DemInstructionType::DEM_ERROR);
106+
EXPECT_NEAR(flat.instructions[1].arg_data[0], 0.2, 1e-9);
107+
}

src/simplex.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
constexpr size_t T_COORD = 2;
2323

2424
SimplexDecoder::SimplexDecoder(SimplexConfig _config) : config(_config) {
25+
config.dem = common::remove_zero_probability_errors(config.dem);
2526
std::vector<double> detector_t_coords(config.dem.count_detectors());
2627
for (const stim::DemInstruction& instruction :
2728
config.dem.flattened().instructions) {
@@ -30,10 +31,8 @@ SimplexDecoder::SimplexDecoder(SimplexConfig _config) : config(_config) {
3031
assert(false && "unreachable");
3132
break;
3233
case stim::DemInstructionType::DEM_ERROR: {
33-
// Ignore zero-probability errors
34-
if (instruction.arg_data[0] > 0) {
35-
errors.emplace_back(instruction);
36-
}
34+
assert(instruction.arg_data[0] > 0);
35+
errors.emplace_back(instruction);
3736
break;
3837
}
3938
case stim::DemInstructionType::DEM_DETECTOR:

src/simplex_main.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ struct Args {
175175
if (!no_merge_errors) {
176176
config.dem = common::merge_identical_errors(config.dem);
177177
}
178+
config.dem = common::remove_zero_probability_errors(config.dem);
178179

179180
if (sample_num_shots > 0) {
180181
assert(!circuit_path.empty());

src/tesseract.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ double TesseractDecoder::get_detcost(size_t d,
3737
}
3838

3939
TesseractDecoder::TesseractDecoder(TesseractConfig config_) : config(config_) {
40+
config.dem = common::remove_zero_probability_errors(config.dem);
4041
if (config.det_orders.empty()) {
4142
config.det_orders.emplace_back(config.dem.count_detectors());
4243
std::iota(config.det_orders[0].begin(), config.det_orders[0].end(), 0);

src/tesseract.perf.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ void benchmark_tesseract(std::string circuit_path, size_t num_shots) {
7777
/*approximate_disjoint_errors_threshold=*/1,
7878
/*ignore_decomposition_failures=*/false,
7979
/*block_decomposition_from_introducing_remnant_edges=*/false);
80+
dem = common::remove_zero_probability_errors(dem);
8081
TesseractConfig config{dem};
8182
config.det_beam = 20;
8283
config.pqlimit = 10'000'000;
@@ -99,6 +100,7 @@ void benchmark_simplex(std::string circuit_path, size_t num_shots) {
99100
/*approximate_disjoint_errors_threshold=*/1,
100101
/*ignore_decomposition_failures=*/false,
101102
/*block_decomposition_from_introducing_remnant_edges=*/false);
103+
dem = common::remove_zero_probability_errors(dem);
102104
SimplexConfig config{dem};
103105
config.parallelize = true;
104106
SimplexDecoder decoder(config);

src/tesseract.test.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,24 @@ TEST(tesseract, Tesseract_simplex_DEM_exhaustive_test) {
195195
ASSERT_TRUE(return_val);
196196
}
197197
}
198+
199+
TEST(tesseract, DecodersStripZeroProbabilityErrors) {
200+
stim::DetectorErrorModel dem(R"DEM(
201+
error(0.1) D0
202+
error(0) D1
203+
error(0.2) D2
204+
detector(0,0,0) D0
205+
detector(0,0,0) D1
206+
detector(0,0,0) D2
207+
)DEM");
208+
209+
TesseractConfig t_config{dem};
210+
TesseractDecoder t_dec(t_config);
211+
EXPECT_EQ(t_dec.config.dem.count_errors(), 2);
212+
EXPECT_EQ(t_dec.errors.size(), 2);
213+
214+
SimplexConfig s_config{dem};
215+
SimplexDecoder s_dec(s_config);
216+
EXPECT_EQ(s_dec.config.dem.count_errors(), 2);
217+
EXPECT_EQ(s_dec.errors.size(), 2);
218+
}

0 commit comments

Comments
 (0)