diff --git a/src/Bridges/Objective/functionize.jl b/src/Bridges/Objective/functionize.jl index c5ce83021b..3971254710 100644 --- a/src/Bridges/Objective/functionize.jl +++ b/src/Bridges/Objective/functionize.jl @@ -24,6 +24,14 @@ function MOIB.set_objective_function_type(::Type{FunctionizeBridge{T}}) where T return MOI.ScalarAffineFunction{T} end +# Attributes, Bridge acting as a model +function MOI.get(bridge::FunctionizeBridge, ::MOI.NumberOfVariables) + return 0 +end +function MOI.get(bridge::FunctionizeBridge, ::MOI.ListOfVariableIndices) + return MOI.VariableIndex[] +end + # No variables or constraints are created in this bridge so there is nothing to # delete. function MOI.delete(model::MOI.ModelLike, bridge::FunctionizeBridge) end diff --git a/src/Bridges/Objective/slack.jl b/src/Bridges/Objective/slack.jl index a1cc85da46..fd75151ea1 100644 --- a/src/Bridges/Objective/slack.jl +++ b/src/Bridges/Objective/slack.jl @@ -28,7 +28,7 @@ function bridge_objective(::Type{SlackBridge{T, F, G}}, model::MOI.ModelLike, error("Set `MOI.ObjectiveSense` before `MOI.ObjectiveFunction` when", " using `MOI.Bridges.Objective.SlackBridge`.") end - constraint = MOIU.add_scalar_constraint(model, f, set) + constraint = MOIU.normalize_and_add_constraint(model, f, set) MOI.set(model, MOI.ObjectiveFunction{MOI.SingleVariable}(), fslack) return SlackBridge{T, F, G}(slack, constraint) end @@ -54,6 +54,25 @@ function concrete_bridge_type(::Type{<:SlackBridge{T}}, return SlackBridge{T, F, G} end +# Attributes, Bridge acting as a model +function MOI.get(bridge::SlackBridge, ::MOI.NumberOfVariables) + return 1 +end +function MOI.get(bridge::SlackBridge, ::MOI.ListOfVariableIndices) + return [bridge.slack] +end +function MOI.get(bridge::SlackBridge{T, F}, ::MOI.NumberOfConstraints{F, S}) where { + T, F, S <: Union{MOI.GreaterThan{T}, MOI.LessThan{T}}} + return bridge.constraint isa MOI.ConstraintIndex{F, S} ? 1 : 0 +end +function MOI.get(bridge::SlackBridge{T, F}, ::MOI.ListOfConstraintIndices{F, S}) where { + T, F, S <: Union{MOI.GreaterThan{T}, MOI.LessThan{T}}} + if bridge.constraint isa MOI.ConstraintIndex{F, S} + return [bridge.constraint] + else + return MOI.ConstraintIndex{F, S}[] + end +end function MOI.delete(model::MOI.ModelLike, bridge::SlackBridge) MOI.delete(model, bridge.constraint) diff --git a/src/Bridges/bridge_optimizer.jl b/src/Bridges/bridge_optimizer.jl index d4f309fba8..ea0706ccfd 100644 --- a/src/Bridges/bridge_optimizer.jl +++ b/src/Bridges/bridge_optimizer.jl @@ -412,6 +412,9 @@ function MOI.get(b::AbstractBridgeOptimizer, for bridge in values(Constraint.bridges(b)) _remove_bridged(list, bridge, attr) end + for bridge in values(Objective.bridges(b)) + _remove_bridged(list, bridge, attr) + end return list end function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.NumberOfVariables) @@ -422,6 +425,9 @@ function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.NumberOfVariables) for bridge in values(Constraint.bridges(b)) s -= MOI.get(bridge, attr) end + for bridge in values(Objective.bridges(b)) + s -= MOI.get(bridge, attr) + end return s end @@ -446,6 +452,9 @@ function MOI.get(b::AbstractBridgeOptimizer, for bridge in values(Constraint.bridges(b)) s -= MOI.get(bridge, attr) end + for bridge in values(Objective.bridges(b)) + s -= MOI.get(bridge, attr) + end return s end function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ListOfConstraints) @@ -569,6 +578,7 @@ function MOI.get(b::AbstractBridgeOptimizer, end function MOI.set(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveSense, value::MOI.OptimizationSense) + MOI.set(b.model, attr, value) if is_objective_bridged(b) if value == MOI.FEASIBILITY_SENSE _delete_objective_bridges(b) @@ -578,7 +588,6 @@ function MOI.set(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveSense, end end end - MOI.set(b.model, attr, value) end function _bridge_objective(b, BridgeType, func) bridge = Objective.bridge_objective(BridgeType, b, func) @@ -588,7 +597,16 @@ function MOI.set(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunction, func::MOI.AbstractScalarFunction) if is_objective_bridged(b) + # Clear objective function by setting sense to `MOI.FEASIBILITY_SENSE` + # first. This is needed if the objective function of `b.model` is + # `MOI.SingleVariable(slack)` where `slack` is the slack variable + # created by `Objective.SlackBridge`. + sense = MOI.get(b.model, MOI.ObjectiveSense()) + MOI.set(b.model, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) _delete_objective_bridges(b) + if sense != MOI.FEASIBILITY_SENSE + MOI.set(b.model, MOI.ObjectiveSense(), sense) + end end if Variable.has_bridges(Variable.bridges(b)) if func isa MOI.SingleVariable diff --git a/src/Utilities/model.jl b/src/Utilities/model.jl index 77cc4747ad..53e46abd7d 100644 --- a/src/Utilities/model.jl +++ b/src/Utilities/model.jl @@ -357,7 +357,12 @@ end # Objective MOI.get(model::AbstractModel, ::MOI.ObjectiveSense) = model.sense MOI.supports(model::AbstractModel, ::MOI.ObjectiveSense) = true -function MOI.set(model::AbstractModel, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense) +function MOI.set(model::AbstractModel{T}, ::MOI.ObjectiveSense, + sense::MOI.OptimizationSense) where T + if sense == MOI.FEASIBILITY_SENSE + model.objectiveset = false + model.objective = zero(MOI.ScalarAffineFunction{T}) + end model.senseset = true model.sense = sense end @@ -922,7 +927,7 @@ macro model(model_name, ss, sst, vs, vst, sf, sft, vf, vft) model.senseset = false model.sense = $MOI.FEASIBILITY_SENSE model.objectiveset = false - model.objective = $SAF{T}(MOI.ScalarAffineTerm{T}[], zero(T)) + model.objective = zero($MOI.ScalarAffineFunction{T}) model.num_variables_created = 0 model.variable_indices = nothing model.single_variable_mask = UInt8[] diff --git a/test/Bridges/Objective/slack.jl b/test/Bridges/Objective/slack.jl index 05601cb63f..c76553ccb4 100644 --- a/test/Bridges/Objective/slack.jl +++ b/test/Bridges/Objective/slack.jl @@ -26,20 +26,24 @@ end MOIU.set_mock_optimize!(mock, (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]) + (MOI.FEASIBLE_POINT, [1.0, 2.0, 5.0]) ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]) + (MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0]) ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]) + (MOI.FEASIBLE_POINT, [1.0, 2.0, 2.0]) ), (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, MOI.OPTIMAL, - (MOI.FEASIBLE_POINT, [1.0, 2.0]) + (MOI.FEASIBLE_POINT, [1.0, 2.0, 7.0]) ) ) - MOIT.solve_qp_edge_cases(mock, config) + MOIT.solve_qp_edge_cases(bridged_mock, config) + test_delete_objective(bridged_mock, 2, ( + (MOI.ScalarQuadraticFunction{Float64}, MOI.GreaterThan{Float64}, 0), + (MOI.ScalarQuadraticFunction{Float64}, MOI.LessThan{Float64}, 0), + )) end diff --git a/test/Bridges/utilities.jl b/test/Bridges/utilities.jl index 35d1111eef..fd21c9ec75 100644 --- a/test/Bridges/utilities.jl +++ b/test/Bridges/utilities.jl @@ -112,22 +112,23 @@ function test_delete_bridged_variables( end function test_delete_objective( m::MOIB.AbstractBridgeOptimizer, nvars::Int, - nocs::Tuple; used_bridges = 1) + list_num_constraints::Tuple; used_bridges = 1) + warn_incomplete_list_num_constraints(typeof(MOIB.Objective.root_bridge(MOIB.Objective.bridges(m))), list_num_constraints) function num_bridges() return length(MOIB.Objective.bridges(m)) end start_num_bridges = num_bridges() @test MOI.get(m, MOI.NumberOfVariables()) == nvars @test length(MOI.get(m, MOI.ListOfVariableIndices())) == nvars - for noc in nocs - test_noc(m, noc...) + for num_constraints in list_num_constraints + test_num_constraints(m, num_constraints...) end MOI.set(m, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE) @test MOI.get(m, MOI.ObjectiveSense()) == MOI.FEASIBILITY_SENSE @test num_bridges() == start_num_bridges - used_bridges @test MOI.get(m, MOI.NumberOfVariables()) == nvars @test length(MOI.get(m, MOI.ListOfVariableIndices())) == nvars - for noc in nocs - test_noc(m, noc...) + for num_constraints in list_num_constraints + test_num_constraints(m, num_constraints...) end end