Skip to content

Commit 3fb87d8

Browse files
committed
Rename to PenaltyRelaxation
1 parent 94065ac commit 3fb87d8

File tree

5 files changed

+82
-42
lines changed

5 files changed

+82
-42
lines changed

docs/src/submodules/Utilities/overview.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,9 @@ $$ \begin{aligned}
315315
In IJulia, calling `print` or ending a cell with
316316
[`Utilities.latex_formulation`](@ref) will render the model in LaTeX.
317317

318-
## Utilities.FeasibilityRelaxation
318+
## Utilities.PenaltyRelaxation
319319

320-
Pass [`Utilities.FeasibilityRelaxation`](@ref) to [`modify`](@ref) to relax the
320+
Pass [`Utilities.PenaltyRelaxation`](@ref) to [`modify`](@ref) to relax the
321321
problem by adding penalized slack variables to the constraints. This is helpful
322322
when debugging sources of infeasible models.
323323

@@ -330,7 +330,7 @@ julia> MOI.set(model, MOI.VariableName(), x, "x")
330330
331331
julia> c = MOI.add_constraint(model, 1.0 * x, MOI.LessThan(2.0));
332332
333-
julia> MOI.modify(model, MOI.Utilities.FeasibilityRelaxation(Dict(c => 2.0)))
333+
julia> MOI.modify(model, MOI.Utilities.PenaltyRelaxation(Dict(c => 2.0)))
334334
335335
julia> print(model)
336336
Minimize ScalarAffineFunction{Float64}:

docs/src/submodules/Utilities/reference.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Utilities.ModelFilter
9393
## Feasibility relaxation
9494

9595
```@docs
96-
Utilities.FeasibilityRelaxation
96+
Utilities.PenaltyRelaxation
9797
```
9898

9999
## MatrixOfConstraints

src/Utilities/Utilities.jl

+1-1
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-
include("feasibility_relaxation.jl")
80+
include("penalty_relaxation.jl")
8181
include("lazy_iterators.jl")
8282

8383
include("precompile.jl")

src/Utilities/feasibility_relaxation.jl src/Utilities/penalty_relaxation.jl

+73-33
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
66

77
"""
8-
FeasibilityRelaxation(
9-
penalties = Dict{MOI.ConstraintIndex,Float64}(),
8+
PenaltyRelaxation(
9+
penalties = Dict{MOI.ConstraintIndex,Float64}();
10+
default::Union{Nothing,T} = 1.0,
1011
)
1112
1213
A problem modifier that, when passed to [`MOI.modify`](@ref), destructively
13-
modifies the model in-place to create a feasibility relaxation.
14+
modifies the model in-place to create a penalized relaxation of the constraints.
1415
1516
!!! warning
1617
This is a destructive routine that modifies the model in-place. If you don't
@@ -19,26 +20,33 @@ modifies the model in-place to create a feasibility relaxation.
1920
2021
## Reformulation
2122
22-
The feasibility relaxation modifies constraints of the form ``f(x) \\in S`` into
23+
The penalty relaxation modifies constraints of the form ``f(x) \\in S`` into
2324
``f(x) + y - z \\in S``, where ``y, z \\ge 0``, and then it introduces a penalty
2425
term into the objective of ``a \\times (y + z)`` (if minimizing, else ``-a``),
2526
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+
constraint that is being relaxed. If no value exists, the default is `default`.
2728
2829
When `S` is [`MOI.LessThan`](@ref) or [`MOI.GreaterThan`](@ref), we omit `y` or
2930
`z` respectively as a performance optimization.
3031
32+
## Relax a subset of constraints
33+
34+
To relax a subset of constraints, pass a `penalties` dictionary` and set
35+
`default = nothing`.
36+
3137
## Supported constraint types
3238
33-
The feasibility relaxation is currently limited to modifying
39+
The penalty relaxation is currently limited to modifying
3440
[`MOI.ScalarAffineFunction`](@ref) and [`MOI.ScalarQuadraticFunction`](@ref)
3541
constraints in the linear sets [`MOI.LessThan`](@ref), [`MOI.GreaterThan`](@ref),
3642
[`MOI.EqualTo`](@ref) and [`MOI.Interval`](@ref).
3743
3844
It does not include variable bound or integrality constraints, because these
3945
cannot be modified in-place.
4046
41-
## Example
47+
To modify variable bounds, rewrite them as linear constraints.
48+
49+
## Examples
4250
4351
```jldoctest; setup=:(import MathOptInterface; const MOI = MathOptInterface)
4452
julia> model = MOI.Utilities.Model{Float64}();
@@ -47,90 +55,118 @@ julia> x = MOI.add_variable(model);
4755
4856
julia> c = MOI.add_constraint(model, 1.0 * x, MOI.LessThan(2.0));
4957
50-
julia> MOI.modify(model, MOI.Utilities.FeasibilityRelaxation(Dict(c => 2.0)))
58+
julia> MOI.modify(model, MOI.Utilities.PenaltyRelaxation(default = 2.0))
5159
5260
julia> print(model)
5361
Minimize ScalarAffineFunction{Float64}:
5462
0.0 + 2.0 v[2]
5563
5664
Subject to:
5765
66+
ScalarAffineFunction{Float64}-in-LessThan{Float64}
67+
0.0 + 1.0 v[1] - 1.0 v[2] <= 2.0
68+
69+
VariableIndex-in-GreaterThan{Float64}
70+
v[2] >= 0.0
71+
```
72+
73+
```jldoctest; setup=:(import MathOptInterface; const MOI = MathOptInterface)
74+
julia> model = MOI.Utilities.Model{Float64}();
75+
76+
julia> x = MOI.add_variable(model);
77+
78+
julia> c = MOI.add_constraint(model, 1.0 * x, MOI.LessThan(2.0));
79+
80+
julia> MOI.modify(model, MOI.Utilities.PenaltyRelaxation(Dict(c => 3.0)))
81+
82+
julia> print(model)
83+
Minimize ScalarAffineFunction{Float64}:
84+
0.0 + 3.0 v[2]
85+
86+
Subject to:
87+
5888
ScalarAffineFunction{Float64}-in-LessThan{Float64}
5989
0.0 + 1.0 v[1] - 1.0 v[2] <= 2.0
6090
6191
VariableIndex-in-GreaterThan{Float64}
6292
v[2] >= 0.0
6393
```
6494
"""
65-
mutable struct FeasibilityRelaxation{T}
95+
mutable struct PenaltyRelaxation{T}
96+
default::Union{Nothing,T}
6697
penalties::Dict{MOI.ConstraintIndex,T}
67-
scale::T
68-
function FeasibilityRelaxation(p::Dict{MOI.ConstraintIndex,T}) where {T}
69-
return new{T}(p, zero(T))
98+
99+
function PenaltyRelaxation(
100+
p::Dict{MOI.ConstraintIndex,T};
101+
default::T = one(T),
102+
) where {T}
103+
return new{T}(default, p)
70104
end
71105
end
72106

73-
function FeasibilityRelaxation()
74-
return FeasibilityRelaxation(Dict{MOI.ConstraintIndex,Float64}())
107+
function PenaltyRelaxation(; kwargs...)
108+
return PenaltyRelaxation(Dict{MOI.ConstraintIndex,Float64}(); kwargs...)
75109
end
76110

77-
function FeasibilityRelaxation(d::Dict{<:MOI.ConstraintIndex,T}) where {T}
78-
return FeasibilityRelaxation(convert(Dict{MOI.ConstraintIndex,T}, d))
111+
function PenaltyRelaxation(
112+
d::Dict{<:MOI.ConstraintIndex,T};
113+
kwargs...,
114+
) where {T}
115+
return PenaltyRelaxation(convert(Dict{MOI.ConstraintIndex,T}, d); kwargs...)
79116
end
80117

81118
function MOI.modify(
82119
model::MOI.ModelLike,
83-
relax::FeasibilityRelaxation{T},
120+
relax::PenaltyRelaxation{T},
84121
) where {T}
85122
sense = MOI.get(model, MOI.ObjectiveSense())
86123
if sense == MOI.FEASIBILITY_SENSE
87-
relax.scale = one(T)
88124
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
89125
f = zero(MOI.ScalarAffineFunction{T})
90126
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
91-
elseif sense == MOI.MIN_SENSE
92-
relax.scale = one(T)
93-
elseif sense == MOI.MAX_SENSE
94-
relax.scale = -one(T)
95127
end
96128
for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
97-
_modify_feasibility_relaxation(model, relax, F, S)
129+
_modify_penalty_relaxation(model, relax, F, S)
98130
end
99131
return
100132
end
101133

102-
function _modify_feasibility_relaxation(
134+
function _modify_penalty_relaxation(
103135
model::MOI.ModelLike,
104-
relax::FeasibilityRelaxation,
136+
relax::PenaltyRelaxation,
105137
::Type{F},
106138
::Type{S},
107139
) where {F,S}
108140
for ci in MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
109-
MOI.modify(model, ci, relax)
141+
if relax.default !== nothing || haskey(relax.penalties, ci)
142+
MOI.modify(model, ci, relax)
143+
end
110144
end
111145
return
112146
end
113147

114148
function MOI.modify(
115149
::MOI.ModelLike,
116150
::MOI.ConstraintIndex,
117-
::FeasibilityRelaxation,
151+
::PenaltyRelaxation,
118152
)
119153
return # Silently skip modifying other constraint types.
120154
end
121155

122156
function MOI.modify(
123157
model::MOI.ModelLike,
124158
ci::MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet},
125-
relax::FeasibilityRelaxation{T},
159+
relax::PenaltyRelaxation{T},
126160
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
127161
y = MOI.add_variable(model)
128162
z = MOI.add_variable(model)
129163
MOI.add_constraint(model, y, MOI.GreaterThan(zero(T)))
130164
MOI.add_constraint(model, z, MOI.GreaterThan(zero(T)))
131165
MOI.modify(model, ci, MOI.ScalarCoefficientChange(y, one(T)))
132166
MOI.modify(model, ci, MOI.ScalarCoefficientChange(z, -one(T)))
133-
a = relax.scale * get(relax.penalties, ci, one(T))
167+
sense = MOI.get(model, MOI.ObjectiveSense())
168+
scale = sense == MOI.MIN_SENSE ? one(T) : -one(T)
169+
a = scale * get(relax.penalties, ci, relax.default)
134170
O = MOI.get(model, MOI.ObjectiveFunctionType())
135171
obj = MOI.ObjectiveFunction{O}()
136172
MOI.modify(model, obj, MOI.ScalarCoefficientChange(y, a))
@@ -141,13 +177,15 @@ end
141177
function MOI.modify(
142178
model::MOI.ModelLike,
143179
ci::MOI.ConstraintIndex{F,MOI.GreaterThan{T}},
144-
relax::FeasibilityRelaxation{T},
180+
relax::PenaltyRelaxation{T},
145181
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
146182
# Performance optimization: we don't need the z relaxation variable.
147183
y = MOI.add_variable(model)
148184
MOI.add_constraint(model, y, MOI.GreaterThan(zero(T)))
149185
MOI.modify(model, ci, MOI.ScalarCoefficientChange(y, one(T)))
150-
a = relax.scale * get(relax.penalties, ci, one(T))
186+
sense = MOI.get(model, MOI.ObjectiveSense())
187+
scale = sense == MOI.MIN_SENSE ? one(T) : -one(T)
188+
a = scale * get(relax.penalties, ci, relax.default)
151189
O = MOI.get(model, MOI.ObjectiveFunctionType())
152190
obj = MOI.ObjectiveFunction{O}()
153191
MOI.modify(model, obj, MOI.ScalarCoefficientChange(y, a))
@@ -157,13 +195,15 @@ end
157195
function MOI.modify(
158196
model::MOI.ModelLike,
159197
ci::MOI.ConstraintIndex{F,MOI.LessThan{T}},
160-
relax::FeasibilityRelaxation{T},
198+
relax::PenaltyRelaxation{T},
161199
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
162200
# Performance optimization: we don't need the y relaxation variable.
163201
z = MOI.add_variable(model)
164202
MOI.add_constraint(model, z, MOI.GreaterThan(zero(T)))
165203
MOI.modify(model, ci, MOI.ScalarCoefficientChange(z, -one(T)))
166-
a = relax.scale * get(relax.penalties, ci, one(T))
204+
sense = MOI.get(model, MOI.ObjectiveSense())
205+
scale = sense == MOI.MIN_SENSE ? one(T) : -one(T)
206+
a = scale * get(relax.penalties, ci, relax.default)
167207
O = MOI.get(model, MOI.ObjectiveFunctionType())
168208
obj = MOI.ObjectiveFunction{O}()
169209
MOI.modify(model, obj, MOI.ScalarCoefficientChange(z, a))

test/Utilities/feasibility_relaxation.jl test/Utilities/penalty_relaxation.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Use of this source code is governed by an MIT-style license that can be found
55
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
66

7-
module TestFeasibilityRelaxation
7+
module TestPenaltyRelaxation
88

99
using Test
1010
using MathOptInterface
@@ -25,7 +25,7 @@ end
2525
function _test_roundtrip(src_str, relaxed_str)
2626
model = MOI.Utilities.Model{Float64}()
2727
MOI.Utilities.loadfromstring!(model, src_str)
28-
MOI.modify(model, MOI.Utilities.FeasibilityRelaxation())
28+
MOI.modify(model, MOI.Utilities.PenaltyRelaxation())
2929
dest = MOI.Utilities.Model{Float64}()
3030
MOI.Utilities.loadfromstring!(dest, relaxed_str)
3131
MOI.Bridges._test_structural_identical(model, dest)
@@ -212,7 +212,7 @@ function test_penalties()
212212
model = MOI.Utilities.Model{Float64}()
213213
x = MOI.add_variable(model)
214214
c = MOI.add_constraint(model, 1.0 * x, MOI.EqualTo(2.0))
215-
MOI.modify(model, MOI.Utilities.FeasibilityRelaxation(Dict(c => 2.0)))
215+
MOI.modify(model, MOI.Utilities.PenaltyRelaxation(Dict(c => 2.0)))
216216
@test sprint(print, model) === """
217217
Minimize ScalarAffineFunction{Float64}:
218218
0.0 + 2.0 v[2] + 2.0 v[3]
@@ -231,4 +231,4 @@ end
231231

232232
end
233233

234-
TestFeasibilityRelaxation.runtests()
234+
TestPenaltyRelaxation.runtests()

0 commit comments

Comments
 (0)