Skip to content

Commit fe0dc21

Browse files
authored
Don't merge PhaseXPowGate into measurement gate in eject_phased_paulis (#7538)
Fixes #6507 If `PhasedXPowGate` is merged into a measurement gate, then the `final_state_vector` for a simulation is wrong, even if the measurement is still correct. Due to this and the issue mentioned in #6507 , we discussed in the Cirq Cync to remove the merging behavior.
1 parent 0e87d09 commit fe0dc21

File tree

2 files changed

+5
-70
lines changed

2 files changed

+5
-70
lines changed

cirq-core/cirq/transformers/eject_phased_paulis.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ def eject_phased_paulis(
4040
"""Transformer pass to push X, Y, PhasedX & (certain) PhasedXZ gates to the end of the circuit.
4141
4242
As the gates get pushed, they may absorb Z gates, cancel against other
43-
X, Y, or PhasedX gates with exponent=1, get merged into measurements (as
44-
output bit flips), and cause phase kickback operations across CZs (which can
45-
then be removed by the `cirq.eject_z` transformation).
43+
X, Y, or PhasedX gates with exponent=1, and cause phase kickback operations
44+
across CZs (which can then be removed by the `cirq.eject_z` transformation).
4645
4746
`cirq.PhasedXZGate` with `z_exponent=0` (i.e. equivalent to PhasedXPow) or with `x_exponent=0`
4847
and `axis_phase_exponent=0` (i.e. equivalent to ZPowGate) are also supported.
@@ -85,10 +84,6 @@ def map_func(op: cirq.Operation, _: int) -> cirq.OP_TREE:
8584
if t is not None:
8685
return _absorb_z_into_w(op, held_w_phases)
8786

88-
# Dump coherent flips into measurement bit flips.
89-
if isinstance(op.gate, ops.MeasurementGate):
90-
return _dump_into_measurement(op, held_w_phases)
91-
9287
# Cross CZs using kickback.
9388
if _try_get_known_cz_half_turns(op, no_symbolic=not eject_parameterized) is not None:
9489
return (
@@ -140,18 +135,6 @@ def _dump_held(
140135
held_w_phases.pop(q, None)
141136

142137

143-
def _dump_into_measurement(
144-
op: ops.Operation, held_w_phases: dict[ops.Qid, value.TParamVal]
145-
) -> cirq.OP_TREE:
146-
measurement = cast(ops.MeasurementGate, cast(ops.GateOperation, op).gate)
147-
new_measurement = measurement.with_bits_flipped(
148-
*[i for i, q in enumerate(op.qubits) if q in held_w_phases]
149-
).on(*op.qubits)
150-
for q in op.qubits:
151-
held_w_phases.pop(q, None)
152-
return new_measurement
153-
154-
155138
def _potential_cross_whole_w(
156139
op: ops.Operation, atol: float, held_w_phases: dict[ops.Qid, value.TParamVal]
157140
) -> cirq.OP_TREE:

cirq-core/cirq/transformers/eject_phased_paulis_test.py

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -235,60 +235,12 @@ def test_crosses_czs() -> None:
235235
)
236236

237237

238-
def test_toggles_measurements() -> None:
238+
def test_doesnt_change_measurements() -> None:
239239
a = cirq.NamedQubit('a')
240-
b = cirq.NamedQubit('b')
241-
x = sympy.Symbol('x')
242240

243-
# Single.
244-
assert_optimizes(
245-
before=quick_circuit(
246-
[cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], [cirq.measure(a, b)]
247-
),
248-
expected=quick_circuit([cirq.measure(a, b, invert_mask=(True,))]),
249-
)
250241
assert_optimizes(
251-
before=quick_circuit(
252-
[cirq.PhasedXPowGate(phase_exponent=0.25).on(b)], [cirq.measure(a, b)]
253-
),
254-
expected=quick_circuit([cirq.measure(a, b, invert_mask=(False, True))]),
255-
)
256-
assert_optimizes(
257-
before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=x).on(b)], [cirq.measure(a, b)]),
258-
expected=quick_circuit([cirq.measure(a, b, invert_mask=(False, True))]),
259-
eject_parameterized=True,
260-
)
261-
262-
# Multiple.
263-
assert_optimizes(
264-
before=quick_circuit(
265-
[cirq.PhasedXPowGate(phase_exponent=0.25).on(a)],
266-
[cirq.PhasedXPowGate(phase_exponent=0.25).on(b)],
267-
[cirq.measure(a, b)],
268-
),
269-
expected=quick_circuit([cirq.measure(a, b, invert_mask=(True, True))]),
270-
)
271-
272-
# Xmon.
273-
assert_optimizes(
274-
before=quick_circuit(
275-
[cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], [cirq.measure(a, b, key='t')]
276-
),
277-
expected=quick_circuit([cirq.measure(a, b, invert_mask=(True,), key='t')]),
278-
)
279-
280-
# CCOs
281-
assert_optimizes(
282-
before=quick_circuit(
283-
[cirq.PhasedXPowGate(phase_exponent=0.25).on(a)],
284-
[cirq.measure(a, key="m")],
285-
[cirq.X(b).with_classical_controls("m")],
286-
),
287-
expected=quick_circuit(
288-
[cirq.measure(a, invert_mask=(True,), key="m")],
289-
[cirq.X(b).with_classical_controls("m")],
290-
),
291-
compare_unitaries=False,
242+
before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], [cirq.measure(a)]),
243+
expected=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], [cirq.measure(a)]),
292244
)
293245

294246

0 commit comments

Comments
 (0)