diff --git a/doc/python_api_reference_vDev.md b/doc/python_api_reference_vDev.md
index 85259a244..6cf06b5c3 100644
--- a/doc/python_api_reference_vDev.md
+++ b/doc/python_api_reference_vDev.md
@@ -294,6 +294,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.Tableau.to_circuit`](#stim.Tableau.to_circuit)
- [`stim.Tableau.to_numpy`](#stim.Tableau.to_numpy)
- [`stim.Tableau.to_pauli_string`](#stim.Tableau.to_pauli_string)
+ - [`stim.Tableau.to_stabilizers`](#stim.Tableau.to_stabilizers)
- [`stim.Tableau.to_state_vector`](#stim.Tableau.to_state_vector)
- [`stim.Tableau.to_unitary_matrix`](#stim.Tableau.to_unitary_matrix)
- [`stim.Tableau.x_output`](#stim.Tableau.x_output)
@@ -10349,6 +10350,62 @@ def to_pauli_string(
"""
```
+
+```python
+# stim.Tableau.to_stabilizers
+
+# (in class stim.Tableau)
+def to_stabilizers(
+ self,
+ *,
+ canonicalize: bool = False,
+) -> List[stim.PauliString]:
+ """Returns the stabilizer generators of the tableau, optionally canonicalized.
+
+ The stabilizer generators of the tableau are its Z outputs. Canonicalizing
+ standardizes the generators, so that states that are equal will produce the
+ same generators. For example, [ZI, IZ], [ZI, ZZ], amd [ZZ, ZI] describe equal
+ states and all canonicalize to [ZI, IZ].
+
+ The canonical form is computed as follows:
+
+ 1. Get a list of stabilizers using `tableau.z_output(k)` for each k.
+ 2. Perform Gaussian elimination. pivoting on standard generators.
+ 2a) Pivot on g=X0 first, then Z0, X1, Z1, X2, Z2, etc.
+ 2b) Find a stabilizer that uses the generator g. If there are none,
+ go to the next g.
+ 2c) Multiply that stabilizer into all other stabilizers that use the
+ generator g.
+ 2d) Swap that stabilizer with the stabilizer at position `r` then
+ increment `r`. `r` starts at 0.
+
+ Args:
+ canonicalize: Defaults to False. When False, the tableau's Z outputs
+ are returned unchanged. When True, the Z outputs are rewritten
+ into a standard form. Two stabilizer states have the same standard
+ form if and only if they describe equivalent quantum states.
+
+ Returns:
+ A List[stim.PauliString] of the tableau's stabilizer generators.
+
+ Examples:
+ >>> import stim
+ >>> t = stim.Tableau.from_named_gate("CNOT")
+
+ >>> raw_stabilizers = t.to_stabilizers()
+ >>> for e in raw_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+ZZ")
+
+ >>> canonical_stabilizers = t.to_stabilizers(canonicalize=True)
+ >>> for e in canonical_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+_Z")
+ """
+```
+
```python
# stim.Tableau.to_state_vector
diff --git a/doc/stim.pyi b/doc/stim.pyi
index aa99273d7..c6c90d962 100644
--- a/doc/stim.pyi
+++ b/doc/stim.pyi
@@ -8070,6 +8070,55 @@ class Tableau:
>>> print(t.to_pauli_string())
+ZY_X
"""
+ def to_stabilizers(
+ self,
+ *,
+ canonicalize: bool = False,
+ ) -> List[stim.PauliString]:
+ """Returns the stabilizer generators of the tableau, optionally canonicalized.
+
+ The stabilizer generators of the tableau are its Z outputs. Canonicalizing
+ standardizes the generators, so that states that are equal will produce the
+ same generators. For example, [ZI, IZ], [ZI, ZZ], amd [ZZ, ZI] describe equal
+ states and all canonicalize to [ZI, IZ].
+
+ The canonical form is computed as follows:
+
+ 1. Get a list of stabilizers using `tableau.z_output(k)` for each k.
+ 2. Perform Gaussian elimination. pivoting on standard generators.
+ 2a) Pivot on g=X0 first, then Z0, X1, Z1, X2, Z2, etc.
+ 2b) Find a stabilizer that uses the generator g. If there are none,
+ go to the next g.
+ 2c) Multiply that stabilizer into all other stabilizers that use the
+ generator g.
+ 2d) Swap that stabilizer with the stabilizer at position `r` then
+ increment `r`. `r` starts at 0.
+
+ Args:
+ canonicalize: Defaults to False. When False, the tableau's Z outputs
+ are returned unchanged. When True, the Z outputs are rewritten
+ into a standard form. Two stabilizer states have the same standard
+ form if and only if they describe equivalent quantum states.
+
+ Returns:
+ A List[stim.PauliString] of the tableau's stabilizer generators.
+
+ Examples:
+ >>> import stim
+ >>> t = stim.Tableau.from_named_gate("CNOT")
+
+ >>> raw_stabilizers = t.to_stabilizers()
+ >>> for e in raw_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+ZZ")
+
+ >>> canonical_stabilizers = t.to_stabilizers(canonicalize=True)
+ >>> for e in canonical_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+_Z")
+ """
def to_state_vector(
self,
*,
diff --git a/glue/python/src/stim/__init__.pyi b/glue/python/src/stim/__init__.pyi
index aa99273d7..c6c90d962 100644
--- a/glue/python/src/stim/__init__.pyi
+++ b/glue/python/src/stim/__init__.pyi
@@ -8070,6 +8070,55 @@ class Tableau:
>>> print(t.to_pauli_string())
+ZY_X
"""
+ def to_stabilizers(
+ self,
+ *,
+ canonicalize: bool = False,
+ ) -> List[stim.PauliString]:
+ """Returns the stabilizer generators of the tableau, optionally canonicalized.
+
+ The stabilizer generators of the tableau are its Z outputs. Canonicalizing
+ standardizes the generators, so that states that are equal will produce the
+ same generators. For example, [ZI, IZ], [ZI, ZZ], amd [ZZ, ZI] describe equal
+ states and all canonicalize to [ZI, IZ].
+
+ The canonical form is computed as follows:
+
+ 1. Get a list of stabilizers using `tableau.z_output(k)` for each k.
+ 2. Perform Gaussian elimination. pivoting on standard generators.
+ 2a) Pivot on g=X0 first, then Z0, X1, Z1, X2, Z2, etc.
+ 2b) Find a stabilizer that uses the generator g. If there are none,
+ go to the next g.
+ 2c) Multiply that stabilizer into all other stabilizers that use the
+ generator g.
+ 2d) Swap that stabilizer with the stabilizer at position `r` then
+ increment `r`. `r` starts at 0.
+
+ Args:
+ canonicalize: Defaults to False. When False, the tableau's Z outputs
+ are returned unchanged. When True, the Z outputs are rewritten
+ into a standard form. Two stabilizer states have the same standard
+ form if and only if they describe equivalent quantum states.
+
+ Returns:
+ A List[stim.PauliString] of the tableau's stabilizer generators.
+
+ Examples:
+ >>> import stim
+ >>> t = stim.Tableau.from_named_gate("CNOT")
+
+ >>> raw_stabilizers = t.to_stabilizers()
+ >>> for e in raw_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+ZZ")
+
+ >>> canonical_stabilizers = t.to_stabilizers(canonicalize=True)
+ >>> for e in canonical_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+_Z")
+ """
def to_state_vector(
self,
*,
diff --git a/src/stim/circuit/export_qasm.cc b/src/stim/circuit/export_qasm.cc
index 1381e436c..a5a9ec8da 100644
--- a/src/stim/circuit/export_qasm.cc
+++ b/src/stim/circuit/export_qasm.cc
@@ -45,7 +45,6 @@ struct QasmExporter {
reference_sample(stats.num_measurements),
measurement_offset(0),
detector_offset(0) {
-
// Init used_gates.
collect_used_gates(circuit);
@@ -71,11 +70,7 @@ struct QasmExporter {
}
void output_decomposed_operation(
- bool invert_measurement_result,
- GateType g,
- const char *q0_name,
- const char *q1_name,
- const char *m_name) {
+ bool invert_measurement_result, GateType g, const char *q0_name, const char *q1_name, const char *m_name) {
auto q2n = [&](GateTarget t) {
return t.qubit_value() == 0 ? q0_name : q1_name;
};
@@ -159,10 +154,7 @@ struct QasmExporter {
buf_m.str("");
buf_q1 << "q[" << t.qubit_value() << "]";
buf_m << "rec[" << measurement_offset << "]";
- output_measurement(
- t.is_inverted_result_target(),
- buf_q1.str().c_str(),
- buf_m.str().c_str());
+ output_measurement(t.is_inverted_result_target(), buf_q1.str().c_str(), buf_m.str().c_str());
measurement_offset++;
}
for (size_t k = 0; k < cnot.targets.size(); k += 2) {
@@ -180,10 +172,7 @@ struct QasmExporter {
});
}
- void output_decomposable_instruction(
- const CircuitInstruction &instruction,
- bool decompose_inline) {
-
+ void output_decomposable_instruction(const CircuitInstruction &instruction, bool decompose_inline) {
auto f = GATE_DATA[instruction.gate_type].flags;
auto step = (f & GATE_TARGETS_PAIRS) ? 2 : 1;
for (size_t k = 0; k < instruction.targets.size(); k += step) {
@@ -234,8 +223,8 @@ struct QasmExporter {
auto t1 = instruction.targets[k];
auto t2 = instruction.targets[k + 1];
if (t1.is_qubit_target() && t2.is_qubit_target()) {
- out << qasm_names[(int)instruction.gate_type] << " q[" << t1.qubit_value() << "], q[" << t2.qubit_value()
- << "];\n";
+ out << qasm_names[(int)instruction.gate_type] << " q[" << t1.qubit_value() << "], q["
+ << t2.qubit_value() << "];\n";
} else if (t1.is_qubit_target() || t2.is_qubit_target()) {
GateTarget control;
GateTarget target;
diff --git a/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc b/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc
index 98a8ec4d2..54c5b56bc 100644
--- a/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc
+++ b/src/stim/diagram/timeline/timeline_ascii_drawer.test.cc
@@ -513,11 +513,6 @@ TEST(circuit_diagram_timeline_text, repetition_code_transposed) {
TEST(circuit_diagram_timeline_text, test_circuit_all_ops) {
auto circuit = generate_test_circuit_with_all_operations();
- std::cerr << "\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n" << "===============\n";
- std::cerr << "\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n" << "===============\n";
- std::cerr << "\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n" << "===============\n";
- std::cerr << "\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n" << "===============\n";
- std::cerr << "\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n" << "===============\n";
ASSERT_EQ("\n" + DiagramTimelineAsciiDrawer::make_diagram(circuit).str() + "\n", R"DIAGRAM(
/-------------------\ /--------------\ /---------------------\ /-----------------------------------------------------------------------------------------------------------------------------------------------------\ /------------------------------------\ /REP 3 /---\ \ /--------------------------------------------------------------------------------------------------------------------------\
q0: -QUBIT_COORDS(1,2,3)-I-C_XYZ-SQRT_X-----ZSWAP-----SQRT_XX-----X------------DEPOLARIZE1(0.02)---------------X_ERROR(0.01)------------------------------------------------------------------------------------------------MPP[X]:rec[2]-MPP[Z]:rec[3]-MRX:rec[4]-MXX:rec[11]-|------H-@---|---MR:rec[15]-X_ERROR(0.1)-MR(0.01):rec[16]-DETECTOR(2,4,6):D0=rec[16]-OBSERVABLE_INCLUDE:L0*=rec[16]-MPAD:rec[17]-MPAD:rec[19]-MRX:rec[20]----X^rec[24]--
diff --git a/src/stim/simulators/tableau_simulator.inl b/src/stim/simulators/tableau_simulator.inl
index 112fde44a..de872532b 100644
--- a/src/stim/simulators/tableau_simulator.inl
+++ b/src/stim/simulators/tableau_simulator.inl
@@ -1445,35 +1445,7 @@ std::pair> TableauSimulator::measure_kickback_x(GateTarg
template
std::vector> TableauSimulator::canonical_stabilizers() const {
- Tableau t = inv_state.inverse();
- size_t n = t.num_qubits;
- std::vector> stabilizers;
- for (size_t k = 0; k < n; k++) {
- stabilizers.push_back(t.zs[k]);
- }
-
- size_t min_pivot = 0;
- for (size_t q = 0; q < n; q++) {
- for (size_t b = 0; b < 2; b++) {
- size_t pivot = min_pivot;
- while (pivot < n && !(b ? stabilizers[pivot].zs : stabilizers[pivot].xs)[q]) {
- pivot++;
- }
- if (pivot == n) {
- continue;
- }
- for (size_t s = 0; s < n; s++) {
- if (s != pivot && (b ? stabilizers[s].zs : stabilizers[s].xs)[q]) {
- stabilizers[s].ref() *= stabilizers[pivot];
- }
- }
- if (min_pivot != pivot) {
- std::swap(stabilizers[min_pivot], stabilizers[pivot]);
- }
- min_pivot += 1;
- }
- }
- return stabilizers;
+ return inv_state.inverse().stabilizers(true);
}
template
diff --git a/src/stim/stabilizers/tableau.h b/src/stim/stabilizers/tableau.h
index 0c728598e..30aa684c7 100644
--- a/src/stim/stabilizers/tableau.h
+++ b/src/stim/stabilizers/tableau.h
@@ -227,6 +227,8 @@ struct Tableau {
PauliString inverse_y_output(size_t input_index, bool skip_sign = false) const;
/// Faster version of tableau.inverse().zs[input_index].
PauliString inverse_z_output(size_t input_index, bool skip_sign = false) const;
+
+ std::vector> stabilizers(bool canonical) const;
};
template
diff --git a/src/stim/stabilizers/tableau.inl b/src/stim/stabilizers/tableau.inl
index 3c7a750a1..88d861599 100644
--- a/src/stim/stabilizers/tableau.inl
+++ b/src/stim/stabilizers/tableau.inl
@@ -749,4 +749,38 @@ PauliString Tableau::y_output(size_t input_index) const {
return result;
}
+template
+std::vector> Tableau::stabilizers(bool canonical) const {
+ std::vector> stabilizers;
+ for (size_t k = 0; k < num_qubits; k++) {
+ stabilizers.push_back(zs[k]);
+ }
+
+ if (canonical) {
+ size_t min_pivot = 0;
+ for (size_t q = 0; q < num_qubits; q++) {
+ for (size_t b = 0; b < 2; b++) {
+ size_t pivot = min_pivot;
+ while (pivot < num_qubits && !(b ? stabilizers[pivot].zs : stabilizers[pivot].xs)[q]) {
+ pivot++;
+ }
+ if (pivot == num_qubits) {
+ continue;
+ }
+ for (size_t s = 0; s < num_qubits; s++) {
+ if (s != pivot && (b ? stabilizers[s].zs : stabilizers[s].xs)[q]) {
+ stabilizers[s].ref() *= stabilizers[pivot];
+ }
+ }
+ if (min_pivot != pivot) {
+ std::swap(stabilizers[min_pivot], stabilizers[pivot]);
+ }
+ min_pivot += 1;
+ }
+ }
+ }
+
+ return stabilizers;
+}
+
} // namespace stim
diff --git a/src/stim/stabilizers/tableau.pybind.cc b/src/stim/stabilizers/tableau.pybind.cc
index ce5eb5d8f..7c946203e 100644
--- a/src/stim/stabilizers/tableau.pybind.cc
+++ b/src/stim/stabilizers/tableau.pybind.cc
@@ -2193,4 +2193,64 @@ void stim_pybind::pybind_tableau_methods(pybind11::module &m, pybind11::class_ &self, bool canonical) {
+ auto stabilizers = self.stabilizers(canonical);
+ std::vector result;
+ result.reserve(stabilizers.size());
+ for (auto &s : stabilizers) {
+ result.emplace_back(std::move(s), false);
+ }
+ return result;
+ },
+ pybind11::kw_only(),
+ pybind11::arg("canonicalize") = false,
+ clean_doc_string(R"DOC(
+ Returns the stabilizer generators of the tableau, optionally canonicalized.
+
+ The stabilizer generators of the tableau are its Z outputs. Canonicalizing
+ standardizes the generators, so that states that are equal will produce the
+ same generators. For example, [ZI, IZ], [ZI, ZZ], amd [ZZ, ZI] describe equal
+ states and all canonicalize to [ZI, IZ].
+
+ The canonical form is computed as follows:
+
+ 1. Get a list of stabilizers using `tableau.z_output(k)` for each k.
+ 2. Perform Gaussian elimination. pivoting on standard generators.
+ 2a) Pivot on g=X0 first, then Z0, X1, Z1, X2, Z2, etc.
+ 2b) Find a stabilizer that uses the generator g. If there are none,
+ go to the next g.
+ 2c) Multiply that stabilizer into all other stabilizers that use the
+ generator g.
+ 2d) Swap that stabilizer with the stabilizer at position `r` then
+ increment `r`. `r` starts at 0.
+
+ Args:
+ canonicalize: Defaults to False. When False, the tableau's Z outputs
+ are returned unchanged. When True, the Z outputs are rewritten
+ into a standard form. Two stabilizer states have the same standard
+ form if and only if they describe equivalent quantum states.
+
+ Returns:
+ A List[stim.PauliString] of the tableau's stabilizer generators.
+
+ Examples:
+ >>> import stim
+ >>> t = stim.Tableau.from_named_gate("CNOT")
+
+ >>> raw_stabilizers = t.to_stabilizers()
+ >>> for e in raw_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+ZZ")
+
+ >>> canonical_stabilizers = t.to_stabilizers(canonicalize=True)
+ >>> for e in canonical_stabilizers:
+ ... print(repr(e))
+ stim.PauliString("+Z_")
+ stim.PauliString("+_Z")
+ )DOC")
+ .data());
}
diff --git a/src/stim/stabilizers/tableau_pybind_test.py b/src/stim/stabilizers/tableau_pybind_test.py
index 95a1e6925..83060b64c 100644
--- a/src/stim/stabilizers/tableau_pybind_test.py
+++ b/src/stim/stabilizers/tableau_pybind_test.py
@@ -923,3 +923,24 @@ def test_signs():
_ = t.y_sign(2)
with pytest.raises(ValueError, match="target"):
_ = t.z_sign(2)
+
+
+def test_to_stabilizers():
+ t = stim.Tableau.from_stabilizers([
+ stim.PauliString("XXXX"),
+ stim.PauliString("YYYY"),
+ stim.PauliString("YYZZ"),
+ stim.PauliString("XXZZ"),
+ ])
+ assert t.to_stabilizers() == [
+ stim.PauliString("XXXX"),
+ stim.PauliString("YYYY"),
+ stim.PauliString("YYZZ"),
+ stim.PauliString("XXZZ"),
+ ]
+ assert t.to_stabilizers(canonicalize=True) == [
+ stim.PauliString("-XX__"),
+ stim.PauliString("-ZZ__"),
+ stim.PauliString("-__XX"),
+ stim.PauliString("-__ZZ"),
+ ]