Skip to content

Commit cc648d1

Browse files
Add local_model and adapt nonlocality_threshold examples
1 parent 8c538b6 commit cc648d1

5 files changed

Lines changed: 61 additions & 18 deletions

File tree

examples/CHSH.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ println()
3030
println("Lower bound")
3131
println(lower_bound) # 0.7071
3232
println("Local model")
33-
display(local_model.x)
34-
println(local_model.x == sum(local_model.weights[i] * local_model.atoms[i] for i in 1:length(local_model))) # true
33+
display(local_model)
34+
# display(sum(weight * atom for (weight, atom) in local_model))
3535

3636
println()
3737

examples/GHZ.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ measurements = [[(σ[1] - v[i, 1] * σ[2] - v[i, 2] * σ[3] - v[i, 3] * σ[4]) /
1919
p = tensor_correlation(rho, measurements, N; marg = false) # the marginals vanish in this cas
2020

2121
# Frank-Wolfe
22-
@time lower_bound, upper_bound, local_model, bell_inequality = nonlocality_threshold(p; sym = false)
22+
lower_bound, upper_bound, local_model, bell_inequality = nonlocality_threshold(p)
2323

2424
println("Correlation tensor")
2525
display(p[:, :, 1]) # only printing part of the tensor
@@ -29,8 +29,8 @@ println()
2929
println("Lower bound")
3030
println(lower_bound) # 0.49315
3131
println("Local model")
32-
display(local_model.x[:, :, 1])
33-
println(local_model.x == sum(local_model.weights[i] * local_model.atoms[i] for i in 1:length(local_model))) # true
32+
# display(local_model)
33+
display(sum(weight * atom for (weight, atom) in local_model)[:, :, 1])
3434

3535
println()
3636

src/fw_methods.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ end
627627
function _muladd_memory_mode(as, d::AbstractArray{T}, a, v) where {T <: Number}
628628
idx_a = _unsafe_find_atom(as, a)
629629
idx_v = _unsafe_find_atom(as, v)
630-
d[1] = typemax(T)
630+
@inbounds d[1] = typemax(T)
631631
if idx_v > idx_a
632632
@inbounds d[2] = ((as.dots_x[idx_a] + as.dots_b[idx_a]) - (as.dots_x[idx_v] + as.dots_b[idx_v])) / (as.dots_A[idx_a][idx_a] + as.dots_A[idx_v][idx_v] - 2as.dots_A[idx_v][idx_a])
633633
else

src/nonlocality_threshold.jl

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,41 @@
44
Compute the nonlocality threshold of the probability/correlation tensor `p`.
55
66
Returns:
7-
- `lower_bound`: a lower bound on the nonlocality threshold under the measurements provided in input,
7+
- `lower_bound`: a (exact up to analyticity step) lower bound on the nonlocality threshold of `p`,
88
- `upper_bound`: a (heuristic) upper bound on the nonlocality threshold of `p`
9-
- `local_model`: a decomposition of the tensor `p` with visibility `lower_bound`,
9+
- `local_model`: a decomposition of the tensor `p` with visibility `lower_bound` (up to a distance `2√epsilon`),
1010
- `bell_inequality`: a (heuristic) Bell inequality corresponding to `upper_bound`.
1111
1212
Optional arguments:
13+
- ``
1314
- `precision`: number of digits of `lower_bound`, 4 by default,
1415
- for the other optional arguments, see `bell_frank_wolfe`.
1516
"""
1617
function nonlocality_threshold(
1718
p::Array{T, N},
1819
lower_bound = zero(T),
1920
upper_bound = one(T);
21+
upper::Bool = true,
2022
precision = 4,
2123
prob::Bool = false,
2224
marg::Bool = false,
23-
v0 = one(T),
2425
epsilon = Base.rtoldefault(T),
2526
o = nothing,
2627
sym = nothing,
2728
deflate = identity,
2829
inflate = identity,
2930
verbose = 0,
3031
active_set = nothing,
31-
shortcut = 10,
32+
shortcut = 4,
3233
kwargs...,
3334
) where {T <: Number, N}
35+
expand_permutedims = sym === nothing
3436
_, _, _, o, sym, deflate, inflate = _bfw_init(p, 0, prob, marg, o, sym, deflate, inflate, verbose > 0)
35-
local_model = nothing
37+
expand_permutedims &= sym
38+
v0 = upper ? upper_bound : lower_bound
39+
ass = nothing
3640
bell_inequality = nothing
37-
while log10(upper_bound - lower_bound) > -precision
41+
while round(log10(upper_bound - lower_bound); digits = 4) > -precision
3842
res = bell_frank_wolfe(p; v0, epsilon, prob, marg, o, sym, deflate, inflate, verbose, verbose_init = false, active_set, shortcut, mode_last = -1, kwargs...)
3943
x, ds, primal, dual_gap, active_set, M, β = res
4044
if dual_gap primal && primal > 10epsilon && dual_gap > 10epsilon
@@ -55,13 +59,13 @@ function nonlocality_threshold(
5559
end
5660
else
5761
lower_bound = v0
58-
local_model = ActiveSetStorage(active_set) # in-place mess otherwise
62+
ass = ActiveSetStorage(active_set)
5963
if upper_bound < lower_bound
6064
upper_bound = round(v0 + 2 * 10.0^(-precision); digits = precision)
6165
end
6266
v0 = (lower_bound + upper_bound) / 2
6367
end
6468
end
65-
return lower_bound, upper_bound, load_active_set(local_model, T; deflate), bell_inequality
69+
return lower_bound, upper_bound, local_model(ass; deflate, expand_permutedims), bell_inequality
6670
end
6771
export nonlocality_threshold

src/types.jl

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,28 @@ function load_active_set(
577577
end
578578
weights = T2.(ass.weights)
579579
weights /= sum(weights)
580-
res = FrankWolfe.ActiveSetQuadraticProductCaching([(weights[i], deflate(atoms[i])) for i in eachindex(ass.weights)], I, deflate(p))
581-
return res
580+
return FrankWolfe.ActiveSetQuadraticProductCaching([(weights[i], deflate(atoms[i])) for i in eachindex(ass.weights)], I, deflate(p))
581+
end
582+
583+
function local_model(
584+
ass::ActiveSetStorage{T, N, HasMarginals};
585+
marg = HasMarginals,
586+
deflate = identity,
587+
expand_permutedims = false,
588+
) where {T <: Number, N, HasMarginals}
589+
as = load_active_set(ass, T; marg, deflate = expand_permutedims ? identity : deflate)
590+
if expand_permutedims
591+
weights = repeat(as.weights / factorial(N); inner = factorial(N))
592+
atoms = Vector{BellCorrelationsDS{T, N, marg}}(undef, factorial(N) * length(as))
593+
i = 0
594+
for atom in as.atoms, per in permutations(1:N)
595+
i += 1
596+
atoms[i] = BellCorrelationsDS(atom.ax[per], atom.lmo)
597+
end
598+
return collect(zip(weights, atoms))
599+
else
600+
return collect(zip(as.weights, as.atoms))
601+
end
582602
end
583603

584604
# for multi-outcome scenarios
@@ -630,6 +650,25 @@ function load_active_set(
630650
end
631651
weights = T2.(ass.weights)
632652
weights /= sum(weights)
633-
res = FrankWolfe.ActiveSetQuadraticProductCaching([(weights[i], deflate(atoms[i])) for i in eachindex(ass.weights)], I, deflate(p))
634-
return res
653+
return FrankWolfe.ActiveSetQuadraticProductCaching([(weights[i], deflate(atoms[i])) for i in eachindex(ass.weights)], I, deflate(p))
654+
end
655+
656+
function local_model(
657+
ass::ActiveSetStorageMulti{T, N};
658+
deflate = identity,
659+
expand_permutedims = false,
660+
) where {T <: Number, N}
661+
as = load_active_set(ass, T; deflate = expand_permutedims ? identity : deflate)
662+
if expand_permutedims
663+
weights = repeat(as.weights / factorial(N); inner = factorial(N))
664+
atoms = Vector{BellProbabilitiesDS{T, N, marg}}(undef, factorial(N) * length(as))
665+
i = 0
666+
for atom in as.atoms, per in permutations(1:N)
667+
i += 1
668+
atoms[i] = BellCorrelationsDS(atom.ax[per], atom.lmo)
669+
end
670+
return collect(zip(weights, atoms))
671+
else
672+
return collect(zip(as.weights, as.atoms))
673+
end
635674
end

0 commit comments

Comments
 (0)