Skip to content

Commit 18147ef

Browse files
committed
Add Variable.FreeBridge
1 parent a1db9f4 commit 18147ef

File tree

4 files changed

+242
-0
lines changed

4 files changed

+242
-0
lines changed

src/Bridges/Variable/Variable.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ include("single_bridge_optimizer.jl")
1717
# Variable bridges
1818
include("zeros.jl")
1919
const Zeros{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ZerosBridge{T}, OT}
20+
include("free.jl")
21+
const Free{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{FreeBridge{T}, OT}
2022
include("flip_sign.jl")
2123
const NonposToNonneg{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonposToNonnegBridge{T}, OT}
2224
include("vectorize.jl")

src/Bridges/Variable/free.jl

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
"""
2+
FreeBridge{T} <: Bridges.Variable.AbstractBridge
3+
4+
Transforms constrained variables in [`MathOptInterface.Reals`](@ref)
5+
to the sum of constrained variables in [`MathOptInterface.Nonnegatives`](@ref)
6+
and constrained variables in [`MathOptInterface.Nonpositives`](@ref).
7+
"""
8+
struct FreeBridge{T} <: AbstractBridge
9+
nonneg_variables::Vector{MOI.VariableIndex}
10+
nonneg_constraint::MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Nonnegatives}
11+
nonpos_variables::Vector{MOI.VariableIndex}
12+
nonpos_constraint::MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Nonpositives}
13+
end
14+
function bridge_constrained_variable(::Type{FreeBridge{T}},
15+
model::MOI.ModelLike,
16+
set::MOI.Reals) where T
17+
nonneg_variables, nonneg_constraint = MOI.add_constrained_variables(
18+
model, MOI.Nonnegatives(MOI.dimension(set)))
19+
nonpos_variables, nonpos_constraint = MOI.add_constrained_variables(
20+
model, MOI.Nonpositives(MOI.dimension(set)))
21+
return FreeBridge{T}(nonneg_variables, nonneg_constraint,
22+
nonpos_variables, nonpos_constraint)
23+
end
24+
25+
function supports_constrained_variable(
26+
::Type{<:FreeBridge}, ::Type{MOI.Reals})
27+
return true
28+
end
29+
function MOIB.added_constrained_variable_types(::Type{<:FreeBridge})
30+
return [(MOI.Nonnegatives,), (MOI.Nonpositives,)]
31+
end
32+
function MOIB.added_constraint_types(::Type{FreeBridge{T}}) where T
33+
return Tuple{DataType, DataType}[]
34+
end
35+
36+
# Attributes, Bridge acting as a model
37+
function MOI.get(bridge::FreeBridge, ::MOI.NumberOfVariables)
38+
return length(bridge.nonneg_variables) + length(bridge.nonpos_variables)
39+
end
40+
function MOI.get(bridge::FreeBridge, ::MOI.ListOfVariableIndices)
41+
return vcat(bridge.nonneg_variables, bridge.nonpos_variables)
42+
end
43+
function MOI.get(bridge::FreeBridge,
44+
::MOI.NumberOfConstraints{MOI.VectorOfVariables,
45+
MOI.Nonnegatives})
46+
return 1
47+
end
48+
function MOI.get(bridge::FreeBridge,
49+
::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,
50+
MOI.Nonnegatives})
51+
return [bridge.nonneg_constraint]
52+
end
53+
function MOI.get(bridge::FreeBridge,
54+
::MOI.NumberOfConstraints{MOI.VectorOfVariables,
55+
MOI.Nonpositives})
56+
return 1
57+
end
58+
function MOI.get(bridge::FreeBridge,
59+
::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,
60+
MOI.Nonpositives})
61+
return [bridge.nonpos_constraint]
62+
end
63+
64+
# References
65+
function MOI.delete(model::MOI.ModelLike, bridge::FreeBridge)
66+
MOI.delete(model, bridge.nonneg_variables)
67+
MOI.delete(model, bridge.nonpos_variables)
68+
end
69+
70+
function MOI.delete(model::MOI.ModelLike, bridge::FreeBridge, i::IndexInVector)
71+
MOI.delete(model, bridge.nonneg_variables[i.value])
72+
deleteat!(bridge.nonneg_variables, i.value)
73+
MOI.delete(model, bridge.nonpos_variables[i.value])
74+
deleteat!(bridge.nonpos_variables, i.value)
75+
end
76+
77+
78+
# Attributes, Bridge acting as a constraint
79+
80+
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal,
81+
bridge::FreeBridge{T}) where T
82+
return MOI.get(model, attr, bridge.nonneg_constraint) +
83+
MOI.get(model, attr, bridge.nonpos_constraint)
84+
end
85+
# The transformation is x_free = [I I] * [x_nonneg; x_nonpos]
86+
# so the transformation of the dual is
87+
# [y_nonneg; y_nonpos] = [I; I] * y_free
88+
# that is
89+
# y_nonneg = y_nonpos = y_free
90+
# We can therefore take either of them, let's take y_nonneg.
91+
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintDual,
92+
bridge::FreeBridge{T}) where T
93+
return MOI.get(model, attr, bridge.nonneg_constraint)
94+
end
95+
96+
function MOI.get(model::MOI.ModelLike, attr::MOI.VariablePrimal,
97+
bridge::FreeBridge{T}, i::IndexInVector) where T
98+
return MOI.get(model, attr, bridge.nonneg_variables[i.value]) +
99+
MOI.get(model, attr, bridge.nonpos_variables[i.value])
100+
end
101+
102+
function MOIB.bridged_function(bridge::FreeBridge{T}, i::IndexInVector) where T
103+
return MOIU.operate(+, T, MOI.SingleVariable(bridge.nonneg_variables[i.value]),
104+
MOI.SingleVariable(bridge.nonpos_variables[i.value]))
105+
end
106+
# x_free has been replaced by x_nonneg + x_nonpos.
107+
# To undo it we replace x_nonneg by x_free and x_nonpos by 0.
108+
function unbridged_map(bridge::FreeBridge{T}, vi::MOI.VariableIndex,
109+
i::IndexInVector) where T
110+
sv = MOI.SingleVariable(vi)
111+
func = convert(MOI.ScalarAffineFunction{T}, sv)
112+
return bridge.nonneg_variables[i.value] => func,
113+
bridge.nonpos_variables[i.value] => zero(MOI.ScalarAffineFunction{T})
114+
end
115+
116+
function MOI.set(model::MOI.ModelLike, attr::MOI.VariablePrimalStart,
117+
bridge::FreeBridge, value, i::IndexInVector)
118+
if value < 0
119+
nonneg = zero(value)
120+
nonpos = value
121+
else
122+
nonneg = value
123+
nonpos = zero(value)
124+
end
125+
MOI.set(model, attr, bridge.nonneg_variables[i.value], nonneg)
126+
MOI.set(model, attr, bridge.nonpos_variables[i.value], nonpos)
127+
end

test/Bridges/Variable/Variable.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ end
55
@testset "Zeros" begin
66
include("zeros.jl")
77
end
8+
@testset "Free" begin
9+
include("free.jl")
10+
end
811
@testset "FlipSign" begin
912
include("flip_sign.jl")
1013
end

test/Bridges/Variable/free.jl

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using Test
2+
3+
using MathOptInterface
4+
const MOI = MathOptInterface
5+
const MOIT = MathOptInterface.Test
6+
const MOIU = MathOptInterface.Utilities
7+
const MOIB = MathOptInterface.Bridges
8+
9+
include("../utilities.jl")
10+
11+
mock = MOIU.MockOptimizer(MOIU.UniversalFallback(MOIU.Model{Float64}()))
12+
config = MOIT.TestConfig()
13+
14+
bridged_mock = MOIB.Variable.Free{Float64}(mock)
15+
16+
@testset "solve_multirow_vectoraffine_nonpos" begin
17+
MOIU.set_mock_optimize!(mock,
18+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
19+
MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.5, 0.0])
20+
),
21+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock,
22+
MOI.OPTIMAL, (MOI.FEASIBLE_POINT, [0.25, 0.0])
23+
)
24+
)
25+
MOIT.solve_multirow_vectoraffine_nonpos(bridged_mock, config)
26+
end
27+
28+
@testset "Linear6" begin
29+
MOIU.set_mock_optimize!(mock,
30+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0, 0, 0]),
31+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0, 0, 0]),
32+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0, 0, -100],
33+
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => [1.0],
34+
(MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) => [-1.0]
35+
))
36+
MOIT.linear6test(bridged_mock, config)
37+
38+
loc = MOI.get(bridged_mock, MOI.ListOfConstraints())
39+
@test length(loc) == 2
40+
@test !((MOI.VectorOfVariables, MOI.Reals) in loc)
41+
@test (MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) in loc
42+
@test (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) in loc
43+
@test MOI.get(mock, MOI.NumberOfVariables()) == 4
44+
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 2
45+
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
46+
@test vis == MOI.VariableIndex.([-1, -2])
47+
48+
cx = MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Reals}(vis[1].value)
49+
@test MOI.get(bridged_mock, MOI.ConstraintPrimal(), cx) == [100.0]
50+
@test MOI.get(bridged_mock, MOI.ConstraintDual(), cx) == [0.0]
51+
cy = MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Reals}(vis[2].value)
52+
@test MOI.get(bridged_mock, MOI.ConstraintPrimal(), cy) == [-100.0]
53+
@test MOI.get(bridged_mock, MOI.ConstraintDual(), cy) == [0.0]
54+
55+
test_delete_bridged_variable(bridged_mock, vis[1], MOI.Reals, 2, (
56+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
57+
(MOI.VectorOfVariables, MOI.Nonpositives, 0)
58+
))
59+
test_delete_bridged_variable(bridged_mock, vis[2], MOI.Reals, 1, (
60+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
61+
(MOI.VectorOfVariables, MOI.Nonpositives, 0)
62+
))
63+
end
64+
65+
@testset "Linear7" begin
66+
function set_mock_optimize_linear7Test!(mock)
67+
MOIU.set_mock_optimize!(mock,
68+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0, 0, 0, 0]),
69+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0, 0, 0]),
70+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [100, 0, 0, -100],
71+
(MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => [[1.0]],
72+
(MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => [[-1.0]]
73+
))
74+
end
75+
set_mock_optimize_linear7Test!(mock)
76+
MOIT.linear7test(bridged_mock, config)
77+
78+
x, y = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
79+
80+
cx = MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Reals}(x.value)
81+
@test MOI.get(bridged_mock, MOI.ConstraintPrimal(), cx) == [100.0]
82+
@test MOI.get(bridged_mock, MOI.ConstraintDual(), cx) == [0.0]
83+
cy = MOI.ConstraintIndex{MOI.VectorOfVariables, MOI.Reals}(y.value)
84+
@test MOI.get(bridged_mock, MOI.ConstraintPrimal(), cy) == [-100.0]
85+
@test MOI.get(bridged_mock, MOI.ConstraintDual(), cy) == [0.0]
86+
87+
@test MOI.supports(bridged_mock, MOI.VariablePrimalStart(), MOI.VariableIndex)
88+
MOI.set(bridged_mock, MOI.VariablePrimalStart(), [x, y], [1.0, -1.0])
89+
xa, xb, ya, yb = MOI.get(mock, MOI.ListOfVariableIndices())
90+
@test MOI.get(mock, MOI.VariablePrimalStart(), [xa, xb, ya, yb]) == [1.0, 0.0, 0.0, -1.0]
91+
end
92+
93+
@testset "Linear11" begin
94+
MOIU.set_mock_optimize!(mock,
95+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.0, 1.0, 0.0]),
96+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 0.0, 0.5, 0.0]))
97+
MOIT.linear11test(bridged_mock, config)
98+
99+
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
100+
@test vis == MOI.VariableIndex.([-1, -2])
101+
102+
test_delete_bridged_variable(bridged_mock, vis[1], MOI.Reals, 2, (
103+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
104+
(MOI.VectorOfVariables, MOI.Nonpositives, 0)
105+
), used_bridges = 0, used_constraints = 0)
106+
test_delete_bridged_variable(bridged_mock, vis[2], MOI.Reals, 1, (
107+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
108+
(MOI.VectorOfVariables, MOI.Nonpositives, 0)
109+
))
110+
end

0 commit comments

Comments
 (0)