Coverage for qpdk / cells / transmon.py: 100%
109 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-14 10:27 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-14 10:27 +0000
1"""Transmon qubit components."""
3from __future__ import annotations
5import operator
6from functools import partial, reduce
8import gdsfactory as gf
9from gdsfactory.component import Component
10from gdsfactory.typings import ComponentSpec, LayerSpec
11from kfactory import kdb
12from klayout.db import DCplxTrans, Region
14from qpdk.cells.bump import indium_bump
15from qpdk.cells.helpers import transform_component
16from qpdk.cells.junction import squid_junction, squid_junction_long
17from qpdk.helper import show_components
18from qpdk.tech import LAYER
21@gf.cell(check_instances=False)
22def double_pad_transmon(
23 pad_size: tuple[float, float] = (250.0, 400.0),
24 pad_gap: float = 15.0,
25 junction_spec: ComponentSpec = squid_junction,
26 junction_displacement: DCplxTrans | None = None,
27 layer_metal: LayerSpec = LAYER.M1_DRAW,
28) -> Component:
29 """Creates a double capacitor pad transmon qubit with Josephson junction.
31 A transmon qubit consists of two capacitor pads connected by a Josephson junction.
32 The junction creates an anharmonic oscillator that can be used as a qubit.
34 .. svgbob::
36 +---------+ +---------+
37 | | | |
38 | pad1 | XX | pad2 |
39 | | XX | |
40 +---------+ junct. +---------+
41 left_pad right_pad
43 See :cite:`kochChargeinsensitiveQubitDesign2007a` for details.
45 Args:
46 pad_size: (width, height) of each capacitor pad in μm.
47 pad_gap: Gap between the two capacitor pads in μm.
48 junction_spec: Component specification for the Josephson junction component.
49 junction_displacement: Optional complex transformation to apply to the junction.
50 layer_metal: Layer for the metal pads.
52 Returns:
53 Component: A gdsfactory component with the transmon geometry.
54 """
55 c = Component()
57 pad_width, pad_height = pad_size
59 def create_capacitor_pad(x_offset: float) -> gf.ComponentReference:
60 pad = gf.components.rectangle(
61 size=pad_size,
62 layer=layer_metal,
63 )
64 pad_ref = c.add_ref(pad)
65 pad_ref.move((x_offset, -pad_height / 2))
66 return pad_ref
68 create_capacitor_pad(-pad_width - pad_gap / 2)
69 create_capacitor_pad(pad_gap / 2)
71 # Create Josephson junction
72 junction_ref = c.add_ref(gf.get_component(junction_spec))
73 junction_ref.rotate(45)
74 # Center the junction between the pads
75 junction_ref.dcenter = c.dcenter # move((-junction_height / 2, 0))
76 if junction_displacement:
77 junction_ref.transform(junction_displacement)
79 # Add ports for easy reference
80 ports_config = [
81 {
82 "name": "left_pad",
83 "center": (-pad_width - pad_gap / 2, 0),
84 "width": pad_height,
85 "orientation": 180,
86 "layer": layer_metal,
87 },
88 {
89 "name": "left_pad_inner",
90 "center": (-pad_gap / 2, 0),
91 "width": pad_height,
92 "orientation": 0,
93 "layer": layer_metal,
94 "port_type": "placement",
95 },
96 {
97 "name": "right_pad",
98 "center": (pad_width + pad_gap / 2, 0),
99 "width": pad_height,
100 "orientation": 0,
101 "layer": layer_metal,
102 },
103 {
104 "name": "right_pad_inner",
105 "center": (pad_gap / 2, 0),
106 "width": pad_height,
107 "orientation": 180,
108 "layer": layer_metal,
109 "port_type": "placement",
110 },
111 {
112 "name": "junction",
113 "center": junction_ref.dcenter,
114 "width": junction_ref.size_info.height,
115 "orientation": 90,
116 "layer": LAYER.JJ_AREA,
117 "port_type": "placement",
118 },
119 ]
120 for port_config in ports_config:
121 c.add_port(**port_config)
123 # Add metadata
124 c.info["qubit_type"] = "transmon"
126 return c
129@gf.cell
130def double_pad_transmon_with_bbox(
131 bbox_extension: float = 200.0,
132 pad_size: tuple[float, float] = (250.0, 400.0),
133 pad_gap: float = 15.0,
134 junction_spec: ComponentSpec = squid_junction,
135 junction_displacement: DCplxTrans | None = None,
136 layer_metal: LayerSpec = LAYER.M1_DRAW,
137) -> Component:
138 """Creates a double capacitor pad transmon qubit with Josephson junction and an etched bounding box.
140 See :func:`~double_pad_transmon` for more details.
142 Args:
143 bbox_extension: Extension size for the bounding box in μm.
144 pad_size: (width, height) of each capacitor pad in μm.
145 pad_gap: Gap between the two capacitor pads in μm.
146 junction_spec: Component specification for the Josephson junction component.
147 junction_displacement: Optional complex transformation to apply to the junction.
148 layer_metal: Layer for the metal pads.
150 Returns:
151 Component: A gdsfactory component with the transmon geometry and etched box.
152 """
153 c = gf.Component()
154 double_pad_ref = c << double_pad_transmon(
155 pad_size=pad_size,
156 pad_gap=pad_gap,
157 junction_spec=junction_spec,
158 junction_displacement=junction_displacement,
159 layer_metal=layer_metal,
160 )
161 double_pad_size = (double_pad_ref.size_info.width, double_pad_ref.size_info.height)
162 bbox_size = (
163 double_pad_size[0] + 2 * bbox_extension,
164 double_pad_size[1] + 2 * bbox_extension,
165 )
167 bbox = gf.container(
168 partial(
169 gf.components.rectangle,
170 size=bbox_size,
171 layer=LAYER.M1_ETCH,
172 ),
173 # Center the bbox around the double pad
174 partial(
175 transform_component, transform=DCplxTrans(*(-e / 2 for e in bbox_size))
176 ),
177 )
178 # Remove additive metal from etch
179 bbox = gf.boolean(
180 A=bbox,
181 B=c,
182 operation="-",
183 layer=LAYER.M1_ETCH,
184 layer1=LAYER.M1_ETCH,
185 layer2=LAYER.M1_DRAW,
186 )
187 bbox_ref = c.add_ref(bbox)
188 c.absorb(bbox_ref)
190 c.add_ports(double_pad_ref.ports)
191 return c
194@gf.cell(check_instances=False)
195def flipmon(
196 inner_circle_radius: float = 60.0,
197 outer_ring_radius: float = 110.0,
198 outer_ring_width: float = 60.0,
199 top_circle_radius: float = 110.0,
200 junction_spec: ComponentSpec = squid_junction_long,
201 junction_displacement: DCplxTrans | None = None,
202 layer_metal: LayerSpec = LAYER.M1_DRAW,
203 layer_metal_top: LayerSpec = LAYER.M2_DRAW,
204) -> Component:
205 """Creates a circular transmon qubit with `flipmon` geometry.
207 A circular variant of the transmon qubit with another circle as the inner pad.
209 See :cite:`liVacuumgapTransmonQubits2021,liCosmicrayinducedCorrelatedErrors2025`
210 for details about the `flipmon` design.
212 Args:
213 inner_circle_radius: Central radius of the inner circular capacitor pad in μm.
214 outer_ring_radius: Central radius of the outer circular capacitor pad in μm.
215 outer_ring_width: Width of the outer circular capacitor pad in μm.
216 top_circle_radius: Central radius of the top circular capacitor pad in μm.
217 There is no separate width as the filled circle is not a ring.
218 junction_spec: Component specification for the Josephson junction component.
219 junction_displacement: Optional complex transformation to apply to the junction.
220 layer_metal: Layer for the metal pads.
221 layer_metal_top: Layer for the other metal layer pad for flip-chip.
223 Returns:
224 Component: A gdsfactory component with the circular transmon geometry.
225 """
226 c = Component()
228 _ = c << gf.c.circle(
229 radius=inner_circle_radius,
230 layer=layer_metal,
231 )
233 _ = c << gf.c.ring(
234 radius=outer_ring_radius,
235 width=outer_ring_width,
236 layer=layer_metal,
237 )
239 # Create Josephson junction
240 junction_ref = c.add_ref(gf.get_component(junction_spec))
241 # Center the junction between the pads
242 junction_ref.dcenter = c.dcenter # move((-junction_height / 2, 0))
243 junction_ref.dcplx_trans *= reduce(
244 operator.mul,
245 (
246 DCplxTrans(1, 45, False, 0, 0),
247 DCplxTrans(
248 (inner_circle_radius + outer_ring_radius - outer_ring_width / 2) / 2,
249 0,
250 ),
251 DCplxTrans(0, -junction_ref.size_info.height),
252 ),
253 )
254 junction_ref.y = 0
256 if junction_displacement:
257 junction_ref.transform(junction_displacement)
259 # Create top circular pad for flip-chip
260 top_circle = gf.components.circle(
261 radius=top_circle_radius,
262 layer=layer_metal_top,
263 )
264 top_circle_ref = c.add_ref(top_circle)
265 top_circle_ref.dcenter = c.dcenter
267 # Add indium bump to flip-chip
268 bump = gf.get_component(indium_bump)
269 bump_ref = c.add_ref(bump)
270 bump_ref.dcenter = c.dcenter
271 c.add_ports(bump_ref.ports)
273 # Add ports for connections
274 c.add_port(
275 name="inner_ring_near_junction",
276 center=(inner_circle_radius / 2, 0),
277 width=outer_ring_width,
278 orientation=0,
279 layer=layer_metal,
280 port_type="placement",
281 )
282 c.add_port(
283 name="outer_ring_near_junction",
284 center=(outer_ring_radius - outer_ring_width / 2, 0),
285 width=outer_ring_width,
286 orientation=180,
287 layer=layer_metal,
288 port_type="placement",
289 )
290 c.add_port(
291 name="outer_ring_outside",
292 center=(outer_ring_radius + outer_ring_width / 2, 0),
293 width=outer_ring_width,
294 orientation=0,
295 layer=layer_metal,
296 )
297 c.add_port(
298 name="junction",
299 center=junction_ref.dcenter,
300 width=junction_ref.size_info.height,
301 orientation=90,
302 layer=LAYER.JJ_AREA,
303 port_type="placement",
304 )
306 return c
309@gf.cell
310def flipmon_with_bbox(
311 inner_circle_radius: float = 60.0,
312 outer_ring_radius: float = 110.0,
313 outer_ring_width: float = 60.0,
314 top_circle_radius: float = 110.0,
315 junction_spec: ComponentSpec = squid_junction_long,
316 junction_displacement: DCplxTrans | None = None,
317 layer_metal: LayerSpec = LAYER.M1_DRAW,
318 layer_metal_top: LayerSpec = LAYER.M2_DRAW,
319 m1_etch_extension_gap: float = 30.0,
320 m2_etch_extension_gap: float = 40.0,
321) -> Component:
322 """Creates a circular transmon qubit with `flipmon` geometry and a circular etched bounding box.
324 See :func:`~flipmon` for more details.
326 Args:
327 inner_circle_radius: Central radius of the inner circular capacitor pad in μm.
328 outer_ring_radius: Central radius of the outer circular capacitor pad in μm.
329 outer_ring_width: Width of the outer circular capacitor pad in μm.
330 top_circle_radius: Central radius of the top circular capacitor pad in μm.
331 junction_spec: Component specification for the Josephson junction component.
332 junction_displacement: Optional complex transformation to apply to the junction.
333 layer_metal: Layer for the metal pads.
334 layer_metal_top: Layer for the other metal layer pad for flip-chip.
335 m1_etch_extension_gap: Radius extension length for the M1 etch bounding box in μm.
336 m2_etch_extension_gap: Radius extension length for the M2 etch bounding box in μm.
338 Returns:
339 Component: A gdsfactory component with the flipmon geometry and etched box.
340 """
341 c = gf.Component()
342 flipmon_ref = c << flipmon(
343 inner_circle_radius=inner_circle_radius,
344 outer_ring_radius=outer_ring_radius,
345 outer_ring_width=outer_ring_width,
346 top_circle_radius=top_circle_radius,
347 junction_spec=junction_spec,
348 junction_displacement=junction_displacement,
349 layer_metal=layer_metal,
350 layer_metal_top=layer_metal_top,
351 )
352 m1_bbox_radius = outer_ring_radius + outer_ring_width / 2 + m1_etch_extension_gap
353 m2_bbox_radius = top_circle_radius + m2_etch_extension_gap
355 for etch_layer, draw_layer, bbox_radius in [
356 (LAYER.M1_ETCH, LAYER.M1_DRAW, m1_bbox_radius),
357 (LAYER.M2_ETCH, LAYER.M2_DRAW, m2_bbox_radius),
358 ]:
359 bbox_comp = gf.boolean(
360 A=gf.components.circle(
361 radius=bbox_radius,
362 layer=etch_layer,
363 ),
364 B=c,
365 operation="-",
366 layer=etch_layer,
367 layer1=etch_layer,
368 layer2=draw_layer,
369 )
370 c.absorb(c.add_ref(bbox_comp))
372 c.add_ports(flipmon_ref.ports)
373 return c
376@gf.cell(check_instances=False)
377def xmon_transmon(
378 arm_width: tuple[float, float, float, float] = (30.0, 20.0, 30.0, 20.0),
379 arm_lengths: tuple[float, float, float, float] = (160.0, 120.0, 160.0, 120.0),
380 gap_width: float = 10.0,
381 junction_spec: ComponentSpec = squid_junction,
382 junction_displacement: DCplxTrans | None = None,
383 layer_metal: LayerSpec = LAYER.M1_DRAW,
384 layer_etch: LayerSpec = LAYER.M1_ETCH,
385) -> Component:
386 """Creates an Xmon style transmon qubit with cross-shaped geometry.
388 An Xmon transmon consists of a cross-shaped capacitor pad with four arms
389 extending from a central region, connected by a Josephson junction at the center.
390 The design provides better control over the coupling to readout resonators
391 and neighboring qubits through the individual arm geometries.
393 See :cite:`barendsCoherentJosephsonQubit2013a` for details about the Xmon design.
395 Args:
396 arm_width: Tuple of (top, right, bottom, left) arm widths in μm.
397 arm_lengths: Tuple of (top, right, bottom, left) arm lengths in μm.
398 Computed from center to end of each arm.
399 gap_width: Width of the etched gap around arms in μm.
400 junction_spec: Component specification for the Josephson junction component.
401 junction_displacement: Optional complex transformation to apply to the junction.
402 layer_metal: Layer for the metal pads.
403 layer_etch: Layer for the etched regions.
405 Returns:
406 Component: A gdsfactory component with the Xmon transmon geometry.
407 """
408 c = Component()
410 arm_width_top, arm_width_right, arm_width_bottom, arm_width_left = arm_width
411 arm_length_top, arm_length_right, arm_length_bottom, arm_length_left = arm_lengths
413 # Define arm configurations: (size, move_offset)
414 arm_configs = [
415 ((arm_width_top, arm_length_top), (-arm_width_top / 2, arm_length_top * 0)),
416 (
417 (arm_length_right, arm_width_right),
418 (arm_length_right * 0, -arm_width_right / 2),
419 ),
420 (
421 (arm_width_bottom, arm_length_bottom),
422 (-arm_width_bottom / 2, -arm_length_bottom),
423 ),
424 ((arm_length_left, arm_width_left), (-arm_length_left, -arm_width_left / 2)),
425 ]
427 # Create the four arms extending from the center
428 for size, move_offset in arm_configs:
429 arm = gf.components.rectangle(
430 size=size,
431 layer=layer_metal,
432 )
433 arm_ref = c.add_ref(arm)
434 arm_ref.move(move_offset)
435 c.absorb(arm_ref)
436 c.flatten(merge=True)
438 # Create etch by sizing drawn metal
439 layer_metal_tuple = gf.get_layer_tuple(layer_metal)
440 etch_region = gf.component.size(
441 Region(
442 kdb.RecursiveShapeIterator(
443 c.kcl.layout,
444 c._base.kdb_cell, # pyright: ignore[reportPrivateUsage]
445 c.kcl.layout.layer(layer_metal_tuple[0], layer_metal_tuple[1]),
446 )
447 ),
448 gap_width,
449 )
450 etch_component = gf.Component()
451 etch_component.add_polygon(etch_region, layer=layer_etch)
453 # Remove additive metal from etch
454 etch_component = gf.boolean(
455 A=etch_component,
456 B=c,
457 operation="-",
458 layer=LAYER.M1_ETCH,
459 layer1=LAYER.M1_ETCH,
460 layer2=LAYER.M1_DRAW,
461 )
462 etch_ref = c.add_ref(etch_component)
463 c.absorb(etch_ref)
465 # Create and place Josephson junction at the y-center of the gap
466 junction_ref = c.add_ref(gf.get_component(junction_spec))
467 junction_ref.rotate(-45)
468 junction_ref.dcenter = (0, c.ymin + gap_width / 2)
469 if junction_displacement:
470 junction_ref.transform(junction_displacement)
472 # Add ports at the ends of each arm for connectivity
473 for name, width, center, orientation in zip(
474 ["top_arm", "right_arm", "bottom_arm", "left_arm"],
475 arm_width,
476 [
477 (0, arm_length_top),
478 (arm_length_right, 0),
479 (0, -arm_length_bottom),
480 (-arm_length_left, 0),
481 ],
482 [90, 0, 270, 180],
483 ):
484 c.add_port(
485 name=name,
486 center=center,
487 width=width,
488 orientation=orientation,
489 layer=layer_metal,
490 )
492 # Add junction port
493 c.add_port(
494 name="junction",
495 center=junction_ref.dcenter,
496 width=junction_ref.size_info.height,
497 orientation=90,
498 layer=LAYER.JJ_AREA,
499 port_type="placement",
500 )
502 # Add metadata
503 c.info["qubit_type"] = "xmon"
505 return c
508if __name__ == "__main__":
509 show_components(
510 double_pad_transmon,
511 partial(double_pad_transmon, junction_displacement=DCplxTrans(0, 150)),
512 double_pad_transmon_with_bbox,
513 flipmon,
514 flipmon_with_bbox,
515 xmon_transmon,
516 )
518 # Visualize flip-chip flipmon
519 # c = gf.Component()
520 # (c << flipmon_with_bbox()).move((0, 0))
521 # c << rectangle(size=(500, 500), layer=LAYER.SIM_AREA, centered=True)
522 # to_stl(c, "flipmon.stl", layer_stack=LAYER_STACK_FLIP_CHIP)
523 # scene = c.to_3d(layer_stack=LAYER_STACK_FLIP_CHIP)
524 # scene.show()