ParameterSweep class#

Composite Hilbert spaces, as defined by HilbertSpace objects, are more complicated than individual qubits. A variety of parameter sweeps can be considered, including multi-dimensional sweeps over a collection of parameters. A parameter to be varied does not need to be one of the initialization parameters. Instead it could be a coupling strength or some other derived quantity.

For flexible parameter scans, scqubits provides the ParameterSweep class. To illustrate its usage, we first define a composite Hilbert space - using the example of two tunable transmon qubits coupled to an oscillator. (See the HilbertSpace section in the user guide for details on this topic.)

[2]:
# Define HilbertSpace object: two transmons coupled to an oscillator


tmon1 = scq.TunableTransmon(
    EJmax=40.0,
    EC=0.2,
    d=0.1,
    flux=0.23,
    ng=0.3,
    ncut=40,
    truncated_dim=3,     # after diagonalization, we will keep 3 levels
    id_str="tmon1"       # optional, used for referencing from within
                         # ParameterSweep or HilbertSpace
)

tmon2 = scq.TunableTransmon(
    EJmax=15.0,
    EC=0.15,
    d=0.2,
    flux=0.0,
    ng=0.0,
    ncut=30,
    truncated_dim=3,
    id_str="tmon2"
)

resonator = scq.Oscillator(
    E_osc=4.5,
    truncated_dim=4    # up to 3 photons (0,1,2,3)
)

hilbertspace = scq.HilbertSpace([tmon1, tmon2, resonator])


g1 = 0.1  # coupling resonator-CPB1 (without charge matrix elements)
g2 = 0.2  # coupling resonator-CPB2 (without charge matrix elements)

hilbertspace.add_interaction(
    g_strength = g1,
    op1 = tmon1.n_operator,
    op2 = resonator.creation_operator,
    add_hc = True,
    id_str="tmon1-resonator"  # optional keyword argument
)

hilbertspace.add_interaction(
    g_strength = g2,
    op1 = tmon2.n_operator,
    op2 = resonator.creation_operator,
    add_hc = True,
    id_str="tmon2-resonator"  # optional keyword argument
)

Creating a ParameterSweep object#

The ParameterSweep class facilitates computation of spectra as function of one or multiple external parameter(s). For efficiency in computing a variety of derived quantities and creating plots, the computed bare and dressed spectral data are stored internally.

A ParameterSweep object is initialized by providing the following parameters:

  • hilbertspace (scq.HilbertSpace) – a HilbertSpace object instance that describes the quantum system of interest

  • paramvals_by_name (Dict[str, np.ndarray]) – a dictionary that maps each parameter name (string) to an array of parameter values

  • update_hilbertspace (Callable) – a function that defines how parameters changes affect the system

  • subsys_update_info (Optional[Dict[str, List[QuantumSystem]]]) – for potential speed-up, specify which subsystems undergo changes as each of the parameters is varied

  • deepcopy (Optional[bool]) – determines whether the HilbertSpace object and all constituents should be duplicated and disconnected from the global objects

  • num_cpus (Optional[int]) – number of CPU cores requested for the sweep evaluation

(See API documentation for additional options.)

These ingredients all enter as initialization arguments of the ParameterSweep object. Once initialized, spectral data is generated and stored.

In our example, we consider the strength of a global magnetic field as the parameter to be changed. This field determines the magnetic fluxes for both qubits, in proportions according to their SQUID loop areas. We will reference the flux for transmon 1, and express the flux for transmon 2 in terms of it via an area ratio. In addition, we will vary the offset charge of transmon 2.

Warning

Caution: When specifying operators in the interaction terms, it is advisable to provide each operator as a Callable. For example, use op1=fluxonium.n_operator, rather than the evaluated variant fluxonium.n_operator() which hands over a fixed matrix for that operator. Since representations of operators can themselves depend upon sweep parameters that are varied as part of a ParameterSweep, it is not always safe to work with fixed matrices.

[3]:
# Set up parameter name and values
pname1 = 'flux'
flux_vals = np.linspace(0.0, 2.0, 171)
pname2 = 'ng'
ng_vals = np.linspace(-0.5, 0.5, 49)

# combine into a dictionary
paramvals_by_name = {pname1: flux_vals, pname2: ng_vals}


area_ratio = 1.2

def update_hilbertspace(flux, ng):  # function that defines how Hilbert space components are updated
    tmon1.flux = flux
    tmon2.flux = area_ratio * flux
    tmon2.ng = ng

# dictionary with information on which subsystems are affected by changing parameters
subsys_update_info = {pname1: [tmon1, tmon2],
                      pname2: [tmon2]}


# create the ParameterSweep
sweep = ParameterSweep(
    hilbertspace=hilbertspace,
    paramvals_by_name=paramvals_by_name,
    update_hilbertspace=update_hilbertspace,
    evals_count=20,
    subsys_update_info=subsys_update_info,
    num_cpus=4
)