From 41fe3d46e8a07f8cb84b3f61f70802c7e5ab3f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 21 Jan 2025 19:58:52 +0100 Subject: [PATCH 1/6] Implement constrained variables --- src/MOI_wrapper.jl | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index f37e2db..2848007 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -196,14 +196,18 @@ function MOI.get(model::Optimizer, tp::Type{MOI.VariableIndex}, attr::String) return MOI.get(model.optimizer, tp, attr) end -function MOI.add_variable(model::Optimizer) +function _add_variable(model::Optimizer, inner_vi) _next_variable_index!(model) return MOI.Utilities.CleverDicts.add_item( model.variables, - MOI.add_variable(model.optimizer), + inner_vi, ) end +function MOI.add_variable(model::Optimizer) + return _add_variable(model, MOI.add_variable(model.optimizer)) +end + function MOI.supports_add_constrained_variable( ::Optimizer{T}, ::Type{MOI.Parameter{T}}, @@ -218,6 +222,20 @@ function MOI.supports_add_constrained_variables( return MOI.supports_add_constrained_variables(model.optimizer, MOI.Reals) end +function MOI.supports_add_constrained_variable( + model::Optimizer, + ::Type{S}, +) where {S<:MOI.AbstractScalarSet} + return MOI.supports_add_constrained_variable(model.optimizer, S) +end + +function MOI.supports_add_constrained_variables( + model::Optimizer, + ::Type{S}, +) where {S<:MOI.AbstractVectorSet} + return MOI.supports_add_constrained_variables(model.optimizer, S) +end + function MOI.add_constrained_variable( model::Optimizer{T}, set::MOI.Parameter{T}, @@ -234,6 +252,22 @@ function MOI.add_constrained_variable( return p, cp end +function MOI.add_constrained_variable( + model::Optimizer, + set::MOI.AbstractScalarSet, +) + inner_vi, inner_ci = MOI.add_constrained_variable(model.optimizer, set) + return _add_variable(model, inner_vi), inner_ci # FIXME map inner_ci +end + +function MOI.add_constrained_variables( + model::Optimizer, + set::MOI.AbstractVectorSet, +) + inner_vis, inner_ci = MOI.add_constrained_variables(model.optimizer, set) + return _add_variable.(Ref(model), inner_vis), inner_ci # FIXME map inner_ci +end + function _add_to_constraint_map!(model::Optimizer, ci) model.constraint_outer_to_inner[ci] = ci return From 3eb26142a85e4275a6b121ca4b41ad2af1e22485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Tue, 21 Jan 2025 22:07:07 +0100 Subject: [PATCH 2/6] Fix format --- src/MOI_wrapper.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 2848007..8587026 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -198,10 +198,7 @@ end function _add_variable(model::Optimizer, inner_vi) _next_variable_index!(model) - return MOI.Utilities.CleverDicts.add_item( - model.variables, - inner_vi, - ) + return MOI.Utilities.CleverDicts.add_item(model.variables, inner_vi) end function MOI.add_variable(model::Optimizer) From abbf63d82e6731e633a3457e59a95993812fb6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 22 Jan 2025 14:31:14 +0100 Subject: [PATCH 3/6] Add tests --- src/ParametricOptInterface.jl | 7 +++-- test/moi_tests.jl | 58 +++++++++++++++++++++++++++++++++++ test/no_free_model.jl | 40 ++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 test/no_free_model.jl diff --git a/src/ParametricOptInterface.jl b/src/ParametricOptInterface.jl index 1b94685..ef86790 100644 --- a/src/ParametricOptInterface.jl +++ b/src/ParametricOptInterface.jl @@ -169,12 +169,11 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer # extension data ext::Dict{Symbol,Any} - function Optimizer( + function Optimizer{T}( optimizer::OT; evaluate_duals::Bool = true, save_original_objective_and_constraints::Bool = true, - ) where {OT} - T = Float64 + ) where {T,OT} return new{T,OT}( optimizer, MOI.Utilities.CleverDicts.CleverDict{ParameterIndex,T}( @@ -231,6 +230,8 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer end end +Optimizer(args...; kws...) = Optimizer{Float64}(args..., kws...) + function _next_variable_index!(model::Optimizer) return model.last_variable_index_added += 1 end diff --git a/test/moi_tests.jl b/test/moi_tests.jl index 107dd44..7ab14c3 100644 --- a/test/moi_tests.jl +++ b/test/moi_tests.jl @@ -1910,6 +1910,7 @@ function test_no_quadratic_terms() @test c isa MOI.ConstraintIndex{typeof(func),typeof(set)} @test MOI.get(optimizer, MOI.ConstraintFunction(), c) ≈ func @test MOI.get(optimizer, MOI.ConstraintSet(), c) == set + @test MOI.supports(optimizer, MOI.ConstraintName(), typeof(c)) MOI.set(optimizer, MOI.ConstraintName(), c, "name") @test MOI.get(optimizer, MOI.ConstraintName(), c) == "name" MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE) @@ -1919,3 +1920,60 @@ function test_no_quadratic_terms() @test MOI.get(optimizer, MOI.ConstraintDual(), c) ≈ -1 atol = ATOL return end + +function test_constrained_variable() + optimizer = POI.Optimizer(GLPK.Optimizer()) + MOI.set(optimizer, MOI.Silent(), true) + set = MOI.LessThan(1.0) + @test MOI.supports_add_constrained_variable(optmizer, typeof(set)) + x, c = MOI.add_constrained_variable(optimizer, set) + @test c isa MOI.ConstraintIndex{typeof(x),typeof(set)} + @test MOI.get(optimizer, MOI.ConstraintFunction(), c) ≈ x + @test MOI.get(optimizer, MOI.ConstraintSet(), c) == set + @test MOI.supports(optimizer, MOI.VariableName(), typeof(x)) + MOI.set(optimizer, MOI.VariableName(), x, "vname") + @test MOI.get(optimizer, MOI.VariableName(), x) == "vname" + MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE) + obj_func = 1.0 * x + MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func) + MOI.optimize!(optimizer) + @test MOI.get(optimizer, MOI.ConstraintDual(), c) ≈ -1 atol = ATOL + @test MOI.get(optimizer, MOI.VariablePrimal(), x) ≈ 1 atol = ATOL + return +end + +include("no_free_model.jl") + +function test_constrained_variable() + optimizer = POI.Optimizer(NoFreeVariablesModel{Float64}()) + set = MOI.LessThan(1.0) + @test MOI.supports_add_constrained_variable(optimizer, typeof(set)) + x, c = MOI.add_constrained_variable(optimizer, set) + @test c isa MOI.ConstraintIndex{typeof(x),typeof(set)} + @test MOI.get(optimizer, MOI.ConstraintFunction(), c) ≈ x + @test MOI.get(optimizer, MOI.ConstraintSet(), c) == set + @test MOI.supports(optimizer, MOI.VariableName(), typeof(x)) + MOI.set(optimizer, MOI.VariableName(), x, "vname") + @test MOI.get(optimizer, MOI.VariableName(), x) == "vname" + MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE) + obj_func = 1.0 * x + MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func) + return +end + +function test_constrained_variables() + optimizer = POI.Optimizer{Int}(NoFreeVariablesModel{Int}()) + set = MOI.Nonnegatives(2) + @test MOI.supports_add_constrained_variables(optimizer, typeof(set)) + x, c = MOI.add_constrained_variables(optimizer, set) + @test c isa MOI.ConstraintIndex{MOI.VectorOfVariables,typeof(set)} + @test MOI.get(optimizer, MOI.ConstraintFunction(), c) ≈ MOI.VectorOfVariables(x) + @test MOI.get(optimizer, MOI.ConstraintSet(), c) == set + @test MOI.supports(optimizer, MOI.VariableName(), eltype(x)) + MOI.set(optimizer, MOI.VariableName(), x[1], "vname") + @test MOI.get(optimizer, MOI.VariableName(), x[1]) == "vname" + MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MIN_SENSE) + obj_func = 1 * x[1] + 1 * x[2] + MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func) + return +end diff --git a/test/no_free_model.jl b/test/no_free_model.jl new file mode 100644 index 0000000..c172a22 --- /dev/null +++ b/test/no_free_model.jl @@ -0,0 +1,40 @@ +MOI.Utilities.@model( + NoFreeVariablesModel, + (), + (), + (MOI.Nonnegatives,), + (), + (), + (), + (MOI.VectorOfVariables,), + (), +) + +function MOI.supports_constraint( + ::NoFreeVariablesModel, + ::Type{MOI.VectorOfVariables}, + ::Type{MOI.Reals}, +) + return false +end + +function MOI.supports_add_constrained_variable( + ::NoFreeVariablesModel{T}, + ::Type{MOI.LessThan{T}}, +) where {T} + return true +end + +function MOI.supports_add_constrained_variables( + ::NoFreeVariablesModel, + ::Type{MOI.Nonnegatives}, +) + return true +end + +function MOI.supports_add_constrained_variables( + ::NoFreeVariablesModel, + ::Type{MOI.Reals}, +) + return false +end From a9266f7678a9641ba390daa9618a6042ea04596d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 22 Jan 2025 14:31:50 +0100 Subject: [PATCH 4/6] Fix format --- test/moi_tests.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/moi_tests.jl b/test/moi_tests.jl index 7ab14c3..9e7f3bc 100644 --- a/test/moi_tests.jl +++ b/test/moi_tests.jl @@ -1967,7 +1967,8 @@ function test_constrained_variables() @test MOI.supports_add_constrained_variables(optimizer, typeof(set)) x, c = MOI.add_constrained_variables(optimizer, set) @test c isa MOI.ConstraintIndex{MOI.VectorOfVariables,typeof(set)} - @test MOI.get(optimizer, MOI.ConstraintFunction(), c) ≈ MOI.VectorOfVariables(x) + @test MOI.get(optimizer, MOI.ConstraintFunction(), c) ≈ + MOI.VectorOfVariables(x) @test MOI.get(optimizer, MOI.ConstraintSet(), c) == set @test MOI.supports(optimizer, MOI.VariableName(), eltype(x)) MOI.set(optimizer, MOI.VariableName(), x[1], "vname") From 69385708861fb453b823c7f014cbaa82a2a2cf95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 22 Jan 2025 19:35:38 +0100 Subject: [PATCH 5/6] Fix --- src/ParametricOptInterface.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ParametricOptInterface.jl b/src/ParametricOptInterface.jl index ef86790..75bb691 100644 --- a/src/ParametricOptInterface.jl +++ b/src/ParametricOptInterface.jl @@ -230,7 +230,7 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer end end -Optimizer(args...; kws...) = Optimizer{Float64}(args..., kws...) +Optimizer(args...; kws...) = Optimizer{Float64}(args...; kws...) function _next_variable_index!(model::Optimizer) return model.last_variable_index_added += 1 From ce4ce348761d50d70057350f8fe07a3e7074be0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 22 Jan 2025 19:52:51 +0100 Subject: [PATCH 6/6] Fix --- test/moi_tests.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/moi_tests.jl b/test/moi_tests.jl index 9e7f3bc..93948dd 100644 --- a/test/moi_tests.jl +++ b/test/moi_tests.jl @@ -1910,7 +1910,6 @@ function test_no_quadratic_terms() @test c isa MOI.ConstraintIndex{typeof(func),typeof(set)} @test MOI.get(optimizer, MOI.ConstraintFunction(), c) ≈ func @test MOI.get(optimizer, MOI.ConstraintSet(), c) == set - @test MOI.supports(optimizer, MOI.ConstraintName(), typeof(c)) MOI.set(optimizer, MOI.ConstraintName(), c, "name") @test MOI.get(optimizer, MOI.ConstraintName(), c) == "name" MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)