Skip to content

Commit b5686eb

Browse files
authored
Add mpp_state{_unsigned} methods to stim.Tableau.to_circuit (#690)
1 parent e741500 commit b5686eb

16 files changed

+404
-97
lines changed

doc/python_api_reference_vDev.md

+39
Original file line numberDiff line numberDiff line change
@@ -10366,7 +10366,33 @@ def to_circuit(
1036610366
Note: "graph_state" treats the tableau as a state instead of as a
1036710367
Clifford operation. It will preserve the set of stabilizers, but
1036810368
not the exact choice of generators.
10369+
"mpp_state": Prepares the tableau's state using MPP and feedback.
10370+
Gate set: MPP, CX rec, CY rec, CZ rec
10371+
Circuit qubit count: n
10372+
Circuit operation count: O(n^2)
10373+
10374+
The circuit will be made up of two layers:
10375+
1. An MPP layer measuring each of the tableau's stabilizers.
10376+
2. A feedback layer using the measurement results to control
10377+
whether or not to apply each of the tableau's destabilizers
10378+
in order to get the correct sign for each stabilizer.
10379+
10380+
Note: "mpp_state" treats the tableau as a state instead of as a
10381+
Clifford operation. It will preserve the set of stabilizers, but
10382+
not the exact choice of generators.
10383+
"mpp_state_unsigned": Prepares the tableau's state up to sign using MPP.
10384+
Gate set: MPP
10385+
Circuit qubit count: n
10386+
Circuit operation count: O(n^2)
1036910387
10388+
The circuit will contain a series of MPP measurements measuring each
10389+
of the tableau's stabilizers. The stabilizers are measured in the
10390+
order used by the tableau (i.e. tableau.z_output(k) is the k'th
10391+
stabilizer measured).
10392+
10393+
Note: "mpp_state_unsigned" treats the tableau as a state instead of
10394+
as a Clifford operation. It will preserve the set of stabilizers,
10395+
but not the exact choice of generators.
1037010396
Returns:
1037110397
The synthesized circuit.
1037210398
@@ -10421,6 +10447,19 @@ def to_circuit(
1042110447
H 3
1042210448
S 3
1042310449
''')
10450+
10451+
>>> tableau.to_circuit("mpp_state_unsigned")
10452+
stim.Circuit('''
10453+
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
10454+
''')
10455+
10456+
>>> tableau.to_circuit("mpp_state")
10457+
stim.Circuit('''
10458+
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
10459+
CX rec[-3] 2 rec[-1] 2
10460+
CY rec[-4] 0 rec[-3] 0 rec[-3] 3 rec[-2] 3 rec[-1] 0
10461+
CZ rec[-4] 1 rec[-1] 1
10462+
''')
1042410463
"""
1042510464
```
1042610465

doc/stim.pyi

+39
Original file line numberDiff line numberDiff line change
@@ -8070,7 +8070,33 @@ class Tableau:
80708070
Note: "graph_state" treats the tableau as a state instead of as a
80718071
Clifford operation. It will preserve the set of stabilizers, but
80728072
not the exact choice of generators.
8073+
"mpp_state": Prepares the tableau's state using MPP and feedback.
8074+
Gate set: MPP, CX rec, CY rec, CZ rec
8075+
Circuit qubit count: n
8076+
Circuit operation count: O(n^2)
8077+
8078+
The circuit will be made up of two layers:
8079+
1. An MPP layer measuring each of the tableau's stabilizers.
8080+
2. A feedback layer using the measurement results to control
8081+
whether or not to apply each of the tableau's destabilizers
8082+
in order to get the correct sign for each stabilizer.
8083+
8084+
Note: "mpp_state" treats the tableau as a state instead of as a
8085+
Clifford operation. It will preserve the set of stabilizers, but
8086+
not the exact choice of generators.
8087+
"mpp_state_unsigned": Prepares the tableau's state up to sign using MPP.
8088+
Gate set: MPP
8089+
Circuit qubit count: n
8090+
Circuit operation count: O(n^2)
80738091
8092+
The circuit will contain a series of MPP measurements measuring each
8093+
of the tableau's stabilizers. The stabilizers are measured in the
8094+
order used by the tableau (i.e. tableau.z_output(k) is the k'th
8095+
stabilizer measured).
8096+
8097+
Note: "mpp_state_unsigned" treats the tableau as a state instead of
8098+
as a Clifford operation. It will preserve the set of stabilizers,
8099+
but not the exact choice of generators.
80748100
Returns:
80758101
The synthesized circuit.
80768102
@@ -8125,6 +8151,19 @@ class Tableau:
81258151
H 3
81268152
S 3
81278153
''')
8154+
8155+
>>> tableau.to_circuit("mpp_state_unsigned")
8156+
stim.Circuit('''
8157+
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
8158+
''')
8159+
8160+
>>> tableau.to_circuit("mpp_state")
8161+
stim.Circuit('''
8162+
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
8163+
CX rec[-3] 2 rec[-1] 2
8164+
CY rec[-4] 0 rec[-3] 0 rec[-3] 3 rec[-2] 3 rec[-1] 0
8165+
CZ rec[-4] 1 rec[-1] 1
8166+
''')
81288167
"""
81298168
def to_numpy(
81308169
self,

glue/python/src/stim/__init__.pyi

+39
Original file line numberDiff line numberDiff line change
@@ -8070,7 +8070,33 @@ class Tableau:
80708070
Note: "graph_state" treats the tableau as a state instead of as a
80718071
Clifford operation. It will preserve the set of stabilizers, but
80728072
not the exact choice of generators.
8073+
"mpp_state": Prepares the tableau's state using MPP and feedback.
8074+
Gate set: MPP, CX rec, CY rec, CZ rec
8075+
Circuit qubit count: n
8076+
Circuit operation count: O(n^2)
8077+
8078+
The circuit will be made up of two layers:
8079+
1. An MPP layer measuring each of the tableau's stabilizers.
8080+
2. A feedback layer using the measurement results to control
8081+
whether or not to apply each of the tableau's destabilizers
8082+
in order to get the correct sign for each stabilizer.
8083+
8084+
Note: "mpp_state" treats the tableau as a state instead of as a
8085+
Clifford operation. It will preserve the set of stabilizers, but
8086+
not the exact choice of generators.
8087+
"mpp_state_unsigned": Prepares the tableau's state up to sign using MPP.
8088+
Gate set: MPP
8089+
Circuit qubit count: n
8090+
Circuit operation count: O(n^2)
80738091
8092+
The circuit will contain a series of MPP measurements measuring each
8093+
of the tableau's stabilizers. The stabilizers are measured in the
8094+
order used by the tableau (i.e. tableau.z_output(k) is the k'th
8095+
stabilizer measured).
8096+
8097+
Note: "mpp_state_unsigned" treats the tableau as a state instead of
8098+
as a Clifford operation. It will preserve the set of stabilizers,
8099+
but not the exact choice of generators.
80748100
Returns:
80758101
The synthesized circuit.
80768102
@@ -8125,6 +8151,19 @@ class Tableau:
81258151
H 3
81268152
S 3
81278153
''')
8154+
8155+
>>> tableau.to_circuit("mpp_state_unsigned")
8156+
stim.Circuit('''
8157+
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
8158+
''')
8159+
8160+
>>> tableau.to_circuit("mpp_state")
8161+
stim.Circuit('''
8162+
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
8163+
CX rec[-3] 2 rec[-1] 2
8164+
CY rec[-4] 0 rec[-3] 0 rec[-3] 3 rec[-2] 3 rec[-1] 0
8165+
CZ rec[-4] 1 rec[-1] 1
8166+
''')
81288167
"""
81298168
def to_numpy(
81308169
self,

src/stim/circuit/gate_target.cc

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818

1919
using namespace stim;
2020

21+
GateTarget GateTarget::pauli_xz(uint32_t qubit, bool x, bool z, bool inverted) {
22+
if (qubit != (qubit & TARGET_VALUE_MASK)) {
23+
throw std::invalid_argument("qubit target larger than " + std::to_string(TARGET_VALUE_MASK));
24+
}
25+
return {qubit | (TARGET_INVERTED_BIT * inverted) | (TARGET_PAULI_X_BIT * x) | (TARGET_PAULI_Z_BIT * z)};
26+
}
27+
2128
GateTarget GateTarget::x(uint32_t qubit, bool inverted) {
2229
if (qubit != (qubit & TARGET_VALUE_MASK)) {
2330
throw std::invalid_argument("qubit target larger than " + std::to_string(TARGET_VALUE_MASK));

src/stim/circuit/gate_target.h

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct GateTarget {
3939
static GateTarget x(uint32_t qubit, bool inverted = false);
4040
static GateTarget y(uint32_t qubit, bool inverted = false);
4141
static GateTarget z(uint32_t qubit, bool inverted = false);
42+
static GateTarget pauli_xz(uint32_t qubit, bool x, bool z, bool inverted = false);
4243
static GateTarget qubit(uint32_t qubit, bool inverted = false);
4344
static GateTarget rec(int32_t lookback);
4445
static GateTarget sweep_bit(uint32_t index);

src/stim/gates/gates.test.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ std::pair<std::vector<PauliString<W>>, std::vector<PauliString<W>>> circuit_outp
7979
}
8080
TableauSimulator<W> sim1(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), -1);
8181
TableauSimulator<W> sim2(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), +1);
82-
sim1.expand_do_circuit(circuit);
83-
sim2.expand_do_circuit(circuit);
82+
sim1.safe_do_circuit(circuit);
83+
sim2.safe_do_circuit(circuit);
8484
return {sim1.canonical_stabilizers(), sim2.canonical_stabilizers()};
8585
}
8686

src/stim/simulators/graph_simulator.test.cc

+7-7
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ void expect_graph_circuit_is_equivalent(const Circuit &circuit) {
1616
Circuit converted = sim.to_circuit();
1717
TableauSimulator<64> sim1(std::mt19937_64{}, n);
1818
TableauSimulator<64> sim2(std::mt19937_64{}, n);
19-
sim1.expand_do_circuit(circuit);
20-
sim2.expand_do_circuit(converted);
19+
sim1.safe_do_circuit(circuit);
20+
sim2.safe_do_circuit(converted);
2121
auto s1 = sim1.canonical_stabilizers();
2222
auto s2 = sim2.canonical_stabilizers();
2323
if (s1 != s2) {
@@ -35,9 +35,9 @@ void expect_graph_sim_effect_matches_tableau_sim(const GraphSimulator &state, co
3535

3636
TableauSimulator<64> tableau_sim1(std::mt19937_64{}, n);
3737
TableauSimulator<64> tableau_sim2(std::mt19937_64{}, n);
38-
tableau_sim1.expand_do_circuit(state.to_circuit());
39-
tableau_sim1.expand_do_circuit(effect);
40-
tableau_sim2.expand_do_circuit(state_after_effect.to_circuit());
38+
tableau_sim1.safe_do_circuit(state.to_circuit());
39+
tableau_sim1.safe_do_circuit(effect);
40+
tableau_sim2.safe_do_circuit(state_after_effect.to_circuit());
4141
auto s1 = tableau_sim1.canonical_stabilizers();
4242
auto s2 = tableau_sim2.canonical_stabilizers();
4343
if (s1 != s2) {
@@ -240,8 +240,8 @@ TEST(graph_simulator, do_complementation) {
240240

241241
TableauSimulator<64> tableau_sim1(std::mt19937_64{}, 8);
242242
TableauSimulator<64> tableau_sim2(std::mt19937_64{}, 8);
243-
tableau_sim1.expand_do_circuit(sim.to_circuit());
244-
tableau_sim2.expand_do_circuit(sim2.to_circuit());
243+
tableau_sim1.safe_do_circuit(sim.to_circuit());
244+
tableau_sim2.safe_do_circuit(sim2.to_circuit());
245245
auto s1 = tableau_sim1.canonical_stabilizers();
246246
auto s2 = tableau_sim2.canonical_stabilizers();
247247
if (s1 != s2) {

src/stim/simulators/tableau_simulator.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ struct TableauSimulator {
9696
/// Runs all of the operations in the given circuit.
9797
///
9898
/// Automatically expands the tableau simulator's state, if needed.
99-
void expand_do_circuit(const Circuit &circuit, uint64_t reps = 1);
99+
void safe_do_circuit(const Circuit &circuit, uint64_t reps = 1);
100100
void do_operation_ensure_size(const CircuitInstruction &operation);
101101

102102
void apply_tableau(const Tableau<W> &tableau, const std::vector<size_t> &targets);

src/stim/simulators/tableau_simulator.inl

+2-2
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@ void TableauSimulator<W>::do_Z(const CircuitInstruction &target_data) {
11071107
template <size_t W>
11081108
simd_bits<W> TableauSimulator<W>::sample_circuit(const Circuit &circuit, std::mt19937_64 &rng, int8_t sign_bias) {
11091109
TableauSimulator<W> sim(std::move(rng), circuit.count_qubits(), sign_bias);
1110-
sim.expand_do_circuit(circuit);
1110+
sim.safe_do_circuit(circuit);
11111111

11121112
const std::vector<bool> &v = sim.measurement_record.storage;
11131113
simd_bits<W> result(v.size());
@@ -1347,7 +1347,7 @@ void TableauSimulator<W>::collapse_isolate_qubit_z(size_t target, TableauTranspo
13471347
}
13481348

13491349
template <size_t W>
1350-
void TableauSimulator<W>::expand_do_circuit(const Circuit &circuit, uint64_t reps) {
1350+
void TableauSimulator<W>::safe_do_circuit(const Circuit &circuit, uint64_t reps) {
13511351
ensure_large_enough_for_qubits(circuit.count_qubits());
13521352
for (uint64_t k = 0; k < reps; k++) {
13531353
circuit.for_each_operation([&](const CircuitInstruction &op) {

src/stim/simulators/tableau_simulator.pybind.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ using namespace stim_pybind;
2929
template <size_t W>
3030
void do_obj(TableauSimulator<W> &self, const pybind11::object &obj) {
3131
if (pybind11::isinstance<Circuit>(obj)) {
32-
self.expand_do_circuit(pybind11::cast<Circuit>(obj));
32+
self.safe_do_circuit(pybind11::cast<Circuit>(obj));
3333
} else if (pybind11::isinstance<CircuitRepeatBlock>(obj)) {
3434
const CircuitRepeatBlock &block = pybind11::cast<CircuitRepeatBlock>(obj);
35-
self.expand_do_circuit(block.body, block.repeat_count);
35+
self.safe_do_circuit(block.body, block.repeat_count);
3636
} else if (pybind11::isinstance<FlexPauliString>(obj)) {
3737
const FlexPauliString &pauli_string = pybind11::cast<FlexPauliString>(obj);
3838
self.ensure_large_enough_for_qubits(pauli_string.value.num_qubits);
@@ -474,7 +474,7 @@ void stim_pybind::pybind_tableau_simulator_methods(
474474
c.def(
475475
"do_circuit",
476476
[](TableauSimulator<MAX_BITWORD_WIDTH> &self, const Circuit &circuit) {
477-
self.expand_do_circuit(circuit);
477+
self.safe_do_circuit(circuit);
478478
},
479479
pybind11::arg("circuit"),
480480
clean_doc_string(R"DOC(

0 commit comments

Comments
 (0)