From 31a258f7540a75be796d0397ef6d36634d8cf730 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Tue, 17 Feb 2026 00:03:10 -0300 Subject: [PATCH 1/2] Remove QuadraticObjectiveCoef --- src/MOI_wrapper.jl | 134 +------------------------------- src/ParametricOptInterface.jl | 11 --- test/test_JuMP.jl | 76 ------------------ test/test_MathOptInterface.jl | 142 ---------------------------------- 4 files changed, 1 insertion(+), 362 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 60060f21..4b7813d3 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -135,7 +135,6 @@ function MOI.is_empty(model::Optimizer) model.affine_objective_cache === nothing && model.quadratic_objective_cache === nothing && MOI.is_empty(model.original_objective_cache) && - isempty(model.quadratic_objective_cache_product) && # isempty(model.vector_affine_constraint_cache) && # @@ -172,7 +171,6 @@ function MOI.empty!(model::Optimizer{T}) where {T} model.affine_objective_cache = nothing model.quadratic_objective_cache = nothing MOI.empty!(model.original_objective_cache) - empty!(model.quadratic_objective_cache_product) # empty!(model.vector_affine_constraint_cache) # @@ -1232,8 +1230,7 @@ function MOI.modify( chg::Union{MOI.ScalarConstantChange{T},MOI.ScalarCoefficientChange{T}}, ) where {F<:MathOptInterface.AbstractScalarFunction,T} if model.quadratic_objective_cache !== nothing || - model.affine_objective_cache !== nothing || - !isempty(model.quadratic_objective_cache_product) + model.affine_objective_cache !== nothing error( "A parametric objective cannot be modified as it would conflict with the parameter update mechanism. Please set a new objective or use parameters to perform such updates.", ) @@ -1946,128 +1943,6 @@ function MOI.set( return model.constraints_interpretation = value end -struct QuadraticObjectiveCoef <: MOI.AbstractModelAttribute end - -function _set_quadratic_product_in_obj!(model::Optimizer{T}) where {T} - n = length(model.quadratic_objective_cache_product) - - f = if model.affine_objective_cache !== nothing - _current_function(model.affine_objective_cache) - elseif model.quadratic_objective_cache !== nothing - _current_function(model.quadratic_objective_cache) - else - F = MOI.get(model.original_objective_cache, MOI.ObjectiveFunctionType()) - MOI.get(model.original_objective_cache, MOI.ObjectiveFunction{F}()) - end - F = typeof(f) - - quadratic_prods_vector = MOI.ScalarQuadraticTerm{T}[] - sizehint!(quadratic_prods_vector, n) - - for ((x, y), fparam) in model.quadratic_objective_cache_product - # x, y = prod_var - evaluated_fparam = _evaluate_parametric_expression(model, fparam) - push!( - quadratic_prods_vector, - MOI.ScalarQuadraticTerm(evaluated_fparam, x, y), - ) - end - - f_new = if F <: MOI.VariableIndex - MOI.ScalarQuadraticFunction( - quadratic_prods_vector, - MOI.ScalarAffineTerm{T}[MOI.ScalarAffineTerm{T}(1.0, f)], - 0.0, - ) - elseif F <: MOI.ScalarAffineFunction{T} - MOI.ScalarQuadraticFunction(quadratic_prods_vector, f.terms, f.constant) - elseif F <: MOI.ScalarQuadraticFunction{T} - quadratic_terms = vcat(f.quadratic_terms, quadratic_prods_vector) - MOI.ScalarQuadraticFunction(quadratic_terms, f.affine_terms, f.constant) - end - - MOI.set( - model.optimizer, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}(), - f_new, - ) - - return -end - -function _evaluate_parametric_expression(model::Optimizer, p::MOI.VariableIndex) - return model.parameters[p_idx(p)] -end - -function _evaluate_parametric_expression( - model::Optimizer, - fparam::MOI.ScalarAffineFunction{T}, -) where {T} - constant = fparam.constant - terms = fparam.terms - evaluated_parameter_expression = zero(T) - for term in terms - coef = term.coefficient - p = term.variable - evaluated_parameter_expression += coef * model.parameters[p_idx(p)] - evaluated_parameter_expression += constant - end - return evaluated_parameter_expression -end - -function MOI.set( - model::Optimizer, - ::QuadraticObjectiveCoef, - (x1, x2)::Tuple{MOI.VariableIndex,MOI.VariableIndex}, - ::Nothing, -) - if x1.value > x2.value - aux = x1 - x1 = x2 - x2 = aux - end - delete!(model.quadratic_objective_cache_product, (x1, x2)) - model.quadratic_objective_cache_product_changed = true - return -end - -function MOI.set( - model::Optimizer, - ::QuadraticObjectiveCoef, - (x1, x2)::Tuple{MOI.VariableIndex,MOI.VariableIndex}, - f_param::Union{MOI.VariableIndex,MOI.ScalarAffineFunction{T}}, -) where {T} - if x1.value > x2.value - aux = x1 - x1 = x2 - x2 = aux - end - model.quadratic_objective_cache_product[(x1, x2)] = f_param - model.quadratic_objective_cache_product_changed = true - return -end - -function MOI.get( - model::Optimizer, - ::QuadraticObjectiveCoef, - (x1, x2)::Tuple{MOI.VariableIndex,MOI.VariableIndex}, -) - if x1.value > x2.value - aux = x1 - x1 = x2 - x2 = aux - end - if haskey(model.quadratic_objective_cache_product, (x1, x2)) - return model.quadratic_objective_cache_product[(x1, x2)] - else - throw( - ErrorException( - "Parameter not set in product of variables ($x1,$x2)", - ), - ) - end -end - # # Optimize # @@ -2076,13 +1951,6 @@ function MOI.optimize!(model::Optimizer) if !isempty(model.updated_parameters) update_parameters!(model) end - if ( - !isempty(model.quadratic_objective_cache_product) || - model.quadratic_objective_cache_product_changed - ) - model.quadratic_objective_cache_product_changed = false - _set_quadratic_product_in_obj!(model) - end MOI.optimize!(model.optimizer) if MOI.get(model, MOI.DualStatus()) != MOI.NO_SOLUTION && model.evaluate_duals diff --git a/src/ParametricOptInterface.jl b/src/ParametricOptInterface.jl index d8fdde27..1a3406d8 100644 --- a/src/ParametricOptInterface.jl +++ b/src/ParametricOptInterface.jl @@ -196,12 +196,6 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer quadratic_objective_cache::Union{Nothing,ParametricQuadraticFunction{T}} cubic_objective_cache::Union{Nothing,ParametricCubicFunction{T}} original_objective_cache::MOI.Utilities.ObjectiveContainer{T} - # Store parametric expressions for product of variables - quadratic_objective_cache_product::Dict{ - Tuple{MOI.VariableIndex,MOI.VariableIndex}, - MOI.AbstractFunction, - } - quadratic_objective_cache_product_changed::Bool # vector affine function data # vector_constraint_cache::DoubleDict{Vector{MOI.VectorAffineTerm{T}}} @@ -273,11 +267,6 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer nothing, nothing, # cubic_objective_cache MOI.Utilities.ObjectiveContainer{T}(), - Dict{ - Tuple{MOI.VariableIndex,MOI.VariableIndex}, - MOI.AbstractFunction, - }(), - false, # vec affine # DoubleDict{Vector{MOI.VectorAffineTerm{T}}}(), DoubleDict{ParametricVectorAffineFunction{T}}(), diff --git a/test/test_JuMP.jl b/test/test_JuMP.jl index 71572806..098cfc93 100644 --- a/test/test_JuMP.jl +++ b/test/test_JuMP.jl @@ -964,82 +964,6 @@ function test_jump_direct_soc_parameters() return end -function test_jump_direct_qp_objective() - optimizer = POI.Optimizer(Ipopt.Optimizer) - model = direct_model(optimizer) - MOI.set(model, MOI.Silent(), true) - @variable(model, x >= 0) - @variable(model, y >= 0) - @variable(model, p in MOI.Parameter(1.0)) - @constraint(model, 2x + y <= 4) - @constraint(model, x + 2y <= 4) - @objective(model, Max, (x^2 + y^2) / 2) - optimize!(model) - @test objective_value(model) ≈ 16 / 9 atol = ATOL - @test value(x) ≈ 4 / 3 atol = ATOL - @test value(y) ≈ 4 / 3 atol = ATOL - MOI.set( - backend(model), - POI.QuadraticObjectiveCoef(), - (index(x), index(y)), - 2index(p) + 3, - ) - optimize!(model) - @test canonical_compare( - MOI.get( - backend(model), - POI.QuadraticObjectiveCoef(), - (index(x), index(y)), - ), - MOI.ScalarAffineFunction{Int64}( - MOI.ScalarAffineTerm{Int64}[MOI.ScalarAffineTerm{Int64}( - 2, - MOI.VariableIndex(POI.PARAMETER_INDEX_THRESHOLD + 1), - )], - 3, - ), - ) - @test objective_value(model) ≈ 32 / 3 atol = ATOL - @test value(x) ≈ 4 / 3 atol = ATOL - @test value(y) ≈ 4 / 3 atol = ATOL - MOI.set(model, POI.ParameterValue(), p, 2.0) - optimize!(model) - @test objective_value(model) ≈ 128 / 9 atol = ATOL - @test value(x) ≈ 4 / 3 atol = ATOL - @test value(y) ≈ 4 / 3 atol = ATOL - MOI.set( - backend(model), - POI.QuadraticObjectiveCoef(), - (index(x), index(y)), - nothing, - ) - optimize!(model) - @test objective_value(model) ≈ 16 / 9 atol = ATOL - @test value(x) ≈ 4 / 3 atol = ATOL - @test value(y) ≈ 4 / 3 atol = ATOL - # now in reverse order - MOI.set( - backend(model), - POI.QuadraticObjectiveCoef(), - (index(y), index(x)), - 2index(p) + 3, - ) - optimize!(model) - @test objective_value(model) ≈ 128 / 9 atol = ATOL - @test value(x) ≈ 4 / 3 atol = ATOL - @test value(y) ≈ 4 / 3 atol = ATOL - MOI.set( - backend(model), - POI.QuadraticObjectiveCoef(), - (index(y), index(x)), - nothing, - ) - optimize!(model) - @test objective_value(model) ≈ 16 / 9 atol = ATOL - @test value(x) ≈ 4 / 3 atol = ATOL - @test value(y) ≈ 4 / 3 atol = ATOL - return -end function test_jump_direct_rsoc_constraints() """ diff --git a/test/test_MathOptInterface.jl b/test/test_MathOptInterface.jl index 8b9f0415..c567c1bb 100644 --- a/test/test_MathOptInterface.jl +++ b/test/test_MathOptInterface.jl @@ -1525,148 +1525,6 @@ function test_qp_objective_affine_parameter() return end -function test_qp_objective_parameter_in_quadratic_part() - model = POI.Optimizer(Ipopt.Optimizer) - MOI.set(model, MOI.Silent(), true) - x = MOI.add_variable(model) - y = MOI.add_variable(model) - z = MOI.add_variable(model) - p = first(MOI.add_constrained_variable.(model, MOI.Parameter(1.0))) - MOI.add_constraint(model, x, MOI.GreaterThan(0.0)) - MOI.add_constraint(model, y, MOI.GreaterThan(0.0)) - cons1 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], [x, y]), 0.0) - ci1 = MOI.add_constraint(model, cons1, MOI.LessThan(4.0)) - cons2 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], [x, y]), 0.0) - ci2 = MOI.add_constraint(model, cons2, MOI.LessThan(4.0)) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - obj_func = MOI.ScalarQuadraticFunction( - [ - MOI.ScalarQuadraticTerm(1.0, x, x) - MOI.ScalarQuadraticTerm(1.0, y, y) - ], - MOI.ScalarAffineTerm{Float64}[], - 0.0, - ) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}(), - obj_func, - ) - MOI.set(model, POI.QuadraticObjectiveCoef(), (x, y), 2p + 3) - @test MOI.get(model, POI.QuadraticObjectiveCoef(), (x, y)) ≈ - MOI.ScalarAffineFunction{Int64}( - MOI.ScalarAffineTerm{Int64}[MOI.ScalarAffineTerm{Int64}( - 2, - MOI.VariableIndex(POI.PARAMETER_INDEX_THRESHOLD + 1), - )], - 3, - ) - @test_throws ErrorException MOI.get( - model, - POI.QuadraticObjectiveCoef(), - (x, z), - ) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 32 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - MOI.set(model, POI.ParameterValue(), p, 2.0) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 128 / 9 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - model = POI.Optimizer(Ipopt.Optimizer) - MOI.set(model, MOI.Silent(), true) - x = MOI.add_variable(model) - y = MOI.add_variable(model) - p = first(MOI.add_constrained_variable.(model, MOI.Parameter(1.0))) - MOI.add_constraint(model, x, MOI.GreaterThan(0.0)) - MOI.add_constraint(model, y, MOI.GreaterThan(0.0)) - cons1 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], [x, y]), 0.0) - ci1 = MOI.add_constraint(model, cons1, MOI.LessThan(4.0)) - cons2 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], [x, y]), 0.0) - ci2 = MOI.add_constraint(model, cons2, MOI.LessThan(4.0)) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - obj_func = MOI.ScalarAffineFunction( - [ - MOI.ScalarAffineTerm(1.0, x) - MOI.ScalarAffineTerm(2.0, y) - ], - 1.0, - ) - MOI.set( - model, - MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), - obj_func, - ) - MOI.set(model, POI.QuadraticObjectiveCoef(), (x, y), p) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 61 / 9 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, POI.QuadraticObjectiveCoef(), (x, y)) ≈ - MOI.VariableIndex(POI.PARAMETER_INDEX_THRESHOLD + 1) - MOI.set(model, POI.ParameterValue(), p, 2.0) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 77 / 9 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - model = POI.Optimizer(Ipopt.Optimizer) - MOI.set(model, MOI.Silent(), true) - x = MOI.add_variable(model) - y = MOI.add_variable(model) - p = first(MOI.add_constrained_variable.(model, MOI.Parameter(1.0))) - MOI.add_constraint(model, x, MOI.GreaterThan(0.0)) - MOI.add_constraint(model, y, MOI.GreaterThan(0.0)) - cons1 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], [x, y]), 0.0) - ci1 = MOI.add_constraint(model, cons1, MOI.LessThan(4.0)) - cons2 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], [x, y]), 0.0) - ci2 = MOI.add_constraint(model, cons2, MOI.LessThan(4.0)) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - obj_func = x - MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), obj_func) - MOI.set(model, POI.QuadraticObjectiveCoef(), (x, y), p) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 28 / 9 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - MOI.set(model, POI.ParameterValue(), p, 2.0) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 44 / 9 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - model = POI.Optimizer(Ipopt.Optimizer) - MOI.set(model, MOI.Silent(), true) - x = MOI.add_variable(model) - y = MOI.add_variable(model) - p = first(MOI.add_constrained_variable.(model, MOI.Parameter(1.0))) - MOI.add_constraint(model, x, MOI.GreaterThan(0.0)) - MOI.add_constraint(model, y, MOI.GreaterThan(0.0)) - cons1 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([2.0, 1.0], [x, y]), 0.0) - ci1 = MOI.add_constraint(model, cons1, MOI.LessThan(4.0)) - cons2 = - MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0, 2.0], [x, y]), 0.0) - ci2 = MOI.add_constraint(model, cons2, MOI.LessThan(4.0)) - MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) - MOI.set(model, POI.QuadraticObjectiveCoef(), (x, y), p) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 16 / 9 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - MOI.set(model, POI.ParameterValue(), p, 2.0) - MOI.optimize!(model) - @test MOI.get(model, MOI.ObjectiveValue()) ≈ 32 / 9 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), x) ≈ 4 / 3 atol = ATOL - @test MOI.get(model, MOI.VariablePrimal(), y) ≈ 4 / 3 atol = ATOL - return -end function test_compute_conflict!() T = Float64 From d6b9d55a3127401d8c3c302ff26a081af8839ed8 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Tue, 17 Feb 2026 00:09:19 -0300 Subject: [PATCH 2/2] format --- test/test_JuMP.jl | 1 - test/test_MathOptInterface.jl | 1 - 2 files changed, 2 deletions(-) diff --git a/test/test_JuMP.jl b/test/test_JuMP.jl index 098cfc93..946d1515 100644 --- a/test/test_JuMP.jl +++ b/test/test_JuMP.jl @@ -964,7 +964,6 @@ function test_jump_direct_soc_parameters() return end - function test_jump_direct_rsoc_constraints() """ Problem RSOC diff --git a/test/test_MathOptInterface.jl b/test/test_MathOptInterface.jl index c567c1bb..3c9053fc 100644 --- a/test/test_MathOptInterface.jl +++ b/test/test_MathOptInterface.jl @@ -1525,7 +1525,6 @@ function test_qp_objective_affine_parameter() return end - function test_compute_conflict!() T = Float64 mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{T}())