Skip to content

Commit 6579600

Browse files
committed
[Utilities] add FeasibilityRelaxation
1 parent d9c998a commit 6579600

File tree

2 files changed

+177
-1
lines changed

2 files changed

+177
-1
lines changed

src/Utilities/Utilities.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ include("mockoptimizer.jl")
7777
include("cachingoptimizer.jl")
7878
include("universalfallback.jl")
7979
include("print.jl")
80-
80+
include("feasibility_relaxation.jl")
8181
include("lazy_iterators.jl")
8282

8383
include("precompile.jl")
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
FeasibilityRelaxation(
9+
penalties = Dict{MOI.ConstraintIndex,Float64}(),
10+
) <: MOI.AbstractModelAttribute
11+
12+
A model attribute that, when set, destructively modifies the model in-place to
13+
create a feasibility relxation.
14+
15+
!!! warning
16+
This is a destructive routine that modifies the model in-place. If you don't
17+
want to modify the original model, use `copy_model` to create a copy before
18+
setting this attribute.
19+
20+
## Reformulation
21+
22+
The feasibility relaxation modifies constraints of the form ``f(x) \\in S`` into
23+
``f(x) + y - z \\in S``, where ``y, z \\ge 0``, and then it introduces a penalty
24+
term into the objective of ``a \\times (y + z)`` (if minimizing, else ``-a``),
25+
where `a` is the value in the `penalties` dictionary associated with the
26+
constraint that is being relaxed. If no value exists, the default is `1.0`.
27+
28+
The feasibility relaxation is limited to modifying constraint types for which
29+
`MOI.supports(model, ::FeasibilityRelaxation, MOI.ConstraintIndex{F,S})` is
30+
`true`. By default, this is only true for [`MOI.ScalarAffineFunction`](@ref) and
31+
[`MOI.MOI.ScalarQuadraticFunction`](@ref) constraints in the linear sets
32+
[`MOI.LessThan`](@ref), [`MOI.GreaterThan`](@ref), [`MOI.EqualTo`](@ref) and
33+
[`MOI.Interval`](@ref). It does not include variable bound or integrality
34+
constraints, because these cannot be modified in-place.
35+
36+
## Example
37+
38+
```jldoctest
39+
julia> model = MOI.Utilities.Model{Float64}();
40+
41+
julia> x = MOI.add_variable(model);
42+
43+
julia> c = MOI.add_constraint(model, 1.0 * x, MOI.LessThan(2.0));
44+
45+
julia> MOI.set(model, MOI.Utilities.FeasibilityRelaxation(Dict(c => 2.0)))
46+
47+
julia> print(model)
48+
Minimize ScalarAffineFunction{Float64}:
49+
0.0 + 2.0 v[2]
50+
51+
Subject to:
52+
53+
ScalarAffineFunction{Float64}-in-LessThan{Float64}
54+
0.0 + 1.0 v[1] - 1.0 v[2] <= 2.0
55+
56+
VariableIndex-in-GreaterThan{Float64}
57+
v[2] >= 0.0
58+
```
59+
"""
60+
mutable struct FeasibilityRelaxation{T} <: MOI.AbstractModelAttribute
61+
penalties::Dict{MOI.ConstraintIndex,T}
62+
scale::T
63+
function FeasibilityRelaxation(p::Dict{MOI.ConstraintIndex,T}) where {T}
64+
return new{T}(p, zero(T))
65+
end
66+
end
67+
68+
function FeasibilityRelaxation()
69+
return FeasibilityRelaxation(Dict{MOI.ConstraintIndex,Float64}())
70+
end
71+
72+
function FeasibilityRelaxation(d::Dict{<:MOI.ConstraintIndex,T}) where {T}
73+
return FeasibilityRelaxation(convert(Dict{MOI.ConstraintIndex,T}, d))
74+
end
75+
76+
function MOI.set(
77+
model::MOI.ModelLike,
78+
relax::FeasibilityRelaxation{T},
79+
) where {T}
80+
sense = MOI.get(model, MOI.ObjectiveSense())
81+
if sense == MOI.FEASIBILITY_SENSE
82+
relax.scale = one(T)
83+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
84+
f = zero(MOI.ScalarAffineFunction{T})
85+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
86+
elseif sense == MOI.MIN_SENSE
87+
relax.scale = one(T)
88+
elseif sense == MOI.MAX_SENSE
89+
relax.scale = -one(T)
90+
end
91+
for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
92+
MOI.set(model, relax, F, S)
93+
end
94+
return
95+
end
96+
97+
function MOI.set(
98+
model::MOI.ModelLike,
99+
relax::FeasibilityRelaxation,
100+
::Type{F},
101+
::Type{S},
102+
) where {F,S}
103+
if MOI.supports(model, relax, MOI.ConstraintIndex{F,S})
104+
for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
105+
MOI.set(model, relax, ci)
106+
end
107+
end
108+
return
109+
end
110+
111+
function MOI.supports(
112+
::MOI.ModelLike,
113+
::FeasibilityRelaxation,
114+
::Type{MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet}},
115+
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
116+
return true
117+
end
118+
119+
function MOI.supports_fallback(
120+
::MOI.ModelLike,
121+
::FeasibilityRelaxation,
122+
::Type{MOI.ConstraintIndex{F,S}},
123+
) where {F,S}
124+
return false
125+
end
126+
127+
function MOI.set(
128+
model::MOI.ModelLike,
129+
relax::FeasibilityRelaxation,
130+
ci::MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet},
131+
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
132+
y = MOI.add_variable(model)
133+
z = MOI.add_variable(model)
134+
MOI.add_constraint(model, y, MOI.GreaterThan(zero(T)))
135+
MOI.add_constraint(model, z, MOI.GreaterThan(zero(T)))
136+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(y, one(T)))
137+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(z, -one(T)))
138+
a = relax.scale * get(relax.penalties, ci, one(T))
139+
O = MOI.get(model, MOI.ObjectiveFunctionType())
140+
obj = MOI.ObjectiveFunction{O}()
141+
MOI.modify(model, obj, MOI.ScalarCoefficientChange(y, a))
142+
MOI.modify(model, obj, MOI.ScalarCoefficientChange(z, a))
143+
return
144+
end
145+
146+
function MOI.set(
147+
model::MOI.ModelLike,
148+
relax::FeasibilityRelaxation,
149+
ci::MOI.ConstraintIndex{F,MOI.GreaterThan{T}},
150+
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
151+
# Performance optimization: we don't need the z relaxation variable.
152+
y = MOI.add_variable(model)
153+
MOI.add_constraint(model, y, MOI.GreaterThan(zero(T)))
154+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(y, one(T)))
155+
a = relax.scale * get(relax.penalties, ci, one(T))
156+
O = MOI.get(model, MOI.ObjectiveFunctionType())
157+
obj = MOI.ObjectiveFunction{O}()
158+
MOI.modify(model, obj, MOI.ScalarCoefficientChange(y, a))
159+
return
160+
end
161+
162+
function MOI.set(
163+
model::MOI.ModelLike,
164+
relax::FeasibilityRelaxation,
165+
ci::MOI.ConstraintIndex{F,MOI.LessThan{T}},
166+
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
167+
# Performance optimization: we don't need the y relaxation variable.
168+
z = MOI.add_variable(model)
169+
MOI.add_constraint(model, z, MOI.GreaterThan(zero(T)))
170+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(z, -one(T)))
171+
a = relax.scale * get(relax.penalties, ci, one(T))
172+
O = MOI.get(model, MOI.ObjectiveFunctionType())
173+
obj = MOI.ObjectiveFunction{O}()
174+
MOI.modify(model, obj, MOI.ScalarCoefficientChange(z, a))
175+
return
176+
end

0 commit comments

Comments
 (0)