diff --git a/Project.toml b/Project.toml index f319b42..dfbba8d 100755 --- a/Project.toml +++ b/Project.toml @@ -27,6 +27,7 @@ JuMP = "1.22" LinearAlgebra = "1" Nemo = "0.39 - 0.45" Quadmath = "0.5.10" +Random = "1" Requires = "1" Test = "1" julia = "1.9" @@ -34,7 +35,8 @@ julia = "1.9" [extras] DoubleFloats = "497a8b3b-efae-58df-a0af-a86822472b78" Quadmath = "be4d8f0f-7fa4-5f49-b795-2f01399ab2dd" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["CyclotomicNumbers", "DoubleFloats", "Quadmath", "Test"] +test = ["CyclotomicNumbers", "DoubleFloats", "Quadmath", "Random", "Test"] diff --git a/src/entanglement.jl b/src/entanglement.jl index 0f83b72..9ae656d 100644 --- a/src/entanglement.jl +++ b/src/entanglement.jl @@ -86,7 +86,7 @@ function entanglement_entropy(ρ::AbstractMatrix{T}, dims::AbstractVector) where JuMP.set_optimizer(model, Hypatia.Optimizer{Rs}) JuMP.set_attribute(model, "verbose", false) JuMP.optimize!(model) - return JuMP.objective_value(model), JuMP.value.(σ) + return JuMP.objective_value(model), LA.Hermitian(JuMP.value.(σ)) end """ @@ -113,3 +113,38 @@ function _svec(M::AbstractMatrix, ::Type{R}) where {R} #the weird stuff here is end return v end + +""" + _test_entanglement_entropy_qubit(h::Real, ρ::AbstractMatrix, σ::AbstractMatrix) + +Tests whether `ρ` is indeed a entangled state whose closest separable state is `σ`. + +Reference: Miranowicz and Ishizaka, [arXiv:0805.3134](https://arxiv.org/abs/0805.3134) +""" +function _test_entanglement_entropy_qubit(h, ρ, σ) + R = typeof(h) + λ, U = LA.eigen(σ) + g = zeros(R, 4, 4) + for j = 1:4 + for i = 1:j-1 + g[i, j] = (λ[i] - λ[j]) / log(λ[i] / λ[j]) + end + g[j, j] = λ[j] + end + g = LA.Hermitian(g) + σT = partial_transpose(σ, 2, [2, 2]) + λ2, U2 = LA.eigen(σT) + phi = partial_transpose(ketbra(U2[:, 1]), 2, [2, 2]) + G = zero(U) + for i = 1:4 + for j = 1:4 + G += g[i, j] * ketbra(U[:, i]) * phi * ketbra(U[:, j]) + end + end + G = LA.Hermitian(G) + x = real(LA.pinv(vec(G)) * vec(σ - ρ)) + ρ2 = σ - x * G + ρ_matches = isapprox(ρ2, ρ; rtol = sqrt(Base.rtoldefault(R))) + h_matches = isapprox(h, relative_entropy(ρ2, σ); rtol = sqrt(Base.rtoldefault(R))) + return ρ_matches && h_matches +end diff --git a/test/entanglement.jl b/test/entanglement.jl index d11df8b..56a9c58 100644 --- a/test/entanglement.jl +++ b/test/entanglement.jl @@ -1,11 +1,29 @@ @testset "Entanglement " begin - for R in [Float64, Double64, Float128, BigFloat] - T = Complex{R} - ψ = random_state_ket(T, 6) - λ, U, V = schmidt_decomposition(ψ, [2, 3]) - @test vec(Diagonal(λ)) ≈ kron(U', V') * ψ - ψ = random_state_ket(T, 4) - λ, U, V = schmidt_decomposition(ψ) - @test vec(Diagonal(λ)) ≈ kron(U', V') * ψ + @testset "Schmidt decomposition" begin + for R in [Float64, Double64, Float128, BigFloat] + T = Complex{R} + ψ = random_state_ket(T, 6) + λ, U, V = schmidt_decomposition(ψ, [2, 3]) + @test vec(Diagonal(λ)) ≈ kron(U', V') * ψ + ψ = random_state_ket(T, 4) + λ, U, V = schmidt_decomposition(ψ) + @test vec(Diagonal(λ)) ≈ kron(U', V') * ψ + end + end + @testset "Entanglement entropy" begin + for R in [Float64, Double64] #Float128 and BigFloat take too long + Random.seed!(8) #makes all states entangled + ψ = random_state_ket(R, 6) + @test entanglement_entropy(ψ, [2, 3]) ≈ entanglement_entropy(ketbra(ψ), [2, 3])[1] atol = 1e-3 rtol = 1e-3 + ρ = random_state(R, 4) + h, σ = entanglement_entropy(ρ) + @test Ket._test_entanglement_entropy_qubit(h, ρ, σ) + T = Complex{R} + ψ = random_state_ket(T, 6) + @test entanglement_entropy(ψ, [2, 3]) ≈ entanglement_entropy(ketbra(ψ), [2, 3])[1] atol = 1e-3 rtol = 1e-3 + ρ = random_state(T, 4) + h, σ = entanglement_entropy(ρ) + @test Ket._test_entanglement_entropy_qubit(h, ρ, σ) + end end end