Parameter handling¶
The tool within \(C^3\) to manipulate the parameters of both the
model and controls is the ParameterMap
. It provides methods to
present the same data for human interaction, i.e. structured information
with physical units and for numerical optimization algorithms that
prefer a linear vector of scale 1. Here, we’ll show some example usage.
We’ll use the ParameterMap
of the model also used in the simulated
calibration example.
from single_qubit_blackbox_exp import create_experiment
exp = create_experiment()
pmap = exp.pmap
The pmap contains a list of all parameters and their values:
pmap.get_full_params()
{'Q1-freq': 5.000 GHz 2pi,
'Q1-anhar': -210.000 MHz 2pi,
'Q1-temp': 0.000 K,
'init_ground-init_temp': -3.469 aK,
'resp-rise_time': 300.000 ps,
'v_to_hz-V_to_Hz': 1.000 GHz/V,
'id[0]-d1-no_drive-amp': 1.000 V,
'id[0]-d1-no_drive-delta': 0.000 V,
'id[0]-d1-no_drive-freq_offset': 0.000 Hz 2pi,
'id[0]-d1-no_drive-xy_angle': 0.000 rad,
'id[0]-d1-no_drive-sigma': 5.000 ns,
'id[0]-d1-no_drive-t_final': 7.000 ns,
'id[0]-d1-carrier-freq': 5.050 GHz 2pi,
'id[0]-d1-carrier-framechange': 5.933 rad,
'rx90p[0]-d1-gauss-amp': 450.000 mV,
'rx90p[0]-d1-gauss-delta': -1.000 ,
'rx90p[0]-d1-gauss-freq_offset': -50.500 MHz 2pi,
'rx90p[0]-d1-gauss-xy_angle': -444.089 arad,
'rx90p[0]-d1-gauss-sigma': 1.750 ns,
'rx90p[0]-d1-gauss-t_final': 7.000 ns,
'rx90p[0]-d1-carrier-freq': 5.050 GHz 2pi,
'rx90p[0]-d1-carrier-framechange': 0.000 rad,
'ry90p[0]-d1-gauss-amp': 450.000 mV,
'ry90p[0]-d1-gauss-delta': -1.000 ,
'ry90p[0]-d1-gauss-freq_offset': -50.500 MHz 2pi,
'ry90p[0]-d1-gauss-xy_angle': 1.571 rad,
'ry90p[0]-d1-gauss-sigma': 1.750 ns,
'ry90p[0]-d1-gauss-t_final': 7.000 ns,
'ry90p[0]-d1-carrier-freq': 5.050 GHz 2pi,
'ry90p[0]-d1-carrier-framechange': 0.000 rad,
'rx90m[0]-d1-gauss-amp': 450.000 mV,
'rx90m[0]-d1-gauss-delta': -1.000 ,
'rx90m[0]-d1-gauss-freq_offset': -50.500 MHz 2pi,
'rx90m[0]-d1-gauss-xy_angle': 3.142 rad,
'rx90m[0]-d1-gauss-sigma': 1.750 ns,
'rx90m[0]-d1-gauss-t_final': 7.000 ns,
'rx90m[0]-d1-carrier-freq': 5.050 GHz 2pi,
'rx90m[0]-d1-carrier-framechange': 0.000 rad,
'ry90m[0]-d1-gauss-amp': 450.000 mV,
'ry90m[0]-d1-gauss-delta': -1.000 ,
'ry90m[0]-d1-gauss-freq_offset': -50.500 MHz 2pi,
'ry90m[0]-d1-gauss-xy_angle': 4.712 rad,
'ry90m[0]-d1-gauss-sigma': 1.750 ns,
'ry90m[0]-d1-gauss-t_final': 7.000 ns,
'ry90m[0]-d1-carrier-freq': 5.050 GHz 2pi,
'ry90m[0]-d1-carrier-framechange': 0.000 rad}
To access a specific parameter, e.g. the frequency of qubit 1, we use
the identifying tuple ('Q1','freq')
.
pmap.get_parameter(('Q1','freq'))
5.000 GHz 2pi
The opt_map¶
To deal with multiple parameters we use the opt_map
, a nested list
of identifyers.
opt_map = [
[
("Q1", "freq")
],
[
("Q1", "anhar")
],
]
Here, we get a list of the parameter values:
pmap.get_parameters(opt_map)
[5.000 GHz 2pi, -210.000 MHz 2pi]
Let’s look at the amplitude values of two gaussian control pulses, rotations about the \(X\) and \(Y\) axes repsectively.
opt_map = [
[
('rx90p[0]','d1','gauss','amp')
],
[
('ry90p[0]','d1','gauss','amp')
],
]
pmap.get_parameters(opt_map)
[450.000 mV, 450.000 mV]
We can set the parameters to new values.
pmap.set_parameters([0.5, 0.6], opt_map)
pmap.get_parameters(opt_map)
[500.000 mV, 600.000 mV]
The opt_map also allows us to specify that two parameters should have identical values. Here, let’s demand our \(X\) and \(Y\) rotations use the same amplitude.
opt_map_ident = [
[
('rx90p[0]','d1','gauss','amp'),
('ry90p[0]','d1','gauss','amp')
],
]
The grouping here means that these parameters share their numerical value.
pmap.set_parameters([0.432], opt_map_ident)
pmap.get_parameters(opt_map_ident)
[432.000 mV]
pmap.get_parameters(opt_map)
[432.000 mV, 432.000 mV]
During an optimization, the varied parameters do not change, so we fix the opt_map
pmap.set_opt_map(opt_map)
pmap.get_parameters()
[432.000 mV, 432.000 mV]
Optimizer scaling¶
To be independent of the choice of numerical optimizer, they should use the methods
pmap.get_parameters_scaled()
array([-0.68, -0.68])
To provide values bound to \([-1, 1]\). Let’s set the parameters to their allowed minimum an maximum value with
pmap.set_parameters_scaled([1.0,-1.0])
pmap.get_parameters()
[600.000 mV, 400.000 mV]
As a safeguard, when setting values outside of the unit range, their physical values get looped back in the specified limits.
pmap.set_parameters_scaled([2.0, 3.0])
pmap.get_parameters()
[500.000 mV, 400.000 mV]
Storing and reading¶
For optimization purposes, we can store and load parameter values in HJSON format.
pmap.store_values("current_vals.c3log")
!cat current_vals.c3log
{
opt_map:
[
[
rx90p[0]-d1-gauss-amp
]
[
ry90p[0]-d1-gauss-amp
]
]
units:
[
V
V
]
optim_status:
{
params:
[
0.5
0.4000000059604645
]
}
}
pmap.load_values("current_vals.c3log")