Writing time-dependent functions
An essential part of writing programs in the Rydberg analog model is to write the time-dependent functions representing the amplitude and detuning terms in the drive Hamiltonian. For that, QoolQit implements a set of waveforms that can be used directly and/or composed together.
Base waveforms
A full list of the available waveforms can be found in the API reference.
from qoolqit import Constant, Ramp, Delay
# An empty waveform
wf1 = Delay(1.0)
# A waveform with a constant value
wf2 = Constant(1.0, 2.0)
# A waveform that ramps linearly between two values
wf3 = Ramp(1.0, -1.0, 1.0)
As shown above, printing a waveform shows the duration interval over which it applies followed by the description of the waveform.
The first argument is always the duration
of the waveform, and the remaining arguments depend on the information required by each waveform. The resulting object is a callable that can be evaluated at any time \(t\).
Each waveform also supports evaluation at multiple time steps by calling it on an array of times.
In the waveform above, we defined it with a duration of \(1.0\), and then evaluated it over nine points from \(t = 0.0\) to \(t=2.0\). As you can see, all points after \(t = 1.0\) evaluated to \(0.0\). By default, any waveform evaluated at a time \(t\) that falls outside the specified duration
gives \(0.0\).
Waveforms can be quickly drawn with the draw()
method.
Composite waveforms
The most straightforward way to arbitrarily compose waveforms is to use the >>
operator. This will create a CompositeWaveform
representing the waveforms in the order provided.
The code above is equivalent to calling CompositeWaveform(wf1, wf2, wf3)
. As shown, printing the composite waveform will automatically show the individual waveforms in the composition and the times at which they are active. These are automatically calculated from the individual waveforms. A
CompositeWaveform
is by itself a subclass of Waveform
, and thus the previous logic on calling it at arbitrary time values also applies.
A few convenient properties are directly available in a composite waveform:
# Total duration
wf_comp.duration
# List of durations of the individual waveforms
wf_comp.durations
# List of times where each individual waveform starts / ends
wf_comp.times
A custom waveform can directly be a CompositeWaveform
. That is the case with the PiecewiseLinear
waveform, which takes a list of durations (of size \(N\)) and a list of values (of size \(N+1\)) and creates a linear interpolation between all values using individual waveforms of type Ramp
.
from qoolqit import PiecewiseLinear
durations = [1.0, 1.0, 2.0]
values = [0.0, 1.0, 0.5, 0.5]
wf_pwl = PiecewiseLinear(durations, values)
wf_pwl.draw()
Defining custom waveforms
The waveform system of QoolQit can be easily extended by subclassing the Waveform
class and defining some key properties and methods. To exemplify this we will create a waveform representing a simple shifted sine function,
from qoolqit.waveforms import Waveform
import math
class Sin(Waveform):
"""A simple sine over a given duration.
Arguments:
duration: the total duration.
omega: the frequency of the sine wave.
shift: the vertical shift of the sine wave.
"""
def __init__(
self,
duration: float,
omega: float = 2.0 * math.pi,
shift: float = 0.0,
) -> None:
super().__init__(duration, omega = omega, shift = shift)
def function(self, t: float) -> float:
return math.sin(self.omega * t) + self.shift
A few things are crucial in the snippet above:
- Keeping the
duration
argument as the first one in the__init__
, and initializing the parent class with that value, to be consistent with other waveforms. - Passing every other parameter needed for the waveform in the
__init__
and passing it as a keyword argument to the parent class. This will automatically create aparams
dictionary of extra parameters, and set them as attributes to be used later. - Overriding the
function
abstract method, which represents the evaluation of the waveform at some timet
. - Optional: overriding the
max
andmin
methods. The intended result ofwf.max()
andwf.min()
is to get the maximum/minimum value the waveform takes over its duration. By default, the baseWaveform
class implements a brute-force sampling method that approximates the maximum and minimum values. However, if this value is easy to know from the waveform parameters, the method should be overriden.
To showcase the usage of the newly defined waveform, let's define a new sine waveform and compose it with a piecewise linear waveform.
from qoolqit import PiecewiseLinear
import math
wf1 = Sin(
duration = 1.0,
omega = 2.0 * math.pi,
shift = 1.0
)
wf2 = PiecewiseLinear(
durations = [0.5, 0.5],
values = [1.0, 1.0, 0.0],
)
wf_comp = wf1 >> wf2
Following this example, more complete Sin
waveform is directly available in QoolQit implementing
from qoolqit import Sin
wf = Sin(
duration = 1.0,
amplitude = 2.0,
omega = 6.0,
phi = -5.0,
shift = 1.0,
)
wf.max()