From 61a5ec7c2cb3c247c2d592e0a4b883ad748c6da1 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 8 Sep 2025 12:33:52 +1200 Subject: [PATCH 1/2] [Bridges] restrict when ToScalarNonlinearBridge applies and simplify tests --- .../bridges/FunctionConversionBridge.jl | 3 +- .../Constraint/ScalarFunctionizeBridge.jl | 14 ++ .../Objective/ToScalarNonlinearBridge.jl | 165 +++++------------- 3 files changed, 57 insertions(+), 125 deletions(-) diff --git a/src/Bridges/Objective/bridges/FunctionConversionBridge.jl b/src/Bridges/Objective/bridges/FunctionConversionBridge.jl index 28cf74bd49..b04ce5c59c 100644 --- a/src/Bridges/Objective/bridges/FunctionConversionBridge.jl +++ b/src/Bridges/Objective/bridges/FunctionConversionBridge.jl @@ -45,7 +45,8 @@ function supports_objective_function( ::Type{<:FunctionConversionBridge{T,F}}, ::Type{G}, ) where {T,F,G<:MOI.AbstractFunction} - return isfinite(MOI.Bridges.Constraint.conversion_cost(F, G)) + return MOI.Utilities.is_coefficient_type(G, T) && + isfinite(MOI.Bridges.Constraint.conversion_cost(F, G)) end function MOI.Bridges.bridging_cost( diff --git a/test/Bridges/Constraint/ScalarFunctionizeBridge.jl b/test/Bridges/Constraint/ScalarFunctionizeBridge.jl index 6dee66b370..04a9e2f363 100644 --- a/test/Bridges/Constraint/ScalarFunctionizeBridge.jl +++ b/test/Bridges/Constraint/ScalarFunctionizeBridge.jl @@ -364,6 +364,20 @@ function test_approx_convert(T = Float64) return end +function test_supports_ScalarNonlinearFunction() + for T in (Int, Float64) + model = MOI.instantiate(MOI.Utilities.Model{T}; with_bridge_type = T) + for (F, flag) in [ + MOI.ScalarNonlinearFunction => true, + MOI.ScalarAffineFunction{Float64} => (T == Float64), + MOI.ScalarAffineFunction{Int} => (T == Int), + ] + @test MOI.supports_constraint(model, F, MOI.EqualTo{T}) == flag + end + end + return +end + end # module TestConstraintFunctionize.runtests() diff --git a/test/Bridges/Objective/ToScalarNonlinearBridge.jl b/test/Bridges/Objective/ToScalarNonlinearBridge.jl index 2a33005fb8..6289cf3f60 100644 --- a/test/Bridges/Objective/ToScalarNonlinearBridge.jl +++ b/test/Bridges/Objective/ToScalarNonlinearBridge.jl @@ -21,140 +21,57 @@ function runtests() return end -include("../utilities.jl") - -function test_solve_singlevariable_obj() - mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) - model = MOI.Bridges.Objective.ToScalarNonlinear{Float64}(mock) - MOI.Utilities.set_mock_optimize!( - mock, - (mock::MOI.Utilities.MockOptimizer) -> - MOI.Utilities.mock_optimize!(mock, [1.0], MOI.FEASIBLE_POINT), - ) - MOI.Test.test_objective_ObjectiveFunction_duplicate_terms( - model, - MOI.Test.Config(; - exclude = Any[MOI.DualObjectiveValue, MOI.ConstraintDual], - ), - ) - @test MOI.get(mock, MOI.ObjectiveFunctionType()) == - MOI.ScalarNonlinearFunction - @test MOI.get(model, MOI.ObjectiveFunctionType()) == - MOI.ScalarAffineFunction{Float64} - @test MOI.get(mock, MOI.ObjectiveSense()) == MOI.MIN_SENSE - @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE - vis = MOI.get(model, MOI.ListOfVariableIndices()) - func = 3.0 * vis[1] + 0.0 - - @test MOI.get( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - ) ≈ func - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - @test MOI.get(mock, MOI.ObjectiveSense()) == MOI.MAX_SENSE - @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE - - _test_delete_objective(model, 1, tuple()) - return -end - -function test_solve_result_index() - mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}()) - model = MOI.Bridges.Objective.ToScalarNonlinear{Float64}(mock) - MOI.Utilities.set_mock_optimize!( - mock, - (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!( - mock, - MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0]), - MOI.FEASIBLE_POINT, - (MOI.VariableIndex, MOI.GreaterThan{Float64}) => [1.0], - ), - ) - MOI.Test.test_solve_result_index( - model, - MOI.Test.Config(; - exclude = Any[MOI.DualObjectiveValue, MOI.ConstraintDual], - ), - ) - - return -end - function test_runtests() MOI.Bridges.runtests( MOI.Bridges.Objective.ToScalarNonlinearBridge, - model -> begin - x = MOI.add_variable(model) - aff = MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([2.0], [x]), - 1.0, - ) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - aff, - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - end, - model -> begin - x = MOI.add_variable(model) - exp = MOI.ScalarNonlinearFunction( - :+, - [ - MOI.ScalarNonlinearFunction( - :*, - [2.0, MOI.VariableIndex(1)], - ), - 1.0, - ], - ) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction}(), - exp, - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) - end, + """ + variables: x + minobjective: 2.0 * x + 1.0 + """, + """ + variables: x + minobjective: ScalarNonlinearFunction(2.0 * x + 1.0) + """, + ) + MOI.Bridges.runtests( + MOI.Bridges.Objective.ToScalarNonlinearBridge, + """ + variables: x + maxobjective: 2.0 * x + 1.0 + """, + """ + variables: x + maxobjective: ScalarNonlinearFunction(2.0 * x + 1.0) + """, ) MOI.Bridges.runtests( MOI.Bridges.Objective.ToScalarNonlinearBridge, - model -> begin - x = MOI.add_variable(model) - aff = MOI.ScalarAffineFunction( - MOI.ScalarAffineTerm.([2.0], [x]), - 1.0, - ) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - aff, - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - end, - model -> begin - x = MOI.add_variable(model) - exp = MOI.ScalarNonlinearFunction( - :+, - [ - MOI.ScalarNonlinearFunction( - :*, - [2.0, MOI.VariableIndex(1)], - ), - 1.0, - ], - ) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction}(), - exp, - ) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - end, + """ + variables: x, y + minobjective: 1.0 * x * x + 2.0 * x * y + 3.0 * y + 4.0 + """, + """ + variables: x, y + minobjective: ScalarNonlinearFunction(1.0 * x * x + 2.0 * x * y + 3.0 * y + 4.0) + """, ) return end +function test_supports() + for T in (Int, Float64) + model = MOI.instantiate(MOI.Utilities.Model{T}; with_bridge_type = T) + for (F, flag) in [ + MOI.ScalarNonlinearFunction => true, + MOI.ScalarAffineFunction{Float64} => (T == Float64), + MOI.ScalarAffineFunction{Int} => (T == Int), + ] + @test MOI.supports(model, MOI.ObjectiveFunction{F}()) == flag + end + end + return +end + end # module TestObjectiveToScalarNonlinear.runtests() From ede681fe25534b385f284da9f1ac43ba344ab970 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 8 Sep 2025 13:28:57 +1200 Subject: [PATCH 2/2] Update --- test/Bridges/Objective/FunctionConversionBridge.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/Bridges/Objective/FunctionConversionBridge.jl b/test/Bridges/Objective/FunctionConversionBridge.jl index 02504aaf3f..8b40871f14 100644 --- a/test/Bridges/Objective/FunctionConversionBridge.jl +++ b/test/Bridges/Objective/FunctionConversionBridge.jl @@ -28,6 +28,13 @@ struct VariableDifference <: MOI.AbstractScalarFunction y::MOI.VariableIndex end +function MOI.Utilities.is_coefficient_type( + ::Type{VariableDifference}, + ::Type{T}, +) where {T} + return true +end + function MOI.Bridges.Constraint.conversion_cost( ::Type{<:MOI.ScalarAffineFunction}, ::Type{VariableDifference},