Skip to content

Commit 9173bcc

Browse files
95-martin-orionrht
authored andcommitted
Allow CircuitOperations in cirq.optimized_for_sycamore (quantumlib#4336)
_EDIT: This description is outdated - see discussion below for details._ This PR enables serialization of `CircuitOperation`s in the default serializers. Additional changes to make this work smoothly: * Added `preserve_structure=True` to the Sycamore optimizer. This ensures that the contents of `CircuitOperation`s are converted to a Sycamore-friendly format, but the `CircuitOperation` itself is preserved. * Updated `cirq.decompose` to support intercepting decomposers alongside `preserve_structure=True`, and removed redundant overloads. * Documented usage of subcircuits in the context of reducing serialization size of circuits. quantumlib#3634 is mostly resolved by this issue, although further optimizations remain possible.
1 parent 8e80b43 commit 9173bcc

File tree

4 files changed

+88
-2
lines changed

4 files changed

+88
-2
lines changed

cirq-google/cirq_google/optimizers/convert_to_sycamore_gates.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ def _is_native_sycamore_op(self, op: cirq.Operation) -> bool:
9797
):
9898
return True
9999

100+
if gate is None and isinstance(op.untagged, cirq.CircuitOperation):
101+
subcircuit = op.untagged.circuit
102+
return all(self._is_native_sycamore_op(op) for op in subcircuit.all_operations())
103+
100104
return False
101105

102106
# TODO(#3388) Add summary line to docstring.
@@ -131,12 +135,13 @@ def on_stuck_raise(bad):
131135
keep=self._is_native_sycamore_op,
132136
intercepting_decomposer=self._convert_one,
133137
on_stuck_raise=None if self.ignore_failures else on_stuck_raise,
138+
preserve_structure=True, # keep CircuitOps but decompose their contents
134139
)
135140

136141
def optimization_at(
137142
self, circuit: cirq.Circuit, index: int, op: cirq.Operation
138143
) -> Optional[cirq.PointOptimizationSummary]:
139-
if not isinstance(op, cirq.GateOperation):
144+
if not isinstance(op, (cirq.GateOperation, cirq.CircuitOperation)):
140145
return None
141146

142147
gate = op.gate

cirq-google/cirq_google/optimizers/convert_to_sycamore_gates_test.py

+40-1
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,52 @@ def test_single_qubit_gate_phased_xz():
7878
assert ops[0].gate == gate
7979

8080

81+
def test_circuit_operation_inspection():
82+
q0, q1 = cirq.LineQubit.range(2)
83+
gate = cirq.PhasedXZGate(axis_phase_exponent=0.2, x_exponent=0.3, z_exponent=0.4)
84+
cop = cirq.CircuitOperation(cirq.FrozenCircuit(gate(q0)))
85+
assert cgoc.ConvertToSycamoreGates()._is_native_sycamore_op(cop)
86+
87+
cop2 = cirq.CircuitOperation(cirq.FrozenCircuit(cirq.SWAP(q0, q1)))
88+
assert not cgoc.ConvertToSycamoreGates()._is_native_sycamore_op(cop2)
89+
90+
91+
def test_circuit_operation_conversion():
92+
q0, q1 = cirq.LineQubit.range(2)
93+
subcircuit = cirq.FrozenCircuit(cirq.X(q0), cirq.SWAP(q0, q1))
94+
circuit = cirq.Circuit(cirq.CircuitOperation(subcircuit))
95+
converted_circuit = circuit.copy()
96+
cgoc.ConvertToSycamoreGates().optimize_circuit(converted_circuit)
97+
# Verify that the CircuitOperation was preserved.
98+
ops = list(converted_circuit.all_operations())
99+
assert isinstance(ops[0], cirq.CircuitOperation)
100+
# Verify that the contents of the CircuitOperation were optimized.
101+
reconverted_subcircuit = ops[0].circuit.unfreeze().copy()
102+
cgoc.ConvertToSycamoreGates().optimize_circuit(reconverted_subcircuit)
103+
assert ops[0].circuit == reconverted_subcircuit
104+
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(
105+
circuit, converted_circuit, atol=1e-8
106+
)
107+
108+
81109
def test_unsupported_gate():
82110
class UnknownGate(cirq.TwoQubitGate):
83111
pass
84112

113+
q0, q1 = cirq.LineQubit.range(2)
114+
circuit = cirq.Circuit(UnknownGate()(q0, q1))
115+
with pytest.raises(ValueError, match='Unrecognized gate: '):
116+
cgoc.ConvertToSycamoreGates().optimize_circuit(circuit)
117+
118+
119+
def test_nested_unsupported_gate():
120+
class UnknownGate(cirq.TwoQubitGate):
121+
pass
122+
85123
q0 = cirq.LineQubit(0)
86124
q1 = cirq.LineQubit(1)
87-
circuit = cirq.Circuit(UnknownGate()(q0, q1))
125+
subcircuit = cirq.FrozenCircuit(UnknownGate()(q0, q1))
126+
circuit = cirq.Circuit(cirq.CircuitOperation(subcircuit))
88127
with pytest.raises(ValueError, match='Unrecognized gate: '):
89128
cgoc.ConvertToSycamoreGates().optimize_circuit(circuit)
90129

docs/google/best_practices.md

+33
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,39 @@ my_circuit = cirq.Circuit()
4949
sycamore_circuit = cg.optimized_for_sycamore(my_circuit, new_device=cg.Sycamore, optimizer_type='sqrt_iswap')
5050
```
5151

52+
### Using CircuitOperation to reduce circuit size
53+
54+
Particularly large batches (or sweeps) of circuits may encounter errors when
55+
sent to Quantum Engine due to an upper limit on request size. If the circuits
56+
in question have a repetitive structure, `cirq.CircuitOperation`s can be used
57+
to reduce the request size and avoid this limit.
58+
59+
`optimized_for_sycamore` will preserve `CircuitOperation`s while optimizing
60+
their contents.
61+
62+
```python
63+
import cirq
64+
import cirq_google as cg
65+
66+
# Repeatedly apply Hadamard and measurement to 10 qubits.
67+
my_circuit = cirq.Circuit()
68+
qubits = cirq.GridQubit.rect(2, 5)
69+
for i in range(100):
70+
my_circuit.append(cirq.H.on_each(*qubits))
71+
for q in qubits:
72+
my_circuit.append(cirq.measure(q, key=f'm{q}'))
73+
74+
# The same circuit, but defined using CircuitOperations.
75+
# This is ~1000x smaller when serialized!
76+
sub_circuit = cirq.Circuit(cirq.H(qubits[0]), cirq.measure(qubits[0], key='m'))
77+
circuit_op = cirq.CircuitOperation(sub_circuit.freeze())
78+
circuit_op = circuit_op.with_qubits([q])
79+
circuit_op = circuit_op.with_measurement_key_mapping({'m': f'm{q}'})
80+
circuit_op = circuit_op.repeat(100)
81+
short_circuit = cirq.Circuit(circuit_op for q in qubits)
82+
```
83+
84+
5285
## Running circuits faster
5386

5487
The following sections give tips and tricks that allow you to improve your

docs/google/devices.md

+9
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ For decay experiments and other applications, a WaitGate is provided
223223
that causes the device to idle for a specified amount of time.
224224
This can be accomplished by specifying a `cirq.WaitGate`.
225225

226+
227+
### Subcircuits
228+
229+
Circuits with a repetitive structure can benefit from using
230+
`cirq.CircuitOperation` to specify "subcircuits" within the overall circuit.
231+
Using this type condenses the serialized representation of the circuit, which
232+
may help for circuits that would otherwise run into size limitations.
233+
234+
226235
## Specific Device Layouts
227236

228237
The following devices are provided as part of cirq and can help you get your

0 commit comments

Comments
 (0)