# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""
This code calculates the energy eigenvalues of the transmon qubit cooper pair box
as a function of the offset charge and plots the corresponding results.
Key References:
- J. Koch et al. "Charge-insensitive qubit design derived from the Cooper pair box"
Phys. Rev. A. 76, 042319 (2007).
"""
import numpy as np
import matplotlib.pyplot as plt
from qiskit_metal.analyses.hamiltonian.transmon_CPB_analytic import mathieu_level
__all__ = ["kidx", "kidx_raw", "plot_eigenvalues", "transmon_eigenvalue"]
[docs]
def kidx_raw(m, my_ng):
"""
This function calculates the integers which correct sort the eigenstates and eigenenergies
of the solution to Mathieu's equation.
Args:
m (int): The energy level of the qubit (m=0,1,2,3,etc.)
my_ng (float): the offset charge of the Josephjunction island (in units of 2e)
Returns:
float: the calculated index
"""
if my_ng == 0:
return m + 1.0 - ((m + 1.0) % 2.0)
return (
m
+ 1.0
- ((m + 1.0) % 2.0)
+ 2.0 * my_ng * ((-1.0) ** (m - 0.5 * (np.sign(my_ng) - 1.0)))
)
[docs]
def kidx(m, my_ng):
"""
This function is a placeholder which performs an additional modulo operation
with an offset of ng=0.5, but is not necessary when only integer values of
kidxRAW(m, my_ng) are used. If non-integer values of kidxRAW are used, then
the modulo operation below needs to be updated to include the offset.
Args:
m (int): The energy level of the qubit (m=0,1,2,3,etc.)
ng (float): the offset charge of the Josephjunction island (in units of 2e)
Returns:
float: the calculated energy eigenvalue, after an ng=0.5 offset.
"""
return kidx_raw(m, my_ng)
# define the Josephson to charging energy ratio E_J / E_C
RATIO = 1.0
# define the charing energy
E_C = 1.0
[docs]
def transmon_eigenvalue(m, my_ng, Ej=None, Ec=None):
"""
This function calculate the energy eigenvalue of the transmon qubit for a given
energy level (m) and offset charge (my_ng), from Mathieu characteristic values
(Koch et al. Eq. 2.11), via
:func:`~qiskit_metal.analyses.hamiltonian.transmon_CPB_analytic.mathieu_level`:
even levels use the even (``a``) and odd levels the odd (``b``) characteristic
value at integer order, and non-integer orders (any ``my_ng`` other than 0 or
1/2 modulo 1) are computed from the Hill matrix.
Args:
m (int): The energy level of the qubit (m=0,1,2,3,etc.)
my_ng (float): the offset charge of the Josephjunction island (in units of 2e)
Ej (float): Josephson energy. Defaults to ``RATIO * E_C`` (module
constants).
Ec (float): Charging energy. Defaults to the module constant ``E_C``.
Returns:
float: the calculated energy eigenvalue.
"""
if Ec is None:
Ec = E_C
if Ej is None:
Ej = RATIO * Ec
return mathieu_level(m, my_ng, Ej, Ec)
# extremely coarse grid: only three points from -0.5 to 0.5. This so that only integer values
# of the index are used in the calculation of the Mathieu characteristic value.
ng = np.linspace(-0.5, 0.5, 3)
# ng is periodic extending from -2 to 2:
ng_periodic = np.linspace(-2.0, 2.0, 9)
# define energies between (-0.5, 0.5) as empty lists.
E0 = []
E1 = []
E2 = []
E3 = []
# define periodic energies between (-2.0, 2.0)
E0_periodic = [None] * 9
E1_periodic = [None] * 9
E2_periodic = [None] * 9
E3_periodic = [None] * 9
# calculate the energies for m=0,1,2 at each value of offset charge
for i in ng:
E0.append(transmon_eigenvalue(0, i))
E1.append(transmon_eigenvalue(1, i))
E2.append(transmon_eigenvalue(2, i))
E3.append(transmon_eigenvalue(3, i))
# define the periodic eigen energies based on the values between (-0.5,0.5)
for i in range(len(E0_periodic)):
E0_periodic[0] = E0[1]
E0_periodic[1] = E0[0]
E1_periodic[0] = E1[1]
E1_periodic[1] = E1[0]
E2_periodic[0] = E2[1]
E2_periodic[1] = E2[0]
E3_periodic[0] = E3[1]
E3_periodic[1] = E3[0]
if i > 1:
E0_periodic[i] = E0_periodic[i - 2]
E1_periodic[i] = E1_periodic[i - 2]
E2_periodic[i] = E2_periodic[i - 2]
E3_periodic[i] = E3_periodic[i - 2]
[docs]
def plot_eigenvalues():
"""
This function actually creates the plot(s) of eigenvalues as a function of
offset charge. No arguments need to be passed.
Returns:
A plot of the eigenvalues as a function of offset charge.
"""
# plot the PERIODIC eigen energies between (-2.0, 2.0)
plt.plot(ng_periodic, E0_periodic, "k") # m=0
plt.plot(ng_periodic, E1_periodic, "r") # m=1
plt.plot(ng_periodic, E2_periodic, "b") # m=2
plt.plot(ng_periodic, E3_periodic, "m") # m=3
plt.xlabel("Offset Charge [ng]")
plt.ylabel("Energy E_m[ng]")