Skip to content

Commit a49050c

Browse files
author
Adria Labay
committed
return list of a and n if multiple subsystems and tests
1 parent f43b74c commit a49050c

File tree

2 files changed

+171
-49
lines changed

2 files changed

+171
-49
lines changed

src/qobj/arithmetic_and_attributes.jl

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -663,64 +663,96 @@ Returns the data of a [`AbstractQuantumObject`](@ref).
663663
get_data(A::AbstractQuantumObject) = A.data
664664

665665
@doc raw"""
666-
get_coherence(ψ::QuantumObject)
666+
get_coherence(ψ::QuantumObject; idx)
667667
668-
Returns the coherence value ``\alpha`` by measuring the expectation value of the destruction operator ``\hat{a}`` on a state ``\ket{\psi}`` or a density matrix ``\hat{\rho}``.
669-
"""
670-
function get_coherence::QuantumObject{<:AbstractArray,KetQuantumObject})
671-
if length.dims) == 1
672-
return mapreduce(n -> sqrt(n - 1) * ψ.data[n] * conj.data[n-1]), +, 2:ψ.dims[1])
673-
else
674-
off = sum(cumprod(reverse.dims[2:end]))) + 1
675-
t = Tuple(reverse.dims))
668+
Returns the coherence value ``\alpha`` by measuring the expectation value of the destruction operator ``\hat{a}`` on a state ``\ket{\psi}``, density matrix ``\hat{\rho}`` or vectorized state ``|\hat{\rho}\rangle\rangle``.
676669
677-
x = 0.0im
678-
for J in off+2:length.data)
679-
x += ψ[J] * conj(ψ[J-off]) * prod(sqrt.(_ind2sub(t, J) .- 1))
670+
If the state is a composite state, the coherence values of each subsystems are returned as an array of complex numbers. It is possible to specify the index of the sybsystem to get the coherence value by fixing `idx`.
671+
"""
672+
function get_coherence::QuantumObject{T,KetQuantumObject,N}; idx::Union{Nothing,Int} = nothing) where {T,N}
673+
dims = reverse(Tuple.dims))
674+
offsets = cumprod([1; dims[1:end-1]...])
675+
676+
expectation_values = zeros(ComplexF64, N)
677+
678+
for J in 2:length.data)
679+
indices = Base._ind2sub(dims, J)
680+
for n in 1:N
681+
if indices[n] > 1
682+
expectation_values[n] += conj.data[J-offsets[n]]) * sqrt(indices[n] - 1) * ψ.data[J]
683+
end
680684
end
681-
return x
682685
end
686+
687+
reverse!(expectation_values)
688+
689+
return isnothing(idx) ? expectation_values : expectation_values[idx]
683690
end
684691

685-
function get_coherence::QuantumObject{<:AbstractArray,OperatorQuantumObject})
686-
if length.dims) == 1
687-
return mapreduce(n -> sqrt(n - 1) * ρ.data[n, n-1], +, 2:ρ.dims[1])
688-
else
689-
off = sum(cumprod(reverse.dims[2:end]))) + 1
690-
t = Tuple(reverse.dims))
692+
function get_coherence::QuantumObject{T,OperatorQuantumObject,N}; idx::Union{Int,Nothing} = nothing) where {T,N}
693+
dims = reverse(Tuple.dims))
694+
offsets = cumprod([1; dims[1:end-1]...])
695+
696+
expectation_values = zeros(ComplexF64, N)
691697

692-
x = 0.0im
693-
for J in off+2:length.data[1, :])
694-
x += ρ[J, J-off] * prod(sqrt.(_ind2sub(t, J) .- 1))
698+
@inbounds for J in 2:size.data, 1)
699+
indices = Base._ind2sub(dims, J)
700+
for n in 1:N
701+
if indices[n] > 1
702+
expectation_values[n] += sqrt(indices[n] - 1) * ρ.data[J, J-offsets[n]]
703+
end
695704
end
696-
return x
697705
end
706+
707+
reverse!(expectation_values)
708+
709+
return isnothing(idx) ? expectation_values : expectation_values[idx]
698710
end
699711

700-
function get_coherence(v::QuantumObject{T,OperatorKetQuantumObject}) where {T}
701-
if length(v.dims) > 1
702-
throw(ArgumentError("Mean photon number not implemented for composite OPeratorKetQuantumObject"))
712+
function get_coherence(v::QuantumObject{T,OperatorKetQuantumObject,N}; idx::Union{Int,Nothing} = nothing) where {T,N}
713+
dims = reverse(Tuple(v.dims))
714+
715+
offsets = cumprod([1; dims[1:end-1]...])
716+
D = prod(dims)
717+
718+
expectation_values = zeros(ComplexF64, N)
719+
720+
@inbounds for J in 2:D
721+
indices = Base._ind2sub(dims, J)
722+
for n in 1:N
723+
if indices[n] > 1
724+
expectation_values[n] += sqrt(indices[n] - 1) * v.data[(J-offsets[n]-1)*D+J]
725+
end
726+
end
703727
end
704728

705-
d = v.dims[1]
706-
return mapreduce(n -> sqrt(n - 1) * v.data[(n-1)*d+n-1], +, 2:d)
729+
reverse!(expectation_values)
730+
731+
return isnothing(idx) ? expectation_values : expectation_values[idx]
707732
end
708733

734+
get_coherence::QuantumObject{T,KetQuantumObject,1}) where {T} =
735+
mapreduce(n -> sqrt(n - 1) * ψ.data[n] * conj.data[n-1]), +, 2:ψ.dims[1])
736+
get_coherence::QuantumObject{T,OperatorQuantumObject,1}) where {T} =
737+
mapreduce(n -> sqrt(n - 1) * ρ.data[n, n-1], +, 2:ρ.dims[1])
738+
get_coherence(v::QuantumObject{T,OperatorKetQuantumObject,1}) where {T} =
739+
mapreduce(n -> sqrt(n - 1) * v.data[(n-2)*v.dims[1]+n], +, 2:v.dims[1])
740+
709741
@doc raw"""
710742
remove_coherence(ψ::QuantumObject)
711743
712744
Returns the corresponding state with the coherence removed: ``\ket{\delta_\alpha} = \exp ( \alpha^* \hat{a} - \alpha \hat{a}^\dagger ) \ket{\psi}`` for a pure state, and ``\hat{\rho_\alpha} = \exp ( \alpha^* \hat{a} - \alpha \hat{a}^\dagger ) \hat{\rho} \exp ( -\bar{\alpha} \hat{a} + \alpha \hat{a}^\dagger )`` for a density matrix. These states correspond to the quantum fluctuations around the coherent state ``\ket{\alpha}`` or ``|\alpha\rangle\langle\alpha|``.
713745
"""
714-
function remove_coherence::QuantumObject{<:AbstractArray,KetQuantumObject,1})
715-
α = get_coherence(ψ)
716-
D = displace.dims[1], α)
746+
function remove_coherence::QuantumObject{T,KetQuantumObject}) where {T}
747+
αs = get_coherence(ψ)
748+
D = mapreduce(k -> displace.dims[k], αs[k]), , eachindex.dims))
717749

718750
return D' * ψ
719751
end
720752

721-
function remove_coherence::QuantumObject{<:AbstractArray,OperatorQuantumObject,1})
722-
α = get_coherence(ρ)
723-
D = displace.dims[1], α)
753+
function remove_coherence::QuantumObject{T,OperatorQuantumObject}) where {T}
754+
αs = get_coherence(ρ)
755+
D = mapreduce(k -> displace.dims[k], αs[k]), , eachindex.dims))
724756

725757
return D' * ρ * D
726758
end
@@ -730,7 +762,7 @@ end
730762
731763
Get the mean occupation number ``n`` by measuring the expectation value of the number operator ``\hat{n}`` on a state ``\ket{\psi}``, a density matrix ``\hat{\rho}`` or a vectorized density matrix ``\ket{\hat{\rho}}``.
732764
733-
It returns the expectation value of the number operator.
765+
If the state is a composite state, the mean occupation numbers of each subsystems are returned as an array of real numbers. It is possible to specify the index of the sybsystem to get the mean occupation number by fixing `idx`.
734766
"""
735767
function mean_occupation::QuantumObject{T,KetQuantumObject}; idx::Union{Int,Nothing} = nothing) where {T}
736768
t = Tuple(reverse.dims))
@@ -751,7 +783,6 @@ function mean_occupation(ρ::QuantumObject{T,OperatorQuantumObject}; idx::Union{
751783
t = Tuple(reverse.dims))
752784
mean_occ = zeros(eltype.data), length.dims))
753785

754-
x = 0.0im
755786
for J in eachindex.data[:, 1])
756787
sub_indices = _ind2sub(t, J) .- 1
757788
mean_occ .+= ρ[J, J] .* sub_indices
@@ -764,15 +795,26 @@ end
764795
mean_occupation::QuantumObject{T,OperatorQuantumObject,1}) where {T} =
765796
mapreduce(k -> ρ[k, k] * (k - 1), +, 1:ρ.dims[1])
766797

767-
function mean_occupation(v::QuantumObject{T,OperatorKetQuantumObject}) where {T}
768-
if length(v.dims) > 1
769-
throw(ArgumentError("Mean photon number not implemented for composite OPeratorKetQuantumObject"))
770-
end
771-
798+
function mean_occupation(v::QuantumObject{T,OperatorKetQuantumObject,1}) where {T}
772799
d = v.dims[1]
773800
return real(mapreduce(k -> v[(k-1)*d+k] * (k - 1), +, 1:d))
774801
end
775802

803+
function mean_occupation(v::QuantumObject{T,OperatorKetQuantumObject}; idx::Union{Int,Nothing} = nothing) where {T}
804+
dims = reverse(Tuple(v.dims))
805+
mean_occ = zeros(eltype(v.data), length(v.dims))
806+
807+
offset = prod(dims)
808+
809+
for J in 1:offset
810+
sub_indices = _ind2sub(dims, J) .- 1
811+
mean_occ .+= v[(J-1)*offset+J] .* sub_indices
812+
end
813+
reverse!(mean_occ)
814+
815+
return isnothing(idx) ? real.(mean_occ) : real(mean_occ[idx])
816+
end
817+
776818
@doc raw"""
777819
permute(A::QuantumObject, order::Union{AbstractVector{Int},Tuple})
778820

test/core-test/quantum_objects.jl

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -443,20 +443,93 @@
443443
end
444444

445445
@testset "get and remove coherence" begin
446-
ψ = coherent(30, 3)
447-
α = get_coherence(ψ)
448-
@test isapprox(abs(α), 3, atol = 1e-5)
446+
α1 = 3.0 + 1.5im
447+
ψ1 = coherent(30, α1)
448+
β1 = get_coherence(ψ1)
449+
@test isapprox(α1, β1, atol = 1e-4)
450+
451+
ρ1 = ket2dm(ψ1)
452+
β1 = get_coherence(ρ1)
453+
@test isapprox(α1, β1, atol = 1e-4)
454+
455+
v1 = mat2vec(ρ1)
456+
β1 = get_coherence(v1)
457+
@test isapprox(α1, β1, atol = 1e-4)
458+
459+
α2 = 1.2 + 0.3im
460+
ψ2 = coherent(15, α2)
461+
β2 = get_coherence(ψ2)
462+
@test isapprox(α2, β2, atol = 1e-4)
463+
464+
ρ2 = ket2dm(ψ2)
465+
β2 = get_coherence(ρ2)
466+
@test isapprox(α2, β2, atol = 1e-4)
467+
468+
v2 = mat2vec(ρ2)
469+
β2 = get_coherence(v2)
470+
@test isapprox(α2, β2, atol = 1e-4)
471+
472+
ψ = ψ1 ψ2
473+
βs = get_coherence(ψ)
474+
@test length(βs) == 2
475+
@test isapprox(βs[1], α1, atol = 1e-4)
476+
@test isapprox(βs[2], α2, atol = 1e-4)
477+
478+
ρ = ρ1 ρ2
479+
βs = get_coherence(ρ)
480+
@test length(βs) == 2
481+
@test isapprox(βs[1], α1, atol = 1e-4)
482+
@test isapprox(βs[2], α2, atol = 1e-4)
483+
484+
v = mat2vec(ρ)
485+
βs = get_coherence(v)
486+
@test length(βs) == 2
487+
@test isapprox(βs[1], α1, atol = 1e-4)
488+
@test isapprox(βs[2], α2, atol = 1e-4)
489+
490+
αs = [α1, α2]
491+
for idx in 1:2
492+
β = get_coherence(ψ, idx = idx)
493+
@test isapprox(β, αs[idx], atol = 1e-4)
494+
β = get_coherence(ρ, idx = idx)
495+
@test isapprox(β, αs[idx], atol = 1e-4)
496+
β = get_coherence(v, idx = idx)
497+
@test isapprox(β, αs[idx], atol = 1e-4)
498+
end
499+
500+
@testset "Type Inference (get_coherence)" begin
501+
@inferred get_coherence(ψ1)
502+
@inferred get_coherence(ρ1)
503+
@inferred get_coherence(v1)
504+
@inferred get_coherence(ψ)
505+
@inferred get_coherence(ρ)
506+
@inferred get_coherence(v)
507+
end
508+
end
509+
510+
@testset "remove coherence" begin
511+
α1 = 3.0 + 1.5im
512+
ψ1 = coherent(30, α1)
513+
ψ2 = coherent(15, 1.2 + 0.3im)
514+
515+
δψ1 = remove_coherence(ψ1)
516+
@test abs2(δψ1[1]) > 0.999
517+
518+
ρ1 = ket2dm(ψ1)
519+
δρ1 = remove_coherence(ρ1)
520+
@test abs2(δρ1[1, 1]) > 0.999
521+
522+
ψ = ψ1 ψ2
449523
δψ = remove_coherence(ψ)
450524
@test abs2(δψ[1]) > 0.999
525+
451526
ρ = ket2dm(ψ)
452-
α = get_coherence(ρ)
453-
@test isapprox(abs(α), 3, atol = 1e-5)
454527
δρ = remove_coherence(ρ)
455528
@test abs2(δρ[1, 1]) > 0.999
456529

457-
@testset "Type Inference (get_coherence)" begin
458-
@inferred get_coherence)
459-
@inferred get_coherence)
530+
@testset "Type Inference (remove_coherence)" begin
531+
@inferred remove_coherence(ψ1)
532+
@inferred remove_coherence(ρ1)
460533
@inferred remove_coherence(ψ)
461534
@inferred remove_coherence(ρ)
462535
end
@@ -489,17 +562,24 @@
489562

490563
@test mean_occupation(ψc) Ns
491564
@test mean_occupation(ρc) Ns
565+
@test mean_occupation(vc) Ns
492566
@test prod(mean_occupation(ψc)) Nc
493567
@test prod(mean_occupation(ρc)) Nc
568+
@test prod(mean_occupation(vc)) Nc
494569
@test mean_occupation(ψc, idx = 1) N1
495570
@test mean_occupation(ρc, idx = 1) N1
496571
@test mean_occupation(ψc, idx = 2) N2
497572
@test mean_occupation(ρc, idx = 2) N2
573+
@test mean_occupation(vc, idx = 1) N1
574+
@test mean_occupation(vc, idx = 2) N2
498575

499576
@testset "Type Inference (mean_occupation)" begin
500577
@inferred mean_occupation(ψ1)
501578
@inferred mean_occupation(ρ1)
502579
@inferred mean_occupation(v1)
580+
@inferred mean_occupation(ψc)
581+
@inferred mean_occupation(ρc)
582+
@inferred mean_occupation(vc)
503583
end
504584
end
505585

0 commit comments

Comments
 (0)