from collections import deque
from heapq import heappush, heappop
demand, capacity, weight = 'demand', 'capacity', 'weight'
def _dijkstra(G, source, get_weight, pred=None, paths=None, cutoff=None, target=None):
G_succ = G.succ if G.is_directed() else G.adj
push = heappush
pop = heappop
dist = {} # dictionary of final distances
seen = {source: 0}
c = count()
fringe = [] # use heapq with (distance,label) tuples
push(fringe, (0, next(c), source))
while fringe:
(d, _, v) = pop(fringe)
if v in dist:
continue # already searched this node.
dist[v] = d
if v == target:
break
for u, e in G_succ[v].items():
cost = get_weight(v, u, e)
if cost is None:
continue
vu_dist = dist[v] + get_weight(v, u, e)
if cutoff is not None:
if vu_dist > cutoff:
continue
if u in dist:
if vu_dist < dist[u]:
raise ValueError('Contradictory paths found:',
'negative weights?')
elif u not in seen or vu_dist < seen[u]:
seen[u] = vu_dist
push(fringe, (vu_dist, next(c), u))
if paths is not None:
paths[u] = paths[v] + [u]
if pred is not None:
pred[u] = [v]
elif vu_dist == seen[u]:
if pred is not None:
pred[u].append(v)
if paths is not None:
return (dist, paths)
if pred is not None:
return (pred, dist)
return dist
class Graph:
def connected_components(G):
seen={}
for v in G:
if v not in seen:
c = sp_length(G, v)
yield list(c)
seen.update(c)
def bidirectional_dijkstra(G, source, target, weight='weight'):
if source == target:
return (0, [source])
push = heappush
pop = heappop
# Init: Forward Backward
dists = [{}, {}] # dictionary of final distances
paths = [{source: [source]}, {target: [target]}] # dictionary of paths
fringe = [[], []] # heap of (distance, node) tuples for
# extracting next node to expand
seen = [{source: 0}, {target: 0}] # dictionary of distances to
# nodes seen
c = count()
# initialize fringe heap
push(fringe[0], (0, next(c), source))
push(fringe[1], (0, next(c), target))
# neighs for extracting correct neighbor information
if G.is_directed():
neighs = [G.successors_iter, G.predecessors_iter]
else:
neighs = [G.neighbors_iter, G.neighbors_iter]
# variables to hold shortest discovered path
#finaldist = 1e30000
finalpath = []
dir = 1
while fringe[0] and fringe[1]:
# choose direction
# dir == 0 is forward direction and dir == 1 is back
dir = 1 - dir
# extract closest to expand
(dist, _, v) = pop(fringe[dir])
if v in dists[dir]:
# Shortest path to v has already been found
continue
# update distance
dists[dir][v] = dist # equal to seen[dir][v]
if v in dists[1 - dir]:
# if we have scanned v in both directions we are done
# we have now discovered the shortest path
return (finaldist, finalpath)
for w in neighs[dir](v):
if(dir == 0): # forward
if G.is_multigraph():
minweight = min((dd.get(weight, 1)
for k, dd in G[v][w].items()))
else:
minweight = G[v][w].get(weight, 1)
vwLength = dists[dir][v] + minweight # G[v][w].get(weight,1)
else: # back, must remember to change v,w->w,v
if G.is_multigraph():
minweight = min((dd.get(weight, 1)
for k, dd in G[w][v].items()))
else:
minweight = G[w][v].get(weight, 1)
vwLength = dists[dir][v] + minweight # G[w][v].get(weight,1)
if w in dists[dir]:
if vwLength < dists[dir][w]:
raise ValueError(
"Contradictory paths found: negative weights?")
elif w not in seen[dir] or vwLength < seen[dir][w]:
# relaxing
seen[dir][w] = vwLength
push(fringe[dir], (vwLength, next(c), w))
paths[dir][w] = paths[dir][v] + [w]
if w in seen[0] and w in seen[1]:
# see if this path is better than than the already
# discovered shortest path
totaldist = seen[0][w] + seen[1][w]
if finalpath == [] or finaldist > totaldist:
finaldist = totaldist
revpath = paths[1][w][:]
revpath.reverse()
finalpath = paths[0][w] + revpath[1:]
raise NetworkXNoPath("No path between %s and %s." % (source, target))
class DiGraph:
def __init__(self, edges):
self.edges = edges
self.nodes = None
pass
def nodes_iter(self):
return iter(self.nodes)
def edges(self):
return self.edges
def add_nodes_from(self, nodes):
self.nodes
def _initial_tree_solution(G):
H = DiGraph((edge for edge in G.edges() if edge[2].get(capacity, 1) > 0))
demand_nodes = (node for node in G.nodes_iter() if node[1].get(demand, 0) != 0)
H.add_nodes_from(demand_nodes)
r = H.nodes()[0]
T = DiGraph()
y = {r: 0}
artificial_edges = []
flow_cost = 0
n = H.number_of_nodes()
try:
max_weight = max(abs(d[weight]) for u, v, d in H.edges() if weight in d)
except ValueError:
max_weight = 0
huge_weight = 1 + n * max_weight
for v, d in H.nodes()[1:]:
v_demand = d.get(demand, 0)
if v_demand >= 0:
if not (r, v) in H.edges():
H.add_edge(r, v, {weight: huge_weight, 'flow': v_demand})
artificial_edges.append((r, v))
y[v] = H[r][v].get(weight, 0)
T.add_edge(r, v)
flow_cost += v_demand * H[r][v].get(weight, 0)
else: # (r, v) in H.edges()
if (not capacity in H[r][v]
or v_demand <= H[r][v][capacity]):
H[r][v]['flow'] = v_demand
y[v] = H[r][v].get(weight, 0)
T.add_edge(r, v)
flow_cost += v_demand * H[r][v].get(weight, 0)
else: # existing edge does not have enough capacity
new_label = generate_unique_node()
H.add_edge(r, new_label, {weight: huge_weight, 'flow': v_demand})
H.add_edge(new_label, v, {weight: huge_weight, 'flow': v_demand})
artificial_edges.append((r, new_label))
artificial_edges.append((new_label, v))
y[v] = 2 * huge_weight
y[new_label] = huge_weight
T.add_edge(r, new_label)
T.add_edge(new_label, v)
flow_cost += 2 * v_demand * huge_weight
else:
if not (v, r) in H.edges():
H.add_edge(v, r, {weight: huge_weight, 'flow': -v_demand})
artificial_edges.append((v, r))
y[v] = -H[v][r].get(weight, 0)
T.add_edge(v, r)
flow_cost += -v_demand * H[v][r].get(weight, 0)
else:
if (not capacity in H[v][r] or -v_demand <= H[v][r][capacity]):
H[v][r]['flow'] = -v_demand
y[v] = -H[v][r].get(weight, 0)
T.add_edge(v, r)
flow_cost += -v_demand * H[v][r].get(weight, 0)
else:
new_label = generate_unique_node()
H.add_edge(v, new_label, {weight: huge_weight, 'flow': -v_demand})
H.add_edge(new_label, r, {weight: huge_weight, 'flow': -v_demand})
artificial_edges.append((v, new_label))
artificial_edges.append((new_label, r))
y[v] = -2 * huge_weight
y[new_label] = -huge_weight
T.add_edge(v, new_label)
T.add_edge(new_label, r)
flow_cost += 2 * -v_demand * huge_weight
return H, T, y, artificial_edges, flow_cost, r
def _find_entering_edge(H, c):
capacity = 'capacity'
new_edge = ()
for u, v, d in H.edges_iter():
if d.get('flow', 0) == 0:
if c[(u, v)] < 0:
new_edge = (u, v)
break
else:
if capacity in d:
if (d.get('flow', 0) == d[capacity]
and c[(u, v)] > 0):
new_edge = (u, v)
break
return new_edge
def _find_leaving_edge(H, T, cycle, new_edge, capacity = 'capacity', reverse=False):
eps = False
leaving_edge = ()
# If cycle is a digon.
if len(cycle) == 3:
u, v = new_edge
if reverse:
if H[u][v].get('flow', 0) > H[v][u].get('flow', 0):
return (v, u), H[v][u].get('flow', 0)
else:
return (u, v), H[u][v].get('flow', 0)
else:
uv_residual = H[u][v].get(capacity, 0) - H[u][v].get('flow', 0)
vu_residual = H[v][u].get(capacity, 0) - H[v][u].get('flow', 0)
if (uv_residual > vu_residual):
return (v, u), vu_residual
else:
return (u, v), uv_residual
# Find the forward edge with the minimum value for capacity - 'flow'
# and the reverse edge with the minimum value for 'flow'.
for index, u in enumerate(cycle[:-1]):
edge_capacity = False
edge = ()
v = cycle[index + 1]
if (u, v) in T.edges() + [new_edge]: #forward edge
if capacity in H[u][v]: # edge (u, v) has finite capacity
edge_capacity = H[u][v][capacity] - H[u][v].get('flow', 0)
edge = (u, v)
else: #reverse edge
edge_capacity = H[v][u].get('flow', 0)
edge = (v, u)
# Determine if edge might be the leaving edge.
if edge:
if leaving_edge:
if edge_capacity < eps:
eps = edge_capacity
leaving_edge = edge
else:
eps = edge_capacity
leaving_edge = edge
if not leaving_edge:
raise ValueError("No leaving edge")
return leaving_edge, eps
def _create_flow_dict(G, H):
flow_dict = dict([(u, {}) for u in G])
for u in G.nodes_iter():
for v in G.neighbors(u):
if H.has_edge(u, v):
flow_dict[u][v] = H[u][v].get('flow', 0)
else:
flow_dict[u][v] = 0
return flow_dict
def network_simplex(G):
demand, capacity, weight = 'demand', 'capacity', 'weight'
# Fix an arbitrarily chosen root node and find an initial tree solution.
H, T, y, artificial_edges, flow_cost, r = \
_initial_tree_solution(G, demand = demand, capacity = capacity, weight = weight)
# Initialize the reduced costs.
c = {}
for u, v, d in H.edges_iter():
c[(u, v)] = d.get(weight, 0) + y[u] - y[v]
# Main loop.
while True:
new_edge = _find_entering_edge(H, c, capacity = capacity)
if not new_edge:
break # Optimal basis found. Main loop is over.
cycle_cost = abs(c[new_edge])
# Find the cycle created by adding new_edge to T.
path1 = shortest_path(T.to_undirected(), r, new_edge[0])
path2 = shortest_path(T.to_undirected(), r, new_edge[1])
join = r
for index, node in enumerate(path1[1:]):
if index + 1 < len(path2) and node == path2[index + 1]:
join = node
else:
break
path1 = path1[path1.index(join):]
path2 = path2[path2.index(join):]
cycle = []
if H[new_edge[0]][new_edge[1]].get('flow', 0) == 0:
reverse = False
path2.reverse()
cycle = path1 + path2
else: # new_edge is at capacity
reverse = True
path1.reverse()
cycle = path2 + path1
# Find the leaving edge. Will stop here if cycle is an infinite
# capacity negative cost cycle.
leaving_edge, eps = _find_leaving_edge(H, T, cycle, new_edge, capacity=capacity, reverse=reverse)
# Actual augmentation happens here. If eps = 0, don't bother.
if eps:
flow_cost -= cycle_cost * eps
if len(cycle) == 3:
if reverse:
eps = -eps
u, v = new_edge
H[u][v]['flow'] = H[u][v].get('flow', 0) + eps
H[v][u]['flow'] = H[v][u].get('flow', 0) + eps
else:
for index, u in enumerate(cycle[:-1]):
v = cycle[index + 1]
if (u, v) in T.edges() + [new_edge]:
H[u][v]['flow'] = H[u][v].get('flow', 0) + eps
else: # (v, u) in T.edges():
H[v][u]['flow'] -= eps
# Update tree solution.
T.add_edge(*new_edge)
T.remove_edge(*leaving_edge)
# Update distances and reduced costs.
if new_edge != leaving_edge:
forest = DiGraph(T)
forest.remove_edge(*new_edge)
R, notR = connected_component_subgraphs(forest.to_undirected())
if r in notR.nodes(): # make sure r is in R
R, notR = notR, R
if new_edge[0] in R.nodes():
for v in notR.nodes():
y[v] += c[new_edge]
else:
for v in notR.nodes():
y[v] -= c[new_edge]
for u, v in H.edges():
if u in notR.nodes() or v in notR.nodes():
c[(u, v)] = H[u][v].get(weight, 0) + y[u] - y[v]
for u, v in artificial_edges:
if H[u][v]['flow'] != 0:
raise ValueError("No flow satisfying all demands.")
H.remove_edge(u, v)
for u in H.nodes():
if not u in G:
H.remove_node(u)
flow_dict = _create_flow_dict(G, H)
return flow_cost, flow_dict
To embed this program on your website, copy the following code and paste it into your website's HTML: