Mercurial > repos > shellac > sam_consensus_v3
diff env/lib/python3.9/site-packages/networkx/algorithms/operators/binary.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/env/lib/python3.9/site-packages/networkx/algorithms/operators/binary.py Mon Mar 22 18:12:50 2021 +0000 @@ -0,0 +1,404 @@ +""" +Operations on graphs including union, intersection, difference. +""" +import networkx as nx + +__all__ = [ + "union", + "compose", + "disjoint_union", + "intersection", + "difference", + "symmetric_difference", + "full_join", +] + + +def union(G, H, rename=(None, None), name=None): + """ Return the union of graphs G and H. + + Graphs G and H must be disjoint, otherwise an exception is raised. + + Parameters + ---------- + G,H : graph + A NetworkX graph + + rename : bool , default=(None, None) + Node names of G and H can be changed by specifying the tuple + rename=('G-','H-') (for example). Node "u" in G is then renamed + "G-u" and "v" in H is renamed "H-v". + + name : string + Specify the name for the union graph + + Returns + ------- + U : A union graph with the same type as G. + + Notes + ----- + To force a disjoint union with node relabeling, use + disjoint_union(G,H) or convert_node_labels_to integers(). + + Graph, edge, and node attributes are propagated from G and H + to the union graph. If a graph attribute is present in both + G and H the value from H is used. + + See Also + -------- + disjoint_union + """ + if not G.is_multigraph() == H.is_multigraph(): + raise nx.NetworkXError("G and H must both be graphs or multigraphs.") + # Union is the same type as G + R = G.__class__() + # add graph attributes, H attributes take precedent over G attributes + R.graph.update(G.graph) + R.graph.update(H.graph) + + # rename graph to obtain disjoint node labels + def add_prefix(graph, prefix): + if prefix is None: + return graph + + def label(x): + if isinstance(x, str): + name = prefix + x + else: + name = prefix + repr(x) + return name + + return nx.relabel_nodes(graph, label) + + G = add_prefix(G, rename[0]) + H = add_prefix(H, rename[1]) + if set(G) & set(H): + raise nx.NetworkXError( + "The node sets of G and H are not disjoint.", + "Use appropriate rename=(Gprefix,Hprefix)" "or use disjoint_union(G,H).", + ) + if G.is_multigraph(): + G_edges = G.edges(keys=True, data=True) + else: + G_edges = G.edges(data=True) + if H.is_multigraph(): + H_edges = H.edges(keys=True, data=True) + else: + H_edges = H.edges(data=True) + + # add nodes + R.add_nodes_from(G) + R.add_nodes_from(H) + # add edges + R.add_edges_from(G_edges) + R.add_edges_from(H_edges) + # add node attributes + for n in G: + R.nodes[n].update(G.nodes[n]) + for n in H: + R.nodes[n].update(H.nodes[n]) + + return R + + +def disjoint_union(G, H): + """ Return the disjoint union of graphs G and H. + + This algorithm forces distinct integer node labels. + + Parameters + ---------- + G,H : graph + A NetworkX graph + + Returns + ------- + U : A union graph with the same type as G. + + Notes + ----- + A new graph is created, of the same class as G. It is recommended + that G and H be either both directed or both undirected. + + The nodes of G are relabeled 0 to len(G)-1, and the nodes of H are + relabeled len(G) to len(G)+len(H)-1. + + Graph, edge, and node attributes are propagated from G and H + to the union graph. If a graph attribute is present in both + G and H the value from H is used. + """ + R1 = nx.convert_node_labels_to_integers(G) + R2 = nx.convert_node_labels_to_integers(H, first_label=len(R1)) + R = union(R1, R2) + R.graph.update(G.graph) + R.graph.update(H.graph) + return R + + +def intersection(G, H): + """Returns a new graph that contains only the edges that exist in + both G and H. + + The node sets of H and G must be the same. + + Parameters + ---------- + G,H : graph + A NetworkX graph. G and H must have the same node sets. + + Returns + ------- + GH : A new graph with the same type as G. + + Notes + ----- + Attributes from the graph, nodes, and edges are not copied to the new + graph. If you want a new graph of the intersection of G and H + with the attributes (including edge data) from G use remove_nodes_from() + as follows + + >>> G = nx.path_graph(3) + >>> H = nx.path_graph(5) + >>> R = G.copy() + >>> R.remove_nodes_from(n for n in G if n not in H) + """ + # create new graph + R = nx.create_empty_copy(G) + + if not G.is_multigraph() == H.is_multigraph(): + raise nx.NetworkXError("G and H must both be graphs or multigraphs.") + if set(G) != set(H): + raise nx.NetworkXError("Node sets of graphs are not equal") + + if G.number_of_edges() <= H.number_of_edges(): + if G.is_multigraph(): + edges = G.edges(keys=True) + else: + edges = G.edges() + for e in edges: + if H.has_edge(*e): + R.add_edge(*e) + else: + if H.is_multigraph(): + edges = H.edges(keys=True) + else: + edges = H.edges() + for e in edges: + if G.has_edge(*e): + R.add_edge(*e) + return R + + +def difference(G, H): + """Returns a new graph that contains the edges that exist in G but not in H. + + The node sets of H and G must be the same. + + Parameters + ---------- + G,H : graph + A NetworkX graph. G and H must have the same node sets. + + Returns + ------- + D : A new graph with the same type as G. + + Notes + ----- + Attributes from the graph, nodes, and edges are not copied to the new + graph. If you want a new graph of the difference of G and H with + with the attributes (including edge data) from G use remove_nodes_from() + as follows: + + >>> G = nx.path_graph(3) + >>> H = nx.path_graph(5) + >>> R = G.copy() + >>> R.remove_nodes_from(n for n in G if n in H) + """ + # create new graph + if not G.is_multigraph() == H.is_multigraph(): + raise nx.NetworkXError("G and H must both be graphs or multigraphs.") + R = nx.create_empty_copy(G) + + if set(G) != set(H): + raise nx.NetworkXError("Node sets of graphs not equal") + + if G.is_multigraph(): + edges = G.edges(keys=True) + else: + edges = G.edges() + for e in edges: + if not H.has_edge(*e): + R.add_edge(*e) + return R + + +def symmetric_difference(G, H): + """Returns new graph with edges that exist in either G or H but not both. + + The node sets of H and G must be the same. + + Parameters + ---------- + G,H : graph + A NetworkX graph. G and H must have the same node sets. + + Returns + ------- + D : A new graph with the same type as G. + + Notes + ----- + Attributes from the graph, nodes, and edges are not copied to the new + graph. + """ + # create new graph + if not G.is_multigraph() == H.is_multigraph(): + raise nx.NetworkXError("G and H must both be graphs or multigraphs.") + R = nx.create_empty_copy(G) + + if set(G) != set(H): + raise nx.NetworkXError("Node sets of graphs not equal") + + gnodes = set(G) # set of nodes in G + hnodes = set(H) # set of nodes in H + nodes = gnodes.symmetric_difference(hnodes) + R.add_nodes_from(nodes) + + if G.is_multigraph(): + edges = G.edges(keys=True) + else: + edges = G.edges() + # we could copy the data here but then this function doesn't + # match intersection and difference + for e in edges: + if not H.has_edge(*e): + R.add_edge(*e) + + if H.is_multigraph(): + edges = H.edges(keys=True) + else: + edges = H.edges() + for e in edges: + if not G.has_edge(*e): + R.add_edge(*e) + return R + + +def compose(G, H): + """Returns a new graph of G composed with H. + + Composition is the simple union of the node sets and edge sets. + The node sets of G and H do not need to be disjoint. + + Parameters + ---------- + G, H : graph + A NetworkX graph + + Returns + ------- + C: A new graph with the same type as G + + Notes + ----- + It is recommended that G and H be either both directed or both undirected. + Attributes from H take precedent over attributes from G. + + For MultiGraphs, the edges are identified by incident nodes AND edge-key. + This can cause surprises (i.e., edge `(1, 2)` may or may not be the same + in two graphs) if you use MultiGraph without keeping track of edge keys. + """ + if not G.is_multigraph() == H.is_multigraph(): + raise nx.NetworkXError("G and H must both be graphs or multigraphs.") + + R = G.__class__() + # add graph attributes, H attributes take precedent over G attributes + R.graph.update(G.graph) + R.graph.update(H.graph) + + R.add_nodes_from(G.nodes(data=True)) + R.add_nodes_from(H.nodes(data=True)) + + if G.is_multigraph(): + R.add_edges_from(G.edges(keys=True, data=True)) + else: + R.add_edges_from(G.edges(data=True)) + if H.is_multigraph(): + R.add_edges_from(H.edges(keys=True, data=True)) + else: + R.add_edges_from(H.edges(data=True)) + return R + + +def full_join(G, H, rename=(None, None)): + """Returns the full join of graphs G and H. + + Full join is the union of G and H in which all edges between + G and H are added. + The node sets of G and H must be disjoint, + otherwise an exception is raised. + + Parameters + ---------- + G, H : graph + A NetworkX graph + + rename : bool , default=(None, None) + Node names of G and H can be changed by specifying the tuple + rename=('G-','H-') (for example). Node "u" in G is then renamed + "G-u" and "v" in H is renamed "H-v". + + Returns + ------- + U : The full join graph with the same type as G. + + Notes + ----- + It is recommended that G and H be either both directed or both undirected. + + If G is directed, then edges from G to H are added as well as from H to G. + + Note that full_join() does not produce parallel edges for MultiGraphs. + + The full join operation of graphs G and H is the same as getting + their complement, performing a disjoint union, and finally getting + the complement of the resulting graph. + + Graph, edge, and node attributes are propagated from G and H + to the union graph. If a graph attribute is present in both + G and H the value from H is used. + + See Also + -------- + union + disjoint_union + """ + R = union(G, H, rename) + + def add_prefix(graph, prefix): + if prefix is None: + return graph + + def label(x): + if isinstance(x, str): + name = prefix + x + else: + name = prefix + repr(x) + return name + + return nx.relabel_nodes(graph, label) + + G = add_prefix(G, rename[0]) + H = add_prefix(H, rename[1]) + + for i in G: + for j in H: + R.add_edge(i, j) + if R.is_directed(): + for i in H: + for j in G: + R.add_edge(i, j) + + return R