Handling parameters in ParaQeet

The optimisation map is a utility class that collects all parameters that shall be considered during optimisation and associates them with the corresponding Optimisable interface. With this class, Quantities can be traced back to the Optimisable to which they belong. Before optimisation, an instance of this class needs to be filled and passed to the optimiser.

from paraqeet.optimisation_map import OptimisationMap

from paraqeet.signal.iq_mixer import IQMixer
from paraqeet.signal.envelopes import ConstantEnvelope, FlatTopGaussianEnvelope

Example devices and their parameters

We define some signal generator and look at its parameters:

tone = ConstantEnvelope()
gen = IQMixer(envelopes=[tone])
params = gen.get_parameters()
params
[Amplitude: 24.7 MHz x 2pi,
 t_final: 32 ns,
 lo_freq: 4.8 GHz x 2pi,
 Phase: 0 rad]

The Optimisation Map

To handle the parameters of both tones, we make an OptimisationMap and add the parameters of the first tone explcitely.

optmap = OptimisationMap()
optmap.add(gen, params)

We can get a list output of all parameters with

optmap.get_all_parameters()
[Amplitude: 24.7 MHz x 2pi,
 t_final: 32 ns,
 lo_freq: 4.8 GHz x 2pi,
 Phase: 0 rad]

Or a human readalbe output to check that we didn’t make a mistake in configuration.

optmap
==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[Amplitude: 24.7 MHz x 2pi, t_final: 32 ns, lo_freq: 4.8 GHz x 2pi, Phase: 0 rad]

If we just want to optimise just the frequency, we set

optmap.add(gen, [params[2]])
optmap
==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[lo_freq: 4.8 GHz x 2pi]

Adding more devices

As a second drive, we create a signal shaped by an error function envelope:

tone2 = FlatTopGaussianEnvelope()
gen2 = IQMixer(envelopes=[tone2])

If we don’t specify an explicit list of parameters, all of them get added.

optmap.add(gen2)
optmap.get_all_parameters()
[lo_freq: 4.8 GHz x 2pi,
 Amplitude: 24.7 MHz x 2pi,
 t_final: 32 ns,
 lo_freq: 4.8 GHz x 2pi,
 Phase: 0 rad]
optmap
==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[lo_freq: 4.8 GHz x 2pi]

==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[Amplitude: 24.7 MHz x 2pi, t_final: 32 ns, lo_freq: 4.8 GHz x 2pi, Phase: 0 rad]

Selecting parameters

There’s a convenient filter method to select parameters based on properties. The following example selects all amplitudes:

def all_amplitudes(par):
    """Get the amplitudes."""
    return par.get_name() == "Amplitude"


optmap.filter_parameters(all_amplitudes)
optmap.get_all_parameters()
[Amplitude: 24.7 MHz x 2pi]
optmap
==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[Amplitude: 24.7 MHz x 2pi]

Adding back all parameters:

optmap.add(gen)
optmap.add(gen2)
optmap
==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[Amplitude: 24.7 MHz x 2pi, t_final: 32 ns, lo_freq: 4.8 GHz x 2pi, Phase: 0 rad]

==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[Amplitude: 24.7 MHz x 2pi, t_final: 32 ns, lo_freq: 4.8 GHz x 2pi, Phase: 0 rad]

Now, we select every parameter with unit “Hz”:

def hz_filter(par):
    """Get every parameter with the unit 'Hz'."""
    return par.get_unit() == "Hz"


optmap.filter_parameters(hz_filter)
optmap
==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[Amplitude: 24.7 MHz x 2pi, lo_freq: 4.8 GHz x 2pi]

==== <class 'paraqeet.signal.iq_mixer.IQMixer'> ====
[Amplitude: 24.7 MHz x 2pi, lo_freq: 4.8 GHz x 2pi]