| # -*- coding: utf-8 -*- |
| """ |
| Minimum cost flow algorithms on directed connected graphs. |
| """ |
| |
| __author__ = """Loïc Séguin-C. <loicseguin@gmail.com>""" |
| # Copyright (C) 2010 Loïc Séguin-C. <loicseguin@gmail.com> |
| # All rights reserved. |
| # BSD license. |
| |
| |
| __all__ = ['network_simplex', |
| 'min_cost_flow_cost', |
| 'min_cost_flow', |
| 'cost_of_flow', |
| 'max_flow_min_cost'] |
| |
| import networkx as nx |
| from networkx.utils import generate_unique_node |
| |
| def _initial_tree_solution(G, demand = 'demand', capacity = 'capacity', |
| weight = 'weight'): |
| """Find a initial tree solution rooted at r. |
| |
| The initial tree solution is obtained by considering edges (r, v) |
| for all nodes v with non-negative demand and (v, r) for all nodes |
| with negative demand. If these edges do not exist, we add them to |
| the graph and call them artificial edges. |
| """ |
| H = nx.DiGraph((edge for edge in G.edges(data=True) if |
| edge[2].get(capacity, 1) > 0)) |
| demand_nodes = (node for node in G.nodes_iter(data=True) if |
| node[1].get(demand, 0) != 0) |
| H.add_nodes_from(demand_nodes) |
| r = H.nodes()[0] |
| |
| T = nx.DiGraph() |
| y = {r: 0} |
| artificialEdges = [] |
| flowCost = 0 |
| |
| n = H.number_of_nodes() |
| try: |
| maxWeight = max(abs(d[weight]) for u, v, d in H.edges(data = True) |
| if weight in d) |
| except ValueError: |
| maxWeight = 0 |
| hugeWeight = 1 + n * maxWeight |
| |
| for v, d in H.nodes(data = True)[1:]: |
| vDemand = d.get(demand, 0) |
| if vDemand >= 0: |
| if not (r, v) in H.edges(): |
| H.add_edge(r, v, {weight: hugeWeight, 'flow': vDemand}) |
| artificialEdges.append((r, v)) |
| y[v] = H[r][v].get(weight, 0) |
| T.add_edge(r, v) |
| flowCost += vDemand * H[r][v].get(weight, 0) |
| |
| else: # (r, v) in H.edges() |
| if (not capacity in H[r][v] |
| or vDemand <= H[r][v][capacity]): |
| H[r][v]['flow'] = vDemand |
| y[v] = H[r][v].get(weight, 0) |
| T.add_edge(r, v) |
| flowCost += vDemand * H[r][v].get(weight, 0) |
| |
| else: # existing edge does not have enough capacity |
| newLabel = generate_unique_node() |
| H.add_edge(r, newLabel, {weight: hugeWeight, 'flow': vDemand}) |
| H.add_edge(newLabel, v, {weight: hugeWeight, 'flow': vDemand}) |
| artificialEdges.append((r, newLabel)) |
| artificialEdges.append((newLabel, v)) |
| y[v] = 2 * hugeWeight |
| y[newLabel] = hugeWeight |
| T.add_edge(r, newLabel) |
| T.add_edge(newLabel, v) |
| flowCost += 2 * vDemand * hugeWeight |
| |
| else: # vDemand < 0 |
| if not (v, r) in H.edges(): |
| H.add_edge(v, r, {weight: hugeWeight, 'flow': -vDemand}) |
| artificialEdges.append((v, r)) |
| y[v] = -H[v][r].get(weight, 0) |
| T.add_edge(v, r) |
| flowCost += -vDemand * H[v][r].get(weight, 0) |
| |
| else: |
| if (not capacity in H[v][r] |
| or -vDemand <= H[v][r][capacity]): |
| H[v][r]['flow'] = -vDemand |
| y[v] = -H[v][r].get(weight, 0) |
| T.add_edge(v, r) |
| flowCost += -vDemand * H[v][r].get(weight, 0) |
| else: # existing edge does not have enough capacity |
| newLabel = generate_unique_node() |
| H.add_edge(v, newLabel, |
| {weight: hugeWeight, 'flow': -vDemand}) |
| H.add_edge(newLabel, r, |
| {weight: hugeWeight, 'flow': -vDemand}) |
| artificialEdges.append((v, newLabel)) |
| artificialEdges.append((newLabel, r)) |
| y[v] = -2 * hugeWeight |
| y[newLabel] = -hugeWeight |
| T.add_edge(v, newLabel) |
| T.add_edge(newLabel, r) |
| flowCost += 2 * -vDemand * hugeWeight |
| |
| return H, T, y, artificialEdges, flowCost, r |
| |
| |
| def _find_entering_edge(H, c, capacity = 'capacity'): |
| """Find an edge which creates a negative cost cycle in the actual |
| tree solution. |
| |
| The reduced cost of every edge gives the value of the cycle |
| obtained by adding that edge to the tree solution. If that value is |
| negative, we will augment the flow in the direction indicated by |
| the edge. Otherwise, we will augment the flow in the reverse |
| direction. |
| |
| If no edge is found, return and empty tuple. This will cause the |
| main loop of the algorithm to terminate. |
| """ |
| newEdge = () |
| for u, v, d in H.edges_iter(data = True): |
| if d.get('flow', 0) == 0: |
| if c[(u, v)] < 0: |
| newEdge = (u, v) |
| break |
| else: |
| if capacity in d: |
| if (d.get('flow', 0) == d[capacity] |
| and c[(u, v)] > 0): |
| newEdge = (u, v) |
| break |
| return newEdge |
| |
| |
| def _find_leaving_edge(H, T, cycle, newEdge, capacity = 'capacity', |
| reverse=False): |
| """Find an edge that will leave the basis and the value by which we |
| can increase or decrease the flow on that edge. |
| |
| The leaving arc rule is used to prevent cycling. |
| |
| If cycle has no reverse edge and no forward edge of finite |
| capacity, it means that cycle is a negative cost infinite capacity |
| cycle. This implies that the cost of a flow satisfying all demands |
| is unbounded below. An exception is raised in this case. |
| """ |
| eps = False |
| leavingEdge = () |
| |
| # If cycle is a digon. |
| if len(cycle) == 3: |
| u, v = newEdge |
| if capacity not in H[u][v] and capacity not in H[v][u]: |
| raise nx.NetworkXUnbounded( |
| "Negative cost cycle of infinite capacity found. " |
| + "Min cost flow unbounded below.") |
| |
| 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]): |
| edgeCapacity = False |
| edge = () |
| v = cycle[index + 1] |
| if (u, v) in T.edges() + [newEdge]: #forward edge |
| if capacity in H[u][v]: # edge (u, v) has finite capacity |
| edgeCapacity = H[u][v][capacity] - H[u][v].get('flow', 0) |
| edge = (u, v) |
| else: #reverse edge |
| edgeCapacity = H[v][u].get('flow', 0) |
| edge = (v, u) |
| |
| # Determine if edge might be the leaving edge. |
| if edge: |
| if leavingEdge: |
| if edgeCapacity < eps: |
| eps = edgeCapacity |
| leavingEdge = edge |
| else: |
| eps = edgeCapacity |
| leavingEdge = edge |
| |
| if not leavingEdge: |
| raise nx.NetworkXUnbounded( |
| "Negative cost cycle of infinite capacity found. " |
| + "Min cost flow unbounded below.") |
| |
| return leavingEdge, eps |
| |
| |
| def _create_flow_dict(G, H): |
| """Creates the flow dict of dicts of graph G with auxiliary graph H.""" |
| flowDict = dict([(u, {}) for u in G]) |
| |
| for u in G.nodes_iter(): |
| for v in G.neighbors(u): |
| if H.has_edge(u, v): |
| flowDict[u][v] = H[u][v].get('flow', 0) |
| else: |
| flowDict[u][v] = 0 |
| return flowDict |
| |
| |
| def network_simplex(G, demand = 'demand', capacity = 'capacity', |
| weight = 'weight'): |
| """Find a minimum cost flow satisfying all demands in digraph G. |
| |
| This is a primal network simplex algorithm that uses the leaving |
| arc rule to prevent cycling. |
| |
| G is a digraph with edge costs and capacities and in which nodes |
| have demand, i.e., they want to send or receive some amount of |
| flow. A negative demand means that the node wants to send flow, a |
| positive demand means that the node want to receive flow. A flow on |
| the digraph G satisfies all demand if the net flow into each node |
| is equal to the demand of that node. |
| |
| Parameters |
| ---------- |
| G : NetworkX graph |
| DiGraph on which a minimum cost flow satisfying all demands is |
| to be found. |
| |
| demand: string |
| Nodes of the graph G are expected to have an attribute demand |
| that indicates how much flow a node wants to send (negative |
| demand) or receive (positive demand). Note that the sum of the |
| demands should be 0 otherwise the problem in not feasible. If |
| this attribute is not present, a node is considered to have 0 |
| demand. Default value: 'demand'. |
| |
| capacity: string |
| Edges of the graph G are expected to have an attribute capacity |
| that indicates how much flow the edge can support. If this |
| attribute is not present, the edge is considered to have |
| infinite capacity. Default value: 'capacity'. |
| |
| weight: string |
| Edges of the graph G are expected to have an attribute weight |
| that indicates the cost incurred by sending one unit of flow on |
| that edge. If not present, the weight is considered to be 0. |
| Default value: 'weight'. |
| |
| Returns |
| ------- |
| flowCost: integer, float |
| Cost of a minimum cost flow satisfying all demands. |
| |
| flowDict: dictionary |
| Dictionary of dictionaries keyed by nodes such that |
| flowDict[u][v] is the flow edge (u, v). |
| |
| Raises |
| ------ |
| NetworkXError |
| This exception is raised if the input graph is not directed, |
| not connected or is a multigraph. |
| |
| NetworkXUnfeasible |
| This exception is raised in the following situations: |
| * The sum of the demands is not zero. Then, there is no |
| flow satisfying all demands. |
| * There is no flow satisfying all demand. |
| |
| NetworkXUnbounded |
| This exception is raised if the digraph G has a cycle of |
| negative cost and infinite capacity. Then, the cost of a flow |
| satisfying all demands is unbounded below. |
| |
| Notes |
| ----- |
| This algorithm is not guaranteed to work if edge weights |
| are floating point numbers (overflows and roundoff errors can |
| cause problems). |
| |
| See also |
| -------- |
| cost_of_flow, max_flow_min_cost, min_cost_flow, min_cost_flow_cost |
| |
| Examples |
| -------- |
| A simple example of a min cost flow problem. |
| |
| >>> import networkx as nx |
| >>> G = nx.DiGraph() |
| >>> G.add_node('a', demand = -5) |
| >>> G.add_node('d', demand = 5) |
| >>> G.add_edge('a', 'b', weight = 3, capacity = 4) |
| >>> G.add_edge('a', 'c', weight = 6, capacity = 10) |
| >>> G.add_edge('b', 'd', weight = 1, capacity = 9) |
| >>> G.add_edge('c', 'd', weight = 2, capacity = 5) |
| >>> flowCost, flowDict = nx.network_simplex(G) |
| >>> flowCost |
| 24 |
| >>> flowDict # doctest: +SKIP |
| {'a': {'c': 1, 'b': 4}, 'c': {'d': 1}, 'b': {'d': 4}, 'd': {}} |
| |
| The mincost flow algorithm can also be used to solve shortest path |
| problems. To find the shortest path between two nodes u and v, |
| give all edges an infinite capacity, give node u a demand of -1 and |
| node v a demand a 1. Then run the network simplex. The value of a |
| min cost flow will be the distance between u and v and edges |
| carrying positive flow will indicate the path. |
| |
| >>> G=nx.DiGraph() |
| >>> G.add_weighted_edges_from([('s','u',10), ('s','x',5), |
| ... ('u','v',1), ('u','x',2), |
| ... ('v','y',1), ('x','u',3), |
| ... ('x','v',5), ('x','y',2), |
| ... ('y','s',7), ('y','v',6)]) |
| >>> G.add_node('s', demand = -1) |
| >>> G.add_node('v', demand = 1) |
| >>> flowCost, flowDict = nx.network_simplex(G) |
| >>> flowCost == nx.shortest_path_length(G, 's', 'v', weight = 'weight') |
| True |
| >>> sorted([(u, v) for u in flowDict for v in flowDict[u] if flowDict[u][v] > 0]) |
| [('s', 'x'), ('u', 'v'), ('x', 'u')] |
| >>> nx.shortest_path(G, 's', 'v', weight = 'weight') |
| ['s', 'x', 'u', 'v'] |
| |
| It is possible to change the name of the attributes used for the |
| algorithm. |
| |
| >>> G = nx.DiGraph() |
| >>> G.add_node('p', spam = -4) |
| >>> G.add_node('q', spam = 2) |
| >>> G.add_node('a', spam = -2) |
| >>> G.add_node('d', spam = -1) |
| >>> G.add_node('t', spam = 2) |
| >>> G.add_node('w', spam = 3) |
| >>> G.add_edge('p', 'q', cost = 7, vacancies = 5) |
| >>> G.add_edge('p', 'a', cost = 1, vacancies = 4) |
| >>> G.add_edge('q', 'd', cost = 2, vacancies = 3) |
| >>> G.add_edge('t', 'q', cost = 1, vacancies = 2) |
| >>> G.add_edge('a', 't', cost = 2, vacancies = 4) |
| >>> G.add_edge('d', 'w', cost = 3, vacancies = 4) |
| >>> G.add_edge('t', 'w', cost = 4, vacancies = 1) |
| >>> flowCost, flowDict = nx.network_simplex(G, demand = 'spam', |
| ... capacity = 'vacancies', |
| ... weight = 'cost') |
| >>> flowCost |
| 37 |
| >>> flowDict # doctest: +SKIP |
| {'a': {'t': 4}, 'd': {'w': 2}, 'q': {'d': 1}, 'p': {'q': 2, 'a': 2}, 't': {'q': 1, 'w': 1}, 'w': {}} |
| |
| References |
| ---------- |
| W. J. Cook, W. H. Cunningham, W. R. Pulleyblank and A. Schrijver. |
| Combinatorial Optimization. Wiley-Interscience, 1998. |
| |
| """ |
| |
| if not G.is_directed(): |
| raise nx.NetworkXError("Undirected graph not supported.") |
| if not nx.is_connected(G.to_undirected()): |
| raise nx.NetworkXError("Not connected graph not supported.") |
| if G.is_multigraph(): |
| raise nx.NetworkXError("MultiDiGraph not supported.") |
| if sum(d[demand] for v, d in G.nodes(data = True) |
| if demand in d) != 0: |
| raise nx.NetworkXUnfeasible("Sum of the demands should be 0.") |
| |
| # Fix an arbitrarily chosen root node and find an initial tree solution. |
| H, T, y, artificialEdges, flowCost, r = \ |
| _initial_tree_solution(G, demand = demand, capacity = capacity, |
| weight = weight) |
| |
| # Initialize the reduced costs. |
| c = {} |
| for u, v, d in H.edges_iter(data = True): |
| c[(u, v)] = d.get(weight, 0) + y[u] - y[v] |
| |
| # Print stuff for debugging. |
| # print('-' * 78) |
| # nbIter = 0 |
| # print('Iteration %d' % nbIter) |
| # nbIter += 1 |
| # print('Tree solution: %s' % T.edges()) |
| # print(' Edge %11s%10s' % ('Flow', 'Red Cost')) |
| # for u, v, d in H.edges(data = True): |
| # flag = '' |
| # if (u, v) in artificialEdges: |
| # flag = '*' |
| # print('(%s, %s)%1s%10d%10d' % (u, v, flag, d.get('flow', 0), |
| # c[(u, v)])) |
| # print('Distances: %s' % y) |
| |
| # Main loop. |
| while True: |
| newEdge = _find_entering_edge(H, c, capacity = capacity) |
| if not newEdge: |
| break # Optimal basis found. Main loop is over. |
| cycleCost = abs(c[newEdge]) |
| |
| # Find the cycle created by adding newEdge to T. |
| path1 = nx.shortest_path(T.to_undirected(), r, newEdge[0]) |
| path2 = nx.shortest_path(T.to_undirected(), r, newEdge[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[newEdge[0]][newEdge[1]].get('flow', 0) == 0: |
| reverse = False |
| path2.reverse() |
| cycle = path1 + path2 |
| else: # newEdge 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. |
| leavingEdge, eps = _find_leaving_edge(H, T, cycle, newEdge, |
| capacity=capacity, |
| reverse=reverse) |
| |
| # Actual augmentation happens here. If eps = 0, don't bother. |
| if eps: |
| flowCost -= cycleCost * eps |
| if len(cycle) == 3: |
| if reverse: |
| eps = -eps |
| u, v = newEdge |
| 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() + [newEdge]: |
| 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(*newEdge) |
| T.remove_edge(*leavingEdge) |
| |
| # Update distances and reduced costs. |
| if newEdge != leavingEdge: |
| forest = nx.DiGraph(T) |
| forest.remove_edge(*newEdge) |
| R, notR = nx.connected_component_subgraphs(forest.to_undirected()) |
| if r in notR.nodes(): # make sure r is in R |
| R, notR = notR, R |
| if newEdge[0] in R.nodes(): |
| for v in notR.nodes(): |
| y[v] += c[newEdge] |
| else: |
| for v in notR.nodes(): |
| y[v] -= c[newEdge] |
| 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] |
| |
| # Print stuff for debugging. |
| # print('-' * 78) |
| # print('Iteration %d' % nbIter) |
| # nbIter += 1 |
| # print('Tree solution: %s' % T.edges()) |
| # print('New edge: (%s, %s)' % (newEdge[0], newEdge[1])) |
| # print('Leaving edge: (%s, %s)' % (leavingEdge[0], leavingEdge[1])) |
| # print('Cycle: %s' % cycle) |
| # print('eps: %d' % eps) |
| # print(' Edge %11s%10s' % ('Flow', 'Red Cost')) |
| # for u, v, d in H.edges(data = True): |
| # flag = '' |
| # if (u, v) in artificialEdges: |
| # flag = '*' |
| # print('(%s, %s)%1s%10d%10d' % (u, v, flag, d.get('flow', 0), |
| # c[(u, v)])) |
| # print('Distances: %s' % y) |
| |
| |
| # If an artificial edge has positive flow, the initial problem was |
| # not feasible. |
| for u, v in artificialEdges: |
| if H[u][v]['flow'] != 0: |
| raise nx.NetworkXUnfeasible("No flow satisfying all demands.") |
| H.remove_edge(u, v) |
| |
| for u in H.nodes(): |
| if not u in G: |
| H.remove_node(u) |
| |
| flowDict = _create_flow_dict(G, H) |
| |
| return flowCost, flowDict |
| |
| |
| def min_cost_flow_cost(G, demand = 'demand', capacity = 'capacity', |
| weight = 'weight'): |
| """Find the cost of a minimum cost flow satisfying all demands in digraph G. |
| |
| G is a digraph with edge costs and capacities and in which nodes |
| have demand, i.e., they want to send or receive some amount of |
| flow. A negative demand means that the node wants to send flow, a |
| positive demand means that the node want to receive flow. A flow on |
| the digraph G satisfies all demand if the net flow into each node |
| is equal to the demand of that node. |
| |
| Parameters |
| ---------- |
| G : NetworkX graph |
| DiGraph on which a minimum cost flow satisfying all demands is |
| to be found. |
| |
| demand: string |
| Nodes of the graph G are expected to have an attribute demand |
| that indicates how much flow a node wants to send (negative |
| demand) or receive (positive demand). Note that the sum of the |
| demands should be 0 otherwise the problem in not feasible. If |
| this attribute is not present, a node is considered to have 0 |
| demand. Default value: 'demand'. |
| |
| capacity: string |
| Edges of the graph G are expected to have an attribute capacity |
| that indicates how much flow the edge can support. If this |
| attribute is not present, the edge is considered to have |
| infinite capacity. Default value: 'capacity'. |
| |
| weight: string |
| Edges of the graph G are expected to have an attribute weight |
| that indicates the cost incurred by sending one unit of flow on |
| that edge. If not present, the weight is considered to be 0. |
| Default value: 'weight'. |
| |
| Returns |
| ------- |
| flowCost: integer, float |
| Cost of a minimum cost flow satisfying all demands. |
| |
| Raises |
| ------ |
| NetworkXError |
| This exception is raised if the input graph is not directed or |
| not connected. |
| |
| NetworkXUnfeasible |
| This exception is raised in the following situations: |
| * The sum of the demands is not zero. Then, there is no |
| flow satisfying all demands. |
| * There is no flow satisfying all demand. |
| |
| NetworkXUnbounded |
| This exception is raised if the digraph G has a cycle of |
| negative cost and infinite capacity. Then, the cost of a flow |
| satisfying all demands is unbounded below. |
| |
| See also |
| -------- |
| cost_of_flow, max_flow_min_cost, min_cost_flow, network_simplex |
| |
| Examples |
| -------- |
| A simple example of a min cost flow problem. |
| |
| >>> import networkx as nx |
| >>> G = nx.DiGraph() |
| >>> G.add_node('a', demand = -5) |
| >>> G.add_node('d', demand = 5) |
| >>> G.add_edge('a', 'b', weight = 3, capacity = 4) |
| >>> G.add_edge('a', 'c', weight = 6, capacity = 10) |
| >>> G.add_edge('b', 'd', weight = 1, capacity = 9) |
| >>> G.add_edge('c', 'd', weight = 2, capacity = 5) |
| >>> flowCost = nx.min_cost_flow_cost(G) |
| >>> flowCost |
| 24 |
| """ |
| return network_simplex(G, demand = demand, capacity = capacity, |
| weight = weight)[0] |
| |
| |
| def min_cost_flow(G, demand = 'demand', capacity = 'capacity', |
| weight = 'weight'): |
| """Return a minimum cost flow satisfying all demands in digraph G. |
| |
| G is a digraph with edge costs and capacities and in which nodes |
| have demand, i.e., they want to send or receive some amount of |
| flow. A negative demand means that the node wants to send flow, a |
| positive demand means that the node want to receive flow. A flow on |
| the digraph G satisfies all demand if the net flow into each node |
| is equal to the demand of that node. |
| |
| Parameters |
| ---------- |
| G : NetworkX graph |
| DiGraph on which a minimum cost flow satisfying all demands is |
| to be found. |
| |
| demand: string |
| Nodes of the graph G are expected to have an attribute demand |
| that indicates how much flow a node wants to send (negative |
| demand) or receive (positive demand). Note that the sum of the |
| demands should be 0 otherwise the problem in not feasible. If |
| this attribute is not present, a node is considered to have 0 |
| demand. Default value: 'demand'. |
| |
| capacity: string |
| Edges of the graph G are expected to have an attribute capacity |
| that indicates how much flow the edge can support. If this |
| attribute is not present, the edge is considered to have |
| infinite capacity. Default value: 'capacity'. |
| |
| weight: string |
| Edges of the graph G are expected to have an attribute weight |
| that indicates the cost incurred by sending one unit of flow on |
| that edge. If not present, the weight is considered to be 0. |
| Default value: 'weight'. |
| |
| Returns |
| ------- |
| flowDict: dictionary |
| Dictionary of dictionaries keyed by nodes such that |
| flowDict[u][v] is the flow edge (u, v). |
| |
| Raises |
| ------ |
| NetworkXError |
| This exception is raised if the input graph is not directed or |
| not connected. |
| |
| NetworkXUnfeasible |
| This exception is raised in the following situations: |
| * The sum of the demands is not zero. Then, there is no |
| flow satisfying all demands. |
| * There is no flow satisfying all demand. |
| |
| NetworkXUnbounded |
| This exception is raised if the digraph G has a cycle of |
| negative cost and infinite capacity. Then, the cost of a flow |
| satisfying all demands is unbounded below. |
| |
| See also |
| -------- |
| cost_of_flow, max_flow_min_cost, min_cost_flow_cost, network_simplex |
| |
| Examples |
| -------- |
| A simple example of a min cost flow problem. |
| |
| >>> import networkx as nx |
| >>> G = nx.DiGraph() |
| >>> G.add_node('a', demand = -5) |
| >>> G.add_node('d', demand = 5) |
| >>> G.add_edge('a', 'b', weight = 3, capacity = 4) |
| >>> G.add_edge('a', 'c', weight = 6, capacity = 10) |
| >>> G.add_edge('b', 'd', weight = 1, capacity = 9) |
| >>> G.add_edge('c', 'd', weight = 2, capacity = 5) |
| >>> flowDict = nx.min_cost_flow(G) |
| """ |
| return network_simplex(G, demand = demand, capacity = capacity, |
| weight = weight)[1] |
| |
| |
| def cost_of_flow(G, flowDict, weight = 'weight'): |
| """Compute the cost of the flow given by flowDict on graph G. |
| |
| Note that this function does not check for the validity of the |
| flow flowDict. This function will fail if the graph G and the |
| flow don't have the same edge set. |
| |
| Parameters |
| ---------- |
| G : NetworkX graph |
| DiGraph on which a minimum cost flow satisfying all demands is |
| to be found. |
| |
| weight: string |
| Edges of the graph G are expected to have an attribute weight |
| that indicates the cost incurred by sending one unit of flow on |
| that edge. If not present, the weight is considered to be 0. |
| Default value: 'weight'. |
| |
| flowDict: dictionary |
| Dictionary of dictionaries keyed by nodes such that |
| flowDict[u][v] is the flow edge (u, v). |
| |
| Returns |
| ------- |
| cost: Integer, float |
| The total cost of the flow. This is given by the sum over all |
| edges of the product of the edge's flow and the edge's weight. |
| |
| See also |
| -------- |
| max_flow_min_cost, min_cost_flow, min_cost_flow_cost, network_simplex |
| """ |
| return sum((flowDict[u][v] * d.get(weight, 0) |
| for u, v, d in G.edges_iter(data = True))) |
| |
| |
| def max_flow_min_cost(G, s, t, capacity = 'capacity', weight = 'weight'): |
| """Return a maximum (s, t)-flow of minimum cost. |
| |
| G is a digraph with edge costs and capacities. There is a source |
| node s and a sink node t. This function finds a maximum flow from |
| s to t whose total cost is minimized. |
| |
| Parameters |
| ---------- |
| G : NetworkX graph |
| DiGraph on which a minimum cost flow satisfying all demands is |
| to be found. |
| |
| s: node label |
| Source of the flow. |
| |
| t: node label |
| Destination of the flow. |
| |
| capacity: string |
| Edges of the graph G are expected to have an attribute capacity |
| that indicates how much flow the edge can support. If this |
| attribute is not present, the edge is considered to have |
| infinite capacity. Default value: 'capacity'. |
| |
| weight: string |
| Edges of the graph G are expected to have an attribute weight |
| that indicates the cost incurred by sending one unit of flow on |
| that edge. If not present, the weight is considered to be 0. |
| Default value: 'weight'. |
| |
| Returns |
| ------- |
| flowDict: dictionary |
| Dictionary of dictionaries keyed by nodes such that |
| flowDict[u][v] is the flow edge (u, v). |
| |
| Raises |
| ------ |
| NetworkXError |
| This exception is raised if the input graph is not directed or |
| not connected. |
| |
| NetworkXUnbounded |
| This exception is raised if there is an infinite capacity path |
| from s to t in G. In this case there is no maximum flow. This |
| exception is also raised if the digraph G has a cycle of |
| negative cost and infinite capacity. Then, the cost of a flow |
| is unbounded below. |
| |
| See also |
| -------- |
| cost_of_flow, ford_fulkerson, min_cost_flow, min_cost_flow_cost, |
| network_simplex |
| |
| Examples |
| -------- |
| >>> G = nx.DiGraph() |
| >>> G.add_edges_from([(1, 2, {'capacity': 12, 'weight': 4}), |
| ... (1, 3, {'capacity': 20, 'weight': 6}), |
| ... (2, 3, {'capacity': 6, 'weight': -3}), |
| ... (2, 6, {'capacity': 14, 'weight': 1}), |
| ... (3, 4, {'weight': 9}), |
| ... (3, 5, {'capacity': 10, 'weight': 5}), |
| ... (4, 2, {'capacity': 19, 'weight': 13}), |
| ... (4, 5, {'capacity': 4, 'weight': 0}), |
| ... (5, 7, {'capacity': 28, 'weight': 2}), |
| ... (6, 5, {'capacity': 11, 'weight': 1}), |
| ... (6, 7, {'weight': 8}), |
| ... (7, 4, {'capacity': 6, 'weight': 6})]) |
| >>> mincostFlow = nx.max_flow_min_cost(G, 1, 7) |
| >>> nx.cost_of_flow(G, mincostFlow) |
| 373 |
| >>> maxFlow = nx.ford_fulkerson_flow(G, 1, 7) |
| >>> nx.cost_of_flow(G, maxFlow) |
| 428 |
| >>> mincostFlowValue = (sum((mincostFlow[u][7] for u in G.predecessors(7))) |
| ... - sum((mincostFlow[7][v] for v in G.successors(7)))) |
| >>> mincostFlowValue == nx.max_flow(G, 1, 7) |
| True |
| |
| |
| """ |
| maxFlow = nx.max_flow(G, s, t, capacity = capacity) |
| H = nx.DiGraph(G) |
| H.add_node(s, demand = -maxFlow) |
| H.add_node(t, demand = maxFlow) |
| return min_cost_flow(H, capacity = capacity, weight = weight) |
| |