[pypy-svn] r18084 - in pypy/dist/pypy/tool/math: . test

arigo at codespeak.net arigo at codespeak.net
Sun Oct 2 18:45:34 CEST 2005


Author: arigo
Date: Sun Oct  2 18:45:26 2005
New Revision: 18084

Added:
   pypy/dist/pypy/tool/math/
   pypy/dist/pypy/tool/math/__init__.py
      - copied unchanged from r18079, pypy/dist/pypy/tool/__init__.py
   pypy/dist/pypy/tool/math/graphlib.py   (contents, props changed)
   pypy/dist/pypy/tool/math/sparsemat.py
      - copied unchanged from r18079, pypy/dist/pypy/translator/backendopt/sparsemat.py
   pypy/dist/pypy/tool/math/test/
   pypy/dist/pypy/tool/math/test/__init__.py
      - copied unchanged from r18079, pypy/dist/pypy/tool/__init__.py
   pypy/dist/pypy/tool/math/test/test_graphlib.py   (contents, props changed)
   pypy/dist/pypy/tool/math/unionfind.py
      - copied unchanged from r18079, pypy/dist/pypy/tool/unionfind.py
Log:
Intermediate check-in, see next one.


Added: pypy/dist/pypy/tool/math/graphlib.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/tool/math/graphlib.py	Sun Oct  2 18:45:26 2005
@@ -0,0 +1,107 @@
+"""
+Utilities to manipulate graphs (vertices and edges, not control flow graphs).
+"""
+
+class Edge:
+    def __init__(self, source, target):
+        self.source = source
+        self.target = target
+
+def depth_first_search(root, vertices, edges):
+    seen = {}
+    result = []
+    def visit(vertex):
+        result.append(('start', vertex))
+        seen[vertex] = True
+        for edge in edges[vertex]:
+            w = edge.target
+            if w in vertices and w not in seen:
+                visit(w)
+        result.append(('stop', vertex))
+    visit(root)
+    return result
+
+def strong_components(vertices, edges):
+    """Enumerates the strongly connected components of a graph.  Each one is
+    a set of vertices where any node can be reached from any other vertex by
+    following the edges.  'edges' is a dict {vertex: [edges]})"""
+
+    component_root = {}
+    discovery_time = {}
+    stack = []
+
+    for root in vertices:
+        if root not in discovery_time:
+
+            for event, v in depth_first_search(root, vertices, edges):
+                if event == 'start':
+                    discovery_time[v] = len(discovery_time)
+                    component_root[v] = v
+                    stack.append(v)
+
+                else:  # event == 'stop'
+                    vroot = v
+                    for edge in edges[v]:
+                        w = edge.target
+                        if w in component_root:
+                            wroot = component_root[w]
+                            if discovery_time[wroot] < discovery_time[vroot]:
+                                vroot = wroot
+                    if vroot is v:
+                        component = {}
+                        while True:
+                            w = stack.pop()
+                            del component_root[w]
+                            component[w] = True
+                            if w is v:
+                                break
+                        yield component
+                    else:
+                        component_root[v] = vroot
+
+def all_cycles(root, vertices, edges):
+    """Enumerates cycles.  Each cycle is a list of edges."""
+    stackpos = {}
+    edgestack = []
+    result = []
+    def visit(v):
+        if v not in stackpos:
+            stackpos[v] = len(edgestack)
+            for edge in edges[v]:
+                if edge.target in vertices:
+                    edgestack.append(edge)
+                    visit(edge.target)
+                    edgestack.pop()
+            stackpos[v] = None
+        else:
+            if stackpos[v] is not None:   # back-edge
+                result.append(edgestack[stackpos[v]:])
+    visit(root)
+    return result
+
+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, vertices, 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))

Added: pypy/dist/pypy/tool/math/test/test_graphlib.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/tool/math/test/test_graphlib.py	Sun Oct  2 18:45:26 2005
@@ -0,0 +1,54 @@
+import autopath
+from pypy.tool.math.graphlib import *
+
+# XXX transform.py is difficult to test directly
+
+edges = {
+    'A': [Edge('A','B'), Edge('A','C')],
+    'B': [Edge('B','D'), Edge('B','E')],
+    'C': [Edge('C','F')],
+    'D': [Edge('D','D')],
+    'E': [Edge('E','A'), Edge('E','C')],
+    'F': [],
+    'G': [],
+    }
+
+def copy_edges(edges):
+    result = {}
+    for key, value in edges.items():
+        result[key] = value[:]
+    return result
+
+
+def test_strong_components():
+    saved = copy_edges(edges)
+    result = list(strong_components(edges, edges))
+    assert edges == saved
+    for comp in result:
+        comp = list(comp)
+        comp.sort()
+    result = [''.join(comp) for comp in result]
+    result.sort()
+    assert result == ['ABE', 'C', 'D', 'F', 'G']
+
+def test_all_cycles():
+    saved = copy_edges(edges)
+    cycles = list(all_cycles('A', edges, edges))
+    assert edges == saved
+    cycles.sort()
+    expected = [
+        [edges['A'][0], edges['B'][1], edges['E'][0]],
+        [edges['D'][0]],
+        ]
+    expected.sort()
+    assert cycles == expected
+
+def test_break_cycles():
+    saved = copy_edges(edges)
+    result = list(break_cycles(edges, edges))
+    assert edges == saved
+    assert len(result) == 2
+    assert edges['D'][0] in result
+    assert (edges['A'][0] in result or
+            edges['B'][1] in result or
+            edges['E'][0] in result)



More information about the Pypy-commit mailing list