Pulses
Pulses in Piccolo.jl parameterize how control signals vary over time. The choice of pulse type affects both the optimization problem structure and the resulting control smoothness.
Overview
| Pulse Type | Description | Use With |
|---|---|---|
ZeroOrderPulse | Piecewise constant | SmoothPulseProblem |
LinearSplinePulse | Linear interpolation | SplinePulseProblem |
CubicSplinePulse | Cubic Hermite splines | SplinePulseProblem |
GaussianPulse | Parametric Gaussian | Analytical evaluation |
CompositePulse | Combination of pulses | Various |
ZeroOrderPulse
Piecewise constant (zero-order hold) controls. The most common choice for initial optimization.
Construction
using Piccolo
n_dr = 2
N = 100
T = 10.0
# Time points
times = collect(range(0, T, length = N))
# Control values: n_drives × N matrix
controls = 0.1 * randn(n_dr, N)
pulse_zop = ZeroOrderPulse(controls, times)ZeroOrderPulse{DataInterpolations.ConstantInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Float64}}(DataInterpolations.ConstantInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Float64}([-0.060146131100994864 -0.047992691017512225 … 0.05893789810987802 0.26367027649393154; 0.16549540383962968 -0.15227029372710413 … -0.048301245927034525 -0.09116904627749467], [0.0, 0.10101010101010101, 0.20202020202020202, 0.30303030303030304, 0.40404040404040403, 0.5050505050505051, 0.6060606060606061, 0.7070707070707071, 0.8080808080808081, 0.9090909090909091 … 9.090909090909092, 9.191919191919192, 9.292929292929292, 9.393939393939394, 9.494949494949495, 9.595959595959595, 9.696969696969697, 9.797979797979798, 9.8989898989899, 10.0], Union{}[], nothing, :left, DataInterpolations.ExtrapolationType.None, DataInterpolations.ExtrapolationType.None, FindFirstFunctions.Guesser{Vector{Float64}}([0.0, 0.10101010101010101, 0.20202020202020202, 0.30303030303030304, 0.40404040404040403, 0.5050505050505051, 0.6060606060606061, 0.7070707070707071, 0.8080808080808081, 0.9090909090909091 … 9.090909090909092, 9.191919191919192, 9.292929292929292, 9.393939393939394, 9.494949494949495, 9.595959595959595, 9.696969696969697, 9.797979797979798, 9.8989898989899, 10.0], Base.RefValue{Int64}(1), true), false, true), 10.0, 2, :u, [0.0, 0.0], [0.0, 0.0])Properties
# Evaluate at time t
u = pulse_zop(T / 2)
u
# Duration
duration(pulse_zop)
# Number of drives
n_drives(pulse_zop)2Visualization
Control Value
│ ┌──┐
│ │ │ ┌──┐
│────┘ │ │ └───
│ └──┘
└─────────────────── TimeUse Case
- Primary use:
SmoothPulseProblem - Characteristics: Simple structure, smoothness via derivative regularization
- Best for: Initial optimization, most quantum control problems
LinearSplinePulse
Linear interpolation between control knots.
Construction
pulse_linear = LinearSplinePulse(controls, times)LinearSplinePulse{DataInterpolations.LinearInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Vector{Vector{Float64}}, Float64}}(DataInterpolations.LinearInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Vector{Vector{Float64}}, Float64}([-0.060146131100994864 -0.047992691017512225 … 0.05893789810987802 0.26367027649393154; 0.16549540383962968 -0.15227029372710413 … -0.048301245927034525 -0.09116904627749467], [0.0, 0.10101010101010101, 0.20202020202020202, 0.30303030303030304, 0.40404040404040403, 0.5050505050505051, 0.6060606060606061, 0.7070707070707071, 0.8080808080808081, 0.9090909090909091 … 9.090909090909092, 9.191919191919192, 9.292929292929292, 9.393939393939394, 9.494949494949495, 9.595959595959595, 9.696969696969697, 9.797979797979798, 9.8989898989899, 10.0], Union{}[], DataInterpolations.LinearParameterCache{Vector{Vector{Float64}}}(Vector{Float64}[]), DataInterpolations.ExtrapolationType.None, DataInterpolations.ExtrapolationType.None, FindFirstFunctions.Guesser{Vector{Float64}}([0.0, 0.10101010101010101, 0.20202020202020202, 0.30303030303030304, 0.40404040404040403, 0.5050505050505051, 0.6060606060606061, 0.7070707070707071, 0.8080808080808081, 0.9090909090909091 … 9.090909090909092, 9.191919191919192, 9.292929292929292, 9.393939393939394, 9.494949494949495, 9.595959595959595, 9.696969696969697, 9.797979797979798, 9.8989898989899, 10.0], Base.RefValue{Int64}(1), true), false, true), 10.0, 2, :u, [0.0, 0.0], [0.0, 0.0])Properties
- Continuous control values
- Discontinuous first derivative (at knots)
- Derivative = slope between knots
u_linear = pulse_linear(T / 2)
u_linear2-element Vector{Float64}:
-0.05563346685587807
0.07406621275850972Visualization
Control Value
│ /\
│ / \ /
│ / \ /
│───/ \/
└─────────────────── TimeCubicSplinePulse
Cubic Hermite spline interpolation with independent tangents at each knot.
Construction
tangents = zeros(n_dr, N) # Initial tangents (slopes)
pulse_cubic = CubicSplinePulse(controls, tangents, times)CubicSplinePulse{DataInterpolations.CubicHermiteSpline{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Matrix{Float64}, Vector{Vector{Float64}}, Float64}}(DataInterpolations.CubicHermiteSpline{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Matrix{Float64}, Vector{Vector{Float64}}, Float64}([0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0], [-0.060146131100994864 -0.047992691017512225 … 0.05893789810987802 0.26367027649393154; 0.16549540383962968 -0.15227029372710413 … -0.048301245927034525 -0.09116904627749467], [0.0, 0.10101010101010101, 0.20202020202020202, 0.30303030303030304, 0.40404040404040403, 0.5050505050505051, 0.6060606060606061, 0.7070707070707071, 0.8080808080808081, 0.9090909090909091 … 9.090909090909092, 9.191919191919192, 9.292929292929292, 9.393939393939394, 9.494949494949495, 9.595959595959595, 9.696969696969697, 9.797979797979798, 9.8989898989899, 10.0], Union{}[], DataInterpolations.CubicHermiteParameterCache{Vector{Vector{Float64}}}(Vector{Float64}[], Vector{Float64}[]), DataInterpolations.ExtrapolationType.None, DataInterpolations.ExtrapolationType.None, FindFirstFunctions.Guesser{Vector{Float64}}([0.0, 0.10101010101010101, 0.20202020202020202, 0.30303030303030304, 0.40404040404040403, 0.5050505050505051, 0.6060606060606061, 0.7070707070707071, 0.8080808080808081, 0.9090909090909091 … 9.090909090909092, 9.191919191919192, 9.292929292929292, 9.393939393939394, 9.494949494949495, 9.595959595959595, 9.696969696969697, 9.797979797979798, 9.8989898989899, 10.0], Base.RefValue{Int64}(1), true), false, true), 10.0, 2, :u, [0.0, 0.0], [0.0, 0.0])Properties
- Continuous control values AND first derivatives
- Tangents are independent optimization variables
- Smooth C¹ continuous curves
u_cubic = pulse_cubic(T / 2)
u_cubic2-element Vector{Float64}:
-0.05563346685587807
0.07406621275850972GaussianPulse
Parametric Gaussian envelope with analytical form.
Construction
amplitudes = [0.5, 0.3]
sigmas = [1.0, 1.5]
centers = [5.0, 5.0]
pulse_gauss = GaussianPulse(amplitudes, sigmas, centers, T)GaussianPulse{Piccolo.Quantum.Pulses.var"#GaussianPulse##0#GaussianPulse##1"{Vector{Float64}, Vector{Float64}, Vector{Float64}, Int64}}(Piccolo.Quantum.Pulses.var"#GaussianPulse##0#GaussianPulse##1"{Vector{Float64}, Vector{Float64}, Vector{Float64}, Int64}([5.0, 5.0], [1.0, 1.5], [0.5, 0.3], 2), [0.5, 0.3], [1.0, 1.5], [5.0, 5.0], 10.0, 2)Mathematical Form
\[u_i(t) = A_i \exp\left(-\frac{(t - \mu_i)^2}{2\sigma_i^2}\right)\]
u_gauss = pulse_gauss(5.0)
u_gauss2-element Vector{Float64}:
0.5
0.3CompositePulse
Combine multiple pulses.
Construction
pulse1 = GaussianPulse([0.5], [0.5], [2.0], T)
pulse2 = GaussianPulse([0.3], [0.5], [8.0], T)
# Interleave the pulses (each pulse contributes different drives)
composite = CompositePulse([pulse1, pulse2])
n_drives(composite)2Choosing a Pulse Type
Decision Guide
Start
│
▼
Is this your first optimization attempt?
│
├── Yes → ZeroOrderPulse + SmoothPulseProblem
│
└── No → Do you have a previous solution?
│
├── Yes → CubicSplinePulse + SplinePulseProblem (warm-start)
│
└── No → Do you need smooth pulses?
│
├── Yes → CubicSplinePulse
│
└── No → ZeroOrderPulsePractical Recommendations
| Scenario | Recommended Pulse |
|---|---|
| Starting fresh | ZeroOrderPulse |
| Refining a solution | CubicSplinePulse |
| Hardware requires smooth pulses | CubicSplinePulse |
| Simple continuous pulses | LinearSplinePulse |
| Analytical pulse design | GaussianPulse |
Converting Between Pulse Types
ZeroOrderPulse → CubicSplinePulse
# Sample control values from the zero-order pulse
ctrl = hcat([pulse_zop(t) for t in times]...)
# Estimate tangents (finite differences)
tgts = similar(ctrl)
for k = 1:(N-1)
tgts[:, k] = (ctrl[:, k+1] - ctrl[:, k]) / (times[k+1] - times[k])
end
tgts[:, N] = tgts[:, N-1]
# Create cubic spline
cubic_from_zop = CubicSplinePulse(ctrl, tgts, times)
duration(cubic_from_zop)10.0Arbitrary → ZeroOrderPulse
# Sample any pulse type to create a ZeroOrderPulse
new_times = collect(range(0, T, length = 200))
new_ctrl = hcat([pulse_cubic(t) for t in new_times]...)
resampled = ZeroOrderPulse(new_ctrl, new_times)
length(new_times)200Best Practices
1. Initialize Appropriately
# Scale by drive bounds
max_amp = 0.1 * maximum(drive_bounds)
controls = max_amp * randn(n_drives, N)2. Use Enough Time Points
# Rule of thumb: ~10 points per characteristic time scale
T = 10.0 # Total time
τ = 1.0 # Shortest feature you want to capture
N = ceil(Int, 10 * T / τ)3. Start with ZeroOrderPulse
Even if you need smooth pulses, optimize with ZeroOrderPulse first, then convert to CubicSplinePulse for refinement.
See Also
- SmoothPulseProblem - Using
ZeroOrderPulse - SplinePulseProblem - Using spline pulses
- Trajectories - Combining pulses with systems
This page was generated using Literate.jl.