From ab2316e714cd0a1dbf6ff47b637bd6b9d94c00a5 Mon Sep 17 00:00:00 2001 From: fpacaud Date: Wed, 29 Apr 2026 21:42:56 +0200 Subject: [PATCH 1/4] Add documentation --- docs/Project.toml | 7 ++ docs/make.jl | 33 +++++++++ docs/src/api/bridges.md | 33 +++++++++ docs/src/api/reformulation.md | 13 ++++ docs/src/api/relaxation.md | 14 ++++ docs/src/equilibrium.md | 78 +++++++++++++++++++++ docs/src/index.md | 26 +++++++ docs/src/quickstart.md | 67 ++++++++++++++++++ src/Bridges/Bridges.jl | 6 +- src/Bridges/complements_vectorize_bridge.jl | 10 +-- src/Bridges/nonlinear.jl | 2 +- src/Bridges/specify_set_type_bridge.jl | 12 ++-- src/Bridges/split_interval_bridge.jl | 8 +-- src/Bridges/to_sos1_bridge.jl | 4 +- src/Bridges/vertical.jl | 8 +-- src/attributes.jl | 2 +- 16 files changed, 297 insertions(+), 26 deletions(-) create mode 100644 docs/Project.toml create mode 100644 docs/make.jl create mode 100644 docs/src/api/bridges.md create mode 100644 docs/src/api/reformulation.md create mode 100644 docs/src/api/relaxation.md create mode 100644 docs/src/equilibrium.md create mode 100644 docs/src/index.md create mode 100644 docs/src/quickstart.md diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..6fbdf9e --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,7 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656" +Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" +JuMP = "4076af6c-e467-56ae-b986-b466b2749572" +MathOptComplements = "3144fb79-1414-4d28-b1d1-63dadd798e24" +PATHSolver = "f5f7c340-0bb3-5c69-969a-41884d311d1b" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..3b9bd43 --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,33 @@ +using Documenter +using DocumenterInterLinks +using MathOptComplements + +links = InterLinks( + "MathOptInterface" => "https://jump.dev/MathOptInterface.jl/stable/", +) + +makedocs( + sitename = "MathOptComplements.jl", + format = Documenter.HTML( + assets = ["assets/favicon.ico"], + prettyurls = Base.get(ENV, "CI", nothing) == "true", + mathengine = Documenter.KaTeX() + ), + modules = [MathOptComplements], + repo = "https://github.com/blegat/MathOptComplements.jl/blob/{commit}{path}#{line}", + checkdocs = :none, + clean=true, + pages = [ + "Home" => "index.md", + "Quickstart" => "quickstart.md", + "Tutorials" => [ + "Equilibrium problem" => "equilibrium.md", + ], + "API reference" => [ + "Reformulations" => "api/reformulation.md", + "Relaxations" => "api/relaxation.md", + "Bridges" => "api/bridges.md", + ] + ], + plugins = [links], +) diff --git a/docs/src/api/bridges.md b/docs/src/api/bridges.md new file mode 100644 index 0000000..fccb4ed --- /dev/null +++ b/docs/src/api/bridges.md @@ -0,0 +1,33 @@ +```@meta +CurrentModule = MathOptComplements +``` + +# Complementarity bridges + +```@docs + +Bridges.SpecifySetTypeBridge +Bridges.VerticalBridge +Bridges.ComplementsVectorizeBridge +Bridges.SplitIntervalBridge +Bridges.FlipSignBridge +``` + + +## SOS1 reformulation + +```@docs +Bridges.ToSOS1Bridge +``` + +## Nonlinear reformulation + +```@docs +Bridges.NonlinearBridge +``` + +## Utilities + +```@docs +Bridges.add_all_bridges +``` diff --git a/docs/src/api/reformulation.md b/docs/src/api/reformulation.md new file mode 100644 index 0000000..120644b --- /dev/null +++ b/docs/src/api/reformulation.md @@ -0,0 +1,13 @@ +```@meta +CurrentModule = MathOptComplements +``` + +# Reformulations + +```@docs + +ComplementsWithSetType +DefaultComplementarityReformulation +ComplementarityReformulation + +``` diff --git a/docs/src/api/relaxation.md b/docs/src/api/relaxation.md new file mode 100644 index 0000000..87384de --- /dev/null +++ b/docs/src/api/relaxation.md @@ -0,0 +1,14 @@ +```@meta +CurrentModule = MathOptComplements +``` + +# Nonlinear relaxations + +```@docs +AbstractComplementarityRelaxation +Bridges.ScholtesRelaxation +Bridges.FischerBurmeisterRelaxation +Bridges.LiuFukushimaRelaxation +Bridges.KanzowSchwarzRelaxation + +``` diff --git a/docs/src/equilibrium.md b/docs/src/equilibrium.md new file mode 100644 index 0000000..081cd1f --- /dev/null +++ b/docs/src/equilibrium.md @@ -0,0 +1,78 @@ +```@meta +CurrentModule = MathOptComplements +``` +## Solving an equilibrium problem with MathOptComplements + +In this tutorial, we show how to solve an equilbrium problem +using the different methods implemented in MathOptComplements. + +We take an example from the [JuMP documentation](https://jump.dev/JuMP.jl/stable/tutorials/nonlinear/complementarity/#Electricity-consumption), +implementing a risk neutral competitive equilibrium between a producer and a consumer of electricity. + +```@example tutorial_equilibrium +using JuMP + +function equilibrium_model() + I = 90_000 # Annualized capital cost + C = 60 # Operation cost per MWh + τ = 8_760 # Hours per year + θ = [0.2, 0.2, 0.2, 0.2, 0.2] # Scenario probabilities + A = [300, 350, 400, 450, 500] # Utility function coefficients + B = 1 # Utility function coefficients + model = Model() + @variable(model, x >= 0, start = 1) # Installed capacity + @variable(model, Q[ω=1:5] >= 0, start = 1) # Consumption + @variable(model, Y[ω=1:5] >= 0, start = 1) # Production + @variable(model, P[ω=1:5], start = 1) # Electricity price + @variable(model, μ[ω=1:5] >= 0, start = 1) # Capital scarcity margin + # Unit investment cost equals annualized scarcity profit or investment is 0 + @constraint(model, I - τ * θ' * μ ⟂ x) + # Difference between price and scarcity margin is equal to operation cost + @constraint(model, [ω = 1:5], C - (P[ω] - μ[ω]) ⟂ Y[ω]) + # Price is equal to consumer's marginal utility + @constraint(model, [ω = 1:5], P[ω] - (A[ω] - B * Q[ω]) ⟂ Q[ω]) + # Production is equal to consumption + @constraint(model, [ω = 1:5], Y[ω] - Q[ω] ⟂ P[ω]) + # Production does not exceed capacity + @constraint(model, [ω = 1:5], x - Y[ω] ⟂ μ[ω]) + return model +end +``` + +This instance is featuring mixed-complementarity constraints, and as such +is a good demo for MathOptComplements' capabilities. + +As a reference, we use the solution returned by the [PATH solver](https://pages.cs.wisc.edu/~ferris/path.html): +```@example tutorial_equilibrium +using PATHSolver +model = equilibrium_model() +JuMP.set_optimizer(model, PATHSolver.Optimizer) +JuMP.optimize!(model) +nothing + +``` +The solution returned by PATH is: +```@example tutorial_equilibrium +JuMP.value(model[:x]) # production in MWh +``` + +### Solution with a nonlinear solver + +We replace the solver PATH by Ipopt. MathOptComplements takes care of +reformulating the problem automatically with appropriate nonlinear constraints. +```@example tutorial_equilibrium +using MathOptComplements +using Ipopt +model = equilibrium_model() +MathOptComplements.Bridges.add_all_bridges(model) +set_optimizer(model, Ipopt.Optimizer) +JuMP.optimize!(model) +nothing +``` +The solution returned by Ipopt is: +```@example tutorial_equilibrium +JuMP.value(model[:x]) # production in MWh +``` + + + diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..cb7dd36 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,26 @@ + +# MathOptComplements + +MathOptComplements is a [MathOptInterface](https://github.com/jump-dev/MathOptInterface.jl/) extension for complementarity constraints. + +## Motivation + +MOI implements a set for mixed-complementarity constraints through [`MathOptInterface.Complements`](@extref). +However, few solvers support mixed-complementarity out of the box, often requiring +users to apply manual reformulations. + +MathOptComplements provides a systematic way to handle the complementarity +constraints in JuMP and MathOptInterface by introducing a new set [`MathOptComplements.ComplementsWithSetType`](@ref). +The package provides a rich collection of tools for manipulating complementarity constraints, including: +- Equivalent **reformulations** in forms better suited to the target solver; +- Automatic **relaxations** that reformulate complementarity constraints as nonlinear constraints. + +Under the hood, MathOptComplements extends [the bridge system](https://jump.dev/MathOptInterface.jl/stable/submodules/Bridges/overview/) implemented in MOI to optimally reformulate +the complementarity constraints within the model. + + + +## Funding +We acknowledge support from the [Fondation Mathématiques Jacques Hadamard](https://www.fondation-hadamard.fr/fr/) +which has funded the PGMO-IROE project "A new optimization suite for large-scale market equilibrium". + diff --git a/docs/src/quickstart.md b/docs/src/quickstart.md new file mode 100644 index 0000000..c6be049 --- /dev/null +++ b/docs/src/quickstart.md @@ -0,0 +1,67 @@ +```@meta +CurrentModule = MathOptComplements +``` +## Quickstart + +The following code shows how to solve a simple Mathematical Program with Complementarity Constraints (MPCC) with Ipopt. This instance is a JuMP translation +of `scholtes4.mod` in [MacMPEC](https://www.mcs.anl.gov/~leyffer/macmpec/comments.html). + +### Basic usage + +We start by writing the model with JuMP: + +```@example quickstart +using JuMP +z0 = [0, 1] +model = Model() +@variable(model, z[i=1:2] >= 0.0, start=z0[i]) +@variable(model, z3, start=0.0) +@objective(model, Min, z[1] + z[2] - z3) +@constraint(model, -4 * z[1] + z3 <= 0) +@constraint(model, -4 * z[2] + z3 <= 0) +@constraint(model, [z[1], z[2]] ∈ MOI.Complements(2)) +model +``` + +Solving this instance with Ipopt simply amounts to: +```@example quickstart +using MathOptComplements +using Ipopt +MathOptComplements.Bridges.add_all_bridges(model) +set_optimizer(model, Ipopt.Optimizer) +JuMP.optimize!(model) +println("Solution: ", JuMP.value.(model[:z])) +``` +Under the hood, MathOptComplements takes the complementarity +constraints and reformulate it as a nonlinear constraint using +the [`ScholtesRelaxation`](@ref) method. + +!!! note + We recommend setting the following options in Ipopt for optimal performance: + ```julia + JuMP.set_optimizer_attribute(model, "mu_strategy", "adaptive") + JuMP.set_optimizer_attribute(model, "bound_push", 1e-1) + JuMP.set_optimizer_attribute(model, "bound_relax_factor", 0.0) + ``` + + +### Changing the relaxation + +The [`ScholtesRelaxation`](@ref) is used by default, but the user +has the freedom to use any of the relaxations implemented in MathOptComplements. +Replacing the [`ScholtesRelaxation`](@ref) by the classical [`FischerBurmeisterRelaxation`](@ref) simply amounts to +```@example quickstart +JuMP.set_optimizer(model, () -> MathOptComplements.Optimizer(Ipopt.Optimizer())) +MOI.set(model, MathOptComplements.DefaultComplementarityReformulation(), MathOptComplements.FischerBurmeisterRelaxation(1e-8)) +JuMP.optimize!(model) + +println("Solution: ", JuMP.value.(model[:z])) +``` + +Observe that the solution is here closer to the true solution `(0, 0)` +than the solution returned by the Scholtes relaxation. + +!!! warning + MPCCs are nonconvex problems and they rarely have a unique solution. + In general, changing the relaxation method can yield a different local solution. + diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index d81f802..c2ab1fd 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -20,15 +20,15 @@ include("to_sos1_bridge.jl") add_all_bridges(model::MOI.ModelLike, ::Type{T} = Float64) Add all `MathOptComplements` bridges to `model`. The model is typically a -[`MOI.Bridges.LazyBridgeOptimizer`](@ref) so that the bridge graph is +[`MathOptInterface.Bridges.LazyBridgeOptimizer`](@extref) so that the bridge graph is extended with the bridges needed to reformulate -[`MathOptComplements.ComplementsWithSetType`](@ref) and [`MOI.Complements`](@ref) +[`MathOptComplements.ComplementsWithSetType`](@ref) and [`MathOptInterface.Complements`](@extref) constraints. When used with a `LazyBridgeOptimizer`, the [`NonlinearBridge`](@ref) uses the default [`ScholtesRelaxation`](@ref) because the [`MathOptComplements.DefaultComplementarityReformulation`](@ref) optimizer -attribute is only supported by [`MathOptComplements.Optimizer`](@ref). +attribute is only supported by `MathOptComplements.Optimizer`. """ function add_all_bridges(model::MOI.ModelLike, ::Type{T} = Float64) where {T} MOI.Bridges.add_bridge(model, SpecifySetTypeBridge{T}) diff --git a/src/Bridges/complements_vectorize_bridge.jl b/src/Bridges/complements_vectorize_bridge.jl index 115a745..3a91945 100644 --- a/src/Bridges/complements_vectorize_bridge.jl +++ b/src/Bridges/complements_vectorize_bridge.jl @@ -16,17 +16,17 @@ where `T` is the coefficient type. `ComplementsVectorizeBridge` supports: - * [`MOI.VectorOfVariables`](@ref) in [`ComplementsWithSetType{S}`](@ref) - where `S` is [`MOI.GreaterThan{T}`](@ref), [`MOI.LessThan{T}`](@ref), or - [`MOI.EqualTo{T}`](@ref) + * [`MathOptInterface.VectorOfVariables`](@extref) in [`ComplementsWithSetType{S}`](@ref) + where `S` is [`MathOptInterface.GreaterThan`](@extref), [`MathOptInterface.LessThan`](@extref), or + [`MathOptInterface.EqualTo`](@extref) ## Target nodes `ComplementsVectorizeBridge` creates: * `F` in [`ComplementsWithSetType{SV}`](@ref), where `SV` is - [`MOI.Nonnegatives`](@ref), [`MOI.Nonpositives`](@ref), or - [`MOI.Zeros`](@ref) depending on the input set type + [`MathOptInterface.Nonnegatives`](@extref), [`MathOptInterface.Nonpositives`](@extref), or + [`MathOptInterface.Zeros`](@extref) depending on the input set type """ struct ComplementsVectorizeBridge{T,F,S,SV} <: MOI.Bridges.Constraint.AbstractBridge diff --git a/src/Bridges/nonlinear.jl b/src/Bridges/nonlinear.jl index 3aceee3..646a357 100644 --- a/src/Bridges/nonlinear.jl +++ b/src/Bridges/nonlinear.jl @@ -19,7 +19,7 @@ The relaxation method is determined by the `NonlinearBridge` supports: - * [`MOI.VectorOfVariables`](@ref) in [`ComplementsWithSetType{S}`](@ref) + * [`MathOptInterface.VectorOfVariables`](@extref) in [`ComplementsWithSetType{S}`](@ref) ## Target nodes diff --git a/src/Bridges/specify_set_type_bridge.jl b/src/Bridges/specify_set_type_bridge.jl index 41ef610..67d8fd0 100644 --- a/src/Bridges/specify_set_type_bridge.jl +++ b/src/Bridges/specify_set_type_bridge.jl @@ -3,7 +3,7 @@ `SpecifySetTypeBridge` implements the following reformulation: - * `(x₁, x₂)` in [`MOI.Complements`](@ref) into `(x₁, x₂)` in + * `(x₁, x₂)` in [`MathOptInterface.Complements`](@extref) into `(x₁, x₂)` in [`ComplementsWithSetType{S}`](@ref) where `S` is determined by the bounds of `x₂`: @@ -13,7 +13,7 @@ where `S` is determined by the bounds of `x₂`: * `x₂ ≤ 0` gives `S = MOI.Nonpositives` * `x₂ ≤ ub` (ub ≠ 0) gives `S = MOI.LessThan{T}` * `lb ≤ x₂ ≤ ub` gives `S = MOI.Interval{T}` - * `x₂` free gives `S = MOI.Zeros` + * `x₂` free gives `S = MOI.Real` The bridge also adds the appropriate bound on the activity variable `x₁` (for example, `x₁ ≥ 0` when `x₂` has a lower bound). @@ -22,15 +22,15 @@ The bridge also adds the appropriate bound on the activity variable `x₁` `SpecifySetTypeBridge` supports: - * [`MOI.VectorOfVariables`](@ref) in [`MOI.Complements`](@ref) + * [`MathOptInterface.VectorOfVariables`](@extref) in [`MathOptInterface.Complements`](@extref) ## Target nodes `SpecifySetTypeBridge` creates: - * [`MOI.VectorOfVariables`](@ref) in [`ComplementsWithSetType{S}`](@ref) - * [`MOI.VariableIndex`](@ref) in [`MOI.GreaterThan{T}`](@ref) or - [`MOI.LessThan{T}`](@ref) (bounds on `x₁`) + * [`MathOptInterface.VectorOfVariables`](@extref) in [`ComplementsWithSetType{S}`](@ref) + * [`MathOptInterface.VariableIndex`](@extref) in [`MathOptInterface.GreaterThan`](@extref) or + [`MathOptInterface.LessThan`](@extref) (bounds on `x₁`) """ mutable struct SpecifySetTypeBridge{T} <: MOI.Bridges.Constraint.AbstractBridge diff --git a/src/Bridges/split_interval_bridge.jl b/src/Bridges/split_interval_bridge.jl index adbf19b..3445433 100644 --- a/src/Bridges/split_interval_bridge.jl +++ b/src/Bridges/split_interval_bridge.jl @@ -18,18 +18,18 @@ must be a variable. `SplitIntervalBridge` supports: - * [`MOI.AbstractVectorFunction`](@ref) in + * [`MathOptInterface.AbstractVectorFunction`](@extref) in [`ComplementsWithSetType{MOI.Interval{T}}`](@ref) ## Target nodes `SplitIntervalBridge` creates: - * [`MOI.VectorOfVariables`](@ref) in + * [`MathOptInterface.VectorOfVariables`](@extref) in [`ComplementsWithSetType{MOI.GreaterThan{T}}`](@ref) - * [`MOI.VectorOfVariables`](@ref) in + * [`MathOptInterface.VectorOfVariables`](@extref) in [`ComplementsWithSetType{MOI.LessThan{T}}`](@ref) - * `G` in [`MOI.EqualTo{T}`](@ref) (the splitting equality) + * `G` in [`MathOptInterface.EqualTo`](@extref) (the splitting equality) where `G` is the scalar function type of the first component. diff --git a/src/Bridges/to_sos1_bridge.jl b/src/Bridges/to_sos1_bridge.jl index a1a025b..937b98f 100644 --- a/src/Bridges/to_sos1_bridge.jl +++ b/src/Bridges/to_sos1_bridge.jl @@ -13,14 +13,14 @@ one of them is nonzero, which is equivalent to the complementarity condition. `ToSOS1Bridge` supports: - * [`MOI.VectorOfVariables`](@ref) in + * [`MathOptInterface.VectorOfVariables`](@extref) in [`ComplementsWithSetType{MOI.Nonnegatives}`](@ref) ## Target nodes `ToSOS1Bridge` creates: - * [`MOI.VectorOfVariables`](@ref) in [`MOI.SOS1{T}`](@ref) + * [`MathOptInterface.VectorOfVariables`](@extref) in [`MathOptInterface.SOS1`](@extref) """ struct ToSOS1Bridge{T} <: MOI.Bridges.Constraint.AbstractBridge diff --git a/src/Bridges/vertical.jl b/src/Bridges/vertical.jl index 7426750..68feebb 100644 --- a/src/Bridges/vertical.jl +++ b/src/Bridges/vertical.jl @@ -15,15 +15,15 @@ to an equality constraint instead. `VerticalBridge` supports: - * [`MOI.AbstractVectorFunction`](@ref) in [`MOI.Complements`](@ref) - * [`MOI.AbstractVectorFunction`](@ref) in [`ComplementsWithSetType{S}`](@ref) + * [`MathOptInterface.AbstractVectorFunction`](@extref) in [`MathOptInterface.Complements`](@extref) + * [`MathOptInterface.AbstractVectorFunction`](@extref) in [`ComplementsWithSetType{S}`](@ref) ## Target nodes `VerticalBridge` creates: - * [`MOI.VectorOfVariables`](@ref) in `S` - * [`MOI.ScalarAffineFunction{T}`](@ref) in [`MOI.EqualTo{T}`](@ref) + * [`MathOptInterface.VectorOfVariables`](@extref) in `S` + * [`MathOptInterface.ScalarAffineFunction`](@extref) in [`MathOptInterface.EqualTo`](@extref) """ struct VerticalBridge{T,S<:MOI.AbstractVectorSet} <: MOI.Bridges.Constraint.AbstractBridge diff --git a/src/attributes.jl b/src/attributes.jl index 28aaaeb..69ef67f 100644 --- a/src/attributes.jl +++ b/src/attributes.jl @@ -30,7 +30,7 @@ Constraint attribute that overrides the [`AbstractComplementarityRelaxation`](@r for a specific complementarity constraint. When set, this takes precedence over the model-wide default set via -[`DefaultComplementarityReformulation`](@ref). When not set, [`MOI.get`](@ref) returns the +[`DefaultComplementarityReformulation`](@ref). When not set, [`MathOptInterface.get`](@extref) returns the model-wide default. ## Example From aa8cc27f81b7ca2c652d090b03e56d7ec8dca8b4 Mon Sep 17 00:00:00 2001 From: fpacaud Date: Wed, 29 Apr 2026 22:12:36 +0200 Subject: [PATCH 2/4] add github action for documentation --- .github/workflows/docs.yml | 23 +++++++++++++++++++++++ docs/make.jl | 14 +++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..5575279 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,23 @@ +name: Documentation +on: + push: + branches: + - main + tags: '*' + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@latest + with: + version: '1' + - name: Install dependencies + run: julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + - name: Build and deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + run: julia --project=docs --color=yes docs/make.jl diff --git a/docs/make.jl b/docs/make.jl index 3b9bd43..f608add 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,32 +2,28 @@ using Documenter using DocumenterInterLinks using MathOptComplements -links = InterLinks( - "MathOptInterface" => "https://jump.dev/MathOptInterface.jl/stable/", -) +links = InterLinks("MathOptInterface" => "https://jump.dev/MathOptInterface.jl/stable/") makedocs( sitename = "MathOptComplements.jl", format = Documenter.HTML( assets = ["assets/favicon.ico"], prettyurls = Base.get(ENV, "CI", nothing) == "true", - mathengine = Documenter.KaTeX() + mathengine = Documenter.KaTeX(), ), modules = [MathOptComplements], repo = "https://github.com/blegat/MathOptComplements.jl/blob/{commit}{path}#{line}", checkdocs = :none, - clean=true, + clean = true, pages = [ "Home" => "index.md", "Quickstart" => "quickstart.md", - "Tutorials" => [ - "Equilibrium problem" => "equilibrium.md", - ], + "Tutorials" => ["Equilibrium problem" => "equilibrium.md"], "API reference" => [ "Reformulations" => "api/reformulation.md", "Relaxations" => "api/relaxation.md", "Bridges" => "api/bridges.md", - ] + ], ], plugins = [links], ) From ccbf97b37e6d0d9ebbb83bfdd265b191e47b8222 Mon Sep 17 00:00:00 2001 From: fpacaud Date: Tue, 19 May 2026 11:00:20 +0200 Subject: [PATCH 3/4] add deployment for doc --- docs/make.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/make.jl b/docs/make.jl index f608add..ba14559 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -27,3 +27,11 @@ makedocs( ], plugins = [links], ) + +deploydocs( + repo = "github.com/blegat/MathOptComplements.jl.git", + target = "build", + devbranch = "main", + devurl = "dev", + push_preview = true, +) From 7b18209a3768092a1d9e3bb9fb0bb73ee72075fa Mon Sep 17 00:00:00 2001 From: fpacaud Date: Tue, 19 May 2026 11:45:28 +0200 Subject: [PATCH 4/4] update URL --- docs/make.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index ba14559..0ce724f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -12,7 +12,7 @@ makedocs( mathengine = Documenter.KaTeX(), ), modules = [MathOptComplements], - repo = "https://github.com/blegat/MathOptComplements.jl/blob/{commit}{path}#{line}", + repo = "https://github.com/jump-dev/MathOptComplements.jl/blob/{commit}{path}#{line}", checkdocs = :none, clean = true, pages = [ @@ -29,7 +29,7 @@ makedocs( ) deploydocs( - repo = "github.com/blegat/MathOptComplements.jl.git", + repo = "github.com/jump-dev/MathOptComplements.jl.git", target = "build", devbranch = "main", devurl = "dev",