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
2 changes: 2 additions & 0 deletions src/Bridges/Variable/Variable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ include("single_bridge_optimizer.jl")
# Variable bridges
include("zeros.jl")
const Zeros{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ZerosBridge{T}, OT}
include("flip_sign.jl")
const NonposToNonneg{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonposToNonnegBridge{T}, OT}

end
95 changes: 95 additions & 0 deletions src/Bridges/Variable/flip_sign.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
FlipSignBridge{T, S1, S2}

Bridge constrained variables in `S1` into constrained variables in `S2` by
multiplying the variables by `-1` and taking the point reflection of the set
across the origin. The flipped `MOI.VectorOfVariables`-in-`S` constraint is
stored in the `flipped_constraint` field by convention.
"""
abstract type FlipSignBridge{
T, S1<:MOI.AbstractSet, S2<:MOI.AbstractSet} <: AbstractBridge end

function supports_constrained_variable(
::Type{<:FlipSignBridge{T, S1}}, ::Type{S1}) where {T, S1<:MOI.AbstractVectorSet}
return true
end
function MOIB.added_constrained_variable_types(
::Type{<:FlipSignBridge{T, S1, S2}}) where {T, S1, S2}
return [(S2,)]
end
function MOIB.added_constraint_types(::Type{<:FlipSignBridge})
return Tuple{DataType, DataType}[]
end

# Attributes, Bridge acting as a model
function MOI.get(bridge::FlipSignBridge, ::MOI.NumberOfVariables)
return length(bridge.flipped_variables)
end
function MOI.get(bridge::FlipSignBridge, ::MOI.ListOfVariableIndices)
return bridge.flipped_variables
end
function MOI.get(::FlipSignBridge{T, S1, S2},
::MOI.NumberOfConstraints{MOI.VectorOfVariables, S2}) where {T, S1, S2<:MOI.AbstractVectorSet}
return 1
end
function MOI.get(bridge::FlipSignBridge{T, S1, S2},
::MOI.ListOfConstraintIndices{MOI.VectorOfVariables, S2}) where {T, S1, S2<:MOI.AbstractVectorSet}
return [bridge.flipped_constraint]
end

# References
function MOI.delete(model::MOI.ModelLike, bridge::FlipSignBridge)
MOI.delete(model, bridge.flipped_variables)
end

function MOI.delete(model::MOI.ModelLike, bridge::FlipSignBridge, i::IndexInVector)
MOI.delete(model, bridge.flipped_variables[i.value])
deleteat!(bridge.flipped_variables, i.value)
end

# Attributes, Bridge acting as a constraint

function MOI.get(model::MOI.ModelLike,
attr::Union{MOI.ConstraintPrimal, MOI.ConstraintDual},
bridge::FlipSignBridge)
return -MOI.get(model, attr, bridge.flipped_constraint)
end

function MOI.get(model::MOI.ModelLike, attr::MOI.VariablePrimal,
bridge::FlipSignBridge, i::IndexInVector)
return -MOI.get(model, attr, bridge.flipped_variables[i.value])
end

function MOIB.bridged_function(bridge::FlipSignBridge{T}, i::IndexInVector) where T
func = MOI.SingleVariable(bridge.flipped_variables[i.value])
return MOIU.operate(-, T, func)
end
function unbridged_map(bridge::FlipSignBridge{T}, vi::MOI.VariableIndex,
i::IndexInVector) where T
func = MOIU.operate(-, T, MOI.SingleVariable(vi))
return (bridge.flipped_variables[i.value] => func,)
end

function MOI.set(model::MOI.ModelLike, attr::MOI.VariablePrimalStart,
bridge::FlipSignBridge, value, i::IndexInVector)
MOI.set(model, attr, bridge.flipped_variables[i.value], -value)
end

"""
NonposToNonnegBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <:
FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives, F, G}

Transforms constrained variables in `Nonpositives` into constrained variables in
`Nonnegatives`.
"""
struct NonposToNonnegBridge{T} <: FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives}
flipped_variables::Vector{MOI.VariableIndex}
flipped_constraint::MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Nonnegatives}
end
function bridge_constrained_variable(::Type{NonposToNonnegBridge{T}},
model::MOI.ModelLike,
set::MOI.Nonpositives) where T
flipped_variables, flipped_constraint = MOI.add_constrained_variables(
model, MOI.Nonnegatives(set.dimension))
return NonposToNonnegBridge{T}(flipped_variables, flipped_constraint)
end
3 changes: 3 additions & 0 deletions test/Bridges/Variable/Variable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ end
@testset "Zeros" begin
include("zeros.jl")
end
@testset "FlipSign" begin
include("flip_sign.jl")
end
60 changes: 60 additions & 0 deletions test/Bridges/Variable/flip_sign.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Test

using MathOptInterface
const MOI = MathOptInterface
const MOIT = MathOptInterface.Test
const MOIU = MathOptInterface.Utilities
const MOIB = MathOptInterface.Bridges

include("../utilities.jl")

mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}()))
config = MOIT.TestConfig()

@testset "NonposToNonneg" begin
bridged_mock = MOIB.Variable.NonposToNonneg{Float64}(mock)

@testset "lin2v" begin
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [-4, 3, 16, 0],
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[7, 2, -4]])
MOIT.lin2vtest(bridged_mock, config)

@test MOI.get(mock, MOI.NumberOfVariables()) == 4
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 4
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
y = vis[4]
@test y.value == -1

@test MOI.supports(bridged_mock, MOI.VariablePrimalStart(), MOI.VariableIndex)
MOI.set(bridged_mock, MOI.VariablePrimalStart(), y, 1.0)
x, y_flipped, z, s = MOI.get(mock, MOI.ListOfVariableIndices())
@test MOI.get(mock, MOI.VariablePrimalStart(), y_flipped) == -1
end

@testset "lin4" begin
MOIU.set_mock_optimize!(mock,
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
mock, MOI.INFEASIBLE, MOI.INFEASIBLE_POINT,
MOI.INFEASIBILITY_CERTIFICATE)
)
MOIT.lin4test(bridged_mock, config)

@test MOI.get(mock, MOI.NumberOfVariables()) == 1
@test length(MOI.get(mock, MOI.ListOfVariableIndices())) == 1
@test first(MOI.get(mock, MOI.ListOfVariableIndices())).value ≥ 0
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 1
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
@test vis == [MOI.VariableIndex(-1)]
test_delete_bridged_variable(bridged_mock, vis[1], MOI.Nonpositives, 1, (
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
))
end

@testset "Delete in vector" begin
MOI.empty!(bridged_mock)
vis, ci = MOI.add_constrained_variables(bridged_mock, MOI.Nonpositives(4))
test_delete_bridged_variable(bridged_mock, vis[2], MOI.Nonpositives, 4, (
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
), used_bridges = 0, used_constraints = 0)
end
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to have test_models_equal tests for all the bridges.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, this is related to #750

Copy link
Member Author

Choose a reason for hiding this comment

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

Opened an issue: #820

end