Arbitrary waveform generator¶
This notebook demonstrates how to create a simple signal chain consisting of an arbitrary waveform generator (AWG), which represents the most minimalistic control stack. The AWG is an indispensable element, enabling precise control over the shape and amplitude of the desired output pulse. Such control is essential for performing high-fidelity gates on qubits.
The qruise-toolset
provides several pre-defined source signals, such as a Gaussian, a local oscillator, and a Heaviside (step) function. In this example, we'll construct a Gaussian source signal.
The tutorial consists of the following:
- Defining parameters
- Constructing the signal chain
- Visualising the Gaussian AWG signal
Tip: Make sure that your environment is correctly set to handle float64
precision by setting JAX_ENABLE_X64=True
or add
import jax
jax.config.update("jax_enable_x64", True)
to your scripts' preamble.
1. Defining parameters¶
We start by defining the time parameters for our pulse. These include the start and end times of the pulse ($t_0$ and $t_\text{final}$, respectively). We then define a time span consisting of $N$ evenly spaced points between $t_0$ and $t_\text{final}$.
import jax.numpy as jnp
t0 = 0.0 # start time of pulse (s)
t_final = 20e-9 # end (final) time of pulse (s)
N = int(1e3) # number of time points
t_span = jnp.linspace(t0, t_final, N) # defines time span array
We also need to define the parameters of the Gaussian function, $G(t)$, which in the qruise-toolset
takes the generic form:
$$ G(t; a, \sigma, \mu) = \frac{a}{\sigma\sqrt{2\pi}} \mathrm{exp}\left[-\frac{(t - \mu)^2}{2\sigma^2}\right].$$
This is characterised by three parameters:
$a$, a scalar that adjusts the amplitude of the pulse
$\sigma$, the pulse variance
$\mu$, the time at which it reaches its maximum amplitude
For this example, we'll set $a = \sigma \sqrt{2\pi}$ and $\mu=\frac{t_\text{final}}{2}$, so that the Gaussian function reaches a peak value of 1.0 in the centre of the simulation time window. We arbitrarily define $\sigma = \frac{1}{4} t_\text{final}$.
sigma = 0.25 * t_final # pulse variance
mean = t_final / 2.0 # mu, set to centre of simulation time window
amplitude = sigma * jnp.sqrt(2.0 * jnp.pi) # a, set so peak value = 1.0
2. Constructing the signal chain¶
As mentioned above, the signal chain will consist of a single component. We start by storing the parameters of the Gaussian function in a dictionary (gauss_params
), and then we define an instance of the Gaussian()
class. We then create a Signal()
chain and add our Gaussian instance to it as an AWG signal.
from qruise.toolset import Gaussian, Signal
# store Gaussian parameters in dictionary
gauss_params = {
"amplitude": (amplitude, True),
"mean": (mean, True),
"sigma": (sigma, True),
}
gauss = Gaussian() # define instance of Gaussian class
# create signal chain named "GaussianAWG"
signal = Signal("GaussianAWG")
# add Gaussian as component named "AWG"
signal.add_from(["AWG"], [gauss()], [gauss_params])
Note: The argument True
in the gauss_params
dictionary indicates that the parameter is modifiable (e.g., for optimisation), but this is not relevant for this tutorial.
Now we need to evaluate the signal chain over the time span we defined earlier (t_span
).
# extract signal chain function and parameter dictionary
signal_chain, signal_params = signal.function()
# evaluate signal over defined time span
awg_result = signal_chain(t_span, signal_params)
3. Visualising the Gaussian AWG signal¶
Let's plot our AWG signal to see if it looks as we'd expect. For this we'll use the PlotUtil
module from the qruise-toolset
.
from qruise.toolset import PlotUtil
canvas = PlotUtil(x_axis_label="t [s]", y_axis_label="Amplitude [a.u.]", notebook=True)
canvas.plot(t_span, awg_result, labels=["Gaussian AWG"])
canvas.show_canvas()
Nice! Our signal looks as we'd expect, and now you know how to create a signal chain containing an AWG.