From 0c32f785cdbc15f2d98ea52a1f83998f2fde6bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Thu, 16 Aug 2018 12:22:22 +0200 Subject: [PATCH 01/13] Add DualObjectiveValue, a fallback and test it --- src/Test/contconic.jl | 37 ++++++++++ src/Test/contlinear.jl | 26 +++++++ src/Utilities/mockoptimizer.jl | 38 +++++++---- src/Utilities/results.jl | 121 ++++++++++++++++++++++++++++++++- test/Bridges/intervalbridge.jl | 10 ++- test/Test/contconic.jl | 4 +- test/Test/contlinear.jl | 10 ++- test/Test/unit.jl | 2 + 8 files changed, 226 insertions(+), 22 deletions(-) diff --git a/src/Test/contconic.jl b/src/Test/contconic.jl index 7a8d7e2033..43318a93e8 100644 --- a/src/Test/contconic.jl +++ b/src/Test/contconic.jl @@ -58,6 +58,9 @@ function _lin1test(model::MOI.ModelLike, config::TestConfig, vecofvars::Bool) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ -11 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ -11 atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [1, 0, 2] atol=atol rtol=rtol @@ -155,6 +158,9 @@ function _lin2test(model::MOI.ModelLike, config::TestConfig, vecofvars::Bool) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ -82 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ -82 atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), x) ≈ -4 atol=atol rtol=rtol @test MOI.get(model, MOI.VariablePrimal(), y) ≈ -3 atol=atol rtol=rtol @@ -343,6 +349,9 @@ function _soc1test(model::MOI.ModelLike, config::TestConfig, vecofvars::Bool) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ √2 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ √2 atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 1 atol=atol rtol=rtol @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 1/√2 atol=atol rtol=rtol @@ -418,6 +427,9 @@ function _soc2test(model::MOI.ModelLike, config::TestConfig, nonneg::Bool) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ -1/√2 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ -1/√2 atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), x) ≈ -1/√2 atol=atol rtol=rtol @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 1/√2 atol=atol rtol=rtol @@ -537,6 +549,9 @@ function soc4test(model::MOI.ModelLike, config::TestConfig) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ -√5 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ -√5 atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), x) ≈ [1.0, 2/√5, 1/√5, 2/√5, 1/√5] atol=atol rtol=rtol @@ -619,6 +634,9 @@ function _rotatedsoc1test(model::MOI.ModelLike, config::TestConfig, abvars::Bool end @test MOI.get(model, MOI.ObjectiveValue()) ≈ √2 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ √2 atol=atol rtol=rtol + end if abvars @test MOI.get(model, MOI.VariablePrimal(), a) ≈ 0.5 atol=atol rtol=rtol @@ -787,6 +805,9 @@ function rotatedsoc3test(model::MOI.ModelLike, config::TestConfig; n=2, ub=3.0) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ √ub atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ √ub atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), x) ≈ [1.0; zeros(n-1)] atol=atol rtol=rtol @test MOI.get(model, MOI.VariablePrimal(), u) ≈ ub atol=atol rtol=rtol @@ -947,6 +968,10 @@ function _exp1test(model::MOI.ModelLike, config::TestConfig, vecofvars::Bool) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ 3 + 2exp(1/2) atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 3 + 2exp(1/2) atol=atol rtol=rtol + end + @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [1., 2., 2exp(1/2)] atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintPrimal(), vc) ≈ [1., 2., 2exp(1/2)] atol=atol rtol=rtol @@ -1013,6 +1038,9 @@ function exp2test(model::MOI.ModelLike, config::TestConfig) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ exp(-0.3) atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ exp(-0.3) atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [0., -0.3, 0., exp(-0.3), exp(-0.3), exp(-0.3), 0., 1.0, 0.] atol=atol rtol=rtol @@ -1078,6 +1106,9 @@ function exp3test(model::MOI.ModelLike, config::TestConfig) end @test MOI.get(model, MOI.ObjectiveValue()) ≈ log(5) atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ log(5) atol=atol rtol=rtol + end @test MOI.get(model, MOI.VariablePrimal(), x) ≈ log(5) atol=atol rtol=rtol @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 5. atol=atol rtol=rtol @@ -1157,6 +1188,9 @@ function _psd0test(model::MOI.ModelLike, vecofvars::Bool, psdcone, config::TestC end @test MOI.get(model, MOI.ObjectiveValue()) ≈ 2 atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 2 atol=atol rtol=rtol + end Xv = square ? ones(4) : ones(3) @test MOI.get(model, MOI.VariablePrimal(), X) ≈ Xv atol=atol rtol=rtol @@ -1302,6 +1336,9 @@ function _psd1test(model::MOI.ModelLike, vecofvars::Bool, psdcone, config::TestC end @test MOI.get(model, MOI.ObjectiveValue()) ≈ obj atol=atol rtol=rtol + if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ obj atol=atol rtol=rtol + end Xv = square ? [α^2, α*β, α^2, α*β, β^2, α*β, α^2, α*β, α^2] : [α^2, α*β, β^2, α^2, α*β, α^2] xv = [√2*x2, x2, x2] diff --git a/src/Test/contlinear.jl b/src/Test/contlinear.jl index 04f9b2a97a..83dff5b34e 100644 --- a/src/Test/contlinear.jl +++ b/src/Test/contlinear.jl @@ -79,6 +79,7 @@ function linear1test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ -1 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol=atol rtol=rtol # reduced costs @@ -112,6 +113,7 @@ function linear1test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 1 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), vc1) ≈ 0 atol=atol rtol=rtol @@ -171,6 +173,7 @@ function linear1test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 2 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -2 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), vc1) ≈ 1 atol=atol rtol=rtol @@ -198,6 +201,11 @@ function linear1test(model::MOI.ModelLike, config::TestConfig) @test MOI.get(model, MOI.ObjectiveValue()) ≈ 3 atol=atol rtol=rtol @test MOI.get(model, MOI.VariablePrimal(), v) ≈ [-1, 0, 2] atol=atol rtol=rtol + + if config.duals + @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 3 atol=atol rtol=rtol + end end # put lb of x back to 0 and fix z to zero to get : @@ -317,6 +325,7 @@ function linear1test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.DualStatus(1)) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 3 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1.5 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c2) ≈ 0.5 atol=atol rtol=rtol @@ -405,6 +414,7 @@ function linear2test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ -1 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol=atol rtol=rtol # reduced costs @@ -1157,6 +1167,7 @@ function linear10test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 10.0 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol=atol rtol=rtol end @@ -1180,6 +1191,7 @@ function linear10test(model::MOI.ModelLike, config::TestConfig) @test MOI.get(model, MOI.ConstraintPrimal(), c) ≈ 5 atol=atol rtol=rtol if config.duals + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 5.0 atol=atol rtol=rtol @test MOI.get(model, MOI.ResultCount()) >= 1 @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.ConstraintDual(), c) ≈ 1 atol=atol rtol=rtol @@ -1213,6 +1225,13 @@ function linear10test(model::MOI.ModelLike, config::TestConfig) MOI.get(model, MOI.ConstraintBasisStatus(), vc[2])== MOI.BASIC) @test MOI.get(model, MOI.ConstraintBasisStatus(), c) == MOI.NONBASIC_AT_LOWER end + + if config.duals + @test MOI.get(model, MOI.ResultCount()) >= 1 + @test MOI.get(model, MOI.DualStatus()) == MOI.FeasiblePoint + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 2.0 atol=atol rtol=rtol + @test MOI.get(model, MOI.ConstraintDual(), c) ≈ 1 atol=atol rtol=rtol + end end MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 1.0], [x, y]), 0.0)) @@ -1529,6 +1548,7 @@ function linear14test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 8 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol=atol rtol=rtol # reduced costs @@ -1565,6 +1585,7 @@ function linear14test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 6 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ -1 atol=atol rtol=rtol # reduced costs @@ -1617,6 +1638,11 @@ function linear15test(model::MOI.ModelLike, config::TestConfig) @test MOI.get(model, MOI.ObjectiveValue()) ≈ 0 atol=atol rtol=rtol @test MOI.get(model, MOI.VariablePrimal(), x[1]) ≈ 0 atol=atol rtol=rtol + + if config.duals + @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT + @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 0 atol=atol rtol=rtol + end end end diff --git a/src/Utilities/mockoptimizer.jl b/src/Utilities/mockoptimizer.jl index 9f2fd0f4ec..3c600c9f88 100644 --- a/src/Utilities/mockoptimizer.jl +++ b/src/Utilities/mockoptimizer.jl @@ -31,8 +31,11 @@ mutable struct MockOptimizer{MT<:MOI.ModelLike} <: MOI.AbstractOptimizer # Computes `ObjectiveValue` by evaluating the `ObjectiveFunction` with # `VariablePrimal`. See `get_fallback`. eval_objective_value::Bool - objectivevalue::Float64 - objectivebound::Float64 # set this using MOI.set(model, MOI.ObjectiveBound(), value) + objective_value::Float64 # set this using MOI.set(model, MOI.ObjectiveValue(), value) + # Computes `DualObjectiveValue` using `get_fallback` + eval_dual_objective_value::Bool + dual_objective_value::Float64 # set this using MOI.set(model, MOI.DualObjectiveValue(), value) + objective_bound::Float64 # set this using MOI.set(model, MOI.ObjectiveBound(), value) primalstatus::MOI.ResultStatusCode dualstatus::MOI.ResultStatusCode varprimal::Dict{MOI.VariableIndex,Float64} @@ -54,6 +57,7 @@ xor_variables(f) = mapvariables(xor_index, f) function MockOptimizer(inner_model::MOI.ModelLike; supports_names=true, needs_allocate_load=false, eval_objective_value=true, + eval_dual_objective_value=true, eval_variable_constraint_dual=true) return MockOptimizer(inner_model, 0, @@ -73,6 +77,8 @@ function MockOptimizer(inner_model::MOI.ModelLike; supports_names=true, 0, eval_objective_value, NaN, + eval_dual_objective_value, + NaN, NaN, MOI.NO_SOLUTION, MOI.NO_SOLUTION, @@ -141,10 +147,11 @@ function MOI.supports(mock::MockOptimizer, return MOI.supports(mock.inner_model, attr, IdxT) end -MOI.supports(mock::MockOptimizer, ::Union{MOI.ResultCount,MOI.TerminationStatus,MOI.ObjectiveValue,MOI.PrimalStatus,MOI.DualStatus,MockModelAttribute}) = true +MOI.supports(mock::MockOptimizer, ::MockModelAttribute) = true MOI.set(mock::MockOptimizer, ::MOI.ResultCount, value::Integer) = (mock.resultcount = value) MOI.set(mock::MockOptimizer, ::MOI.TerminationStatus, value::MOI.TerminationStatusCode) = (mock.terminationstatus = value) -MOI.set(mock::MockOptimizer, ::MOI.ObjectiveValue, value::Real) = (mock.objectivevalue = value) +MOI.set(mock::MockOptimizer, ::MOI.ObjectiveValue, value::Real) = (mock.objective_value = value) +MOI.set(mock::MockOptimizer, ::MOI.DualObjectiveValue, value::Real) = (mock.dual_objective_value = value) MOI.set(mock::MockOptimizer, ::MOI.PrimalStatus, value::MOI.ResultStatusCode) = (mock.primalstatus = value) MOI.set(mock::MockOptimizer, ::MOI.DualStatus, value::MOI.ResultStatusCode) = (mock.dualstatus = value) MOI.set(mock::MockOptimizer, ::MockModelAttribute, value::Integer) = (mock.attribute = value) @@ -237,7 +244,14 @@ function MOI.get(mock::MockOptimizer, attr::MOI.ObjectiveValue) if mock.eval_objective_value return get_fallback(mock, attr) else - return mock.objectivevalue + return mock.objective_value + end +end +function MOI.get(mock::MockOptimizer, attr::MOI.DualObjectiveValue) + if mock.eval_dual_objective_value + return get_fallback(mock, attr, Float64) + else + return mock.dual_objective_value end end MOI.get(mock::MockOptimizer, ::MOI.PrimalStatus) = mock.primalstatus @@ -283,10 +297,9 @@ end MOI.get(mock::MockOptimizer, ::MockConstraintAttribute, idx::MOI.ConstraintIndex) = mock.conattribute[xor_index(idx)] MOI.get(mock::MockOptimizer, ::MOI.ConstraintBasisStatus, idx::MOI.ConstraintIndex) = mock.con_basis[xor_index(idx)] -MOI.supports(mock::MockOptimizer, ::MOI.ObjectiveBound) = true -MOI.get(mock::MockOptimizer, ::MOI.ObjectiveBound) = mock.objectivebound +MOI.get(mock::MockOptimizer, ::MOI.ObjectiveBound) = mock.objective_bound function MOI.set(mock::MockOptimizer, ::MOI.ObjectiveBound, value::Float64) - mock.objectivebound = value + mock.objective_bound = value end MOI.get(::MockOptimizer, ::MOI.SolverName) = "Mock" @@ -301,8 +314,9 @@ function MOI.empty!(mock::MockOptimizer) mock.hasdual = false mock.terminationstatus = MOI.OPTIMIZE_NOT_CALLED mock.resultcount = 0 - mock.objectivevalue = NaN - mock.objectivebound = NaN + mock.objective_value = NaN + mock.dual_objective_value = NaN + mock.objective_bound = NaN mock.primalstatus = MOI.NO_SOLUTION mock.dualstatus = MOI.NO_SOLUTION mock.varprimal = Dict{MOI.VariableIndex,Float64}() @@ -318,8 +332,8 @@ function MOI.is_empty(mock::MockOptimizer) return MOI.is_empty(mock.inner_model) && mock.attribute == 0 && !mock.solved && !mock.hasprimal && !mock.hasdual && mock.terminationstatus == MOI.OPTIMIZE_NOT_CALLED && - mock.resultcount == 0 && isnan(mock.objectivevalue) && - isnan(mock.objectivebound) && + mock.resultcount == 0 && isnan(mock.objective_value) && + isnan(mock.dual_objective_value) && isnan(mock.objective_bound) && mock.primalstatus == MOI.NO_SOLUTION && mock.dualstatus == MOI.NO_SOLUTION && isempty(mock.con_basis) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 452a6e7dec..395853a16f 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -23,6 +23,115 @@ function get_fallback(model::MOI.ModelLike, ::MOI.ObjectiveValue) return evalvariables(vi -> MOI.get(model, MOI.VariablePrimal(), vi), f) end +scalar_constant(T::Type, ::MOI.SingleVariable) = zero(T) +scalar_constant(::Type, f::MOI.AbstractScalarFunction) = _constant(f) + +function constraint_constant(model::MOI.ModelLike, + ci::MOI.ConstraintIndex{<:MOI.AbstractVectorFunction, + <:MOI.AbstractVectorSet}, + T::Type) + return _constant(MOI.get(model, MOI.ConstraintFunction(), ci)) +end +function constraint_constant(model::MOI.ModelLike, + ci::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction, + <:MOI.AbstractScalarSet}, + T::Type) + return scalar_constant(T, MOI.get(model, MOI.ConstraintFunction(), ci)) - + getconstant(MOI.get(model, MOI.ConstraintSet(), ci)) +end + +""" + dual_objective_value(model::MOI.ModelLike, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + T::Type) + +Return the part of `DualObjectiveValue` due to the constraint of index `ci` using +scalar type `T`. +""" +function dual_objective_value(model::MOI.ModelLike, + ci::MOI.ConstraintIndex, + T::Type) + return set_dot(constraint_constant(model, ci, T), + MOI.get(model, MOI.ConstraintDual(), ci), + MOI.get(model, MOI.ConstraintSet(), ci)) +end + +function dual_objective_value(model::MOI.ModelLike, + ci::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction, + <:MOI.Interval}, + T::Type) + constant = scalar_constant(T, MOI.get(model, MOI.ConstraintFunction(), ci)) + set = MOI.get(model, MOI.ConstraintSet(), ci) + dual = MOI.get(model, MOI.ConstraintDual(), ci) + if dual < zero(dual) + # The dual is negative so it is in the dual of the MOI.LessThan cone + # hence the upper bound of the Interval set is tight + constant -= set.upper + else + # the lower bound is tight + constant -= set.lower + end + return set_dot(constant, dual, set) +end + + + +""" + dual_objective_value(model::MOI.ModelLike, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + T::Type) + +Return the part of `DualObjectiveValue` due to `F`-in-`S` constraints using scalar +type `T`. +""" +function dual_objective_value(model::MOI.ModelLike, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + T::Type) + value = zero(T) # sum won't work if there are now constraints. + for ci in MOI.get(model, MOI.ListOfConstraintIndices{F, S}()) + value += dual_objective_value(model, ci, T) + end + return value +end + +function dual_objective_value(model::MOI.ModelLike, + F::Type{MOI.VectorOfVariables}, + S::Type{<:MOI.AbstractVectorSet}, + T::Type) + # No constant in the function nor set so no contribution to the dual + # objective value. + return zero(T) +end + + +""" + get_fallback(model::MOI.ModelLike, ::MOI.DualObjectiveValue, T::Type)::T + +Compute the dual objective value of type `T` using the `ConstraintDual` results +and the `ConstraintFunction` and `ConstraintSet` values. +""" +function get_fallback(model::MOI.ModelLike, ::MOI.DualObjectiveValue, T::Type) + value = zero(T) # sum will not work if there are zero constraints + for (F, S) in MOI.get(model, MOI.ListOfConstraints()) + value += dual_objective_value(model, F, S, T)::T + end + if MOI.get(model, MOI.ObjectiveSense()) != MOI.MaxSense + value = -value + end + dual_status = MOI.get(model, MOI.DualStatus()) + if dual_status == MOI.INFEASIBILITY_CERTIFICATE || + status == MOI.NEARLY_INFEASIBILITY_CERTIFICATE + # The objective constant should not be present in rays + F = MOI.get(model, MOI.ObjectiveFunctionType()) + f = MOI.get(model, MOI.ObjectiveFunction{F}()) + value += scalar_constant(T, f) + end + return value::T +end + """ get_fallback(model::MOI.ModelLike, ::MOI.ConstraintPrimal, constraint_index::MOI.ConstraintIndex) @@ -266,9 +375,15 @@ end Return the scalar product between a vector `x` of the set `set` and a vector `y` of the dual of the set `s`. """ -function set_dot(x::Vector, y::Vector, set::MOI.AbstractVectorSet) - return dot(x, y) -end +set_dot(x::Vector, y::Vector, set::MOI.AbstractVectorSet) = dot(x, y) + +""" + set_dot(x, y, set::AbstractScalarSet) + +Return the scalar product between a number `x` of the set `set` and a number +`y` of the dual of the set `s`. +""" +set_dot(x, y, set::MOI.AbstractScalarSet) = dot(x, y) function triangle_dot(x::Vector{T}, y::Vector{T}, dim::Int, offset::Int) where T result = zero(T) diff --git a/test/Bridges/intervalbridge.jl b/test/Bridges/intervalbridge.jl index cb923af73d..9d079e0aff 100644 --- a/test/Bridges/intervalbridge.jl +++ b/test/Bridges/intervalbridge.jl @@ -20,7 +20,10 @@ (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [MOI.BASIC, MOI.BASIC]], (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [0]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0], con_basis = + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [0], + con_basis = [(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.NONBASIC], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [MOI.BASIC, MOI.BASIC]]), @@ -30,7 +33,10 @@ (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [MOI.BASIC, MOI.BASIC]])) MOIT.linear10test(bridged_mock, config_with_basis) MOIU.set_mock_optimize!(mock, - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.0, 0.0], con_basis = + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.0, 0.0], + (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [0], + (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1], + con_basis = [(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [MOI.BASIC], (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [MOI.BASIC], (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [MOI.NONBASIC, MOI.NONBASIC]], diff --git a/test/Test/contconic.jl b/test/Test/contconic.jl index 1a45a44b74..1efbaf03c4 100644 --- a/test/Test/contconic.jl +++ b/test/Test/contconic.jl @@ -124,14 +124,14 @@ end (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [2]) MOIT.psdt0vtest(mock, config) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, ones(3), - (MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle) => [[1, -1, 1]], + (MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle) => [[1.0, -1.0, 1.0]], (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [2]) MOIT.psdt0ftest(mock, config) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, ones(4), (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [2]) MOIT.psds0vtest(mock, config) mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, ones(4), - (MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeSquare) => [[1, -2, 0, 1]], + (MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeSquare) => [[1.0, -2.0, 0.0, 1.0]], (MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => [2]) MOIT.psds0ftest(mock, config) # PSD1 diff --git a/test/Test/contlinear.jl b/test/Test/contlinear.jl index e7d8a6a188..c6200cf850 100644 --- a/test/Test/contlinear.jl +++ b/test/Test/contlinear.jl @@ -112,10 +112,14 @@ MOIU.set_mock_optimize!(mock, [(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [MOI.BASIC, MOI.BASIC]], (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0], con_basis = + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [1], + con_basis = [(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_LOWER], (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [MOI.BASIC, MOI.BASIC]]), - (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [6.0, 6.0], con_basis = + (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [6.0, 6.0], + (MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [-1], + con_basis = [(MOI.ScalarAffineFunction{Float64}, MOI.Interval{Float64}) => [MOI.NONBASIC_AT_UPPER], (MOI.SingleVariable, MOI.GreaterThan{Float64}) => [MOI.BASIC, MOI.BASIC]])) MOIT.linear10test(mock, config) @@ -160,7 +164,7 @@ MOIT.linear14test(mock, config) mock.eval_variable_constraint_dual = true MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.0], - (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [0.0, 0.0])) + (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[0.0, 0.0]])) MOIT.linear15test(mock, config) MOIU.set_mock_optimize!(mock, diff --git a/test/Test/unit.jl b/test/Test/unit.jl index 8402a4b184..8034666582 100644 --- a/test/Test/unit.jl +++ b/test/Test/unit.jl @@ -254,6 +254,7 @@ end MOIT.solve_integer_edge_cases(mock, config) end @testset "solve_objbound_edge_cases" begin + mock.eval_objective_bound = false MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.ObjectiveBound(), 3.0) @@ -273,6 +274,7 @@ end end ) MOIT.solve_objbound_edge_cases(mock, config) + mock.eval_objective_bound = true end end From a6ef6a1cf83e4d71ede15b0c4b2ef066659fa0a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 17 Apr 2019 17:52:23 +0200 Subject: [PATCH 02/13] Add DualObjectiveValue --- src/attributes.jl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/attributes.jl b/src/attributes.jl index 0fdd7aa9c6..1e957c8c79 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -468,6 +468,17 @@ struct ObjectiveValue <: AbstractModelAttribute (::Type{ObjectiveValue})(resultindex=1) = new(resultindex) end +""" + DualObjectiveValue(result_index::Int=1) + +A model attribute for the value of the objective function of the dual problem +for the `resultindex`th dual result. +""" +struct DualObjectiveValue <: AbstractModelAttribute + result_index::Int + (::Type{DualObjectiveValue})(result_index=1) = new(result_index) +end + """ ObjectiveBound() From 2a8c4134c2fc2eda3a0b7473555ee2a237e60d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 17 Apr 2019 17:52:34 +0200 Subject: [PATCH 03/13] Use result_index --- src/Utilities/results.jl | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 395853a16f..e02cd52b33 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -44,26 +44,29 @@ end dual_objective_value(model::MOI.ModelLike, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, - T::Type) + T::Type, + result_index::Int64) Return the part of `DualObjectiveValue` due to the constraint of index `ci` using scalar type `T`. """ function dual_objective_value(model::MOI.ModelLike, ci::MOI.ConstraintIndex, - T::Type) + T::Type, + result_index::Int64) return set_dot(constraint_constant(model, ci, T), - MOI.get(model, MOI.ConstraintDual(), ci), + MOI.get(model, MOI.ConstraintDual(result_index), ci), MOI.get(model, MOI.ConstraintSet(), ci)) end function dual_objective_value(model::MOI.ModelLike, ci::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction, <:MOI.Interval}, - T::Type) + T::Type, + result_index::Int64) constant = scalar_constant(T, MOI.get(model, MOI.ConstraintFunction(), ci)) set = MOI.get(model, MOI.ConstraintSet(), ci) - dual = MOI.get(model, MOI.ConstraintDual(), ci) + dual = MOI.get(model, MOI.ConstraintDual(result_index), ci) if dual < zero(dual) # The dual is negative so it is in the dual of the MOI.LessThan cone # hence the upper bound of the Interval set is tight @@ -81,7 +84,8 @@ end dual_objective_value(model::MOI.ModelLike, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, - T::Type) + T::Type, + result_index::Int64) Return the part of `DualObjectiveValue` due to `F`-in-`S` constraints using scalar type `T`. @@ -89,10 +93,11 @@ type `T`. function dual_objective_value(model::MOI.ModelLike, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, - T::Type) + T::Type, + result_index::Int64) value = zero(T) # sum won't work if there are now constraints. for ci in MOI.get(model, MOI.ListOfConstraintIndices{F, S}()) - value += dual_objective_value(model, ci, T) + value += dual_objective_value(model, ci, T, result_index) end return value end @@ -100,7 +105,8 @@ end function dual_objective_value(model::MOI.ModelLike, F::Type{MOI.VectorOfVariables}, S::Type{<:MOI.AbstractVectorSet}, - T::Type) + T::Type, + result_index::Int64) # No constant in the function nor set so no contribution to the dual # objective value. return zero(T) @@ -113,10 +119,10 @@ end Compute the dual objective value of type `T` using the `ConstraintDual` results and the `ConstraintFunction` and `ConstraintSet` values. """ -function get_fallback(model::MOI.ModelLike, ::MOI.DualObjectiveValue, T::Type) +function get_fallback(model::MOI.ModelLike, attr::MOI.DualObjectiveValue, T::Type) value = zero(T) # sum will not work if there are zero constraints for (F, S) in MOI.get(model, MOI.ListOfConstraints()) - value += dual_objective_value(model, F, S, T)::T + value += dual_objective_value(model, F, S, T, attr.result_index)::T end if MOI.get(model, MOI.ObjectiveSense()) != MOI.MaxSense value = -value From 28422305c486c94b916712c7914ab0f1afa39f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Apr 2019 14:52:09 +0200 Subject: [PATCH 04/13] Remove eval_objective_bound --- test/Test/unit.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/Test/unit.jl b/test/Test/unit.jl index 8034666582..8402a4b184 100644 --- a/test/Test/unit.jl +++ b/test/Test/unit.jl @@ -254,7 +254,6 @@ end MOIT.solve_integer_edge_cases(mock, config) end @testset "solve_objbound_edge_cases" begin - mock.eval_objective_bound = false MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> begin MOI.set(mock, MOI.ObjectiveBound(), 3.0) @@ -274,7 +273,6 @@ end end ) MOIT.solve_objbound_edge_cases(mock, config) - mock.eval_objective_bound = true end end From 09f167ed1913c614b79fd52559d01bc1b2de09ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Apr 2019 14:56:51 +0200 Subject: [PATCH 05/13] _constant -> MOI._constant --- src/Utilities/results.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index e02cd52b33..f4fd9402c3 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -24,13 +24,13 @@ function get_fallback(model::MOI.ModelLike, ::MOI.ObjectiveValue) end scalar_constant(T::Type, ::MOI.SingleVariable) = zero(T) -scalar_constant(::Type, f::MOI.AbstractScalarFunction) = _constant(f) +scalar_constant(::Type, f::MOI.AbstractScalarFunction) = MOI._constant(f) function constraint_constant(model::MOI.ModelLike, ci::MOI.ConstraintIndex{<:MOI.AbstractVectorFunction, <:MOI.AbstractVectorSet}, T::Type) - return _constant(MOI.get(model, MOI.ConstraintFunction(), ci)) + return MOI._constant(MOI.get(model, MOI.ConstraintFunction(), ci)) end function constraint_constant(model::MOI.ModelLike, ci::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction, From 9065da721bee5294b488927f9eb9d93c0e2c6584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Apr 2019 14:57:50 +0200 Subject: [PATCH 06/13] MaxSense -> MAX_SENSE --- src/Utilities/results.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index f4fd9402c3..13aef0ffc2 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -124,7 +124,7 @@ function get_fallback(model::MOI.ModelLike, attr::MOI.DualObjectiveValue, T::Typ for (F, S) in MOI.get(model, MOI.ListOfConstraints()) value += dual_objective_value(model, F, S, T, attr.result_index)::T end - if MOI.get(model, MOI.ObjectiveSense()) != MOI.MaxSense + if MOI.get(model, MOI.ObjectiveSense()) != MOI.MAX_SENSE value = -value end dual_status = MOI.get(model, MOI.DualStatus()) From d69e3ccf9ce7d2a6b1e722348c114ed024b68d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Apr 2019 15:15:39 +0200 Subject: [PATCH 07/13] Fix ray check --- src/Utilities/results.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 13aef0ffc2..95fdf5bf69 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -112,6 +112,10 @@ function dual_objective_value(model::MOI.ModelLike, return zero(T) end +function is_ray(status::MOI.ResultStatusCode) + return status == MOI.INFEASIBILITY_CERTIFICATE || + status == MOI.NEARLY_INFEASIBILITY_CERTIFICATE +end """ get_fallback(model::MOI.ModelLike, ::MOI.DualObjectiveValue, T::Type)::T @@ -127,9 +131,7 @@ function get_fallback(model::MOI.ModelLike, attr::MOI.DualObjectiveValue, T::Typ if MOI.get(model, MOI.ObjectiveSense()) != MOI.MAX_SENSE value = -value end - dual_status = MOI.get(model, MOI.DualStatus()) - if dual_status == MOI.INFEASIBILITY_CERTIFICATE || - status == MOI.NEARLY_INFEASIBILITY_CERTIFICATE + if is_ray(MOI.get(model, MOI.DualStatus())) # The objective constant should not be present in rays F = MOI.get(model, MOI.ObjectiveFunctionType()) f = MOI.get(model, MOI.ObjectiveFunction{F}()) @@ -301,9 +303,7 @@ function variable_dual(model::MOI.ModelLike, attr::MOI.ConstraintDual, ci::MOI.ConstraintIndex, vi::MOI.VariableIndex) - status = MOI.get(model, MOI.DualStatus()) - ray = status == MOI.INFEASIBILITY_CERTIFICATE || - status == MOI.NEARLY_INFEASIBILITY_CERTIFICATE + ray = is_ray(MOI.get(model, MOI.DualStatus())) dual = 0.0 if !ray sense = MOI.get(model, MOI.ObjectiveSense()) From 5f02d0c5142cd8fcc9a53f1915ee65c43a512af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Apr 2019 15:31:31 +0200 Subject: [PATCH 08/13] FeasiblePoint -> FEASIBLE_POINT --- src/Test/contlinear.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Test/contlinear.jl b/src/Test/contlinear.jl index 83dff5b34e..72c77eada1 100644 --- a/src/Test/contlinear.jl +++ b/src/Test/contlinear.jl @@ -1228,7 +1228,7 @@ function linear10test(model::MOI.ModelLike, config::TestConfig) if config.duals @test MOI.get(model, MOI.ResultCount()) >= 1 - @test MOI.get(model, MOI.DualStatus()) == MOI.FeasiblePoint + @test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT @test MOI.get(model, MOI.DualObjectiveValue()) ≈ 2.0 atol=atol rtol=rtol @test MOI.get(model, MOI.ConstraintDual(), c) ≈ 1 atol=atol rtol=rtol end From 2143b1599452c93e075435ca1f3930bf03163478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Apr 2019 15:31:43 +0200 Subject: [PATCH 09/13] Add constant if not ray --- src/Utilities/results.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 95fdf5bf69..b326c2e110 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -131,7 +131,7 @@ function get_fallback(model::MOI.ModelLike, attr::MOI.DualObjectiveValue, T::Typ if MOI.get(model, MOI.ObjectiveSense()) != MOI.MAX_SENSE value = -value end - if is_ray(MOI.get(model, MOI.DualStatus())) + if !is_ray(MOI.get(model, MOI.DualStatus())) # The objective constant should not be present in rays F = MOI.get(model, MOI.ObjectiveFunctionType()) f = MOI.get(model, MOI.ObjectiveFunction{F}()) From 1f1e0124805943662dbb57210c27e6ddb427ce64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 19 Apr 2019 17:40:06 +0200 Subject: [PATCH 10/13] Fix triangle_dot for different eltypes --- src/Utilities/results.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index b326c2e110..b750d780d7 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -391,8 +391,9 @@ Return the scalar product between a number `x` of the set `set` and a number """ set_dot(x, y, set::MOI.AbstractScalarSet) = dot(x, y) -function triangle_dot(x::Vector{T}, y::Vector{T}, dim::Int, offset::Int) where T - result = zero(T) +function triangle_dot(x::Vector{S}, y::Vector{T}, dim::Int, offset::Int) where {S, T} + U = typeof(zero(S) * zero(T) + 2 * zero(S) * zero(T)) + result = zero(U) k = offset for i in 1:dim for j in 1:i From 709f08fc380a313dddd694769ee29208e9bd69c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 20 Apr 2019 22:37:50 +0200 Subject: [PATCH 11/13] 32-bits fix --- src/Utilities/results.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index b750d780d7..3f387bf4ff 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -45,7 +45,7 @@ end F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, T::Type, - result_index::Int64) + result_index::Integer) Return the part of `DualObjectiveValue` due to the constraint of index `ci` using scalar type `T`. @@ -53,7 +53,7 @@ scalar type `T`. function dual_objective_value(model::MOI.ModelLike, ci::MOI.ConstraintIndex, T::Type, - result_index::Int64) + result_index::Integer) return set_dot(constraint_constant(model, ci, T), MOI.get(model, MOI.ConstraintDual(result_index), ci), MOI.get(model, MOI.ConstraintSet(), ci)) @@ -63,7 +63,7 @@ function dual_objective_value(model::MOI.ModelLike, ci::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction, <:MOI.Interval}, T::Type, - result_index::Int64) + result_index::Integer) constant = scalar_constant(T, MOI.get(model, MOI.ConstraintFunction(), ci)) set = MOI.get(model, MOI.ConstraintSet(), ci) dual = MOI.get(model, MOI.ConstraintDual(result_index), ci) @@ -85,7 +85,7 @@ end F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, T::Type, - result_index::Int64) + result_index::Integer) Return the part of `DualObjectiveValue` due to `F`-in-`S` constraints using scalar type `T`. @@ -94,7 +94,7 @@ function dual_objective_value(model::MOI.ModelLike, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, T::Type, - result_index::Int64) + result_index::Integer) value = zero(T) # sum won't work if there are now constraints. for ci in MOI.get(model, MOI.ListOfConstraintIndices{F, S}()) value += dual_objective_value(model, ci, T, result_index) @@ -106,7 +106,7 @@ function dual_objective_value(model::MOI.ModelLike, F::Type{MOI.VectorOfVariables}, S::Type{<:MOI.AbstractVectorSet}, T::Type, - result_index::Int64) + result_index::Integer) # No constant in the function nor set so no contribution to the dual # objective value. return zero(T) From 1fe14b4f028c10261b9d88490dc22d4f3237d29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sun, 21 Apr 2019 00:21:57 +0200 Subject: [PATCH 12/13] Add in doc --- docs/src/apireference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 2a592ea5f3..2e30149b98 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -100,6 +100,7 @@ ResultCount ObjectiveFunction ObjectiveFunctionType ObjectiveValue +DualObjectiveValue ObjectiveBound RelativeGap SolveTime From 1e9be178bae3cefef926e83d2f2ca31655fbcfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Sat, 27 Apr 2019 23:48:10 +0200 Subject: [PATCH 13/13] Address comments --- src/Utilities/results.jl | 5 +++-- src/attributes.jl | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 3f387bf4ff..88d10a50f2 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -95,7 +95,7 @@ function dual_objective_value(model::MOI.ModelLike, S::Type{<:MOI.AbstractSet}, T::Type, result_index::Integer) - value = zero(T) # sum won't work if there are now constraints. + value = zero(T) # sum won't work if there are no constraints. for ci in MOI.get(model, MOI.ListOfConstraintIndices{F, S}()) value += dual_objective_value(model, ci, T, result_index) end @@ -121,7 +121,8 @@ end get_fallback(model::MOI.ModelLike, ::MOI.DualObjectiveValue, T::Type)::T Compute the dual objective value of type `T` using the `ConstraintDual` results -and the `ConstraintFunction` and `ConstraintSet` values. +and the `ConstraintFunction` and `ConstraintSet` values. Note that the nonlinear +part of the model is ignored. """ function get_fallback(model::MOI.ModelLike, attr::MOI.DualObjectiveValue, T::Type) value = zero(T) # sum will not work if there are zero constraints diff --git a/src/attributes.jl b/src/attributes.jl index 1e957c8c79..220990f7bc 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -472,7 +472,7 @@ end DualObjectiveValue(result_index::Int=1) A model attribute for the value of the objective function of the dual problem -for the `resultindex`th dual result. +for the `result_index`th dual result. """ struct DualObjectiveValue <: AbstractModelAttribute result_index::Int