From be8c8b3f45c3da1dc2b0c53a9b614cbe73dec018 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 8 Dec 2025 17:05:04 +1300 Subject: [PATCH 1/3] [docs] clarify that new sets must not contain references to variables --- docs/make.jl | 7 +- docs/src/developer/checklists.md | 1 + docs/src/developer/defining_a_new_set.md | 84 ++++++++++++++++++++++++ docs/src/manual/standard_form.md | 5 +- 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 docs/src/developer/defining_a_new_set.md diff --git a/docs/make.jl b/docs/make.jl index 99dbb2330d..f3acc68725 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -83,8 +83,11 @@ const _PAGES = [ "API Reference" => "submodules/Test/reference.md", ], ], - "Developer Docs" => - ["developer/checklists.md", "developer/contributing.md"], + "Developer Docs" => [ + "developer/checklists.md", + "developer/contributing.md", + "tutorials/defining_a_new_set.md", + ], "Release notes" => "release_notes.md", ] diff --git a/docs/src/developer/checklists.md b/docs/src/developer/checklists.md index 533288db3c..113362cc11 100644 --- a/docs/src/developer/checklists.md +++ b/docs/src/developer/checklists.md @@ -42,6 +42,7 @@ Use this checklist when adding a new set to the MathOptInterface repository. - [ ] If `isbitstype(S) == false`, implement `Base.:(==)(x::S, y::S)` - [ ] If an `AbstractVectorSet`, implement `dimension(set::S)`, unless the dimension is given by `set.dimension`. + - [ ] Ensure the set does not contain references to any variables or constraints ## Utilities diff --git a/docs/src/developer/defining_a_new_set.md b/docs/src/developer/defining_a_new_set.md new file mode 100644 index 0000000000..1bb40737ea --- /dev/null +++ b/docs/src/developer/defining_a_new_set.md @@ -0,0 +1,84 @@ +```@meta +CurrentModule = MathOptInterface +DocTestSetup = quote + import MathOptInterface as MOI +end +DocTestFilters = [r"MathOptInterface|MOI"] +``` + +# 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: +```math +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 inital thought for representing the _LinMax_ constraint in MathOptInterface +may be to represent it as: +```math +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: + +!!! rule + Sets cannot contain references to decision variables or other constraints. + +Instead, we could model the _LinMax_ constraint as: +```math +[t, f_1(x), f_2(x), \ldots, f_N(x)] \in LinMax(N+1) +``` + +In the language of MathOptInterface, this is a [`AbstractVectorFunction`](@ref) +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`](@ref) +`t`. + +Now `LinMax` can be trivially defined as a new [`AbstractVectorSet`](@ref): +```jldoctest define_new_set +julia> import MathOptInterface as MOI + +julia> struct LinMax <: MOI.AbstractVectorSet + dimension::Int + end +``` +and it can immediately be used in MathOptInterface: +```jldoctest define_new_set +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) +``` diff --git a/docs/src/manual/standard_form.md b/docs/src/manual/standard_form.md index 1700236547..98a0965ab8 100644 --- a/docs/src/manual/standard_form.md +++ b/docs/src/manual/standard_form.md @@ -22,6 +22,9 @@ where: * the sets ``\mathcal{S}_1, \ldots, \mathcal{S}_m`` are specified by [`AbstractSet`](@ref) objects +An important design consideration is that the sets are independent of the +decision variables. + !!! tip For more information on this standard form, read [our paper](https://arxiv.org/abs/2002.03447). @@ -43,8 +46,6 @@ The function types implemented in MathOptInterface.jl are: | [`VectorQuadraticFunction`](@ref) | A vector of scalar-valued quadratic functions. | | [`VectorNonlinearFunction`](@ref) | ``f(x)``, where ``f`` is a vector-valued nonlinear function. | -Extensions for nonlinear programming are present but not yet well documented. - ## One-dimensional sets The one-dimensional set types implemented in MathOptInterface.jl are: From 68a4e2a01312be1beb3634300adc43d6c3b8b45e Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 8 Dec 2025 20:39:39 +1300 Subject: [PATCH 2/3] Update docs/make.jl --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index f3acc68725..0bd6f6f707 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -86,7 +86,7 @@ const _PAGES = [ "Developer Docs" => [ "developer/checklists.md", "developer/contributing.md", - "tutorials/defining_a_new_set.md", + "developer/defining_a_new_set.md", ], "Release notes" => "release_notes.md", ] From aad75377f52020533d7d188c5f36a5ae055f0413 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 8 Dec 2025 21:03:37 +1300 Subject: [PATCH 3/3] Update docs/src/developer/defining_a_new_set.md --- docs/src/developer/defining_a_new_set.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/developer/defining_a_new_set.md b/docs/src/developer/defining_a_new_set.md index 1bb40737ea..32c865211a 100644 --- a/docs/src/developer/defining_a_new_set.md +++ b/docs/src/developer/defining_a_new_set.md @@ -25,7 +25,7 @@ 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 inital thought for representing the _LinMax_ constraint in MathOptInterface +Your initial thought for representing the _LinMax_ constraint in MathOptInterface may be to represent it as: ```math F(x) \in LinMax(t)