Skip to content

Commit 380d017

Browse files
authored
Support time-dependent lindblad_dissipator (#280)
2 parents 33944ac + 66566e7 commit 380d017

File tree

12 files changed

+176
-65
lines changed

12 files changed

+176
-65
lines changed

docs/src/api.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ SuperOperatorQuantumObject
2828
SuperOperator
2929
QuantumObject
3030
QuantumObjectEvolution
31-
size
32-
eltype
33-
length
31+
Base.size
32+
Base.eltype
33+
Base.length
34+
SciMLOperators.cache_operator
3435
```
3536

3637
## [Qobj boolean functions](@id doc-API:Qobj-boolean-functions)
@@ -46,7 +47,8 @@ LinearAlgebra.ishermitian
4647
LinearAlgebra.issymmetric
4748
LinearAlgebra.isposdef
4849
isunitary
49-
isconstant
50+
SciMLOperators.iscached
51+
SciMLOperators.isconstant
5052
```
5153

5254
## [Qobj arithmetic and attributes](@id doc-API:Qobj-arithmetic-and-attributes)

src/QuantumToolbox.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ module QuantumToolbox
33
# Re-export:
44
# 1. StaticArraysCore.SVector for the type of dims
55
# 2. basic functions in LinearAlgebra and SparseArrays
6+
# 3. some functions in SciMLOperators
67
import Reexport: @reexport
78
@reexport import StaticArraysCore: SVector
89
@reexport using LinearAlgebra
910
@reexport using SparseArrays
11+
@reexport import SciMLOperators: cache_operator, iscached, isconstant
1012

1113
# other functions in LinearAlgebra
1214
import LinearAlgebra: BlasReal, BlasInt, BlasFloat, BlasComplex, checksquare
1315
import LinearAlgebra.BLAS: @blasfunc
1416
import LinearAlgebra.LAPACK: hseqr!
1517

16-
# SciML packages (for OrdinaryDiffEq and LinearSolve)
18+
# SciML packages (for QobjEvo, OrdinaryDiffEq, and LinearSolve)
1719
import SciMLBase:
1820
solve,
1921
solve!,
@@ -34,16 +36,15 @@ import SciMLBase:
3436
DiscreteCallback
3537
import StochasticDiffEq: StochasticDiffEqAlgorithm, SRA1
3638
import SciMLOperators:
39+
SciMLOperators,
3740
AbstractSciMLOperator,
3841
MatrixOperator,
3942
ScalarOperator,
4043
ScaledOperator,
4144
AddedOperator,
4245
IdentityOperator,
43-
cache_operator,
4446
update_coefficients!,
45-
concretize,
46-
isconstant
47+
concretize
4748
import LinearSolve: LinearProblem, SciMLLinearSolveAlgorithm, KrylovJL_MINRES, KrylovJL_GMRES
4849
import DiffEqBase: get_tstops
4950
import DiffEqCallbacks: PeriodicCallback, PresetTimeCallback, TerminateSteadyState

src/qobj/boolean_functions.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ All boolean functions for checking the data or type in `QuantumObject`
33
=#
44

55
export isket, isbra, isoper, isoperbra, isoperket, issuper
6-
export isunitary, isconstant
6+
export isunitary
77

88
@doc raw"""
99
isbra(A)
@@ -91,8 +91,15 @@ isunitary(U::QuantumObject{<:AbstractArray{T}}; kwargs...) where {T} =
9191
isoper(U) ? isapprox(U.data * U.data', I(size(U, 1)); kwargs...) : false
9292

9393
@doc raw"""
94-
isconstant(A::AbstractQuantumObject)
94+
SciMLOperators.iscached(A::AbstractQuantumObject)
95+
96+
Test whether the [`AbstractQuantumObject`](@ref) `A` has preallocated caches for inplace evaluations.
97+
"""
98+
SciMLOperators.iscached(A::AbstractQuantumObject) = iscached(A.data)
99+
100+
@doc raw"""
101+
SciMLOperators.isconstant(A::AbstractQuantumObject)
95102
96103
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.
97104
"""
98-
isconstant(A::AbstractQuantumObject) = isconstant(A.data)
105+
SciMLOperators.isconstant(A::AbstractQuantumObject) = isconstant(A.data)

src/qobj/quantum_object.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ This file defines the QuantumObject (Qobj) structure.
33
It also implements the fundamental functions in Julia standard library:
44
- Base: show, real, imag, Vector, Matrix
55
- SparseArrays: sparse, nnz, nonzeros, rowvals, droptol!, dropzeros, dropzeros!, SparseVector, SparseMatrixCSC
6+
- SciMLOperators: cache_operator
67
=#
78

89
export QuantumObject
@@ -167,6 +168,41 @@ SparseArrays.droptol!(A::QuantumObject{<:AbstractSparseArray}, tol::Real) = (dro
167168
SparseArrays.dropzeros(A::QuantumObject{<:AbstractSparseArray}) = QuantumObject(dropzeros(A.data), A.type, A.dims)
168169
SparseArrays.dropzeros!(A::QuantumObject{<:AbstractSparseArray}) = (dropzeros!(A.data); return A)
169170

171+
@doc raw"""
172+
SciMLOperators.cached_operator(L::AbstractQuantumObject, u)
173+
174+
Allocate caches for [`AbstractQuantumObject`](@ref) `L` for in-place evaluation with `u`-like input vectors.
175+
176+
Here, `u` can be in either the following types:
177+
- `AbstractVector`
178+
- [`Ket`](@ref)-type [`QuantumObject`](@ref) (if `L` is an [`Operator`](@ref))
179+
- [`OperatorKet`](@ref)-type [`QuantumObject`](@ref) (if `L` is a [`SuperOperator`](@ref))
180+
"""
181+
SciMLOperators.cache_operator(
182+
L::AbstractQuantumObject{DT,OpType},
183+
u::AbstractVector,
184+
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
185+
get_typename_wrapper(L)(cache_operator(L.data, sparse_to_dense(similar(u))), L.type, L.dims)
186+
187+
function SciMLOperators.cache_operator(
188+
L::AbstractQuantumObject{DT1,OpType},
189+
u::QuantumObject{DT2,SType},
190+
) where {
191+
DT1,
192+
DT2,
193+
OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},
194+
SType<:Union{KetQuantumObject,OperatorKetQuantumObject},
195+
}
196+
check_dims(L, u)
197+
198+
if isoper(L) && isoperket(u)
199+
throw(ArgumentError("The input state `u` must be a Ket if `L` is an Operator."))
200+
elseif issuper(L) && isket(u)
201+
throw(ArgumentError("The input state `u` must be an OperatorKet if `L` is a SuperOperator."))
202+
end
203+
return cache_operator(L, u.data)
204+
end
205+
170206
# data type conversions
171207
Base.Vector(A::QuantumObject{<:AbstractVector}) = QuantumObject(Vector(A.data), A.type, A.dims)
172208
Base.Vector{T}(A::QuantumObject{<:AbstractVector}) where {T<:Number} = QuantumObject(Vector{T}(A.data), A.type, A.dims)

src/qobj/quantum_object_base.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,6 @@ function _check_QuantumObject(type::OperatorBraQuantumObject, dims, m::Int, n::I
209209
return nothing
210210
end
211211

212-
get_typename_wrapper(A::AbstractQuantumObject) = Base.typename(typeof(A)).wrapper
213-
214212
# functions for getting Float or Complex element type
215213
_FType(A::AbstractQuantumObject) = _FType(eltype(A))
216214
_CType(A::AbstractQuantumObject) = _CType(eltype(A))

src/qobj/quantum_object_evo.jl

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,8 @@ function QuantumObjectEvolution(
158158
op_func_list::Tuple,
159159
α::Union{Nothing,Number} = nothing;
160160
type::Union{Nothing,QuantumObjectType} = nothing,
161-
f::Function = identity,
162161
)
163-
op, data = _QobjEvo_generate_data(op_func_list, α; f = f)
162+
op, data = _QobjEvo_generate_data(op_func_list, α)
164163
dims = op.dims
165164
if type isa Nothing
166165
type = op.type
@@ -177,24 +176,21 @@ function QuantumObjectEvolution(
177176
op::QuantumObject,
178177
α::Union{Nothing,Number} = nothing;
179178
type::Union{Nothing,QuantumObjectType} = nothing,
180-
f::Function = identity,
181179
)
182180
if type isa Nothing
183181
type = op.type
184182
end
185-
return QuantumObjectEvolution(_make_SciMLOperator(op, α, f = f), type, op.dims)
183+
return QuantumObjectEvolution(_make_SciMLOperator(op, α), type, op.dims)
186184
end
187185

188186
function QuantumObjectEvolution(
189187
op::QuantumObjectEvolution,
190188
α::Union{Nothing,Number} = nothing;
191189
type::Union{Nothing,QuantumObjectType} = nothing,
192-
f::Function = identity,
193190
)
194-
f !== identity && throw(ArgumentError("The function `f` is not supported for QuantumObjectEvolution inputs."))
195191
if type isa Nothing
196192
type = op.type
197-
else
193+
elseif type != op.type
198194
throw(
199195
ArgumentError(
200196
"The type of the QuantumObjectEvolution object cannot be changed when using another QuantumObjectEvolution object as input.",
@@ -217,7 +213,7 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution`
217213
- `α`: A scalar to pre-multiply the operators.
218214
- `f::Function=identity`: A function to pre-apply to the operators.
219215
=#
220-
@generated function _QobjEvo_generate_data(op_func_list::Tuple, α; f::Function = identity)
216+
@generated function _QobjEvo_generate_data(op_func_list::Tuple, α)
221217
op_func_list_types = op_func_list.parameters
222218
N = length(op_func_list_types)
223219

@@ -242,9 +238,9 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution`
242238
data_type = op_type.parameters[1]
243239
dims_expr = (dims_expr..., :($op.dims))
244240
if i == 1
245-
first_op = :(f($op))
241+
first_op = :($op)
246242
end
247-
data_expr = :($data_expr + _make_SciMLOperator(op_func_list[$i], α, f = f))
243+
data_expr = :($data_expr + _make_SciMLOperator(op_func_list[$i], α))
248244
else
249245
op_type = op_func_type
250246
(isoper(op_type) || issuper(op_type)) ||
@@ -255,7 +251,7 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution`
255251
if i == 1
256252
first_op = :(op_func_list[$i])
257253
end
258-
qobj_expr_const = :($qobj_expr_const + f(op_func_list[$i]))
254+
qobj_expr_const = :($qobj_expr_const + op_func_list[$i])
259255
end
260256
end
261257

@@ -272,20 +268,20 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution`
272268
end
273269
end
274270

275-
function _make_SciMLOperator(op_func::Tuple, α; f::Function = identity)
271+
function _make_SciMLOperator(op_func::Tuple, α)
276272
T = eltype(op_func[1])
277273
update_func = (a, u, p, t) -> op_func[2](p, t)
278274
if α isa Nothing
279-
return ScalarOperator(zero(T), update_func) * MatrixOperator(f(op_func[1]).data)
275+
return ScalarOperator(zero(T), update_func) * MatrixOperator(op_func[1].data)
280276
end
281-
return ScalarOperator(zero(T), update_func) * MatrixOperator* f(op_func[1]).data)
277+
return ScalarOperator(zero(T), update_func) * MatrixOperator* op_func[1].data)
282278
end
283279

284-
function _make_SciMLOperator(op::QuantumObject, α; f::Function = identity)
280+
function _make_SciMLOperator(op::QuantumObject, α)
285281
if α isa Nothing
286-
return MatrixOperator(f(op).data)
282+
return MatrixOperator(op.data)
287283
end
288-
return MatrixOperator* f(op.data))
284+
return MatrixOperator* op.data)
289285
end
290286

291287
@doc raw"""
@@ -375,15 +371,11 @@ function (A::QuantumObjectEvolution)(
375371
check_dims(ψout, A)
376372

377373
if isoper(A) && isoperket(ψin)
378-
throw(
379-
ArgumentError(
380-
"The input state must be a KetQuantumObject if the QuantumObjectEvolution object is an Operator.",
381-
),
382-
)
374+
throw(ArgumentError("The input state must be a Ket if the QuantumObjectEvolution object is an Operator."))
383375
elseif issuper(A) && isket(ψin)
384376
throw(
385377
ArgumentError(
386-
"The input state must be an OperatorKetQuantumObject if the QuantumObjectEvolution object is a SuperOperator.",
378+
"The input state must be an OperatorKet if the QuantumObjectEvolution object is a SuperOperator.",
387379
),
388380
)
389381
end

0 commit comments

Comments
 (0)