Skip to content

Commit 3357706

Browse files
Introduce QobjEvo and use SciMLOperators for time evolution (#266)
* First working implementation * Minor changes * First working case of sesolve * Minor changes * Rebase commits * Apply Yi-Te comments * Working mesolve * Make dsf_mesolve and dfd_mesolve work * Minor changes * Working version of ssesolve * Remove sleep in runtests * Remove OperatorSum and TimeDependentOperatorSum * Remove alg as argument from all problem definitions * First working runtests * Add tests for QObjEvo and time evolution * Add docstrings to documentation * Reduce tolerance for stochastic runtests * Make runtests multithreaded and reduce time for timeevolution tests * Add QobjEvo tests and minor changes * Update docstrings * Add comment on the use of MatrixOperator
1 parent d3f1313 commit 3357706

26 files changed

+2104
-1286
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ jobs:
7171
- uses: julia-actions/julia-runtest@v1
7272
env:
7373
GROUP: ${{ matrix.group }}
74+
JULIA_NUM_THREADS: auto
7475
- uses: julia-actions/julia-processcoverage@v1
7576
with:
7677
directories: src,ext

docs/src/api.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Pages = ["api.md"]
1313
## [Quantum object (Qobj) and type](@id doc-API:Quantum-object-and-type)
1414

1515
```@docs
16+
AbstractQuantumObject
1617
BraQuantumObject
1718
Bra
1819
KetQuantumObject
@@ -26,10 +27,15 @@ OperatorKet
2627
SuperOperatorQuantumObject
2728
SuperOperator
2829
QuantumObject
29-
OperatorSum
30+
QuantumObjectEvolution
3031
size
3132
eltype
3233
length
34+
```
35+
36+
## [Qobj boolean functions](@id doc-API:Qobj-boolean-functions)
37+
38+
```@docs
3339
isbra
3440
isket
3541
isoper
@@ -40,6 +46,7 @@ LinearAlgebra.ishermitian
4046
LinearAlgebra.issymmetric
4147
LinearAlgebra.isposdef
4248
isunitary
49+
isconstant
4350
```
4451

4552
## [Qobj arithmetic and attributes](@id doc-API:Qobj-arithmetic-and-attributes)
@@ -154,6 +161,7 @@ lindblad_dissipator
154161
## [Synonyms of functions for Qobj](@id doc-API:Synonyms-of-functions-for-Qobj)
155162
```@docs
156163
Qobj
164+
QobjEvo
157165
shape
158166
isherm
159167
trans

src/QuantumToolbox.jl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import SciMLBase:
2121
reinit!,
2222
remake,
2323
u_modified!,
24+
ODEFunction,
2425
ODEProblem,
2526
SDEProblem,
2627
EnsembleProblem,
@@ -32,13 +33,21 @@ import SciMLBase:
3233
ContinuousCallback,
3334
DiscreteCallback
3435
import StochasticDiffEq: StochasticDiffEqAlgorithm, SRA1
35-
import SciMLOperators: MatrixOperator
36+
import SciMLOperators:
37+
AbstractSciMLOperator,
38+
MatrixOperator,
39+
ScalarOperator,
40+
IdentityOperator,
41+
cache_operator,
42+
update_coefficients!,
43+
concretize,
44+
isconstant
3645
import LinearSolve: LinearProblem, SciMLLinearSolveAlgorithm, KrylovJL_MINRES, KrylovJL_GMRES
3746
import DiffEqBase: get_tstops
3847
import DiffEqCallbacks: PeriodicCallback, PresetTimeCallback, TerminateSteadyState
3948
import OrdinaryDiffEqCore: OrdinaryDiffEqAlgorithm
4049
import OrdinaryDiffEqTsit5: Tsit5
41-
import DiffEqNoiseProcess: RealWienerProcess
50+
import DiffEqNoiseProcess: RealWienerProcess!
4251

4352
# other dependencies (in alphabetical order)
4453
import ArrayInterface: allowed_getindex, allowed_setindex!
@@ -62,7 +71,9 @@ include("progress_bar.jl")
6271
include("linear_maps.jl")
6372

6473
# Quantum Object
74+
include("qobj/quantum_object_base.jl")
6575
include("qobj/quantum_object.jl")
76+
include("qobj/quantum_object_evo.jl")
6677
include("qobj/boolean_functions.jl")
6778
include("qobj/arithmetic_and_attributes.jl")
6879
include("qobj/eigsolve.jl")
@@ -71,7 +82,6 @@ include("qobj/states.jl")
7182
include("qobj/operators.jl")
7283
include("qobj/superoperators.jl")
7384
include("qobj/synonyms.jl")
74-
include("qobj/operator_sum.jl")
7585

7686
# time evolution
7787
include("time_evolution/time_evolution.jl")

src/qobj/arithmetic_and_attributes.jl

Lines changed: 85 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -36,84 +36,80 @@ end
3636

3737
for op in (:(+), :(-), :(*))
3838
@eval begin
39-
function LinearAlgebra.$op(
40-
A::QuantumObject{<:AbstractArray{T1},OpType},
41-
B::QuantumObject{<:AbstractArray{T2},OpType},
42-
) where {T1,T2,OpType<:QuantumObjectType}
43-
A.dims != B.dims &&
44-
throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
45-
return QuantumObject($(op)(A.data, B.data), A.type, A.dims)
39+
function LinearAlgebra.$op(A::AbstractQuantumObject, B::AbstractQuantumObject)
40+
check_dims(A, B)
41+
QType = promote_op_type(A, B)
42+
return QType($(op)(A.data, B.data), A.type, A.dims)
4643
end
47-
LinearAlgebra.$op(A::QuantumObject{<:AbstractArray{T}}) where {T} = QuantumObject($(op)(A.data), A.type, A.dims)
44+
LinearAlgebra.$op(A::AbstractQuantumObject) = get_typename_wrapper(A)($(op)(A.data), A.type, A.dims)
4845

49-
LinearAlgebra.$op(n::T1, A::QuantumObject{<:AbstractArray{T2}}) where {T1<:Number,T2} =
50-
QuantumObject($(op)(n * I, A.data), A.type, A.dims)
51-
LinearAlgebra.$op(A::QuantumObject{<:AbstractArray{T1}}, n::T2) where {T1,T2<:Number} =
52-
QuantumObject($(op)(A.data, n * I), A.type, A.dims)
46+
LinearAlgebra.$op(n::T, A::AbstractQuantumObject) where {T<:Number} =
47+
get_typename_wrapper(A)($(op)(n * I, A.data), A.type, A.dims)
48+
LinearAlgebra.$op(A::AbstractQuantumObject, n::T) where {T<:Number} =
49+
get_typename_wrapper(A)($(op)(A.data, n * I), A.type, A.dims)
5350
end
5451
end
5552

5653
function LinearAlgebra.:(*)(
57-
A::QuantumObject{<:AbstractArray{T1},OperatorQuantumObject},
58-
B::QuantumObject{<:AbstractArray{T2},KetQuantumObject},
59-
) where {T1,T2}
60-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
54+
A::AbstractQuantumObject{DT1,OperatorQuantumObject},
55+
B::QuantumObject{DT2,KetQuantumObject},
56+
) where {DT1,DT2}
57+
check_dims(A, B)
6158
return QuantumObject(A.data * B.data, Ket, A.dims)
6259
end
6360
function LinearAlgebra.:(*)(
64-
A::QuantumObject{<:AbstractArray{T1},BraQuantumObject},
65-
B::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject},
66-
) where {T1,T2}
67-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
61+
A::QuantumObject{DT1,BraQuantumObject},
62+
B::AbstractQuantumObject{DT2,OperatorQuantumObject},
63+
) where {DT1,DT2}
64+
check_dims(A, B)
6865
return QuantumObject(A.data * B.data, Bra, A.dims)
6966
end
7067
function LinearAlgebra.:(*)(
71-
A::QuantumObject{<:AbstractArray{T1},KetQuantumObject},
72-
B::QuantumObject{<:AbstractArray{T2},BraQuantumObject},
73-
) where {T1,T2}
74-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
68+
A::QuantumObject{DT1,KetQuantumObject},
69+
B::QuantumObject{DT2,BraQuantumObject},
70+
) where {DT1,DT2}
71+
check_dims(A, B)
7572
return QuantumObject(A.data * B.data, Operator, A.dims)
7673
end
7774
function LinearAlgebra.:(*)(
78-
A::QuantumObject{<:AbstractArray{T1},BraQuantumObject},
79-
B::QuantumObject{<:AbstractArray{T2},KetQuantumObject},
80-
) where {T1,T2}
81-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
75+
A::QuantumObject{DT1,BraQuantumObject},
76+
B::QuantumObject{DT2,KetQuantumObject},
77+
) where {DT1,DT2}
78+
check_dims(A, B)
8279
return A.data * B.data
8380
end
8481
function LinearAlgebra.:(*)(
85-
A::QuantumObject{<:AbstractArray{T1},SuperOperatorQuantumObject},
86-
B::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject},
87-
) where {T1,T2}
88-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
82+
A::AbstractQuantumObject{DT1,SuperOperatorQuantumObject},
83+
B::QuantumObject{DT2,OperatorQuantumObject},
84+
) where {DT1,DT2}
85+
check_dims(A, B)
8986
return QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator, A.dims)
9087
end
9188
function LinearAlgebra.:(*)(
92-
A::QuantumObject{<:AbstractArray{T1},OperatorBraQuantumObject},
93-
B::QuantumObject{<:AbstractArray{T2},OperatorKetQuantumObject},
94-
) where {T1,T2}
95-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
89+
A::QuantumObject{DT1,OperatorBraQuantumObject},
90+
B::QuantumObject{DT2,OperatorKetQuantumObject},
91+
) where {DT1,DT2}
92+
check_dims(A, B)
9693
return A.data * B.data
9794
end
9895
function LinearAlgebra.:(*)(
99-
A::QuantumObject{<:AbstractArray{T1},SuperOperatorQuantumObject},
100-
B::QuantumObject{<:AbstractArray{T2},OperatorKetQuantumObject},
101-
) where {T1,T2}
102-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
96+
A::AbstractQuantumObject{DT1,SuperOperatorQuantumObject},
97+
B::QuantumObject{DT2,OperatorKetQuantumObject},
98+
) where {DT1,DT2}
99+
check_dims(A, B)
103100
return QuantumObject(A.data * B.data, OperatorKet, A.dims)
104101
end
105102
function LinearAlgebra.:(*)(
106103
A::QuantumObject{<:AbstractArray{T1},OperatorBraQuantumObject},
107-
B::QuantumObject{<:AbstractArray{T2},SuperOperatorQuantumObject},
104+
B::AbstractQuantumObject{<:AbstractArray{T2},SuperOperatorQuantumObject},
108105
) where {T1,T2}
109-
A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension."))
106+
check_dims(A, B)
110107
return QuantumObject(A.data * B.data, OperatorBra, A.dims)
111108
end
112109

113-
LinearAlgebra.:(^)(A::QuantumObject{<:AbstractArray{T}}, n::T1) where {T,T1<:Number} =
114-
QuantumObject(^(A.data, n), A.type, A.dims)
115-
LinearAlgebra.:(/)(A::QuantumObject{<:AbstractArray{T}}, n::T1) where {T,T1<:Number} =
116-
QuantumObject(/(A.data, n), A.type, A.dims)
110+
LinearAlgebra.:(^)(A::QuantumObject{DT}, n::T) where {DT,T<:Number} = QuantumObject(^(A.data, n), A.type, A.dims)
111+
LinearAlgebra.:(/)(A::AbstractQuantumObject{DT}, n::T) where {DT,T<:Number} =
112+
get_typename_wrapper(A)(A.data / n, A.type, A.dims)
117113

118114
@doc raw"""
119115
dot(A::QuantumObject, B::QuantumObject)
@@ -125,93 +121,91 @@ Note that `A` and `B` should be [`Ket`](@ref) or [`OperatorKet`](@ref)
125121
`A ⋅ B` (where `⋅` can be typed by tab-completing `\cdot` in the REPL) is a synonym for `dot(A, B)`
126122
"""
127123
function LinearAlgebra.dot(
128-
A::QuantumObject{<:AbstractArray{T1},OpType},
129-
B::QuantumObject{<:AbstractArray{T2},OpType},
130-
) where {T1<:Number,T2<:Number,OpType<:Union{KetQuantumObject,OperatorKetQuantumObject}}
124+
A::QuantumObject{DT1,OpType},
125+
B::QuantumObject{DT2,OpType},
126+
) where {DT1,DT2,OpType<:Union{KetQuantumObject,OperatorKetQuantumObject}}
131127
A.dims != B.dims && throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension."))
132128
return LinearAlgebra.dot(A.data, B.data)
133129
end
134130

135131
@doc raw"""
136-
dot(i::QuantumObject, A::QuantumObject j::QuantumObject)
132+
dot(i::QuantumObject, A::AbstractQuantumObject j::QuantumObject)
137133
138-
Compute the generalized dot product `dot(i, A*j)` between three [`QuantumObject`](@ref): ``\langle i | \hat{A} | j \rangle``
134+
Compute the generalized dot product `dot(i, A*j)` between a [`AbstractQuantumObject`](@ref) and two [`QuantumObject`](@ref) (`i` and `j`), namely ``\langle i | \hat{A} | j \rangle``.
139135
140136
Supports the following inputs:
141137
- `A` is in the type of [`Operator`](@ref), with `i` and `j` are both [`Ket`](@ref).
142138
- `A` is in the type of [`SuperOperator`](@ref), with `i` and `j` are both [`OperatorKet`](@ref)
143139
"""
144140
function LinearAlgebra.dot(
145-
i::QuantumObject{<:AbstractArray{T1},KetQuantumObject},
146-
A::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject},
147-
j::QuantumObject{<:AbstractArray{T3},KetQuantumObject},
148-
) where {T1<:Number,T2<:Number,T3<:Number}
141+
i::QuantumObject{DT1,KetQuantumObject},
142+
A::AbstractQuantumObject{DT2,OperatorQuantumObject},
143+
j::QuantumObject{DT3,KetQuantumObject},
144+
) where {DT1,DT2,DT3}
149145
((i.dims != A.dims) || (A.dims != j.dims)) &&
150146
throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension."))
151147
return LinearAlgebra.dot(i.data, A.data, j.data)
152148
end
153149
function LinearAlgebra.dot(
154-
i::QuantumObject{<:AbstractArray{T1},OperatorKetQuantumObject},
155-
A::QuantumObject{<:AbstractArray{T2},SuperOperatorQuantumObject},
156-
j::QuantumObject{<:AbstractArray{T3},OperatorKetQuantumObject},
157-
) where {T1<:Number,T2<:Number,T3<:Number}
150+
i::QuantumObject{DT1,OperatorKetQuantumObject},
151+
A::AbstractQuantumObject{DT2,SuperOperatorQuantumObject},
152+
j::QuantumObject{DT3,OperatorKetQuantumObject},
153+
) where {DT1,DT2,DT3}
158154
((i.dims != A.dims) || (A.dims != j.dims)) &&
159155
throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension."))
160156
return LinearAlgebra.dot(i.data, A.data, j.data)
161157
end
162158

163159
@doc raw"""
164-
conj(A::QuantumObject)
160+
conj(A::AbstractQuantumObject)
165161
166-
Return the element-wise complex conjugation of the [`QuantumObject`](@ref).
162+
Return the element-wise complex conjugation of the [`AbstractQuantumObject`](@ref).
167163
"""
168-
Base.conj(A::QuantumObject{<:AbstractArray{T}}) where {T} = QuantumObject(conj(A.data), A.type, A.dims)
164+
Base.conj(A::AbstractQuantumObject) = get_typename_wrapper(A)(conj(A.data), A.type, A.dims)
169165

170166
@doc raw"""
171-
transpose(A::QuantumObject)
167+
transpose(A::AbstractQuantumObject)
172168
173-
Lazy matrix transpose of the [`QuantumObject`](@ref).
169+
Lazy matrix transpose of the [`AbstractQuantumObject`](@ref).
174170
"""
175171
LinearAlgebra.transpose(
176-
A::QuantumObject{<:AbstractArray{T},OpType},
177-
) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
178-
QuantumObject(transpose(A.data), A.type, A.dims)
172+
A::AbstractQuantumObject{DT,OpType},
173+
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
174+
get_typename_wrapper(A)(transpose(A.data), A.type, A.dims)
179175

180176
@doc raw"""
181177
A'
182-
adjoint(A::QuantumObject)
178+
adjoint(A::AbstractQuantumObject)
183179
184-
Lazy adjoint (conjugate transposition) of the [`QuantumObject`](@ref)
180+
Lazy adjoint (conjugate transposition) of the [`AbstractQuantumObject`](@ref)
185181
186182
Note that `A'` is a synonym for `adjoint(A)`
187183
"""
188184
LinearAlgebra.adjoint(
189-
A::QuantumObject{<:AbstractArray{T},OpType},
190-
) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
191-
QuantumObject(adjoint(A.data), A.type, A.dims)
192-
LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},KetQuantumObject}) where {T} =
193-
QuantumObject(adjoint(A.data), Bra, A.dims)
194-
LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},BraQuantumObject}) where {T} =
195-
QuantumObject(adjoint(A.data), Ket, A.dims)
196-
LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},OperatorKetQuantumObject}) where {T} =
185+
A::AbstractQuantumObject{DT,OpType},
186+
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
187+
get_typename_wrapper(A)(adjoint(A.data), A.type, A.dims)
188+
LinearAlgebra.adjoint(A::QuantumObject{DT,KetQuantumObject}) where {DT} = QuantumObject(adjoint(A.data), Bra, A.dims)
189+
LinearAlgebra.adjoint(A::QuantumObject{DT,BraQuantumObject}) where {DT} = QuantumObject(adjoint(A.data), Ket, A.dims)
190+
LinearAlgebra.adjoint(A::QuantumObject{DT,OperatorKetQuantumObject}) where {DT} =
197191
QuantumObject(adjoint(A.data), OperatorBra, A.dims)
198-
LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},OperatorBraQuantumObject}) where {T} =
192+
LinearAlgebra.adjoint(A::QuantumObject{DT,OperatorBraQuantumObject}) where {DT} =
199193
QuantumObject(adjoint(A.data), OperatorKet, A.dims)
200194

201195
@doc raw"""
202-
inv(A::QuantumObject)
196+
inv(A::AbstractQuantumObject)
203197
204-
Matrix inverse of the [`QuantumObject`](@ref)
198+
Matrix inverse of the [`AbstractQuantumObject`](@ref). If `A` is a [`QuantumObjectEvolution`](@ref), the inverse is computed at the last computed time.
205199
"""
206200
LinearAlgebra.inv(
207-
A::QuantumObject{<:AbstractArray{T},OpType},
208-
) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
201+
A::AbstractQuantumObject{DT,OpType},
202+
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
209203
QuantumObject(sparse(inv(Matrix(A.data))), A.type, A.dims)
210204

211205
LinearAlgebra.Hermitian(
212-
A::QuantumObject{<:AbstractArray{T},OpType},
206+
A::QuantumObject{DT,OpType},
213207
uplo::Symbol = :U,
214-
) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
208+
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
215209
QuantumObject(Hermitian(A.data, uplo), A.type, A.dims)
216210

217211
@doc raw"""
@@ -436,8 +430,8 @@ Matrix sine of [`QuantumObject`](@ref), defined as
436430
Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref)
437431
"""
438432
LinearAlgebra.sin(
439-
A::QuantumObject{<:AbstractMatrix{T},ObjType},
440-
) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (exp(1im * A) - exp(-1im * A)) / 2im
433+
A::QuantumObject{DT,ObjType},
434+
) where {DT,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (exp(1im * A) - exp(-1im * A)) / 2im
441435

442436
@doc raw"""
443437
cos(A::QuantumObject)
@@ -449,8 +443,8 @@ Matrix cosine of [`QuantumObject`](@ref), defined as
449443
Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref)
450444
"""
451445
LinearAlgebra.cos(
452-
A::QuantumObject{<:AbstractMatrix{T},ObjType},
453-
) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (exp(1im * A) + exp(-1im * A)) / 2
446+
A::QuantumObject{DT,ObjType},
447+
) where {DT,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (exp(1im * A) + exp(-1im * A)) / 2
454448

455449
@doc raw"""
456450
diag(A::QuantumObject, k::Int=0)
@@ -659,11 +653,11 @@ tidyup!(A::AbstractArray{T}, tol::T2 = 1e-14) where {T,T2<:Real} =
659653
@. A = T(abs(real(A)) > tol) * real(A) + 1im * T(abs(imag(A)) > tol) * imag(A)
660654

661655
@doc raw"""
662-
get_data(A::QuantumObject)
656+
get_data(A::AbstractQuantumObject)
663657
664-
Returns the data of a QuantumObject.
658+
Returns the data of a [`AbstractQuantumObject`](@ref).
665659
"""
666-
get_data(A::QuantumObject) = A.data
660+
get_data(A::AbstractQuantumObject) = A.data
667661

668662
@doc raw"""
669663
get_coherence(ψ::QuantumObject)

0 commit comments

Comments
 (0)