From f5050041ad0900ded3824b1257e57eef8cc5b1c9 Mon Sep 17 00:00:00 2001 From: Abbas Bracken Ziad Date: Thu, 26 Feb 2026 18:41:23 +0000 Subject: [PATCH] Add support for comparing DEMs up to instruction ordering The current `==` overload of the `DetectorErrorModel` only returns true if the DEMs have the same instruction ordering. It is sometimes useful to be able to compare two DEMs for equality when this is not the case, so I implemented an `equal_up_to_instruction_ordering` member function. I opted not to change the `==` operator because (a) checking for equality up to instruction ordering requires sorting so turns an O(n) op into an O(n log n) op; and (b) it's possible some of the backend simulation code depends on the ordering constraint. Signed-off-by: Abbas Bracken Ziad --- src/stim/dem/detector_error_model.cc | 29 +++++++++++++++++++++++ src/stim/dem/detector_error_model.h | 7 ++++++ src/stim/dem/detector_error_model.test.cc | 18 ++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/stim/dem/detector_error_model.cc b/src/stim/dem/detector_error_model.cc index 6f663dbf6..2e3167c7f 100644 --- a/src/stim/dem/detector_error_model.cc +++ b/src/stim/dem/detector_error_model.cc @@ -16,9 +16,11 @@ #include "stim/dem/detector_error_model.h" +#include #include #include #include +#include #include "stim/util_bot/str_util.h" @@ -793,3 +795,30 @@ DetectorErrorModel DetectorErrorModel::without_tags() const { } return result; } + +bool DetectorErrorModel::equal_up_to_instruction_ordering(const DetectorErrorModel &other) const { + if (instructions.size() != other.instructions.size()) { + return false; + } + + auto get_sorted_indices = [](const std::vector &ins) { + std::vector indices(ins.size()); + std::iota(indices.begin(), indices.end(), 0); + std::sort(indices.begin(), indices.end(), [&ins](size_t i, size_t j) { + return ins[i] < ins[j]; + }); + return indices; + }; + + // sort the indices to avoid copying instructions + auto sorted_lhs_indices = get_sorted_indices(instructions); + auto sorted_rhs_indices = get_sorted_indices(other.instructions); + + for (size_t i = 0; i < sorted_lhs_indices.size(); i++) { + if (!(instructions[sorted_lhs_indices[i]] == other.instructions[sorted_rhs_indices[i]])) { + return false; + } + } + + return true; +} diff --git a/src/stim/dem/detector_error_model.h b/src/stim/dem/detector_error_model.h index 0f5d53d55..2f4edc806 100644 --- a/src/stim/dem/detector_error_model.h +++ b/src/stim/dem/detector_error_model.h @@ -135,6 +135,13 @@ struct DetectorErrorModel { /// Returns an equivalent detector error model with no repeat blocks or detector_shift instructions. DetectorErrorModel flattened() const; + + /// Returns true if the detector error model is equal to the other detector error model up to instruction ordering. + /// + /// For example, error(0.01) D0 error(0.002) D1 L0 is considered the same as error(0.002) D1 L0 error(0.01) D0. + /// + /// Note: requires O(n log n) time due to sorting. Prefer `==` when you know the instructions are in the same order. + bool equal_up_to_instruction_ordering(const DetectorErrorModel &other) const; }; void print_detector_error_model(std::ostream &out, const DetectorErrorModel &v, size_t indent); diff --git a/src/stim/dem/detector_error_model.test.cc b/src/stim/dem/detector_error_model.test.cc index 429642c90..484ad1e9d 100644 --- a/src/stim/dem/detector_error_model.test.cc +++ b/src/stim/dem/detector_error_model.test.cc @@ -894,3 +894,21 @@ TEST(detector_error_model, parse_windows_newlines) { DetectorErrorModel("error(0.125) D0\r\ndetector(5) D10\r\n"), DetectorErrorModel("error(0.125) D0\r\ndetector(5) D10\r\n")); } + +TEST(detector_error_model, equal_up_to_instruction_ordering) { + DetectorErrorModel lhs(R"MODEL( + error(0.01) D0 + error(0.002) D1 L0 + detector(5, 10) D0 + detector(5, 15) D1 + logical_observable L0 + )MODEL"); + DetectorErrorModel rhs(R"MODEL( + error(0.002) D1 L0 + error(0.01) D0 + detector(5, 15) D1 + detector(5, 10) D0 + logical_observable L0 + )MODEL"); + EXPECT_TRUE(lhs.equal_up_to_instruction_ordering(rhs)); +}