|
| 1 | +```@meta |
| 2 | +CurrentModule = MathOptInterface |
| 3 | +DocTestSetup = quote |
| 4 | + import MathOptInterface as MOI |
| 5 | +end |
| 6 | +DocTestFilters = [r"MathOptInterface|MOI"] |
| 7 | +``` |
| 8 | + |
| 9 | +# Defining a new set |
| 10 | + |
| 11 | +The easiest way to extend the behavior of MathOptInterface is to design a new |
| 12 | +set. The purpose of this tutorial is to explain how to define a new set and some |
| 13 | +of the associated nuances. |
| 14 | + |
| 15 | +Defining a new function is an order of magnitude (if not two) harder than |
| 16 | +defining a new set. Don't consider it as an option unless you have already tried |
| 17 | +to support your behavior as a set. |
| 18 | + |
| 19 | +As a motivation for this tutorial, we consider the _LinMax_ constraint type, |
| 20 | +which appears often in constraint programming: |
| 21 | +```math |
| 22 | +t = \max(f_1(x), f_2(x), \ldots, f_N(x)) |
| 23 | +``` |
| 24 | + |
| 25 | +The first step to design a new set for MathOptInterface is to define the |
| 26 | +mathematical relationship you want to model as a _function-in-set_ $f(x) \in S$. |
| 27 | + |
| 28 | +Your initial thought for representing the _LinMax_ constraint in MathOptInterface |
| 29 | +may be to represent it as: |
| 30 | +```math |
| 31 | +F(x) \in LinMax(t) |
| 32 | +``` |
| 33 | +where $F(x)$ is the vector-valued function created by concatenating the $f_i$ |
| 34 | +functions. This formulation violates a basic rule of MathOptInterface: |
| 35 | + |
| 36 | +!!! rule |
| 37 | + Sets cannot contain references to decision variables or other constraints. |
| 38 | + |
| 39 | +Instead, we could model the _LinMax_ constraint as: |
| 40 | +```math |
| 41 | +[t, f_1(x), f_2(x), \ldots, f_N(x)] \in LinMax(N+1) |
| 42 | +``` |
| 43 | + |
| 44 | +In the language of MathOptInterface, this is a [`AbstractVectorFunction`](@ref) |
| 45 | +in the `LinMax` set of dimension $N+1$. The type of the function depends on the |
| 46 | +types of the component scalar functions, with a special convention that the |
| 47 | +first element in the function is interpretable as a [`VariableIndex`](@ref) |
| 48 | +`t`. |
| 49 | + |
| 50 | +Now `LinMax` can be trivially defined as a new [`AbstractVectorSet`](@ref): |
| 51 | +```jldoctest define_new_set |
| 52 | +julia> import MathOptInterface as MOI |
| 53 | +
|
| 54 | +julia> struct LinMax <: MOI.AbstractVectorSet |
| 55 | + dimension::Int |
| 56 | + end |
| 57 | +``` |
| 58 | +and it can immediately be used in MathOptInterface: |
| 59 | +```jldoctest define_new_set |
| 60 | +julia> model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()); |
| 61 | +
|
| 62 | +julia> t = MOI.VariableIndex(1); |
| 63 | +
|
| 64 | +julia> x = MOI.VariableIndex.(2:3); |
| 65 | +
|
| 66 | +julia> F = 1.0 .* x .+ 2.0; |
| 67 | +
|
| 68 | +julia> g = MOI.Utilities.operate(vcat, Float64, t, F...); |
| 69 | +
|
| 70 | +julia> MOI.add_constraint(model, g, LinMax(3)) |
| 71 | +MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, LinMax}(1) |
| 72 | +
|
| 73 | +julia> print(model) |
| 74 | +Feasibility |
| 75 | +
|
| 76 | +Subject to: |
| 77 | +
|
| 78 | +VectorAffineFunction{Float64}-in-LinMax |
| 79 | + ┌ ┐ |
| 80 | + │0.0 + 1.0 v[1]│ |
| 81 | + │2.0 + 1.0 v[2]│ |
| 82 | + │2.0 + 1.0 v[3]│ |
| 83 | + └ ┘ ∈ LinMax(3) |
| 84 | +``` |
0 commit comments