The Artima Developer Community
Sponsored Link

Python Buzz Forum
generator-based graph algorithms - take 1

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
maxim khesin

Posts: 251
Nickname: xamdam
Registered: Mar, 2005

Maxim Khesin is developer for Liquidnet. I like C++, python, attend design patterns study group/NYC.
generator-based graph algorithms - take 1 Posted: Mar 9, 2005 5:06 AM
Reply to this message Reply

This post originated from an RSS feed registered with Python Buzz by maxim khesin.
Original Post: generator-based graph algorithms - take 1
Feed Title: python and the web
Feed URL: http://feeds.feedburner.com/PythonAndTheWeb
Feed Description: blog dedicated to python and the networks we live in
Latest Python Buzz Posts
Latest Python Buzz Posts by maxim khesin
Latest Posts From python and the web

Advertisement
So Paul Moore gave me a much needed nudge to look into generator-based versions of graph algorithms. (BTW Paul, I'd like to see your implementation). Something like


for vertex in bfs():
# do stuff with the vertex
Is a very clear, powerful construct and very Pythonic, to boot (wait, that's the same thing! :). Since the main inspiration of this effort is Boost.Graph (BGL), my first thought was whether one of BGL's main constructs (Visitors) was compatible with generators. Basically I was wondering if I would have to keep two versions of the algorithm to have both Visitors and generators.
Another question that was bothering me was the following: this


for vertex in bfs():
# do stuff with the vertex
code is very cute, but what if I am not interested in the vertecies, but I am interested in the edges (for one, edges have enough information to build a graph, vertecies don't). Do I have to have both


for vertex in bfs_vertecies():
# do stuff with the vertex
and


for edge in bfs_edges():
# do stuff with the edge
??
Looks like generators are going to generate some serious PITA, both in implementation and naming (which is very important in libraries, BTW).
Then I rethought the whole Visitor-generator conflict. What do we want to visit? Vertecies, edges, back edges, etc. What do we want to yeild with generators? Pretty much the same things. So I guess they are not so different after all. So what if we put a yield in front of every call to a visitor function? E.g. (somewhere in bfs) we would have
yield visitor.visit_edge(e, graph)
and we'll have this visitor return the edge that's passed to it? This is not bad, as it gives us the opportunity to decide which sequence is going to generated (vertecies or edges) by simply passing the appropriate visitor. Well, there is a minor problem. As all of the visitor functions are going to be called with a 'yield' by convention, and the guts of the algorithm are oblivious to the fact that we only want the edges, we are going to get a bunch of None's (which are returned from any python function even if it does not explicitly return anything) intermixed with the returned edges. We could do


val = visitor.visit_edge(edge, graph)
if val:
return val
, but it will 2) ruin the readability of the algorithms and b) is going to be a burden to program. Then I remambered that at least in case of bfs there is a function that wraps the entire bfs algorithm. It is mainly needed to call visitor.initialize_vertex() on all the vertecies in the graph. I could filter out all of the None's right there! It ends up looking like something


def breadth_first_search(graph, start, bfs_visitor = default_bfs_visitor()):
for v in graph.vertices():
bfs_visitor.initialize_vertex(v, graph)
try:
for res in breadth_first_visit(graph, start, bfs_visitor): # call the inner func
if res:
yield res
except traversal_termination_exception, e:
pass # this is a valid termination, no rethrow necessary
Now we can pass an edge or a vertex visitor to bfs. As long as their add_edge and add_vertex functions, respectively, return their args they are going to make bfs() into a fine generator func. As a small application I thought of creating a bunch of images from my previous Amazon graph traversal and making an animation out of it. This would work very well with a generator as I could do this:


graph = pygraphlib.UGraph()
for edge in bfs(edges()):
graph.add_edge(edge)
dot = pydot.Dot(graph)
dot.save_image()
going through the edges in a loop, adding them to a graph structure, and taking a 'current snapshot' of the graph inside the loop. As a final result I wanted to use some software to create an animation out of the images. Well, everything worked untill the last step, mainly due to the fact that 'dot' produced images of different size for almost each stem of the graph, which does not look good as a movie. I put up the generator-based code here.
One remaining issue is how to call the new-style algorithms when sequence generation is not desired/is irrelevant. In my unit test I have just been forcing the algorithm to execute with a list comprehension, but it is obviously a hack. I have some idea, but now it's time to sleep on it :). gnite.

Read: generator-based graph algorithms - take 1

Topic: the bubble sort of graph programming Previous Topic   Next Topic Topic: Unfinished Business

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use