passagemath-polyhedra 10.6.37__cp314-cp314-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_polyhedra/__init__.py +3 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.37.dist-info/RECORD +209 -0
- passagemath_polyhedra-10.6.37.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.37.dist-info/top_level.txt +3 -0
- passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3905 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
|
@@ -0,0 +1,1262 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
# sage.doctest: needs sage.graphs
|
|
3
|
+
r"""
|
|
4
|
+
Base class for polyhedra: Graph-theoretic methods
|
|
5
|
+
|
|
6
|
+
Define methods relying on :mod:`sage.graphs`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# ****************************************************************************
|
|
10
|
+
# Copyright (C) 2008-2012 Marshall Hampton <hamptonio@gmail.com>
|
|
11
|
+
# Copyright (C) 2011-2015 Volker Braun <vbraun.name@gmail.com>
|
|
12
|
+
# Copyright (C) 2012-2018 Frederic Chapoton
|
|
13
|
+
# Copyright (C) 2013 Andrey Novoseltsev
|
|
14
|
+
# Copyright (C) 2014-2017 Moritz Firsching
|
|
15
|
+
# Copyright (C) 2014-2019 Thierry Monteil
|
|
16
|
+
# Copyright (C) 2015 Nathann Cohen
|
|
17
|
+
# Copyright (C) 2015-2017 Jeroen Demeyer
|
|
18
|
+
# Copyright (C) 2015-2017 Vincent Delecroix
|
|
19
|
+
# Copyright (C) 2015-2018 Dima Pasechnik
|
|
20
|
+
# Copyright (C) 2015-2020 Jean-Philippe Labbe <labbe at math.huji.ac.il>
|
|
21
|
+
# Copyright (C) 2015-2021 Matthias Koeppe
|
|
22
|
+
# Copyright (C) 2016-2019 Daniel Krenn
|
|
23
|
+
# Copyright (C) 2017 Marcelo Forets
|
|
24
|
+
# Copyright (C) 2017-2018 Mark Bell
|
|
25
|
+
# Copyright (C) 2019 Julian Ritter
|
|
26
|
+
# Copyright (C) 2019-2020 Laith Rastanawi
|
|
27
|
+
# Copyright (C) 2019-2020 Sophia Elia
|
|
28
|
+
# Copyright (C) 2019-2021 Jonathan Kliem <jonathan.kliem@gmail.com>
|
|
29
|
+
#
|
|
30
|
+
# This program is free software: you can redistribute it and/or modify
|
|
31
|
+
# it under the terms of the GNU General Public License as published by
|
|
32
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
33
|
+
# (at your option) any later version.
|
|
34
|
+
# https://www.gnu.org/licenses/
|
|
35
|
+
# ****************************************************************************
|
|
36
|
+
|
|
37
|
+
from sage.misc.cachefunc import cached_method
|
|
38
|
+
from .base3 import Polyhedron_base3
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Polyhedron_base4(Polyhedron_base3):
|
|
42
|
+
"""
|
|
43
|
+
Methods relying on :mod:`sage.graphs`.
|
|
44
|
+
|
|
45
|
+
See :class:`sage.geometry.polyhedron.base.Polyhedron_base`.
|
|
46
|
+
|
|
47
|
+
TESTS::
|
|
48
|
+
|
|
49
|
+
sage: from sage.geometry.polyhedron.base4 import Polyhedron_base4
|
|
50
|
+
sage: P = polytopes.cube()
|
|
51
|
+
sage: Polyhedron_base4.vertex_facet_graph.f(P)
|
|
52
|
+
Digraph on 14 vertices
|
|
53
|
+
sage: Polyhedron_base4.vertex_graph(P)
|
|
54
|
+
Graph on 8 vertices
|
|
55
|
+
sage: Polyhedron_base4.face_lattice(P)
|
|
56
|
+
Finite lattice containing 28 elements
|
|
57
|
+
sage: Polyhedron_base4.flag_f_vector(P, 0, 2)
|
|
58
|
+
24
|
|
59
|
+
sage: Polyhedron_base4.is_self_dual(P)
|
|
60
|
+
False
|
|
61
|
+
sage: Q = polytopes.cube(intervals='zero_one')
|
|
62
|
+
sage: P == Q
|
|
63
|
+
False
|
|
64
|
+
sage: Polyhedron_base4.is_combinatorially_isomorphic(P, Q)
|
|
65
|
+
True
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
@cached_method
|
|
69
|
+
def vertex_facet_graph(self, labels=True):
|
|
70
|
+
r"""
|
|
71
|
+
Return the vertex-facet graph.
|
|
72
|
+
|
|
73
|
+
This function constructs a directed bipartite graph.
|
|
74
|
+
The nodes of the graph correspond to the vertices of the polyhedron
|
|
75
|
+
and the facets of the polyhedron. There is a directed edge
|
|
76
|
+
from a vertex to a face if and only if the vertex is incident to the face.
|
|
77
|
+
|
|
78
|
+
INPUT:
|
|
79
|
+
|
|
80
|
+
- ``labels`` -- boolean (default: ``True``); decide how the nodes
|
|
81
|
+
of the graph are labelled. Either with the original vertices/facets
|
|
82
|
+
of the Polyhedron or with integers.
|
|
83
|
+
|
|
84
|
+
OUTPUT:
|
|
85
|
+
|
|
86
|
+
- a bipartite DiGraph. If ``labels`` is ``True``, then the nodes
|
|
87
|
+
of the graph will actually be the vertices and facets of ``self``,
|
|
88
|
+
otherwise they will be integers.
|
|
89
|
+
|
|
90
|
+
.. SEEALSO::
|
|
91
|
+
|
|
92
|
+
:meth:`combinatorial_automorphism_group`,
|
|
93
|
+
:meth:`is_combinatorially_isomorphic`.
|
|
94
|
+
|
|
95
|
+
EXAMPLES::
|
|
96
|
+
|
|
97
|
+
sage: P = polytopes.cube()
|
|
98
|
+
sage: G = P.vertex_facet_graph(); G
|
|
99
|
+
Digraph on 14 vertices
|
|
100
|
+
sage: G.vertices(sort=True, key=lambda v: str(v))
|
|
101
|
+
[A vertex at (-1, -1, -1),
|
|
102
|
+
A vertex at (-1, -1, 1),
|
|
103
|
+
A vertex at (-1, 1, -1),
|
|
104
|
+
A vertex at (-1, 1, 1),
|
|
105
|
+
A vertex at (1, -1, -1),
|
|
106
|
+
A vertex at (1, -1, 1),
|
|
107
|
+
A vertex at (1, 1, -1),
|
|
108
|
+
A vertex at (1, 1, 1),
|
|
109
|
+
An inequality (-1, 0, 0) x + 1 >= 0,
|
|
110
|
+
An inequality (0, -1, 0) x + 1 >= 0,
|
|
111
|
+
An inequality (0, 0, -1) x + 1 >= 0,
|
|
112
|
+
An inequality (0, 0, 1) x + 1 >= 0,
|
|
113
|
+
An inequality (0, 1, 0) x + 1 >= 0,
|
|
114
|
+
An inequality (1, 0, 0) x + 1 >= 0]
|
|
115
|
+
sage: G.automorphism_group().is_isomorphic(P.hasse_diagram().automorphism_group()) # needs sage.groups
|
|
116
|
+
True
|
|
117
|
+
sage: O = polytopes.octahedron(); O
|
|
118
|
+
A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices
|
|
119
|
+
sage: O.vertex_facet_graph()
|
|
120
|
+
Digraph on 14 vertices
|
|
121
|
+
sage: H = O.vertex_facet_graph()
|
|
122
|
+
sage: G.is_isomorphic(H) # needs sage.groups
|
|
123
|
+
False
|
|
124
|
+
sage: G2 = copy(G)
|
|
125
|
+
sage: G2.reverse_edges(G2.edges(sort=True))
|
|
126
|
+
sage: G2.is_isomorphic(H) # needs sage.groups
|
|
127
|
+
True
|
|
128
|
+
|
|
129
|
+
TESTS:
|
|
130
|
+
|
|
131
|
+
Check that :issue:`28828` is fixed::
|
|
132
|
+
|
|
133
|
+
sage: G._immutable
|
|
134
|
+
True
|
|
135
|
+
|
|
136
|
+
Check that :issue:`29188` is fixed::
|
|
137
|
+
|
|
138
|
+
sage: P = polytopes.cube()
|
|
139
|
+
sage: P.vertex_facet_graph().is_isomorphic(P.vertex_facet_graph(False))
|
|
140
|
+
True
|
|
141
|
+
"""
|
|
142
|
+
return self.combinatorial_polyhedron().vertex_facet_graph(names=labels)
|
|
143
|
+
|
|
144
|
+
def vertex_graph(self, **kwds):
|
|
145
|
+
"""
|
|
146
|
+
Return a graph in which the vertices correspond to vertices
|
|
147
|
+
of the polyhedron, and edges to edges.
|
|
148
|
+
|
|
149
|
+
INPUT:
|
|
150
|
+
|
|
151
|
+
- ``names`` -- boolean (default: ``True``); if ``False``,
|
|
152
|
+
then the nodes of the graph are labeld by the
|
|
153
|
+
indices of the Vrepresentation
|
|
154
|
+
|
|
155
|
+
- ``algorithm`` -- string (optional);
|
|
156
|
+
specify whether the face generator starts with facets or vertices:
|
|
157
|
+
|
|
158
|
+
* ``'primal'`` -- start with the facets
|
|
159
|
+
* ``'dual'`` -- start with the vertices
|
|
160
|
+
* ``None`` -- choose automatically
|
|
161
|
+
|
|
162
|
+
.. NOTE::
|
|
163
|
+
|
|
164
|
+
The graph of a polyhedron with lines has no vertices,
|
|
165
|
+
as the polyhedron has no vertices (`0`-faces).
|
|
166
|
+
|
|
167
|
+
The method :meth:`~sage.geometry.polyhedron.base0.Polyhedron_base0.vertices` returns
|
|
168
|
+
the defining points in this case.
|
|
169
|
+
|
|
170
|
+
EXAMPLES::
|
|
171
|
+
|
|
172
|
+
sage: g3 = polytopes.hypercube(3).vertex_graph(); g3
|
|
173
|
+
Graph on 8 vertices
|
|
174
|
+
sage: g3.automorphism_group().cardinality() # needs sage.groups
|
|
175
|
+
48
|
|
176
|
+
sage: s4 = polytopes.simplex(4).vertex_graph(); s4
|
|
177
|
+
Graph on 5 vertices
|
|
178
|
+
sage: s4.is_eulerian()
|
|
179
|
+
True
|
|
180
|
+
|
|
181
|
+
The graph of an unbounded polyhedron
|
|
182
|
+
is the graph of the bounded complex::
|
|
183
|
+
|
|
184
|
+
sage: open_triangle = Polyhedron(vertices=[[1,0], [0,1]],
|
|
185
|
+
....: rays =[[1,1]])
|
|
186
|
+
sage: open_triangle.vertex_graph()
|
|
187
|
+
Graph on 2 vertices
|
|
188
|
+
|
|
189
|
+
The graph of a polyhedron with lines has no vertices::
|
|
190
|
+
|
|
191
|
+
sage: line = Polyhedron(lines=[[0,1]])
|
|
192
|
+
sage: line.vertex_graph()
|
|
193
|
+
Graph on 0 vertices
|
|
194
|
+
|
|
195
|
+
TESTS:
|
|
196
|
+
|
|
197
|
+
Check for a line segment (:issue:`30545`)::
|
|
198
|
+
|
|
199
|
+
sage: polytopes.simplex(1).graph().edges(sort=True)
|
|
200
|
+
[(A vertex at (0, 1), A vertex at (1, 0), None)]
|
|
201
|
+
"""
|
|
202
|
+
return self.combinatorial_polyhedron().vertex_graph(**kwds)
|
|
203
|
+
|
|
204
|
+
graph = vertex_graph
|
|
205
|
+
|
|
206
|
+
def vertex_digraph(self, f, increasing=True):
|
|
207
|
+
r"""
|
|
208
|
+
Return the directed graph of the polyhedron according to a linear form.
|
|
209
|
+
|
|
210
|
+
The underlying undirected graph is the graph of vertices and edges.
|
|
211
|
+
|
|
212
|
+
INPUT:
|
|
213
|
+
|
|
214
|
+
- ``f`` -- a linear form. The linear form can be provided as:
|
|
215
|
+
|
|
216
|
+
- a vector space morphism with one-dimensional codomain, (see
|
|
217
|
+
:func:`sage.modules.vector_space_morphism.linear_transformation`
|
|
218
|
+
and
|
|
219
|
+
:class:`sage.modules.vector_space_morphism.VectorSpaceMorphism`)
|
|
220
|
+
- a vector ; in this case the linear form is obtained by duality
|
|
221
|
+
using the dot product: ``f(v) = v.dot_product(f)``.
|
|
222
|
+
|
|
223
|
+
- ``increasing`` -- boolean (default: ``True``); whether to orient
|
|
224
|
+
edges in the increasing or decreasing direction
|
|
225
|
+
|
|
226
|
+
By default, an edge is oriented from `v` to `w` if
|
|
227
|
+
`f(v) \leq f(w)`.
|
|
228
|
+
|
|
229
|
+
If `f(v)=f(w)`, then two opposite edges are created.
|
|
230
|
+
|
|
231
|
+
EXAMPLES::
|
|
232
|
+
|
|
233
|
+
sage: penta = Polyhedron([[0,0],[1,0],[0,1],[1,2],[3,2]])
|
|
234
|
+
sage: G = penta.vertex_digraph(vector([1,1])); G
|
|
235
|
+
Digraph on 5 vertices
|
|
236
|
+
sage: G.sinks()
|
|
237
|
+
[A vertex at (3, 2)]
|
|
238
|
+
|
|
239
|
+
sage: A = matrix(ZZ, [[1], [-1]])
|
|
240
|
+
sage: f = linear_transformation(A)
|
|
241
|
+
sage: G = penta.vertex_digraph(f) ; G
|
|
242
|
+
Digraph on 5 vertices
|
|
243
|
+
sage: G.is_directed_acyclic()
|
|
244
|
+
False
|
|
245
|
+
|
|
246
|
+
.. SEEALSO::
|
|
247
|
+
|
|
248
|
+
:meth:`vertex_graph`
|
|
249
|
+
"""
|
|
250
|
+
from sage.modules.vector_space_morphism import VectorSpaceMorphism
|
|
251
|
+
if isinstance(f, VectorSpaceMorphism):
|
|
252
|
+
if f.codomain().dimension() == 1:
|
|
253
|
+
orientation_check = lambda v: f(v) >= 0
|
|
254
|
+
else:
|
|
255
|
+
raise TypeError('the linear map f must have '
|
|
256
|
+
'one-dimensional codomain')
|
|
257
|
+
else:
|
|
258
|
+
try:
|
|
259
|
+
if f.is_vector():
|
|
260
|
+
orientation_check = lambda v: v.dot_product(f) >= 0
|
|
261
|
+
else:
|
|
262
|
+
raise TypeError('f must be a linear map or a vector')
|
|
263
|
+
except AttributeError:
|
|
264
|
+
raise TypeError('f must be a linear map or a vector')
|
|
265
|
+
if not increasing:
|
|
266
|
+
f = -f
|
|
267
|
+
from sage.graphs.digraph import DiGraph
|
|
268
|
+
dg = DiGraph()
|
|
269
|
+
for j in range(self.n_vertices()):
|
|
270
|
+
vj = self.Vrepresentation(j)
|
|
271
|
+
for vi in vj.neighbors():
|
|
272
|
+
if orientation_check(vj.vector() - vi.vector()):
|
|
273
|
+
dg.add_edge(vi, vj)
|
|
274
|
+
return dg
|
|
275
|
+
|
|
276
|
+
def face_lattice(self):
|
|
277
|
+
"""
|
|
278
|
+
Return the face-lattice poset.
|
|
279
|
+
|
|
280
|
+
OUTPUT:
|
|
281
|
+
|
|
282
|
+
A :class:`~sage.combinat.posets.posets.FinitePoset`. Elements
|
|
283
|
+
are given as
|
|
284
|
+
:class:`~sage.geometry.polyhedron.face.PolyhedronFace`.
|
|
285
|
+
|
|
286
|
+
In the case of a full-dimensional polytope, the faces are
|
|
287
|
+
pairs (vertices, inequalities) of the spanning vertices and
|
|
288
|
+
corresponding saturated inequalities. In general, a face is
|
|
289
|
+
defined by a pair (V-rep. objects, H-rep. objects). The
|
|
290
|
+
V-representation objects span the face, and the corresponding
|
|
291
|
+
H-representation objects are those inequalities and equations
|
|
292
|
+
that are saturated on the face.
|
|
293
|
+
|
|
294
|
+
The bottom-most element of the face lattice is the "empty
|
|
295
|
+
face". It contains no V-representation object. All
|
|
296
|
+
H-representation objects are incident.
|
|
297
|
+
|
|
298
|
+
The top-most element is the "full face". It is spanned by all
|
|
299
|
+
V-representation objects. The incident H-representation
|
|
300
|
+
objects are all equations and no inequalities.
|
|
301
|
+
|
|
302
|
+
In the case of a full-dimensional polytope, the "empty face"
|
|
303
|
+
and the "full face" are the empty set (no vertices, all
|
|
304
|
+
inequalities) and the full polytope (all vertices, no
|
|
305
|
+
inequalities), respectively.
|
|
306
|
+
|
|
307
|
+
ALGORITHM:
|
|
308
|
+
|
|
309
|
+
See :mod:`sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator`.
|
|
310
|
+
|
|
311
|
+
.. NOTE::
|
|
312
|
+
|
|
313
|
+
The face lattice is not cached, as long as this creates a memory leak, see :issue:`28982`.
|
|
314
|
+
|
|
315
|
+
EXAMPLES::
|
|
316
|
+
|
|
317
|
+
sage: square = polytopes.hypercube(2)
|
|
318
|
+
sage: fl = square.face_lattice();fl
|
|
319
|
+
Finite lattice containing 10 elements
|
|
320
|
+
sage: list(f.ambient_V_indices() for f in fl)
|
|
321
|
+
[(), (0,), (1,), (0, 1), (2,), (1, 2), (3,), (0, 3), (2, 3), (0, 1, 2, 3)]
|
|
322
|
+
sage: poset_element = fl[5]
|
|
323
|
+
sage: a_face = poset_element
|
|
324
|
+
sage: a_face
|
|
325
|
+
A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 2 vertices
|
|
326
|
+
sage: a_face.ambient_V_indices()
|
|
327
|
+
(1, 2)
|
|
328
|
+
sage: set(a_face.ambient_Vrepresentation()) == \
|
|
329
|
+
....: set([square.Vrepresentation(1), square.Vrepresentation(2)])
|
|
330
|
+
True
|
|
331
|
+
sage: a_face.ambient_Vrepresentation()
|
|
332
|
+
(A vertex at (1, 1), A vertex at (-1, 1))
|
|
333
|
+
sage: a_face.ambient_Hrepresentation()
|
|
334
|
+
(An inequality (0, -1) x + 1 >= 0,)
|
|
335
|
+
|
|
336
|
+
A more complicated example::
|
|
337
|
+
|
|
338
|
+
sage: c5_10 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5] for i in range(1,11)])
|
|
339
|
+
sage: c5_10_fl = c5_10.face_lattice()
|
|
340
|
+
sage: [len(x) for x in c5_10_fl.level_sets()]
|
|
341
|
+
[1, 10, 45, 100, 105, 42, 1]
|
|
342
|
+
|
|
343
|
+
Note that if the polyhedron contains lines then there is a
|
|
344
|
+
dimension gap between the empty face and the first non-empty
|
|
345
|
+
face in the face lattice::
|
|
346
|
+
|
|
347
|
+
sage: line = Polyhedron(vertices=[(0,)], lines=[(1,)])
|
|
348
|
+
sage: [ fl.dim() for fl in line.face_lattice() ]
|
|
349
|
+
[-1, 1]
|
|
350
|
+
|
|
351
|
+
TESTS::
|
|
352
|
+
|
|
353
|
+
sage: c5_20 = Polyhedron(vertices = [[i,i^2,i^3,i^4,i^5]
|
|
354
|
+
....: for i in range(1,21)])
|
|
355
|
+
sage: c5_20_fl = c5_20.face_lattice() # long time
|
|
356
|
+
sage: [len(x) for x in c5_20_fl.level_sets()] # long time
|
|
357
|
+
[1, 20, 190, 580, 680, 272, 1]
|
|
358
|
+
sage: polytopes.hypercube(2).face_lattice().plot() # needs sage.plot
|
|
359
|
+
Graphics object consisting of 27 graphics primitives
|
|
360
|
+
sage: level_sets = polytopes.cross_polytope(2).face_lattice().level_sets()
|
|
361
|
+
sage: level_sets[0][0].ambient_V_indices(), level_sets[-1][0].ambient_V_indices()
|
|
362
|
+
((), (0, 1, 2, 3))
|
|
363
|
+
|
|
364
|
+
Various degenerate polyhedra::
|
|
365
|
+
|
|
366
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[[0,0,0],[1,0,0],[0,1,0]]).face_lattice().level_sets()]
|
|
367
|
+
[[()], [(0,), (1,), (2,)], [(0, 1), (0, 2), (1, 2)], [(0, 1, 2)]]
|
|
368
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(1,0,0),(0,1,0)], rays=[(0,0,1)]).face_lattice().level_sets()]
|
|
369
|
+
[[()], [(1,), (2,)], [(0, 1), (0, 2), (1, 2)], [(0, 1, 2)]]
|
|
370
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(rays=[(1,0,0),(0,1,0)], vertices=[(0,0,1)]).face_lattice().level_sets()]
|
|
371
|
+
[[()], [(0,)], [(0, 1), (0, 2)], [(0, 1, 2)]]
|
|
372
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(rays=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()]
|
|
373
|
+
[[()], [(0,)], [(0, 1), (0, 2)], [(0, 1, 2)]]
|
|
374
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(1,),(0,)]).face_lattice().level_sets()]
|
|
375
|
+
[[()], [(0,), (1,)], [(0, 1)]]
|
|
376
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(1,0,0),(0,1,0)], lines=[(0,0,1)]).face_lattice().level_sets()]
|
|
377
|
+
[[()], [(0, 1), (0, 2)], [(0, 1, 2)]]
|
|
378
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0,0)], vertices=[(0,0,1)]).face_lattice().level_sets()]
|
|
379
|
+
[[()], [(0, 1)]]
|
|
380
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0),(0,1)], vertices=[(0,0)]).face_lattice().level_sets()]
|
|
381
|
+
[[()], [(0, 1, 2)]]
|
|
382
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0)], rays=[(0,1)], vertices=[(0,0)]).face_lattice().level_sets()]
|
|
383
|
+
[[()], [(0, 1)], [(0, 1, 2)]]
|
|
384
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(vertices=[(0,)], lines=[(1,)]).face_lattice().level_sets()]
|
|
385
|
+
[[()], [(0, 1)]]
|
|
386
|
+
sage: [[ls.ambient_V_indices() for ls in lss] for lss in Polyhedron(lines=[(1,0)], vertices=[(0,0)]).face_lattice().level_sets()]
|
|
387
|
+
[[()], [(0, 1)]]
|
|
388
|
+
"""
|
|
389
|
+
from sage.combinat.posets.lattices import FiniteLatticePoset
|
|
390
|
+
return FiniteLatticePoset(self.hasse_diagram())
|
|
391
|
+
|
|
392
|
+
@cached_method
|
|
393
|
+
def hasse_diagram(self):
|
|
394
|
+
r"""
|
|
395
|
+
Return the Hasse diagram of the face lattice of ``self``.
|
|
396
|
+
|
|
397
|
+
This is the Hasse diagram of the poset of the faces of ``self``.
|
|
398
|
+
|
|
399
|
+
OUTPUT: a directed graph
|
|
400
|
+
|
|
401
|
+
EXAMPLES::
|
|
402
|
+
|
|
403
|
+
sage: # needs sage.rings.number_field
|
|
404
|
+
sage: P = polytopes.regular_polygon(4).pyramid()
|
|
405
|
+
sage: D = P.hasse_diagram(); D
|
|
406
|
+
Digraph on 20 vertices
|
|
407
|
+
sage: D.degree_polynomial()
|
|
408
|
+
x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
|
|
409
|
+
|
|
410
|
+
Faces of a mutable polyhedron are not hashable. Hence those are not suitable as
|
|
411
|
+
vertices of the hasse diagram. Use the combinatorial polyhedron instead::
|
|
412
|
+
|
|
413
|
+
sage: # needs sage.rings.number_field
|
|
414
|
+
sage: P = polytopes.regular_polygon(4).pyramid()
|
|
415
|
+
sage: parent = P.parent()
|
|
416
|
+
sage: parent = parent.change_ring(QQ, backend='ppl')
|
|
417
|
+
sage: Q = parent._element_constructor_(P, mutable=True)
|
|
418
|
+
sage: Q.hasse_diagram()
|
|
419
|
+
Traceback (most recent call last):
|
|
420
|
+
...
|
|
421
|
+
TypeError: ...mutable polyhedra are unhashable...
|
|
422
|
+
sage: C = Q.combinatorial_polyhedron()
|
|
423
|
+
sage: D = C.hasse_diagram()
|
|
424
|
+
sage: set(D.vertices(sort=False)) == set(range(20))
|
|
425
|
+
True
|
|
426
|
+
sage: def index_to_combinatorial_face(n):
|
|
427
|
+
....: return C.face_by_face_lattice_index(n)
|
|
428
|
+
sage: D.relabel(index_to_combinatorial_face, inplace=True)
|
|
429
|
+
sage: D.vertices(sort=True)
|
|
430
|
+
[A -1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
431
|
+
A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
432
|
+
A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
433
|
+
A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
434
|
+
A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
435
|
+
A 0-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
436
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
437
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
438
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
439
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
440
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
441
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
442
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
443
|
+
A 1-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
444
|
+
A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
445
|
+
A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
446
|
+
A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
447
|
+
A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
448
|
+
A 2-dimensional face of a 3-dimensional combinatorial polyhedron,
|
|
449
|
+
A 3-dimensional face of a 3-dimensional combinatorial polyhedron]
|
|
450
|
+
sage: D.degree_polynomial()
|
|
451
|
+
x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face
|
|
455
|
+
C = self.combinatorial_polyhedron()
|
|
456
|
+
D = C.hasse_diagram()
|
|
457
|
+
|
|
458
|
+
def index_to_polyhedron_face(n):
|
|
459
|
+
return combinatorial_face_to_polyhedral_face(
|
|
460
|
+
self, C.face_by_face_lattice_index(n))
|
|
461
|
+
|
|
462
|
+
return D.relabel(index_to_polyhedron_face, inplace=False, immutable=True)
|
|
463
|
+
|
|
464
|
+
def flag_f_vector(self, *args):
|
|
465
|
+
r"""
|
|
466
|
+
Return the flag f-vector.
|
|
467
|
+
|
|
468
|
+
For each `-1 < i_0 < \dots < i_n < d` the flag f-vector
|
|
469
|
+
counts the number of flags `F_0 \subset \dots \subset F_n`
|
|
470
|
+
with `F_j` of dimension `i_j` for each `0 \leq j \leq n`,
|
|
471
|
+
where `d` is the dimension of the polyhedron.
|
|
472
|
+
|
|
473
|
+
INPUT:
|
|
474
|
+
|
|
475
|
+
- ``args`` -- integer (optional); specify an entry of the
|
|
476
|
+
flag-f-vector; must be an increasing sequence of integers
|
|
477
|
+
|
|
478
|
+
OUTPUT: a dictionary, if no arguments were given
|
|
479
|
+
|
|
480
|
+
- an Integer, if arguments were given
|
|
481
|
+
|
|
482
|
+
EXAMPLES:
|
|
483
|
+
|
|
484
|
+
Obtain the entire flag-f-vector::
|
|
485
|
+
|
|
486
|
+
sage: P = polytopes.twenty_four_cell()
|
|
487
|
+
sage: P.flag_f_vector()
|
|
488
|
+
{(-1,): 1,
|
|
489
|
+
(0,): 24,
|
|
490
|
+
(0, 1): 192,
|
|
491
|
+
(0, 1, 2): 576,
|
|
492
|
+
(0, 1, 2, 3): 1152,
|
|
493
|
+
(0, 1, 3): 576,
|
|
494
|
+
(0, 2): 288,
|
|
495
|
+
(0, 2, 3): 576,
|
|
496
|
+
(0, 3): 144,
|
|
497
|
+
(1,): 96,
|
|
498
|
+
(1, 2): 288,
|
|
499
|
+
(1, 2, 3): 576,
|
|
500
|
+
(1, 3): 288,
|
|
501
|
+
(2,): 96,
|
|
502
|
+
(2, 3): 192,
|
|
503
|
+
(3,): 24,
|
|
504
|
+
(4,): 1}
|
|
505
|
+
|
|
506
|
+
Specify an entry::
|
|
507
|
+
|
|
508
|
+
sage: P.flag_f_vector(0,3)
|
|
509
|
+
144
|
|
510
|
+
sage: P.flag_f_vector(2)
|
|
511
|
+
96
|
|
512
|
+
|
|
513
|
+
Leading ``-1`` and trailing entry of dimension are allowed::
|
|
514
|
+
|
|
515
|
+
sage: P.flag_f_vector(-1,0,3)
|
|
516
|
+
144
|
|
517
|
+
sage: P.flag_f_vector(-1,0,3,4)
|
|
518
|
+
144
|
|
519
|
+
|
|
520
|
+
One can get the number of trivial faces::
|
|
521
|
+
|
|
522
|
+
sage: P.flag_f_vector(-1)
|
|
523
|
+
1
|
|
524
|
+
sage: P.flag_f_vector(4)
|
|
525
|
+
1
|
|
526
|
+
|
|
527
|
+
Polyhedra with lines, have ``0`` entries accordingly::
|
|
528
|
+
|
|
529
|
+
sage: P = (Polyhedron(lines=[[1]]) * polytopes.cross_polytope(3))
|
|
530
|
+
sage: P.flag_f_vector()
|
|
531
|
+
{(-1,): 1,
|
|
532
|
+
(0, 1): 0,
|
|
533
|
+
(0, 1, 2): 0,
|
|
534
|
+
(0, 1, 3): 0,
|
|
535
|
+
(0, 2): 0,
|
|
536
|
+
(0, 2, 3): 0,
|
|
537
|
+
(0, 3): 0,
|
|
538
|
+
(0,): 0,
|
|
539
|
+
(1, 2): 24,
|
|
540
|
+
(1, 2, 3): 48,
|
|
541
|
+
(1, 3): 24,
|
|
542
|
+
(1,): 6,
|
|
543
|
+
(2, 3): 24,
|
|
544
|
+
(2,): 12,
|
|
545
|
+
(3,): 8,
|
|
546
|
+
4: 1}
|
|
547
|
+
|
|
548
|
+
If the arguments are not strictly increasing or out of range,
|
|
549
|
+
a key error is raised::
|
|
550
|
+
|
|
551
|
+
sage: P.flag_f_vector(-1,0,3,6)
|
|
552
|
+
Traceback (most recent call last):
|
|
553
|
+
...
|
|
554
|
+
KeyError: (0, 3, 6)
|
|
555
|
+
sage: P.flag_f_vector(-1,3,0)
|
|
556
|
+
Traceback (most recent call last):
|
|
557
|
+
...
|
|
558
|
+
KeyError: (3, 0)
|
|
559
|
+
"""
|
|
560
|
+
flag = self._flag_f_vector()
|
|
561
|
+
if len(args) == 0:
|
|
562
|
+
return flag
|
|
563
|
+
elif len(args) == 1:
|
|
564
|
+
return flag[(args[0],)]
|
|
565
|
+
else:
|
|
566
|
+
dim = self.dimension()
|
|
567
|
+
if args[0] == -1:
|
|
568
|
+
args = args[1:]
|
|
569
|
+
if args[-1] == dim:
|
|
570
|
+
args = args[:-1]
|
|
571
|
+
return flag[tuple(args)]
|
|
572
|
+
|
|
573
|
+
@cached_method(do_pickle=True)
|
|
574
|
+
def _flag_f_vector(self):
|
|
575
|
+
r"""
|
|
576
|
+
Return the flag-f-vector.
|
|
577
|
+
|
|
578
|
+
See :meth:`flag_f_vector`.
|
|
579
|
+
|
|
580
|
+
TESTS::
|
|
581
|
+
|
|
582
|
+
sage: polytopes.hypercube(4)._flag_f_vector()
|
|
583
|
+
{(-1,): 1,
|
|
584
|
+
(0,): 16,
|
|
585
|
+
(0, 1): 64,
|
|
586
|
+
(0, 1, 2): 192,
|
|
587
|
+
(0, 1, 2, 3): 384,
|
|
588
|
+
(0, 1, 3): 192,
|
|
589
|
+
(0, 2): 96,
|
|
590
|
+
(0, 2, 3): 192,
|
|
591
|
+
(0, 3): 64,
|
|
592
|
+
(1,): 32,
|
|
593
|
+
(1, 2): 96,
|
|
594
|
+
(1, 2, 3): 192,
|
|
595
|
+
(1, 3): 96,
|
|
596
|
+
(2,): 24,
|
|
597
|
+
(2, 3): 48,
|
|
598
|
+
(3,): 8,
|
|
599
|
+
(4,): 1}
|
|
600
|
+
"""
|
|
601
|
+
return self.combinatorial_polyhedron()._flag_f_vector()
|
|
602
|
+
|
|
603
|
+
@cached_method
|
|
604
|
+
def combinatorial_automorphism_group(self, vertex_graph_only=False):
|
|
605
|
+
"""
|
|
606
|
+
Compute the combinatorial automorphism group.
|
|
607
|
+
|
|
608
|
+
If ``vertex_graph_only`` is ``True``, the automorphism group
|
|
609
|
+
of the vertex-edge graph of the polyhedron is returned. Otherwise
|
|
610
|
+
the automorphism group of the vertex-facet graph, which is
|
|
611
|
+
isomorphic to the automorphism group of the face lattice is returned.
|
|
612
|
+
|
|
613
|
+
INPUT:
|
|
614
|
+
|
|
615
|
+
- ``vertex_graph_only`` -- boolean (default: ``False``); whether
|
|
616
|
+
to return the automorphism group of the vertex edges graph or
|
|
617
|
+
of the lattice
|
|
618
|
+
|
|
619
|
+
OUTPUT:
|
|
620
|
+
|
|
621
|
+
A
|
|
622
|
+
:class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic_with_category'>`
|
|
623
|
+
that is isomorphic to the combinatorial automorphism group is
|
|
624
|
+
returned.
|
|
625
|
+
|
|
626
|
+
- if ``vertex_graph_only`` is ``True``:
|
|
627
|
+
The automorphism group of the vertex-edge graph of the polyhedron
|
|
628
|
+
|
|
629
|
+
- if ``vertex_graph_only`` is ``False`` (default):
|
|
630
|
+
The automorphism group of the vertex-facet graph of the polyhedron,
|
|
631
|
+
see :meth:`vertex_facet_graph`. This group is isomorphic to the
|
|
632
|
+
automorphism group of the face lattice of the polyhedron.
|
|
633
|
+
|
|
634
|
+
NOTE:
|
|
635
|
+
|
|
636
|
+
Depending on ``vertex_graph_only``, this method returns groups
|
|
637
|
+
that are not necessarily isomorphic, see the examples below.
|
|
638
|
+
|
|
639
|
+
.. SEEALSO::
|
|
640
|
+
|
|
641
|
+
:meth:`is_combinatorially_isomorphic`,
|
|
642
|
+
:meth:`graph`,
|
|
643
|
+
:meth:`vertex_facet_graph`.
|
|
644
|
+
|
|
645
|
+
EXAMPLES::
|
|
646
|
+
|
|
647
|
+
sage: quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)])
|
|
648
|
+
sage: quadrangle.combinatorial_automorphism_group().is_isomorphic( # needs sage.groups
|
|
649
|
+
....: groups.permutation.Dihedral(4))
|
|
650
|
+
True
|
|
651
|
+
sage: quadrangle.restricted_automorphism_group() # needs sage.groups
|
|
652
|
+
Permutation Group with generators [()]
|
|
653
|
+
|
|
654
|
+
Permutations of the vertex graph only exchange vertices with vertices::
|
|
655
|
+
|
|
656
|
+
sage: P = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)])
|
|
657
|
+
sage: P.combinatorial_automorphism_group(vertex_graph_only=True) # needs sage.groups
|
|
658
|
+
Permutation Group with generators [(A vertex at (1,0),A vertex at (1,1))]
|
|
659
|
+
|
|
660
|
+
This shows an example of two polytopes whose vertex-edge graphs are isomorphic,
|
|
661
|
+
but their face lattices are not isomorphic::
|
|
662
|
+
|
|
663
|
+
sage: # needs sage.groups
|
|
664
|
+
sage: Q = Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773],
|
|
665
|
+
....: [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817],
|
|
666
|
+
....: [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199],
|
|
667
|
+
....: [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097],
|
|
668
|
+
....: [-19184/902877, 26136/300959, -21472/902877, 899005/902877],
|
|
669
|
+
....: [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933],
|
|
670
|
+
....: [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433],
|
|
671
|
+
....: [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]])
|
|
672
|
+
sage: C = polytopes.cyclic_polytope(4,8)
|
|
673
|
+
sage: C.is_combinatorially_isomorphic(Q)
|
|
674
|
+
False
|
|
675
|
+
sage: C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic(
|
|
676
|
+
....: Q.combinatorial_automorphism_group(vertex_graph_only=True))
|
|
677
|
+
True
|
|
678
|
+
sage: C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic(
|
|
679
|
+
....: Q.combinatorial_automorphism_group(vertex_graph_only=False))
|
|
680
|
+
False
|
|
681
|
+
|
|
682
|
+
The automorphism group of the face lattice is isomorphic to the combinatorial automorphism group::
|
|
683
|
+
|
|
684
|
+
sage: # needs sage.groups
|
|
685
|
+
sage: CG = C.hasse_diagram().automorphism_group()
|
|
686
|
+
sage: C.combinatorial_automorphism_group().is_isomorphic(CG)
|
|
687
|
+
True
|
|
688
|
+
sage: QG = Q.hasse_diagram().automorphism_group()
|
|
689
|
+
sage: Q.combinatorial_automorphism_group().is_isomorphic(QG)
|
|
690
|
+
True
|
|
691
|
+
"""
|
|
692
|
+
if vertex_graph_only:
|
|
693
|
+
G = self.graph()
|
|
694
|
+
else:
|
|
695
|
+
G = self.vertex_facet_graph()
|
|
696
|
+
return G.automorphism_group(edge_labels=True)
|
|
697
|
+
|
|
698
|
+
@cached_method
|
|
699
|
+
def restricted_automorphism_group(self, output='abstract'):
|
|
700
|
+
r"""
|
|
701
|
+
Return the restricted automorphism group.
|
|
702
|
+
|
|
703
|
+
First, let the linear automorphism group be the subgroup of
|
|
704
|
+
the affine group `AGL(d,\RR) = GL(d,\RR) \ltimes \RR^d`
|
|
705
|
+
preserving the `d`-dimensional polyhedron. The affine group
|
|
706
|
+
acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the
|
|
707
|
+
ambient space.
|
|
708
|
+
|
|
709
|
+
The restricted automorphism group is the subgroup of the linear
|
|
710
|
+
automorphism group generated by permutations of the generators
|
|
711
|
+
of the same type. That is, vertices can only be permuted with
|
|
712
|
+
vertices, ray generators with ray generators, and line
|
|
713
|
+
generators with line generators.
|
|
714
|
+
|
|
715
|
+
For example, take the first quadrant
|
|
716
|
+
|
|
717
|
+
.. MATH::
|
|
718
|
+
|
|
719
|
+
Q = \Big\{ (x,y) \Big| x\geq 0,\; y\geq0 \Big\}
|
|
720
|
+
\subset \QQ^2
|
|
721
|
+
|
|
722
|
+
Then the linear automorphism group is
|
|
723
|
+
|
|
724
|
+
.. MATH::
|
|
725
|
+
|
|
726
|
+
\mathrm{Aut}(Q) =
|
|
727
|
+
\left\{
|
|
728
|
+
\begin{pmatrix}
|
|
729
|
+
a & 0 \\ 0 & b
|
|
730
|
+
\end{pmatrix}
|
|
731
|
+
,~
|
|
732
|
+
\begin{pmatrix}
|
|
733
|
+
0 & c \\ d & 0
|
|
734
|
+
\end{pmatrix}
|
|
735
|
+
:~
|
|
736
|
+
a, b, c, d \in \QQ_{>0}
|
|
737
|
+
\right\}
|
|
738
|
+
\subset
|
|
739
|
+
GL(2,\QQ)
|
|
740
|
+
\subset
|
|
741
|
+
E(d)
|
|
742
|
+
|
|
743
|
+
Note that there are no translations that map the quadrant `Q`
|
|
744
|
+
to itself, so the linear automorphism group is contained in
|
|
745
|
+
the general linear group (the subgroup of transformations
|
|
746
|
+
preserving the origin). The restricted automorphism group is
|
|
747
|
+
|
|
748
|
+
.. MATH::
|
|
749
|
+
|
|
750
|
+
\mathrm{Aut}(Q) =
|
|
751
|
+
\left\{
|
|
752
|
+
\begin{pmatrix}
|
|
753
|
+
1 & 0 \\ 0 & 1
|
|
754
|
+
\end{pmatrix}
|
|
755
|
+
,~
|
|
756
|
+
\begin{pmatrix}
|
|
757
|
+
0 & 1 \\ 1 & 0
|
|
758
|
+
\end{pmatrix}
|
|
759
|
+
\right\}
|
|
760
|
+
\simeq \ZZ_2
|
|
761
|
+
|
|
762
|
+
INPUT:
|
|
763
|
+
|
|
764
|
+
- ``output`` -- how the group should be represented:
|
|
765
|
+
|
|
766
|
+
- ``'abstract'`` -- default; return an abstract permutation
|
|
767
|
+
group without further meaning
|
|
768
|
+
|
|
769
|
+
- ``'permutation'`` -- return a permutation group on the
|
|
770
|
+
indices of the polyhedron generators. For example, the
|
|
771
|
+
permutation ``(0,1)`` would correspond to swapping
|
|
772
|
+
``self.Vrepresentation(0)`` and ``self.Vrepresentation(1)``.
|
|
773
|
+
|
|
774
|
+
- ``'matrix'`` -- return a matrix group representing affine
|
|
775
|
+
transformations. When acting on affine vectors, you should
|
|
776
|
+
append a `1` to every vector. If the polyhedron is not full
|
|
777
|
+
dimensional, the returned matrices act as the identity on
|
|
778
|
+
the orthogonal complement of the affine space spanned by
|
|
779
|
+
the polyhedron.
|
|
780
|
+
|
|
781
|
+
- ``'matrixlist'`` -- like ``matrix``, but return the list of
|
|
782
|
+
elements of the matrix group. Useful for fields without a
|
|
783
|
+
good implementation of matrix groups or to avoid the
|
|
784
|
+
overhead of creating the group.
|
|
785
|
+
|
|
786
|
+
OUTPUT:
|
|
787
|
+
|
|
788
|
+
- For ``output="abstract"`` and ``output="permutation"``:
|
|
789
|
+
a :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`.
|
|
790
|
+
|
|
791
|
+
- For ``output="matrix"``: a :func:`~sage.groups.matrix_gps.finitely_generated.MatrixGroup`.
|
|
792
|
+
|
|
793
|
+
- For ``output="matrixlist"``: a list of matrices.
|
|
794
|
+
|
|
795
|
+
REFERENCES:
|
|
796
|
+
|
|
797
|
+
- [BSS2009]_
|
|
798
|
+
|
|
799
|
+
EXAMPLES:
|
|
800
|
+
|
|
801
|
+
A cross-polytope example::
|
|
802
|
+
|
|
803
|
+
sage: # needs sage.groups
|
|
804
|
+
sage: P = polytopes.cross_polytope(3)
|
|
805
|
+
sage: P.restricted_automorphism_group() == PermutationGroup([[(3,4)], [(2,3),(4,5)],[(2,5)],[(1,2),(5,6)],[(1,6)]])
|
|
806
|
+
True
|
|
807
|
+
sage: P.restricted_automorphism_group(output='permutation') == PermutationGroup([[(2,3)],[(1,2),(3,4)],[(1,4)],[(0,1),(4,5)],[(0,5)]])
|
|
808
|
+
True
|
|
809
|
+
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]]]
|
|
810
|
+
|
|
811
|
+
We test groups for equality in a fool-proof way; they can have different generators, etc::
|
|
812
|
+
|
|
813
|
+
sage: # needs sage.groups
|
|
814
|
+
sage: poly_g = P.restricted_automorphism_group(output='matrix')
|
|
815
|
+
sage: matrix_g = MatrixGroup([matrix(QQ,t) for t in mgens])
|
|
816
|
+
sage: all(t.matrix() in poly_g for t in matrix_g.gens())
|
|
817
|
+
True
|
|
818
|
+
sage: all(t.matrix() in matrix_g for t in poly_g.gens())
|
|
819
|
+
True
|
|
820
|
+
|
|
821
|
+
24-cell example::
|
|
822
|
+
|
|
823
|
+
sage: # needs sage.groups
|
|
824
|
+
sage: P24 = polytopes.twenty_four_cell()
|
|
825
|
+
sage: AutP24 = P24.restricted_automorphism_group()
|
|
826
|
+
sage: PermutationGroup([
|
|
827
|
+
....: '(1,20,2,24,5,23)(3,18,10,19,4,14)(6,21,11,22,7,15)(8,12,16,17,13,9)',
|
|
828
|
+
....: '(1,21,8,24,4,17)(2,11,6,15,9,13)(3,20)(5,22)(10,16,12,23,14,19)'
|
|
829
|
+
....: ]).is_isomorphic(AutP24)
|
|
830
|
+
True
|
|
831
|
+
sage: AutP24.order()
|
|
832
|
+
1152
|
|
833
|
+
|
|
834
|
+
Here is the quadrant example mentioned in the beginning::
|
|
835
|
+
|
|
836
|
+
sage: # needs sage.groups
|
|
837
|
+
sage: P = Polyhedron(rays=[(1,0),(0,1)])
|
|
838
|
+
sage: P.Vrepresentation()
|
|
839
|
+
(A vertex at (0, 0), A ray in the direction (0, 1), A ray in the direction (1, 0))
|
|
840
|
+
sage: P.restricted_automorphism_group(output='permutation')
|
|
841
|
+
Permutation Group with generators [(1,2)]
|
|
842
|
+
|
|
843
|
+
Also, the polyhedron need not be full-dimensional::
|
|
844
|
+
|
|
845
|
+
sage: # needs sage.groups
|
|
846
|
+
sage: P = Polyhedron(vertices=[(1,2,3,4,5),(7,8,9,10,11)])
|
|
847
|
+
sage: P.restricted_automorphism_group()
|
|
848
|
+
Permutation Group with generators [(1,2)]
|
|
849
|
+
sage: G = P.restricted_automorphism_group(output='matrixlist'); G
|
|
850
|
+
(
|
|
851
|
+
[1 0 0 0 0 0] [ -87/55 -82/55 -2/5 38/55 98/55 12/11]
|
|
852
|
+
[0 1 0 0 0 0] [-142/55 -27/55 -2/5 38/55 98/55 12/11]
|
|
853
|
+
[0 0 1 0 0 0] [-142/55 -82/55 3/5 38/55 98/55 12/11]
|
|
854
|
+
[0 0 0 1 0 0] [-142/55 -82/55 -2/5 93/55 98/55 12/11]
|
|
855
|
+
[0 0 0 0 1 0] [-142/55 -82/55 -2/5 38/55 153/55 12/11]
|
|
856
|
+
[0 0 0 0 0 1], [ 0 0 0 0 0 1]
|
|
857
|
+
)
|
|
858
|
+
sage: g = AffineGroup(5, QQ)(G[1]); g
|
|
859
|
+
[ -87/55 -82/55 -2/5 38/55 98/55] [12/11]
|
|
860
|
+
[-142/55 -27/55 -2/5 38/55 98/55] [12/11]
|
|
861
|
+
x |-> [-142/55 -82/55 3/5 38/55 98/55] x + [12/11]
|
|
862
|
+
[-142/55 -82/55 -2/5 93/55 98/55] [12/11]
|
|
863
|
+
[-142/55 -82/55 -2/5 38/55 153/55] [12/11]
|
|
864
|
+
sage: g^2
|
|
865
|
+
[1 0 0 0 0] [0]
|
|
866
|
+
[0 1 0 0 0] [0]
|
|
867
|
+
x |-> [0 0 1 0 0] x + [0]
|
|
868
|
+
[0 0 0 1 0] [0]
|
|
869
|
+
[0 0 0 0 1] [0]
|
|
870
|
+
sage: g(list(P.vertices()[0]))
|
|
871
|
+
(7, 8, 9, 10, 11)
|
|
872
|
+
sage: g(list(P.vertices()[1]))
|
|
873
|
+
(1, 2, 3, 4, 5)
|
|
874
|
+
|
|
875
|
+
Affine transformations do not change the restricted automorphism
|
|
876
|
+
group. For example, any non-degenerate triangle has the
|
|
877
|
+
dihedral group with 6 elements, `D_6`, as its automorphism
|
|
878
|
+
group::
|
|
879
|
+
|
|
880
|
+
sage: # needs sage.groups
|
|
881
|
+
sage: initial_points = [vector([1,0]), vector([0,1]), vector([-2,-1])]
|
|
882
|
+
sage: points = initial_points
|
|
883
|
+
sage: Polyhedron(vertices=points).restricted_automorphism_group()
|
|
884
|
+
Permutation Group with generators [(2,3), (1,2)]
|
|
885
|
+
sage: points = [pt - initial_points[0] for pt in initial_points]
|
|
886
|
+
sage: Polyhedron(vertices=points).restricted_automorphism_group()
|
|
887
|
+
Permutation Group with generators [(2,3), (1,2)]
|
|
888
|
+
sage: points = [pt - initial_points[1] for pt in initial_points]
|
|
889
|
+
sage: Polyhedron(vertices=points).restricted_automorphism_group()
|
|
890
|
+
Permutation Group with generators [(2,3), (1,2)]
|
|
891
|
+
sage: points = [pt - 2*initial_points[1] for pt in initial_points]
|
|
892
|
+
sage: Polyhedron(vertices=points).restricted_automorphism_group()
|
|
893
|
+
Permutation Group with generators [(2,3), (1,2)]
|
|
894
|
+
|
|
895
|
+
The ``output="matrixlist"`` can be used over fields without a
|
|
896
|
+
complete implementation of matrix groups::
|
|
897
|
+
|
|
898
|
+
sage: # needs sage.groups sage.rings.number_field
|
|
899
|
+
sage: P = polytopes.dodecahedron(); P
|
|
900
|
+
A 3-dimensional polyhedron in (Number Field in sqrt5 with defining
|
|
901
|
+
polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^3
|
|
902
|
+
defined as the convex hull of 20 vertices
|
|
903
|
+
sage: G = P.restricted_automorphism_group(output='matrixlist')
|
|
904
|
+
sage: len(G)
|
|
905
|
+
120
|
|
906
|
+
|
|
907
|
+
Floating-point computations are supported with a simple fuzzy
|
|
908
|
+
zero implementation::
|
|
909
|
+
|
|
910
|
+
sage: P = Polyhedron(vertices=[(1/3,0,0,1),(0,1/4,0,1),(0,0,1/5,1)],
|
|
911
|
+
....: base_ring=RDF)
|
|
912
|
+
sage: P.restricted_automorphism_group() # needs sage.groups
|
|
913
|
+
Permutation Group with generators [(2,3), (1,2)]
|
|
914
|
+
sage: len(P.restricted_automorphism_group(output='matrixlist'))
|
|
915
|
+
6
|
|
916
|
+
|
|
917
|
+
TESTS::
|
|
918
|
+
|
|
919
|
+
sage: P = Polyhedron(vertices=[(1,0), (1,1)], rays=[(1,0)])
|
|
920
|
+
sage: P.restricted_automorphism_group(output='permutation') # needs sage.groups
|
|
921
|
+
Permutation Group with generators [(1,2)]
|
|
922
|
+
sage: P.restricted_automorphism_group(output='matrix')
|
|
923
|
+
Matrix group over Rational Field with 1 generators (
|
|
924
|
+
[ 1 0 0]
|
|
925
|
+
[ 0 -1 1]
|
|
926
|
+
[ 0 0 1]
|
|
927
|
+
)
|
|
928
|
+
sage: P.restricted_automorphism_group(output='foobar')
|
|
929
|
+
Traceback (most recent call last):
|
|
930
|
+
...
|
|
931
|
+
ValueError: unknown output 'foobar', valid values are
|
|
932
|
+
('abstract', 'permutation', 'matrix', 'matrixlist')
|
|
933
|
+
|
|
934
|
+
Check that :issue:`28828` is fixed::
|
|
935
|
+
|
|
936
|
+
sage: P.restricted_automorphism_group(output='matrixlist')[0].is_immutable()
|
|
937
|
+
True
|
|
938
|
+
"""
|
|
939
|
+
# The algorithm works as follows:
|
|
940
|
+
#
|
|
941
|
+
# Let V be the matrix where every column is a homogeneous
|
|
942
|
+
# coordinate of a V-representation object (vertex, ray, line).
|
|
943
|
+
# Let us assume that V has full rank, that the polyhedron is
|
|
944
|
+
# full dimensional.
|
|
945
|
+
#
|
|
946
|
+
# Let Q = V Vt and C = Vt Q^-1 V. The rows and columns of C
|
|
947
|
+
# can be thought of as being indexed by the V-rep objects of the
|
|
948
|
+
# polytope.
|
|
949
|
+
#
|
|
950
|
+
# It turns out that we can identify the restricted automorphism
|
|
951
|
+
# group with the automorphism group of the edge-colored graph
|
|
952
|
+
# on the V-rep objects with colors determined by the symmetric
|
|
953
|
+
# matrix C.
|
|
954
|
+
#
|
|
955
|
+
# An automorphism of this graph is equivalent to a permutation
|
|
956
|
+
# matrix P such that C = Pt C P. If we now define
|
|
957
|
+
# A = V P Vt Q^-1, then one can check that V P = A V.
|
|
958
|
+
# In other words: permuting the generators is the same as
|
|
959
|
+
# applying the affine transformation A on the generators.
|
|
960
|
+
#
|
|
961
|
+
# If the given polyhedron is not fully-dimensional,
|
|
962
|
+
# then Q will be not invertible. In this case, we use a
|
|
963
|
+
# pseudoinverse Q+ instead of Q^-1. The formula for A acting on
|
|
964
|
+
# the space spanned by V then simplifies to A = V P V+ where V+
|
|
965
|
+
# denotes the pseudoinverse of V, which also equals V+ = Vt Q+.
|
|
966
|
+
#
|
|
967
|
+
# If we are asked to return the (group of) transformation
|
|
968
|
+
# matrices to the user, we also require that those
|
|
969
|
+
# transformations act as the identity on the orthogonal
|
|
970
|
+
# complement of the space spanned by V. This complement is the
|
|
971
|
+
# space spanned by the columns of W = 1 - V V+. One can check
|
|
972
|
+
# that B = (V P V+) + W is the correct matrix: it acts the same
|
|
973
|
+
# as A on V and it satisfies B W = W.
|
|
974
|
+
|
|
975
|
+
outputs = ("abstract", "permutation", "matrix", "matrixlist")
|
|
976
|
+
if output not in outputs:
|
|
977
|
+
raise ValueError("unknown output {!r}, valid values are {}".format(output, outputs))
|
|
978
|
+
|
|
979
|
+
# For backwards compatibility, we treat "abstract" as
|
|
980
|
+
# "permutation", but where we add 1 to the indices of the
|
|
981
|
+
# permutations.
|
|
982
|
+
index0 = 0
|
|
983
|
+
if output == "abstract":
|
|
984
|
+
index0 = 1
|
|
985
|
+
output = "permutation"
|
|
986
|
+
|
|
987
|
+
if self.base_ring().is_exact():
|
|
988
|
+
def rational_approximation(c):
|
|
989
|
+
return c
|
|
990
|
+
else:
|
|
991
|
+
c_list = []
|
|
992
|
+
|
|
993
|
+
def rational_approximation(c):
|
|
994
|
+
# Implementation detail: Return unique integer if two
|
|
995
|
+
# c-values are the same up to machine precision. But
|
|
996
|
+
# you can think of it as a uniquely-chosen rational
|
|
997
|
+
# approximation.
|
|
998
|
+
for i, x in enumerate(c_list):
|
|
999
|
+
if self._is_zero(x - c):
|
|
1000
|
+
return i
|
|
1001
|
+
c_list.append(c)
|
|
1002
|
+
return len(c_list) - 1
|
|
1003
|
+
|
|
1004
|
+
if self.is_compact():
|
|
1005
|
+
def edge_label(i, j, c_ij):
|
|
1006
|
+
return c_ij
|
|
1007
|
+
else:
|
|
1008
|
+
# In the non-compact case, we also label the edges by the
|
|
1009
|
+
# type of the V-representation object. This ensures that
|
|
1010
|
+
# vertices, rays, and lines are only permuted amongst
|
|
1011
|
+
# themselves.
|
|
1012
|
+
def edge_label(i, j, c_ij):
|
|
1013
|
+
return (self.Vrepresentation(i).type(), c_ij, self.Vrepresentation(j).type())
|
|
1014
|
+
|
|
1015
|
+
# Homogeneous coordinates for the V-representation objects.
|
|
1016
|
+
# Mathematically, V is a matrix. For efficiency however, we
|
|
1017
|
+
# represent it as a list of column vectors.
|
|
1018
|
+
V = [v.homogeneous_vector() for v in self.Vrepresentation()]
|
|
1019
|
+
|
|
1020
|
+
# Pseudoinverse of V Vt
|
|
1021
|
+
Qplus = sum(v.column() * v.row() for v in V).pseudoinverse()
|
|
1022
|
+
|
|
1023
|
+
# Construct the graph.
|
|
1024
|
+
from sage.graphs.graph import Graph
|
|
1025
|
+
G = Graph()
|
|
1026
|
+
for i in range(len(V)):
|
|
1027
|
+
for j in range(i+1, len(V)):
|
|
1028
|
+
c_ij = rational_approximation(V[i] * Qplus * V[j])
|
|
1029
|
+
G.add_edge(index0+i, index0+j, edge_label(i, j, c_ij))
|
|
1030
|
+
|
|
1031
|
+
permgroup = G.automorphism_group(edge_labels=True)
|
|
1032
|
+
if output == "permutation":
|
|
1033
|
+
return permgroup
|
|
1034
|
+
elif output == "matrix":
|
|
1035
|
+
permgroup = permgroup.gens()
|
|
1036
|
+
|
|
1037
|
+
# Compute V+ = Vt Q+ as list of row vectors
|
|
1038
|
+
from sage.matrix.constructor import matrix
|
|
1039
|
+
Vplus = list(matrix(V) * Qplus) # matrix(V) is Vt
|
|
1040
|
+
|
|
1041
|
+
# Compute W = 1 - V V+
|
|
1042
|
+
W = 1 - sum(V[i].column() * Vplus[i].row() for i in range(len(V)))
|
|
1043
|
+
|
|
1044
|
+
# Convert the permutation group to a matrix group.
|
|
1045
|
+
# If P is a permutation, then we return the matrix
|
|
1046
|
+
# B = (V P V+) + W.
|
|
1047
|
+
#
|
|
1048
|
+
# If output == "matrix", we loop over the generators of the group.
|
|
1049
|
+
# Otherwise, we loop over all elements.
|
|
1050
|
+
matrices = []
|
|
1051
|
+
for perm in permgroup:
|
|
1052
|
+
A = sum(V[perm(i)].column() * Vplus[i].row() for i in range(len(V)))
|
|
1053
|
+
matrices.append(A + W)
|
|
1054
|
+
|
|
1055
|
+
for mat in matrices:
|
|
1056
|
+
mat.set_immutable()
|
|
1057
|
+
|
|
1058
|
+
if output == "matrixlist":
|
|
1059
|
+
return tuple(matrices)
|
|
1060
|
+
else:
|
|
1061
|
+
from sage.groups.matrix_gps.finitely_generated import MatrixGroup
|
|
1062
|
+
return MatrixGroup(matrices)
|
|
1063
|
+
|
|
1064
|
+
def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'):
|
|
1065
|
+
r"""
|
|
1066
|
+
Return whether the polyhedron is combinatorially isomorphic to another polyhedron.
|
|
1067
|
+
|
|
1068
|
+
We only consider bounded polyhedra. By definition, they are
|
|
1069
|
+
combinatorially isomorphic if their face lattices are isomorphic.
|
|
1070
|
+
|
|
1071
|
+
INPUT:
|
|
1072
|
+
|
|
1073
|
+
- ``other`` -- a polyhedron object
|
|
1074
|
+
- ``algorithm`` -- (default: ``'bipartite_graph'``) the algorithm to
|
|
1075
|
+
use; the other possible value is ``'face_lattice'``
|
|
1076
|
+
|
|
1077
|
+
OUTPUT:
|
|
1078
|
+
|
|
1079
|
+
- ``True`` if the two polyhedra are combinatorially isomorphic
|
|
1080
|
+
- ``False`` otherwise
|
|
1081
|
+
|
|
1082
|
+
.. SEEALSO::
|
|
1083
|
+
|
|
1084
|
+
:meth:`combinatorial_automorphism_group`,
|
|
1085
|
+
:meth:`vertex_facet_graph`.
|
|
1086
|
+
|
|
1087
|
+
REFERENCES:
|
|
1088
|
+
|
|
1089
|
+
For the equivalence of the two algorithms see [KK1995]_, p. 877-878
|
|
1090
|
+
|
|
1091
|
+
EXAMPLES:
|
|
1092
|
+
|
|
1093
|
+
The square is combinatorially isomorphic to the 2-dimensional cube::
|
|
1094
|
+
|
|
1095
|
+
sage: polytopes.hypercube(2).is_combinatorially_isomorphic(polytopes.regular_polygon(4))
|
|
1096
|
+
True
|
|
1097
|
+
|
|
1098
|
+
All the faces of the 3-dimensional permutahedron are either
|
|
1099
|
+
combinatorially isomorphic to a square or a hexagon::
|
|
1100
|
+
|
|
1101
|
+
sage: H = polytopes.regular_polygon(6) # needs sage.rings.number_field
|
|
1102
|
+
sage: S = polytopes.hypercube(2)
|
|
1103
|
+
sage: P = polytopes.permutahedron(4)
|
|
1104
|
+
sage: all(F.as_polyhedron().is_combinatorially_isomorphic(S) # needs sage.rings.number_field
|
|
1105
|
+
....: or F.as_polyhedron().is_combinatorially_isomorphic(H)
|
|
1106
|
+
....: for F in P.faces(2))
|
|
1107
|
+
True
|
|
1108
|
+
|
|
1109
|
+
Checking that a regular simplex intersected with its reflection
|
|
1110
|
+
through the origin is combinatorially isomorphic to the intersection
|
|
1111
|
+
of a cube with a hyperplane perpendicular to its long diagonal::
|
|
1112
|
+
|
|
1113
|
+
sage: def simplex_intersection(k):
|
|
1114
|
+
....: S1 = Polyhedron([vector(v)-vector(polytopes.simplex(k).center()) for v in polytopes.simplex(k).vertices_list()])
|
|
1115
|
+
....: S2 = Polyhedron([-vector(v) for v in S1.vertices_list()])
|
|
1116
|
+
....: return S1.intersection(S2)
|
|
1117
|
+
sage: def cube_intersection(k):
|
|
1118
|
+
....: C = polytopes.hypercube(k+1)
|
|
1119
|
+
....: H = Polyhedron(eqns=[[0]+[1 for i in range(k+1)]])
|
|
1120
|
+
....: return C.intersection(H)
|
|
1121
|
+
sage: [simplex_intersection(k).is_combinatorially_isomorphic(cube_intersection(k)) for k in range(2,5)]
|
|
1122
|
+
[True, True, True]
|
|
1123
|
+
sage: simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # needs sage.rings.number_field
|
|
1124
|
+
True
|
|
1125
|
+
sage: simplex_intersection(3).is_combinatorially_isomorphic(polytopes.octahedron())
|
|
1126
|
+
True
|
|
1127
|
+
|
|
1128
|
+
Two polytopes with the same `f`-vector, but different combinatorial types::
|
|
1129
|
+
|
|
1130
|
+
sage: P = Polyhedron([[-605520/1525633, -605520/1525633, -1261500/1525633, -52200/1525633, 11833/1525633],\
|
|
1131
|
+
....: [-720/1769, -600/1769, 1500/1769, 0, -31/1769], [-216/749, 240/749, -240/749, -432/749, 461/749], \
|
|
1132
|
+
....: [-50/181, 50/181, 60/181, -100/181, -119/181], [-32/51, -16/51, -4/51, 12/17, 1/17],\
|
|
1133
|
+
....: [1, 0, 0, 0, 0], [16/129, 128/129, 0, 0, 1/129], [64/267, -128/267, 24/89, -128/267, 57/89],\
|
|
1134
|
+
....: [1200/3953, -1200/3953, -1440/3953, -360/3953, -3247/3953], [1512/5597, 1512/5597, 588/5597, 4704/5597, 2069/5597]])
|
|
1135
|
+
sage: C = polytopes.cyclic_polytope(5,10)
|
|
1136
|
+
sage: C.f_vector() == P.f_vector(); C.f_vector()
|
|
1137
|
+
True
|
|
1138
|
+
(1, 10, 45, 100, 105, 42, 1)
|
|
1139
|
+
sage: C.is_combinatorially_isomorphic(P)
|
|
1140
|
+
False
|
|
1141
|
+
|
|
1142
|
+
sage: S = polytopes.simplex(3)
|
|
1143
|
+
sage: S = S.face_truncation(S.faces(0)[3])
|
|
1144
|
+
sage: S = S.face_truncation(S.faces(0)[4])
|
|
1145
|
+
sage: S = S.face_truncation(S.faces(0)[5])
|
|
1146
|
+
sage: T = polytopes.simplex(3)
|
|
1147
|
+
sage: T = T.face_truncation(T.faces(0)[3])
|
|
1148
|
+
sage: T = T.face_truncation(T.faces(0)[4])
|
|
1149
|
+
sage: T = T.face_truncation(T.faces(0)[4])
|
|
1150
|
+
sage: T.is_combinatorially_isomorphic(S)
|
|
1151
|
+
False
|
|
1152
|
+
sage: T.f_vector(), S.f_vector()
|
|
1153
|
+
((1, 10, 15, 7, 1), (1, 10, 15, 7, 1))
|
|
1154
|
+
|
|
1155
|
+
sage: C = polytopes.hypercube(5)
|
|
1156
|
+
sage: C.is_combinatorially_isomorphic(C)
|
|
1157
|
+
True
|
|
1158
|
+
sage: C.is_combinatorially_isomorphic(C, algorithm='magic')
|
|
1159
|
+
Traceback (most recent call last):
|
|
1160
|
+
...
|
|
1161
|
+
AssertionError: `algorithm` must be 'bipartite graph' or 'face_lattice'
|
|
1162
|
+
|
|
1163
|
+
sage: G = Graph()
|
|
1164
|
+
sage: C.is_combinatorially_isomorphic(G)
|
|
1165
|
+
Traceback (most recent call last):
|
|
1166
|
+
...
|
|
1167
|
+
AssertionError: input `other` must be a polyhedron
|
|
1168
|
+
|
|
1169
|
+
sage: H = Polyhedron(eqns=[[0,1,1,1,1]]); H
|
|
1170
|
+
A 3-dimensional polyhedron in QQ^4 defined as the convex hull of 1 vertex and 3 lines
|
|
1171
|
+
sage: C.is_combinatorially_isomorphic(H)
|
|
1172
|
+
Traceback (most recent call last):
|
|
1173
|
+
...
|
|
1174
|
+
AssertionError: polyhedron `other` must be bounded
|
|
1175
|
+
"""
|
|
1176
|
+
assert isinstance(other, Polyhedron_base4), "input `other` must be a polyhedron"
|
|
1177
|
+
assert self.is_compact(), "polyhedron `self` must be bounded"
|
|
1178
|
+
assert other.is_compact(), "polyhedron `other` must be bounded"
|
|
1179
|
+
assert algorithm in ['bipartite_graph', 'face_lattice'], "`algorithm` must be 'bipartite graph' or 'face_lattice'"
|
|
1180
|
+
|
|
1181
|
+
# For speed, we check if the polyhedra have the same number of facets and vertices.
|
|
1182
|
+
# This is faster than building the bipartite graphs first and
|
|
1183
|
+
# then check that they won't be isomorphic.
|
|
1184
|
+
if self.n_vertices() != other.n_vertices() or self.n_facets() != other.n_facets():
|
|
1185
|
+
return False
|
|
1186
|
+
|
|
1187
|
+
if algorithm == 'bipartite_graph':
|
|
1188
|
+
G_self = self.vertex_facet_graph(False)
|
|
1189
|
+
G_other = other.vertex_facet_graph(False)
|
|
1190
|
+
|
|
1191
|
+
return G_self.is_isomorphic(G_other)
|
|
1192
|
+
else:
|
|
1193
|
+
return self.face_lattice().is_isomorphic(other.face_lattice())
|
|
1194
|
+
|
|
1195
|
+
def _test_is_combinatorially_isomorphic(self, tester=None, **options):
|
|
1196
|
+
"""
|
|
1197
|
+
Run tests on the method :meth:`.is_combinatorially_isomorphic`.
|
|
1198
|
+
|
|
1199
|
+
TESTS::
|
|
1200
|
+
|
|
1201
|
+
sage: polytopes.cross_polytope(3)._test_is_combinatorially_isomorphic()
|
|
1202
|
+
"""
|
|
1203
|
+
if tester is None:
|
|
1204
|
+
tester = self._tester(**options)
|
|
1205
|
+
|
|
1206
|
+
if not self.is_compact():
|
|
1207
|
+
with tester.assertRaises(AssertionError):
|
|
1208
|
+
self.is_combinatorially_isomorphic(self)
|
|
1209
|
+
return
|
|
1210
|
+
|
|
1211
|
+
if self.n_vertices() > 200 or self.n_facets() > 200:
|
|
1212
|
+
# Avoid very long doctests.
|
|
1213
|
+
return
|
|
1214
|
+
|
|
1215
|
+
try:
|
|
1216
|
+
import sage.graphs.graph
|
|
1217
|
+
except ImportError:
|
|
1218
|
+
return
|
|
1219
|
+
|
|
1220
|
+
from sage.rings.integer_ring import ZZ
|
|
1221
|
+
tester.assertTrue(self.is_combinatorially_isomorphic(ZZ(4)*self))
|
|
1222
|
+
if self.n_vertices():
|
|
1223
|
+
tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center()))
|
|
1224
|
+
|
|
1225
|
+
if self.n_vertices() < 20 and self.n_facets() < 20 and self.is_immutable():
|
|
1226
|
+
tester.assertTrue(self.is_combinatorially_isomorphic(ZZ(4)*self, algorithm='face_lattice'))
|
|
1227
|
+
if self.n_vertices():
|
|
1228
|
+
tester.assertTrue(self.is_combinatorially_isomorphic(self + self.center(), algorithm='face_lattice'))
|
|
1229
|
+
|
|
1230
|
+
def is_self_dual(self):
|
|
1231
|
+
r"""
|
|
1232
|
+
Return whether the polytope is self-dual.
|
|
1233
|
+
|
|
1234
|
+
A polytope is self-dual if its face lattice is isomorphic to the face
|
|
1235
|
+
lattice of its dual polytope.
|
|
1236
|
+
|
|
1237
|
+
EXAMPLES::
|
|
1238
|
+
|
|
1239
|
+
sage: polytopes.simplex().is_self_dual()
|
|
1240
|
+
True
|
|
1241
|
+
sage: polytopes.twenty_four_cell().is_self_dual()
|
|
1242
|
+
True
|
|
1243
|
+
sage: polytopes.cube().is_self_dual()
|
|
1244
|
+
False
|
|
1245
|
+
sage: polytopes.hypersimplex(5,2).is_self_dual() # needs sage.combinat
|
|
1246
|
+
False
|
|
1247
|
+
sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]).is_self_dual()
|
|
1248
|
+
Traceback (most recent call last):
|
|
1249
|
+
...
|
|
1250
|
+
ValueError: polyhedron has to be compact
|
|
1251
|
+
"""
|
|
1252
|
+
if not self.is_compact():
|
|
1253
|
+
raise ValueError("polyhedron has to be compact")
|
|
1254
|
+
|
|
1255
|
+
n = self.n_vertices()
|
|
1256
|
+
m = self.n_facets()
|
|
1257
|
+
if n != m:
|
|
1258
|
+
return False
|
|
1259
|
+
|
|
1260
|
+
G1 = self.vertex_facet_graph()
|
|
1261
|
+
G2 = G1.reverse()
|
|
1262
|
+
return G1.is_isomorphic(G2)
|