Skip to content

Commit

Permalink
Add mpp_state{_unsigned} methods to stim.Tableau.to_circuit (#690)
Browse files Browse the repository at this point in the history
  • Loading branch information
Strilanc authored Jan 29, 2024
1 parent e741500 commit b5686eb
Show file tree
Hide file tree
Showing 16 changed files with 404 additions and 97 deletions.
39 changes: 39 additions & 0 deletions doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -10366,7 +10366,33 @@ def to_circuit(
Note: "graph_state" treats the tableau as a state instead of as a
Clifford operation. It will preserve the set of stabilizers, but
not the exact choice of generators.
"mpp_state": Prepares the tableau's state using MPP and feedback.
Gate set: MPP, CX rec, CY rec, CZ rec
Circuit qubit count: n
Circuit operation count: O(n^2)
The circuit will be made up of two layers:
1. An MPP layer measuring each of the tableau's stabilizers.
2. A feedback layer using the measurement results to control
whether or not to apply each of the tableau's destabilizers
in order to get the correct sign for each stabilizer.
Note: "mpp_state" treats the tableau as a state instead of as a
Clifford operation. It will preserve the set of stabilizers, but
not the exact choice of generators.
"mpp_state_unsigned": Prepares the tableau's state up to sign using MPP.
Gate set: MPP
Circuit qubit count: n
Circuit operation count: O(n^2)
The circuit will contain a series of MPP measurements measuring each
of the tableau's stabilizers. The stabilizers are measured in the
order used by the tableau (i.e. tableau.z_output(k) is the k'th
stabilizer measured).
Note: "mpp_state_unsigned" treats the tableau as a state instead of
as a Clifford operation. It will preserve the set of stabilizers,
but not the exact choice of generators.
Returns:
The synthesized circuit.
Expand Down Expand Up @@ -10421,6 +10447,19 @@ def to_circuit(
H 3
S 3
''')
>>> tableau.to_circuit("mpp_state_unsigned")
stim.Circuit('''
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
''')
>>> tableau.to_circuit("mpp_state")
stim.Circuit('''
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
CX rec[-3] 2 rec[-1] 2
CY rec[-4] 0 rec[-3] 0 rec[-3] 3 rec[-2] 3 rec[-1] 0
CZ rec[-4] 1 rec[-1] 1
''')
"""
```

Expand Down
39 changes: 39 additions & 0 deletions doc/stim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8070,7 +8070,33 @@ class Tableau:
Note: "graph_state" treats the tableau as a state instead of as a
Clifford operation. It will preserve the set of stabilizers, but
not the exact choice of generators.
"mpp_state": Prepares the tableau's state using MPP and feedback.
Gate set: MPP, CX rec, CY rec, CZ rec
Circuit qubit count: n
Circuit operation count: O(n^2)
The circuit will be made up of two layers:
1. An MPP layer measuring each of the tableau's stabilizers.
2. A feedback layer using the measurement results to control
whether or not to apply each of the tableau's destabilizers
in order to get the correct sign for each stabilizer.
Note: "mpp_state" treats the tableau as a state instead of as a
Clifford operation. It will preserve the set of stabilizers, but
not the exact choice of generators.
"mpp_state_unsigned": Prepares the tableau's state up to sign using MPP.
Gate set: MPP
Circuit qubit count: n
Circuit operation count: O(n^2)
The circuit will contain a series of MPP measurements measuring each
of the tableau's stabilizers. The stabilizers are measured in the
order used by the tableau (i.e. tableau.z_output(k) is the k'th
stabilizer measured).
Note: "mpp_state_unsigned" treats the tableau as a state instead of
as a Clifford operation. It will preserve the set of stabilizers,
but not the exact choice of generators.
Returns:
The synthesized circuit.
Expand Down Expand Up @@ -8125,6 +8151,19 @@ class Tableau:
H 3
S 3
''')
>>> tableau.to_circuit("mpp_state_unsigned")
stim.Circuit('''
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
''')
>>> tableau.to_circuit("mpp_state")
stim.Circuit('''
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
CX rec[-3] 2 rec[-1] 2
CY rec[-4] 0 rec[-3] 0 rec[-3] 3 rec[-2] 3 rec[-1] 0
CZ rec[-4] 1 rec[-1] 1
''')
"""
def to_numpy(
self,
Expand Down
39 changes: 39 additions & 0 deletions glue/python/src/stim/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8070,7 +8070,33 @@ class Tableau:
Note: "graph_state" treats the tableau as a state instead of as a
Clifford operation. It will preserve the set of stabilizers, but
not the exact choice of generators.
"mpp_state": Prepares the tableau's state using MPP and feedback.
Gate set: MPP, CX rec, CY rec, CZ rec
Circuit qubit count: n
Circuit operation count: O(n^2)
The circuit will be made up of two layers:
1. An MPP layer measuring each of the tableau's stabilizers.
2. A feedback layer using the measurement results to control
whether or not to apply each of the tableau's destabilizers
in order to get the correct sign for each stabilizer.
Note: "mpp_state" treats the tableau as a state instead of as a
Clifford operation. It will preserve the set of stabilizers, but
not the exact choice of generators.
"mpp_state_unsigned": Prepares the tableau's state up to sign using MPP.
Gate set: MPP
Circuit qubit count: n
Circuit operation count: O(n^2)
The circuit will contain a series of MPP measurements measuring each
of the tableau's stabilizers. The stabilizers are measured in the
order used by the tableau (i.e. tableau.z_output(k) is the k'th
stabilizer measured).
Note: "mpp_state_unsigned" treats the tableau as a state instead of
as a Clifford operation. It will preserve the set of stabilizers,
but not the exact choice of generators.
Returns:
The synthesized circuit.
Expand Down Expand Up @@ -8125,6 +8151,19 @@ class Tableau:
H 3
S 3
''')
>>> tableau.to_circuit("mpp_state_unsigned")
stim.Circuit('''
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
''')
>>> tableau.to_circuit("mpp_state")
stim.Circuit('''
MPP X0*Z1*Y2*Y3 !X0*Y1*X2 !Z0*X1*X2*Z3 X0*X1*Z2
CX rec[-3] 2 rec[-1] 2
CY rec[-4] 0 rec[-3] 0 rec[-3] 3 rec[-2] 3 rec[-1] 0
CZ rec[-4] 1 rec[-1] 1
''')
"""
def to_numpy(
self,
Expand Down
7 changes: 7 additions & 0 deletions src/stim/circuit/gate_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@

using namespace stim;

GateTarget GateTarget::pauli_xz(uint32_t qubit, bool x, bool z, bool inverted) {
if (qubit != (qubit & TARGET_VALUE_MASK)) {
throw std::invalid_argument("qubit target larger than " + std::to_string(TARGET_VALUE_MASK));
}
return {qubit | (TARGET_INVERTED_BIT * inverted) | (TARGET_PAULI_X_BIT * x) | (TARGET_PAULI_Z_BIT * z)};
}

GateTarget GateTarget::x(uint32_t qubit, bool inverted) {
if (qubit != (qubit & TARGET_VALUE_MASK)) {
throw std::invalid_argument("qubit target larger than " + std::to_string(TARGET_VALUE_MASK));
Expand Down
1 change: 1 addition & 0 deletions src/stim/circuit/gate_target.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct GateTarget {
static GateTarget x(uint32_t qubit, bool inverted = false);
static GateTarget y(uint32_t qubit, bool inverted = false);
static GateTarget z(uint32_t qubit, bool inverted = false);
static GateTarget pauli_xz(uint32_t qubit, bool x, bool z, bool inverted = false);
static GateTarget qubit(uint32_t qubit, bool inverted = false);
static GateTarget rec(int32_t lookback);
static GateTarget sweep_bit(uint32_t index);
Expand Down
4 changes: 2 additions & 2 deletions src/stim/gates/gates.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ std::pair<std::vector<PauliString<W>>, std::vector<PauliString<W>>> circuit_outp
}
TableauSimulator<W> sim1(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), -1);
TableauSimulator<W> sim2(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), +1);
sim1.expand_do_circuit(circuit);
sim2.expand_do_circuit(circuit);
sim1.safe_do_circuit(circuit);
sim2.safe_do_circuit(circuit);
return {sim1.canonical_stabilizers(), sim2.canonical_stabilizers()};
}

Expand Down
14 changes: 7 additions & 7 deletions src/stim/simulators/graph_simulator.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ void expect_graph_circuit_is_equivalent(const Circuit &circuit) {
Circuit converted = sim.to_circuit();
TableauSimulator<64> sim1(std::mt19937_64{}, n);
TableauSimulator<64> sim2(std::mt19937_64{}, n);
sim1.expand_do_circuit(circuit);
sim2.expand_do_circuit(converted);
sim1.safe_do_circuit(circuit);
sim2.safe_do_circuit(converted);
auto s1 = sim1.canonical_stabilizers();
auto s2 = sim2.canonical_stabilizers();
if (s1 != s2) {
Expand All @@ -35,9 +35,9 @@ void expect_graph_sim_effect_matches_tableau_sim(const GraphSimulator &state, co

TableauSimulator<64> tableau_sim1(std::mt19937_64{}, n);
TableauSimulator<64> tableau_sim2(std::mt19937_64{}, n);
tableau_sim1.expand_do_circuit(state.to_circuit());
tableau_sim1.expand_do_circuit(effect);
tableau_sim2.expand_do_circuit(state_after_effect.to_circuit());
tableau_sim1.safe_do_circuit(state.to_circuit());
tableau_sim1.safe_do_circuit(effect);
tableau_sim2.safe_do_circuit(state_after_effect.to_circuit());
auto s1 = tableau_sim1.canonical_stabilizers();
auto s2 = tableau_sim2.canonical_stabilizers();
if (s1 != s2) {
Expand Down Expand Up @@ -240,8 +240,8 @@ TEST(graph_simulator, do_complementation) {

TableauSimulator<64> tableau_sim1(std::mt19937_64{}, 8);
TableauSimulator<64> tableau_sim2(std::mt19937_64{}, 8);
tableau_sim1.expand_do_circuit(sim.to_circuit());
tableau_sim2.expand_do_circuit(sim2.to_circuit());
tableau_sim1.safe_do_circuit(sim.to_circuit());
tableau_sim2.safe_do_circuit(sim2.to_circuit());
auto s1 = tableau_sim1.canonical_stabilizers();
auto s2 = tableau_sim2.canonical_stabilizers();
if (s1 != s2) {
Expand Down
2 changes: 1 addition & 1 deletion src/stim/simulators/tableau_simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ struct TableauSimulator {
/// Runs all of the operations in the given circuit.
///
/// Automatically expands the tableau simulator's state, if needed.
void expand_do_circuit(const Circuit &circuit, uint64_t reps = 1);
void safe_do_circuit(const Circuit &circuit, uint64_t reps = 1);
void do_operation_ensure_size(const CircuitInstruction &operation);

void apply_tableau(const Tableau<W> &tableau, const std::vector<size_t> &targets);
Expand Down
4 changes: 2 additions & 2 deletions src/stim/simulators/tableau_simulator.inl
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,7 @@ void TableauSimulator<W>::do_Z(const CircuitInstruction &target_data) {
template <size_t W>
simd_bits<W> TableauSimulator<W>::sample_circuit(const Circuit &circuit, std::mt19937_64 &rng, int8_t sign_bias) {
TableauSimulator<W> sim(std::move(rng), circuit.count_qubits(), sign_bias);
sim.expand_do_circuit(circuit);
sim.safe_do_circuit(circuit);

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

template <size_t W>
void TableauSimulator<W>::expand_do_circuit(const Circuit &circuit, uint64_t reps) {
void TableauSimulator<W>::safe_do_circuit(const Circuit &circuit, uint64_t reps) {
ensure_large_enough_for_qubits(circuit.count_qubits());
for (uint64_t k = 0; k < reps; k++) {
circuit.for_each_operation([&](const CircuitInstruction &op) {
Expand Down
6 changes: 3 additions & 3 deletions src/stim/simulators/tableau_simulator.pybind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ using namespace stim_pybind;
template <size_t W>
void do_obj(TableauSimulator<W> &self, const pybind11::object &obj) {
if (pybind11::isinstance<Circuit>(obj)) {
self.expand_do_circuit(pybind11::cast<Circuit>(obj));
self.safe_do_circuit(pybind11::cast<Circuit>(obj));
} else if (pybind11::isinstance<CircuitRepeatBlock>(obj)) {
const CircuitRepeatBlock &block = pybind11::cast<CircuitRepeatBlock>(obj);
self.expand_do_circuit(block.body, block.repeat_count);
self.safe_do_circuit(block.body, block.repeat_count);
} else if (pybind11::isinstance<FlexPauliString>(obj)) {
const FlexPauliString &pauli_string = pybind11::cast<FlexPauliString>(obj);
self.ensure_large_enough_for_qubits(pauli_string.value.num_qubits);
Expand Down Expand Up @@ -474,7 +474,7 @@ void stim_pybind::pybind_tableau_simulator_methods(
c.def(
"do_circuit",
[](TableauSimulator<MAX_BITWORD_WIDTH> &self, const Circuit &circuit) {
self.expand_do_circuit(circuit);
self.safe_do_circuit(circuit);
},
pybind11::arg("circuit"),
clean_doc_string(R"DOC(
Expand Down
Loading

0 comments on commit b5686eb

Please sign in to comment.