Coverage for qpdk / models / qubit.py: 87%

91 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-02 17:50 +0000

1"""Qubit LC resonator models. 

2 

3This module provides LC resonator models for superconducting transmon qubits 

4and coupled qubit systems. The models are based on the standard LC resonator 

5formulation with appropriate grounding configurations. 

6 

7For double-pad transmon qubits, we use an ungrounded LC resonator since 

8the two islands are floating. For shunted transmon qubits, one island is 

9grounded, so we use a grounded LC resonator. 

10 

11The helper functions convert between qubit Hamiltonian parameters (charging 

12energy :math:`E_C`, Josephson energy :math:`E_J`, coupling strength :math:`g`) and the 

13corresponding circuit parameters (capacitance, inductance). 

14 

15References: 

16 - :cite:`kochChargeinsensitiveQubitDesign2007a` 

17 - :cite:`gaoPhysicsSuperconductingMicrowave2008` 

18""" 

19 

20import math 

21from functools import partial 

22 

23import jax 

24import jax.numpy as jnp 

25import sax 

26from sax.models.rf import capacitor, electrical_short, tee 

27 

28from qpdk.models.constants import DEFAULT_FREQUENCY, Φ_0, e, h 

29from qpdk.models.generic import lc_resonator, lc_resonator_coupled 

30from qpdk.models.waveguides import straight_shorted 

31 

32 

33@partial(jax.jit, inline=True) 

34def ec_to_capacitance(ec_ghz: float) -> float: 

35 r"""Convert charging energy :math:`E_C` to total capacitance :math:`C_\Sigma`. 

36 

37 The charging energy is related to capacitance by: 

38 

39 .. math:: 

40 

41 E_C = \frac{e^2}{2 C_\Sigma} 

42 

43 where :math:`e` is the electron charge. 

44 

45 Args: 

46 ec_ghz: Charging energy in GHz. 

47 

48 Returns: 

49 Total capacitance in Farads. 

50 

51 Example: 

52 >>> C = ec_to_capacitance(0.2) # 0.2 GHz (200 MHz) charging energy 

53 >>> print(f"{C * 1e15:.1f} fF") # ~96 fF 

54 """ 

55 ec_joules = ec_ghz * 1e9 * h # Convert GHz to Joules 

56 return e**2 / (2 * ec_joules) 

57 

58 

59@partial(jax.jit, inline=True) 

60def ej_to_inductance(ej_ghz: float) -> float: 

61 r"""Convert Josephson energy :math:`E_J` to Josephson inductance :math:`L_\text{J}`. 

62 

63 The Josephson energy is related to inductance by: 

64 

65 .. math:: 

66 

67 E_J = \frac{\Phi_0^2}{4 \pi^2 L_\text{J}} = \frac{(\hbar / 2e)^2}{L_\text{J}} 

68 

69 This is equivalent to: 

70 

71 .. math:: 

72 

73 L_\text{J} = \frac{\Phi_0}{2 \pi I_c} 

74 

75 where :math:`I_c` is the critical current and :math:`\Phi_0` is the magnetic flux quantum. 

76 

77 Args: 

78 ej_ghz: Josephson energy in GHz. 

79 

80 Returns: 

81 Josephson inductance in Henries. 

82 

83 Example: 

84 >>> L = ej_to_inductance(20.0) # 20 GHz Josephson energy 

85 >>> print(f"{L * 1e9:.2f} nH") # ~1.0 nH 

86 """ 

87 ej_joules = ej_ghz * 1e9 * h # Convert GHz to Joules 

88 return Φ_0**2 / (4 * math.pi**2 * ej_joules) 

89 

90 

91el_to_inductance = ej_to_inductance 

92 

93 

94@partial(jax.jit, inline=True) 

95def coupling_strength_to_capacitance( 

96 g_ghz: float, 

97 c_sigma: float, 

98 c_r: float, 

99 f_q_ghz: float, 

100 f_r_ghz: float, 

101) -> jax.Array: 

102 r"""Convert coupling strength :math:`g` to coupling capacitance :math:`C_c`. 

103 

104 In the dispersive limit (:math:`g \ll f_q, f_r`), the coupling strength 

105 can be related to a coupling capacitance via: 

106 

107 .. math:: 

108 

109 g \approx \frac{1}{2} \frac{C_c}{\sqrt{C_\Sigma C_r}} \sqrt{f_q f_r} 

110 

111 Solving for :math:`C_c`: 

112 

113 .. math:: 

114 

115 C_c = \frac{2g}{\sqrt{f_q f_r}} \sqrt{C_\Sigma C_r} 

116 

117 See :cite:`Savola2023,krantzQuantumEngineersGuide2019` for details. 

118 

119 Args: 

120 g_ghz: Coupling strength in GHz. 

121 c_sigma: Total qubit capacitance in Farads. 

122 c_r: Total resonator capacitance in Farads. 

123 f_q_ghz: Qubit frequency in GHz. 

124 f_r_ghz: Resonator frequency in GHz. 

125 

126 Returns: 

127 Coupling capacitance in Farads. 

128 

129 Example: 

130 >>> C_c = coupling_strength_to_capacitance( 

131 ... g_ghz=0.1, 

132 ... c_sigma=100e-15, # 100 fF 

133 ... c_r=50e-15, # 50 fF 

134 ... f_q_ghz=5.0, 

135 ... f_r_ghz=7.0, 

136 ... ) 

137 >>> print(f"{C_c * 1e15:.2f} fF") 

138 """ 

139 # Use frequencies directly (already in GHz, ratio is dimensionless) 

140 sqrt_freq = jnp.sqrt(f_q_ghz * f_r_ghz) 

141 sqrt_c = jnp.sqrt(c_sigma * c_r) 

142 return 2 * g_ghz / sqrt_freq * sqrt_c 

143 

144 

145@partial(jax.jit, inline=True) 

146def fluxonium( 

147 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

148 capacitance: float = 10e-15, 

149 josephson_inductance: float = 10e-9, 

150 superinductance: float = 500e-9, 

151 ground_capacitance: float = 0.0, 

152) -> sax.SDict: 

153 r"""S-parameter model for a fluxonium qubit. 

154 

155 A fluxonium qubit consists of a Josephson junction shunted by a large 

156 superinductance and a capacitor. In the linear regime, this is modeled 

157 as a parallel LCL circuit. 

158 

159 For an accurate microwave model reflecting the real physical layout, 

160 the circuit is treated as ungrounded (floating) with optional symmetric 

161 parasitic capacitances connecting both ends to ground :cite:`nguyen_blueprint_2019`. 

162 

163 .. svgbob:: 

164 

165 ┌────── C ──────┐ 

166 o1 ──┼────── Lj ─────┼── o2 

167 ├────── Ls ─────┤ 

168 │ │ 

169 Cg Cg 

170 │ │ 

171 GND GND 

172 

173 Note: 

174 The total shunt capacitance `capacitance` should include the pads, 

175 junction capacitance, and parasitic meander capacitance. Spurious 

176 array self-resonance (SR) modes can be phenomenologically modeled 

177 by adding to the shunt capacitance or using a multimode model. 

178 

179 Args: 

180 f: Array of frequency points in Hz. 

181 capacitance: Total shunt capacitance :math:`C_\Sigma` in Farads. 

182 josephson_inductance: Josephson inductance :math:`L_\text{J}` in Henries. 

183 superinductance: Superinductance :math:`L_\text{s}` in Henries. 

184 ground_capacitance: Parasitic capacitance to ground :math:`C_g` at each port in Farads. 

185 

186 Returns: 

187 sax.SDict: S-parameters dictionary with ports o1 and o2. 

188 """ 

189 # Combine the two inductors in parallel: L = (L1 * L2) / (L1 + L2) 

190 L_total = (josephson_inductance * superinductance) / ( 

191 josephson_inductance + superinductance 

192 ) 

193 return lc_resonator( 

194 f=f, 

195 capacitance=capacitance, 

196 inductance=L_total, 

197 grounded=False, 

198 ground_capacitance=ground_capacitance, 

199 ) 

200 

201 

202@partial(jax.jit, inline=True) 

203def fluxonium_with_bbox( 

204 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

205 capacitance: float = 10e-15, 

206 josephson_inductance: float = 10e-9, 

207 superinductance: float = 500e-9, 

208 ground_capacitance: float = 0.0, 

209) -> sax.SType: 

210 """S-parameter model for a fluxonium qubit with bounding box ports. 

211 

212 This model is the same as :func:`fluxonium`. 

213 

214 Returns: 

215 sax.SType: S-parameters dictionary. 

216 """ 

217 return fluxonium( 

218 f=f, 

219 capacitance=capacitance, 

220 josephson_inductance=josephson_inductance, 

221 superinductance=superinductance, 

222 ground_capacitance=ground_capacitance, 

223 ) 

224 

225 

226@partial(jax.jit, inline=True) 

227def double_island_transmon( 

228 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

229 capacitance: float = 100e-15, 

230 inductance: float = 7e-9, 

231 ground_capacitance: float = 0.0, 

232) -> sax.SDict: 

233 r"""LC resonator model for a double-island transmon qubit. 

234 

235 A double-island transmon has two superconducting islands connected by 

236 Josephson junctions, with both islands floating (not grounded). This is 

237 modeled as an ungrounded parallel LC resonator. 

238 

239 The qubit frequency is approximately: 

240 

241 .. math:: 

242 

243 f_q \approx \frac{1}{2\pi} \sqrt{8 E_J E_C} - E_C 

244 

245 For the LC model, the resonance frequency is: 

246 

247 .. math:: 

248 

249 f_r = \frac{1}{2\pi\sqrt{LC}} 

250 

251 Use :func:`ec_to_capacitance` and :func:`ej_to_inductance` to convert 

252 from qubit Hamiltonian parameters. 

253 

254 .. svgbob:: 

255 

256 o1 ──┬──L──┬── o2 

257 │ │ 

258 └──C──┘ 

259 

260 Args: 

261 f: Array of frequency points in Hz. 

262 capacitance: Total capacitance :math:`C_\Sigma` of the qubit in Farads. 

263 inductance: Josephson inductance :math:`L_\text{J}` in Henries. 

264 ground_capacitance: Parasitic capacitance to ground :math:`C_g` at each port in Farads. 

265 

266 Returns: 

267 sax.SDict: S-parameters dictionary with ports o1 and o2. 

268 """ 

269 return lc_resonator( 

270 f=f, 

271 capacitance=capacitance, 

272 inductance=inductance, 

273 grounded=False, 

274 ground_capacitance=ground_capacitance, 

275 ) 

276 

277 

278@partial(jax.jit, inline=True) 

279def double_island_transmon_with_bbox( 

280 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

281 capacitance: float = 100e-15, 

282 inductance: float = 7e-9, 

283 ground_capacitance: float = 0.0, 

284) -> sax.SType: 

285 """LC resonator model for a double-island transmon qubit with bounding box ports. 

286 

287 This model is the same as :func:`double_island_transmon`. 

288 

289 Returns: 

290 sax.SType: S-parameters dictionary. 

291 """ 

292 return double_island_transmon( 

293 f=f, 

294 capacitance=capacitance, 

295 inductance=inductance, 

296 ground_capacitance=ground_capacitance, 

297 ) 

298 

299 

300@partial(jax.jit, inline=True) 

301def flipmon( 

302 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

303 capacitance: float = 100e-15, 

304 inductance: float = 7e-9, 

305 ground_capacitance: float = 0.0, 

306) -> sax.SType: 

307 r"""LC resonator model for a flipmon qubit. 

308 

309 This model is identical to :func:`double_island_transmon`. 

310 

311 Returns: 

312 sax.SType: S-parameters dictionary. 

313 """ 

314 return double_island_transmon( 

315 f=f, 

316 capacitance=capacitance, 

317 inductance=inductance, 

318 ground_capacitance=ground_capacitance, 

319 ) 

320 

321 

322@partial(jax.jit, inline=True) 

323def flipmon_with_bbox( 

324 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

325 capacitance: float = 100e-15, 

326 inductance: float = 7e-9, 

327 ground_capacitance: float = 0.0, 

328) -> sax.SType: 

329 """LC resonator model for a flipmon qubit with bounding box ports. 

330 

331 This model is the same as :func:`flipmon`. 

332 

333 Returns: 

334 sax.SType: S-parameters dictionary. 

335 """ 

336 return flipmon( 

337 f=f, 

338 capacitance=capacitance, 

339 inductance=inductance, 

340 ground_capacitance=ground_capacitance, 

341 ) 

342 

343 

344@partial(jax.jit, inline=True) 

345def shunted_transmon( 

346 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

347 capacitance: float = 100e-15, 

348 inductance: float = 7e-9, 

349 ground_capacitance: float = 0.0, 

350) -> sax.SDict: 

351 r"""LC resonator model for a shunted transmon qubit. 

352 

353 A shunted transmon has one island grounded and the other island connected 

354 to the junction. This is modeled as a grounded parallel LC resonator. 

355 

356 The qubit frequency is approximately: 

357 

358 .. math:: 

359 

360 f_q \approx \frac{1}{2\pi} \sqrt{8 E_J E_C} - E_C 

361 

362 For the LC model, the resonance frequency is: 

363 

364 .. math:: 

365 

366 f_r = \frac{1}{2\pi\sqrt{LC}} 

367 

368 Use :func:`ec_to_capacitance` and :func:`ej_to_inductance` to convert 

369 from qubit Hamiltonian parameters. 

370 

371 .. svgbob:: 

372 

373 o1 ──┬──L──┬──. 

374 │ │ | "2-port ground" 

375 └──C──┘ | 

376 "o2" 

377 

378 Args: 

379 f: Array of frequency points in Hz. 

380 capacitance: Total capacitance :math:`C_\Sigma` of the qubit in Farads. 

381 inductance: Josephson inductance :math:`L_\text{J}` in Henries. 

382 ground_capacitance: Parasitic capacitance to ground :math:`C_g` at the floating port in Farads. 

383 

384 Returns: 

385 sax.SDict: S-parameters dictionary with ports o1 and o2. 

386 """ 

387 return lc_resonator( 

388 f=f, 

389 capacitance=capacitance, 

390 inductance=inductance, 

391 grounded=True, 

392 ground_capacitance=ground_capacitance, 

393 ) 

394 

395 

396@partial(jax.jit, static_argnames=["grounded"]) 

397def transmon_coupled( 

398 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

399 capacitance: float = 100e-15, 

400 inductance: float = 1e-9, 

401 grounded: bool = False, 

402 ground_capacitance: float = 0.0, 

403 coupling_capacitance: float = 10e-15, 

404 coupling_inductance: float = 0.0, 

405) -> sax.SDict: 

406 r"""Coupled transmon qubit model. 

407 

408 This model extends the basic transmon qubit by adding a coupling network 

409 consisting of a parallel capacitor and/or inductor. This can represent 

410 capacitive or inductive coupling between qubits or between a qubit and 

411 a readout resonator. 

412 

413 The coupling network is connected in series with the LC resonator: 

414 

415 .. svgbob:: 

416 

417 +──Lc──+ +──L──+ 

418 o1 ──│ │────| │─── o2 or grounded o2 

419 +──Cc──+ +──C──+ 

420 "LC resonator" 

421 

422 For capacitive coupling (common for qubit-resonator coupling): 

423 - Set ``coupling_capacitance`` to the coupling capacitor value 

424 - Set ``coupling_inductance=0.0`` 

425 

426 For inductive coupling (common for flux-tunable coupling): 

427 - Set ``coupling_inductance`` to the coupling inductor value 

428 - Set ``coupling_capacitance=0.0`` (or small value) 

429 

430 Use :func:`coupling_strength_to_capacitance` to convert from the 

431 qubit-resonator coupling strength :math:`g` to the coupling capacitance. 

432 

433 Args: 

434 f: Array of frequency points in Hz. 

435 capacitance: Total capacitance :math:`C_\Sigma` of the qubit in Farads. 

436 inductance: Josephson inductance :math:`L_\text{J}` in Henries. 

437 grounded: If True, the qubit is a shunted transmon (grounded). 

438 If False, it is a double-pad transmon (ungrounded). 

439 ground_capacitance: Parasitic capacitance to ground :math:`C_g` in Farads. 

440 coupling_capacitance: Coupling capacitance :math:`C_c` in Farads. 

441 coupling_inductance: Coupling inductance :math:`L_c` in Henries. 

442 

443 Returns: 

444 sax.SDict: S-parameters dictionary with ports o1 and o2. 

445 """ 

446 return lc_resonator_coupled( 

447 f=f, 

448 capacitance=capacitance, 

449 inductance=inductance, 

450 grounded=grounded, 

451 ground_capacitance=ground_capacitance, 

452 coupling_capacitance=coupling_capacitance, 

453 coupling_inductance=coupling_inductance, 

454 ) 

455 

456 

457@partial(jax.jit) 

458def fluxonium_coupled( 

459 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

460 capacitance: float = 10e-15, 

461 josephson_inductance: float = 10e-9, 

462 superinductance: float = 500e-9, 

463 ground_capacitance: float = 0.0, 

464 coupling_capacitance: float = 10e-15, 

465 coupling_inductance: float = 0.0, 

466) -> sax.SDict: 

467 r"""Coupled fluxonium qubit model. 

468 

469 This model adds a coupling network to the fluxonium model. 

470 

471 Args: 

472 f: Array of frequency points in Hz. 

473 capacitance: Total shunt capacitance :math:`C_\Sigma` in Farads. 

474 josephson_inductance: Josephson inductance :math:`L_\text{J}` in Henries. 

475 superinductance: Superinductance :math:`L_\text{s}` in Henries. 

476 ground_capacitance: Parasitic capacitance to ground :math:`C_g` in Farads. 

477 coupling_capacitance: Coupling capacitance :math:`C_c` in Farads. 

478 coupling_inductance: Coupling inductance :math:`L_c` in Henries. 

479 

480 Returns: 

481 sax.SDict: S-parameters dictionary with ports o1 and o2. 

482 """ 

483 L_total = (josephson_inductance * superinductance) / ( 

484 josephson_inductance + superinductance 

485 ) 

486 return lc_resonator_coupled( 

487 f=f, 

488 capacitance=capacitance, 

489 inductance=L_total, 

490 grounded=False, 

491 ground_capacitance=ground_capacitance, 

492 coupling_capacitance=coupling_capacitance, 

493 coupling_inductance=coupling_inductance, 

494 ) 

495 

496 

497def fluxonium_with_resonator( 

498 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

499 capacitance: float = 10e-15, 

500 josephson_inductance: float = 10e-9, 

501 superinductance: float = 500e-9, 

502 ground_capacitance: float = 0.0, 

503 resonator_length: float = 5000.0, 

504 resonator_cross_section: str = "cpw", 

505 coupling_capacitance: float = 10e-15, 

506) -> sax.SDict: 

507 r"""Model for a fluxonium qubit coupled to a quarter-wave resonator. 

508 

509 Args: 

510 f: Array of frequency points in Hz. 

511 capacitance: Total shunt capacitance :math:`C_\Sigma` in Farads. 

512 josephson_inductance: Josephson inductance :math:`L_\text{J}` in Henries. 

513 superinductance: Superinductance :math:`L_\text{s}` in Henries. 

514 ground_capacitance: Parasitic capacitance to ground :math:`C_g` in Farads. 

515 resonator_length: Length of the quarter-wave resonator in µm. 

516 resonator_cross_section: Cross-section specification for the resonator. 

517 coupling_capacitance: Coupling capacitance between qubit and resonator in Farads. 

518 

519 Returns: 

520 sax.SDict: S-parameters dictionary. 

521 """ 

522 f = jnp.asarray(f) 

523 resonator = straight_shorted( 

524 f=f, 

525 length=resonator_length, 

526 cross_section=resonator_cross_section, 

527 ) 

528 qubit = fluxonium( 

529 f=f, 

530 capacitance=capacitance, 

531 josephson_inductance=josephson_inductance, 

532 superinductance=superinductance, 

533 ground_capacitance=ground_capacitance, 

534 ) 

535 coupling_cap = capacitor(f=f, capacitance=coupling_capacitance) 

536 tee_junction = tee(f=f) 

537 terminator = electrical_short(f=f, n_ports=1) 

538 

539 instances: dict[str, sax.SType] = { 

540 "resonator": resonator, 

541 "qubit": qubit, 

542 "coupling_capacitor": coupling_cap, 

543 "tee": tee_junction, 

544 "terminator": terminator, 

545 } 

546 

547 connections = { 

548 "resonator,o1": "tee,o1", 

549 "resonator,o2": "terminator,o1", 

550 "tee,o2": "coupling_capacitor,o1", 

551 "coupling_capacitor,o2": "qubit,o1", 

552 } 

553 

554 ports = { 

555 "o1": "tee,o3", 

556 "o2": "qubit,o2", 

557 } 

558 

559 return sax.evaluate_circuit_fg((connections, ports), instances) 

560 

561 

562def qubit_with_resonator( 

563 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

564 qubit_capacitance: float = 100e-15, 

565 qubit_inductance: float = 1e-9, 

566 qubit_grounded: bool = False, 

567 resonator_length: float = 5000.0, 

568 resonator_cross_section: str = "cpw", 

569 coupling_capacitance: float = 10e-15, 

570) -> sax.SDict: 

571 r"""Model for a transmon qubit coupled to a quarter-wave resonator. 

572 

573 This model corresponds to the layout function 

574 :func:`~qpdk.cells.derived.transmon_with_resonator.transmon_with_resonator`. 

575 

576 The model combines: 

577 - A transmon qubit (LC resonator) 

578 - A quarter-wave coplanar waveguide resonator 

579 - A coupling capacitor connecting the qubit to the resonator 

580 

581 .. svgbob:: 

582 

583 "quarter-wave resonator" 

584 (straight_shorted) 

585 

586 o1 ── tee ─┤ 

587 

588 +── Cc ── qubit ── o2 

589 

590 The qubit can be either: 

591 - A double-island transmon (``qubit_grounded=False``): both islands floating 

592 - A shunted transmon (``qubit_grounded=True``): one island grounded 

593 

594 Use :func:`ec_to_capacitance` and :func:`ej_to_inductance` to convert 

595 from qubit Hamiltonian parameters (:math:`E_C`, :math:`E_J`) to circuit parameters. 

596 

597 Note: 

598 This function is not JIT-compiled because it depends on :func:`~straight_shorted`, 

599 which internally uses cpw_parameters for transmission line modeling. 

600 

601 Args: 

602 f: Array of frequency points in Hz. 

603 qubit_capacitance: Total capacitance :math:`C_\Sigma` of the qubit in Farads. 

604 Convert from charging energy using :func:`ec_to_capacitance`. 

605 qubit_inductance: Josephson inductance :math:`L_\text{J}` in Henries. 

606 Convert from Josephson energy using :func:`ej_to_inductance`. 

607 qubit_grounded: If True, the qubit is a shunted transmon (grounded). 

608 If False, it is a double-island transmon (ungrounded). 

609 resonator_length: Length of the quarter-wave resonator in µm. 

610 resonator_cross_section: Cross-section specification for the resonator. 

611 coupling_capacitance: Coupling capacitance between qubit and resonator in 

612 Farads. Use :func:`coupling_strength_to_capacitance` to convert from 

613 qubit-resonator coupling strength :math:`g`. 

614 

615 Returns: 

616 sax.SDict: S-parameters dictionary with ports ``o1`` (resonator input) 

617 and ``o2`` (qubit ground or floating). 

618 """ 

619 f = jnp.asarray(f) 

620 

621 # Create instances for circuit composition 

622 resonator = straight_shorted( 

623 f=f, 

624 length=resonator_length, 

625 cross_section=resonator_cross_section, 

626 ) 

627 qubit_func = shunted_transmon if qubit_grounded else double_island_transmon 

628 qubit = qubit_func( 

629 f=f, 

630 capacitance=qubit_capacitance, 

631 inductance=qubit_inductance, 

632 ) 

633 coupling_cap = capacitor(f=f, capacitance=coupling_capacitance) 

634 tee_junction = tee(f=f) 

635 # Use a 1-port short to terminate the internally shorted resonator end 

636 # to avoid dangling ports in the circuit evaluation. 

637 # Also short the grounded qubit's o2 port 

638 terminator = electrical_short(f=f, n_ports=2 if qubit_grounded else 1) 

639 

640 instances: dict[str, sax.SType] = { 

641 "resonator": resonator, 

642 "qubit": qubit, 

643 "coupling_capacitor": coupling_cap, 

644 "tee": tee_junction, 

645 "terminator": terminator, 

646 } 

647 

648 # Connect: resonator -- tee -- capacitor -- qubit 

649 # The tee splits the resonator signal to the coupling capacitor 

650 connections = { 

651 "resonator,o1": "tee,o1", 

652 "resonator,o2": "terminator,o1", # Explicitly terminate 

653 "tee,o2": "coupling_capacitor,o1", 

654 "coupling_capacitor,o2": "qubit,o1", 

655 } 

656 

657 if qubit_grounded: 

658 connections["qubit,o2"] = "terminator,o2" 

659 

660 ports = { 

661 "o1": "tee,o3", # External port for resonator coupling 

662 } 

663 if not qubit_grounded: 

664 ports["o2"] = "qubit,o2" # Qubit floating port 

665 

666 return sax.evaluate_circuit_fg((connections, ports), instances) 

667 

668 

669def flipmon_with_resonator( 

670 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

671 qubit_capacitance: float = 100e-15, 

672 qubit_inductance: float = 1e-9, 

673 resonator_length: float = 5000.0, 

674 resonator_cross_section: str = "cpw", 

675 coupling_capacitance: float = 10e-15, 

676) -> sax.SDict: 

677 """Model for a flipmon qubit coupled to a quarter-wave resonator. 

678 

679 This model is identical to :func:`qubit_with_resonator` but the qubit is set to floating. 

680 

681 Returns: 

682 sax.SDict: S-parameters dictionary. 

683 """ 

684 return qubit_with_resonator( 

685 f=f, 

686 qubit_capacitance=qubit_capacitance, 

687 qubit_inductance=qubit_inductance, 

688 qubit_grounded=False, # Flipmon is ungrounded 

689 resonator_length=resonator_length, 

690 resonator_cross_section=resonator_cross_section, 

691 coupling_capacitance=coupling_capacitance, 

692 ) 

693 

694 

695def double_island_transmon_with_resonator( 

696 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

697 qubit_capacitance: float = 100e-15, 

698 qubit_inductance: float = 1e-9, 

699 resonator_length: float = 5000.0, 

700 resonator_cross_section: str = "cpw", 

701 coupling_capacitance: float = 10e-15, 

702) -> sax.SDict: 

703 """Model for a double-island transmon qubit coupled to a quarter-wave resonator. 

704 

705 This model is identical to :func:`qubit_with_resonator` but the qubit is set to floating. 

706 

707 Returns: 

708 sax.SDict: S-parameters dictionary. 

709 """ 

710 return qubit_with_resonator( 

711 f=f, 

712 qubit_capacitance=qubit_capacitance, 

713 qubit_inductance=qubit_inductance, 

714 qubit_grounded=False, 

715 resonator_length=resonator_length, 

716 resonator_cross_section=resonator_cross_section, 

717 coupling_capacitance=coupling_capacitance, 

718 ) 

719 

720 

721def transmon_with_resonator( 

722 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

723 qubit_capacitance: float = 100e-15, 

724 qubit_inductance: float = 1e-9, 

725 qubit_grounded: bool = False, 

726 resonator_length: float = 5000.0, 

727 resonator_cross_section: str = "cpw", 

728 coupling_capacitance: float = 10e-15, 

729) -> sax.SDict: 

730 """Model for a transmon qubit coupled to a quarter-wave resonator. 

731 

732 This model is identical to :func:`qubit_with_resonator`. 

733 

734 Returns: 

735 sax.SDict: S-parameters dictionary. 

736 """ 

737 return qubit_with_resonator( 

738 f=f, 

739 qubit_capacitance=qubit_capacitance, 

740 qubit_inductance=qubit_inductance, 

741 qubit_grounded=qubit_grounded, 

742 resonator_length=resonator_length, 

743 resonator_cross_section=resonator_cross_section, 

744 coupling_capacitance=coupling_capacitance, 

745 ) 

746 

747 

748@partial(jax.jit, inline=True) 

749def xmon_transmon( 

750 f: sax.FloatArrayLike = DEFAULT_FREQUENCY, 

751 capacitance: float = 100e-15, 

752 inductance: float = 7e-9, 

753) -> sax.SType: 

754 """LC resonator model for an Xmon style transmon qubit. 

755 

756 An Xmon transmon is typically shunted, so this model wraps :func:`shunted_transmon`. 

757 

758 Returns: 

759 sax.SType: S-parameters dictionary. 

760 """ 

761 return shunted_transmon( 

762 f=f, 

763 capacitance=capacitance, 

764 inductance=inductance, 

765 ) 

766 

767 

768# Aliases for backward compatibility or to match cell naming 

769double_pad_transmon = double_island_transmon 

770double_pad_transmon_with_bbox = double_island_transmon_with_bbox 

771double_pad_transmon_with_resonator = double_island_transmon_with_resonator 

772 

773 

774if __name__ == "__main__": 

775 from typing import TypedDict 

776 

777 import matplotlib.pyplot as plt 

778 

779 from qpdk import PDK 

780 

781 class _QubitConfig(TypedDict): 

782 """Configuration for a qubit type in the demo plot.""" 

783 

784 label: str 

785 grounded: bool 

786 linestyle: str 

787 

788 PDK.activate() 

789 

790 f = jnp.linspace(1e9, 10e9, 2001) 

791 

792 # Calculate bare qubit resonance frequency 

793 C_q = 100e-15 

794 L_q = 7e-9 

795 f_q_bare = 1 / (2 * jnp.pi * jnp.sqrt(L_q * C_q)) 

796 

797 configs: list[_QubitConfig] = [ 

798 {"label": "Shunted Transmon", "grounded": True, "linestyle": "-"}, 

799 {"label": "Double Island Transmon", "grounded": False, "linestyle": "--"}, 

800 ] 

801 

802 fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True) 

803 

804 for config in configs: 

805 S_coupled = qubit_with_resonator( 

806 f=f, 

807 qubit_capacitance=C_q, 

808 qubit_inductance=L_q, 

809 qubit_grounded=config["grounded"], 

810 resonator_length=5000.0, 

811 resonator_cross_section="cpw", 

812 coupling_capacitance=20e-15, 

813 ) 

814 

815 s11 = S_coupled["o1", "o1"] 

816 s11_mag = 20 * jnp.log10(jnp.abs(s11)) 

817 s11_phase = jnp.unwrap(jnp.angle(s11)) 

818 

819 # Plot S11 magnitude 

820 ax1.plot( 

821 f / 1e9, 

822 s11_mag, 

823 label=f"$|S_{{11}}|$ {config['label']}", 

824 linestyle=config["linestyle"], 

825 ) 

826 

827 # Plot S11 phase 

828 ax2.plot( 

829 f / 1e9, 

830 s11_phase, 

831 label=f"$\\angle S_{{11}}$ {config['label']}", 

832 linestyle=config["linestyle"], 

833 ) 

834 

835 for ax in (ax1, ax2): 

836 ax.axvline( 

837 f_q_bare / 1e9, 

838 color="r", 

839 linestyle=":", 

840 label=rf"Bare Qubit ($f_q = {f_q_bare / 1e9:.3f}$ GHz)", 

841 ) 

842 ax.grid(True) 

843 ax.legend() 

844 

845 ax2.set_xlabel("Frequency (GHz)") 

846 ax1.set_ylabel("Magnitude (dB)") 

847 ax2.set_ylabel("Phase (rad)") 

848 fig.suptitle( 

849 rf"Qubit Coupled to Quarter-Wave Resonator ($C_q=${C_q * 1e15:.0f} fF, $L_q=${L_q * 1e9:.0f} nH)" 

850 ) 

851 plt.tight_layout() 

852 plt.show()