diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 71ac674..c1f5f0c 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -1950,6 +1950,7 @@ end function MOI.optimize!(model::Optimizer) if !isempty(model.updated_parameters) + MOI.Utilities.final_touch(model, nothing) update_parameters!(model) end MOI.optimize!(model.optimizer) diff --git a/src/ParametricOptInterface.jl b/src/ParametricOptInterface.jl index ce0d928..5ade1c4 100644 --- a/src/ParametricOptInterface.jl +++ b/src/ParametricOptInterface.jl @@ -57,13 +57,17 @@ include("parametric_cubic_function.jl") evaluate_duals::Bool = true, save_original_objective_and_constraints::Bool = true, with_bridge_type = nothing, + with_cache_type = nothing, ) Create an `Optimizer`, which allows the handling of parameters in an optimization model. If `optimizer` is not a `MOI.ModelLike,` the inner optimizer is constructed -using `MOI.instantiate(optimizer; with_bridge_type)`. +using `MOI.instantiate(optimizer; with_cache_type)`. + +If `with_bridge_type !== nothing`, a `MOI.Bridges.full_bridge_optimizer` is +applied as an outer layer. The `{T}` type parameter is optional; it defaults to `Float64`. @@ -82,8 +86,9 @@ The `{T}` type parameter is optional; it defaults to `Float64`. Note that this might break printing or queries such as `MOI.get(model, MOI.ConstraintFunction(), c)`. Defaults to `true`. -- `with_bridge_type`: this is ignroed if `optimizer::MOI.ModelLike`, otherwise - it is passed to `MOI.instantiate`. +- `with_bridge_type`: the type passed to `MOI.Bridges.full_bridge_optimizer` + +- `with_cache_type`: the type passed to `MOI.instantiate` ## Example @@ -276,13 +281,24 @@ Optimizer(arg; kwargs...) = Optimizer{Float64}(arg; kwargs...) function Optimizer{T}( optimizer_fn; with_bridge_type = nothing, + with_cache_type = nothing, kwargs..., ) where {T} - inner = MOI.instantiate(optimizer_fn; with_bridge_type) + inner = MOI.instantiate(optimizer_fn; with_cache_type) if !MOI.supports_incremental_interface(inner) - cache = MOI.default_cache(inner, T) + # Don't use `default_cache` for the cache because, for example, SCS's + # default cache doesn't support modifying coefficients of the constraint + # matrix. JuMP uses the default cache with SCS because it has an outer + # layer of caching; we don't have that here, so we can't use the + # default. + # + # We could revert to using the default cache if we fix this in MOI. + cache = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{T}()) inner = MOI.Utilities.CachingOptimizer(cache, inner) end + if with_bridge_type !== nothing + inner = MOI.Bridges.full_bridge_optimizer(inner, with_bridge_type) + end return Optimizer{T}(inner; kwargs...) end diff --git a/test/Project.toml b/test/Project.toml index 4adebdb..3642db5 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -12,4 +12,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" HiGHS = "1" Ipopt = "1" JuMP = "1" -SCS = "1" +SCS = "2" diff --git a/test/test_JuMP.jl b/test/test_JuMP.jl index df6d176..7a1e6c2 100644 --- a/test/test_JuMP.jl +++ b/test/test_JuMP.jl @@ -834,12 +834,7 @@ function test_jump_dual_delete_constraint_2() end function test_jump_dual_delete_constraint_3() - cached = MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - SCS.Optimizer(), - ) - optimizer = POI.Optimizer(cached) - model = direct_model(optimizer) + model = direct_model(POI.Optimizer(SCS.Optimizer)) set_silent(model) list = [] @variable(model, α in Parameter(1.0)) @@ -1211,15 +1206,8 @@ function test_parameter_Cannot_be_inf_2() end function test_jump_psd_cone_with_parameter_pv() - cached = MOI.Bridges.full_bridge_optimizer( - MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - SCS.Optimizer(), - ), - Float64, - ) - optimizer = POI.Optimizer(cached) - model = direct_model(optimizer) + inner = POI.Optimizer(SCS.Optimizer; with_bridge_type = Float64) + model = direct_model(inner) set_silent(model) @variable(model, x) @variable(model, p in Parameter(1.0)) @@ -1406,18 +1394,8 @@ function test_jump_psd_cone_without_parameter_v_and_vv() end function test_variable_and_constraint_not_registered() - cached1 = MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - SCS.Optimizer(), - ) - optimizer1 = POI.Optimizer(cached1) - model1 = direct_model(optimizer1) - cached2 = MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - SCS.Optimizer(), - ) - optimizer2 = POI.Optimizer(cached2) - model2 = direct_model(optimizer2) + model1 = direct_model(POI.Optimizer(SCS.Optimizer)) + model2 = direct_model(POI.Optimizer(SCS.Optimizer)) set_silent(model1) set_silent(model2) @variable(model1, x) @@ -1505,12 +1483,7 @@ function test_variable_and_constraint_not_registered() end function test_jump_errors() - cached1 = MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - SCS.Optimizer(), - ) - optimizer1 = POI.Optimizer(cached1) - model = direct_model(optimizer1) + model = direct_model(POI.Optimizer(SCS.Optimizer)) @test_throws MOI.UnsupportedAttribute MOI.get( backend(model), MOI.NLPBlock(), diff --git a/test/test_MathOptInterface.jl b/test/test_MathOptInterface.jl index 8fc7167..82fc5ea 100644 --- a/test/test_MathOptInterface.jl +++ b/test/test_MathOptInterface.jl @@ -322,12 +322,10 @@ function test_moi_highs() end function test_moi_ipopt() - model = MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - MOI.Bridges.full_bridge_optimizer( - POI.Optimizer(Ipopt.Optimizer), - Float64, - ), + model = POI.Optimizer( + Ipopt.Optimizer; + with_bridge_type = Float64, + with_cache_type = Float64, ) MOI.set(model, MOI.Silent(), true) # Without fixed_variable_treatment set, duals are not computed for variables @@ -364,6 +362,7 @@ function test_moi_ipopt() # - CachingOptimizer does not throw if optimizer not attached "test_model_copy_to_UnsupportedAttribute", "test_model_copy_to_UnsupportedConstraint", + "test_model_ModelFilter_AbstractConstraintAttribute", # - POI only supports cubic polynomial ScalarNonlinearFunction "test_nonlinear_duals", "test_nonlinear_expression_", @@ -570,14 +569,7 @@ function test_production_problem_example_duals() end function test_production_problem_example_parameters_for_duals_and_intervals() - cached = MOI.Bridges.full_bridge_optimizer( - MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - HiGHS.Optimizer(), - ), - Float64, - ) - optimizer = POI.Optimizer(cached) + optimizer = POI.Optimizer(HiGHS.Optimizer; with_bridge_type = Float64) MOI.set(optimizer, MOI.Silent(), true) c = [4.0, 3.0] A1 = [2.0, 1.0, 3.0] @@ -639,14 +631,14 @@ function test_production_problem_example_parameters_for_duals_and_intervals() MOI.set(optimizer, MOI.ConstraintSet(), cz, MOI.Parameter(1.0)) MOI.optimize!(optimizer) @test ≈(MOI.get(optimizer, MOI.ObjectiveValue()), 7.0, atol = ATOL) - @test MOI.get.(optimizer, MOI.VariablePrimal(), x) == [0.0, 1.0] + @test MOI.get.(optimizer, MOI.VariablePrimal(), x) ≈ [0.0, 1.0] @test ≈(MOI.get(optimizer, MOI.ConstraintDual(), cy), 9.0, atol = ATOL) @test ≈(MOI.get(optimizer, MOI.ConstraintDual(), cz), 0.0, atol = ATOL) @test ≈(MOI.get(optimizer, MOI.ConstraintDual(), cw), -2.0, atol = ATOL) MOI.set(optimizer, MOI.ConstraintSet(), cw, MOI.Parameter(0.0)) MOI.optimize!(optimizer) @test ≈(MOI.get(optimizer, MOI.ObjectiveValue()), 3.0, atol = ATOL) - @test MOI.get.(optimizer, MOI.VariablePrimal(), x) == [0.0, 1.0] + @test MOI.get.(optimizer, MOI.VariablePrimal(), x) ≈ [0.0, 1.0] @test ≈(MOI.get(optimizer, MOI.ConstraintDual(), cy), 9.0, atol = ATOL) @test ≈(MOI.get(optimizer, MOI.ConstraintDual(), cz), 0.0, atol = ATOL) @test ≈(MOI.get(optimizer, MOI.ConstraintDual(), cw), -2.0, atol = ATOL) @@ -2035,6 +2027,7 @@ function test_psd_cone_with_parameter() @test MOI.get(model, MOI.ConstraintName(), c_index) == "" MOI.set(model, MOI.ConstraintName(), c_index, "psd_cone") @test MOI.get(model, MOI.ConstraintName(), c_index) == "psd_cone" + return end function test_copy_model() @@ -2125,15 +2118,13 @@ 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) +function test_AA_variable_attribute_error() model = POI.Optimizer( - MOI.Utilities.CachingOptimizer( - MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()), - MOI.Bridges.full_bridge_optimizer(solver, Float64), - ), + HiGHS.Optimizer; + with_bridge_type = Float64, + with_cache_type = Float64, ) + MOI.set(model, MOI.Silent(), true) x = MOI.add_variable(model) MOI.set(model, VariableAttributeForTest(), x, 1.0) p, pc = MOI.add_constrained_variable(model, MOI.Parameter(1.0)) @@ -2148,13 +2139,8 @@ function test_variable_attribute_error() 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), - ), - ) + model = + POI.Optimizer(MOI.Utilities.Model{Float64}; with_bridge_type = Float64) MOI.supports( model, ConstraintAttributeForTest(), @@ -2170,13 +2156,8 @@ function test_constraint_attribute_error() 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), - ), - ) + model = + POI.Optimizer(MOI.Utilities.Model{Float64}; with_bridge_type = 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) @@ -2200,15 +2181,12 @@ function test_get_constraint_set() 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), - ), + Ipopt.Optimizer; + with_bridge_type = Float64, + with_cache_type = Float64, ) + MOI.set(model, MOI.Silent(), true) 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