Skip to content

Commit 739852c

Browse files
Fix type instabilities for qobj methods
1 parent 367f476 commit 739852c

File tree

3 files changed

+126
-29
lines changed

3 files changed

+126
-29
lines changed

src/qobj/arithmetic_and_attributes.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ julia> tr(a' * a)
239239
"""
240240
LinearAlgebra.tr(
241241
A::QuantumObject{<:AbstractArray{T},OpType},
242-
) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
243-
ishermitian(A) ? real(tr(A.data)) : tr(A.data)
242+
) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = tr(A.data)
243+
LinearAlgebra.tr(A::QuantumObject{<:Union{<:Hermitian{TF}, Symmetric{TR}},OpType}) where {TF<:BlasFloat,TR<:Real,OpType<:OperatorQuantumObject} = real(tr(A.data))
244244

245245
@doc raw"""
246246
svdvals(A::QuantumObject)
@@ -673,6 +673,9 @@ julia> ψ_123 = tensor(ψ1, ψ2, ψ3)
673673
julia> permute(ψ_123, [2, 1, 3]) ≈ tensor(ψ2, ψ1, ψ3)
674674
true
675675
```
676+
677+
!!! warning "Beware of type-stability!"
678+
It is highly recommended to use `permute(A, order)` with `order` as `Tuple` or `SVector` to keep type stability. See the [related Section](@ref doc:Type-Stability) about type stability for more details.
676679
"""
677680
function permute(
678681
A::QuantumObject{<:AbstractArray{T},ObjType},
@@ -691,7 +694,7 @@ function permute(
691694
dims, perm = _dims_and_perm(A.type, A.dims, order_svector, length(order_svector))
692695

693696
return QuantumObject(
694-
reshape(PermutedDimsArray(reshape(A.data, dims...), Tuple(perm)), size(A)),
697+
reshape(permutedims(reshape(A.data, dims...), Tuple(perm)), size(A)),
695698
A.type,
696699
A.dims[order_svector],
697700
)

src/qobj/functions.jl

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ If `ψ` is a [`Ket`](@ref) or [`Bra`](@ref), the function calculates ``\langle\p
2525
2626
If `ψ` is a density matrix ([`Operator`](@ref)), the function calculates ``\textrm{Tr} \left[ \hat{O} \hat{\psi} \right]``
2727
28-
The function returns a real number if `O` is hermitian, and returns a complex number otherwise.
28+
The function returns a real number if `O` is of `Hermitian` type or `Symmetric` type, and returns a complex number otherwise. You can make an operator `O` hermitian by using `Hermitian(O)`.
2929
3030
# Examples
3131
@@ -34,31 +34,48 @@ julia> ψ = 1 / √2 * (fock(10,2) + fock(10,4));
3434
3535
julia> a = destroy(10);
3636
37-
julia> expect(a' * a, ψ) ≈ 3
38-
true
37+
julia> expect(a' * a, ψ) |> round
38+
3.0 + 0.0im
39+
40+
julia> expect(Hermitian(a' * a), ψ) |> round
41+
3.0
3942
```
4043
"""
4144
function expect(
4245
O::QuantumObject{<:AbstractArray{T1},OperatorQuantumObject},
4346
ψ::QuantumObject{<:AbstractArray{T2},KetQuantumObject},
4447
) where {T1,T2}
45-
ψd = ψ.data
46-
Od = O.data
47-
return ishermitian(O) ? real(dot(ψd, Od, ψd)) : dot(ψd, Od, ψd)
48+
return dot.data, O.data, ψ.data)
4849
end
4950
function expect(
5051
O::QuantumObject{<:AbstractArray{T1},OperatorQuantumObject},
5152
ψ::QuantumObject{<:AbstractArray{T2},BraQuantumObject},
5253
) where {T1,T2}
53-
ψd = ψ.data'
54-
Od = O.data
55-
return ishermitian(O) ? real(dot(ψd, Od, ψd)) : dot(ψd, Od, ψd)
54+
return expect(O, ψ')
5655
end
5756
function expect(
5857
O::QuantumObject{<:AbstractArray{T1},OperatorQuantumObject},
5958
ρ::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject},
6059
) where {T1,T2}
61-
return ishermitian(O) ? real(tr(O * ρ)) : tr(O * ρ)
60+
return tr(O * ρ)
61+
end
62+
function expect(
63+
O::QuantumObject{<:Union{<:Hermitian{TF},<:Symmetric{TR}},OperatorQuantumObject},
64+
ψ::QuantumObject{<:AbstractArray{T2},KetQuantumObject},
65+
) where {TF<:Number,TR<:Real,T2}
66+
return real(dot.data, O.data, ψ.data))
67+
end
68+
function expect(
69+
O::QuantumObject{<:Union{<:Hermitian{TF},<:Symmetric{TR}},OperatorQuantumObject},
70+
ψ::QuantumObject{<:AbstractArray{T2},BraQuantumObject},
71+
) where {TF<:Number,TR<:Real,T2}
72+
return real(expect(O, ψ'))
73+
end
74+
function expect(
75+
O::QuantumObject{<:Union{<:Hermitian{TF},<:Symmetric{TR}},OperatorQuantumObject},
76+
ρ::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject},
77+
) where {TF<:Number,TR<:Real,T2}
78+
return real(tr(O * ρ))
6279
end
6380

6481
@doc raw"""

test/quantum_objects.jl

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -317,38 +317,56 @@
317317

318318
@testset "projection" begin
319319
N = 10
320-
a = fock(N, 3)
321-
@test proj(a) proj(a') sparse(ket2dm(a)) projection(N, 3, 3)
322-
@test isket(a') == false
323-
@test isbra(a') == true
324-
@test shape(a) == (N,)
325-
@test shape(a') == (1, N)
326-
@test norm(a) 1
327-
@test norm(a') 1
320+
ψ = fock(N, 3)
321+
@test proj(ψ) proj') sparse(ket2dm(ψ)) projection(N, 3, 3)
322+
@test isket') == false
323+
@test isbra') == true
324+
@test shape(ψ) == (N,)
325+
@test shape') == (1, N)
326+
@test norm(ψ) 1
327+
@test norm') 1
328+
329+
@testset "Type Inference (proj)" begin
330+
@inferred proj(ψ)
331+
@inferred proj')
332+
end
328333
end
329334

330335
@testset "dot product" begin
331336
ψ = rand_ket(10)
332337
@test dot(ψ, ψ) ψ' * ψ norm(ψ) 1.0
338+
339+
@testset "Type Inference (dot)" begin
340+
@inferred dot(ψ, ψ)
341+
@inferred ψ' * ψ
342+
@inferred norm(ψ)
343+
end
333344
end
334345

335346
@testset "normalization" begin
336347
# normalize, normalize!, unit
337348
N = 10
338-
a = Qobj(rand(ComplexF64, N))
339-
M = a * a'
340-
@test (norm(a) 1) == false
349+
ψ = Qobj(rand(ComplexF64, N))
350+
M = ψ * ψ'
351+
@test (norm(ψ) 1) == false
341352
@test (norm(M) 1) == false
342-
@test (norm(unit(a)) 1) == true
353+
@test (norm(unit(ψ)) 1) == true
343354
@test (norm(unit(M)) 1) == true
344-
@test (norm(a) 1) == false # Again, to be sure that it is still non-normalized
355+
@test (norm(ψ) 1) == false # Again, to be sure that it is still non-normalized
345356
@test (norm(M) 1) == false # Again, to be sure that it is still non-normalized
346-
normalize!(a)
357+
normalize!(ψ)
347358
normalize!(M)
348-
@test (norm(a) 1) == true
359+
@test (norm(ψ) 1) == true
349360
@test (norm(M) 1) == true
350-
@test M a * a'
361+
@test M ψ * ψ'
351362
@test (unit(qeye(N)) (qeye(N) / N)) == true
363+
364+
@testset "Type Inference (normalize)" begin
365+
ψ = Qobj(rand(ComplexF64, N))
366+
M = ket2dm(ψ)
367+
@inferred normalize(ψ)
368+
@inferred normalize(M)
369+
end
352370
end
353371

354372
@testset "expectation value" begin
@@ -362,6 +380,19 @@
362380
ψ = fock(N, 3)
363381
@test norm' * a) 2
364382
@test expect(a' * a, ψ' * a) 16
383+
384+
ρ = rand_dm(N)
385+
@test expect(a, ρ) tr(a * ρ)
386+
@test variance(a, ρ) tr(a^2 * ρ) - tr(a * ρ)^2
387+
388+
@testset "Type Inference (expect)" begin
389+
@inferred expect(a, ψ)
390+
@inferred expect(a, ψ')
391+
@inferred variance(a, ψ)
392+
@inferred variance(a, ψ')
393+
@inferred expect(a, ρ)
394+
@inferred variance(a, ρ)
395+
end
365396
end
366397

367398
@testset "get coherence" begin
@@ -371,6 +402,11 @@
371402
ρ = ket2dm(ψ)
372403
α, δρ = get_coherence(ρ)
373404
@test isapprox(abs(α), 3, atol = 1e-5) && abs2(δρ[1, 1]) > 0.999
405+
406+
@testset "Type Inference (get_coherence)" begin
407+
@inferred get_coherence(ψ)
408+
@inferred get_coherence(ρ)
409+
end
374410
end
375411

376412
@testset "SVD and Schatten p-norm" begin
@@ -382,6 +418,13 @@
382418
@test svdvals(vs)[1] (vs' * vs)
383419
@test norm(Md, 1) sum(sqrt, abs.(eigenenergies(Md' * Md))) atol = 1e-6
384420
@test norm(Ms, 1) sum(sqrt, abs.(eigenenergies(Ms' * Ms))) atol = 1e-6
421+
422+
@testset "Type Inference (SVD and Schatten p-norm)" begin
423+
@inferred svdvals(vd)
424+
@inferred svdvals(vs)
425+
@inferred norm(Md, 1)
426+
@inferred norm(Ms, 1)
427+
end
385428
end
386429

387430
@testset "purity" begin
@@ -395,6 +438,13 @@
395438
@test purity(ψd) norm(ψd)^2 1.0
396439
@test purity(ρ1) 1.0
397440
@test (1.0 / N) <= purity(ρ2) <= 1.0
441+
442+
@testset "Type Inference (purity)" begin
443+
@inferred purity(ψ)
444+
@inferred purity(ψd)
445+
@inferred purity(ρ1)
446+
@inferred purity(ρ2)
447+
end
398448
end
399449

400450
@testset "trace distance" begin
@@ -407,6 +457,13 @@
407457
@test tracedist(ρz0, ψz1) 1.0
408458
@test tracedist(ψz1, ρz0) 1.0
409459
@test tracedist(ρz0, ρz1) 1.0
460+
461+
@testset "Type Inference (trace distance)" begin
462+
@inferred tracedist(ψz0, ψx0)
463+
@inferred tracedist(ρz0, ψz1)
464+
@inferred tracedist(ψz1, ρz0)
465+
@inferred tracedist(ρz0, ρz1)
466+
end
410467
end
411468

412469
@testset "sqrt and fidelity" begin
@@ -418,6 +475,13 @@
418475
@test sqrtm(M0) sqrtm(sparse_to_dense(M0))
419476
@test isapprox(fidelity(M0, M1), fidelity(ψ1, M0); atol = 1e-6)
420477
@test isapprox(fidelity(ψ1, ψ2), fidelity(ket2dm(ψ1), ket2dm(ψ2)); atol = 1e-6)
478+
479+
@testset "Type Inference (sqrt and fidelity)" begin
480+
@inferred sqrtm(M0)
481+
@inferred fidelity(M0, M1)
482+
@inferred fidelity(ψ1, M0)
483+
@inferred fidelity(ψ1, ψ2)
484+
end
421485
end
422486

423487
@testset "log, exp, sinm, cosm" begin
@@ -469,6 +533,13 @@
469533
ρ2 = dense_to_sparse(ρ1)
470534
@test tidyup(ρ2, tol) != ρ2
471535
@test dense_to_sparse(tidyup(ρ1, tol)) == tidyup(ρ2, tol)
536+
537+
@testset "Type Inference (tidyup)" begin
538+
@inferred tidyup(ψ1, tol)
539+
@inferred tidyup(ρ1, tol)
540+
@inferred tidyup(ψ2, tol)
541+
@inferred tidyup(ρ2, tol)
542+
end
472543
end
473544

474545
@testset "ptrace" begin
@@ -550,5 +621,11 @@
550621
@test_throws ArgumentError permute(bra_bdca, wrong_order2)
551622
@test_throws ArgumentError permute(op_bdca, wrong_order1)
552623
@test_throws ArgumentError permute(op_bdca, wrong_order2)
624+
625+
@testset "Type Inference (permute)" begin
626+
@inferred permute(ket_bdca, (2, 4, 3, 1))
627+
@inferred permute(bra_bdca, (2, 4, 3, 1))
628+
@inferred permute(op_bdca, (2, 4, 3, 1))
629+
end
553630
end
554631
end

0 commit comments

Comments
 (0)