From 5c729b36459fbecb623656fed41849dded184596 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Tue, 18 Feb 2025 14:43:47 +0900 Subject: [PATCH 01/11] add a new file for entropy related functions --- src/QuantumToolbox.jl | 1 + src/entropy.jl | 76 +++++++++++++++++++++++++++++++++++++++++++ src/metrics.jl | 76 +------------------------------------------ 3 files changed, 78 insertions(+), 75 deletions(-) create mode 100644 src/entropy.jl diff --git a/src/QuantumToolbox.jl b/src/QuantumToolbox.jl index d06979dda..44b1aa84e 100644 --- a/src/QuantumToolbox.jl +++ b/src/QuantumToolbox.jl @@ -117,6 +117,7 @@ include("spectrum.jl") include("wigner.jl") include("spin_lattice.jl") include("arnoldi.jl") +include("entropy.jl") include("metrics.jl") include("negativity.jl") include("steadystate.jl") diff --git a/src/entropy.jl b/src/entropy.jl new file mode 100644 index 000000000..7889aa6b8 --- /dev/null +++ b/src/entropy.jl @@ -0,0 +1,76 @@ +#= +Entropy related functions. +=# + +export entropy_vn +export entanglement + +@doc raw""" + entropy_vn(ρ::QuantumObject; base::Int=0, tol::Real=1e-15) + +Calculates the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_entropy) ``S = - \textrm{Tr} \left[ \hat{\rho} \log \left( \hat{\rho} \right) \right]``, where ``\hat{\rho}`` is the density matrix of the system. + +# Notes + +- `ρ` is the quantum state, can be either a [`Ket`](@ref) or [`Operator`](@ref). +- `base` specifies the base of the logarithm to use, and when using the default value `0`, the natural logarithm is used. +- `tol` describes the absolute tolerance for detecting the zero-valued eigenvalues of the density matrix ``\hat{\rho}``. + +# Examples + +Pure state: +```jldoctest +julia> ψ = fock(2,0) + +Quantum Object: type=Ket dims=[2] size=(2,) +2-element Vector{ComplexF64}: + 1.0 + 0.0im + 0.0 + 0.0im + +julia> entropy_vn(ψ, base=2) +-0.0 +``` + +Mixed state: +```jldoctest +julia> ρ = maximally_mixed_dm(2) + +Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +2×2 Diagonal{ComplexF64, Vector{ComplexF64}}: + 0.5-0.0im ⋅ + ⋅ 0.5-0.0im + +julia> entropy_vn(ρ, base=2) +1.0 +``` +""" +function entropy_vn( + ρ::QuantumObject{ObjType}; + base::Int = 0, + tol::Real = 1e-15, +) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject}} + T = eltype(ρ) + vals = eigenenergies(ket2dm(ρ)) + indexes = findall(x -> abs(x) > tol, vals) + length(indexes) == 0 && return zero(real(T)) + nzvals = vals[indexes] + logvals = base != 0 ? log.(base, Complex.(nzvals)) : log.(Complex.(nzvals)) + return -real(mapreduce(*, +, nzvals, logvals)) +end + +""" + entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple}) + +Calculates the entanglement by doing the partial trace of `QO`, selecting only the dimensions +with the indices contained in the `sel` vector, and then using the Von Neumann entropy [`entropy_vn`](@ref). +""" +function entanglement( + QO::QuantumObject{OpType}, + sel::Union{AbstractVector{Int},Tuple}, +) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} + ψ = normalize(QO) + ρ_tr = ptrace(ψ, sel) + entropy = entropy_vn(ρ_tr) + return (entropy > 0) * entropy +end +entanglement(QO::QuantumObject, sel::Int) = entanglement(QO, (sel,)) diff --git a/src/metrics.jl b/src/metrics.jl index e178e579b..39ff1e3ad 100644 --- a/src/metrics.jl +++ b/src/metrics.jl @@ -2,81 +2,7 @@ Functions for calculating metrics (distance measures) between states and operators. =# -export entropy_vn, entanglement, tracedist, fidelity - -@doc raw""" - entropy_vn(ρ::QuantumObject; base::Int=0, tol::Real=1e-15) - -Calculates the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_entropy) -``S = - \textrm{Tr} \left[ \hat{\rho} \log \left( \hat{\rho} \right) \right]`` where ``\hat{\rho}`` -is the density matrix of the system. - -The `base` parameter specifies the base of the logarithm to use, and when using the default value 0, -the natural logarithm is used. The `tol` parameter -describes the absolute tolerance for detecting the zero-valued eigenvalues of the density -matrix ``\hat{\rho}``. - -# Examples - -Pure state: -```jldoctest -julia> ψ = fock(2,0) - -Quantum Object: type=Ket dims=[2] size=(2,) -2-element Vector{ComplexF64}: - 1.0 + 0.0im - 0.0 + 0.0im - -julia> ρ = ket2dm(ψ) - -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true -2×2 Matrix{ComplexF64}: - 1.0+0.0im 0.0+0.0im - 0.0+0.0im 0.0+0.0im - -julia> entropy_vn(ρ, base=2) --0.0 -``` - -Mixed state: -```jldoctest -julia> ρ = maximally_mixed_dm(2) - -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true -2×2 Diagonal{ComplexF64, Vector{ComplexF64}}: - 0.5-0.0im ⋅ - ⋅ 0.5-0.0im - -julia> entropy_vn(ρ, base=2) -1.0 -``` -""" -function entropy_vn(ρ::QuantumObject{OperatorQuantumObject}; base::Int = 0, tol::Real = 1e-15) - T = eltype(ρ) - vals = eigenenergies(ρ) - indexes = findall(x -> abs(x) > tol, vals) - length(indexes) == 0 && return zero(real(T)) - nzvals = vals[indexes] - logvals = base != 0 ? log.(base, Complex.(nzvals)) : log.(Complex.(nzvals)) - return -real(mapreduce(*, +, nzvals, logvals)) -end - -""" - entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple}) - -Calculates the entanglement by doing the partial trace of `QO`, selecting only the dimensions -with the indices contained in the `sel` vector, and then using the Von Neumann entropy [`entropy_vn`](@ref). -""" -function entanglement( - QO::QuantumObject{OpType}, - sel::Union{AbstractVector{Int},Tuple}, -) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} - ψ = normalize(QO) - ρ_tr = ptrace(ψ, sel) - entropy = entropy_vn(ρ_tr) - return (entropy > 0) * entropy -end -entanglement(QO::QuantumObject, sel::Int) = entanglement(QO, (sel,)) +export tracedist, fidelity @doc raw""" tracedist(ρ::QuantumObject, σ::QuantumObject) From 8d0930400bfe06ab420ae2282164bf001f403045 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Tue, 18 Feb 2025 15:06:25 +0900 Subject: [PATCH 02/11] introduce `entropy_linear` --- docs/src/resources/api.md | 3 ++- src/entropy.jl | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/src/resources/api.md b/docs/src/resources/api.md index a6f5389a9..e8e7c80ff 100644 --- a/docs/src/resources/api.md +++ b/docs/src/resources/api.md @@ -248,10 +248,11 @@ ExponentialSeries PseudoInverse ``` -## [Metrics](@id doc-API:Metrics) +## [Entropies and Metrics](@id doc-API:Entropies-and-Metrics) ```@docs entropy_vn +entropy_linear entanglement tracedist fidelity diff --git a/src/entropy.jl b/src/entropy.jl index 7889aa6b8..010b2ee96 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -2,7 +2,7 @@ Entropy related functions. =# -export entropy_vn +export entropy_vn, entropy_linear export entanglement @doc raw""" @@ -12,7 +12,7 @@ Calculates the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_e # Notes -- `ρ` is the quantum state, can be either a [`Ket`](@ref) or [`Operator`](@ref). +- `ρ` is the quantum state, can be either a [`Ket`](@ref) or an [`Operator`](@ref). - `base` specifies the base of the logarithm to use, and when using the default value `0`, the natural logarithm is used. - `tol` describes the absolute tolerance for detecting the zero-valued eigenvalues of the density matrix ``\hat{\rho}``. @@ -58,6 +58,16 @@ function entropy_vn( return -real(mapreduce(*, +, nzvals, logvals)) end +@doc raw""" + entropy_linear(ρ::QuantumObject) + +Calculates the linear entropy ``S_L = 1 - \textrm{Tr} \left[ \hat{\rho}^2 \right]``, where ``\hat{\rho}`` is the density matrix of the system. + +Note that `ρ` can be either a [`Ket`](@ref) or an [`Operator`](@ref). +""" +entropy_linear(ρ::QuantumObject{ObjType}) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject}} = + 1.0 - purity(ρ) # use 1.0 to make sure it always return value in Float-type + """ entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple}) From 6f5885d682ff8f50eb5dad24b9d8127fa0269525 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Tue, 18 Feb 2025 15:49:04 +0900 Subject: [PATCH 03/11] introduce `entropy_mutual` --- docs/src/resources/api.md | 1 + src/entropy.jl | 42 +++++++++++++++++++++++++-- src/qobj/arithmetic_and_attributes.jl | 10 +++---- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/docs/src/resources/api.md b/docs/src/resources/api.md index e8e7c80ff..ae63cb2d8 100644 --- a/docs/src/resources/api.md +++ b/docs/src/resources/api.md @@ -253,6 +253,7 @@ PseudoInverse ```@docs entropy_vn entropy_linear +entropy_mutual entanglement tracedist fidelity diff --git a/src/entropy.jl b/src/entropy.jl index 010b2ee96..95367ba7f 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -2,7 +2,7 @@ Entropy related functions. =# -export entropy_vn, entropy_linear +export entropy_vn, entropy_linear, entropy_mutual export entanglement @doc raw""" @@ -68,11 +68,47 @@ Note that `ρ` can be either a [`Ket`](@ref) or an [`Operator`](@ref). entropy_linear(ρ::QuantumObject{ObjType}) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject}} = 1.0 - purity(ρ) # use 1.0 to make sure it always return value in Float-type +@doc raw""" + entropy_mutual(ρAB::QuantumObject, selA, selB; kwargs...) + +Calculates the mutual information ``I(A:B) = S(\hat{\rho}_A) + S(\hat{\rho}_B) - S(\hat{\rho}_{AB})`` between subsystems ``A`` and ``B``. + +Here, ``S`` is the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_entropy), ``\hat{\rho}_{AB}`` is the density matrix of the entire system, ``\hat{\rho}_A = \textrm{Tr}_B \left[ \hat{\rho}_{AB} \right]``, ``\hat{\rho}_B = \textrm{Tr}_A \left[ \hat{\rho}_{AB} \right]``. + +# Notes + +- `ρ` can be either a [`Ket`](@ref) or an [`Operator`](@ref). +- `selA` specifies the indices of the sub-system `A` in `ρAB.dimensions`. See also [`ptrace`](@ref). +- `selB` specifies the indices of the sub-system `B` in `ρAB.dimensions`. See also [`ptrace`](@ref). +- `kwargs` are the keyword arguments for calculating Von Neumann entropy. See also [`entropy_vn`](@ref). +""" +function entropy_mutual( + ρAB::QuantumObject{ObjType,<:AbstractDimensions{N}}, + selA::AType, + selB::BType; + kwargs..., +) where { + ObjType<:Union{KetQuantumObject,OperatorQuantumObject}, + N, + AType<:Union{Int,AbstractVector{Int},Tuple}, + BType<:Union{Int,AbstractVector{Int},Tuple}, +} + # check if selA and selB matches the dimensions of ρAB + sel_A_B = vcat(selA, selB) + (length(sel_A_B) != N) && ArgumentError( + "The indices in `selA = $(selA)` and `selB = $(selB)` does not match the given QuantumObject which has $N sub-systems", + ) + allunique(sel_A_B) || throw(ArgumentError("Duplicate selection indices in `selA = $(selA)` and `selB = $(selB)`")) + + ρA = ptrace(ρAB, selA) + ρB = ptrace(ρAB, selB) + return entropy_vn(ρA; kwargs...) + entropy_vn(ρB; kwargs...) - entropy_vn(ρAB; kwargs...) +end + """ entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple}) -Calculates the entanglement by doing the partial trace of `QO`, selecting only the dimensions -with the indices contained in the `sel` vector, and then using the Von Neumann entropy [`entropy_vn`](@ref). +Calculates the entanglement by doing the partial trace of `QO`, selecting only the dimensions with the indices contained in the `sel` vector, and then using the Von Neumann entropy [`entropy_vn`](@ref). """ function entanglement( QO::QuantumObject{OpType}, diff --git a/src/qobj/arithmetic_and_attributes.jl b/src/qobj/arithmetic_and_attributes.jl index add9248e8..2cf9503cc 100644 --- a/src/qobj/arithmetic_and_attributes.jl +++ b/src/qobj/arithmetic_and_attributes.jl @@ -568,8 +568,7 @@ Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true function ptrace(QO::QuantumObject{KetQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) _non_static_array_warning("sel", sel) - n_s = length(sel) - if n_s == 0 # return full trace for empty sel + if length(sel) == 0 # return full trace for empty sel return tr(ket2dm(QO)) else n_d = length(QO.dimensions) @@ -577,7 +576,7 @@ function ptrace(QO::QuantumObject{KetQuantumObject}, sel::Union{AbstractVector{I (any(>(n_d), sel) || any(<(1), sel)) && throw( ArgumentError("Invalid indices in `sel`: $(sel), the given QuantumObject only have $(n_d) sub-systems"), ) - (n_s != length(unique(sel))) && throw(ArgumentError("Duplicate selection indices in `sel`: $(sel)")) + allunique(sel) || throw(ArgumentError("Duplicate selection indices in `sel`: $(sel)")) (n_d == 1) && return ket2dm(QO) # ptrace should always return Operator end @@ -596,8 +595,7 @@ function ptrace(QO::QuantumObject{OperatorQuantumObject}, sel::Union{AbstractVec _non_static_array_warning("sel", sel) - n_s = length(sel) - if n_s == 0 # return full trace for empty sel + if length(sel) == 0 # return full trace for empty sel return tr(QO) else n_d = length(QO.dimensions) @@ -605,7 +603,7 @@ function ptrace(QO::QuantumObject{OperatorQuantumObject}, sel::Union{AbstractVec (any(>(n_d), sel) || any(<(1), sel)) && throw( ArgumentError("Invalid indices in `sel`: $(sel), the given QuantumObject only have $(n_d) sub-systems"), ) - (n_s != length(unique(sel))) && throw(ArgumentError("Duplicate selection indices in `sel`: $(sel)")) + allunique(sel) || throw(ArgumentError("Duplicate selection indices in `sel`: $(sel)")) (n_d == 1) && return QO end From 5b93860a5aff60e3fdd9a031b6358a833b0d50c9 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Tue, 18 Feb 2025 15:57:02 +0900 Subject: [PATCH 04/11] introduce `entropy_conditional` --- docs/src/resources/api.md | 1 + src/entropy.jl | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/src/resources/api.md b/docs/src/resources/api.md index ae63cb2d8..cc7f4cc86 100644 --- a/docs/src/resources/api.md +++ b/docs/src/resources/api.md @@ -254,6 +254,7 @@ PseudoInverse entropy_vn entropy_linear entropy_mutual +entropy_conditional entanglement tracedist fidelity diff --git a/src/entropy.jl b/src/entropy.jl index 95367ba7f..02c908901 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -2,7 +2,7 @@ Entropy related functions. =# -export entropy_vn, entropy_linear, entropy_mutual +export entropy_vn, entropy_linear, entropy_mutual, entropy_conditional export entanglement @doc raw""" @@ -77,7 +77,7 @@ Here, ``S`` is the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neuma # Notes -- `ρ` can be either a [`Ket`](@ref) or an [`Operator`](@ref). +- `ρAB` can be either a [`Ket`](@ref) or an [`Operator`](@ref). - `selA` specifies the indices of the sub-system `A` in `ρAB.dimensions`. See also [`ptrace`](@ref). - `selB` specifies the indices of the sub-system `B` in `ρAB.dimensions`. See also [`ptrace`](@ref). - `kwargs` are the keyword arguments for calculating Von Neumann entropy. See also [`entropy_vn`](@ref). @@ -105,6 +105,29 @@ function entropy_mutual( return entropy_vn(ρA; kwargs...) + entropy_vn(ρB; kwargs...) - entropy_vn(ρAB; kwargs...) end +@doc raw""" + entropy_conditional(ρAB::QuantumObject, selB; kwargs...) + +Calculates the conditional entropy with respect to sub-system ``B``: ``S(A|B) = S(\hat{\rho}_{AB}) - S(\hat{\rho}_{B})``. + +Here, ``S`` is the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_entropy), ``\hat{\rho}_{AB}`` is the density matrix of the entire system, and ``\hat{\rho}_B = \textrm{Tr}_A \left[ \hat{\rho}_{AB} \right]``. + +# Notes + +- `ρAB` can be either a [`Ket`](@ref) or an [`Operator`](@ref). +- `selB` specifies the indices of the sub-system `B` in `ρAB.dimensions`. See also [`ptrace`](@ref). +- `kwargs` are the keyword arguments for calculating Von Neumann entropy. See also [`entropy_vn`](@ref). +""" +entropy_conditional( + ρAB::QuantumObject{ObjType,<:AbstractDimensions{N}}, + selB::BType; + kwargs..., +) where { + ObjType<:Union{KetQuantumObject,OperatorQuantumObject}, + N, + BType<:Union{Int,AbstractVector{Int},Tuple}, +} = entropy_vn(ρAB; kwargs...) - entropy_vn(ptrace(ρAB, selB); kwargs...) + """ entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple}) From 35507726ceaf7ee15a7f3d7c31e21eb6443c1199 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Tue, 18 Feb 2025 15:57:22 +0900 Subject: [PATCH 05/11] format files --- src/entropy.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/entropy.jl b/src/entropy.jl index 02c908901..969cf6ebc 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -122,11 +122,8 @@ entropy_conditional( ρAB::QuantumObject{ObjType,<:AbstractDimensions{N}}, selB::BType; kwargs..., -) where { - ObjType<:Union{KetQuantumObject,OperatorQuantumObject}, - N, - BType<:Union{Int,AbstractVector{Int},Tuple}, -} = entropy_vn(ρAB; kwargs...) - entropy_vn(ptrace(ρAB, selB); kwargs...) +) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject},N,BType<:Union{Int,AbstractVector{Int},Tuple}} = + entropy_vn(ρAB; kwargs...) - entropy_vn(ptrace(ρAB, selB); kwargs...) """ entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple}) From 0ffbb5d89682c21170624a64d4bf8f13e0a968c8 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Wed, 19 Feb 2025 11:51:22 +0900 Subject: [PATCH 06/11] introduce `entropy_relative` --- docs/src/resources/api.md | 1 + src/entropy.jl | 72 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/docs/src/resources/api.md b/docs/src/resources/api.md index cc7f4cc86..e647cff3d 100644 --- a/docs/src/resources/api.md +++ b/docs/src/resources/api.md @@ -252,6 +252,7 @@ PseudoInverse ```@docs entropy_vn +entropy_relative entropy_linear entropy_mutual entropy_conditional diff --git a/src/entropy.jl b/src/entropy.jl index 969cf6ebc..3b796d25d 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -2,7 +2,7 @@ Entropy related functions. =# -export entropy_vn, entropy_linear, entropy_mutual, entropy_conditional +export entropy_vn, entropy_relative, entropy_linear, entropy_mutual, entropy_conditional export entanglement @doc raw""" @@ -58,10 +58,74 @@ function entropy_vn( return -real(mapreduce(*, +, nzvals, logvals)) end +@doc raw""" + entropy_relative(ρ::QuantumObject, σ::QuantumObject; base::Int=0, tol::Real=1e-15) + +Calculates the [quantum relative entropy](https://en.wikipedia.org/wiki/Quantum_relative_entropy) of ``\hat{\rho}`` with respect to ``\hat{\sigma}``: ``D(\hat{\rho}||\hat{\sigma}) = \textrm{Tr} \left[ \hat{\rho} \log \left( \hat{\rho} \right) \right] - \textrm{Tr} \left[ \hat{\rho} \log \left( \hat{\sigma} \right) \right]``. + +# Notes + +- `ρ` is a quantum state, can be either a [`Ket`](@ref) or an [`Operator`](@ref). +- `σ` is a quantum state, can be either a [`Ket`](@ref) or an [`Operator`](@ref). +- `base` specifies the base of the logarithm to use, and when using the default value `0`, the natural logarithm is used. +- `tol` describes the absolute tolerance for detecting the zero-valued eigenvalues of the density matrix ``\hat{\rho}``. + +# References +- [Nielsen-Chuang2011; section 11.3.1, page 511](@citet) +""" +function entropy_relative( + ρ::QuantumObject{ObjType1}, + σ::QuantumObject{ObjType2}; + base::Int = 0, + tol::Real = 1e-15, +) where { + ObjType1<:Union{KetQuantumObject,OperatorQuantumObject}, + ObjType2<:Union{KetQuantumObject,OperatorQuantumObject}, +} + check_dimensions(ρ, σ) + + # the logic of this code follows the detail given in the reference of the docstring + # consider the eigen decompositions: + # ρ = Σ_i p_i |i⟩⟨i| + # σ = Σ_j q_j |j⟩⟨j| + ρ_result = eigenstates(ket2dm(ρ)) + σ_result = eigenstates(ket2dm(σ)) + + # make sure all p_i and q_j are real + any(p_i -> imag(p_i) >= tol, ρ_result.values) && error("Input `ρ` has non-real eigenvalues.") + any(q_j -> imag(q_j) >= tol, σ_result.values) && error("Input `σ` has non-real eigenvalues.") + p = real(ρ_result.values) + q = real(σ_result.values) + Uρ = ρ_result.vectors + Uσ = σ_result.vectors + + # create P_ij matrix (all elements should be real) + P = abs2(Uρ' * Uσ) # this equals to ⟨i|j⟩⟨j|i⟩ + + # return +∞ if kernel of σ overlaps with support of ρ + dot((p .>= tol), (P .>= tol), (q .< tol)) && return Inf + + # Avoid -∞ from log(0), these terms will be multiplied by zero later anyway + replace!(q_j -> abs(q_j) < tol ? 1 : q_j, q) + p_vals = filter(p_i -> abs(p_i) >= tol, p) + + if base == 0 + log_p = log.(p_vals) + log_q = log.(q) + else + log_p = log.(base, p_vals) + log_q = log.(base, q) + end + + # the relative entropy is guaranteed to be ≥ 0 + # so we calculate the value to 0 to avoid small violations of the lower bound. + return max(0, dot(p_vals, log_p) - dot(p, P, log_q)) +end + @doc raw""" entropy_linear(ρ::QuantumObject) -Calculates the linear entropy ``S_L = 1 - \textrm{Tr} \left[ \hat{\rho}^2 \right]``, where ``\hat{\rho}`` is the density matrix of the system. +Calculates the quantum linear entropy ``S_L = 1 - \textrm{Tr} \left[ \hat{\rho}^2 \right]``, where ``\hat{\rho}`` is the density matrix of the system. Note that `ρ` can be either a [`Ket`](@ref) or an [`Operator`](@ref). """ @@ -71,7 +135,7 @@ entropy_linear(ρ::QuantumObject{ObjType}) where {ObjType<:Union{KetQuantumObjec @doc raw""" entropy_mutual(ρAB::QuantumObject, selA, selB; kwargs...) -Calculates the mutual information ``I(A:B) = S(\hat{\rho}_A) + S(\hat{\rho}_B) - S(\hat{\rho}_{AB})`` between subsystems ``A`` and ``B``. +Calculates the [quantum mutual information](https://en.wikipedia.org/wiki/Quantum_mutual_information) ``I(A:B) = S(\hat{\rho}_A) + S(\hat{\rho}_B) - S(\hat{\rho}_{AB})`` between subsystems ``A`` and ``B``. Here, ``S`` is the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_entropy), ``\hat{\rho}_{AB}`` is the density matrix of the entire system, ``\hat{\rho}_A = \textrm{Tr}_B \left[ \hat{\rho}_{AB} \right]``, ``\hat{\rho}_B = \textrm{Tr}_A \left[ \hat{\rho}_{AB} \right]``. @@ -108,7 +172,7 @@ end @doc raw""" entropy_conditional(ρAB::QuantumObject, selB; kwargs...) -Calculates the conditional entropy with respect to sub-system ``B``: ``S(A|B) = S(\hat{\rho}_{AB}) - S(\hat{\rho}_{B})``. +Calculates the [conditional quantum entropy](https://en.wikipedia.org/wiki/Conditional_quantum_entropy) with respect to sub-system ``B``: ``S(A|B) = S(\hat{\rho}_{AB}) - S(\hat{\rho}_{B})``. Here, ``S`` is the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neumann_entropy), ``\hat{\rho}_{AB}`` is the density matrix of the entire system, and ``\hat{\rho}_B = \textrm{Tr}_A \left[ \hat{\rho}_{AB} \right]``. From 84aa723ae0d485fdf085e2f3774eaf6c6729ab7c Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Wed, 19 Feb 2025 15:24:07 +0900 Subject: [PATCH 07/11] add tests for entropy functions --- src/entropy.jl | 21 +++--- src/qobj/functions.jl | 2 +- test/core-test/entanglement.jl | 13 ---- test/core-test/entropy_and_metric.jl | 101 +++++++++++++++++++++++++++ test/core-test/quantum_objects.jl | 42 ++--------- test/runtests.jl | 2 +- 6 files changed, 119 insertions(+), 62 deletions(-) delete mode 100644 test/core-test/entanglement.jl create mode 100644 test/core-test/entropy_and_metric.jl diff --git a/src/entropy.jl b/src/entropy.jl index 3b796d25d..13b8f57c9 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -100,10 +100,12 @@ function entropy_relative( Uσ = σ_result.vectors # create P_ij matrix (all elements should be real) - P = abs2(Uρ' * Uσ) # this equals to ⟨i|j⟩⟨j|i⟩ + P = abs2.(Uρ' * Uσ) # this equals to ⟨i|j⟩⟨j|i⟩ - # return +∞ if kernel of σ overlaps with support of ρ - dot((p .>= tol), (P .>= tol), (q .< tol)) && return Inf + # return +∞ if kernel of σ overlaps with support of ρ, i.e., supp(p) ⊆ supp(q) + # That is, if σ is not full rank, S(ρ||σ) = +∞ + # note that, one special case is that S(ρ||σ) = 0 (if ρ == σ) + ((transpose(p .>= tol) * (P .>= tol) * (q .< tol)) == 0) || return Inf # Avoid -∞ from log(0), these terms will be multiplied by zero later anyway replace!(q_j -> abs(q_j) < tol ? 1 : q_j, q) @@ -119,7 +121,7 @@ function entropy_relative( # the relative entropy is guaranteed to be ≥ 0 # so we calculate the value to 0 to avoid small violations of the lower bound. - return max(0, dot(p_vals, log_p) - dot(p, P, log_q)) + return max(0.0, dot(p_vals, log_p) - dot(p, P, log_q)) end @doc raw""" @@ -158,9 +160,11 @@ function entropy_mutual( BType<:Union{Int,AbstractVector{Int},Tuple}, } # check if selA and selB matches the dimensions of ρAB - sel_A_B = vcat(selA, selB) - (length(sel_A_B) != N) && ArgumentError( - "The indices in `selA = $(selA)` and `selB = $(selB)` does not match the given QuantumObject which has $N sub-systems", + sel_A_B = (selA..., selB...) + (length(sel_A_B) != N) && throw( + ArgumentError( + "The indices in `selA = $(selA)` and `selB = $(selB)` does not match the given QuantumObject which has $N sub-systems", + ), ) allunique(sel_A_B) || throw(ArgumentError("Duplicate selection indices in `selA = $(selA)` and `selB = $(selB)`")) @@ -196,11 +200,10 @@ Calculates the entanglement by doing the partial trace of `QO`, selecting only t """ function entanglement( QO::QuantumObject{OpType}, - sel::Union{AbstractVector{Int},Tuple}, + sel::Union{Int,AbstractVector{Int},Tuple}, ) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} ψ = normalize(QO) ρ_tr = ptrace(ψ, sel) entropy = entropy_vn(ρ_tr) return (entropy > 0) * entropy end -entanglement(QO::QuantumObject, sel::Int) = entanglement(QO, (sel,)) diff --git a/src/qobj/functions.jl b/src/qobj/functions.jl index 1d602bd92..4cb8d6c38 100644 --- a/src/qobj/functions.jl +++ b/src/qobj/functions.jl @@ -125,7 +125,7 @@ to_dense(::Type{T}, A::AbstractSparseArray) where {T<:Number} = Array{T}(A) to_dense(::Type{T1}, A::AbstractArray{T2}) where {T1<:Number,T2<:Number} = Array{T1}(A) to_dense(::Type{T}, A::AbstractArray{T}) where {T<:Number} = A -function to_dense(::Type{M}) where {M<:SparseMatrixCSC} +function to_dense(::Type{M}) where {M<:Union{Diagonal,SparseMatrixCSC}} T = M par = T.parameters npar = length(par) diff --git a/test/core-test/entanglement.jl b/test/core-test/entanglement.jl deleted file mode 100644 index c9df3e204..000000000 --- a/test/core-test/entanglement.jl +++ /dev/null @@ -1,13 +0,0 @@ -@testset "Entanglement" begin - g = fock(2, 1) - e = fock(2, 0) - state = normalize(kron(g, e) + kron(e, g)) - rho = state * state' - @test entanglement(state, 1) / log(2) ≈ 1 - @test entanglement(rho, 1) / log(2) ≈ 1 - - @testset "Type Stability (entanglement)" begin - @inferred entanglement(state, 1) - @inferred entanglement(rho, 1) - end -end diff --git a/test/core-test/entropy_and_metric.jl b/test/core-test/entropy_and_metric.jl new file mode 100644 index 000000000..ecf3faa84 --- /dev/null +++ b/test/core-test/entropy_and_metric.jl @@ -0,0 +1,101 @@ +@testset "entropy" begin + base = 2 + λ = rand() + ψ = rand_ket(10) + ρ1 = rand_dm(10) + ρ2 = rand_dm(10) + σ1 = rand_dm(10) + σ2 = rand_dm(10) + + dims = (2, 3) + ρAB = rand_dm((dims..., dims...)) + selA = (1, 2) + selB = (3, 4) + ρA = ptrace(ρAB, selA) + ρB = ptrace(ρAB, selB) + nA = nB = prod(dims) + IA = qeye(nA, dims = dims) + IB = qeye(nB, dims = dims) + + # quantum relative entropy + @test entropy_relative(ρ1, ψ) == Inf + @test entropy_relative(ρ1, rand_dm(10, rank = 9)) == Inf + @test entropy_relative(ψ, ψ) ≈ 0 + @test entropy_relative(λ * ρ1 + (1 - λ) * ρ2, λ * σ1 + (1 - λ) * σ2) <= + λ * entropy_relative(ρ1, σ1) + (1 - λ) * entropy_relative(ρ2, σ2) # joint convexity + + # relations between different entropies + @test entropy_relative(ρA, IA / nA) ≈ log(nA) - entropy_vn(ρA) + @test entropy_relative(ρB, IB / nB; base = base) ≈ log(base, nB) - entropy_vn(ρB; base = base) + @test entropy_relative(ρAB, tensor(ρA, ρB)) ≈ entropy_mutual(ρAB, selA, selB) + @test entropy_relative(ρAB, tensor(ρA, ρB)) ≈ entropy_mutual(ρAB, selA, selB) + @test entropy_relative(ρAB, tensor(ρA, IB / nB)) ≈ log(nB) - entropy_conditional(ρAB, selA) + @test entropy_linear(ρ1) == 1 - purity(ρ1) + + ρ_wrong = Qobj(rand(ComplexF64, 10, 10)) + @test_throws ErrorException entropy_relative(ρ1, ρ_wrong) + @test_throws ErrorException entropy_relative(ρ_wrong, ρ1) + @test_throws ArgumentError entropy_mutual(ρAB, 1, 3) + @test_throws ArgumentError entropy_mutual(ρAB, 1, (3, 4)) + @test_throws ArgumentError entropy_mutual(ρAB, (1, 2), 3) + @test_throws ArgumentError entropy_mutual(ρAB, (1, 2), (1, 3)) + + @testset "Type Stability (entropy)" begin + @inferred entropy_vn(ρ1) + @inferred entropy_vn(ρ1, base = base) + @inferred entropy_relative(ρ1, ψ) + @inferred entropy_relative(ρ1, σ1, base = base) + @inferred entropy_linear(ρ1) + @inferred entropy_mutual(ρAB, selA, selB) + @inferred entropy_conditional(ρAB, selA) + end +end + +@testset "Entanglement" begin + g = fock(2, 1) + e = fock(2, 0) + state = normalize(kron(g, e) + kron(e, g)) + rho = state * state' + @test entanglement(state, 1) / log(2) ≈ 1 + @test entanglement(rho, 1) / log(2) ≈ 1 + + @testset "Type Stability (entanglement)" begin + @inferred entanglement(state, 1) + @inferred entanglement(rho, 1) + end +end + +@testset "trace distance" begin + ψz0 = basis(2, 0) + ψz1 = basis(2, 1) + ρz0 = to_sparse(ket2dm(ψz0)) + ρz1 = to_sparse(ket2dm(ψz1)) + ψx0 = sqrt(0.5) * (basis(2, 0) + basis(2, 1)) + @test tracedist(ψz0, ψx0) ≈ sqrt(0.5) + @test tracedist(ρz0, ψz1) ≈ 1.0 + @test tracedist(ψz1, ρz0) ≈ 1.0 + @test tracedist(ρz0, ρz1) ≈ 1.0 + + @testset "Type Inference (trace distance)" begin + @inferred tracedist(ψz0, ψx0) + @inferred tracedist(ρz0, ψz1) + @inferred tracedist(ψz1, ρz0) + @inferred tracedist(ρz0, ρz1) + end +end + +@testset "fidelity" begin + M = sprand(ComplexF64, 5, 5, 0.5) + M0 = Qobj(M * M') + ψ1 = Qobj(rand(ComplexF64, 5)) + ψ2 = Qobj(rand(ComplexF64, 5)) + M1 = ψ1 * ψ1' + @test isapprox(fidelity(M0, M1), fidelity(ψ1, M0); atol = 1e-6) + @test isapprox(fidelity(ψ1, ψ2), fidelity(ket2dm(ψ1), ket2dm(ψ2)); atol = 1e-6) + + @testset "Type Inference (fidelity)" begin + @inferred fidelity(M0, M1) + @inferred fidelity(ψ1, M0) + @inferred fidelity(ψ1, ψ2) + end +end diff --git a/test/core-test/quantum_objects.jl b/test/core-test/quantum_objects.jl index 2bc8648f3..8aebcc244 100644 --- a/test/core-test/quantum_objects.jl +++ b/test/core-test/quantum_objects.jl @@ -566,54 +566,20 @@ end end - @testset "trace distance" begin - ψz0 = basis(2, 0) - ψz1 = basis(2, 1) - ρz0 = to_sparse(ket2dm(ψz0)) - ρz1 = to_sparse(ket2dm(ψz1)) - ψx0 = sqrt(0.5) * (basis(2, 0) + basis(2, 1)) - @test tracedist(ψz0, ψx0) ≈ sqrt(0.5) - @test tracedist(ρz0, ψz1) ≈ 1.0 - @test tracedist(ψz1, ρz0) ≈ 1.0 - @test tracedist(ρz0, ρz1) ≈ 1.0 - - @testset "Type Inference (trace distance)" begin - @inferred tracedist(ψz0, ψx0) - @inferred tracedist(ρz0, ψz1) - @inferred tracedist(ψz1, ρz0) - @inferred tracedist(ρz0, ρz1) - end - end - - @testset "sqrt and fidelity" begin - M = sprand(ComplexF64, 5, 5, 0.5) - M0 = Qobj(M * M') - ψ1 = Qobj(rand(ComplexF64, 5)) - ψ2 = Qobj(rand(ComplexF64, 5)) - M1 = ψ1 * ψ1' - @test sqrtm(M0) ≈ sqrtm(to_dense(M0)) - @test isapprox(fidelity(M0, M1), fidelity(ψ1, M0); atol = 1e-6) - @test isapprox(fidelity(ψ1, ψ2), fidelity(ket2dm(ψ1), ket2dm(ψ2)); atol = 1e-6) - - @testset "Type Inference (sqrt and fidelity)" begin - @inferred sqrtm(M0) - @inferred fidelity(M0, M1) - @inferred fidelity(ψ1, M0) - @inferred fidelity(ψ1, ψ2) - end - end - - @testset "log, exp, sinm, cosm" begin + @testset "sqrt, log, exp, sinm, cosm" begin M0 = rand(ComplexF64, 4, 4) Md = Qobj(M0 * M0') Ms = to_sparse(Md) e_p = expm(1im * Md) e_m = expm(-1im * Md) + @test sqrtm(Md) ≈ sqrtm(Ms) @test logm(expm(Ms)) ≈ expm(logm(Md)) @test cosm(Ms) ≈ (e_p + e_m) / 2 @test sinm(Ms) ≈ (e_p - e_m) / 2im @testset "Type Inference" begin + @inferred sqrtm(Md) + @inferred sqrtm(Ms) @inferred expm(Md) @inferred expm(Ms) @inferred logm(Md) diff --git a/test/runtests.jl b/test/runtests.jl index 21d133dbe..aa768d718 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,7 +12,7 @@ core_tests = [ "dynamical_fock_dimension_mesolve.jl", "dynamical-shifted-fock.jl", "eigenvalues_and_operators.jl", - "entanglement.jl", + "entropy_and_metric.jl", "generalized_master_equation.jl", "low_rank_dynamics.jl", "negativity_and_partial_transpose.jl", From c540cef8ee96abf3cf2592f2770d97a59e8b1b71 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Wed, 19 Feb 2025 15:26:14 +0900 Subject: [PATCH 08/11] update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2439ab0a6..d96870bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Change save callbacks from `PresetTimeCallback` to `FunctionCallingCallback`. ([#410]) - Align `eigenstates` and `eigenenergies` to QuTiP. ([#411]) - Introduce `vector_to_operator` and `operator_to_vector`. ([#413]) +- Introduce some entropy related functions. ([#416]) + - `entropy_linear` + - `entropy_mutual` + - `entropy_conditional` + - `entropy_relative` ## [v0.27.0] Release date: 2025-02-14 @@ -149,3 +154,4 @@ Release date: 2024-11-13 [#410]: /~https://github.com/qutip/QuantumToolbox.jl/issues/410 [#411]: /~https://github.com/qutip/QuantumToolbox.jl/issues/411 [#413]: /~https://github.com/qutip/QuantumToolbox.jl/issues/413 +[#416]: /~https://github.com/qutip/QuantumToolbox.jl/issues/416 From c5d8b68aad54b6cc8983c7147ecc4d677e4a6756 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Wed, 19 Feb 2025 15:44:30 +0900 Subject: [PATCH 09/11] minor changes --- src/entropy.jl | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/entropy.jl b/src/entropy.jl index 13b8f57c9..c9f2555ab 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -150,15 +150,10 @@ Here, ``S`` is the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neuma """ function entropy_mutual( ρAB::QuantumObject{ObjType,<:AbstractDimensions{N}}, - selA::AType, - selB::BType; + selA::Union{Int,AbstractVector{Int},Tuple}, + selB::Union{Int,AbstractVector{Int},Tuple}; kwargs..., -) where { - ObjType<:Union{KetQuantumObject,OperatorQuantumObject}, - N, - AType<:Union{Int,AbstractVector{Int},Tuple}, - BType<:Union{Int,AbstractVector{Int},Tuple}, -} +) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject},N} # check if selA and selB matches the dimensions of ρAB sel_A_B = (selA..., selB...) (length(sel_A_B) != N) && throw( @@ -188,22 +183,29 @@ Here, ``S`` is the [Von Neumann entropy](https://en.wikipedia.org/wiki/Von_Neuma """ entropy_conditional( ρAB::QuantumObject{ObjType,<:AbstractDimensions{N}}, - selB::BType; + selB::Union{Int,AbstractVector{Int},Tuple}; kwargs..., -) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject},N,BType<:Union{Int,AbstractVector{Int},Tuple}} = +) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject},N} = entropy_vn(ρAB; kwargs...) - entropy_vn(ptrace(ρAB, selB); kwargs...) -""" - entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple}) +@doc raw""" + entanglement(ρ::QuantumObject, sel; kwargs...) + +Calculates the [entanglement entropy](https://en.wikipedia.org/wiki/Entropy_of_entanglement) by doing the partial trace of `ρ`, selecting only the dimensions with the indices contained in the `sel` vector, and then use the Von Neumann entropy [`entropy_vn`](@ref). -Calculates the entanglement by doing the partial trace of `QO`, selecting only the dimensions with the indices contained in the `sel` vector, and then using the Von Neumann entropy [`entropy_vn`](@ref). +# Notes + +- `ρ` can be either a [`Ket`](@ref) or an [`Operator`](@ref). +- `sel` specifies the indices of the remaining sub-system. See also [`ptrace`](@ref). +- `kwargs` are the keyword arguments for calculating Von Neumann entropy. See also [`entropy_vn`](@ref). """ function entanglement( - QO::QuantumObject{OpType}, + ρ::QuantumObject{OpType}, sel::Union{Int,AbstractVector{Int},Tuple}, -) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} - ψ = normalize(QO) - ρ_tr = ptrace(ψ, sel) - entropy = entropy_vn(ρ_tr) - return (entropy > 0) * entropy + kwargs..., +) where {OpType<:Union{KetQuantumObject,OperatorQuantumObject}} + _ρ = normalize(ρ) + ρ_tr = ptrace(_ρ, sel) + val = entropy_vn(ρ_tr; kwargs...) + return (val > 0) * val end From df7545895863b7c696f69f0df12486c544b3a6c2 Mon Sep 17 00:00:00 2001 From: Yi-Te Huang Date: Wed, 19 Feb 2025 16:39:11 +0900 Subject: [PATCH 10/11] fix test --- test/core-test/entropy_and_metric.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core-test/entropy_and_metric.jl b/test/core-test/entropy_and_metric.jl index ecf3faa84..5225c5ea2 100644 --- a/test/core-test/entropy_and_metric.jl +++ b/test/core-test/entropy_and_metric.jl @@ -20,7 +20,7 @@ # quantum relative entropy @test entropy_relative(ρ1, ψ) == Inf @test entropy_relative(ρ1, rand_dm(10, rank = 9)) == Inf - @test entropy_relative(ψ, ψ) ≈ 0 + @test entropy_relative(ψ, ψ) + 1 ≈ 1 @test entropy_relative(λ * ρ1 + (1 - λ) * ρ2, λ * σ1 + (1 - λ) * σ2) <= λ * entropy_relative(ρ1, σ1) + (1 - λ) * entropy_relative(ρ2, σ2) # joint convexity From 21601255ade330aedf3969309a5ccd47adef4e5d Mon Sep 17 00:00:00 2001 From: Yi-Te Huang <44385685+ytdHuang@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:41:37 +0900 Subject: [PATCH 11/11] [no ci] minor change --- docs/src/resources/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/resources/api.md b/docs/src/resources/api.md index fb87185e5..4a5ead010 100644 --- a/docs/src/resources/api.md +++ b/docs/src/resources/api.md @@ -250,7 +250,7 @@ ExponentialSeries PseudoInverse ``` -## [Entropies and Metrics](@id doc-API:Entropies-and-Metrics) +## [Entropy and Metrics](@id doc-API:Entropy-and-Metrics) ```@docs entropy_vn