Skip to content

Commit

Permalink
Unify behavior of getdual and getvariable: NaN+warn
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Dec 21, 2016
1 parent 03e0284 commit 8c9eb41
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 47 deletions.
88 changes: 49 additions & 39 deletions src/JuMP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,15 @@ end
# internal method that doesn't print a warning if the value is NaN
_getDual(v::Variable) = v.m.redCosts[v.col]

getdualwarn(::Variable) = error("Variable bound duals (reduced costs) not available. Check that the model was properly solved and no integer variables are present.")
getdualwarn(::Variable) = warn("Variable bound duals (reduced costs) not available. Check that the model was properly solved and no integer variables are present.")

function getdual(v::Variable)
if length(v.m.redCosts) < MathProgBase.numvar(v.m)
getdualwarn(v)
NaN
else
_getDual(v)
end
return _getDual(v)
end

const var_cats = [:Cont, :Int, :Bin, :SemiCont, :SemiInt]
Expand Down Expand Up @@ -550,13 +552,15 @@ linearindex(x::ConstraintRef) = x.idx
# internal method that doesn't print a warning if the value is NaN
_getDual(c::LinConstrRef) = c.m.linconstrDuals[c.idx]

getdualwarn(::LinConstrRef) = error("Dual solution not available. Check that the model was properly solved and no integer variables are present.")
getdualwarn{T<:Union{ConstraintRef, Int}}(::T) = warn("Dual solution not available. Check that the model was properly solved and no integer variables are present.")

function getdual(c::LinConstrRef)
if length(c.m.linconstrDuals) != MathProgBase.numlinconstr(c.m)
getdualwarn(c)
NaN
else
_getDual(c)
end
return _getDual(c)
end

# Returns the number of non-infinity and nonzero bounds on variables
Expand Down Expand Up @@ -586,24 +590,18 @@ function getNumBndRows(m::Model)
end

# Returns the number of second-order cone constraints
function getNumSOCRows(m::Model)
numSOCRows = 0
for con in m.socconstr
numSOCRows += length(con.normexpr.norm.terms) + 1
end
return numSOCRows
end
getNumRows(c::SOCConstraint) = length(c.normexpr.norm.terms) + 1
getNumSOCRows(m::Model) = sum(getNumRows.(m.socconstr))

# Returns the number of rows used by SDP constraints in the MPB conic representation
# (excluding symmetry constraints)
function getNumSDPRows(m::Model)
numSDPRows = 0
for con in m.sdpconstr
n = size(con.terms, 1)
numSDPRows += convert(Int, n*(n+1)/2)
end
return numSDPRows
# Julia seems to not be able to infer the return type (probably because c.terms is Any)
# so getNumSDPRows tries to call zero(Any)... Using ::Int solves this issue
function getNumRows(c::SDConstraint)::Int
n = size(c.terms, 1)
(n * (n+1)) ÷ 2
end
getNumSDPRows(m::Model) = sum(getNumRows.(m.sdpconstr))

# Returns the number of symmetry-enforcing constraints for SDP constraints
function getNumSymRows(m::Model)
Expand All @@ -613,26 +611,36 @@ end
# Returns the dual variables corresponding to
# m.sdpconstr[idx] if issdp is true
# m.socconstr[idx] if sdp is not true
function getconicdualaux(m::Model, idx, issdp)
function getconicdualaux(m::Model, idx::Int, issdp::Bool)
numLinRows = MathProgBase.numlinconstr(m)
numBndRows = getNumBndRows(m)
numSOCRows = getNumSOCRows(m)
numSDPRows = getNumSDPRows(m)
numSymRows = getNumSymRows(m)
if length(m.conicconstrDuals) != (numLinRows + numBndRows + numSOCRows + numSDPRows + numSymRows)
error("Dual solution not available. Check that the model was properly solved and no integer variables are present.")
end
offset = numLinRows + numBndRows
if issdp
offset += length(m.socconstr)
end
dual = m.conicconstrDuals[m.constrDualMap[offset + idx]]
if issdp
offset += length(m.sdpconstr)
symdual = m.conicconstrDuals[m.constrDualMap[offset + idx]]
dual, symdual
numRows = numLinRows + numBndRows + numSOCRows + numSDPRows + numSymRows
if length(m.conicconstrDuals) != numRows
# solve might not have been called so m.constrDualMap might be empty
getdualwarn(idx)
c = issdp ? m.sdpconstr[idx] : m.socconstr[idx]
duals = fill(NaN, getNumRows(c))
if issdp
duals, Float64[]
else
duals
end
else
dual
offset = numLinRows + numBndRows
if issdp
offset += length(m.socconstr)
end
dual = m.conicconstrDuals[m.constrDualMap[offset + idx]]
if issdp
offset += length(m.sdpconstr)
symdual = m.conicconstrDuals[m.constrDualMap[offset + idx]]
dual, symdual
else
dual
end
end
end

Expand Down Expand Up @@ -691,13 +699,15 @@ function getdual(c::ConstraintRef{Model,SDConstraint})
end
end
end
@assert length(symdual) == length(c.m.sdpconstrSym[c.idx])
idx = 0
for (i,j) in c.m.sdpconstrSym[c.idx]
idx += 1
s = symdual[idx]
X[i,j] -= s
X[j,i] += s
if !isempty(symdual)
@assert length(symdual) == length(c.m.sdpconstrSym[c.idx])
idx = 0
for (i,j) in c.m.sdpconstrSym[c.idx]
idx += 1
s = symdual[idx]
X[i,j] -= s
X[j,i] += s
end
end
X
end
Expand Down
6 changes: 4 additions & 2 deletions src/nlp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ function getdual(c::ConstraintRef{Model,NonlinearConstraint})
initNLP(c.m)
nldata::NLPData = c.m.nlpdata
if length(nldata.nlconstrDuals) != length(nldata.nlconstr)
error("Dual solution not available. Check that the model was properly solved.")
getdualwarn(c)
NaN
else
nldata.nlconstrDuals[c.idx]
end
return nldata.nlconstrDuals[c.idx]
end

type FunctionStorage
Expand Down
4 changes: 2 additions & 2 deletions test/model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ end
@test_throws ErrorException setobjectivesense(modErr, :Maximum)
@variable(modErr, errVar)
@test isnan(getvalue(errVar))
@test_throws ErrorException getdual(errVar)
@test isnan(getdual(errVar))

modErr = Model()
@variable(modErr, x, Bin)
@objective(modErr, Max, x)
con = @constraint(modErr, x <= 0.5)
solve(modErr)
@test_throws ErrorException getdual(con)
@test isnan(getdual(con))

modErr = Model()
@variable(modErr, 0 <= x <= 1)
Expand Down
8 changes: 4 additions & 4 deletions test/sdp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ ispsd(x::JuMP.JuMPArray) = ispsd(x.innerArray)
@variable(m, X[1:2,1:2], SDP)
c = @constraint(m, X[1,1]+X[2,2] == 1)
@objective(m, Min, 2*X[1,1]+2*X[1,2])
@test_throws ErrorException getdual(X)
@test all(isnan.(getdual(X)))
status = solve(m)

@test status == :Optimal
Expand All @@ -599,7 +599,7 @@ ispsd(x::JuMP.JuMPArray) = ispsd(x.innerArray)
@variable(m, y)
c = @SDconstraint(m, [2-y 1; 1 -y] >= 0)
@objective(m, Max, y)
@test_throws ErrorException getdual(c)
@test all(isnan(getdual(c)))
status = solve(m)

@test status == :Optimal
Expand All @@ -625,7 +625,7 @@ ispsd(x::JuMP.JuMPArray) = ispsd(x.innerArray)
@variable(m, y)
c = @SDconstraint(m, [0 y; y 0] <= [1 0; 0 0])
@objective(m, Max, y)
@test_throws ErrorException getdual(c)
@test all(isnan(getdual(c)))
status = solve(m)

@test status == :Optimal
Expand All @@ -645,7 +645,7 @@ ispsd(x::JuMP.JuMPArray) = ispsd(x.innerArray)
@variable(m, X[1:2,1:2], SDP)
c = @constraint(m, 2*X[1,2] == 1)
@objective(m, Min, X[1,1])
@test_throws ErrorException getdual(X)
@test all(isnan(getdual(X)))
status = solve(m)

@test status == :Optimal
Expand Down

0 comments on commit 8c9eb41

Please sign in to comment.