State Transfer

This tutorial covers state-to-state transfer using KetTrajectory. We'll prepare quantum states and implement gates via state mappings.

Overview

While UnitaryTrajectory optimizes for a full unitary gate, KetTrajectory optimizes for transferring a specific initial state to a target state. This is useful for:

  • State preparation
  • Gates where you only care about specific state mappings
  • Problems where full unitary tracking is expensive
using Piccolo
using CairoMakie
using Random
Random.seed!(123)
Random.TaskLocalRNG()

Single State Transfer

Let's prepare the |1⟩ state starting from |0⟩.

Setup

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

# Time parameters
T, N = 10.0, 100
times = collect(range(0, T, length = N))

# Initial pulse
pulse = ZeroOrderPulse(0.1 * randn(2, N), times)
ZeroOrderPulse
  drives: 2
  duration: 10.0

Define State Transfer

# Initial state: |0⟩
ψ_init = ComplexF64[1.0, 0.0]

# Target state: |1⟩
ψ_goal = ComplexF64[0.0, 1.0]

# Create KetTrajectory
qtraj = KetTrajectory(sys, pulse, ψ_init, ψ_goal)
KetTrajectory{ZeroOrderPulse{DataInterpolations.ConstantInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Float64}}, SciMLBase.ODESolution{ComplexF64, 2, Vector{Vector{ComplexF64}}, Nothing, Nothing, Vector{Float64}, Vector{Vector{Vector{ComplexF64}}}, Nothing, SciMLBase.ODEProblem{Vector{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{Vector{ComplexF64}}, Vector{Float64}, Vector{Vector{Vector{ComplexF64}}}, Nothing, OrdinaryDiffEqLinear.MagnusAdapt4Cache{Vector{ComplexF64}, Vector{ComplexF64}, Matrix{ComplexF64}, Vector{ComplexF64}, Nothing}, Nothing}, SciMLBase.DEStats, Nothing, Nothing, Nothing, Nothing}}(QuantumSystem: levels = 2, n_drives = 2, ZeroOrderPulse(Number of drives = 2, T = 10.0), ComplexF64[1.0 + 0.0im, 0.0 + 0.0im], ComplexF64[0.0 + 0.0im, 1.0 + 0.0im], ComplexF64[1.0 + 0.0im 0.9986546829501518 - 0.049977576037549286im … 0.21989024288659764 + 0.9686899992340066im 0.2656562004673531 + 0.9540494064401241im; 0.0 + 0.0im -0.011215692818783948 - 0.008079254281018215im … -0.10623435203872292 - 0.04474627262931361im -0.11812797096511285 - 0.07254168250166698im])

Solve

qcp = SmoothPulseProblem(qtraj, N; Q = 100.0, R = 1e-2)
solve!(qcp; max_iter = 20, verbose = false, print_level = 1)

fidelity(qcp)
0.9669714846109944

Visualize State Evolution

traj = get_trajectory(qcp)

# Convert isomorphic states to physical states
n_steps = size(traj[:ψ̃], 2)
populations = zeros(2, n_steps)

for k = 1:n_steps
    ψ = iso_to_ket(traj[:ψ̃][:, k])
    populations[:, k] = abs2.(ψ)
end

# Plot
fig = Figure(size = (800, 400))

ax1 = Axis(fig[1, 1], xlabel = "Timestep", ylabel = "Population", title = "State Evolution")
lines!(ax1, 1:n_steps, populations[1, :], label = "|0⟩", linewidth = 2)
lines!(ax1, 1:n_steps, populations[2, :], label = "|1⟩", linewidth = 2)
axislegend(ax1, position = :rt)

fig
Example block output

Multi-State Transfer (Gate via States)

We can define a gate by specifying how it maps multiple states. MultiKetTrajectory optimizes all mappings simultaneously with coherent phases.

Define X Gate via State Mappings

The X gate maps:

  • |0⟩ → |1⟩
  • |1⟩ → |0⟩
# Basis states
ψ0 = ComplexF64[1.0, 0.0]  # |0⟩
ψ1 = ComplexF64[0.0, 1.0]  # |1⟩

# Initial states and their targets
initial_states = [ψ0, ψ1]
goal_states = [ψ1, ψ0]  # X gate swaps them

# Create new pulse for this problem
pulse_multi = ZeroOrderPulse(0.1 * randn(2, N), times)

# Create MultiKetTrajectory
qtraj_multi = MultiKetTrajectory(sys, pulse_multi, initial_states, goal_states)
MultiKetTrajectory{ZeroOrderPulse{DataInterpolations.ConstantInterpolation{Matrix{Float64}, Vector{Float64}, Vector{Union{}}, Float64}}, SciMLBase.EnsembleSolution{ComplexF64, 3, Vector{SciMLBase.ODESolution{ComplexF64, 2, Vector{Vector{ComplexF64}}, Nothing, Nothing, Vector{Float64}, Vector{Vector{Vector{ComplexF64}}}, Nothing, SciMLBase.ODEProblem{Vector{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{Vector{ComplexF64}}, Vector{Float64}, Vector{Vector{Vector{ComplexF64}}}, Nothing, OrdinaryDiffEqLinear.MagnusAdapt4Cache{Vector{ComplexF64}, Vector{ComplexF64}, Matrix{ComplexF64}, Vector{ComplexF64}, Nothing}, Nothing}, SciMLBase.DEStats, Nothing, Nothing, Nothing, Nothing}}}}(QuantumSystem: levels = 2, n_drives = 2, ZeroOrderPulse(Number of drives = 2, T = 10.0), Vector{ComplexF64}[[1.0 + 0.0im, 0.0 + 0.0im], [0.0 + 0.0im, 1.0 + 0.0im]], Vector{ComplexF64}[[0.0 + 0.0im, 1.0 + 0.0im], [1.0 + 0.0im, 0.0 + 0.0im]], [0.5, 0.5], ComplexF64[1.0 + 0.0im 0.9984186197391062 - 0.04997364088264558im … 0.23362005723166956 + 0.969270754287099im 0.28146561110526647 + 0.956487839495784im; 0.0 + 0.0im 0.025562895335622466 + 0.0030713770935270183im … -0.050707250396324524 - 0.058005590248229336im -0.044279319118150244 - 0.06282885133655476im;;; 0.0 + 0.0im -0.02556289533562247 + 0.0030713770935270174im … 0.05070725039632503 - 0.058005590248228996im 0.04427931911815074 - 0.06282885133655443im; 1.0 + 0.0im 0.9984186197391061 + 0.049973640882645574im … 0.233620057231671 - 0.9692707542871023im 0.28146561110526813 - 0.9564878394957875im])

Solve with Coherent Fidelity

MultiKetTrajectory uses CoherentKetInfidelityObjective, which ensures not just that each state reaches its target, but that the relative phases between states are preserved correctly.

qcp_multi = SmoothPulseProblem(qtraj_multi, N; Q = 100.0, R = 1e-2)
solve!(
    qcp_multi;
    max_iter = 20,
    verbose = false,
    print_level = 1,
)

fidelity(qcp_multi)
0.9999991778522876

Visualize Both State Evolutions

traj_multi = get_trajectory(qcp_multi)

# Extract populations for both initial states
# MultiKetTrajectory stores states as :ψ̃1, :ψ̃2, etc.
n_steps = size(traj_multi[:ψ̃1], 2)

pops1 = zeros(2, n_steps)  # Evolution from |0⟩
pops2 = zeros(2, n_steps)  # Evolution from |1⟩

for k = 1:n_steps
    ψ1_k = iso_to_ket(traj_multi[:ψ̃1][:, k])
    ψ2_k = iso_to_ket(traj_multi[:ψ̃2][:, k])
    pops1[:, k] = abs2.(ψ1_k)
    pops2[:, k] = abs2.(ψ2_k)
end

fig2 = Figure(size = (800, 400))

ax1 = Axis(fig2[1, 1], xlabel = "Timestep", ylabel = "Population", title = "|0⟩ → |1⟩")
lines!(ax1, 1:n_steps, pops1[1, :], label = "|0⟩", linewidth = 2, color = :blue)
lines!(ax1, 1:n_steps, pops1[2, :], label = "|1⟩", linewidth = 2, color = :red)
axislegend(ax1, position = :rt)

ax2 = Axis(fig2[1, 2], xlabel = "Timestep", ylabel = "Population", title = "|1⟩ → |0⟩")
lines!(ax2, 1:n_steps, pops2[1, :], label = "|0⟩", linewidth = 2, color = :blue)
lines!(ax2, 1:n_steps, pops2[2, :], label = "|1⟩", linewidth = 2, color = :red)
axislegend(ax2, position = :rt)

fig2
Example block output

When to Use Each Trajectory Type

TrajectoryUse CaseProsCons
UnitaryTrajectoryFull gate synthesisComplete gate, any inputTracks d² elements
KetTrajectorySingle state prepFast, simpleSingle state only
MultiKetTrajectoryGate via state mapsPhase coherentNeed all relevant states

Superposition State Preparation

Let's prepare a superposition state: |+⟩ = (|0⟩ + |1⟩)/√2

ψ_plus = ComplexF64[1, 1] / sqrt(2)

pulse_super = ZeroOrderPulse(0.1 * randn(2, N), times)
qtraj_super = KetTrajectory(sys, pulse_super, ψ0, ψ_plus)

qcp_super = SmoothPulseProblem(qtraj_super, N; Q = 100.0, R = 1e-2)
solve!(
    qcp_super;
    max_iter = 20,
    verbose = false,
    print_level = 1,
)

fidelity(qcp_super)
0.985337917192531

Verify the final state:

traj_super = get_trajectory(qcp_super)
ψ_final = iso_to_ket(traj_super[:ψ̃][:, end])
round.(ψ_final, digits = 3)
2-element Vector{ComplexF64}:
 0.419 + 0.637im
 0.248 + 0.61im

Next Steps


This page was generated using Literate.jl.