Linear program solvers: PuLP
We will solve the instance of a Minimum cost flow problem described in (1) now with another linear program solver: PuLP. Node 1 is the source node, nodes 2 and 3 are the transshipment nodes and node 4 is the sink node.
While lpsolve has this nice feature of reading DIMACS network flow problems, PuLP has nothing comparable to offer. So we have to transform the whole network flow problem into a plain linear program on our own.
At the PuLP wiki I found a sample for solving the transshipment problem with PuLP. Actually these code snippets work also for the minimum cost flow problem but in the original code there is a typo:
Wrong:
for n in Nodes: prob += (supply[n]+ lpSum([route_vars[(i,j)] for (i,j) in Arcs if j == n]) >= demand[n]+ lpSum([route_vars[(i,j)] for (i,j) in Arcs if i == n])), \ "Steel Flow Conservation in Node:%s"%n
Right:
for n in Nodes: prob += (supply[n]+ lpSum([route_vars[(i,j)] for (i,j) in Arcs if j == n]) >= demand[n]+ lpSum([route_vars[(i,j)] for (i,j) in Arcs if i == n])), \ "Steel Flow Conservation in Node %s"%n
The problem in the original code is the colon (‚:‘) at 'Steel Flow Conservation in Node:%s"%n'.
This causes the generation of an incorrect *.lp file that is generated when you use PuLP. This *.lp file will be sent to a solver package of your (or PuLP’s) choice. So just remove the colon in this line.
Here we go with the full working code for our sample instance of the MCFP:
''' Minimum Cost Flow Problem Solver with PuLP Adapted from: https://code.google.com/p/pulp-or/wiki/ATransshipmentProblem The American Steel Problem for the PuLP Modeller Authors: Antony Phillips, Dr Stuart Mitchell 2007 ''' # Import PuLP modeller functions from pulp import * # list of nodes nodes = [1,2,3,4] # supply or demand of nodes #NodeID : [Supply,Demand] nodeData = {1:[4,0], 2:[0,0], 3:[0,0], 4:[0,4]} # arcs list arcs = [ (1,2), (1,3), (2,3), (2,4), (3,4)] # arcs cost, lower bound and capacity #Arc : [Cost,MinFlow,MaxFlow] arcData = { (1,2):[2,0,4], (1,3):[2,0,2], (2,3):[1,0,2], (2,4):[3,0,3], (3,4):[1,0,5] } # Splits the dictionaries to be more understandable (supply, demand) = splitDict(nodeData) (costs, mins, maxs) = splitDict(arcData) # Creates the boundless Variables as Integers vars = LpVariable.dicts("Route",arcs,None,None,LpInteger) # Creates the upper and lower bounds on the variables for a in arcs: vars[a].bounds(mins[a], maxs[a]) # Creates the 'prob' variable to contain the problem data prob = LpProblem("Minimum Cost Flow Problem Sample",LpMinimize) # Creates the objective function prob += lpSum([vars[a]* costs[a] for a in arcs]), "Total Cost of Transport" # Creates all problem constraints - this ensures the amount going into each node is # at least equal to the amount leaving for n in nodes: prob += (supply[n]+ lpSum([vars[(i,j)] for (i,j) in arcs if j == n]) >= demand[n]+ lpSum([vars[(i,j)] for (i,j) in arcs if i == n])), \ "Flow Conservation in Node %s"%n # The problem data is written to an .lp file prob.writeLP("simple_MCFP.lp") # The problem is solved using PuLP's choice of Solver prob.solve() # The optimised objective function value is printed to the screen print "Total Cost of Transportation = ", value(prob.objective)
The code is well commented. For a very detailed description have a look at the wiki of PuLP. Because there is no special network component in the PuLP modeler – any problem to solve is a linear program. So you have to write any instance of a MCFP as a plain LP which is not easy to understand at first glance.