import pandas as pd
import numpy as np
import gurobipy as gp
from gurobipy import GRB
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
= pd.read_excel('NodeLocations.xlsx', sheet_name = 'MoleStNicholas').to_numpy()
nLocs = len(nLocs)
N = cdist(nLocs, nLocs)
d = 100
Dmax
for i in range(N):
0], nLocs[i,1], color = 'red', s = 2) plt.scatter(nLocs[i,
Set Covering
Minimum Set Cover
Problem
Minimum number of facilities with coverage distance Dmax, co-located with demand points, to serve all demand points.
\[ \begin{split} \begin{align} &N: \text{number of demand points} \\ &d_{ij}: \text{euclidian distance between locations i and j} \\ &y_{j}: 1, \text{if a facility is opened at location j} \\ &x_{ij} : 1, \text{if node i is served by facility at j} \\ \end{align} \end{split} \\ \\ \]
\[ \begin{split} \begin{align} &\text{minimize} \ \sum_{j \in N} y_{j} \\ &\text{st} \\ &\\ &\sum_{j \in N} x_{ij} = 1 \qquad \qquad \qquad\ \ \forall i \in N \\ &y_{j} \ge x_{ij} \qquad \qquad \qquad \qquad \forall i \epsilon N, \forall j \in N \\ &\sum_{j \in N} x_{ij}d_{ij} \le Dmax \qquad \quad\ \forall i \in N\\ & x_{ij}, y_{j} \ \in \ \{0,1\} \qquad \qquad \quad \forall i \in N, \forall j \in N \\ \end{align} \end{split} \\ \\ \]
#model
= gp.Model('MinSetCover-1')
model
#dvar
= model.addVars(N,N, vtype = gp.GRB.BINARY, name = 'x')
x = model.addVars(N, vtype = gp.GRB.BINARY, name = 'y')
y
#obj
for i in range(N)), GRB.MINIMIZE)
model.setObjective(gp.quicksum(y[i]
#constr
for i in range(N):
for j in range(N)) == 1)
model.addConstr(gp.quicksum(x[i,j]
for i in range(N):
for j in range(N):
<= y[j])
model.addConstr(x[i,j]
for i in range(N):
*d[i,j] for j in range(N)) <= Dmax)
model.addConstr(gp.quicksum(x[i,j]
'TimeLimit', 120)
model.setParam(#model.write('MinSetCover.lp')
model.optimize()
Set parameter Username
Academic license - for non-commercial use only - expires 2024-11-09
Set parameter TimeLimit to value 120
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (win64)
CPU model: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 4 physical cores, 8 logical processors, using up to 8 threads
Optimize a model with 739599 rows, 738740 columns and 2950665 nonzeros
Model fingerprint: 0xfc528306
Variable types: 0 continuous, 738740 integer (738740 binary)
Coefficient statistics:
Matrix range [1e+00, 1e+04]
Objective range [1e+00, 1e+00]
Bounds range [1e+00, 1e+00]
RHS range [1e+00, 1e+02]
Found heuristic solution: objective 576.0000000
Presolve removed 725159 rows and 724301 columns
Presolve time: 2.51s
Presolved: 14440 rows, 14439 columns, 41453 nonzeros
Found heuristic solution: objective 454.0000000
Variable types: 0 continuous, 14439 integer (14439 binary)
Root simplex log...
Iteration Objective Primal Inf. Dual Inf. Time
17553 8.0253314e+01 4.315775e+02 0.000000e+00 5s
23251 8.0579933e+01 0.000000e+00 0.000000e+00 7s
Root relaxation: objective 8.057993e+01, 23251 iterations, 4.01 seconds (2.26 work units)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 80.57993 0 4043 454.00000 80.57993 82.3% - 9s
H 0 0 309.0000000 80.57993 73.9% - 9s
H 0 0 109.0000000 80.57993 26.1% - 9s
H 0 0 107.0000000 80.57993 24.7% - 9s
H 0 0 105.0000000 80.57993 23.3% - 9s
H 0 0 92.0000000 80.57993 12.4% - 13s
0 0 80.58013 0 4063 92.00000 80.58013 12.4% - 14s
H 0 0 88.0000000 80.58013 8.43% - 14s
0 0 80.58013 0 4089 88.00000 80.58013 8.43% - 14s
0 0 80.75072 0 4061 88.00000 80.75072 8.24% - 22s
0 0 80.75072 0 4083 88.00000 80.75072 8.24% - 22s
H 0 0 87.0000000 80.86783 7.05% - 32s
H 0 0 86.0000000 80.86783 5.97% - 32s
H 0 0 83.0000000 80.86783 2.57% - 32s
0 0 80.86783 0 4206 83.00000 80.86783 2.57% - 32s
0 0 80.86783 0 4379 83.00000 80.86783 2.57% - 33s
0 0 80.87154 0 4227 83.00000 80.87154 2.56% - 41s
0 0 80.94153 0 4228 83.00000 80.94153 2.48% - 41s
0 0 80.94678 0 4240 83.00000 80.94678 2.47% - 45s
0 0 80.94678 0 4101 83.00000 80.94678 2.47% - 47s
0 2 80.94678 0 4101 83.00000 80.94678 2.47% - 71s
3 8 80.94678 2 4429 83.00000 80.94678 2.47% 6190 75s
19 24 81.15542 5 3652 83.00000 80.94678 2.47% 2801 80s
60 33 81.28425 4 3404 83.00000 80.94678 2.47% 1419 86s
83 50 81.53238 5 3165 83.00000 80.94678 2.47% 1346 90s
134 50 cutoff 13 83.00000 80.94678 2.47% 1126 96s
169 67 81.90021 9 2731 83.00000 80.94678 2.47% 1098 100s
205 71 81.13124 6 4331 83.00000 80.94678 2.47% 1057 105s
230 81 81.98003 11 3105 83.00000 80.94678 2.47% 1072 110s
264 88 81.32847 6 3915 83.00000 81.03924 2.36% 1028 115s
302 103 cutoff 11 83.00000 81.03924 2.36% 1020 120s
Cutting planes:
Zero half: 14
Explored 303 nodes (385526 simplex iterations) in 120.17 seconds (82.00 work units)
Thread count was 8 (of 8 available processors)
Solution count 10: 83 86 87 ... 454
Time limit reached
Best objective 8.300000000000e+01, best bound 8.200000000000e+01, gap 1.2048%
= plt.subplots(figsize=(10,10))
fig,ax 'equal')
ax.set_aspect(for i in range(N):
if y[i].x > 0.9:
0], nLocs[i,1], color = 'black', s = 2)
ax.scatter(nLocs[i,= plt.Circle([nLocs[i,0], nLocs[i,1]], Dmax, color = 'blue', fill = False)
circle
ax.add_artist(circle)else:
0], nLocs[i,1], color = 'red', s = 2) ax.scatter(nLocs[i,