Source code for qpdk.cells.transmon

"""Transmon qubit components."""

from __future__ import annotations

import operator
from functools import partial, reduce
from operator import itemgetter
from typing import TypedDict, Unpack

import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.components import rectangle
from gdsfactory.export import to_stl
from gdsfactory.typings import ComponentSpec, LayerSpec
from kfactory import kdb
from klayout.db import DCplxTrans, Region

from qpdk.cells.bump import indium_bump
from qpdk.cells.helpers import transform_component
from qpdk.cells.junction import squid_junction
from qpdk.tech import LAYER, LAYER_STACK_FLIP_CHIP


class DoublePadTransmonParams(TypedDict):
    """Parameters for double pad transmon qubit.

    Keyword Args:
        pad_size: (width, height) of each capacitor pad in μm.
        pad_gap: Gap between the two capacitor pads in μm.
        junction_spec: Component specification for the Josephson junction component.
        junction_displacement: Optional complex transformation to apply to the junction.
        layer_metal: Layer for the metal pads.
    """

    pad_size: tuple[float, float]
    pad_gap: float
    junction_spec: ComponentSpec
    junction_displacement: DCplxTrans | None
    layer_metal: LayerSpec


_double_pad_transmon_default_params = DoublePadTransmonParams(
    pad_size=(250.0, 400.0),
    pad_gap=15.0,
    junction_spec=squid_junction,
    junction_displacement=None,
    layer_metal=LAYER.M1_DRAW,
)


[docs] @gf.cell(check_instances=False) def double_pad_transmon(**kwargs: Unpack[DoublePadTransmonParams]) -> Component: """Creates a double capacitor pad transmon qubit with Josephson junction. A transmon qubit consists of two capacitor pads connected by a Josephson junction. The junction creates an anharmonic oscillator that can be used as a qubit. See :cite:`kochChargeinsensitiveQubitDesign2007a` for details. Args: **kwargs: :class:`~DoublePadTransmonParams` for the transmon qubit. Returns: Component: A gdsfactory component with the transmon geometry. """ c = Component() params = _double_pad_transmon_default_params | kwargs # Extract wire parameters using dictionary unpacking pad_size, pad_gap, junction_spec, junction_displacement, layer_metal = itemgetter( "pad_size", "pad_gap", "junction_spec", "junction_displacement", "layer_metal" )(params) pad_width, pad_height = pad_size def create_capacitor_pad(x_offset: float) -> gf.ComponentReference: pad = gf.components.rectangle( size=pad_size, layer=layer_metal, ) pad_ref = c.add_ref(pad) pad_ref.move((x_offset, -pad_height / 2)) return pad_ref create_capacitor_pad(-pad_width - pad_gap / 2) create_capacitor_pad(pad_gap / 2) # Create Josephson junction junction_ref = c.add_ref(gf.get_component(junction_spec)) junction_ref.rotate(45) # Center the junction between the pads junction_ref.dcenter = c.dcenter # move((-junction_height / 2, 0)) if junction_displacement: junction_ref.transform(junction_displacement) # Add ports for connections c.add_port( name="left_pad", center=(-pad_width - pad_gap / 2, 0), width=pad_height, orientation=180, layer=layer_metal, ) c.add_port( name="right_pad", center=(pad_width + pad_gap / 2, 0), width=pad_height, orientation=0, layer=layer_metal, ) c.add_port( name="junction", center=junction_ref.dcenter, width=junction_ref.size_info.height, orientation=90, layer=LAYER.JJ_AREA, ) # Add metadata c.info["qubit_type"] = "transmon" return c
[docs] @gf.cell def double_pad_transmon_with_bbox( bbox_extension: float = 200.0, **kwargs: Unpack[DoublePadTransmonParams], ) -> Component: """Creates a double capacitor pad transmon qubit with Josephson junction and an etched bounding box. See :func:`~double_pad_transmon` for more details. Args: bbox_extension: Extension size for the bounding box in μm. **kwargs: :class:`~DoublePadTransmonParams` for the transmon qubit. Returns: Component: A gdsfactory component with the transmon geometry and etched box. """ c = gf.Component() double_pad_ref = c << double_pad_transmon(**kwargs) double_pad_size = (double_pad_ref.size_info.width, double_pad_ref.size_info.height) bbox_size = ( double_pad_size[0] + 2 * bbox_extension, double_pad_size[1] + 2 * bbox_extension, ) bbox = gf.container( partial( gf.components.rectangle, size=bbox_size, layer=LAYER.M1_ETCH, ), # Center the bbox around the double pad partial( transform_component, transform=DCplxTrans(*(-e / 2 for e in bbox_size)) ), ) # Remove additive metal from etch bbox = gf.boolean( A=bbox, B=c, operation="-", layer=LAYER.M1_ETCH, layer1=LAYER.M1_ETCH, layer2=LAYER.M1_DRAW, ) bbox_ref = c.add_ref(bbox) c.absorb(bbox_ref) c.add_ports(double_pad_ref.ports) return c
class FlipmonParams(TypedDict): """Parameters for flipmon style transmon qubit. Keyword Args: inner_ring_radius: Central radius of the inner circular capacitor pad in μm. inner_ring_width: Width of the inner circular capacitor pad in μm. outer_ring_radius: Central radius of the outer circular capacitor pad in μm. outer_ring_width: Width of the outer circular capacitor pad in μm. top_circle_radius: Central radius of the top circular capacitor pad in μm. There is no separate width as the filled circle is not a ring. junction_spec: Component specification for the Josephson junction component. junction_displacement: Optional complex transformation to apply to the junction. layer_metal: Layer for the metal pads. layer_metal_top: Layer for the other metal layer pad for flip-chip. """ inner_ring_radius: float inner_ring_width: float outer_ring_radius: float outer_ring_width: float top_circle_radius: float junction_spec: ComponentSpec junction_displacement: DCplxTrans | None layer_metal: LayerSpec layer_metal_top: LayerSpec _flipmon_default_params = FlipmonParams( inner_ring_radius=50, inner_ring_width=30, outer_ring_radius=110, outer_ring_width=60, top_circle_radius=110, junction_spec=squid_junction, junction_displacement=None, layer_metal=LAYER.M1_DRAW, layer_metal_top=LAYER.M2_DRAW, )
[docs] @gf.cell(check_instances=False) def flipmon(**kwargs: Unpack[FlipmonParams]) -> Component: """Creates a circular transmon qubit with `flipmon` geometry. A circular variant of the transmon qubit with another circle as the inner pad. See :cite:`liVacuumgapTransmonQubits2021,liCosmicrayinducedCorrelatedErrors2025` for details about the `flipmon` design. Args: **kwargs: :class:`~FlipmonParams` for the flipmon qubit. Keyword Args: inner_ring_radius: Central radius of the inner circular capacitor pad in μm. inner_ring_width: Width of the inner circular capacitor pad in μm. outer_ring_radius: Central radius of the outer circular capacitor pad in μm. outer_ring_width: Width of the outer circular capacitor pad in μm. top_circle_radius: Central radius of the top circular capacitor pad in μm. There is no separate width as the filled circle is not a ring. junction_spec: Component specification for the Josephson junction component. junction_displacement: Optional complex transformation to apply to the junction. layer_metal: Layer for the metal pads. layer_metal_top: Layer for the other metal layer pad for flip-chip. Returns: Component: A gdsfactory component with the circular transmon geometry. """ c = Component() params = _flipmon_default_params | kwargs # Extract wire parameters using dictionary unpacking ( inner_ring_radius, inner_ring_width, outer_ring_radius, outer_ring_width, top_circle_radius, junction_spec, junction_displacement, layer_metal, layer_metal_top, ) = itemgetter( "inner_ring_radius", "inner_ring_width", "outer_ring_radius", "outer_ring_width", "top_circle_radius", "junction_spec", "junction_displacement", "layer_metal", "layer_metal_top", )(params) def create_circular_pad(radius: float, width: float) -> gf.ComponentReference: pad = gf.c.ring( radius=radius, width=width, layer=layer_metal, ) return c.add_ref(pad) create_circular_pad(inner_ring_radius, inner_ring_width) create_circular_pad(outer_ring_radius, outer_ring_width) # Create Josephson junction junction_ref = c.add_ref(gf.get_component(junction_spec)) # Center the junction between the pads # junction_ref.rotate(45) junction_ref.dcenter = c.dcenter # move((-junction_height / 2, 0)) junction_ref.dcplx_trans *= reduce( operator.mul, ( DCplxTrans( ( inner_ring_radius + inner_ring_width / 2 + outer_ring_radius - outer_ring_width / 2 ) / 2, 0, ), DCplxTrans(1, 45, False, 0, 0), # DCplxTrans(1, 45, False, 0, 0), ), ) junction_ref.y = 0 if junction_displacement: junction_ref.transform(junction_displacement) # Create top circular pad for flip-chip top_circle = gf.components.circle( radius=top_circle_radius, layer=layer_metal_top, ) top_circle_ref = c.add_ref(top_circle) top_circle_ref.dcenter = c.dcenter # Add indium bump to flip-chip bump = gf.get_component(indium_bump) bump_ref = c.add_ref(bump) bump_ref.dcenter = c.dcenter c.add_ports(bump_ref.ports) # Add ports for connections c.add_port( name="inner_ring_near_junction", center=(inner_ring_radius + inner_ring_width / 2, 0), width=inner_ring_width, orientation=0, layer=layer_metal, ) c.add_port( name="outer_ring_near_junction", center=(outer_ring_radius - outer_ring_width / 2, 0), width=outer_ring_width, orientation=180, layer=layer_metal, ) c.add_port( name="outer_ring_outside", center=(outer_ring_radius + outer_ring_width / 2, 0), width=outer_ring_width, orientation=0, layer=layer_metal, ) c.add_port( name="junction", center=junction_ref.dcenter, width=junction_ref.size_info.height, orientation=90, layer=LAYER.JJ_AREA, ) return c
[docs] @gf.cell def flipmon_with_bbox( flipmon_params: FlipmonParams | None = None, m1_etch_extension_gap: float = 30.0, m2_etch_extension_gap: float = 40.0, ) -> Component: """Creates a circular transmon qubit with `flipmon` geometry and a circular etched bounding box. See :func:`~flipmon` for more details. Args: flipmon_params: :class:`~FlipmonParams` for the flipmon qubit. m1_etch_extension_gap: Radius extension length for the M1 etch bounding box in μm. m2_etch_extension_gap: Radius extension length for the M2 etch bounding box in μm. Returns: Component: A gdsfactory component with the flipmon geometry and etched box. """ c = gf.Component() flipmon_params = flipmon_params or _flipmon_default_params flipmon_ref = c << flipmon(**flipmon_params) m1_bbox_radius = ( flipmon_params["outer_ring_radius"] + flipmon_params["outer_ring_width"] / 2 + m1_etch_extension_gap ) m2_bbox_radius = flipmon_params["top_circle_radius"] + m2_etch_extension_gap for etch_layer, draw_layer, bbox_radius in [ (LAYER.M1_ETCH, LAYER.M1_DRAW, m1_bbox_radius), (LAYER.M2_ETCH, LAYER.M2_DRAW, m2_bbox_radius), ]: bbox_comp = gf.boolean( A=gf.components.circle( radius=bbox_radius, layer=etch_layer, ), B=c, operation="-", layer=etch_layer, layer1=etch_layer, layer2=draw_layer, ) c.absorb(c.add_ref(bbox_comp)) c.add_ports(flipmon_ref.ports) return c
class XmonTransmonParams(TypedDict): """Parameters for Xmon style transmon qubit. Keyword Args: center_width: Width of the central cross intersection in μm. center_height: Height of the central cross intersection in μm. arm_width: Tuple of (top, right, bottom, left) arm widths in μm. arm_lengths: Tuple of (top, right, bottom, left) arm lengths in μm. Computed from center to end of each arm. gap_width: Width of the etched gap around arms in μm. junction_spec: Component specification for the Josephson junction component. junction_displacement: Optional complex transformation to apply to the junction. layer_metal: Layer for the metal pads. layer_etch: Layer for the etched regions. """ center_width: float center_height: float arm_width: tuple[float, float, float, float] # top, right, bottom, left arm_lengths: tuple[float, float, float, float] # top, right, bottom, left gap_width: float junction_spec: ComponentSpec junction_displacement: DCplxTrans | None layer_metal: LayerSpec layer_etch: LayerSpec _xmon_transmon_default_params = XmonTransmonParams( arm_width=(30.0, 20.0, 30.0, 20.0), # top, right, bottom, left arm_lengths=(160.0, 120.0, 160.0, 120.0), # top, right, bottom, left gap_width=10.0, junction_spec=squid_junction, junction_displacement=None, layer_metal=LAYER.M1_DRAW, layer_etch=LAYER.M1_ETCH, )
[docs] @gf.cell(check_instances=False) def xmon_transmon(**kwargs: Unpack[XmonTransmonParams]) -> Component: """Creates an Xmon style transmon qubit with cross-shaped geometry. An Xmon transmon consists of a cross-shaped capacitor pad with four arms extending from a central region, connected by a Josephson junction at the center. The design provides better control over the coupling to readout resonators and neighboring qubits through the individual arm geometries. See :cite:`barendsCoherentJosephsonQubit2013a` for details about the Xmon design. Args: **kwargs: :class:`~XmonTransmonParams` for the Xmon transmon qubit. Returns: Component: A gdsfactory component with the Xmon transmon geometry. """ c = Component() params = _xmon_transmon_default_params | kwargs # Extract parameters ( arm_width, arm_lengths, gap_width, junction_spec, junction_displacement, layer_metal, layer_etch, ) = itemgetter( "arm_width", "arm_lengths", "gap_width", "junction_spec", "junction_displacement", "layer_metal", "layer_etch", )(params) arm_width_top, arm_width_right, arm_width_bottom, arm_width_left = arm_width arm_length_top, arm_length_right, arm_length_bottom, arm_length_left = arm_lengths # Define arm configurations: (size, move_offset) arm_configs = [ ((arm_width_top, arm_length_top), (-arm_width_top / 2, arm_length_top * 0)), ( (arm_length_right, arm_width_right), (arm_length_right * 0, -arm_width_right / 2), ), ( (arm_width_bottom, arm_length_bottom), (-arm_width_bottom / 2, -arm_length_bottom), ), ((arm_length_left, arm_width_left), (-arm_length_left, -arm_width_left / 2)), ] # Create the four arms extending from the center for size, move_offset in arm_configs: arm = gf.components.rectangle( size=size, layer=layer_metal, ) arm_ref = c.add_ref(arm) arm_ref.move(move_offset) c.absorb(arm_ref) c.flatten(merge=True) # Create etch by sizing drawn metal etch_region = gf.component.size( Region( kdb.RecursiveShapeIterator( c.kcl.layout, c._base.kdb_cell, # pyright: ignore[reportPrivateUsage] layer_metal, ) ), gap_width, ) etch_component = gf.Component() etch_component.add_polygon(etch_region, layer=layer_etch) # Remove additive metal from etch etch_component = gf.boolean( A=etch_component, B=c, operation="-", layer=LAYER.M1_ETCH, layer1=LAYER.M1_ETCH, layer2=LAYER.M1_DRAW, ) etch_ref = c.add_ref(etch_component) c.absorb(etch_ref) # Create and place Josephson junction at the y-center of the gap junction_ref = c.add_ref(gf.get_component(junction_spec)) junction_ref.rotate(-45) junction_ref.dcenter = (0, c.ymin + gap_width / 2) if junction_displacement: junction_ref.transform(junction_displacement) # Add ports at the ends of each arm for connectivity for name, width, center, orientation in zip( ["top_arm", "right_arm", "bottom_arm", "left_arm"], arm_width, [ (0, arm_length_top), (arm_length_right, 0), (0, -arm_length_bottom), (-arm_length_left, 0), ], [90, 0, 270, 180], ): c.add_port( name=name, center=center, width=width, orientation=orientation, layer=layer_metal, ) # Add junction port c.add_port( name="junction", center=junction_ref.dcenter, width=junction_ref.size_info.height, orientation=90, layer=LAYER.JJ_AREA, ) # Add metadata c.info["qubit_type"] = "xmon" return c
if __name__ == "__main__": from qpdk import PDK PDK.activate() c = gf.Component() for i, component in enumerate( ( double_pad_transmon(), double_pad_transmon(junction_displacement=DCplxTrans(0, 150)), double_pad_transmon_with_bbox(), flipmon(), flipmon_with_bbox(), xmon_transmon(), ) ): (c << component).move((0, i * 700)) c.show() # Visualize flip-chip flipmon c = gf.Component() (c << flipmon_with_bbox()).move((0, 0)) c << rectangle(size=(500, 500), layer=LAYER.SIM_AREA, centered=True) to_stl(c, "flipmon.stl", layer_stack=LAYER_STACK_FLIP_CHIP) scene = c.to_3d(layer_stack=LAYER_STACK_FLIP_CHIP) scene.show()