PropertyT.jl/src/SDPs.jl

177 lines
5.3 KiB
Julia
Raw Normal View History

2017-03-13 14:49:55 +01:00
using JuMP
import MathProgBase: AbstractMathProgSolver
function constraints(pm, total_length=maximum(pm))
2017-03-13 14:49:55 +01:00
n = size(pm,1)
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]
push!(constraints[idx], (i,j))
2017-03-13 14:49:55 +01:00
end
end
return constraints
end
function spLaplacian(RG::GroupRing, S, T::Type=Float64)
result = RG(T)
result[RG.group()] = T(length(S))
for s in S
result[s] -= one(T)
end
return result
end
2018-08-15 19:20:55 +02:00
function spLaplacian(RG::GroupRing{R}, S, T::Type=Float64) where {R<:Ring}
result = RG(T)
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
function create_SDP_problem(Δ::GroupRingElem, matrix_constraints; upper_bound=Inf)
N = size(parent(Δ).pm, 1)
2017-03-17 16:27:01 +01:00
Δ² = Δ*Δ
@assert length(Δ.coeffs) == length(matrix_constraints)
2017-03-13 14:49:55 +01:00
m = JuMP.Model();
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
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
return m, λ, P
2017-03-13 14:49:55 +01:00
end
2018-01-02 02:52:45 +01:00
function solve_SDP(m, varλ, varP; warmstart=nothing)
2017-03-13 14:49:55 +01:00
2018-01-02 02:52:45 +01:00
traits = JuMP.ProblemTraits(m, relaxation=false)
2017-03-16 09:35:32 +01:00
2018-01-02 02:52:45 +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);
end
2017-03-16 09:35:32 +01:00
2018-01-02 02:52:45 +01:00
MathProgBase.optimize!(m.internalModel)
2017-03-13 14:49:55 +01:00
2018-01-02 02:52:45 +01:00
λ = MathProgBase.getobjval(m.internalModel)
warmstart = (m.internalModel.primal_sol, m.internalModel.dual_sol,
m.internalModel.slack)
fillfrominternal!(m, traits)
P = JuMP.getvalue(varP)
λ = JuMP.getvalue(varλ)
2017-03-13 14:49:55 +01:00
2018-01-02 02:52:45 +01:00
return λ, P, warmstart
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