Custom Objectives

While Piccolo.jl provides standard objectives for fidelity and regularization, you may need custom objectives for specialized optimization goals. This guide shows how to create and use them.

Setup

We'll work with a simple single-qubit X gate problem:

using Piccolo
using DirectTrajOpt
using LinearAlgebra
using Random
Random.seed!(42)

# Define system
H_drift = 0.5 * PAULIS[:Z]
H_drives = [PAULIS[:X], PAULIS[:Y]]
drive_bounds = [1.0, 1.0]
sys = QuantumSystem(H_drift, H_drives, drive_bounds)

# Create initial pulse and trajectory
T = 10.0
N = 50
times = collect(range(0, T, length = N))
initial_controls = 0.1 * randn(2, N)
pulse = ZeroOrderPulse(initial_controls, times)

U_goal = GATES[:X]
qtraj = UnitaryTrajectory(sys, pulse, U_goal)
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.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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}, saveat::Vector{Float64}}}, SciMLBase.StandardODEProblem}, OrdinaryDiffEqLinear.MagnusGL4, OrdinaryDiffEqCore.InterpolationData{SciMLBase.ODEFunction{true, SciMLBase.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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.MagnusGL4Cache{Matrix{ComplexF64}, Matrix{ComplexF64}, Matrix{ComplexF64}, Nothing}, Nothing}, SciMLBase.DEStats, Nothing, Nothing, Nothing, Nothing}, Matrix{ComplexF64}}(QuantumSystem: levels = 2, n_drives = 2, ZeroOrderPulse{DataInterpolations.ConstantInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Float64}}(DataInterpolations.ConstantInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Float64}([-0.03633574814517775 -0.031498797116895606 … -0.020661311448119266 -0.00404734002483549; 0.02517372155742292 -0.03112524013244207 … -0.031074387308373416 0.010477079178892111], [0.0, 0.20408163265306123, 0.40816326530612246, 0.6122448979591837, 0.8163265306122449, 1.0204081632653061, 1.2244897959183674, 1.4285714285714286, 1.6326530612244898, 1.836734693877551  …  8.16326530612245, 8.36734693877551, 8.571428571428571, 8.775510204081632, 8.979591836734693, 9.183673469387756, 9.387755102040817, 9.591836734693878, 9.795918367346939, 10.0], Union{}[], nothing, :left, DataInterpolations.ExtrapolationType.None, DataInterpolations.ExtrapolationType.None, FindFirstFunctions.Guesser{Vector{Float64}}([0.0, 0.20408163265306123, 0.40816326530612246, 0.6122448979591837, 0.8163265306122449, 1.0204081632653061, 1.2244897959183674, 1.4285714285714286, 1.6326530612244898, 1.836734693877551  …  8.16326530612245, 8.36734693877551, 8.571428571428571, 8.775510204081632, 8.979591836734693, 9.183673469387756, 9.387755102040817, 9.591836734693878, 9.795918367346939, 10.0], Base.RefValue{Int64}(50), true), false, false), 10.0, 2, :u, [0.0, 0.0], [0.0, 0.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], 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.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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}, saveat::Vector{Float64}}}, SciMLBase.StandardODEProblem}, OrdinaryDiffEqLinear.MagnusGL4, OrdinaryDiffEqCore.InterpolationData{SciMLBase.ODEFunction{true, SciMLBase.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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.MagnusGL4Cache{Matrix{ComplexF64}, Matrix{ComplexF64}, Matrix{ComplexF64}, Nothing}, Nothing}, SciMLBase.DEStats, Nothing, Nothing, Nothing, Nothing}(Matrix{ComplexF64}[[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im], [0.9987404944669499 - 0.049979006477971294im -0.002516315185586171 + 0.003632049183859544im; 0.0025163151855861713 + 0.003632049183859544im 0.9987404944669498 + 0.04997900647797128im], [0.9949651505761753 - 0.09983211528555189im -0.005026291745374055 + 0.007254949195632325im; 0.005026291745374055 + 0.007254949195632325im 0.9949651505761751 + 0.09983211528555185im], [0.9887152372777896 - 0.14939534455615436im -0.0018761402879584588 + 0.01094032436868276im; 0.001876140287958457 + 0.01094032436868276im 0.9887152372777893 + 0.1493953445561543im], [0.9799746706494219 - 0.198582234919078im 0.0012787373179774988 + 0.014598139983336013im; -0.0012787373179775014 + 0.014598139983336013im 0.9799746706494215 + 0.1985822349190779im], [0.9689065695145163 - 0.2473619109215686im -0.0010428845248709336 + 0.005572877955798115im; 0.0010428845248709332 + 0.005572877955798113im 0.9689065695145159 + 0.24736191092156848im], [0.9553301582696501 - 0.2955012151880159im -0.0033618065431134034 - 0.0034668111657010824im; 0.003361806543113405 - 0.003466811165701085im 0.9553301582696498 + 0.2955012151880158im], [0.9393088601165602 - 0.34285545370350246im 0.007960833755982026 + 0.00925355597616057im; -0.007960833755982027 + 0.00925355597616057im 0.93930886011656 + 0.34285545370350234im], [0.9206677207586047 - 0.3892534286479057im 0.019261270367680737 + 0.021948113878625865im; -0.01926127036768074 + 0.02194811387862587im 0.9206677207586046 + 0.3892534286479056im], [0.8994028302467633 - 0.4351073890497396im 0.011700795934541623 + 0.04023928817622323im; -0.011700795934541616 + 0.04023928817622324im 0.8994028302467629 + 0.4351073890497394im]  …  [-0.162330310516659 + 0.9776863530921438im -0.09211288008898694 + 0.09640271047361523im; 0.09211288008898716 + 0.0964027104736151im -0.1623303105166594 - 0.9776863530921454im], [-0.11340823665747114 + 0.9851691121988067im -0.08375996985188089 + 0.09779907810439306im; 0.08375996985188111 + 0.09779907810439291im -0.11340823665747142 - 0.985169112198808im], [-0.06302380855624713 + 0.9886998445737325im -0.08882662794492435 + 0.10300702433754506im; 0.08882662794492457 + 0.10300702433754493im -0.06302380855624735 - 0.9886998445737339im], [-0.012475275886258899 + 0.9896561504684319im -0.09366199478776101 + 0.10794675568975075im; 0.09366199478776124 + 0.10794675568975064im -0.012475275886259036 - 0.9896561504684335im], [0.03513659061109398 + 0.9900493229410063im -0.07326064774684647 + 0.11489401914805875im; 0.07326064774684674 + 0.11489401914805865im 0.035136590611093935 - 0.990049322941008im], [0.0826524806733045 + 0.9877381520786593im -0.05265918749959422 + 0.12152744685323233im; 0.05265918749959453 + 0.12152744685323226im 0.08265248067330455 - 0.9877381520786611im], [0.130070131900829 + 0.9815639330511405im -0.036893893781669025 + 0.1351030966414246im; 0.03689389378166935 + 0.13510309664142456im 0.13007013190082917 - 0.9815639330511423im], [0.17713407075222337 + 0.9727204516340926im -0.021028270905592263 + 0.14831134742234833im; 0.021028270905592603 + 0.14831134742234828im 0.17713407075222368 - 0.9727204516340944im], [0.22528612812725657 + 0.9631491457480755im -0.011030255227385374 + 0.146520363730776im; 0.011030255227385717 + 0.14652036373077593im 0.22528612812725693 - 0.9631491457480771im], [0.2728719516934668 + 0.9511570622767048im -0.0010045161226115143 + 0.14436111598178003im; 0.0010045161226118673 + 0.14436111598177992im 0.2728719516934672 - 0.9511570622767062im]], nothing, nothing, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9  …  9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0], Vector{Matrix{ComplexF64}}[[[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]]], nothing, SciMLBase.ODEProblem{Matrix{ComplexF64}, Tuple{Float64, Float64}, true, SciMLBase.NullParameters, SciMLBase.ODEFunction{true, SciMLBase.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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}, saveat::Vector{Float64}}}, SciMLBase.StandardODEProblem}(SciMLBase.ODEFunction{true, SciMLBase.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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}(MatrixOperator(2 × 2), LinearAlgebra.UniformScaling{Bool}(true), nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, SciMLBase.DEFAULT_OBSERVED, nothing, Piccolo.Quantum.Rollouts.PiccoloRolloutSystem{Union{Int64, AbstractVector{Int64}, CartesianIndex, CartesianIndices}}(Dict{Symbol, Union{Int64, AbstractVector{Int64}, CartesianIndex, CartesianIndices}}(:U => CartesianIndices((2, 2)), :U_2_2 => CartesianIndex(2, 2), :U_1_1 => CartesianIndex(1, 1), :U_2_1 => CartesianIndex(2, 1), :U_1_2 => CartesianIndex(1, 2)), :t, Dict{Symbol, Float64}()), nothing, nothing), ComplexF64[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im], (0.0, 10.0), SciMLBase.NullParameters(), Base.Pairs(:tstops => [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9  …  9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0], :saveat => [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9  …  9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0]), SciMLBase.StandardODEProblem()), OrdinaryDiffEqLinear.MagnusGL4(false, 30, 0), OrdinaryDiffEqCore.InterpolationData{SciMLBase.ODEFunction{true, SciMLBase.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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.MagnusGL4Cache{Matrix{ComplexF64}, Matrix{ComplexF64}, Matrix{ComplexF64}, Nothing}, Nothing}(SciMLBase.ODEFunction{true, SciMLBase.FullSpecialize, SciMLOperators.MatrixOperator{ComplexF64, Matrix{ComplexF64}, SciMLOperators.FilterKwargs{Nothing, Val{()}}, SciMLOperators.FilterKwargs{Piccolo.Quantum.Rollouts.var"#update!#_construct_operator##2"{QuantumSystem{Piccolo.Quantum.QuantumSystems.var"#26#27"{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}, Vector{SparseArrays.SparseMatrixCSC{ComplexF64, Int64}}, Int64}, Piccolo.Quantum.QuantumSystems.var"#28#29"{Vector{SparseArrays.SparseMatrixCSC{Float64, Int64}}, Int64, SparseArrays.SparseMatrixCSC{Float64, Int64}}, @NamedTuple{}}}, Val{()}}}, LinearAlgebra.UniformScaling{Bool}, 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}(MatrixOperator(2 × 2), LinearAlgebra.UniformScaling{Bool}(true), nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, SciMLBase.DEFAULT_OBSERVED, nothing, Piccolo.Quantum.Rollouts.PiccoloRolloutSystem{Union{Int64, AbstractVector{Int64}, CartesianIndex, CartesianIndices}}(Dict{Symbol, Union{Int64, AbstractVector{Int64}, CartesianIndex, CartesianIndices}}(:U => CartesianIndices((2, 2)), :U_2_2 => CartesianIndex(2, 2), :U_1_1 => CartesianIndex(1, 1), :U_2_1 => CartesianIndex(2, 1), :U_1_2 => CartesianIndex(1, 2)), :t, Dict{Symbol, Float64}()), nothing, nothing), Matrix{ComplexF64}[[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im], [0.9987404944669499 - 0.049979006477971294im -0.002516315185586171 + 0.003632049183859544im; 0.0025163151855861713 + 0.003632049183859544im 0.9987404944669498 + 0.04997900647797128im], [0.9949651505761753 - 0.09983211528555189im -0.005026291745374055 + 0.007254949195632325im; 0.005026291745374055 + 0.007254949195632325im 0.9949651505761751 + 0.09983211528555185im], [0.9887152372777896 - 0.14939534455615436im -0.0018761402879584588 + 0.01094032436868276im; 0.001876140287958457 + 0.01094032436868276im 0.9887152372777893 + 0.1493953445561543im], [0.9799746706494219 - 0.198582234919078im 0.0012787373179774988 + 0.014598139983336013im; -0.0012787373179775014 + 0.014598139983336013im 0.9799746706494215 + 0.1985822349190779im], [0.9689065695145163 - 0.2473619109215686im -0.0010428845248709336 + 0.005572877955798115im; 0.0010428845248709332 + 0.005572877955798113im 0.9689065695145159 + 0.24736191092156848im], [0.9553301582696501 - 0.2955012151880159im -0.0033618065431134034 - 0.0034668111657010824im; 0.003361806543113405 - 0.003466811165701085im 0.9553301582696498 + 0.2955012151880158im], [0.9393088601165602 - 0.34285545370350246im 0.007960833755982026 + 0.00925355597616057im; -0.007960833755982027 + 0.00925355597616057im 0.93930886011656 + 0.34285545370350234im], [0.9206677207586047 - 0.3892534286479057im 0.019261270367680737 + 0.021948113878625865im; -0.01926127036768074 + 0.02194811387862587im 0.9206677207586046 + 0.3892534286479056im], [0.8994028302467633 - 0.4351073890497396im 0.011700795934541623 + 0.04023928817622323im; -0.011700795934541616 + 0.04023928817622324im 0.8994028302467629 + 0.4351073890497394im]  …  [-0.162330310516659 + 0.9776863530921438im -0.09211288008898694 + 0.09640271047361523im; 0.09211288008898716 + 0.0964027104736151im -0.1623303105166594 - 0.9776863530921454im], [-0.11340823665747114 + 0.9851691121988067im -0.08375996985188089 + 0.09779907810439306im; 0.08375996985188111 + 0.09779907810439291im -0.11340823665747142 - 0.985169112198808im], [-0.06302380855624713 + 0.9886998445737325im -0.08882662794492435 + 0.10300702433754506im; 0.08882662794492457 + 0.10300702433754493im -0.06302380855624735 - 0.9886998445737339im], [-0.012475275886258899 + 0.9896561504684319im -0.09366199478776101 + 0.10794675568975075im; 0.09366199478776124 + 0.10794675568975064im -0.012475275886259036 - 0.9896561504684335im], [0.03513659061109398 + 0.9900493229410063im -0.07326064774684647 + 0.11489401914805875im; 0.07326064774684674 + 0.11489401914805865im 0.035136590611093935 - 0.990049322941008im], [0.0826524806733045 + 0.9877381520786593im -0.05265918749959422 + 0.12152744685323233im; 0.05265918749959453 + 0.12152744685323226im 0.08265248067330455 - 0.9877381520786611im], [0.130070131900829 + 0.9815639330511405im -0.036893893781669025 + 0.1351030966414246im; 0.03689389378166935 + 0.13510309664142456im 0.13007013190082917 - 0.9815639330511423im], [0.17713407075222337 + 0.9727204516340926im -0.021028270905592263 + 0.14831134742234833im; 0.021028270905592603 + 0.14831134742234828im 0.17713407075222368 - 0.9727204516340944im], [0.22528612812725657 + 0.9631491457480755im -0.011030255227385374 + 0.146520363730776im; 0.011030255227385717 + 0.14652036373077593im 0.22528612812725693 - 0.9631491457480771im], [0.2728719516934668 + 0.9511570622767048im -0.0010045161226115143 + 0.14436111598178003im; 0.0010045161226118673 + 0.14436111598177992im 0.2728719516934672 - 0.9511570622767062im]], [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9  …  9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0], Vector{Matrix{ComplexF64}}[[[1.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 1.0 + 0.0im]]], nothing, false, OrdinaryDiffEqLinear.MagnusGL4Cache{Matrix{ComplexF64}, Matrix{ComplexF64}, Matrix{ComplexF64}, Nothing}(ComplexF64[0.2728719516934668 + 0.9511570622767048im -0.0010045161226115143 + 0.14436111598178003im; 0.0010045161226118673 + 0.14436111598177992im 0.2728719516934672 - 0.9511570622767062im], ComplexF64[0.22528612812725657 + 0.9631491457480755im -0.011030255227385374 + 0.146520363730776im; 0.011030255227385717 + 0.14652036373077593im 0.22528612812725693 - 0.9631491457480771im], ComplexF64[0.22528612812725657 + 0.9631491457480755im -0.011030255227385374 + 0.146520363730776im; 0.011030255227385717 + 0.14652036373077593im 0.22528612812725693 - 0.9631491457480771im], ComplexF64[0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im], ComplexF64[0.4788900284285505 - 0.10786213399388914im 0.10016073473730927 - 0.0197594351188339im; -0.10016073473730917 - 0.019759435118833688im 0.4788900284285512 + 0.10786213399388933im], ComplexF64[0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im; 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im], ComplexF64[0.4749837282206564 - 0.1379443930709191im 0.07317131299535619 + 0.011572011486085169im; -0.07317131299535613 + 0.011572011486085328im 0.47498372822065704 + 0.1379443930709193im], nothing), nothing, false), false, 0, SciMLBase.DEStats(101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0.0), nothing, SciMLBase.ReturnCode.Success, nothing, nothing, nothing))

Objective Types

Piccolo uses two objective types from DirectTrajOpt.jl:

TypeWhen EvaluatedConstructor
TerminalObjectiveAt final timestep onlyTerminalObjective(loss, name, traj; Q=weight)
KnotPointObjectiveAt specified timestepsKnotPointObjective(loss, name, traj; Qs=weights)

Both take a loss function as their first argument — a regular Julia function that maps a component vector to a scalar cost.

Creating a Custom Terminal Objective

A terminal objective evaluates a loss function on a trajectory component at the final timestep. Let's create one that penalizes trace distance from the target:

# Build the problem to get the internal NamedTrajectory
qcp = SmoothPulseProblem(qtraj, N; Q = 100.0, R = 1e-2, ddu_bound = 1.0)
traj = get_trajectory(qcp)

# Define a custom loss: trace distance penalty on the isomorphic unitary vector
trace_distance_loss(Ũ⃗) =
    let
        U = iso_vec_to_operator(Ũ⃗)
        n = size(U, 1)
        diff = U - U_goal
        real(tr(diff' * diff)) / n
    end

# Wrap it in a TerminalObjective (evaluated on :Ũ⃗ at the final timestep)
custom_terminal_obj = TerminalObjective(trace_distance_loss, :Ũ⃗, traj; Q = 50.0)
KnotPointObjective(DirectTrajOpt.Objectives.var"#14#15"{typeof(Main.trace_distance_loss)}(Main.trace_distance_loss), [:Ũ⃗], [50], [nothing], [50.0])

Creating a Custom Knotpoint Objective

A knotpoint objective evaluates at every (or selected) timesteps. Let's penalize control energy:

# Loss function: squared norm of control values
control_energy_loss(u) = dot(u, u)

# Applied at all timesteps on the :u component
custom_knotpoint_obj =
    KnotPointObjective(control_energy_loss, :u, traj; Qs = fill(0.1, N), times = 1:N)
KnotPointObjective(DirectTrajOpt.Objectives.var"#14#15"{typeof(Main.control_energy_loss)}(Main.control_energy_loss), [:u], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10  …  41, 42, 43, 44, 45, 46, 47, 48, 49, 50], [nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing  …  nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1  …  0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1])

Adding Custom Objectives to a Problem

Building the objective manually

Combine objective terms with +:

# Start with the standard infidelity objective
J = UnitaryInfidelityObjective(U_goal, :Ũ⃗, traj; Q = 100.0)

# Add standard regularization
J += QuadraticRegularizer(:u, traj, 1e-2)
J += QuadraticRegularizer(:du, traj, 1e-2)
J += QuadraticRegularizer(:ddu, traj, 1e-2)

# Add our custom objective
J += custom_knotpoint_obj

typeof(J)
CompositeObjective

Adding to an existing problem

qcp = SmoothPulseProblem(qtraj, N; Q = 100.0, R = 1e-2, ddu_bound = 1.0)
qcp.prob.objective += custom_knotpoint_obj
CompositeObjective

Solving and Comparing

Solve with the extra control energy penalty:

cached_solve!(
    qcp,
    "custom_objectives_with_penalty";
    max_iter = 50,
    verbose = false,
    print_level = 1,
)
fidelity(qcp)
0.9993769104306977

Compare against the standard problem:

qcp_standard = SmoothPulseProblem(qtraj, N; Q = 100.0, R = 1e-2, ddu_bound = 1.0)
cached_solve!(
    qcp_standard,
    "custom_objectives_standard";
    max_iter = 50,
    verbose = false,
    print_level = 1,
)
fidelity(qcp_standard)
0.9996342448080662

Example: Leakage-Style Penalty

Here's how the built-in LeakageObjective works under the hood — it's just a KnotPointObjective with a custom loss function:

leakage_indices = [3, 4]  # Indices of leakage states in the isomorphic vector

leakage_loss(x) = sum(abs2, x[leakage_indices]) / length(leakage_indices)

leakage_obj = KnotPointObjective(leakage_loss, :Ũ⃗, traj; Qs = fill(1.0, N), times = 1:N)
KnotPointObjective(DirectTrajOpt.Objectives.var"#14#15"{typeof(Main.leakage_loss)}(Main.leakage_loss), [:Ũ⃗], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10  …  41, 42, 43, 44, 45, 46, 47, 48, 49, 50], [nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing  …  nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing], [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0  …  1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0])

Tips for Custom Objectives

1. Scale Appropriately

Match the scale of built-in objectives:

  • Built-in fidelity uses Q ~ 100
  • Custom objectives should use similar magnitude
custom_obj = TerminalObjective(my_loss, :Ũ⃗, traj; Q = 50.0)

2. Ensure Smoothness

Avoid discontinuities that can cause optimization issues:

## Bad: discontinuous
penalty = x > 0 ? x^2 : 0

## Good: smooth approximation
penalty = max(0, x)^2

## Also good: softplus
penalty = log(1 + exp(k * x)) / k

3. Test Independently

Verify your objective computes expected values:

## Evaluate the full objective on a trajectory
value = objective_value(custom_obj, traj)
println("Objective value: ", value)

## Check gradient
∇ = zeros(traj.dim * traj.N + traj.global_dim)
gradient!(∇, custom_obj, traj)

4. Start Simple

Add custom objectives incrementally:

## Step 1: Solve with standard objectives
qcp = SmoothPulseProblem(qtraj, N)
solve!(qcp)

## Step 2: Check if solution needs improvement
println("Fidelity: ", fidelity(qcp))

## Step 3: Add custom objective with small weight
custom_obj = KnotPointObjective(my_loss, :u, traj; Qs = fill(0.1, N))
qcp.prob.objective += custom_obj
solve!(qcp)

## Step 4: Increase weight if needed

5. Gradients are Automatic

DirectTrajOpt uses ForwardDiff for automatic differentiation, so gradients are computed automatically for any loss function you provide. No need to implement gradient methods manually.

See Also


This page was generated using Literate.jl.