[pypy-svn] r50538 - in pypy/dist/pypy/tool/algo: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Sun Jan 13 09:06:39 CET 2008


Author: cfbolz
Date: Sun Jan 13 09:06:38 2008
New Revision: 50538

Modified:
   pypy/dist/pypy/tool/algo/graphlib.py
   pypy/dist/pypy/tool/algo/test/test_graphlib.py
Log:
make the implementation of the cycle breaker algorithm less braindead: don't
recompute the cycles every time you break a few, reuse the existing information
instead. The algorithm stays exponential but I think it's still reasonable for
"practical" call trees which are far from being complete graphs.


Modified: pypy/dist/pypy/tool/algo/graphlib.py
==============================================================================
--- pypy/dist/pypy/tool/algo/graphlib.py	(original)
+++ pypy/dist/pypy/tool/algo/graphlib.py	Sun Jan 13 09:06:38 2008
@@ -100,26 +100,42 @@
 def break_cycles(vertices, edges):
     """Enumerates a reasonably minimal set of edges that must be removed to
     make the graph acyclic."""
-    graphs = [(vertices, edges)]
-    for vertices, edges in graphs:
-        #print ''.join(vertices),
-        #print [e.source+e.target for l in edges.values() for e in l]
-        for component in strong_components(vertices, edges):
-            #print '-->', ''.join(component)
-            edge_weights = {}
-            random_vertex = component.iterkeys().next()
-            for cycle in all_cycles(random_vertex, component, edges):
-                #print '\tcycle:', [e.source+e.target for e in cycle]
-                for edge in cycle:
-                    edge_weights[edge] = edge_weights.get(edge, 0) + 1
-            if edge_weights:
-                max_weight = max(edge_weights.values())
-                for edge, weight in edge_weights.iteritems():
-                    if weight == max_weight:
-                        break
-                # kill this edge
-                yield edge
-                new_edges = edges.copy()
-                new_edges[edge.source] = [e for e in new_edges[edge.source]
-                                            if e is not edge]
-                graphs.append((component, new_edges))
+    # the approach is as follows: for each strongly connected component, find
+    # all cycles (which takens exponential time, potentially). Then break the
+    # edges that are part of the most cycles, until all cycles in that
+    # component are broken.
+    for component in strong_components(vertices, edges):
+        #print '-->', ''.join(component)
+        random_vertex = component.iterkeys().next()
+        cycles = all_cycles(random_vertex, component, edges)
+        if not cycles:
+            continue
+        allcycles = dict.fromkeys([id(cycle) for cycle in cycles])
+        edge2cycles = {}
+        edge_weights = {}
+        for cycle in cycles:
+            #print '\tcycle:', [e.source+e.target for e in cycle]
+            for edge in cycle:
+                edge2cycles.setdefault(edge, []).append(cycle)
+                edge_weights[edge] = edge_weights.get(edge, 0) + 1
+        while allcycles:
+            max_weight = 0
+            max_edge = None
+            for edge, weight in edge_weights.iteritems():
+                if weight >= max_weight:
+                    max_edge = edge
+                    max_weight = weight
+            broken_cycles = edge2cycles[max_edge]
+            assert max_edge is not None
+            # kill this edge
+            yield max_edge
+            for broken_cycle in broken_cycles:
+                try:
+                    del allcycles[id(broken_cycle)]
+                except KeyError:
+                    pass
+                else:
+                    for edge in broken_cycle:
+                        edge_weights[edge] -= 1
+                        if edge_weights[edge] == 0:
+                            del edge_weights[edge]

Modified: pypy/dist/pypy/tool/algo/test/test_graphlib.py
==============================================================================
--- pypy/dist/pypy/tool/algo/test/test_graphlib.py	(original)
+++ pypy/dist/pypy/tool/algo/test/test_graphlib.py	Sun Jan 13 09:06:38 2008
@@ -146,7 +146,7 @@
 
 class TestBadCase:
     # a complete graph
-    NUM = 30
+    NUM = 50
     edges = make_edge_dict([Edge(i, j) for i in range(NUM)
                                        for j in range(NUM)])
     vertices = dict.fromkeys(range(NUM))



More information about the Pypy-commit mailing list