passagemath-polyhedra 10.6.31rc3__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.
Potentially problematic release.
This version of passagemath-polyhedra might be problematic. Click here for more details.
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +208 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -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 +3895 -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,1257 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
r"""
|
|
3
|
+
Fast Lattice Polytopes using PPL.
|
|
4
|
+
|
|
5
|
+
The :func:`LatticePolytope_PPL` class is a thin wrapper around PPL
|
|
6
|
+
polyhedra. Its main purpose is to be fast to construct, at the cost of
|
|
7
|
+
being much less full-featured than the usual polyhedra. This makes it
|
|
8
|
+
possible to iterate with it over the list of all 473800776 reflexive
|
|
9
|
+
polytopes in 4 dimensions.
|
|
10
|
+
|
|
11
|
+
.. NOTE::
|
|
12
|
+
|
|
13
|
+
For general lattice polyhedra you should use
|
|
14
|
+
:func:`~sage.geometry.polyhedron.constructor.Polyhedron` with
|
|
15
|
+
``base_ring=ZZ``.
|
|
16
|
+
|
|
17
|
+
The class derives from the PPL :class:`ppl.polyhedron.C_Polyhedron`
|
|
18
|
+
class, so you can work with the underlying generator and constraint
|
|
19
|
+
objects. However, integral points are generally represented by
|
|
20
|
+
`\ZZ`-vectors. In the following, we always use *generator* to refer
|
|
21
|
+
the PPL generator objects and *vertex* (or integral point) for the
|
|
22
|
+
corresponding `\ZZ`-vector.
|
|
23
|
+
|
|
24
|
+
EXAMPLES::
|
|
25
|
+
|
|
26
|
+
sage: vertices = [(1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1), (-9, -6, -1, -1)]
|
|
27
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
28
|
+
sage: P = LatticePolytope_PPL(vertices); P
|
|
29
|
+
A 4-dimensional lattice polytope in ZZ^4 with 5 vertices
|
|
30
|
+
sage: P.integral_points()
|
|
31
|
+
((-9, -6, -1, -1), (-3, -2, 0, 0), (-2, -1, 0, 0), (-1, -1, 0, 0),
|
|
32
|
+
(-1, 0, 0, 0), (0, 0, 0, 0), (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0))
|
|
33
|
+
sage: P.integral_points_not_interior_to_facets()
|
|
34
|
+
((-9, -6, -1, -1), (-3, -2, 0, 0), (0, 0, 0, 0), (1, 0, 0, 0),
|
|
35
|
+
(0, 1, 0, 0), (0, 0, 0, 1), (0, 0, 1, 0))
|
|
36
|
+
|
|
37
|
+
Fibrations of the lattice polytopes are defined as lattice
|
|
38
|
+
sub-polytopes and give rise to fibrations of toric varieties for
|
|
39
|
+
suitable fan refinements. We can compute them using
|
|
40
|
+
:meth:`~LatticePolytope_PPL.fibration_generator` ::
|
|
41
|
+
|
|
42
|
+
sage: F = next(P.fibration_generator(2))
|
|
43
|
+
sage: F.vertices()
|
|
44
|
+
((1, 0, 0, 0), (0, 1, 0, 0), (-3, -2, 0, 0))
|
|
45
|
+
|
|
46
|
+
Finally, we can compute automorphisms and identify fibrations that
|
|
47
|
+
only differ by a lattice automorphism::
|
|
48
|
+
|
|
49
|
+
sage: square = LatticePolytope_PPL((-1,-1), (-1,1), (1,-1), (1,1))
|
|
50
|
+
sage: fibers = [ f.vertices() for f in square.fibration_generator(1) ]; fibers
|
|
51
|
+
[((1, 0), (-1, 0)), ((0, 1), (0, -1)), ((-1, -1), (1, 1)), ((-1, 1), (1, -1))]
|
|
52
|
+
sage: square.pointsets_mod_automorphism(fibers) # needs sage.groups
|
|
53
|
+
(frozenset({(-1, -1), (1, 1)}), frozenset({(-1, 0), (1, 0)}))
|
|
54
|
+
|
|
55
|
+
AUTHORS:
|
|
56
|
+
|
|
57
|
+
- Volker Braun: initial version, 2012
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
# ****************************************************************************
|
|
61
|
+
# Copyright (C) 2012 Volker Braun <vbraun.name@gmail.com>
|
|
62
|
+
#
|
|
63
|
+
# This program is free software: you can redistribute it and/or modify
|
|
64
|
+
# it under the terms of the GNU General Public License as published by
|
|
65
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
66
|
+
# (at your option) any later version.
|
|
67
|
+
# https://www.gnu.org/licenses/
|
|
68
|
+
# ****************************************************************************
|
|
69
|
+
import copy
|
|
70
|
+
from sage.rings.integer import GCD_list, Integer
|
|
71
|
+
from sage.rings.integer_ring import ZZ
|
|
72
|
+
from sage.misc.cachefunc import cached_method
|
|
73
|
+
from sage.modules.free_module_element import vector
|
|
74
|
+
from sage.matrix.constructor import matrix
|
|
75
|
+
from ppl import (
|
|
76
|
+
C_Polyhedron, Linear_Expression, Variable,
|
|
77
|
+
point, line, Generator, Generator_System,
|
|
78
|
+
Poly_Con_Relation )
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
########################################################################
|
|
82
|
+
def _class_for_LatticePolytope(dim):
|
|
83
|
+
"""
|
|
84
|
+
Return the appropriate class in the given dimension.
|
|
85
|
+
|
|
86
|
+
Helper function for :func:`LatticePolytope_PPL`. You should not
|
|
87
|
+
have to use this function manually.
|
|
88
|
+
|
|
89
|
+
INPUT:
|
|
90
|
+
|
|
91
|
+
- ``dim`` -- integer; the ambient space dimension
|
|
92
|
+
|
|
93
|
+
OUTPUT: the appropriate class for the lattice polytope
|
|
94
|
+
|
|
95
|
+
EXAMPLES::
|
|
96
|
+
|
|
97
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import _class_for_LatticePolytope
|
|
98
|
+
sage: _class_for_LatticePolytope(2)
|
|
99
|
+
<class 'sage.geometry.polyhedron.ppl_lattice_polygon.LatticePolygon_PPL_class'>
|
|
100
|
+
sage: _class_for_LatticePolytope(3)
|
|
101
|
+
<class 'sage.geometry.polyhedron.ppl_lattice_polytope.LatticePolytope_PPL_class'>
|
|
102
|
+
"""
|
|
103
|
+
if dim <= 2:
|
|
104
|
+
from sage.geometry.polyhedron.ppl_lattice_polygon import LatticePolygon_PPL_class
|
|
105
|
+
return LatticePolygon_PPL_class
|
|
106
|
+
return LatticePolytope_PPL_class
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
########################################################################
|
|
110
|
+
def LatticePolytope_PPL(*args):
|
|
111
|
+
"""
|
|
112
|
+
Construct a new instance of the PPL-based lattice polytope class.
|
|
113
|
+
|
|
114
|
+
EXAMPLES::
|
|
115
|
+
|
|
116
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
117
|
+
sage: LatticePolytope_PPL((0,0), (1,0), (0,1))
|
|
118
|
+
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
|
|
119
|
+
|
|
120
|
+
sage: from ppl import point, Generator_System, C_Polyhedron, Linear_Expression # needs pplpy
|
|
121
|
+
sage: p = point(Linear_Expression([2,3],0)); p # needs pplpy
|
|
122
|
+
point(2/1, 3/1)
|
|
123
|
+
sage: LatticePolytope_PPL(p) # needs pplpy
|
|
124
|
+
A 0-dimensional lattice polytope in ZZ^2 with 1 vertex
|
|
125
|
+
|
|
126
|
+
sage: P = C_Polyhedron(Generator_System(p)); P # needs pplpy
|
|
127
|
+
A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point
|
|
128
|
+
sage: LatticePolytope_PPL(P) # needs pplpy
|
|
129
|
+
A 0-dimensional lattice polytope in ZZ^2 with 1 vertex
|
|
130
|
+
|
|
131
|
+
A :exc:`TypeError` is raised if the arguments do not specify a lattice polytope::
|
|
132
|
+
|
|
133
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
134
|
+
sage: LatticePolytope_PPL((0,0), (1/2,1)) # needs pplpy
|
|
135
|
+
Traceback (most recent call last):
|
|
136
|
+
...
|
|
137
|
+
TypeError: unable to convert rational 1/2 to an integer
|
|
138
|
+
|
|
139
|
+
sage: from ppl import point, Generator_System, C_Polyhedron, Linear_Expression # needs pplpy
|
|
140
|
+
sage: p = point(Linear_Expression([2,3],0), 5); p # needs pplpy
|
|
141
|
+
point(2/5, 3/5)
|
|
142
|
+
sage: LatticePolytope_PPL(p) # needs pplpy
|
|
143
|
+
Traceback (most recent call last):
|
|
144
|
+
...
|
|
145
|
+
TypeError: generator is not a lattice polytope generator
|
|
146
|
+
|
|
147
|
+
sage: P = C_Polyhedron(Generator_System(p)); P # needs pplpy
|
|
148
|
+
A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point
|
|
149
|
+
sage: LatticePolytope_PPL(P) # needs pplpy
|
|
150
|
+
Traceback (most recent call last):
|
|
151
|
+
...
|
|
152
|
+
TypeError: polyhedron has non-integral generators
|
|
153
|
+
"""
|
|
154
|
+
polytope_class = LatticePolytope_PPL_class
|
|
155
|
+
if len(args) == 1 and isinstance(args[0], C_Polyhedron):
|
|
156
|
+
polyhedron = args[0]
|
|
157
|
+
polytope_class = _class_for_LatticePolytope(polyhedron.space_dimension())
|
|
158
|
+
if not all(p.is_point() and p.divisor() == 1 for p in polyhedron.generators()):
|
|
159
|
+
raise TypeError('polyhedron has non-integral generators')
|
|
160
|
+
return polytope_class(polyhedron)
|
|
161
|
+
if len(args) == 1 \
|
|
162
|
+
and isinstance(args[0], (list, tuple)) \
|
|
163
|
+
and isinstance(args[0][0], (list,tuple)):
|
|
164
|
+
vertices = args[0]
|
|
165
|
+
else:
|
|
166
|
+
vertices = args
|
|
167
|
+
gs = Generator_System()
|
|
168
|
+
for v in vertices:
|
|
169
|
+
if isinstance(v, Generator):
|
|
170
|
+
if (not v.is_point()) or v.divisor() != 1:
|
|
171
|
+
raise TypeError('generator is not a lattice polytope generator')
|
|
172
|
+
gs.insert(v)
|
|
173
|
+
else:
|
|
174
|
+
gs.insert(point(Linear_Expression(v, 0)))
|
|
175
|
+
if not gs.empty():
|
|
176
|
+
dim = next(iter(gs)).space_dimension()
|
|
177
|
+
polytope_class = _class_for_LatticePolytope(dim)
|
|
178
|
+
return polytope_class(gs)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
########################################################################
|
|
182
|
+
class LatticePolytope_PPL_class(C_Polyhedron):
|
|
183
|
+
"""
|
|
184
|
+
The lattice polytope class.
|
|
185
|
+
|
|
186
|
+
You should use :func:`LatticePolytope_PPL` to construct instances.
|
|
187
|
+
|
|
188
|
+
EXAMPLES::
|
|
189
|
+
|
|
190
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
191
|
+
sage: LatticePolytope_PPL((0,0), (1,0), (0,1))
|
|
192
|
+
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
def __repr__(self):
|
|
196
|
+
"""
|
|
197
|
+
Return the string representation.
|
|
198
|
+
|
|
199
|
+
OUTPUT: string
|
|
200
|
+
|
|
201
|
+
EXAMPLES::
|
|
202
|
+
|
|
203
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
204
|
+
sage: P = LatticePolytope_PPL((0,0), (1,0), (0,1))
|
|
205
|
+
sage: P
|
|
206
|
+
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
|
|
207
|
+
sage: P.__repr__()
|
|
208
|
+
'A 2-dimensional lattice polytope in ZZ^2 with 3 vertices'
|
|
209
|
+
|
|
210
|
+
sage: LatticePolytope_PPL()
|
|
211
|
+
The empty lattice polytope in ZZ^0
|
|
212
|
+
"""
|
|
213
|
+
desc = ''
|
|
214
|
+
if self.n_vertices() == 0:
|
|
215
|
+
desc += 'The empty lattice polytope'
|
|
216
|
+
else:
|
|
217
|
+
desc += 'A ' + repr(self.affine_dimension()) + '-dimensional lattice polytope'
|
|
218
|
+
desc += ' in ZZ^' + repr(self.space_dimension())
|
|
219
|
+
|
|
220
|
+
if self.n_vertices() > 0:
|
|
221
|
+
desc += ' with '
|
|
222
|
+
desc += repr(self.n_vertices())
|
|
223
|
+
if self.n_vertices() == 1:
|
|
224
|
+
desc += ' vertex'
|
|
225
|
+
else:
|
|
226
|
+
desc += ' vertices'
|
|
227
|
+
return desc
|
|
228
|
+
|
|
229
|
+
def is_bounded(self):
|
|
230
|
+
"""
|
|
231
|
+
Return whether the lattice polytope is compact.
|
|
232
|
+
|
|
233
|
+
OUTPUT: always ``True``, since polytopes are by definition compact
|
|
234
|
+
|
|
235
|
+
EXAMPLES::
|
|
236
|
+
|
|
237
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
238
|
+
sage: LatticePolytope_PPL((0,0), (1,0), (0,1)).is_bounded()
|
|
239
|
+
True
|
|
240
|
+
"""
|
|
241
|
+
return True
|
|
242
|
+
|
|
243
|
+
@cached_method
|
|
244
|
+
def n_vertices(self):
|
|
245
|
+
"""
|
|
246
|
+
Return the number of vertices.
|
|
247
|
+
|
|
248
|
+
OUTPUT: integer; the number of vertices
|
|
249
|
+
|
|
250
|
+
EXAMPLES::
|
|
251
|
+
|
|
252
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
253
|
+
sage: LatticePolytope_PPL((0,0,0), (1,0,0), (0,1,0)).n_vertices()
|
|
254
|
+
3
|
|
255
|
+
"""
|
|
256
|
+
return len(self.minimized_generators())
|
|
257
|
+
|
|
258
|
+
@cached_method
|
|
259
|
+
def is_simplex(self):
|
|
260
|
+
r"""
|
|
261
|
+
Return whether the polyhedron is a simplex.
|
|
262
|
+
|
|
263
|
+
OUTPUT:
|
|
264
|
+
|
|
265
|
+
Boolean, whether the polyhedron is a simplex (possibly of
|
|
266
|
+
strictly smaller dimension than the ambient space).
|
|
267
|
+
|
|
268
|
+
EXAMPLES::
|
|
269
|
+
|
|
270
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
271
|
+
sage: LatticePolytope_PPL((0,0,0), (1,0,0), (0,1,0)).is_simplex()
|
|
272
|
+
True
|
|
273
|
+
"""
|
|
274
|
+
return self.affine_dimension()+1 == self.n_vertices()
|
|
275
|
+
|
|
276
|
+
@cached_method
|
|
277
|
+
def bounding_box(self):
|
|
278
|
+
r"""
|
|
279
|
+
Return the coordinates of a rectangular box containing the non-empty polytope.
|
|
280
|
+
|
|
281
|
+
OUTPUT:
|
|
282
|
+
|
|
283
|
+
A pair of tuples ``(box_min, box_max)`` where ``box_min`` are
|
|
284
|
+
the coordinates of a point bounding the coordinates of the
|
|
285
|
+
polytope from below and ``box_max`` bounds the coordinates
|
|
286
|
+
from above.
|
|
287
|
+
|
|
288
|
+
EXAMPLES::
|
|
289
|
+
|
|
290
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
291
|
+
sage: LatticePolytope_PPL((0,0), (1,0), (0,1)).bounding_box()
|
|
292
|
+
((0, 0), (1, 1))
|
|
293
|
+
"""
|
|
294
|
+
box_min = []
|
|
295
|
+
box_max = []
|
|
296
|
+
if self.is_empty():
|
|
297
|
+
raise ValueError('empty polytope is not allowed')
|
|
298
|
+
for i in range(self.space_dimension()):
|
|
299
|
+
x = Variable(i)
|
|
300
|
+
coords = [Integer(v.coefficient(x)) for v in self.generators()]
|
|
301
|
+
max_coord = max(coords)
|
|
302
|
+
min_coord = min(coords)
|
|
303
|
+
box_max.append(max_coord)
|
|
304
|
+
box_min.append(min_coord)
|
|
305
|
+
return (tuple(box_min), tuple(box_max))
|
|
306
|
+
|
|
307
|
+
@cached_method
|
|
308
|
+
def n_integral_points(self):
|
|
309
|
+
"""
|
|
310
|
+
Return the number of integral points.
|
|
311
|
+
|
|
312
|
+
OUTPUT:
|
|
313
|
+
|
|
314
|
+
Integer. The number of integral points contained in the
|
|
315
|
+
lattice polytope.
|
|
316
|
+
|
|
317
|
+
EXAMPLES::
|
|
318
|
+
|
|
319
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
320
|
+
sage: LatticePolytope_PPL((0,0), (1,0), (0,1)).n_integral_points()
|
|
321
|
+
3
|
|
322
|
+
"""
|
|
323
|
+
if self.is_empty():
|
|
324
|
+
return tuple()
|
|
325
|
+
box_min, box_max = self.bounding_box()
|
|
326
|
+
from sage.geometry.integral_points import rectangular_box_points
|
|
327
|
+
return rectangular_box_points(list(box_min), list(box_max), self, count_only=True)
|
|
328
|
+
|
|
329
|
+
@cached_method
|
|
330
|
+
def integral_points(self):
|
|
331
|
+
r"""
|
|
332
|
+
Return the integral points in the polyhedron.
|
|
333
|
+
|
|
334
|
+
Uses the naive algorithm (iterate over a rectangular bounding
|
|
335
|
+
box).
|
|
336
|
+
|
|
337
|
+
OUTPUT:
|
|
338
|
+
|
|
339
|
+
The list of integral points in the polyhedron. If the
|
|
340
|
+
polyhedron is not compact, a :exc:`ValueError` is raised.
|
|
341
|
+
|
|
342
|
+
EXAMPLES::
|
|
343
|
+
|
|
344
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
345
|
+
sage: LatticePolytope_PPL((-1,-1), (1,0), (1,1), (0,1)).integral_points()
|
|
346
|
+
((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))
|
|
347
|
+
|
|
348
|
+
sage: simplex = LatticePolytope_PPL((1,2,3), (2,3,7), (-2,-3,-11))
|
|
349
|
+
sage: simplex.integral_points()
|
|
350
|
+
((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))
|
|
351
|
+
|
|
352
|
+
The polyhedron need not be full-dimensional::
|
|
353
|
+
|
|
354
|
+
sage: simplex = LatticePolytope_PPL((1,2,3,5), (2,3,7,5), (-2,-3,-11,5))
|
|
355
|
+
sage: simplex.integral_points()
|
|
356
|
+
((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))
|
|
357
|
+
|
|
358
|
+
sage: point = LatticePolytope_PPL((2,3,7))
|
|
359
|
+
sage: point.integral_points()
|
|
360
|
+
((2, 3, 7),)
|
|
361
|
+
|
|
362
|
+
sage: empty = LatticePolytope_PPL()
|
|
363
|
+
sage: empty.integral_points()
|
|
364
|
+
()
|
|
365
|
+
|
|
366
|
+
Here is a simplex where the naive algorithm of running over
|
|
367
|
+
all points in a rectangular bounding box no longer works fast
|
|
368
|
+
enough::
|
|
369
|
+
|
|
370
|
+
sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
|
|
371
|
+
sage: simplex = LatticePolytope_PPL(v); simplex
|
|
372
|
+
A 4-dimensional lattice polytope in ZZ^4 with 5 vertices
|
|
373
|
+
sage: len(simplex.integral_points())
|
|
374
|
+
49
|
|
375
|
+
|
|
376
|
+
Finally, the 3-d reflexive polytope number 4078::
|
|
377
|
+
|
|
378
|
+
sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),
|
|
379
|
+
....: (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]
|
|
380
|
+
sage: P = LatticePolytope_PPL(*v)
|
|
381
|
+
sage: pts1 = P.integral_points() # Sage's own code
|
|
382
|
+
sage: pts2 = LatticePolytope(v).points() # needs palp
|
|
383
|
+
sage: for p in pts1: p.set_immutable()
|
|
384
|
+
sage: set(pts1) == set(pts2) # needs palp
|
|
385
|
+
True
|
|
386
|
+
|
|
387
|
+
sage: len(Polyhedron(v).integral_points()) # takes about 1 ms
|
|
388
|
+
23
|
|
389
|
+
sage: len(LatticePolytope(v).points()) # takes about 13 ms # needs palp
|
|
390
|
+
23
|
|
391
|
+
sage: len(LatticePolytope_PPL(*v).integral_points()) # takes about 0.5 ms
|
|
392
|
+
23
|
|
393
|
+
"""
|
|
394
|
+
if self.is_empty():
|
|
395
|
+
return tuple()
|
|
396
|
+
box_min, box_max = self.bounding_box()
|
|
397
|
+
from sage.geometry.integral_points import rectangular_box_points
|
|
398
|
+
points = rectangular_box_points(list(box_min), list(box_max), self)
|
|
399
|
+
if not self.n_integral_points.is_in_cache():
|
|
400
|
+
self.n_integral_points.set_cache(len(points))
|
|
401
|
+
return points
|
|
402
|
+
|
|
403
|
+
@cached_method
|
|
404
|
+
def _integral_points_saturating(self):
|
|
405
|
+
"""
|
|
406
|
+
Return the integral points together with information about
|
|
407
|
+
which inequalities are saturated.
|
|
408
|
+
|
|
409
|
+
See :func:`~sage.geometry.integral_points.rectangular_box_points`.
|
|
410
|
+
|
|
411
|
+
OUTPUT:
|
|
412
|
+
|
|
413
|
+
A tuple of pairs (one for each integral point) consisting of a
|
|
414
|
+
pair ``(point, Hrep)``, where ``point`` is the coordinate
|
|
415
|
+
vector of the integral point and ``Hrep`` is the set of
|
|
416
|
+
indices of the :meth:`minimized_constraints` that are
|
|
417
|
+
saturated at the point.
|
|
418
|
+
|
|
419
|
+
EXAMPLES::
|
|
420
|
+
|
|
421
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
422
|
+
sage: quad = LatticePolytope_PPL((-1,-1), (0,1), (1,0), (1,1))
|
|
423
|
+
sage: quad._integral_points_saturating()
|
|
424
|
+
(((-1, -1), frozenset({0, 1})),
|
|
425
|
+
((0, 0), frozenset()),
|
|
426
|
+
((0, 1), frozenset({0, 3})),
|
|
427
|
+
((1, 0), frozenset({1, 2})),
|
|
428
|
+
((1, 1), frozenset({2, 3})))
|
|
429
|
+
"""
|
|
430
|
+
if self.is_empty():
|
|
431
|
+
return tuple()
|
|
432
|
+
box_min, box_max = self.bounding_box()
|
|
433
|
+
from sage.geometry.integral_points import rectangular_box_points
|
|
434
|
+
points = rectangular_box_points(list(box_min), list(box_max), self,
|
|
435
|
+
return_saturated=True)
|
|
436
|
+
if not self.n_integral_points.is_in_cache():
|
|
437
|
+
self.n_integral_points.set_cache(len(points))
|
|
438
|
+
if not self.integral_points.is_in_cache():
|
|
439
|
+
self.integral_points.set_cache(tuple(p[0] for p in points))
|
|
440
|
+
return points
|
|
441
|
+
|
|
442
|
+
@cached_method
|
|
443
|
+
def integral_points_not_interior_to_facets(self):
|
|
444
|
+
"""
|
|
445
|
+
Return the integral points not interior to facets.
|
|
446
|
+
|
|
447
|
+
OUTPUT:
|
|
448
|
+
|
|
449
|
+
A tuple whose entries are the coordinate vectors of integral
|
|
450
|
+
points not interior to facets (codimension one faces) of the
|
|
451
|
+
lattice polytope.
|
|
452
|
+
|
|
453
|
+
EXAMPLES::
|
|
454
|
+
|
|
455
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
456
|
+
sage: square = LatticePolytope_PPL((-1,-1), (-1,1), (1,-1), (1,1))
|
|
457
|
+
sage: square.n_integral_points()
|
|
458
|
+
9
|
|
459
|
+
sage: square.integral_points_not_interior_to_facets()
|
|
460
|
+
((-1, -1), (-1, 1), (0, 0), (1, -1), (1, 1))
|
|
461
|
+
"""
|
|
462
|
+
n = 1 + self.space_dimension() - self.affine_dimension()
|
|
463
|
+
return tuple(p[0] for p in self._integral_points_saturating() if len(p[1]) != n)
|
|
464
|
+
|
|
465
|
+
@cached_method
|
|
466
|
+
def vertices(self):
|
|
467
|
+
r"""
|
|
468
|
+
Return the vertices as a tuple of `\ZZ`-vectors.
|
|
469
|
+
|
|
470
|
+
OUTPUT:
|
|
471
|
+
|
|
472
|
+
A tuple of `\ZZ`-vectors. Each entry is the coordinate vector
|
|
473
|
+
of an integral points of the lattice polytope.
|
|
474
|
+
|
|
475
|
+
EXAMPLES::
|
|
476
|
+
|
|
477
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
478
|
+
sage: p = LatticePolytope_PPL((-9,-6,-1,-1),
|
|
479
|
+
....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
|
|
480
|
+
sage: p.vertices()
|
|
481
|
+
((-9, -6, -1, -1), (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0), (1, 0, 0, 0))
|
|
482
|
+
sage: p.minimized_generators()
|
|
483
|
+
Generator_System {point(-9/1, -6/1, -1/1, -1/1), point(0/1, 0/1, 0/1, 1/1),
|
|
484
|
+
point(0/1, 0/1, 1/1, 0/1), point(0/1, 1/1, 0/1, 0/1), point(1/1, 0/1, 0/1, 0/1)}
|
|
485
|
+
"""
|
|
486
|
+
d = self.space_dimension()
|
|
487
|
+
v = vector(ZZ, d)
|
|
488
|
+
points = []
|
|
489
|
+
for g in self.minimized_generators():
|
|
490
|
+
for i in range(d):
|
|
491
|
+
v[i] = g.coefficient(Variable(i))
|
|
492
|
+
v_copy = copy.copy(v)
|
|
493
|
+
v_copy.set_immutable()
|
|
494
|
+
points.append(v_copy)
|
|
495
|
+
return tuple(points)
|
|
496
|
+
|
|
497
|
+
def vertices_saturating(self, constraint):
|
|
498
|
+
r"""
|
|
499
|
+
Return the vertices saturating the constraint.
|
|
500
|
+
|
|
501
|
+
INPUT:
|
|
502
|
+
|
|
503
|
+
- ``constraint`` -- a constraint (inequality or equation) of
|
|
504
|
+
the polytope
|
|
505
|
+
|
|
506
|
+
OUTPUT:
|
|
507
|
+
|
|
508
|
+
The tuple of vertices saturating the constraint. The vertices
|
|
509
|
+
are returned as `\ZZ`-vectors, as in :meth:`vertices`.
|
|
510
|
+
|
|
511
|
+
EXAMPLES::
|
|
512
|
+
|
|
513
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
514
|
+
sage: p = LatticePolytope_PPL((0,0), (0,1), (1,0))
|
|
515
|
+
sage: ieq = next(iter(p.constraints())); ieq
|
|
516
|
+
x0>=0
|
|
517
|
+
sage: p.vertices_saturating(ieq)
|
|
518
|
+
((0, 0), (0, 1))
|
|
519
|
+
"""
|
|
520
|
+
from ppl import C_Polyhedron, Poly_Con_Relation
|
|
521
|
+
result = []
|
|
522
|
+
for i,v in enumerate(self.minimized_generators()):
|
|
523
|
+
v = C_Polyhedron(v)
|
|
524
|
+
if v.relation_with(constraint).implies(Poly_Con_Relation.saturates()):
|
|
525
|
+
result.append(self.vertices()[i])
|
|
526
|
+
return tuple(result)
|
|
527
|
+
|
|
528
|
+
@cached_method
|
|
529
|
+
def is_full_dimensional(self):
|
|
530
|
+
"""
|
|
531
|
+
Return whether the lattice polytope is full dimensional.
|
|
532
|
+
|
|
533
|
+
OUTPUT: boolean; whether the :meth:`affine_dimension` equals the
|
|
534
|
+
ambient space dimension
|
|
535
|
+
|
|
536
|
+
EXAMPLES::
|
|
537
|
+
|
|
538
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
539
|
+
sage: p = LatticePolytope_PPL((0,0), (0,1))
|
|
540
|
+
sage: p.is_full_dimensional()
|
|
541
|
+
False
|
|
542
|
+
sage: q = LatticePolytope_PPL((0,0), (0,1), (1,0))
|
|
543
|
+
sage: q.is_full_dimensional()
|
|
544
|
+
True
|
|
545
|
+
"""
|
|
546
|
+
|
|
547
|
+
return self.affine_dimension() == self.space_dimension()
|
|
548
|
+
|
|
549
|
+
def fibration_generator(self, dim):
|
|
550
|
+
"""
|
|
551
|
+
Generate the lattice polytope fibrations.
|
|
552
|
+
|
|
553
|
+
For the purposes of this function, a lattice polytope fiber is
|
|
554
|
+
a sub-lattice polytope. Projecting the plane spanned by the
|
|
555
|
+
subpolytope to a point yields another lattice polytope, the
|
|
556
|
+
base of the fibration.
|
|
557
|
+
|
|
558
|
+
INPUT:
|
|
559
|
+
|
|
560
|
+
- ``dim`` -- integer; the dimension of the lattice polytope
|
|
561
|
+
fiber
|
|
562
|
+
|
|
563
|
+
OUTPUT:
|
|
564
|
+
|
|
565
|
+
A generator yielding the distinct lattice polytope fibers of
|
|
566
|
+
given dimension.
|
|
567
|
+
|
|
568
|
+
EXAMPLES::
|
|
569
|
+
|
|
570
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
571
|
+
sage: p = LatticePolytope_PPL((-9,-6,-1,-1),
|
|
572
|
+
....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
|
|
573
|
+
sage: list(p.fibration_generator(2))
|
|
574
|
+
[A 2-dimensional lattice polytope in ZZ^4 with 3 vertices]
|
|
575
|
+
"""
|
|
576
|
+
assert self.is_full_dimensional()
|
|
577
|
+
# "points" are the potential vertices of the fiber. They are
|
|
578
|
+
# in the $codim$-skeleton of the polytope, which is contained
|
|
579
|
+
# in the points that saturate at least $dim$ equations.
|
|
580
|
+
points = [ p for p in self._integral_points_saturating() if len(p[1]) >= dim ]
|
|
581
|
+
points = sorted(points, key=lambda x:len(x[1]))
|
|
582
|
+
|
|
583
|
+
# iterate over point combinations subject to all points being on one facet.
|
|
584
|
+
def point_combinations_iterator(n, i0=0, saturated=None):
|
|
585
|
+
for i in range(i0, len(points)):
|
|
586
|
+
p, ieqs = points[i]
|
|
587
|
+
if saturated is None:
|
|
588
|
+
saturated_ieqs = ieqs
|
|
589
|
+
else:
|
|
590
|
+
saturated_ieqs = saturated.intersection(ieqs)
|
|
591
|
+
if len(saturated_ieqs) == 0:
|
|
592
|
+
continue
|
|
593
|
+
if n == 1:
|
|
594
|
+
yield [i]
|
|
595
|
+
else:
|
|
596
|
+
for c in point_combinations_iterator(n-1, i+1, saturated_ieqs):
|
|
597
|
+
yield [i] + c
|
|
598
|
+
|
|
599
|
+
point_lines = [ line(Linear_Expression(p[0].list(),0)) for p in points ]
|
|
600
|
+
origin = point()
|
|
601
|
+
fibers = set()
|
|
602
|
+
gs = Generator_System()
|
|
603
|
+
for indices in point_combinations_iterator(dim):
|
|
604
|
+
gs.clear()
|
|
605
|
+
gs.insert(origin)
|
|
606
|
+
for i in indices:
|
|
607
|
+
gs.insert(point_lines[i])
|
|
608
|
+
plane = C_Polyhedron(gs)
|
|
609
|
+
if plane.affine_dimension() != dim:
|
|
610
|
+
continue
|
|
611
|
+
plane.intersection_assign(self)
|
|
612
|
+
if (not self.is_full_dimensional()) and (plane.affine_dimension() != dim):
|
|
613
|
+
continue
|
|
614
|
+
try:
|
|
615
|
+
fiber = LatticePolytope_PPL(plane)
|
|
616
|
+
except TypeError: # not a lattice polytope
|
|
617
|
+
continue
|
|
618
|
+
fiber_vertices = tuple(sorted(fiber.vertices()))
|
|
619
|
+
if fiber_vertices not in fibers:
|
|
620
|
+
yield fiber
|
|
621
|
+
fibers.update([fiber_vertices])
|
|
622
|
+
|
|
623
|
+
def pointsets_mod_automorphism(self, pointsets):
|
|
624
|
+
"""
|
|
625
|
+
Return ``pointsets`` modulo the automorphisms of ``self``.
|
|
626
|
+
|
|
627
|
+
INPUT:
|
|
628
|
+
|
|
629
|
+
- ``polytopes`` -- tuple/list/iterable of subsets of the
|
|
630
|
+
integral points of ``self``
|
|
631
|
+
|
|
632
|
+
OUTPUT:
|
|
633
|
+
|
|
634
|
+
Representatives of the point sets modulo the
|
|
635
|
+
:meth:`lattice_automorphism_group` of ``self``.
|
|
636
|
+
|
|
637
|
+
EXAMPLES::
|
|
638
|
+
|
|
639
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
640
|
+
sage: square = LatticePolytope_PPL((-1,-1), (-1,1), (1,-1), (1,1))
|
|
641
|
+
sage: fibers = [f.vertices() for f in square.fibration_generator(1)]
|
|
642
|
+
sage: square.pointsets_mod_automorphism(fibers) # needs sage.graphs sage.groups
|
|
643
|
+
(frozenset({(-1, -1), (1, 1)}), frozenset({(-1, 0), (1, 0)}))
|
|
644
|
+
|
|
645
|
+
sage: cell24 = LatticePolytope_PPL(
|
|
646
|
+
....: (1,0,0,0), (0,1,0,0), (0,0,1,0), (0,0,0,1), (1,-1,-1,1), (0,0,-1,1),
|
|
647
|
+
....: (0,-1,0,1), (-1,0,0,1), (1,0,0,-1), (0,1,0,-1), (0,0,1,-1), (-1,1,1,-1),
|
|
648
|
+
....: (1,-1,-1,0), (0,0,-1,0), (0,-1,0,0), (-1,0,0,0), (1,-1,0,0), (1,0,-1,0),
|
|
649
|
+
....: (0,1,1,-1), (-1,1,1,0), (-1,1,0,0), (-1,0,1,0), (0,-1,-1,1), (0,0,0,-1))
|
|
650
|
+
sage: fibers = [f.vertices() for f in cell24.fibration_generator(2)]
|
|
651
|
+
sage: cell24.pointsets_mod_automorphism(fibers) # long time # needs sage.graphs sage.groups
|
|
652
|
+
(frozenset({(-1, 0, 0, 0),
|
|
653
|
+
(-1, 0, 0, 1),
|
|
654
|
+
(0, 0, 0, -1),
|
|
655
|
+
(0, 0, 0, 1),
|
|
656
|
+
(1, 0, 0, -1),
|
|
657
|
+
(1, 0, 0, 0)}),
|
|
658
|
+
frozenset({(-1, 0, 0, 0), (-1, 1, 1, 0), (1, -1, -1, 0), (1, 0, 0, 0)}))
|
|
659
|
+
"""
|
|
660
|
+
points = set()
|
|
661
|
+
for ps in pointsets:
|
|
662
|
+
points.update(ps)
|
|
663
|
+
points = tuple(sorted(points))
|
|
664
|
+
Aut = self.lattice_automorphism_group(points,
|
|
665
|
+
point_labels=tuple(range(len(points))))
|
|
666
|
+
point_to_index = {p: i for i, p in enumerate(points)}
|
|
667
|
+
indexsets = set(frozenset(point_to_index[p] for p in ps)
|
|
668
|
+
for ps in pointsets)
|
|
669
|
+
orbits = []
|
|
670
|
+
while indexsets:
|
|
671
|
+
idx = indexsets.pop()
|
|
672
|
+
min_orb = idx
|
|
673
|
+
for g in Aut:
|
|
674
|
+
g_idx = frozenset(g(i) for i in idx)
|
|
675
|
+
if sorted(g_idx) < sorted(min_orb):
|
|
676
|
+
min_orb = g_idx
|
|
677
|
+
indexsets.difference_update([g_idx])
|
|
678
|
+
orbits.append(frozenset(points[i] for i in min_orb))
|
|
679
|
+
return tuple(sorted(orbits, key=sorted))
|
|
680
|
+
|
|
681
|
+
@cached_method
|
|
682
|
+
def ambient_space(self):
|
|
683
|
+
r"""
|
|
684
|
+
Return the ambient space.
|
|
685
|
+
|
|
686
|
+
OUTPUT:
|
|
687
|
+
|
|
688
|
+
The free module `\ZZ^d`, where `d` is the ambient space
|
|
689
|
+
dimension.
|
|
690
|
+
|
|
691
|
+
EXAMPLES::
|
|
692
|
+
|
|
693
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
694
|
+
sage: point = LatticePolytope_PPL((1,2,3))
|
|
695
|
+
sage: point.ambient_space()
|
|
696
|
+
Ambient free module of rank 3 over the principal ideal domain Integer Ring
|
|
697
|
+
"""
|
|
698
|
+
from sage.modules.free_module import FreeModule
|
|
699
|
+
return FreeModule(ZZ, self.space_dimension())
|
|
700
|
+
|
|
701
|
+
def contains(self, point_coordinates):
|
|
702
|
+
r"""
|
|
703
|
+
Test whether point is contained in the polytope.
|
|
704
|
+
|
|
705
|
+
INPUT:
|
|
706
|
+
|
|
707
|
+
- ``point_coordinates`` -- list/tuple/iterable of rational
|
|
708
|
+
numbers; the coordinates of the point
|
|
709
|
+
|
|
710
|
+
OUTPUT: boolean
|
|
711
|
+
|
|
712
|
+
EXAMPLES::
|
|
713
|
+
|
|
714
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
715
|
+
sage: line = LatticePolytope_PPL((1,2,3), (-1,-2,-3))
|
|
716
|
+
sage: line.contains([0,0,0])
|
|
717
|
+
True
|
|
718
|
+
sage: line.contains([1,0,0])
|
|
719
|
+
False
|
|
720
|
+
"""
|
|
721
|
+
p = C_Polyhedron(point(Linear_Expression(list(point_coordinates), 1)))
|
|
722
|
+
is_included = Poly_Con_Relation.is_included()
|
|
723
|
+
for c in self.constraints():
|
|
724
|
+
if not p.relation_with(c).implies(is_included):
|
|
725
|
+
return False
|
|
726
|
+
return True
|
|
727
|
+
|
|
728
|
+
@cached_method
|
|
729
|
+
def contains_origin(self):
|
|
730
|
+
"""
|
|
731
|
+
Test whether the polytope contains the origin.
|
|
732
|
+
|
|
733
|
+
OUTPUT: boolean
|
|
734
|
+
|
|
735
|
+
EXAMPLES::
|
|
736
|
+
|
|
737
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
738
|
+
sage: LatticePolytope_PPL((1,2,3), (-1,-2,-3)).contains_origin()
|
|
739
|
+
True
|
|
740
|
+
sage: LatticePolytope_PPL((1,2,5), (-1,-2,-3)).contains_origin()
|
|
741
|
+
False
|
|
742
|
+
"""
|
|
743
|
+
return self.contains(self.ambient_space().zero())
|
|
744
|
+
|
|
745
|
+
@cached_method
|
|
746
|
+
def affine_space(self):
|
|
747
|
+
r"""
|
|
748
|
+
Return the affine space spanned by the polytope.
|
|
749
|
+
|
|
750
|
+
OUTPUT:
|
|
751
|
+
|
|
752
|
+
The free module `\ZZ^n`, where `n` is the dimension of the
|
|
753
|
+
affine space spanned by the points of the polytope.
|
|
754
|
+
|
|
755
|
+
EXAMPLES::
|
|
756
|
+
|
|
757
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
758
|
+
sage: point = LatticePolytope_PPL((1,2,3))
|
|
759
|
+
sage: point.affine_space()
|
|
760
|
+
Free module of degree 3 and rank 0 over Integer Ring
|
|
761
|
+
Echelon basis matrix:
|
|
762
|
+
[]
|
|
763
|
+
sage: line = LatticePolytope_PPL((1,1,1), (1,2,3))
|
|
764
|
+
sage: line.affine_space()
|
|
765
|
+
Free module of degree 3 and rank 1 over Integer Ring
|
|
766
|
+
Echelon basis matrix:
|
|
767
|
+
[0 1 2]
|
|
768
|
+
"""
|
|
769
|
+
vertices = self.vertices()
|
|
770
|
+
if not self.contains_origin():
|
|
771
|
+
v0 = vertices[0]
|
|
772
|
+
vertices = [v-v0 for v in vertices]
|
|
773
|
+
return self.ambient_space().span(vertices).saturation()
|
|
774
|
+
|
|
775
|
+
def affine_lattice_polytope(self):
|
|
776
|
+
"""
|
|
777
|
+
Return the lattice polytope restricted to
|
|
778
|
+
:meth:`affine_space`.
|
|
779
|
+
|
|
780
|
+
OUTPUT:
|
|
781
|
+
|
|
782
|
+
A new, full-dimensional lattice polytope.
|
|
783
|
+
|
|
784
|
+
EXAMPLES::
|
|
785
|
+
|
|
786
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
787
|
+
sage: poly_4d = LatticePolytope_PPL((-9,-6,0,0), (0,1,0,0), (1,0,0,0)); poly_4d
|
|
788
|
+
A 2-dimensional lattice polytope in ZZ^4 with 3 vertices
|
|
789
|
+
sage: poly_4d.space_dimension()
|
|
790
|
+
4
|
|
791
|
+
sage: poly_2d = poly_4d.affine_lattice_polytope(); poly_2d
|
|
792
|
+
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
|
|
793
|
+
sage: poly_2d.space_dimension()
|
|
794
|
+
2
|
|
795
|
+
"""
|
|
796
|
+
V = self.affine_space()
|
|
797
|
+
if self.contains_origin():
|
|
798
|
+
vertices = [ V.coordinates(v) for v in self.vertices() ]
|
|
799
|
+
else:
|
|
800
|
+
v0 = vertices[0]
|
|
801
|
+
vertices = [ V.coordinates(v-v0) for v in self.vertices() ]
|
|
802
|
+
return LatticePolytope_PPL(*vertices)
|
|
803
|
+
|
|
804
|
+
def base_projection(self, fiber):
|
|
805
|
+
"""
|
|
806
|
+
The projection that maps the sub-polytope ``fiber`` to a
|
|
807
|
+
single point.
|
|
808
|
+
|
|
809
|
+
OUTPUT:
|
|
810
|
+
|
|
811
|
+
The quotient module of the ambient space modulo the
|
|
812
|
+
:meth:`affine_space` spanned by the fiber.
|
|
813
|
+
|
|
814
|
+
EXAMPLES::
|
|
815
|
+
|
|
816
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
817
|
+
sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),
|
|
818
|
+
....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
|
|
819
|
+
sage: fiber = next(poly.fibration_generator(2))
|
|
820
|
+
sage: poly.base_projection(fiber) # needs sage.libs.pari
|
|
821
|
+
Finitely generated module V/W over Integer Ring with invariants (0, 0)
|
|
822
|
+
"""
|
|
823
|
+
return self.ambient_space().quotient(fiber.affine_space())
|
|
824
|
+
|
|
825
|
+
def base_projection_matrix(self, fiber):
|
|
826
|
+
"""
|
|
827
|
+
The projection that maps the sub-polytope ``fiber`` to a
|
|
828
|
+
single point.
|
|
829
|
+
|
|
830
|
+
OUTPUT:
|
|
831
|
+
|
|
832
|
+
An integer matrix that represents the projection to the
|
|
833
|
+
base.
|
|
834
|
+
|
|
835
|
+
.. SEEALSO::
|
|
836
|
+
|
|
837
|
+
The :meth:`base_projection` yields equivalent information,
|
|
838
|
+
and is easier to use. However, just returning the matrix
|
|
839
|
+
has lower overhead.
|
|
840
|
+
|
|
841
|
+
EXAMPLES::
|
|
842
|
+
|
|
843
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
844
|
+
sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),
|
|
845
|
+
....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
|
|
846
|
+
sage: fiber = next(poly.fibration_generator(2))
|
|
847
|
+
sage: poly.base_projection_matrix(fiber)
|
|
848
|
+
[ 0 0 -1 0]
|
|
849
|
+
[ 0 0 0 -1]
|
|
850
|
+
|
|
851
|
+
Note that the basis choice in :meth:`base_projection` for the
|
|
852
|
+
quotient is usually different::
|
|
853
|
+
|
|
854
|
+
sage: proj = poly.base_projection(fiber)
|
|
855
|
+
sage: proj_matrix = poly.base_projection_matrix(fiber)
|
|
856
|
+
sage: [proj(p) for p in poly.integral_points()] # needs sage.libs.pari
|
|
857
|
+
[(-1, -1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 1), (1, 0)]
|
|
858
|
+
sage: [proj_matrix*p for p in poly.integral_points()]
|
|
859
|
+
[(1, 1), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, -1), (-1, 0)]
|
|
860
|
+
"""
|
|
861
|
+
return matrix(ZZ, fiber.vertices()).right_kernel_matrix()
|
|
862
|
+
|
|
863
|
+
def base_rays(self, fiber, points):
|
|
864
|
+
r"""
|
|
865
|
+
Return the primitive lattice vectors that generate the
|
|
866
|
+
direction given by the base projection of points.
|
|
867
|
+
|
|
868
|
+
INPUT:
|
|
869
|
+
|
|
870
|
+
- ``fiber`` -- a sub-lattice polytope defining the
|
|
871
|
+
:meth:`base_projection`
|
|
872
|
+
|
|
873
|
+
- ``points`` -- the points to project to the base
|
|
874
|
+
|
|
875
|
+
OUTPUT: a tuple of primitive `\ZZ`-vectors
|
|
876
|
+
|
|
877
|
+
EXAMPLES::
|
|
878
|
+
|
|
879
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
880
|
+
sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),
|
|
881
|
+
....: (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0))
|
|
882
|
+
sage: fiber = next(poly.fibration_generator(2))
|
|
883
|
+
sage: poly.base_rays(fiber, poly.integral_points_not_interior_to_facets()) # needs sage.libs.pari
|
|
884
|
+
((-1, -1), (0, 1), (1, 0))
|
|
885
|
+
|
|
886
|
+
sage: p = LatticePolytope_PPL((1,0), (1,2), (-1,0))
|
|
887
|
+
sage: f = LatticePolytope_PPL((1,0), (-1,0))
|
|
888
|
+
sage: p.base_rays(f, p.integral_points()) # needs sage.libs.pari
|
|
889
|
+
((1),)
|
|
890
|
+
"""
|
|
891
|
+
quo = self.base_projection(fiber)
|
|
892
|
+
vertices = set()
|
|
893
|
+
for p in points:
|
|
894
|
+
v = quo(p).vector()
|
|
895
|
+
if v.is_zero():
|
|
896
|
+
continue
|
|
897
|
+
d = GCD_list(v.list())
|
|
898
|
+
if d > 1:
|
|
899
|
+
v = v.__copy__()
|
|
900
|
+
for i in range(v.degree()):
|
|
901
|
+
v[i] /= d
|
|
902
|
+
v.set_immutable()
|
|
903
|
+
vertices.add(v)
|
|
904
|
+
return tuple(sorted(vertices))
|
|
905
|
+
|
|
906
|
+
@cached_method
|
|
907
|
+
def has_IP_property(self) -> bool:
|
|
908
|
+
"""
|
|
909
|
+
Whether the lattice polytope has the IP property.
|
|
910
|
+
|
|
911
|
+
That is, the polytope is full-dimensional and the origin is a
|
|
912
|
+
interior point not on the boundary.
|
|
913
|
+
|
|
914
|
+
OUTPUT: boolean
|
|
915
|
+
|
|
916
|
+
EXAMPLES::
|
|
917
|
+
|
|
918
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
919
|
+
sage: LatticePolytope_PPL((-1,-1), (0,1), (1,0)).has_IP_property()
|
|
920
|
+
True
|
|
921
|
+
sage: LatticePolytope_PPL((-1,-1), (1,1)).has_IP_property()
|
|
922
|
+
False
|
|
923
|
+
"""
|
|
924
|
+
origin = C_Polyhedron(point(0*Variable(self.space_dimension())))
|
|
925
|
+
is_included = Poly_Con_Relation.is_included()
|
|
926
|
+
saturates = Poly_Con_Relation.saturates()
|
|
927
|
+
for c in self.constraints():
|
|
928
|
+
rel = origin.relation_with(c)
|
|
929
|
+
if (not rel.implies(is_included)) or rel.implies(saturates):
|
|
930
|
+
return False
|
|
931
|
+
return True
|
|
932
|
+
|
|
933
|
+
@cached_method
|
|
934
|
+
def restricted_automorphism_group(self, vertex_labels=None):
|
|
935
|
+
r"""
|
|
936
|
+
Return the restricted automorphism group.
|
|
937
|
+
|
|
938
|
+
First, let the linear automorphism group be the subgroup of
|
|
939
|
+
the Euclidean group `E(d) = GL(d,\RR) \ltimes \RR^d`
|
|
940
|
+
preserving the `d`-dimensional polyhedron. The Euclidean group
|
|
941
|
+
acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the
|
|
942
|
+
ambient space. The restricted automorphism group is the
|
|
943
|
+
subgroup of the linear automorphism group generated by
|
|
944
|
+
permutations of vertices. If the polytope is full-dimensional,
|
|
945
|
+
it is equal to the full (unrestricted) automorphism group.
|
|
946
|
+
|
|
947
|
+
INPUT:
|
|
948
|
+
|
|
949
|
+
- ``vertex_labels`` -- tuple or ``None`` (default). The
|
|
950
|
+
labels of the vertices that will be used in the output
|
|
951
|
+
permutation group. By default, the vertices are used
|
|
952
|
+
themselves.
|
|
953
|
+
|
|
954
|
+
OUTPUT:
|
|
955
|
+
|
|
956
|
+
A
|
|
957
|
+
:class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`
|
|
958
|
+
acting on the vertices (or the ``vertex_labels``, if
|
|
959
|
+
specified).
|
|
960
|
+
|
|
961
|
+
REFERENCES:
|
|
962
|
+
|
|
963
|
+
[BSS2009]_
|
|
964
|
+
|
|
965
|
+
EXAMPLES::
|
|
966
|
+
|
|
967
|
+
sage: # needs sage.graphs sage.groups
|
|
968
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
969
|
+
sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
|
|
970
|
+
sage: G1234 = Z3square.restricted_automorphism_group(
|
|
971
|
+
....: vertex_labels=(1,2,3,4))
|
|
972
|
+
sage: G1234 == PermutationGroup([[(2,3)], [(1,2),(3,4)]])
|
|
973
|
+
True
|
|
974
|
+
sage: G = Z3square.restricted_automorphism_group()
|
|
975
|
+
sage: G == PermutationGroup([[((1,2),(2,1))], [((0,0),(1,2)),
|
|
976
|
+
....: ((2,1),(3,3))], [((0,0),(3,3))]])
|
|
977
|
+
True
|
|
978
|
+
sage: set(G.domain()) == set(Z3square.vertices())
|
|
979
|
+
True
|
|
980
|
+
sage: (set(tuple(x) for x in G.orbit(Z3square.vertices()[0]))
|
|
981
|
+
....: == set([(0, 0), (1, 2), (3, 3), (2, 1)]))
|
|
982
|
+
True
|
|
983
|
+
sage: cell24 = LatticePolytope_PPL(
|
|
984
|
+
....: (1,0,0,0), (0,1,0,0), (0,0,1,0), (0,0,0,1), (1,-1,-1,1), (0,0,-1,1),
|
|
985
|
+
....: (0,-1,0,1), (-1,0,0,1), (1,0,0,-1), (0,1,0,-1), (0,0,1,-1), (-1,1,1,-1),
|
|
986
|
+
....: (1,-1,-1,0), (0,0,-1,0), (0,-1,0,0), (-1,0,0,0), (1,-1,0,0), (1,0,-1,0),
|
|
987
|
+
....: (0,1,1,-1), (-1,1,1,0), (-1,1,0,0), (-1,0,1,0), (0,-1,-1,1), (0,0,0,-1))
|
|
988
|
+
sage: cell24.restricted_automorphism_group().cardinality()
|
|
989
|
+
1152
|
|
990
|
+
"""
|
|
991
|
+
if not self.is_full_dimensional():
|
|
992
|
+
return self.affine_lattice_polytope().\
|
|
993
|
+
restricted_automorphism_group(vertex_labels=vertex_labels)
|
|
994
|
+
if vertex_labels is None:
|
|
995
|
+
vertex_labels = self.vertices()
|
|
996
|
+
from sage.graphs.graph import Graph
|
|
997
|
+
# good coordinates for the vertices
|
|
998
|
+
v_list = []
|
|
999
|
+
for v in self.minimized_generators():
|
|
1000
|
+
assert v.divisor() == 1
|
|
1001
|
+
v_coords = (1,) + tuple(Integer(mpz) for mpz in v.coefficients())
|
|
1002
|
+
v_list.append(vector(v_coords))
|
|
1003
|
+
|
|
1004
|
+
# Finally, construct the graph
|
|
1005
|
+
Qinv = sum( v.column() * v.row() for v in v_list ).inverse()
|
|
1006
|
+
G = Graph()
|
|
1007
|
+
for i in range(len(v_list)):
|
|
1008
|
+
for j in range(i+1,len(v_list)):
|
|
1009
|
+
v_i = v_list[i]
|
|
1010
|
+
v_j = v_list[j]
|
|
1011
|
+
G.add_edge(vertex_labels[i], vertex_labels[j], v_i * Qinv * v_j)
|
|
1012
|
+
return G.automorphism_group(edge_labels=True)
|
|
1013
|
+
|
|
1014
|
+
@cached_method
|
|
1015
|
+
def lattice_automorphism_group(self, points=None, point_labels=None):
|
|
1016
|
+
"""
|
|
1017
|
+
The integral subgroup of the restricted automorphism group.
|
|
1018
|
+
|
|
1019
|
+
INPUT:
|
|
1020
|
+
|
|
1021
|
+
- ``points`` -- tuple of coordinate vectors or ``None``
|
|
1022
|
+
(default). If specified, the points must form complete
|
|
1023
|
+
orbits under the lattice automorphism group. If ``None`` all
|
|
1024
|
+
vertices are used.
|
|
1025
|
+
|
|
1026
|
+
- ``point_labels`` -- tuple of labels for the ``points`` or
|
|
1027
|
+
``None`` (default). These will be used as labels for the do
|
|
1028
|
+
permutation group. If ``None``, the ``points`` will be used
|
|
1029
|
+
themselves.
|
|
1030
|
+
|
|
1031
|
+
OUTPUT:
|
|
1032
|
+
|
|
1033
|
+
The integral subgroup of the restricted automorphism group
|
|
1034
|
+
acting on the given ``points``, or all vertices if not
|
|
1035
|
+
specified.
|
|
1036
|
+
|
|
1037
|
+
EXAMPLES::
|
|
1038
|
+
|
|
1039
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
1040
|
+
sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
|
|
1041
|
+
sage: Z3square.lattice_automorphism_group() # needs sage.graphs sage.groups
|
|
1042
|
+
Permutation Group with generators [(), ((1,2),(2,1)),
|
|
1043
|
+
((0,0),(3,3)), ((0,0),(3,3))((1,2),(2,1))]
|
|
1044
|
+
|
|
1045
|
+
sage: G1 = Z3square.lattice_automorphism_group(point_labels=(1,2,3,4)) # needs sage.graphs sage.groups
|
|
1046
|
+
sage: G1 # needs sage.graphs sage.groups
|
|
1047
|
+
Permutation Group with generators [(), (2,3), (1,4), (1,4)(2,3)]
|
|
1048
|
+
sage: G1.cardinality() # needs sage.graphs sage.groups
|
|
1049
|
+
4
|
|
1050
|
+
|
|
1051
|
+
sage: G2 = Z3square.restricted_automorphism_group(vertex_labels=(1,2,3,4)) # needs sage.graphs sage.groups
|
|
1052
|
+
sage: G2 == PermutationGroup([[(2,3)], [(1,2),(3,4)], [(1,4)]]) # needs sage.graphs sage.groups
|
|
1053
|
+
True
|
|
1054
|
+
sage: G2.cardinality() # needs sage.graphs sage.groups
|
|
1055
|
+
8
|
|
1056
|
+
|
|
1057
|
+
sage: points = Z3square.integral_points(); points
|
|
1058
|
+
((0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (3, 3))
|
|
1059
|
+
sage: Z3square.lattice_automorphism_group(points, # needs sage.graphs sage.groups
|
|
1060
|
+
....: point_labels=(1,2,3,4,5,6))
|
|
1061
|
+
Permutation Group with generators [(), (3,4), (1,6)(2,5), (1,6)(2,5)(3,4)]
|
|
1062
|
+
|
|
1063
|
+
Point labels also work for lattice polytopes that are not
|
|
1064
|
+
full-dimensional, see :issue:`16669`::
|
|
1065
|
+
|
|
1066
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
1067
|
+
sage: lp = LatticePolytope_PPL((1,0,0), (0,1,0), (-1,-1,0))
|
|
1068
|
+
sage: lp.lattice_automorphism_group(point_labels=(0,1,2)) # needs sage.graphs sage.groups
|
|
1069
|
+
Permutation Group with generators [(), (1,2), (0,1), (0,1,2), (0,2,1), (0,2)]
|
|
1070
|
+
"""
|
|
1071
|
+
if not self.is_full_dimensional():
|
|
1072
|
+
return self.affine_lattice_polytope().lattice_automorphism_group(
|
|
1073
|
+
point_labels=point_labels)
|
|
1074
|
+
|
|
1075
|
+
if points is None:
|
|
1076
|
+
points = self.vertices()
|
|
1077
|
+
if point_labels is None:
|
|
1078
|
+
point_labels = tuple(points)
|
|
1079
|
+
points = [ vector(ZZ, [1]+v.list()) for v in points ]
|
|
1080
|
+
for p in points:
|
|
1081
|
+
p.set_immutable()
|
|
1082
|
+
|
|
1083
|
+
vertices = [ vector(ZZ, [1]+v.list()) for v in self.vertices() ]
|
|
1084
|
+
pivots = matrix(ZZ, vertices).pivot_rows()
|
|
1085
|
+
basis = matrix(ZZ, [ vertices[i] for i in pivots ])
|
|
1086
|
+
Mat_ZZ = basis.parent()
|
|
1087
|
+
basis_inverse = basis.inverse()
|
|
1088
|
+
|
|
1089
|
+
from sage.groups.perm_gps.permgroup import PermutationGroup
|
|
1090
|
+
lattice_gens = []
|
|
1091
|
+
G = self.restricted_automorphism_group(
|
|
1092
|
+
vertex_labels=tuple(range(len(vertices))))
|
|
1093
|
+
for g in G:
|
|
1094
|
+
image = matrix(ZZ, [ vertices[g(i)] for i in pivots ])
|
|
1095
|
+
m = basis_inverse*image
|
|
1096
|
+
if m not in Mat_ZZ:
|
|
1097
|
+
continue
|
|
1098
|
+
perm_list = [ point_labels[points.index(p*m)]
|
|
1099
|
+
for p in points ]
|
|
1100
|
+
lattice_gens.append(perm_list)
|
|
1101
|
+
return PermutationGroup(lattice_gens, domain=point_labels)
|
|
1102
|
+
|
|
1103
|
+
def sub_polytope_generator(self):
|
|
1104
|
+
"""
|
|
1105
|
+
Generate the maximal lattice sub-polytopes.
|
|
1106
|
+
|
|
1107
|
+
OUTPUT:
|
|
1108
|
+
|
|
1109
|
+
A generator yielding the maximal (with respect to inclusion)
|
|
1110
|
+
lattice sub polytopes. That is, each can be gotten as the
|
|
1111
|
+
convex hull of the integral points of ``self`` with one vertex
|
|
1112
|
+
removed.
|
|
1113
|
+
|
|
1114
|
+
EXAMPLES::
|
|
1115
|
+
|
|
1116
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
1117
|
+
sage: P = LatticePolytope_PPL((1,0,0), (0,1,0), (0,0,1), (-1,-1,-1))
|
|
1118
|
+
sage: for p in P.sub_polytope_generator():
|
|
1119
|
+
....: print(p.vertices())
|
|
1120
|
+
((0, 0, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0))
|
|
1121
|
+
((-1, -1, -1), (0, 0, 0), (0, 1, 0), (1, 0, 0))
|
|
1122
|
+
((-1, -1, -1), (0, 0, 0), (0, 0, 1), (1, 0, 0))
|
|
1123
|
+
((-1, -1, -1), (0, 0, 0), (0, 0, 1), (0, 1, 0))
|
|
1124
|
+
"""
|
|
1125
|
+
pointset = set(self.integral_points())
|
|
1126
|
+
for v in self.vertices():
|
|
1127
|
+
sub = list(pointset.difference([v]))
|
|
1128
|
+
yield LatticePolytope_PPL(*sub)
|
|
1129
|
+
|
|
1130
|
+
@cached_method
|
|
1131
|
+
def _find_isomorphism_to_subreflexive_polytope(self):
|
|
1132
|
+
"""
|
|
1133
|
+
Find an isomorphism to a sub-polytope of a maximal reflexive
|
|
1134
|
+
polytope.
|
|
1135
|
+
|
|
1136
|
+
OUTPUT:
|
|
1137
|
+
|
|
1138
|
+
A tuple consisting of the ambient reflexive polytope, the
|
|
1139
|
+
subpolytope, and the embedding of ``self`` into the ambient
|
|
1140
|
+
polytope.
|
|
1141
|
+
|
|
1142
|
+
EXAMPLES::
|
|
1143
|
+
|
|
1144
|
+
sage: # needs sage.libs.pari
|
|
1145
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
1146
|
+
sage: polygon = LatticePolytope_PPL((0,0,2,1), (0,1,2,0), (2,3,0,0), (2,0,0,3))
|
|
1147
|
+
sage: polygon._find_isomorphism_to_subreflexive_polytope()
|
|
1148
|
+
(A 2-dimensional lattice polytope in ZZ^2 with 3 vertices,
|
|
1149
|
+
A 2-dimensional lattice polytope in ZZ^2 with 4 vertices,
|
|
1150
|
+
The map A*x+b with
|
|
1151
|
+
A=
|
|
1152
|
+
[ 1 1]
|
|
1153
|
+
[ 0 1]
|
|
1154
|
+
[-1 -1]
|
|
1155
|
+
[ 1 0]
|
|
1156
|
+
b = (-1, 0, 3, 0))
|
|
1157
|
+
sage: ambient, sub, embedding = _
|
|
1158
|
+
sage: ambient.vertices()
|
|
1159
|
+
((0, 0), (0, 3), (3, 0))
|
|
1160
|
+
sage: sub.vertices()
|
|
1161
|
+
((0, 1), (3, 0), (0, 3), (1, 0))
|
|
1162
|
+
"""
|
|
1163
|
+
from .ppl_lattice_polygon import sub_reflexive_polygons
|
|
1164
|
+
from sage.geometry.polyhedron.lattice_euclidean_group_element import \
|
|
1165
|
+
LatticePolytopesNotIsomorphicError, LatticePolytopeNoEmbeddingError
|
|
1166
|
+
for p, ambient in sub_reflexive_polygons():
|
|
1167
|
+
try:
|
|
1168
|
+
return (ambient, p, p.find_isomorphism(self))
|
|
1169
|
+
except LatticePolytopesNotIsomorphicError:
|
|
1170
|
+
pass
|
|
1171
|
+
raise LatticePolytopeNoEmbeddingError('not a sub-polytope of a reflexive polygon')
|
|
1172
|
+
|
|
1173
|
+
def embed_in_reflexive_polytope(self, output='hom'):
|
|
1174
|
+
"""
|
|
1175
|
+
Find an embedding as a sub-polytope of a maximal reflexive
|
|
1176
|
+
polytope.
|
|
1177
|
+
|
|
1178
|
+
INPUT:
|
|
1179
|
+
|
|
1180
|
+
- ``hom`` -- string. One of ``'hom'`` (default),
|
|
1181
|
+
``'polytope'``, or ``points``. How the embedding is
|
|
1182
|
+
returned. See the output section for details.
|
|
1183
|
+
|
|
1184
|
+
OUTPUT:
|
|
1185
|
+
|
|
1186
|
+
An embedding into a reflexive polytope. Depending on the
|
|
1187
|
+
``output`` option slightly different data is returned.
|
|
1188
|
+
|
|
1189
|
+
- If ``output='hom'``, a map from a reflexive polytope onto
|
|
1190
|
+
``self`` is returned.
|
|
1191
|
+
|
|
1192
|
+
- If ``output='polytope'``, a reflexive polytope that contains
|
|
1193
|
+
``self`` (up to a lattice linear transformation) is
|
|
1194
|
+
returned. That is, the domain of the ``output='hom'`` map is
|
|
1195
|
+
returned. If the affine span of ``self`` is less or equal
|
|
1196
|
+
2-dimensional, the output is one of the following three
|
|
1197
|
+
possibilities:
|
|
1198
|
+
|
|
1199
|
+
:func:`~sage.geometry.polyhedron.ppl_lattice_polygon.polar_P2_polytope`,
|
|
1200
|
+
:func:`~sage.geometry.polyhedron.ppl_lattice_polygon.polar_P1xP1_polytope`,
|
|
1201
|
+
or
|
|
1202
|
+
:func:`~sage.geometry.polyhedron.ppl_lattice_polygon.polar_P2_112_polytope`.
|
|
1203
|
+
|
|
1204
|
+
- If ``output='points'``, a dictionary containing the integral
|
|
1205
|
+
points of ``self`` as keys and the corresponding integral
|
|
1206
|
+
point of the reflexive polytope as value.
|
|
1207
|
+
|
|
1208
|
+
If there is no such embedding, a
|
|
1209
|
+
:class:`~sage.geometry.polyhedron.lattice_euclidean_group_element.LatticePolytopeNoEmbeddingError`
|
|
1210
|
+
is raised. Even if it exists, the ambient reflexive polytope
|
|
1211
|
+
is usually not uniquely determined and a random but fixed
|
|
1212
|
+
choice will be returned.
|
|
1213
|
+
|
|
1214
|
+
EXAMPLES::
|
|
1215
|
+
|
|
1216
|
+
sage: # needs sage.libs.pari
|
|
1217
|
+
sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
|
|
1218
|
+
sage: polygon = LatticePolytope_PPL((0,0,2,1), (0,1,2,0), (2,3,0,0), (2,0,0,3))
|
|
1219
|
+
sage: polygon.embed_in_reflexive_polytope()
|
|
1220
|
+
The map A*x+b with
|
|
1221
|
+
A=
|
|
1222
|
+
[ 1 1]
|
|
1223
|
+
[ 0 1]
|
|
1224
|
+
[-1 -1]
|
|
1225
|
+
[ 1 0]
|
|
1226
|
+
b = (-1, 0, 3, 0)
|
|
1227
|
+
sage: polygon.embed_in_reflexive_polytope('polytope')
|
|
1228
|
+
A 2-dimensional lattice polytope in ZZ^2 with 3 vertices
|
|
1229
|
+
sage: polygon.embed_in_reflexive_polytope('points')
|
|
1230
|
+
{(0, 0, 2, 1): (1, 0),
|
|
1231
|
+
(0, 1, 2, 0): (0, 1),
|
|
1232
|
+
(1, 0, 1, 2): (2, 0),
|
|
1233
|
+
(1, 1, 1, 1): (1, 1),
|
|
1234
|
+
(1, 2, 1, 0): (0, 2),
|
|
1235
|
+
(2, 0, 0, 3): (3, 0),
|
|
1236
|
+
(2, 1, 0, 2): (2, 1),
|
|
1237
|
+
(2, 2, 0, 1): (1, 2),
|
|
1238
|
+
(2, 3, 0, 0): (0, 3)}
|
|
1239
|
+
sage: LatticePolytope_PPL((0,0), (4,0), (0,4)).embed_in_reflexive_polytope()
|
|
1240
|
+
Traceback (most recent call last):
|
|
1241
|
+
...
|
|
1242
|
+
LatticePolytopeNoEmbeddingError: not a sub-polytope of a reflexive polygon
|
|
1243
|
+
"""
|
|
1244
|
+
if self.affine_dimension() > 2:
|
|
1245
|
+
raise NotImplementedError('can only embed in reflexive polygons')
|
|
1246
|
+
ambient, subreflexive, hom = self._find_isomorphism_to_subreflexive_polytope()
|
|
1247
|
+
if output == 'hom':
|
|
1248
|
+
return hom
|
|
1249
|
+
elif output == 'polytope':
|
|
1250
|
+
return ambient
|
|
1251
|
+
elif output == 'points':
|
|
1252
|
+
points = dict()
|
|
1253
|
+
for p in subreflexive.integral_points():
|
|
1254
|
+
points[ tuple(hom(p)) ] = p
|
|
1255
|
+
return points
|
|
1256
|
+
else:
|
|
1257
|
+
raise ValueError('output='+str(output)+' is not valid.')
|