r"""Unimon qubit components.
The unimon qubit consists of a Josephson junction (or SQUID) embedded within
a coplanar waveguide resonator, with two grounded :math:`\lambda/4` arms
extending from the junction. The large geometric inductance of the resonator
arms, combined with the Josephson nonlinearity, creates a qubit with large
anharmonicity and insensitivity to charge and flux noise.
References:
- :cite:`hyyppaUnimonQubit2022`
- :cite:`tuohinoMultimodePhysicsUnimon2024`
- :cite:`dudaParameterOptimizationUnimon2025`
"""
from __future__ import annotations
from functools import partial
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.typings import ComponentSpec, CrossSectionSpec
from kfactory import kdb
from kfactory.conf import CheckInstances
from qpdk.cells.capacitor import half_circle_coupler
from qpdk.cells.junction import josephson_junction, squid_junction
from qpdk.cells.resonator import resonator
from qpdk.cells.waveguides import bend_circular, straight
from qpdk.logger import logger
from qpdk.tech import LAYER, get_etch_section
[docs]
@gf.cell(tags=["qubits", "resonators"])
def unimon_arm(
arm_length: float = 3000.0,
arm_meanders: int = 6,
bend_spec: ComponentSpec = bend_circular,
cross_section: CrossSectionSpec = "cpw",
junction_gap: float = 6.0,
) -> Component:
"""Creates a quarter-wave resonator arm for the unimon qubit.
Args:
arm_length: Length of the resonator arm in µm.
arm_meanders: Number of meanders in the arm.
bend_spec: Specification for the bend component.
cross_section: Cross-section specification for the arm.
junction_gap: Gap for the junction in µm.
Returns:
Component: The quarter-wave resonator arm component.
"""
c = Component()
cross_section_obj = gf.get_cross_section(cross_section)
bend = gf.get_component(
bend_spec, cross_section=cross_section_obj, angle=180, angular_step=4
)
bend_length = bend.info["length"]
# With end_with_bend=True, there are arm_meanders straight sections.
# We add an extra half straight, so total straights = arm_meanders + 0.5
length_per_one_straight = (arm_length - arm_meanders * bend_length) / (
arm_meanders + 0.5
)
base_resonator_length = (
arm_meanders * bend_length + arm_meanders * length_per_one_straight
)
# Create the base quarter-wave resonator arm ending with a bend
arm_base = resonator(
length=base_resonator_length,
meanders=arm_meanders,
bend_spec=bend_spec,
cross_section=cross_section,
open_start=False, # shorted at far end (port o1)
open_end=False, # open end connects to junction
end_with_bend=True,
)
# Short straight CPW section to be attached after the bend.
# Its length matches half of the resonator straight lengths minus half the gap.
half_straight_length = (length_per_one_straight / 2) - (junction_gap / 2)
half_straight = straight(
length=half_straight_length,
cross_section=cross_section,
)
arm_base_ref = c.add_ref(arm_base)
half_straight_ref = c.add_ref(half_straight)
half_straight_ref.connect(
"o1",
arm_base_ref.ports["o2"],
allow_width_mismatch=True,
allow_layer_mismatch=True,
)
c.add_port("o1", port=arm_base_ref.ports["o1"])
c.add_port("o2", port=half_straight_ref.ports["o2"])
cross_section_etch_section = get_etch_section(cross_section_obj)
# Add a port for readout coupling at the center of the last bend
# We find the last bend instance in the resonator
bend_instances = [inst for inst in arm_base.insts if "bend" in inst.cell.name]
if bend_instances:
last_bend = bend_instances[-1]
radius = last_bend.cell.info["radius"]
# Record the actual bend radius used so downstream cells (e.g. unimon)
# can place readout couplers concentric with the meander bends.
c.info["radius"] = radius
# Locate the center of curvature of the last meander bend.
# The bbox extends radius + width/2 + etch_width beyond the
# center in every direction that the arc reaches.
bbox = last_bend.dbbox()
half_cpw = cross_section_obj.width / 2 + cross_section_etch_section.width
if abs(bbox.left) > abs(bbox.right):
center_x = bbox.left + radius + half_cpw
orientation = 180 # Pointing LEFT
else:
center_x = bbox.right - radius - half_cpw
orientation = 0 # Pointing RIGHT
c.add_port(
name="readout",
center=(
center_x,
bbox.top - radius - half_cpw,
),
width=cross_section_obj.width,
orientation=orientation,
layer=LAYER.M1_DRAW,
port_type="placement",
)
return c
[docs]
@gf.cell(check_instances=CheckInstances.IGNORE, tags=["qubits"])
def unimon(
arm_length: float = 3000.0,
arm_meanders: int = 6,
bend_spec: ComponentSpec = bend_circular,
cross_section: CrossSectionSpec = "cpw",
junction_spec: ComponentSpec = partial(
squid_junction,
junction_spec=partial(
josephson_junction,
junction_overlap_displacement=1.8,
wide_straight_length=4.0,
narrow_straight_length=0.5,
taper_length=4,
),
),
junction_gap: float = 10.0,
junction_etch_width: float = 22.0,
) -> Component:
r"""Creates a unimon qubit from two grounded :math:`\lambda/4` CPW resonator arms connected by a SQUID junction.
The unimon is a superconducting qubit consisting of a single Josephson
junction (or SQUID for flux tunability) embedded in the center of
a two grounded :math:`\lambda/4` CPW resonators, providing
a large geometric inductance that, together with the Josephson
nonlinearity, yields high anharmonicity and resilience to charge noise.
.. svgbob::
o1 (shorted to ground)
┌──
│ :math:`\lambda/4`
└─┐ resonator arm
│ (meandered)
┌─┘
│
└X┐ junction (SQUID)
│
┌─┘
│ :math:`\lambda/4`
└─┐ resonator arm
│ (meandered)
──┘
o2 (shorted to ground)
See :cite:`hyyppaUnimonQubit2022,tuohinoMultimodePhysicsUnimon2024` for details.
Args:
arm_length: Length of each :math:`\lambda/4` resonator arm in µm.
arm_meanders: Number of meander sections in each arm.
bend_spec: Specification for the bend component used in meanders.
cross_section: Cross-section specification for the resonator arms.
junction_spec: Component specification for the junction (SQUID) component.
junction_gap: Length of the etched gap on which the junction sits in µm.
junction_etch_width: Width of the etched region where the junction sits in µm.
Returns:
Component: A gdsfactory component with the unimon qubit geometry.
"""
c = Component()
arm = unimon_arm(
arm_length=arm_length,
arm_meanders=arm_meanders,
bend_spec=bend_spec,
cross_section=cross_section,
junction_gap=junction_gap,
)
cross_section_obj = gf.get_cross_section(cross_section)
cross_section_etch_section = get_etch_section(cross_section_obj)
# Place the SQUID junction at the center
junction_comp = gf.get_component(junction_spec)
junction_ref = c.add_ref(junction_comp)
junction_ref.dcplx_trans *= kdb.DCplxTrans(1, -45, False, 0, 0)
junction_ref.dcenter = (0.0, 0.0)
gap_comp = gf.c.rectangle(
size=(junction_gap, junction_etch_width),
layer=cross_section_etch_section.layer,
centered=True,
port_type="optical",
port_orientations=(0, 180),
)
gap_ref = c.add_ref(gap_comp)
gap_ref.dcenter = (0.0, 0.0)
gap_ref.rotate(90)
arm_top_ref = c.add_ref(arm)
arm_top_ref.connect(
"o2",
gap_ref.ports["o2"],
allow_width_mismatch=True,
allow_layer_mismatch=True,
)
arm_bottom_ref = c.add_ref(arm)
arm_bottom_ref.rotate(180)
arm_bottom_ref.connect(
"o2",
gap_ref.ports["o1"],
allow_width_mismatch=True,
allow_layer_mismatch=True,
)
c.add_port("o1", port=arm_top_ref.ports["o1"], port_type="placement")
c.add_port("o2", port=arm_bottom_ref.ports["o1"], port_type="placement")
# Promote readout ports for coupling
c.add_port("readout_top", port=arm_top_ref.ports["readout"])
c.add_port("readout_bottom", port=arm_bottom_ref.ports["readout"])
# Add placement port for the junction center
c.add_port(
name="junction",
center=junction_ref.dcenter,
width=junction_ref.size_info.height,
orientation=90,
layer=LAYER.JJ_AREA,
port_type="placement",
)
# Add metadata
c.info["qubit_type"] = "unimon"
c.info["arm_length"] = arm_length
c.info["total_resonator_length"] = 2 * arm_length
c.info["meander_radius"] = arm.info.get("radius", 100.0)
# Rotate whole component to be vertical
c.rotate(-90)
return c
[docs]
@gf.cell(tags=["qubits", "couplers"])
def unimon_coupled(
arm_length: float = 3000.0,
arm_meanders: int = 6,
bend_spec: ComponentSpec = bend_circular,
cross_section: CrossSectionSpec = "cpw",
junction_spec: ComponentSpec = partial(
squid_junction,
junction_spec=partial(
josephson_junction,
junction_overlap_displacement=1.8,
wide_straight_length=4.0,
narrow_straight_length=0.5,
taper_length=4,
),
),
junction_gap: float = 6.0,
junction_etch_width: float = 22.0,
coupling_gap: float = 30.0,
coupling_angle: float = 180.0,
coupling_extension_length: float = 50.0,
cross_section_non_resonator: CrossSectionSpec = "cpw",
) -> Component:
r"""Creates a unimon qubit with a half-circle coupling waveguide for readout.
This component combines a :func:`unimon` qubit with a half-circle coupler
placed at a specified gap for proximity coupling to a readout resonator.
.. svgbob::
o1 (shorted to ground)
┌──
│
└─┐
│
┌─┘
│ ─┐
└X┐│
│+-- coupling_o3
┌─┘│
│ ─┘
└─┐
│
──┘
o2 (shorted to ground)
Args:
arm_length: Length of each :math:`\lambda/4` resonator arm in µm.
arm_meanders: Number of meander sections in each arm.
bend_spec: Specification for the bend component used in meanders.
cross_section: Cross-section specification for the resonator arms.
junction_spec: Component specification for the junction (SQUID) component.
junction_gap: Length of the etched gap on which the junction sits in µm.
junction_etch_width: Width of the etched region where the junction sits in µm.
coupling_gap: Edge-to-edge gap between M1_DRAW centre conductors of the
unimon resonator and the coupling waveguide in µm. The coupling
radius is automatically computed as
``meander_radius + coupling_gap + width_resonator/2 + width_coupler/2``
to ensure a uniform gap across the bend.
coupling_angle: Angle of the circular arc in degrees.
coupling_extension_length: Length of the straight sections extending from the
ends of the half-circle in μm.
cross_section_non_resonator: Cross-section for the coupling waveguide.
Returns:
Component: A gdsfactory component with the unimon and half-circle coupler.
"""
c = Component()
unimon_ref = c.add_ref(
unimon(
arm_length=arm_length,
arm_meanders=arm_meanders,
bend_spec=bend_spec,
cross_section=cross_section,
junction_spec=junction_spec,
junction_gap=junction_gap,
junction_etch_width=junction_etch_width,
)
)
# Compute coupling_radius so that the edge-to-edge gap between the
# M1_DRAW centre conductors equals coupling_gap for concentric arcs.
meander_radius = unimon_ref.cell.info["meander_radius"]
xs_resonator = gf.get_cross_section(cross_section)
xs_coupler = gf.get_cross_section(cross_section_non_resonator)
coupling_radius = (
meander_radius + coupling_gap + xs_resonator.width / 2 + xs_coupler.width / 2
)
# Warn if the coupler would cross a meander straight: the straights beyond
# the two adjacent to the last bend sit at odd multiples of the bend radius
# from the bend center, so a small bend radius can bring the concentric
# coupler into M1_DRAW overlap with them.
half_width_sum = (xs_resonator.width + xs_coupler.width) / 2
for k in range(1, arm_meanders - 1):
straight_distance = (2 * k + 1) * meander_radius
if abs(coupling_radius - straight_distance) < half_width_sum:
logger.warning(
"coupling_radius {} µm is within {} µm of a meander straight at "
"{} µm from the last bend center, so the readout coupler would "
"overlap the resonator M1_DRAW metal. Use a larger bend radius "
"or coupling_gap.",
coupling_radius,
half_width_sum,
straight_distance,
)
coupler = c.add_ref(
half_circle_coupler(
radius=coupling_radius,
angle=coupling_angle,
extension_length=coupling_extension_length,
cross_section=cross_section_non_resonator,
)
)
# Align coupler anchor (at arc center) with the unimon readout port
# (at the meander bend center) for concentric alignment and uniform gap
coupler.connect(
"anchor",
unimon_ref.ports["readout_top"],
allow_width_mismatch=True,
allow_layer_mismatch=True,
)
for port in unimon_ref.ports:
if port.name == "readout_top":
continue # connected to coupler anchor internally
c.add_port(f"unimon_{port.name}", port=port)
for port in coupler.ports:
if port.name == "anchor":
continue # anchor was used for alignment, not an external port
c.add_port(f"coupling_{port.name}", port=port)
c.info += unimon_ref.cell.info
c.info["coupling_gap"] = coupling_gap
c.info["coupling_radius"] = coupling_radius
return c