From 495688e1963175e4e26a5ea81866ff8400653bcc Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 28 Aug 2025 10:58:52 +1200 Subject: [PATCH] Update set_dot for sets with non-zero constants --- src/Utilities/results.jl | 130 ++++++++------------------------------- src/Utilities/set_dot.jl | 39 ++++++++++++ 2 files changed, 63 insertions(+), 106 deletions(-) diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index 21380315d5..48f471efe1 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -52,112 +52,6 @@ end # MOI.DualObjectiveValue -function _constraint_constant( - model::MOI.ModelLike, - ci::MOI.ConstraintIndex{ - <:MOI.AbstractVectorFunction, - <:MOI.AbstractVectorSet, - }, - ::Type{T}, -) where {T} - return MOI.constant(MOI.get(model, MOI.ConstraintFunction(), ci), T) -end - -function _constraint_constant( - model::MOI.ModelLike, - ci::MOI.ConstraintIndex{ - <:MOI.AbstractScalarFunction, - <:MOI.AbstractScalarSet, - }, - ::Type{T}, -) where {T} - return MOI.constant(MOI.get(model, MOI.ConstraintFunction(), ci), T) - - MOI.constant(MOI.get(model, MOI.ConstraintSet(), ci)) -end - -function _dual_objective_value( - model::MOI.ModelLike, - ci::MOI.ConstraintIndex, - ::Type{T}, - result_index::Integer, -) where {T} - return set_dot( - _constraint_constant(model, ci, T), - MOI.get(model, MOI.ConstraintDual(result_index), ci), - MOI.get(model, MOI.ConstraintSet(), ci), - ) -end - -""" -Given lower <= f(x) <= upper [dual], return the expression to be multiplied by -the dual variable. This is one of the following cases: - - 1. f(x) - lower: if `lower > -Inf` and the lower bound is binding (either no - `upper` or `dual > 0`) - 2. f(x) - upper: if `upper < Inf` and the upper bound is binding (either no - `lower` or `dual < 0`) - 3. f(x): if `lower = -Inf` and `upper = Inf` or `dual = 0` -""" -function _constant_minus_bound(constant, lower, upper, dual) - if isfinite(lower) && (!isfinite(upper) || dual > zero(dual)) - return constant - lower - elseif isfinite(upper) && (!isfinite(lower) || dual < zero(dual)) - return constant - upper - else - return constant - end -end - -function _dual_objective_value( - model::MOI.ModelLike, - ci::MOI.ConstraintIndex{<:MOI.AbstractScalarFunction,<:MOI.Interval}, - ::Type{T}, - result_index::Integer, -) where {T} - 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 = _constant_minus_bound(constant, set.lower, set.upper, dual) - 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) - constants = map(enumerate(func_constant)) do (i, c) - return _constant_minus_bound(c, set.lower[i], set.upper[i], dual[i]) - end - return set_dot(constants, dual, set) -end - -function _dual_objective_value( - model::MOI.ModelLike, - ::Type{F}, - ::Type{S}, - ::Type{T}, - 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 - -_has_constant(::Type{<:MOI.AbstractScalarSet}) = true -_has_constant(::Type{<:MOI.AbstractVectorSet}) = false -_has_constant(::Type{<:MOI.HyperRectangle}) = true - """ get_fallback( model::MOI.ModelLike, @@ -192,6 +86,30 @@ function get_fallback( return value::T end +function _dual_objective_value( + model::MOI.ModelLike, + ::Type{F}, + ::Type{S}, + ::Type{T}, + 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 + end + for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) + constants = MOI.constant(MOI.get(model, MOI.ConstraintFunction(), ci), T) + dual = MOI.get(model, MOI.ConstraintDual(result_index), ci) + set = MOI.get(model, MOI.ConstraintSet(), ci) + value += set_dot(constants, dual, set) + end + return value +end + +_has_constant(::Type{<:MOI.AbstractScalarSet}) = true +_has_constant(::Type{<:MOI.AbstractVectorSet}) = false +_has_constant(::Type{<:MOI.HyperRectangle}) = true + # MOI.ConstraintPrimal """ diff --git a/src/Utilities/set_dot.jl b/src/Utilities/set_dot.jl index 77ebd8cb1d..648457a5f9 100644 --- a/src/Utilities/set_dot.jl +++ b/src/Utilities/set_dot.jl @@ -26,6 +26,27 @@ Return the scalar product between a number `x` of the set `set` and a number """ set_dot(x, y, ::MOI.AbstractScalarSet) = LinearAlgebra.dot(x, y) +function set_dot( + x::Real, + y::Real, + set::Union{MOI.EqualTo,MOI.GreaterThan,MOI.LessThan}, +) + return (x - MOI.constant(set)) * y +end + +function set_dot( + x::Real, + y::Real, + set::MOI.Interval, +) + if isfinite(set.lower) && (!isfinite(set.upper) || y > zero(y)) + return (x - set.lower) * y + elseif isfinite(upper) && (!isfinite(set.lower) || y < zero(y)) + return (x - set.upper) * y + end + return x * y +end + function triangle_dot( x::AbstractVector{S}, y::AbstractVector{T}, @@ -86,6 +107,24 @@ function set_dot( return x[1] * y[1] + x[2] * y[2] + triangle_dot(x, y, set.side_dimension, 2) end +function set_dot( + x::AbstractVector{A}, + y::AbstractVector{B}, + set::MOI.HyperRectangle{C}, +) where {A,B,C} + ret = zero(promote_type(A, B, C)) + for (xi, yi, li, ui) in zip(x, y, set.lower, set.upper) + if isfinite(li) && (!isfinite(ui) || yi > zero(yi)) + ret += (xi - li) * yi + elseif isfinite(upper) && (!isfinite(li) || yi < zero(yi)) + ret += (xi - ui) * yi + else + ret += xi * yi + end + end + return ret +end + """ dot_coefficients(a::AbstractVector, set::AbstractVectorSet)