2017-03-13 15:56:07 +01:00
|
|
|
module PropertyT
|
2017-03-13 14:49:55 +01:00
|
|
|
|
2017-06-22 15:15:43 +02:00
|
|
|
using Nemo
|
|
|
|
using Groups
|
2017-05-28 20:03:57 +02:00
|
|
|
using GroupRings
|
2017-03-15 17:48:52 +01:00
|
|
|
|
2017-11-17 15:28:11 +01:00
|
|
|
import Nemo: Group, GroupElem, Ring, Generic.perm
|
2017-06-22 15:15:43 +02:00
|
|
|
|
|
|
|
using JLD
|
|
|
|
using JuMP
|
2017-12-01 17:03:24 +01:00
|
|
|
using MathProgBase
|
2017-06-22 15:15:43 +02:00
|
|
|
|
|
|
|
using Memento
|
2017-06-06 11:48:52 +02:00
|
|
|
|
2018-01-01 13:13:44 +01:00
|
|
|
const LOGGER = Memento.config("info", fmt="{msg}")
|
|
|
|
const LOGGER_SOLVER = Memento.config("info", fmt="{msg}")
|
2017-03-13 14:49:55 +01:00
|
|
|
|
2017-07-21 17:12:27 +02:00
|
|
|
function setup_logging(name::String)
|
2018-01-01 13:13:44 +01:00
|
|
|
isdir(name) || mkdir(name)
|
2017-07-21 17:12:27 +02:00
|
|
|
|
2018-01-01 13:13:44 +01:00
|
|
|
handler = Memento.DefaultHandler(
|
|
|
|
joinpath(name,"full_$(string((now()))).log"), Memento.DefaultFormatter("{date}| {msg}")
|
|
|
|
)
|
|
|
|
handler.levels.x = LOGGER.levels
|
|
|
|
LOGGER.handlers["full_log"] = handler
|
2017-07-21 17:12:27 +02:00
|
|
|
|
2018-01-01 13:13:44 +01:00
|
|
|
e = redirect_stderr(logger.handlers["full_log"].io)
|
2017-07-21 17:12:27 +02:00
|
|
|
|
2018-01-01 23:55:07 +01:00
|
|
|
return LOGGER
|
2017-07-21 17:12:27 +02:00
|
|
|
end
|
|
|
|
|
2017-11-02 13:44:08 +01:00
|
|
|
macro logtime(logger, ex)
|
|
|
|
quote
|
|
|
|
local stats = Base.gc_num()
|
|
|
|
local elapsedtime = Base.time_ns()
|
|
|
|
local val = $(esc(ex))
|
|
|
|
elapsedtime = Base.time_ns() - elapsedtime
|
|
|
|
local diff = Base.GC_Diff(Base.gc_num(), stats)
|
|
|
|
local ts = time_string(elapsedtime, diff.allocd, diff.total_time,
|
2018-01-01 13:13:44 +01:00
|
|
|
Base.gc_alloc_count(diff))
|
2018-01-01 23:54:36 +01:00
|
|
|
$(esc(info))($(esc(logger)), ts)
|
2017-11-02 13:44:08 +01:00
|
|
|
val
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function time_string(elapsedtime, bytes, gctime, allocs)
|
|
|
|
str = @sprintf("%10.6f seconds", elapsedtime/1e9)
|
|
|
|
if bytes != 0 || allocs != 0
|
|
|
|
bytes, mb = Base.prettyprint_getunits(bytes, length(Base._mem_units), Int64(1024))
|
|
|
|
allocs, ma = Base.prettyprint_getunits(allocs, length(Base._cnt_units), Int64(1000))
|
|
|
|
if ma == 1
|
|
|
|
str*= @sprintf(" (%d%s allocation%s: ", allocs, Base._cnt_units[ma], allocs==1 ? "" : "s")
|
|
|
|
else
|
|
|
|
str*= @sprintf(" (%.2f%s allocations: ", allocs, Base._cnt_units[ma])
|
|
|
|
end
|
|
|
|
if mb == 1
|
|
|
|
str*= @sprintf("%d %s%s", bytes, Base._mem_units[mb], bytes==1 ? "" : "s")
|
|
|
|
else
|
|
|
|
str*= @sprintf("%.3f %s", bytes, Base._mem_units[mb])
|
|
|
|
end
|
|
|
|
if gctime > 0
|
|
|
|
str*= @sprintf(", %.2f%% gc time", 100*gctime/elapsedtime)
|
|
|
|
end
|
|
|
|
str*=")"
|
|
|
|
elseif gctime > 0
|
|
|
|
str*= @sprintf(", %.2f%% gc time", 100*gctime/elapsedtime)
|
|
|
|
end
|
|
|
|
return str
|
|
|
|
end
|
|
|
|
|
2018-01-01 14:06:33 +01:00
|
|
|
exists(fname::String) = isfile(fname) || islink(fname)
|
2017-09-06 16:02:46 +02:00
|
|
|
|
2018-01-01 23:57:03 +01:00
|
|
|
function filename(prefix, s::Symbol)
|
2018-01-01 14:06:33 +01:00
|
|
|
isdir(prefix) || mkdir(prefix)
|
2018-01-01 23:57:03 +01:00
|
|
|
return filename(prefix, Val{s})
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
|
2018-01-01 23:57:03 +01:00
|
|
|
filename(prefix::String, ::Type{Val{:pm}}) = joinpath(prefix, "pm.jld")
|
|
|
|
filename(prefix::String, ::Type{Val{:Δ}}) = joinpath(prefix, "delta.jld")
|
|
|
|
filename(prefix::String, ::Type{Val{:λ}}) = joinpath(prefix, "lambda.jld")
|
|
|
|
filename(prefix::String, ::Type{Val{:P}}) = joinpath(prefix, "SDPmatrix.jld")
|
2017-03-13 14:49:55 +01:00
|
|
|
|
2018-01-02 00:07:05 +01:00
|
|
|
function Laplacian(name::String, G::Group)
|
2018-01-02 00:01:24 +01:00
|
|
|
info(LOGGER, "Loading precomputed Δ...")
|
|
|
|
if exists(filename(name, :Δ)) && exists(filename(name, :pm))
|
|
|
|
RG = GroupRing(G, load(filename(name, :pm), "pm"))
|
|
|
|
Δ = GroupRingElem(load(filename(name, :Δ), "Δ")[:, 1], RG)
|
|
|
|
else
|
|
|
|
throw("You need to precompute $(filename(name, :pm)) and $(filename(name, :Δ)) to load it!")
|
|
|
|
end
|
|
|
|
return Δ
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
|
2018-01-02 00:07:05 +01:00
|
|
|
function Laplacian{T<:GroupElem}(name::String, S::Vector{T}, Id::T; radius::Int=2)
|
2018-01-02 00:01:24 +01:00
|
|
|
info(LOGGER, "Computing multiplication table, Δ...")
|
2018-01-02 00:07:05 +01:00
|
|
|
Δ = Laplacian(S, Id, radius=radius)
|
2018-01-02 00:01:24 +01:00
|
|
|
save(filename(name, :pm), "pm", parent(Δ).pm)
|
|
|
|
save(filename(name, :Δ), "Δ", Δ.coeffs)
|
|
|
|
return Δ
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
|
2018-01-02 00:07:05 +01:00
|
|
|
function Laplacian{T<:GroupElem}(S::Vector{T}, Id::T; radius::Int=2)
|
2018-01-02 00:01:24 +01:00
|
|
|
info(LOGGER, "Generating metric ball of radius $radius...")
|
2018-01-01 13:13:44 +01:00
|
|
|
@logtime LOGGER E_R, sizes = Groups.generate_balls(S, Id, radius=2*radius)
|
2018-01-02 00:01:24 +01:00
|
|
|
info(LOGGER, "Generated balls of sizes $sizes.")
|
2017-06-05 13:20:32 +02:00
|
|
|
|
2018-01-01 13:13:44 +01:00
|
|
|
info(LOGGER, "Creating product matrix...")
|
|
|
|
@logtime LOGGER pm = GroupRings.create_pm(E_R, GroupRings.reverse_dict(E_R), sizes[radius]; twisted=true)
|
2017-06-05 13:20:32 +02:00
|
|
|
|
2017-11-08 09:25:40 +01:00
|
|
|
RG = GroupRing(parent(Id), E_R, pm)
|
2017-06-05 13:20:32 +02:00
|
|
|
|
2017-10-27 18:36:59 +02:00
|
|
|
Δ = splaplacian(RG, S)
|
2018-01-02 00:01:24 +01:00
|
|
|
return Δ
|
2017-06-05 13:20:32 +02:00
|
|
|
end
|
|
|
|
|
2017-04-01 15:21:57 +02:00
|
|
|
function λandP(name::String)
|
2018-01-01 23:57:03 +01:00
|
|
|
λ_fname = filename(name, :λ)
|
|
|
|
P_fname = filename(name, :P)
|
2017-04-01 15:21:57 +02:00
|
|
|
|
2018-01-01 23:57:03 +01:00
|
|
|
if exists(λ_fname) && exists(P_fname)
|
2017-04-01 15:21:57 +02:00
|
|
|
λ = load(λ_fname, "λ")
|
2018-01-01 23:57:03 +01:00
|
|
|
P = load(P_fname, "P")
|
2017-03-13 14:49:55 +01:00
|
|
|
else
|
2018-01-01 23:57:03 +01:00
|
|
|
throw("You need to precompute $λ_fname and $P_fname to load it!")
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
2017-04-01 15:21:57 +02:00
|
|
|
return λ, P
|
2017-03-15 17:49:39 +01:00
|
|
|
end
|
|
|
|
|
2017-12-01 17:27:18 +01:00
|
|
|
function λandP(name::String, SDP_problem::JuMP.Model, varλ, varP, warmstart=false)
|
2018-01-01 13:13:44 +01:00
|
|
|
|
|
|
|
handler = DefaultHandler(
|
|
|
|
joinpath(name, "solver_$(string(now())).log"),
|
|
|
|
DefaultFormatter("{date}| {msg}")
|
|
|
|
)
|
|
|
|
handler.levels.x = LOGGER_SOLVER.levels
|
|
|
|
LOGGER_SOLVER.handlers["solver_log"] = handler
|
|
|
|
|
2018-01-01 14:06:33 +01:00
|
|
|
if warmstart && isfile(joinpath(name, "warmstart.jld"))
|
|
|
|
ws = load(joinpath(name, "warmstart.jld"), "warmstart")
|
|
|
|
else
|
|
|
|
ws = nothing
|
|
|
|
end
|
2017-03-31 16:07:08 +02:00
|
|
|
|
2018-01-01 14:06:33 +01:00
|
|
|
λ, P, warmstart = compute_λandP(SDP_problem, varλ, varP, warmstart=ws)
|
2017-03-31 16:07:08 +02:00
|
|
|
|
2018-01-01 14:06:33 +01:00
|
|
|
delete!(LOGGER_SOLVER.handlers, "solver_log")
|
2017-03-31 16:07:08 +02:00
|
|
|
|
2018-01-01 14:06:33 +01:00
|
|
|
if λ > 0
|
2018-01-01 23:57:03 +01:00
|
|
|
save(filename(name, :λ), "λ", λ)
|
|
|
|
save(filename(name, :P), "P", P)
|
2018-01-01 14:06:33 +01:00
|
|
|
save(joinpath(name, "warmstart.jld"), "warmstart", warmstart)
|
|
|
|
else
|
2018-01-01 23:57:03 +01:00
|
|
|
throw(ErrorException("Solver did not produce a valid solution: λ = $λ"))
|
2018-01-01 14:06:33 +01:00
|
|
|
end
|
|
|
|
return λ, P
|
2017-03-31 16:07:08 +02:00
|
|
|
end
|
2017-03-16 09:35:32 +01:00
|
|
|
|
2017-12-01 14:12:52 +01:00
|
|
|
function compute_λandP(m, varλ, varP; warmstart=nothing)
|
2017-04-01 15:21:57 +02:00
|
|
|
λ = 0.0
|
2017-10-27 18:20:28 +02:00
|
|
|
P = nothing
|
2017-12-01 17:04:53 +01:00
|
|
|
|
|
|
|
traits = JuMP.ProblemTraits(m, relaxation=false)
|
|
|
|
|
2018-01-01 23:59:31 +01:00
|
|
|
JuMP.build(m, traits=traits)
|
|
|
|
if warmstart != nothing
|
|
|
|
p_sol, d_sol, s = warmstart
|
|
|
|
MathProgBase.SolverInterface.setwarmstart!(m.internalModel, p_sol; dual_sol = d_sol, slack=s);
|
2017-03-20 21:48:22 +01:00
|
|
|
end
|
2018-01-01 23:59:31 +01:00
|
|
|
solve_SDP(m)
|
|
|
|
λ = MathProgBase.getobjval(m.internalModel)
|
2017-12-01 17:05:35 +01:00
|
|
|
|
|
|
|
warmstart = (m.internalModel.primal_sol, m.internalModel.dual_sol,
|
|
|
|
m.internalModel.slack)
|
2017-12-01 17:06:20 +01:00
|
|
|
|
|
|
|
fillfrominternal!(m, traits)
|
|
|
|
|
2017-12-01 17:05:35 +01:00
|
|
|
P = JuMP.getvalue(varP)
|
|
|
|
λ = JuMP.getvalue(varλ)
|
|
|
|
|
|
|
|
return λ, P, warmstart
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
|
2017-04-01 14:22:30 +02:00
|
|
|
Kazhdan_from_sgap(λ,N) = sqrt(2*λ/N)
|
|
|
|
|
2017-06-05 17:26:55 +02:00
|
|
|
function check_property_T(name::String, S, Id, solver, upper_bound, tol, radius)
|
2017-03-13 14:49:55 +01:00
|
|
|
|
2017-06-05 17:29:05 +02:00
|
|
|
isdir(name) || mkdir(name)
|
2017-03-13 14:49:55 +01:00
|
|
|
|
2018-01-01 23:57:03 +01:00
|
|
|
if exists(filename(name, :pm)) && exists(filename(name, :Δ))
|
2017-06-05 13:48:44 +02:00
|
|
|
# cached
|
2018-01-02 00:07:05 +01:00
|
|
|
Δ = Laplacian(name, parent(S[1]))
|
2017-06-05 13:17:49 +02:00
|
|
|
else
|
2017-06-05 13:48:44 +02:00
|
|
|
# compute
|
2018-01-02 00:07:05 +01:00
|
|
|
Δ = Laplacian(name, S, Id, radius=radius)
|
2017-06-05 13:23:10 +02:00
|
|
|
end
|
2017-03-31 16:07:08 +02:00
|
|
|
|
2018-01-01 23:57:03 +01:00
|
|
|
if exists(filename(name, :λ)) && exists(filename(name, :P))
|
|
|
|
info(LOGGER, "Loading precomputed λ, P...")
|
2018-01-01 14:06:33 +01:00
|
|
|
λ, P = λandP(name)
|
|
|
|
else
|
|
|
|
info(LOGGER, "Creating SDP problem...")
|
2018-01-02 00:01:24 +01:00
|
|
|
SDP_problem, λ_var, P_var = create_SDP_problem(Δ, PropertyT.constraints(parent(Δ).pm), upper_bound=upper_bound)
|
2018-01-01 14:06:33 +01:00
|
|
|
JuMP.setsolver(SDP_problem, solver)
|
|
|
|
|
2018-01-02 00:01:24 +01:00
|
|
|
λ, P = λandP(name, SDP_problem, λ_var, P_var)
|
2018-01-01 14:06:33 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
info(LOGGER, "λ = $λ")
|
|
|
|
info(LOGGER, "sum(P) = $(sum(P))")
|
|
|
|
info(LOGGER, "maximum(P) = $(maximum(P))")
|
|
|
|
info(LOGGER, "minimum(P) = $(minimum(P))")
|
|
|
|
|
|
|
|
if λ > 0
|
2018-01-01 23:57:03 +01:00
|
|
|
|
|
|
|
RG = GroupRing(parent(first(S)), load(filename(name, :pm), "pm"))
|
|
|
|
Δ = GroupRingElem(load(filename(name, :Δ), "Δ")[:, 1], RG)
|
2018-01-01 14:06:33 +01:00
|
|
|
|
|
|
|
isapprox(eigvals(P), abs(eigvals(P)), atol=tol) ||
|
|
|
|
warn("The solution matrix doesn't seem to be positive definite!")
|
|
|
|
@logtime LOGGER Q = real(sqrtm(Symmetric(P)))
|
|
|
|
|
2018-01-02 00:02:00 +01:00
|
|
|
sgap = check_distance_to_cone(Δ, λ, Q, 2*radius, LOGGER)
|
|
|
|
|
2018-01-01 14:06:33 +01:00
|
|
|
if sgap > 0
|
|
|
|
info(LOGGER, "λ ≥ $(Float64(trunc(sgap,12)))")
|
|
|
|
Kazhdan_κ = Kazhdan_from_sgap(sgap, length(S))
|
|
|
|
Kazhdan_κ = Float64(trunc(Kazhdan_κ, 12))
|
|
|
|
info(LOGGER, "κ($name, S) ≥ $Kazhdan_κ: Group HAS property (T)!")
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
sgap = Float64(trunc(sgap, 12))
|
|
|
|
info(LOGGER, "λ($name, S) ≥ $sgap: Group may NOT HAVE property (T)!")
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
info(LOGGER, "κ($name, S) ≥ $λ < 0: Tells us nothing about property (T)")
|
|
|
|
return false
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
|
2017-06-22 15:15:55 +02:00
|
|
|
include("SDPs.jl")
|
|
|
|
include("CheckSolution.jl")
|
|
|
|
include("Orbit-wise.jl")
|
|
|
|
|
2017-03-13 14:49:55 +01:00
|
|
|
end # module Property(T)
|