4
4
# Use of this source code is governed by an MIT-style license that can be found
5
5
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6
6
7
+ """
8
+ ScalarPenaltyRelaxation(penalty::T) where {T}
9
+
10
+ A problem modifier that, when passed to [`MOI.modify`](@ref), destructively
11
+ modifies the constraint in-place to create a penalized relaxation of the
12
+ constraint.
13
+
14
+ !!! warning
15
+ This is a destructive routine that modifies the constraint in-place. If you
16
+ don't want to modify the original model, use `JuMP.copy_model` to create a
17
+ copy before calling [`MOI.modify`](@ref).
18
+
19
+ ## Reformulation
20
+
21
+ The penalty relaxation modifies constraints of the form ``f(x) \\ in S`` into
22
+ ``f(x) + y - z \\ in S``, where ``y, z \\ ge 0``, and then it introduces a penalty
23
+ term into the objective of ``a \\ times (y + z)`` (if minimizing, else ``-a``),
24
+ where `a` is the value in the `penalties` dictionary associated with the
25
+ constraint that is being relaxed. If no value exists, the default is `default`.
26
+
27
+ When `S` is [`MOI.LessThan`](@ref) or [`MOI.GreaterThan`](@ref), we omit `y` or
28
+ `z` respectively as a performance optimization.
29
+
30
+ ## Return value
31
+
32
+ `MOI.modify(model, ci, ScalarPenaltyRelaxation(penalty))` returns a
33
+ [`MOI.ScalarAffineFunction`](@ref) comprised of `y + z`. In an optimal solution,
34
+ query the value of this function to compute the violation of the constraint.
35
+
36
+ ## Examples
37
+
38
+ ```jldoctest; setup=:(import MathOptInterface; const MOI = MathOptInterface)
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> f = MOI.modify(model, c, MOI.Utilities.ScalarPenaltyRelaxation(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
+ julia> f isa MOI.ScalarAffineFunction{Float64}
60
+ true
61
+ ```
62
+ """
63
+ struct ScalarPenaltyRelaxation{T}
64
+ penalty:: T
65
+ end
66
+
67
+ function MOI. supports (
68
+ :: MOI.ModelLike ,
69
+ :: ScalarPenaltyRelaxation{T} ,
70
+ :: Type{<:MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet}} ,
71
+ ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
72
+ return true
73
+ end
74
+
75
+ function MOI. supports (
76
+ :: MOI.ModelLike ,
77
+ :: ScalarPenaltyRelaxation ,
78
+ :: Type{<:MOI.ConstraintIndex} ,
79
+ )
80
+ return false
81
+ end
82
+
83
+ function MOI. modify (
84
+ model:: MOI.ModelLike ,
85
+ ci:: MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet} ,
86
+ relax:: ScalarPenaltyRelaxation{T} ,
87
+ ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
88
+ y = MOI. add_variable (model)
89
+ z = MOI. add_variable (model)
90
+ MOI. add_constraint (model, y, MOI. GreaterThan (zero (T)))
91
+ MOI. add_constraint (model, z, MOI. GreaterThan (zero (T)))
92
+ MOI. modify (model, ci, MOI. ScalarCoefficientChange (y, one (T)))
93
+ MOI. modify (model, ci, MOI. ScalarCoefficientChange (z, - one (T)))
94
+ sense = MOI. get (model, MOI. ObjectiveSense ())
95
+ scale = sense == MOI. MIN_SENSE ? one (T) : - one (T)
96
+ a = scale * relax. penalty
97
+ O = MOI. get (model, MOI. ObjectiveFunctionType ())
98
+ obj = MOI. ObjectiveFunction {O} ()
99
+ MOI. modify (model, obj, MOI. ScalarCoefficientChange (y, a))
100
+ MOI. modify (model, obj, MOI. ScalarCoefficientChange (z, a))
101
+ return one (T) * y + one (T) * z
102
+ end
103
+
104
+ function MOI. modify (
105
+ model:: MOI.ModelLike ,
106
+ ci:: MOI.ConstraintIndex{F,MOI.GreaterThan{T}} ,
107
+ relax:: ScalarPenaltyRelaxation{T} ,
108
+ ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
109
+ # Performance optimization: we don't need the z relaxation variable.
110
+ y = MOI. add_variable (model)
111
+ MOI. add_constraint (model, y, MOI. GreaterThan (zero (T)))
112
+ MOI. modify (model, ci, MOI. ScalarCoefficientChange (y, one (T)))
113
+ sense = MOI. get (model, MOI. ObjectiveSense ())
114
+ scale = sense == MOI. MIN_SENSE ? one (T) : - one (T)
115
+ a = scale * relax. penalty
116
+ O = MOI. get (model, MOI. ObjectiveFunctionType ())
117
+ obj = MOI. ObjectiveFunction {O} ()
118
+ MOI. modify (model, obj, MOI. ScalarCoefficientChange (y, a))
119
+ return one (T) * y
120
+ end
121
+
122
+ function MOI. modify (
123
+ model:: MOI.ModelLike ,
124
+ ci:: MOI.ConstraintIndex{F,MOI.LessThan{T}} ,
125
+ relax:: ScalarPenaltyRelaxation{T} ,
126
+ ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
127
+ # Performance optimization: we don't need the y relaxation variable.
128
+ z = MOI. add_variable (model)
129
+ MOI. add_constraint (model, z, MOI. GreaterThan (zero (T)))
130
+ MOI. modify (model, ci, MOI. ScalarCoefficientChange (z, - one (T)))
131
+ sense = MOI. get (model, MOI. ObjectiveSense ())
132
+ scale = sense == MOI. MIN_SENSE ? one (T) : - one (T)
133
+ a = scale * relax. penalty
134
+ O = MOI. get (model, MOI. ObjectiveFunctionType ())
135
+ obj = MOI. ObjectiveFunction {O} ()
136
+ MOI. modify (model, obj, MOI. ScalarCoefficientChange (z, a))
137
+ return one (T) * z
138
+ end
139
+
7
140
"""
8
141
PenaltyRelaxation(
9
142
penalties = Dict{MOI.ConstraintIndex,Float64}();
@@ -16,15 +149,18 @@ modifies the model in-place to create a penalized relaxation of the constraints.
16
149
!!! warning
17
150
This is a destructive routine that modifies the model in-place. If you don't
18
151
want to modify the original model, use `JuMP.copy_model` to create a copy
19
- before setting this attribute .
152
+ before calling [`MOI.modify`](@ref) .
20
153
21
154
## Reformulation
22
155
23
156
The penalty relaxation modifies constraints of the form ``f(x) \\ in S`` into
24
157
``f(x) + y - z \\ in S``, where ``y, z \\ ge 0``, and then it introduces a penalty
25
158
term into the objective of ``a \\ times (y + z)`` (if minimizing, else ``-a``),
26
159
where `a` is the value in the `penalties` dictionary associated with the
27
- constraint that is being relaxed. If no value exists, the default is `default`.
160
+ constraint that is being relaxed.
161
+
162
+ If no value exists for the constraint in `penalties`, the penalty is `default`.
163
+ If `default` is also `nothing`, then the constraint is skipped.
28
164
29
165
When `S` is [`MOI.LessThan`](@ref) or [`MOI.GreaterThan`](@ref), we omit `y` or
30
166
`z` respectively as a performance optimization.
@@ -138,11 +274,7 @@ function MOI.modify(model::MOI.ModelLike, relax::PenaltyRelaxation{T}) where {T}
138
274
end
139
275
map = Dict {MOI.ConstraintIndex,MOI.ScalarAffineFunction{T}} ()
140
276
for (F, S) in MOI. get (model, MOI. ListOfConstraintTypesPresent ())
141
- if MOI. supports (model, relax, MOI. ConstraintIndex{F,S})
142
- _modify_penalty_relaxation (map, model, relax, F, S)
143
- else
144
- @warn (" Skipping PenaltyRelaxation of constraints of type $F -in-$S " )
145
- end
277
+ _modify_penalty_relaxation (map, model, relax, F, S)
146
278
end
147
279
return map
148
280
end
@@ -155,85 +287,16 @@ function _modify_penalty_relaxation(
155
287
:: Type{S} ,
156
288
) where {T,F,S}
157
289
for ci in MOI. get (model, MOI. ListOfConstraintIndices {F,S} ())
158
- if relax. default != = nothing || haskey (relax. penalties, ci)
159
- f = MOI. modify (model, ci, relax)
160
- if f != = nothing
161
- map[ci] = f
162
- end
290
+ penalty = get (relax. penalties, ci, relax. default)
291
+ if penalty === nothing
292
+ continue
163
293
end
294
+ attr = ScalarPenaltyRelaxation (penalty)
295
+ if ! MOI. supports (model, attr, MOI. ConstraintIndex{F,S})
296
+ @warn (" Skipping PenaltyRelaxation of constraints of type $F -in-$S " )
297
+ return
298
+ end
299
+ map[ci] = MOI. modify (model, ci, attr)
164
300
end
165
301
return map
166
302
end
167
-
168
- function MOI. supports (
169
- :: MOI.ModelLike ,
170
- :: PenaltyRelaxation{T} ,
171
- :: Type{<:MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet}} ,
172
- ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
173
- return true
174
- end
175
-
176
- function MOI. supports (
177
- :: MOI.ModelLike ,
178
- :: PenaltyRelaxation ,
179
- :: Type{<:MOI.ConstraintIndex} ,
180
- )
181
- return false
182
- end
183
-
184
- function MOI. modify (
185
- model:: MOI.ModelLike ,
186
- ci:: MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet} ,
187
- relax:: PenaltyRelaxation{T} ,
188
- ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
189
- y = MOI. add_variable (model)
190
- z = MOI. add_variable (model)
191
- MOI. add_constraint (model, y, MOI. GreaterThan (zero (T)))
192
- MOI. add_constraint (model, z, MOI. GreaterThan (zero (T)))
193
- MOI. modify (model, ci, MOI. ScalarCoefficientChange (y, one (T)))
194
- MOI. modify (model, ci, MOI. ScalarCoefficientChange (z, - one (T)))
195
- sense = MOI. get (model, MOI. ObjectiveSense ())
196
- scale = sense == MOI. MIN_SENSE ? one (T) : - one (T)
197
- a = scale * get (relax. penalties, ci, relax. default)
198
- O = MOI. get (model, MOI. ObjectiveFunctionType ())
199
- obj = MOI. ObjectiveFunction {O} ()
200
- MOI. modify (model, obj, MOI. ScalarCoefficientChange (y, a))
201
- MOI. modify (model, obj, MOI. ScalarCoefficientChange (z, a))
202
- return one (T) * y + one (T) * z
203
- end
204
-
205
- function MOI. modify (
206
- model:: MOI.ModelLike ,
207
- ci:: MOI.ConstraintIndex{F,MOI.GreaterThan{T}} ,
208
- relax:: PenaltyRelaxation{T} ,
209
- ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
210
- # Performance optimization: we don't need the z relaxation variable.
211
- y = MOI. add_variable (model)
212
- MOI. add_constraint (model, y, MOI. GreaterThan (zero (T)))
213
- MOI. modify (model, ci, MOI. ScalarCoefficientChange (y, one (T)))
214
- sense = MOI. get (model, MOI. ObjectiveSense ())
215
- scale = sense == MOI. MIN_SENSE ? one (T) : - one (T)
216
- a = scale * get (relax. penalties, ci, relax. default)
217
- O = MOI. get (model, MOI. ObjectiveFunctionType ())
218
- obj = MOI. ObjectiveFunction {O} ()
219
- MOI. modify (model, obj, MOI. ScalarCoefficientChange (y, a))
220
- return one (T) * y
221
- end
222
-
223
- function MOI. modify (
224
- model:: MOI.ModelLike ,
225
- ci:: MOI.ConstraintIndex{F,MOI.LessThan{T}} ,
226
- relax:: PenaltyRelaxation{T} ,
227
- ) where {T,F<: Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}} }
228
- # Performance optimization: we don't need the y relaxation variable.
229
- z = MOI. add_variable (model)
230
- MOI. add_constraint (model, z, MOI. GreaterThan (zero (T)))
231
- MOI. modify (model, ci, MOI. ScalarCoefficientChange (z, - one (T)))
232
- sense = MOI. get (model, MOI. ObjectiveSense ())
233
- scale = sense == MOI. MIN_SENSE ? one (T) : - one (T)
234
- a = scale * get (relax. penalties, ci, relax. default)
235
- O = MOI. get (model, MOI. ObjectiveFunctionType ())
236
- obj = MOI. ObjectiveFunction {O} ()
237
- MOI. modify (model, obj, MOI. ScalarCoefficientChange (z, a))
238
- return one (T) * z
239
- end
0 commit comments