From 4147306b50427bf69455da802d2ba41f514bfeaf Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Wed, 27 Mar 2024 00:27:25 -0700 Subject: [PATCH] Fix interleaved feedback being removed incorrectly - When a pure-feedback instruction was between two instructions of the same type, they were fused when partially reversed when the feedback instruction was removed - Fixed by turning off fusion while the circuit is only partially reversed - Fixes https://github.com/quantumlib/Stim/issues/730 --- src/stim/circuit/circuit.cc | 8 ++--- src/stim/circuit/circuit.h | 4 +-- .../simulators/transform_without_feedback.cc | 2 +- .../transform_without_feedback.test.cc | 30 +++++++++++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/stim/circuit/circuit.cc b/src/stim/circuit/circuit.cc index 8ee1cf832..3d88616a4 100644 --- a/src/stim/circuit/circuit.cc +++ b/src/stim/circuit/circuit.cc @@ -404,8 +404,8 @@ void Circuit::append_from_text(const char *text) { READ_CONDITION::READ_UNTIL_END_OF_FILE); } -void Circuit::safe_append(const CircuitInstruction &operation) { - safe_append(operation.gate_type, operation.targets, operation.args); +void Circuit::safe_append(const CircuitInstruction &operation, bool block_fusion) { + safe_append(operation.gate_type, operation.targets, operation.args, block_fusion); } void Circuit::safe_append_ua(const std::string &gate_name, const std::vector &targets, double singleton_arg) { @@ -433,7 +433,7 @@ void Circuit::safe_append_u( safe_append(gate.id, converted, args); } -void Circuit::safe_append(GateType gate_type, SpanRef targets, SpanRef args) { +void Circuit::safe_append(GateType gate_type, SpanRef targets, SpanRef args, bool block_fusion) { auto flags = GATE_DATA[gate_type].flags; if (flags & GATE_IS_BLOCK) { throw std::invalid_argument("Can't append a block like a normal operation."); @@ -446,7 +446,7 @@ void Circuit::safe_append(GateType gate_type, SpanRef targets, to_add.args = arg_buf.take_copy(to_add.args); to_add.targets = target_buf.take_copy(to_add.targets); - if (!operations.empty() && operations.back().can_fuse(to_add)) { + if (!block_fusion && !operations.empty() && operations.back().can_fuse(to_add)) { // Extend targets of last gate. fuse_data(operations.back().targets, to_add.targets, target_buf); } else { diff --git a/src/stim/circuit/circuit.h b/src/stim/circuit/circuit.h index a52945150..c69e4a5d4 100644 --- a/src/stim/circuit/circuit.h +++ b/src/stim/circuit/circuit.h @@ -105,14 +105,14 @@ struct Circuit { Circuit &operator*=(uint64_t repetitions); /// Safely adds an operation at the end of the circuit, copying its data into the circuit's jagged data as needed. - void safe_append(const CircuitInstruction &operation); + void safe_append(const CircuitInstruction &operation, bool block_fusion = false); /// Safely adds an operation at the end of the circuit, copying its data into the circuit's jagged data as needed. void safe_append_ua(const std::string &gate_name, const std::vector &targets, double singleton_arg); /// Safely adds an operation at the end of the circuit, copying its data into the circuit's jagged data as needed. void safe_append_u( const std::string &gate_name, const std::vector &targets, const std::vector &args = {}); /// Safely adds an operation at the end of the circuit, copying its data into the circuit's jagged data as needed. - void safe_append(GateType gate_type, SpanRef targets, SpanRef args); + void safe_append(GateType gate_type, SpanRef targets, SpanRef args, bool block_fusion = false); /// Safely copies a repeat block to the end of the circuit. void append_repeat_block(uint64_t repeat_count, const Circuit &body); /// Safely moves a repeat block to the end of the circuit. diff --git a/src/stim/simulators/transform_without_feedback.cc b/src/stim/simulators/transform_without_feedback.cc index 908b92816..24151906b 100644 --- a/src/stim/simulators/transform_without_feedback.cc +++ b/src/stim/simulators/transform_without_feedback.cc @@ -127,7 +127,7 @@ struct WithoutFeedbackHelper { if (GATE_DATA[op.gate_type].flags & GATE_CAN_TARGET_BITS) { undo_feedback_capable_pcp_operation(op); } else { - reversed_semi_flattened_output.safe_append(op); + reversed_semi_flattened_output.safe_append(op, true); tracker.undo_gate(op); } } diff --git a/src/stim/simulators/transform_without_feedback.test.cc b/src/stim/simulators/transform_without_feedback.test.cc index 7743d4e9a..85071f2d4 100644 --- a/src/stim/simulators/transform_without_feedback.test.cc +++ b/src/stim/simulators/transform_without_feedback.test.cc @@ -144,3 +144,33 @@ TEST(circuit_with_inlined_feedback, mpp) { DETECTOR rec[-3] rec[-1] )CIRCUIT")); } + +TEST(circuit_with_inlined_feedback, interleaved_feedback_does_not_reorder_operations) { + ASSERT_EQ(circuit_with_inlined_feedback(Circuit(R"CIRCUIT( + H 0 + CZ + H 1 + )CIRCUIT")), Circuit(R"CIRCUIT( + H 0 1 + )CIRCUIT")); + + ASSERT_EQ(circuit_with_inlined_feedback(Circuit(R"CIRCUIT( + M 0 + CX + M 1 + )CIRCUIT")), Circuit(R"CIRCUIT( + M 0 1 + )CIRCUIT")); + + ASSERT_EQ(circuit_with_inlined_feedback(Circuit(R"CIRCUIT( + M 0 1 + CX + M 2 + CX rec[-1] 3 + M 3 + DETECTOR rec[-1] + )CIRCUIT")), Circuit(R"CIRCUIT( + M 0 1 2 3 + DETECTOR rec[-2] rec[-1] + )CIRCUIT")); +}