Skip to content

Commit

Permalink
[Bridges] fix deleting variable in bridged objective (#2150)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Apr 25, 2023
1 parent e767bbb commit 6954608
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "MathOptInterface"
uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
version = "1.15.0"
version = "1.15.1"

[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
Expand Down
8 changes: 7 additions & 1 deletion docs/src/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ CurrentModule = MathOptInterface
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v1.15.1 (April 25, 2023)

### Fixed

- Fixed deleting a variable in a bridged objective (#2150)

## v1.15.0 (April 19, 2023)

### Added
Expand All @@ -21,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed [`modify`](@ref) in [`Bridges.Objective.VectorSlackBridge`](@ref) (#2144)
- Fixed NAME record with spaces in `FileFormats.MPS` (#2146)
- Fixed deleting a variable in a bridged objective (#2147)

### Other

- Add a test for variables in one-sided open [`Interval`](@ref) sets (#2133)
Expand Down
77 changes: 62 additions & 15 deletions src/Bridges/bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -545,15 +545,67 @@ function _delete_variables_in_variables_constraints(
end
end

function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex})
function _delete_variables_in_bridged_objective(
b::AbstractBridgeOptimizer,
vis::Vector{MOI.VariableIndex},
)
F = MOI.get(b, MOI.ObjectiveFunctionType())
if is_objective_bridged(b) && F == MOI.VectorOfVariables
f = MOI.get(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}())
discard = Base.Fix2(in, vis)
if any(discard, f.variables)
g = MOI.VectorOfVariables(filter(!discard, f.variables))
MOI.set(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}(), g)
end
_delete_variables_in_bridged_objective(b, vis, F)
return
end

function _delete_variables_in_bridged_objective(
b::AbstractBridgeOptimizer,
vis::Vector{MOI.VariableIndex},
::Type{MOI.VectorOfVariables},
)
obj_f = MOI.get(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}())
discard = Base.Fix2(in, vis)
if !any(discard, obj_f.variables)
# There are no variables in the objective function to delete, so we
# don't need to do anything.
return
end
new_obj_f = MOI.VectorOfVariables(filter(!discard, obj_f.variables))
if MOI.output_dimension(new_obj_f) == 0
# We've deleted all variables in the objective. Zero the objective by
# setting FEASIBILITY_SENSE, but restore the sense afterwards.
sense = MOI.get(b, MOI.ObjectiveSense())
MOI.set(b, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
MOI.set(b, MOI.ObjectiveSense(), sense)
else
MOI.set(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}(), new_obj_f)
end
return
end

function _delete_variables_in_bridged_objective(
b::AbstractBridgeOptimizer,
vis::Vector{MOI.VariableIndex},
::Type{MOI.VariableIndex},
)
obj_f = MOI.get(b, MOI.ObjectiveFunction{MOI.VariableIndex}())
if obj_f in vis
sense = MOI.get(b, MOI.ObjectiveSense())
MOI.set(b, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
MOI.set(b, MOI.ObjectiveSense(), sense)
end
return
end

function _delete_variables_in_bridged_objective(
::AbstractBridgeOptimizer,
::Vector{MOI.VariableIndex},
::Type{F},
) where {F}
# Nothing to do here. The variables can be deleted without changing the type
# of the objective, or whether one exists.
return
end

function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex})
if is_objective_bridged(b)
_delete_variables_in_bridged_objective(b, vis)
end
if Constraint.has_bridges(Constraint.bridges(b))
_delete_variables_in_variables_constraints(b, vis)
Expand Down Expand Up @@ -584,13 +636,8 @@ function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex})
end

function MOI.delete(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex)
F = MOI.get(b, MOI.ObjectiveFunctionType())
if is_objective_bridged(b) && F == MOI.VectorOfVariables
f = MOI.get(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}())
if any(isequal(vi), f.variables)
g = MOI.VectorOfVariables(filter(!isequal(vi), f.variables))
MOI.set(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}(), g)
end
if is_objective_bridged(b)
_delete_variables_in_bridged_objective(b, [vi])
end
if Constraint.has_bridges(Constraint.bridges(b))
_delete_variables_in_variables_constraints(b, [vi])
Expand Down
59 changes: 59 additions & 0 deletions test/Bridges/bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,65 @@ function test_deleting_variables_in_bridged_objective()
return
end

function test_deleting_all_variables_in_bridged_objective()
# We need to make sure that the bridges have the same functionality as the
# base Utilities.Model.
model_1 = MOI.Utilities.Model{Float64}()
model_2 = MOI.Bridges.Objective.VectorFunctionize{Float64}(
MOI.Utilities.Model{Float64}(),
)
for model in (model_1, model_2)
x = MOI.add_variable(model)
f = MOI.VectorOfVariables([x])
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.delete(model, x)
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
@test length(attrs) == 1
@test attrs[1] == MOI.ObjectiveSense()
end
return
end

function test_deleting_all_vector_variables_in_bridged_objective()
# We need to make sure that the bridges have the same functionality as the
# base Utilities.Model.
model_1 = MOI.Utilities.Model{Float64}()
model_2 = MOI.Bridges.Objective.VectorFunctionize{Float64}(
MOI.Utilities.Model{Float64}(),
)
for model in (model_1, model_2)
x = MOI.add_variable(model)
f = MOI.VectorOfVariables([x])
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.delete(model, [x])
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
@test length(attrs) == 1
@test attrs[1] == MOI.ObjectiveSense()
end
return
end

function test_deleting_all_variables_in_bridged_functionize_objective()
# We need to make sure that the bridges have the same functionality as the
# base Utilities.Model.
model_1 = MOI.Utilities.Model{Float64}()
model_2 = MOI.Bridges.Objective.Functionize{Float64}(
MOI.Utilities.Model{Float64}(),
)
for model in (model_1, model_2)
x = MOI.add_variable(model)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(x)}(), x)
MOI.delete(model, x)
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
@test length(attrs) == 1
@test MOI.ObjectiveSense() in attrs
end
return
end

end # module

TestBridgeOptimizer.runtests()

2 comments on commit 6954608

@odow
Copy link
Member Author

@odow odow commented on 6954608 Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/82332

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.15.1 -m "<description of version>" 6954608abccc05fd2152f19bdf571b022a8a60c1
git push origin v1.15.1

Please sign in to comment.