Skip to content

Commit

Permalink
Temporarily mark time-reversing feedback as not supported (#766)
Browse files Browse the repository at this point in the history
Mitigates #764
  • Loading branch information
Strilanc authored Sep 10, 2024
1 parent e5172a4 commit b6174b7
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 93 deletions.
70 changes: 37 additions & 33 deletions doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.FlipSimulator.__init__`](#stim.FlipSimulator.__init__)
- [`stim.FlipSimulator.batch_size`](#stim.FlipSimulator.batch_size)
- [`stim.FlipSimulator.broadcast_pauli_errors`](#stim.FlipSimulator.broadcast_pauli_errors)
- [`stim.FlipSimulator.clear`](#stim.FlipSimulator.clear)
- [`stim.FlipSimulator.copy`](#stim.FlipSimulator.copy)
- [`stim.FlipSimulator.do`](#stim.FlipSimulator.do)
- [`stim.FlipSimulator.get_detector_flips`](#stim.FlipSimulator.get_detector_flips)
Expand All @@ -198,7 +199,6 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.FlipSimulator.num_observables`](#stim.FlipSimulator.num_observables)
- [`stim.FlipSimulator.num_qubits`](#stim.FlipSimulator.num_qubits)
- [`stim.FlipSimulator.peek_pauli_flips`](#stim.FlipSimulator.peek_pauli_flips)
- [`stim.FlipSimulator.reset`](#stim.FlipSimulator.reset)
- [`stim.FlipSimulator.set_pauli_flip`](#stim.FlipSimulator.set_pauli_flip)
- [`stim.FlippedMeasurement`](#stim.FlippedMeasurement)
- [`stim.FlippedMeasurement.__init__`](#stim.FlippedMeasurement.__init__)
Expand Down Expand Up @@ -7343,6 +7343,42 @@ def broadcast_pauli_errors(
"""
```

<a name="stim.FlipSimulator.clear"></a>
```python
# stim.FlipSimulator.clear

# (in class stim.FlipSimulator)
def clear(
self,
) -> None:
"""Clears the simulator's state, so it can be reused for another simulation.
This clears the measurement flip history, clears the detector flip history,
and zeroes the observable flip state. It also resets all qubits to |0>. If
stabilizer randomization is disabled, this zeros all pauli flip data. Otherwise
it randomizes all pauli flips to be I or Z with equal probability.
Behind the scenes, this doesn't free memory or resize the simulator. So,
repeating the same simulation with calls to `clear` in between will be faster
than allocating a new simulator each time (by avoiding re-allocations).
Examples:
>>> import stim
>>> sim = stim.FlipSimulator(batch_size=256)
>>> sim.do(stim.Circuit("M(0.1) 9"))
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(1, 256)
>>> sim.clear()
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(0, 256)
"""
```

<a name="stim.FlipSimulator.copy"></a>
```python
# stim.FlipSimulator.copy
Expand Down Expand Up @@ -7856,38 +7892,6 @@ def peek_pauli_flips(
"""
```

<a name="stim.FlipSimulator.reset"></a>
```python
# stim.FlipSimulator.reset

# (in class stim.FlipSimulator)
def reset(
self,
) -> None:
"""Resets the simulator's state, so it can be reused for another simulation.
This empties the measurement flip history, empties the detector flip history,
and zeroes the observable flip state. It also resets all qubits to |0>. If
stabilizer randomization is disabled, this zeros all pauli flips data. Otherwise
it randomizes all pauli flips to be I or Z with equal probability.
Examples:
>>> import stim
>>> sim = stim.FlipSimulator(batch_size=256)
>>> sim.do(stim.Circuit("M(0.1) 9"))
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(1, 256)
>>> sim.reset()
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(0, 256)
"""
```

<a name="stim.FlipSimulator.set_pauli_flip"></a>
```python
# stim.FlipSimulator.set_pauli_flip
Expand Down
54 changes: 29 additions & 25 deletions doc/stim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5723,6 +5723,35 @@ class FlipSimulator:
>>> sim.peek_pauli_flips()
[stim.PauliString("+X_Y"), stim.PauliString("+Z_Y")]
"""
def clear(
self,
) -> None:
"""Clears the simulator's state, so it can be reused for another simulation.
This clears the measurement flip history, clears the detector flip history,
and zeroes the observable flip state. It also resets all qubits to |0>. If
stabilizer randomization is disabled, this zeros all pauli flip data. Otherwise
it randomizes all pauli flips to be I or Z with equal probability.
Behind the scenes, this doesn't free memory or resize the simulator. So,
repeating the same simulation with calls to `clear` in between will be faster
than allocating a new simulator each time (by avoiding re-allocations).
Examples:
>>> import stim
>>> sim = stim.FlipSimulator(batch_size=256)
>>> sim.do(stim.Circuit("M(0.1) 9"))
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(1, 256)
>>> sim.clear()
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(0, 256)
"""
def copy(
self,
*,
Expand Down Expand Up @@ -6166,31 +6195,6 @@ class FlipSimulator:
>>> sorted(set(str(flips))) # Should have Zs from stabilizer randomization
['+', 'Z', '_']
"""
def reset(
self,
) -> None:
"""Resets the simulator's state, so it can be reused for another simulation.
This empties the measurement flip history, empties the detector flip history,
and zeroes the observable flip state. It also resets all qubits to |0>. If
stabilizer randomization is disabled, this zeros all pauli flips data. Otherwise
it randomizes all pauli flips to be I or Z with equal probability.
Examples:
>>> import stim
>>> sim = stim.FlipSimulator(batch_size=256)
>>> sim.do(stim.Circuit("M(0.1) 9"))
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(1, 256)
>>> sim.reset()
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(0, 256)
"""
def set_pauli_flip(
self,
pauli: Union[str, int],
Expand Down
54 changes: 29 additions & 25 deletions glue/python/src/stim/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5723,6 +5723,35 @@ class FlipSimulator:
>>> sim.peek_pauli_flips()
[stim.PauliString("+X_Y"), stim.PauliString("+Z_Y")]
"""
def clear(
self,
) -> None:
"""Clears the simulator's state, so it can be reused for another simulation.
This clears the measurement flip history, clears the detector flip history,
and zeroes the observable flip state. It also resets all qubits to |0>. If
stabilizer randomization is disabled, this zeros all pauli flip data. Otherwise
it randomizes all pauli flips to be I or Z with equal probability.
Behind the scenes, this doesn't free memory or resize the simulator. So,
repeating the same simulation with calls to `clear` in between will be faster
than allocating a new simulator each time (by avoiding re-allocations).
Examples:
>>> import stim
>>> sim = stim.FlipSimulator(batch_size=256)
>>> sim.do(stim.Circuit("M(0.1) 9"))
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(1, 256)
>>> sim.clear()
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(0, 256)
"""
def copy(
self,
*,
Expand Down Expand Up @@ -6166,31 +6195,6 @@ class FlipSimulator:
>>> sorted(set(str(flips))) # Should have Zs from stabilizer randomization
['+', 'Z', '_']
"""
def reset(
self,
) -> None:
"""Resets the simulator's state, so it can be reused for another simulation.
This empties the measurement flip history, empties the detector flip history,
and zeroes the observable flip state. It also resets all qubits to |0>. If
stabilizer randomization is disabled, this zeros all pauli flips data. Otherwise
it randomizes all pauli flips to be I or Z with equal probability.
Examples:
>>> import stim
>>> sim = stim.FlipSimulator(batch_size=256)
>>> sim.do(stim.Circuit("M(0.1) 9"))
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(1, 256)
>>> sim.reset()
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
(0, 256)
"""
def set_pauli_flip(
self,
pauli: Union[str, int],
Expand Down
14 changes: 9 additions & 5 deletions src/stim/simulators/frame_simulator.pybind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -956,18 +956,22 @@ void stim_pybind::pybind_frame_simulator_methods(
.data());

c.def(
"reset",
"clear",
[](FrameSimulator<MAX_BITWORD_WIDTH> &self) {
self.reset_all();
},
clean_doc_string(R"DOC(
Resets the simulator's state, so it can be reused for another simulation.
Clears the simulator's state, so it can be reused for another simulation.
This empties the measurement flip history, empties the detector flip history,
This clears the measurement flip history, clears the detector flip history,
and zeroes the observable flip state. It also resets all qubits to |0>. If
stabilizer randomization is disabled, this zeros all pauli flips data. Otherwise
stabilizer randomization is disabled, this zeros all pauli flip data. Otherwise
it randomizes all pauli flips to be I or Z with equal probability.
Behind the scenes, this doesn't free memory or resize the simulator. So,
repeating the same simulation with calls to `clear` in between will be faster
than allocating a new simulator each time (by avoiding re-allocations).
Examples:
>>> import stim
>>> sim = stim.FlipSimulator(batch_size=256)
Expand All @@ -977,7 +981,7 @@ void stim_pybind::pybind_frame_simulator_methods(
>>> sim.get_measurement_flips().shape
(1, 256)
>>> sim.reset()
>>> sim.clear()
>>> sim.num_qubits
10
>>> sim.get_measurement_flips().shape
Expand Down
21 changes: 16 additions & 5 deletions src/stim/util_top/circuit_inverse_qec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ void CircuitFlowReverser::do_measuring_instruction(const CircuitInstruction &ins
rev.undo_gate(inst);
}

void CircuitFlowReverser::do_feedback_capable_instruction(const CircuitInstruction &inst) {
for (GateTarget t : inst.targets) {
if (t.is_measurement_record_target()) {
throw std::invalid_argument("Time-reversing feedback isn't supported yet. Found feedback in: " + inst.str());
}
}
do_simple_instruction(inst);
}

void CircuitFlowReverser::do_simple_instruction(const CircuitInstruction &inst) {
Gate g = GATE_DATA[inst.gate_type];
rev.undo_gate(inst);
Expand Down Expand Up @@ -207,13 +216,8 @@ void CircuitFlowReverser::do_instruction(const CircuitInstruction &inst) {
case GateType::ISWAP_DAG:
case GateType::XCX:
case GateType::XCY:
case GateType::XCZ:
case GateType::YCX:
case GateType::YCY:
case GateType::YCZ:
case GateType::CX:
case GateType::CY:
case GateType::CZ:
case GateType::H:
case GateType::H_XY:
case GateType::H_YZ:
Expand All @@ -229,6 +233,13 @@ void CircuitFlowReverser::do_instruction(const CircuitInstruction &inst) {
case GateType::HERALDED_PAULI_CHANNEL_1:
do_simple_instruction(inst);
return;
case GateType::XCZ:
case GateType::YCZ:
case GateType::CX:
case GateType::CY:
case GateType::CZ:
do_feedback_capable_instruction(inst);
break;
case GateType::MRX:
case GateType::MRY:
case GateType::MR:
Expand Down
1 change: 1 addition & 0 deletions src/stim/util_top/circuit_inverse_qec.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct CircuitFlowReverser {
void do_m2r_instruction(const CircuitInstruction &inst);
void do_measuring_instruction(const CircuitInstruction &inst);
void do_simple_instruction(const CircuitInstruction &inst);
void do_feedback_capable_instruction(const CircuitInstruction &inst);
void flush_detectors_and_observables();

void do_instruction(const CircuitInstruction &inst);
Expand Down
12 changes: 12 additions & 0 deletions src/stim/util_top/circuit_inverse_qec_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
import stim


Expand Down Expand Up @@ -151,3 +152,14 @@ def test_measurement_ordering_3():
assert circuit.has_all_flows(flows, unsigned=True)
new_circuit, new_flows = circuit.time_reversed_for_flows(flows)
assert new_circuit.has_all_flows(new_flows, unsigned=True)


def test_feedback():
c = stim.Circuit("""
R 1
M 1
CX rec[-1] 0
""")
with pytest.raises(ValueError):
c.time_reversed_for_flows([stim.Flow("Z0 -> Z0")])
# TODO: once feedback is supported verify the inv flow is correct

0 comments on commit b6174b7

Please sign in to comment.