Waveforms
Waveforms are essential ingredients for generating the Rydberg Hamiltonian. By controlling the waveforms of $\Omega$, $\Delta$, and $\phi$, one can prepare the ground states of certain target Hamiltonians and study their non-equilibrium dynamics. Bloqade supports several built-in waveforms and allows the users to specify waveforms by inputting functions. It also supports different operations of waveforms, such as waveform smoothening, composing, and more.
The generated waveforms can be directly used to build the time-dependent Hamiltonians. Please see the Hamiltonians section for more details.
Creating Waveforms
In Bloqade, the waveforms are defined as a Waveform
object, which is a composition of a callable object and a real number duration
:
BloqadeWaveforms.Waveform
— Typestruct Waveform
Type for waveforms. Waveform
s are defined as a function combined with a real number duration.
Fields
f
: a callable object.duration
: a real number defines the duration of this waveform; default unit isμs
.
Bloqade gives users the flexibility to specify general waveforms by inputting functions. The following code constructs a sinusoidal waveform with a time duration of 2 μs:
using Bloqade
using PythonCall
plt = pyimport("matplotlib.pyplot")
waveform = Waveform(t->2.2*2π*sin(2π*t), duration = 2);
Bloqade.plot(waveform)
In our documentation, we use the python package matplotlib
for plotting.
Bloqade supports built-in waveforms for convenience (see References below). For example, the codes below create different waveform shapes with a single line:
waveform = piecewise_linear(clocks=[0.0, 0.2, 0.5, 0.8, 1.0], values= 2π* [0.0, 1.5, 3.1, 3.1, 0.0]);
Bloqade.plot(waveform)
waveform = piecewise_constant(clocks=[0.0, 0.2, 0.5, 0.7], values= 2π*[0.0, 1.5, 3.1]);
Bloqade.plot(waveform)
waveform = linear_ramp(duration=0.5, start_value=0.0, stop_value=2π*1.0);
Bloqade.plot(waveform)
waveform = constant(duration=0.5, value=2π*2.1);
Bloqade.plot(waveform)
waveform = sinusoidal(duration=2, amplitude=2π*2.2);
Bloqade.plot(waveform)
In some cases, users may have their own waveforms specified by a vector of clocks and a vector of signal strengths. To build a waveform from the two vectors, we can directly use the functions piecewise_linear
or piecewise_constant
, corresponding to different interpolations.
clocks = collect(0:1e-1:2);
values1 = 2π*rand(length(clocks));
wf1 = piecewise_linear(;clocks, values=values1);
values2 = 2π*rand(length(clocks)-1)
wf2 = piecewise_constant(;clocks, values=values2);
fig, (ax1, ax2) = plt.subplots(figsize=(12, 4), ncols=2)
Bloqade.plot!(ax1, wf1)
Bloqade.plot!(ax2, wf2)
fig
For more advanced interpolation options, please see the JuliaMath/Interpolations package.
Operations on Waveforms
Bloqade also supports several operations on the waveforms.
Waveforms can be sliced using the duration syntax start..stop
, e.g.:
wf = 2π*sinusoidal(duration=2.2);
wf1 = wf[1.1..1.5];
Bloqade.plot(wf1)
Note that time starts from 0.0
again, so the total duration is stop - start
.
Waveforms can be composed together via append
:
wf2 = linear_ramp(;start_value=0.0, stop_value=1.1*2π, duration=0.5);
waveform = append(wf1, wf2);
Bloqade.plot(waveform)
where the waveform wf2
is appended at the end of wf1
.
Sharp points in waveforms may result in bad performance in practice (e.g. for adiabatically preparing a ground state of a target Hamiltonian). It is sometimes preferred to smoothen the waveform using the moving average methods. One can use the smooth
function to create a smoothened waveform from a piecewise linear waveform:
wf = piecewise_linear(clocks=[0.0, 2.0, 3.0, 4.0], values=2π*[0.0, 3.0, 1.1, 2.2]);
swf = smooth(wf;kernel_radius=0.1);
fig, (ax1, ax2) = plt.subplots(figsize=(12, 4), ncols=2)
Bloqade.plot!(ax1, wf)
Bloqade.plot!(ax2, swf)
fig
Waveform Arithmetics
Bloqade also supports several arithmetics on the waveforms. If two waveforms have the same duration, we can directly add up or subtract the strength of them, simply by using +
or -
:
wf1 = linear_ramp(;duration=2.2, start_value=0.0, stop_value=1.0*2π);
wf2 = sinusoidal(duration = 2.2, amplitude = 2.2*2π);
wf3 = wf1 + wf2;
wf4 = wf1 - wf2;
fig, (ax1, ax2) = plt.subplots(figsize=(12, 4), ncols=2)
Bloqade.plot!(ax1, wf3)
Bloqade.plot!(ax2, wf4)
fig
To increase the strength of a waveform by some factors, we can directly use *
:
wf = linear_ramp(;duration=2.2, start_value=0.0, stop_value=1.0*2π);
wf_t = 3 * wf;
fig, (ax1, ax2) = plt.subplots(figsize=(12, 4), ncols=2)
Bloqade.plot!(ax1, wf)
Bloqade.plot!(ax2, wf_t)
fig
Such operations can also be broadcasted by using .*
:
wf2, wf3 = [2.0, 3.0] .* wf1;
fig, (ax1, ax2) = plt.subplots(figsize=(12, 4), ncols=2)
Bloqade.plot!(ax1, wf2)
Bloqade.plot!(ax2, wf3)
fig
References
BloqadeWaveforms.piecewise_linear
— Functionpiecewise_linear(;clocks, values)
Create a piecewise linear waveform.
Keyword Arguments
clocks::Vector{<:Real}
: the list of clocks for the corresponding values.values::Vector{<:Real}
: the list of values at each clock.
Example
julia> piecewise_linear(clocks=[0.0, 2.0, 3.0, 4.0], values=[0.0, 2.0, 2.0, 0.0])
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Waveform{_, Float64}⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
┌────────────────────────────────────────┐
2 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢧⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⡀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀│
value (2π ⋅ MHz) │⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⡴⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⢀⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀⠀│
│⠀⠀⠀⠀⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⠀│
│⠀⠀⠀⠀⡴⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⡀⠀│
│⠀⠀⢀⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀│
│⠀⣠⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⡆│
0 │⡴⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱│
└────────────────────────────────────────┘
⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀clock (μs)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀4⠀
BloqadeWaveforms.piecewise_constant
— Functionpiecewise_constant(;clocks, values, duration=last(clocks))
Create a piecewise constant waveform.
Keyword Arguments
clocks::Vector{<:Real}
: the list of clocks for the corresponding values.values::Vector{<:Real}
: the list of values at each clock.duration::Real
: the duration of the entire waveform, default is the last clock.
Example
julia> piecewise_constant(clocks=[0.0, 0.2, 0.5], values=[0.0, 1.5, 3.1], duration=1.1)
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Waveform{_, Float64}⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
┌────────────────────────────────────────┐
4 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
value (2π ⋅ MHz) │⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⢠⠒⠒⠒⠒⠒⠚⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
0 │⣀⣀⣀⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
└────────────────────────────────────────┘
⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀clock (μs)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀
BloqadeWaveforms.linear_ramp
— Functionlinear_ramp(;duration, start_value, stop_value)
Create a linear ramp waveform.
Keyword Arguments
duration::Real
: duration of the whole waveform.start_value::Real
: start value of the linear ramp.stop_value::Real
: stop value of the linear ramp.
Example
julia> linear_ramp(;duration=2.2, start_value=0.0, stop_value=1.0)
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Waveform{_, Float64}⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
┌────────────────────────────────────────┐
1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
value (2π ⋅ MHz) │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⠀⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
│⠀⢀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
0 │⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
└────────────────────────────────────────┘
⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀clock (μs)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀3⠀
BloqadeWaveforms.constant
— Functionconstant(;duration::Real, value::Real)
Create a constant waveform.
Keyword Arguments
duration::Real
: duration of the whole waveform.value::Real
: value of the constant waveform.
BloqadeWaveforms.sinusoidal
— Functionsinusoidal(;duration::Real, amplitude::Real=one(start))
Create a sinusoidal waveform of the following expression.
amplitude * sin(2π*t)
Keyword Arguments
duration
: duration of the waveform.amplitude
: amplitude of the sin waveform.
BloqadeWaveforms.append
— Functionappend(wf::Waveform, wfs::Waveform...)
Append other waveforms to wf
on time axis.
BloqadeWaveforms.smooth
— Functionsmooth([kernel=Kernel.gaussian], f; edge_pad_size::Int=length(f.clocks))
Kernel smoother function for piece-wise linear function/waveform via weighted moving average method.
Arguments
kernel
: the kernel function, default isKernels.gaussian
.f
: aUnion{PiecewiseLinear, PiecewiseConstant}
function or aWaveform{<:Union{PiecewiseLinear, PiecewiseConstant}}
.
Keyword Arguments
kernel_radius
: radius of the kernel.edge_pad_size
: the size of edge padding.
BloqadeWaveforms.smooth
— Methodsmooth(kernel, Xi::Vector, Yi::Vector, kernel_radius::Real)
Kernel smoother function via weighted moving average method. See also Kernel Smoother.
Theory
Kernel function smoothing is a technique to define a smooth function $f: \mathcal{R}^p → \mathbf{R}$ from a set of discrete points by weighted averaging the neighboring points. It can be written as the following equation.
\[Ŷ(X) = \sum_i K(X, X_i) Y_i / \sum_i K(X, X_i)\]
where $Ŷ(X)$ is the smooth function by calculating the moving average of known data points $X_i$ and $Y_i$. K
is the kernel function, where $K(\frac{||X - X_i||}{h_λ})$ decrease when the Euclidean norm $||X - X_i||$ increase, $h_λ$ is a parameter controls the radius of the kernel.
Available Kernels
The following kernel functions are available via the Kernels
module:
biweight
; cosine
; gaussian
; include
; logistic
; parabolic
; sigmoid
; triangle
; tricube
; triweight
; uniform
Arguments
kernel
: a Julia function that has methodkernel(t::Real)
.Xi::Vector
: a list of inputsX_i
.Yi::Vector
: a list of outputsY_i
.kernel_radius::Real
: the radius of the kernel.