Inspired from a JuMP tutorial
using JuMP
import HiGHS
import LinearAlgebra
import Plots
import Random
We are given
A set of clients
A set of sites where a facility can be built
Decision variables Decision variables are split into two categories:
Binary variable indicates whether facility is built or not
Binary variable indicates whether client is assigned to facility
Objective The objective is to minimize the total cost of serving all clients. This costs breaks down into two components:
Fixed cost of building a facility.
In this example, this cost is .
Cost of serving clients from the assigned facility.
In this example, the cost of serving client from facility is the Euclidean distance between the two.
Constraints
Each customer must be served by exactly one facility
A facility cannot serve any client unless it is open
The problem can be formulated as the following MILP:
where the first set of constraints ensures that each client is served exactly once, and the second set of constraints ensures that no client is served from an unopened facility.
Random.seed!(314)
number of clients
m = 12
number of facility locations
n = 5
Clients' locations
Xc = rand(m)
Yc = rand(m)
Facilities' potential locations
Xf = rand(n)
Yf = rand(n)
Fixed costs
f = ones(n);
Distance
c = zeros(m, n)
for i in 1:m
for j in 1:n
c[i, j] = LinearAlgebra.norm([Xc[i] - Xf[j], Yc[i] - Yf[j]], 2)
end
end
Display the data
Plots.scatter(
Xc,
Yc,
label = "Clients",
markershape = :circle,
markercolor = :blue,
)
Plots.scatter!(
Xf,
Yf,
label = "Facility",
markershape = :square,
markercolor = :white,
markersize = 6,
markerstrokecolor = :red,
markerstrokewidth = 2,
)
Create a JuMP model
ufl = Model(HiGHS.Optimizer)
Variables
@variable(ufl, y[1:n], Bin);
@variable(ufl, x[1:m, 1:n], Bin);
Each client is served exactly once
@constraint(ufl, client_service[i in 1:m], sum(x[i, j] for j in 1:n) == 1);
A facility must be open to serve a client
@constraint(ufl, open_facility[i in 1:m, j in 1:n], x[i, j] <= y[j]);
Objective
@objective(ufl, Min, f'y + sum(c .* x));
Solve the uncapacitated facility location problem with HiGHS
optimize!(ufl)
solution_summary(ufl)
The threshold 1e-5 ensure that edges between clients and facilities are drawn when x[i, j] ≈ 1.
x_ = value.(x) .> 1 - 1e-5
y_ = value.(y) .> 1 - 1e-5
Display clients
p = Plots.scatter(
Xc,
Yc,
markershape = :circle,
markercolor = :blue,
label = nothing,
)
Show open facility
mc = [(y_[j] ? :red : :white) for j in 1:n]
Plots.scatter!(
Xf,
Yf,
markershape = :square,
markercolor = mc,
markersize = 6,
markerstrokecolor = :red,
markerstrokewidth = 2,
label = nothing,
)
Show client-facility assignment
for i in 1:m
for j in 1:n
if x_[i, j] == 1
Plots.plot!(
[Xc[i], Xf[j]],
[Yc[i], Yf[j]],
color = :black,
label = nothing,
)
end
end
end
p
The capacitated variant introduces a capacity constraint on each facility, i.e., clients have a certain level of demand to be served, while each facility only has finite capacity which cannot be exceeded.
Specifically,
The demand of client is denoted by
The capacity of facility is denoted by
The capacity constraints then write
Note that, if is set to , the capacity constraint above automatically forces to .
Thus, the capacitated facility location can be formulated as follows
For simplicity, we will assume that there is enough capacity to serve the demand, i.e., there exists at least one feasible solution.
Random.seed!(314)
Demands
a = rand(1:3, m);
Capacities
q = rand(5:10, n);
Display the data
Plots.scatter(
Xc,
Yc,
label = nothing,
markershape = :circle,
markercolor = :blue,
markersize = 2 .* (2 .+ a),
)
Plots.scatter!(
Xf,
Yf,
label = nothing,
markershape = :rect,
markercolor = :white,
markersize = q,
markerstrokecolor = :red,
markerstrokewidth = 2,
)
This page was generated using Literate.jl.