Ticket #11908: trac_11908-fc.patch

File trac_11908-fc.patch, 17.3 KB (added by chapoton, 8 years ago)
  • sage/graphs/generic_graph.py

    # HG changeset patch
    # User Frederic Chapoton <chapoton at math.univ-lyon1.fr>
    # Date 1367956092 -7200
    # Node ID fd8e81af89b306a04f03f7e648fe6896cebe912d
    # Parent  b4db5e332c582a8b104cf14494d5aeda84295792
    trac #11908 re-factor layout of trees
    
    diff --git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
    a b from sage.rings.integer import Integer 
    307307from sage.rings.rational import Rational
    308308from generic_graph_pyx import GenericGraph_pyx, spring_layout_fast
    309309from sage.graphs.dot2tex_utils import assert_have_dot2tex
    310 from sage.misc.superseded import deprecated_function_alias
    311310
    312311class GenericGraph(GenericGraph_pyx):
    313312    """
    class GenericGraph(GenericGraph_pyx): 
    474473        """
    475474        if getattr(self, "_immutable", False):
    476475            return hash((tuple(self.vertices()), tuple(self.edges())))
    477         raise TypeError, "graphs are mutable, and thus not hashable"
     476        raise TypeError("graphs are mutable, and thus not hashable")
    478477       
    479478    def __mul__(self, n):
    480479        """
    class GenericGraph(GenericGraph_pyx): 
    824823        """
    825824        try:
    826825            if copy:
    827                 from copy import copy
    828826                return self._backend._nxg.copy()
    829827            else:
    830828                return self._backend._nxg
    class GenericGraph(GenericGraph_pyx): 
    11121110                D[(i,j)] = 1
    11131111                if not directed and i != j:
    11141112                    D[(j,i)] = 1
    1115         from sage.rings.integer_ring import IntegerRing
    11161113        from sage.matrix.constructor import matrix
    1117         M = matrix(IntegerRing(), n, n, D, sparse=sparse)
     1114        M = matrix(ZZ, n, n, D, sparse=sparse)
    11181115        return M
    11191116
    11201117    am = adjacency_matrix # shorter call makes life easier
    class GenericGraph(GenericGraph_pyx): 
    13401337            [ 3 -3]
    13411338            [-4  4]
    13421339        """
    1343         from sage.matrix.constructor import matrix, diagonal_matrix
    1344         from sage.rings.integer_ring import IntegerRing
     1340        from sage.matrix.constructor import diagonal_matrix
    13451341        from sage.functions.all import sqrt
    13461342       
    13471343        if weighted is None:
    class GenericGraph(GenericGraph_pyx): 
    22122208        """
    22132209        if self.has_multiple_edges():
    22142210            raise TypeError("Density is not well-defined for multigraphs.")
    2215         from sage.rings.rational import Rational
    22162211        n = self.order()
    22172212        if self.allows_loops():
    22182213            if n == 0:
    class GenericGraph(GenericGraph_pyx): 
    28032798            if root_vertex == None:
    28042799                root_vertex=self.vertex_iterator().next()
    28052800            if root_vertex not in self.vertices():
    2806                 raise ValueError, ("Vertex (%s) not in the graph."%root_vertex)
     2801                raise ValueError("Vertex (%s) not in the graph."%root_vertex)
    28072802
    28082803            M=self.kirchhoff_matrix()
    28092804
    class GenericGraph(GenericGraph_pyx): 
    35173512       
    35183513        if circular:
    35193514            if maximal:
    3520                 raise NotImplementedError, "Cannot compute the maximal genus of a genus respecting a boundary."
     3515                raise NotImplementedError("Cannot compute the maximal genus of a genus respecting a boundary.")
    35213516            boundary = G.get_boundary()
    35223517            if hasattr(G, '_embedding'):
    35233518                del(G._embedding)
    class GenericGraph(GenericGraph_pyx): 
    35463541       
    35473542        if on_embedding is not None:
    35483543            if self.has_loops() or self.is_directed() or self.has_multiple_edges():
    3549                 raise NotImplementedError, "Can't work with embeddings of non-simple graphs"
     3544                raise NotImplementedError("Can't work with embeddings of non-simple graphs")
    35503545            if on_embedding: #i.e., if on_embedding True (returns False if on_embedding is of type dict)
    35513546                try:
    35523547                    faces = len(self.trace_faces(self._embedding))
    class GenericGraph(GenericGraph_pyx): 
    35613556
    35623557            if set_embedding:
    35633558                if self.has_loops() or self.is_directed() or self.has_multiple_edges():
    3564                     raise NotImplementedError, "Can't work with embeddings of non-simple graphs"
     3559                    raise NotImplementedError("Can't work with embeddings of non-simple graphs")
    35653560                if minimal:
    35663561                    B,C = G.blocks_and_cut_vertices()
    35673562                    embedding = {}
    class GenericGraph(GenericGraph_pyx): 
    35823577                return g
    35833578            else:
    35843579                if maximal and (self.has_multiple_edges() or self.has_loops()):
    3585                     raise NotImplementedError, "Can't compute the maximal genus of a graph with loops or multiple edges"
     3580                    raise NotImplementedError("Can't compute the maximal genus of a graph with loops or multiple edges")
    35863581                if minimal:
    35873582                    B,C = G.blocks_and_cut_vertices()
    35883583                    g = 0
    class GenericGraph(GenericGraph_pyx): 
    39913986                    label = None
    39923987
    39933988        if not self.has_edge(u,v):
    3994             raise ValueError, 'edge not in graph'
     3989            raise ValueError('edge not in graph')
    39953990
    39963991        # If edge (u,v) is a pending edge, it is also a cut-edge
    39973992        if self.degree(u) == 1 or self.degree(v) == 1:
    class GenericGraph(GenericGraph_pyx): 
    42074202        """
    42084203
    42094204        if self.is_directed():
     4205            from sage.graphs.all import Graph
    42104206            g = Graph(self)
    42114207        else:
    42124208            g = self
    class GenericGraph(GenericGraph_pyx): 
    47734769        from sage.numerical.mip import MixedIntegerLinearProgram
    47744770        g = self
    47754771        if g.has_edge(s,t):
    4776             raise ValueError, "There can be no vertex cut between adjacent vertices !"
     4772            raise ValueError("There can be no vertex cut between adjacent vertices !")
    47774773        if vertices:
    47784774            value_only = False
    47794775
    class GenericGraph(GenericGraph_pyx): 
    1375913755            option prog : Which graphviz layout program to use -- one of "circo", "dot", "fdp", "neato", or "twopi".
    1376013756            option save_pos : Whether or not to save the computed position for the graph.
    1376113757            option spring : Use spring layout to finalize the current layout.
    13762             option tree_orientation : The direction of tree branches -- "up" or "down".
    13763             option tree_root : A vertex designation for drawing trees. a vertex of the tree to be used as the root for the ``layout="tree"`` option. If no root is specified, then one is chosen at random. Ignored unless ``layout='tree'``
     13758            option tree_orientation : The direction of tree branches -- 'up', 'down', 'left' or 'right'.
     13759            option tree_root : A vertex designation for drawing trees. A vertex of the tree to be used as the root for the ``layout='tree'`` option. If no root is specified, then one is chosen close to the center of the tree. Ignored unless ``layout='tree'``
    1376413760
    1376513761        Some of them only apply to certain layout algorithms. For
    1376613762        details, see :meth:`.layout_acyclic`, :meth:`.layout_planar`,
    class GenericGraph(GenericGraph_pyx): 
    1379013786        if hasattr(self, "layout_%s"%layout):
    1379113787            pos = getattr(self, "layout_%s"%layout)(dim = dim, **options)
    1379213788        elif layout is not None:
    13793             raise ValueError, "unknown layout algorithm: %s"%layout
     13789            raise ValueError("unknown layout algorithm: %s"%layout)
    1379413790
    1379513791        if len(pos) < self.order():
    1379613792            pos = self.layout_extend_randomly(pos, dim = dim)
    class GenericGraph(GenericGraph_pyx): 
    1397313969        return pos
    1397413970
    1397513971    def layout_tree(self, tree_orientation = "down", tree_root = None, dim = 2, **options):
    13976         """
     13972        r"""
    1397713973        Computes an ordered tree layout for this graph, which should
    1397813974        be a tree (no non-oriented cycles).
    1397913975
    1398013976        INPUT:
    1398113977
    13982          - ``tree_root`` -- a vertex
    13983          - ``tree_orientation`` -- "up" or "down"
     13978        - ``tree_root`` -- the root vertex. By default ``None``. In
     13979          this case, a vertex is chosen close to the center of the
     13980          tree.
     13981
     13982        - ``tree_orientation`` -- the direction in which the tree is
     13983          growing, can be 'up', 'down', 'left' or 'right' (default is
     13984          'down')
    1398413985
    1398513986        OUTPUT: a dictionary mapping vertices to positions
    1398613987
    1398713988        EXAMPLES::
    1398813989
    13989             sage: G = graphs.BalancedTree(2,2)
     13990            sage: T = graphs.RandomLobster(25, 0.3, 0.3)
     13991            sage: T.show(layout='tree', tree_orientation='up')
     13992
     13993            sage: G = graphs.HoffmanSingletonGraph()
     13994            sage: T = Graph()
     13995            sage: T.add_edges(G.min_spanning_tree(starting_vertex=0))
     13996            sage: T.show(layout='tree', tree_root=0)
     13997
     13998            sage: G = graphs.BalancedTree(2, 2)
    1399013999            sage: G.layout_tree(tree_root = 0)
    13991             {0: [1.0..., 2],
    13992              1: [0.8..., 1],
    13993              2: [1.2..., 1],
    13994              3: [0.4..., 0],
    13995              4: [0.8..., 0],
    13996              5: [1.2..., 0],
    13997              6: [1.6..., 0]}
     14000            {0: (1.5, 0),
     14001             1: (2.5, -1),
     14002             2: (0.5, -1),
     14003             3: (3.0, -2),
     14004             4: (2.0, -2),
     14005             5: (1.0, -2),
     14006             6: (0.0, -2)}
     14007
    1399814008            sage: G = graphs.BalancedTree(2,4)
    1399914009            sage: G.plot(layout="tree", tree_root = 0, tree_orientation = "up")
    14000         """
    14001         assert dim == 2, "3D tree layout not implemented"
    14002         from sage.graphs.graph import Graph
     14010
     14011            sage: G = graphs.RandomTree(80)
     14012            sage: G.plot(layout="tree", tree_orientation = "right")
     14013
     14014        TESTS::
     14015
     14016            sage: G = graphs.CycleGraph(3)
     14017            sage: G.plot(layout='tree')
     14018            Traceback (most recent call last):
     14019            ...
     14020            RuntimeError: Cannot use tree layout on this graph: self.is_tree() returns False.
     14021        """
     14022        if not(dim == 2):
     14023            raise ValueError('only implemented in 2D')
     14024
     14025        from sage.graphs.all import Graph
    1400314026        if not Graph(self).is_tree():
    1400414027            raise RuntimeError("Cannot use tree layout on this graph: self.is_tree() returns False.")
     14028
    1400514029        n = self.order()
    1400614030        vertices = self.vertices()
     14031
    1400714032        if tree_root is None:
    14008             from sage.misc.prandom import randrange
    14009             root = vertices[randrange(n)]
     14033            root = self.center()[0]
    1401014034        else:
    1401114035            root = tree_root
    14012         # BFS search for heights
    14013         seen = [root]
    14014         queue = [root]
    14015         heights = [-1]*n
    14016         heights[vertices.index(root)] = 0
    14017         while queue:
    14018             u = queue.pop(0)
    14019             for v in self.neighbors(u):
    14020                 if v not in seen:
    14021                     seen.append(v)
    14022                     queue.append(v)
    14023                     heights[vertices.index(v)] = heights[vertices.index(u)] + 1
    14024         if tree_orientation == 'down':
    14025             maxx = max(heights)
    14026             heights = [maxx-heights[i] for i in xrange(n)]
    14027         heights_dict = {}
    14028         for v in vertices:
    14029             if not heights_dict.has_key(heights[vertices.index(v)]):
    14030                 heights_dict[heights[vertices.index(v)]] = [v]
    14031             else:
    14032                 heights_dict[heights[vertices.index(v)]].append(v)
    14033 
    14034         return self.layout_ranked(heights_dict)
     14036
     14037        children = {root:self.neighbors(root)}
     14038
     14039        # always make a copy of the children because they get eaten
     14040        stack = [[u for u in children[root]]]
     14041        stick = [root]
     14042        parent = dict([(u,root) for u in children[root]])
     14043        pos = {}
     14044        obstruction = [0.0]*self.num_verts()
     14045
     14046        if tree_orientation in ['down', 'left']:
     14047            o = -1
     14048        elif tree_orientation in ['up', 'right']:
     14049            o = 1
     14050        else:
     14051            raise ValueError('orientation should be "up", "down", "left" or "right"')
     14052
     14053        def slide(v, dx):
     14054            """
     14055            shift the vertex v and its descendants to the right by dx
     14056
     14057            Precondition: v and its descendents have already had their
     14058            positions computed.
     14059            """
     14060            level = [v]
     14061            while level:
     14062                nextlevel = []
     14063                for u in level:
     14064                    x, y = pos[u]
     14065                    x += dx
     14066                    obstruction[y] = max(x+1, obstruction[y])
     14067                    pos[u] = x, y
     14068                    nextlevel += children[u]
     14069
     14070                level = nextlevel
     14071
     14072        while stack:
     14073            C = stack[-1]
     14074            if len(C) == 0:
     14075                p = stick.pop()
     14076                stack.pop()
     14077                cp = children[p]
     14078                y = o*len(stack)
     14079                if len(cp) == 0:
     14080                    x = obstruction[y]
     14081                    pos[p] = x, y
     14082                else:
     14083                    x = sum([pos[c][0] for c in cp])/len(cp)
     14084                    pos[p] = x, y
     14085                    ox = obstruction[y]
     14086                    if x < ox:
     14087                        slide(p, ox-x)
     14088                        x = ox
     14089                obstruction[y] = x+1
     14090                continue
     14091
     14092            t = C.pop()
     14093            pt = parent[t]
     14094
     14095            ct = [u for u in self.neighbors(t) if u != pt]
     14096            for c in ct:
     14097                parent[c] = t
     14098            children[t] = ct
     14099
     14100            stack.append([c for c in ct])
     14101            stick.append(t)
     14102
     14103        if tree_orientation in ['right', 'left']:
     14104            return dict([[point,[pos[point][1],pos[point][0]]] for point in pos])
     14105
     14106        return pos
    1403514107
    1403614108    def layout_graphviz(self, dim = 2, prog = 'dot', **options):
    1403714109        """
    class GenericGraph(GenericGraph_pyx): 
    1447214544            sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True)
    1447314545            sage: P.show()  # long time (3s on sage.math, 2011)
    1447414546        """
    14475         from sage.graphs.graph_plot import GraphPlot
    1447614547        from graph_plot import graphplot_options
    1447714548
    1447814549        # This dictionary only contains the options that graphplot
    class GenericGraph(GenericGraph_pyx): 
    1467614747                return graphic
    1467714748
    1467814749            except KeyError:
    14679                 raise KeyError, "Oops! You haven't specified positions for all the vertices."
     14750                raise KeyError("Oops! You haven't specified positions for all the vertices.")
    1468014751
    1468114752        elif engine == 'tachyon':
    1468214753            TT, pos3d = tachyon_vertex_plot(self, bgcolor=bgcolor, vertex_colors=vertex_colors,
    class GenericGraph(GenericGraph_pyx): 
    1509915170        .. [dotspec] http://www.graphviz.org/doc/info/lang.html
    1510015171
    1510115172        """
    15102         import re
    1510315173        from sage.graphs.dot2tex_utils import quoted_latex, quoted_str
    1510415174
    1510515175        if self.is_directed():
    class GenericGraph(GenericGraph_pyx): 
    1512515195            edge_option_functions.append(lambda (u,v,label): {"color": color_by_label(label)})
    1512615196        elif options['edge_colors'] is not None:
    1512715197            if not isinstance(options['edge_colors'],dict):
    15128                 raise ValueError, "incorrect format for edge_colors"
     15198                raise ValueError("incorrect format for edge_colors")
    1512915199            color_by_edge = {}
    1513015200            for color in options['edge_colors'].keys():
    1513115201                for edge in options['edge_colors'][color]:
    class GenericGraph(GenericGraph_pyx): 
    1580715877        # Whether to check input
    1580815878        if check_input:
    1580915879            if len(set(perm.values())) < len(perm):
    15810                 raise NotImplementedError, "Non injective relabeling"
     15880                raise NotImplementedError("Non injective relabeling")
    1581115881
    1581215882            for v in perm.iterkeys():
    1581315883                if v in self:
    def tachyon_vertex_plot(g, bgcolor=(1,1, 
    1684616916            c[1] += pos3d[v][1]
    1684716917            c[2] += pos3d[v][2]
    1684816918    except KeyError:
    16849         raise KeyError, "Oops! You haven't specified positions for all the vertices."
     16919        raise KeyError("Oops! You haven't specified positions for all the vertices.")
    1685016920
    1685116921    order = g.order()
    1685216922    c[0] = c[0]/order
  • sage/graphs/graph_plot.py

    diff --git a/sage/graphs/graph_plot.py b/sage/graphs/graph_plot.py
    a b layout_options = { 
    4242                    'iterations': 'The number of times to execute the spring layout algorithm.',
    4343                    'heights': 'A dictionary mapping heights to the list of vertices at this height.',
    4444                    'spring': 'Use spring layout to finalize the current layout.',
    45                     'tree_root': 'A vertex designation for drawing trees. a vertex of the tree to be used as the root for the ``layout="tree"`` option. If no root is specified, then one is chosen at random. Ignored unless ``layout=\'tree\'``',
    46                     'tree_orientation': 'The direction of tree branches -- "up" or "down".',
     45                    'tree_root': 'A vertex designation for drawing trees. A vertex of the tree to be used as the root for the ``layout=\'tree\'`` option. If no root is specified, then one is chosen close to the center of the tree. Ignored unless ``layout=\'tree\'``',
     46                    'tree_orientation': 'The direction of tree branches -- \'up\', \'down\', \'left\' or \'right\'.',
    4747                    'save_pos': 'Whether or not to save the computed position for the graph.',
    4848                    'dim': 'The dimension of the layout -- 2 or 3.',
    4949                    'prog': 'Which graphviz layout program to use -- one of "circo", "dot", "fdp", "neato", or "twopi".',