Skip to content

Commit af1b237

Browse files
authored
Implement constrained variables (#167)
* Implement constrained variables * Fix format * Add tests * Fix format * Fix * Fix * Add tests * Fix format * Fix format
1 parent 2774d92 commit af1b237

4 files changed

Lines changed: 146 additions & 9 deletions

File tree

src/MOI_wrapper.jl

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,13 @@ function MOI.get(model::Optimizer, tp::Type{MOI.VariableIndex}, attr::String)
247247
return MOI.get(model.optimizer, tp, attr)
248248
end
249249

250-
function MOI.add_variable(model::Optimizer)
250+
function _add_variable(model::Optimizer, inner_vi)
251251
_next_variable_index!(model)
252-
return MOI.Utilities.CleverDicts.add_item(
253-
model.variables,
254-
MOI.add_variable(model.optimizer),
255-
)
252+
return MOI.Utilities.CleverDicts.add_item(model.variables, inner_vi)
253+
end
254+
255+
function MOI.add_variable(model::Optimizer)
256+
return _add_variable(model, MOI.add_variable(model.optimizer))
256257
end
257258

258259
function MOI.supports_add_constrained_variable(
@@ -269,6 +270,20 @@ function MOI.supports_add_constrained_variables(
269270
return MOI.supports_add_constrained_variables(model.optimizer, MOI.Reals)
270271
end
271272

273+
function MOI.supports_add_constrained_variable(
274+
model::Optimizer,
275+
::Type{S},
276+
) where {S<:MOI.AbstractScalarSet}
277+
return MOI.supports_add_constrained_variable(model.optimizer, S)
278+
end
279+
280+
function MOI.supports_add_constrained_variables(
281+
model::Optimizer,
282+
::Type{S},
283+
) where {S<:MOI.AbstractVectorSet}
284+
return MOI.supports_add_constrained_variables(model.optimizer, S)
285+
end
286+
272287
function _assert_parameter_is_finite(set::MOI.Parameter{T}) where {T}
273288
if !isfinite(set.value)
274289
throw(
@@ -296,6 +311,27 @@ function MOI.add_constrained_variable(
296311
return p, cp
297312
end
298313

314+
function MOI.add_constrained_variable(
315+
model::Optimizer,
316+
set::MOI.AbstractScalarSet,
317+
)
318+
inner_vi, inner_ci = MOI.add_constrained_variable(model.optimizer, set)
319+
outer_vi = _add_variable(model, inner_vi)
320+
outer_ci =
321+
MOI.ConstraintIndex{MOI.VariableIndex,typeof(set)}(outer_vi.value)
322+
model.constraint_outer_to_inner[outer_ci] = inner_ci
323+
return outer_vi, outer_ci
324+
end
325+
326+
function MOI.add_constrained_variables(
327+
model::Optimizer,
328+
set::MOI.AbstractVectorSet,
329+
)
330+
inner_vis, inner_ci = MOI.add_constrained_variables(model.optimizer, set)
331+
_add_to_constraint_map!(model, inner_ci)
332+
return _add_variable.(model, inner_vis), inner_ci
333+
end
334+
299335
function _add_to_constraint_map!(model::Optimizer, ci)
300336
model.constraint_outer_to_inner[ci] = ci
301337
return

src/ParametricOptInterface.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,11 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer
180180

181181
# extension data
182182
ext::Dict{Symbol,Any}
183-
function Optimizer(
183+
function Optimizer{T}(
184184
optimizer::OT;
185185
evaluate_duals::Bool = true,
186186
save_original_objective_and_constraints::Bool = true,
187-
) where {OT}
188-
T = Float64
187+
) where {T,OT}
189188
return new{T,OT}(
190189
optimizer,
191190
MOI.Utilities.CleverDicts.CleverDict{ParameterIndex,T}(
@@ -248,6 +247,8 @@ mutable struct Optimizer{T,OT<:MOI.ModelLike} <: MOI.AbstractOptimizer
248247
end
249248
end
250249

250+
Optimizer(args...; kws...) = Optimizer{Float64}(args...; kws...)
251+
251252
function _next_variable_index!(model::Optimizer)
252253
return model.last_variable_index_added += 1
253254
end

test/moi_tests.jl

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2097,7 +2097,7 @@ function test_psd_cone_with_parameter()
20972097
model = POI.Optimizer(cached)
20982098
MOI.set(model, MOI.Silent(), true)
20992099
x = MOI.add_variable(model)
2100-
p = first.(MOI.add_constrained_variable.(model, MOI.Parameter(1.0)),)
2100+
p = first.(MOI.add_constrained_variable.(model, MOI.Parameter(1.0)))
21012101

21022102
# Set objective: minimize x
21032103
obj_func = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, x)], 0.0)
@@ -2142,3 +2142,63 @@ function test_copy_model()
21422142
MOI.optimize!(poi)
21432143
@test MOI.get(poi, MOI.VariablePrimal(), x) 1.0
21442144
end
2145+
2146+
function test_constrained_variable_glpk()
2147+
optimizer = POI.Optimizer(GLPK.Optimizer())
2148+
MOI.set(optimizer, MOI.Silent(), true)
2149+
set = MOI.LessThan(1.0)
2150+
@test MOI.supports_add_constrained_variable(optimizer, typeof(set))
2151+
x, c = MOI.add_constrained_variable(optimizer, set)
2152+
@test x.value == c.value
2153+
@test c isa MOI.ConstraintIndex{typeof(x),typeof(set)}
2154+
@test MOI.get(optimizer, MOI.ConstraintFunction(), c) x
2155+
@test MOI.get(optimizer, MOI.ConstraintSet(), c) == set
2156+
@test MOI.supports(optimizer, MOI.VariableName(), typeof(x))
2157+
MOI.set(optimizer, MOI.VariableName(), x, "vname")
2158+
@test MOI.get(optimizer, MOI.VariableName(), x) == "vname"
2159+
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)
2160+
obj_func = 1.0 * x
2161+
MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func)
2162+
MOI.optimize!(optimizer)
2163+
@test MOI.get(optimizer, MOI.ConstraintDual(), c) -1 atol = ATOL
2164+
@test MOI.get(optimizer, MOI.VariablePrimal(), x) 1 atol = ATOL
2165+
return
2166+
end
2167+
2168+
include("no_free_model.jl")
2169+
2170+
function test_constrained_variable_no_free()
2171+
optimizer = POI.Optimizer(NoFreeVariablesModel{Float64}())
2172+
set = MOI.LessThan(1.0)
2173+
@test MOI.supports_add_constrained_variable(optimizer, typeof(set))
2174+
x, c = MOI.add_constrained_variable(optimizer, set)
2175+
@test c isa MOI.ConstraintIndex{typeof(x),typeof(set)}
2176+
@test x.value == c.value
2177+
@test MOI.get(optimizer, MOI.ConstraintFunction(), c) x
2178+
@test MOI.get(optimizer, MOI.ConstraintSet(), c) == set
2179+
@test MOI.supports(optimizer, MOI.VariableName(), typeof(x))
2180+
MOI.set(optimizer, MOI.VariableName(), x, "vname")
2181+
@test MOI.get(optimizer, MOI.VariableName(), x) == "vname"
2182+
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MAX_SENSE)
2183+
obj_func = 1.0 * x
2184+
MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func)
2185+
return
2186+
end
2187+
2188+
function test_constrained_variables()
2189+
optimizer = POI.Optimizer{Int}(NoFreeVariablesModel{Int}())
2190+
set = MOI.Nonnegatives(2)
2191+
@test MOI.supports_add_constrained_variables(optimizer, typeof(set))
2192+
x, c = MOI.add_constrained_variables(optimizer, set)
2193+
@test c isa MOI.ConstraintIndex{MOI.VectorOfVariables,typeof(set)}
2194+
@test MOI.get(optimizer, MOI.ConstraintFunction(), c)
2195+
MOI.VectorOfVariables(x)
2196+
@test MOI.get(optimizer, MOI.ConstraintSet(), c) == set
2197+
@test MOI.supports(optimizer, MOI.VariableName(), eltype(x))
2198+
MOI.set(optimizer, MOI.VariableName(), x[1], "vname")
2199+
@test MOI.get(optimizer, MOI.VariableName(), x[1]) == "vname"
2200+
MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MIN_SENSE)
2201+
obj_func = 1 * x[1] + 1 * x[2]
2202+
MOI.set(optimizer, MOI.ObjectiveFunction{typeof(obj_func)}(), obj_func)
2203+
return
2204+
end

test/no_free_model.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
MOI.Utilities.@model(
2+
NoFreeVariablesModel,
3+
(),
4+
(),
5+
(MOI.Nonnegatives,),
6+
(),
7+
(),
8+
(),
9+
(MOI.VectorOfVariables,),
10+
(),
11+
)
12+
13+
function MOI.supports_constraint(
14+
::NoFreeVariablesModel,
15+
::Type{MOI.VectorOfVariables},
16+
::Type{MOI.Reals},
17+
)
18+
return false
19+
end
20+
21+
function MOI.supports_add_constrained_variable(
22+
::NoFreeVariablesModel{T},
23+
::Type{MOI.LessThan{T}},
24+
) where {T}
25+
return true
26+
end
27+
28+
function MOI.supports_add_constrained_variables(
29+
::NoFreeVariablesModel,
30+
::Type{MOI.Nonnegatives},
31+
)
32+
return true
33+
end
34+
35+
function MOI.supports_add_constrained_variables(
36+
::NoFreeVariablesModel,
37+
::Type{MOI.Reals},
38+
)
39+
return false
40+
end

0 commit comments

Comments
 (0)