iPython Notebooks And PuLP

Lately, we’ve begun working on various constrained optimization problems, and this was a good opportunity to use Python, and more specifically, iPython Notebooks which I’ve been learning about in recent days.

Assuming you’ve got iPython already working, try typing:

[code language=“python”]ipython notebook[/code]

If it works, great. If not, you may have some dependent python libraries to install e.g.

[code language=“python”] pip install pyzmq pip install tornado [/code]

Once you’ve got the dependent libraries installed, typing ipython notebook should reveal a similar screenshot in your browser;

ipython notebook screenshot

Ok, with that out of the way, I’ve been using a library called PuLP to test out various optimization problems, and so far so good. There’s also a good introduction to PuLP with examples that you can follow. I recreated those examples in notebook and to show them in this blog post, I had to first convert the notebook to HTML format using nbconvert (you may need to pip install pygments to get it working):

[code language=“python”] ipython nbconvert tutorial.ipynb [/code]

I then had the option of hosting the HTML page somewhere (Wordpress doesn’t seem to like the HTML page) or, the other option is simply using the nbviewer, which is what I used. In that case, you just pass in the URL of your .ipynb file and it creates a viewing friendly version of it.

Alternatively, you can just use Wordpress’ code block functionality and paste in your code:

[code language=“python”] #Whiskas optimization problem import pulp #initialise the model whiskas_model = pulp.LpProblem(‘The Whiskas Problem’, pulp.LpMinimize)

make a list of ingredients

ingredients = [‘chicken’, ‘beef’, ‘mutton’, ‘rice’, ‘wheat’, ‘gel’]

create a dictionary of pulp variables with keys from ingredients

the default lower bound is -inf

x = pulp.LpVariable.dict(‘x_%s’, ingredients, lowBound =0)

cost data

cost = dict(zip(ingredients, [0.013, 0.008, 0.010, 0.002, 0.005, 0.001]))

create the objective

whiskas_model += sum( [cost[i] * x[i] for i in ingredients])

ingredient parameters

protein = dict(zip(ingredients, [0.100, 0.200, 0.150, 0.000, 0.040, 0.000])) fat = dict(zip(ingredients, [0.080, 0.100, 0.110, 0.010, 0.010, 0.000])) fibre = dict(zip(ingredients, [0.001, 0.005, 0.003, 0.100, 0.150, 0.000])) salt = dict(zip(ingredients, [0.002, 0.005, 0.007, 0.002, 0.008, 0.000])) #note these are constraints and not an objective as there is a equality/inequality whiskas_model += sum([protein[i]*x[i] for i in ingredients]) >= 8.0 whiskas_model += sum([fat[i]*x[i] for i in ingredients]) >= 6.0 whiskas_model += sum([fibre[i]*x[i] for i in ingredients]) <= 2.0 whiskas_model += sum([salt[i]*x[i] for i in ingredients]) <= 0.4

#problem is then solved with the default solver whiskas_model.solve() #print the result for ingredient in ingredients: print ‘The mass of %s is %s grams per can’%(ingredient, x[ingredient].value()) [/code]

The next one is called the beer distribution problem. And no, drinking them all is not the answer…

[code language=“python”] #The Beer Distribution Problem for the PuLP Modeller

Import PuLP modeler functions

import pulp

Creates a list of all the supply nodes

warehouses = [“A”, “B”]

Creates a dictionary for the number of units of supply for each supply node

supply = {“A”: 1000, “B”: 4000}

Creates a list of all demand nodes

bars = [“1”, “2”, “3”, “4”, “5”]

Creates a dictionary for the number of units of demand for each demand node

demand = {“1”:500, “2”:900, “3”:1800, “4”:200, “5”:700,}

Creates a list of costs of each transportation path

costs = [ #Bars #1 2 3 4 5 [2,4,5,2,1],#A Warehouses [3,1,3,2,3] #B ]

The cost data is made into a dictionary

costs = pulp.makeDict([warehouses, bars], costs,0)

Creates the ‘prob’ variable to contain the problem data

prob = pulp.LpProblem(“Beer Distribution Problem”, pulp.LpMinimize)

Creates a list of tuples containing all the possible routes for transport

routes = [(w,b) for w in warehouses for b in bars]

A dictionary called x is created to contain quantity shipped on the routes

x = pulp.LpVariable.dicts(“route”, (warehouses, bars), lowBound = 0, cat = pulp.LpInteger)

The objective function is added to ‘prob’ first

prob += sum([x[w][b]*costs[w][b] for (w,b) in routes]),

Supply maximum constraints are added to prob for each supply node (warehouse)

for w in warehouses: prob += sum([x[w][b] for b in bars]) <= supply[w],

Demand minimum constraints are added to prob for each demand node (bar)

for b in bars: prob += sum([x[w][b] for w in warehouses]) >= demand[b],

The problem data is written to an .lp file


The problem is solved using PuLP’s choice of Solver


The status of the solution is printed to the screen

print “Status:“, pulp.LpStatus[prob.status]

Each of the variables is printed with it’s resolved optimum value

for v in prob.variables(): print v.name, “=”, v.varValue

The optimised objective function value is printed to the screen

print “Total Cost of Transportation = “, prob.objective.value() [/code] [code language=“css”] Status: Optimal route_A_1 = 300.0 route_A_2 = 0.0 route_A_3 = 0.0 route_A_4 = 0.0 route_A_5 = 700.0 route_B_1 = 200.0 route_B_2 = 900.0 route_B_3 = 1800.0 route_B_4 = 200.0 route_B_5 = 0.0 Total Cost of Transportation = 8600.0 [/code]

comments powered by Disqus