diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 981135ab02..151debdce4 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -108,6 +108,29 @@ function _dual_objective_value( return set_dot(constant, dual, set) end +function _dual_objective_value( + model::MOI.ModelLike, + ci::MOI.ConstraintIndex{<:MOI.AbstractVectorFunction,<:MOI.HyperRectangle}, + ::Type{T}, + result_index::Integer, +) where {T} + func_constant = + MOI.constant(MOI.get(model, MOI.ConstraintFunction(), ci), T) + set = MOI.get(model, MOI.ConstraintSet(), ci) + dual = MOI.get(model, MOI.ConstraintDual(result_index), ci) + constant = map(eachindex(func_constant)) do i + return func_constant[i] - if dual[i] < zero(dual[i]) + # 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 + set.upper[i] + else + # the lower bound is tight + set.lower[i] + end + end + return set_dot(constant, dual, set) +end + function _dual_objective_value( model::MOI.ModelLike, ::Type{F}, @@ -116,23 +139,18 @@ function _dual_objective_value( result_index::Integer, ) where {T,F<:MOI.AbstractFunction,S<:MOI.AbstractSet} value = zero(T) + if F == variable_function_type(S) && !_has_constant(S) + return value # Shortcut + end for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) value += _dual_objective_value(model, ci, T, result_index) end return value end -function _dual_objective_value( - ::MOI.ModelLike, - ::Type{MOI.VectorOfVariables}, - ::Type{<:MOI.AbstractVectorSet}, - ::Type{T}, - ::Integer, -) where {T} - # No constant in the function nor set so no contribution to the dual - # objective value. - return zero(T) -end +_has_constant(::Type{<:MOI.AbstractScalarSet}) = true +_has_constant(::Type{<:MOI.AbstractVectorSet}) = false +_has_constant(::Type{<:MOI.HyperRectangle}) = true """ get_fallback( diff --git a/test/Utilities/results.jl b/test/Utilities/results.jl new file mode 100644 index 0000000000..5286604f7d --- /dev/null +++ b/test/Utilities/results.jl @@ -0,0 +1,56 @@ +# Copyright (c) 2017: Miles Lubin and contributors +# Copyright (c) 2017: Google Inc. +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +module TestResults + +using Test + +import MathOptInterface as MOI + +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end + +test_hyperrectangle_Int() = _test_hyperrectangle(Int) + +test_hyperrectangle_Float64() = _test_hyperrectangle(Float64) + +function _test_hyperrectangle(T) + model = MOI.Utilities.MockOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{T}()), + T, + ) + x = MOI.add_variables(model, 2) + c1 = MOI.add_constraint( + model, + MOI.VectorOfVariables(x), + MOI.HyperRectangle(T[3, -7], T[5, -2]), + ) + c2 = MOI.add_constraint( + model, + MOI.Utilities.vectorize(x .+ T[11, 13]), + MOI.HyperRectangle(T[-T(6), -T(4)], [T(3), T(2)]), + ) + MOI.set(model, MOI.ConstraintDual(), c1, T[4, -3]) + MOI.set(model, MOI.ConstraintDual(), c2, T[-2, 5]) + @test -53 == @inferred MOI.Utilities.get_fallback( + model, + MOI.DualObjectiveValue(), + T, + ) + return +end + +end # module TestResults + +TestResults.runtests()