Skip to content

Commit 7a11ddf

Browse files
Add tests for QObjEvo and time evolution
1 parent f58a263 commit 7a11ddf

File tree

9 files changed

+357
-43
lines changed

9 files changed

+357
-43
lines changed

src/qobj/arithmetic_and_attributes.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ for op in (:(+), :(-), :(*))
3838
@eval begin
3939
function LinearAlgebra.$op(A::AbstractQuantumObject, B::AbstractQuantumObject)
4040
check_dims(A, B)
41-
QType = promote_type(A, B)
41+
QType = promote_op_type(A, B)
4242
return QType($(op)(A.data, B.data), A.type, A.dims)
4343
end
4444
LinearAlgebra.$op(A::AbstractQuantumObject) = get_typename_wrapper(A)($(op)(A.data), A.type, A.dims)

src/qobj/boolean_functions.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export isunitary, isconstant
1010
1111
Checks if the [`QuantumObject`](@ref) `A` is a [`BraQuantumObject`](@ref). Default case returns `false` for any other inputs.
1212
"""
13-
isbra(A::QuantumObject) = A.type isa BraQuantumObject
13+
isbra(A::QuantumObject) = isbra(typeof(A))
1414
isbra(::Type{QuantumObject{DT,BraQuantumObject,N}}) where {DT,N} = true
1515
isbra(A) = false # default case
1616

@@ -19,7 +19,7 @@ isbra(A) = false # default case
1919
2020
Checks if the [`QuantumObject`](@ref) `A` is a [`KetQuantumObject`](@ref). Default case returns `false` for any other inputs.
2121
"""
22-
isket(A::QuantumObject) = A.type isa KetQuantumObject
22+
isket(A::QuantumObject) = isket(typeof(A))
2323
isket(::Type{QuantumObject{DT,KetQuantumObject,N}}) where {DT,N} = true
2424
isket(A) = false # default case
2525

@@ -28,16 +28,16 @@ isket(A) = false # default case
2828
2929
Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`OperatorQuantumObject`](@ref). Default case returns `false` for any other inputs.
3030
"""
31-
isoper(A::AbstractQuantumObject) = A.type isa OperatorQuantumObject
32-
isoper(::Type{QuantumObject{DT,OperatorQuantumObject,N}}) where {DT,N} = true
31+
isoper(A::AbstractQuantumObject) = isoper(typeof(A))
32+
isoper(::Type{<:AbstractQuantumObject{DT,OperatorQuantumObject,N}}) where {DT,N} = true
3333
isoper(A) = false # default case
3434

3535
@doc raw"""
3636
isoperbra(A::QuantumObject)
3737
3838
Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorBraQuantumObject`](@ref). Default case returns `false` for any other inputs.
3939
"""
40-
isoperbra(A::QuantumObject) = A.type isa OperatorBraQuantumObject
40+
isoperbra(A::QuantumObject) = isoperbra(typeof(A))
4141
isoperbra(::Type{QuantumObject{DT,OperatorBraQuantumObject,N}}) where {DT,N} = true
4242
isoperbra(A) = false # default case
4343

@@ -46,7 +46,7 @@ isoperbra(A) = false # default case
4646
4747
Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorKetQuantumObject`](@ref). Default case returns `false` for any other inputs.
4848
"""
49-
isoperket(A::QuantumObject) = A.type isa OperatorKetQuantumObject
49+
isoperket(A::QuantumObject) = isoperket(typeof(A))
5050
isoperket(::Type{QuantumObject{DT,OperatorKetQuantumObject,N}}) where {DT,N} = true
5151
isoperket(A) = false # default case
5252

@@ -55,8 +55,8 @@ isoperket(A) = false # default case
5555
5656
Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`SuperOperatorQuantumObject`](@ref). Default case returns `false` for any other inputs.
5757
"""
58-
issuper(A::AbstractQuantumObject) = A.type isa SuperOperatorQuantumObject
59-
issuper(::Type{QuantumObject{DT,SuperOperatorQuantumObject,N}}) where {DT,N} = true
58+
issuper(A::AbstractQuantumObject) = issuper(typeof(A))
59+
issuper(::Type{<:AbstractQuantumObject{DT,SuperOperatorQuantumObject,N}}) where {DT,N} = true
6060
issuper(A) = false # default case
6161

6262
@doc raw"""

src/qobj/functions.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ function LinearAlgebra.kron(
193193
A::AbstractQuantumObject{DT1,OpType},
194194
B::AbstractQuantumObject{DT2,OpType},
195195
) where {DT1,DT2,OpType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject}}
196-
QType = promote_type(A, B)
196+
QType = promote_op_type(A, B)
197197
return QType(kron(A.data, B.data), A.type, vcat(A.dims, B.dims))
198198
end
199199
LinearAlgebra.kron(A::AbstractQuantumObject) = A

src/qobj/quantum_object_evo.jl

Lines changed: 144 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ coef1 (generic function with 1 method)
4242
julia> coef2(p, t) = sin(t)
4343
coef2 (generic function with 1 method)
4444
45-
julia> op1 = QobjEvo(((a, coef1), (σm, coef2)))
45+
julia> op1 = QuantumObjectEvolution(((a, coef1), (σm, coef2)))
4646
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true
4747
(ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20))
4848
```
@@ -67,7 +67,7 @@ coef1 (generic function with 1 method)
6767
julia> coef2(p, t) = sin(p.ω2 * t)
6868
coef2 (generic function with 1 method)
6969
70-
julia> op1 = QobjEvo(((a, coef1), (σm, coef2)))
70+
julia> op1 = QuantumObjectEvolution(((a, coef1), (σm, coef2)))
7171
Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true
7272
(ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20))
7373
@@ -270,15 +270,149 @@ function _make_SciMLOperator(op::QuantumObject, α; f::Function = identity)
270270
return MatrixOperator* f(op.data))
271271
end
272272

273-
function (QO::QuantumObjectEvolution)(p, t)
273+
@doc raw"""
274+
(A::QuantumObjectEvolution)(ψout, ψin, p, t)
275+
276+
Apply the time-dependent [`QuantumObjectEvolution`](@ref) object `A` to the input state `ψin` at time `t` with parameters `p`. The output state is stored in `ψout`. This function mimics the behavior of a `AbstractSciMLOperator` object.
277+
278+
# Arguments
279+
- `ψout::QuantumObject`: The output state. It must have the same type as `ψin`.
280+
- `ψin::QuantumObject`: The input state. It must be either a [`KetQuantumObject`](@ref) or a [`OperatorKetQuantumObject`](@ref).
281+
- `p`: The parameters of the time-dependent coefficients.
282+
- `t`: The time at which the coefficients are evaluated.
283+
284+
# Returns
285+
- `ψout::QuantumObject`: The output state.
286+
287+
# Examples
288+
```
289+
julia> a = destroy(20)
290+
Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false
291+
20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries:
292+
⎡⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⎤
293+
⎢⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⎥
294+
⎢⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⎥
295+
⎢⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀⎥
296+
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⎦
297+
298+
julia> coef1(p, t) = sin(t)
299+
coef1 (generic function with 1 method)
300+
301+
julia> coef2(p, t) = cos(t)
302+
coef2 (generic function with 1 method)
303+
304+
julia> A = QobjEvo(((a, coef1), (a', coef2)))
305+
Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=true
306+
(ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20))
307+
308+
julia> ψ1 = fock(20, 3)
309+
Quantum Object: type=Ket dims=[20] size=(20,)
310+
20-element Vector{ComplexF64}:
311+
0.0 + 0.0im
312+
0.0 + 0.0im
313+
0.0 + 0.0im
314+
1.0 + 0.0im
315+
0.0 + 0.0im
316+
317+
0.0 + 0.0im
318+
0.0 + 0.0im
319+
0.0 + 0.0im
320+
0.0 + 0.0im
321+
322+
julia> ψ2 = zero_ket(20)
323+
Quantum Object: type=Ket dims=[20] size=(20,)
324+
20-element Vector{ComplexF64}:
325+
0.0 + 0.0im
326+
0.0 + 0.0im
327+
0.0 + 0.0im
328+
0.0 + 0.0im
329+
0.0 + 0.0im
330+
331+
0.0 + 0.0im
332+
0.0 + 0.0im
333+
0.0 + 0.0im
334+
0.0 + 0.0im
335+
336+
julia> A(ψ2, ψ1, nothing, 0.1)
337+
20-element Vector{ComplexF64}:
338+
0.0 + 0.0im
339+
0.0 + 0.0im
340+
0.1729165499254989 + 0.0im
341+
0.0 + 0.0im
342+
1.9900083305560516 + 0.0im
343+
344+
0.0 + 0.0im
345+
0.0 + 0.0im
346+
0.0 + 0.0im
347+
0.0 + 0.0im
348+
```
349+
"""
350+
function (A::QuantumObjectEvolution)(
351+
ψout::QuantumObject{DT1,QobjType},
352+
ψin::QuantumObject{DT2,QobjType},
353+
p,
354+
t,
355+
) where {DT1,DT2,QobjType<:Union{KetQuantumObject,OperatorKetQuantumObject}}
356+
check_dims(ψout, ψin)
357+
check_dims(ψout, A)
358+
359+
if isoper(A) && isoperket(ψin)
360+
throw(
361+
ArgumentError(
362+
"The input state must be a KetQuantumObject if the QuantumObjectEvolution object is an Operator.",
363+
),
364+
)
365+
elseif issuper(A) && isket(ψin)
366+
throw(
367+
ArgumentError(
368+
"The input state must be an OperatorKetQuantumObject if the QuantumObjectEvolution object is a SuperOperator.",
369+
),
370+
)
371+
end
372+
373+
A.data(ψout.data, ψin.data, p, t)
374+
375+
return ψout
376+
end
377+
378+
@doc raw"""
379+
(A::QuantumObjectEvolution)(ψ, p, t)
380+
381+
Apply the time-dependent [`QuantumObjectEvolution`](@ref) object `A` to the input state `ψ` at time `t` with parameters `p`. Out-of-place version of [`(A::QuantumObjectEvolution)(ψout, ψin, p, t)`](@ref). The output state is stored in a new [`QuantumObject`](@ref) object. This function mimics the behavior of a `AbstractSciMLOperator` object.
382+
"""
383+
function (A::QuantumObjectEvolution)(
384+
ψ::QuantumObject{DT,QobjType},
385+
p,
386+
t,
387+
) where {DT,QobjType<:Union{KetQuantumObject,OperatorKetQuantumObject}}
388+
ψout = QuantumObject(similar.data), ψ.type, ψ.dims)
389+
return A(ψout, ψ, p, t)
390+
end
391+
392+
@doc raw"""
393+
(A::QuantumObjectEvolution)(p, t)
394+
395+
Calculate the time-dependent [`QuantumObjectEvolution`](@ref) object `A` at time `t` with parameters `p`.
396+
397+
# Arguments
398+
- `p`: The parameters of the time-dependent coefficients.
399+
- `t`: The time at which the coefficients are evaluated.
400+
401+
# Returns
402+
- `A::QuantumObject`: The output state.
403+
"""
404+
function (A::QuantumObjectEvolution)(p, t)
274405
# We put 0 in the place of `u` because the time-dependence doesn't depend on the state
275-
update_coefficients!(QO.data, 0, p, t)
276-
return QuantumObject(concretize(QO.data), QO.type, QO.dims)
406+
update_coefficients!(A.data, 0, p, t)
407+
return QuantumObject(concretize(A.data), A.type, A.dims)
277408
end
278409

279-
(QO::QuantumObjectEvolution)(t) = QO((), t)
410+
(A::QuantumObjectEvolution)(t) = A(nothing, t)
280411

281-
Base.promote_type(A::QuantumObjectEvolution, B::QuantumObjectEvolution) = get_typename_wrapper(A)
282-
Base.promote_type(A::QuantumObjectEvolution, B::QuantumObject) = get_typename_wrapper(A)
283-
Base.promote_type(A::QuantumObject, B::QuantumObjectEvolution) = get_typename_wrapper(B)
284-
Base.promote_type(A::QuantumObject, B::QuantumObject) = get_typename_wrapper(A)
412+
#=
413+
`promote_type` should be applied on types. Here I define `promote_op_type` because it is applied to operators.
414+
=#
415+
promote_op_type(A::QuantumObjectEvolution, B::QuantumObjectEvolution) = get_typename_wrapper(A)
416+
promote_op_type(A::QuantumObjectEvolution, B::QuantumObject) = get_typename_wrapper(A)
417+
promote_op_type(A::QuantumObject, B::QuantumObjectEvolution) = get_typename_wrapper(B)
418+
promote_op_type(A::QuantumObject, B::QuantumObject) = get_typename_wrapper(A)

src/time_evolution/mcsolve.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ function _mcsolve_prob_func(prob, i, repeat)
8181
),
8282
)
8383

84-
return remake(prob, p = prm)
84+
f = deepcopy(prob.f.f)
85+
86+
return remake(prob, f = f, p = prm)
8587
end
8688

8789
# Standard output function

src/time_evolution/ssesolve.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ function ssesolveProblem(
175175
check_dims(H_eff_evo, ψ0)
176176
dims = H_eff_evo.dims
177177

178-
ψ0 = get_data(ψ0)
178+
ψ0 = sparse_to_dense(_CType(ψ0), get_data(ψ0))
179179

180180
progr = ProgressBar(length(tlist), enable = false)
181181

test/core-test/quantum_objects.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,24 @@
120120
@test_throws DimensionMismatch Qobj(ρ_bra.data, type = OperatorBra, dims = 4)
121121
end
122122

123+
@testset "Checks on non-QuantumObjects" begin
124+
x = 1
125+
@test isket(x) == false
126+
@test isbra(x) == false
127+
@test isoper(x) == false
128+
@test issuper(x) == false
129+
@test isoperket(x) == false
130+
@test isoperbra(x) == false
131+
132+
x = rand(ComplexF64, 2)
133+
@test isket(x) == false
134+
@test isbra(x) == false
135+
@test isoper(x) == false
136+
@test issuper(x) == false
137+
@test isoperket(x) == false
138+
@test isoperbra(x) == false
139+
end
140+
123141
@testset "arithmetic" begin
124142
a = sprand(ComplexF64, 100, 100, 0.1)
125143
a2 = Qobj(a)

test/core-test/quantum_objects_evo.jl

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
@testset "Quantum Objects Evolution" verbose = true begin
22
# DomainError: incompatible between size of array and type
3-
@testset "DomainError" begin
3+
@testset "Thrown Errors" begin
44
a = MatrixOperator(rand(ComplexF64, 3, 2))
55
for t in [Operator, SuperOperator]
66
@test_throws DomainError QobjEvo(a, type = t)
77
end
8-
end
98

10-
@testset "ArgumentErro" begin
119
a = MatrixOperator(rand(ComplexF64, 3, 2))
1210
for t in (Ket, Bra, OperatorKet, OperatorBra)
1311
@test_throws ArgumentError QobjEvo(a, type = t)
1412
end
13+
14+
a = QobjEvo(destroy(20))
15+
@test_throws ArgumentError QobjEvo(a, type = SuperOperator)
16+
17+
a = MatrixOperator(rand(ComplexF64, 5, 5))
18+
@test_throws DimensionMismatch QobjEvo(a, type = SuperOperator)
19+
20+
ψ = fock(10, 3)
21+
@test_throws TypeError QobjEvo(ψ)
1522
end
1623

1724
# unsupported type of dims
@@ -47,6 +54,15 @@
4754
@test_throws DimensionMismatch QobjEvo(a, dims = 2)
4855
end
4956

57+
@testset "Promote Operators Type" begin
58+
a = destroy(20)
59+
A = QobjEvo(a)
60+
@test QuantumToolbox.promote_op_type(a, A) == QuantumObjectEvolution
61+
@test QuantumToolbox.promote_op_type(A, a) == QuantumObjectEvolution
62+
@test QuantumToolbox.promote_op_type(A, A) == QuantumObjectEvolution
63+
@test QuantumToolbox.promote_op_type(a, a) == QuantumObject
64+
end
65+
5066
@testset "arithmetic" begin
5167
a = MatrixOperator(sprand(ComplexF64, 100, 100, 0.1))
5268
a2 = QobjEvo(a)
@@ -65,7 +81,7 @@
6581
@test adjoint(a2).data == adjoint(a2.data)
6682

6783
N = 10
68-
a = QobjEvo(destroy(N))
84+
a = QobjEvo(destroy(N), Operator, N)
6985
a_d = a'
7086
X = a + a_d
7187
# Y = 1im * (a - a_d) # Currently doesn't work. Fix in SciMLOperators.jl
@@ -148,5 +164,16 @@
148164
@test_throws MethodError QobjEvo([[a, coef1], a' * a, [a', coef2]])
149165

150166
op1 = QobjEvo(((a, coef1), a' * a, (a', coef2)))
167+
op1 = QobjEvo(((a, coef1), a' * a, (a', coef2)))
168+
169+
p = (ω1 = 1, ω2 = 2)
170+
@test op1(p, 0.1) coef1(p, 0.1) * a + a' * a + coef2(p, 0.1) * a'
171+
172+
ψ = fock(N, 1)
173+
@test op1(ψ, p, 0.1) (coef1(p, 0.1) * a + a' * a + coef2(p, 0.1) * a') * ψ
174+
175+
@test iscontant(a) == true
176+
@test iscontant(op1) == false
177+
@test isconstant(Qobj(a)) == true
151178
end
152179
end

0 commit comments

Comments
 (0)