Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mpp_state{_unsigned} methods to stim.Tableau.to_circuit #690

Merged
merged 2 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading