File: a_transportation_problem.rst

package info (click to toggle)
python-pulp 2.6.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 14,720 kB
  • sloc: python: 7,505; makefile: 16; sh: 16
file content (341 lines) | stat: -rw-r--r-- 14,671 bytes parent folder | download
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
A Transportation Problem
========================

Problem Description
-------------------
A boutique brewery has two warehouses from which it distributes beer to five 
carefully chosen bars. At the start of every week, each bar sends an order to 
the brewery’s head office for so many crates of beer, which is then dispatched 
from the appropriate warehouse to the bar. The brewery would like to have an 
interactive computer program which they can run week by week to tell them which 
warehouse should supply which bar so as to minimize the costs of the whole 
operation. For example, suppose that at the start of a given week the brewery 
has 1000 cases at warehouse A, and 4000 cases at warehouse B, and that the bars 
require 500, 900, 1800, 200, and 700 cases respectively. Which warehouse should 
supply which bar?

Formulation
-----------
For transportation problems, using a graphical representation of the problem 
is often helpful during formulation. Here is a graphical representation of The 
Beer Distribution Problem.

.. image:: images/brewery_nodes.jpg

Identify the Decision Variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In transportation problems we are deciding how to transport goods from their 
supply nodes to their demand nodes. The decision variables are the Arcs 
connecting these nodes, as shown in the diagram below. We are deciding how many 
crates of beer to transport from each warehouse to each pub. 
  
.. image:: images/brewery_arcs.jpg

* A1 = number of crates of beer to ship from Warehouse A to Bar 1
* A5 = number of crates of beer to ship from Warehouse A to Bar 5
* B1 = number of crates of beer to ship from Warehouse B to Bar 1
* B5 = number of crates of beer to ship from Warehouse B to Bar 5

Let:

.. math::

    W &= \{A,B\} \\
    B &= \{1, 2, 3, 4, 5 \} \\
    x_{(w,b)} &\ge 0 \ldots \forall w \in W, b \in B \\
    x_{(w,b)} & \in \mathbb{Z}^+ \ldots \forall w \in W, b \in B \\
 
The lower bound on the variables is Zero, and the values must all be Integers 
(since the number of crates cannot be negative or fractional). There is no 
upper bound.
   
Formulate the Objective Function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
The objective function has been loosely defined as cost. The problem can only be
formulated as a linear program if the cost of transportation from warehouse to 
pub is a linear function of the amounts of crates transported. Note that this is 
sometimes not the case. This may be due to factors such as economies of scale or 
fixed costs. For example, transporting 10 crates may not cost 10 times as much 
as transporting one crate, since it may be the case that one truck can 
accommodate 10 crates as easily as one. Usually in this situation there are 
fixed costs in operating a truck which implies that the costs go up in jumps 
(when an extra truck is required). In these situations, it is possible to model 
such a cost by using zero-one integer variables: we will look at this later in 
the course.

We shall assume then that there is a fixed transportation cost per crate. (If 
the capacity of a truck is small compared with the number of crates that must be 
delivered then this is a valid assumption). Lets assume we talk with the 
financial manager, and she gives us the following transportation costs (dollars 
per crate):

    ======================= === ===
     From Warehouse to Bar   A   B  
    ======================= === ===
            1                2   3 
            2                4   1  
            3                5   3    
            4                2   2   
            5                1   3
    ======================= === ===

Minimise the Transporting Costs = Cost per crate for RouteA1 * A1 (number of crates on RouteA1) 
                            \+ ... 
                            \+ Cost per crate for RouteB5 * B5 (number of crates on RouteB5)

.. math::

    \min \sum_{w \in W, b \in B} c_{(w,b)} x_{(w,b)}
    
Formulate the Constraints
~~~~~~~~~~~~~~~~~~~~~~~~~ 
The constraints come from considerations of supply and demand. The supply of 
beer at warehouse A is 1000 cases. The total amount of beer shipped from 
warehouse A cannot exceed this amount. Similarly, the amount of beer shipped 
from warehouse B cannot exceed the supply of beer at warehouse B. The sum of 
the values on all the arcs leading out of a warehouse, must be less than or 
equal to the supply value at that warehouse:

Such that:

* A1 + A2 + A3 + A4 + A5 <= 1000
* B1 + B2 + B3 + B4 + B5 <= 4000

.. math::

    \sum_{ b \in B} x_{(w,b)} <= s_w \ldots \forall w \in W 
    


The demand for beer at bar 1 is 500 cases, so the amount of beer delivered there 
must be at least 500 to avoid lost sales. Similarly, considering the amounts 
delivered to the other bars must be at least equal to the demand at those bars. 
Note, we are assuming there are no penalties for oversupplying bars (other than 
the extra transportation cost we incur). We can _balance_ the transportation 
problem to make sure that demand is met exactly - there will be more on this 
later. For now, the sum of the values on all the arcs leading into a bar, must 
be greater than or equal to the demand value at that bar:

* A1 + B1 >= 500 
* A2 + B2 >= 900 
* A3 + B3 >= 1800 
* A4 + B4 >= 200 
* A5 + B5 >= 700 

.. math::

    \sum_{ w \in W} x_{(w,b)} >= d_b \ldots \forall b \in B 

Finally, we have already specified the amount of beer shipped must be 
non-negative.

PuLP Model
----------

Whilst the LP as defined above could be formulated into Python code in the same 
way as the `A Blending Problem` (Whiskas), for Transportation Problems, there is 
a more efficient way which we will use in this course. The example file for this 
problem is found in the examples directory BeerDistributionProblem.py

First, start your Python file with a heading and the import PuLP statement:

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 1-8

The start of the formulation is a simple definition of the nodes and their limits/capacities. The node names are put into lists, and their associated capacities are put into dictionaries with the node names as the reference keys:

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 10-26

The cost data is then inputted into a list, with two sub lists: the first 
containing the costs of shipping from Warehouse A, and the second containing the 
costs of shipping from Warehouse B. Note here that the commenting and structure 
of the code makes the data appear as a table for easy editing. The `Warehouses` 
and `Bars` lists (Supply and Demand nodes) are added to make a large list (of 
all nodes) and inputted into PuLPs `makeDict` function. The second parameter is 
the costs list as was previously created, and the last parameter sets the 
default value for an arc cost. Once the cost dictionary is created, if 
`costs["A"]["1"]` is called, it will return the cost of transporting from 
warehouse A to bar 1, 2. If `costs["C"]["2"]` is called, it will return 0, since 
this is the defined default.

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 28-36

The `prob` variable is created using the `LpProblem` function, with the usual 
input parameters.

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 38-39

A list of tuples is created containing all the arcs.

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 41-42

A dictionary called `vars` is created which contains the LP variables. The
reference keys to the dictionary are the warehouse name, then the bar 
name(`["A"]["2"]`) , and the data is `Route_Tuple`. (e.g. `["A"]["2"]`: 
Route_A_2). The lower limit of zero is set, the upper limit of `None` is set, 
and the variables are defined to be Integers.

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 44-45

The objective function is added to the variable `prob` using a list 
comprehension. Since `vars` and `costs` are now dictionaries (with further
internal dictionaries), they can be used as if they were tables, as `for (w,b) 
in Routes` will cycle through all the combinations/arcs. Note that `i` and `j` 
could have been used, but `w` and `b` are more meaningful.

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 47-51

The supply and demand constraints are added using a normal `for` loop and a list 
comprehension. Supply Constraints: For each warehouse in turn, the values of the 
decision variables (number of beer cases on arc) to each of the bars is summed, 
and then constrained to being less than or equal to the supply max for that 
warehouse. Demand Constraints: For each bar in turn, the values of the decision 
variables (number on arc) from each of the warehouses is summed, and then 
constrained to being greater than or equal to the demand minimum.

.. literalinclude:: ../../../examples/BeerDistributionProblem.py
    :lines: 53-65

Following this is the `prob.writeLP` line, and the rest as explained in previous 
examples.

The code for this example is found in  `BeerDistributionProblem.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/BeerDistributionProblem.py?format=txt>`_

You will notice that the linear programme solution was also an integer solution. 
As long as Supply and Demand are integers, the linear programming solution will 
always be an integer. Read about naturally integer solutions for more details.

Extensions
----------

Validation
~~~~~~~~~~ 

Since we have guaranteed the Supply and Demand are integer, we know that the 
solution to the linear programme will be integer, so we don't need to check the 
integrality of the solution.

Storage and "Buying In"
~~~~~~~~~~~~~~~~~~~~~~~

Transportation models are usually _balanced_, i.e., the total supply = the total 
demand. This is because extra supply usually must be stored somewhere (with an 
associated storage cost) and extra demand is usually satisfied by purchasing 
extra goods from alternative sources (this is know as "buying in" extra goods) 
or by substituting another product (incurring a penalty cost).

In The Beer Distribution Problem, the total supply is 5000 cases of beer, but 
the total demand is only for 4100 cases. The extra supply can be sent to an 
_dummy_ demand node. The cost of flow going to the dummy demand node is then the 
storage cost at each of the supply nodes.
    
.. image:: images/extra_supply.jpg

This is added into the above model very simply. Since the objective function and 
constraints all operated on the original supply, demand and cost 
lists/dictionaries, the only changes that must be made to include another demand node are:

.. literalinclude:: ../../../examples/BeerDistributionProblemWarehouseExtension.py
    :lines: 11-25


The `Bars` list is expanded and the `Demand` dictionary is expanded to make the 
Dummy Demand require 900 crates, to balance the problem. The cost list is also 
expanded, to show the cost of "sending to the Dummy Node" which is realistically 
just leaving the stock at the warehouses. This may have an associated cost which 
could be entered here instead of the zeros. Note that the solution could still 
be solved when there was an unbalanced excess supply.

If a transportation problem has more demand than supply, we can balance the 
problem using a dummy supply node. Note that with excess demand, the problem is 
"Infeasible" when unbalanced.

Assume there has been a production problem and only 4000 cases of beer could be 
produced. Since the total demand is 4100, we need to get extra cases of beer from the dummy supply node.

The code for this example is found in  `BeerDistributionProblemWarehouseExtension.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/BeerDistributionProblemWarehouseExtension.py?format=txt>`_

    

.. image:: images/extra_demand.jpg 
    

This dummy supply node is added in simply and logically into the `Warehouse` 
list, `Supply` dictionary, and `costs` list. The Supply value is chosen to 
balance the problem, and cost of transport is zero to all demand nodes.

.. literalinclude:: ../../../examples/BeerDistributionProblemCompetitorExtension.py
    :lines: 8-32

The code for this example is found in  `BeerDistributionProblemCompetitorExtension.py <https://projects.coin-or.org/PuLP/browser/trunk/examples/BeerDistributionProblemCompetitorExtension.py?format=txt>`_

Presentation of Solution and Analysis
-------------------------------------

There are many ways to present the solution to The Beer Distribution Problem: 
as a list of shipments, in a table, etc. 


::

    TRANSPORTATION SOLUTION -- Non-zero shipments
    TotalCost = ____

    Ship ___ crates of beer from warehouse A to pub 1
    Ship ___ crates of beer from warehouse A to pub 5
    Ship ___ crates of beer from warehouse B to pub 1
    Ship ___ crates of beer from warehouse B to pub 2
    Ship ___ crates of beer from warehouse B to pub 3
    Ship ___ crates of beer from warehouse B to pub 4

This information gives rise to the following management summary:

::

    The Beer Distribution Problem 
    Mike O'Sullivan, 1234567 
    We are minimising the transportation cost for a brewery operation. The brewery 
    transports cases of beer from its warehouses to several bars.

    The brewery has two warehouses (A and B respectively) and 5 bars (1, 2, 3, 4 and
    5).

    The supply of crates of beer at the warehouses is:

    __________

    The forecasted demand (in crates of beer) at the bars is:

    __________

    The cost of transporting 1 crate of beer from a warehouse to a bar is given in 
    the following table:

    __________

    To minimise the transportation cost the brewery should make the following 
    shipments:

    Ship ___ crates of beer from warehouse A to pub 1
    Ship ___ crates of beer from warehouse A to pub 5
    Ship ___ crates of beer from warehouse B to pub 1
    Ship ___ crates of beer from warehouse B to pub 2
    Ship ___ crates of beer from warehouse B to pub 3
    Ship ___ crates of beer from warehouse B to pub 4

    The total transportation cost of this shipping schedule is $_____.

Ongoing Monitoring
------------------

Ongoing Monitoring may take the form of:

* Updating your data files and resolving as the data changes (changing costs, supplies, demands);
* Resolving our model for new nodes (e.g., new warehouses or bars);
* Looking to see if cheaper transportation options are available along routes where the transportation cost affects the optimal solution (e.g., how much total savings can we get by reducing the transportation cost from Warehouse B to Bar 1).