Skip to content

Commit 6954608

Browse files
authored
[Bridges] fix deleting variable in bridged objective (#2150)
1 parent e767bbb commit 6954608

File tree

4 files changed

+129
-17
lines changed

4 files changed

+129
-17
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "MathOptInterface"
22
uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
3-
version = "1.15.0"
3+
version = "1.15.1"
44

55
[deps]
66
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"

docs/src/changelog.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ CurrentModule = MathOptInterface
77
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
88
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
99

10+
## v1.15.1 (April 25, 2023)
11+
12+
### Fixed
13+
14+
- Fixed deleting a variable in a bridged objective (#2150)
15+
1016
## v1.15.0 (April 19, 2023)
1117

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

2733
- Add a test for variables in one-sided open [`Interval`](@ref) sets (#2133)

src/Bridges/bridge_optimizer.jl

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -545,15 +545,67 @@ function _delete_variables_in_variables_constraints(
545545
end
546546
end
547547

548-
function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex})
548+
function _delete_variables_in_bridged_objective(
549+
b::AbstractBridgeOptimizer,
550+
vis::Vector{MOI.VariableIndex},
551+
)
549552
F = MOI.get(b, MOI.ObjectiveFunctionType())
550-
if is_objective_bridged(b) && F == MOI.VectorOfVariables
551-
f = MOI.get(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}())
552-
discard = Base.Fix2(in, vis)
553-
if any(discard, f.variables)
554-
g = MOI.VectorOfVariables(filter(!discard, f.variables))
555-
MOI.set(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}(), g)
556-
end
553+
_delete_variables_in_bridged_objective(b, vis, F)
554+
return
555+
end
556+
557+
function _delete_variables_in_bridged_objective(
558+
b::AbstractBridgeOptimizer,
559+
vis::Vector{MOI.VariableIndex},
560+
::Type{MOI.VectorOfVariables},
561+
)
562+
obj_f = MOI.get(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}())
563+
discard = Base.Fix2(in, vis)
564+
if !any(discard, obj_f.variables)
565+
# There are no variables in the objective function to delete, so we
566+
# don't need to do anything.
567+
return
568+
end
569+
new_obj_f = MOI.VectorOfVariables(filter(!discard, obj_f.variables))
570+
if MOI.output_dimension(new_obj_f) == 0
571+
# We've deleted all variables in the objective. Zero the objective by
572+
# setting FEASIBILITY_SENSE, but restore the sense afterwards.
573+
sense = MOI.get(b, MOI.ObjectiveSense())
574+
MOI.set(b, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
575+
MOI.set(b, MOI.ObjectiveSense(), sense)
576+
else
577+
MOI.set(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}(), new_obj_f)
578+
end
579+
return
580+
end
581+
582+
function _delete_variables_in_bridged_objective(
583+
b::AbstractBridgeOptimizer,
584+
vis::Vector{MOI.VariableIndex},
585+
::Type{MOI.VariableIndex},
586+
)
587+
obj_f = MOI.get(b, MOI.ObjectiveFunction{MOI.VariableIndex}())
588+
if obj_f in vis
589+
sense = MOI.get(b, MOI.ObjectiveSense())
590+
MOI.set(b, MOI.ObjectiveSense(), MOI.FEASIBILITY_SENSE)
591+
MOI.set(b, MOI.ObjectiveSense(), sense)
592+
end
593+
return
594+
end
595+
596+
function _delete_variables_in_bridged_objective(
597+
::AbstractBridgeOptimizer,
598+
::Vector{MOI.VariableIndex},
599+
::Type{F},
600+
) where {F}
601+
# Nothing to do here. The variables can be deleted without changing the type
602+
# of the objective, or whether one exists.
603+
return
604+
end
605+
606+
function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex})
607+
if is_objective_bridged(b)
608+
_delete_variables_in_bridged_objective(b, vis)
557609
end
558610
if Constraint.has_bridges(Constraint.bridges(b))
559611
_delete_variables_in_variables_constraints(b, vis)
@@ -584,13 +636,8 @@ function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex})
584636
end
585637

586638
function MOI.delete(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex)
587-
F = MOI.get(b, MOI.ObjectiveFunctionType())
588-
if is_objective_bridged(b) && F == MOI.VectorOfVariables
589-
f = MOI.get(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}())
590-
if any(isequal(vi), f.variables)
591-
g = MOI.VectorOfVariables(filter(!isequal(vi), f.variables))
592-
MOI.set(b, MOI.ObjectiveFunction{MOI.VectorOfVariables}(), g)
593-
end
639+
if is_objective_bridged(b)
640+
_delete_variables_in_bridged_objective(b, [vi])
594641
end
595642
if Constraint.has_bridges(Constraint.bridges(b))
596643
_delete_variables_in_variables_constraints(b, [vi])

test/Bridges/bridge_optimizer.jl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,65 @@ function test_deleting_variables_in_bridged_objective()
979979
return
980980
end
981981

982+
function test_deleting_all_variables_in_bridged_objective()
983+
# We need to make sure that the bridges have the same functionality as the
984+
# base Utilities.Model.
985+
model_1 = MOI.Utilities.Model{Float64}()
986+
model_2 = MOI.Bridges.Objective.VectorFunctionize{Float64}(
987+
MOI.Utilities.Model{Float64}(),
988+
)
989+
for model in (model_1, model_2)
990+
x = MOI.add_variable(model)
991+
f = MOI.VectorOfVariables([x])
992+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
993+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
994+
MOI.delete(model, x)
995+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
996+
@test length(attrs) == 1
997+
@test attrs[1] == MOI.ObjectiveSense()
998+
end
999+
return
1000+
end
1001+
1002+
function test_deleting_all_vector_variables_in_bridged_objective()
1003+
# We need to make sure that the bridges have the same functionality as the
1004+
# base Utilities.Model.
1005+
model_1 = MOI.Utilities.Model{Float64}()
1006+
model_2 = MOI.Bridges.Objective.VectorFunctionize{Float64}(
1007+
MOI.Utilities.Model{Float64}(),
1008+
)
1009+
for model in (model_1, model_2)
1010+
x = MOI.add_variable(model)
1011+
f = MOI.VectorOfVariables([x])
1012+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
1013+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
1014+
MOI.delete(model, [x])
1015+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
1016+
@test length(attrs) == 1
1017+
@test attrs[1] == MOI.ObjectiveSense()
1018+
end
1019+
return
1020+
end
1021+
1022+
function test_deleting_all_variables_in_bridged_functionize_objective()
1023+
# We need to make sure that the bridges have the same functionality as the
1024+
# base Utilities.Model.
1025+
model_1 = MOI.Utilities.Model{Float64}()
1026+
model_2 = MOI.Bridges.Objective.Functionize{Float64}(
1027+
MOI.Utilities.Model{Float64}(),
1028+
)
1029+
for model in (model_1, model_2)
1030+
x = MOI.add_variable(model)
1031+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
1032+
MOI.set(model, MOI.ObjectiveFunction{typeof(x)}(), x)
1033+
MOI.delete(model, x)
1034+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
1035+
@test length(attrs) == 1
1036+
@test MOI.ObjectiveSense() in attrs
1037+
end
1038+
return
1039+
end
1040+
9821041
end # module
9831042

9841043
TestBridgeOptimizer.runtests()

0 commit comments

Comments
 (0)