Skip to content

Commit

Permalink
Merge branch 'master' of github.com:araujoms/Ket.jl
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastiendesignolle committed Nov 25, 2024
2 parents 6438173 + 21f049c commit 0794dc8
Show file tree
Hide file tree
Showing 18 changed files with 472 additions and 151 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ KetMATLAB = "MATLAB"
Combinatorics = "1"
CyclotomicNumbers = "0.1"
DoubleFloats = "1.4.0"
GenericLinearAlgebra = "0.3"
GenericLinearAlgebra = "0.3.14"
Hypatia = "0.8.1"
JuMP = "1.23"
LinearAlgebra = "1"
Expand Down
2 changes: 1 addition & 1 deletion TODO
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MA - See-saw
MA - translations between FP, CG, and FC (SD)
MA - multipartite Bell stuff (SD)
MA - generalized Bell states
SD - noise maps: general last argument or separate function?
SD - NL/steering/incompatibility/entanglement robustness SDPs?
SD - KetSparse
Expand Down
3 changes: 3 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,14 @@ incompatibility_robustness_generalized
```@docs
chsh
cglmp
inn22
local_bound
tsirelson_bound
seesaw
correlation_tensor
probability_tensor
fp2cg
cg2fp
```

## Norms
Expand Down
3 changes: 2 additions & 1 deletion src/Ket.jl
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module Ket

using LinearAlgebra
import Combinatorics
import GenericLinearAlgebra
import LinearAlgebra as LA
import SparseArrays as SA
import Nemo
import JuMP
Expand All @@ -21,5 +21,6 @@ include("norms.jl")
include("multilinear.jl")
include("supermaps.jl")
include("entanglement.jl")
include("seesaw.jl")

end # module Ket
30 changes: 15 additions & 15 deletions src/basic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export ket
Produces a ketbra of vector `v`.
"""
function ketbra(v::AbstractVector)
return LA.Hermitian(v * v')
return Hermitian(v * v')
end
export ketbra

Expand All @@ -27,14 +27,14 @@ export ketbra
Produces a projector onto the basis state `i` in dimension `d`.
"""
function proj(::Type{T}, i::Integer, d::Integer = 2) where {T<:Number}
p = LA.Hermitian(zeros(T, d, d))
p = Hermitian(zeros(T, d, d))
p[i, i] = 1
return p
end
proj(i::Integer, d::Integer = 2) = proj(Bool, i, d)
export proj

const Measurement{T} = Vector{LA.Hermitian{T,Matrix{T}}}
const Measurement{T} = Vector{Hermitian{T,Matrix{T}}}
export Measurement

"""
Expand All @@ -55,7 +55,7 @@ By default, the second argument is fixed by the size of `A`.
It can also contain custom number of outcomes if there are measurements with less outcomes.
"""
function tensor_to_povm(Aax::Array{T,4}, o::Vector{Int64} = fill(size(Aax, 3), size(Aax, 4))) where {T}
return [[LA.Hermitian(Aax[:, :, a, x]) for a in 1:o[x]] for x in axes(Aax, 4)]
return [[Hermitian(Aax[:, :, a, x]) for a in 1:o[x]] for x in axes(Aax, 4)]
end
export tensor_to_povm

Expand Down Expand Up @@ -94,11 +94,11 @@ _measurements_parameters(Aa::Measurement) = _measurements_parameters([Aa])
Checks if the measurement defined by A is valid (hermitian, semi-definite positive, and normalized).
"""
function test_povm(E::Vector{<:AbstractMatrix{T}}) where {T<:Number}
!all(LA.ishermitian.(E)) && return false
!all(ishermitian.(E)) && return false
d = size(E[1], 1)
!(sum(E) LA.I(d)) && return false
!(sum(E) I(d)) && return false
for i = 1:length(E)
minimum(LA.eigvals(E[i])) < -_rtol(T) && return false
minimum(eigvals(E[i])) < -_rtol(T) && return false
end
return true
end
Expand Down Expand Up @@ -145,7 +145,7 @@ function clock(::Type{T}, d::Integer, p::Integer = 1) where {T<:Number}
z[i+1] = ω^exponent
end
end
return LA.Diagonal(z)
return Diagonal(z)
end
clock(d::Integer, p::Integer = 1) = clock(ComplexF64, d, p)
export clock
Expand Down Expand Up @@ -292,16 +292,16 @@ function _cleanup!(M; tol)
end

function applykraus(K, M)
return sum(LA.Hermitian(Ki * M * Ki') for Ki in K)
return sum(Hermitian(Ki * M * Ki') for Ki in K)
end
export applykraus

function _orthonormal_range_svd!(
A::AbstractMatrix{T};
tol::Union{Real,Nothing} = nothing,
alg = LA.default_svd_alg(A)
alg = LinearAlgebra.default_svd_alg(A)
) where {T<:Number}
dec = LA.svd!(A; alg = alg)
dec = svd!(A; alg = alg)
tol = isnothing(tol) ? maximum(dec.S) * _eps(T) * minimum(size(A)) : tol
rank = sum(dec.S .> tol)
dec.U[:, 1:rank]
Expand All @@ -311,9 +311,9 @@ _orthonormal_range_svd(A::AbstractMatrix; tol::Union{Real,Nothing} = nothing) =
_orthonormal_range_svd!(deepcopy(A); tol = tol)

function _orthonormal_range_qr(A::SA.AbstractSparseMatrix{T,M}; tol::Union{Real,Nothing} = nothing) where {T<:Number,M}
dec = LA.qr(A)
dec = qr(A)
tol = isnothing(tol) ? maximum(abs.(dec.R)) * _eps(T) : tol
rank = sum(abs.(LA.Diagonal(dec.R)) .> tol)
rank = sum(abs.(Diagonal(dec.R)) .> tol)
SA.sparse(@view dec.Q[dec.rpivinv, 1:rank])
end

Expand Down Expand Up @@ -369,7 +369,7 @@ symmetric_projection(dim::Integer, n::Integer; partial::Bool = true) = symmetric
n_parties::Integer;
sb::AbstractVector{<:AbstractMatrix} = [pauli(1), pauli(2), pauli(3)],
sparse::Bool = true,
eye::AbstractMatrix = LA.I(size(sb[1], 1))
eye::AbstractMatrix = I(size(sb[1], 1))
Return the basis of `n` nontrivial operators acting on `n_parties`, by default using Pauli matrices.
Expand All @@ -387,7 +387,7 @@ function n_body_basis(
n_parties::Integer;
sb::AbstractVector{<:AbstractMatrix} = [pauli(1), pauli(2), pauli(3)],
sparse::Bool = true,
eye::AbstractMatrix = LA.I(size(sb[1], 1))
eye::AbstractMatrix = I(size(sb[1], 1))
)
(n >= 0 && n_parties >= 2) || throw(ArgumentError("Number of parties must be ≥ 2 and n ≥ 0."))
n <= n_parties || throw(ArgumentError("Number of parties cannot be larger than n."))
Expand Down
70 changes: 35 additions & 35 deletions src/entanglement.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Reference: [Schmidt decomposition](https://en.wikipedia.org/wiki/Schmidt_decompo
function schmidt_decomposition::AbstractVector, dims::AbstractVector{<:Integer} = _equal_sizes(ψ))
length(dims) != 2 && throw(ArgumentError("Two subsystem sizes must be specified."))
m = transpose(reshape(ψ, dims[2], dims[1])) #necessary because the natural reshaping would be row-major, but Julia does it col-major
U, λ, V = LA.svd(m)
U, λ, V = svd(m)
return λ, U, conj(V)
end
export schmidt_decomposition
Expand All @@ -39,7 +39,7 @@ export entanglement_entropy
Lower bounds the relative entropy of entanglement of a bipartite state `ρ` with subsystem dimensions `dims` using level `n` of the DPS hierarchy. If the argument `dims` is omitted equally-sized subsystems are assumed.
"""
function entanglement_entropy::AbstractMatrix{T}, dims::AbstractVector = _equal_sizes(ρ), n::Integer = 1) where {T}
LA.ishermitian(ρ) || throw(ArgumentError("State needs to be Hermitian"))
ishermitian(ρ) || throw(ArgumentError("State needs to be Hermitian"))
length(dims) != 2 && throw(ArgumentError("Two subsystem sizes must be specified."))

d = size(ρ, 1)
Expand All @@ -54,7 +54,7 @@ function entanglement_entropy(ρ::AbstractMatrix{T}, dims::AbstractVector = _equ
JuMP.@variable(model, σ[1:d, 1:d], Symmetric)
end
_dps_constraints!(model, σ, dims, n; is_complex)
JuMP.@constraint(model, LA.tr(σ) == 1)
JuMP.@constraint(model, tr(σ) == 1)

vec_dim = Cones.svec_length(Ts, d)
ρvec = _svec(ρ, Ts)
Expand All @@ -66,23 +66,23 @@ function entanglement_entropy(ρ::AbstractMatrix{T}, dims::AbstractVector = _equ
JuMP.set_optimizer(model, Hypatia.Optimizer{Rs})
JuMP.set_silent(model)
JuMP.optimize!(model)
return JuMP.objective_value(model), LA.Hermitian(JuMP.value.(σ))
return JuMP.objective_value(model), Hermitian(JuMP.value.(σ))
end

"""
_svec(M::AbstractMatrix, ::Type{R})
_svec(M::AbstractMatrix, ::Type{T})
Produces the scaled vectorized version of a Hermitian matrix `M` with coefficient type `R`. The transformation preserves inner products, i.e., ⟨M,N⟩ = ⟨svec(M,R),svec(N,R)⟩.
Produces the scaled vectorized version of a Hermitian matrix `M` with coefficient type `T`. The transformation preserves inner products, i.e., ⟨M,N⟩ = ⟨svec(M,T),svec(N,T)⟩.
"""
function _svec(M::AbstractMatrix, ::Type{R}) where {R} #the weird stuff here is to make it work with JuMP variables
function _svec(M::AbstractMatrix, ::Type{T}) where {T} #the weird stuff here is to make it work with JuMP variables
d = size(M, 1)
T = real(R)
vec_dim = Cones.svec_length(R, d)
R = real(T)
vec_dim = Cones.svec_length(T, d)
v = Vector{real(eltype(1 * M))}(undef, vec_dim)
if R <: Real
Cones.smat_to_svec!(v, 1 * M, sqrt(T(2)))
if T <: Real
Cones.smat_to_svec!(v, 1 * M, sqrt(R(2)))
else
Cones._smat_to_svec_complex!(v, M, sqrt(T(2)))
Cones._smat_to_svec_complex!(v, M, sqrt(R(2)))
end
return v
end
Expand All @@ -96,26 +96,26 @@ Reference: Miranowicz and Ishizaka, [arXiv:0805.3134](https://arxiv.org/abs/0805
"""
function _test_entanglement_entropy_qubit(h, ρ, σ)
R = typeof(h)
λ, U = LA.eigen(σ)
λ, U = 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)
g = Hermitian(g)
σT = partial_transpose(σ, 2, [2, 2])
λ2, U2 = LA.eigen(σT)
λ2, U2 = 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- ρ))
G = Hermitian(G)
x = real(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)))
Expand Down Expand Up @@ -153,26 +153,26 @@ function schmidt_number(
verbose::Bool = false,
solver = Hypatia.Optimizer{_solver_type(T)}
) where {T<:Number}
LA.ishermitian(ρ) || throw(ArgumentError("State must be Hermitian"))
ishermitian(ρ) || throw(ArgumentError("State must be Hermitian"))
s >= 1 || throw(ArgumentError("Schmidt number must be ≥ 1"))
if s == 1
return random_robustness(ρ, dims, n; ppt, verbose, solver)[1]
end

is_complex = (T <: Complex)
wrapper = is_complex ? LA.Hermitian : LA.Symmetric
wrapper = is_complex ? Hermitian : Symmetric

V = kron(LA.I(dims[1]), SA.sparse(state_ghz_ket(T, s, 2; coeff = 1)), LA.I(dims[2])) #this is an isometry up to normalization
V = kron(I(dims[1]), SA.sparse(state_ghz_ket(T, s, 2; coeff = 1)), I(dims[2])) #this is an isometry up to normalization
lifted_dims = [dims[1] * s, dims[2] * s] # with the ancilla spaces A'B'...

model = JuMP.GenericModel{_solver_type(T)}()

JuMP.@variable(model, λ)
noisy_state = wrapper+ λ * LA.I(size(ρ, 1)))
noisy_state = wrapper+ λ * I(size(ρ, 1)))
JuMP.@objective(model, Min, λ)

_dps_constraints!(model, noisy_state, lifted_dims, n; ppt, is_complex, isometry = V)
JuMP.@constraint(model, LA.tr(model[:symmetric_meat]) == s * (LA.tr(ρ) + λ * size(ρ, 1)))
JuMP.@constraint(model, tr(model[:symmetric_meat]) == s * (tr(ρ) + λ * size(ρ, 1)))

JuMP.set_optimizer(model, solver)
!verbose && JuMP.set_silent(model)
Expand Down Expand Up @@ -205,15 +205,15 @@ function random_robustness(
verbose::Bool = false,
solver = Hypatia.Optimizer{_solver_type(T)}
) where {T<:Number}
LA.ishermitian(ρ) || throw(ArgumentError("State must be Hermitian"))
ishermitian(ρ) || throw(ArgumentError("State must be Hermitian"))

is_complex = (T <: Complex)
wrapper = is_complex ? LA.Hermitian : LA.Symmetric
wrapper = is_complex ? Hermitian : Symmetric

model = JuMP.GenericModel{_solver_type(T)}()

JuMP.@variable(model, λ)
noisy_state = wrapper+ λ * LA.I(size(ρ, 1)))
noisy_state = wrapper+ λ * I(size(ρ, 1)))
_dps_constraints!(model, noisy_state, dims, n; ppt, is_complex)
JuMP.@objective(model, Min, λ)

Expand Down Expand Up @@ -246,23 +246,23 @@ function _dps_constraints!(
n::Integer;
ppt::Bool = true,
is_complex::Bool = true,
isometry::AbstractMatrix = LA.I(size(ρ, 1))
isometry::AbstractMatrix = I(size(ρ, 1))
) where {T}
LA.ishermitian(ρ) || throw(ArgumentError("State must be Hermitian"))
ishermitian(ρ) || throw(ArgumentError("State must be Hermitian"))

dA, dB = dims
ext_dims = [dA; repeat([dB], n)]

# Dimension of the extension space w/ bosonic symmetries: A dim. + `n` copies of B
d = dA * binomial(n + dB - 1, n)
V = kron(LA.I(dA), symmetric_projection(T, dB, n; partial = true)) # Bosonic subspace isometry
V = kron(I(dA), symmetric_projection(T, dB, n; partial = true)) # Bosonic subspace isometry

if is_complex
psd_cone = JuMP.HermitianPSDCone()
wrapper = LA.Hermitian
wrapper = Hermitian
else
psd_cone = JuMP.PSDCone()
wrapper = LA.Symmetric
wrapper = Symmetric
end

JuMP.@variable(model, symmetric_meat[1:d, 1:d] in psd_cone)
Expand All @@ -284,19 +284,19 @@ function _fully_decomposable_witness_constraints!(model, dims, W)

Ps = [JuMP.@variable(model, [1:dim,1:dim] in JuMP.HermitianPSDCone()) for _ in 1:length(biparts)]

JuMP.@constraint(model, LA.tr(W) == 1)
JuMP.@constraint(model, tr(W) == 1)
# this can be used instead of tr(W) = 1 if we want a GME entanglement quantifier (see ref.)
# [JuMP.@constraint(model, (LA.I(dim) - (W - Ps[i])) in JuMP.HermitianPSDCone()) for i in 1:length(biparts)]
# [JuMP.@constraint(model, (I(dim) - (W - Ps[i])) in JuMP.HermitianPSDCone()) for i in 1:length(biparts)]

# constraints for W = Q^{T_M} + P^M:
for (i, part) in enumerate(biparts)
JuMP.@constraint(model, LA.Hermitian(partial_transpose(W - Ps[i], part[1], dims)) in JuMP.HermitianPSDCone())
JuMP.@constraint(model, Hermitian(partial_transpose(W - Ps[i], part[1], dims)) in JuMP.HermitianPSDCone())
end
end

function _min_dotprod!(model, ρ, W, solver, verbose)
JuMP.@variable(model, λ)
JuMP.@constraint(model, real(LA.dot(ρ, W)) <= λ)
JuMP.@constraint(model, real(dot(ρ, W)) <= λ)
JuMP.@objective(model, Min, λ)

JuMP.set_optimizer(model, solver)
Expand Down Expand Up @@ -336,7 +336,7 @@ function ppt_mixture(

if JuMP.is_solved_and_feasible(model)
JuMP.objective_value(model) 0 ? W = JuMP.value.(W) : W = zeros(T, size(W))
return 1 / (1 - dim * JuMP.value.(model[])), LA.Hermitian(W)
return 1 / (1 - dim * JuMP.value.(model[])), Hermitian(W)
else
return "Something went wrong: $(JuMP.raw_status(model))"
end
Expand Down
Loading

0 comments on commit 0794dc8

Please sign in to comment.