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

1"""Transmon qubit components.""" 

2 

3from __future__ import annotations 

4 

5import operator 

6from functools import partial, reduce 

7 

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 

13 

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 

22 

23 

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. 

33 

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. 

36 

37 .. svgbob:: 

38 

39 +---------+ +---------+ 

40 | | | | 

41 | pad1 | XX | pad2 | 

42 | | XX | | 

43 +---------+ junct. +---------+ 

44 left_pad right_pad 

45 

46 See :cite:`kochChargeinsensitiveQubitDesign2007a` for details. 

47 

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. 

54 

55 Returns: 

56 Component: A gdsfactory component with the transmon geometry. 

57 """ 

58 c = Component() 

59 

60 pad_width, pad_height = pad_size 

61 

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 

70 

71 create_capacitor_pad(-pad_width - pad_gap / 2) 

72 create_capacitor_pad(pad_gap / 2) 

73 

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) 

81 

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) 

125 

126 # Add metadata 

127 c.info["qubit_type"] = "transmon" 

128 

129 return c 

130 

131 

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. 

143 

144 See :func:`~double_pad_transmon` for more details. 

145 

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. 

154 

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 ) 

171 

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 ) 

190 

191 c.add_ports(double_pad_ref.ports) 

192 return c 

193 

194 

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. 

207 

208 A circular variant of the transmon qubit with another circle as the inner pad. 

209 

210 .. svgbob:: 

211 

212 .─────────. 

213 ,─' .─────. '─. 

214 ,' ,' '. '. 

215 ; ; .─. ; : outer ring 

216 ; ; │*│ XX : : (M1_DRAW) 

217 ; ; '─' ; : 

218 '. '. ,' ,' inner pad 

219 '─. '─────' ,─' (M1_DRAW) 

220 '─────────' 

221 * = bump (center) 

222 XX = junction 

223 

224 See :cite:`liVacuumgapTransmonQubits2021,liCosmicrayinducedCorrelatedErrors2025` 

225 for details about the `flipmon` design. 

226 

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. 

237 

238 Returns: 

239 Component: A gdsfactory component with the circular transmon geometry. 

240 """ 

241 c = Component() 

242 

243 _ = c << gf.c.circle( 

244 radius=inner_circle_radius, 

245 layer=layer_metal, 

246 ) 

247 

248 _ = c << gf.c.ring( 

249 radius=outer_ring_radius, 

250 width=outer_ring_width, 

251 layer=layer_metal, 

252 ) 

253 

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 

270 

271 if junction_displacement: 

272 junction_ref.transform(junction_displacement) 

273 

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 

281 

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) 

287 

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 ) 

320 

321 return c 

322 

323 

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. 

340 

341 See :func:`~flipmon` for more details. 

342 

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. 

356 

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 

373 

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 ) 

387 

388 c.add_ports(flipmon_ref.ports) 

389 return c 

390 

391 

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. 

403 

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. 

408 

409 .. svgbob:: 

410 

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 

428 

429 See :cite:`barendsCoherentJosephsonQubit2013a` for details about the Xmon design. 

430 

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. 

440 

441 Returns: 

442 Component: A gdsfactory component with the Xmon transmon geometry. 

443 """ 

444 c = Component() 

445 

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 

448 

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 ] 

462 

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) 

473 

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) 

488 

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 ) 

496 

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) 

503 

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 ) 

523 

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 ) 

533 

534 # Add metadata 

535 c.info["qubit_type"] = "xmon" 

536 

537 return c 

538 

539 

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 ) 

549 

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()