2017-03-13 14:49:55 +01:00
|
|
|
using JuMP
|
|
|
|
import MathProgBase: AbstractMathProgSolver
|
|
|
|
|
2017-11-08 09:25:40 +01:00
|
|
|
function constraints(pm, total_length=maximum(pm))
|
2017-03-13 14:49:55 +01:00
|
|
|
n = size(pm,1)
|
2017-11-08 09:25:40 +01:00
|
|
|
constraints = [Vector{Tuple{Int,Int}}() for _ in 1:total_length]
|
2017-03-13 14:49:55 +01:00
|
|
|
for j in 1:n
|
2017-03-14 16:42:04 +01:00
|
|
|
for i in 1:n
|
2017-03-13 14:49:55 +01:00
|
|
|
idx = pm[i,j]
|
2017-11-08 09:25:40 +01:00
|
|
|
push!(constraints[idx], (i,j))
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
return constraints
|
|
|
|
end
|
|
|
|
|
2018-01-02 00:07:05 +01:00
|
|
|
function spLaplacian(RG::GroupRing, S, T::Type=Float64)
|
2017-06-08 21:45:46 +02:00
|
|
|
result = RG(T)
|
2017-10-27 14:29:47 +02:00
|
|
|
result[RG.group()] = T(length(S))
|
2017-06-08 21:45:46 +02:00
|
|
|
for s in S
|
|
|
|
result[s] -= one(T)
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2018-01-02 00:07:05 +01:00
|
|
|
function spLaplacian{TT<:Ring}(RG::GroupRing{TT}, S, T::Type=Float64)
|
2017-06-08 21:45:46 +02:00
|
|
|
result = RG(T)
|
2017-10-27 14:29:47 +02:00
|
|
|
result[one(RG.group)] = T(length(S))
|
2017-03-13 14:49:55 +01:00
|
|
|
for s in S
|
2017-06-06 11:51:15 +02:00
|
|
|
result[s] -= one(T)
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2017-06-04 20:13:27 +02:00
|
|
|
function create_SDP_problem(Δ::GroupRingElem, matrix_constraints; upper_bound=Inf)
|
2017-05-16 18:53:25 +02:00
|
|
|
N = size(parent(Δ).pm, 1)
|
2017-03-17 16:27:01 +01:00
|
|
|
Δ² = Δ*Δ
|
2017-05-28 20:01:52 +02:00
|
|
|
@assert length(Δ.coeffs) == length(matrix_constraints)
|
2017-03-13 14:49:55 +01:00
|
|
|
m = JuMP.Model();
|
2017-06-05 13:55:24 +02:00
|
|
|
JuMP.@variable(m, P[1:N, 1:N])
|
2017-04-01 15:21:57 +02:00
|
|
|
JuMP.@SDconstraint(m, P >= 0)
|
|
|
|
JuMP.@constraint(m, sum(P[i] for i in eachindex(P)) == 0)
|
2017-03-20 21:41:12 +01:00
|
|
|
|
2017-03-13 14:49:55 +01:00
|
|
|
if upper_bound < Inf
|
2017-04-01 15:21:57 +02:00
|
|
|
JuMP.@variable(m, 0.0 <= λ <= upper_bound)
|
2017-03-20 21:41:12 +01:00
|
|
|
else
|
2017-04-01 15:21:57 +02:00
|
|
|
JuMP.@variable(m, λ >= 0)
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
|
2017-05-16 18:53:25 +02:00
|
|
|
for (pairs, δ², δ) in zip(matrix_constraints, Δ².coeffs, Δ.coeffs)
|
2017-04-01 15:21:57 +02:00
|
|
|
JuMP.@constraint(m, sum(P[i,j] for (i,j) in pairs) == δ² - λ*δ)
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
2017-03-20 21:41:12 +01:00
|
|
|
|
2017-04-01 15:21:57 +02:00
|
|
|
JuMP.@objective(m, Max, λ)
|
2017-03-20 21:41:12 +01:00
|
|
|
|
2017-06-04 20:13:27 +02:00
|
|
|
return m, λ, P
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
|
|
|
|
2017-06-04 20:26:05 +02:00
|
|
|
function solve_SDP(SDP_problem)
|
2018-01-01 13:13:44 +01:00
|
|
|
info(LOGGER, Base.repr(SDP_problem))
|
2017-03-13 14:49:55 +01:00
|
|
|
|
2018-01-01 13:13:44 +01:00
|
|
|
o = redirect_stdout(LOGGER_SOLVER.handlers["solver_log"].io)
|
2017-09-19 17:37:35 +02:00
|
|
|
Base.Libc.flush_cstdio()
|
2017-03-16 09:35:32 +01:00
|
|
|
|
2018-01-01 13:13:44 +01:00
|
|
|
@logtime LOGGER solution_status = MathProgBase.optimize!(SDP_problem.internalModel)
|
2017-03-16 15:37:52 +01:00
|
|
|
Base.Libc.flush_cstdio()
|
2017-03-16 09:35:32 +01:00
|
|
|
|
2017-03-16 15:37:52 +01:00
|
|
|
redirect_stdout(o)
|
2017-03-13 14:49:55 +01:00
|
|
|
|
|
|
|
if solution_status != :Optimal
|
2018-01-01 13:13:44 +01:00
|
|
|
warn(LOGGER, "The solver did not solve the problem successfully!")
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
2018-01-01 13:13:44 +01:00
|
|
|
info(LOGGER, solution_status)
|
2017-03-13 14:49:55 +01:00
|
|
|
|
2017-06-04 20:26:05 +02:00
|
|
|
return 0
|
2017-03-13 14:49:55 +01:00
|
|
|
end
|
2018-01-01 23:59:31 +01:00
|
|
|
|
|
|
|
function fillfrominternal!(m::JuMP.Model, traits)
|
|
|
|
# Copied from JuMP/src/solvers.jl:178
|
|
|
|
|
|
|
|
stat::Symbol = MathProgBase.status(m.internalModel)
|
|
|
|
|
|
|
|
numRows, numCols = length(m.linconstr), m.numCols
|
|
|
|
m.objBound = NaN
|
|
|
|
m.objVal = NaN
|
|
|
|
m.colVal = fill(NaN, numCols)
|
|
|
|
m.linconstrDuals = Array{Float64}(0)
|
|
|
|
|
|
|
|
discrete = (traits.int || traits.sos)
|
|
|
|
|
|
|
|
if stat == :Optimal
|
|
|
|
# If we think dual information might be available, try to get it
|
|
|
|
# If not, return an array of the correct length
|
|
|
|
if discrete
|
|
|
|
m.redCosts = fill(NaN, numCols)
|
|
|
|
m.linconstrDuals = fill(NaN, numRows)
|
|
|
|
else
|
|
|
|
if !traits.conic
|
|
|
|
m.redCosts = try
|
|
|
|
MathProgBase.getreducedcosts(m.internalModel)[1:numCols]
|
|
|
|
catch
|
|
|
|
fill(NaN, numCols)
|
|
|
|
end
|
|
|
|
|
|
|
|
m.linconstrDuals = try
|
|
|
|
MathProgBase.getconstrduals(m.internalModel)[1:numRows]
|
|
|
|
catch
|
|
|
|
fill(NaN, numRows)
|
|
|
|
end
|
|
|
|
elseif !traits.qp && !traits.qc
|
|
|
|
JuMP.fillConicDuals(m)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
# Problem was not solved to optimality, attempt to extract useful
|
|
|
|
# information anyway
|
|
|
|
|
|
|
|
if traits.lin
|
|
|
|
if stat == :Infeasible
|
|
|
|
m.linconstrDuals = try
|
|
|
|
infray = MathProgBase.getinfeasibilityray(m.internalModel)
|
|
|
|
@assert length(infray) == numRows
|
|
|
|
infray
|
|
|
|
catch
|
|
|
|
suppress_warnings || warn("Infeasibility ray (Farkas proof) not available")
|
|
|
|
fill(NaN, numRows)
|
|
|
|
end
|
|
|
|
elseif stat == :Unbounded
|
|
|
|
m.colVal = try
|
|
|
|
unbdray = MathProgBase.getunboundedray(m.internalModel)
|
|
|
|
@assert length(unbdray) == numCols
|
|
|
|
unbdray
|
|
|
|
catch
|
|
|
|
suppress_warnings || warn("Unbounded ray not available")
|
|
|
|
fill(NaN, numCols)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# conic duals (currently, SOC and SDP only)
|
|
|
|
if !discrete && traits.conic && !traits.qp && !traits.qc
|
|
|
|
if stat == :Infeasible
|
|
|
|
JuMP.fillConicDuals(m)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# If the problem was solved, or if it terminated prematurely, try
|
|
|
|
# to extract a solution anyway. This commonly occurs when a time
|
|
|
|
# limit or tolerance is set (:UserLimit)
|
|
|
|
if !(stat == :Infeasible || stat == :Unbounded)
|
|
|
|
try
|
|
|
|
# Do a separate try since getobjval could work while getobjbound does not and vice versa
|
|
|
|
objBound = MathProgBase.getobjbound(m.internalModel) + m.obj.aff.constant
|
|
|
|
m.objBound = objBound
|
|
|
|
end
|
|
|
|
try
|
|
|
|
objVal = MathProgBase.getobjval(m.internalModel) + m.obj.aff.constant
|
|
|
|
colVal = MathProgBase.getsolution(m.internalModel)[1:numCols]
|
|
|
|
# Rescale off-diagonal terms of SDP variables
|
|
|
|
if traits.sdp
|
|
|
|
offdiagvars = JuMP.offdiagsdpvars(m)
|
|
|
|
colVal[offdiagvars] /= sqrt(2)
|
|
|
|
end
|
|
|
|
# Don't corrupt the answers if one of the above two calls fails
|
|
|
|
m.objVal = objVal
|
|
|
|
m.colVal = colVal
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return stat
|
|
|
|
end
|