Skip to content

Commit 518d828

Browse files
authored
Add support for deep=True to merge_single_qubit_gates* transformers (#5123)
- Adds support to recursively run `cirq.merge_single_qubit_moments_to_phxz` transformer on circuits wrapped inside a circuit operation by setting deep=True in transformer context. - Also adds tests for `cirq.merge_single_qubit_gates_to_phxz` and `cirq.merge_single_qubit_gates_to_phased_x_and_z`, both of which automatically support deep=True flag after #5122 - Part of #5039
1 parent 92d19f6 commit 518d828

File tree

2 files changed

+106
-11
lines changed

2 files changed

+106
-11
lines changed

cirq-core/cirq/transformers/merge_single_qubit_gates.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,9 @@ def merge_func(m1: 'cirq.Moment', m2: 'cirq.Moment') -> Optional['cirq.Moment']:
129129
ret_ops.append(gate(q))
130130
return circuits.Moment(ret_ops)
131131

132-
return transformer_primitives.merge_moments(circuit, merge_func).unfreeze(copy=False)
132+
return transformer_primitives.merge_moments(
133+
circuit,
134+
merge_func,
135+
deep=context.deep if context else False,
136+
tags_to_ignore=tuple(tags_to_ignore),
137+
).unfreeze(copy=False)

cirq-core/cirq/transformers/merge_single_qubit_gates_test.py

+100-10
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,38 @@ def test_merge_single_qubit_gates_into_phased_x_z():
5454
)
5555

5656

57-
def test_merge_single_qubit_gates_into_phxz():
58-
def phxz(a, x, z):
59-
return cirq.PhasedXZGate(
60-
axis_phase_exponent=a,
61-
x_exponent=x,
62-
z_exponent=z,
63-
)
57+
def test_merge_single_qubit_gates_into_phased_x_z_deep():
58+
a = cirq.NamedQubit("a")
59+
c_nested = cirq.FrozenCircuit(cirq.H(a), cirq.Z(a), cirq.H(a).with_tags("ignore"))
60+
c_nested_merged = cirq.FrozenCircuit(
61+
cirq.PhasedXPowGate(phase_exponent=-0.5, exponent=0.5).on(a), cirq.H(a).with_tags("ignore")
62+
)
63+
c_orig = cirq.Circuit(
64+
c_nested,
65+
cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"),
66+
c_nested,
67+
cirq.CircuitOperation(c_nested).repeat(5).with_tags("preserve_tags"),
68+
c_nested,
69+
cirq.CircuitOperation(c_nested).repeat(6),
70+
)
71+
c_expected = cirq.Circuit(
72+
c_nested_merged,
73+
cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"),
74+
c_nested_merged,
75+
cirq.CircuitOperation(c_nested_merged).repeat(5).with_tags("preserve_tags"),
76+
c_nested_merged,
77+
cirq.CircuitOperation(c_nested_merged).repeat(6),
78+
)
79+
context = cirq.TransformerContext(tags_to_ignore=["ignore"], deep=True)
80+
c_new = cirq.merge_single_qubit_gates_to_phased_x_and_z(c_orig, context=context)
81+
cirq.testing.assert_same_circuits(c_new, c_expected)
82+
83+
84+
def _phxz(a: float, x: float, z: float):
85+
return cirq.PhasedXZGate(axis_phase_exponent=a, x_exponent=x, z_exponent=z)
86+
6487

88+
def test_merge_single_qubit_gates_into_phxz():
6589
a, b = cirq.LineQubit.range(2)
6690
c = cirq.Circuit(
6791
cirq.X(a),
@@ -75,16 +99,41 @@ def phxz(a, x, z):
7599
assert_optimizes(
76100
optimized=cirq.merge_single_qubit_gates_to_phxz(c),
77101
expected=cirq.Circuit(
78-
phxz(-1, 1, 0).on(a),
79-
phxz(0.5, 0.5, 0).on(b),
102+
_phxz(-1, 1, 0).on(a),
103+
_phxz(0.5, 0.5, 0).on(b),
80104
cirq.CZ(a, b),
81-
phxz(-0.5, 0.5, 0).on(a),
105+
_phxz(-0.5, 0.5, 0).on(a),
82106
cirq.measure(b, key="m"),
83107
cirq.H(a).with_classical_controls("m"),
84108
),
85109
)
86110

87111

112+
def test_merge_single_qubit_gates_into_phxz_deep():
113+
a = cirq.NamedQubit("a")
114+
c_nested = cirq.FrozenCircuit(cirq.H(a), cirq.Z(a), cirq.H(a).with_tags("ignore"))
115+
c_nested_merged = cirq.FrozenCircuit(_phxz(-0.5, 0.5, 0).on(a), cirq.H(a).with_tags("ignore"))
116+
c_orig = cirq.Circuit(
117+
c_nested,
118+
cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"),
119+
c_nested,
120+
cirq.CircuitOperation(c_nested).repeat(5).with_tags("preserve_tags"),
121+
c_nested,
122+
cirq.CircuitOperation(c_nested).repeat(6),
123+
)
124+
c_expected = cirq.Circuit(
125+
c_nested_merged,
126+
cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"),
127+
c_nested_merged,
128+
cirq.CircuitOperation(c_nested_merged).repeat(5).with_tags("preserve_tags"),
129+
c_nested_merged,
130+
cirq.CircuitOperation(c_nested_merged).repeat(6),
131+
)
132+
context = cirq.TransformerContext(tags_to_ignore=["ignore"], deep=True)
133+
c_new = cirq.merge_single_qubit_gates_to_phxz(c_orig, context=context)
134+
cirq.testing.assert_same_circuits(c_new, c_expected)
135+
136+
88137
def test_merge_single_qubit_moments_to_phxz():
89138
q = cirq.LineQubit.range(3)
90139
c_orig = cirq.Circuit(
@@ -127,3 +176,44 @@ def test_merge_single_qubit_moments_to_phxz():
127176
a: ══════════════════════════════════════════════════════════════════════════════════@═══^═══════
128177
''',
129178
)
179+
180+
181+
def test_merge_single_qubit_moments_to_phxz_deep():
182+
q = cirq.LineQubit.range(3)
183+
x_t_y = cirq.FrozenCircuit(
184+
cirq.Moment(cirq.X.on_each(*q[:2])),
185+
cirq.Moment(cirq.T.on_each(*q[1:])),
186+
cirq.Moment(cirq.Y.on_each(*q[:2])),
187+
)
188+
c_nested = cirq.FrozenCircuit(
189+
x_t_y,
190+
cirq.Moment(cirq.CZ(*q[:2]), cirq.Y(q[2])),
191+
x_t_y,
192+
cirq.Moment(cirq.Y(q[0]).with_tags("ignore"), cirq.Z.on_each(*q[1:])),
193+
)
194+
195+
c_nested_merged = cirq.FrozenCircuit(
196+
[_phxz(-0.25, 0.0, 0.75)(q[1]), _phxz(0.25, 0.0, 0.25)(q[2]), _phxz(-0.5, 0.0, -1.0)(q[0])],
197+
[cirq.CZ(q[0], q[1]), cirq.Y(q[2])],
198+
[_phxz(-0.25, 0.0, 0.75)(q[1]), _phxz(0.25, 0.0, 0.25)(q[2]), _phxz(-0.5, 0.0, -1.0)(q[0])],
199+
cirq.Moment(cirq.Y(q[0]).with_tags("ignore"), cirq.Z.on_each(*q[1:])),
200+
)
201+
c_orig = cirq.Circuit(
202+
c_nested,
203+
cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"),
204+
c_nested,
205+
cirq.CircuitOperation(c_nested).repeat(5).with_tags("preserve_tags"),
206+
c_nested,
207+
cirq.CircuitOperation(c_nested).repeat(6),
208+
)
209+
c_expected = cirq.Circuit(
210+
c_nested_merged,
211+
cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"),
212+
c_nested_merged,
213+
cirq.CircuitOperation(c_nested_merged).repeat(5).with_tags("preserve_tags"),
214+
c_nested_merged,
215+
cirq.CircuitOperation(c_nested_merged).repeat(6),
216+
)
217+
context = cirq.TransformerContext(tags_to_ignore=["ignore"], deep=True)
218+
c_new = cirq.merge_single_qubit_moments_to_phxz(c_orig, context=context)
219+
cirq.testing.assert_same_circuits(c_new, c_expected)

0 commit comments

Comments
 (0)