-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
331 additions
and
0 deletions.
There are no files selected for viewing
331 changes: 331 additions & 0 deletions
331
klayout_package/python/kqcircuits/chips/munch_qubits.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()}" | ||
) |