Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Demo for school files #88

Merged
merged 2 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
331 changes: 331 additions & 0 deletions klayout_package/python/kqcircuits/chips/munch_qubits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
# This code is part of KQCircuits
# Copyright (C) 2024 IQM Finland Oy
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# https://www.gnu.org/licenses/gpl-3.0.html.
#
# The software distribution should follow IQM trademark policy for open-source software
# (meetiqm.com/developers/osstmpolicy). IQM welcomes contributions to the code. Please see our contribution agreements
# for individuals (meetiqm.com/developers/clas/individual) and organizations (meetiqm.com/developers/clas/organization).

import logging

from kqcircuits.chips.chip import Chip
from kqcircuits.elements.airbridge_connection import AirbridgeConnection
from kqcircuits.elements.meander import Meander
from kqcircuits.elements.waveguide_composite import WaveguideComposite, Node
from kqcircuits.elements.waveguide_coplanar_splitter import WaveguideCoplanarSplitter
from kqcircuits.pya_resolver import pya
from kqcircuits.qubits.circular_transmon_single_island import CircularTransmonSingleIsland
from kqcircuits.qubits.double_pads import DoublePads
from kqcircuits.util.coupler_lib import cap_params
from kqcircuits.util.parameters import Param, pdt, add_parameters_from


@add_parameters_from(Chip, name_chip="EM1")
@add_parameters_from(
DoublePads,
coupler_extent=[150, 20],
island1_extent=[1000, 200],
island2_extent=[1000, 200],
island_island_gap=200,
ground_gap=[1400, 900],
drive_position=[-1100, 400],
)
class MunchQubits(Chip):
"""Demonstration chip with two circular single island qubits, one floating island qubit, three readout resonators,
one probe line, three drivelines and one resonant coupler.

"""

# Readout parameters
readout_res_lengths = Param(pdt.TypeList, "Readout resonator lengths", [11500, 12700, 8000], unit="[μm]")
kappa_finger_control = Param(
pdt.TypeList, "Finger control for the input capacitor", [3.32, 4.21, 1.46], unit="[μm]"
)
# Coupling parameters
coupler_length = Param(pdt.TypeDouble, "Resonant coupler length", 9800, unit="µm")
# Circular qubit 1 parameters
couplers_a_qb1 = Param(pdt.TypeList, "Width of the coupler waveguide's center conductors", [10, 3], unit="[μm]")
couplers_b_qb1 = Param(pdt.TypeList, "Width of the coupler waveguide's gaps", [6, 32], unit="[μm]")
couplers_angle_qb1 = Param(
pdt.TypeList,
"Positioning angles of the couplers, where 0deg corresponds to positive x-axis",
[225, 315],
unit="[degrees]",
)
couplers_width_qb1 = Param(pdt.TypeList, "Radial widths of the arc couplers", [30, 50], unit="[μm]")
couplers_arc_amplitude_qb1 = Param(pdt.TypeList, "Couplers angular extension", [25, 65], unit="[degrees]")
# Circular qubit 2 parameters
couplers_a_qb2 = Param(pdt.TypeList, "Width of the coupler waveguide's center conductors", [10, 3], unit="[μm]")
couplers_b_qb2 = Param(pdt.TypeList, "Width of the coupler waveguide's gaps", [6, 32], unit="[μm]")
couplers_angle_qb2 = Param(
pdt.TypeList,
"Positioning angles of the couplers, where 0deg corresponds to positive x-axis",
[315, 225],
unit="[degrees]",
)
couplers_width_qb2 = Param(pdt.TypeList, "Radial widths of the arc couplers", [30, 50], unit="[μm]")
couplers_arc_amplitude_qb2 = Param(pdt.TypeList, "Couplers angular extension", [35, 65], unit="[degrees]")

drive_line_offsets = Param(
pdt.TypeList, "Distance between the end of a drive line and the qubit pair", [550.0] * 2, unit="[µm]"
)

# Floating double pad qubit 3 parameters are added instead as @add_parameters_from the element since there is only
# one qubit, they will be passed automatically to the element when added

def build(self):
# Define launchpads positioning and function
launcher_assignments = {
1: "DL-QB1",
2: "DL-QB2",
3: "PL-1-OUT",
5: "DL-QB3",
8: "PL-1-IN",
}
# Use an 8 port default launcher
self.produce_launchers("SMA8", launcher_assignments)
self.produce_qubits()
self.produce_coupler()
self.produce_probeline()
self.produce_readout_resonators()
self.produce_drivelines()

def produce_qubits(self):
# Position the circular qubits
transformations = [pya.DCplxTrans(1, 0, False, 3500, 7000), pya.DCplxTrans(1, 0, False, 6500, 7000)]
drive_angles = [110, 70]

# Make a function to add a single circular qubit
def produce_circular_qubit(
name,
trans,
couplers_a,
couplers_b,
couplers_angle,
couplers_width,
couplers_arc_amplitude,
drive_angle,
drive_line_offset,
):
qubit_cell = self.add_element(
CircularTransmonSingleIsland,
r_island=300,
ground_gap=200,
squid_angle=90,
drive_angle=drive_angle,
drive_distance=float(drive_line_offset),
couplers_r=400,
couplers_a=list(map(float, couplers_a)),
couplers_b=list(map(float, couplers_b)),
couplers_angle=list(map(float, couplers_angle)),
couplers_width=list(map(float, couplers_width)),
couplers_arc_amplitude=list(map(float, couplers_arc_amplitude)),
)
_, _ = self.insert_cell(qubit_cell, trans, name, rec_levels=None)

# Insert both circular qubits
produce_circular_qubit(
"QB1",
transformations[0],
self.couplers_a_qb1,
self.couplers_b_qb1,
self.couplers_angle_qb1,
self.couplers_width_qb1,
self.couplers_arc_amplitude_qb1,
drive_angles[0],
self.drive_line_offsets[0],
)
produce_circular_qubit(
"QB2",
transformations[1],
self.couplers_a_qb2,
self.couplers_b_qb2,
self.couplers_angle_qb2,
self.couplers_width_qb2,
self.couplers_arc_amplitude_qb2,
drive_angles[1],
self.drive_line_offsets[1],
)

# Add now the floating island qubit
qubit_cell = self.add_element(DoublePads)
_, _ = self.insert_cell(qubit_cell, pya.DCplxTrans(1, 180, False, 5000, 4000), "QB3", rec_levels=None)

def produce_coupler(self):
# Insert a fixed coupler of a variable meander size in between qubits
_, _, length = WaveguideComposite.produce_fixed_length_waveguide(
self,
lambda x: [
Node(self.refpoints["QB1_port_coupler_2"]),
Node(self.refpoints["QB1_port_coupler_2_corner"], n_bridges=1),
Node(pya.DPoint(4500, 6500), n_bridges=2),
Node(pya.DPoint(5500, 6500), length_before=x, n_bridges=6),
Node(self.refpoints["QB2_port_coupler_2_corner"], n_bridges=2),
Node(self.refpoints["QB2_port_coupler_2"], n_bridges=1),
],
initial_guess=5000,
length=self.coupler_length,
a=float(self.couplers_a_qb1[1]),
b=float(self.couplers_b_qb1[1]),
term1=0,
term2=0,
)

logging.info(f"Coupler line length: {length:.2f}")

def produce_probeline(self):
# Make the probeline pass through the resonators tees
probeline = self.add_element(
WaveguideComposite,
nodes=[
Node(self.refpoints["PL-1-IN_base"]),
Node(self.refpoints["PL-1-IN_port_corner"], n_bridges=1),
Node(pya.DPoint(self.refpoints["QB1_base"].x - 1000, 1500), n_bridges=4),
Node(
pya.DPoint(self.refpoints["QB1_base"].x, 1500),
WaveguideCoplanarSplitter,
align=("port_a", "port_c"),
angles=[180, 90, 0],
lengths=[50, 150, 50],
inst_name="QB1_tee",
use_airbridges=True,
n_bridges=1,
),
Node(
pya.DPoint(5000, 1500),
WaveguideCoplanarSplitter,
align=("port_a", "port_c"),
angles=[180, 90, 0],
lengths=[50, 150, 50],
inst_name="QB3_tee",
use_airbridges=True,
n_bridges=2,
),
Node(
pya.DPoint(self.refpoints["QB2_base"].x, 1500),
WaveguideCoplanarSplitter,
align=("port_a", "port_c"),
angles=[180, 90, 0],
lengths=[50, 150, 50],
inst_name="QB2_tee",
use_airbridges=True,
n_bridges=2,
),
Node(pya.DPoint(self.refpoints["QB2_base"].x + 1000, 1500), n_bridges=1),
Node(self.refpoints["PL-1-OUT_port_corner"], n_bridges=4),
Node(self.refpoints["PL-1-OUT_base"], n_bridges=1),
],
a=self.a,
b=self.b,
term1=0,
term2=0,
)
self.insert_cell(probeline, inst_name="pl")

def produce_drivelines(self):
# Connect the drivelines to the qubit ports
# Circular qubits
for qubit_nr in range(1, 3):
self.insert_cell(
WaveguideComposite,
nodes=[
Node(self.refpoints[f"DL-QB{qubit_nr}_base"]),
Node(self.refpoints[f"DL-QB{qubit_nr}_port_corner"], n_bridges=1),
Node(self.refpoints[f"DL-QB{qubit_nr}_port_corner"] + pya.DPoint(0, -1200), n_bridges=1),
Node(self.refpoints[f"QB{qubit_nr}_port_drive_corner"], n_bridges=1),
Node(self.refpoints[f"QB{qubit_nr}_port_drive"]),
],
term2=self.b,
)
# Double pad qubit
airbridge_crossing_coordinate = (
self.refpoints["pl_QB2_tee_base"] - self.refpoints["pl_QB3_tee_base"]
) / 2 + self.refpoints["pl_QB3_tee_base"]
self.insert_cell(
WaveguideComposite,
nodes=[
Node(self.refpoints["DL-QB3_base"]),
Node(self.refpoints["DL-QB3_port_corner"], n_bridges=1),
Node(airbridge_crossing_coordinate + pya.DVector(0, -300), n_bridges=1),
Node(airbridge_crossing_coordinate, AirbridgeConnection, n_bridges=1),
Node(airbridge_crossing_coordinate + pya.DVector(0, 200), n_bridges=1),
Node(
pya.DPoint(
self.refpoints["QB3_port_drive_corner"].x,
(airbridge_crossing_coordinate + pya.DVector(0, 300)).y,
),
),
Node(self.refpoints["QB3_port_drive_corner"], n_bridges=2),
Node(self.refpoints["QB3_port_drive"]),
],
term2=self.b,
)

def produce_readout_resonators(self):
# Break down the resonator in few parts for simplicity
tee_angles = [90, 90, 90] # Coupling port direction at the probeline
for i, t_angle in enumerate(tee_angles):
capacitor = self.add_element(
**cap_params(
fingers=float(self.kappa_finger_control[i]),
coupler_type="smooth",
element_key="cls",
fixed_length=160,
)
)
_, cplr_ref = self.insert_cell(
capacitor,
trans=pya.DCplxTrans(1, t_angle, False, 0, 0),
align_to=f"pl_QB{i + 1}_tee_port_b",
align="port_a",
)
# Add the lower part of the resonator, align it, measure it
# Qubits ports are called slightly different for the circular qubits and the double pad
qubits_couplers_corners = [f"QB{i + 1}_port_coupler_1_corner"] * 2 + [f"QB{i + 1}_port_cplr_corner"]
qubits_couplers_refp = [f"QB{i + 1}_port_coupler_1"] * 2 + [f"QB{i + 1}_port_cplr"]
resonator_bottom, _ = self.insert_cell(
WaveguideComposite,
nodes=[
Node(cplr_ref["port_b"]),
Node(cplr_ref["port_b_corner"]),
Node(pya.DPoint(self.refpoints[qubits_couplers_corners[i]].x, cplr_ref["port_b_corner"].y + 100)),
Node(
pya.DPoint(self.refpoints[qubits_couplers_corners[i]].x, cplr_ref["port_b_corner"].y + 200),
n_bridges=1,
),
],
inst_name=f"resonator_bottom_{i + 1}",
)
length_nonmeander_bottom = resonator_bottom.cell.length()
# Add the upper part of the resonator, align it, measure it
resonator_top, _ = self.insert_cell(
WaveguideComposite,
nodes=[
Node(self.refpoints[qubits_couplers_refp[i]]),
Node(self.refpoints[qubits_couplers_corners[i]]),
Node(self.refpoints[qubits_couplers_corners[i]] + pya.DPoint(0, -300), n_bridges=1),
],
inst_name=f"resonator_top_{i + 1}",
)
length_nonmeander_top = resonator_top.cell.length()
# Add the missing part in the center in the correct length
meander, _ = self.insert_cell(
Meander,
start=self.refpoints[qubits_couplers_corners[i]] + pya.DPoint(0, -300),
end=self.refpoints[f"resonator_bottom_{i + 1}_port_b"],
length=float(self.readout_res_lengths[i]) - length_nonmeander_top - length_nonmeander_bottom,
n_bridges=18,
)
logging.info(
f"Resonator QB{i + 1} length: "
f"{length_nonmeander_bottom + length_nonmeander_top + meander.cell.length()}"
)
Loading
Loading