Skip to content

Commit b8105c5

Browse files
Working mesolve
1 parent c00a344 commit b8105c5

File tree

11 files changed

+212
-119
lines changed

11 files changed

+212
-119
lines changed

src/qobj/boolean_functions.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,6 @@ isunitary(U::QuantumObject{<:AbstractArray{T}}; kwargs...) where {T} =
9393
@doc raw"""
9494
isconstant(A::AbstractQuantumObject)
9595
96-
Test whether the [`AbstractQuantumObject`](@ref) `A` is constant in time. For a [`QuantumObject`](@ref), this function returns `false`, while for a [`QuantumObjectEvolution`](@ref), this function returns `true` if the operator is contant in time.
96+
Test whether the [`AbstractQuantumObject`](@ref) `A` is constant in time. For a [`QuantumObject`](@ref), this function returns `true`, while for a [`QuantumObjectEvolution`](@ref), this function returns `true` if the operator is contant in time.
9797
"""
9898
isconstant(A::AbstractQuantumObject) = isconstant(A.data)

src/qobj/eigsolve.jl

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -362,26 +362,24 @@ Solve the eigenvalue problem for a Liouvillian superoperator `L` using the Arnol
362362
- [1] Minganti, F., & Huybrechts, D. (2022). Arnoldi-Lindblad time evolution: Faster-than-the-clock algorithm for the spectrum of time-independent and Floquet open quantum systems. Quantum, 6, 649.
363363
"""
364364
function eigsolve_al(
365-
H::QuantumObject{MT1,HOpType},
365+
H::Union{AbstractQuantumObject{DT1,HOpType},Tuple},
366366
T::Real,
367367
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
368368
alg::OrdinaryDiffEqAlgorithm = Tsit5(),
369-
H_t::Union{Nothing,Function} = nothing,
370369
params::NamedTuple = NamedTuple(),
371370
ρ0::AbstractMatrix = rand_dm(prod(H.dims)).data,
372371
k::Int = 1,
373372
krylovdim::Int = min(10, size(H, 1)),
374373
maxiter::Int = 200,
375374
eigstol::Real = 1e-6,
376375
kwargs...,
377-
) where {MT1<:AbstractMatrix,HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}}
378-
L = liouvillian(H, c_ops)
376+
) where {DT1,HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}}
377+
L_evo = _mesolve_make_L_QobjEvo(H, c_ops)
379378
prob = mesolveProblem(
380-
L,
379+
L_evo,
381380
QuantumObject(ρ0, type = Operator, dims = H.dims),
382381
[zero(T), T];
383382
alg = alg,
384-
H_t = H_t,
385383
params = params,
386384
progress_bar = Val(false),
387385
kwargs...,
@@ -390,17 +388,17 @@ function eigsolve_al(
390388

391389
# prog = ProgressUnknown(desc="Applications:", showspeed = true, enabled=progress)
392390

393-
Lmap = ArnoldiLindbladIntegratorMap(eltype(MT1), size(L), integrator)
391+
Lmap = ArnoldiLindbladIntegratorMap(eltype(DT1), size(L_evo), integrator)
394392

395-
res = _eigsolve(Lmap, mat2vec(ρ0), L.type, L.dims, k, krylovdim, maxiter = maxiter, tol = eigstol)
393+
res = _eigsolve(Lmap, mat2vec(ρ0), L_evo.type, L_evo.dims, k, krylovdim, maxiter = maxiter, tol = eigstol)
396394
# finish!(prog)
397395

398396
vals = similar(res.values)
399397
vecs = similar(res.vectors)
400398

401399
for i in eachindex(res.values)
402400
vec = view(res.vectors, :, i)
403-
vals[i] = dot(vec, L.data, vec)
401+
vals[i] = dot(vec, L_evo.data, vec)
404402
@. vecs[:, i] = vec * exp(-1im * angle(vec[1]))
405403
end
406404

src/qobj/quantum_object.jl

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
#=
2-
This file defines:
3-
1. the QuantumObject (Qobj) structure
4-
2. all the type structures for QuantumObject
5-
Also support for fundamental functions in Julia standard library:
6-
- Base: show, length, size, eltype, getindex, setindex!, isequal, :(==), isapprox, Vector, Matrix
2+
This file defines the QuantumObject (Qobj) structure.
3+
It also implements the fundamental functions in Julia standard library:
74
- SparseArrays: sparse, nnz, nonzeros, rowvals, droptol!, dropzeros, dropzeros!, SparseVector, SparseMatrixCSC
85
=#
96

src/qobj/quantum_object_base.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
#=
2+
This file defines the AbstractQuantumObject structure, all the type structures for QuantumObject and fundamental functions in Julia standard library.
3+
Also support for fundamental functions in Julia standard library:
4+
- Base: show, length, size, eltype, getindex, setindex!, isequal, :(==), isapprox, Vector, Matrix
5+
=#
6+
17
export AbstractQuantumObject
28
export QuantumObjectType,
39
BraQuantumObject,

src/qobj/quantum_object_evo.jl

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=fal
8282
⎢⠀⠀⠀⠀⠂⡑⢄⠀⠀⠀⎥
8383
⎢⠀⠀⠀⠀⠀⠀⠂⡑⢄⠀⎥
8484
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠂⡑⎦
85+
````
8586
"""
8687
struct QuantumObjectEvolution{
8788
DT<:AbstractSciMLOperator,
@@ -94,8 +95,8 @@ struct QuantumObjectEvolution{
9495
end
9596

9697
# Make the QuantumObjectEvolution, with the option to pre-multiply by a scalar
97-
function QuantumObjectEvolution(op_func_list::Tuple, α::Union{Nothing,Number} = nothing)
98-
op, data = _generate_data(op_func_list, α)
98+
function QuantumObjectEvolution(op_func_list::Tuple, α::Union{Nothing,Number} = nothing; f::Function = identity)
99+
op, data = _QobjEvo_generate_data(op_func_list, α; f = f)
99100
dims = op.dims
100101
type = op.type
101102

@@ -106,23 +107,35 @@ function QuantumObjectEvolution(op_func_list::Tuple, α::Union{Nothing,Number} =
106107
return QuantumObjectEvolution(data, type, dims)
107108
end
108109

109-
QuantumObjectEvolution(op::QuantumObject, α::Union{Nothing,Number} = nothing) =
110+
QuantumObjectEvolution(op::QuantumObject, α::Union{Nothing,Number} = nothing; f::Function = identity) =
110111
QuantumObjectEvolution(_make_SciMLOperator(op, α), op.type, op.dims)
111112

112-
function QuantumObjectEvolution(op::QuantumObjectEvolution, α::Union{Nothing,Number} = nothing)
113+
function QuantumObjectEvolution(op::QuantumObjectEvolution, α::Union{Nothing,Number} = nothing; f::Function = identity)
114+
f !== identity && throw(ArgumentError("The function `f` is not supported for QuantumObjectEvolution inputs."))
113115
if α isa Nothing
114116
return QuantumObjectEvolution(op.data, op.type, op.dims)
115117
end
116118
return QuantumObjectEvolution* op.data, op.type, op.dims)
117119
end
118120

119-
@generated function _generate_data(op_func_list::Tuple, α)
121+
#=
122+
_QobjEvo_generate_data(op_func_list::Tuple, α; f::Function=identity)
123+
124+
Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` object. The `op_func_list` is a tuple of tuples or operators. Each element of the tuple can be a tuple with two elements (operator, function) or an operator. The function is used to generate the time-dependent coefficients for the operators. The `α` parameter is used to pre-multiply the operators by a scalar. The `f` parameter is used to pre-applying a function to the operators before converting them to SciML operators. During the parsing, the dimensions of the operators are checked to be the same, and all the constant operators are summed together.
125+
126+
# Arguments
127+
- `op_func_list::Tuple`: A tuple of tuples or operators.
128+
- `α`: A scalar to pre-multiply the operators.
129+
- `f::Function=identity`: A function to pre-apply to the operators.
130+
=#
131+
@generated function _QobjEvo_generate_data(op_func_list::Tuple, α; f::Function = identity)
120132
op_func_list_types = op_func_list.parameters
121133
N = length(op_func_list_types)
122134

123135
dims_expr = ()
124136
first_op = nothing
125137
data_expr = :(0)
138+
qobj_expr_const = :(0)
126139

127140
for i in 1:N
128141
op_func_type = op_func_list_types[i]
@@ -136,49 +149,54 @@ end
136149
),
137150
)
138151

152+
op = :(op_func_list[$i][1])
139153
data_type = op_type.parameters[1]
140-
dims_expr = (dims_expr..., :(op_func_list[$i][1].dims))
154+
dims_expr = (dims_expr..., :($op.dims))
141155
if i == 1
142-
first_op = :(op_func_list[$i][1])
156+
first_op = :(f($op))
143157
end
158+
data_expr = :($data_expr + _make_SciMLOperator(op_func_list[$i], α, f = f))
144159
else
145160
op_type = op_func_type
146161
(isoper(op_type) || issuper(op_type)) ||
147162
throw(ArgumentError("The element must be a Operator or SuperOperator."))
148163

149164
data_type = op_type.parameters[1]
150165
dims_expr = (dims_expr..., :(op_func_list[$i].dims))
151-
152166
if i == 1
153167
first_op = :(op_func_list[$i])
154168
end
169+
qobj_expr_const = :($qobj_expr_const + f(op_func_list[$i]))
155170
end
156-
data_expr = :($data_expr + _make_SciMLOperator(op_func_list[$i], α))
157171
end
158172

173+
data_expr_const = :(_make_SciMLOperator($qobj_expr_const, α))
174+
159175
quote
160176
dims = tuple($(dims_expr...))
161177

162178
length(unique(dims)) == 1 || throw(ArgumentError("The dimensions of the operators must be the same."))
163179

164-
return $first_op, $data_expr
180+
data_expr = $data_expr_const + $data_expr
181+
182+
return $first_op, data_expr
165183
end
166184
end
167185

168-
function _make_SciMLOperator(op_func::Tuple, α)
186+
function _make_SciMLOperator(op_func::Tuple, α; f::Function = identity)
169187
T = eltype(op_func[1])
170188
update_func = (a, u, p, t) -> op_func[2](p, t)
171189
if α isa Nothing
172-
return ScalarOperator(zero(T), update_func) * MatrixOperator(op_func[1].data)
190+
return ScalarOperator(zero(T), update_func) * MatrixOperator(f(op_func[1]).data)
173191
end
174-
return ScalarOperator(zero(T), update_func) * MatrixOperator* op_func[1].data)
192+
return ScalarOperator(zero(T), update_func) * MatrixOperator* f(op_func[1]).data)
175193
end
176194

177-
function _make_SciMLOperator(op::QuantumObject, α)
195+
function _make_SciMLOperator(op::QuantumObject, α; f::Function = identity)
178196
if α isa Nothing
179-
return MatrixOperator(op.data)
197+
return MatrixOperator(f(op).data)
180198
end
181-
return MatrixOperator* op.data)
199+
return MatrixOperator* f(op.data))
182200
end
183201

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

src/qobj/synonyms.jl

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,91 @@ 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,AbstractQuantumObject}, α::Union{Nothing,Number}=nothing)
21+
QobjEvo(op_func_list::Union{Tuple,AbstractQuantumObject}, α::Union{Nothing,Number}=nothing; f::Function=identity)
2222
2323
Generate [`QuantumObjectEvolution`](@ref)
2424
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 `α`.
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 `α`. The `f` parameter is used to pre-apply a function to the operators before converting them to SciML operators.
26+
27+
# Arguments
28+
- `op_func_list::Union{Tuple,AbstractQuantumObject}`: A tuple of tuples or operators.
29+
- `α::Union{Nothing,Number}=nothing`: A scalar to pre-multiply the operators.
30+
- `f::Function=identity`: A function to pre-apply to the operators.
31+
32+
!!! warning "Beware of type-stability!"
33+
Please note that, unlike QuTiP, this function doesn't support `op_func_list` as `Vector` type. This is related to the type-stability issue. See the Section [The Importance of Type-Stability](@ref doc:Type-Stability) for more details.
34+
35+
# Examples
36+
This operator can be initialized in the same way as the QuTiP `QobjEvo` object. For example
37+
```
38+
julia> a = tensor(destroy(10), qeye(2))
39+
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false
40+
20×20 SparseMatrixCSC{ComplexF64, Int64} with 18 stored entries:
41+
⎡⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⎤
42+
⎢⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⎥
43+
⎢⠀⠀⠀⠀⠀⠑⢄⠀⠀⠀⎥
44+
⎢⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⎥
45+
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⎦
46+
47+
julia> σm = tensor(qeye(10), sigmam())
48+
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false
49+
20×20 SparseMatrixCSC{ComplexF64, Int64} with 10 stored entries:
50+
⎡⠂⡀⠀⠀⠀⠀⠀⠀⠀⠀⎤
51+
⎢⠀⠀⠂⡀⠀⠀⠀⠀⠀⠀⎥
52+
⎢⠀⠀⠀⠀⠂⡀⠀⠀⠀⠀⎥
53+
⎢⠀⠀⠀⠀⠀⠀⠂⡀⠀⠀⎥
54+
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠂⡀⎦
55+
56+
julia> coef1(p, t) = exp(-1im * t)
57+
coef1 (generic function with 1 method)
58+
59+
julia> coef2(p, t) = sin(t)
60+
coef2 (generic function with 1 method)
61+
62+
julia> op1 = QobjEvo(((a, coef1), (σm, coef2)))
63+
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true
64+
(ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20))
65+
```
66+
67+
We can also concretize the operator at a specific time `t`
68+
```
69+
julia> op1(0.1)
70+
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false
71+
20×20 SparseMatrixCSC{ComplexF64, Int64} with 28 stored entries:
72+
⎡⠂⡑⢄⠀⠀⠀⠀⠀⠀⠀⎤
73+
⎢⠀⠀⠂⡑⢄⠀⠀⠀⠀⠀⎥
74+
⎢⠀⠀⠀⠀⠂⡑⢄⠀⠀⠀⎥
75+
⎢⠀⠀⠀⠀⠀⠀⠂⡑⢄⠀⎥
76+
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠂⡑⎦
77+
```
78+
79+
It also supports parameter-dependent time evolution
80+
```
81+
julia> coef1(p, t) = exp(-1im * p.ω1 * t)
82+
coef1 (generic function with 1 method)
83+
84+
julia> coef2(p, t) = sin(p.ω2 * t)
85+
coef2 (generic function with 1 method)
86+
87+
julia> op1 = QobjEvo(((a, coef1), (σm, coef2)))
88+
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true
89+
(ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20))
90+
91+
julia> p = (ω1 = 1.0, ω2 = 0.5)
92+
(ω1 = 1.0, ω2 = 0.5)
93+
94+
julia> op1(p, 0.1)
95+
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false
96+
20×20 SparseMatrixCSC{ComplexF64, Int64} with 28 stored entries:
97+
⎡⠂⡑⢄⠀⠀⠀⠀⠀⠀⠀⎤
98+
⎢⠀⠀⠂⡑⢄⠀⠀⠀⠀⠀⎥
99+
⎢⠀⠀⠀⠀⠂⡑⢄⠀⠀⠀⎥
100+
⎢⠀⠀⠀⠀⠀⠀⠂⡑⢄⠀⎥
101+
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠂⡑⎦
102+
````
26103
"""
27-
QobjEvo(op_func_list::Union{Tuple,AbstractQuantumObject}, α::Union{Nothing,Number} = nothing) =
28-
QuantumObjectEvolution(op_func_list, α)
104+
QobjEvo(op_func_list::Union{Tuple,AbstractQuantumObject}, α::Union{Nothing,Number} = nothing; f::Function = identity) =
105+
QuantumObjectEvolution(op_func_list, α; f = f)
29106

30107
@doc raw"""
31108
shape(A::QuantumObject)

src/steadystate.jl

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -179,15 +179,15 @@ _steadystate(
179179
kwargs...,
180180
) where {T} = throw(
181181
ArgumentError(
182-
"The initial state ψ0 is required for SteadyStateODESolver, use the following call instead: `steadystate(H, ψ0, tspan, c_ops)`.",
182+
"The initial state ψ0 is required for SteadyStateODESolver, use the following call instead: `steadystate(H, ψ0, tmax, c_ops)`.",
183183
),
184184
)
185185

186186
@doc raw"""
187187
steadystate(
188188
H::QuantumObject,
189189
ψ0::QuantumObject,
190-
tspan::Real = Inf,
190+
tmax::Real = Inf,
191191
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
192192
solver::SteadyStateODESolver = SteadyStateODESolver(),
193193
reltol::Real = 1.0e-8,
@@ -212,48 +212,34 @@ or
212212
# Parameters
213213
- `H::QuantumObject`: The Hamiltonian or the Liouvillian of the system.
214214
- `ψ0::QuantumObject`: The initial state of the system.
215-
- `tspan::Real=Inf`: The final time step for the steady state problem.
215+
- `tmax::Real=Inf`: The final time step for the steady state problem.
216216
- `c_ops::Union{Nothing,AbstractVector,Tuple}=nothing`: The list of the collapse operators.
217217
- `solver::SteadyStateODESolver=SteadyStateODESolver()`: see [`SteadyStateODESolver`](@ref) for more details.
218218
- `reltol::Real=1.0e-8`: Relative tolerance in steady state terminate condition and solver adaptive timestepping.
219219
- `abstol::Real=1.0e-10`: Absolute tolerance in steady state terminate condition and solver adaptive timestepping.
220220
- `kwargs...`: The keyword arguments for the ODEProblem.
221221
"""
222222
function steadystate(
223-
H::QuantumObject{MT1,HOpType},
224-
ψ0::QuantumObject{<:AbstractArray{T2},StateOpType},
225-
tspan::Real = Inf,
223+
H::QuantumObject{DT1,HOpType},
224+
ψ0::QuantumObject{DT2,StateOpType},
225+
tmax::Real = Inf,
226226
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
227227
solver::SteadyStateODESolver = SteadyStateODESolver(),
228228
reltol::Real = 1.0e-8,
229229
abstol::Real = 1.0e-10,
230230
kwargs...,
231231
) where {
232-
MT1<:AbstractMatrix,
233-
T2,
232+
DT1,
233+
DT2,
234234
HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},
235235
StateOpType<:Union{KetQuantumObject,OperatorQuantumObject},
236236
}
237-
check_dims(H, ψ0)
238-
239-
N = prod(H.dims)
240-
u0 = sparse_to_dense(_CType(ψ0), mat2vec(ket2dm(ψ0).data))
241-
242-
L = MatrixOperator(liouvillian(H, c_ops).data)
243-
244237
ftype = _FType(ψ0)
245-
prob = ODEProblem{true}(L, u0, (ftype(0), ftype(tspan))) # Convert tspan to support GPUs and avoid type instabilities for OrdinaryDiffEq.jl
246-
sol = solve(
247-
prob,
248-
solver.alg;
249-
callback = TerminateSteadyState(abstol, reltol, _steadystate_ode_condition),
250-
reltol = reltol,
251-
abstol = abstol,
252-
kwargs...,
253-
)
238+
cb = TerminateSteadyState(abstol, reltol, _steadystate_ode_condition)
239+
sol = mesolve(H, ψ0, [ftype(0), ftype(tmax)], c_ops, progress_bar = Val(false), callback = cb)
254240

255-
ρss = reshape(sol.u[end], N, N)
256-
return QuantumObject(ρss, Operator, H.dims)
241+
ρss = sol.states[end]
242+
return ρss
257243
end
258244

259245
function _steadystate_ode_condition(integrator, abstol, reltol, min_t)

0 commit comments

Comments
 (0)