[1]:
%matplotlib inline
%config InlineBackend.figure_format = 'svg'
import numpy as np
import scqubits as qubit
from scqubits import HilbertSpace, InteractionTerm, ParameterSweep, Explorer
Explorer¶
Sometimes, exploring the properties of coupled quantum systems benefits from visual aides, and the possibility to see how properties change when system parameters are changed. The Explorer
class in scqubits provides an interactive plot with multiple panels collecting an important set of information.
The idea behind the Explorer
class is simple: the user selects an external parameter that they wish to sweep, e.g., an external magnetic flux. The composite system is user-defined through the HilbertSpace
and InteractionTerm
classes, allowing flexibility to include different types and numbers of superconducting qubits and harmonic modes.
Once defined, the parameter sweep is computed by means of the ParameterSweep
class, and spectral data is stored in memory to allow efficient, interactive display of data.
Example 1: fluxonium coupled to resonator¶
As a first example, we consider a system composed of a fluxonium qubit, coupled through its charge operator to the voltage inside a resonator.
HilbertSpace setup¶
The initialization of the composite Hilbert space proceeds as usual; we first define the individual two subsystems that will make up the Hilbert space:
[2]:
qbt = qubit.Fluxonium(
EJ=2.55,
EC=0.72,
EL=0.12,
flux=0.0,
cutoff=110,
truncated_dim=9
)
osc = qubit.Oscillator(
E_osc=4.0,
truncated_dim=5
)
Here, the truncated_dim
parameters are important. For the fluxonium, with cutoff
set to 110, the internal Hilbert space dimension is 110. Once diagonalized, we will only keep a few eigenstates going forward - in the above example 9. Similarly, we keep 5 levels for the resonators, i.e., photon states n=0,1,…,4 are included in the following.
Next, the two subsystems are declared as the two components of a joint Hilbert space:
[3]:
hilbertspace = qubit.HilbertSpace([qbt, osc])
The interaction between fluxonium and resonator is of the form \(H_\text{int} = g n (a+a^\dagger)\), where \(n\) is the fluxonium’s charge operator: qbt.n_operator()
. This structure is captured by creating an InteractionTerm
object. Since this is the only interaction term, the interaction_list
contains only this one term. It is inserted into the HilbertSpace
object.
[4]:
interaction = InteractionTerm(
g_strength=0.2,
op1=qbt.n_operator(),
subsys1=qbt,
op2=osc.creation_operator() + osc.annihilation_operator(),
subsys2=osc
)
interaction_list = [interaction]
hilbertspace.interaction_list = interaction_list
Parameter sweep setup¶
We consider sweeping the external flux through the fluxonium loop. To create the necessary ParameterSweep
object, we specify: 1. param_name
: the name of the sweep parameter (below expressed in LaTeX format as the flux in units of the flux quantum) 2. param_vals
: an array with the flux values used in the sweep 3. subsys_update_list
: a list containing all Hilbert space subsystems that change as the flux is varied 4. update_hilbertspace(param_val)
: a function that shows how a
change in the sweep parameter affects the Hilbert space; here only the .flux
attributed of the fluxonium qubit needs to be changed
These ingredients all make it into the initialization of the ParameterSweep
object. Once initialized, spectral data is generated and stored. Here, we additionally generate data for dispersive shifts and charge matrix elements.
[5]:
param_name = '$\Phi_{ext}/\Phi_0$'
param_vals = np.linspace(-0.5, 0.5, 100)
subsys_update_list = [qbt]
def update_hilbertspace(param_val):
qbt.flux = param_val
sweep = ParameterSweep(
param_name=param_name,
param_vals=param_vals,
evals_count=10,
hilbertspace=hilbertspace,
subsys_update_list=subsys_update_list,
update_hilbertspace=update_hilbertspace
)
Starting the Explorer class¶
At this point, everything is prepared to start the interactive Explorer
and play with the interactive display!
[6]:
explorer = Explorer(
sweep=sweep,
evals_count=10
)
explorer.interact()
The Explorer
plots are currently fixed to:
Bare spectra of the individual qubits
Wave functions of the bare qubits
Dressed spectrum of the composite Hilbert space
Spectrum for n-photon qubit transitions, starting from a given initial state
AC Stark shift \(\chi_{01}\) for any of the qubits
Charge matrix elements for any of the qubits, using the same initial state as in point 4.
Note
The n-photon qubit transition plot gathers data from the full system spectrum by assessing maximum overlap with bare qubit states. Whenever qubit levels cross and hybridize with, e.g., a resonator, a discontinuity will show up in the spectrum. These discontinuities are correct representations, and not a bug.
Example 2: two transmons coupled to a resonator¶
In the following second example, we consider a system composed of two flux-tunable transmons, capacitively coupled to one joint harmonic mode. (The flux is assumed to arise from a global field, and the SQUID-loop areas of the two transmons are different with an area ratio of 1.4)
Hilbert space setup¶
[8]:
qbt1 = qubit.Transmon(
EJ=25.0,
EC=0.2,
ng=0,
ncut=30,
truncated_dim=3)
qbt2 = qubit.Transmon(
EJ=15.0,
EC=0.15,
ng=0,
ncut=30,
truncated_dim=3)
resonator = qubit.Oscillator(
E_osc=5.5,
truncated_dim=4)
hilbertspace = HilbertSpace([qbt1, qbt2, resonator])
g1 = 0.1 # coupling resonator-CPB1 (without charge matrix elements)
g2 = 0.2 # coupling resonator-CPB2 (without charge matrix elements)
interaction1 = InteractionTerm(
g_strength = g1,
op1 = qbt1.n_operator(),
subsys1 = qbt1,
op2 = resonator.creation_operator() + resonator.annihilation_operator(),
subsys2 =resonator
)
interaction2 = InteractionTerm(
g_strength = g2,
op1 = qbt2.n_operator(),
subsys1 = qbt2,
op2 = resonator.creation_operator() + resonator.annihilation_operator(),
subsys2 =resonator
)
interaction_list = [interaction1, interaction2]
hilbertspace.interaction_list = interaction_list
Parameter sweep setup¶
[9]:
param_name = '$\Phi_{ext}/\Phi_0$'
param_vals = np.linspace(0.0, 1.0, 150)
subsys_update_list = [qbt1, qbt2]
def update_hilbertspace(param_val): # function that shows how Hilbert space components are updated
qbt1.EJ = 30*np.abs(np.cos(np.pi * param_val))
qbt2.EJ = 40*np.abs(np.cos(np.pi * param_val * 2))
sweep = ParameterSweep(
param_name=param_name,
param_vals=param_vals,
evals_count=15,
hilbertspace=hilbertspace,
subsys_update_list=subsys_update_list,
update_hilbertspace=update_hilbertspace,
)
Start Explorer¶
[10]:
explorer = Explorer(
sweep=sweep,
evals_count=10
)
explorer.interact()
[ ]: