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