Defining custom waveforms
In this page, you will learn how to:
- subclass
Waveformto define a custom pulse shape, - pass and access extra parameters through the base class,
- override the
functionmethod to implement the waveform evaluation, - optionally override
maxandminfor analytical bounds, - compose custom waveforms with built-in ones,
- use a custom waveform as part of a
Drive.
In the waveforms page you saw the usage of the pre-defined waveforms available in QoolQit. The waveform system is designed to be easily extended by subclassing the Waveform class and defining a small number of key properties and methods.
Subclassing Waveform
Section titled “Subclassing Waveform”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.shiftA few things are crucial in the snippet above:
- Keeping the
durationargument 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 aparamsdictionary of extra parameters, and set them as attributes to be used later. - Overriding the
functionabstract method, which represents the evaluation of the waveform at some timet. - Optional: overriding the
maxandminmethods. The intended result ofwf.max()andwf.min()is to get the maximum/minimum value the waveform takes over its duration. By default, the baseWaveformclass 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 overridden. - Internally, before being executed by an emulator or a QPU, custom defined waveforms will be converted to an
Interpolatedwaveform with a maximum of 100 points. If you need a finer time resolution, please, consider using directly anInterpolatedwaveform. port fig_to_html # markdown-exec: hide
More examples
Section titled “More examples”Smooth pulse
Section titled “Smooth pulse”A profile shaped by starts and ends at zero and reaches at , with smooth derivatives everywhere:
import mathfrom qoolqit.waveforms import Waveform
class SmoothPulse(Waveform): """Smooth bell-shaped pulse: Ω_max · sin²((π/2)·sin(πt/T))."""
def __init__(self, duration: float, omega_max: float) -> None: super().__init__(duration, omega_max=omega_max)
def function(self, t: float) -> float: return self.omega_max * math.sin(0.5 * math.pi * math.sin(math.pi * t / self.duration)) ** 2
def max(self) -> float: return self.omega_max # analytic maximum is always omega_max
def min(self) -> float: return 0.0 # starts and ends at zero
pulse = SmoothPulse(2 * math.pi, omega_max=1.0)0.00 ≤ t ≤ 6.28: SmoothPulse(t, 1.00)pulse.draw()Using a custom waveform in a Drive
Section titled “Using a custom waveform in a Drive”import mathfrom qoolqit.waveforms import Waveformfrom qoolqit import Ramp, Drive
class SmoothPulse(Waveform): def __init__(self, duration: float, omega_max: float) -> None: super().__init__(duration, omega_max=omega_max)
def function(self, t: float) -> float: return self.omega_max * math.sin(0.5 * math.pi * math.sin(math.pi * t / self.duration)) ** 2
def max(self) -> float: return self.omega_max
def min(self) -> float: return 0.0
T = 2 * math.piamplitude = SmoothPulse(T, omega_max=1.0)detuning = Ramp(T, -1.0, 1.0)
drive = Drive(amplitude=amplitude, detuning=detuning)Amplitude:0.00 ≤ t ≤ 6.28: SmoothPulse(t, 1.00)
Detuning:0.00 ≤ t ≤ 6.28: Ramp(t, -1.00, 1.00)drive.draw()