-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shouldn't OrderedGraph.subgraph() preserve edge order? #2048
Comments
I'd say that is unintended. The DiGraph.subgraph function intentionally uses a shortcut in assigning the successors and predecessors which can permute the order of predecessors (as you have found). This implementation of subgraph is slower but should preserve the order. def subgraph(self, nbunch):
"""Return the subgraph induced on nodes in nbunch.
The induced subgraph of the graph contains the nodes in nbunch
and the edges between those nodes.
Parameters
----------
nbunch : list, iterable
A container of nodes which will be iterated through once.
Returns
-------
G : Graph
A subgraph of the graph with the same edge attributes.
Notes
-----
The graph, edge or node attributes just point to the original graph.
So changes to the node or edge structure will not be reflected in
the original graph while changes to the attributes will.
To create a subgraph with its own copy of the edge/node attributes use:
nx.Graph(G.subgraph(nbunch))
If edge attributes are containers, a deep copy can be obtained using:
G.subgraph(nbunch).copy()
For an inplace reduction of a graph to a subgraph you can remove nodes:
G.remove_nodes_from([ n in G if n not in set(nbunch)])
Examples
--------
>>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
>>> G.add_path([0,1,2,3])
>>> H = G.subgraph([0,1,2])
>>> list(H.edges())
[(0, 1), (1, 2)]
"""
bunch = self.nbunch_iter(nbunch)
# create new graph and copy subgraph into it
H = self.__class__()
# copy node and attribute dictionaries
for n in bunch:
H.node[n]=self.node[n]
# namespace shortcuts for speed
H_succ=H.succ
H_pred=H.pred
self_succ=self.succ
self_pred=self.pred
# add nodes
for n in H:
H_succ[n]=H.adjlist_dict_factory()
H_pred[n]=H.adjlist_dict_factory()
# add successors
for u in H_succ:
Hnbrs=H_succ[u]
for v,datadict in self_succ[u].items():
if v in H_succ:
Hnbrs[v]=datadict
# add predecessors
for u in H_pred:
Hnbrs=H_pred[u]
for v,datadict in self_pred[u].items():
if v in H_pred:
Hnbrs[v]=datadict
H.graph=self.graph
return H |
Comments on adding this as a fix or a more efficient solution? |
I haven't tested it yet but your suggested solution is fine with me. Should I just add this method to my https://networkx.github.io/documentation/latest/reference/classes.digraph.html as well. As it is now in the documentation, I see this issue as a banana skin. |
Try adding it and see if it works for you. We'll figure out how to proceed with a fix with the code and/or docs. |
OK. At the moment we are in the middle of releasing another, unrelated open source software here. I will get back to you soon. |
I did some sanity checks and some profiling, and everything seems sane and the performance is also OK. The code is here: https://github.com/baharev/nx-ordered-graph/blob/master/main.py I certainly did not go overboard with the testing: I just checked the simplest case. As for the performance, the So everything looks fine to me. The only catch that I see is that the nodes to the One solution would be to provide a keyword argument to What do you think? Did I miss anything? |
Thanks @baharev for tracking this down, creating a very nice example of the problem, and putting together the code to check @hagberg's fix. I agree that there doesn't seem to be a better way to create the subgraph while matching the order of both successors and predecessors in the subgraph to the order in the graph. As far as how best to implement this, it looks like this version of subgraph takes twice as long as the existing version for simple (unordered, no attributes, etc) example graphs (which makes sense since we are essentially traversing the edges twice). So I guess we shouldn't put it into the standard We definitely should add it to the new |
@dschult Tough questions. I am working with expression graphs for automatic differentiation. I don't exactly have uses cases for As for The The edge order in the |
If you want to help you could check out the behavior of |
I did some checking on whether OrderedDiGraph adjdict order is preserved with methods.
I also re-noticed that the edges are reported in a different order than the order they are added. They are reported in the order of the adjacency dict-of-dict. Even if the dicts are ordered, the edges may lead to differences between edge-add-order and edge-report-order.
Even if we don't use |
The ordering works as you expect for the subgraph filters proposed in #2523 |
The bug in networkx#2048 would be fixed by the subgraph view machinery.
* Add reversed/to_directed/to_undirected views Refactor views. Add tests. * correct treatment of slots in coreviews * Add tests for subgraph filters and to show #2048 reported bug The bug in #2048 would be fixed by the subgraph view machinery. * Fix to_(un)directed when already that direction * Rename subgraph module to avoid conflict * Simplify subgraphviews and tests * refactor subgraphviews to fit coreviews structure * Move subgraphviews into graphviews * Rename views.py to reportviews.py * Move functions out of views modules into function.py and simplify * separate contextmanager from views for now. * Fix induced_subgraph in function.py * get reshuffle of modules right
My expectation was that when running:
to see:
that is, the relative order of the predecessors will be the same in the subgraph but this is clearly not the case, as I get
In graph
g
, the relative order of nodes 1 and 2 is 1, 2 in the nodes, but 2, 1 in the predecessors; most likely this is the reason why I see this. I checked with b6b362a.What is the intended behavior? Or am I using the
OrderedGraph
wrong?I am currently implementing reverse-mode automatic differentiation where the argument order of an operation, such as subtraction or division, has to be kept (
z=x-y
but in the subgraph I getz=y-x
which is a disaster for me). Maybe I misunderstood what theOrderedGraph
is meant to be used for.The text was updated successfully, but these errors were encountered: