Schnyder’s algorithm for straight-line planar embeddings#
A module for computing the (x,y) coordinates for a straight-line planar embedding of any connected planar graph with at least three vertices. Uses Walter Schnyder’s Algorithm from [Sch1990].
AUTHORS:
Jonathan Bober, Emily Kirkman (2008-02-09) – initial version
- class sage.graphs.schnyder.TreeNode(parent=None, children=None, label=None)[source]#
Bases:
object
A class to represent each node in the trees used by
_realizer
and_compute_coordinates
when finding a planar geometric embedding in the grid.Each tree node is doubly linked to its parent and children.
INPUT:
parent
– the parent TreeNode ofself
children
– a list of TreeNode children ofself
label
– the associated realizer vertex label
EXAMPLES:
sage: from sage.graphs.schnyder import TreeNode sage: tn = TreeNode(label=5) sage: tn2 = TreeNode(label=2,parent=tn) sage: tn3 = TreeNode(label=3) sage: tn.append_child(tn3) sage: tn.compute_number_of_descendants() 2 sage: tn.number_of_descendants 2 sage: tn3.number_of_descendants 1 sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2
>>> from sage.all import * >>> from sage.graphs.schnyder import TreeNode >>> tn = TreeNode(label=Integer(5)) >>> tn2 = TreeNode(label=Integer(2),parent=tn) >>> tn3 = TreeNode(label=Integer(3)) >>> tn.append_child(tn3) >>> tn.compute_number_of_descendants() 2 >>> tn.number_of_descendants 2 >>> tn3.number_of_descendants 1 >>> tn.compute_depth_of_self_and_children() >>> tn3.depth 2
- append_child(child)[source]#
Add a child to list of children.
EXAMPLES:
sage: from sage.graphs.schnyder import TreeNode sage: tn = TreeNode(label=5) sage: tn2 = TreeNode(label=2,parent=tn) sage: tn3 = TreeNode(label=3) sage: tn.append_child(tn3) sage: tn.compute_number_of_descendants() 2 sage: tn.number_of_descendants 2 sage: tn3.number_of_descendants 1 sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2
>>> from sage.all import * >>> from sage.graphs.schnyder import TreeNode >>> tn = TreeNode(label=Integer(5)) >>> tn2 = TreeNode(label=Integer(2),parent=tn) >>> tn3 = TreeNode(label=Integer(3)) >>> tn.append_child(tn3) >>> tn.compute_number_of_descendants() 2 >>> tn.number_of_descendants 2 >>> tn3.number_of_descendants 1 >>> tn.compute_depth_of_self_and_children() >>> tn3.depth 2
- compute_depth_of_self_and_children()[source]#
Computes the depth of self and all descendants.
For each TreeNode, sets result as attribute self.depth
EXAMPLES:
sage: from sage.graphs.schnyder import TreeNode sage: tn = TreeNode(label=5) sage: tn2 = TreeNode(label=2,parent=tn) sage: tn3 = TreeNode(label=3) sage: tn.append_child(tn3) sage: tn.compute_number_of_descendants() 2 sage: tn.number_of_descendants 2 sage: tn3.number_of_descendants 1 sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2
>>> from sage.all import * >>> from sage.graphs.schnyder import TreeNode >>> tn = TreeNode(label=Integer(5)) >>> tn2 = TreeNode(label=Integer(2),parent=tn) >>> tn3 = TreeNode(label=Integer(3)) >>> tn.append_child(tn3) >>> tn.compute_number_of_descendants() 2 >>> tn.number_of_descendants 2 >>> tn3.number_of_descendants 1 >>> tn.compute_depth_of_self_and_children() >>> tn3.depth 2
- compute_number_of_descendants()[source]#
Computes the number of descendants of self and all descendants.
For each TreeNode, sets result as attribute self.number_of_descendants
EXAMPLES:
sage: from sage.graphs.schnyder import TreeNode sage: tn = TreeNode(label=5) sage: tn2 = TreeNode(label=2,parent=tn) sage: tn3 = TreeNode(label=3) sage: tn.append_child(tn3) sage: tn.compute_number_of_descendants() 2 sage: tn.number_of_descendants 2 sage: tn3.number_of_descendants 1 sage: tn.compute_depth_of_self_and_children() sage: tn3.depth 2
>>> from sage.all import * >>> from sage.graphs.schnyder import TreeNode >>> tn = TreeNode(label=Integer(5)) >>> tn2 = TreeNode(label=Integer(2),parent=tn) >>> tn3 = TreeNode(label=Integer(3)) >>> tn.append_child(tn3) >>> tn.compute_number_of_descendants() 2 >>> tn.number_of_descendants 2 >>> tn3.number_of_descendants 1 >>> tn.compute_depth_of_self_and_children() >>> tn3.depth 2
- sage.graphs.schnyder.minimal_schnyder_wood(graph, root_edge=None, minimal=True, check=True)[source]#
Return the minimal Schnyder wood of a planar rooted triangulation.
INPUT:
graph – a planar triangulation, given by a graph with an embedding.
root_edge – a pair of vertices (default is from
-1
to-2
) The third boundary vertex is then determined using the orientation and will be labelled-3
.minimal – boolean (default
True
), whether to return a minimal or a maximal Schnyder wood.check – boolean (default
True
), whether to check if the input is a planar triangulation
OUTPUT:
A planar graph, with edges oriented and colored. The three outer edges of the initial graph are removed. For the three outer vertices the list of the neighbors stored in the combinatorial embedding is in the order of the incident edges between the two incident (and removed) outer edges, and not a cyclic shift of it.
The algorithm is taken from [Bre2000] (section 4.2).
EXAMPLES:
sage: from sage.graphs.schnyder import minimal_schnyder_wood sage: g = Graph([(0,-1),(0,-2),(0,-3),(-1,-2),(-2,-3), ....: (-3,-1)], format='list_of_edges') sage: g.set_embedding({-1:[-2,0,-3],-2:[-3,0,-1], ....: -3:[-1,0,-2],0:[-1,-2,-3]}) sage: newg = minimal_schnyder_wood(g) sage: newg.edges(sort=True) [(0, -3, 'red'), (0, -2, 'blue'), (0, -1, 'green')] sage: newg.plot(color_by_label={'red':'red','blue':'blue', # needs sage.plot ....: 'green':'green',None:'black'}) Graphics object consisting of 8 graphics primitives
>>> from sage.all import * >>> from sage.graphs.schnyder import minimal_schnyder_wood >>> g = Graph([(Integer(0),-Integer(1)),(Integer(0),-Integer(2)),(Integer(0),-Integer(3)),(-Integer(1),-Integer(2)),(-Integer(2),-Integer(3)), ... (-Integer(3),-Integer(1))], format='list_of_edges') >>> g.set_embedding({-Integer(1):[-Integer(2),Integer(0),-Integer(3)],-Integer(2):[-Integer(3),Integer(0),-Integer(1)], ... -Integer(3):[-Integer(1),Integer(0),-Integer(2)],Integer(0):[-Integer(1),-Integer(2),-Integer(3)]}) >>> newg = minimal_schnyder_wood(g) >>> newg.edges(sort=True) [(0, -3, 'red'), (0, -2, 'blue'), (0, -1, 'green')] >>> newg.plot(color_by_label={'red':'red','blue':'blue', # needs sage.plot ... 'green':'green',None:'black'}) Graphics object consisting of 8 graphics primitives
A larger example:
sage: g = Graph([(0,-1),(0,2),(0,1),(0,-3),(-1,-3),(-1,2), ....: (-1,-2),(1,2),(1,-3),(2,-2),(1,-2),(-2,-3)], format='list_of_edges') sage: g.set_embedding({-1:[-2,2,0,-3],-2:[-3,1,2,-1], ....: -3:[-1,0,1,-2],0:[-1,2,1,-3],1:[-2,-3,0,2],2:[-1,-2,1,0]}) sage: newg = minimal_schnyder_wood(g) sage: newg.edges(sort=True, key=lambda e:(str(e[0]),str(e[1]))) [(0, -1, 'green'), (0, -3, 'red'), (0, 2, 'blue'), (1, -2, 'blue'), (1, -3, 'red'), (1, 0, 'green'), (2, -1, 'green'), (2, -2, 'blue'), (2, 1, 'red')] sage: newg2 = minimal_schnyder_wood(g, minimal=False) sage: newg2.edges(sort=True, key=lambda e:(str(e[0]),str(e[1]))) [(0, -1, 'green'), (0, -3, 'red'), (0, 1, 'blue'), (1, -2, 'blue'), (1, -3, 'red'), (1, 2, 'green'), (2, -1, 'green'), (2, -2, 'blue'), (2, 0, 'red')]
>>> from sage.all import * >>> g = Graph([(Integer(0),-Integer(1)),(Integer(0),Integer(2)),(Integer(0),Integer(1)),(Integer(0),-Integer(3)),(-Integer(1),-Integer(3)),(-Integer(1),Integer(2)), ... (-Integer(1),-Integer(2)),(Integer(1),Integer(2)),(Integer(1),-Integer(3)),(Integer(2),-Integer(2)),(Integer(1),-Integer(2)),(-Integer(2),-Integer(3))], format='list_of_edges') >>> g.set_embedding({-Integer(1):[-Integer(2),Integer(2),Integer(0),-Integer(3)],-Integer(2):[-Integer(3),Integer(1),Integer(2),-Integer(1)], ... -Integer(3):[-Integer(1),Integer(0),Integer(1),-Integer(2)],Integer(0):[-Integer(1),Integer(2),Integer(1),-Integer(3)],Integer(1):[-Integer(2),-Integer(3),Integer(0),Integer(2)],Integer(2):[-Integer(1),-Integer(2),Integer(1),Integer(0)]}) >>> newg = minimal_schnyder_wood(g) >>> newg.edges(sort=True, key=lambda e:(str(e[Integer(0)]),str(e[Integer(1)]))) [(0, -1, 'green'), (0, -3, 'red'), (0, 2, 'blue'), (1, -2, 'blue'), (1, -3, 'red'), (1, 0, 'green'), (2, -1, 'green'), (2, -2, 'blue'), (2, 1, 'red')] >>> newg2 = minimal_schnyder_wood(g, minimal=False) >>> newg2.edges(sort=True, key=lambda e:(str(e[Integer(0)]),str(e[Integer(1)]))) [(0, -1, 'green'), (0, -3, 'red'), (0, 1, 'blue'), (1, -2, 'blue'), (1, -3, 'red'), (1, 2, 'green'), (2, -1, 'green'), (2, -2, 'blue'), (2, 0, 'red')]