diff --git a/src/JuMP.jl b/src/JuMP.jl index bfea018a5e1..105eacef19a 100644 --- a/src/JuMP.jl +++ b/src/JuMP.jl @@ -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] @@ -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 @@ -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) @@ -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 @@ -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 diff --git a/src/nlp.jl b/src/nlp.jl index 17a124b4744..d96a23107e7 100644 --- a/src/nlp.jl +++ b/src/nlp.jl @@ -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 diff --git a/test/model.jl b/test/model.jl index ca924208f9e..2c3b957f768 100644 --- a/test/model.jl +++ b/test/model.jl @@ -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) diff --git a/test/sdp.jl b/test/sdp.jl index b387b1768d4..07188ccd718 100644 --- a/test/sdp.jl +++ b/test/sdp.jl @@ -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 @@ -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 @@ -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 @@ -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