Skip to content

Commit 39d55bf

Browse files
BichengYingddddddanni
authored andcommitted
Add AnalogDetuneCouplerOnly (quantumlib#7444)
1 parent b2ece33 commit 39d55bf

File tree

7 files changed

+303
-3
lines changed

7 files changed

+303
-3
lines changed

cirq-google/cirq_google/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
)
5454

5555
from cirq_google.ops import (
56+
AnalogDetuneCouplerOnly as AnalogDetuneCouplerOnly,
5657
AnalogDetuneQubit as AnalogDetuneQubit,
5758
CalibrationTag as CalibrationTag,
5859
Coupler as Coupler,

cirq-google/cirq_google/json_resolver_cache.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def _old_xmon(*args, **kwargs):
4444

4545
return {
4646
'_NamedConstantXmonDevice': _old_xmon,
47+
'AnalogDetuneCouplerOnly': cirq_google.AnalogDetuneCouplerOnly,
4748
'AnalogDetuneQubit': cirq_google.AnalogDetuneQubit,
4849
'Calibration': cirq_google.Calibration,
4950
'CalibrationTag': cirq_google.CalibrationTag,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"cirq_type": "AnalogDetuneCouplerOnly",
3+
"length": {
4+
"cirq_type": "sympy.Symbol",
5+
"name": "l"
6+
},
7+
"w": 10,
8+
"g_0": 5,
9+
"g_max": 20,
10+
"g_ramp_exponent": 1.0,
11+
"neighbor_qubits_freq": [
12+
null,
13+
null
14+
],
15+
"prev_neighbor_qubits_freq": [
16+
null,
17+
null
18+
],
19+
"interpolate_coupling_cal": true,
20+
"analog_cal_for_pulseshaping": false
21+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cirq_google.AnalogDetuneCouplerOnly(length=sympy.Symbol('l'), w=10, g_0=5, g_max=20)

cirq-google/cirq_google/ops/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
from cirq_google.ops.analog_detune_gates import AnalogDetuneQubit as AnalogDetuneQubit
1818

19+
from cirq_google.ops.analog_detune_gates import AnalogDetuneCouplerOnly as AnalogDetuneCouplerOnly
20+
1921
from cirq_google.ops.calibration_tag import CalibrationTag as CalibrationTag
2022

2123
from cirq_google.ops.coupler import Coupler as Coupler

cirq-google/cirq_google/ops/analog_detune_gates.py

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
"""Define detuning gates for Analog Experiment usage."""
1616
from __future__ import annotations
1717

18-
from typing import AbstractSet, Any, TYPE_CHECKING
18+
from typing import AbstractSet, Any, Iterable, TYPE_CHECKING
1919

2020
import cirq
21+
from cirq_google.ops import coupler
2122
from cirq_google.study import symbol_util as su
2223

2324
if TYPE_CHECKING:
@@ -59,6 +60,7 @@ def __init__(
5960
linear_rise: bool = True,
6061
):
6162
"""Inits AnalogDetuneQubit.
63+
6264
Args:
6365
length: The duration of gate.
6466
w: Width of the step envelope raising edge.
@@ -171,3 +173,180 @@ def _json_dict_(self):
171173
'linear_rise',
172174
],
173175
)
176+
177+
178+
@cirq.value_equality(approximate=True)
179+
class AnalogDetuneCouplerOnly(cirq.ops.Gate):
180+
"""Set a coupler detuning from g_0 to g_max according to analog model.
181+
182+
The shape of pulse followed by the g=g_0+A*t^g_exp (1 gives linear ramp),
183+
where the coefficient is auto calculated by the g_max.
184+
185+
Pulse shape:
186+
187+
.. svgbob::
188+
:align: center
189+
190+
| ,--------|---- amp_max (parsed from g_max)
191+
| / |
192+
amp_0---|-' - - - - -| - -
193+
|
194+
| |-w -| |
195+
| |---length --|
196+
|
197+
--------------------------(calculated from the g_0)
198+
"""
199+
200+
def __init__(
201+
self,
202+
length: su.ValueOrSymbol,
203+
w: su.ValueOrSymbol,
204+
g_0: su.ValueOrSymbol,
205+
g_max: su.ValueOrSymbol,
206+
g_ramp_exponent: cirq.TParamVal = 1.0,
207+
neighbor_qubits_freq: tuple[su.ValueOrSymbol | None, su.ValueOrSymbol | None] = (
208+
None,
209+
None,
210+
),
211+
prev_neighbor_qubits_freq: tuple[su.ValueOrSymbol | None, su.ValueOrSymbol | None] = (
212+
None,
213+
None,
214+
),
215+
interpolate_coupling_cal: bool = True,
216+
analog_cal_for_pulseshaping: bool = False,
217+
):
218+
"""Inits AnalogDetuneCouplerOnly.
219+
220+
Args:
221+
length: The duration of gate.
222+
w: Width of the step envelope raising edge.
223+
g_0: The pulse shape is specified with the equation g(t) = g_0+A*t^g_exp.
224+
where g(0)=g_0 and g(w)=g_max. The ramp is according to the power law
225+
with exponent specified by g_ramp_exponent.
226+
g_max: See g_0.
227+
g_ramp_exponent: See g_0.
228+
neighbor_qubits_freq: Two frequency of the neighbor qubits at the moment.
229+
If the provided value is None, we assume neighbor qubits are at idle freq.
230+
prev_neighbor_qubits_freq: Two frequency of the neighbor qubits at preivous moment.
231+
interpolate_coupling_cal: If true, find the required amp for the coupling strength
232+
through interpolation. If not true, require all coupling strength has associated
233+
amp calibrated in the registry.
234+
analog_cal_for_pulseshaping: If ture, using the analog model instead of
235+
standard transmon model to find the amp of pulse.
236+
"""
237+
self.length = length
238+
self.w = w
239+
self.g_0 = g_0
240+
self.g_max = g_max
241+
self.g_ramp_exponent = g_ramp_exponent
242+
self.neighbor_qubits_freq = tuple(neighbor_qubits_freq)
243+
self.prev_neighbor_qubits_freq = tuple(prev_neighbor_qubits_freq)
244+
self.interpolate_coupling_cal = interpolate_coupling_cal
245+
self.analog_cal_for_pulseshaping = analog_cal_for_pulseshaping
246+
247+
def _unitary_(self) -> np.ndarray:
248+
return NotImplemented # pragma: no cover
249+
250+
def on(self, *qubits: cirq.Qid) -> cirq.Operation:
251+
"""Returns an application of this gate to the given qubits.
252+
253+
Coupler gate will silently change two qubit Qids into
254+
a single Coupler Qid object so that adjacent couplers
255+
can be simultaneously acted on in the same moment.
256+
"""
257+
if len(qubits) == 2:
258+
return super().on(coupler.Coupler(qubits[0], qubits[1]))
259+
else:
260+
return super().on(*qubits)
261+
262+
def on_each(self, *targets: cirq.Qid | Iterable[Any]) -> list[cirq.Operation]:
263+
"""Returns a list of operations applying the gate to all targets."""
264+
return [self.on(t) if isinstance(t, cirq.Qid) else self.on(*t) for t in targets]
265+
266+
def _num_qubits_(self) -> int:
267+
return 1
268+
269+
def _is_parameterized_(self) -> bool:
270+
return (
271+
cirq.is_parameterized(self.length)
272+
or cirq.is_parameterized(self.w)
273+
or cirq.is_parameterized(self.g_0)
274+
or cirq.is_parameterized(self.g_max)
275+
or cirq.is_parameterized(self.g_ramp_exponent)
276+
or cirq.is_parameterized(self.neighbor_qubits_freq)
277+
or cirq.is_parameterized(self.prev_neighbor_qubits_freq)
278+
)
279+
280+
def _parameter_names_(self) -> AbstractSet[str]:
281+
return (
282+
cirq.parameter_names(self.length)
283+
| cirq.parameter_names(self.w)
284+
| cirq.parameter_names(self.g_0)
285+
| cirq.parameter_names(self.g_max)
286+
| cirq.parameter_names(self.g_ramp_exponent)
287+
| cirq.parameter_names(self.neighbor_qubits_freq)
288+
| cirq.parameter_names(self.prev_neighbor_qubits_freq)
289+
)
290+
291+
def _resolve_parameters_(
292+
self, resolver: cirq.ParamResolverOrSimilarType, recursive: bool
293+
) -> AnalogDetuneCouplerOnly:
294+
resolver_ = cirq.ParamResolver(resolver)
295+
return AnalogDetuneCouplerOnly(
296+
length=su.direct_symbol_replacement(self.length, resolver_),
297+
w=su.direct_symbol_replacement(self.w, resolver_),
298+
g_0=su.direct_symbol_replacement(self.g_0, resolver_),
299+
g_max=su.direct_symbol_replacement(self.g_max, resolver_),
300+
g_ramp_exponent=su.direct_symbol_replacement(self.g_ramp_exponent, resolver_),
301+
neighbor_qubits_freq=tuple(
302+
su.direct_symbol_replacement(f, resolver_) for f in self.neighbor_qubits_freq
303+
),
304+
prev_neighbor_qubits_freq=tuple(
305+
su.direct_symbol_replacement(f, resolver_) for f in self.prev_neighbor_qubits_freq
306+
),
307+
interpolate_coupling_cal=self.interpolate_coupling_cal,
308+
analog_cal_for_pulseshaping=self.analog_cal_for_pulseshaping,
309+
)
310+
311+
def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs) -> str:
312+
return f"AnalogDetuneCouplerOnly(length={self.length}, g_max={self.g_max})"
313+
314+
def __repr__(self) -> str:
315+
return (
316+
f'AnalogDetuneCouplerOnly(length={self.length}, '
317+
f'w={self.w}, '
318+
f'g_0={self.g_0}, '
319+
f'g_max={self.g_max}, '
320+
f'g_ramp_exponent={self.g_ramp_exponent}, '
321+
f'neighbor_qubits_freq={self.neighbor_qubits_freq}, '
322+
f'prev_neighbor_qubits_freq={self.prev_neighbor_qubits_freq})'
323+
)
324+
325+
def _value_equality_values_(self) -> Any:
326+
return (
327+
self.length,
328+
self.w,
329+
self.g_0,
330+
self.g_max,
331+
self.g_ramp_exponent,
332+
self.neighbor_qubits_freq,
333+
self.prev_neighbor_qubits_freq,
334+
self.interpolate_coupling_cal,
335+
self.analog_cal_for_pulseshaping,
336+
)
337+
338+
def _json_dict_(self):
339+
return cirq.obj_to_dict_helper(
340+
self,
341+
[
342+
'length',
343+
'w',
344+
'g_0',
345+
'g_max',
346+
'g_ramp_exponent',
347+
'neighbor_qubits_freq',
348+
'prev_neighbor_qubits_freq',
349+
'interpolate_coupling_cal',
350+
'analog_cal_for_pulseshaping',
351+
],
352+
)

cirq-google/cirq_google/ops/analog_detune_gates_test.py

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import textwrap
16+
1517
import pytest
1618
import sympy
1719
import tunits as tu
1820

1921
import cirq
20-
import cirq_google.ops.analog_detune_gates as adg
22+
import cirq_google as cg
23+
from cirq_google.ops import analog_detune_gates as adg
2124

2225

23-
def test_equality():
26+
def test_analog_detune_qubit_equality():
2427
g1 = adg.AnalogDetuneQubit(length=20 * tu.ns, w=10 * tu.ns, target_freq=5 * tu.GHz)
2528
g2 = adg.AnalogDetuneQubit(length=20 * tu.ns, w=10 * tu.ns, target_freq=5 * tu.GHz)
2629
assert g1.num_qubits() == 1
@@ -141,3 +144,95 @@ def test_analog_detune_qubit_repr():
141144
"AnalogDetuneQubit(length=l, w=10 ns, target_freq=t_freq, prev_freq=p_freq,"
142145
" neighbor_coupler_g_dict=None, prev_neighbor_coupler_g_dict=None)"
143146
)
147+
148+
149+
def test_analog_detune_coupler_equality() -> None:
150+
g1 = adg.AnalogDetuneCouplerOnly(
151+
length=20 * tu.ns, w=10 * tu.ns, g_0=5 * tu.GHz, g_max=sympy.Symbol("g")
152+
)
153+
g2 = adg.AnalogDetuneCouplerOnly(
154+
length=20 * tu.ns, w=10 * tu.ns, g_0=5 * tu.GHz, g_max=sympy.Symbol("g")
155+
)
156+
assert g1.num_qubits() == 1
157+
assert g1 == g2
158+
159+
160+
def test_analog_detune_coupler_resolution() -> None:
161+
gate = adg.AnalogDetuneCouplerOnly(
162+
length=sympy.Symbol('length'),
163+
w=10 * tu.ns,
164+
g_0=5 * tu.MHz,
165+
g_max=sympy.Symbol('g'),
166+
neighbor_qubits_freq=(sympy.Symbol('q'), None),
167+
prev_neighbor_qubits_freq=(5, 6),
168+
)
169+
resolver = {'length': 50 * tu.ns, sympy.Symbol('g'): 2 * tu.MHz, 'q': 5}
170+
assert cirq.resolve_parameters(gate, resolver) == adg.AnalogDetuneCouplerOnly(
171+
length=50 * tu.ns,
172+
w=10 * tu.ns,
173+
g_0=5 * tu.MHz,
174+
g_max=2 * tu.MHz,
175+
neighbor_qubits_freq=(5, None),
176+
prev_neighbor_qubits_freq=(5, 6),
177+
)
178+
179+
180+
def test_analog_detune_coupler_parameter_names() -> None:
181+
gate = adg.AnalogDetuneCouplerOnly(
182+
length=sympy.Symbol('l'),
183+
w=10 * tu.ns,
184+
g_0=5 * tu.MHz,
185+
g_max=sympy.Symbol('g'),
186+
neighbor_qubits_freq=(sympy.Symbol('q'), None),
187+
prev_neighbor_qubits_freq=(5, 6),
188+
)
189+
assert cirq.parameter_names(gate) == {'l', 'g', 'q'}
190+
191+
192+
def test_analog_detune_coupler_repr() -> None:
193+
gate = adg.AnalogDetuneCouplerOnly(
194+
length=sympy.Symbol('l'),
195+
w=10 * tu.ns,
196+
g_0=5 * tu.MHz,
197+
g_max=sympy.Symbol('g'),
198+
neighbor_qubits_freq=(sympy.Symbol('q'), None),
199+
prev_neighbor_qubits_freq=(5, 6),
200+
)
201+
assert repr(gate) == (
202+
"AnalogDetuneCouplerOnly(length=l, w=10 ns, g_0=5 MHz, g_max=g, g_ramp_exponent=1.0,"
203+
" neighbor_qubits_freq=(q, None), prev_neighbor_qubits_freq=(5, 6))"
204+
)
205+
206+
207+
def test_analog_detune_coupler_circuit_diagram() -> None:
208+
q1, q2 = cirq.q(0, 0), cirq.q(0, 1)
209+
gate = adg.AnalogDetuneCouplerOnly(
210+
length=sympy.Symbol('l'), w=10 * tu.ns, g_0=5 * tu.MHz, g_max=20 * tu.MHz
211+
)
212+
cirq.testing.assert_has_diagram(
213+
cirq.Circuit(gate.on(q1, q2)),
214+
"c(q(0, 0),q(0, 1)): ───AnalogDetuneCouplerOnly(length=l, g_max=20 MHz)───",
215+
)
216+
217+
gate.g_max = None
218+
cirq.testing.assert_has_diagram(
219+
cirq.Circuit(gate.on(cg.Coupler(q1, q2))),
220+
"c(q(0, 0),q(0, 1)): ───AnalogDetuneCouplerOnly(length=l, g_max=None)───",
221+
)
222+
223+
q3, q4 = cirq.q(0, 2), cirq.q(0, 3)
224+
cirq.testing.assert_has_diagram(
225+
cirq.Circuit(gate.on_each((q1, q2), (q3, q4))),
226+
textwrap.dedent(
227+
"""
228+
c(q(0, 0),q(0, 1)): ───AnalogDetuneCouplerOnly(length=l, g_max=None)───
229+
230+
c(q(0, 2),q(0, 3)): ───AnalogDetuneCouplerOnly(length=l, g_max=None)───
231+
"""
232+
),
233+
)
234+
235+
236+
def test_analog_detune_coupler_jsonify() -> None:
237+
gate = adg.AnalogDetuneCouplerOnly(length=sympy.Symbol('l'), w=10, g_0=5, g_max=20)
238+
assert gate == cirq.read_json(json_text=cirq.to_json(gate))

0 commit comments

Comments
 (0)