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


title1

The Explorer plots are currently fixed to:

  1. Bare spectra of the individual qubits

  2. Wave functions of the bare qubits

  3. Dressed spectrum of the composite Hilbert space

  4. Spectrum for n-photon qubit transitions, starting from a given initial state

  5. AC Stark shift \(\chi_{01}\) for any of the qubits

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




title2

[ ]: