Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ function conversion_cost(
::Type{
<:Union{
MOI.VariableIndex,
MOI.ScalarAffineFunction,
MOI.ScalarQuadraticFunction,
MOI.ScalarAffineFunction{Float64},
MOI.ScalarQuadraticFunction{Float64},
},
},
)
Expand Down
1 change: 1 addition & 0 deletions src/Bridges/Objective/Objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The coefficient type used is `T`.
function add_all_bridges(model, ::Type{T}) where {T}
MOI.Bridges.add_bridge(model, FunctionizeBridge{T})
MOI.Bridges.add_bridge(model, QuadratizeBridge{T})
MOI.Bridges.add_bridge(model, ToScalarNonlinearBridge{T})
MOI.Bridges.add_bridge(model, SlackBridge{T})
MOI.Bridges.add_bridge(model, VectorFunctionizeBridge{T})
MOI.Bridges.add_bridge(model, VectorSlackBridge{T})
Expand Down
30 changes: 29 additions & 1 deletion src/Bridges/Objective/bridges/FunctionConversionBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

for these pairs of functions:

* [`MOI.ScalarAffineFunction`](@ref)` to [`MOI.ScalarQuadraticFunction`](@ref)
* [`MOI.ScalarAffineFunction`](@ref) to [`MOI.ScalarQuadraticFunction`](@ref)
* [`MOI.ScalarQuadraticFunction`](@ref) to [`MOI.ScalarNonlinearFunction`](@ref)
* [`MOI.VectorAffineFunction`](@ref) to [`MOI.VectorQuadraticFunction`](@ref)

Expand Down Expand Up @@ -173,6 +173,34 @@ const QuadratizeBridge{T,G} =
const Quadratize{T,OT<:MOI.ModelLike} =
SingleBridgeOptimizer{QuadratizeBridge{T},OT}

"""
ToScalarNonlinearBridge{T,G} <: FunctionConversionBridge{T,MOI.ScalarNonlinearFunction,G}

`ToScalarNonlinearBridge` implements the following reformulations:

* ``\\min\\{x^\\top \\mathbf{𝑄} x + a^\\top x + b\\}`` into ``\\min\\{f(x)\\}``
* ``\\max\\{x^\\top \\mathbf{𝑄} x + a^\\top x + b\\}`` into ``\\max\\{f(x)\\}``

where `f(x)` is a `MOI.ScalarNonlinearFunction`.

## Source node

`ToScalarNonlinearBridge` supports:

* [`MOI.ObjectiveFunction{G}`](@ref)

## Target nodes

`ToScalarNonlinearBridge` creates:

* One objective node: [`MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction}`](@ref)
"""
const ToScalarNonlinearBridge{T,G} =
FunctionConversionBridge{T,MOI.ScalarNonlinearFunction,G}

const ToScalarNonlinear{T,OT<:MOI.ModelLike} =
SingleBridgeOptimizer{ToScalarNonlinearBridge{T},OT}

"""
VectorFunctionizeBridge{T,G} <: FunctionConversionBridge{T,MOI.VectorAffineFunction{T},G}

Expand Down
160 changes: 160 additions & 0 deletions test/Bridges/Objective/ToScalarNonlinearBridge.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Copyright (c) 2017: Miles Lubin and contributors
# Copyright (c) 2017: Google Inc.
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

module TestObjectiveToScalarNonlinear

using Test

import MathOptInterface as MOI

function runtests()
for name in names(@__MODULE__; all = true)
if startswith("$(name)", "test_")
@testset "$(name)" begin
getfield(@__MODULE__, name)()
end
end
end
return
end

include("../utilities.jl")

function test_solve_singlevariable_obj()
mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}())
model = MOI.Bridges.Objective.ToScalarNonlinear{Float64}(mock)
MOI.Utilities.set_mock_optimize!(
mock,
(mock::MOI.Utilities.MockOptimizer) ->
MOI.Utilities.mock_optimize!(mock, [1.0], MOI.FEASIBLE_POINT),
)
MOI.Test.test_objective_ObjectiveFunction_duplicate_terms(
model,
MOI.Test.Config(;
exclude = Any[MOI.DualObjectiveValue, MOI.ConstraintDual],
),
)
@test MOI.get(mock, MOI.ObjectiveFunctionType()) ==
MOI.ScalarNonlinearFunction
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
MOI.ScalarAffineFunction{Float64}
@test MOI.get(mock, MOI.ObjectiveSense()) == MOI.MIN_SENSE
@test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE
vis = MOI.get(model, MOI.ListOfVariableIndices())
func = 3.0 * vis[1] + 0.0

@test MOI.get(
model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
) ≈ func
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
@test MOI.get(mock, MOI.ObjectiveSense()) == MOI.MAX_SENSE
@test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE

_test_delete_objective(model, 1, tuple())
return
end

function test_solve_result_index()
mock = MOI.Utilities.MockOptimizer(MOI.Utilities.Model{Float64}())
model = MOI.Bridges.Objective.ToScalarNonlinear{Float64}(mock)
MOI.Utilities.set_mock_optimize!(
mock,
(mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
mock,
MOI.OPTIMAL,
(MOI.FEASIBLE_POINT, [1.0]),
MOI.FEASIBLE_POINT,
(MOI.VariableIndex, MOI.GreaterThan{Float64}) => [1.0],
),
)
MOI.Test.test_solve_result_index(
model,
MOI.Test.Config(;
exclude = Any[MOI.DualObjectiveValue, MOI.ConstraintDual],
),
)

return
end

function test_runtests()
MOI.Bridges.runtests(
MOI.Bridges.Objective.ToScalarNonlinearBridge,
model -> begin
x = MOI.add_variable(model)
aff = MOI.ScalarAffineFunction(
MOI.ScalarAffineTerm.([2.0], [x]),
1.0,
)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
aff,
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
end,
model -> begin
x = MOI.add_variable(model)
exp = MOI.ScalarNonlinearFunction(
:+,
[
MOI.ScalarNonlinearFunction(
:*,
[2.0, MOI.VariableIndex(1)],
),
1.0,
],
)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction}(),
exp,
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
end,
)
MOI.Bridges.runtests(
MOI.Bridges.Objective.ToScalarNonlinearBridge,
model -> begin
x = MOI.add_variable(model)
aff = MOI.ScalarAffineFunction(
MOI.ScalarAffineTerm.([2.0], [x]),
1.0,
)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(),
aff,
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
end,
model -> begin
x = MOI.add_variable(model)
exp = MOI.ScalarNonlinearFunction(
:+,
[
MOI.ScalarNonlinearFunction(
:*,
[2.0, MOI.VariableIndex(1)],
),
1.0,
],
)
MOI.set(
model,
MOI.ObjectiveFunction{MOI.ScalarNonlinearFunction}(),
exp,
)
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
end,
)
return
end

end # module

TestObjectiveToScalarNonlinear.runtests()
Loading