From 199bfe7e94c243b5f6a18395db882d7c71d5b7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 3 Apr 2026 18:40:35 +0200 Subject: [PATCH 1/2] Add support for - in convert --- src/functions.jl | 33 ++++++++++++++++++++++++++++++--- test/General/test_functions.jl | 11 +++++++++++ test/param.jl | 21 +++++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 test/param.jl diff --git a/src/functions.jl b/src/functions.jl index f5bf984029..b8058a5cb5 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -1086,9 +1086,27 @@ end _add_to_function(::ScalarAffineFunction, ::Any) = nothing -# This is a very rough-and-ready conversion function that only works for very -# basic expressions, such as those created by -# `convert(ScalarNonlinearFunction, f)`. +function _to_scalar_affine(::Type{ScalarAffineFunction{T}}, x::Real) where {T} + return ScalarAffineFunction{T}(ScalarAffineTerm{T}[], T(x)) +end + +function _to_scalar_affine( + ::Type{ScalarAffineFunction{T}}, + x::VariableIndex, +) where {T} + return ScalarAffineFunction{T}(x) +end + +function _to_scalar_affine( + ::Type{ScalarAffineFunction{T}}, + f::ScalarNonlinearFunction, +) where {T} + return convert(ScalarAffineFunction{T}, f) +end + +# This conversion function handles expressions created by +# `convert(ScalarNonlinearFunction, f)` and also expressions using `:+`, `:-`, +# and `:*` operators with affine structure. function Base.convert( ::Type{ScalarAffineFunction{T}}, f::ScalarNonlinearFunction, @@ -1097,6 +1115,15 @@ function Base.convert( term = convert(ScalarAffineTerm{T}, f) return ScalarAffineFunction{T}([term], zero(T)) end + if f.head == :- && length(f.args) == 2 + lhs = _to_scalar_affine(ScalarAffineFunction{T}, f.args[1]) + rhs = _to_scalar_affine(ScalarAffineFunction{T}, f.args[2]) + return Utilities.operate(-, T, lhs, rhs) + end + if f.head == :- && length(f.args) == 1 + inner = _to_scalar_affine(ScalarAffineFunction{T}, f.args[1]) + return Utilities.operate(-, T, inner) + end if f.head != :+ throw(InexactError(:convert, ScalarAffineFunction{T}, f)) end diff --git a/test/General/test_functions.jl b/test/General/test_functions.jl index fdea07e6c1..24894d3c0f 100644 --- a/test/General/test_functions.jl +++ b/test/General/test_functions.jl @@ -371,6 +371,17 @@ function test_convert_ScalarNonlinearFunction_ScalarAffineFunction() convert(MOI.ScalarAffineFunction{Float64}, f_error), ) end + # Test :- support (binary subtraction and unary negation) + f_sub = MOI.ScalarNonlinearFunction(:-, Any[x, 1.0]) + @test convert(MOI.ScalarAffineFunction{Float64}, f_sub) ≈ 1.0 * x - 1.0 + f_sub2 = MOI.ScalarNonlinearFunction(:-, Any[ + MOI.ScalarNonlinearFunction(:+, Any[x, y]), + 2.0, + ]) + @test convert(MOI.ScalarAffineFunction{Float64}, f_sub2) ≈ + 1.0 * x + 1.0 * y - 2.0 + f_neg = MOI.ScalarNonlinearFunction(:-, Any[x]) + @test convert(MOI.ScalarAffineFunction{Float64}, f_neg) ≈ -1.0 * x return end diff --git a/test/param.jl b/test/param.jl new file mode 100644 index 0000000000..c268e08b10 --- /dev/null +++ b/test/param.jl @@ -0,0 +1,21 @@ +import MathOptInterface as MOI + +model = MOI.instantiate( + MOI.FileFormats.CBF.Model{Float64}, + with_bridge_type = Float64, +) +x = MOI.add_variable(model) +p, _ = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) +MOI.add_constraint(model, 1.0x * p, MOI.LessThan(1.0)) + +model = Model(() -> DiffOpt.diff_optimizer(Ipopt.Optimizer)) +set_silent(model) + +p_val = 4.0 +pc_val = 2.0 +@variable(model, x) +@variable(model, p in Parameter(p_val)) +@variable(model, pc in Parameter(pc_val)) +@constraint(model, cons, pc * x >= 3 * p) +@objective(model, Min, x^4) +optimize!(model) From 905ea3d21987610fc27971bcd9e88a8a1cc4bd75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Fri, 3 Apr 2026 20:33:36 +0200 Subject: [PATCH 2/2] Fix --- test/General/test_functions.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/General/test_functions.jl b/test/General/test_functions.jl index 24894d3c0f..adbb29e140 100644 --- a/test/General/test_functions.jl +++ b/test/General/test_functions.jl @@ -374,10 +374,10 @@ function test_convert_ScalarNonlinearFunction_ScalarAffineFunction() # Test :- support (binary subtraction and unary negation) f_sub = MOI.ScalarNonlinearFunction(:-, Any[x, 1.0]) @test convert(MOI.ScalarAffineFunction{Float64}, f_sub) ≈ 1.0 * x - 1.0 - f_sub2 = MOI.ScalarNonlinearFunction(:-, Any[ - MOI.ScalarNonlinearFunction(:+, Any[x, y]), - 2.0, - ]) + f_sub2 = MOI.ScalarNonlinearFunction( + :-, + Any[MOI.ScalarNonlinearFunction(:+, Any[x, y]), 2.0], + ) @test convert(MOI.ScalarAffineFunction{Float64}, f_sub2) ≈ 1.0 * x + 1.0 * y - 2.0 f_neg = MOI.ScalarNonlinearFunction(:-, Any[x])