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+ """ 
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+ 
7140""" 
8141    PenaltyRelaxation( 
9142        penalties = Dict{MOI.ConstraintIndex,Float64}(); 
@@ -16,15 +149,18 @@ modifies the model in-place to create a penalized relaxation of the constraints.
16149!!! warning 
17150    This is a destructive routine that modifies the model in-place. If you don't 
18151    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) . 
20153
21154## Reformulation 
22155
23156The penalty relaxation modifies constraints of the form ``f(x) \\ in S`` into 
24157``f(x) + y - z \\ in S``, where ``y, z \\ ge 0``, and then it introduces a penalty 
25158term into the objective of ``a \\ times (y + z)`` (if minimizing, else ``-a``), 
26159where `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. 
28164
29165When `S` is [`MOI.LessThan`](@ref) or [`MOI.GreaterThan`](@ref), we omit `y` or 
30166`z` respectively as a performance optimization. 
@@ -138,11 +274,7 @@ function MOI.modify(model::MOI.ModelLike, relax::PenaltyRelaxation{T}) where {T}
138274    end 
139275    map =  Dict {MOI.ConstraintIndex,MOI.ScalarAffineFunction{T}} ()
140276    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)
146278    end 
147279    return  map
148280end 
@@ -155,85 +287,16 @@ function _modify_penalty_relaxation(
155287    :: Type{S} ,
156288) where  {T,F,S}
157289    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 
163293        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)
164300    end 
165301    return  map
166302end 
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