Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for vector-valued objectives #3176

Merged
merged 24 commits into from
Feb 15, 2023
Merged
Show file tree
Hide file tree
Changes from 23 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
4 changes: 3 additions & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
MultiObjectiveAlgorithms = "0327d340-17cd-11ea-3e99-2fd5d98cecda"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand All @@ -33,7 +34,8 @@ Ipopt = "=1.1.0"
JSON = "0.21"
JSONSchema = "1"
Literate = "2.8"
MathOptInterface = "=1.11.5"
MathOptInterface = "=1.12.0"
MultiObjectiveAlgorithms = "=0.1.1"
Plots = "1"
SCS = "=1.1.3"
SQLite = "1"
Expand Down
4 changes: 3 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ const _PAGES = [
"tutorials/linear/factory_schedule.md",
"tutorials/linear/finance.md",
"tutorials/linear/geographic_clustering.md",
"tutorials/linear/multi_objective_knapsack.md",
"tutorials/linear/multi_objective_examples.md",
"tutorials/linear/knapsack.md",
"tutorials/linear/multi.md",
"tutorials/linear/n-queens.md",
Expand Down Expand Up @@ -284,7 +286,7 @@ function _add_moi_pages()
!!! warning
This documentation in this section is a copy of the official
MathOptInterface documentation available at
[https://jump.dev/MathOptInterface.jl/v1.11.5](https://jump.dev/MathOptInterface.jl/v1.11.5).
[https://jump.dev/MathOptInterface.jl/v1.12.0](https://jump.dev/MathOptInterface.jl/v1.20.0).
It is included here to make it easier to link concepts between JuMP and
MathOptInterface.
"""
Expand Down
81 changes: 81 additions & 0 deletions docs/src/manual/objective.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,84 @@ julia> @objective(model, Min, 2x)
julia> @objective(model, Max, objective_function(model))
2 x
```

## Set a vector-valued objective

Define a multi-objective optimization problem by passing a vector of objectives:

```jldoctest; setup = :(model=Model())
julia> @variable(model, x[1:2]);

julia> @objective(model, Min, [1 + x[1], 2 * x[2]])
2-element Vector{AffExpr}:
x[1] + 1
2 x[2]

julia> f = objective_function(model)
2-element Vector{AffExpr}:
x[1] + 1
2 x[2]
```

!!! tip
The [Multi-objective knapsack](@ref) tutorial provides an example of
solving a multi-objective integer program.

In most cases, multi-objective optimization solvers will return multiple
solutions, corresponding to points on the Pareto frontier. See [Multiple solutions](@ref)
for information on how to query and work with multiple solutions.

Note that you must set a single objective sense, that is, you cannot have
both minimization and maximization objectives. Work around this limitation by
choosing `Min` and negating any objectives you want to maximize:

```jldoctest; setup = :(model=Model())
julia> @variable(model, x[1:2]);

julia> @expression(model, obj1, 1 + x[1])
x[1] + 1

julia> @expression(model, obj2, 2 * x[1])
2 x[1]

julia> @objective(model, Min, [obj1, -obj2])
2-element Vector{AffExpr}:
x[1] + 1
-2 x[1]
```

Defining your objectives as expressions allows flexibility in how you can solve
variations of the same problem, with some objectives removed and constrained to
be no worse that a fixed value.

```jldoctest; setup = :(model=Model())
julia> @variable(model, x[1:2]);

julia> @expression(model, obj1, 1 + x[1])
x[1] + 1

julia> @expression(model, obj2, 2 * x[1])
2 x[1]

julia> @expression(model, obj3, x[1] + x[2])
x[1] + x[2]

julia> @objective(model, Min, [obj1, obj2, obj3]) # Three-objective problem
3-element Vector{AffExpr}:
x[1] + 1
2 x[1]
x[1] + x[2]

julia> # optimize!(model), look at the solution, talk to stakeholders, then
# decide you want to solve a new problem where the third objective is
# removed and constrained to be better than 2.0.
nothing

julia> @objective(model, Min, [obj1, obj2]) # Two-objective problem
2-element Vector{AffExpr}:
x[1] + 1
2 x[1]

julia> @constraint(model, obj3 <= 2.0)
x[1] + x[2] ≤ 2.0
```
4 changes: 4 additions & 0 deletions docs/src/manual/solutions.md
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,10 @@ for i in 2:result_count(model)
end
```

!!! tip
The [Multi-objective knapsack](@ref) tutorial provides an example of
querying multiple solutions.

## Checking feasibility of solutions

To check the feasibility of a primal solution, use
Expand Down
124 changes: 124 additions & 0 deletions docs/src/tutorials/linear/multi_objective_examples.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Copyright 2017, Iain Dunning, Joey Huchette, Miles Lubin, and contributors #src
# This Source Code Form is subject to the terms of the Mozilla Public License #src
# v.2.0. If a copy of the MPL was not distributed with this file, You can #src
# obtain one at https://mozilla.org/MPL/2.0/. #src

# # Simple multi-objective examples

# This tutorial contains a number of examples of multi-objective programs from
# the literature.

# ## Required packages

# This tutorial requires the following packages:

using JuMP
import HiGHS
import MultiObjectiveAlgorithms as MOA

# ## Bi-objective linear problem

# This is example is taken from Example 6.3 (from Steuer, 1985), page 154 of
# Multicriteria Optimization (2nd ed), M. Ehrgott, Springer 2005. The code was
# adapted from an example in [vOptGeneric](https://github.com/vOptSolver/vOptGeneric.jl)
# by [@xgandibleux](https://github.com/xgandibleux).

model = Model()
set_silent(model)
@variable(model, x1 >= 0)
@variable(model, 0 <= x2 <= 3)
@objective(model, Min, [3x1 + x2, -x1 - 2x2])
@constraint(model, 3x1 - x2 <= 6)
set_optimizer(model, () -> MOA.Optimizer(HiGHS.Optimizer))
set_optimizer_attribute(
model,
MOA.Algorithm(),
MOA.Lexicographic(; all_permutations = true),
)
optimize!(model)
solution_summary(model)

#-

for i in 1:result_count(model)
print(i, ": z = ", round.(Int, objective_value(model; result = i)), " | ")
println("x = ", value.([x1, x2]; result = i))
end

# ## Bi-objective linear assignment problem

# This is example is taken from Example 9.38 (from Ulungu and Teghem, 1994),
# page 255 of Multicriteria Optimization (2nd ed), M. Ehrgott, Springer 2005.
# The code was adapted from an example in [vOptGeneric](https://github.com/vOptSolver/vOptGeneric.jl)
# by [@xgandibleux](https://github.com/xgandibleux).

C1 = [5 1 4 7; 6 2 2 6; 2 8 4 4; 3 5 7 1]
C2 = [3 6 4 2; 1 3 8 3; 5 2 2 3; 4 2 3 5]
n = size(C2, 1)
model = Model()
set_silent(model)
@variable(model, x[1:n, 1:n], Bin)
@objective(model, Min, [sum(C1 .* x), sum(C2 .* x)])
@constraint(model, [i = 1:n], sum(x[i, :]) == 1)
@constraint(model, [j = 1:n], sum(x[:, j]) == 1)
set_optimizer(model, () -> MOA.Optimizer(HiGHS.Optimizer))
set_optimizer_attribute(model, MOA.Algorithm(), MOA.EpsilonConstraint())
optimize!(model)
solution_summary(model)

#-

for i in 1:result_count(model)
print(i, ": z = ", round.(Int, objective_value(model; result = i)), " | ")
println("x = ", round.(Int, value.(x; result = i)))
end

# ## Bi-objective shortest path problem

# This is example is taken from Exercise 9.5 page 269 of Multicriteria
# Optimization (2nd edt), M. Ehrgott, Springer 2005. The code was adapted from
# an example in [vOptGeneric](https://github.com/vOptSolver/vOptGeneric.jl) by
# [@xgandibleux](https://github.com/xgandibleux).

M = 50
C1 = [
M 4 5 M M M
M M 2 1 2 7
M M M 5 2 M
M M 5 M M 3
M M M M M 4
M M M M M M
]
C2 = [
M 3 1 M M M
M M 1 4 2 2
M M M 1 7 M
M M 1 M M 2
M M M M M 2
M M M M M M
]
n = size(C2, 1)
model = Model()
set_silent(model)
@variable(model, x[1:n, 1:n], Bin)
@objective(model, Min, [sum(C1 .* x), sum(C2 .* x)])
@constraint(model, sum(x[1, :]) == 1)
@constraint(model, sum(x[:, n]) == 1)
@constraint(model, [i = 2:n-1], sum(x[i, :]) - sum(x[:, i]) == 0)
set_optimizer(model, () -> MOA.Optimizer(HiGHS.Optimizer))
set_optimizer_attribute(model, MOA.Algorithm(), MOA.EpsilonConstraint())
optimize!(model)
solution_summary(model)

#-

for i in 1:result_count(model)
print(i, ": z = ", round.(Int, objective_value(model; result = i)), " | ")
X = round.(Int, value.(x; result = i))
print("Path:")
for ind in findall(val -> val ≈ 1, X)
i, j = ind.I
print(" $i->$j")
end
println()
end
Loading