Models#

airbridge#

qpdk.models.airbridge(f=5000000000.0, cpw_width=10.0, bridge_width=10.0, airgap_height=3.0, loss_tangent=1.2e-08)[source]#

S-parameter model for a superconducting CPW airbridge.

The airbridge is modeled as a lumped lossy shunt admittance (accounting for dielectric loss and shunt capacitance) embedded between two sections of transmission line that represent the physical footprint of the bridge.

Parallel plate capacitor model is as done in [CMK+14] The default value for the loss tangent \(\tan\,\delta\) is also taken from there.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • cpw_width (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Width of the CPW center conductor in µm.

  • bridge_width (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Width of the airbridge in µm.

  • airgap_height (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Height of the airgap in µm.

  • loss_tangent (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Dielectric loss tangent of the supporting layer/residues.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import airbridge
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = airbridge(f=freq)

plt.figure()
plt.title("airbridge", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-1.png

bend_circular#

qpdk.models.bend_circular(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a circular bend, wrapped to straight().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import bend_circular
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = bend_circular(f=freq)

plt.figure()
plt.title("bend_circular", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-2.png

bend_euler#

qpdk.models.bend_euler(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for an Euler bend, wrapped to straight().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import bend_euler
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = bend_euler(f=freq)

plt.figure()
plt.title("bend_euler", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-3.png

bend_s#

qpdk.models.bend_s(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for an S-bend, wrapped to straight().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import bend_s
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = bend_s(f=freq)

plt.figure()
plt.title("bend_s", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-4.png

coupler_ring#

qpdk.models.coupler_ring(f=5000000000.0, length=20.0, gap=0.27, cross_section='cpw')[source]#

S-parameter model for two coupled coplanar waveguides in a ring configuration.

The implementation is the same as straight coupler for now.

TODO: Fetch coupling capacitance from a curved simulation library.

Parameters:
  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Array of frequency points in Hz

  • length (int | float) – Physical length of coupling section in µm

  • gap (int | float) – Gap between the coupled waveguides in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the CPW.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import coupler_ring
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = coupler_ring(f=freq)

plt.figure()
plt.title("coupler_ring", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-5.png

coupler_straight#

qpdk.models.coupler_straight(f=5000000000.0, length=20.0, gap=0.27, cross_section='cpw')[source]#

S-parameter model for two coupled coplanar waveguides, coupler_straight().

Parameters:
  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Array of frequency points in Hz

  • length (int | float) – Physical length of coupling section in µm

  • gap (int | float) – Gap between the coupled waveguides in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the CPW.

Returns:

S-parameters dictionary

Return type:

sax.SDict

o2──────▲───────o3
        │gap
o1──────▼───────o4
import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import coupler_straight
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = coupler_straight(f=freq)

plt.figure()
plt.title("coupler_straight", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-6.png

coupling_strength_to_capacitance#

qpdk.models.coupling_strength_to_capacitance(g_ghz, c_sigma, c_r, f_q_ghz, f_r_ghz)[source]#

Convert coupling strength \(g\) to coupling capacitance \(C_c\).

In the dispersive limit (\(g \ll f_q, f_r\)), the coupling strength can be related to a coupling capacitance via:

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

Solving for \(C_c\):

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

See [KKY+19, Sav23] for details.

Parameters:
  • g_ghz (float) – Coupling strength in GHz.

  • c_sigma (float) – Total qubit capacitance in Farads.

  • c_r (float) – Total resonator capacitance in Farads.

  • f_q_ghz (float) – Qubit frequency in GHz.

  • f_r_ghz (float) – Resonator frequency in GHz.

Returns:

Coupling capacitance in Farads.

Return type:

Array

Example

>>> C_c = coupling_strength_to_capacitance(
...     g_ghz=0.1,
...     c_sigma=100e-15,  # 100 fF
...     c_r=50e-15,  # 50 fF
...     f_q_ghz=5.0,
...     f_r_ghz=7.0,
... )
>>> print(f"{C_c * 1e15:.2f} fF")

cpw_cpw_coupling_capacitance#

qpdk.models.cpw_cpw_coupling_capacitance(f, length, gap, cross_section)[source]#

Calculate the coupling capacitance between two parallel CPWs.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Frequency array in Hz.

  • length (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – The coupling length in µm.

  • gap (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – The gap between the two center conductors in µm.

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the CPW.

Returns:

The total coupling capacitance in Farads.

Return type:

float | Array

cpw_epsilon_eff#

qpdk.models.cpw_epsilon_eff(w, s, h, ep_r)[source]#

Effective permittivity of a CPW on a finite-height substrate.

Uses conformal mapping (Simons [Sim01], Eq. 2.37; Ghione & Naldi [GN84]):

\[\begin{aligned} k_0 &= \frac{w}{w + 2s} \\ k_1 &= \frac{\sinh(\pi w / 4h)} {\sinh\bigl(\pi(w + 2s) / 4h\bigr)} \\ q_1 &= \frac{K(k_1^2)\,/\,K(1 - k_1^2)} {K(k_0^2)\,/\,K(1 - k_0^2)} \\ \varepsilon_{\mathrm{eff}} &= 1 + \frac{q_1\,(\varepsilon_r - 1)}{2} \end{aligned}\]

where \(K\) is the complete elliptic integral of the first kind in the parameter convention (\(m = k^2\)).

Parameters:
  • w (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Centre-conductor width (m).

  • s (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Gap to ground plane (m).

  • h (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Substrate height (m).

  • ep_r (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Relative permittivity of the substrate.

Returns:

Effective permittivity (dimensionless).

Return type:

Array

cpw_parameters#

qpdk.models.cpw_parameters(width, gap)[source]#

Compute effective permittivity and characteristic impedance for a CPW.

Uses the JAX-jittable functions from qpdk.models.cpw with the PDK layer stack (substrate height, conductor thickness, material permittivity).

Conductor thickness corrections follow Gupta, Garg, Bahl & Bhartia [GGBB96] (§7.3, Eqs. 7.98-7.100).

Parameters:
  • width (float) – Centre-conductor width in µm.

  • gap (float) – Gap between centre conductor and ground plane in µm.

Returns:

(ep_eff, z0) — effective permittivity (dimensionless) and characteristic impedance (Ω).

Return type:

tuple[float, float]

cpw_thickness_correction#

qpdk.models.cpw_thickness_correction(w, s, t, ep_eff)[source]#

Apply conductor thickness correction to CPW ε_eff and Z₀.

First-order correction from Gupta, Garg, Bahl & Bhartia [GGBB96] (§7.3, Eqs. 7.98-7.100):

\[\Delta &= \frac{1.25\,t}{\pi} \left(1 + \ln\\frac{4\pi w}{t}\right) \\ k_e &= k_0 + (1 - k_0^2)\,\frac{\Delta}{2s} \\ \varepsilon_{\mathrm{eff},t} &= \varepsilon_{\mathrm{eff}} - \frac{0.7\,(\varepsilon_{\mathrm{eff}} - 1)\,t/s} {K(k_0^2)/K(1-k_0^2) + 0.7\,t/s} \\ Z_{0,t} &= \frac{30\pi} {\sqrt{\varepsilon_{\mathrm{eff},t}}\; K(k_e^2)/K(1-k_e^2)}\]
Parameters:
  • w (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Centre-conductor width (m).

  • s (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Gap to ground plane (m).

  • t (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Conductor thickness (m).

  • ep_eff (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Uncorrected effective permittivity.

Returns:

(ep_eff_t, z0_t) — thickness-corrected effective permittivity and characteristic impedance (Ω).

Return type:

tuple[Array, Array]

cpw_z0#

qpdk.models.cpw_z0(w, s, ep_eff)[source]#

Characteristic impedance of a CPW.

\[Z_0 = \frac{30\,\pi} {\sqrt{\varepsilon_{\mathrm{eff}}}\; K(k_0^2)\,/\,K(1 - k_0^2)}\]

(Simons [Sim01], Eq. 2.38.)

Note that our \(w\) and \(s\) correspond to Simons’ \(s\) and \(w\), respectively.

Parameters:
  • w (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Centre-conductor width (m).

  • s (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Gap to ground plane (m).

  • ep_eff (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Effective permittivity (see cpw_epsilon_eff()).

Returns:

Characteristic impedance (Ω).

Return type:

Array

cpw_z0_from_cross_section#

qpdk.models.cpw_z0_from_cross_section(cross_section, f=None)[source]#

Characteristic impedance of a CPW defined by a layout cross-section.

Parameters:
  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – A gdsfactory cross-section specification.

  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray | None) – Frequency array (Hz). Used only to determine the output shape; the impedance is frequency-independent in the quasi-static model.

Returns:

Characteristic impedance broadcast to the shape of f (Ω).

Return type:

Array

dispersive_shift#

qpdk.models.dispersive_shift(ω_t_ghz, ω_r_ghz, α_ghz, g_ghz)[source]#

Compute the dispersive shift numerically.

Evaluates the second-order dispersive shift for a transmon coupled to a resonator. Uses the analytical formula derived from perturbation theory (without the rotating wave approximation) [KYG+07, BGGW21]:

\[\chi = \frac{2g^2}{\Delta - \alpha} - \frac{2g^2}{\Delta} - \frac{2g^2}{\omega_t + \omega_r + \alpha} + \frac{2g^2}{\omega_t + \omega_r}\]

where \(\Delta = \omega_t - \omega_r\). The first two terms give the rotating-wave-approximation (RWA) contribution

\[\chi_\text{RWA} = \frac{2 \alpha g^2}{\Delta(\Delta - \alpha)}\]

and the last two are corrections from the counter-rotating terms.

All parameters are in GHz, and the returned value is also in GHz.

Parameters:
  • ω_t_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Transmon frequency in GHz.

  • ω_r_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Resonator frequency in GHz.

  • α_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Transmon anharmonicity in GHz (positive value).

  • g_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Coupling strength in GHz.

Returns:

Dispersive shift \(\chi\) in GHz.

Return type:

float | Array

Example

>>> χ = dispersive_shift(5.0, 7.0, 0.2, 0.1)
>>> print(f"χ = {χ * 1e3:.2f} MHz")

dispersive_shift_to_coupling#

qpdk.models.dispersive_shift_to_coupling(χ_ghz, ω_t_ghz, ω_r_ghz, α_ghz)[source]#

Compute the coupling strength from a target dispersive shift.

Inverts the dispersive shift relation to find the coupling strength \(g\) required to achieve a desired \(\chi\). Uses only the dominant rotating-wave term [KYG+07]:

\[g \approx \sqrt{\frac{-\chi\,\Delta\,(\Delta - \alpha)}{2\alpha}}\]

where \(\Delta = \omega_t - \omega_r\).

Note

The expression under the square root may be negative when the sign of the target \(\chi\) is inconsistent with the detuning and anharmonicity (e.g., positive \(\chi\) with \(\Delta < 0\)). In that case the absolute value is taken so that the returned coupling strength is always real and non-negative, but the caller should verify self-consistency via dispersive_shift().

Parameters:
  • χ_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Target dispersive shift in GHz (typically negative).

  • ω_t_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Transmon frequency in GHz.

  • ω_r_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Resonator frequency in GHz.

  • α_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Transmon anharmonicity in GHz (positive value; the physical anharmonicity of a transmon is negative, but following the Hamiltonian convention used throughout this module, \(\alpha\) is taken as positive).

Returns:

Coupling strength \(g\) in GHz.

Return type:

float | Array

Example

>>> g = dispersive_shift_to_coupling(-0.001, 5.0, 7.0, 0.2)
>>> print(f"g = {g * 1e3:.1f} MHz")

double_island_transmon#

qpdk.models.double_island_transmon(f=5000000000.0, capacitance=1e-13, inductance=7e-09)[source]#

LC resonator model for a double-island transmon qubit.

A double-island transmon has two superconducting islands connected by Josephson junctions, with both islands floating (not grounded). This is modeled as an ungrounded parallel LC resonator.

The qubit frequency is approximately:

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

For the LC model, the resonance frequency is:

\[f_r = \frac{1}{2\pi\sqrt{LC}}\]

Use ec_to_capacitance() and ej_to_inductance() to convert from qubit Hamiltonian parameters.

o1 ──┬──L──┬── o2      │     │      └──C──┘
Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • capacitance (float) – Total capacitance \(C_\Sigma\) of the qubit in Farads.

  • inductance (float) – Josephson inductance \(L_\text{J}\) in Henries.

Returns:

S-parameters dictionary with ports o1 and o2.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import double_island_transmon
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = double_island_transmon(f=freq)

plt.figure()
plt.title("double_island_transmon", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-7.png

double_island_transmon_with_bbox#

qpdk.models.double_island_transmon_with_bbox(f=5000000000.0, capacitance=1e-13, inductance=7e-09)[source]#

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

This model is the same as double_island_transmon().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • capacitance (float)

  • inductance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import double_island_transmon_with_bbox
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = double_island_transmon_with_bbox(f=freq)

plt.figure()
plt.title("double_island_transmon_with_bbox", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-8.png

double_island_transmon_with_resonator#

qpdk.models.double_island_transmon_with_resonator(f=5000000000.0, qubit_capacitance=1e-13, qubit_inductance=1e-09, resonator_length=5000.0, resonator_cross_section='cpw', coupling_capacitance=1e-14)[source]#

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

This model is identical to qubit_with_resonator() but the qubit is set to floating.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • qubit_capacitance (float)

  • qubit_inductance (float)

  • resonator_length (float)

  • resonator_cross_section (str)

  • coupling_capacitance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import double_island_transmon_with_resonator
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = double_island_transmon_with_resonator(f=freq)

plt.figure()
plt.title("double_island_transmon_with_resonator", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-9.png

double_pad_transmon#

qpdk.models.double_pad_transmon(f=5000000000.0, capacitance=1e-13, inductance=7e-09)#

LC resonator model for a double-island transmon qubit.

A double-island transmon has two superconducting islands connected by Josephson junctions, with both islands floating (not grounded). This is modeled as an ungrounded parallel LC resonator.

The qubit frequency is approximately:

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

For the LC model, the resonance frequency is:

\[f_r = \frac{1}{2\pi\sqrt{LC}}\]

Use ec_to_capacitance() and ej_to_inductance() to convert from qubit Hamiltonian parameters.

o1 ──┬──L──┬── o2      │     │      └──C──┘
Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • capacitance (float) – Total capacitance \(C_\Sigma\) of the qubit in Farads.

  • inductance (float) – Josephson inductance \(L_\text{J}\) in Henries.

Returns:

S-parameters dictionary with ports o1 and o2.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import double_pad_transmon
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = double_pad_transmon(f=freq)

plt.figure()
plt.title("double_pad_transmon", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-10.png

double_pad_transmon_with_bbox#

qpdk.models.double_pad_transmon_with_bbox(f=5000000000.0, capacitance=1e-13, inductance=7e-09)#

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

This model is the same as double_island_transmon().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • capacitance (float)

  • inductance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import double_pad_transmon_with_bbox
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = double_pad_transmon_with_bbox(f=freq)

plt.figure()
plt.title("double_pad_transmon_with_bbox", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-11.png

double_pad_transmon_with_resonator#

qpdk.models.double_pad_transmon_with_resonator(f=5000000000.0, qubit_capacitance=1e-13, qubit_inductance=1e-09, resonator_length=5000.0, resonator_cross_section='cpw', coupling_capacitance=1e-14)#

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

This model is identical to qubit_with_resonator() but the qubit is set to floating.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • qubit_capacitance (float)

  • qubit_inductance (float)

  • resonator_length (float)

  • resonator_cross_section (str)

  • coupling_capacitance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import double_pad_transmon_with_resonator
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = double_pad_transmon_with_resonator(f=freq)

plt.figure()
plt.title("double_pad_transmon_with_resonator", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-12.png

ec_to_capacitance#

qpdk.models.ec_to_capacitance(ec_ghz)[source]#

Convert charging energy \(E_C\) to total capacitance \(C_\Sigma\).

The charging energy is related to capacitance by:

\[E_C = \frac{e^2}{2 C_\Sigma}\]

where \(e\) is the electron charge.

Parameters:

ec_ghz (float) – Charging energy in GHz.

Returns:

Total capacitance in Farads.

Return type:

float

Example

>>> C = ec_to_capacitance(0.2)  # 0.2 GHz (200 MHz) charging energy
>>> print(f"{C * 1e15:.1f} fF")  # ~96 fF

ej_ec_to_frequency_and_anharmonicity#

qpdk.models.ej_ec_to_frequency_and_anharmonicity(ej_ghz, ec_ghz)[source]#

Convert \(E_J\) and \(E_C\) to qubit frequency and anharmonicity.

Uses the standard transmon approximations [KYG+07]:

\[\begin{aligned} \omega_q &\approx \sqrt{8 E_J E_C} - E_C \\ \alpha &\approx E_C \end{aligned}\]

Note

The physical anharmonicity of a transmon is negative (\(\alpha = -E_C\)), but the Hamiltonian convention used in this module and in pymablock takes \(\alpha\) as positive.

Parameters:
  • ej_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Josephson energy in GHz.

  • ec_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Charging energy in GHz.

Returns:

Tuple of (ω_q_ghz, α_ghz).

Return type:

tuple[float | Array, float | Array]

Example

>>> ω_q, α = ej_ec_to_frequency_and_anharmonicity(20.0, 0.2)
>>> print(f"ω_q = {ω_q:.2f} GHz, α = {α:.1f} GHz")

ej_to_inductance#

qpdk.models.ej_to_inductance(ej_ghz)[source]#

Convert Josephson energy \(E_J\) to Josephson inductance \(L_\text{J}\).

The Josephson energy is related to inductance by:

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

This is equivalent to:

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

where \(I_c\) is the critical current and \(\Phi_0\) is the magnetic flux quantum.

Parameters:

ej_ghz (float) – Josephson energy in GHz.

Returns:

Josephson inductance in Henries.

Return type:

float

Example

>>> L = ej_to_inductance(20.0)  # 20 GHz Josephson energy
>>> print(f"{L * 1e9:.2f} nH")  # ~1.0 nH

electrical_short_2_port#

qpdk.models.electrical_short_2_port(f=5000000000.0)[source]#

Electrical short 2-port connection Sax model.

Parameters:

f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import electrical_short_2_port
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = electrical_short_2_port(f=freq)

plt.figure()
plt.title("electrical_short_2_port", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-13.png

flipmon#

qpdk.models.flipmon(f=5000000000.0, capacitance=1e-13, inductance=7e-09)[source]#

LC resonator model for a flipmon qubit.

This model is identical to double_island_transmon().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • capacitance (float)

  • inductance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import flipmon
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = flipmon(f=freq)

plt.figure()
plt.title("flipmon", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-14.png

flipmon_with_bbox#

qpdk.models.flipmon_with_bbox(f=5000000000.0, capacitance=1e-13, inductance=7e-09)[source]#

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

This model is the same as flipmon().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • capacitance (float)

  • inductance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import flipmon_with_bbox
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = flipmon_with_bbox(f=freq)

plt.figure()
plt.title("flipmon_with_bbox", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-15.png

flipmon_with_resonator#

qpdk.models.flipmon_with_resonator(f=5000000000.0, qubit_capacitance=1e-13, qubit_inductance=1e-09, resonator_length=5000.0, resonator_cross_section='cpw', coupling_capacitance=1e-14)[source]#

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

This model is identical to qubit_with_resonator() but the qubit is set to floating.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • qubit_capacitance (float)

  • qubit_inductance (float)

  • resonator_length (float)

  • resonator_cross_section (str)

  • coupling_capacitance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import flipmon_with_resonator
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = flipmon_with_resonator(f=freq)

plt.figure()
plt.title("flipmon_with_resonator", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-16.png

indium_bump#

qpdk.models.indium_bump(f=5000000000.0, bump_height=10.0)[source]#

S-parameter model for an indium bump, wrapped to straight().

TODO: add a constant loss channel for indium bumps.

Parameters:
  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Array of frequency points in Hz

  • bump_height (float) – Physical height (length) of the indium bump in µm.

Returns:

S-parameters dictionary

Return type:

sax.SType

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import indium_bump
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = indium_bump(f=freq)

plt.figure()
plt.title("indium_bump", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-17.png

interdigital_capacitor#

qpdk.models.interdigital_capacitor(*, f=5000000000.0, fingers=4, finger_length=20.0, finger_gap=2.0, thickness=5.0, cross_section='cpw')[source]#

Interdigital capacitor Sax model.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • fingers (int) – Total number of fingers (must be >= 2)

  • finger_length (float) – Length of each finger in μm

  • finger_gap (float) – Gap between adjacent fingers in μm

  • thickness (float) – Thickness of fingers in μm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – Cross-section specification

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import interdigital_capacitor
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = interdigital_capacitor(f=freq)

plt.figure()
plt.title("interdigital_capacitor", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-18.png

josephson_junction#

qpdk.models.josephson_junction(*, f=5000000000.0, ic=1e-06, capacitance=5e-15, resistance=10000.0, ib=0.0)[source]#

Josephson junction (RCSJ) small-signal Sax model.

Linearized RCSJ model consisting of a bias-dependent Josephson inductance in parallel with capacitance and resistance.

Valid in the superconducting (zero-voltage) state and for small AC signals.

Default capacitance taken from [SFS+15].

See [McC68] for details.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • ic (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Critical current \(I_c\) in Amperes

  • capacitance (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Junction capacitance \(C\) in Farads

  • resistance (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Shunt resistance \(R\) in Ohms

  • ib (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – DC bias current \(I_b\) in Amperes (\(\|I_b\| < I_c\))

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import josephson_junction
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = josephson_junction(f=freq)

plt.figure()
plt.title("josephson_junction", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-19.png

launcher#

qpdk.models.launcher(f=5000000000.0, straight_length=200.0, taper_length=100.0, cross_section_big=None, cross_section_small='cpw')[source]#

S-parameter model for a launcher, effectively a straight section followed by a taper.

Parameters:
  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Array of frequency points in Hz

  • straight_length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Length of the straight section in µm.

  • taper_length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Length of the taper section in µm.

  • cross_section_big (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection | None) – Cross-section for the wide section.

  • cross_section_small (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – Cross-section for the narrow section.

Returns:

S-parameters dictionary

Return type:

sax.SDict

lc_resonator#

qpdk.models.lc_resonator(f=5000000000.0, capacitance=1e-13, inductance=1e-09, grounded=False)[source]#

LC resonator Sax model with capacitor and inductor in parallel.

The resonance frequency is given by:

o1 ──┬──L──┬── o2      │     │      └──C──┘

If grounded=True, a 2-port short is connected to port o2:

o1 ──┬──L──┬──.      │     │  | "2-port ground"      └──C──┘  |              "o2"
\[f_r = \frac{1}{2 \pi \sqrt{LC}}\]

For theory and relation to superconductors, see [Gao08].

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • capacitance (float) – Capacitance of the resonator in Farads.

  • inductance (float) – Inductance of the resonator in Henries.

  • grounded (bool) – If True, add a 2-port ground to the second port.

Returns:

S-parameters dictionary with ports o1 and o2.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import lc_resonator
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = lc_resonator(f=freq)

plt.figure()
plt.title("lc_resonator", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-20.png

lc_resonator_coupled#

qpdk.models.lc_resonator_coupled(f=5000000000.0, capacitance=1e-13, inductance=1e-09, grounded=False, coupling_capacitance=1e-14, coupling_inductance=0.0)[source]#

Coupled LC resonator Sax model.

This model extends the basic LC resonator by adding a coupling network consisting of a parallel capacitor and inductor connected to one port of the LC resonator via a tee junction.

The resonance frequency of the main LC resonator is given by:

\[f_r = \frac{1}{2 \pi \sqrt{LC}}\]

The coupling network modifies the effective coupling to the resonator.

         +──Lc──+    +──L──+ o1 ──────│      │────|     │─── o2 or grounded o2          +──Cc──+    +──C──+                    "LC resonator"

Where \(L_\text{c}\) and \(C_\text{c}\) are the coupling inductance and capacitance, respectively.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • capacitance (float) – Capacitance of the main resonator in Farads.

  • inductance (float) – Inductance of the main resonator in Henries.

  • grounded (bool) – If True, the resonator is grounded.

  • coupling_capacitance (float) – Coupling capacitance in Farads.

  • coupling_inductance (float) – Coupling inductance in Henries.

Returns:

S-parameters dictionary with ports o1 and o2.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import lc_resonator_coupled
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = lc_resonator_coupled(f=freq)

plt.figure()
plt.title("lc_resonator_coupled", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-21.png

measurement_induced_dephasing#

qpdk.models.measurement_induced_dephasing(χ_ghz, κ_ghz, n_bar)[source]#

Estimate measurement-induced dephasing rate.

During dispersive readout, photons in the resonator cause additional dephasing of the qubit [BGGW21, GBS+06]:

\[\Gamma_\phi = \frac{8 \chi^2 \bar{n}}{\kappa}\]

where \(\bar{n}\) is the mean photon number in the resonator.

Parameters:
  • χ_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Dispersive shift in GHz.

  • κ_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Resonator linewidth in GHz.

  • n_bar (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Mean photon number in the resonator during measurement.

Returns:

Measurement-induced dephasing rate in GHz.

Return type:

float | Array

Example

>>> Γ_φ = measurement_induced_dephasing(-0.001, 0.001, 5.0)
>>> print(f"Γ_φ = {Γ_φ * 1e6:.1f} kHz")

microstrip_epsilon_eff#

qpdk.models.microstrip_epsilon_eff(w, h, ep_r)[source]#

Effective permittivity of a microstrip line.

Uses the Hammerstad-Jensen [HJ80] formula as given in Pozar [MP12] (Eq. 3.195-3.196):

\[\varepsilon_{\mathrm{eff}} = \frac{\varepsilon_r + 1}{2} + \frac{\varepsilon_r - 1}{2} \left(\frac{1}{\sqrt{1 + 12\,h/w}} + 0.04\,(1 - w/h)^2\;\Theta(1 - w/h)\right)\]

where the last term contributes only for narrow strips (\(w/h < 1\)).

Parameters:
  • w (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Strip width (m).

  • h (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Substrate height (m).

  • ep_r (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Relative permittivity of the substrate.

Returns:

Effective permittivity (dimensionless).

Return type:

Array

microstrip_thickness_correction#

qpdk.models.microstrip_thickness_correction(w, h, t, ep_r, ep_eff)[source]#

Conductor thickness correction for a microstrip line.

Uses the widely-adopted Schneider correction as presented in Pozar [MP12] (§3.8) and Gupta et al. [GGBB96]:

\[w_e &= w + \frac{t}{\pi} \ln\frac{4e}{\sqrt{(t/h)^2 + (t/(w\pi + 1.1t\pi))^2}} \\ \varepsilon_{\mathrm{eff},t} &= \varepsilon_{\mathrm{eff}} - \frac{(\varepsilon_r - 1)\,t/h} {4.6\,\sqrt{w/h}}\]

Then the corrected \(Z_0\) is computed with the effective width \(w_e\) and corrected \(\varepsilon_{\mathrm{eff},t}\).

Parameters:
  • w (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Strip width (m).

  • h (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Substrate height (m).

  • t (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Conductor thickness (m).

  • ep_r (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Relative permittivity of the substrate.

  • ep_eff (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Uncorrected effective permittivity.

Returns:

(w_eff, ep_eff_t, z0_t) — effective width (m), thickness-corrected effective permittivity, and characteristic impedance (Ω).

Return type:

tuple[Array, Array, Array]

microstrip_z0#

qpdk.models.microstrip_z0(w, h, ep_eff)[source]#

Characteristic impedance of a microstrip line.

Uses the Hammerstad-Jensen [HJ80] approximation as given in Pozar [MP12] (Eq. 3.197-3.198):

\[Z_0 = \begin{cases} \displaystyle\frac{60}{\sqrt{\varepsilon_{\mathrm{eff}}}} \ln\!\left(\frac{8h}{w} + \frac{w}{4h}\right) & w/h \le 1 \\[6pt] \displaystyle\frac{120\pi} {\sqrt{\varepsilon_{\mathrm{eff}}}\, \bigl[w/h + 1.393 + 0.667\ln(w/h + 1.444)\bigr]} & w/h \ge 1 \end{cases}\]
Parameters:
  • w (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Strip width (m).

  • h (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Substrate height (m).

  • ep_eff (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Effective permittivity (see microstrip_epsilon_eff()).

Returns:

Characteristic impedance (Ω).

Return type:

Array

nxn#

qpdk.models.nxn(f=5000000000.0, west=1, east=1, north=1, south=1)[source]#

NxN junction model using tee components.

This model creates an N-port divider/combiner by chaining 3-port tee junctions. All ports are connected to a single node.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • west (int) – Number of ports on the west side.

  • east (int) – Number of ports on the east side.

  • north (int) – Number of ports on the north side.

  • south (int) – Number of ports on the south side.

Returns:

S-parameters dictionary with ports o1, o2, …, oN.

Return type:

sax.SType

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import nxn
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = nxn(f=freq)

plt.figure()
plt.title("nxn", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-22.png

open#

qpdk.models.open(*, f=5000000000.0, n_ports=1)#

Electrical open connection Sax model.

Useful for specifying some ports to remain open while not exposing them for connections in circuits.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • n_ports (int) – Number of ports to set as opened

Returns:

S-dictionary where \(S = I_\text{n\_ports}\)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]]

References

[@pozar2012]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import open
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = open(f=freq)

plt.figure()
plt.title("open", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-23.png

plate_capacitor#

qpdk.models.plate_capacitor(*, f=5000000000.0, length=26.0, width=5.0, gap=7.0, cross_section='cpw')[source]#

Plate capacitor Sax model.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (float) – Length of the capacitor pad in μm

  • width (float) – Width of the capacitor pad in μm

  • gap (float) – Gap between plates in μm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – Cross-section specification

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import plate_capacitor
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = plate_capacitor(f=freq)

plt.figure()
plt.title("plate_capacitor", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-24.png

propagation_constant#

qpdk.models.propagation_constant(f, ep_eff, tand=0.0, ep_r=1.0)[source]#

Complex propagation constant of a quasi-TEM transmission line.

For the general lossy case (Pozar [MP12], §3.8):

\[\gamma = \alpha_d + j\,\beta\]

where the dielectric attenuation is

\[\alpha_d = \frac{\pi f}{c_0} \frac{\varepsilon_r}{\sqrt{\varepsilon_{\mathrm{eff}}}} \frac{\varepsilon_{\mathrm{eff}} - 1} {\varepsilon_r - 1} \tan\delta\]

and the phase constant is

\[\beta = \frac{2\pi f}{c_0}\,\sqrt{\varepsilon_{\mathrm{eff}}}\]

For a superconducting line (\(\tan\delta = 0\)) the propagation is purely imaginary: \(\gamma = j\beta\).

Parameters:
  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Frequency (Hz).

  • ep_eff (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Effective permittivity.

  • tand (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Dielectric loss tangent (default 0 — lossless).

  • ep_r (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Substrate relative permittivity (only needed when tand > 0).

Returns:

Complex propagation constant \(\gamma\) (1/m).

Return type:

Array

purcell_decay_rate#

qpdk.models.purcell_decay_rate(g_ghz, ω_t_ghz, ω_r_ghz, κ_ghz)[source]#

Estimate the Purcell decay rate of a transmon through a resonator.

The Purcell effect limits qubit lifetime when coupled to a lossy resonator. In the dispersive regime [BGGW21, HSJ+08]:

\[\gamma_\text{Purcell} = \kappa \left(\frac{g}{\Delta}\right)^2\]

where \(\kappa\) is the resonator decay rate and \(\Delta = \omega_t - \omega_r\).

Parameters:
  • g_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Coupling strength in GHz.

  • ω_t_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Transmon frequency in GHz.

  • ω_r_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Resonator frequency in GHz.

  • κ_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Resonator linewidth (decay rate) in GHz.

Returns:

Purcell decay rate in GHz.

Return type:

float | Array

Example

>>> γ = purcell_decay_rate(0.1, 5.0, 7.0, 0.001)
>>> T_purcell = 1 / (γ * 1e9)
>>> print(f"T_Purcell = {T_purcell * 1e6:.0f} µs")

quarter_wave_resonator_coupled#

qpdk.models.quarter_wave_resonator_coupled(f=5000000000.0, length=5000.0, coupling_gap=0.27, coupling_straight_length=20, cross_section='cpw')[source]#

Model for a quarter-wave coplanar waveguide resonator coupled to a probeline.

Parameters:
  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the CPW.

  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Frequency in Hz at which to evaluate the S-parameters.

  • length (float) – Total length of the resonator in μm.

  • coupling_gap (float) – Gap between the resonator and the probeline in μm.

  • coupling_straight_length (float) – Length of the coupling section in μm.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import quarter_wave_resonator_coupled
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = quarter_wave_resonator_coupled(f=freq)

plt.figure()
plt.title("quarter_wave_resonator_coupled", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-25.png

qubit_with_resonator#

qpdk.models.qubit_with_resonator(f=5000000000.0, qubit_capacitance=1e-13, qubit_inductance=1e-09, qubit_grounded=False, resonator_length=5000.0, resonator_cross_section='cpw', coupling_capacitance=1e-14)[source]#

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

This model corresponds to the layout function transmon_with_resonator().

The model combines: - A transmon qubit (LC resonator) - A quarter-wave coplanar waveguide resonator - A coupling capacitor connecting the qubit to the resonator

    "quarter-wave resonator"     (straight_shorted)            │ o1 ── tee ─┤            │            +── Cc ── qubit ── o2

The qubit can be either: - A double-island transmon (qubit_grounded=False): both islands floating - A shunted transmon (qubit_grounded=True): one island grounded

Use ec_to_capacitance() and ej_to_inductance() to convert from qubit Hamiltonian parameters (\(E_C\), \(E_J\)) to circuit parameters.

Note

This function is not JIT-compiled because it depends on straight_shorted(), which internally uses cpw_parameters for transmission line modeling.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • qubit_capacitance (float) – Total capacitance \(C_\Sigma\) of the qubit in Farads. Convert from charging energy using ec_to_capacitance().

  • qubit_inductance (float) – Josephson inductance \(L_\text{J}\) in Henries. Convert from Josephson energy using ej_to_inductance().

  • qubit_grounded (bool) – If True, the qubit is a shunted transmon (grounded). If False, it is a double-island transmon (ungrounded).

  • resonator_length (float) – Length of the quarter-wave resonator in µm.

  • resonator_cross_section (str) – Cross-section specification for the resonator.

  • coupling_capacitance (float) – Coupling capacitance between qubit and resonator in Farads. Use coupling_strength_to_capacitance() to convert from qubit-resonator coupling strength \(g\).

Returns:

S-parameters dictionary with ports o1 (resonator input)

and o2 (qubit ground or floating).

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import qubit_with_resonator
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = qubit_with_resonator(f=freq)

plt.figure()
plt.title("qubit_with_resonator", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-26.png

rectangle#

qpdk.models.rectangle(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a rectangular section, wrapped to straight().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import rectangle
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = rectangle(f=freq)

plt.figure()
plt.title("rectangle", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-27.png

resonator#

qpdk.models.resonator(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a simple transmission line resonator.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SType

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import resonator
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = resonator(f=freq)

plt.figure()
plt.title("resonator", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-28.png

resonator_coupled#

qpdk.models.resonator_coupled(f=5000000000.0, length=5000.0, coupling_gap=0.27, coupling_straight_length=20, cross_section='cpw', open_start=True, open_end=False)[source]#

Model for a coplanar waveguide resonator coupled to a probeline.

Parameters:
  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the CPW.

  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Frequency in Hz at which to evaluate the S-parameters.

  • length (float) – Total length of the resonator in μm.

  • coupling_gap (float) – Gap between the resonator and the probeline in μm.

  • coupling_straight_length (float) – Length of the coupling section in μm.

  • open_start (bool) – If True, adds an electrical open at the start.

  • open_end (bool) – If True, adds an electrical open at the end.

Returns:

S-parameters dictionary with 4 ports.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import resonator_coupled
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = resonator_coupled(f=freq)

plt.figure()
plt.title("resonator_coupled", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-29.png

resonator_frequency#

qpdk.models.resonator_frequency(*, length, epsilon_eff=None, media=None, cross_section='cpw', is_quarter_wave=True)[source]#

Calculate the resonance frequency of a quarter- or half-wave CPW resonator.

\[\begin{aligned} f &= \frac{v_p}{4L} \mathtt{ (quarter-wave resonator)} \\ f &= \frac{v_p}{2L} \mathtt{ (half-wave resonator)} \end{aligned}\]

The phase velocity is \(v_p = c_0 / \sqrt{\varepsilon_{\mathrm{eff}}}\).

See [MP12, Sim01] for details.

Parameters:
  • length (float) – Length of the resonator in μm.

  • epsilon_eff (float | None) – Effective permittivity. If None (default), computed from cross_section using cpw_parameters().

  • media (Any) – Deprecated. Use epsilon_eff or cross_section instead.

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – Cross-section specification (used only when epsilon_eff and media are not provided).

  • is_quarter_wave (bool) – If True, calculates for a quarter-wave resonator; if False, for a half-wave resonator. default is True.

Returns:

Resonance frequency in Hz.

Return type:

float

resonator_half_wave#

qpdk.models.resonator_half_wave(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a half-wave resonator (open at both ends).

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SType

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import resonator_half_wave
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = resonator_half_wave(f=freq)

plt.figure()
plt.title("resonator_half_wave", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-30.png

resonator_linewidth_from_q#

qpdk.models.resonator_linewidth_from_q(ω_r_ghz, q_ext)[source]#

Compute resonator linewidth from external quality factor.

Converts external quality factor to linewidth (half-linewidth at half-maximum) [MP12, GopplFB+08]:

\[\kappa = \frac{\omega_r}{Q_\text{ext}}\]
Parameters:
  • ω_r_ghz (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – Resonator frequency in GHz.

  • q_ext (float | Array | ndarray | bool | number | bool | int | complex | TypedNdArray) – External quality factor.

Returns:

Resonator linewidth \(\kappa\) in GHz.

Return type:

float | Array

Example

>>> κ = resonator_linewidth_from_q(7.0, 10_000)
>>> print(f"κ = {κ * 1e6:.1f} kHz")

resonator_quarter_wave#

qpdk.models.resonator_quarter_wave(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a quarter-wave resonator (shorted at one end).

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SType

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import resonator_quarter_wave
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = resonator_quarter_wave(f=freq)

plt.figure()
plt.title("resonator_quarter_wave", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-31.png

short#

qpdk.models.short(*, f=5000000000.0, n_ports=1)#

Electrical short connection Sax model.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • n_ports (int) – Number of ports to set as shorted

Returns:

S-dictionary where \(S = -I_\text{n\_ports}\)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]]

References

[@pozar2012]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import short
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = short(f=freq)

plt.figure()
plt.title("short", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-32.png

short_2_port#

qpdk.models.short_2_port(f=5000000000.0)#

Electrical short 2-port connection Sax model.

Parameters:

f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import short_2_port
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = short_2_port(f=freq)

plt.figure()
plt.title("short_2_port", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-33.png

shunted_transmon#

qpdk.models.shunted_transmon(f=5000000000.0, capacitance=1e-13, inductance=7e-09)[source]#

LC resonator model for a shunted transmon qubit.

A shunted transmon has one island grounded and the other island connected to the junction. This is modeled as a grounded parallel LC resonator.

The qubit frequency is approximately:

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

For the LC model, the resonance frequency is:

\[f_r = \frac{1}{2\pi\sqrt{LC}}\]

Use ec_to_capacitance() and ej_to_inductance() to convert from qubit Hamiltonian parameters.

o1 ──┬──L──┬──.      │     │  | "2-port ground"      └──C──┘  |              "o2"
Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • capacitance (float) – Total capacitance \(C_\Sigma\) of the qubit in Farads.

  • inductance (float) – Josephson inductance \(L_\text{J}\) in Henries.

Returns:

S-parameters dictionary with ports o1 and o2.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import shunted_transmon
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = shunted_transmon(f=freq)

plt.figure()
plt.title("shunted_transmon", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-34.png

squid_junction#

qpdk.models.squid_junction(*, f=5000000000.0, ic_tot=2e-06, asymmetry=0.0, capacitance=1e-14, resistance=5000.0, ib=0.0, flux=0.0)[source]#

DC SQUID small-signal Sax model in the zero-screening limit.

Treats the DC SQUID as a single effective RCSJ whose critical current is tunable by an external magnetic flux. Assumes negligible loop geometric inductance.

See [KYG+07] and [Tin15] for details on asymmetric SQUIDs and effective Josephson inductance.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • ic_tot (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Total critical current sum \(I_{c1} + I_{c2}\) in Amperes

  • asymmetry (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Junction asymmetry \((I_{c1} - I_{c2}) / I_{c,tot}\)

  • capacitance (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Total SQUID capacitance \(C_1 + C_2\) in Farads

  • resistance (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Total SQUID shunt resistance \(R_1 || R_2\) in Ohms

  • ib (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – DC bias current \(I_b\) in Amperes

  • flux (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – External magnetic flux \(\Phi_{ext}\) in Webers

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import squid_junction
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = squid_junction(f=freq)

plt.figure()
plt.title("squid_junction", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-35.png

straight#

qpdk.models.straight(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a straight coplanar waveguide.

Computes S-parameters analytically using conformal-mapping CPW theory following Simons [Sim01] (ch. 2) and the Qucs-S CPW model (Qucs technical documentation, §12.4). Conductor thickness corrections use the first-order model of Gupta, Garg, Bahl, and Bhartia [GGBB96].

The propagation constant and characteristic impedance are evaluated with pure-JAX functions (see qpdk.models.cpw) so the model composes with jax.jit, jax.grad, and jax.vmap.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import straight
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = straight(f=freq)

plt.figure()
plt.title("straight", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-36.png

straight_double_open#

qpdk.models.straight_double_open(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a straight waveguide with open ends.

Note

Ports o1 and o2 are internally open-circuited and should not be used. They are provided to match the number of ports in the layout component.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SType

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import straight_double_open
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = straight_double_open(f=freq)

plt.figure()
plt.title("straight_double_open", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-37.png

straight_microstrip#

qpdk.models.straight_microstrip(f=5000000000.0, length=1000, width=10.0, h=500.0, t=0.2, ep_r=11.45, tand=0.0)[source]#

S-parameter model for a straight microstrip transmission line.

Computes S-parameters analytically using the Hammerstad-Jensen [HJ80] closed-form expressions for effective permittivity and characteristic impedance, as described in Pozar [MP12] (ch. 3, §3.8). Conductor thickness corrections follow Gupta et al. [GGBB96] (§2.2.4).

All computation is done with pure-JAX functions (see qpdk.models.cpw) so the model composes with jax.jit, jax.grad, and jax.vmap.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm.

  • width (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Strip width in µm.

  • h (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Substrate height in µm.

  • t (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Conductor thickness in µm (default 0.2 µm = 200 nm).

  • ep_r (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Relative permittivity of the substrate (default 11.45 for Si).

  • tand (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Dielectric loss tangent (default 0 — lossless).

Returns:

S-parameters dictionary.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import straight_microstrip
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = straight_microstrip(f=freq)

plt.figure()
plt.title("straight_microstrip", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-38.png

straight_open#

qpdk.models.straight_open(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a straight waveguide with one open end.

Note

The port o2 is internally open-circuited and should not be used. It is provided to match the number of ports in the layout component.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SType

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import straight_open
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = straight_open(f=freq)

plt.figure()
plt.title("straight_open", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-39.png

straight_shorted#

qpdk.models.straight_shorted(f=5000000000.0, length=1000, cross_section='cpw')[source]#

S-parameter model for a straight waveguide with one shorted end.

This may be used to model a quarter-wave coplanar waveguide resonator.

Note

The port o2 is internally shorted and should not be used. It seems to be a Sax limitation that we need to define at least two ports.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – The cross-section of the waveguide.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import straight_shorted
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = straight_shorted(f=freq)

plt.figure()
plt.title("straight_shorted", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-40.png

taper_cross_section#

qpdk.models.taper_cross_section(f=5000000000.0, length=1000, cross_section_1='cpw', cross_section_2='cpw', n_points=50)[source]#

S-parameter model for a cross-section taper using linear interpolation.

Parameters:
  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Array of frequency points in Hz

  • length (Annotated[float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)]) – Physical length in µm

  • cross_section_1 (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – Cross-section for the start of the taper.

  • cross_section_2 (CrossSection | str | dict[str, Any] | Callable[[...], CrossSection] | SymmetricalCrossSection | DCrossSection) – Cross-section for the end of the taper.

  • n_points (int) – Number of segments to divide the taper into for simulation.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import taper_cross_section
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = taper_cross_section(f=freq)

plt.figure()
plt.title("taper_cross_section", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-41.png

transmission_line_s_params#

qpdk.models.transmission_line_s_params(gamma, z0, length, z_ref=None)[source]#

S-parameters of a uniform transmission line (ABCD→S conversion).

The ABCD matrix of a line with characteristic impedance \(Z_0\), propagation constant \(\gamma\), and length \(\ell\) is

\[\begin{pmatrix} A & B \\ C & D \end{pmatrix} = \begin{pmatrix} \cosh\theta & Z_0\sinh\theta \\ \sinh\theta / Z_0 & \cosh\theta \end{pmatrix}, \quad \theta = \gamma\,\ell.\]

Converting to S-parameters referenced to \(Z_{\mathrm{ref}}\) (Pozar [MP12], Table 4.2):

\[\begin{aligned} S_{11} &= \frac{A + B/Z_{\mathrm{ref}} - C\,Z_{\mathrm{ref}} - D} {A + B/Z_{\mathrm{ref}} + C\,Z_{\mathrm{ref}} + D} \\ S_{21} &= \frac{2} {A + B/Z_{\mathrm{ref}} + C\,Z_{\mathrm{ref}} + D} \end{aligned}\]

When z_ref is None the reference impedance defaults to z0 (matched case), giving \(S_{11} = 0\) and \(S_{21} = e^{-\gamma\ell}\).

Parameters:
  • gamma (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Complex propagation constant (1/m).

  • z0 (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Characteristic impedance (Ω).

  • length (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Physical length (m).

  • z_ref (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray | None) – Reference (port) impedance (Ω). Defaults to z0.

Returns:

(S11, S21) — complex S-parameter arrays.

Return type:

tuple[Array, Array]

transmon_coupled#

qpdk.models.transmon_coupled(f=5000000000.0, capacitance=1e-13, inductance=1e-09, grounded=False, coupling_capacitance=1e-14, coupling_inductance=0.0)[source]#

Coupled transmon qubit model.

This model extends the basic transmon qubit by adding a coupling network consisting of a parallel capacitor and/or inductor. This can represent capacitive or inductive coupling between qubits or between a qubit and a readout resonator.

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

     +──Lc──+    +──L──+ o1 ──│      │────|     │─── o2 or grounded o2      +──Cc──+    +──C──+                "LC resonator"
For capacitive coupling (common for qubit-resonator coupling):
  • Set coupling_capacitance to the coupling capacitor value

  • Set coupling_inductance=0.0

For inductive coupling (common for flux-tunable coupling):
  • Set coupling_inductance to the coupling inductor value

  • Set coupling_capacitance=0.0 (or small value)

Use coupling_strength_to_capacitance() to convert from the qubit-resonator coupling strength \(g\) to the coupling capacitance.

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)]) – Array of frequency points in Hz.

  • capacitance (float) – Total capacitance \(C_\Sigma\) of the qubit in Farads.

  • inductance (float) – Josephson inductance \(L_\text{J}\) in Henries.

  • grounded (bool) – If True, the qubit is a shunted transmon (grounded). If False, it is a double-pad transmon (ungrounded).

  • coupling_capacitance (float) – Coupling capacitance \(C_c\) in Farads.

  • coupling_inductance (float) – Coupling inductance \(L_c\) in Henries.

Returns:

S-parameters dictionary with ports o1 and o2.

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import transmon_coupled
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = transmon_coupled(f=freq)

plt.figure()
plt.title("transmon_coupled", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-42.png

transmon_with_resonator#

qpdk.models.transmon_with_resonator(f=5000000000.0, qubit_capacitance=1e-13, qubit_inductance=1e-09, qubit_grounded=False, resonator_length=5000.0, resonator_cross_section='cpw', coupling_capacitance=1e-14)[source]#

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

This model is identical to qubit_with_resonator().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • qubit_capacitance (float)

  • qubit_inductance (float)

  • qubit_grounded (bool)

  • resonator_length (float)

  • resonator_cross_section (str)

  • coupling_capacitance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import transmon_with_resonator
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = transmon_with_resonator(f=freq)

plt.figure()
plt.title("transmon_with_resonator", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-43.png

tsv#

qpdk.models.tsv(f=5000000000.0, via_height=1000.0)[source]#

S-parameter model for a through-silicon via (TSV), wrapped to straight().

TODO: add a constant loss channel for TSVs.

Parameters:
  • f (Array | ndarray | bool | number | bool | int | float | complex | TypedNdArray) – Array of frequency points in Hz

  • via_height (float) – Physical height (length) of the TSV in µm.

Returns:

S-parameters dictionary

Return type:

sax.SDict

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import tsv
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = tsv(f=freq)

plt.figure()
plt.title("tsv", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-44.png

xmon_transmon#

qpdk.models.xmon_transmon(f=5000000000.0, capacitance=1e-13, inductance=7e-09)[source]#

LC resonator model for an Xmon style transmon qubit.

An Xmon transmon is typically shunted, so this model wraps shunted_transmon().

Parameters:
  • f (Annotated[Array | ndarray | Annotated[Annotated[int | integer, PlainValidator(func=~sax.saxtypes.core.val_int, json_schema_input_type=~typing.Any)] | float | floating, PlainValidator(func=~sax.saxtypes.core.val_float, json_schema_input_type=~typing.Any)], floating, PlainValidator(func=~sax.saxtypes.core.val_float_array, json_schema_input_type=~typing.Any)])

  • capacitance (float)

  • inductance (float)

Return type:

dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.singlemode.val_port, json_schema_input_type=~typing.Any)], int]] | dict[tuple[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)]], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)]] | tuple[Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array | ndarray, signedinteger, 1, PlainValidator(func=~sax.saxtypes.core.val_int_array_1d, json_schema_input_type=~typing.Any)], Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]] | tuple[Annotated[Array, complexfloating, PlainValidator(func=~sax.saxtypes.core.val_complex_array, json_schema_input_type=~typing.Any)], dict[Annotated[str, PlainValidator(func=~sax.saxtypes.multimode.val_port_mode, json_schema_input_type=~typing.Any)], int]]

import jax.numpy as jnp
import matplotlib.pyplot as plt
from qpdk.models import xmon_transmon
from qpdk import PDK

PDK.activate()

freq = jnp.linspace(2e9, 8e9, 501)
freq_ghz = freq / 1e9
s_model = xmon_transmon(f=freq)

plt.figure()
plt.title("xmon_transmon", fontsize=14)

for (pout, pin), sij in s_model.items():
   i = int(pout[-1])  # "o2" -> 2
   j = int(pin[-1])   # "o1" -> 1

   if i >= j:
       plt.plot(
           freq_ghz,
           20 * jnp.log10(jnp.abs(sij)),
           label = "$S_{" + str(i) + str(j) + "}$"
       )
plt.xlabel("Frequency [GHz]", fontsize=12)
plt.ylabel("Magnitude [dB]", fontsize=12)
plt.grid(True)
plt.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/models-45.png

References#

[BGGW21] (1,2,3)

Alexandre Blais, Arne L. Grimsmo, S. M. Girvin, and Andreas Wallraff. Circuit quantum electrodynamics. Reviews of Modern Physics, 93(2):025005, May 2021. doi:10.1103/RevModPhys.93.025005.

[CMK+14]

Zijun Chen, A. Megrant, J. Kelly, R. Barends, J. Bochmann, Yu Chen, B. Chiaro, A. Dunsworth, E. Jeffrey, J. Y. Mutus, P. J. J. O'Malley, C. Neill, P. Roushan, D. Sank, A. Vainsencher, J. Wenner, T. C. White, A. N. Cleland, and John M. Martinis. Fabrication and characterization of aluminum airbridges for superconducting microwave circuits. Applied Physics Letters, February 2014. doi:10.1063/1.4863745.

[GBS+06]

J. Gambetta, A. Blais, D. I. Schuster, A. Wallraff, L. Frunzio, J. Majer, M. H. Devoret, S. M. Girvin, and R. J. Schoelkopf. Qubit-photon interactions in a cavity: measurement-induced dephasing and number splitting. Physical Review A, 74(4):042318, October 2006. doi:10.1103/PhysRevA.74.042318.

[Gao08]

Jiansong Gao. The Physics of Superconducting Microwave Resonators. PhD thesis, California Institute of Technology, 2008. doi:10.7907/RAT0-VM75.

[GN84]

G. Ghione and C. Naldi. Analytical formulas for coplanar lines in hybrid and monolithic MICs. Electronics Letters, 20(4):179–181, February 1984. doi:10.1049/el:19840120.

[GGBB96] (1,2,3,4,5)

Kuldip C. Gupta, R. Garg, I. Bahl, and P. Bhartia. Microstrip Lines and Slotlines. Artech House, Boston, 2. ed edition, 1996. ISBN 978-0-89006-766-6.

[GopplFB+08]

M. Göppl, A. Fragner, M. Baur, R. Bianchetti, S. Filipp, J. M. Fink, P. J. Leek, G. Puebla, L. Steffen, and A. Wallraff. Coplanar waveguide resonators for circuit quantum electrodynamics. Journal of Applied Physics, 104(11):113904, December 2008. doi:10.1063/1.3010859.

[HJ80] (1,2,3)

E. Hammerstad and O. Jensen. Accurate Models for Microstrip Computer-Aided Design. In 1980 IEEE MTT-S International Microwave Symposium Digest, 407–409. Washington, DC, USA, 1980. IEEE. doi:10.1109/MWSYM.1980.1124303.

[HSJ+08]

A. A. Houck, J. A. Schreier, B. R. Johnson, J. M. Chow, Jens Koch, J. M. Gambetta, D. I. Schuster, L. Frunzio, M. H. Devoret, S. M. Girvin, and R. J. Schoelkopf. Controlling the spontaneous emission of a superconducting transmon qubit. Physical Review Letters, 101(8):080502, August 2008. doi:10.1103/PhysRevLett.101.080502.

[KYG+07] (1,2,3,4)

Jens Koch, Terri M. Yu, Jay Gambetta, A. A. Houck, D. I. Schuster, J. Majer, Alexandre Blais, M. H. Devoret, S. M. Girvin, and R. J. Schoelkopf. Charge-insensitive qubit design derived from the Cooper pair box. Physical Review A, 76(4):042319, October 2007. doi:10.1103/PhysRevA.76.042319.

[KKY+19]

P. Krantz, M. Kjaergaard, F. Yan, T. P. Orlando, S. Gustavsson, and W. D. Oliver. A quantum engineer's guide to superconducting qubits. Applied Physics Reviews, 6(2):021318, June 2019. doi:10.1063/1.5089550.

[MP12] (1,2,3,4,5,6,7,8)

David M. Pozar. Microwave Engineering. John Wiley & Sons, Inc., 4 edition, 2012. ISBN 978-0-470-63155-3.

[McC68]

D. E. McCumber. Effect of ac impedance on dc voltage-current characteristics of superconductor weak-link junctions. Journal of Applied Physics, 39(7):3113–3118, June 1968. doi:10.1063/1.1656743.

[Sav23]

Niko Savola. Design and modelling of long-coherence qubits using energy participation ratios. Master's thesis, Aalto University, February 2023. URL: http://urn.fi/URN:NBN:fi:aalto-202305213270.

[SFS+15]

A V Shcherbakova, K G Fedorov, K V Shulga, V V Ryazanov, V V Bolginov, V A Oboznov, S V Egorov, V O Shkolnikov, M J Wolf, D Beckmann, and A V Ustinov. Fabrication and measurements of hybrid Nb/Al Josephson junctions and flux qubits with \mkbibemph \ensuremath \pi -shifters. Superconductor Science and Technology, 28(2):025009, February 2015. doi:10.1088/0953-2048/28/2/025009.

[Sim01] (1,2,3,4)

Rainee Simons. Coplanar Waveguide Circuits, Components, and Systems. Number v. 165 in Wiley Series in Microwave and Optical Engineering. Wiley Interscience, New York, 2001. ISBN 978-0-471-46393-1. doi:10.1002/0471224758.

[Tin15]

Michael Tinkham. Introduction to Superconductivity. Dover Books on Physics. Dover Publ, Mineola, NY, 2 ed edition, 2015. ISBN 978-0-486-43503-9.