Repository: networkx/networkx

Issue:
Update documentation surrounding `union`, `disjoint_union`, `compose` and `Graph.update`
This is an action item extracted from the discussion in #4208.

It's not always immediately clear to users how the various operators `union`, `disjoint_union`, and `compose` differ and for which use-cases each is appropriate. There is also the `update` method of the graph classes, which simply updates the nodes/edges of a graph in-place with those of another, which is related to the above operations. It would be an improvement to ensure that the docstrings for these functions all include a link to the `update` method.

It may also be worthwhile to update the `operators` module docstring with examples highlighting the use-cases and differences between all of these operations.


Discussion:
I'd like to be assigned to this issue. I've read through the discussion in https://github.com/networkx/networkx/issues/4208. I think I can clarify the difference between `union`, `disjoint_union`, `compose` and `Graph.update`

The first time I wanted to combine two graphs, I started at `union`, which was not what I really wanted. It took me a while to find compose. I can leave some descriptive bread crumbs in the "See Also" section.

Also, can an update the `operators` module so the descriptions there are consistent with the updates elsewhere.

There were some comments about improving the error message of `union`, which I could do, but perhaps that was meant to be raised in a separate issue.
Sounds good @brocla ! Just as a heads up - NetworkX typically doesn't assign issues. Generally we just ask that you check whether there is already an open PR linked to the issue (you can usually see this in the `Development` panel on the RHS of the page in GitHub). Since there isn't anything open in this case, please feel free to make a PR!

If you're interested in more tid-bits about the typical project workflow, you can check out [this FAQ](https://networkx.org/documentation/latest/developer/new_contributor_faq.html#q-i-ve-found-an-issue-i-m-interested-in-can-i-have-it-assigned-to-me) (feedback welcome of course)!

Resolving patch:
diff --git a/networkx/algorithms/operators/binary.py b/networkx/algorithms/operators/binary.py
--- a/networkx/algorithms/operators/binary.py
+++ b/networkx/algorithms/operators/binary.py
@@ -15,14 +15,16 @@
 
 
 def union(G, H, rename=(None, None)):
-    """Return the union of graphs G and H.
+    """Combine graphs G and H. The names of nodes must be unique.
+
+    A name collision between the graphs will raise an exception.
+
+    A renaming facility is provided to avoid name collisions.
 
-    Graphs G and H must be disjoint after the renaming takes place,
-    otherwise an exception is raised.
 
     Parameters
     ----------
-    G,H : graph
+    G, H : graph
        A NetworkX graph
 
     rename : tuple , default=(None, None)
@@ -34,14 +36,23 @@ def union(G, H, rename=(None, None)):
     -------
     U : A union graph with the same type as G.
 
+    See Also
+    --------
+    compose
+    :func:`~networkx.Graph.update`
+    disjoint_union
+
     Notes
     -----
-    To force a disjoint union with node relabeling, use
-    disjoint_union(G,H) or convert_node_labels_to integers().
+    To combine graphs that have common nodes, consider compose(G, H)
+    or the method, Graph.update().
 
-    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.
+    disjoint_union() is similar to union() except that it avoids name clashes
+    by relabeling the nodes with sequential integers.
+
+    Edge and node attributes are propagated from G and H to the union graph.
+    Graph attributes are also propagated, but if they are present in both G and H,
+    then the value from H is used.
 
     Examples
     --------
@@ -53,17 +64,15 @@ def union(G, H, rename=(None, None)):
     >>> U.edges
     EdgeView([('G0', 'G1'), ('G0', 'G2'), ('G1', 'G2'), ('H0', 'H1'), ('H0', 'H3'), ('H1', 'H3'), ('H1', 'H2')])
 
-    See Also
-    --------
-    disjoint_union
+
     """
     return nx.union_all([G, H], rename)
 
 
 def disjoint_union(G, H):
-    """Return the disjoint union of graphs G and H.
+    """Combine graphs G and H. The nodes are assumed to be unique (disjoint).
 
-    This algorithm forces distinct integer node labels.
+    This algorithm automatically relabels nodes to avoid name collisions.
 
     Parameters
     ----------
@@ -74,6 +83,12 @@ def disjoint_union(G, H):
     -------
     U : A union graph with the same type as G.
 
+    See Also
+    --------
+    union
+    compose
+    :func:`~networkx.Graph.update`
+
     Notes
     -----
     A new graph is created, of the same class as G.  It is recommended
@@ -82,9 +97,15 @@ def disjoint_union(G, H):
     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.
+    Renumbering forces G and H to be disjoint, so no exception is ever raised for a name collision.
+    To preserve the check for common nodes, use union().
+
+    Edge and node attributes are propagated from G and H to the union graph.
+    Graph attributes are also propagated, but if they are present in both G and H,
+    then the value from H is used.
+
+    To combine graphs that have common nodes, consider compose(G, H)
+    or the method, Graph.update().
 
     Examples
     --------
@@ -262,10 +283,12 @@ def symmetric_difference(G, H):
 
 
 def compose(G, H):
-    """Returns a new graph of G composed with H.
+    """Compose graph G with H by combining nodes and edges into a single graph.
+
+    The node sets and edges sets do not need to be disjoint.
 
-    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.
+    Composing preserves the attributes of nodes and edges.
+    Attribute values from H take precedent over attribute values from G.
 
     Parameters
     ----------
@@ -274,17 +297,25 @@ def compose(G, H):
 
     Returns
     -------
-    C: A new graph  with the same type as G
+    C: A new graph with the same type as G
+
+    See Also
+    --------
+    :func:`~networkx.Graph.update`
+    union
+    disjoint_union
 
     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 combining the attributes of common nodes is not desired, consider union(),
+    which raises an exception for name collisions.
+
     Examples
     --------
     >>> G = nx.Graph([(0, 1), (0, 2)])


Test patch: