diff --git a/src/Utilities/results.jl b/src/Utilities/results.jl index d1f199334d..d5d86ab368 100644 --- a/src/Utilities/results.jl +++ b/src/Utilities/results.jl @@ -52,6 +52,26 @@ end # MOI.DualObjectiveValue +function _validate_function( + ::Type{F}, + attr, + key, +) where { + F<:Union{ + MOI.VariableIndex, + MOI.ScalarAffineFunction, + MOI.VectorOfVariables, + MOI.VectorAffineFunction, + }, +} + return nothing +end + +function _validate_function(::Type{F}, attr, key) where {F} + msg = "Cannot get the dual objective with $key function of type `$F`" + return throw(MOI.GetAttributeNotAllowed(attr, msg)) +end + """ get_fallback( model::MOI.ModelLike, @@ -75,15 +95,10 @@ function get_fallback( throw(MOI.GetAttributeNotAllowed(attr, "No dual solution is available")) end F = MOI.get(model, MOI.ObjectiveFunctionType()) - if !(F <: Union{MOI.VariableIndex,MOI.ScalarAffineFunction}) - err = MOI.GetAttributeNotAllowed( - attr, - "Cannot get the dual objective with an objective function of type `$F`", - ) - throw(err) - end + _validate_function(F, attr, "an objective") value = zero(T) # sum will not work if there are zero constraints for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent()) + _validate_function(F, attr, "a constraint") value += _dual_objective_value(model, F, S, T, attr.result_index)::T end if MOI.get(model, MOI.ObjectiveSense()) != MOI.MAX_SENSE diff --git a/test/Utilities/test_results.jl b/test/Utilities/test_results.jl index 6b0c685dda..3ddd8ab59f 100644 --- a/test/Utilities/test_results.jl +++ b/test/Utilities/test_results.jl @@ -305,16 +305,59 @@ function test_get_fallback_DualObjectiveValue_Quadratic() MOI.set(model, MOI.PrimalStatus(), MOI.FEASIBLE_POINT) MOI.set(model, MOI.DualStatus(), MOI.FEASIBLE_POINT) MOI.set(model, MOI.VariablePrimal(), x, 0.0) + msg = "Cannot get the dual objective with an objective function of type `$(typeof(f))`" @test_throws( - MOI.GetAttributeNotAllowed( - MOI.DualObjectiveValue(), - "Cannot get the dual objective with an objective function of type `$(typeof(f))`", - ), + MOI.GetAttributeNotAllowed(MOI.DualObjectiveValue(), msg), MOI.Utilities.get_fallback(model, MOI.DualObjectiveValue(), T), ) return end +function test_get_fallback_scalar_nonlinear() + inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + model = MOI.Utilities.MockOptimizer(inner, Float64) + x = MOI.add_variable(model) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + f = 1.0 * x + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + g = MOI.ScalarNonlinearFunction(:-, Any[x, 1.0]) + c = MOI.add_constraint(model, g, MOI.GreaterThan(0.0)) + MOI.set(model, MOI.ResultCount(), 1) + MOI.set(model, MOI.PrimalStatus(), MOI.FEASIBLE_POINT) + MOI.set(model, MOI.DualStatus(), MOI.FEASIBLE_POINT) + MOI.set(model, MOI.VariablePrimal(), x, 1.0) + MOI.set(model, MOI.ConstraintDual(), c, 1.0) + msg = "Cannot get the dual objective with a constraint function of type `$(typeof(g))`" + @test_throws( + MOI.GetAttributeNotAllowed(MOI.DualObjectiveValue(), msg), + MOI.Utilities.get_fallback(model, MOI.DualObjectiveValue(), Float64), + ) + return +end + +function test_get_fallback_vector_nonlinear() + inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()) + model = MOI.Utilities.MockOptimizer(inner, Float64) + x = MOI.add_variable(model) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + f = 1.0 * x + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + g1 = MOI.ScalarNonlinearFunction(:-, Any[x, 1.0]) + g = MOI.VectorNonlinearFunction([g1]) + c = MOI.add_constraint(model, g, MOI.Nonnegatives(1)) + MOI.set(model, MOI.ResultCount(), 1) + MOI.set(model, MOI.PrimalStatus(), MOI.FEASIBLE_POINT) + MOI.set(model, MOI.DualStatus(), MOI.FEASIBLE_POINT) + MOI.set(model, MOI.VariablePrimal(), x, 1.0) + MOI.set(model, MOI.ConstraintDual(), c, 1.0) + msg = "Cannot get the dual objective with a constraint function of type `$(typeof(g))`" + @test_throws( + MOI.GetAttributeNotAllowed(MOI.DualObjectiveValue(), msg), + MOI.Utilities.get_fallback(model, MOI.DualObjectiveValue(), Float64), + ) + return +end + end # module TestResults TestResults.runtests()