Skip to content

Commit 29cae76

Browse files
author
Adria Labay
committed
return list of a and n if multiple subsystems and tests
1 parent 0d91c32 commit 29cae76

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
@@ -697,64 +697,96 @@ Returns the data of a [`AbstractQuantumObject`](@ref).
697697
get_data(A::AbstractQuantumObject) = A.data
698698

699699
@doc raw"""
700-
get_coherence(ψ::QuantumObject)
700+
get_coherence(ψ::QuantumObject; idx)
701701
702-
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}``.
703-
"""
704-
function get_coherence::QuantumObject{<:AbstractArray,KetQuantumObject})
705-
if length.dims) == 1
706-
return mapreduce(n -> sqrt(n - 1) * ψ.data[n] * conj.data[n-1]), +, 2:ψ.dims[1])
707-
else
708-
off = sum(cumprod(reverse.dims[2:end]))) + 1
709-
t = Tuple(reverse.dims))
702+
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``.
710703
711-
x = 0.0im
712-
for J in off+2:length.data)
713-
x += ψ[J] * conj(ψ[J-off]) * prod(sqrt.(_ind2sub(t, J) .- 1))
704+
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`.
705+
"""
706+
function get_coherence::QuantumObject{T,KetQuantumObject,N}; idx::Union{Nothing,Int} = nothing) where {T,N}
707+
dims = reverse(Tuple.dims))
708+
offsets = cumprod([1; dims[1:end-1]...])
709+
710+
expectation_values = zeros(ComplexF64, N)
711+
712+
for J in 2:length.data)
713+
indices = Base._ind2sub(dims, J)
714+
for n in 1:N
715+
if indices[n] > 1
716+
expectation_values[n] += conj.data[J-offsets[n]]) * sqrt(indices[n] - 1) * ψ.data[J]
717+
end
714718
end
715-
return x
716719
end
720+
721+
reverse!(expectation_values)
722+
723+
return isnothing(idx) ? expectation_values : expectation_values[idx]
717724
end
718725

719-
function get_coherence::QuantumObject{<:AbstractArray,OperatorQuantumObject})
720-
if length.dims) == 1
721-
return mapreduce(n -> sqrt(n - 1) * ρ.data[n, n-1], +, 2:ρ.dims[1])
722-
else
723-
off = sum(cumprod(reverse.dims[2:end]))) + 1
724-
t = Tuple(reverse.dims))
726+
function get_coherence::QuantumObject{T,OperatorQuantumObject,N}; idx::Union{Int,Nothing} = nothing) where {T,N}
727+
dims = reverse(Tuple.dims))
728+
offsets = cumprod([1; dims[1:end-1]...])
729+
730+
expectation_values = zeros(ComplexF64, N)
725731

726-
x = 0.0im
727-
for J in off+2:length.data[1, :])
728-
x += ρ[J, J-off] * prod(sqrt.(_ind2sub(t, J) .- 1))
732+
@inbounds for J in 2:size.data, 1)
733+
indices = Base._ind2sub(dims, J)
734+
for n in 1:N
735+
if indices[n] > 1
736+
expectation_values[n] += sqrt(indices[n] - 1) * ρ.data[J, J-offsets[n]]
737+
end
729738
end
730-
return x
731739
end
740+
741+
reverse!(expectation_values)
742+
743+
return isnothing(idx) ? expectation_values : expectation_values[idx]
732744
end
733745

734-
function get_coherence(v::QuantumObject{T,OperatorKetQuantumObject}) where {T}
735-
if length(v.dims) > 1
736-
throw(ArgumentError("Mean photon number not implemented for composite OPeratorKetQuantumObject"))
746+
function get_coherence(v::QuantumObject{T,OperatorKetQuantumObject,N}; idx::Union{Int,Nothing} = nothing) where {T,N}
747+
dims = reverse(Tuple(v.dims))
748+
749+
offsets = cumprod([1; dims[1:end-1]...])
750+
D = prod(dims)
751+
752+
expectation_values = zeros(ComplexF64, N)
753+
754+
@inbounds for J in 2:D
755+
indices = Base._ind2sub(dims, J)
756+
for n in 1:N
757+
if indices[n] > 1
758+
expectation_values[n] += sqrt(indices[n] - 1) * v.data[(J-offsets[n]-1)*D+J]
759+
end
760+
end
737761
end
738762

739-
d = v.dims[1]
740-
return mapreduce(n -> sqrt(n - 1) * v.data[(n-1)*d+n-1], +, 2:d)
763+
reverse!(expectation_values)
764+
765+
return isnothing(idx) ? expectation_values : expectation_values[idx]
741766
end
742767

768+
get_coherence::QuantumObject{T,KetQuantumObject,1}) where {T} =
769+
mapreduce(n -> sqrt(n - 1) * ψ.data[n] * conj.data[n-1]), +, 2:ψ.dims[1])
770+
get_coherence::QuantumObject{T,OperatorQuantumObject,1}) where {T} =
771+
mapreduce(n -> sqrt(n - 1) * ρ.data[n, n-1], +, 2:ρ.dims[1])
772+
get_coherence(v::QuantumObject{T,OperatorKetQuantumObject,1}) where {T} =
773+
mapreduce(n -> sqrt(n - 1) * v.data[(n-2)*v.dims[1]+n], +, 2:v.dims[1])
774+
743775
@doc raw"""
744776
remove_coherence(ψ::QuantumObject)
745777
746778
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|``.
747779
"""
748-
function remove_coherence::QuantumObject{<:AbstractArray,KetQuantumObject,1})
749-
α = get_coherence(ψ)
750-
D = displace.dims[1], α)
780+
function remove_coherence::QuantumObject{T,KetQuantumObject}) where {T}
781+
αs = get_coherence(ψ)
782+
D = mapreduce(k -> displace.dims[k], αs[k]), , eachindex.dims))
751783

752784
return D' * ψ
753785
end
754786

755-
function remove_coherence::QuantumObject{<:AbstractArray,OperatorQuantumObject,1})
756-
α = get_coherence(ρ)
757-
D = displace.dims[1], α)
787+
function remove_coherence::QuantumObject{T,OperatorQuantumObject}) where {T}
788+
αs = get_coherence(ρ)
789+
D = mapreduce(k -> displace.dims[k], αs[k]), , eachindex.dims))
758790

759791
return D' * ρ * D
760792
end
@@ -764,7 +796,7 @@ end
764796
765797
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}}``.
766798
767-
It returns the expectation value of the number operator.
799+
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`.
768800
"""
769801
function mean_occupation::QuantumObject{T,KetQuantumObject}; idx::Union{Int,Nothing} = nothing) where {T}
770802
t = Tuple(reverse.dims))
@@ -785,7 +817,6 @@ function mean_occupation(ρ::QuantumObject{T,OperatorQuantumObject}; idx::Union{
785817
t = Tuple(reverse.dims))
786818
mean_occ = zeros(eltype.data), length.dims))
787819

788-
x = 0.0im
789820
for J in eachindex.data[:, 1])
790821
sub_indices = _ind2sub(t, J) .- 1
791822
mean_occ .+= ρ[J, J] .* sub_indices
@@ -798,15 +829,26 @@ end
798829
mean_occupation::QuantumObject{T,OperatorQuantumObject,1}) where {T} =
799830
mapreduce(k -> ρ[k, k] * (k - 1), +, 1:ρ.dims[1])
800831

801-
function mean_occupation(v::QuantumObject{T,OperatorKetQuantumObject}) where {T}
802-
if length(v.dims) > 1
803-
throw(ArgumentError("Mean photon number not implemented for composite OPeratorKetQuantumObject"))
804-
end
805-
832+
function mean_occupation(v::QuantumObject{T,OperatorKetQuantumObject,1}) where {T}
806833
d = v.dims[1]
807834
return real(mapreduce(k -> v[(k-1)*d+k] * (k - 1), +, 1:d))
808835
end
809836

837+
function mean_occupation(v::QuantumObject{T,OperatorKetQuantumObject}; idx::Union{Int,Nothing} = nothing) where {T}
838+
dims = reverse(Tuple(v.dims))
839+
mean_occ = zeros(eltype(v.data), length(v.dims))
840+
841+
offset = prod(dims)
842+
843+
for J in 1:offset
844+
sub_indices = _ind2sub(dims, J) .- 1
845+
mean_occ .+= v[(J-1)*offset+J] .* sub_indices
846+
end
847+
reverse!(mean_occ)
848+
849+
return isnothing(idx) ? real.(mean_occ) : real(mean_occ[idx])
850+
end
851+
810852
@doc raw"""
811853
permute(A::QuantumObject, order::Union{AbstractVector{Int},Tuple})
812854

test/core-test/quantum_objects.jl

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -451,20 +451,93 @@
451451
end
452452

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

465-
@testset "Type Inference (get_coherence)" begin
466-
@inferred get_coherence)
467-
@inferred get_coherence)
538+
@testset "Type Inference (remove_coherence)" begin
539+
@inferred remove_coherence(ψ1)
540+
@inferred remove_coherence(ρ1)
468541
@inferred remove_coherence(ψ)
469542
@inferred remove_coherence(ρ)
470543
end
@@ -497,17 +570,24 @@
497570

498571
@test mean_occupation(ψc) Ns
499572
@test mean_occupation(ρc) Ns
573+
@test mean_occupation(vc) Ns
500574
@test prod(mean_occupation(ψc)) Nc
501575
@test prod(mean_occupation(ρc)) Nc
576+
@test prod(mean_occupation(vc)) Nc
502577
@test mean_occupation(ψc, idx = 1) N1
503578
@test mean_occupation(ρc, idx = 1) N1
504579
@test mean_occupation(ψc, idx = 2) N2
505580
@test mean_occupation(ρc, idx = 2) N2
581+
@test mean_occupation(vc, idx = 1) N1
582+
@test mean_occupation(vc, idx = 2) N2
506583

507584
@testset "Type Inference (mean_occupation)" begin
508585
@inferred mean_occupation(ψ1)
509586
@inferred mean_occupation(ρ1)
510587
@inferred mean_occupation(v1)
588+
@inferred mean_occupation(ψc)
589+
@inferred mean_occupation(ρc)
590+
@inferred mean_occupation(vc)
511591
end
512592
end
513593

0 commit comments

Comments
 (0)