Skip to content

Commit 213f40b

Browse files
First working case of sesolve
1 parent a1c4c16 commit 213f40b

File tree

6 files changed

+101
-76
lines changed

6 files changed

+101
-76
lines changed

src/qobj/boolean_functions.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export isunitary
1111
Checks if the [`QuantumObject`](@ref) `A` is a [`BraQuantumObject`](@ref). Default case returns `false` for any other inputs.
1212
"""
1313
isbra(A::QuantumObject) = A.type isa BraQuantumObject
14+
isbra(::Type{QuantumObject{DT,BraQuantumObject,N}}) where {DT,N} = true
1415
isbra(A) = false # default case
1516

1617
@doc raw"""
@@ -19,6 +20,7 @@ isbra(A) = false # default case
1920
Checks if the [`QuantumObject`](@ref) `A` is a [`KetQuantumObject`](@ref). Default case returns `false` for any other inputs.
2021
"""
2122
isket(A::QuantumObject) = A.type isa KetQuantumObject
23+
isket(::Type{QuantumObject{DT,KetQuantumObject,N}}) where {DT,N} = true
2224
isket(A) = false # default case
2325

2426
@doc raw"""
@@ -27,6 +29,7 @@ isket(A) = false # default case
2729
Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`OperatorQuantumObject`](@ref). Default case returns `false` for any other inputs.
2830
"""
2931
isoper(A::AbstractQuantumObject) = A.type isa OperatorQuantumObject
32+
isoper(::Type{QuantumObject{DT,OperatorQuantumObject,N}}) where {DT,N} = true
3033
isoper(A) = false # default case
3134

3235
@doc raw"""
@@ -35,6 +38,7 @@ isoper(A) = false # default case
3538
Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorBraQuantumObject`](@ref). Default case returns `false` for any other inputs.
3639
"""
3740
isoperbra(A::QuantumObject) = A.type isa OperatorBraQuantumObject
41+
isoperbra(::Type{QuantumObject{DT,OperatorBraQuantumObject,N}}) where {DT,N} = true
3842
isoperbra(A) = false # default case
3943

4044
@doc raw"""
@@ -43,6 +47,7 @@ isoperbra(A) = false # default case
4347
Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorKetQuantumObject`](@ref). Default case returns `false` for any other inputs.
4448
"""
4549
isoperket(A::QuantumObject) = A.type isa OperatorKetQuantumObject
50+
isoperket(::Type{QuantumObject{DT,OperatorKetQuantumObject,N}}) where {DT,N} = true
4651
isoperket(A) = false # default case
4752

4853
@doc raw"""
@@ -51,6 +56,7 @@ isoperket(A) = false # default case
5156
Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`SuperOperatorQuantumObject`](@ref). Default case returns `false` for any other inputs.
5257
"""
5358
issuper(A::AbstractQuantumObject) = A.type isa SuperOperatorQuantumObject
59+
issuper(::Type{QuantumObject{DT,SuperOperatorQuantumObject,N}}) where {DT,N} = true
5460
issuper(A) = false # default case
5561

5662
@doc raw"""

src/qobj/eigsolve.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ function eigsolve_al(
378378
L = liouvillian(H, c_ops)
379379
prob = mesolveProblem(
380380
L,
381-
QuantumObject(ρ0, type=Operator, dims = H.dims),
381+
QuantumObject(ρ0, type = Operator, dims = H.dims),
382382
[zero(T), T];
383383
alg = alg,
384384
H_t = H_t,

src/qobj/quantum_object_evo.jl

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,14 @@ struct QuantumObjectEvolution{DT<:AbstractSciMLOperator,ObjType<:QuantumObjectTy
9090
dims::SVector{N,Int}
9191
end
9292

93-
function QuantumObjectEvolution(op_func_list::Tuple)
93+
# Make the QuantumObjectEvolution, with the option to pre-multiply by a scalar
94+
function QuantumObjectEvolution(op_func_list::Tuple, α = true)
9495
op = _get_op_func_first(op_func_list)
9596
dims = op.dims
9697
type = op.type
9798
T = eltype(op)
98-
data = sum(op_func_list) do op_func
99-
if op_func isa Tuple
100-
return ScalarOperator(zero(T), update_func = (a, u, p, t) -> op_func[2](p, t)) * MatrixOperator(op_func[1].data)
101-
else
102-
return MatrixOperator(op_func.data)
103-
end
104-
end
99+
100+
data = _generate_data(T, op_func_list, α)
105101

106102
# Preallocate the SciMLOperator cache using a dense vector as a reference
107103
v0 = sparse_to_dense(similar(op.data, size(op, 1)))
@@ -110,31 +106,75 @@ function QuantumObjectEvolution(op_func_list::Tuple)
110106
return QuantumObjectEvolution(data, type, dims)
111107
end
112108

113-
QuantumObjectEvolution(op::QuantumObject) = QuantumObjectEvolution(MatrixOperator(op.data), op.type, op.dims)
114-
115-
function _get_op_func_first(op_func_list)
116-
dims_type_op = map(op_func_list) do op_func
117-
if op_func isa Tuple
118-
length(op_func) == 2 || throw(ArgumentError("The tuple must have two elements."))
119-
((isoper(op_func[1]) || issuper(op_func[1])) && op_func[2] isa Function) || throw(
109+
QuantumObjectEvolution(op::QuantumObject, α = true) =
110+
QuantumObjectEvolution(MatrixOperator* op.data), op.type, op.dims)
111+
112+
@generated function _get_op_func_first(op_func_list::Tuple)
113+
op_func_list_types = op_func_list.parameters
114+
N = length(op_func_list_types)
115+
T = ()
116+
dims_expr = ()
117+
first_op = nothing
118+
for i in 1:N
119+
op_func_type = op_func_list_types[i]
120+
if op_func_type <: Tuple
121+
length(op_func_type.parameters) == 2 || throw(ArgumentError("The tuple must have two elements."))
122+
op_type = op_func_type.parameters[1]
123+
func_type = op_func_type.parameters[2]
124+
((isoper(op_type) || issuper(op_type)) && func_type <: Function) || throw(
120125
ArgumentError(
121126
"The first element must be a Operator or SuperOperator, and the second element must be a function.",
122127
),
123128
)
124-
return (op_func[1].dims, op_func[1].type, op_func[1])
129+
data_type = op_type.parameters[1]
130+
T = (T..., eltype(data_type))
131+
dims_expr = (dims_expr..., :(op_func_list[$i][1].dims))
132+
if i == 1
133+
first_op = :(op_func_list[$i][1])
134+
end
125135
else
126-
(isoper(op_func) || issuper(op_func)) || throw(ArgumentError("The element must be a QuantumObject."))
127-
return (op_func.dims, op_func.type, op_func)
136+
op_type = op_func_type
137+
(isoper(op_type) || issuper(op_type)) ||
138+
throw(ArgumentError("The element must be a Operator or SuperOperator."))
139+
data_type = op_type.parameters[1]
140+
T = (T..., eltype(data_type))
141+
dims_expr = (dims_expr..., :(op_func_list[$i].dims))
142+
if i == 1
143+
first_op = :(op_func_list[$i])
144+
end
128145
end
129146
end
130147

131-
length(unique(getindex.(dims_type_op, 1))) == 1 ||
132-
throw(ArgumentError("The dimensions of the operators must be the same."))
148+
length(unique(T)) == 1 || throw(ArgumentError("The types of the operators must be the same."))
149+
150+
quote
151+
dims = tuple($(dims_expr...))
133152

134-
length(unique(getindex.(dims_type_op, 2))) == 1 ||
135-
throw(ArgumentError("The types of the operators must be the same."))
153+
length(unique(dims)) == 1 || throw(ArgumentError("The dimensions of the operators must be the same."))
136154

137-
return dims_type_op[1][3]
155+
return $first_op
156+
end
157+
end
158+
159+
@generated function _generate_data(T, op_func_list::Tuple, α)
160+
op_func_list_types = op_func_list.parameters
161+
N = length(op_func_list_types)
162+
data_expr = :(0)
163+
for i in 1:N
164+
op_func_type = op_func_list_types[i]
165+
if op_func_type <: Tuple
166+
data_expr = :(
167+
$data_expr +
168+
ScalarOperator(zero(T), op_func_list[$i][2]) * MatrixOperator* op_func_list[$i][1].data)
169+
)
170+
else
171+
data_expr = :($data_expr + MatrixOperator* op_func_list[$i].data))
172+
end
173+
end
174+
175+
quote
176+
return $data_expr
177+
end
138178
end
139179

140180
function (QO::QuantumObjectEvolution)(p, t)

src/qobj/superoperators.jl

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,13 @@ the same function is applied multiple times with a known Hilbert space dimension
8989
9090
See also [`spre`](@ref) and [`spost`](@ref).
9191
"""
92-
function lindblad_dissipator(
93-
O::QuantumObject{DT,OperatorQuantumObject},
94-
Id_cache = I(size(O, 1)),
95-
) where {DT}
92+
function lindblad_dissipator(O::QuantumObject{DT,OperatorQuantumObject}, Id_cache = I(size(O, 1))) where {DT}
9693
Od_O = O' * O
9794
return sprepost(O, O') - spre(Od_O, Id_cache) / 2 - spost(Od_O, Id_cache) / 2
9895
end
9996

10097
# It is already a SuperOperator
101-
lindblad_dissipator(O::QuantumObject{DT,SuperOperatorQuantumObject}, Id_cache=nothing) where {DT} = O
98+
lindblad_dissipator(O::QuantumObject{DT,SuperOperatorQuantumObject}, Id_cache = nothing) where {DT} = O
10299

103100
@doc raw"""
104101
liouvillian(H::QuantumObject, c_ops::Union{Nothing,AbstractVector,Tuple}=nothing, Id_cache=I(prod(H.dims)))

src/qobj/synonyms.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ Note that this functions is same as `QuantumObject(A; type=type, dims=dims)`.
1818
Qobj(A; kwargs...) = QuantumObject(A; kwargs...)
1919

2020
@doc raw"""
21-
QobjEvo(op_func_list::Union{Tuple,QuantumObject})
21+
QobjEvo(op_func_list::Union{Tuple,QuantumObject}, α::Real=true)
2222
2323
Generate [`QuantumObjectEvolution`](@ref)
2424
25-
Note that this functions is same as `QuantumObjectEvolution(op_func_list)`.
25+
Note that this functions is same as `QuantumObjectEvolution(op_func_list)`. If `α` is provided, all the operators in `op_func_list` will be pre-multiplied by `α`.
2626
"""
27-
QobjEvo(op_func_list::Union{Tuple,QuantumObject}) = QuantumObjectEvolution(op_func_list)
27+
QobjEvo(op_func_list::Union{Tuple,QuantumObject}, α = true) = QuantumObjectEvolution(op_func_list, α)
2828

2929
@doc raw"""
3030
shape(A::QuantumObject)

src/time_evolution/sesolve.jl

Lines changed: 26 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ function _save_func_sesolve(integrator)
44
internal_params = integrator.p
55
progr = internal_params.progr
66

7-
if !internal_params.is_empty_e_ops
7+
if internal_params.is_empty_e_ops
88
e_ops = internal_params.e_ops
99
expvals = internal_params.expvals
1010

@@ -44,14 +44,13 @@ function _generate_sesolve_kwargs(e_ops, progress_bar::Val{false}, t_l, kwargs)
4444
end
4545

4646
@doc raw"""
47-
sesolveProblem(H::QuantumObject,
48-
ψ0::QuantumObject,
49-
tlist::AbstractVector;
50-
alg::OrdinaryDiffEqAlgorithm=Tsit5()
51-
e_ops::Union{Nothing,AbstractVector,Tuple} = nothing,
52-
H_t::Union{Nothing,Function,TimeDependentOperatorSum}=nothing,
53-
params::NamedTuple=NamedTuple(),
54-
progress_bar::Union{Val,Bool}=Val(true),
47+
sesolveProblem(H,
48+
ψ0,
49+
tlist;
50+
alg=Tsit5()
51+
e_ops = nothing,
52+
params=NamedTuple(),
53+
progress_bar=Val(true),
5554
kwargs...)
5655
5756
Generates the ODEProblem for the Schrödinger time evolution of a quantum system:
@@ -62,7 +61,7 @@ Generates the ODEProblem for the Schrödinger time evolution of a quantum system
6261
6362
# Arguments
6463
65-
- `H::QuantumObject`: The Hamiltonian of the system ``\hat{H}``.
64+
- `H::Union{QuantumObject,Tuple}`: The Hamiltonian of the system ``\hat{H}``.
6665
- `ψ0::QuantumObject`: The initial state of the system ``|\psi(0)\rangle``.
6766
- `tlist::AbstractVector`: The time list of the evolution.
6867
- `alg::OrdinaryDiffEqAlgorithm`: The algorithm used for the time evolution.
@@ -85,47 +84,43 @@ Generates the ODEProblem for the Schrödinger time evolution of a quantum system
8584
- `prob`: The `ODEProblem` for the Schrödinger time evolution of the system.
8685
"""
8786
function sesolveProblem(
88-
H::Union{QuantumObject{MT1,OperatorQuantumObject},Tuple},
89-
ψ0::QuantumObject{<:AbstractVector{T2},KetQuantumObject},
87+
H::Union{QuantumObject{DT1,OperatorQuantumObject},Tuple},
88+
ψ0::QuantumObject{DT2,KetQuantumObject},
9089
tlist::AbstractVector;
91-
alg::OrdinaryDiffEqAlgorithm = Tsit5(),
9290
e_ops::Union{Nothing,AbstractVector,Tuple} = nothing,
93-
H_t::Union{Nothing,Function,TimeDependentOperatorSum} = nothing,
9491
params::NamedTuple = NamedTuple(),
9592
progress_bar::Union{Val,Bool} = Val(true),
9693
kwargs...,
97-
) where {MT1<:AbstractMatrix,T2}
98-
check_dims(H, ψ0)
99-
94+
) where {DT1,DT2}
10095
haskey(kwargs, :save_idxs) &&
10196
throw(ArgumentError("The keyword argument \"save_idxs\" is not supported in QuantumToolbox."))
10297

103-
is_time_dependent = !(H_t isa Nothing)
104-
10598
ϕ0 = sparse_to_dense(_CType(ψ0), get_data(ψ0)) # Convert it to dense vector with complex element type
10699

107100
t_l = convert(Vector{_FType(ψ0)}, tlist) # Convert it to support GPUs and avoid type instabilities for OrdinaryDiffEq.jl
108101

109-
U = -1im * get_data(H)
102+
H_evo = QobjEvo(H, -1im) # pre-multiply by -i
103+
isoper(H_evo) || throw(ArgumentError("The Hamiltonian must be an Operator."))
104+
check_dims(H_evo, ψ0)
105+
U = H_evo.data
106+
110107
progr = ProgressBar(length(t_l), enable = getVal(progress_bar))
111108

112109
if e_ops isa Nothing
113110
expvals = Array{ComplexF64}(undef, 0, length(t_l))
114-
e_ops2 = MT1[]
111+
e_ops_data = ()
115112
is_empty_e_ops = true
116113
else
117114
expvals = Array{ComplexF64}(undef, length(e_ops), length(t_l))
118-
e_ops2 = get_data.(e_ops)
115+
e_ops_data = get_data.(e_ops)
119116
is_empty_e_ops = isempty(e_ops)
120117
end
121118

122119
p = (
123-
U = U,
124-
e_ops = e_ops2,
120+
e_ops = e_ops_data,
125121
expvals = expvals,
126122
progr = progr,
127-
Hdims = H.dims,
128-
H_t = H_t,
123+
Hdims = H_evo.dims,
129124
times = t_l,
130125
is_empty_e_ops = is_empty_e_ops,
131126
params...,
@@ -136,10 +131,8 @@ function sesolveProblem(
136131
kwargs2 = merge(default_values, kwargs)
137132
kwargs3 = _generate_sesolve_kwargs(e_ops, makeVal(progress_bar), t_l, kwargs2)
138133

139-
dudt! = is_time_dependent ? sesolve_td_dudt! : sesolve_ti_dudt!
140-
141134
tspan = (t_l[1], t_l[end])
142-
return ODEProblem{true,FullSpecialize}(dudt!, ϕ0, tspan, p; kwargs3...)
135+
return ODEProblem{true}(U, ϕ0, tspan, p; kwargs3...)
143136
end
144137

145138
@doc raw"""
@@ -184,27 +177,16 @@ Time evolution of a closed quantum system using the Schrödinger equation:
184177
- `sol::TimeEvolutionSol`: The solution of the time evolution. See also [`TimeEvolutionSol`](@ref)
185178
"""
186179
function sesolve(
187-
H::QuantumObject{MT1,OperatorQuantumObject},
188-
ψ0::QuantumObject{<:AbstractVector{T2},KetQuantumObject},
180+
H::Union{QuantumObject{DT1,OperatorQuantumObject},Tuple},
181+
ψ0::QuantumObject{DT2,KetQuantumObject},
189182
tlist::AbstractVector;
190183
alg::OrdinaryDiffEqAlgorithm = Tsit5(),
191184
e_ops::Union{Nothing,AbstractVector,Tuple} = nothing,
192-
H_t::Union{Nothing,Function,TimeDependentOperatorSum} = nothing,
193185
params::NamedTuple = NamedTuple(),
194186
progress_bar::Union{Val,Bool} = Val(true),
195187
kwargs...,
196-
) where {MT1<:AbstractMatrix,T2}
197-
prob = sesolveProblem(
198-
H,
199-
ψ0,
200-
tlist;
201-
alg = alg,
202-
e_ops = e_ops,
203-
H_t = H_t,
204-
params = params,
205-
progress_bar = progress_bar,
206-
kwargs...,
207-
)
188+
) where {DT1,DT2}
189+
prob = sesolveProblem(H, ψ0, tlist; e_ops = e_ops, params = params, progress_bar = progress_bar, kwargs...)
208190

209191
return sesolve(prob, alg)
210192
end

0 commit comments

Comments
 (0)