Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
78e5ec8
Add visualization library to CMake build
noajshu Aug 11, 2025
482cb81
Merge pull request #36 from noajshu/codex/update-cmakelists-to-includ…
noajshu Aug 11, 2025
106ce02
Merge branch 'main' into main
noajshu Aug 12, 2025
b4c9d08
Merge remote-tracking branch 'quantum/main'
noajshu Aug 12, 2025
5c211bc
Fix CMake Python module placement and add agent instructions
noajshu Aug 13, 2025
d93e31d
Merge pull request #37 from noajshu/codex/add-agents.md-documentation…
noajshu Aug 13, 2025
7c10268
Allow decode_to_errors to accept bitstring
noajshu Aug 20, 2025
8a27808
Merge pull request #38 from noajshu/codex/update-decode_to_errors-to-…
noajshu Aug 20, 2025
1e52d57
Merge branch 'quantumlib:main' into main
noajshu Aug 20, 2025
c34dd80
clang-format
noajshu Aug 20, 2025
24c0d69
Update src/tesseract.pybind.h
noajshu Aug 20, 2025
96849b6
remove stringstream
noajshu Aug 20, 2025
3d486b0
more fixes
noajshu Aug 20, 2025
20e506d
Merge remote-tracking branch 'quantum'
noajshu Aug 29, 2025
61867e2
Handle explicit DetIndex case
noajshu Aug 29, 2025
a3e2663
Merge pull request #39 from noajshu/codex/create-detorder-enum-and-up…
noajshu Aug 29, 2025
7309377
Merge remote-tracking branch 'quantum'
noajshu Aug 29, 2025
454521f
expand det index test
noajshu Aug 29, 2025
18b12cd
Merge pull request #40 from noajshu/codex/refactor-test_build_det_ord…
noajshu Aug 29, 2025
41f7c3e
update beam climbing for when det orders > beam+1
noajshu Aug 29, 2025
e9a0774
Merge remote-tracking branch 'origin'
noajshu Aug 29, 2025
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
12 changes: 9 additions & 3 deletions src/py/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
"""
)

_DETECTOR_ERROR_MODEL_10 = stim.DetectorErrorModel(
"\n".join(f"error(0.1) D{i}" for i in range(10))
)


def test_module_has_global_constants():
assert tesseract_decoder.utils.EPSILON <= 1e-7
Expand All @@ -44,7 +48,7 @@ def test_build_detector_graph():
]


def test_build_det_orders():
def test_build_det_orders_bfs():
assert tesseract_decoder.utils.build_det_orders(
_DETECTOR_ERROR_MODEL, num_det_orders=1, seed=0
) == [[0, 1]]
Expand All @@ -61,12 +65,14 @@ def test_build_det_orders_coordinate():

def test_build_det_orders_index():
res = tesseract_decoder.utils.build_det_orders(
_DETECTOR_ERROR_MODEL,
_DETECTOR_ERROR_MODEL_10,
num_det_orders=1,
method=tesseract_decoder.utils.DetOrder.DetIndex,
seed=0,
)
assert res == [[0, 1]] or res == [[1, 0]]
expected_asc = list(range(10))
expected_desc = list(range(9, -1, -1))
assert res == [expected_asc] or res == [expected_desc]


def test_get_errors_from_dem():
Expand Down
10 changes: 8 additions & 2 deletions src/tesseract.cc
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,10 @@ void TesseractDecoder::decode_to_errors(const std::vector<uint64_t>& detections)
}

if (config.beam_climbing) {
for (int beam = config.det_beam; beam >= 0; --beam) {
size_t detector_order = beam % config.det_orders.size();
int beam = 0;
int detector_order = 0;
for (int trial = 0; trial < std::max(config.det_beam + 1, int(config.det_orders.size()));
++trial) {
decode_to_errors(detections, detector_order, beam);
double local_cost = cost_from_errors(predicted_errors_buffer);
if (!low_confidence_flag && local_cost < best_cost) {
Expand All @@ -215,6 +217,10 @@ void TesseractDecoder::decode_to_errors(const std::vector<uint64_t>& detections)
<< " and obs_mask " << get_flipped_observables(predicted_errors_buffer)
<< ". Best cost so far: " << best_cost << std::endl;
}
beam += 1;
detector_order += 1;
beam %= (config.det_beam + 1);
detector_order %= config.det_orders.size();
}
} else {
for (size_t detector_order = 0; detector_order < config.det_orders.size(); ++detector_order) {
Expand Down
186 changes: 103 additions & 83 deletions src/utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,104 +82,124 @@ std::vector<std::vector<size_t>> build_detector_graph(const stim::DetectorErrorM
return neighbors;
}

std::vector<std::vector<size_t>> build_det_orders(const stim::DetectorErrorModel& dem,
size_t num_det_orders, DetOrder method,
uint64_t seed) {
static std::vector<std::vector<size_t>> build_det_orders_bfs(const stim::DetectorErrorModel& dem,
size_t num_det_orders,
std::mt19937_64& rng) {
std::vector<std::vector<size_t>> det_orders(num_det_orders);
std::mt19937_64 rng(seed);
std::normal_distribution<double> dist(0, 1);

auto detector_coords = get_detector_coords(dem);

if (method == DetOrder::DetBFS) {
auto graph = build_detector_graph(dem);
std::uniform_int_distribution<size_t> dist_det(0, graph.size() - 1);
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
std::vector<size_t> perm;
perm.reserve(graph.size());
std::vector<char> visited(graph.size(), false);
std::queue<size_t> q;
size_t start = dist_det(rng);
while (perm.size() < graph.size()) {
if (!visited[start]) {
visited[start] = true;
q.push(start);
perm.push_back(start);
}
while (!q.empty()) {
size_t cur = q.front();
q.pop();
auto neigh = graph[cur];
std::shuffle(neigh.begin(), neigh.end(), rng);
for (size_t n : neigh) {
if (!visited[n]) {
visited[n] = true;
q.push(n);
perm.push_back(n);
}
auto graph = build_detector_graph(dem);
std::uniform_int_distribution<size_t> dist_det(0, graph.size() - 1);
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
std::vector<size_t> perm;
perm.reserve(graph.size());
std::vector<char> visited(graph.size(), false);
std::queue<size_t> q;
size_t start = dist_det(rng);
while (perm.size() < graph.size()) {
if (!visited[start]) {
visited[start] = true;
q.push(start);
perm.push_back(start);
}
while (!q.empty()) {
size_t cur = q.front();
q.pop();
auto neigh = graph[cur];
std::shuffle(neigh.begin(), neigh.end(), rng);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we are shuffling here? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is to generate diversity in the ensemble!

for (size_t n : neigh) {
if (!visited[n]) {
visited[n] = true;
q.push(n);
perm.push_back(n);
}
}
if (perm.size() < graph.size()) {
do {
start = dist_det(rng);
} while (visited[start]);
}
}
std::vector<size_t> inv_perm(graph.size());
for (size_t i = 0; i < perm.size(); ++i) {
inv_perm[perm[i]] = i;
if (perm.size() < graph.size()) {
do {
start = dist_det(rng);
} while (visited[start]);
}
det_orders[det_order] = inv_perm;
}
} else if (method == DetOrder::DetCoordinate) {
std::vector<double> inner_products(dem.count_detectors());
if (!detector_coords.size() || !detector_coords.at(0).size()) {
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
det_orders[det_order].resize(dem.count_detectors());
std::iota(det_orders[det_order].begin(), det_orders[det_order].end(), 0);
}
} else {
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
std::vector<double> orientation_vector;
for (size_t i = 0; i < detector_coords.at(0).size(); ++i) {
orientation_vector.push_back(dist(rng));
}
std::vector<size_t> inv_perm(graph.size());
for (size_t i = 0; i < perm.size(); ++i) {
inv_perm[perm[i]] = i;
}
det_orders[det_order] = inv_perm;
}
return det_orders;
}

for (size_t i = 0; i < detector_coords.size(); ++i) {
inner_products[i] = 0;
for (size_t j = 0; j < orientation_vector.size(); ++j) {
inner_products[i] += detector_coords[i][j] * orientation_vector[j];
}
}
std::vector<size_t> perm(dem.count_detectors());
std::iota(perm.begin(), perm.end(), 0);
std::sort(perm.begin(), perm.end(), [&](const size_t& i, const size_t& j) {
return inner_products[i] > inner_products[j];
});
std::vector<size_t> inv_perm(dem.count_detectors());
for (size_t i = 0; i < perm.size(); ++i) {
inv_perm[perm[i]] = i;
}
det_orders[det_order] = inv_perm;
static std::vector<std::vector<size_t>> build_det_orders_coordinate(
const stim::DetectorErrorModel& dem, size_t num_det_orders, std::mt19937_64& rng) {
std::vector<std::vector<size_t>> det_orders(num_det_orders);
auto detector_coords = get_detector_coords(dem);
std::vector<double> inner_products(dem.count_detectors());
std::normal_distribution<double> dist(0, 1);
if (detector_coords.empty() || detector_coords.at(0).empty()) {
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
det_orders[det_order].resize(dem.count_detectors());
std::iota(det_orders[det_order].begin(), det_orders[det_order].end(), 0);
}
return det_orders;
}
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
std::vector<double> orientation_vector;
for (size_t i = 0; i < detector_coords.at(0).size(); ++i) {
orientation_vector.push_back(dist(rng));
}
for (size_t i = 0; i < detector_coords.size(); ++i) {
inner_products[i] = 0;
for (size_t j = 0; j < orientation_vector.size(); ++j) {
inner_products[i] += detector_coords[i][j] * orientation_vector[j];
}
}
} else if (method == DetOrder::DetIndex) {
std::uniform_int_distribution<int> dist_bool(0, 1);
size_t n = dem.count_detectors();
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
det_orders[det_order].resize(n);
if (dist_bool(rng)) {
for (size_t i = 0; i < n; ++i) {
det_orders[det_order][i] = n - 1 - i;
}
} else {
std::iota(det_orders[det_order].begin(), det_orders[det_order].end(), 0);
std::vector<size_t> perm(dem.count_detectors());
std::iota(perm.begin(), perm.end(), 0);
std::sort(perm.begin(), perm.end(), [&](const size_t& i, const size_t& j) {
return inner_products[i] > inner_products[j];
});
std::vector<size_t> inv_perm(dem.count_detectors());
for (size_t i = 0; i < perm.size(); ++i) {
inv_perm[perm[i]] = i;
}
det_orders[det_order] = inv_perm;
}
return det_orders;
}

static std::vector<std::vector<size_t>> build_det_orders_index(const stim::DetectorErrorModel& dem,
size_t num_det_orders,
std::mt19937_64& rng) {
std::vector<std::vector<size_t>> det_orders(num_det_orders);
std::uniform_int_distribution<int> dist_bool(0, 1);
size_t n = dem.count_detectors();
for (size_t det_order = 0; det_order < num_det_orders; ++det_order) {
det_orders[det_order].resize(n);
if (dist_bool(rng)) {
for (size_t i = 0; i < n; ++i) {
det_orders[det_order][i] = n - 1 - i;
}
} else {
std::iota(det_orders[det_order].begin(), det_orders[det_order].end(), 0);
}
}
return det_orders;
}

std::vector<std::vector<size_t>> build_det_orders(const stim::DetectorErrorModel& dem,
size_t num_det_orders, DetOrder method,
uint64_t seed) {
std::mt19937_64 rng(seed);
switch (method) {
case DetOrder::DetBFS:
return build_det_orders_bfs(dem, num_det_orders, rng);
case DetOrder::DetCoordinate:
return build_det_orders_coordinate(dem, num_det_orders, rng);
case DetOrder::DetIndex:
return build_det_orders_index(dem, num_det_orders, rng);
}
throw std::invalid_argument("Unknown det order method");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should have one of them as default 🤔


bool sampling_from_dem(uint64_t seed, size_t num_shots, stim::DetectorErrorModel dem,
std::vector<stim::SparseShot>& shots) {
stim::DemSampler<stim::MAX_BITWORD_WIDTH> sampler(dem, std::mt19937_64{seed}, num_shots);
Expand Down
Loading