Skip to content

Commit d082808

Browse files
authored
Merge pull request #1101 from ilancoulon/supports_add_constrained_variable
Supports add constrained variable
2 parents 535e7f0 + edf8e48 commit d082808

File tree

4 files changed

+138
-15
lines changed

4 files changed

+138
-15
lines changed

src/Bridges/lazy_bridge_optimizer.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ end
113113

114114
function node(b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet})
115115
F = MOIU.variable_function_type(S)
116-
if MOI.supports_constraint(b.model, F, S)
116+
if (MOI.supports_constraint(b.model, F, S)
117+
|| (S <: MOI.AbstractScalarSet && MOI.supports_add_constrained_variable(b.model, S))
118+
|| (S <: MOI.AbstractVectorSet && MOI.supports_add_constrained_variables(b.model, S)))
117119
return VariableNode(0)
118120
end
119121
variable_node = get(b.variable_node, (S,), nothing)

src/Utilities/copy.jl

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -302,13 +302,15 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike, copy_names::Bo
302302

303303
vis_src = MOI.get(src, MOI.ListOfVariableIndices())
304304
constraint_types = MOI.get(src, MOI.ListOfConstraints())
305-
single_variable_types = [S for (F, S) in constraint_types
306-
if F == MOI.SingleVariable]
307-
vector_of_variables_types = [S for (F, S) in constraint_types
308-
if F == MOI.VectorOfVariables]
305+
single_variable_types = Type{<:MOI.AbstractScalarSet}[]
306+
vector_of_variables_types = Type{<:MOI.AbstractVectorSet}[]
309307

310308
# The `NLPBlock` assumes that the order of variables does not change (#849)
311309
if MOI.NLPBlock() in MOI.get(src, MOI.ListOfModelAttributesSet())
310+
single_variable_types = [S for (F, S) in constraint_types
311+
if F == MOI.SingleVariable]
312+
vector_of_variables_types = [S for (F, S) in constraint_types
313+
if F == MOI.VectorOfVariables]
312314
vector_of_variables_not_added = [
313315
MOI.get(src, MOI.ListOfConstraintIndices{MOI.VectorOfVariables, S}())
314316
for S in vector_of_variables_types
@@ -318,14 +320,23 @@ function default_copy_to(dest::MOI.ModelLike, src::MOI.ModelLike, copy_names::Bo
318320
for S in single_variable_types
319321
]
320322
else
321-
vector_of_variables_not_added = [
322-
copy_vector_of_variables(dest, src, idxmap, S)
323-
for S in vector_of_variables_types
324-
]
325-
single_variable_not_added = [
326-
copy_single_variable(dest, src, idxmap, S)
327-
for S in single_variable_types
328-
]
323+
# Order the copying of the variables by 1) their variable bridging cost 2) by starting with the vectors, as what was done before
324+
# See issue #987
325+
single_or_vector_variables_types = [(F, S) for (F, S) in constraint_types
326+
if F == MOI.SingleVariable || F == MOI.VectorOfVariables]
327+
sorted_by_cost = sortperm(single_or_vector_variables_types; by=((F, S),) -> (MOI.get(dest, MOI.VariableBridgingCost{S}()) - MOI.get(dest, MOI.ConstraintBridgingCost{F, S}()), F == MOI.SingleVariable))
328+
vector_of_variables_not_added = Vector{Array{MOI.ConstraintIndex{MOI.VectorOfVariables, <:MOI.AbstractVectorSet}}}()
329+
single_variable_not_added = Vector{Array{MOI.ConstraintIndex{MOI.SingleVariable, <:MOI.AbstractScalarSet}}}()
330+
for i in sorted_by_cost
331+
F, S = single_or_vector_variables_types[i]
332+
if F == MOI.VectorOfVariables
333+
push!(vector_of_variables_not_added, copy_vector_of_variables(dest, src, idxmap, S))
334+
push!(vector_of_variables_types, S)
335+
elseif F == MOI.SingleVariable
336+
push!(single_variable_not_added, copy_single_variable(dest, src, idxmap, S))
337+
push!(single_variable_types, S)
338+
end
339+
end
329340
end
330341

331342
copy_free_variables(dest, idxmap, vis_src, MOI.add_variables)

src/attributes.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,9 @@ function get end
269269
get(model::ModelLike, attr::AnyAttribute, idxs::Vector) = get.(model, attr, idxs)
270270

271271
function get(model::ModelLike, attr::AnyAttribute, args...)
272-
throw(ArgumentError("ModelLike of type $(typeof(model)) does not support accessing the attribute $attr"))
272+
get_fallback(model, attr, args...)
273273
end
274+
get_fallback(model::ModelLike, attr::AnyAttribute, args...) = throw(ArgumentError("ModelLike of type $(typeof(model)) does not support accessing the attribute $attr"))
274275

275276
"""
276277
get!(output, model::ModelLike, args...)
@@ -1348,6 +1349,19 @@ end
13481349
DualStatus() = DualStatus(1)
13491350
_result_index_field(attr::DualStatus) = attr.N
13501351

1352+
1353+
# Cost of bridging constrained variable in S
1354+
struct VariableBridgingCost{S <: AbstractSet} <: AbstractModelAttribute
1355+
end
1356+
get_fallback(model::ModelLike, ::VariableBridgingCost{S}) where {S<:AbstractScalarSet} = supports_add_constrained_variable(model, S) ? 0.0 : Inf
1357+
get_fallback(model::ModelLike, ::VariableBridgingCost{S}) where {S<:AbstractVectorSet} = supports_add_constrained_variables(model, S) ? 0.0 : Inf
1358+
1359+
# Cost of bridging F-in-S constraints
1360+
struct ConstraintBridgingCost{F <: AbstractFunction, S <: AbstractSet} <: AbstractModelAttribute
1361+
end
1362+
get_fallback(model::ModelLike, ::ConstraintBridgingCost{F, S}) where {F<:AbstractFunction, S<:AbstractSet} = supports_constraint(model, F, S) ? 0.0 : Inf
1363+
1364+
13511365
"""
13521366
is_set_by_optimize(::AnyAttribute)
13531367
@@ -1427,6 +1441,8 @@ function is_copyable(::Union{ListOfOptimizerAttributesSet,
14271441
ListOfConstraintIndices,
14281442
ListOfConstraints,
14291443
ConstraintFunction,
1430-
ConstraintSet})
1444+
ConstraintSet,
1445+
VariableBridgingCost,
1446+
ConstraintBridgingCost})
14311447
return false
14321448
end

test/Utilities/copy.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,97 @@ end
157157
@test dest.added_constrained[idxmap[vi].value]
158158
end
159159
end
160+
161+
162+
abstract type AbstractConstrainedVariablesModel <: MOI.ModelLike end
163+
mutable struct OrderConstrainedVariablesModel <: AbstractConstrainedVariablesModel
164+
constraintIndices ::Array{MOI.ConstraintIndex}
165+
inner ::MOIU.Model{Float64}
166+
OrderConstrainedVariablesModel() = new(MOI.ConstraintIndex[], MOIU.Model{Float64}())
167+
end
168+
mutable struct ReverseOrderConstrainedVariablesModel <: AbstractConstrainedVariablesModel
169+
constraintIndices ::Array{MOI.ConstraintIndex}
170+
inner ::MOIU.Model{Float64}
171+
ReverseOrderConstrainedVariablesModel() = new(MOI.ConstraintIndex[], MOIU.Model{Float64}())
172+
end
173+
174+
175+
176+
MOI.add_variables(model::AbstractConstrainedVariablesModel, n) = MOI.add_variables(model.inner, n)
177+
MOI.add_variable(model::AbstractConstrainedVariablesModel) = MOI.add_variable(model.inner)
178+
179+
function MOI.add_constraint(model::AbstractConstrainedVariablesModel, f::F, s::S) where {F<:MOI.AbstractFunction, S<:MOI.AbstractSet}
180+
ci = MOI.add_constraint(model.inner, f, s)
181+
push!(model.constraintIndices, ci)
182+
return ci
183+
end
184+
185+
function MOI.copy_to(dest::AbstractConstrainedVariablesModel, src::MOI.ModelLike; kws...)
186+
MOIU.automatic_copy_to(dest, src; kws...)
187+
end
188+
189+
MOIU.supports_default_copy_to(model::AbstractConstrainedVariablesModel, ::Bool) = true
190+
191+
function MOI.empty!(model::AbstractConstrainedVariablesModel)
192+
model.constraintIndices = MOI.ConstraintIndex[]
193+
MOI.empty!(model.inner)
194+
end
195+
196+
197+
MOI.supports_constraint(::OrderConstrainedVariablesModel, ::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonnegatives}) = false
198+
MOI.supports_add_constrained_variables(::OrderConstrainedVariablesModel, ::Type{MOI.Nonnegatives}) = true
199+
MOI.supports_constraint(::OrderConstrainedVariablesModel, ::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonnegatives}) = true
200+
MOI.supports_add_constrained_variables(::OrderConstrainedVariablesModel, ::Type{MOI.Nonpositives}) = false
201+
202+
MOI.supports_constraint(::ReverseOrderConstrainedVariablesModel, ::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonnegatives}) = true
203+
MOI.supports_add_constrained_variables(::ReverseOrderConstrainedVariablesModel, ::Type{MOI.Nonnegatives}) = false
204+
MOI.supports_constraint(::ReverseOrderConstrainedVariablesModel, ::Type{MOI.VectorOfVariables}, ::Type{MOI.Nonnegatives}) = false
205+
MOI.supports_add_constrained_variables(::ReverseOrderConstrainedVariablesModel, ::Type{MOI.Nonpositives}) = true
206+
207+
208+
MOI.supports_constraint(::OrderConstrainedVariablesModel, ::Type{MOI.SingleVariable}, ::Type{<:MOI.GreaterThan}) = true
209+
MOI.supports_add_constrained_variable(::OrderConstrainedVariablesModel, ::Type{<:MOI.GreaterThan}) = false
210+
MOI.supports_constraint(::OrderConstrainedVariablesModel, ::Type{MOI.SingleVariable}, ::Type{<:MOI.LessThan}) = false
211+
MOI.supports_add_constrained_variable(::OrderConstrainedVariablesModel, ::Type{<:MOI.LessThan}) = true
212+
213+
MOI.supports_constraint(::ReverseOrderConstrainedVariablesModel, ::Type{MOI.SingleVariable}, ::Type{<:MOI.GreaterThan}) = false
214+
MOI.supports_add_constrained_variable(::ReverseOrderConstrainedVariablesModel, ::Type{<:MOI.GreaterThan}) = true
215+
MOI.supports_constraint(::ReverseOrderConstrainedVariablesModel, ::Type{MOI.SingleVariable}, ::Type{<:MOI.LessThan}) = true
216+
MOI.supports_add_constrained_variable(::ReverseOrderConstrainedVariablesModel, ::Type{<:MOI.LessThan}) = false
217+
218+
219+
@testset "Create variables using supports_add_constrained_variable(s) (#987)" begin
220+
# With vectors
221+
src = MOIU.Model{Float64}()
222+
a, c1 = MOI.add_constrained_variables(src, MOI.Nonpositives(3))
223+
c2 = MOI.add_constraint(src, a, MOI.Nonnegatives(3))
224+
225+
226+
dest = OrderConstrainedVariablesModel()
227+
index_map = MOI.copy_to(dest, src)
228+
@test typeof(c1) == typeof(dest.constraintIndices[2])
229+
@test typeof(c2) == typeof(dest.constraintIndices[1])
230+
231+
dest = ReverseOrderConstrainedVariablesModel()
232+
index_map = MOI.copy_to(dest, src)
233+
@test typeof(c1) == typeof(dest.constraintIndices[1])
234+
@test typeof(c2) == typeof(dest.constraintIndices[2])
235+
236+
237+
238+
# With single variables
239+
src = MOIU.Model{Float64}()
240+
a, c1 = MOI.add_constrained_variable(src, MOI.GreaterThan{Float64}(5.0))
241+
c2 = MOI.add_constraint(src, a, MOI.LessThan{Float64}(1.0))
242+
243+
244+
dest = OrderConstrainedVariablesModel()
245+
index_map = MOI.copy_to(dest, src)
246+
@test typeof(c1) == typeof(dest.constraintIndices[2])
247+
@test typeof(c2) == typeof(dest.constraintIndices[1])
248+
249+
dest = ReverseOrderConstrainedVariablesModel()
250+
index_map = MOI.copy_to(dest, src)
251+
@test typeof(c1) == typeof(dest.constraintIndices[1])
252+
@test typeof(c2) == typeof(dest.constraintIndices[2])
253+
end

0 commit comments

Comments
 (0)