Composing Templates
Problem templates in Piccolo.jl are designed to be composable. You can chain them together to build sophisticated optimization pipelines that combine multiple capabilities.
Composition Overview
The templates form a hierarchy:
Base Problems (create from trajectory):
├── SmoothPulseProblem
├── BangBangPulseProblem
└── SplinePulseProblem
│
▼
Wrapper Problems (wrap existing problem):
├── SamplingProblem (adds robustness)
└── MinimumTimeProblem (adds time optimization)Any wrapper can wrap another wrapper, enabling combinations like:
MinimumTimeProblem(SamplingProblem(SmoothPulseProblem(...)))MinimumTimeProblem(SplinePulseProblem(...))
Full Pipeline Example
Here's a complete pipeline: base optimization → robust optimization → time-optimal control.
using PiccoloStep 1: Setup
# Define nominal system
H_drift = 0.5 * PAULIS[:Z]
H_drives = [PAULIS[:X], PAULIS[:Y]]
sys_nominal = QuantumSystem(H_drift, H_drives, [1.0, 1.0])
# Create initial trajectory
T, N = 20.0, 100
times = collect(range(0, T, length = N))
pulse = ZeroOrderPulse(0.1 * randn(2, N), times)
qtraj = UnitaryTrajectory(sys_nominal, pulse, GATES[:X])UnitaryTrajectory{ZeroOrderPulse{DataInterpolations.ConstantInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Float64}}, SciMLBase.ODESolution{ComplexF64, 3, Vector{Matrix{ComplexF64}}, Nothing, Nothing, Vector{Float64}, Vector{Vector{Matrix{ComplexF64}}}, Nothing, SciMLBase.ODEProblem{Matrix{ComplexF64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, SciMLBase.ODEFunction{true, SciMLBase.AutoSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#53#54"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#55#56"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}, Vector{DriftTerm{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Returns{Float64}, Returns{Float64}}}, SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Piccolo.Quantum.Rollouts.PiccoloRolloutSystem{Union{Int64, AbstractVector{Int64}, CartesianIndex, CartesianIndices}}, Nothing, Nothing}, Base.Pairs{Symbol, Vector{Float64}, Nothing, @NamedTuple{tstops::Vector{Float64}}}, SciMLBase.StandardODEProblem}, OrdinaryDiffEqLinear.MagnusAdapt4, OrdinaryDiffEqCore.InterpolationData{SciMLBase.ODEFunction{true, SciMLBase.AutoSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#53#54"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#55#56"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}, Vector{DriftTerm{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Returns{Float64}, Returns{Float64}}}, SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, typeof(SciMLBase.DEFAULT_OBSERVED), Nothing, Piccolo.Quantum.Rollouts.PiccoloRolloutSystem{Union{Int64, AbstractVector{Int64}, CartesianIndex, CartesianIndices}}, Nothing, Nothing}, Vector{Matrix{ComplexF64}}, Vector{Float64}, Vector{Vector{Matrix{ComplexF64}}}, Nothing, OrdinaryDiffEqLinear.MagnusAdapt4Cache{Matrix{ComplexF64}, Matrix{ComplexF64}, Matrix{ComplexF64}, Matrix{ComplexF64}, Nothing}, Nothing}, SciMLBase.DEStats, Nothing, Nothing, Nothing, Nothing}}(QuantumSystem: levels = 2, n_drives = 2, ZeroOrderPulse(Number of drives = 2, T = 20.0), ComplexF64[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im], ComplexF64[0.0 + 0.0im 1.0 + 0.0im; 1.0 + 0.0im 0.0 + 0.0im], ComplexF64[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im;;; 0.9949908641865791 - 0.09983297298085714im 0.0013991200978624883 - 0.004959854231740058im; -0.001399120097862489 - 0.00495985423174006im 0.9949908641865789 + 0.09983297298085711im;;; 0.9798867132626343 - 0.1987377280285841im 0.018018704700881 - 0.0008190911371980083im; -0.018018704700881003 - 0.00081909113719801im 0.9798867132626345 + 0.19873772802858414im;;; … ;;; -0.8525249543266193 + 0.34934637043930183im 0.022308781537524667 + 0.388150272417244im; -0.022308781537525 + 0.38815027241724437im -0.8525249543266186 - 0.34934637043930167im;;; -0.8078207059964332 + 0.4243998944186706im 0.07518886305747637 + 0.402066003851305im; -0.07518886305747677 + 0.40206600385130553im -0.8078207059964327 - 0.42439989441867065im;;; -0.7687780993192775 + 0.4990666877845331im 0.12790691320550931 + 0.3788832230450973im; -0.12790691320550981 + 0.3788832230450978im -0.7687780993192772 - 0.4990666877845332im])Step 2: Base Problem (with free time enabled)
qcp_base = SmoothPulseProblem(
qtraj,
N;
Q = 100.0,
R = 1e-2,
Δt_bounds = (0.05, 0.5), ## Required for MinimumTimeProblem
)
solve!(qcp_base; max_iter = 100)
fidelity(qcp_base)0.989797705664722sum(get_timesteps(get_trajectory(qcp_base)))21.966699602348307Step 3: Add Robustness
# Create perturbed systems (±5% drift variation)
sys_high = QuantumSystem(1.05 * H_drift, H_drives, [1.0, 1.0])
sys_low = QuantumSystem(0.95 * H_drift, H_drives, [1.0, 1.0])
qcp_robust = SamplingProblem(qcp_base, [sys_nominal, sys_high, sys_low]; Q = 100.0)
solve!(qcp_robust; max_iter = 100)
fidelity(qcp_robust)3-element Vector{Float64}:
0.9999794169785005
0.9999917626760731
0.9999924228552589Step 4: Minimize Time
qcp_mintime = MinimumTimeProblem(qcp_robust; final_fidelity = 0.95, D = 100.0)
solve!(qcp_mintime; max_iter = 100)
fidelity(qcp_mintime)3-element Vector{Float64}:
0.9997574757773747
0.9973204700506145
0.9953739450195523sum(get_timesteps(get_trajectory(qcp_mintime)))13.125869584312529Common Composition Patterns
Pattern 1: Robust Gate
Optimize for parameter uncertainty without time constraints.
qcp_base = SmoothPulseProblem(qtraj, N; Q=100.0)
solve!(qcp_base)
qcp_robust = SamplingProblem(qcp_base, systems)
solve!(qcp_robust)Pattern 2: Fast Gate
Minimize time without robustness requirements.
qcp_base = SmoothPulseProblem(qtraj, N; Q=100.0, Δt_bounds=(0.01, 0.5))
solve!(qcp_base)
qcp_fast = MinimumTimeProblem(qcp_base; final_fidelity=0.99)
solve!(qcp_fast)Pattern 3: Fast + Robust Gate
The full pipeline for production-quality gates.
qcp_base = SmoothPulseProblem(qtraj, N; Q=100.0, Δt_bounds=(0.01, 0.5))
solve!(qcp_base)
qcp_robust = SamplingProblem(qcp_base, systems)
solve!(qcp_robust)
qcp_final = MinimumTimeProblem(qcp_robust; final_fidelity=0.95)
solve!(qcp_final)Pattern 4: Spline Warm-Start Pipeline
Start with smooth problem, refine with splines.
# Initial optimization with piecewise constant
qcp_smooth = SmoothPulseProblem(qtraj_smooth, N; Q=100.0)
solve!(qcp_smooth)
# Extract optimized pulse and convert to spline
optimized_pulse = get_pulse(qcp_smooth.qtraj)
spline_pulse = CubicSplinePulse(optimized_pulse)
qtraj_spline = UnitaryTrajectory(sys, spline_pulse, U_goal)
# Refine with spline problem
qcp_spline = SplinePulseProblem(qtraj_spline; Q=100.0)
solve!(qcp_spline; max_iter=50) # Quick refinementIteration and Refinement
You can iteratively refine solutions:
# First pass: coarse optimization
qcp = SmoothPulseProblem(qtraj, 50; Q=10.0)
solve!(qcp; max_iter=50)
# Second pass: increase resolution
# ... resample to higher N ...
# Third pass: tighten tolerances
solve!(qcp; max_iter=100, tol=1e-8)Accessing Results Through the Chain
Each wrapper preserves access to the underlying trajectory:
# Access trajectory at any level
traj = get_trajectory(qcp_mintime)
sys = get_system(qcp_mintime)
# Fidelity evaluation uses the innermost trajectory type
fid = fidelity(qcp_mintime)
# Get optimized pulse
pulse = get_pulse(qcp_mintime.qtraj)Order Matters
The order of composition affects the optimization:
MinimumTimeProblem(SamplingProblem(base)):
- First achieves robustness, then minimizes time
- Time minimization respects the robust solution
- Generally preferred for production gates
SamplingProblem(MinimumTimeProblem(base)):
- First minimizes time, then adds robustness
- May require re-solving if time-optimal solution isn't robust
- Less common, but useful for exploring trade-offs
Tips for Complex Pipelines
1. Solve Each Stage
Always solve! after each composition step:
qcp_base = SmoothPulseProblem(qtraj, N)
solve!(qcp_base) # Important!
qcp_robust = SamplingProblem(qcp_base, systems)
solve!(qcp_robust) # Important!
qcp_mintime = MinimumTimeProblem(qcp_robust)
solve!(qcp_mintime) # Important!2. Monitor Progress
Check fidelity at each stage to catch issues early:
for (name, prob) in [("Base", qcp_base), ("Robust", qcp_robust), ("MinTime", qcp_mintime)]
println("$name: Fidelity = $(fidelity(prob))")
endBase: Fidelity = 0.9997574757773747
Robust: Fidelity = [0.9997574757773747, 0.9973204700506145, 0.9953739450195523]
MinTime: Fidelity = [0.9997574757773747, 0.9973204700506145, 0.9953739450195523]3. Adjust Parameters at Each Stage
Different stages may need different settings:
# Base: prioritize finding a solution
qcp_base = SmoothPulseProblem(qtraj, N; Q=100.0, R=1e-2)
solve!(qcp_base; max_iter=200)
# Robust: may need more iterations
qcp_robust = SamplingProblem(qcp_base, systems; Q=100.0)
solve!(qcp_robust; max_iter=300)
# MinTime: typically faster since starting from good solution
qcp_mintime = MinimumTimeProblem(qcp_robust; final_fidelity=0.95, D=100.0)
solve!(qcp_mintime; max_iter=100)See Also
- SmoothPulseProblem - Base problem template
- BangBangPulseProblem - Bang-bang base problem
- SplinePulseProblem - Spline-based base problem
- MinimumTimeProblem - Time optimization wrapper
- SamplingProblem - Robustness wrapper
This page was generated using Literate.jl.