Defining a new set
The easiest way to extend the behavior of MathOptInterface is to design a new set. The purpose of this tutorial is to explain how to define a new set and some of the associated nuances.
Defining a new function is an order of magnitude (if not two) harder than defining a new set. Don't consider it as an option unless you have already tried to support your behavior as a set.
As a motivation for this tutorial, we consider the LinMax constraint type, which appears often in constraint programming:
\[t = \max(f_1(x), f_2(x), \ldots, f_N(x))\]
The first step to design a new set for MathOptInterface is to define the mathematical relationship you want to model as a function-in-set $f(x) \in S$.
Your initial thought for representing the LinMax constraint in MathOptInterface may be to represent it as:
\[F(x) \in LinMax(t)\]
where $F(x)$ is the vector-valued function created by concatenating the $f_i$ functions. This formulation violates a basic rule of MathOptInterface:
Instead, we could model the LinMax constraint as:
\[[t, f_1(x), f_2(x), \ldots, f_N(x)] \in LinMax(N+1)\]
In the language of MathOptInterface, this is a AbstractVectorFunction in the LinMax set of dimension $N+1$. The type of the function depends on the types of the component scalar functions, with a special convention that the first element in the function is interpretable as a VariableIndex t.
Now LinMax can be trivially defined as a new AbstractVectorSet:
julia> import MathOptInterface as MOI
julia> struct LinMax <: MOI.AbstractVectorSet
dimension::Int
endand it can immediately be used in MathOptInterface:
julia> model = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}());
julia> t = MOI.VariableIndex(1);
julia> x = MOI.VariableIndex.(2:3);
julia> F = 1.0 .* x .+ 2.0;
julia> g = MOI.Utilities.operate(vcat, Float64, t, F...);
julia> MOI.add_constraint(model, g, LinMax(3))
MathOptInterface.ConstraintIndex{MathOptInterface.VectorAffineFunction{Float64}, LinMax}(1)
julia> print(model)
Feasibility
Subject to:
VectorAffineFunction{Float64}-in-LinMax
┌ ┐
│0.0 + 1.0 v[1]│
│2.0 + 1.0 v[2]│
│2.0 + 1.0 v[3]│
└ ┘ ∈ LinMax(3)