Skip to content

Commit 0adae07

Browse files
authored
[Bridges] add Objective.VectorFunctionizeBridge (#2139)
1 parent 46adb40 commit 0adae07

File tree

6 files changed

+247
-0
lines changed

6 files changed

+247
-0
lines changed

docs/src/submodules/Bridges/list_of_bridges.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ These bridges are subtypes of [`Bridges.Objective.AbstractBridge`](@ref).
7878
Bridges.Objective.FunctionizeBridge
7979
Bridges.Objective.QuadratizeBridge
8080
Bridges.Objective.SlackBridge
81+
Bridges.Objective.VectorFunctionizeBridge
8182
Bridges.Objective.VectorSlackBridge
8283
```
8384

src/Bridges/Objective/Objective.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ include("single_bridge_optimizer.jl")
1515
include("bridges/functionize.jl")
1616
include("bridges/quadratize.jl")
1717
include("bridges/slack.jl")
18+
include("bridges/vector_functionize.jl")
1819
include("bridges/vector_slack.jl")
1920

2021
"""
@@ -28,6 +29,7 @@ function add_all_bridges(model, ::Type{T}) where {T}
2829
MOI.Bridges.add_bridge(model, FunctionizeBridge{T})
2930
MOI.Bridges.add_bridge(model, QuadratizeBridge{T})
3031
MOI.Bridges.add_bridge(model, SlackBridge{T})
32+
MOI.Bridges.add_bridge(model, VectorFunctionizeBridge{T})
3133
MOI.Bridges.add_bridge(model, VectorSlackBridge{T})
3234
return
3335
end
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
VectorFunctionizeBridge{T}
9+
10+
`VectorFunctionizeBridge` implements the following reformulations:
11+
12+
* ``\\min \\{x\\}`` into ``\\min\\{1x + 0\\}``
13+
* ``\\max \\{x\\}`` into ``\\max\\{1x + 0\\}``
14+
15+
where `T` is the coefficient type of `1` and `0`.
16+
17+
## Source node
18+
19+
`VectorFunctionizeBridge` supports:
20+
21+
* [`MOI.ObjectiveFunction{MOI.VectorOfVariables}`](@ref)
22+
23+
## Target nodes
24+
25+
`VectorFunctionizeBridge` creates:
26+
27+
* One objective node: [`MOI.ObjectiveFunction{MOI.VectorAffineFunction{T}}`](@ref)
28+
"""
29+
struct VectorFunctionizeBridge{T} <: AbstractBridge end
30+
31+
const VectorFunctionize{T,OT<:MOI.ModelLike} =
32+
SingleBridgeOptimizer{VectorFunctionizeBridge{T},OT}
33+
34+
function bridge_objective(
35+
::Type{VectorFunctionizeBridge{T}},
36+
model::MOI.ModelLike,
37+
f::MOI.VectorOfVariables,
38+
) where {T}
39+
F = MOI.VectorAffineFunction{T}
40+
MOI.set(model, MOI.ObjectiveFunction{F}(), convert(F, f))
41+
return VectorFunctionizeBridge{T}()
42+
end
43+
44+
function supports_objective_function(
45+
::Type{<:VectorFunctionizeBridge},
46+
::Type{MOI.VectorOfVariables},
47+
)
48+
return true
49+
end
50+
51+
function MOI.Bridges.added_constrained_variable_types(
52+
::Type{<:VectorFunctionizeBridge},
53+
)
54+
return Tuple{Type}[]
55+
end
56+
57+
function MOI.Bridges.added_constraint_types(::Type{<:VectorFunctionizeBridge})
58+
return Tuple{Type,Type}[]
59+
end
60+
61+
function MOI.Bridges.set_objective_function_type(
62+
::Type{VectorFunctionizeBridge{T}},
63+
) where {T}
64+
return MOI.VectorAffineFunction{T}
65+
end
66+
67+
MOI.get(::VectorFunctionizeBridge, ::MOI.NumberOfVariables)::Int64 = 0
68+
69+
function MOI.get(::VectorFunctionizeBridge, ::MOI.ListOfVariableIndices)
70+
return MOI.VariableIndex[]
71+
end
72+
73+
MOI.delete(::MOI.ModelLike, ::VectorFunctionizeBridge) = nothing
74+
75+
function MOI.set(
76+
::MOI.ModelLike,
77+
::MOI.ObjectiveSense,
78+
::VectorFunctionizeBridge,
79+
::MOI.OptimizationSense,
80+
)
81+
# `VectorFunctionizeBridge` is sense agnostic, therefore, we don't need to
82+
# change anything.
83+
return
84+
end
85+
86+
function MOI.get(
87+
model::MOI.ModelLike,
88+
attr::MOI.Bridges.ObjectiveFunctionValue{MOI.VectorOfVariables},
89+
::VectorFunctionizeBridge{T},
90+
) where {T}
91+
F = MOI.VectorAffineFunction{T}
92+
attr_f = MOI.Bridges.ObjectiveFunctionValue{F}(attr.result_index)
93+
return MOI.get(model, attr_f)
94+
end
95+
96+
function MOI.get(
97+
model::MOI.ModelLike,
98+
::MOI.ObjectiveFunction{MOI.VectorOfVariables},
99+
::VectorFunctionizeBridge{T},
100+
) where {T}
101+
f = MOI.get(model, MOI.ObjectiveFunction{MOI.VectorAffineFunction{T}}())
102+
return convert(MOI.VectorOfVariables, f)
103+
end

src/functions.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,26 @@ function Base.convert(::Type{VectorOfVariables}, g::VariableIndex)
810810
return VectorOfVariables([g])
811811
end
812812

813+
function Base.convert(::Type{VectorOfVariables}, f::VectorAffineFunction)
814+
variables = Vector{VariableIndex}(undef, length(f.constants))
815+
assigned = fill(false, length(variables))
816+
if any(!iszero, f.constants)
817+
throw(InexactError(:convert, VectorOfVariables, f))
818+
end
819+
for term in f.terms
820+
if assigned[term.output_index] || !isone(term.scalar_term.coefficient)
821+
throw(InexactError(:convert, VectorOfVariables, f))
822+
end
823+
x = convert(VariableIndex, term.scalar_term.variable)
824+
variables[term.output_index] = x
825+
assigned[term.output_index] = true
826+
end
827+
if !all(assigned)
828+
throw(InexactError(:convert, VectorOfVariables, f))
829+
end
830+
return VectorOfVariables(variables)
831+
end
832+
813833
# VectorAffineFunction
814834

815835
function Base.convert(
@@ -846,6 +866,17 @@ function Base.convert(
846866
return VectorAffineFunction{T}(f.terms, f.constants)
847867
end
848868

869+
function Base.convert(
870+
::Type{VectorAffineFunction{T}},
871+
f::VectorOfVariables,
872+
) where {T}
873+
terms = VectorAffineTerm{T}[
874+
VectorAffineTerm{T}(i, ScalarAffineTerm{T}(one(T), x)) for
875+
(i, x) in enumerate(f.variables)
876+
]
877+
return VectorAffineFunction{T}(terms, zeros(T, length(terms)))
878+
end
879+
849880
# VectorQuadraticFunction
850881

851882
function Base.convert(
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
module TestObjectiveVectorFunctionize
8+
9+
using Test
10+
11+
import MathOptInterface as MOI
12+
13+
function runtests()
14+
for name in names(@__MODULE__; all = true)
15+
if startswith("$(name)", "test_")
16+
@testset "$(name)" begin
17+
getfield(@__MODULE__, name)()
18+
end
19+
end
20+
end
21+
return
22+
end
23+
24+
function test_runtests()
25+
MOI.Bridges.runtests(
26+
MOI.Bridges.Objective.VectorFunctionizeBridge,
27+
"""
28+
variables: x, y
29+
minobjective: [x, y]
30+
""",
31+
"""
32+
variables: x, y
33+
minobjective: [1.0 * x + 0.0, 1.0 * y + 0.0]
34+
""",
35+
)
36+
MOI.Bridges.runtests(
37+
MOI.Bridges.Objective.VectorFunctionizeBridge,
38+
"""
39+
variables: x, y
40+
maxobjective: [x, y]
41+
""",
42+
"""
43+
variables: x, y
44+
maxobjective: [1.0 * x + 0.0, 1.0 * y + 0.0]
45+
""",
46+
)
47+
return
48+
end
49+
50+
function test_objective_function_value()
51+
inner = MOI.Utilities.MockOptimizer(
52+
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
53+
)
54+
model = MOI.Bridges.Objective.VectorFunctionize{Float64}(inner)
55+
MOI.Utilities.loadfromstring!(
56+
model,
57+
"""
58+
variables: x, y
59+
minobjective: [x, y]
60+
""",
61+
)
62+
MOI.Utilities.set_mock_optimize!(
63+
inner,
64+
mock -> MOI.Utilities.mock_optimize!(mock, [3.0, 5.6]),
65+
)
66+
MOI.optimize!(model)
67+
@test MOI.get(model, MOI.ObjectiveValue()) [3.0, 5.6]
68+
return
69+
end
70+
71+
function test_set_objective_sense()
72+
inner = MOI.Utilities.MockOptimizer(
73+
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
74+
)
75+
model = MOI.Bridges.Objective.VectorFunctionize{Float64}(inner)
76+
MOI.Utilities.loadfromstring!(
77+
model,
78+
"""
79+
variables: x, y
80+
minobjective: [x, y]
81+
""",
82+
)
83+
@test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE
84+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
85+
@test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE
86+
return
87+
end
88+
89+
end # module
90+
91+
TestObjectiveVectorFunctionize.runtests()

test/functions.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,25 @@ function test_isapprox_issue_1483()
243243
return
244244
end
245245

246+
function test_convert_vectorofvariables()
247+
x = MOI.VariableIndex(1)
248+
y = MOI.VariableIndex(2)
249+
f = MOI.VectorOfVariables([x, y])
250+
g = MOI.Utilities.operate(vcat, Float64, 1.0 * x, 1.0 * y)
251+
@test convert(MOI.VectorOfVariables, g) == f
252+
for g in (
253+
MOI.Utilities.operate(vcat, Float64, 1.2 * x, 1.0 * y),
254+
MOI.Utilities.operate(vcat, Float64, 1.0 * y, 1.2 * x),
255+
MOI.Utilities.operate(vcat, Float64, 1.0 * x, 0.0),
256+
MOI.Utilities.operate(vcat, Float64, 0.0, 1.0 * x),
257+
MOI.Utilities.operate(vcat, Float64, 1.0 * x, 1.0 * y + 1.0),
258+
MOI.Utilities.operate(vcat, Float64, 1.0 * x + 1.0, 1.0 * y),
259+
)
260+
@test_throws InexactError convert(MOI.VectorOfVariables, g)
261+
end
262+
return
263+
end
264+
246265
function runtests()
247266
for name in names(@__MODULE__; all = true)
248267
if startswith("$name", "test_")

0 commit comments

Comments
 (0)