Skip to content

Commit 11726fc

Browse files
authoredJul 7, 2022
Call cirq.testing.assert_equivalent_repr for cirq.SingleQubitCliffordGate and cirq.CliffordTableau (#5664)
`cirq.CliffordTableau` is represented by `rs`, `xs` and `zs` yet these can't be passed into the `__init__` but are instead calculated and/or set in the class. This PR adds `xs`, `xs`, and `zs` as `Optional` arguments so that the `CliffordTableau` can be reconstructed from the `__repr__`. Fixes: #5641
1 parent 1ca3e95 commit 11726fc

File tree

4 files changed

+114
-35
lines changed

4 files changed

+114
-35
lines changed
 

‎cirq-core/cirq/ops/clifford_gate.py

+1-10
Original file line numberDiff line numberDiff line change
@@ -879,16 +879,7 @@ def equivalent_gate_before(self, after: 'SingleQubitCliffordGate') -> 'SingleQub
879879
return self.merged_with(after).merged_with(self**-1)
880880

881881
def __repr__(self) -> str:
882-
x = self.pauli_tuple(pauli_gates.X)
883-
y = self.pauli_tuple(pauli_gates.Y)
884-
z = self.pauli_tuple(pauli_gates.Z)
885-
x_sign = '-' if x[1] else '+'
886-
y_sign = '-' if y[1] else '+'
887-
z_sign = '-' if z[1] else '+'
888-
return (
889-
f'cirq.SingleQubitCliffordGate(X:{x_sign}{x[0]!s}, '
890-
f'Y:{y_sign}{y[0]!s}, Z:{z_sign}{z[0]!s})'
891-
)
882+
return f'cirq.CliffordGate.from_clifford_tableau({self.clifford_tableau!r})'
892883

893884
def _circuit_diagram_info_(
894885
self, args: 'cirq.CircuitDiagramInfoArgs'

‎cirq-core/cirq/ops/clifford_gate_test.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -329,16 +329,22 @@ def test_eq_ne_and_hash():
329329

330330

331331
@pytest.mark.parametrize(
332-
'gate,rep',
332+
'gate',
333333
(
334-
(cirq.SingleQubitCliffordGate.I, 'cirq.SingleQubitCliffordGate(X:+X, Y:+Y, Z:+Z)'),
335-
(cirq.SingleQubitCliffordGate.H, 'cirq.SingleQubitCliffordGate(X:+Z, Y:-Y, Z:+X)'),
336-
(cirq.SingleQubitCliffordGate.X, 'cirq.SingleQubitCliffordGate(X:+X, Y:-Y, Z:-Z)'),
337-
(cirq.SingleQubitCliffordGate.X_sqrt, 'cirq.SingleQubitCliffordGate(X:+X, Y:+Z, Z:-Y)'),
334+
cirq.SingleQubitCliffordGate.I,
335+
cirq.SingleQubitCliffordGate.H,
336+
cirq.SingleQubitCliffordGate.X,
337+
cirq.SingleQubitCliffordGate.X_sqrt,
338338
),
339339
)
340-
def test_repr(gate, rep):
341-
assert repr(gate) == rep
340+
def test_repr_gate(gate):
341+
cirq.testing.assert_equivalent_repr(gate)
342+
343+
344+
def test_repr_operation():
345+
cirq.testing.assert_equivalent_repr(
346+
cirq.SingleQubitCliffordGate.from_pauli(cirq.Z).on(cirq.LineQubit(2))
347+
)
342348

343349

344350
@pytest.mark.parametrize(

‎cirq-core/cirq/qis/clifford_tableau.py

+84-16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import numpy as np
1818

1919
from cirq import protocols
20+
from cirq._compat import proper_repr
2021
from cirq.qis import quantum_state_representation
2122
from cirq.value import big_endian_int_to_digits, linear_dict
2223

@@ -137,30 +138,92 @@ class CliffordTableau(StabilizerState):
137138
an eigenoperator of the state vector with eigenvalue one: P|psi> = |psi>.
138139
"""
139140

140-
def __init__(self, num_qubits, initial_state: int = 0):
141+
def __init__(
142+
self,
143+
num_qubits,
144+
initial_state: int = 0,
145+
rs: Optional[np.ndarray] = None,
146+
xs: Optional[np.ndarray] = None,
147+
zs: Optional[np.ndarray] = None,
148+
):
141149
"""Initializes CliffordTableau
142150
Args:
143151
num_qubits: The number of qubits in the system.
144152
initial_state: The computational basis representation of the
145153
state as a big endian int.
146154
"""
147155
self.n = num_qubits
148-
149-
# The last row (`2n+1`-th row) is the scratch row used in _measurement
156+
self.initial_state = initial_state
157+
# _reconstruct_* adds the last row (`2n+1`-th row) to the input arrays,
158+
# which is the scratch row used in _measurement
150159
# computation process only. It should not be exposed to external usage.
151-
self._rs = np.zeros(2 * self.n + 1, dtype=bool)
152-
153-
for (i, val) in enumerate(
154-
big_endian_int_to_digits(initial_state, digit_count=num_qubits, base=2)
155-
):
156-
self._rs[self.n + i] = bool(val)
160+
self._rs = self._reconstruct_rs(rs)
161+
self._xs = self._reconstruct_xs(xs)
162+
self._zs = self._reconstruct_zs(zs)
163+
164+
def _reconstruct_rs(self, rs: Optional[np.ndarray]) -> np.ndarray:
165+
if rs is None:
166+
new_rs = np.zeros(2 * self.n + 1, dtype=bool)
167+
for (i, val) in enumerate(
168+
big_endian_int_to_digits(self.initial_state, digit_count=self.n, base=2)
169+
):
170+
new_rs[self.n + i] = bool(val)
171+
else:
172+
shape = rs.shape
173+
if len(shape) == 1 and shape[0] == 2 * self.n and rs.dtype == np.dtype(bool):
174+
new_rs = np.append(rs, np.zeros(1, dtype=bool))
175+
else:
176+
raise ValueError(
177+
f"The value you passed for rs is not the correct shape and/or type. "
178+
f"Please confirm that it's a single row with 2*num_qubits columns "
179+
f"and of type bool."
180+
)
181+
return new_rs
182+
183+
def _reconstruct_xs(self, xs: Optional[np.ndarray]) -> np.ndarray:
184+
if xs is None:
185+
new_xs = np.zeros((2 * self.n + 1, self.n), dtype=bool)
186+
for i in range(self.n):
187+
new_xs[i, i] = True
188+
else:
189+
shape = xs.shape
190+
if (
191+
len(shape) == 2
192+
and shape[0] == 2 * self.n
193+
and shape[1] == self.n
194+
and xs.dtype == np.dtype(bool)
195+
):
196+
new_xs = np.append(xs, np.zeros((1, self.n), dtype=bool), axis=0)
197+
else:
198+
raise ValueError(
199+
f"The value you passed for xs is not the correct shape and/or type. "
200+
f"Please confirm that it's 2*num_qubits rows, num_qubits columns, "
201+
f"and of type bool."
202+
)
203+
return new_xs
157204

158-
self._xs = np.zeros((2 * self.n + 1, self.n), dtype=bool)
159-
self._zs = np.zeros((2 * self.n + 1, self.n), dtype=bool)
205+
def _reconstruct_zs(self, zs: Optional[np.ndarray]) -> np.ndarray:
160206

161-
for i in range(self.n):
162-
self._xs[i, i] = True
163-
self._zs[self.n + i, i] = True
207+
if zs is None:
208+
new_zs = np.zeros((2 * self.n + 1, self.n), dtype=bool)
209+
for i in range(self.n):
210+
new_zs[self.n + i, i] = True
211+
else:
212+
shape = zs.shape
213+
if (
214+
len(shape) == 2
215+
and shape[0] == 2 * self.n
216+
and shape[1] == self.n
217+
and zs.dtype == np.dtype(bool)
218+
):
219+
new_zs = np.append(zs, np.zeros((1, self.n), dtype=bool), axis=0)
220+
else:
221+
raise ValueError(
222+
f"The value you passed for zs is not the correct shape and/or type. "
223+
f"Please confirm that it's 2*num_qubits rows, num_qubits columns, "
224+
f"and of type bool."
225+
)
226+
return new_zs
164227

165228
@property
166229
def xs(self) -> np.ndarray:
@@ -233,8 +296,13 @@ def copy(self, deep_copy_buffers: bool = True) -> 'CliffordTableau':
233296
return state
234297

235298
def __repr__(self) -> str:
236-
stabilizers = ", ".join([repr(stab) for stab in self.stabilizers()])
237-
return f'stabilizers: [{stabilizers}]'
299+
return (
300+
f"cirq.CliffordTableau({self.n},"
301+
f"rs={proper_repr(np.delete(self._rs, len(self._rs)-1))}, "
302+
f"xs={proper_repr(np.delete(self._xs, len(self._xs)-1, axis=0))},"
303+
f"zs={proper_repr(np.delete(self._zs, len(self._zs)-1, axis=0))}, "
304+
f"initial_state={self.initial_state})"
305+
)
238306

239307
def __str__(self) -> str:
240308
string = ''

‎cirq-core/cirq/qis/clifford_tableau_test.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ def test_tableau_initial_state_string(num_qubits):
5555
assert splitted_represent_string[n] == expected_string
5656

5757

58+
def test_tableau_invalid_initial_state():
59+
with pytest.raises(ValueError, match="2*num_qubits columns and of type bool."):
60+
cirq.CliffordTableau(1, rs=np.zeros(1, dtype=bool))
61+
62+
with pytest.raises(
63+
ValueError, match="2*num_qubits rows, num_qubits columns, and of type bool."
64+
):
65+
cirq.CliffordTableau(1, xs=np.zeros(1, dtype=bool))
66+
67+
with pytest.raises(
68+
ValueError, match="2*num_qubits rows, num_qubits columns, and of type bool."
69+
):
70+
cirq.CliffordTableau(1, zs=np.zeros(1, dtype=bool))
71+
72+
5873
def test_stabilizers():
5974
# Note: the stabilizers are not unique for one state. We just use the one
6075
# produced by the tableau algorithm.
@@ -271,8 +286,7 @@ def test_str():
271286

272287

273288
def test_repr():
274-
t = cirq.CliffordTableau(num_qubits=1)
275-
assert repr(t) == "stabilizers: [cirq.DensePauliString('Z', coefficient=(1+0j))]"
289+
cirq.testing.assert_equivalent_repr(cirq.CliffordTableau(num_qubits=1))
276290

277291

278292
def test_str_full():

0 commit comments

Comments
 (0)
Please sign in to comment.