Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 69 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,92 @@
# MathOptComplements
# MathOptComplements.jl

| **Build Status** |
|:----------------:|
| [![Build Status][build-img]][build-url] [![Codecov branch][codecov-img]][codecov-url] |
[![Build Status](https://github.com/jump-dev/MathOptComplements.jl/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/jump-dev/MathOptComplements.jl/actions?query=workflow%3ACI)
[![Codecov branch](https://codecov.io/gh/jump-dev/MathOptComplements.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/jump-dev/MathOptComplements.jl/branch/main)

This package provides a set of routines to reformulate as a nonlinear program any JuMP's problem with
[complementarity constraints](https://jump.dev/JuMP.jl/v1.29.3/moi/reference/standard_form/#MathOptInterface.Complements).
Once reformulated, the problem can be solved using a nonlinear programming solver like [Ipopt](https://coin-or.github.io/Ipopt/).
[MathOptComplements.jl](https://github.com/jump-dev/MathOptComplements.jl) is a
JuMP extension for reformulating complementarity constraints.

## Quick-start
## License

`MathOptComplements.jl` is licensed under the [MIT License](https://github.com/jump-dev/MathOptComplements.jl/blob/main/LICENSE.md).

## Getting help

If you need help, please ask a question on the [JuMP community forum](https://jump.dev/forum).

If you have a reproducible example of a bug, please [open a GitHub issue](https://github.com/jump-dev/MathOptLazy.jl/issues/new).

## Installation

Install `MathOptComplements` using `Pkg.add`:

We aim at solving the following mathematical program with complementarity constraints (MPCC):
```julia
using JuMP
import Pkg
Pkg.add(; url = "https://github.com/jump-dev/MathOptComplements.jl")
```

## Use with JuMP

model = Model()
Use `MathOptComplements.jl` with JuMP as follows:
```julia
using JuMP
import Ipopt
import MathOptComplements
model = Model(() -> MathOptComplements.Optimizer(Ipopt.Optimizer()))
set_attribute(
model,
MathOptComplements.DefaultComplementarityReformulation(),
MathOptComplements.ScholtesRelaxation(0.0),
)
@variable(model, z[1:2])
set_lower_bound(z[2], 0)
@objective(model, Min, (z[1] - 1)^2 + z[2]^2)
@constraint(model, [z[2] - z[1], z[2]] ∈ MOI.Complements(2))
@constraint(model, z[2] - z[1] ⟂ z[2])
optimize!(model)
```
MathOptComplements reformulates automatically all the complementarity
constraints using [MOI.Bridges](https://jump.dev/MathOptInterface.jl/stable/submodules/Bridges/overview/).
Solving the problem with Ipopt and MathOptComplements just amounts to either do:
```julia
using Ipopt
using MathOptComplements

MathOptComplements.Bridges.add_all_bridges(model)
set_optimizer(model, Ipopt.Optimizer)
```
or
If you use Ipopt, we recommend setting the following options to improve the
performance:
```julia
using Ipopt
using MathOptComplements

set_optimizer(model, () -> MathOptComplements.Optimizer(Ipopt.Optimizer()))
```
before you call
```julia
JuMP.optimize!(model)
set_attribute(model, "mu_strategy", "adaptive")
set_attribute(model, "bound_push", 1e-1)
set_attribute(model, "bound_relax_factor", 0.0)
```

We recommend setting the following options in Ipopt to improve the 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)
## Supported reformulations

```
You can change the reformulation by using the optimizer attribute
`MathOptComplements.DefaultComplementarityReformulation`. MathOptComplements
supports the following reformulations:

## Supported reformulations
- `MathOptComplements.ScholtesRelaxation(tau)` (**default**)

You can change the reformulation by using the optimizer attribute `MathOptComplements.DefaultComplementarityReformulation`:
```julia
MOI.set(model, MathOptComplements.DefaultComplementarityReformulation(), MathOptComplements.ScholtesRelaxation(0.0))
```
Reformulates the complementarity $0 \le a \perp b \ge 0$ as $a, b \ge 0$ and
$a \cdot b ≤ \tau$. For $\tau = 0$, the reformulation is exact and leads to the
formulation of a degenerate nonlinear program. The larger the parameter
$\tau \ge 0$, the better the behavior in Ipopt.

- `MathOptComplements.FischerBurmeisterRelaxation(tau)`

Reformulates the complementarity $0 \le a \perp b \ge 0$ as $a, b \ge 0$ and
$a + b \le \sqrt{(a+b)^2 + \tau}$.

> [!note]
> The `MathOptComplements.DefaultComplementarityReformulation` attribute only works if you used
> `set_optimizer(model, () -> MathOptComplements.Optimizer(...))`, not `MathOptComplements.Bridges.add_all_bridges(model)`.
> That is the only difference between the two though so if you are not using setting
> `MathOptComplements.DefaultComplementarityReformulation` because you don't change the default reformulation
> or because you set it constraint-wise with the constraint attribute `MathOptComplements.ComplementarityReformulation`
> then `MathOptComplements.Bridges.add_all_bridges(model)` will work.
- `MathOptComplements.LiuFukushimaRelaxation(tau)`

MathOptComplements supports the following reformulations:
- `MathOptComplements.ScholtesRelaxation(tau)` (**default**): reformulates the complementarity `0 ≤ a ⟂ b ≥ 0` as `0 ≤ (a, b)` and `a b ≤ tau`. For `tau = 0`, the reformulation is exact and leads to the formulation of a degenerate nonlinear program. The higher the parameter `tau`, the better the behavior in Ipopt.
- `MathOptComplements.FischerBurmeisterRelaxation(tau)`: reformulates the complementarity `0 ≤ a ⟂ b ≥ 0` as `0 ≤ (a, b)` and `a + b - sqrt((a+b)^2 + tau) ≤ 0`.
- `MathOptComplements.LiuFukushimaRelaxation(tau)`: reformulates the complementarity `0 ≤ a ⟂ b ≥ 0` as `a b ≤ tau^2` and `(a + tau)(b + tau) ≥ tau^2`.
- `MathOptComplements.KanzowSchwarzRelaxation(tau)`: reformulates the complementarity `0 ≤ a ⟂ b ≥ 0` as `0 ≤ (a, b)` and `ϕ(a, b) ≤ 0`, with `ϕ(a, b) = (a - tau)(b - tau)` if `a + b > 2tau`, `-0.5((a -tau)^2 + (b - tau)^2)` otherwise.
Reformulates the complementarity $0 \le a \perp b \ge 0$ as $a\cdot b \le \tau^2$
and $(a + \tau)(b + \tau) \ge \tau^2$.

Most reformulations are not equivalent to the original problem, explaining why they are not activated by default.
You can find [here](https://arxiv.org/html/2312.11022v2) a recent benchmark comparing the different reformulations on [MacMPEC](https://www.mcs.anl.gov/~leyffer/macmpec/).
- `MathOptComplements.KanzowSchwarzRelaxation(tau)`

Reformulates the complementarity $0 \le a \perp b \ge 0$ as $a, b \ge 0$ and
$\phi(a, b) \le 0$, with $\phi(a, b) = (a - \tau)(b - \tau)$ if $a + b > 2\tau$,
$-0.5((a - \tau)^2 + (b - \tau)^2)$ otherwise.

Most reformulations are not equivalent to the original problem, which is why
they are not activated by default. [This arXiv paper](https://arxiv.org/html/2312.11022v2)
has a recent benchmark comparing the different reformulations on [MacMPEC](https://www.mcs.anl.gov/~leyffer/macmpec/).

## 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".

[build-img]: https://github.com/jump-dev/MathOptComplements.jl/actions/workflows/ci.yml/badge.svg?branch=main
[build-url]: https://github.com/jump-dev/MathOptComplements.jl/actions?query=workflow%3ACI
[codecov-img]: https://codecov.io/gh/jump-dev/MathOptComplements.jl/branch/main/graph/badge.svg
[codecov-url]: https://codecov.io/gh/jump-dev/MathOptComplements.jl/branch/main
which has funded the PGMO-IROE project "A new optimization suite for large-scale
market equilibrium".
Loading