Repository: networkx/networkx

Issue:
Have `compose` offer an option to sum attributes.
This will need a bit of thought about how best to handle this...

Right now in `compose` where there are nodes with the same name or edges between the same nodes in both graphs and the attributes are in conflict, it takes the attributes of one of them over the other.

I can imagine many scenarios where we might want to sum the attributes if they are numeric.  I think it would be good to include an optional flag to allow summing of node or edge attributes.


Discussion:
Wouldn’t that complicate the composition operation? It is defined as “the simple union of the node sets and edge sets” in [binary operations](https://github.com/networkx/networkx/blob/master/networkx/algorithms/operators/binary.py).
There's some choice about how to interpret the union of the two graphs both with edges 1-2 and in one case the weight is 1 and the other the weight is 2.

How does one do a simple union of this?  What does the "simple union" mean when two edges have different weights?

Right now the algorithm chooses one of them.

Is that the right result?  I think it will depend on context.  But I can definitely think of contexts where we'll want to do the sum.
While I would agree the ability to perform any binary operation on the weights of two like edges would be useful, I also think edge weight operations don’t belong in the same algorithm that takes their union.

A union of two sets not disjoint would behave like they do in this function (“losing” elements from one set or the other), so I would argue this operation behaves as expected. `Compose` produces a sort of non-commutative union but then that is something that should be handled outside of the operation in my opinion (e.g. making an isomorphic graph for preserving vertices/edges before calling).
I guess the current workaround is something like the following.
```python
GcomposeH = nx.compose(G, H)
node_data = {n: G.nodes[n].get('fun') + H.nodes[n].get('fun') for n in G.nodes & H.nodes}
nx.set_node_attributes(GcomposeH, node_data, 'fun')
edge_data = {e: G.edges[e].get('weight') + H.edges[e].get('weight') for e in G.edges & H.edges} 
nx.set_edge_attributes(GcomposeH, edge_data, 'weight')
```
It seems like it would be hard to generalize the combining of attribute dictionaries.
For example, do we sum attribute 'fun' but multiply attribute 'goodfun' and ignore another?
Perhaps an example like this would help people construct their own though.
Hello @dschult , I want to work on this issue if it is still needed.
Hello @dtekinoglu , I think what is needed here is an example in the doc_string similar to the "workaround" above that shows users ho to update their edge attributes after the `compose` operation.
Thanks!!

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
@@ -309,6 +309,35 @@ def compose(G, H):
     NodeView((0, 1, 2))
     >>> R.edges
     EdgeView([(0, 1), (0, 2), (1, 2)])
+
+    By default, the attributes from `H` take precedent over attributes from `G`.
+    If you prefer another way of combining attributes, you can update them after the compose operation:
+
+    >>> G = nx.Graph([(0, 1, {'weight': 2.0}), (3, 0, {'weight': 100.0})])
+    >>> H = nx.Graph([(0, 1, {'weight': 10.0}), (1, 2, {'weight': -1.0})])
+    >>> nx.set_node_attributes(G, {0: 'dark', 1: 'light', 3: 'black'}, name='color')
+    >>> nx.set_node_attributes(H, {0: 'green', 1: 'orange', 2: 'yellow'}, name='color')
+    >>> GcomposeH = nx.compose(G, H)
+
+    Normally, color attribute values of nodes of GcomposeH come from H. We can workaround this as follows:
+
+    >>> node_data = {n: G.nodes[n]['color'] + " " + H.nodes[n]['color'] for n in G.nodes & H.nodes}
+    >>> nx.set_node_attributes(GcomposeH, node_data, 'color')
+    >>> print(GcomposeH.nodes[0]['color'])
+    dark green
+
+    >>> print(GcomposeH.nodes[3]['color'])
+    black
+
+    Similarly, we can update edge attributes after the compose operation in a way we prefer:
+
+    >>> edge_data = {e: G.edges[e]['weight'] * H.edges[e]['weight'] for e in G.edges & H.edges}
+    >>> nx.set_edge_attributes(GcomposeH, edge_data, 'weight')
+    >>> print(GcomposeH.edges[(0, 1)]['weight'])
+    20.0
+
+    >>> print(GcomposeH.edges[(3, 0)]['weight'])
+    100.0
     """
     return nx.compose_all([G, H])
 


Test patch: