Base class for polyhedra: Graph-theoretic methods¶
Define methods relying on sage.graphs
.
- class sage.geometry.polyhedron.base4.Polyhedron_base4(parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, mutable=False, **kwds)[source]¶
Bases:
Polyhedron_base3
Methods relying on
sage.graphs
.See
sage.geometry.polyhedron.base.Polyhedron_base
.- combinatorial_automorphism_group(vertex_graph_only=False)[source]¶
Compute the combinatorial automorphism group.
If
vertex_graph_only
isTrue
, the automorphism group of the vertex-edge graph of the polyhedron is returned. Otherwise the automorphism group of the vertex-facet graph, which is isomorphic to the automorphism group of the face lattice is returned.INPUT:
vertex_graph_only
– boolean (default:False
); whether to return the automorphism group of the vertex edges graph or of the lattice
OUTPUT:
A
PermutationGroup
that is isomorphic to the combinatorial automorphism group is returned.if
vertex_graph_only
isTrue
: The automorphism group of the vertex-edge graph of the polyhedronif
vertex_graph_only
isFalse
(default): The automorphism group of the vertex-facet graph of the polyhedron, seevertex_facet_graph()
. This group is isomorphic to the automorphism group of the face lattice of the polyhedron.
NOTE:
Depending on
vertex_graph_only
, this method returns groups that are not necessarily isomorphic, see the examples below.EXAMPLES:
sage: quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)]) sage: quadrangle.combinatorial_automorphism_group().is_isomorphic( # needs sage.groups ....: groups.permutation.Dihedral(4)) True sage: quadrangle.restricted_automorphism_group() # needs sage.groups Permutation Group with generators [()]
>>> from sage.all import * >>> quadrangle = Polyhedron(vertices=[(Integer(0),Integer(0)),(Integer(1),Integer(0)),(Integer(0),Integer(1)),(Integer(2),Integer(3))]) >>> quadrangle.combinatorial_automorphism_group().is_isomorphic( # needs sage.groups ... groups.permutation.Dihedral(Integer(4))) True >>> quadrangle.restricted_automorphism_group() # needs sage.groups Permutation Group with generators [()]
quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)]) quadrangle.combinatorial_automorphism_group().is_isomorphic( # needs sage.groups groups.permutation.Dihedral(4)) quadrangle.restricted_automorphism_group() # needs sage.groups
Permutations of the vertex graph only exchange vertices with vertices:
sage: P = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)]) sage: P.combinatorial_automorphism_group(vertex_graph_only=True) # needs sage.groups Permutation Group with generators [(A vertex at (1,0),A vertex at (1,1))]
>>> from sage.all import * >>> P = Polyhedron(vertices=[(Integer(1),Integer(0)), (Integer(1),Integer(1))], rays=[(Integer(1),Integer(0))]) >>> P.combinatorial_automorphism_group(vertex_graph_only=True) # needs sage.groups Permutation Group with generators [(A vertex at (1,0),A vertex at (1,1))]
P = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)]) P.combinatorial_automorphism_group(vertex_graph_only=True) # needs sage.groups
This shows an example of two polytopes whose vertex-edge graphs are isomorphic, but their face lattices are not isomorphic:
sage: # needs sage.groups sage: Q = Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773], ....: [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817], ....: [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199], ....: [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097], ....: [-19184/902877, 26136/300959, -21472/902877, 899005/902877], ....: [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933], ....: [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433], ....: [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]]) sage: C = polytopes.cyclic_polytope(4,8) sage: C.is_combinatorially_isomorphic(Q) False sage: C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic( ....: Q.combinatorial_automorphism_group(vertex_graph_only=True)) True sage: C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic( ....: Q.combinatorial_automorphism_group(vertex_graph_only=False)) False
>>> from sage.all import * >>> # needs sage.groups >>> Q = Polyhedron([[-Integer(123984206864)/Integer(2768850730773), -Integer(101701330976)/Integer(922950243591), -Integer(64154618668)/Integer(2768850730773), -Integer(2748446474675)/Integer(2768850730773)], ... [-Integer(11083969050)/Integer(98314591817), -Integer(4717557075)/Integer(98314591817), -Integer(32618537490)/Integer(98314591817), -Integer(91960210208)/Integer(98314591817)], ... [-Integer(9690950)/Integer(554883199), -Integer(73651220)/Integer(554883199), Integer(1823050)/Integer(554883199), -Integer(549885101)/Integer(554883199)], ... [-Integer(5174928)/Integer(72012097), Integer(5436288)/Integer(72012097), -Integer(37977984)/Integer(72012097), Integer(60721345)/Integer(72012097)], ... [-Integer(19184)/Integer(902877), Integer(26136)/Integer(300959), -Integer(21472)/Integer(902877), Integer(899005)/Integer(902877)], ... [Integer(53511524)/Integer(1167061933), Integer(88410344)/Integer(1167061933), Integer(621795064)/Integer(1167061933), Integer(982203941)/Integer(1167061933)], ... [Integer(4674489456)/Integer(83665171433), -Integer(4026061312)/Integer(83665171433), Integer(28596876672)/Integer(83665171433), -Integer(78383796375)/Integer(83665171433)], ... [Integer(857794884940)/Integer(98972360190089), -Integer(10910202223200)/Integer(98972360190089), Integer(2974263671400)/Integer(98972360190089), -Integer(98320463346111)/Integer(98972360190089)]]) >>> C = polytopes.cyclic_polytope(Integer(4),Integer(8)) >>> C.is_combinatorially_isomorphic(Q) False >>> C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic( ... Q.combinatorial_automorphism_group(vertex_graph_only=True)) True >>> C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic( ... Q.combinatorial_automorphism_group(vertex_graph_only=False)) False
# needs sage.groups Q = Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773], [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817], [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199], [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097], [-19184/902877, 26136/300959, -21472/902877, 899005/902877], [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933], [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433], [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]]) C = polytopes.cyclic_polytope(4,8) C.is_combinatorially_isomorphic(Q) C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic( Q.combinatorial_automorphism_group(vertex_graph_only=True)) C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic( Q.combinatorial_automorphism_group(vertex_graph_only=False))
The automorphism group of the face lattice is isomorphic to the combinatorial automorphism group:
sage: # needs sage.groups sage: CG = C.hasse_diagram().automorphism_group() sage: C.combinatorial_automorphism_group().is_isomorphic(CG) True sage: QG = Q.hasse_diagram().automorphism_group() sage: Q.combinatorial_automorphism_group().is_isomorphic(QG) True
>>> from sage.all import * >>> # needs sage.groups >>> CG = C.hasse_diagram().automorphism_group() >>> C.combinatorial_automorphism_group().is_isomorphic(CG) True >>> QG = Q.hasse_diagram().automorphism_group() >>> Q.combinatorial_automorphism_group().is_isomorphic(QG) True
# needs sage.groups CG = C.hasse_diagram().automorphism_group() C.combinatorial_automorphism_group().is_isomorphic(CG) QG = Q.hasse_diagram().automorphism_group() Q.combinatorial_automorphism_group().is_isomorphic(QG)
- face_lattice()[source]¶
Return the face-lattice poset.
OUTPUT:
A
FinitePoset
. Elements are given asPolyhedronFace
.In the case of a full-dimensional polytope, the faces are pairs (vertices, inequalities) of the spanning vertices and corresponding saturated inequalities. In general, a face is defined by a pair (V-rep. objects, H-rep. objects). The V-representation objects span the face, and the corresponding H-representation objects are those inequalities and equations that are saturated on the face.
The bottom-most element of the face lattice is the “empty face”. It contains no V-representation object. All H-representation objects are incident.
The top-most element is the “full face”. It is spanned by all V-representation objects. The incident H-representation objects are all equations and no inequalities.
In the case of a full-dimensional polytope, the “empty face” and the “full face” are the empty set (no vertices, all inequalities) and the full polytope (all vertices, no inequalities), respectively.
ALGORITHM:
See
sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator
.Note
The face lattice is not cached, as long as this creates a memory leak, see Issue #28982.
EXAMPLES:
sage: square = polytopes.hypercube(2) sage: fl = square.face_lattice();fl Finite lattice containing 10 elements sage: list(f.ambient_V_indices() for f in fl) [(), (0,), (1,), (0, 1), (2,), (1, 2), (3,), (0, 3), (2, 3), (0, 1, 2, 3)] sage: poset_element = fl[5] sage: a_face = poset_element sage: a_face A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: a_face.ambient_V_indices() (1, 2) sage: set(a_face.ambient_Vrepresentation()) == ....: set([square.Vrepresentation(1), square.Vrepresentation(2)]) True sage: a_face.ambient_Vrepresentation() (A vertex at (1, 1), A vertex at (-1, 1)) sage: a_face.ambient_Hrepresentation() (An inequality (0, -1) x + 1 >= 0,)
>>> from sage.all import * >>> square = polytopes.hypercube(Integer(2)) >>> fl = square.face_lattice();fl Finite lattice containing 10 elements >>> list(f.ambient_V_indices() for f in fl) [(), (0,), (1,), (0, 1), (2,), (1, 2), (3,), (0, 3), (2, 3), (0, 1, 2, 3)] >>> poset_element = fl[Integer(5)] >>> a_face = poset_element >>> a_face A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 2 vertices >>> a_face.ambient_V_indices() (1, 2) >>> set(a_face.ambient_Vrepresentation()) == Ellipsis.: set([square.Vrepresentation(Integer(1)), square.Vrepresentation(Integer(2))]) True >>> a_face.ambient_Vrepresentation() (A vertex at (1, 1), A vertex at (-1, 1)) >>> a_face.ambient_Hrepresentation() (An inequality (0, -1) x + 1 >= 0,)
square = polytopes.hypercube(2) fl = square.face_lattice();fl list(f.ambient_V_indices() for f in fl) poset_element = fl[5] a_face = poset_element a_face a_face.ambient_V_indices() set(a_face.ambient_Vrepresentation()) == ....: set([square.Vrepresentation(1), square.Vrepresentation(2)]) a_face.ambient_Vrepresentation() a_face.ambient_Hrepresentation()
A more complicated example:
sage: c5_10 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5] for i in range(1,11)]) sage: c5_10_fl = c5_10.face_lattice() sage: [len(x) for x in c5_10_fl.level_sets()] [1, 10, 45, 100, 105, 42, 1]
>>> from sage.all import * >>> c5_10 = Polyhedron(vertices = [[i,i**Integer(2),i**Integer(3),i**Integer(4),i**Integer(5)] for i in range(Integer(1),Integer(11))]) >>> c5_10_fl = c5_10.face_lattice() >>> [len(x) for x in c5_10_fl.level_sets()] [1, 10, 45, 100, 105, 42, 1]
c5_10 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5] for i in range(1,11)]) c5_10_fl = c5_10.face_lattice() [len(x) for x in c5_10_fl.level_sets()]
Note that if the polyhedron contains lines then there is a dimension gap between the empty face and the first non-empty face in the face lattice:
sage: line = Polyhedron(vertices=[(0,)], lines=[(1,)]) sage: [ fl.dim() for fl in line.face_lattice() ] [-1, 1]
>>> from sage.all import * >>> line = Polyhedron(vertices=[(Integer(0),)], lines=[(Integer(1),)]) >>> [ fl.dim() for fl in line.face_lattice() ] [-1, 1]
line = Polyhedron(vertices=[(0,)], lines=[(1,)]) [ fl.dim() for fl in line.face_lattice() ]
- flag_f_vector(*args)[source]¶
Return the flag f-vector.
For each \(-1 < i_0 < \dots < i_n < d\) the flag f-vector counts the number of flags \(F_0 \subset \dots \subset F_n\) with \(F_j\) of dimension \(i_j\) for each \(0 \leq j \leq n\), where \(d\) is the dimension of the polyhedron.
INPUT:
args
– integer (optional); specify an entry of the flag-f-vector; must be an increasing sequence of integers
OUTPUT: a dictionary, if no arguments were given
an Integer, if arguments were given
EXAMPLES:
Obtain the entire flag-f-vector:
sage: P = polytopes.twenty_four_cell() sage: P.flag_f_vector() {(-1,): 1, (0,): 24, (0, 1): 192, (0, 1, 2): 576, (0, 1, 2, 3): 1152, (0, 1, 3): 576, (0, 2): 288, (0, 2, 3): 576, (0, 3): 144, (1,): 96, (1, 2): 288, (1, 2, 3): 576, (1, 3): 288, (2,): 96, (2, 3): 192, (3,): 24, (4,): 1}
>>> from sage.all import * >>> P = polytopes.twenty_four_cell() >>> P.flag_f_vector() {(-1,): 1, (0,): 24, (0, 1): 192, (0, 1, 2): 576, (0, 1, 2, 3): 1152, (0, 1, 3): 576, (0, 2): 288, (0, 2, 3): 576, (0, 3): 144, (1,): 96, (1, 2): 288, (1, 2, 3): 576, (1, 3): 288, (2,): 96, (2, 3): 192, (3,): 24, (4,): 1}
P = polytopes.twenty_four_cell() P.flag_f_vector()
Specify an entry:
sage: P.flag_f_vector(0,3) 144 sage: P.flag_f_vector(2) 96
>>> from sage.all import * >>> P.flag_f_vector(Integer(0),Integer(3)) 144 >>> P.flag_f_vector(Integer(2)) 96
P.flag_f_vector(0,3) P.flag_f_vector(2)
Leading
-1
and trailing entry of dimension are allowed:sage: P.flag_f_vector(-1,0,3) 144 sage: P.flag_f_vector(-1,0,3,4) 144
>>> from sage.all import * >>> P.flag_f_vector(-Integer(1),Integer(0),Integer(3)) 144 >>> P.flag_f_vector(-Integer(1),Integer(0),Integer(3),Integer(4)) 144
P.flag_f_vector(-1,0,3) P.flag_f_vector(-1,0,3,4)
One can get the number of trivial faces:
sage: P.flag_f_vector(-1) 1 sage: P.flag_f_vector(4) 1
>>> from sage.all import * >>> P.flag_f_vector(-Integer(1)) 1 >>> P.flag_f_vector(Integer(4)) 1
P.flag_f_vector(-1) P.flag_f_vector(4)
Polyhedra with lines, have
0
entries accordingly:sage: P = (Polyhedron(lines=[[1]]) * polytopes.cross_polytope(3)) sage: P.flag_f_vector() {(-1,): 1, (0, 1): 0, (0, 1, 2): 0, (0, 1, 3): 0, (0, 2): 0, (0, 2, 3): 0, (0, 3): 0, (0,): 0, (1, 2): 24, (1, 2, 3): 48, (1, 3): 24, (1,): 6, (2, 3): 24, (2,): 12, (3,): 8, 4: 1}
>>> from sage.all import * >>> P = (Polyhedron(lines=[[Integer(1)]]) * polytopes.cross_polytope(Integer(3))) >>> P.flag_f_vector() {(-1,): 1, (0, 1): 0, (0, 1, 2): 0, (0, 1, 3): 0, (0, 2): 0, (0, 2, 3): 0, (0, 3): 0, (0,): 0, (1, 2): 24, (1, 2, 3): 48, (1, 3): 24, (1,): 6, (2, 3): 24, (2,): 12, (3,): 8, 4: 1}
P = (Polyhedron(lines=[[1]]) * polytopes.cross_polytope(3)) P.flag_f_vector()
If the arguments are not stricly increasing or out of range, a key error is raised:
sage: P.flag_f_vector(-1,0,3,6) Traceback (most recent call last): ... KeyError: (0, 3, 6) sage: P.flag_f_vector(-1,3,0) Traceback (most recent call last): ... KeyError: (3, 0)
>>> from sage.all import * >>> P.flag_f_vector(-Integer(1),Integer(0),Integer(3),Integer(6)) Traceback (most recent call last): ... KeyError: (0, 3, 6) >>> P.flag_f_vector(-Integer(1),Integer(3),Integer(0)) Traceback (most recent call last): ... KeyError: (3, 0)
P.flag_f_vector(-1,0,3,6) P.flag_f_vector(-1,3,0)
- graph(**kwds)[source]¶
Return a graph in which the vertices correspond to vertices of the polyhedron, and edges to edges.
INPUT:
names
– boolean (default:True
); ifFalse
, then the nodes of the graph are labeld by the indices of the Vrepresentationalgorithm
– string (optional); specify whether the face generator starts with facets or vertices:'primal'
– start with the facets'dual'
– start with the verticesNone
– choose automatically
Note
The graph of a polyhedron with lines has no vertices, as the polyhedron has no vertices (\(0\)-faces).
The method
vertices()
returns the defining points in this case.EXAMPLES:
sage: g3 = polytopes.hypercube(3).vertex_graph(); g3 Graph on 8 vertices sage: g3.automorphism_group().cardinality() # needs sage.groups 48 sage: s4 = polytopes.simplex(4).vertex_graph(); s4 Graph on 5 vertices sage: s4.is_eulerian() True
>>> from sage.all import * >>> g3 = polytopes.hypercube(Integer(3)).vertex_graph(); g3 Graph on 8 vertices >>> g3.automorphism_group().cardinality() # needs sage.groups 48 >>> s4 = polytopes.simplex(Integer(4)).vertex_graph(); s4 Graph on 5 vertices >>> s4.is_eulerian() True
g3 = polytopes.hypercube(3).vertex_graph(); g3 g3.automorphism_group().cardinality() # needs sage.groups s4 = polytopes.simplex(4).vertex_graph(); s4 s4.is_eulerian()
The graph of an unbounded polyhedron is the graph of the bounded complex:
sage: open_triangle = Polyhedron(vertices=[[1,0], [0,1]], ....: rays =[[1,1]]) sage: open_triangle.vertex_graph() Graph on 2 vertices
>>> from sage.all import * >>> open_triangle = Polyhedron(vertices=[[Integer(1),Integer(0)], [Integer(0),Integer(1)]], ... rays =[[Integer(1),Integer(1)]]) >>> open_triangle.vertex_graph() Graph on 2 vertices
open_triangle = Polyhedron(vertices=[[1,0], [0,1]], rays =[[1,1]]) open_triangle.vertex_graph()
The graph of a polyhedron with lines has no vertices:
sage: line = Polyhedron(lines=[[0,1]]) sage: line.vertex_graph() Graph on 0 vertices
>>> from sage.all import * >>> line = Polyhedron(lines=[[Integer(0),Integer(1)]]) >>> line.vertex_graph() Graph on 0 vertices
line = Polyhedron(lines=[[0,1]]) line.vertex_graph()
- hasse_diagram()[source]¶
Return the Hasse diagram of the face lattice of
self
.This is the Hasse diagram of the poset of the faces of
self
.OUTPUT: a directed graph
EXAMPLES:
sage: # needs sage.rings.number_field sage: P = polytopes.regular_polygon(4).pyramid() sage: D = P.hasse_diagram(); D Digraph on 20 vertices sage: D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
>>> from sage.all import * >>> # needs sage.rings.number_field >>> P = polytopes.regular_polygon(Integer(4)).pyramid() >>> D = P.hasse_diagram(); D Digraph on 20 vertices >>> D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
# needs sage.rings.number_field P = polytopes.regular_polygon(4).pyramid() D = P.hasse_diagram(); D D.degree_polynomial()
Faces of a mutable polyhedron are not hashable. Hence those are not suitable as vertices of the hasse diagram. Use the combinatorial polyhedron instead:
sage: # needs sage.rings.number_field sage: P = polytopes.regular_polygon(4).pyramid() sage: parent = P.parent() sage: parent = parent.change_ring(QQ, backend='ppl') sage: Q = parent._element_constructor_(P, mutable=True) sage: Q.hasse_diagram() Traceback (most recent call last): ... TypeError: mutable polyhedra are unhashable sage: C = Q.combinatorial_polyhedron() sage: D = C.hasse_diagram() sage: set(D.vertices(sort=False)) == set(range(20)) True sage: def index_to_combinatorial_face(n): ....: return C.face_by_face_lattice_index(n) sage: D.relabel(index_to_combinatorial_face, inplace=True) sage: D.vertices(sort=True) [A -1-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 3-dimensional face of a 3-dimensional combinatorial polyhedron] sage: D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
>>> from sage.all import * >>> # needs sage.rings.number_field >>> P = polytopes.regular_polygon(Integer(4)).pyramid() >>> parent = P.parent() >>> parent = parent.change_ring(QQ, backend='ppl') >>> Q = parent._element_constructor_(P, mutable=True) >>> Q.hasse_diagram() Traceback (most recent call last): ... TypeError: mutable polyhedra are unhashable >>> C = Q.combinatorial_polyhedron() >>> D = C.hasse_diagram() >>> set(D.vertices(sort=False)) == set(range(Integer(20))) True >>> def index_to_combinatorial_face(n): ... return C.face_by_face_lattice_index(n) >>> D.relabel(index_to_combinatorial_face, inplace=True) >>> D.vertices(sort=True) [A -1-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 1-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 3-dimensional face of a 3-dimensional combinatorial polyhedron] >>> D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
# needs sage.rings.number_field P = polytopes.regular_polygon(4).pyramid() parent = P.parent() parent = parent.change_ring(QQ, backend='ppl') Q = parent._element_constructor_(P, mutable=True) Q.hasse_diagram() C = Q.combinatorial_polyhedron() D = C.hasse_diagram() set(D.vertices(sort=False)) == set(range(20)) def index_to_combinatorial_face(n): return C.face_by_face_lattice_index(n) D.relabel(index_to_combinatorial_face, inplace=True) D.vertices(sort=True) D.degree_polynomial()
- is_combinatorially_isomorphic(other, algorithm='bipartite_graph')[source]¶
Return whether the polyhedron is combinatorially isomorphic to another polyhedron.
We only consider bounded polyhedra. By definition, they are combinatorially isomorphic if their face lattices are isomorphic.
INPUT:
other
– a polyhedron objectalgorithm
– (default:'bipartite_graph'
) the algorithm to use; the other possible value is'face_lattice'
OUTPUT:
True
if the two polyhedra are combinatorially isomorphicFalse
otherwise
REFERENCES:
For the equivalence of the two algorithms see [KK1995], p. 877-878
EXAMPLES:
The square is combinatorially isomorphic to the 2-dimensional cube:
sage: polytopes.hypercube(2).is_combinatorially_isomorphic(polytopes.regular_polygon(4)) True
>>> from sage.all import * >>> polytopes.hypercube(Integer(2)).is_combinatorially_isomorphic(polytopes.regular_polygon(Integer(4))) True
polytopes.hypercube(2).is_combinatorially_isomorphic(polytopes.regular_polygon(4))
All the faces of the 3-dimensional permutahedron are either combinatorially isomorphic to a square or a hexagon:
sage: H = polytopes.regular_polygon(6) # needs sage.rings.number_field sage: S = polytopes.hypercube(2) sage: P = polytopes.permutahedron(4) sage: all(F.as_polyhedron().is_combinatorially_isomorphic(S) # needs sage.rings.number_field ....: or F.as_polyhedron().is_combinatorially_isomorphic(H) ....: for F in P.faces(2)) True
>>> from sage.all import * >>> H = polytopes.regular_polygon(Integer(6)) # needs sage.rings.number_field >>> S = polytopes.hypercube(Integer(2)) >>> P = polytopes.permutahedron(Integer(4)) >>> all(F.as_polyhedron().is_combinatorially_isomorphic(S) # needs sage.rings.number_field ... or F.as_polyhedron().is_combinatorially_isomorphic(H) ... for F in P.faces(Integer(2))) True
H = polytopes.regular_polygon(6) # needs sage.rings.number_field S = polytopes.hypercube(2) P = polytopes.permutahedron(4) all(F.as_polyhedron().is_combinatorially_isomorphic(S) # needs sage.rings.number_field or F.as_polyhedron().is_combinatorially_isomorphic(H) for F in P.faces(2))
Checking that a regular simplex intersected with its reflection through the origin is combinatorially isomorphic to the intersection of a cube with a hyperplane perpendicular to its long diagonal:
sage: def simplex_intersection(k): ....: S1 = Polyhedron([vector(v)-vector(polytopes.simplex(k).center()) for v in polytopes.simplex(k).vertices_list()]) ....: S2 = Polyhedron([-vector(v) for v in S1.vertices_list()]) ....: return S1.intersection(S2) sage: def cube_intersection(k): ....: C = polytopes.hypercube(k+1) ....: H = Polyhedron(eqns=[[0]+[1 for i in range(k+1)]]) ....: return C.intersection(H) sage: [simplex_intersection(k).is_combinatorially_isomorphic(cube_intersection(k)) for k in range(2,5)] [True, True, True] sage: simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # needs sage.rings.number_field True sage: simplex_intersection(3).is_combinatorially_isomorphic(polytopes.octahedron()) True
>>> from sage.all import * >>> def simplex_intersection(k): ... S1 = Polyhedron([vector(v)-vector(polytopes.simplex(k).center()) for v in polytopes.simplex(k).vertices_list()]) ... S2 = Polyhedron([-vector(v) for v in S1.vertices_list()]) ... return S1.intersection(S2) >>> def cube_intersection(k): ... C = polytopes.hypercube(k+Integer(1)) ... H = Polyhedron(eqns=[[Integer(0)]+[Integer(1) for i in range(k+Integer(1))]]) ... return C.intersection(H) >>> [simplex_intersection(k).is_combinatorially_isomorphic(cube_intersection(k)) for k in range(Integer(2),Integer(5))] [True, True, True] >>> simplex_intersection(Integer(2)).is_combinatorially_isomorphic(polytopes.regular_polygon(Integer(6))) # needs sage.rings.number_field True >>> simplex_intersection(Integer(3)).is_combinatorially_isomorphic(polytopes.octahedron()) True
def simplex_intersection(k): S1 = Polyhedron([vector(v)-vector(polytopes.simplex(k).center()) for v in polytopes.simplex(k).vertices_list()]) S2 = Polyhedron([-vector(v) for v in S1.vertices_list()]) return S1.intersection(S2) def cube_intersection(k): C = polytopes.hypercube(k+1) H = Polyhedron(eqns=[[0]+[1 for i in range(k+1)]]) return C.intersection(H) [simplex_intersection(k).is_combinatorially_isomorphic(cube_intersection(k)) for k in range(2,5)] simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # needs sage.rings.number_field simplex_intersection(3).is_combinatorially_isomorphic(polytopes.octahedron())
Two polytopes with the same \(f\)-vector, but different combinatorial types:
sage: P = Polyhedron([[-605520/1525633, -605520/1525633, -1261500/1525633, -52200/1525633, 11833/1525633],\ [-720/1769, -600/1769, 1500/1769, 0, -31/1769], [-216/749, 240/749, -240/749, -432/749, 461/749], \ [-50/181, 50/181, 60/181, -100/181, -119/181], [-32/51, -16/51, -4/51, 12/17, 1/17],\ [1, 0, 0, 0, 0], [16/129, 128/129, 0, 0, 1/129], [64/267, -128/267, 24/89, -128/267, 57/89],\ [1200/3953, -1200/3953, -1440/3953, -360/3953, -3247/3953], [1512/5597, 1512/5597, 588/5597, 4704/5597, 2069/5597]]) sage: C = polytopes.cyclic_polytope(5,10) sage: C.f_vector() == P.f_vector(); C.f_vector() True (1, 10, 45, 100, 105, 42, 1) sage: C.is_combinatorially_isomorphic(P) False sage: S = polytopes.simplex(3) sage: S = S.face_truncation(S.faces(0)[3]) sage: S = S.face_truncation(S.faces(0)[4]) sage: S = S.face_truncation(S.faces(0)[5]) sage: T = polytopes.simplex(3) sage: T = T.face_truncation(T.faces(0)[3]) sage: T = T.face_truncation(T.faces(0)[4]) sage: T = T.face_truncation(T.faces(0)[4]) sage: T.is_combinatorially_isomorphic(S) False sage: T.f_vector(), S.f_vector() ((1, 10, 15, 7, 1), (1, 10, 15, 7, 1)) sage: C = polytopes.hypercube(5) sage: C.is_combinatorially_isomorphic(C) True sage: C.is_combinatorially_isomorphic(C, algorithm='magic') Traceback (most recent call last): ... AssertionError: `algorithm` must be 'bipartite graph' or 'face_lattice' sage: G = Graph() sage: C.is_combinatorially_isomorphic(G) Traceback (most recent call last): ... AssertionError: input `other` must be a polyhedron sage: H = Polyhedron(eqns=[[0,1,1,1,1]]); H A 3-dimensional polyhedron in QQ^4 defined as the convex hull of 1 vertex and 3 lines sage: C.is_combinatorially_isomorphic(H) Traceback (most recent call last): ... AssertionError: polyhedron `other` must be bounded
>>> from sage.all import * >>> P = Polyhedron([[-Integer(605520)/Integer(1525633), -Integer(605520)/Integer(1525633), -Integer(1261500)/Integer(1525633), -Integer(52200)/Integer(1525633), Integer(11833)/Integer(1525633)],\ [-720/1769, -600/1769, 1500/1769, 0, -31/1769], [-216/749, 240/749, -240/749, -432/749, 461/749], \ [-50/181, 50/181, 60/181, -100/181, -119/181], [-32/51, -16/51, -4/51, 12/17, 1/17],\ [1, 0, 0, 0, 0], [16/129, 128/129, 0, 0, 1/129], [64/267, -128/267, 24/89, -128/267, 57/89],\ [1200/3953, -1200/3953, -1440/3953, -360/3953, -3247/3953], [1512/5597, 1512/5597, 588/5597, 4704/5597, 2069/5597]]) >>> C = polytopes.cyclic_polytope(Integer(5),Integer(10)) >>> C.f_vector() == P.f_vector(); C.f_vector() True (1, 10, 45, 100, 105, 42, 1) >>> C.is_combinatorially_isomorphic(P) False >>> S = polytopes.simplex(Integer(3)) >>> S = S.face_truncation(S.faces(Integer(0))[Integer(3)]) >>> S = S.face_truncation(S.faces(Integer(0))[Integer(4)]) >>> S = S.face_truncation(S.faces(Integer(0))[Integer(5)]) >>> T = polytopes.simplex(Integer(3)) >>> T = T.face_truncation(T.faces(Integer(0))[Integer(3)]) >>> T = T.face_truncation(T.faces(Integer(0))[Integer(4)]) >>> T = T.face_truncation(T.faces(Integer(0))[Integer(4)]) >>> T.is_combinatorially_isomorphic(S) False >>> T.f_vector(), S.f_vector() ((1, 10, 15, 7, 1), (1, 10, 15, 7, 1)) >>> C = polytopes.hypercube(Integer(5)) >>> C.is_combinatorially_isomorphic(C) True >>> C.is_combinatorially_isomorphic(C, algorithm='magic') Traceback (most recent call last): ... AssertionError: `algorithm` must be 'bipartite graph' or 'face_lattice' >>> G = Graph() >>> C.is_combinatorially_isomorphic(G) Traceback (most recent call last): ... AssertionError: input `other` must be a polyhedron >>> H = Polyhedron(eqns=[[Integer(0),Integer(1),Integer(1),Integer(1),Integer(1)]]); H A 3-dimensional polyhedron in QQ^4 defined as the convex hull of 1 vertex and 3 lines >>> C.is_combinatorially_isomorphic(H) Traceback (most recent call last): ... AssertionError: polyhedron `other` must be bounded
P = Polyhedron([[-605520/1525633, -605520/1525633, -1261500/1525633, -52200/1525633, 11833/1525633],\ C = polytopes.cyclic_polytope(5,10) C.f_vector() == P.f_vector(); C.f_vector() C.is_combinatorially_isomorphic(P) S = polytopes.simplex(3) S = S.face_truncation(S.faces(0)[3]) S = S.face_truncation(S.faces(0)[4]) S = S.face_truncation(S.faces(0)[5]) T = polytopes.simplex(3) T = T.face_truncation(T.faces(0)[3]) T = T.face_truncation(T.faces(0)[4]) T = T.face_truncation(T.faces(0)[4]) T.is_combinatorially_isomorphic(S) T.f_vector(), S.f_vector() C = polytopes.hypercube(5) C.is_combinatorially_isomorphic(C) C.is_combinatorially_isomorphic(C, algorithm='magic') G = Graph() C.is_combinatorially_isomorphic(G) H = Polyhedron(eqns=[[0,1,1,1,1]]); H C.is_combinatorially_isomorphic(H)
- is_self_dual()[source]¶
Return whether the polytope is self-dual.
A polytope is self-dual if its face lattice is isomorphic to the face lattice of its dual polytope.
EXAMPLES:
sage: polytopes.simplex().is_self_dual() True sage: polytopes.twenty_four_cell().is_self_dual() True sage: polytopes.cube().is_self_dual() False sage: polytopes.hypersimplex(5,2).is_self_dual() # needs sage.combinat False sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]).is_self_dual() Traceback (most recent call last): ... ValueError: polyhedron has to be compact
>>> from sage.all import * >>> polytopes.simplex().is_self_dual() True >>> polytopes.twenty_four_cell().is_self_dual() True >>> polytopes.cube().is_self_dual() False >>> polytopes.hypersimplex(Integer(5),Integer(2)).is_self_dual() # needs sage.combinat False >>> P = Polyhedron(vertices=[[Integer(1)/Integer(2), Integer(1)/Integer(3)]], rays=[[Integer(1), Integer(1)]]).is_self_dual() Traceback (most recent call last): ... ValueError: polyhedron has to be compact
polytopes.simplex().is_self_dual() polytopes.twenty_four_cell().is_self_dual() polytopes.cube().is_self_dual() polytopes.hypersimplex(5,2).is_self_dual() # needs sage.combinat P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]).is_self_dual()
- restricted_automorphism_group(output='abstract')[source]¶
Return the restricted automorphism group.
First, let the linear automorphism group be the subgroup of the affine group \(AGL(d,\RR) = GL(d,\RR) \ltimes \RR^d\) preserving the \(d\)-dimensional polyhedron. The affine group acts in the usual way \(\vec{x}\mapsto A\vec{x}+b\) on the ambient space.
The restricted automorphism group is the subgroup of the linear automorphism group generated by permutations of the generators of the same type. That is, vertices can only be permuted with vertices, ray generators with ray generators, and line generators with line generators.
For example, take the first quadrant
\[Q = \Big\{ (x,y) \Big| x\geq 0,\; y\geq0 \Big\} \subset \QQ^2\]Then the linear automorphism group is
\[\begin{split}\mathrm{Aut}(Q) = \left\{ \begin{pmatrix} a & 0 \\ 0 & b \end{pmatrix} ,~ \begin{pmatrix} 0 & c \\ d & 0 \end{pmatrix} :~ a, b, c, d \in \QQ_{>0} \right\} \subset GL(2,\QQ) \subset E(d)\end{split}\]Note that there are no translations that map the quadrant \(Q\) to itself, so the linear automorphism group is contained in the general linear group (the subgroup of transformations preserving the origin). The restricted automorphism group is
\[\begin{split}\mathrm{Aut}(Q) = \left\{ \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} ,~ \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} \right\} \simeq \ZZ_2\end{split}\]INPUT:
output
– how the group should be represented:'abstract'
– default; return an abstract permutation group without further meaning'permutation'
– return a permutation group on the indices of the polyhedron generators. For example, the permutation(0,1)
would correspond to swappingself.Vrepresentation(0)
andself.Vrepresentation(1)
.'matrix'
– return a matrix group representing affine transformations. When acting on affine vectors, you should append a \(1\) to every vector. If the polyhedron is not full dimensional, the returned matrices act as the identity on the orthogonal complement of the affine space spanned by the polyhedron.'matrixlist'
– likematrix
, but return the list of elements of the matrix group. Useful for fields without a good implementation of matrix groups or to avoid the overhead of creating the group.
OUTPUT:
For
output="abstract"
andoutput="permutation"
: aPermutationGroup
.For
output="matrix"
: aMatrixGroup()
.For
output="matrixlist"
: a list of matrices.
REFERENCES:
EXAMPLES:
A cross-polytope example:
sage: # needs sage.groups sage: P = polytopes.cross_polytope(3) sage: P.restricted_automorphism_group() == PermutationGroup([[(3,4)], [(2,3),(4,5)],[(2,5)],[(1,2),(5,6)],[(1,6)]]) True sage: P.restricted_automorphism_group(output='permutation') == PermutationGroup([[(2,3)],[(1,2),(3,4)],[(1,4)],[(0,1),(4,5)],[(0,5)]]) True sage: mgens = [[[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]], [[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]], [[0,1,0,0],[1,0,0,0],[0,0,1,0],[0,0,0,1]]]
>>> from sage.all import * >>> # needs sage.groups >>> P = polytopes.cross_polytope(Integer(3)) >>> P.restricted_automorphism_group() == PermutationGroup([[(Integer(3),Integer(4))], [(Integer(2),Integer(3)),(Integer(4),Integer(5))],[(Integer(2),Integer(5))],[(Integer(1),Integer(2)),(Integer(5),Integer(6))],[(Integer(1),Integer(6))]]) True >>> P.restricted_automorphism_group(output='permutation') == PermutationGroup([[(Integer(2),Integer(3))],[(Integer(1),Integer(2)),(Integer(3),Integer(4))],[(Integer(1),Integer(4))],[(Integer(0),Integer(1)),(Integer(4),Integer(5))],[(Integer(0),Integer(5))]]) True >>> mgens = [[[Integer(1),Integer(0),Integer(0),Integer(0)],[Integer(0),Integer(1),Integer(0),Integer(0)],[Integer(0),Integer(0),-Integer(1),Integer(0)],[Integer(0),Integer(0),Integer(0),Integer(1)]], [[Integer(1),Integer(0),Integer(0),Integer(0)],[Integer(0),Integer(0),Integer(1),Integer(0)],[Integer(0),Integer(1),Integer(0),Integer(0)],[Integer(0),Integer(0),Integer(0),Integer(1)]], [[Integer(0),Integer(1),Integer(0),Integer(0)],[Integer(1),Integer(0),Integer(0),Integer(0)],[Integer(0),Integer(0),Integer(1),Integer(0)],[Integer(0),Integer(0),Integer(0),Integer(1)]]]
# needs sage.groups P = polytopes.cross_polytope(3) P.restricted_automorphism_group() == PermutationGroup([[(3,4)], [(2,3),(4,5)],[(2,5)],[(1,2),(5,6)],[(1,6)]]) P.restricted_automorphism_group(output='permutation') == PermutationGroup([[(2,3)],[(1,2),(3,4)],[(1,4)],[(0,1),(4,5)],[(0,5)]]) mgens = [[[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]], [[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]], [[0,1,0,0],[1,0,0,0],[0,0,1,0],[0,0,0,1]]]
We test groups for equality in a fool-proof way; they can have different generators, etc:
sage: # needs sage.groups sage: poly_g = P.restricted_automorphism_group(output='matrix') sage: matrix_g = MatrixGroup([matrix(QQ,t) for t in mgens]) sage: all(t.matrix() in poly_g for t in matrix_g.gens()) True sage: all(t.matrix() in matrix_g for t in poly_g.gens()) True
>>> from sage.all import * >>> # needs sage.groups >>> poly_g = P.restricted_automorphism_group(output='matrix') >>> matrix_g = MatrixGroup([matrix(QQ,t) for t in mgens]) >>> all(t.matrix() in poly_g for t in matrix_g.gens()) True >>> all(t.matrix() in matrix_g for t in poly_g.gens()) True
# needs sage.groups poly_g = P.restricted_automorphism_group(output='matrix') matrix_g = MatrixGroup([matrix(QQ,t) for t in mgens]) all(t.matrix() in poly_g for t in matrix_g.gens()) all(t.matrix() in matrix_g for t in poly_g.gens())
24-cell example:
sage: # needs sage.groups sage: P24 = polytopes.twenty_four_cell() sage: AutP24 = P24.restricted_automorphism_group() sage: PermutationGroup([ ....: '(1,20,2,24,5,23)(3,18,10,19,4,14)(6,21,11,22,7,15)(8,12,16,17,13,9)', ....: '(1,21,8,24,4,17)(2,11,6,15,9,13)(3,20)(5,22)(10,16,12,23,14,19)' ....: ]).is_isomorphic(AutP24) True sage: AutP24.order() 1152
>>> from sage.all import * >>> # needs sage.groups >>> P24 = polytopes.twenty_four_cell() >>> AutP24 = P24.restricted_automorphism_group() >>> PermutationGroup([ ... '(1,20,2,24,5,23)(3,18,10,19,4,14)(6,21,11,22,7,15)(8,12,16,17,13,9)', ... '(1,21,8,24,4,17)(2,11,6,15,9,13)(3,20)(5,22)(10,16,12,23,14,19)' ... ]).is_isomorphic(AutP24) True >>> AutP24.order() 1152
# needs sage.groups P24 = polytopes.twenty_four_cell() AutP24 = P24.restricted_automorphism_group() PermutationGroup([ '(1,20,2,24,5,23)(3,18,10,19,4,14)(6,21,11,22,7,15)(8,12,16,17,13,9)', '(1,21,8,24,4,17)(2,11,6,15,9,13)(3,20)(5,22)(10,16,12,23,14,19)' ]).is_isomorphic(AutP24) AutP24.order()
Here is the quadrant example mentioned in the beginning:
sage: # needs sage.groups sage: P = Polyhedron(rays=[(1,0),(0,1)]) sage: P.Vrepresentation() (A vertex at (0, 0), A ray in the direction (0, 1), A ray in the direction (1, 0)) sage: P.restricted_automorphism_group(output='permutation') Permutation Group with generators [(1,2)]
>>> from sage.all import * >>> # needs sage.groups >>> P = Polyhedron(rays=[(Integer(1),Integer(0)),(Integer(0),Integer(1))]) >>> P.Vrepresentation() (A vertex at (0, 0), A ray in the direction (0, 1), A ray in the direction (1, 0)) >>> P.restricted_automorphism_group(output='permutation') Permutation Group with generators [(1,2)]
# needs sage.groups P = Polyhedron(rays=[(1,0),(0,1)]) P.Vrepresentation() P.restricted_automorphism_group(output='permutation')
Also, the polyhedron need not be full-dimensional:
sage: # needs sage.groups sage: P = Polyhedron(vertices=[(1,2,3,4,5),(7,8,9,10,11)]) sage: P.restricted_automorphism_group() Permutation Group with generators [(1,2)] sage: G = P.restricted_automorphism_group(output='matrixlist'); G ( [1 0 0 0 0 0] [ -87/55 -82/55 -2/5 38/55 98/55 12/11] [0 1 0 0 0 0] [-142/55 -27/55 -2/5 38/55 98/55 12/11] [0 0 1 0 0 0] [-142/55 -82/55 3/5 38/55 98/55 12/11] [0 0 0 1 0 0] [-142/55 -82/55 -2/5 93/55 98/55 12/11] [0 0 0 0 1 0] [-142/55 -82/55 -2/5 38/55 153/55 12/11] [0 0 0 0 0 1], [ 0 0 0 0 0 1] ) sage: g = AffineGroup(5, QQ)(G[1]); g [ -87/55 -82/55 -2/5 38/55 98/55] [12/11] [-142/55 -27/55 -2/5 38/55 98/55] [12/11] x |-> [-142/55 -82/55 3/5 38/55 98/55] x + [12/11] [-142/55 -82/55 -2/5 93/55 98/55] [12/11] [-142/55 -82/55 -2/5 38/55 153/55] [12/11] sage: g^2 [1 0 0 0 0] [0] [0 1 0 0 0] [0] x |-> [0 0 1 0 0] x + [0] [0 0 0 1 0] [0] [0 0 0 0 1] [0] sage: g(list(P.vertices()[0])) (7, 8, 9, 10, 11) sage: g(list(P.vertices()[1])) (1, 2, 3, 4, 5)
>>> from sage.all import * >>> # needs sage.groups >>> P = Polyhedron(vertices=[(Integer(1),Integer(2),Integer(3),Integer(4),Integer(5)),(Integer(7),Integer(8),Integer(9),Integer(10),Integer(11))]) >>> P.restricted_automorphism_group() Permutation Group with generators [(1,2)] >>> G = P.restricted_automorphism_group(output='matrixlist'); G ( [1 0 0 0 0 0] [ -87/55 -82/55 -2/5 38/55 98/55 12/11] [0 1 0 0 0 0] [-142/55 -27/55 -2/5 38/55 98/55 12/11] [0 0 1 0 0 0] [-142/55 -82/55 3/5 38/55 98/55 12/11] [0 0 0 1 0 0] [-142/55 -82/55 -2/5 93/55 98/55 12/11] [0 0 0 0 1 0] [-142/55 -82/55 -2/5 38/55 153/55 12/11] [0 0 0 0 0 1], [ 0 0 0 0 0 1] ) >>> g = AffineGroup(Integer(5), QQ)(G[Integer(1)]); g [ -87/55 -82/55 -2/5 38/55 98/55] [12/11] [-142/55 -27/55 -2/5 38/55 98/55] [12/11] x |-> [-142/55 -82/55 3/5 38/55 98/55] x + [12/11] [-142/55 -82/55 -2/5 93/55 98/55] [12/11] [-142/55 -82/55 -2/5 38/55 153/55] [12/11] >>> g**Integer(2) [1 0 0 0 0] [0] [0 1 0 0 0] [0] x |-> [0 0 1 0 0] x + [0] [0 0 0 1 0] [0] [0 0 0 0 1] [0] >>> g(list(P.vertices()[Integer(0)])) (7, 8, 9, 10, 11) >>> g(list(P.vertices()[Integer(1)])) (1, 2, 3, 4, 5)
# needs sage.groups P = Polyhedron(vertices=[(1,2,3,4,5),(7,8,9,10,11)]) P.restricted_automorphism_group() G = P.restricted_automorphism_group(output='matrixlist'); G g = AffineGroup(5, QQ)(G[1]); g g^2 g(list(P.vertices()[0])) g(list(P.vertices()[1]))
Affine transformations do not change the restricted automorphism group. For example, any non-degenerate triangle has the dihedral group with 6 elements, \(D_6\), as its automorphism group:
sage: # needs sage.groups sage: initial_points = [vector([1,0]), vector([0,1]), vector([-2,-1])] sage: points = initial_points sage: Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)] sage: points = [pt - initial_points[0] for pt in initial_points] sage: Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)] sage: points = [pt - initial_points[1] for pt in initial_points] sage: Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)] sage: points = [pt - 2*initial_points[1] for pt in initial_points] sage: Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)]
>>> from sage.all import * >>> # needs sage.groups >>> initial_points = [vector([Integer(1),Integer(0)]), vector([Integer(0),Integer(1)]), vector([-Integer(2),-Integer(1)])] >>> points = initial_points >>> Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)] >>> points = [pt - initial_points[Integer(0)] for pt in initial_points] >>> Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)] >>> points = [pt - initial_points[Integer(1)] for pt in initial_points] >>> Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)] >>> points = [pt - Integer(2)*initial_points[Integer(1)] for pt in initial_points] >>> Polyhedron(vertices=points).restricted_automorphism_group() Permutation Group with generators [(2,3), (1,2)]
# needs sage.groups initial_points = [vector([1,0]), vector([0,1]), vector([-2,-1])] points = initial_points Polyhedron(vertices=points).restricted_automorphism_group() points = [pt - initial_points[0] for pt in initial_points] Polyhedron(vertices=points).restricted_automorphism_group() points = [pt - initial_points[1] for pt in initial_points] Polyhedron(vertices=points).restricted_automorphism_group() points = [pt - 2*initial_points[1] for pt in initial_points] Polyhedron(vertices=points).restricted_automorphism_group()
The
output="matrixlist"
can be used over fields without a complete implementation of matrix groups:sage: # needs sage.groups sage.rings.number_field sage: P = polytopes.dodecahedron(); P A 3-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^3 defined as the convex hull of 20 vertices sage: G = P.restricted_automorphism_group(output='matrixlist') sage: len(G) 120
>>> from sage.all import * >>> # needs sage.groups sage.rings.number_field >>> P = polytopes.dodecahedron(); P A 3-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^3 defined as the convex hull of 20 vertices >>> G = P.restricted_automorphism_group(output='matrixlist') >>> len(G) 120
# needs sage.groups sage.rings.number_field P = polytopes.dodecahedron(); P G = P.restricted_automorphism_group(output='matrixlist') len(G)
Floating-point computations are supported with a simple fuzzy zero implementation:
sage: P = Polyhedron(vertices=[(1/3,0,0,1),(0,1/4,0,1),(0,0,1/5,1)], ....: base_ring=RDF) sage: P.restricted_automorphism_group() # needs sage.groups Permutation Group with generators [(2,3), (1,2)] sage: len(P.restricted_automorphism_group(output='matrixlist')) 6
>>> from sage.all import * >>> P = Polyhedron(vertices=[(Integer(1)/Integer(3),Integer(0),Integer(0),Integer(1)),(Integer(0),Integer(1)/Integer(4),Integer(0),Integer(1)),(Integer(0),Integer(0),Integer(1)/Integer(5),Integer(1))], ... base_ring=RDF) >>> P.restricted_automorphism_group() # needs sage.groups Permutation Group with generators [(2,3), (1,2)] >>> len(P.restricted_automorphism_group(output='matrixlist')) 6
P = Polyhedron(vertices=[(1/3,0,0,1),(0,1/4,0,1),(0,0,1/5,1)], base_ring=RDF) P.restricted_automorphism_group() # needs sage.groups len(P.restricted_automorphism_group(output='matrixlist'))
- vertex_digraph(f, increasing=True)[source]¶
Return the directed graph of the polyhedron according to a linear form.
The underlying undirected graph is the graph of vertices and edges.
INPUT:
f
– a linear form. The linear form can be provided as:a vector space morphism with one-dimensional codomain, (see
sage.modules.vector_space_morphism.linear_transformation()
andsage.modules.vector_space_morphism.VectorSpaceMorphism
)a vector ; in this case the linear form is obtained by duality using the dot product:
f(v) = v.dot_product(f)
.
increasing
– boolean (default:True
); whether to orient edges in the increasing or decreasing direction
By default, an edge is oriented from \(v\) to \(w\) if \(f(v) \leq f(w)\).
If \(f(v)=f(w)\), then two opposite edges are created.
EXAMPLES:
sage: penta = Polyhedron([[0,0],[1,0],[0,1],[1,2],[3,2]]) sage: G = penta.vertex_digraph(vector([1,1])); G Digraph on 5 vertices sage: G.sinks() [A vertex at (3, 2)] sage: A = matrix(ZZ, [[1], [-1]]) sage: f = linear_transformation(A) sage: G = penta.vertex_digraph(f) ; G Digraph on 5 vertices sage: G.is_directed_acyclic() False
>>> from sage.all import * >>> penta = Polyhedron([[Integer(0),Integer(0)],[Integer(1),Integer(0)],[Integer(0),Integer(1)],[Integer(1),Integer(2)],[Integer(3),Integer(2)]]) >>> G = penta.vertex_digraph(vector([Integer(1),Integer(1)])); G Digraph on 5 vertices >>> G.sinks() [A vertex at (3, 2)] >>> A = matrix(ZZ, [[Integer(1)], [-Integer(1)]]) >>> f = linear_transformation(A) >>> G = penta.vertex_digraph(f) ; G Digraph on 5 vertices >>> G.is_directed_acyclic() False
penta = Polyhedron([[0,0],[1,0],[0,1],[1,2],[3,2]]) G = penta.vertex_digraph(vector([1,1])); G G.sinks() A = matrix(ZZ, [[1], [-1]]) f = linear_transformation(A) G = penta.vertex_digraph(f) ; G G.is_directed_acyclic()
See also
- vertex_facet_graph(labels=True)[source]¶
Return the vertex-facet graph.
This function constructs a directed bipartite graph. The nodes of the graph correspond to the vertices of the polyhedron and the facets of the polyhedron. There is a directed edge from a vertex to a face if and only if the vertex is incident to the face.
INPUT:
labels
– boolean (default:True
); decide how the nodes of the graph are labelled. Either with the original vertices/facets of the Polyhedron or with integers.
OUTPUT:
a bipartite DiGraph. If
labels
isTrue
, then the nodes of the graph will actually be the vertices and facets ofself
, otherwise they will be integers.
EXAMPLES:
sage: P = polytopes.cube() sage: G = P.vertex_facet_graph(); G Digraph on 14 vertices sage: G.vertices(sort=True, key=lambda v: str(v)) [A vertex at (-1, -1, -1), A vertex at (-1, -1, 1), A vertex at (-1, 1, -1), A vertex at (-1, 1, 1), A vertex at (1, -1, -1), A vertex at (1, -1, 1), A vertex at (1, 1, -1), A vertex at (1, 1, 1), An inequality (-1, 0, 0) x + 1 >= 0, An inequality (0, -1, 0) x + 1 >= 0, An inequality (0, 0, -1) x + 1 >= 0, An inequality (0, 0, 1) x + 1 >= 0, An inequality (0, 1, 0) x + 1 >= 0, An inequality (1, 0, 0) x + 1 >= 0] sage: G.automorphism_group().is_isomorphic(P.hasse_diagram().automorphism_group()) # needs sage.groups True sage: O = polytopes.octahedron(); O A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices sage: O.vertex_facet_graph() Digraph on 14 vertices sage: H = O.vertex_facet_graph() sage: G.is_isomorphic(H) # needs sage.groups False sage: G2 = copy(G) sage: G2.reverse_edges(G2.edges(sort=True)) sage: G2.is_isomorphic(H) # needs sage.groups True
>>> from sage.all import * >>> P = polytopes.cube() >>> G = P.vertex_facet_graph(); G Digraph on 14 vertices >>> G.vertices(sort=True, key=lambda v: str(v)) [A vertex at (-1, -1, -1), A vertex at (-1, -1, 1), A vertex at (-1, 1, -1), A vertex at (-1, 1, 1), A vertex at (1, -1, -1), A vertex at (1, -1, 1), A vertex at (1, 1, -1), A vertex at (1, 1, 1), An inequality (-1, 0, 0) x + 1 >= 0, An inequality (0, -1, 0) x + 1 >= 0, An inequality (0, 0, -1) x + 1 >= 0, An inequality (0, 0, 1) x + 1 >= 0, An inequality (0, 1, 0) x + 1 >= 0, An inequality (1, 0, 0) x + 1 >= 0] >>> G.automorphism_group().is_isomorphic(P.hasse_diagram().automorphism_group()) # needs sage.groups True >>> O = polytopes.octahedron(); O A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices >>> O.vertex_facet_graph() Digraph on 14 vertices >>> H = O.vertex_facet_graph() >>> G.is_isomorphic(H) # needs sage.groups False >>> G2 = copy(G) >>> G2.reverse_edges(G2.edges(sort=True)) >>> G2.is_isomorphic(H) # needs sage.groups True
P = polytopes.cube() G = P.vertex_facet_graph(); G G.vertices(sort=True, key=lambda v: str(v)) G.automorphism_group().is_isomorphic(P.hasse_diagram().automorphism_group()) # needs sage.groups O = polytopes.octahedron(); O O.vertex_facet_graph() H = O.vertex_facet_graph() G.is_isomorphic(H) # needs sage.groups G2 = copy(G) G2.reverse_edges(G2.edges(sort=True)) G2.is_isomorphic(H) # needs sage.groups
- vertex_graph(**kwds)[source]¶
Return a graph in which the vertices correspond to vertices of the polyhedron, and edges to edges.
INPUT:
names
– boolean (default:True
); ifFalse
, then the nodes of the graph are labeld by the indices of the Vrepresentationalgorithm
– string (optional); specify whether the face generator starts with facets or vertices:'primal'
– start with the facets'dual'
– start with the verticesNone
– choose automatically
Note
The graph of a polyhedron with lines has no vertices, as the polyhedron has no vertices (\(0\)-faces).
The method
vertices()
returns the defining points in this case.EXAMPLES:
sage: g3 = polytopes.hypercube(3).vertex_graph(); g3 Graph on 8 vertices sage: g3.automorphism_group().cardinality() # needs sage.groups 48 sage: s4 = polytopes.simplex(4).vertex_graph(); s4 Graph on 5 vertices sage: s4.is_eulerian() True
>>> from sage.all import * >>> g3 = polytopes.hypercube(Integer(3)).vertex_graph(); g3 Graph on 8 vertices >>> g3.automorphism_group().cardinality() # needs sage.groups 48 >>> s4 = polytopes.simplex(Integer(4)).vertex_graph(); s4 Graph on 5 vertices >>> s4.is_eulerian() True
g3 = polytopes.hypercube(3).vertex_graph(); g3 g3.automorphism_group().cardinality() # needs sage.groups s4 = polytopes.simplex(4).vertex_graph(); s4 s4.is_eulerian()
The graph of an unbounded polyhedron is the graph of the bounded complex:
sage: open_triangle = Polyhedron(vertices=[[1,0], [0,1]], ....: rays =[[1,1]]) sage: open_triangle.vertex_graph() Graph on 2 vertices
>>> from sage.all import * >>> open_triangle = Polyhedron(vertices=[[Integer(1),Integer(0)], [Integer(0),Integer(1)]], ... rays =[[Integer(1),Integer(1)]]) >>> open_triangle.vertex_graph() Graph on 2 vertices
open_triangle = Polyhedron(vertices=[[1,0], [0,1]], rays =[[1,1]]) open_triangle.vertex_graph()
The graph of a polyhedron with lines has no vertices:
sage: line = Polyhedron(lines=[[0,1]]) sage: line.vertex_graph() Graph on 0 vertices
>>> from sage.all import * >>> line = Polyhedron(lines=[[Integer(0),Integer(1)]]) >>> line.vertex_graph() Graph on 0 vertices
line = Polyhedron(lines=[[0,1]]) line.vertex_graph()