forked from scipopt/PySCIPOpt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
flp-benders.py
128 lines (102 loc) · 4 KB
/
flp-benders.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
##@file flp-benders.py
#@brief model for solving the capacitated facility location problem using Benders' decomposition
"""
minimize the total (weighted) travel cost from n customers
to some facilities with fixed costs and capacities.
Copyright (c) by Joao Pedro PEDROSO and Mikio KUBO, 2012
"""
from pyscipopt import Model, quicksum, multidict, SCIP_PARAMSETTING
import pdb
def flp(I,J,d,M,f,c):
"""flp -- model for the capacitated facility location problem
Parameters:
- I: set of customers
- J: set of facilities
- d[i]: demand for customer i
- M[j]: capacity of facility j
- f[j]: fixed cost for using a facility in point j
- c[i,j]: unit cost of servicing demand point i from facility j
Returns a model, ready to be solved.
"""
master = Model("flp-master")
subprob = Model("flp-subprob")
# creating the problem
y = {}
for j in J:
y[j] = master.addVar(vtype="B", name="y(%s)"%j)
master.setObjective(
quicksum(f[j]*y[j] for j in J),
"minimize")
master.data = y
# creating the subproblem
x,y = {},{}
for j in J:
y[j] = subprob.addVar(vtype="B", name="y(%s)"%j)
for i in I:
x[i,j] = subprob.addVar(vtype="C", name="x(%s,%s)"%(i,j))
for i in I:
subprob.addCons(quicksum(x[i,j] for j in J) == d[i], "Demand(%s)"%i)
for j in M:
subprob.addCons(quicksum(x[i,j] for i in I) <= M[j]*y[j], "Capacity(%s)"%i)
for (i,j) in x:
subprob.addCons(x[i,j] <= d[i]*y[j], "Strong(%s,%s)"%(i,j))
subprob.setObjective(
quicksum(c[i,j]*x[i,j] for i in I for j in J),
"minimize")
subprob.data = x,y
return master, subprob
def make_data():
"""creates example data set"""
I,d = multidict({1:80, 2:270, 3:250, 4:160, 5:180}) # demand
J,M,f = multidict({1:[500,1000], 2:[500,1000], 3:[500,1000]}) # capacity, fixed costs
c = {(1,1):4, (1,2):6, (1,3):9, # transportation costs
(2,1):5, (2,2):4, (2,3):7,
(3,1):6, (3,2):3, (3,3):4,
(4,1):8, (4,2):5, (4,3):3,
(5,1):10, (5,2):8, (5,3):4,
}
return I,J,d,M,f,c
if __name__ == "__main__":
I,J,d,M,f,c = make_data()
master, subprob = flp(I,J,d,M,f,c)
# initializing the default Benders' decomposition with the subproblem
master.setPresolve(SCIP_PARAMSETTING.OFF)
master.setBoolParam("misc/allowstrongdualreds", False)
master.setBoolParam("benders/copybenders", False)
master.initBendersDefault(subprob)
# optimizing the problem using Benders' decomposition
master.optimize()
# solving the subproblems to get the best solution
master.computeBestSolSubproblems()
EPS = 1.e-6
y = master.data
facilities = [j for j in y if master.getVal(y[j]) > EPS]
x, suby = subprob.data
edges = [(i,j) for (i,j) in x if subprob.getVal(x[i,j]) > EPS]
print("Optimal value:", master.getObjVal())
print("Facilities at nodes:", facilities)
print("Edges:", edges)
master.printStatistics()
# since computeBestSolSubproblems() was called above, we need to free the
# subproblems. This must happen after the solution is extracted, otherwise
# the solution will be lost
master.freeBendersSubproblems()
try: # plot the result using networkx and matplotlib
import networkx as NX
import matplotlib.pyplot as P
P.clf()
G = NX.Graph()
other = [j for j in y if j not in facilities]
customers = ["c%s"%i for i in d]
G.add_nodes_from(facilities)
G.add_nodes_from(other)
G.add_nodes_from(customers)
for (i,j) in edges:
G.add_edge("c%s"%i,j)
position = NX.drawing.layout.spring_layout(G)
NX.draw(G,position,node_color="y",nodelist=facilities)
NX.draw(G,position,node_color="g",nodelist=other)
NX.draw(G,position,node_color="b",nodelist=customers)
P.show()
except ImportError:
print("install 'networkx' and 'matplotlib' for plotting")