Skip to content

Commit aaa36d0

Browse files
committed
Update
1 parent 9b6e6b9 commit aaa36d0

File tree

2 files changed

+99
-106
lines changed

2 files changed

+99
-106
lines changed

src/Utilities/penalty_relaxation.jl

Lines changed: 98 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -71,162 +71,143 @@ function _change_sense_to_min_if_necessary(
7171
::Type{T},
7272
model::MOI.ModelLike,
7373
) where {T}
74-
sense = MOI.get(model, MOI.ObjectiveSense())
75-
if sense != MOI.FEASIBILITY_SENSE
76-
return sense
74+
if MOI.get(model, MOI.ObjectiveSense()) != MOI.FEASIBILITY_SENSE
75+
return
7776
end
7877
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
7978
f = zero(MOI.ScalarAffineFunction{T})
8079
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
81-
return MOI.MIN_SENSE
80+
return
8281
end
8382

8483
function _add_penalty_to_objective(
8584
model::MOI.ModelLike,
8685
::Type{F},
87-
penalty::T,
88-
x::Vector{MOI.VariableIndex},
89-
) where {T,F<:MOI.VariableIndex}
90-
g = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(penalty, x), zero(T))
86+
penalty::MOI.ScalarAffineFunction{T},
87+
) where {
88+
T,
89+
F<:Union{
90+
MOI.VariableIndex,
91+
MOI.ScalarAffineFunction{T},
92+
MOI.ScalarQuadraticFunction{T},
93+
MOI.ScalarNonlinearFunction,
94+
},
95+
}
9196
f = MOI.get(model, MOI.ObjectiveFunction{F}())
92-
push!(g.terms, MOI.ScalarAffineTerm(one(T), f))
93-
MOI.set(model, MOI.ObjectiveFunction{typeof(g)}(), g)
94-
return MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(one(T), x), zero(T))
95-
end
96-
97-
function _add_penalty_to_objective(
98-
model::MOI.ModelLike,
99-
::Type{F},
100-
penalty::T,
101-
x::Vector{MOI.VariableIndex},
102-
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
103-
obj = MOI.ObjectiveFunction{F}()
104-
for xi in x
105-
MOI.modify(model, obj, MOI.ScalarCoefficientChange(xi, penalty))
97+
g = if MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE
98+
MOI.Utilities.operate(+, T, f, penalty)
99+
else
100+
MOI.Utilities.operate(-, T, f, penalty)
106101
end
107-
return MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(one(T), x), zero(T))
108-
end
109-
110-
function _add_penalty_to_objective(
111-
model::MOI.ModelLike,
112-
::Type{F},
113-
penalty::T,
114-
x::Vector{MOI.VariableIndex},
115-
) where {T,F<:MOI.ScalarNonlinearFunction}
116-
attr = MOI.ObjectiveFunction{F}()
117-
f = MOI.get(model, attr)
118-
g = Any[MOI.ScalarNonlinearFunction(:*, Any[penalty, xi]) for xi in x]
119-
MOI.set(model, attr, MOI.ScalarNonlinearFunction(:+, vcat(f, g)))
120-
return MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(one(T), x), zero(T))
102+
MOI.set(model, MOI.ObjectiveFunction{typeof(g)}(), g)
103+
return
121104
end
122105

123106
function _add_penalty_to_objective(
124107
::MOI.ModelLike,
125108
::Type{F},
126-
::T,
127-
::Vector{MOI.VariableIndex},
128-
) where {T,F}
109+
::MOI.ScalarAffineFunction,
110+
) where {F}
129111
return error(
130112
"Cannot perform `ScalarPenaltyRelaxation` with an objective function of type `$F`",
131113
)
132114
end
133115

134-
function MOI.modify(
116+
function _relax_constraint(
117+
::Type{T},
135118
model::MOI.ModelLike,
136119
ci::MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet},
137-
relax::ScalarPenaltyRelaxation{T},
138120
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
139-
sense = _change_sense_to_min_if_necessary(T, model)
140-
y = MOI.add_variable(model)
141-
z = MOI.add_variable(model)
142-
MOI.add_constraint(model, y, MOI.GreaterThan(zero(T)))
143-
MOI.add_constraint(model, z, MOI.GreaterThan(zero(T)))
144-
MOI.modify(model, ci, MOI.ScalarCoefficientChange(y, one(T)))
145-
MOI.modify(model, ci, MOI.ScalarCoefficientChange(z, -one(T)))
146-
penalty = sense == MOI.MIN_SENSE ? relax.penalty : -relax.penalty
147-
O = MOI.get(model, MOI.ObjectiveFunctionType())
148-
return _add_penalty_to_objective(model, O, penalty, [y, z])
121+
x = MOI.add_variables(model, 2)
122+
MOI.add_constraint.(model, x, MOI.GreaterThan(zero(T)))
123+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(x[1], one(T)))
124+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(x[2], -one(T)))
125+
return x
149126
end
150127

151-
function MOI.modify(
128+
function _relax_constraint(
129+
::Type{T},
152130
model::MOI.ModelLike,
153131
ci::MOI.ConstraintIndex{F,MOI.GreaterThan{T}},
154-
relax::ScalarPenaltyRelaxation{T},
155132
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
156-
sense = _change_sense_to_min_if_necessary(T, model)
157-
# Performance optimization: we don't need the z relaxation variable.
158-
y = MOI.add_variable(model)
159-
MOI.add_constraint(model, y, MOI.GreaterThan(zero(T)))
160-
MOI.modify(model, ci, MOI.ScalarCoefficientChange(y, one(T)))
161-
penalty = sense == MOI.MIN_SENSE ? relax.penalty : -relax.penalty
162-
O = MOI.get(model, MOI.ObjectiveFunctionType())
163-
return _add_penalty_to_objective(model, O, penalty, [y])
133+
x = MOI.add_variable(model)
134+
MOI.add_constraint(model, x, MOI.GreaterThan(zero(T)))
135+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(x, one(T)))
136+
return [x]
164137
end
165138

166-
function MOI.modify(
139+
function _relax_constraint(
140+
::Type{T},
167141
model::MOI.ModelLike,
168142
ci::MOI.ConstraintIndex{F,MOI.LessThan{T}},
169-
relax::ScalarPenaltyRelaxation{T},
170143
) where {T,F<:Union{MOI.ScalarAffineFunction{T},MOI.ScalarQuadraticFunction{T}}}
171-
sense = _change_sense_to_min_if_necessary(T, model)
172-
# Performance optimization: we don't need the y relaxation variable.
173-
z = MOI.add_variable(model)
174-
MOI.add_constraint(model, z, MOI.GreaterThan(zero(T)))
175-
MOI.modify(model, ci, MOI.ScalarCoefficientChange(z, -one(T)))
176-
penalty = sense == MOI.MIN_SENSE ? relax.penalty : -relax.penalty
177-
O = MOI.get(model, MOI.ObjectiveFunctionType())
178-
return _add_penalty_to_objective(model, O, penalty, [z])
144+
x = MOI.add_variable(model)
145+
MOI.add_constraint(model, x, MOI.GreaterThan(zero(T)))
146+
MOI.modify(model, ci, MOI.ScalarCoefficientChange(x, -one(T)))
147+
return [x]
179148
end
180149

181-
function MOI.modify(
150+
function _relax_constraint(
151+
::Type{T},
182152
model::MOI.ModelLike,
183153
ci::MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,S},
184-
relax::ScalarPenaltyRelaxation{T},
185154
) where {T,S<:MOI.AbstractScalarSet}
186-
sense = _change_sense_to_min_if_necessary(T, model)
155+
x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
187156
y, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
188-
z, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
189157
f = MOI.get(model, MOI.ConstraintFunction(), ci)
190-
f = MOI.ScalarNonlinearFunction(
158+
g = MOI.ScalarNonlinearFunction(
191159
:+,
192-
Any[f, y, MOI.ScalarNonlinearFunction(:-, Any[z])],
160+
Any[f, x, MOI.ScalarNonlinearFunction(:-, Any[y])],
193161
)
194-
MOI.set(model, MOI.ConstraintFunction(), ci, f)
195-
penalty = sense == MOI.MIN_SENSE ? relax.penalty : -relax.penalty
196-
O = MOI.get(model, MOI.ObjectiveFunctionType())
197-
return _add_penalty_to_objective(model, O, penalty, [y, z])
162+
MOI.set(model, MOI.ConstraintFunction(), ci, g)
163+
return [x, y]
198164
end
199165

200-
function MOI.modify(
166+
function _relax_constraint(
167+
::Type{T},
201168
model::MOI.ModelLike,
202169
ci::MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.GreaterThan{T}},
203-
relax::ScalarPenaltyRelaxation{T},
204170
) where {T}
205-
sense = _change_sense_to_min_if_necessary(T, model)
206-
y = MOI.add_variable(model)
207-
MOI.add_constraint(model, y, MOI.GreaterThan(zero(T)))
208-
func = MOI.get(model, MOI.ConstraintFunction(), ci)
209-
newfunc = MOI.ScalarNonlinearFunction(:+, [func, y])
210-
MOI.set(model, MOI.ConstraintFunction(), ci, newfunc)
211-
penalty = sense == MOI.MIN_SENSE ? relax.penalty : -relax.penalty
212-
O = MOI.get(model, MOI.ObjectiveFunctionType())
213-
return _add_penalty_to_objective(model, O, penalty, [y])
171+
x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
172+
f = MOI.get(model, MOI.ConstraintFunction(), ci)
173+
g = MOI.ScalarNonlinearFunction(:+, [f, x])
174+
MOI.set(model, MOI.ConstraintFunction(), ci, g)
175+
return [x]
214176
end
215177

216-
function MOI.modify(
178+
function _relax_constraint(
179+
::Type{T},
217180
model::MOI.ModelLike,
218181
ci::MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.LessThan{T}},
219-
relax::ScalarPenaltyRelaxation{T},
220182
) where {T}
221-
sense = _change_sense_to_min_if_necessary(T, model)
222-
z = MOI.add_variable(model)
223-
MOI.add_constraint(model, z, MOI.GreaterThan(zero(T)))
224-
func = MOI.get(model, MOI.ConstraintFunction(), ci)
225-
newfunc = MOI.ScalarNonlinearFunction(:-, [func, z])
226-
MOI.set(model, MOI.ConstraintFunction(), ci, newfunc)
227-
penalty = sense == MOI.MIN_SENSE ? relax.penalty : -relax.penalty
183+
x, _ = MOI.add_constrained_variable(model, MOI.GreaterThan(zero(T)))
184+
f = MOI.get(model, MOI.ConstraintFunction(), ci)
185+
g = MOI.ScalarNonlinearFunction(:-, [f, x])
186+
MOI.set(model, MOI.ConstraintFunction(), ci, g)
187+
return [x]
188+
end
189+
190+
function MOI.modify(
191+
model::MOI.ModelLike,
192+
ci::MOI.ConstraintIndex{F,<:MOI.AbstractScalarSet},
193+
relax::ScalarPenaltyRelaxation{T},
194+
) where {
195+
T,
196+
F<:Union{
197+
MOI.ScalarAffineFunction{T},
198+
MOI.ScalarQuadraticFunction{T},
199+
MOI.ScalarNonlinearFunction,
200+
},
201+
}
202+
x = _relax_constraint(T, model, ci)
203+
p = MOI.ScalarAffineFunction(
204+
MOI.ScalarAffineTerm.(relax.penalty, x),
205+
zero(T),
206+
)
207+
_change_sense_to_min_if_necessary(T, model)
228208
O = MOI.get(model, MOI.ObjectiveFunctionType())
229-
return _add_penalty_to_objective(model, O, penalty, [z])
209+
_add_penalty_to_objective(model, O, p)
210+
return MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(one(T), x), zero(T))
230211
end
231212

232213
"""
@@ -361,13 +342,20 @@ end
361342

362343
function MOI.modify(model::MOI.ModelLike, relax::PenaltyRelaxation{T}) where {T}
363344
map = Dict{MOI.ConstraintIndex,MOI.ScalarAffineFunction{T}}()
345+
penalty_expr = zero(MOI.ScalarAffineFunction{T})
364346
for (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
365-
_modify_penalty_relaxation(map, model, relax, F, S)
347+
_modify_penalty_relaxation(penalty_expr, map, model, relax, F, S)
348+
end
349+
if !isempty(penalty_expr.terms)
350+
_change_sense_to_min_if_necessary(T, model)
351+
O = MOI.get(model, MOI.ObjectiveFunctionType())
352+
_add_penalty_to_objective(model, O, penalty_expr)
366353
end
367354
return map
368355
end
369356

370357
function _modify_penalty_relaxation(
358+
penalty_expr::MOI.ScalarAffineFunction{T},
371359
map::Dict{MOI.ConstraintIndex,MOI.ScalarAffineFunction{T}},
372360
model::MOI.ModelLike,
373361
relax::PenaltyRelaxation,
@@ -380,9 +368,14 @@ function _modify_penalty_relaxation(
380368
continue
381369
end
382370
try
383-
map[ci] = MOI.modify(model, ci, ScalarPenaltyRelaxation(penalty))
371+
x = _relax_constraint(T, model, ci)
372+
map[ci] = MOI.ScalarAffineFunction(
373+
MOI.ScalarAffineTerm.(one(T), x),
374+
zero(T),
375+
)
376+
append!(penalty_expr.terms, MOI.ScalarAffineTerm{T}.(penalty, x))
384377
catch err
385-
if err isa MethodError && err.f == MOI.modify
378+
if err isa MethodError && err.f == _relax_constraint
386379
if relax.warn
387380
@warn(
388381
"Skipping PenaltyRelaxation for ConstraintIndex{$F,$S}"

test/Utilities/penalty_relaxation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function test_relax_scalar_nonlinear_objective()
110110
""",
111111
"""
112112
variables: x, y, a
113-
minobjective: ScalarNonlinearFunction(+(exp(x), *(1.0, a)))
113+
minobjective: ScalarNonlinearFunction(+(exp(x), esc(1.0 * a)))
114114
c1: x + y + -1.0 * a <= 1.0
115115
a >= 0.0
116116
""",

0 commit comments

Comments
 (0)