# 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;

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]),

“Sum_of_Transporting_Costs”

# 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],

“Sum_of_Products_out_of*Warehouse*%s”%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],

“Sum_of_Products_into_Bar%s”%b

# The problem data is written to an .lp file

prob.writeLP(“BeerDistributionProblem.lp”)

# The problem is solved using PuLP’s choice of Solver

prob.solve()

# 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]