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