def add_path(G_to_add_to, nodes_for_path, **attr): """Add a path to the Graph G_to_add_to. Parameters ---------- G_to_add_to : graph A NetworkX graph nodes_for_path : iterable container A container of nodes. A path will be constructed from the nodes (in order) and added to the graph. attr : keyword arguments, optional (default= no attributes) Attributes to add to every edge in path. See Also -------- add_star, add_cycle Examples -------- >>> G = nx.Graph() >>> nx.add_path(G, [0, 1, 2, 3]) >>> nx.add_path(G, [10, 11, 12], weight=7) """ nlist = iter(nodes_for_path) try: first_node = next(nlist) except StopIteration: return G_to_add_to.add_node(first_node) G_to_add_to.add_edges_from(pairwise(chain((first_node, ), nlist)), **attr)
def cut_size(G, S, T=None, weight=None): """Returns the size of the cut between two sets of nodes. A *cut* is a partition of the nodes of a graph into two sets. The *cut size* is the sum of the weights of the edges "between" the two sets of nodes. Parameters ---------- G : NetworkX graph S : sequence A sequence of nodes in `G`. T : sequence A sequence of nodes in `G`. If not specified, this is taken to be the set complement of `S`. weight : object Edge attribute key to use as weight. If not specified, edges have weight one. Returns ------- number Total weight of all edges from nodes in set `S` to nodes in set `T` (and, in the case of directed graphs, all edges from nodes in `T` to nodes in `S`). Examples -------- In the graph with two cliques joined by a single edges, the natural bipartition of the graph into two blocks, one for each clique, yields a cut of weight one:: >>> G = nx.barbell_graph(3, 0) >>> S = {0, 1, 2} >>> T = {3, 4, 5} >>> nx.cut_size(G, S, T) 1 Each parallel edge in a multigraph is counted when determining the cut size:: >>> G = nx.MultiGraph(['ab', 'ab']) >>> S = {'a'} >>> T = {'b'} >>> nx.cut_size(G, S, T) 2 Notes ----- In a multigraph, the cut size is the total weight of edges including multiplicity. """ edges = nx.edge_boundary(G, S, T, data=weight, default=1) if G.is_directed(): edges = chain(edges, nx.edge_boundary(G, T, S, data=weight, default=1)) return sum(weight for u, v, weight in edges)
def pairwise(iterable, cyclic=False): "s -> (s0, s1), (s1, s2), (s2, s3), ..." a, b = tee(iterable) first = next(b, None) if cyclic is True: return zip(a, chain(b, (first, ))) return zip(a, b)
def test_tree_all_pairs_lowest_common_ancestor3(self): """Specifying no pairs same as specifying all.""" all_pairs = chain(combinations(self.DG, 2), ((node, node) for node in self.DG)) ans = dict(tree_all_pairs_lca(self.DG, 0, all_pairs)) self.assert_has_same_pairs(ans, self.ans)
def add_children(n, G): nbrs = G[n] if len(nbrs) == 0: return [] children_ = [] for child in nbrs: d = dict(chain(G.nodes[child].items(), [(id_, child)])) c = add_children(child, G) if c: d[children] = c children_.append(d) return children_
def _directed_triangles_and_degree_iter(G, nodes=None): """ Return an iterator of (node, total_degree, reciprocal_degree, directed_triangles). Used for directed clustering. """ nodes_nbrs = ((n, G._pred[n], G._succ[n]) for n in G.nbunch_iter(nodes)) for i, preds, succs in nodes_nbrs: ipreds = set(preds) - set([i]) isuccs = set(succs) - set([i]) directed_triangles = 0 for j in chain(ipreds, isuccs): jpreds = set(G._pred[j]) - set([j]) jsuccs = set(G._succ[j]) - set([j]) directed_triangles += sum((1 for k in chain((ipreds & jpreds), ( ipreds & jsuccs), (isuccs & jpreds), (isuccs & jsuccs)))) dtotal = len(ipreds) + len(isuccs) dbidirectional = len(ipreds & isuccs) yield (i, dtotal, dbidirectional, directed_triangles)
def assert_lca_dicts_same(self, d1, d2, G=None): """Checks if d1 and d2 contain the same pairs and have a node at the same distance from root for each. If G is None use self.DG.""" if G is None: G = self.DG root_distance = self.root_distance else: roots = [n for n, deg in G.in_degree if deg == 0] assert(len(roots) == 1) root_distance = nx.shortest_path_length(G, source=roots[0]) for a, b in ((min(pair), max(pair)) for pair in chain(d1, d2)): assert_equal(root_distance[get_pair(d1, a, b)], root_distance[get_pair(d2, a, b)])
def find_entering_edges(): """Yield entering edges until none can be found. """ if e == 0: return # Entering edges are found by combining Dantzig's rule and Bland's # rule. The edges are cyclically grouped into blocks of size B. Within # each block, Dantzig's rule is applied to find an entering edge. The # blocks to search is determined following Bland's rule. B = int(ceil(sqrt(e))) # pivot block size M = (e + B - 1) // B # number of blocks needed to cover all edges m = 0 # number of consecutive blocks without eligible # entering edges f = 0 # first edge in block while m < M: # Determine the next block of edges. l = f + B if l <= e: edges = range(f, l) else: l -= e edges = chain(range(f, e), range(l)) f = l # Find the first edge with the lowest reduced cost. i = min(edges, key=reduced_cost) c = reduced_cost(i) if c >= 0: # No entering edge found in the current block. m += 1 else: # Entering edge found. if x[i] == 0: p = S[i] q = T[i] else: p = T[i] q = S[i] yield i, p, q m = 0
def all_neighbors(graph, node): """Returns all of the neighbors of a node in the graph. If the graph is directed returns predecessors as well as successors. Parameters ---------- graph : NetworkX graph Graph to find neighbors. node : node The node whose neighbors will be returned. Returns ------- neighbors : iterator Iterator of neighbors """ if graph.is_directed(): values = chain(graph.predecessors(node), graph.successors(node)) else: values = graph.neighbors(node) return values
def enumerate_all_cliques(G): """Returns all cliques in an undirected graph. This function returns an iterator over cliques, each of which is a list of nodes. The iteration is ordered by cardinality of the cliques: first all cliques of size one, then all cliques of size two, etc. Parameters ---------- G : NetworkX graph An undirected graph. Returns ------- iterator An iterator over cliques, each of which is a list of nodes in `G`. The cliques are ordered according to size. Notes ----- To obtain a list of all cliques, use `list(enumerate_all_cliques(G))`. However, be aware that in the worst-case, the length of this list can be exponential in the number of nodes in the graph (for example, when the graph is the complete graph). This function avoids storing all cliques in memory by only keeping current candidate node lists in memory during its search. The implementation is adapted from the algorithm by Zhang, et al. (2005) [1]_ to output all cliques discovered. This algorithm ignores self-loops and parallel edges, since cliques are not conventionally defined with such edges. References ---------- .. [1] Yun Zhang, Abu-Khzam, F.N., Baldwin, N.E., Chesler, E.J., Langston, M.A., Samatova, N.F., "Genome-Scale Computational Approaches to Memory-Intensive Applications in Systems Biology". *Supercomputing*, 2005. Proceedings of the ACM/IEEE SC 2005 Conference, pp. 12, 12--18 Nov. 2005. <https://doi.org/10.1109/SC.2005.29>. """ index = {} nbrs = {} for u in G: index[u] = len(index) # Neighbors of u that appear after u in the iteration order of G. nbrs[u] = set([v for v in G[u] if v not in index]) queue = deque(([u], sorted(nbrs[u], key=index.__getitem__)) for u in G) # Loop invariants: # 1. len(base) is nondecreasing. # 2. (base + cnbrs) is sorted with respect to the iteration order of G. # 3. cnbrs is a set of common neighbors of nodes in base. while queue: base, cnbrs = map(list, queue.popleft()) yield base for i, u in enumerate(cnbrs): # Use generators to reduce memory consumption. queue.append((chain(base, [u]), filter(nbrs[u].__contains__, islice(cnbrs, i + 1, None))))
def from_nested_tuple(sequence, sensible_relabeling=False): """Returns the rooted tree corresponding to the given nested tuple. The nested tuple representation of a tree is defined recursively. The tree with one node and no edges is represented by the empty tuple, ``()``. A tree with ``k`` subtrees is represented by a tuple of length ``k`` in which each element is the nested tuple representation of a subtree. Parameters ---------- sequence : tuple A nested tuple representing a rooted tree. sensible_relabeling : bool Whether to relabel the nodes of the tree so that nodes are labeled in increasing order according to their breadth-first search order from the root node. Returns ------- NetworkX graph The tree corresponding to the given nested tuple, whose root node is node 0. If ``sensible_labeling`` is ``True``, nodes will be labeled in breadth-first search order starting from the root node. Notes ----- This function is *not* the inverse of :func:`to_nested_tuple`; the only guarantee is that the rooted trees are isomorphic. See also -------- to_nested_tuple from_prufer_sequence Examples -------- Sensible relabeling ensures that the nodes are labeled from the root starting at 0:: >>> balanced = (((), ()), ((), ())) >>> T = nx.from_nested_tuple(balanced, sensible_relabeling=True) >>> edges = [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)] >>> all((u, v) in T.edges() or (v, u) in T.edges() for (u, v) in edges) True """ def _make_tree(sequence): """Recursively creates a tree from the given sequence of nested tuples. This function employs the :func:`~networkx.tree.join` function to recursively join subtrees into a larger tree. """ # The empty sequence represents the empty tree, which is the # (unique) graph with a single node. We mark the single node # with an attribute that indicates that it is the root of the # graph. if len(sequence) == 0: return nx.empty_graph(1) # For a nonempty sequence, get the subtrees for each child # sequence and join all the subtrees at their roots. After # joining the subtrees, the root is node 0. return nx.tree.join([(_make_tree(child), 0) for child in sequence]) # Make the tree and remove the `is_root` node attribute added by the # helper function. T = _make_tree(sequence) if sensible_relabeling: # Relabel the nodes according to their breadth-first search # order, starting from the root node (that is, the node 0). bfs_nodes = chain([0], (v for u, v in nx.bfs_edges(T, 0))) labels = dict((v, i) for i, v in enumerate(bfs_nodes)) # We would like to use `copy=False`, but `relabel_nodes` doesn't # allow a relabel mapping that can't be topologically sorted. T = nx.relabel_nodes(T, labels) return T
def tree_data(G, root, attrs=_attrs): """Return data in tree format that is suitable for JSON serialization and use in Javascript documents. Parameters ---------- G : NetworkX graph G must be an oriented tree root : node The root of the tree attrs : dict A dictionary that contains two keys 'id' and 'children'. The corresponding values provide the attribute names for storing NetworkX-internal graph data. The values should be unique. Default value: :samp:`dict(id='id', children='children')`. If some user-defined graph data use these attribute names as data keys, they may be silently dropped. Returns ------- data : dict A dictionary with node-link formatted data. Raises ------ NetworkXError If values in attrs are not unique. Examples -------- >>> from networkx.readwrite import json_graph >>> G = nx.DiGraph([(1,2)]) >>> data = json_graph.tree_data(G,root=1) To serialize with json >>> import json >>> s = json.dumps(data) Notes ----- Node attributes are stored in this format but keys for attributes must be strings if you want to serialize with JSON. Graph and edge attributes are not stored. The default value of attrs will be changed in a future release of NetworkX. See Also -------- tree_graph, node_link_data, node_link_data """ if G.number_of_nodes() != G.number_of_edges() + 1: raise TypeError("G is not a tree.") if not G.is_directed(): raise TypeError("G is not directed.") id_ = attrs['id'] children = attrs['children'] if id_ == children: raise nx.NetworkXError('Attribute names are not unique.') def add_children(n, G): nbrs = G[n] if len(nbrs) == 0: return [] children_ = [] for child in nbrs: d = dict(chain(G.nodes[child].items(), [(id_, child)])) c = add_children(child, G) if c: d[children] = c children_.append(d) return children_ data = dict(chain(G.nodes[root].items(), [(id_, root)])) data[children] = add_children(root, G) return data
def from_prufer_sequence(sequence): r"""Returns the tree corresponding to the given Prüfer sequence. A *Prüfer sequence* is a list of *n* - 2 numbers between 0 and *n* - 1, inclusive. The tree corresponding to a given Prüfer sequence can be recovered by repeatedly joining a node in the sequence with a node with the smallest potential degree according to the sequence. Parameters ---------- sequence : list A Prüfer sequence, which is a list of *n* - 2 integers between zero and *n* - 1, inclusive. Returns ------- NetworkX graph The tree corresponding to the given Prüfer sequence. Notes ----- There is a bijection from labeled trees to Prüfer sequences. This function is the inverse of the :func:`from_prufer_sequence` function. Sometimes Prüfer sequences use nodes labeled from 1 to *n* instead of from 0 to *n* - 1. This function requires nodes to be labeled in the latter form. You can use :func:`networkx.relabel_nodes` to relabel the nodes of your tree to the appropriate format. This implementation is from [1]_ and has a running time of $O(n \log n)$. References ---------- .. [1] Wang, Xiaodong, Lei Wang, and Yingjie Wu. "An optimal algorithm for Prufer codes." *Journal of Software Engineering and Applications* 2.02 (2009): 111. <https://doi.org/10.4236/jsea.2009.22016> See also -------- from_nested_tuple to_prufer_sequence Examples -------- There is a bijection between Prüfer sequences and labeled trees, so this function is the inverse of the :func:`to_prufer_sequence` function: >>> edges = [(0, 3), (1, 3), (2, 3), (3, 4), (4, 5)] >>> tree = nx.Graph(edges) >>> sequence = nx.to_prufer_sequence(tree) >>> sequence [3, 3, 3, 4] >>> tree2 = nx.from_prufer_sequence(sequence) >>> list(tree2.edges()) == edges True """ n = len(sequence) + 2 # `degree` stores the remaining degree (plus one) for each node. The # degree of a node in the decoded tree is one more than the number # of times it appears in the code. degree = Counter(chain(sequence, range(n))) T = nx.empty_graph(n) # `not_orphaned` is the set of nodes that have a parent in the # tree. After the loop, there should be exactly two nodes that are # not in this set. not_orphaned = set() index = u = min(k for k in range(n) if degree[k] == 1) for v in sequence: T.add_edge(u, v) not_orphaned.add(u) degree[v] -= 1 if v < index and degree[v] == 1: u = v else: index = u = min(k for k in range(index + 1, n) if degree[k] == 1) # At this point, there must be exactly two orphaned nodes; join them. orphans = set(T) - not_orphaned u, v = orphans T.add_edge(u, v) return T
def join(rooted_trees, label_attribute=None): """Returns a new rooted tree with a root node joined with the roots of each of the given rooted trees. Parameters ---------- rooted_trees : list A list of pairs in which each left element is a NetworkX graph object representing a tree and each right element is the root node of that tree. The nodes of these trees will be relabeled to integers. label_attribute : str If provided, the old node labels will be stored in the new tree under this node attribute. If not provided, the node attribute ``'_old'`` will store the original label of the node in the rooted trees given in the input. Returns ------- NetworkX graph The rooted tree whose subtrees are the given rooted trees. The new root node is labeled 0. Each non-root node has an attribute, as described under the keyword argument ``label_attribute``, that indicates the label of the original node in the input tree. Notes ----- Graph, edge, and node attributes are propagated from the given rooted trees to the created tree. If there are any overlapping graph attributes, those from later trees will overwrite those from earlier trees in the tuple of positional arguments. Examples -------- Join two full balanced binary trees of height *h* to get a full balanced binary tree of depth *h* + 1:: >>> h = 4 >>> left = nx.balanced_tree(2, h) >>> right = nx.balanced_tree(2, h) >>> joined_tree = nx.join([(left, 0), (right, 0)]) >>> nx.is_isomorphic(joined_tree, nx.balanced_tree(2, h + 1)) True """ if len(rooted_trees) == 0: return nx.empty_graph(1) # Unzip the zipped list of (tree, root) pairs. trees, roots = zip(*rooted_trees) # The join of the trees has the same type as the type of the first # tree. R = type(trees[0])() # Relabel the nodes so that their union is the integers starting at 1. if label_attribute is None: label_attribute = '_old' relabel = partial(nx.convert_node_labels_to_integers, label_attribute=label_attribute) lengths = (len(tree) for tree in trees[:-1]) first_labels = chain([0], accumulate(lengths)) trees = [ relabel(tree, first_label=first_label + 1) for tree, first_label in zip(trees, first_labels) ] # Get the relabeled roots. roots = [ next(v for v, d in tree.nodes(data=True) if d.get('_old') == root) for tree, root in zip(trees, roots) ] # Remove the old node labels. for tree in trees: for v in tree: tree.nodes[v].pop('_old') # Add all sets of nodes and edges, with data. nodes = (tree.nodes(data=True) for tree in trees) edges = (tree.edges(data=True) for tree in trees) R.add_nodes_from(chain.from_iterable(nodes)) R.add_edges_from(chain.from_iterable(edges)) # Add graph attributes; later attributes take precedent over earlier # attributes. for tree in trees: R.graph.update(tree.graph) # Finally, join the subtrees at the root. We know 0 is unused by the # way we relabeled the subtrees. R.add_node(0) R.add_edges_from((0, root) for root in roots) return R
def network_simplex(G, demand='demand', capacity='capacity', weight='weight'): r"""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 or demands are floating point numbers (overflows and roundoff errors can cause problems). As a workaround you can use integer numbers by multiplying the relevant edge attributes by a convenient constant factor (eg 100). 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 ---------- .. [1] Z. Kiraly, P. Kovacs. Efficient implementation of minimum-cost flow algorithms. Acta Universitatis Sapientiae, Informatica 4(1):67--118. 2012. .. [2] R. Barr, F. Glover, D. Klingman. Enhancement of spanning tree labeling procedures for network optimization. INFOR 17(1):16--34. 1979. """ ########################################################################### # Problem essentials extraction and sanity check ########################################################################### if len(G) == 0: raise nx.NetworkXError('graph has no nodes') # Number all nodes and edges and hereafter reference them using ONLY their # numbers N = list(G) # nodes I = dict((u, i) for i, u in enumerate(N)) # node indices D = [G.nodes[u].get(demand, 0) for u in N] # node demands inf = float('inf') for p, b in zip(N, D): if abs(b) == inf: raise nx.NetworkXError('node %r has infinite demand' % (p, )) multigraph = G.is_multigraph() S = [] # edge sources T = [] # edge targets if multigraph: K = [] # edge keys E = {} # edge indices U = [] # edge capacities C = [] # edge weights if not multigraph: edges = G.edges(data=True) else: edges = G.edges(data=True, keys=True) edges = (e for e in edges if e[0] != e[1] and e[-1].get(capacity, inf) != 0) for i, e in enumerate(edges): S.append(I[e[0]]) T.append(I[e[1]]) if multigraph: K.append(e[2]) E[e[:-1]] = i U.append(e[-1].get(capacity, inf)) C.append(e[-1].get(weight, 0)) for e, c in zip(E, C): if abs(c) == inf: raise nx.NetworkXError('edge %r has infinite weight' % (e, )) if not multigraph: edges = nx.selfloop_edges(G, data=True) else: edges = nx.selfloop_edges(G, data=True, keys=True) for e in edges: if abs(e[-1].get(weight, 0)) == inf: raise nx.NetworkXError('edge %r has infinite weight' % (e[:-1], )) ########################################################################### # Quick infeasibility detection ########################################################################### if sum(D) != 0: raise nx.NetworkXUnfeasible('total node demand is not zero') for e, u in zip(E, U): if u < 0: raise nx.NetworkXUnfeasible('edge %r has negative capacity' % (e, )) if not multigraph: edges = nx.selfloop_edges(G, data=True) else: edges = nx.selfloop_edges(G, data=True, keys=True) for e in edges: if e[-1].get(capacity, inf) < 0: raise nx.NetworkXUnfeasible('edge %r has negative capacity' % (e[:-1], )) ########################################################################### # Initialization ########################################################################### # Add a dummy node -1 and connect all existing nodes to it with infinite- # capacity dummy edges. Node -1 will serve as the root of the # spanning tree of the network simplex method. The new edges will used to # trivially satisfy the node demands and create an initial strongly # feasible spanning tree. n = len(N) # number of nodes for p, d in enumerate(D): if d > 0: # Must be greater-than here. Zero-demand nodes must have # edges pointing towards the root to ensure strong # feasibility. S.append(-1) T.append(p) else: S.append(p) T.append(-1) faux_inf = 3 * max( chain([sum(u for u in U if u < inf), sum(abs(c) for c in C)], (abs(d) for d in D))) or 1 C.extend(repeat(faux_inf, n)) U.extend(repeat(faux_inf, n)) # Construct the initial spanning tree. e = len(E) # number of edges x = list(chain(repeat(0, e), (abs(d) for d in D))) # edge flows pi = [faux_inf if d <= 0 else -faux_inf for d in D] # node potentials parent = list(chain(repeat(-1, n), [None])) # parent nodes edge = list(range(e, e + n)) # edges to parents size = list(chain(repeat(1, n), [n + 1])) # subtree sizes next = list(chain(range(1, n), [-1, 0])) # next nodes in depth-first thread prev = list(range(-1, n)) # previous nodes in depth-first thread last = list(chain(range(n), [n - 1])) # last descendants in depth-first thread ########################################################################### # Pivot loop ########################################################################### def reduced_cost(i): """Return the reduced cost of an edge i. """ c = C[i] - pi[S[i]] + pi[T[i]] return c if x[i] == 0 else -c def find_entering_edges(): """Yield entering edges until none can be found. """ if e == 0: return # Entering edges are found by combining Dantzig's rule and Bland's # rule. The edges are cyclically grouped into blocks of size B. Within # each block, Dantzig's rule is applied to find an entering edge. The # blocks to search is determined following Bland's rule. B = int(ceil(sqrt(e))) # pivot block size M = (e + B - 1) // B # number of blocks needed to cover all edges m = 0 # number of consecutive blocks without eligible # entering edges f = 0 # first edge in block while m < M: # Determine the next block of edges. l = f + B if l <= e: edges = range(f, l) else: l -= e edges = chain(range(f, e), range(l)) f = l # Find the first edge with the lowest reduced cost. i = min(edges, key=reduced_cost) c = reduced_cost(i) if c >= 0: # No entering edge found in the current block. m += 1 else: # Entering edge found. if x[i] == 0: p = S[i] q = T[i] else: p = T[i] q = S[i] yield i, p, q m = 0 # All edges have nonnegative reduced costs. The current flow is # optimal. def find_apex(p, q): """Find the lowest common ancestor of nodes p and q in the spanning tree. """ size_p = size[p] size_q = size[q] while True: while size_p < size_q: p = parent[p] size_p = size[p] while size_p > size_q: q = parent[q] size_q = size[q] if size_p == size_q: if p != q: p = parent[p] size_p = size[p] q = parent[q] size_q = size[q] else: return p def trace_path(p, w): """Return the nodes and edges on the path from node p to its ancestor w. """ Wn = [p] We = [] while p != w: We.append(edge[p]) p = parent[p] Wn.append(p) return Wn, We def find_cycle(i, p, q): """Return the nodes and edges on the cycle containing edge i == (p, q) when the latter is added to the spanning tree. The cycle is oriented in the direction from p to q. """ w = find_apex(p, q) Wn, We = trace_path(p, w) Wn.reverse() We.reverse() if We != [i]: We.append(i) WnR, WeR = trace_path(q, w) del WnR[-1] Wn += WnR We += WeR return Wn, We def residual_capacity(i, p): """Return the residual capacity of an edge i in the direction away from its endpoint p. """ return U[i] - x[i] if S[i] == p else x[i] def find_leaving_edge(Wn, We): """Return the leaving edge in a cycle represented by Wn and We. """ j, s = min(zip(reversed(We), reversed(Wn)), key=lambda i_p: residual_capacity(*i_p)) t = T[j] if S[j] == s else S[j] return j, s, t def augment_flow(Wn, We, f): """Augment f units of flow along a cycle represented by Wn and We. """ for i, p in zip(We, Wn): if S[i] == p: x[i] += f else: x[i] -= f def trace_subtree(p): """Yield the nodes in the subtree rooted at a node p. """ yield p l = last[p] while p != l: p = next[p] yield p def remove_edge(s, t): """Remove an edge (s, t) where parent[t] == s from the spanning tree. """ size_t = size[t] prev_t = prev[t] last_t = last[t] next_last_t = next[last_t] # Remove (s, t). parent[t] = None edge[t] = None # Remove the subtree rooted at t from the depth-first thread. next[prev_t] = next_last_t prev[next_last_t] = prev_t next[last_t] = t prev[t] = last_t # Update the subtree sizes and last descendants of the (old) acenstors # of t. while s is not None: size[s] -= size_t if last[s] == last_t: last[s] = prev_t s = parent[s] def make_root(q): """Make a node q the root of its containing subtree. """ ancestors = [] while q is not None: ancestors.append(q) q = parent[q] ancestors.reverse() for p, q in zip(ancestors, islice(ancestors, 1, None)): size_p = size[p] last_p = last[p] prev_q = prev[q] last_q = last[q] next_last_q = next[last_q] # Make p a child of q. parent[p] = q parent[q] = None edge[p] = edge[q] edge[q] = None size[p] = size_p - size[q] size[q] = size_p # Remove the subtree rooted at q from the depth-first thread. next[prev_q] = next_last_q prev[next_last_q] = prev_q next[last_q] = q prev[q] = last_q if last_p == last_q: last[p] = prev_q last_p = prev_q # Add the remaining parts of the subtree rooted at p as a subtree # of q in the depth-first thread. prev[p] = last_q next[last_q] = p next[last_p] = q prev[q] = last_p last[q] = last_p def add_edge(i, p, q): """Add an edge (p, q) to the spanning tree where q is the root of a subtree. """ last_p = last[p] next_last_p = next[last_p] size_q = size[q] last_q = last[q] # Make q a child of p. parent[q] = p edge[q] = i # Insert the subtree rooted at q into the depth-first thread. next[last_p] = q prev[q] = last_p prev[next_last_p] = last_q next[last_q] = next_last_p # Update the subtree sizes and last descendants of the (new) ancestors # of q. while p is not None: size[p] += size_q if last[p] == last_p: last[p] = last_q p = parent[p] def update_potentials(i, p, q): """Update the potentials of the nodes in the subtree rooted at a node q connected to its parent p by an edge i. """ if q == T[i]: d = pi[p] - C[i] - pi[q] else: d = pi[p] + C[i] - pi[q] for q in trace_subtree(q): pi[q] += d # Pivot loop for i, p, q in find_entering_edges(): Wn, We = find_cycle(i, p, q) j, s, t = find_leaving_edge(Wn, We) augment_flow(Wn, We, residual_capacity(j, s)) if i != j: # Do nothing more if the entering edge is the same as the # the leaving edge. if parent[t] != s: # Ensure that s is the parent of t. s, t = t, s if We.index(i) > We.index(j): # Ensure that q is in the subtree rooted at t. p, q = q, p remove_edge(s, t) make_root(q) add_edge(i, p, q) update_potentials(i, p, q) ########################################################################### # Infeasibility and unboundedness detection ########################################################################### if any(x[i] != 0 for i in range(-n, 0)): raise nx.NetworkXUnfeasible('no flow satisfies all node demands') if (any(x[i] * 2 >= faux_inf for i in range(e)) or any(e[-1].get(capacity, inf) == inf and e[-1].get(weight, 0) < 0 for e in nx.selfloop_edges(G, data=True))): raise nx.NetworkXUnbounded( 'negative cycle with infinite capacity found') ########################################################################### # Flow cost calculation and flow dict construction ########################################################################### del x[e:] flow_cost = sum(c * x for c, x in zip(C, x)) flow_dict = dict((n, {}) for n in N) def add_entry(e): """Add a flow dict entry. """ d = flow_dict[e[0]] for k in e[1:-2]: try: d = d[k] except KeyError: t = {} d[k] = t d = t d[e[-2]] = e[-1] S = (N[s] for s in S) # Use original nodes. T = (N[t] for t in T) # Use original nodes. if not multigraph: for e in zip(S, T, x): add_entry(e) edges = G.edges(data=True) else: for e in zip(S, T, K, x): add_entry(e) edges = G.edges(data=True, keys=True) for e in edges: if e[0] != e[1]: if e[-1].get(capacity, inf) == 0: add_entry(e[:-1] + (0, )) else: c = e[-1].get(weight, 0) if c >= 0: add_entry(e[:-1] + (0, )) else: u = e[-1][capacity] flow_cost += c * u add_entry(e[:-1] + (u, )) return flow_cost, flow_dict
def node_link_data(G, attrs=None): """Return data in node-link format that is suitable for JSON serialization and use in Javascript documents. Parameters ---------- G : NetworkX graph attrs : dict A dictionary that contains five keys 'source', 'target', 'name', 'key' and 'link'. The corresponding values provide the attribute names for storing NetworkX-internal graph data. The values should be unique. Default value:: dict(source='source', target='target', name='id', key='key', link='links') If some user-defined graph data use these attribute names as data keys, they may be silently dropped. Returns ------- data : dict A dictionary with node-link formatted data. Raises ------ NetworkXError If values in attrs are not unique. Examples -------- >>> from networkx.readwrite import json_graph >>> G = nx.Graph([('A', 'B')]) >>> data1 = json_graph.node_link_data(G) >>> H = nx.gn_graph(2) >>> data2 = json_graph.node_link_data(H, {'link': 'edges', 'source': 'from', 'target': 'to'}) To serialize with json >>> import json >>> s1 = json.dumps(data1) >>> s2 = json.dumps(data2, default={'link': 'edges', 'source': 'from', 'target': 'to'}) Notes ----- Graph, node, and link attributes are stored in this format. Note that attribute keys will be converted to strings in order to comply with JSON. Attribute 'key' is only used for multigraphs. See Also -------- node_link_graph, adjacency_data, tree_data """ multigraph = G.is_multigraph() # Allow 'attrs' to keep default values. if attrs is None: attrs = _attrs else: attrs.update( dict((k, v) for (k, v) in _attrs.items() if k not in attrs)) name = attrs['name'] source = attrs['source'] target = attrs['target'] links = attrs['link'] # Allow 'key' to be omitted from attrs if the graph is not a multigraph. key = None if not multigraph else attrs['key'] if len(set([source, target, key])) < 3: raise nx.NetworkXError('Attribute names are not unique.') data = { 'directed': G.is_directed(), 'multigraph': multigraph, 'graph': G.graph, 'nodes': [dict(chain(G.nodes[n].items(), [(name, n)])) for n in G] } if multigraph: data[links] = [ dict(chain(d.items(), [(source, u), (target, v), (key, k)])) for u, v, k, d in G.edges(keys=True, data=True) ] else: data[links] = [ dict(chain(d.items(), [(source, u), (target, v)])) for u, v, d in G.edges(data=True) ] return data
def assert_has_same_pairs(d1, d2): for (a, b) in ((min(pair), max(pair)) for pair in chain(d1, d2)): assert_equal(get_pair(d1, a, b), get_pair(d2, a, b))
def capacity_scaling(G, demand='demand', capacity='capacity', weight='weight', heap=BinaryHeap): r"""Find a minimum cost flow satisfying all demands in digraph G. This is a capacity scaling successive shortest augmenting path algorithm. 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 or MultiDiGraph 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'. heap : class Type of heap to be used in the algorithm. It should be a subclass of :class:`MinHeap` or implement a compatible interface. If a stock heap implementation is to be used, :class:`BinaryHeap` is recommended over :class:`PairingHeap` for Python implementations without optimized attribute accesses (e.g., CPython) despite a slower asymptotic running time. For Python implementations with optimized attribute accesses (e.g., PyPy), :class:`PairingHeap` provides better performance. Default value: :class:`BinaryHeap`. Returns ------- flowCost : integer Cost of a minimum cost flow satisfying all demands. flowDict : dictionary If G is a digraph, a dict-of-dicts keyed by nodes such that flowDict[u][v] is the flow on edge (u, v). If G is a MultiDiGraph, a dict-of-dicts-of-dicts keyed by nodes so that flowDict[u][v][key] is the flow on edge (u, v, key). Raises ------ NetworkXError This exception is raised if the input graph is not directed, 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. Notes ----- This algorithm does not work if edge weights are floating-point numbers. See also -------- :meth:`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, flowDict = nx.capacity_scaling(G) >>> flowCost 24 >>> flowDict # doctest: +SKIP {'a': {'c': 1, 'b': 4}, 'c': {'d': 1}, 'b': {'d': 4}, 'd': {}} 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.capacity_scaling(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': {}} """ R = _build_residual_network(G, demand, capacity, weight) inf = float('inf') # Account cost of negative selfloops. flow_cost = sum(0 if e.get(capacity, inf) <= 0 or e.get(weight, 0) >= 0 else e[capacity] * e[weight] for u, v, e in nx.selfloop_edges(G, data=True)) # Determine the maxmimum edge capacity. wmax = max(chain([-inf], (e['capacity'] for u, v, e in R.edges(data=True)))) if wmax == -inf: # Residual network has no edges. return flow_cost, _build_flow_dict(G, R, capacity, weight) R_nodes = R.nodes R_succ = R.succ delta = 2**int(log(wmax, 2)) while delta >= 1: # Saturate Δ-residual edges with negative reduced costs to achieve # Δ-optimality. for u in R: p_u = R_nodes[u]['potential'] for v, es in R_succ[u].items(): for k, e in es.items(): flow = e['capacity'] - e['flow'] if e['weight'] - p_u + R_nodes[v]['potential'] < 0: flow = e['capacity'] - e['flow'] if flow >= delta: e['flow'] += flow R_succ[v][u][(k[0], not k[1])]['flow'] -= flow R_nodes[u]['excess'] -= flow R_nodes[v]['excess'] += flow # Determine the Δ-active nodes. S = set() T = set() S_add = S.add S_remove = S.remove T_add = T.add T_remove = T.remove for u in R: excess = R_nodes[u]['excess'] if excess >= delta: S_add(u) elif excess <= -delta: T_add(u) # Repeatedly augment flow from S to T along shortest paths until # Δ-feasibility is achieved. while S and T: s = arbitrary_element(S) t = None # Search for a shortest path in terms of reduce costs from s to # any t in T in the Δ-residual network. d = {} pred = {s: None} h = heap() h_insert = h.insert h_get = h.get h_insert(s, 0) while h: u, d_u = h.pop() d[u] = d_u if u in T: # Path found. t = u break p_u = R_nodes[u]['potential'] for v, es in R_succ[u].items(): if v in d: continue wmin = inf # Find the minimum-weighted (u, v) Δ-residual edge. for k, e in es.items(): if e['capacity'] - e['flow'] >= delta: w = e['weight'] if w < wmin: wmin = w kmin = k emin = e if wmin == inf: continue # Update the distance label of v. d_v = d_u + wmin - p_u + R_nodes[v]['potential'] if h_insert(v, d_v): pred[v] = (u, kmin, emin) if t is not None: # Augment Δ units of flow from s to t. while u != s: v = u u, k, e = pred[v] e['flow'] += delta R_succ[v][u][(k[0], not k[1])]['flow'] -= delta # Account node excess and deficit. R_nodes[s]['excess'] -= delta R_nodes[t]['excess'] += delta if R_nodes[s]['excess'] < delta: S_remove(s) if R_nodes[t]['excess'] > -delta: T_remove(t) # Update node potentials. d_t = d[t] for u, d_u in d.items(): R_nodes[u]['potential'] -= d_u - d_t else: # Path not found. S_remove(s) delta //= 2 if any(R.nodes[u]['excess'] != 0 for u in R): raise nx.NetworkXUnfeasible('No flow satisfying all demands.') # Calculate the flow cost. for u in R: for v, es in R_succ[u].items(): for e in es.values(): flow = e['flow'] if flow > 0: flow_cost += flow * e['weight'] return flow_cost, _build_flow_dict(G, R, capacity, weight)
def contracted_nodes(G, u, v, self_loops=True): """Returns the graph that results from contracting `u` and `v`. Node contraction identifies the two nodes as a single node incident to any edge that was incident to the original two nodes. Parameters ---------- G : NetworkX graph The graph whose nodes will be contracted. u, v : nodes Must be nodes in `G`. self_loops : Boolean If this is True, any edges joining `u` and `v` in `G` become self-loops on the new node in the returned graph. Returns ------- Networkx graph A new graph object of the same type as `G` (leaving `G` unmodified) with `u` and `v` identified in a single node. The right node `v` will be merged into the node `u`, so only `u` will appear in the returned graph. Notes ----- For multigraphs, the edge keys for the realigned edges may not be the same as the edge keys for the old edges. This is natural because edge keys are unique only within each pair of nodes. Examples -------- Contracting two nonadjacent nodes of the cycle graph on four nodes `C_4` yields the path graph (ignoring parallel edges):: >>> G = nx.cycle_graph(4) >>> M = nx.contracted_nodes(G, 1, 3) >>> P3 = nx.path_graph(3) >>> nx.is_isomorphic(M, P3) True >>> G = nx.MultiGraph(P3) >>> M = nx.contracted_nodes(G, 0, 2) >>> M.edges MultiEdgeView([(0, 1, 0), (0, 1, 1)]) >>> G = nx.Graph([(1,2), (2,2)]) >>> H = nx.contracted_nodes(G, 1, 2, self_loops=False) >>> list(H.nodes()) [1] >>> list(H.edges()) [(1, 1)] See also -------- contracted_edge quotient_graph Notes ----- This function is also available as `identified_nodes`. """ H = G.copy() # edge code uses G.edges(v) instead of G.adj[v] to handle multiedges if H.is_directed(): in_edges = ((w if w != v else u, u, d) for w, x, d in G.in_edges(v, data=True) if self_loops or w != u) out_edges = ((u, w if w != v else u, d) for x, w, d in G.out_edges(v, data=True) if self_loops or w != u) new_edges = chain(in_edges, out_edges) else: new_edges = ((u, w if w != v else u, d) for x, w, d in G.edges(v, data=True) if self_loops or w != u) v_data = H.nodes[v] H.remove_node(v) H.add_edges_from(new_edges) if 'contraction' in H.nodes[u]: H.nodes[u]['contraction'][v] = v_data else: H.nodes[u]['contraction'] = {v: v_data} return H
def adjacency_data(G, attrs=_attrs): """Return data in adjacency format that is suitable for JSON serialization and use in Javascript documents. Parameters ---------- G : NetworkX graph attrs : dict A dictionary that contains two keys 'id' and 'key'. The corresponding values provide the attribute names for storing NetworkX-internal graph data. The values should be unique. Default value: :samp:`dict(id='id', key='key')`. If some user-defined graph data use these attribute names as data keys, they may be silently dropped. Returns ------- data : dict A dictionary with adjacency formatted data. Raises ------ NetworkXError If values in attrs are not unique. Examples -------- >>> from networkx.readwrite import json_graph >>> G = nx.Graph([(1,2)]) >>> data = json_graph.adjacency_data(G) To serialize with json >>> import json >>> s = json.dumps(data) Notes ----- Graph, node, and link attributes will be written when using this format but attribute keys must be strings if you want to serialize the resulting data with JSON. The default value of attrs will be changed in a future release of NetworkX. See Also -------- adjacency_graph, node_link_data, tree_data """ multigraph = G.is_multigraph() id_ = attrs['id'] # Allow 'key' to be omitted from attrs if the graph is not a multigraph. key = None if not multigraph else attrs['key'] if id_ == key: raise nx.NetworkXError('Attribute names are not unique.') data = {} data['directed'] = G.is_directed() data['multigraph'] = multigraph data['graph'] = list(G.graph.items()) data['nodes'] = [] data['adjacency'] = [] for n, nbrdict in G.adjacency(): data['nodes'].append(dict(chain(G.nodes[n].items(), [(id_, n)]))) adj = [] if multigraph: for nbr, keys in nbrdict.items(): for k, d in keys.items(): adj.append(dict(chain(d.items(), [(id_, nbr), (key, k)]))) else: for nbr, d in nbrdict.items(): adj.append(dict(chain(d.items(), [(id_, nbr)]))) data['adjacency'].append(adj) return data