Skip to content

Commit 1294710

Browse files
Refactor det order creation, restore optional index-based det ordering (#112)
In the paper we used detector indices to sort them and make the det orders. This was actually an accident where we were trying to use the 3d coordinates (xyt) from Stim but used Stim incorrectly (my bad). This got fixed in a77f5e7 where we started using the actual xyt coordinates. This seems to offer a better tradeoff between beam and error rate, but also led to issue #103 because at the same large beam we get worse performance (likely because there are more distinct orderings which means more ways to get stuck) We also added BFS det ordering in bf62f84 and then made it the default in 0471d4e Unfortunately due to a bug in p-ranav's argparse: p-ranav/argparse#413 flags with implicit_value(false) do not work with store_into, which means the `--no-det-order-bfs` arg had no effect. Here, we refactor the det order creation to have 3 arguments: ``` --det-order-bfs Use BFS-based detector ordering (default if no method specified) --det-order-index Randomly choose increasing or decreasing detector index order --det-order-coordinate Random geometric detector orientation ordering ``` We also eliminate `--no-det-order-bfs` which had no effect anyways. This is an example where the `--det-order-index` is faster: ``` bazel build src:all ./bazel-bin/src/tesseract --sample-num-shots 100 --circuit testdata/colorcodes/r\=11\,d\=11\,p\=0.001\,noise\=si1000\,c\=superdense_color_code_X\,q\=181\,gates\=cz.stim --sample-seed 71734 --threads 64 --beam 20 --beam-climbing --num-det-orders 21 --pqlimit 1000000 --det-order-seed 2384257 --print-stats --no-revisit-dets --det-order-coordinate num_shots = 100 num_low_confidence = 0 num_errors = 0 total_time_seconds = 340.9752209999999 ./bazel-bin/src/tesseract --sample-num-shots 100 --circuit testdata/colorcodes/r\=11\,d\=11\,p\=0.001\,noise\=si1000\,c\=superdense_color_code_X\,q\=181\,gates\=cz.stim --sample-seed 71734 --threads 64 --beam 20 --beam-climbing --num-det-orders 21 --pqlimit 1000000 --det-order-seed 2384257 --print-stats --no-revisit-dets --det-order-index num_shots = 100 num_low_confidence = 0 num_errors = 0 total_time_seconds = 142.994753 ``` This is achieved via a new enum class `DetOrder` in utils. It is also now exposed via python API: ``` tesseract_decoder.utils.build_det_orders( _DETECTOR_ERROR_MODEL, num_det_orders=1, method=tesseract_decoder.utils.DetOrder.DetIndex, seed=0, ``` Fixes #103 --------- Co-authored-by: Noureldin <noureldinyosri@gmail.com>
1 parent 127d840 commit 1294710

File tree

6 files changed

+80
-33
lines changed

6 files changed

+80
-33
lines changed

src/py/utils_test.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,25 @@ def test_build_det_orders():
5050
) == [[0, 1]]
5151

5252

53-
def test_build_det_orders_no_bfs():
53+
def test_build_det_orders_coordinate():
5454
assert tesseract_decoder.utils.build_det_orders(
55-
_DETECTOR_ERROR_MODEL, num_det_orders=1, det_order_bfs=False, seed=0
55+
_DETECTOR_ERROR_MODEL,
56+
num_det_orders=1,
57+
method=tesseract_decoder.utils.DetOrder.DetCoordinate,
58+
seed=0,
5659
) == [[0, 1]]
5760

5861

62+
def test_build_det_orders_index():
63+
res = tesseract_decoder.utils.build_det_orders(
64+
_DETECTOR_ERROR_MODEL,
65+
num_det_orders=1,
66+
method=tesseract_decoder.utils.DetOrder.DetIndex,
67+
seed=0,
68+
)
69+
assert res == [[0, 1]] or res == [[1, 0]]
70+
71+
5972
def test_get_errors_from_dem():
6073
expected = "Error{cost=1.945910, symptom=Symptom{detectors=[0], observables=[]}}, Error{cost=0.510826, symptom=Symptom{detectors=[0 1], observables=[]}}, Error{cost=1.098612, symptom=Symptom{detectors=[1], observables=[]}}"
6174
assert (

src/tesseract.pybind.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ TesseractConfig tesseract_config_maker_no_dem(
4141
double det_penalty = 0.0, bool create_visualization = false) {
4242
stim::DetectorErrorModel empty_dem;
4343
if (det_orders.empty()) {
44-
det_orders = build_det_orders(empty_dem, 20, /*det_order_bfs=*/true, 2384753);
44+
det_orders = build_det_orders(empty_dem, 20, DetOrder::DetBFS, 2384753);
4545
}
4646
return TesseractConfig({empty_dem, det_beam, beam_climbing, no_revisit_dets,
4747
at_most_two_errors_per_detector, verbose, merge_errors, pqlimit,
@@ -57,7 +57,7 @@ TesseractConfig tesseract_config_maker(
5757
double det_penalty = 0.0, bool create_visualization = false) {
5858
stim::DetectorErrorModel input_dem = parse_py_object<stim::DetectorErrorModel>(dem);
5959
if (det_orders.empty()) {
60-
det_orders = build_det_orders(input_dem, 20, true, 2384753);
60+
det_orders = build_det_orders(input_dem, 20, DetOrder::DetBFS, 2384753);
6161
}
6262
return TesseractConfig({input_dem, det_beam, beam_climbing, no_revisit_dets,
6363
at_most_two_errors_per_detector, verbose, merge_errors, pqlimit,

src/tesseract_main.cc

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ struct Args {
3434
// Manifold orientation options
3535
uint64_t det_order_seed;
3636
size_t num_det_orders = 10;
37-
bool det_order_bfs = true;
37+
bool det_order_bfs = false;
38+
bool det_order_index = false;
39+
bool det_order_coordinate = false;
3840

3941
// Sampling options
4042
size_t sample_num_shots = 0;
@@ -88,6 +90,12 @@ struct Args {
8890
throw std::invalid_argument("Must provide at least one of --circuit or --dem");
8991
}
9092

93+
int det_order_flags = int(det_order_bfs) + int(det_order_index) + int(det_order_coordinate);
94+
if (det_order_flags > 1) {
95+
throw std::invalid_argument(
96+
"Only one of --det-order-bfs, --det-order-index, or --det-order-coordinate may be set.");
97+
}
98+
9199
int num_data_sources = int(sample_num_shots > 0) + int(!in_fname.empty());
92100
if (num_data_sources != 1) {
93101
throw std::invalid_argument("Requires exactly 1 source of shots.");
@@ -180,8 +188,13 @@ struct Args {
180188
std::cout << ")" << std::endl;
181189
}
182190
}
183-
config.det_orders =
184-
build_det_orders(config.dem, num_det_orders, det_order_bfs, det_order_seed);
191+
DetOrder order = DetOrder::DetBFS;
192+
if (det_order_index) {
193+
order = DetOrder::DetIndex;
194+
} else if (det_order_coordinate) {
195+
order = DetOrder::DetCoordinate;
196+
}
197+
config.det_orders = build_det_orders(config.dem, num_det_orders, order, det_order_seed);
185198
}
186199

187200
if (sample_num_shots > 0) {
@@ -296,21 +309,18 @@ int main(int argc, char* argv[]) {
296309
.metavar("N")
297310
.default_value(size_t(1))
298311
.store_into(args.num_det_orders);
299-
program.add_argument("--no-det-order-bfs")
300-
.help("Disable BFS-based detector ordering and use geometric orientation")
301-
.default_value(true)
302-
.implicit_value(false)
303-
.store_into(args.det_order_bfs);
304312
program.add_argument("--det-order-bfs")
305-
.action([&](auto const&) {
306-
std::cout << "BFS-based detector ordering is the default now; "
307-
"--det-order-bfs is ignored."
308-
<< std::endl;
309-
})
310-
.default_value(true)
311-
.implicit_value(true)
312-
.store_into(args.det_order_bfs)
313-
.hidden();
313+
.help("Use BFS-based detector ordering (default if no method specified)")
314+
.flag()
315+
.store_into(args.det_order_bfs);
316+
program.add_argument("--det-order-index")
317+
.help("Randomly choose increasing or decreasing detector index order")
318+
.flag()
319+
.store_into(args.det_order_index);
320+
program.add_argument("--det-order-coordinate")
321+
.help("Random geometric detector orientation ordering")
322+
.flag()
323+
.store_into(args.det_order_coordinate);
314324
program.add_argument("--det-order-seed")
315325
.help(
316326
"Seed used when initializing the random detector traversal "

src/utils.cc

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,15 @@ std::vector<std::vector<size_t>> build_detector_graph(const stim::DetectorErrorM
8383
}
8484

8585
std::vector<std::vector<size_t>> build_det_orders(const stim::DetectorErrorModel& dem,
86-
size_t num_det_orders, bool det_order_bfs,
86+
size_t num_det_orders, DetOrder method,
8787
uint64_t seed) {
8888
std::vector<std::vector<size_t>> det_orders(num_det_orders);
8989
std::mt19937_64 rng(seed);
9090
std::normal_distribution<double> dist(0, 1);
9191

9292
auto detector_coords = get_detector_coords(dem);
9393

94-
if (det_order_bfs) {
94+
if (method == DetOrder::DetBFS) {
9595
auto graph = build_detector_graph(dem);
9696
std::uniform_int_distribution<size_t> dist_det(0, graph.size() - 1);
9797
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
@@ -131,7 +131,7 @@ std::vector<std::vector<size_t>> build_det_orders(const stim::DetectorErrorModel
131131
}
132132
det_orders[det_order] = inv_perm;
133133
}
134-
} else {
134+
} else if (method == DetOrder::DetCoordinate) {
135135
std::vector<double> inner_products(dem.count_detectors());
136136
if (!detector_coords.size() || !detector_coords.at(0).size()) {
137137
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
@@ -163,6 +163,19 @@ std::vector<std::vector<size_t>> build_det_orders(const stim::DetectorErrorModel
163163
det_orders[det_order] = inv_perm;
164164
}
165165
}
166+
} else if (method == DetOrder::DetIndex) {
167+
std::uniform_int_distribution<int> dist_bool(0, 1);
168+
size_t n = dem.count_detectors();
169+
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
170+
det_orders[det_order].resize(n);
171+
if (dist_bool(rng)) {
172+
for (size_t i = 0; i < n; ++i) {
173+
det_orders[det_order][i] = n - 1 - i;
174+
}
175+
} else {
176+
std::iota(det_orders[det_order].begin(), det_orders[det_order].end(), 0);
177+
}
178+
}
166179
}
167180
return det_orders;
168181
}

src/utils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ std::vector<std::vector<double>> get_detector_coords(const stim::DetectorErrorMo
3434
// in the model activates them both.
3535
std::vector<std::vector<size_t>> build_detector_graph(const stim::DetectorErrorModel& dem);
3636

37+
enum class DetOrder { DetBFS, DetIndex, DetCoordinate };
38+
3739
std::vector<std::vector<size_t>> build_det_orders(const stim::DetectorErrorModel& dem,
38-
size_t num_det_orders, bool det_order_bfs = true,
40+
size_t num_det_orders,
41+
DetOrder method = DetOrder::DetBFS,
3942
uint64_t seed = 0);
4043

4144
const double INF = std::numeric_limits<double>::infinity();

src/utils.pybind.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ void add_utils_module(py::module& root) {
3232
m.attr("INF") = INF;
3333
m.doc() = "A representation of infinity for floating point numbers.";
3434

35+
py::enum_<DetOrder>(m, "DetOrder", "Detector ordering methods")
36+
.value("DetBFS", DetOrder::DetBFS)
37+
.value("DetIndex", DetOrder::DetIndex)
38+
.value("DetCoordinate", DetOrder::DetCoordinate)
39+
.export_values();
40+
3541
m.def(
3642
"get_detector_coords",
3743
[](py::object dem) {
@@ -79,11 +85,11 @@ void add_utils_module(py::module& root) {
7985
)pbdoc");
8086
m.def(
8187
"build_det_orders",
82-
[](py::object dem, size_t num_det_orders, bool det_order_bfs, uint64_t seed) {
88+
[](py::object dem, size_t num_det_orders, DetOrder method, uint64_t seed) {
8389
auto input_dem = parse_py_object<stim::DetectorErrorModel>(dem);
84-
return build_det_orders(input_dem, num_det_orders, det_order_bfs, seed);
90+
return build_det_orders(input_dem, num_det_orders, method, seed);
8591
},
86-
py::arg("dem"), py::arg("num_det_orders"), py::arg("det_order_bfs") = true,
92+
py::arg("dem"), py::arg("num_det_orders"), py::arg("method") = DetOrder::DetBFS,
8793
py::arg("seed") = 0, R"pbdoc(
8894
Generates various detector orderings for decoding.
8995
@@ -93,17 +99,19 @@ void add_utils_module(py::module& root) {
9399
The detector error model to generate orders for.
94100
num_det_orders : int
95101
The number of detector orderings to generate.
96-
det_order_bfs : bool, default=True
97-
If True, uses a Breadth-First Search (BFS) to generate
98-
the orders. If False, uses a randomized ordering.
102+
method : tesseract_decoder.utils.DetOrder, default=tesseract_decoder.utils.DetOrder.DetBFS
103+
Strategy for ordering detectors. ``DetBFS`` performs a breadth-first
104+
traversal, ``DetCoordinate`` uses randomized geometric orientations,
105+
and ``DetIndex`` chooses either increasing or decreasing detector
106+
index order at random.
99107
seed : int, default=0
100108
A seed for the random number generator.
101109
102110
Returns
103111
-------
104112
list[list[int]]
105-
A list of detector orderings. Each inner list is a
106-
permutation of the detector indices.
113+
A list of detector orderings. Each inner list maps a detector index
114+
to its position in the ordering.
107115
)pbdoc");
108116
m.def(
109117
"get_errors_from_dem",

0 commit comments

Comments
 (0)