From 938eb1cdc43a6af21adfadded29a5c60fc4435d7 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Sun, 5 Oct 2025 19:40:06 -0300 Subject: [PATCH 01/11] start print tests --- test/jump_tests.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 4a5bb004..7776ff49 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1675,3 +1675,8 @@ function test_jump_errors() ) return end + +function test_print() + + return +end \ No newline at end of file From f5daebec56e74369d848e93381251e80ac0f0164 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Sun, 5 Oct 2025 19:53:08 -0300 Subject: [PATCH 02/11] add test --- test/jump_tests.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 7776ff49..888eab0a 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1677,6 +1677,13 @@ function test_jump_errors() end function test_print() - + model = Model(() -> POI.Optimizer(GLPK.Optimizer())) + @variable(model, p in MOI.Parameter(1.0)) + @variable(model, x) + @constraint(model, con, x >= p) + @objective(model, Min, x) + filename = tempdir() * "/test.lp" + write_to_file(model, filename) + readlines(filename) |> println return end \ No newline at end of file From ae4e9089cf993089df38a66057b23bde587f80ea Mon Sep 17 00:00:00 2001 From: joaquimg Date: Tue, 7 Oct 2025 04:41:17 -0300 Subject: [PATCH 03/11] fix ListOfConstraintIndices --- src/MOI_wrapper.jl | 21 +++++++++++---------- test/jump_tests.jl | 8 ++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index b9a0ff4c..f54e0d14 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -805,6 +805,7 @@ function MOI.modify( c::MOI.ConstraintIndex{F,S}, chg::MOI.ScalarCoefficientChange{T}, ) where {F,S,T} + # TODO - outer or inner? if haskey(model.quadratic_constraint_cache, c) || haskey(model.affine_constraint_cache, c) error( @@ -1365,15 +1366,15 @@ function MOI.get( model::Optimizer, ::MOI.ListOfConstraintAttributesSet{F,S}, ) where {F,S} - if F <: MOI.ScalarQuadraticFunction - error( - "MOI.ListOfConstraintAttributesSet is not implemented for ScalarQuadraticFunction in ParametricOptInterface.", - ) - elseif F <: MOI.VectorQuadraticFunction - error( - "MOI.ListOfConstraintAttributesSet is not implemented for VectorQuadraticFunction in ParametricOptInterface.", - ) - end + # if F <: MOI.ScalarQuadraticFunction + # error( + # "MOI.ListOfConstraintAttributesSet is not implemented for ScalarQuadraticFunction in ParametricOptInterface.", + # ) + # elseif F <: MOI.VectorQuadraticFunction + # error( + # "MOI.ListOfConstraintAttributesSet is not implemented for VectorQuadraticFunction in ParametricOptInterface.", + # ) + # end return MOI.get(model.optimizer, MOI.ListOfConstraintAttributesSet{F,S}()) end @@ -1404,7 +1405,7 @@ function MOI.get( model::Optimizer, ::MOI.ListOfConstraintIndices{F,S}, ) where {S,F} - list = collect(values(model.constraint_outer_to_inner[F, S])) + list = collect(keys(model.constraint_outer_to_inner[F, S])) sort!(list, lt = (x, y) -> (x.value < y.value)) return list end diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 888eab0a..755a5c6c 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1677,13 +1677,13 @@ function test_jump_errors() end function test_print() - model = Model(() -> POI.Optimizer(GLPK.Optimizer())) + model = direct_model(POI.Optimizer(HiGHS.Optimizer())) @variable(model, p in MOI.Parameter(1.0)) @variable(model, x) - @constraint(model, con, x >= p) - @objective(model, Min, x) + @constraint(model, con, x >= p + p * p + p * x) + @objective(model, Min, 1 + 2x) filename = tempdir() * "/test.lp" write_to_file(model, filename) - readlines(filename) |> println + @test readlines(filename) == ["minimize", "obj: 1 + 2 x", "subject to", "c1: 1 x - 1 p + [ -1 p * x - 1 p ^ 2 ] >= 0", "Bounds", "x free", "p = 1", "End"] return end \ No newline at end of file From 42be2ce5c56396f494db62cd684b16558560b7d5 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Tue, 7 Oct 2025 23:41:42 -0300 Subject: [PATCH 04/11] fix attributes conditionals and printing --- src/MOI_wrapper.jl | 96 +++++++++++++++++++++++++++++------ src/ParametricOptInterface.jl | 3 ++ test/jump_tests.jl | 46 ++++++++++++++--- 3 files changed, 123 insertions(+), 22 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index f54e0d14..ed4bb876 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -566,8 +566,11 @@ function MOI.supports( # We can't tell at type-time whether the constraints will be quadratic or # lowered to affine, so we return the conservative choice for supports of # needing to support names for both quadratic and affine constraints. - return MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{F,S}) && - MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{G,S}) + if MOI.supports_constraint(model.optimizer, F, S) + return MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{F,S}) && + MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{G,S}) + end + return MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{G,S}) end function MOI.supports( @@ -579,8 +582,14 @@ function MOI.supports( # We can't tell at type-time whether the constraints will be quadratic or # lowered to affine, so we return the conservative choice for supports of # needing to support names for both quadratic and affine constraints. - return MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{F,S}) && - MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{G,S}) + # TODO: + # switch to only check support name for the case of linear + # is a solver does not support quadratic constraints it will fain in add_ + if MOI.supports_constraint(model.optimizer, F, S) + return MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{F,S}) && + MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{G,S}) + end + return MOI.supports(model.optimizer, attr, MOI.ConstraintIndex{G,S}) end function MOI.set( @@ -805,9 +814,9 @@ function MOI.modify( c::MOI.ConstraintIndex{F,S}, chg::MOI.ScalarCoefficientChange{T}, ) where {F,S,T} - # TODO - outer or inner? - if haskey(model.quadratic_constraint_cache, c) || - haskey(model.affine_constraint_cache, c) + if haskey(model.quadratic_outer_to_inner, c) || + haskey(model.vector_quadratic_outer_to_inner, c) || + haskey(model.affine_outer_to_inner, c) error( "Parametric constraint cannot be modified in ParametricOptInterface, because it would conflict with parameter updates. You can update the parameters instead.", ) @@ -1362,19 +1371,50 @@ function MOI.get(model::Optimizer, ::MOI.ListOfVariableAttributesSet) return MOI.get(model.optimizer, MOI.ListOfVariableAttributesSet()) end +function MOI.get( + model::Optimizer, + ::MOI.ListOfConstraintAttributesSet{F,S}, +) where {T,F<:MOI.ScalarQuadraticFunction{T},S} + if MOI.supports_constraint(model.optimizer, F, S) + # in this case we cant tell if the constraint will be quadratic or + # lowered to affine + if model.warn_quad_affine_ambiguous + println( + "MOI.ListOfConstraintAttributesSet is not supported for ScalarQuadraticFunction in ParametricOptInterface, an empty list will be returned. This message can be suppressed by setting `POI._WarnIfQuadraticOfAffineFunctionAmbiguous` to false.", + ) + end + return [] + end + return MOI.get( + model.optimizer, + MOI.ListOfConstraintAttributesSet{MOI.ScalarAffineFunction{T},S}(), + ) +end + +function MOI.get( + model::Optimizer, + ::MOI.ListOfConstraintAttributesSet{F,S}, +) where {T,F<:MOI.VectorQuadraticFunction{T},S} + if MOI.supports_constraint(model.optimizer, F, S) + # in this case we cant tell if the constraint will be quadratic or + # lowered to affine + if model.warn_quad_affine_ambiguous + println( + "MOI.ListOfConstraintAttributesSet is not supported for VectorQuadraticFunction in ParametricOptInterface, an empty list will be returned. This message can be suppressed by setting `POI._WarnIfQuadraticOfAffineFunctionAmbiguous` to false.", + ) + end + return [] + end + return MOI.get( + model.optimizer, + MOI.ListOfConstraintAttributesSet{MOI.VectorAffineFunction{T},S}(), + ) +end + function MOI.get( model::Optimizer, ::MOI.ListOfConstraintAttributesSet{F,S}, ) where {F,S} - # if F <: MOI.ScalarQuadraticFunction - # error( - # "MOI.ListOfConstraintAttributesSet is not implemented for ScalarQuadraticFunction in ParametricOptInterface.", - # ) - # elseif F <: MOI.VectorQuadraticFunction - # error( - # "MOI.ListOfConstraintAttributesSet is not implemented for VectorQuadraticFunction in ParametricOptInterface.", - # ) - # end return MOI.get(model.optimizer, MOI.ListOfConstraintAttributesSet{F,S}()) end @@ -2115,3 +2155,27 @@ end function MOI.Utilities.final_touch(model::Optimizer, index_map) return MOI.Utilities.final_touch(model.optimizer, index_map) end + +""" + _WarnIfQuadraticOfAffineFunctionAmbiguous + +Some attributes such as `MOI.ListOfConstraintAttributesSet` are ambiguous +when the model contains parametric quadratic functions that can be lowered +to affine functions. This attribute can be set to `false` to skip the warning +when such ambiguity arises. The default value is `true`. +""" +struct _WarnIfQuadraticOfAffineFunctionAmbiguous <: + MOI.AbstractOptimizerAttribute end + +function MOI.set( + model::Optimizer, + ::_WarnIfQuadraticOfAffineFunctionAmbiguous, + value::Bool, +) + model.warn_quad_affine_ambiguous = value + return +end + +function MOI.get(model::Optimizer, ::_WarnIfQuadraticOfAffineFunctionAmbiguous) + return model.warn_quad_affine_ambiguous +end diff --git a/src/ParametricOptInterface.jl b/src/ParametricOptInterface.jl index 4c08b86d..5c2080ad 100644 --- a/src/ParametricOptInterface.jl +++ b/src/ParametricOptInterface.jl @@ -179,6 +179,8 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer parameters_in_conflict::Set{MOI.VariableIndex} + warn_quad_affine_ambiguous::Bool + # extension data ext::Dict{Symbol,Any} function Optimizer{T}( @@ -243,6 +245,7 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer ONLY_CONSTRAINTS, save_original_objective_and_constraints, Set{MOI.VariableIndex}(), + true, Dict{Symbol,Any}(), ) end diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 755a5c6c..d3cad950 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1659,20 +1659,45 @@ function test_jump_errors() backend(model1), MOI.NLPBlock(), ) - @test_throws ErrorException MOI.get( + + MOI.get( backend(model1), MOI.ListOfConstraintAttributesSet{ MOI.VectorQuadraticFunction{Float64}, - MOI.Nonpositives, + MOI.Nonnegatives, }(), ) - @test_throws ErrorException MOI.get( + + MOI.get( backend(model1), MOI.ListOfConstraintAttributesSet{ MOI.ScalarQuadraticFunction{Float64}, - MOI.EqualTo{Float64}, + MOI.LessThan{Float64}, }(), ) + + MOI.set( + backend(model1), + POI._WarnIfQuadraticOfAffineFunctionAmbiguous(), + false, + ) + + MOI.get( + backend(model1), + MOI.ListOfConstraintAttributesSet{ + MOI.VectorQuadraticFunction{Float64}, + MOI.Nonnegatives, + }(), + ) + + MOI.get( + backend(model1), + MOI.ListOfConstraintAttributesSet{ + MOI.ScalarQuadraticFunction{Float64}, + MOI.LessThan{Float64}, + }(), + ) + return end @@ -1684,6 +1709,15 @@ function test_print() @objective(model, Min, 1 + 2x) filename = tempdir() * "/test.lp" write_to_file(model, filename) - @test readlines(filename) == ["minimize", "obj: 1 + 2 x", "subject to", "c1: 1 x - 1 p + [ -1 p * x - 1 p ^ 2 ] >= 0", "Bounds", "x free", "p = 1", "End"] + @test readlines(filename) == [ + "minimize", + "obj: 1 + 2 x", + "subject to", + "c1: 1 x - 1 p + [ -1 p * x - 1 p ^ 2 ] >= 0", + "Bounds", + "x free", + "p = 1", + "End", + ] return -end \ No newline at end of file +end From 26d7992e96bd3e34559d6836d54dc2e738575a01 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Wed, 8 Oct 2025 00:02:25 -0300 Subject: [PATCH 05/11] fix con name --- test/jump_tests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 67d7bddc..0be81d61 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1713,7 +1713,7 @@ function test_print() "minimize", "obj: 1 + 2 x", "subject to", - "c1: 1 x - 1 p + [ -1 p * x - 1 p ^ 2 ] >= 0", + "con: 1 x - 1 p + [ -1 p * x - 1 p ^ 2 ] >= 0", "Bounds", "x free", "p = 1", From 01130d9f94c6ff33eea30dc6075b4d381ab3aad3 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Wed, 8 Oct 2025 00:40:28 -0300 Subject: [PATCH 06/11] add more tests --- test/jump_tests.jl | 65 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 0be81d61..84ca0d54 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1654,14 +1654,60 @@ function test_jump_errors() SCS.Optimizer(), ) optimizer1 = POI.Optimizer(cached1) - model1 = direct_model(optimizer1) + model = direct_model(optimizer1) @test_throws MOI.UnsupportedAttribute MOI.get( - backend(model1), + backend(model), MOI.NLPBlock(), ) MOI.get( - backend(model1), + backend(model), + MOI.ListOfConstraintAttributesSet{ + MOI.VectorQuadraticFunction{Float64}, + MOI.Nonnegatives, + }(), + ) + + MOI.get( + backend(model), + MOI.ListOfConstraintAttributesSet{ + MOI.ScalarQuadraticFunction{Float64}, + MOI.LessThan{Float64}, + }(), + ) + + MOI.set( + backend(model), + POI._WarnIfQuadraticOfAffineFunctionAmbiguous(), + false, + ) + + @test MOI.get( + backend(model), + POI._WarnIfQuadraticOfAffineFunctionAmbiguous(), + ) == false + + MOI.get( + backend(model), + MOI.ListOfConstraintAttributesSet{ + MOI.VectorQuadraticFunction{Float64}, + MOI.Nonnegatives, + }(), + ) + + MOI.get( + backend(model), + MOI.ListOfConstraintAttributesSet{ + MOI.ScalarQuadraticFunction{Float64}, + MOI.LessThan{Float64}, + }(), + ) + + model = Model(() -> ParametricOptInterface.Optimizer(Ipopt.Optimizer())) + + + MOI.get( + backend(model), MOI.ListOfConstraintAttributesSet{ MOI.VectorQuadraticFunction{Float64}, MOI.Nonnegatives, @@ -1669,7 +1715,7 @@ function test_jump_errors() ) MOI.get( - backend(model1), + backend(model), MOI.ListOfConstraintAttributesSet{ MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, @@ -1677,13 +1723,18 @@ function test_jump_errors() ) MOI.set( - backend(model1), + backend(model), POI._WarnIfQuadraticOfAffineFunctionAmbiguous(), false, ) + @test MOI.get( + backend(model), + POI._WarnIfQuadraticOfAffineFunctionAmbiguous(), + ) == false + MOI.get( - backend(model1), + backend(model), MOI.ListOfConstraintAttributesSet{ MOI.VectorQuadraticFunction{Float64}, MOI.Nonnegatives, @@ -1691,7 +1742,7 @@ function test_jump_errors() ) MOI.get( - backend(model1), + backend(model), MOI.ListOfConstraintAttributesSet{ MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, From 4c7066893fc7d6e972ed147bce2b67b79ba4b721 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Wed, 8 Oct 2025 00:44:41 -0300 Subject: [PATCH 07/11] fmt --- test/jump_tests.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 84ca0d54..72bddda2 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1705,7 +1705,6 @@ function test_jump_errors() model = Model(() -> ParametricOptInterface.Optimizer(Ipopt.Optimizer())) - MOI.get( backend(model), MOI.ListOfConstraintAttributesSet{ From 59e5d64073c7d5c7e6c95d3dcef7c0f28b3bb3e7 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Wed, 8 Oct 2025 01:52:32 -0300 Subject: [PATCH 08/11] fix tests --- test/jump_tests.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 72bddda2..3bfb1b80 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1703,9 +1703,9 @@ function test_jump_errors() }(), ) - model = Model(() -> ParametricOptInterface.Optimizer(Ipopt.Optimizer())) + model = direct_model(POI.Optimizer(Ipopt.Optimizer())) - MOI.get( + @test_throws MOI.GetAttributeNotAllowed MOI.get( backend(model), MOI.ListOfConstraintAttributesSet{ MOI.VectorQuadraticFunction{Float64}, @@ -1732,7 +1732,7 @@ function test_jump_errors() POI._WarnIfQuadraticOfAffineFunctionAmbiguous(), ) == false - MOI.get( + @test_throws MOI.GetAttributeNotAllowed MOI.get( backend(model), MOI.ListOfConstraintAttributesSet{ MOI.VectorQuadraticFunction{Float64}, From 560a1907bfd95c861bc58328d08349e83efbd38c Mon Sep 17 00:00:00 2001 From: joaquimg Date: Thu, 30 Oct 2025 09:55:59 -0300 Subject: [PATCH 09/11] Add tests --- src/MOI_wrapper.jl | 2 +- test/jump_tests.jl | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index f91a2ddd..def730e6 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -813,7 +813,7 @@ end function MOI.modify( model::Optimizer, c::MOI.ConstraintIndex{F,S}, - chg::MOI.ScalarCoefficientChange{T}, + chg::Union{MOI.ScalarConstantChange{T},MOI.ScalarCoefficientChange{T}}, ) where {F,S,T} if haskey(model.quadratic_outer_to_inner, c) || haskey(model.vector_quadratic_outer_to_inner, c) || diff --git a/test/jump_tests.jl b/test/jump_tests.jl index 3bfb1b80..89353a29 100644 --- a/test/jump_tests.jl +++ b/test/jump_tests.jl @@ -1771,3 +1771,46 @@ function test_print() ] return end + +function test_set_normalized_coefficient() + model = direct_model(POI.Optimizer(HiGHS.Optimizer())) + @variable(model, p in MOI.Parameter(1.0)) + @variable(model, x) + @constraint(model, con, x >= p) + @constraint(model, con1, x >= 1) + @constraint(model, con2, x >= x * p) + @test_throws ErrorException set_normalized_coefficient(con, x, 2.0) + set_normalized_coefficient(con1, x, 2.0) + @test_throws ErrorException set_normalized_coefficient(con2, x, 2.0) + return +end + +function test_ListOfConstraintAttributesSet() + cached = MOI.Utilities.CachingOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + MOI.Utilities.AUTOMATIC, + ) + optimizer = POI.Optimizer(cached) + model = direct_model(optimizer) + @variable(model, p in MOI.Parameter(1.0)) + @variable(model, x) + @constraint(model, con, [x * p] in MOI.Nonnegatives(1)) + ret = get_attribute( + model, + MOI.ListOfConstraintAttributesSet{ + MOI.VectorQuadraticFunction{Float64}, + MOI.Nonnegatives, + }(), + ) + @test ret == [] + set_attribute(model, POI._WarnIfQuadraticOfAffineFunctionAmbiguous(), false) + ret = get_attribute( + model, + MOI.ListOfConstraintAttributesSet{ + MOI.VectorQuadraticFunction{Float64}, + MOI.Nonnegatives, + }(), + ) + @test ret == [] + return +end From 83612895977a379ad4b6ddf28034f5fa6ff96fe8 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 3 Nov 2025 01:33:29 -0300 Subject: [PATCH 10/11] Add tests and improve coverage --- src/MOI_wrapper.jl | 12 +-- test/moi_tests.jl | 218 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 214 insertions(+), 16 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index def730e6..167124bd 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -7,7 +7,7 @@ function _is_variable(v::MOI.VariableIndex) end function _is_parameter(v::MOI.VariableIndex) - return PARAMETER_INDEX_THRESHOLD < v.value < PARAMETER_INDEX_THRESHOLD_MAX + return PARAMETER_INDEX_THRESHOLD < v.value <= PARAMETER_INDEX_THRESHOLD_MAX end function _has_parameters(f::MOI.ScalarAffineFunction{T}) where {T} @@ -1042,8 +1042,6 @@ function MOI.add_constraint( model.constraint_outer_to_inner[outer_ci] = inner_ci model.quadratic_constraint_cache_set[inner_ci] = set return outer_ci - else - return _add_constraint_direct_and_cache_map!(model, f, set) end return _add_constraint_direct_and_cache_map!(model, f, set) else @@ -1536,8 +1534,12 @@ function MOI.get( return MOI.get(model.optimizer, attr) end -function MOI.supports(model::Optimizer, attr::MOI.AbstractConstraintAttribute) - return MOI.supports(model.optimizer, attr) +function MOI.supports( + model::Optimizer, + attr::MOI.AbstractConstraintAttribute, + ::Type{T}, +) where {T} + return MOI.supports(model.optimizer, attr, T) end function MOI.get( diff --git a/test/moi_tests.jl b/test/moi_tests.jl index 3430d61f..5dc5d1b6 100644 --- a/test/moi_tests.jl +++ b/test/moi_tests.jl @@ -294,9 +294,13 @@ function test_moi_ListOfConstraintTypesPresent() model = POI.Optimizer(ipopt) MOI.set(model, MOI.Silent(), true) x = MOI.add_variables(model, N / 2) - y = first.( - MOI.add_constrained_variable.(model, MOI.Parameter.(ones(Int(N / 2)))), - ) + y = + first.( + MOI.add_constrained_variable.( + model, + MOI.Parameter.(ones(Int(N / 2))), + ), + ) MOI.add_constraint( model, @@ -598,10 +602,11 @@ function test_vector_parameter_affine_nonnegatives() t, ct = MOI.add_constrained_variable(model, MOI.Parameter(5.0)) A = [1.0 0 -1; 0 1 -1] b = [1.0; 2] - terms = MOI.VectorAffineTerm.( - 1:2, - MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), - ) + terms = + MOI.VectorAffineTerm.( + 1:2, + MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), + ) f = MOI.VectorAffineFunction(vec(terms), b) set = MOI.Nonnegatives(2) cnn = MOI.add_constraint(model, f, MOI.Nonnegatives(2)) @@ -657,10 +662,11 @@ function test_vector_parameter_affine_nonpositives() t, ct = MOI.add_constrained_variable(model, MOI.Parameter(5.0)) A = [-1.0 0 1; 0 -1 1] b = [-1.0; -2] - terms = MOI.VectorAffineTerm.( - 1:2, - MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), - ) + terms = + MOI.VectorAffineTerm.( + 1:2, + MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), + ) f = MOI.VectorAffineFunction(vec(terms), b) set = MOI.Nonnegatives(2) cnn = MOI.add_constraint(model, f, MOI.Nonpositives(2)) @@ -2197,3 +2203,193 @@ function test_constrained_variables() MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func) return end + +function test_parameter_index_error() + model = POI.Optimizer(MOI.Utilities.Model{Float64}()) + POI._add_variable(model, MOI.VariableIndex(1)) + POI._add_variable(model, MOI.VariableIndex(POI.PARAMETER_INDEX_THRESHOLD)) + @test_throws ErrorException POI._add_variable( + model, + MOI.VariableIndex(POI.PARAMETER_INDEX_THRESHOLD + 1), + ) + @test_throws ErrorException POI._add_variable( + model, + MOI.VariableIndex(typemax(Int64)), + ) + return +end + +struct VariableAttributeForTest <: MOI.AbstractVariableAttribute end +struct ConstraintAttributeForTest <: MOI.AbstractConstraintAttribute end + +function test_variable_attribute_error() + solver = HiGHS.Optimizer() + MOI.set(solver, MOI.Silent(), true) + model = POI.Optimizer( + MOI.Utilities.CachingOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + MOI.Bridges.full_bridge_optimizer(solver, Float64), + ), + ) + x = MOI.add_variable(model) + MOI.set(model, VariableAttributeForTest(), x, 1.0) + p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + @test_throws ErrorException MOI.set( + model, + VariableAttributeForTest(), + p, + 1.0, + ) + @test_throws ErrorException MOI.get(model, VariableAttributeForTest(), p) + return +end + +function test_constraint_attribute_error() + solver = MOI.Utilities.Model{Float64}() + model = POI.Optimizer( + MOI.Utilities.CachingOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + MOI.Bridges.full_bridge_optimizer(solver, Float64), + ), + ) + MOI.supports( + model, + ConstraintAttributeForTest(), + MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}, + ) + @test_throws MOI.InvalidIndex MOI.set( + model, + ConstraintAttributeForTest(), + MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(1), + 1.0, + ) + return +end + +function test_name_from_bound() + solver = MOI.Utilities.Model{Float64}() + model = POI.Optimizer( + MOI.Utilities.CachingOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + MOI.Bridges.full_bridge_optimizer(solver, Float64), + ), + ) + x = MOI.add_variable(model) + p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + MOI.set(model, POI.ConstraintsInterpretation(), POI.ONLY_BOUNDS) + ci = MOI.add_constraint(model, x + 1.4 * p, MOI.LessThan(10.0)) + name = MOI.get(model, MOI.ConstraintName(), ci) + @test name == "ParametricBound_MathOptInterface.LessThan{Float64}_" + MOI.set(model, MOI.VariableName(), x, "x_var") + name = MOI.get(model, MOI.ConstraintName(), ci) + @test name == "ParametricBound_MathOptInterface.LessThan{Float64}_x_var" + return +end + +function test_get_constraint_set() + model = POI.Optimizer(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + ci = MOI.add_constraint(model, x + 1.4 * p, MOI.LessThan(10.0)) + set = MOI.get(model, MOI.ConstraintSet(), ci) + @test set isa MOI.LessThan{Float64} + return +end + +function test_quadratic_variable_parameter() + # model = POI.Optimizer(MOI.Utilities.Model{Float64}()) + solver = Ipopt.Optimizer() + MOI.set(solver, MOI.Silent(), true) + model = POI.Optimizer( + MOI.Utilities.CachingOptimizer( + MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), + MOI.Bridges.full_bridge_optimizer(solver, Float64), + ), + ) + x = MOI.add_variable(model) + p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + f = 1.0 * x * x - 2.0 * p * p + ci = MOI.add_constraint(model, MOI.Utilities.vectorize([f]), MOI.Zeros(1)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + obj_func = 1.0 * x + 2.0 * p + MOI.set(model, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func) + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.LOCALLY_SOLVED + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ sqrt(2) atol = 1e-4 + MOI.set(model, MOI.ConstraintSet(), pc, MOI.Parameter(2.0)) + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.LOCALLY_SOLVED + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ sqrt(8) atol = 1e-4 + MOI.set(model, MOI.ConstraintSet(), pc, MOI.Parameter(3.0)) + MOI.optimize!(model) + @test MOI.get(model, MOI.TerminationStatus()) == MOI.LOCALLY_SOLVED + @test MOI.get(model, MOI.VariablePrimal(), x) ≈ sqrt(18) atol = 1e-4 + return +end + +function test_error_modify_quadratic_objective() + model = POI.Optimizer(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + y = MOI.add_variable(model) + p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + quad_func = MOI.ScalarQuadraticFunction( + MOI.ScalarQuadraticTerm.([1.0, 1.0], [x, p], [x, x]), + MOI.ScalarAffineTerm.([2.0, 2.0], [x, y]), + 0.0, + ) + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + quad_func, + ) + @test_throws ErrorException MOI.modify( + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarConstantChange{Float64}(1.1), + ) + @test_throws ErrorException MOI.modify( + model, + MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), + MOI.ScalarCoefficientChange{Float64}(x, 2.2), + ) + return +end + +function test_list_of_parametric_constraint_types_present_conflict() + inner = MOI.Utilities.Model{Float64}() + mock = MOI.Utilities.MockOptimizer(inner; supports_names = false) + model = POI.Optimizer(MOI.Bridges.full_bridge_optimizer(mock, Float64)) + x = MOI.add_variable(model) + x_ci = MOI.add_constrained_variable(model, MOI.Parameter(2.0)) + p, p_ci = MOI.add_constrained_variable(model, MOI.Parameter(2.0)) + f = MOI.Utilities.vectorize([1.0 * x + 2.0 * p]) + ci = MOI.add_constraint(model, f, MOI.Nonpositives(1)) + @test MOI.get(model, POI.ListOfParametricConstraintTypesPresent()) == + Tuple{DataType,DataType,DataType}[( + MOI.VectorAffineFunction{Float64}, + MOI.Nonpositives, + POI.ParametricVectorAffineFunction{Float64}, + )] + return +end + +function test_get_constraint_function_vector() + model = POI.Optimizer(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + f = 1.0 * x * x - 2.0 * p * p + ci = MOI.add_constraint(model, MOI.Utilities.vectorize([f]), MOI.Zeros(1)) + f2 = MOI.get(model, MOI.ConstraintFunction(), ci) + @test canonical_compare(MOI.Utilities.vectorize([f]), f2) + return +end + +function test_multiplicative_dual_error() + model = POI.Optimizer(MOI.Utilities.Model{Float64}()) + x = MOI.add_variable(model) + p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) + f = 1.0 * x * p + ci = MOI.add_constraint(model, f, MOI.EqualTo{Float64}(0.0)) + @test_throws ErrorException MOI.get(model, MOI.ConstraintDual(), pc) + return +end From 94bd8c68226d606e417151b76251371cbf563a9b Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 3 Nov 2025 01:39:20 -0300 Subject: [PATCH 11/11] format --- test/moi_tests.jl | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/test/moi_tests.jl b/test/moi_tests.jl index 5dc5d1b6..9bae2f2f 100644 --- a/test/moi_tests.jl +++ b/test/moi_tests.jl @@ -294,13 +294,9 @@ function test_moi_ListOfConstraintTypesPresent() model = POI.Optimizer(ipopt) MOI.set(model, MOI.Silent(), true) x = MOI.add_variables(model, N / 2) - y = - first.( - MOI.add_constrained_variable.( - model, - MOI.Parameter.(ones(Int(N / 2))), - ), - ) + y = first.( + MOI.add_constrained_variable.(model, MOI.Parameter.(ones(Int(N / 2)))), + ) MOI.add_constraint( model, @@ -602,11 +598,10 @@ function test_vector_parameter_affine_nonnegatives() t, ct = MOI.add_constrained_variable(model, MOI.Parameter(5.0)) A = [1.0 0 -1; 0 1 -1] b = [1.0; 2] - terms = - MOI.VectorAffineTerm.( - 1:2, - MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), - ) + terms = MOI.VectorAffineTerm.( + 1:2, + MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), + ) f = MOI.VectorAffineFunction(vec(terms), b) set = MOI.Nonnegatives(2) cnn = MOI.add_constraint(model, f, MOI.Nonnegatives(2)) @@ -662,11 +657,10 @@ function test_vector_parameter_affine_nonpositives() t, ct = MOI.add_constrained_variable(model, MOI.Parameter(5.0)) A = [-1.0 0 1; 0 -1 1] b = [-1.0; -2] - terms = - MOI.VectorAffineTerm.( - 1:2, - MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), - ) + terms = MOI.VectorAffineTerm.( + 1:2, + MOI.ScalarAffineTerm.(A, reshape([x, y, t], 1, 3)), + ) f = MOI.VectorAffineFunction(vec(terms), b) set = MOI.Nonnegatives(2) cnn = MOI.add_constraint(model, f, MOI.Nonpositives(2))