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
) – aHilbertSpace
object instance that describes the quantum system of interestparamvals_by_name (
Dict[str, np.ndarray]
) – a dictionary that maps each parameter name (string) to an array of parameter valuesupdate_hilbertspace (
Callable
) – a function that defines how parameters changes affect the systemsubsys_update_info (
Optional[Dict[str, List[QuantumSystem]]]
) – for potential speed-up, specify which subsystems undergo changes as each of the parameters is varieddeepcopy (
Optional[bool]
) – determines whether theHilbertSpace
object and all constituents should be duplicated and disconnected from the global objectsnum_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
)