Skip to content

Commit e1de0e6

Browse files
authored
Fix stim.Circuit.to_tableau ignoring SPP and SPP_DAG (#847)
- Fix `SPP` not being classified as a unitary gate - Fix `SPP_DAG` not being classified as a unitary gate - Fix some code/tests assuming a gate being unitary implies it has a non-target-dependent unitary matrix Fixes #846
1 parent 34b4168 commit e1de0e6

18 files changed

+53
-23
lines changed

doc/gates.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3043,7 +3043,7 @@ Decomposition (into H, S, CX, M, R):
30433043

30443044

30453045
<a name="SPP"></a>
3046-
### The 'SPP' Instruction
3046+
### The 'SPP' Gate
30473047

30483048
The generalized S gate. Phases the -1 eigenspace of Pauli product observables by i.
30493049

@@ -3109,7 +3109,7 @@ Decomposition (into H, S, CX, M, R):
31093109

31103110

31113111
<a name="SPP_DAG"></a>
3112-
### The 'SPP_DAG' Instruction
3112+
### The 'SPP_DAG' Gate
31133113

31143114
The generalized S_DAG gate. Phases the -1 eigenspace of Pauli product observables by -i.
31153115

src/stim/circuit/circuit_pybind_test.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,6 @@ def test_likeliest_error_sat_problem():
864864
DETECTOR rec[-1] rec[-2]
865865
""")
866866
sat_str = c.likeliest_error_sat_problem(quantization=100)
867-
print(sat_str)
868867
assert sat_str == 'p wcnf 2 4 401\n18 -1 0\n100 -2 0\n401 -1 0\n401 2 0\n'
869868

870869

@@ -1902,3 +1901,14 @@ def test_pop():
19021901
def test_circuit_create_with_odd_cx():
19031902
with pytest.raises(ValueError, match="0, 1, 2"):
19041903
stim.Circuit("CX 0 1 2")
1904+
1905+
1906+
def test_to_tableau():
1907+
assert stim.Circuit().to_tableau() == stim.Tableau(0)
1908+
assert stim.Circuit("QUBIT_COORDS 0").to_tableau() == stim.Tableau(1)
1909+
assert stim.Circuit("I 0").to_tableau() == stim.Tableau(1)
1910+
assert stim.Circuit("H 0").to_tableau() == stim.Tableau.from_named_gate("H")
1911+
assert stim.Circuit("CX 0 1").to_tableau() == stim.Tableau.from_named_gate("CX")
1912+
assert stim.Circuit("SPP Z0").to_tableau() == stim.Tableau.from_named_gate("S")
1913+
assert stim.Circuit("SPP X0").to_tableau() == stim.Tableau.from_named_gate("SQRT_X")
1914+
assert stim.Circuit("SPP_DAG Y0*Y1").to_tableau() == stim.Tableau.from_named_gate("SQRT_YY_DAG")

src/stim/cmd/command_help.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ void print_stabilizer_generators(Acc &out, const Gate &gate) {
290290
}
291291

292292
void print_bloch_vector(Acc &out, const Gate &gate) {
293-
if (!(gate.flags & GATE_IS_UNITARY) || (gate.flags & GATE_TARGETS_PAIRS)) {
293+
if (!(gate.flags & GATE_IS_UNITARY) || !(gate.flags & GATE_IS_SINGLE_QUBIT_GATE)) {
294294
return;
295295
}
296296

@@ -343,7 +343,7 @@ void print_bloch_vector(Acc &out, const Gate &gate) {
343343
}
344344

345345
void print_unitary_matrix(Acc &out, const Gate &gate) {
346-
if (!(gate.flags & GATE_IS_UNITARY)) {
346+
if (!gate.has_known_unitary_matrix()) {
347347
return;
348348
}
349349
auto matrix = gate.unitary();

src/stim/gates/gate_data_pauli_product.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ S 1
9999
.id = GateType::SPP,
100100
.best_candidate_inverse_id = GateType::SPP_DAG,
101101
.arg_count = 0,
102-
.flags = (GateFlags)(GATE_TARGETS_PAULI_STRING | GATE_TARGETS_COMBINERS),
102+
.flags = (GateFlags)(GATE_TARGETS_PAULI_STRING | GATE_TARGETS_COMBINERS | GATE_IS_UNITARY),
103103
.category = "P_Generalized Pauli Product Gates",
104104
.help = R"MARKDOWN(
105105
The generalized S gate. Phases the -1 eigenspace of Pauli product observables by i.
@@ -174,7 +174,7 @@ CX 2 1
174174
.id = GateType::SPP_DAG,
175175
.best_candidate_inverse_id = GateType::SPP,
176176
.arg_count = 0,
177-
.flags = (GateFlags)(GATE_TARGETS_PAULI_STRING | GATE_TARGETS_COMBINERS),
177+
.flags = (GateFlags)(GATE_TARGETS_PAULI_STRING | GATE_TARGETS_COMBINERS | GATE_IS_UNITARY),
178178
.category = "P_Generalized Pauli Product Gates",
179179
.help = R"MARKDOWN(
180180
The generalized S_DAG gate. Phases the -1 eigenspace of Pauli product observables by -i.

src/stim/gates/gates.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ std::array<float, 4> Gate::to_axis_angle() const {
267267
return {rx, ry, rz, acosf(rs) * 2};
268268
}
269269

270+
bool Gate::has_known_unitary_matrix() const {
271+
return (flags & GateFlags::GATE_IS_UNITARY) && (flags & (GateFlags::GATE_IS_SINGLE_QUBIT_GATE | GateFlags::GATE_TARGETS_PAIRS));
272+
}
273+
270274
std::vector<std::vector<std::complex<float>>> Gate::unitary() const {
271275
if (unitary_data.size() != 2 && unitary_data.size() != 4) {
272276
throw std::out_of_range(std::string(name) + " doesn't have 1q or 2q unitary data.");

src/stim/gates/gates.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ struct Gate {
258258

259259
template <size_t W>
260260
std::vector<Flow<W>> flows() const {
261-
if (flags & GateFlags::GATE_IS_UNITARY) {
261+
if (has_known_unitary_matrix()) {
262262
auto t = tableau<W>();
263263
if (flags & GateFlags::GATE_TARGETS_PAIRS) {
264264
return {
@@ -285,6 +285,12 @@ struct Gate {
285285
bool is_symmetric() const;
286286
GateType hadamard_conjugated(bool ignoring_sign) const;
287287

288+
/// Determines if the gate has a specified unitary matrix.
289+
///
290+
/// Some unitary gates, such as SPP, don't have a specified matrix because the
291+
/// matrix depends crucially on the targets.
292+
bool has_known_unitary_matrix() const;
293+
288294
/// Converts a single qubit unitary gate into an euler-angles rotation.
289295
///
290296
/// Returns:

src/stim/gates/gates.pybind.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pybind11::object gate_tableau(const Gate &self) {
5050
return pybind11::none();
5151
}
5252
pybind11::object gate_unitary_matrix(const Gate &self) {
53-
if (self.flags & GATE_IS_UNITARY) {
53+
if (self.has_known_unitary_matrix()) {
5454
auto r = self.unitary();
5555
auto n = r.size();
5656
std::complex<float> *buffer = new std::complex<float>[n * n];

src/stim/gates/gates.test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ TEST_EACH_WORD_SIZE_W(gate_data, decompositions_are_correct, {
144144

145145
TEST_EACH_WORD_SIZE_W(gate_data, unitary_inverses_are_correct, {
146146
for (const auto &g : GATE_DATA.items) {
147-
if (g.flags & GATE_IS_UNITARY) {
147+
if (g.has_known_unitary_matrix()) {
148148
auto g_t_inv = g.tableau<W>().inverse(false);
149149
auto g_inv_t = GATE_DATA[g.best_candidate_inverse_id].tableau<W>();
150150
EXPECT_EQ(g_t_inv, g_inv_t) << g.name;

src/stim/gates/gates_test.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,17 @@ def test_gate_data_repr():
5959
def test_gate_data_inverse():
6060
for v in stim.gate_data().values():
6161
assert v.is_unitary == (v.inverse is not None)
62-
if v.is_unitary:
63-
assert np.allclose(v.unitary_matrix.conj().T, v.inverse.unitary_matrix, atol=1e-6)
62+
matrix = v.unitary_matrix
63+
if matrix is not None:
64+
assert v.is_unitary
65+
assert np.allclose(matrix.conj().T, v.inverse.unitary_matrix, atol=1e-6)
6466
assert v.inverse == v.generalized_inverse
6567

6668
assert stim.gate_data('H').inverse == stim.gate_data('H')
6769
assert stim.gate_data('S').inverse == stim.gate_data('S_DAG')
6870
assert stim.gate_data('M').inverse is None
6971
assert stim.gate_data('CXSWAP').inverse == stim.gate_data('SWAPCX')
72+
assert stim.gate_data('SPP').inverse == stim.gate_data('SPP_DAG')
7073

7174
assert stim.gate_data('S').generalized_inverse == stim.gate_data('S_DAG')
7275
assert stim.gate_data('M').generalized_inverse == stim.gate_data('M')

src/stim/py/stim_pybind_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ def test_main_write_to_file():
136136
assert "Generated repetition_code" in f.read()
137137

138138

139+
def test_main_help(capsys):
140+
assert stim.main(command_line_args=["help"]) == 0
141+
captured = capsys.readouterr()
142+
assert captured.err == ""
143+
assert 'Available stim commands' in captured.out
144+
145+
139146
def test_main_redirects_stdout(capsys):
140147
assert stim.main(command_line_args=[
141148
"gen",

0 commit comments

Comments
 (0)