Ticket #11908: trac_11908-fc.patch
File trac_11908-fc.patch, 17.3 KB (added by , 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 307 307 from sage.rings.rational import Rational 308 308 from generic_graph_pyx import GenericGraph_pyx, spring_layout_fast 309 309 from sage.graphs.dot2tex_utils import assert_have_dot2tex 310 from sage.misc.superseded import deprecated_function_alias311 310 312 311 class GenericGraph(GenericGraph_pyx): 313 312 """ … … class GenericGraph(GenericGraph_pyx): 474 473 """ 475 474 if getattr(self, "_immutable", False): 476 475 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") 478 477 479 478 def __mul__(self, n): 480 479 """ … … class GenericGraph(GenericGraph_pyx): 824 823 """ 825 824 try: 826 825 if copy: 827 from copy import copy828 826 return self._backend._nxg.copy() 829 827 else: 830 828 return self._backend._nxg … … class GenericGraph(GenericGraph_pyx): 1112 1110 D[(i,j)] = 1 1113 1111 if not directed and i != j: 1114 1112 D[(j,i)] = 1 1115 from sage.rings.integer_ring import IntegerRing1116 1113 from sage.matrix.constructor import matrix 1117 M = matrix( IntegerRing(), n, n, D, sparse=sparse)1114 M = matrix(ZZ, n, n, D, sparse=sparse) 1118 1115 return M 1119 1116 1120 1117 am = adjacency_matrix # shorter call makes life easier … … class GenericGraph(GenericGraph_pyx): 1340 1337 [ 3 -3] 1341 1338 [-4 4] 1342 1339 """ 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 1345 1341 from sage.functions.all import sqrt 1346 1342 1347 1343 if weighted is None: … … class GenericGraph(GenericGraph_pyx): 2212 2208 """ 2213 2209 if self.has_multiple_edges(): 2214 2210 raise TypeError("Density is not well-defined for multigraphs.") 2215 from sage.rings.rational import Rational2216 2211 n = self.order() 2217 2212 if self.allows_loops(): 2218 2213 if n == 0: … … class GenericGraph(GenericGraph_pyx): 2803 2798 if root_vertex == None: 2804 2799 root_vertex=self.vertex_iterator().next() 2805 2800 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) 2807 2802 2808 2803 M=self.kirchhoff_matrix() 2809 2804 … … class GenericGraph(GenericGraph_pyx): 3517 3512 3518 3513 if circular: 3519 3514 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.") 3521 3516 boundary = G.get_boundary() 3522 3517 if hasattr(G, '_embedding'): 3523 3518 del(G._embedding) … … class GenericGraph(GenericGraph_pyx): 3546 3541 3547 3542 if on_embedding is not None: 3548 3543 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") 3550 3545 if on_embedding: #i.e., if on_embedding True (returns False if on_embedding is of type dict) 3551 3546 try: 3552 3547 faces = len(self.trace_faces(self._embedding)) … … class GenericGraph(GenericGraph_pyx): 3561 3556 3562 3557 if set_embedding: 3563 3558 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") 3565 3560 if minimal: 3566 3561 B,C = G.blocks_and_cut_vertices() 3567 3562 embedding = {} … … class GenericGraph(GenericGraph_pyx): 3582 3577 return g 3583 3578 else: 3584 3579 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") 3586 3581 if minimal: 3587 3582 B,C = G.blocks_and_cut_vertices() 3588 3583 g = 0 … … class GenericGraph(GenericGraph_pyx): 3991 3986 label = None 3992 3987 3993 3988 if not self.has_edge(u,v): 3994 raise ValueError , 'edge not in graph'3989 raise ValueError('edge not in graph') 3995 3990 3996 3991 # If edge (u,v) is a pending edge, it is also a cut-edge 3997 3992 if self.degree(u) == 1 or self.degree(v) == 1: … … class GenericGraph(GenericGraph_pyx): 4207 4202 """ 4208 4203 4209 4204 if self.is_directed(): 4205 from sage.graphs.all import Graph 4210 4206 g = Graph(self) 4211 4207 else: 4212 4208 g = self … … class GenericGraph(GenericGraph_pyx): 4773 4769 from sage.numerical.mip import MixedIntegerLinearProgram 4774 4770 g = self 4775 4771 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 !") 4777 4773 if vertices: 4778 4774 value_only = False 4779 4775 … … class GenericGraph(GenericGraph_pyx): 13759 13755 option prog : Which graphviz layout program to use -- one of "circo", "dot", "fdp", "neato", or "twopi". 13760 13756 option save_pos : Whether or not to save the computed position for the graph. 13761 13757 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'`` 13764 13760 13765 13761 Some of them only apply to certain layout algorithms. For 13766 13762 details, see :meth:`.layout_acyclic`, :meth:`.layout_planar`, … … class GenericGraph(GenericGraph_pyx): 13790 13786 if hasattr(self, "layout_%s"%layout): 13791 13787 pos = getattr(self, "layout_%s"%layout)(dim = dim, **options) 13792 13788 elif layout is not None: 13793 raise ValueError , "unknown layout algorithm: %s"%layout13789 raise ValueError("unknown layout algorithm: %s"%layout) 13794 13790 13795 13791 if len(pos) < self.order(): 13796 13792 pos = self.layout_extend_randomly(pos, dim = dim) … … class GenericGraph(GenericGraph_pyx): 13973 13969 return pos 13974 13970 13975 13971 def layout_tree(self, tree_orientation = "down", tree_root = None, dim = 2, **options): 13976 """13972 r""" 13977 13973 Computes an ordered tree layout for this graph, which should 13978 13974 be a tree (no non-oriented cycles). 13979 13975 13980 13976 INPUT: 13981 13977 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') 13984 13985 13985 13986 OUTPUT: a dictionary mapping vertices to positions 13986 13987 13987 13988 EXAMPLES:: 13988 13989 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) 13990 13999 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 13998 14008 sage: G = graphs.BalancedTree(2,4) 13999 14009 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 14003 14026 if not Graph(self).is_tree(): 14004 14027 raise RuntimeError("Cannot use tree layout on this graph: self.is_tree() returns False.") 14028 14005 14029 n = self.order() 14006 14030 vertices = self.vertices() 14031 14007 14032 if tree_root is None: 14008 from sage.misc.prandom import randrange 14009 root = vertices[randrange(n)] 14033 root = self.center()[0] 14010 14034 else: 14011 14035 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 14035 14107 14036 14108 def layout_graphviz(self, dim = 2, prog = 'dot', **options): 14037 14109 """ … … class GenericGraph(GenericGraph_pyx): 14472 14544 sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) 14473 14545 sage: P.show() # long time (3s on sage.math, 2011) 14474 14546 """ 14475 from sage.graphs.graph_plot import GraphPlot14476 14547 from graph_plot import graphplot_options 14477 14548 14478 14549 # This dictionary only contains the options that graphplot … … class GenericGraph(GenericGraph_pyx): 14676 14747 return graphic 14677 14748 14678 14749 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.") 14680 14751 14681 14752 elif engine == 'tachyon': 14682 14753 TT, pos3d = tachyon_vertex_plot(self, bgcolor=bgcolor, vertex_colors=vertex_colors, … … class GenericGraph(GenericGraph_pyx): 15099 15170 .. [dotspec] http://www.graphviz.org/doc/info/lang.html 15100 15171 15101 15172 """ 15102 import re15103 15173 from sage.graphs.dot2tex_utils import quoted_latex, quoted_str 15104 15174 15105 15175 if self.is_directed(): … … class GenericGraph(GenericGraph_pyx): 15125 15195 edge_option_functions.append(lambda (u,v,label): {"color": color_by_label(label)}) 15126 15196 elif options['edge_colors'] is not None: 15127 15197 if not isinstance(options['edge_colors'],dict): 15128 raise ValueError , "incorrect format for edge_colors"15198 raise ValueError("incorrect format for edge_colors") 15129 15199 color_by_edge = {} 15130 15200 for color in options['edge_colors'].keys(): 15131 15201 for edge in options['edge_colors'][color]: … … class GenericGraph(GenericGraph_pyx): 15807 15877 # Whether to check input 15808 15878 if check_input: 15809 15879 if len(set(perm.values())) < len(perm): 15810 raise NotImplementedError , "Non injective relabeling"15880 raise NotImplementedError("Non injective relabeling") 15811 15881 15812 15882 for v in perm.iterkeys(): 15813 15883 if v in self: … … def tachyon_vertex_plot(g, bgcolor=(1,1, 16846 16916 c[1] += pos3d[v][1] 16847 16917 c[2] += pos3d[v][2] 16848 16918 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.") 16850 16920 16851 16921 order = g.order() 16852 16922 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 = { 42 42 'iterations': 'The number of times to execute the spring layout algorithm.', 43 43 'heights': 'A dictionary mapping heights to the list of vertices at this height.', 44 44 '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\'.', 47 47 'save_pos': 'Whether or not to save the computed position for the graph.', 48 48 'dim': 'The dimension of the layout -- 2 or 3.', 49 49 'prog': 'Which graphviz layout program to use -- one of "circo", "dot", "fdp", "neato", or "twopi".',