From 2fd1d795a8b5f123b8c6fc6196e42911be150c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Wed, 7 Aug 2019 16:07:45 +0200 Subject: [PATCH] Add Variable.FlipSignBridge --- src/Bridges/Variable/Variable.jl | 2 + src/Bridges/Variable/flip_sign.jl | 95 ++++++++++++++++++++++++++++++ test/Bridges/Variable/Variable.jl | 3 + test/Bridges/Variable/flip_sign.jl | 60 +++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 src/Bridges/Variable/flip_sign.jl create mode 100644 test/Bridges/Variable/flip_sign.jl diff --git a/src/Bridges/Variable/Variable.jl b/src/Bridges/Variable/Variable.jl index 23936738be..f76a2cd3d2 100644 --- a/src/Bridges/Variable/Variable.jl +++ b/src/Bridges/Variable/Variable.jl @@ -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 diff --git a/src/Bridges/Variable/flip_sign.jl b/src/Bridges/Variable/flip_sign.jl new file mode 100644 index 0000000000..8eaada975a --- /dev/null +++ b/src/Bridges/Variable/flip_sign.jl @@ -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 diff --git a/test/Bridges/Variable/Variable.jl b/test/Bridges/Variable/Variable.jl index 5a27995233..8f7a73db6f 100644 --- a/test/Bridges/Variable/Variable.jl +++ b/test/Bridges/Variable/Variable.jl @@ -5,3 +5,6 @@ end @testset "Zeros" begin include("zeros.jl") end +@testset "FlipSign" begin + include("flip_sign.jl") +end diff --git a/test/Bridges/Variable/flip_sign.jl b/test/Bridges/Variable/flip_sign.jl new file mode 100644 index 0000000000..c0a426b415 --- /dev/null +++ b/test/Bridges/Variable/flip_sign.jl @@ -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 +end