Interferometer readout
We want to model a general interferometer readout protocol. We will model different temporal modes of the output signal by using a cascaded series of filters which collect light according to $\kappa_i(t)$, the `filter function.'
First, we will look at building the Hamiltonian of this cascaded system using the SLH formalism. For now we will model our nonlinear interferometer with the Jaynes-Cummings system. The package SystemLevelHamiltonian.jl implements the SLH composition rules, using the quantum algebra provided by the package QuantumCumulants.jl.
using SystemLevelHamiltonian
ifo = qed_cavity(:ifo)
SLH(:ifo, [:In], [:Out], [1], QuantumCumulants.QMul{Nothing}[sqrt(κ_ifo)*(a_ifo)], (g_ifo*(a_ifo′*σ_ifo12)+g_ifo*(a_ifo*σ_ifo21)+Δ_ifo*(a_ifo′*a_ifo)+h_ifo*(a_ifo)+h_ifo*(a_ifo′)))
Now we create our chain of filters
filters = [cavity(symb) for symb in [:A,:B]]
2-element Vector{SLH}:
SLH(:A, [:In], [:Out], [1], QuantumCumulants.QMul{Nothing}[sqrt(κ_A)*(a_A)], Δ_A*(a_A′*a_A))
SLH(:B, [:In], [:Out], [1], QuantumCumulants.QMul{Nothing}[sqrt(κ_B)*(a_B)], Δ_B*(a_B′*a_B))
We use the SLH composition rules to assemble our system.
push!(filters,ifo)
sys = concatenate(:sys,filters)
sys = feedbackreduce(sys,:Out_ifo,:In_A)
sys = feedbackreduce(sys,:Out_A,:In_B)
SLH(:sys, [:In_ifo], [:Out_B], [1.0;;], Vector{QuantumCumulants.QAdd}[[(sqrt(κ_A)*(a_A)+sqrt(κ_ifo)*(a_ifo)+sqrt(κ_B)*(a_B))]], (Δ_A*(a_A′*a_A)+Δ_B*(a_B′*a_B)+g_ifo*(a_ifo′*σ_ifo12)+g_ifo*(a_ifo*σ_ifo21)+Δ_ifo*(a_ifo′*a_ifo)+h_ifo*(a_ifo)+h_ifo*(a_ifo′)+(0.0 - 0.5im)*sqrt(κ_A)*sqrt(κ_ifo)*(a_A′*a_ifo)+(-0.0 + 0.5im)*conj(conj(conj(sqrt(κ_ifo))))*sqrt(κ_A)*(a_A*a_ifo′)+(0.0 - 0.5im)*sqrt(κ_B)*sqrt(κ_A)*(a_A*a_B′)+(0.0 - 0.5im)*sqrt(κ_B)*sqrt(κ_ifo)*(a_B′*a_ifo)+(-0.0 + 0.5im)*sqrt(κ_B)*sqrt(κ_A)*(a_A′*a_B)+(-0.0 + 0.5im)*sqrt(κ_B)*conj(conj(conj(sqrt(κ_ifo))))*(a_B*a_ifo′)))
Even with two output filters, this Hamiltonian is a mess. It doesn't help that for now the symbolic system does not simplify or group terms nicely. We want to represent this Hamiltonian as a matrix to facilitate a density matrix calculation. Automating this procedure is WIP, so let's do something a bit manual to organize this Hamiltonian.
\[H_0 = Δ_A a_A^\dagger a_A+Δ_B a_B^\dagger a_B +g_{ifo}(a_{ifo}^\dagger σ_{ifo12} + a_{ifo} σ_{ifo21})+Δ_{ifo} a_{ifo}^\dagger a_{ifo}\]
\[H_1 = h_{ifo} (a_{ifo} + a_{ifo}^\dagger)\]
\[H_2 = \frac{i}{2}\sqrt{κ_A κ_{ifo}} (a_A a_{ifo}^\dagger -a_A^\dagger a_{ifo})\]
\[H_3 = \frac{i}{2}\sqrt{κ_B κ_A}(a_A^\dagger a_B - a_A a_B^\dagger)\]
\[H_4 = \frac{i}{2}\sqrt{κ_B κ_{ifo}}(a_B a_{ifo}^\dagger - a_B^\dagger a_{ifo})\]
Now, we will build this system and simulate its time evolution using the package QuantumToolbox. We will send $\Delta_A$ and $\Delta_B$ to zero, as well as make the coupling rates $\kappa_A$ and $\kappa_B$ functions of time.
using QuantumToolbox
We first need to establish an order on the subspaces of our Hilbert space, as well as a cutoff for our Fock states.
\[\text{spin} \otimes \text{ifo} \otimes \text{A} \otimes \text{B}\]
N = 4
σ = sigmam() ⊗ qeye(N) ⊗ qeye(N) ⊗ qeye(N)
a_ifo = qeye(2) ⊗ destroy(N) ⊗ qeye(N) ⊗ qeye(N)
a_A = qeye(2) ⊗ qeye(N) ⊗ destroy(N) ⊗ qeye(N)
a_B = qeye(2) ⊗ qeye(N) ⊗ qeye(N) ⊗ destroy(N)
We can now define our constants and build the first part of the Hamiltonian
g = 2
Δ = 0.5
H_0 = Δ*a_ifo'*a_ifo + g*(a_ifo'*σ + a_ifo*σ')
Quantum Object: type=Operator() dims=[2, 4, 4, 4] size=(128, 128) ishermitian=true
128×128 SparseArrays.SparseMatrixCSC{ComplexF64, Int64} with 192 stored entries:
⎡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠓⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎤
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠱⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠓⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⢄⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⣄⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠓⢄⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠱⢄⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠓⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⢤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠑⢆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠑⢤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠙⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠑⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀⎥
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⎦
We now define the first time dependent part
function signal(p,t)
return exp(-(t-p.d)^2/p.s^2)*p.A*sin(p.w*t)
end
H_1 = QobjEvo(a_ifo+a_ifo', signal)
kappaifo = 0.3
function kappaA(p,t)
return Complex(exp(-(t-p.dA)^2/p.sA^2)*p.AA*sin(p.wA*t))
end
function kappaB(p,t)
return Complex(exp(-(t-p.dB)^2/p.sB^2)*p.AB*sin(p.wB*t))
end
function f2(p,t)
return 1.0im/2*sqrt(kappaA(p,t)*kappaifo)
end
H_2 = QobjEvo(a_A*a_ifo' - a_A'*a_ifo,f2)
function f3(p,t)
return 1.0im/2*sqrt(kappaB(p,t)*kappaA(p,t))
end
H_3 = QobjEvo(a_A'*a_B - a_A*a_B',f3)
function f4(p,t)
return 1.0im/2*sqrt(kappaB(p,t)*kappaifo)
end
H_4 = QobjEvo(a_B*a_ifo' - a_B'*a_ifo,f4)
H_total = H_0 + H_1 + H_2 + H_3 + H_4
Quantum Object Evo.: type=Operator() dims=[2, 4, 4, 4] size=(128, 128) ishermitian=true isconstant=false
(MatrixOperator(128 × 128) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(128 × 128) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(128 × 128) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(128 × 128) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(128 × 128))
We now have the total Hamiltonian, which we can simulate by providing an initial state as well as parameters for all the time dependent functions.
ψ0 = basis(2,0) ⊗ fock(N, 0) ⊗ fock(N, 0) ⊗ fock(N, 0)
tlist = 0:0.1:100
p = (
d = 5, #center of signal pulse
s = 2, #width of signal pulse
A = 4, #amplitude of signal pulse
w = 3*2*pi, #frequency of signal pulse
dA = 3, #center
sA = 2, #width
AA = 2, #amplitude
wA = 2*2*pi, #frequency
dB = 8, #center
sB = 2, #width
AB = 2, #amplitude
wB = 4*2*pi #frequency
)
sol_me = mesolve(H_total, ψ0, tlist, params = p)
Solution of time evolution
(return code: Success)
--------------------------
num_states = 1001
num_expect = 0
ODE alg.: OrdinaryDiffEqTsit5.Tsit5{typeof(OrdinaryDiffEqCore.trivial_limiter!), typeof(OrdinaryDiffEqCore.trivial_limiter!), Static.False}(OrdinaryDiffEqCore.trivial_limiter!, OrdinaryDiffEqCore.trivial_limiter!, static(false))
abstol = 1.0e-8
reltol = 1.0e-6
Now, we want to calculate the quantum Fisher information of the final state left in the filters with respect to the frequency of the signal (for example). To do this, we are going to wrap this solver call in a function which takes a frequency, time evolves, extracts the final state, and traces out the interferometer. This will then allow us to use the finite difference method to calculate the derivative with respect to a parameter and calculate the QFI.
function final_state(omega)
p = (
d = 5, #center of signal pulse
s = 2, #width of signal pulse
A = 4, #amplitude of signal pulse
w = omega, #frequency of signal pulse
dA = 3, #center
sA = 2, #width
AA = 2, #amplitude
wA = 2*2*pi, #frequency
dB = 8, #center
sB = 2, #width
AB = 2, #amplitude
wB = 4*2*pi #frequency
)
sol_me = mesolve(H_total, ψ0, tlist, params = p)
rho = ptrace(sol_me.states[end],(3,4))
return hermitian_data(rho)
end
final_state(3*2*pi)
16×16 Matrix{ComplexF64}:
0.991873+0.0im … 9.61716e-8-9.09681e-8im
-5.01644e-5-0.000118818im -1.22202e-11-9.30754e-11im
-0.000454612-0.000905623im -2.66048e-10-9.77298e-11im
8.18787e-5+8.65377e-5im 3.32911e-11+2.19757e-12im
-0.000183892+2.37099e-5im 1.27208e-10+2.4713e-10im
-0.000209538-0.000215858im … -8.50183e-11-8.16506e-12im
-4.12425e-5+4.37888e-6im -8.26885e-12+8.24427e-12im
-1.5653e-5-2.42436e-5im -7.95094e-12-1.82761e-12im
-5.66176e-5-3.14811e-5im -1.18901e-11+6.5457e-12im
-2.45605e-5+2.00545e-5im 1.43714e-13+9.71092e-12im
1.42538e-5-5.08985e-6im … 2.04559e-12-3.70144e-12im
-1.38019e-7+3.5115e-7im 4.63325e-14+9.37875e-14im
7.21348e-6+1.98748e-5im 2.61284e-12+1.12805e-12im
9.59475e-6+8.70784e-7im 2.15836e-12-1.58206e-12im
-7.22915e-7-9.96253e-8im -1.602e-13+1.3018e-13im
9.61716e-8+9.09681e-8im … 3.72788e-14+0.0im
function derivative(A,dA)
rho1 = final_state(A)
rho2 = final_state(A+dA)
return (rho1, rho2, (rho2 - rho1)/dA)
end
(rho1, rho2, rhodot) = derivative(3*2*pi,0.001)
L = sld_operator(rho1,rhodot)
qfi = tr(L*L*rho1)
0.06101882572685508 - 3.1402767280789332e-18im
We can wrap all of this into a single function which will output the QFI of the system as a whole as well as each subsystem
function all_qfi(p,eps)
sol_me = mesolve(H_total, ψ0, tlist, params = p)
rho_final = ket2dm(sol_me.states[end])
pp= merge(p,(w=p.w+eps,))
sol_me_prime = mesolve(H_total, ψ0, tlist, params = pp)
rho_final_prime = ket2dm(sol_me_prime.states[end])
rho_dot_full = (hermitian_data(rho_final_prime) - hermitian_data(rho_final))/eps
L_full = sld_operator(hermitian_data(rho_final),rho_dot_full)
qfi_full = real(tr(L_full*L_full*hermitian_data(rho_final)))
println("qfi_full = $qfi_full")
rho_ifo = ptrace(rho_final,(1,2))
rho_ifo_prime = ptrace(rho_final_prime,(1,2))
rho_dot_ifo = (hermitian_data(rho_ifo_prime) - hermitian_data(rho_ifo))/eps
L_ifo = sld_operator(hermitian_data(rho_ifo),rho_dot_ifo)
qfi_ifo = real(tr(L_ifo*L_ifo*hermitian_data(rho_ifo)))
println("qfi_ifo = $qfi_ifo")
rho_filters = ptrace(rho_final,(3,4))
rho_filters_prime = ptrace(rho_final_prime,(3,4))
rho_dot_filters = (hermitian_data(rho_filters_prime) - hermitian_data(rho_filters))/eps
L_filters = sld_operator(hermitian_data(rho_filters),rho_dot_filters)
qfi_filters = real(tr(L_filters*L_filters*hermitian_data(rho_filters)))
println("qfi_filters = $qfi_filters")
rho_A = ptrace(rho_final,3)
rho_A_prime = ptrace(rho_final_prime,3)
rho_dot_A = (hermitian_data(rho_A_prime) - hermitian_data(rho_A))/eps
L_A = sld_operator(hermitian_data(rho_A),rho_dot_A)
qfi_A = real(tr(L_A*L_A*hermitian_data(rho_A)))
println("qfi_A = $qfi_A")
rho_B = ptrace(rho_final,4)
rho_B_prime = ptrace(rho_final_prime,4)
rho_dot_B = (hermitian_data(rho_B_prime) - hermitian_data(rho_B))/eps
L_B = sld_operator(hermitian_data(rho_B),rho_dot_B)
qfi_B = real(tr(L_B*L_B*hermitian_data(rho_B)))
println("qfi_B = $qfi_B")
return
end
eps = 0.0005
Nominal parameters
p_nominal = (
d = 5, #center of signal pulse
s = 2, #width of signal pulse
A = 4, #amplitude of signal pulse
w = 3*2*pi, #frequency of signal pulse
dA = 3, #center
sA = 2, #width
AA = 2, #amplitude
wA = 2*2*pi, #frequency
dB = 8, #center
sB = 2, #width
AB = 2, #amplitude
wB = 4*2*pi #frequency
)
all_qfi(p_nominal,eps)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
qfi_full = 0.24045544113368622
qfi_ifo = 0.05866214145981759
qfi_filters = 0.17748928922700064
qfi_A = 0.0008089985839518995
qfi_B = 0.12115758718911955
Sanity check - second filter off
p = merge(p_nominal,(AB=0,))
all_qfi(p,eps)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
qfi_full = 0.1964976126783722
qfi_ifo = 0.1884578778914792
qfi_filters = 0.1706741039777998
qfi_A = 0.17067410397779992
qfi_B = 0.00045653332689608747
sanity check - late signal
p = merge(p_nominal,(d=50,))
all_qfi(p,eps)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
qfi_full = 8.505711181134919e-7
qfi_ifo = 8.504982860701582e-7
qfi_filters = 1.983496688342785e-15
qfi_A = 1.9736883925777066e-15
qfi_B = 1.888241272533022e-15
increase signal amplitude
p = merge(p_nominal,(A=p_nominal.A*4,))
all_qfi(p,eps)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
Progress: [==============================] 100.0% --- Elapsed Time: 0h 00m 00s (ETA: 0h 00m 00s)
qfi_full = 0.847639547372202
qfi_ifo = 0.61190892480424
qfi_filters = 0.26508383308929107
qfi_A = 0.053017756419404524
qfi_B = 0.2392701103783253