-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support time-dependent operators in timeevolution (#366)
Support time-dependent operators in timeevolution by providing `x_dynamic` methods that take those operators directly, as an alternative to the function-based interface.
- Loading branch information
Showing
7 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# Convert storage of heterogeneous stuff to tuples for maximal compilation | ||
# and to avoid runtime dispatch. | ||
_tuplify(o::TimeDependentSum) = TimeDependentSum(Tuple, o) | ||
_tuplify(o::LazySum) = LazySum(eltype(o.factors), o.factors, (o.operators...,)) | ||
_tuplify(o::AbstractOperator) = o | ||
|
||
""" | ||
schroedinger_dynamic_function(H::AbstractTimeDependentOperator) | ||
Creates a function `f(t, state) -> H(t)`. The `state` argument is ignored. | ||
This is the function expected by [`timeevolution.schroedinger_dynamic()`](@ref). | ||
""" | ||
function schroedinger_dynamic_function(H::AbstractTimeDependentOperator) | ||
_getfunc(op) = (@inline _tdop_schroedinger_wrapper(t, _) = (op)(t)) | ||
Htup = _tuplify(H) | ||
return _getfunc(Htup) | ||
end | ||
|
||
_tdopdagger(o) = dagger(o) | ||
function _tdopdagger(o::TimeDependentSum) | ||
# This is a kind-of-hacky, more efficient TimeDependentSum dagger operation | ||
# that requires that the original operator sticks around and is always | ||
# updated first (though this is checked). | ||
# Copies and conjugates the coefficients from the original op. | ||
o_ls = QuantumOpticsBase.static_operator(o) | ||
facs = o_ls.factors | ||
c1 = (t)->(@assert current_time(o) == t; conj(facs[1])) | ||
crest = (((_)->conj(facs[i])) for i in 2:length(facs)) | ||
odag = TimeDependentSum((c1, crest...), dagger(o_ls), current_time(o)) | ||
return odag | ||
end | ||
|
||
""" | ||
master_h_dynamic_function(H::AbstractTimeDependentOperator, Js) | ||
Returns a function `f(t, state) -> H(t), Js, dagger.(Js)`. | ||
The `state` argument is ignored. | ||
This is the function expected by [`timeevolution.master_h_dynamic()`](@ref), | ||
where `H` is represents the Hamiltonian and `Js` are the (time independent) jump | ||
operators. | ||
""" | ||
function master_h_dynamic_function(H::AbstractTimeDependentOperator, Js) | ||
Htup = _tuplify(H) | ||
Js_tup = ((_tuplify(J) for J in Js)...,) | ||
|
||
Jdags_tup = _tdopdagger.(Js_tup) | ||
function _getfunc(Hop, Jops, Jdops) | ||
return (@inline _tdop_master_wrapper_1(t, _) = ((Hop)(t), set_time!.(Jops, t), set_time!.(Jdops, t))) | ||
end | ||
return _getfunc(Htup, Js_tup, Jdags_tup) | ||
end | ||
|
||
""" | ||
master_nh_dynamic_function(Hnh::AbstractTimeDependentOperator, Js) | ||
Returns a function `f(t, state) -> Hnh(t), Hnh(t)', Js, dagger.(Js)`. | ||
The `state` argument is currently ignored. | ||
This is the function expected by [`timeevolution.master_nh_dynamic()`](@ref), | ||
where `Hnh` is represents the non-Hermitian Hamiltonian and `Js` are the | ||
(time independent) jump operators. | ||
""" | ||
function master_nh_dynamic_function(Hnh::AbstractTimeDependentOperator, Js) | ||
Hnhtup = _tuplify(Hnh) | ||
Js_tup = ((_tuplify(J) for J in Js)...,) | ||
|
||
Jdags_tup = _tdopdagger.(Js_tup) | ||
Htdagup = _tdopdagger(Hnhtup) | ||
|
||
function _getfunc(Hop, Hdop, Jops, Jdops) | ||
return (@inline _tdop_master_wrapper_2(t, _) = ((Hop)(t), (Hdop)(t), set_time!.(Jops, t), set_time!.(Jdops, t))) | ||
end | ||
return _getfunc(Hnhtup, Htdagup, Js_tup, Jdags_tup) | ||
end | ||
|
||
""" | ||
mcfw_dynamic_function(H, Js) | ||
Returns a function `f(t, state) -> H(t), Js, dagger.(Js)`. | ||
The `state` argument is currently ignored. | ||
This is the function expected by [`timeevolution.mcwf_dynamic()`](@ref), | ||
where `H` is represents the Hamiltonian and `Js` are the (time independent) jump | ||
operators. | ||
""" | ||
mcfw_dynamic_function(H, Js) = master_h_dynamic_function(H, Js) | ||
|
||
""" | ||
mcfw_nh_dynamic_function(Hnh, Js) | ||
Returns a function `f(t, state) -> Hnh(t), Js, dagger.(Js)`. | ||
The `state` argument is currently ignored. | ||
This is the function expected by [`timeevolution.mcwf_dynamic()`](@ref), | ||
where `Hnh` is represents the non-Hermitian Hamiltonian and `Js` are the (time | ||
independent) jump operators. | ||
""" | ||
mcfw_nh_dynamic_function(Hnh, Js) = master_h_dynamic_function(Hnh, Js) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
using Test | ||
using QuantumOptics | ||
|
||
@testset "time-dependent operators" begin | ||
|
||
b = FockBasis(7) | ||
|
||
a = destroy(b) | ||
|
||
H0 = number(b) | ||
Hd = (a + a') | ||
H = TimeDependentSum(1.0=>H0, cos=>Hd) | ||
|
||
ts = [0.0, 0.4] | ||
ts_half = 0.5 * ts | ||
|
||
_h(t, H0, Hd) = H0 + cos(t)*Hd | ||
_getf = (H0, Hd) -> (t,_) -> _h(t, H0, Hd) | ||
fman = _getf(H0, Hd) | ||
|
||
psi0 = basisstate(b, 1) | ||
ts_out, psis = timeevolution.schroedinger_dynamic(ts, psi0, H) | ||
# check this is not trivial | ||
@test !(psis[1].data ≈ psis[end].data) | ||
|
||
ts_out2, psis2 = timeevolution.schroedinger_dynamic(ts, psi0, fman) | ||
@test psis[end].data ≈ psis2[end].data | ||
|
||
_getf = (H0, Hd, a) -> (t,_) -> (_h(t, H0, Hd), (), ()) | ||
fman = _getf(H0, Hd, a) | ||
|
||
ts_out, rhos = timeevolution.master_dynamic(ts, psi0, H, []) | ||
ts_out2, rhos2 = timeevolution.master_dynamic(ts, psi0, fman) | ||
@test rhos[end].data ≈ rhos2[end].data | ||
|
||
Js = [TimeDependentSum(cos=>a), 0.01 * a', 0.01 * LazySum(a' * a)] | ||
Jdags = dagger.(Js) | ||
|
||
_js(t, a) = (cos(t)*a, 0.01*a', 0.01*a'*a) | ||
|
||
_getf = (H0, Hd, a) -> (t,_) -> (_h(t, H0, Hd), _js(t, a), dagger.(_js(t, a))) | ||
fman = _getf(H0, Hd, a) | ||
|
||
ts_out, psis = timeevolution.mcwf_dynamic(ts, psi0, H, Js; seed=0) | ||
ts_out2, psis2 = timeevolution.mcwf_dynamic(ts, psi0, fman; seed=0) | ||
@test psis[end].data ≈ psis2[end].data | ||
|
||
ts_out, rhos = timeevolution.master_dynamic(ts, psi0, H, Js) | ||
ts_out2, rhos2 = timeevolution.master_dynamic(ts, psi0, fman) | ||
@test rhos[end].data ≈ rhos2[end].data | ||
|
||
Hnh = H - 0.5im * sum(J' * J for J in Js) | ||
|
||
_getf = (H0, Hd, a) -> (t,_) -> ( | ||
_h(t, H0, Hd) - 0.5im * sum(dagger.(_js(t, a)) .* _js(t, a)), | ||
_js(t, a), | ||
dagger.(_js(t, a))) | ||
|
||
fman = _getf(H0, Hd, a) | ||
|
||
ts_out, psis = timeevolution.mcwf_nh_dynamic(ts, psi0, Hnh, Js; seed=0) | ||
ts_out2, psis2 = timeevolution.mcwf_nh_dynamic(ts, psi0, fman; seed=0) | ||
@test psis[end].data ≈ psis2[end].data | ||
|
||
_getf = (H0, Hd, a) -> (t,_) -> ( | ||
_h(t, H0, Hd) - 0.5im * sum(dagger.(_js(t, a)) .* _js(t, a)), | ||
_h(t, H0, Hd) + 0.5im * sum(dagger.(_js(t, a)) .* _js(t, a)), | ||
_js(t, a), | ||
dagger.(_js(t, a))) | ||
|
||
fman = _getf(H0, Hd, a) | ||
|
||
ts_out, rhos = timeevolution.master_nh_dynamic(ts, psi0, Hnh, Js) | ||
ts_out2, rhos2 = timeevolution.master_nh_dynamic(ts, psi0, fman) | ||
@test rhos[end].data ≈ rhos2[end].data | ||
|
||
# for sparse operators, we should not be allocating at each timestep | ||
allocs1 = @allocated timeevolution.schroedinger_dynamic(ts, psi0, H) | ||
allocs2 = @allocated timeevolution.schroedinger_dynamic(ts_half, psi0, H) | ||
@test allocs1 == allocs2 | ||
|
||
allocs1 = @allocated timeevolution.master_nh_dynamic(ts, psi0, Hnh, Js) | ||
allocs2 = @allocated timeevolution.master_nh_dynamic(ts_half, psi0, Hnh, Js) | ||
@test allocs1 == allocs2 | ||
|
||
end |