-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPPMultiCommodity.py
173 lines (149 loc) · 6.32 KB
/
PPMultiCommodity.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
"""
This module contains the class PPMultiCommodity
can be executed as a demo script that solves a random initialized problem and prints the result
"""
from pyomo.environ import *
from pyomo.opt import SolverFactory
from pyomo.repn.plugins.baron_writer import Binary, NonNegativeReals
from ParamGenerator import ParamGenerator
from termcolor import colored
class PPMultiCommodity:
"""
This class manages the model for O(n^2) variables production planning problem as multi commodity
"""
def __init__(self, param_generator):
self.param_generator = param_generator
self.week_iter = self.param_generator.init_production_slots()
# objective function to be passed to the model
@staticmethod
def __obj_rule(model):
return sum(
model.ProductionCost[i] * model.Production[i, destination_slot]
+ model.StockingCost[i] * model.Stock[i, destination_slot]
for (i, destination_slot) in model.SourceDestinationIndex
# if destination_slot >= i
) + sum(
model.SetUpCost[i] * model.SetUp[i]
for i in model.Weeks
)
# rule about stocking quantities to be passed to the model
@staticmethod
def __stock_rule(model, i, destination_slot):
pre_stocked = 0
if i == model.Weeks[1] and destination_slot == i: # initial stock only for demand 1
pre_stocked = model.InitialStock
elif i > model.Weeks[1]: # picks the already stocked value if exists
pre_stocked = model.Stock[i - 1, destination_slot]
return model.Stock[i, destination_slot] == (
pre_stocked
- (model.Demand[i] if i == destination_slot else 0) # delta(t)*(- demand(t))
+ model.Production[i, destination_slot]
)
# rule about empty stock if the destination slot is the current slot
@staticmethod
def __zero_stock_rule(model, i, destination_slot):
return model.Stock[i, destination_slot] == 0 if i == destination_slot else Constraint.Feasible
# rule about production quantities to be passed to the model
@staticmethod
def __production_rule(model, i, destination_slot):
# if we don't pay the setup we don't produce
# demand(x) is big_M for production(i,x)
return model.Demand[destination_slot] * model.SetUp[i] >= model.Production[i, destination_slot]
# filter that keeps only the triangular matrix of used variables of the total n^2 variables
@staticmethod
def __source_destination_filter(model, source, destination):
return source <= destination
def build_model(self):
"""
Builds the abstract model for the production planning problem
:return: the built abstract model
"""
# Model
model = AbstractModel()
# Sets
model.Weeks = RangeSet(next(self.week_iter))
model.SourceDestinationIndex = Set(
initialize=model.Weeks * model.Weeks,
filter=self.__source_destination_filter)
# variables
model.Production = Var(model.SourceDestinationIndex, domain=NonNegativeReals)
model.SetUp = Var(model.Weeks, domain=Binary)
model.Stock = Var(model.SourceDestinationIndex, domain=NonNegativeReals)
# params
model.InitialStock = Param(
initialize=lambda mod: self.param_generator.init_initial_stock(mod),
default=0)
model.ProductionCost = Param(
model.Weeks,
initialize=lambda mod, i: self.param_generator.init_production_cost(mod, i),
default=0)
model.SetUpCost = Param(
model.Weeks,
initialize=lambda mod, i: self.param_generator.init_setup_costs(mod, i),
default=0)
model.StockingCost = Param(
model.Weeks,
initialize=lambda mod, i: self.param_generator.init_stocking_cost(mod, i),
default=0)
model.Demand = Param(
model.Weeks,
initialize=lambda mod, i: self.param_generator.init_demand(mod, i),
default=0)
# objective
model.obj = Objective(rule=self.__obj_rule)
# constraints
model.pc = Constraint(model.SourceDestinationIndex, rule=self.__production_rule)
model.sc = Constraint(model.SourceDestinationIndex, rule=self.__stock_rule)
model.zs = Constraint(model.SourceDestinationIndex, rule=self.__zero_stock_rule)
return model
def get_solution(self):
"""
helper method that generates and solves an instance of the production planning problem
:return: the solved instance
"""
model = self.build_model()
opt = SolverFactory('cplex_persistent')
instance = model.create_instance()
opt.set_instance(instance)
res = opt.solve(tee=False)
return instance
if __name__ == '__main__':
instance = PPMultiCommodity(ParamGenerator()).get_solution()
print("prod each column a production-slot row destination-slot")
for w in instance.Weeks:
print(
*(
(
(
colored(value(instance.Production[i, w]), "green")
if i == w
else value(instance.Production[i, w])
)
if (i, w) in instance.SourceDestinationIndex
else "x"
for i in instance.Weeks))
, sep="|\t"
)
print("setup")
print(*(round(value(instance.SetUp[w])) for w in instance.Weeks), sep="\t")
print("stock each column a production-slot row destination-slot")
for w in instance.Weeks:
print(
*(
(
(
colored(value(instance.Stock[i, w]), "green")
if i == w
else value(instance.Stock[i, w])
)
if (i, w) in instance.SourceDestinationIndex
else "x"
for i in instance.Weeks))
, sep="|\t"
)
print("parameters")
print("A0 = {}".format(value(instance.InitialStock)))
for w in instance.Weeks:
print("slot{} # demand = {} # P-cost = {} # S-cost = {}".format(
w, value(instance.Demand[w]), value(instance.ProductionCost[w]), value(instance.StockingCost[w])
))