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,2503 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-polyhedra
|
|
2
|
+
# sage.doctest: optional - pynormaliz
|
|
3
|
+
"""
|
|
4
|
+
The Normaliz backend for polyhedral computations
|
|
5
|
+
|
|
6
|
+
.. NOTE::
|
|
7
|
+
|
|
8
|
+
This backend requires `PyNormaliz <https://pypi.org/project/PyNormaliz>`_.
|
|
9
|
+
To install PyNormaliz, type :code:`sage -i pynormaliz` in the terminal.
|
|
10
|
+
|
|
11
|
+
AUTHORS:
|
|
12
|
+
|
|
13
|
+
- Matthias Köppe (2016-12): initial version
|
|
14
|
+
- Jean-Philippe Labbé (2019-04): Expose normaliz features and added functionalities
|
|
15
|
+
"""
|
|
16
|
+
# ****************************************************************************
|
|
17
|
+
# Copyright (C) 2016-2022 Matthias Köppe <mkoeppe at math.ucdavis.edu>
|
|
18
|
+
# 2016-2018 Travis Scrimshaw
|
|
19
|
+
# 2017 Jeroen Demeyer
|
|
20
|
+
# 2018-2020 Jean-Philippe Labbé
|
|
21
|
+
# 2019 Vincent Delecroix
|
|
22
|
+
# 2019-2021 Jonathan Kliem
|
|
23
|
+
# 2019-2021 Sophia Elia
|
|
24
|
+
# 2020 Frédéric Chapoton
|
|
25
|
+
# 2022 Yuan Zhou
|
|
26
|
+
#
|
|
27
|
+
# This program is free software: you can redistribute it and/or modify
|
|
28
|
+
# it under the terms of the GNU General Public License as published by
|
|
29
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
30
|
+
# (at your option) any later version.
|
|
31
|
+
# https://www.gnu.org/licenses/
|
|
32
|
+
# ****************************************************************************
|
|
33
|
+
|
|
34
|
+
from sage.structure.element import Element
|
|
35
|
+
from sage.misc.cachefunc import cached_method
|
|
36
|
+
from sage.misc.misc_c import prod
|
|
37
|
+
from sage.misc.lazy_import import lazy_import
|
|
38
|
+
import sage.features.normaliz
|
|
39
|
+
lazy_import('PyNormaliz', ['NmzResult', 'NmzCompute', 'NmzCone', 'NmzConeCopy'],
|
|
40
|
+
feature=sage.features.normaliz.PyNormaliz())
|
|
41
|
+
|
|
42
|
+
from sage.rings.integer_ring import ZZ
|
|
43
|
+
from sage.rings.rational_field import QQ
|
|
44
|
+
from sage.arith.functions import LCM_list
|
|
45
|
+
from sage.misc.functional import denominator
|
|
46
|
+
from sage.matrix.constructor import vector
|
|
47
|
+
|
|
48
|
+
from .base_QQ import Polyhedron_QQ
|
|
49
|
+
from .base_ZZ import Polyhedron_ZZ
|
|
50
|
+
from .base_number_field import Polyhedron_base_number_field
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _format_function_call(fn_name, *v, **k):
|
|
54
|
+
"""
|
|
55
|
+
Return a Python function call as a string.
|
|
56
|
+
|
|
57
|
+
Keywords are sorted.
|
|
58
|
+
|
|
59
|
+
EXAMPLES::
|
|
60
|
+
|
|
61
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import _format_function_call
|
|
62
|
+
sage: _format_function_call('foo', 17, hellooooo='goodbyeeee')
|
|
63
|
+
"foo(17, hellooooo='goodbyeeee')"
|
|
64
|
+
"""
|
|
65
|
+
args = [repr(a) for a in v] + ["%s=%r" % (arg, val) for arg, val in sorted(k.items())]
|
|
66
|
+
return "{}({})".format(fn_name, ", ".join(args))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
#########################################################################
|
|
70
|
+
class Polyhedron_normaliz(Polyhedron_base_number_field):
|
|
71
|
+
"""
|
|
72
|
+
Polyhedra with normaliz.
|
|
73
|
+
|
|
74
|
+
INPUT:
|
|
75
|
+
|
|
76
|
+
- ``parent`` -- :class:`~sage.geometry.polyhedron.parent.Polyhedra`
|
|
77
|
+
the parent
|
|
78
|
+
|
|
79
|
+
- ``Vrep`` -- list ``[vertices, rays, lines]`` or ``None``; the
|
|
80
|
+
V-representation of the polyhedron; if ``None``, the polyhedron
|
|
81
|
+
is determined by the H-representation
|
|
82
|
+
|
|
83
|
+
- ``Hrep`` -- list ``[ieqs, eqns]`` or ``None``; the
|
|
84
|
+
H-representation of the polyhedron; if ``None``, the polyhedron
|
|
85
|
+
is determined by the V-representation
|
|
86
|
+
|
|
87
|
+
- ``normaliz_cone`` -- a PyNormaliz wrapper of a normaliz cone
|
|
88
|
+
|
|
89
|
+
Only one of ``Vrep``, ``Hrep``, or ``normaliz_cone`` can be different
|
|
90
|
+
from ``None``.
|
|
91
|
+
|
|
92
|
+
EXAMPLES::
|
|
93
|
+
|
|
94
|
+
sage: p = Polyhedron(vertices=[(0,0), (1,0), (0,1)],
|
|
95
|
+
....: rays=[(1,1)], lines=[],
|
|
96
|
+
....: backend='normaliz')
|
|
97
|
+
sage: TestSuite(p).run()
|
|
98
|
+
|
|
99
|
+
Two ways to get the full space::
|
|
100
|
+
|
|
101
|
+
sage: Polyhedron(eqns=[[0, 0, 0]], backend='normaliz')
|
|
102
|
+
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 lines
|
|
103
|
+
sage: Polyhedron(ieqs=[[0, 0, 0]], backend='normaliz')
|
|
104
|
+
A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 lines
|
|
105
|
+
|
|
106
|
+
A lower-dimensional affine cone; we test that there are no mysterious
|
|
107
|
+
inequalities coming in from the homogenization::
|
|
108
|
+
|
|
109
|
+
sage: P = Polyhedron(vertices=[(1, 1)], rays=[(0, 1)],
|
|
110
|
+
....: backend='normaliz')
|
|
111
|
+
sage: P.n_inequalities()
|
|
112
|
+
1
|
|
113
|
+
sage: P.equations()
|
|
114
|
+
(An equation (1, 0) x - 1 == 0,)
|
|
115
|
+
|
|
116
|
+
The empty polyhedron::
|
|
117
|
+
|
|
118
|
+
sage: P = Polyhedron(ieqs=[[-2, 1, 1], [-3, -1, -1], [-4, 1, -2]],
|
|
119
|
+
....: backend='normaliz')
|
|
120
|
+
sage: P
|
|
121
|
+
The empty polyhedron in QQ^2
|
|
122
|
+
sage: P.Vrepresentation()
|
|
123
|
+
()
|
|
124
|
+
sage: P.Hrepresentation()
|
|
125
|
+
(An equation -1 == 0,)
|
|
126
|
+
|
|
127
|
+
TESTS:
|
|
128
|
+
|
|
129
|
+
Tests copied from various methods in :mod:`sage.geometry.polyhedron.base`::
|
|
130
|
+
|
|
131
|
+
sage: p = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]],
|
|
132
|
+
....: backend='normaliz')
|
|
133
|
+
sage: p.n_equations()
|
|
134
|
+
1
|
|
135
|
+
sage: p.n_inequalities()
|
|
136
|
+
3
|
|
137
|
+
|
|
138
|
+
sage: p = Polyhedron(vertices=[[t,t^2,t^3] for t in range(6)],
|
|
139
|
+
....: backend='normaliz')
|
|
140
|
+
sage: p.n_facets()
|
|
141
|
+
8
|
|
142
|
+
|
|
143
|
+
sage: p = Polyhedron(vertices=[[1,0], [0,1], [1,1]], rays=[[1,1]],
|
|
144
|
+
....: backend='normaliz')
|
|
145
|
+
sage: p.n_vertices()
|
|
146
|
+
2
|
|
147
|
+
|
|
148
|
+
sage: p = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]],
|
|
149
|
+
....: backend='normaliz')
|
|
150
|
+
sage: p.n_rays()
|
|
151
|
+
1
|
|
152
|
+
|
|
153
|
+
sage: p = Polyhedron(vertices=[[0,0]], rays=[[0,1], [0,-1]],
|
|
154
|
+
....: backend='normaliz')
|
|
155
|
+
sage: p.n_lines()
|
|
156
|
+
1
|
|
157
|
+
|
|
158
|
+
Algebraic polyhedra::
|
|
159
|
+
|
|
160
|
+
sage: P = Polyhedron(vertices=[[1], [sqrt(2)]], # needs sage.rings.number_field sage.symbolic
|
|
161
|
+
....: backend='normaliz', verbose=True)
|
|
162
|
+
# ----8<---- Equivalent Normaliz input file ----8<----
|
|
163
|
+
amb_space 1
|
|
164
|
+
number_field min_poly (a^2 - 2) embedding [1.414213562373095 +/- 2.99e-16]
|
|
165
|
+
cone 0
|
|
166
|
+
subspace 0
|
|
167
|
+
vertices 2
|
|
168
|
+
1 1
|
|
169
|
+
(a) 1
|
|
170
|
+
# ----8<-------------------8<-------------------8<----
|
|
171
|
+
# Calling PyNormaliz.NmzCone(cone=[], number_field=['a^2 - 2', 'a', '[1.414213562373095 +/- 2.99e-16]'], subspace=[], vertices=[[1, 1], [[[0, 1], [1, 1]], 1]])
|
|
172
|
+
sage: P # needs sage.rings.number_field sage.symbolic
|
|
173
|
+
A 1-dimensional polyhedron in (Symbolic Ring)^1 defined as
|
|
174
|
+
the convex hull of 2 vertices
|
|
175
|
+
sage: P.vertices() # needs sage.rings.number_field sage.symbolic
|
|
176
|
+
(A vertex at (1), A vertex at (sqrt(2)))
|
|
177
|
+
|
|
178
|
+
sage: P = polytopes.icosahedron(exact=True, # needs sage.rings.number_field
|
|
179
|
+
....: backend='normaliz'); P
|
|
180
|
+
A 3-dimensional polyhedron in
|
|
181
|
+
(Number Field in sqrt5 with defining polynomial x^2 - 5
|
|
182
|
+
with sqrt5 = 2.236067977499790?)^3
|
|
183
|
+
defined as the convex hull of 12 vertices
|
|
184
|
+
|
|
185
|
+
sage: x = polygen(ZZ)
|
|
186
|
+
sage: P = Polyhedron(vertices=[[sqrt(2)], # needs sage.rings.number_field sage.symbolic
|
|
187
|
+
....: [AA.polynomial_root(x^3 - 2, RIF(0,3))]],
|
|
188
|
+
....: backend='normaliz', verbose=True)
|
|
189
|
+
# ----8<---- Equivalent Normaliz input file ----8<----
|
|
190
|
+
amb_space 1
|
|
191
|
+
number_field min_poly (a^6 - 2) embedding [1.122462048309373 +/- 5.38e-16]
|
|
192
|
+
cone 0
|
|
193
|
+
subspace 0
|
|
194
|
+
vertices 2
|
|
195
|
+
(a^3) 1
|
|
196
|
+
(a^2) 1
|
|
197
|
+
# ----8<-------------------8<-------------------8<----
|
|
198
|
+
# Calling PyNormaliz.NmzCone(cone=[], number_field=['a^6 - 2', 'a', '[1.122462048309373 +/- 5.38e-16]'], subspace=[], vertices=[[[[0, 1], [0, 1], [0, 1], [1, 1], [0, 1], [0, 1]], 1], [[[0, 1], [0, 1], [1, 1], [0, 1], [0, 1], [0, 1]], 1]])
|
|
199
|
+
sage: P # needs sage.rings.number_field sage.symbolic
|
|
200
|
+
A 1-dimensional polyhedron in (Symbolic Ring)^1 defined as
|
|
201
|
+
the convex hull of 2 vertices
|
|
202
|
+
sage: P.vertices() # needs sage.rings.number_field sage.symbolic
|
|
203
|
+
(A vertex at (2^(1/3)), A vertex at (sqrt(2)))
|
|
204
|
+
"""
|
|
205
|
+
def __init__(self, parent, Vrep, Hrep, normaliz_cone=None, normaliz_data=None, internal_base_ring=None, **kwds):
|
|
206
|
+
"""
|
|
207
|
+
Initialize the polyhedron.
|
|
208
|
+
|
|
209
|
+
See :class:`Polyhedron_normaliz` for a description of the input
|
|
210
|
+
data.
|
|
211
|
+
|
|
212
|
+
TESTS::
|
|
213
|
+
|
|
214
|
+
sage: p = Polyhedron(backend='normaliz')
|
|
215
|
+
sage: TestSuite(p).run()
|
|
216
|
+
sage: p = Polyhedron(vertices=[(1, 1)], rays=[(0, 1)],
|
|
217
|
+
....: backend='normaliz')
|
|
218
|
+
sage: TestSuite(p).run()
|
|
219
|
+
sage: p = Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)],
|
|
220
|
+
....: backend='normaliz')
|
|
221
|
+
sage: TestSuite(p).run()
|
|
222
|
+
"""
|
|
223
|
+
if normaliz_cone:
|
|
224
|
+
if Hrep is not None or Vrep is not None or normaliz_data is not None:
|
|
225
|
+
raise ValueError("only one of Vrep, Hrep, normaliz_cone, or normaliz_data can be different from None")
|
|
226
|
+
Element.__init__(self, parent=parent)
|
|
227
|
+
self._init_from_normaliz_cone(normaliz_cone, internal_base_ring)
|
|
228
|
+
elif normaliz_data:
|
|
229
|
+
if Hrep is not None or Vrep is not None:
|
|
230
|
+
raise ValueError("only one of Vrep, Hrep, normaliz_cone, or normaliz_data can be different from None")
|
|
231
|
+
Element.__init__(self, parent=parent)
|
|
232
|
+
self._init_from_normaliz_data(normaliz_data, internal_base_ring)
|
|
233
|
+
else:
|
|
234
|
+
if internal_base_ring:
|
|
235
|
+
raise ValueError("if Vrep or Hrep are given, cannot provide internal_base_ring")
|
|
236
|
+
Polyhedron_base_number_field.__init__(self, parent, Vrep, Hrep, **kwds)
|
|
237
|
+
|
|
238
|
+
def _nmz_result(self, normaliz_cone, property):
|
|
239
|
+
"""
|
|
240
|
+
Call PyNormaliz's NmzResult function with appropriate conversion between number format.
|
|
241
|
+
|
|
242
|
+
TESTS::
|
|
243
|
+
|
|
244
|
+
sage: p = Polyhedron(vertices=[(0, 0), (1, 0), (0, 1)], rays=[(1,1)],
|
|
245
|
+
....: lines=[], backend='normaliz')
|
|
246
|
+
sage: p._nmz_result(p._normaliz_cone, 'EquivariantXyzzyModuleSeries')
|
|
247
|
+
Traceback (most recent call last):
|
|
248
|
+
...
|
|
249
|
+
NormalizError: Some error in the normaliz input data detected: Unknown ConeProperty...
|
|
250
|
+
|
|
251
|
+
sage: # needs sage.rings.number_field
|
|
252
|
+
sage: x = polygen(QQ, 'x')
|
|
253
|
+
sage: K.<a> = NumberField(x^3 - 3, embedding=AA(3)**(1/3))
|
|
254
|
+
sage: p = Polyhedron(vertices=[(0, 0), (1, 1), (a, 3), (-1, a**2)],
|
|
255
|
+
....: rays=[(-1,-a)], backend='normaliz')
|
|
256
|
+
sage: sorted(p._nmz_result(p._normaliz_cone, 'VerticesOfPolyhedron'))
|
|
257
|
+
[[-1, a^2, 1], [1, 1, 1], [a, 3, 1]]
|
|
258
|
+
sage: triangulation_generators = p._nmz_result(p._normaliz_cone,
|
|
259
|
+
....: 'Triangulation')[1]
|
|
260
|
+
sage: sorted(triangulation_generators)
|
|
261
|
+
[[-a^2, -3, 0], [-1, a^2, 1], [0, 0, 1], [1, 1, 1], [a, 3, 1]]
|
|
262
|
+
sage: p._nmz_result(p._normaliz_cone, 'AffineDim') == 2
|
|
263
|
+
True
|
|
264
|
+
sage: p._nmz_result(p._normaliz_cone, 'EmbeddingDim') == 3
|
|
265
|
+
True
|
|
266
|
+
sage: p._nmz_result(p._normaliz_cone, 'ExtremeRays')
|
|
267
|
+
[[-1/3*a^2, -1, 0]]
|
|
268
|
+
sage: p._nmz_result(p._normaliz_cone, 'MaximalSubspace')
|
|
269
|
+
[]
|
|
270
|
+
"""
|
|
271
|
+
def rational_handler(list):
|
|
272
|
+
return QQ(tuple(list))
|
|
273
|
+
|
|
274
|
+
def nfelem_handler(coords):
|
|
275
|
+
# coords might be too short which is not accepted by Sage number field
|
|
276
|
+
v = list(coords) + [0] * (self._internal_base_ring.degree() - len(coords))
|
|
277
|
+
return self._internal_base_ring(v)
|
|
278
|
+
return NmzResult(normaliz_cone, property,
|
|
279
|
+
RationalHandler=rational_handler,
|
|
280
|
+
NumberfieldElementHandler=nfelem_handler)
|
|
281
|
+
|
|
282
|
+
def _init_from_normaliz_cone(self, normaliz_cone, internal_base_ring):
|
|
283
|
+
"""
|
|
284
|
+
Construct polyhedron from a PyNormaliz wrapper of a normaliz cone.
|
|
285
|
+
|
|
286
|
+
TESTS::
|
|
287
|
+
|
|
288
|
+
sage: p = Polyhedron(backend='normaliz')
|
|
289
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
|
|
290
|
+
sage: Polyhedron_normaliz._init_from_Hrepresentation(p, [], []) # indirect doctest
|
|
291
|
+
"""
|
|
292
|
+
if internal_base_ring is None:
|
|
293
|
+
internal_base_ring = QQ
|
|
294
|
+
self._internal_base_ring = internal_base_ring
|
|
295
|
+
|
|
296
|
+
if normaliz_cone and self._nmz_result(normaliz_cone, "AffineDim") < 0:
|
|
297
|
+
# Empty polyhedron. Special case because Normaliz defines the
|
|
298
|
+
# recession cone of an empty polyhedron given by an
|
|
299
|
+
# H-representation as the cone defined by the homogenized system.
|
|
300
|
+
self._init_empty_polyhedron()
|
|
301
|
+
else:
|
|
302
|
+
self._normaliz_cone = normaliz_cone
|
|
303
|
+
self._init_Vrepresentation_from_normaliz()
|
|
304
|
+
self._init_Hrepresentation_from_normaliz()
|
|
305
|
+
|
|
306
|
+
@staticmethod
|
|
307
|
+
def _convert_to_pynormaliz(x):
|
|
308
|
+
"""
|
|
309
|
+
Convert a number or nested lists and tuples of numbers to pynormaliz input format.
|
|
310
|
+
|
|
311
|
+
TESTS::
|
|
312
|
+
|
|
313
|
+
sage: K.<sqrt2> = QuadraticField(2) # needs sage.rings.number_field
|
|
314
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz as Pn
|
|
315
|
+
sage: Pn._convert_to_pynormaliz(17)
|
|
316
|
+
17
|
|
317
|
+
sage: Pn._convert_to_pynormaliz(901824309821093821093812093810928309183091832091)
|
|
318
|
+
901824309821093821093812093810928309183091832091
|
|
319
|
+
sage: Pn._convert_to_pynormaliz(QQ(17))
|
|
320
|
+
17
|
|
321
|
+
sage: Pn._convert_to_pynormaliz(28/5)
|
|
322
|
+
[[28, 5]]
|
|
323
|
+
sage: Pn._convert_to_pynormaliz(28901824309821093821093812093810928309183091832091/5234573685674784567853456543456456786543456765)
|
|
324
|
+
[[28901824309821093821093812093810928309183091832091, 5234573685674784567853456543456456786543456765]]
|
|
325
|
+
sage: Pn._convert_to_pynormaliz(7 + sqrt2) # needs sage.rings.number_field
|
|
326
|
+
[[7, 1], [1, 1]]
|
|
327
|
+
sage: Pn._convert_to_pynormaliz(7/2 + sqrt2) # needs sage.rings.number_field
|
|
328
|
+
[[7, 2], [1, 1]]
|
|
329
|
+
sage: Pn._convert_to_pynormaliz([[1, 2], (3, 4)])
|
|
330
|
+
[[1, 2], [3, 4]]
|
|
331
|
+
|
|
332
|
+
Check that :issue:`29836` is fixed::
|
|
333
|
+
|
|
334
|
+
sage: P = polytopes.simplex(backend='normaliz')
|
|
335
|
+
sage: K.<sqrt2> = QuadraticField(2) # needs sage.rings.number_field
|
|
336
|
+
sage: P.dilation(sqrt2) # needs sage.rings.number_field
|
|
337
|
+
A 3-dimensional polyhedron in
|
|
338
|
+
(Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.41...)^4
|
|
339
|
+
defined as the convex hull of 4 vertices
|
|
340
|
+
"""
|
|
341
|
+
def _QQ_pair(x):
|
|
342
|
+
x = QQ(x)
|
|
343
|
+
return [int(x.numerator()), int(x.denominator())]
|
|
344
|
+
from sage.rings.rational import Rational
|
|
345
|
+
from types import GeneratorType
|
|
346
|
+
if isinstance(x, (list, tuple, GeneratorType)):
|
|
347
|
+
return [Polyhedron_normaliz._convert_to_pynormaliz(y) for y in x]
|
|
348
|
+
try:
|
|
349
|
+
return int(ZZ(x))
|
|
350
|
+
except TypeError:
|
|
351
|
+
pass
|
|
352
|
+
|
|
353
|
+
if isinstance(x, Rational):
|
|
354
|
+
return [_QQ_pair(x)] # need extra brackets to distinguish from quadratic numberfield element
|
|
355
|
+
# number field
|
|
356
|
+
return [_QQ_pair(c) for c in x.list()]
|
|
357
|
+
|
|
358
|
+
def _init_from_normaliz_data(self, data, internal_base_ring=None, verbose=False):
|
|
359
|
+
"""
|
|
360
|
+
Construct polyhedron from normaliz ``data`` (a dictionary).
|
|
361
|
+
|
|
362
|
+
TESTS::
|
|
363
|
+
|
|
364
|
+
sage: p = Polyhedron(backend='normaliz', ambient_dim=2)
|
|
365
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_QQ_normaliz
|
|
366
|
+
sage: data = {'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
|
|
367
|
+
sage: Polyhedron_QQ_normaliz._init_from_normaliz_data(p, data)
|
|
368
|
+
sage: p.inequalities_list()
|
|
369
|
+
[[0, -1, 2], [0, 2, -1]]
|
|
370
|
+
|
|
371
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
|
|
372
|
+
sage: from sage.rings.qqbar import AA # needs sage.rings.number_field
|
|
373
|
+
sage: from sage.rings.number_field.number_field import QuadraticField # needs sage.rings.number_field
|
|
374
|
+
sage: data = {'number_field': ['a^2 - 2', 'a', '[1.4 +/- 0.1]'],
|
|
375
|
+
....: 'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
|
|
376
|
+
sage: from sage.geometry.polyhedron.parent import Polyhedra_normaliz
|
|
377
|
+
sage: parent = Polyhedra_normaliz(AA, 2, 'normaliz') # needs sage.rings.number_field
|
|
378
|
+
sage: Polyhedron_normaliz(parent, None, None, # needs sage.rings.number_field
|
|
379
|
+
....: normaliz_data=data,
|
|
380
|
+
....: internal_base_ring=QuadraticField(2))
|
|
381
|
+
A 2-dimensional polyhedron in AA^2 defined as the convex hull of 1 vertex and 2 rays
|
|
382
|
+
sage: _.inequalities_list() # needs sage.rings.number_field
|
|
383
|
+
[[0, -1/2, 1], [0, 2, -1]]
|
|
384
|
+
"""
|
|
385
|
+
if internal_base_ring is None:
|
|
386
|
+
internal_base_ring = QQ
|
|
387
|
+
cone = self._cone_from_normaliz_data(data, verbose)
|
|
388
|
+
self._init_from_normaliz_cone(cone, internal_base_ring)
|
|
389
|
+
|
|
390
|
+
def _cone_from_normaliz_data(self, data, verbose=False):
|
|
391
|
+
"""
|
|
392
|
+
Construct a normaliz cone from ``data`` (a dictionary).
|
|
393
|
+
|
|
394
|
+
EXAMPLES::
|
|
395
|
+
|
|
396
|
+
sage: p = Polyhedron(backend='normaliz', ambient_dim=2)
|
|
397
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_QQ_normaliz
|
|
398
|
+
sage: data = {'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
|
|
399
|
+
sage: cone = Polyhedron_QQ_normaliz._cone_from_normaliz_data(p, data)
|
|
400
|
+
sage: p._nmz_result(cone,'SupportHyperplanes')
|
|
401
|
+
[[-1, 2, 0], [0, 0, 1], [2, -1, 0]]
|
|
402
|
+
"""
|
|
403
|
+
if verbose:
|
|
404
|
+
if isinstance(verbose, str):
|
|
405
|
+
print("# Wrote equivalent Normaliz input file to {}".format(verbose))
|
|
406
|
+
self._normaliz_format(data, file_output=verbose)
|
|
407
|
+
else:
|
|
408
|
+
print("# ----8<---- Equivalent Normaliz input file ----8<----")
|
|
409
|
+
print(self._normaliz_format(data), end='')
|
|
410
|
+
print("# ----8<-------------------8<-------------------8<----")
|
|
411
|
+
|
|
412
|
+
for key, value in data.items():
|
|
413
|
+
if key != 'number_field':
|
|
414
|
+
data[key] = self._convert_to_pynormaliz(value)
|
|
415
|
+
|
|
416
|
+
if verbose:
|
|
417
|
+
print("# Calling {}".format(_format_function_call('PyNormaliz.NmzCone', **data)))
|
|
418
|
+
|
|
419
|
+
cone = NmzCone(**data)
|
|
420
|
+
assert cone, "{} did not return a cone".format(_format_function_call('PyNormaliz.NmzCone', **data))
|
|
421
|
+
return cone
|
|
422
|
+
|
|
423
|
+
def _is_zero(self, x) -> bool:
|
|
424
|
+
"""
|
|
425
|
+
Test whether ``x`` is zero.
|
|
426
|
+
|
|
427
|
+
INPUT:
|
|
428
|
+
|
|
429
|
+
- ``x`` -- a number in the base ring
|
|
430
|
+
|
|
431
|
+
OUTPUT: boolean
|
|
432
|
+
|
|
433
|
+
EXAMPLES::
|
|
434
|
+
|
|
435
|
+
sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic
|
|
436
|
+
sage: p._is_zero(0) # needs sage.rings.number_field sage.symbolic
|
|
437
|
+
True
|
|
438
|
+
sage: p._is_zero(1/100000) # needs sage.rings.number_field sage.symbolic
|
|
439
|
+
False
|
|
440
|
+
"""
|
|
441
|
+
return x == 0
|
|
442
|
+
|
|
443
|
+
def _is_nonneg(self, x) -> bool:
|
|
444
|
+
"""
|
|
445
|
+
Test whether ``x`` is nonnegative.
|
|
446
|
+
|
|
447
|
+
INPUT:
|
|
448
|
+
|
|
449
|
+
- ``x`` -- a number in the base ring
|
|
450
|
+
|
|
451
|
+
OUTPUT: boolean
|
|
452
|
+
|
|
453
|
+
EXAMPLES::
|
|
454
|
+
|
|
455
|
+
sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic
|
|
456
|
+
sage: p._is_nonneg(1) # needs sage.rings.number_field sage.symbolic
|
|
457
|
+
True
|
|
458
|
+
sage: p._is_nonneg(-1/100000) # needs sage.rings.number_field sage.symbolic
|
|
459
|
+
False
|
|
460
|
+
"""
|
|
461
|
+
return x >= 0
|
|
462
|
+
|
|
463
|
+
def _is_positive(self, x) -> bool:
|
|
464
|
+
"""
|
|
465
|
+
Test whether ``x`` is positive.
|
|
466
|
+
|
|
467
|
+
INPUT:
|
|
468
|
+
|
|
469
|
+
- ``x`` -- a number in the base ring
|
|
470
|
+
|
|
471
|
+
OUTPUT: boolean
|
|
472
|
+
|
|
473
|
+
EXAMPLES::
|
|
474
|
+
|
|
475
|
+
sage: p = Polyhedron([(sqrt(3),sqrt(2))], base_ring=AA) # needs sage.rings.number_field sage.symbolic
|
|
476
|
+
sage: p._is_positive(1) # needs sage.rings.number_field sage.symbolic
|
|
477
|
+
True
|
|
478
|
+
sage: p._is_positive(0) # needs sage.rings.number_field sage.symbolic
|
|
479
|
+
False
|
|
480
|
+
"""
|
|
481
|
+
return x > 0
|
|
482
|
+
|
|
483
|
+
def _init_from_Vrepresentation(self, vertices, rays, lines, minimize=True, verbose=False):
|
|
484
|
+
r"""
|
|
485
|
+
Construct polyhedron from V-representation data.
|
|
486
|
+
|
|
487
|
+
INPUT:
|
|
488
|
+
|
|
489
|
+
- ``vertices`` -- list of point; each point can be specified
|
|
490
|
+
as any iterable container of
|
|
491
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
492
|
+
|
|
493
|
+
- ``rays`` -- list of rays; each ray can be specified as any
|
|
494
|
+
iterable container of
|
|
495
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
496
|
+
|
|
497
|
+
- ``lines`` -- list of lines; each line can be specified as
|
|
498
|
+
any iterable container of
|
|
499
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
500
|
+
|
|
501
|
+
- ``verbose`` -- boolean (default: ``False``); whether to print
|
|
502
|
+
verbose output for debugging purposes
|
|
503
|
+
|
|
504
|
+
EXAMPLES::
|
|
505
|
+
|
|
506
|
+
sage: p = Polyhedron(backend='normaliz')
|
|
507
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
|
|
508
|
+
sage: Polyhedron_normaliz._init_from_Vrepresentation(p, [], [], [])
|
|
509
|
+
"""
|
|
510
|
+
|
|
511
|
+
def vert_ray_line_QQ(vertices, rays, lines):
|
|
512
|
+
nmz_vertices = []
|
|
513
|
+
for v in vertices:
|
|
514
|
+
d = LCM_list([denominator(v_i) for v_i in v])
|
|
515
|
+
dv = [d * v_i for v_i in v]
|
|
516
|
+
nmz_vertices.append(dv + [d])
|
|
517
|
+
nmz_rays = []
|
|
518
|
+
for r in rays:
|
|
519
|
+
d = LCM_list([denominator(r_i) for r_i in r])
|
|
520
|
+
dr = [d * r_i for r_i in r]
|
|
521
|
+
nmz_rays.append(dr)
|
|
522
|
+
nmz_lines = []
|
|
523
|
+
for l in lines:
|
|
524
|
+
d = LCM_list([denominator(l_i) for l_i in l])
|
|
525
|
+
dl = [d * l_i for l_i in l]
|
|
526
|
+
nmz_lines.append(dl)
|
|
527
|
+
return nmz_vertices, nmz_rays, nmz_lines
|
|
528
|
+
|
|
529
|
+
def vert_ray_line_NF(vertices, rays, lines):
|
|
530
|
+
h_vertices = [list(v) + [1] for v in vertices]
|
|
531
|
+
return h_vertices, rays, lines
|
|
532
|
+
|
|
533
|
+
if vertices is None:
|
|
534
|
+
vertices = []
|
|
535
|
+
if rays is None:
|
|
536
|
+
rays = []
|
|
537
|
+
if lines is None:
|
|
538
|
+
lines = []
|
|
539
|
+
|
|
540
|
+
(nmz_vertices, nmz_rays, nmz_lines), internal_base_ring \
|
|
541
|
+
= self._compute_data_lists_and_internal_base_ring(
|
|
542
|
+
(vertices, rays, lines), vert_ray_line_QQ, vert_ray_line_NF)
|
|
543
|
+
|
|
544
|
+
if not nmz_vertices and not nmz_rays and not nmz_lines:
|
|
545
|
+
# Special case to avoid:
|
|
546
|
+
# error: Some error in the normaliz input data detected:
|
|
547
|
+
# All input matrices empty!
|
|
548
|
+
self._init_empty_polyhedron()
|
|
549
|
+
else:
|
|
550
|
+
data = {"vertices": nmz_vertices,
|
|
551
|
+
"cone": nmz_rays,
|
|
552
|
+
"subspace": nmz_lines}
|
|
553
|
+
number_field_data = self._number_field_triple(internal_base_ring)
|
|
554
|
+
if number_field_data:
|
|
555
|
+
data["number_field"] = number_field_data
|
|
556
|
+
self._init_from_normaliz_data(data, internal_base_ring=internal_base_ring, verbose=verbose)
|
|
557
|
+
|
|
558
|
+
def _init_from_Hrepresentation(self, ieqs, eqns, minimize=True, verbose=False):
|
|
559
|
+
r"""
|
|
560
|
+
Construct polyhedron from H-representation data.
|
|
561
|
+
|
|
562
|
+
INPUT:
|
|
563
|
+
|
|
564
|
+
- ``ieqs`` -- list of inequalities; each line can be specified
|
|
565
|
+
as any iterable container of
|
|
566
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
567
|
+
|
|
568
|
+
- ``eqns`` -- list of equalities; each line can be specified
|
|
569
|
+
as any iterable container of
|
|
570
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
571
|
+
|
|
572
|
+
- ``minimize`` -- boolean (default: ``True``); ignored
|
|
573
|
+
|
|
574
|
+
- ``verbose`` -- boolean (default: ``False``); whether to print
|
|
575
|
+
verbose output for debugging purposes
|
|
576
|
+
|
|
577
|
+
EXAMPLES::
|
|
578
|
+
|
|
579
|
+
sage: p = Polyhedron(backend='normaliz')
|
|
580
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
|
|
581
|
+
sage: Polyhedron_normaliz._init_from_Hrepresentation(p, [], [])
|
|
582
|
+
|
|
583
|
+
TESTS::
|
|
584
|
+
|
|
585
|
+
sage: K.<a> = QuadraticField(2) # needs sage.rings.number_field
|
|
586
|
+
sage: p = Polyhedron(ieqs=[(1, a, 0)], backend='normaliz') # needs sage.rings.number_field
|
|
587
|
+
sage: p & p == p # needs sage.rings.number_field
|
|
588
|
+
True
|
|
589
|
+
|
|
590
|
+
Check that :issue:`30248` is fixed, that maps as input works::
|
|
591
|
+
|
|
592
|
+
sage: # needs sage.rings.number_field
|
|
593
|
+
sage: q = Polyhedron(backend='normaliz', base_ring=AA,
|
|
594
|
+
....: rays=[(0, 0, 1), (0, 1, -1), (1, 0, -1)])
|
|
595
|
+
sage: def make_new_Hrep(h):
|
|
596
|
+
....: return tuple(x if i == 0 else -1*x
|
|
597
|
+
....: for i, x in enumerate(h._vector))
|
|
598
|
+
sage: new_inequalities = map(make_new_Hrep, q.inequality_generator())
|
|
599
|
+
sage: new_equations = map(make_new_Hrep, q.equation_generator())
|
|
600
|
+
sage: parent = q.parent()
|
|
601
|
+
sage: new_q = parent.element_class(parent, None,
|
|
602
|
+
....: [new_inequalities, new_equations])
|
|
603
|
+
sage: new_q
|
|
604
|
+
A 3-dimensional polyhedron in AA^3 defined as the convex hull of 1 vertex and 3 rays
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
def nmz_ieqs_eqns_NF(ieqs, eqns):
|
|
608
|
+
nmz_ieqs = [list(ieq[1:]) + [ieq[0]] for ieq in ieqs]
|
|
609
|
+
nmz_eqns = [list(eqn[1:]) + [eqn[0]] for eqn in eqns]
|
|
610
|
+
return nmz_ieqs, nmz_eqns
|
|
611
|
+
|
|
612
|
+
def nmz_ieqs_eqns_QQ(ieqs, eqns):
|
|
613
|
+
nmz_ieqs = []
|
|
614
|
+
for ieq in ieqs:
|
|
615
|
+
d = LCM_list([denominator(ieq_i) for ieq_i in ieq])
|
|
616
|
+
dieq = [ZZ(d * ieq_i) for ieq_i in ieq]
|
|
617
|
+
b = dieq[0]
|
|
618
|
+
A = dieq[1:]
|
|
619
|
+
nmz_ieqs.append(A + [b])
|
|
620
|
+
nmz_eqns = []
|
|
621
|
+
for eqn in eqns:
|
|
622
|
+
d = LCM_list([denominator(eqn_i) for eqn_i in eqn])
|
|
623
|
+
deqn = [ZZ(d * eqn_i) for eqn_i in eqn]
|
|
624
|
+
b = deqn[0]
|
|
625
|
+
A = deqn[1:]
|
|
626
|
+
nmz_eqns.append(A + [b])
|
|
627
|
+
return nmz_ieqs, nmz_eqns
|
|
628
|
+
|
|
629
|
+
if ieqs is None:
|
|
630
|
+
ieqs = []
|
|
631
|
+
if eqns is None:
|
|
632
|
+
eqns = []
|
|
633
|
+
|
|
634
|
+
(nmz_ieqs, nmz_eqns), internal_base_ring \
|
|
635
|
+
= self._compute_data_lists_and_internal_base_ring(
|
|
636
|
+
(ieqs, eqns), nmz_ieqs_eqns_QQ, nmz_ieqs_eqns_NF)
|
|
637
|
+
if not nmz_ieqs:
|
|
638
|
+
# If normaliz gets an empty list of inequalities, it adds
|
|
639
|
+
# nonnegativities. So let's add a tautological inequality to work
|
|
640
|
+
# around this.
|
|
641
|
+
nmz_ieqs.append([0] * self.ambient_dim() + [0])
|
|
642
|
+
data = {"inhom_equations": nmz_eqns,
|
|
643
|
+
"inhom_inequalities": nmz_ieqs}
|
|
644
|
+
number_field_data = self._number_field_triple(internal_base_ring)
|
|
645
|
+
if number_field_data:
|
|
646
|
+
data["number_field"] = number_field_data
|
|
647
|
+
self._init_from_normaliz_data(data, internal_base_ring=internal_base_ring, verbose=verbose)
|
|
648
|
+
|
|
649
|
+
def _cone_from_Vrepresentation_and_Hrepresentation(self, vertices, rays, lines, ieqs, eqns=None, verbose=False, homogeneous=False):
|
|
650
|
+
r"""
|
|
651
|
+
Construct cone from V-representation data and H-representation data.
|
|
652
|
+
|
|
653
|
+
INPUT:
|
|
654
|
+
|
|
655
|
+
- ``vertices`` -- list of point; each point can be specified
|
|
656
|
+
as any iterable container of
|
|
657
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
658
|
+
|
|
659
|
+
- ``rays`` -- list of rays; each ray can be specified as any
|
|
660
|
+
iterable container of
|
|
661
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
662
|
+
|
|
663
|
+
- ``ieqs`` -- list of inequalities; each line can be specified
|
|
664
|
+
as any iterable container of
|
|
665
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
666
|
+
|
|
667
|
+
- ``eqns`` -- list of equalities; each line can be specified
|
|
668
|
+
as any iterable container of
|
|
669
|
+
:meth:`~sage.geometry.polyhedron.base.base_ring` elements
|
|
670
|
+
|
|
671
|
+
- ``verbose`` -- boolean (default: ``False``); whether to print
|
|
672
|
+
verbose output for debugging purposes
|
|
673
|
+
|
|
674
|
+
- ``homogeneous`` -- boolean (default: ``False``); if ``True`` set
|
|
675
|
+
up the cone without explicit inhomogenization
|
|
676
|
+
|
|
677
|
+
EXAMPLES::
|
|
678
|
+
|
|
679
|
+
sage: P = (polytopes.hypercube(4, backend='normaliz')
|
|
680
|
+
....: * Polyhedron(rays=[[0,1]])
|
|
681
|
+
....: * Polyhedron(lines=[[1,0]])); P
|
|
682
|
+
A 6-dimensional polyhedron in ZZ^8 defined as the convex hull of 16 vertices, 1 ray, 1 line
|
|
683
|
+
|
|
684
|
+
sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
|
|
685
|
+
....: P.vertices(), P.rays(), P.lines(),
|
|
686
|
+
....: P.inequalities(), P.equations())
|
|
687
|
+
sage: import PyNormaliz
|
|
688
|
+
sage: PyNormaliz.NmzIsComputed(cone, "VerticesOfPolyhedron")
|
|
689
|
+
True
|
|
690
|
+
sage: PyNormaliz.NmzIsComputed(cone, "ExtremeRays")
|
|
691
|
+
True
|
|
692
|
+
sage: PyNormaliz.NmzIsComputed(cone, "MaximalSubspace")
|
|
693
|
+
True
|
|
694
|
+
sage: PyNormaliz.NmzIsComputed(cone, "SupportHyperplanes")
|
|
695
|
+
True
|
|
696
|
+
sage: PyNormaliz.NmzIsComputed(cone, "Equations")
|
|
697
|
+
False
|
|
698
|
+
|
|
699
|
+
All values must be specified::
|
|
700
|
+
|
|
701
|
+
sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
|
|
702
|
+
....: P.vertices(), None, P.lines(),
|
|
703
|
+
....: P.inequalities(), P.equations())
|
|
704
|
+
Traceback (most recent call last):
|
|
705
|
+
...
|
|
706
|
+
ValueError: please specify vertices, rays, lines, inequalities and equations completely
|
|
707
|
+
|
|
708
|
+
This method cannot be used for the empty cone::
|
|
709
|
+
|
|
710
|
+
sage: P = Polyhedron(backend='normaliz')
|
|
711
|
+
sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
|
|
712
|
+
....: P.vertices(), P.rays(), P.lines(),
|
|
713
|
+
....: P.inequalities(), P.equations())
|
|
714
|
+
Traceback (most recent call last):
|
|
715
|
+
...
|
|
716
|
+
ValueError: this method cannot be used to initialize the empty cone
|
|
717
|
+
|
|
718
|
+
TESTS::
|
|
719
|
+
|
|
720
|
+
sage: def test_poly(P):
|
|
721
|
+
....: cone = P._cone_from_Vrepresentation_and_Hrepresentation(P.vertices(),P.rays(),P.lines(),P.inequalities(),P.equations())
|
|
722
|
+
....: cone2 = P._normaliz_cone
|
|
723
|
+
....: args = ['Equations','VerticesOfPolyhedron','ExtremeRays','SupportHyperplanes','MaximalSubspace']
|
|
724
|
+
....: return all(P._nmz_result(cone,arg) == P._nmz_result(cone2, arg)
|
|
725
|
+
....: for arg in args)
|
|
726
|
+
sage: test_poly(polytopes.simplex(backend='normaliz'))
|
|
727
|
+
True
|
|
728
|
+
sage: test_poly(polytopes.dodecahedron(backend='normaliz')) # needs sage.rings.number_field
|
|
729
|
+
True
|
|
730
|
+
sage: test_poly(Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]],
|
|
731
|
+
....: backend='normaliz'))
|
|
732
|
+
True
|
|
733
|
+
sage: test_poly(Polyhedron(vertices=[[-1,0], [1,0]], lines=[[0,1]],
|
|
734
|
+
....: backend='normaliz'))
|
|
735
|
+
True
|
|
736
|
+
sage: test_poly(Polyhedron(rays=[[1,0,0],[0,1,0]],
|
|
737
|
+
....: backend='normaliz'))
|
|
738
|
+
True
|
|
739
|
+
sage: test_poly(Polyhedron(vertices=[[1,0,0], [0,1,0]], rays=[[1,0,0], [0,1,0]],
|
|
740
|
+
....: backend='normaliz'))
|
|
741
|
+
True
|
|
742
|
+
sage: test_poly(Polyhedron(vertices=[[0,0,0], [0,1,1], [1,0,1], [-1,-1,1]],
|
|
743
|
+
....: rays=[[0,0,1]],
|
|
744
|
+
....: backend='normaliz'))
|
|
745
|
+
True
|
|
746
|
+
|
|
747
|
+
Old input format will give a meaningful error message::
|
|
748
|
+
|
|
749
|
+
sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
|
|
750
|
+
....: P.vertices(), P.rays(),
|
|
751
|
+
....: P.inequalities(), P.equations())
|
|
752
|
+
Traceback (most recent call last):
|
|
753
|
+
...
|
|
754
|
+
ValueError: the specification of this method has changed; please specify the lines as well
|
|
755
|
+
|
|
756
|
+
sage: cone = P._cone_from_Vrepresentation_and_Hrepresentation(
|
|
757
|
+
....: P.vertices(), P.rays(),
|
|
758
|
+
....: P.inequalities(), P.equations(), True)
|
|
759
|
+
Traceback (most recent call last):
|
|
760
|
+
...
|
|
761
|
+
ValueError: the specification of this method has changed; please specify the lines as well
|
|
762
|
+
|
|
763
|
+
Check that :issue:`30891` is fixed::
|
|
764
|
+
|
|
765
|
+
sage: p = Polyhedron(vertices=[(-3,-3), (3,0), (3,3), (0,3)],
|
|
766
|
+
....: backend='normaliz')
|
|
767
|
+
sage: q = loads(p.dumps())
|
|
768
|
+
sage: q.volume()
|
|
769
|
+
18
|
|
770
|
+
sage: q.ehrhart_series()
|
|
771
|
+
(13*t^2 + 22*t + 1)/(-t^3 + 3*t^2 - 3*t + 1)
|
|
772
|
+
"""
|
|
773
|
+
if eqns in (True, False, None):
|
|
774
|
+
# Previously, the method had input ``vertices, rays, ieqs, eqns`` (optionally ``verbose``).
|
|
775
|
+
# Now it requires ``vertices, rays, lines, ieqs, eqns``.
|
|
776
|
+
# Actually, ``eqns`` wouldn't be required, but we keep it to catch deprecated calls.
|
|
777
|
+
# (And it's more stable against changes of normaliz now.)
|
|
778
|
+
raise ValueError("the specification of this method has changed; please specify the lines as well")
|
|
779
|
+
if None in (vertices, rays, lines, ieqs, eqns):
|
|
780
|
+
raise ValueError("please specify vertices, rays, lines, inequalities and equations completely")
|
|
781
|
+
if not vertices:
|
|
782
|
+
raise ValueError("this method cannot be used to initialize the empty cone")
|
|
783
|
+
|
|
784
|
+
def rays_subspace_lattice_ieqs_QQ(vertices, rays, lines, ieqs):
|
|
785
|
+
nmz_vertices = []
|
|
786
|
+
for v in vertices:
|
|
787
|
+
d = LCM_list([denominator(v_i) for v_i in v])
|
|
788
|
+
dv = [d * v_i for v_i in v]
|
|
789
|
+
nmz_vertices.append(dv + [d])
|
|
790
|
+
nmz_rays = []
|
|
791
|
+
for r in rays:
|
|
792
|
+
d = LCM_list([denominator(r_i) for r_i in r])
|
|
793
|
+
dr = [d * r_i for r_i in r]
|
|
794
|
+
nmz_rays.append(dr + [0])
|
|
795
|
+
nmz_lines = []
|
|
796
|
+
for l in lines:
|
|
797
|
+
d = LCM_list([denominator(l_i) for l_i in l])
|
|
798
|
+
dl = [d * l_i for l_i in l]
|
|
799
|
+
nmz_lines.append(dl + [0])
|
|
800
|
+
|
|
801
|
+
nmz_ieqs = []
|
|
802
|
+
for ieq in ieqs:
|
|
803
|
+
d = LCM_list([denominator(ieq_i) for ieq_i in ieq])
|
|
804
|
+
dieq = [ZZ(d * ieq_i) for ieq_i in ieq]
|
|
805
|
+
b = dieq[0]
|
|
806
|
+
A = dieq[1:]
|
|
807
|
+
nmz_ieqs.append(A + [b])
|
|
808
|
+
|
|
809
|
+
from sage.matrix.constructor import Matrix
|
|
810
|
+
lattice = Matrix(ZZ, nmz_vertices + nmz_rays + nmz_lines).saturation()
|
|
811
|
+
nmz_lattice = [list(y) for y in lattice]
|
|
812
|
+
|
|
813
|
+
if Matrix(ZZ, nmz_vertices + nmz_rays).rank() == Matrix(ZZ, nmz_rays).rank() + 1:
|
|
814
|
+
# The recession cone is full-dimensional.
|
|
815
|
+
# In this case the homogenized inequalities
|
|
816
|
+
# do not ensure nonnegativy in the last coordinate.
|
|
817
|
+
# In the homogeneous cone the far face is a facet.
|
|
818
|
+
pos_ieq = [ZZ.zero()] * len(nmz_vertices[0])
|
|
819
|
+
pos_ieq[-1] = ZZ.one()
|
|
820
|
+
nmz_ieqs.append(pos_ieq)
|
|
821
|
+
|
|
822
|
+
return nmz_vertices + nmz_rays, nmz_lines, nmz_lattice, nmz_ieqs
|
|
823
|
+
|
|
824
|
+
def rays_subspace_lattice_ieqs_NF(vertices, rays, lines, ieqs):
|
|
825
|
+
nmz_vertices = [list(v) + [1] for v in vertices]
|
|
826
|
+
nmz_rays = [list(r) + [0] for r in rays]
|
|
827
|
+
nmz_lines = [list(l) + [1] for l in lines]
|
|
828
|
+
|
|
829
|
+
nmz_ieqs = []
|
|
830
|
+
for ieq in ieqs:
|
|
831
|
+
b = ieq[0]
|
|
832
|
+
A = ieq[1:]
|
|
833
|
+
nmz_ieqs.append(list(A) + [b])
|
|
834
|
+
|
|
835
|
+
from sage.matrix.constructor import Matrix
|
|
836
|
+
lattice = Matrix(nmz_vertices + nmz_rays + nmz_lines).row_space().basis()
|
|
837
|
+
nmz_lattice = [list(y) for y in lattice]
|
|
838
|
+
|
|
839
|
+
if Matrix(nmz_vertices + nmz_rays).rank() == Matrix(nmz_rays).rank() + 1:
|
|
840
|
+
# The recession cone is full-dimensional.
|
|
841
|
+
# In this case the homogenized inequalities
|
|
842
|
+
# do not ensure nonnegativy in the last coordinate.
|
|
843
|
+
# In the homogeneous cone the far face is a facet.
|
|
844
|
+
pos_ieq = [0] * len(nmz_vertices[0])
|
|
845
|
+
pos_ieq[-1] = 1
|
|
846
|
+
nmz_ieqs.append(pos_ieq)
|
|
847
|
+
|
|
848
|
+
return nmz_vertices + nmz_rays, nmz_lines, nmz_lattice, nmz_ieqs
|
|
849
|
+
|
|
850
|
+
(nmz_extreme_rays, nmz_subspace, nmz_lattice, nmz_ieqs), internal_base_ring \
|
|
851
|
+
= self._compute_data_lists_and_internal_base_ring(
|
|
852
|
+
(vertices, rays, lines, ieqs), rays_subspace_lattice_ieqs_QQ,
|
|
853
|
+
rays_subspace_lattice_ieqs_NF)
|
|
854
|
+
|
|
855
|
+
data = {"extreme_rays": nmz_extreme_rays,
|
|
856
|
+
"maximal_subspace": nmz_subspace,
|
|
857
|
+
"generated_lattice": nmz_lattice,
|
|
858
|
+
"support_hyperplanes": nmz_ieqs}
|
|
859
|
+
|
|
860
|
+
ambient_dim = len(data["extreme_rays"][0])
|
|
861
|
+
if not homogeneous:
|
|
862
|
+
data["dehomogenization"] = [[0] * (ambient_dim - 1) + [1]]
|
|
863
|
+
|
|
864
|
+
number_field_data = self._number_field_triple(internal_base_ring)
|
|
865
|
+
if number_field_data:
|
|
866
|
+
data["number_field"] = number_field_data
|
|
867
|
+
return self._cone_from_normaliz_data(data, verbose=verbose)
|
|
868
|
+
|
|
869
|
+
def _test_far_facet_condition(self, tester=None, **options):
|
|
870
|
+
"""
|
|
871
|
+
Test that we add an extra inequality in the correct cases.
|
|
872
|
+
|
|
873
|
+
TESTS::
|
|
874
|
+
|
|
875
|
+
sage: P = Polyhedron(rays=[[1,1]], backend='normaliz')
|
|
876
|
+
sage: P._test_far_facet_condition()
|
|
877
|
+
|
|
878
|
+
sage: P = Polyhedron(vertices=[[1,0], [0,1]],
|
|
879
|
+
....: rays=[[1,1]], backend='normaliz')
|
|
880
|
+
sage: P._test_far_facet_condition()
|
|
881
|
+
|
|
882
|
+
sage: P = Polyhedron(rays=[[1,1,0]],
|
|
883
|
+
....: lines=[[0,0,1]], backend='normaliz')
|
|
884
|
+
sage: P._test_far_facet_condition()
|
|
885
|
+
|
|
886
|
+
sage: P = Polyhedron(vertices=[[1,0,0], [0,1,0]],
|
|
887
|
+
....: rays=[[1,1,0]],
|
|
888
|
+
....: lines=[[0,0,1]], backend='normaliz')
|
|
889
|
+
sage: P._test_far_facet_condition()
|
|
890
|
+
"""
|
|
891
|
+
if tester is None:
|
|
892
|
+
tester = self._tester(**options)
|
|
893
|
+
|
|
894
|
+
if self.is_empty():
|
|
895
|
+
return
|
|
896
|
+
|
|
897
|
+
nmz_vertices = self._nmz_result(self._normaliz_cone, "VerticesOfPolyhedron")
|
|
898
|
+
nmz_rays = self._nmz_result(self._normaliz_cone, "ExtremeRays")
|
|
899
|
+
nmz_ieqs = self._nmz_result(self._normaliz_cone, "SupportHyperplanes")
|
|
900
|
+
|
|
901
|
+
from sage.matrix.constructor import Matrix
|
|
902
|
+
far_facet_condition = Matrix(nmz_vertices + nmz_rays).rank() == Matrix(nmz_rays).rank() + 1
|
|
903
|
+
|
|
904
|
+
tester.assertEqual(far_facet_condition, self.n_inequalities() != len(nmz_ieqs))
|
|
905
|
+
|
|
906
|
+
if far_facet_condition:
|
|
907
|
+
tester.assertEqual(self.n_inequalities() + 1, len(nmz_ieqs))
|
|
908
|
+
tester.assertTrue(any(ieq == [0] * self.ambient_dim() + [1] for ieq in nmz_ieqs))
|
|
909
|
+
|
|
910
|
+
def _init_Vrepresentation_from_normaliz(self):
|
|
911
|
+
r"""
|
|
912
|
+
Create the Vrepresentation objects from the normaliz polyhedron.
|
|
913
|
+
|
|
914
|
+
EXAMPLES::
|
|
915
|
+
|
|
916
|
+
sage: p = Polyhedron(vertices=[(0,1/2), (2,0), (4,5/6)], # indirect doctest
|
|
917
|
+
....: backend='normaliz')
|
|
918
|
+
sage: p.Hrepresentation()
|
|
919
|
+
(An inequality (-5, 12) x + 10 >= 0,
|
|
920
|
+
An inequality (1, -12) x + 6 >= 0,
|
|
921
|
+
An inequality (1, 4) x - 2 >= 0)
|
|
922
|
+
sage: p.Vrepresentation()
|
|
923
|
+
(A vertex at (0, 1/2), A vertex at (2, 0), A vertex at (4, 5/6))
|
|
924
|
+
"""
|
|
925
|
+
self._Vrepresentation = []
|
|
926
|
+
parent = self.parent()
|
|
927
|
+
base_ring = self.base_ring()
|
|
928
|
+
cone = self._normaliz_cone
|
|
929
|
+
for g in self._nmz_result(cone, "VerticesOfPolyhedron"):
|
|
930
|
+
d = g[-1]
|
|
931
|
+
if d == 1:
|
|
932
|
+
parent._make_Vertex(self, g[:-1])
|
|
933
|
+
else:
|
|
934
|
+
parent._make_Vertex(self, [base_ring(x) / d for x in g[:-1]])
|
|
935
|
+
for g in self._nmz_result(cone, "ExtremeRays"):
|
|
936
|
+
parent._make_Ray(self, g[:-1])
|
|
937
|
+
for g in self._nmz_result(cone, "MaximalSubspace"):
|
|
938
|
+
parent._make_Line(self, g[:-1])
|
|
939
|
+
self._Vrepresentation = tuple(self._Vrepresentation)
|
|
940
|
+
|
|
941
|
+
def _init_Hrepresentation_from_normaliz(self):
|
|
942
|
+
r"""
|
|
943
|
+
Create the Hrepresentation objects from the normaliz polyhedron.
|
|
944
|
+
|
|
945
|
+
EXAMPLES::
|
|
946
|
+
|
|
947
|
+
sage: p = Polyhedron(vertices=[(0,1/2), (2,0), (4,5/6)], # indirect doctest
|
|
948
|
+
....: backend='normaliz')
|
|
949
|
+
sage: p.Hrepresentation()
|
|
950
|
+
(An inequality (-5, 12) x + 10 >= 0,
|
|
951
|
+
An inequality (1, -12) x + 6 >= 0,
|
|
952
|
+
An inequality (1, 4) x - 2 >= 0)
|
|
953
|
+
sage: p.Vrepresentation()
|
|
954
|
+
(A vertex at (0, 1/2), A vertex at (2, 0), A vertex at (4, 5/6))
|
|
955
|
+
"""
|
|
956
|
+
self._Hrepresentation = []
|
|
957
|
+
cone = self._normaliz_cone
|
|
958
|
+
parent = self.parent()
|
|
959
|
+
for g in self._nmz_result(cone, "SupportHyperplanes"):
|
|
960
|
+
if all(x == 0 for x in g[:-1]):
|
|
961
|
+
# Ignore vertical inequality
|
|
962
|
+
pass
|
|
963
|
+
else:
|
|
964
|
+
parent._make_Inequality(self, (g[-1],) + tuple(g[:-1]))
|
|
965
|
+
for g in self._nmz_result(cone, "Equations"):
|
|
966
|
+
parent._make_Equation(self, (g[-1],) + tuple(g[:-1]))
|
|
967
|
+
self._Hrepresentation = tuple(self._Hrepresentation)
|
|
968
|
+
|
|
969
|
+
def _init_empty_polyhedron(self):
|
|
970
|
+
r"""
|
|
971
|
+
Initialize an empty polyhedron.
|
|
972
|
+
|
|
973
|
+
TESTS::
|
|
974
|
+
|
|
975
|
+
sage: empty = Polyhedron(backend='normaliz'); empty
|
|
976
|
+
The empty polyhedron in ZZ^0
|
|
977
|
+
sage: empty.Vrepresentation()
|
|
978
|
+
()
|
|
979
|
+
sage: empty.Hrepresentation()
|
|
980
|
+
(An equation -1 == 0,)
|
|
981
|
+
sage: Polyhedron(vertices=[], backend='normaliz')
|
|
982
|
+
The empty polyhedron in ZZ^0
|
|
983
|
+
sage: Polyhedron(backend='normaliz')._init_empty_polyhedron()
|
|
984
|
+
"""
|
|
985
|
+
super()._init_empty_polyhedron()
|
|
986
|
+
# Can't seem to set up an empty _normaliz_cone.
|
|
987
|
+
# For example, PyNormaliz.NmzCone(vertices=[]) gives
|
|
988
|
+
# error: Some error in the normaliz input data detected: All input matrices empty!
|
|
989
|
+
self._normaliz_cone = None
|
|
990
|
+
|
|
991
|
+
@classmethod
|
|
992
|
+
def _from_normaliz_cone(cls, parent, normaliz_cone, internal_base_ring=None):
|
|
993
|
+
r"""
|
|
994
|
+
Initialize a polyhedron from a PyNormaliz wrapper of a normaliz cone.
|
|
995
|
+
|
|
996
|
+
TESTS::
|
|
997
|
+
|
|
998
|
+
sage: P=Polyhedron(ieqs=[[1, 0, 2], [3, 0, -2], [3, 2, -2]],
|
|
999
|
+
....: backend='normaliz')
|
|
1000
|
+
sage: PI = P.integral_hull() # indirect doctest
|
|
1001
|
+
"""
|
|
1002
|
+
return cls(parent, None, None, normaliz_cone=normaliz_cone, internal_base_ring=internal_base_ring)
|
|
1003
|
+
|
|
1004
|
+
@staticmethod
|
|
1005
|
+
def _number_field_triple(internal_base_ring) -> list:
|
|
1006
|
+
r"""
|
|
1007
|
+
Construct the PyNormaliz triple that describes ``internal_base_ring``.
|
|
1008
|
+
|
|
1009
|
+
TESTS::
|
|
1010
|
+
|
|
1011
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz as Pn
|
|
1012
|
+
sage: Pn._number_field_triple(QQ) is None
|
|
1013
|
+
True
|
|
1014
|
+
sage: Pn._number_field_triple(QuadraticField(5)) # needs sage.rings.number_field
|
|
1015
|
+
['a^2 - 5', 'a', '[2.236067977499789 +/- 8.06e-16]']
|
|
1016
|
+
"""
|
|
1017
|
+
R = internal_base_ring
|
|
1018
|
+
if R is QQ:
|
|
1019
|
+
return None
|
|
1020
|
+
from sage.rings.real_arb import RealBallField
|
|
1021
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
1022
|
+
emb = RealBallField(53)(R.gen(0))
|
|
1023
|
+
gen = 'a'
|
|
1024
|
+
R_a = PolynomialRing(QQ, gen)
|
|
1025
|
+
min_poly = R_a(R.polynomial())
|
|
1026
|
+
return [str(min_poly), gen, str(emb)]
|
|
1027
|
+
|
|
1028
|
+
@staticmethod
|
|
1029
|
+
def _make_normaliz_cone(data, verbose=False):
|
|
1030
|
+
r"""
|
|
1031
|
+
Return a normaliz cone from ``data``.
|
|
1032
|
+
|
|
1033
|
+
INPUT:
|
|
1034
|
+
|
|
1035
|
+
- ``data`` -- dictionary
|
|
1036
|
+
|
|
1037
|
+
- ``verbose`` -- boolean (default: ``False``)
|
|
1038
|
+
|
|
1039
|
+
TESTS::
|
|
1040
|
+
|
|
1041
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
|
|
1042
|
+
sage: data = {'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]}
|
|
1043
|
+
sage: nmz_cone = Polyhedron_normaliz._make_normaliz_cone(data,verbose=False)
|
|
1044
|
+
sage: from PyNormaliz import NmzResult
|
|
1045
|
+
sage: NmzResult(nmz_cone, "ExtremeRays")
|
|
1046
|
+
[[1, 2, 0], [2, 1, 0]]
|
|
1047
|
+
"""
|
|
1048
|
+
if verbose:
|
|
1049
|
+
print("# Calling PyNormaliz.NmzCone(**{})".format(data))
|
|
1050
|
+
cone = NmzCone(**data)
|
|
1051
|
+
assert cone, "NmzCone(**{}) did not return a cone".format(data)
|
|
1052
|
+
return cone
|
|
1053
|
+
|
|
1054
|
+
def _get_nmzcone_data(self) -> dict:
|
|
1055
|
+
r"""
|
|
1056
|
+
Get the data necessary to reproduce the normaliz cone.
|
|
1057
|
+
|
|
1058
|
+
OUTPUT: ``data`` -- dictionary
|
|
1059
|
+
|
|
1060
|
+
TESTS:
|
|
1061
|
+
|
|
1062
|
+
The empty polyhedron::
|
|
1063
|
+
|
|
1064
|
+
sage: P = Polyhedron(backend='normaliz')
|
|
1065
|
+
sage: P._get_nmzcone_data()
|
|
1066
|
+
{}
|
|
1067
|
+
|
|
1068
|
+
Another simple example::
|
|
1069
|
+
|
|
1070
|
+
sage: C = Polyhedron(backend='normaliz', rays=[[1, 2], [2, 1]])
|
|
1071
|
+
sage: C._get_nmzcone_data()
|
|
1072
|
+
{'cone': [[1, 2], [2, 1]],
|
|
1073
|
+
'inhom_equations': [],
|
|
1074
|
+
'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]],
|
|
1075
|
+
'subspace': [],
|
|
1076
|
+
'vertices': [[0, 0, 1]]}
|
|
1077
|
+
"""
|
|
1078
|
+
if self.is_empty():
|
|
1079
|
+
return {}
|
|
1080
|
+
|
|
1081
|
+
vertices = self._nmz_result(self._normaliz_cone, "VerticesOfPolyhedron")
|
|
1082
|
+
# get rid of the last 0 in rays:
|
|
1083
|
+
rays = [r[:-1] for r in self._nmz_result(self._normaliz_cone, "ExtremeRays")]
|
|
1084
|
+
lines = self._nmz_result(self._normaliz_cone, "MaximalSubspace")
|
|
1085
|
+
ineqs = self._nmz_result(self._normaliz_cone, "SupportHyperplanes")
|
|
1086
|
+
eqs = self._nmz_result(self._normaliz_cone, "Equations")
|
|
1087
|
+
|
|
1088
|
+
return {'vertices': vertices,
|
|
1089
|
+
'cone': rays,
|
|
1090
|
+
'subspace': lines,
|
|
1091
|
+
'inhom_equations': eqs,
|
|
1092
|
+
'inhom_inequalities': ineqs}
|
|
1093
|
+
|
|
1094
|
+
def _normaliz_format(self, data, file_output=None):
|
|
1095
|
+
r"""
|
|
1096
|
+
Return a string containing normaliz format.
|
|
1097
|
+
|
|
1098
|
+
INPUT:
|
|
1099
|
+
|
|
1100
|
+
- ``data`` -- dictionary of PyNormaliz cone input properties
|
|
1101
|
+
|
|
1102
|
+
- ``file_output`` -- string (optional); a filename to which the
|
|
1103
|
+
representation should be written. If set to ``None`` (default),
|
|
1104
|
+
representation is returned as a string.
|
|
1105
|
+
|
|
1106
|
+
EXAMPLES::
|
|
1107
|
+
|
|
1108
|
+
sage: P = Polyhedron(vertices=[[0, 0], [0, 1], [1, 0]], # indirect doctest
|
|
1109
|
+
....: backend='normaliz', verbose=True)
|
|
1110
|
+
# ----8<---- Equivalent Normaliz input file ----8<----
|
|
1111
|
+
amb_space 2
|
|
1112
|
+
cone 0
|
|
1113
|
+
subspace 0
|
|
1114
|
+
vertices 3
|
|
1115
|
+
0 0 1
|
|
1116
|
+
0 1 1
|
|
1117
|
+
1 0 1
|
|
1118
|
+
# ----8<-------------------8<-------------------8<----
|
|
1119
|
+
# Calling ...
|
|
1120
|
+
"""
|
|
1121
|
+
def format_number(x):
|
|
1122
|
+
try:
|
|
1123
|
+
return '{}'.format(QQ(x))
|
|
1124
|
+
except (ValueError, TypeError):
|
|
1125
|
+
return '({})'.format(x.polynomial('a'))
|
|
1126
|
+
|
|
1127
|
+
def format_field(key, value):
|
|
1128
|
+
if isinstance(value, (list, tuple)):
|
|
1129
|
+
s = '{} {}\n'.format(key, len(value))
|
|
1130
|
+
for e in value:
|
|
1131
|
+
for x in e:
|
|
1132
|
+
s += ' ' + format_number(x)
|
|
1133
|
+
s += '\n'
|
|
1134
|
+
return s
|
|
1135
|
+
return '{} {}\n'.format(key, value)
|
|
1136
|
+
|
|
1137
|
+
def format_number_field_data(nf_triple):
|
|
1138
|
+
min_poly, gen, emb = nf_triple
|
|
1139
|
+
return 'min_poly ({}) embedding {}'.format(min_poly, emb)
|
|
1140
|
+
|
|
1141
|
+
s = format_field('amb_space', self.ambient_dim())
|
|
1142
|
+
if 'number_field' in data:
|
|
1143
|
+
from copy import copy
|
|
1144
|
+
data = copy(data)
|
|
1145
|
+
s += 'number_field {}\n'.format(format_number_field_data(data['number_field']))
|
|
1146
|
+
del data['number_field']
|
|
1147
|
+
for key, value in sorted(data.items()):
|
|
1148
|
+
s += format_field(key, value)
|
|
1149
|
+
if file_output is not None:
|
|
1150
|
+
with open(file_output, 'w') as in_file:
|
|
1151
|
+
in_file.write(s)
|
|
1152
|
+
else:
|
|
1153
|
+
return s
|
|
1154
|
+
|
|
1155
|
+
def __copy__(self):
|
|
1156
|
+
r"""
|
|
1157
|
+
Return a copy of ``self``.
|
|
1158
|
+
|
|
1159
|
+
TESTS::
|
|
1160
|
+
|
|
1161
|
+
sage: P = polytopes.cube(backend='normaliz')
|
|
1162
|
+
sage: Q = copy(P)
|
|
1163
|
+
sage: P._normaliz_cone is Q._normaliz_cone
|
|
1164
|
+
False
|
|
1165
|
+
"""
|
|
1166
|
+
other = super().__copy__()
|
|
1167
|
+
|
|
1168
|
+
# Make a copy of the cone.
|
|
1169
|
+
cone = self._normaliz_cone
|
|
1170
|
+
conecopy = NmzConeCopy(cone)
|
|
1171
|
+
other._normaliz_cone = conecopy
|
|
1172
|
+
return other
|
|
1173
|
+
|
|
1174
|
+
def __getstate__(self):
|
|
1175
|
+
r"""
|
|
1176
|
+
Remove the normaliz cone for pickling.
|
|
1177
|
+
|
|
1178
|
+
TESTS::
|
|
1179
|
+
|
|
1180
|
+
sage: P = polytopes.simplex(backend='normaliz')
|
|
1181
|
+
sage: P.__getstate__()
|
|
1182
|
+
(Polyhedra in ZZ^4,
|
|
1183
|
+
{'_Hrepresentation': (An inequality (0, 0, 0, 1) x + 0 >= 0,
|
|
1184
|
+
An inequality (0, 0, 1, 0) x + 0 >= 0,
|
|
1185
|
+
An inequality (0, 1, 0, 0) x + 0 >= 0,
|
|
1186
|
+
An inequality (1, 0, 0, 0) x + 0 >= 0,
|
|
1187
|
+
An equation (1, 1, 1, 1) x - 1 == 0),
|
|
1188
|
+
'_Vrepresentation': (A vertex at (0, 0, 0, 1),
|
|
1189
|
+
A vertex at (0, 0, 1, 0),
|
|
1190
|
+
A vertex at (0, 1, 0, 0),
|
|
1191
|
+
A vertex at (1, 0, 0, 0)),
|
|
1192
|
+
'_internal_base_ring': Rational Field,
|
|
1193
|
+
'_pickle_equations': [(-1, 1, 1, 1, 1)],
|
|
1194
|
+
'_pickle_inequalities': [(0, 0, 0, 0, 1),
|
|
1195
|
+
(0, 0, 0, 1, 0),
|
|
1196
|
+
(0, 0, 1, 0, 0),
|
|
1197
|
+
(0, 1, 0, 0, 0)],
|
|
1198
|
+
'_pickle_lines': [],
|
|
1199
|
+
'_pickle_rays': [],
|
|
1200
|
+
'_pickle_vertices': [(0, 0, 0, 1),
|
|
1201
|
+
(0, 0, 1, 0),
|
|
1202
|
+
(0, 1, 0, 0),
|
|
1203
|
+
(1, 0, 0, 0)]})
|
|
1204
|
+
"""
|
|
1205
|
+
state = super().__getstate__()
|
|
1206
|
+
state = (state[0], state[1].copy())
|
|
1207
|
+
# Remove the unpicklable entries.
|
|
1208
|
+
del state[1]['_normaliz_cone']
|
|
1209
|
+
state[1]["_pickle_vertices"] = [v._vector for v in self.vertices()]
|
|
1210
|
+
state[1]["_pickle_rays"] = [v._vector for v in self.rays()]
|
|
1211
|
+
state[1]["_pickle_lines"] = [v._vector for v in self.lines()]
|
|
1212
|
+
state[1]["_pickle_inequalities"] = [v._vector for v in self.inequalities()]
|
|
1213
|
+
state[1]["_pickle_equations"] = [v._vector for v in self.equations()]
|
|
1214
|
+
return state
|
|
1215
|
+
|
|
1216
|
+
def __setstate__(self, state):
|
|
1217
|
+
r"""
|
|
1218
|
+
Initialize the normaliz cone after pickling.
|
|
1219
|
+
|
|
1220
|
+
TESTS::
|
|
1221
|
+
|
|
1222
|
+
sage: P = polytopes.permutahedron(4, backend='normaliz')
|
|
1223
|
+
sage: P.volume(measure='induced_lattice', engine='normaliz')
|
|
1224
|
+
96
|
|
1225
|
+
sage: P.volume.clear_cache()
|
|
1226
|
+
sage: P1 = loads(dumps(P)) # indirect doctest
|
|
1227
|
+
sage: P1.volume(measure='induced_lattice', engine='normaliz')
|
|
1228
|
+
96
|
|
1229
|
+
|
|
1230
|
+
Test that the obtained cone is valid::
|
|
1231
|
+
|
|
1232
|
+
sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz
|
|
1233
|
+
sage: P = polytopes.permutahedron(4, backend='normaliz')
|
|
1234
|
+
sage: P1 = loads(dumps(P))
|
|
1235
|
+
sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
|
|
1236
|
+
sage: P2 == P
|
|
1237
|
+
True
|
|
1238
|
+
|
|
1239
|
+
sage: P = Polyhedron(lines=[[1,0], [0,1]], backend='normaliz')
|
|
1240
|
+
sage: P1 = loads(dumps(P))
|
|
1241
|
+
sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
|
|
1242
|
+
sage: P2 == P
|
|
1243
|
+
True
|
|
1244
|
+
|
|
1245
|
+
sage: P = Polyhedron(backend='normaliz')
|
|
1246
|
+
sage: P1 = loads(dumps(P))
|
|
1247
|
+
sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
|
|
1248
|
+
sage: P2 == P
|
|
1249
|
+
True
|
|
1250
|
+
|
|
1251
|
+
sage: P = polytopes.permutahedron(4, backend='normaliz') * Polyhedron(lines=[[1]], backend='normaliz')
|
|
1252
|
+
sage: P1 = loads(dumps(P))
|
|
1253
|
+
sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone)
|
|
1254
|
+
sage: P2 == P
|
|
1255
|
+
True
|
|
1256
|
+
|
|
1257
|
+
sage: # needs sage.rings.number_field
|
|
1258
|
+
sage: P = polytopes.dodecahedron(backend='normaliz')
|
|
1259
|
+
sage: P1 = loads(dumps(P))
|
|
1260
|
+
sage: P2 = Polyhedron_normaliz(P1.parent(), None, None, P1._normaliz_cone,
|
|
1261
|
+
....: internal_base_ring=P1._internal_base_ring)
|
|
1262
|
+
sage: P == P2
|
|
1263
|
+
True
|
|
1264
|
+
|
|
1265
|
+
Test that :issue:`31820` is fixed::
|
|
1266
|
+
|
|
1267
|
+
sage: P = polytopes.cube(backend='normaliz')
|
|
1268
|
+
sage: v = P.Vrepresentation()[0]
|
|
1269
|
+
sage: v1 = loads(v.dumps())
|
|
1270
|
+
"""
|
|
1271
|
+
if "_pickle_vertices" in state[1]:
|
|
1272
|
+
vertices = state[1].pop("_pickle_vertices")
|
|
1273
|
+
rays = state[1].pop("_pickle_rays")
|
|
1274
|
+
lines = state[1].pop("_pickle_lines")
|
|
1275
|
+
inequalities = state[1].pop("_pickle_inequalities")
|
|
1276
|
+
equations = state[1].pop("_pickle_equations")
|
|
1277
|
+
else:
|
|
1278
|
+
vertices = None
|
|
1279
|
+
|
|
1280
|
+
super().__setstate__(state)
|
|
1281
|
+
|
|
1282
|
+
if self.is_empty():
|
|
1283
|
+
# Special case to avoid.
|
|
1284
|
+
self._normaliz_cone = None
|
|
1285
|
+
return
|
|
1286
|
+
|
|
1287
|
+
if vertices is None:
|
|
1288
|
+
vertices = self.vertices()
|
|
1289
|
+
rays = self.rays()
|
|
1290
|
+
lines = self.lines()
|
|
1291
|
+
inequalities = self.inequalities()
|
|
1292
|
+
equations = self.equations()
|
|
1293
|
+
|
|
1294
|
+
self._normaliz_cone = \
|
|
1295
|
+
self._cone_from_Vrepresentation_and_Hrepresentation(
|
|
1296
|
+
vertices, rays, lines, inequalities, equations)
|
|
1297
|
+
|
|
1298
|
+
def integral_hull(self):
|
|
1299
|
+
r"""
|
|
1300
|
+
Return the integral hull in the polyhedron.
|
|
1301
|
+
|
|
1302
|
+
This is a new polyhedron that is the convex hull of all integral
|
|
1303
|
+
points.
|
|
1304
|
+
|
|
1305
|
+
EXAMPLES:
|
|
1306
|
+
|
|
1307
|
+
Unbounded example from Normaliz manual, "a dull polyhedron"::
|
|
1308
|
+
|
|
1309
|
+
sage: P = Polyhedron(ieqs=[[1, 0, 2], [3, 0, -2], [3, 2, -2]],
|
|
1310
|
+
....: backend='normaliz')
|
|
1311
|
+
sage: PI = P.integral_hull()
|
|
1312
|
+
sage: P.plot(color='yellow') + PI.plot(color='green') # needs sage.plot
|
|
1313
|
+
Graphics object consisting of 10 graphics primitives
|
|
1314
|
+
sage: PI.Vrepresentation()
|
|
1315
|
+
(A vertex at (-1, 0),
|
|
1316
|
+
A vertex at (0, 1),
|
|
1317
|
+
A ray in the direction (1, 0))
|
|
1318
|
+
|
|
1319
|
+
Nonpointed case::
|
|
1320
|
+
|
|
1321
|
+
sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]],
|
|
1322
|
+
....: lines=[[-1, 1]], backend='normaliz')
|
|
1323
|
+
sage: PI = P.integral_hull()
|
|
1324
|
+
sage: PI.Vrepresentation()
|
|
1325
|
+
(A vertex at (1, 0),
|
|
1326
|
+
A ray in the direction (1, 0),
|
|
1327
|
+
A line in the direction (1, -1))
|
|
1328
|
+
|
|
1329
|
+
Empty polyhedron::
|
|
1330
|
+
|
|
1331
|
+
sage: P = Polyhedron(backend='normaliz')
|
|
1332
|
+
sage: PI = P.integral_hull()
|
|
1333
|
+
sage: PI.Vrepresentation()
|
|
1334
|
+
()
|
|
1335
|
+
"""
|
|
1336
|
+
if self.is_empty():
|
|
1337
|
+
return self
|
|
1338
|
+
cone = self._nmz_result(self._normaliz_cone, "IntegerHull")
|
|
1339
|
+
return self.parent().element_class._from_normaliz_cone(parent=self.parent(),
|
|
1340
|
+
normaliz_cone=cone)
|
|
1341
|
+
|
|
1342
|
+
def _h_star_vector_normaliz(self) -> list:
|
|
1343
|
+
r"""
|
|
1344
|
+
Return the `h^*`-vector of the lattice polytope.
|
|
1345
|
+
|
|
1346
|
+
INPUT:
|
|
1347
|
+
|
|
1348
|
+
- ``self`` -- a lattice polytope with backend ``'normaliz'``
|
|
1349
|
+
|
|
1350
|
+
OUTPUT:
|
|
1351
|
+
|
|
1352
|
+
The `h^*`-vector as a list.
|
|
1353
|
+
|
|
1354
|
+
EXAMPLES:
|
|
1355
|
+
|
|
1356
|
+
The `h^*`-vector of a unimodular simplex is 1::
|
|
1357
|
+
|
|
1358
|
+
sage: s3 = polytopes.simplex(3, backend='normaliz')
|
|
1359
|
+
sage: s3._h_star_vector_normaliz()
|
|
1360
|
+
[1]
|
|
1361
|
+
|
|
1362
|
+
The `h^*`-vector of the `0/1`-cube is [1,4,1]::
|
|
1363
|
+
|
|
1364
|
+
sage: cube = polytopes.cube(intervals='zero_one', backend='normaliz')
|
|
1365
|
+
sage: cube.h_star_vector()
|
|
1366
|
+
[1, 4, 1]
|
|
1367
|
+
|
|
1368
|
+
TESTS:
|
|
1369
|
+
|
|
1370
|
+
Check that :issue:`33847` is fixed::
|
|
1371
|
+
|
|
1372
|
+
sage: L = [[1, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0],
|
|
1373
|
+
....: [1, 0, 0, 1, 0, 0], [1, 0, 0, 0, 1, 0], [1, 0, 0, 1, 2, 3]]
|
|
1374
|
+
sage: P = Polyhedron(vertices=L, backend='normaliz')
|
|
1375
|
+
sage: P.h_star_vector()
|
|
1376
|
+
[1, 0, 2]
|
|
1377
|
+
"""
|
|
1378
|
+
return self.ehrhart_series().numerator().list()
|
|
1379
|
+
|
|
1380
|
+
def _volume_normaliz(self, measure='euclidean'):
|
|
1381
|
+
r"""
|
|
1382
|
+
Compute the volume of a polytope using normaliz.
|
|
1383
|
+
|
|
1384
|
+
INPUT:
|
|
1385
|
+
|
|
1386
|
+
- ``measure`` -- string. The measure to use. Allowed values are:
|
|
1387
|
+
|
|
1388
|
+
* ``'euclidean'`` (default): corresponds to ``'EuclideanVolume`` in normaliz
|
|
1389
|
+
* ``'induced_lattice'``: corresponds to ``'Volume'`` in normaliz
|
|
1390
|
+
* ``'ambient'``: Lebesgue measure of ambient space (volume)
|
|
1391
|
+
|
|
1392
|
+
OUTPUT:
|
|
1393
|
+
|
|
1394
|
+
A float value (when ``measure`` is 'euclidean'),
|
|
1395
|
+
a rational number (when ``measure`` is 'induced_lattice'),
|
|
1396
|
+
a rational number or symbolic number otherwise (dependent on base ring).
|
|
1397
|
+
|
|
1398
|
+
.. NOTE::
|
|
1399
|
+
|
|
1400
|
+
This function depends on Normaliz (i.e., the ``pynormaliz`` optional
|
|
1401
|
+
package).
|
|
1402
|
+
|
|
1403
|
+
REFERENCES:
|
|
1404
|
+
|
|
1405
|
+
See section 6.1.1 of [NormalizMan]_.
|
|
1406
|
+
|
|
1407
|
+
EXAMPLES:
|
|
1408
|
+
|
|
1409
|
+
For normaliz, the default is the euclidean volume in the ambient
|
|
1410
|
+
space and the result is a float::
|
|
1411
|
+
|
|
1412
|
+
sage: s = polytopes.simplex(3, backend='normaliz')
|
|
1413
|
+
sage: s._volume_normaliz()
|
|
1414
|
+
0.3333333333333333
|
|
1415
|
+
|
|
1416
|
+
One other possibility is to compute the scaled volume where a unimodular
|
|
1417
|
+
simplex has volume 1::
|
|
1418
|
+
|
|
1419
|
+
sage: s._volume_normaliz(measure='induced_lattice')
|
|
1420
|
+
1
|
|
1421
|
+
sage: v = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]
|
|
1422
|
+
sage: cube = Polyhedron(vertices=v, backend='normaliz')
|
|
1423
|
+
sage: cube._volume_normaliz()
|
|
1424
|
+
1.0
|
|
1425
|
+
sage: cube._volume_normaliz(measure='induced_lattice')
|
|
1426
|
+
6
|
|
1427
|
+
|
|
1428
|
+
Or one can calculate the ambient volume, which is the above multiplied by the
|
|
1429
|
+
volume of the unimodular simplex (or zero if not full-dimensional)::
|
|
1430
|
+
|
|
1431
|
+
sage: cube._volume_normaliz(measure='ambient')
|
|
1432
|
+
1
|
|
1433
|
+
sage: s._volume_normaliz(measure='ambient')
|
|
1434
|
+
0
|
|
1435
|
+
|
|
1436
|
+
TESTS:
|
|
1437
|
+
|
|
1438
|
+
Check that :issue:`28872` is fixed::
|
|
1439
|
+
|
|
1440
|
+
sage: P = polytopes.dodecahedron(backend='normaliz') # needs sage.rings.number_field
|
|
1441
|
+
sage: P.volume(measure='induced_lattice') # needs sage.rings.number_field
|
|
1442
|
+
-1056*sqrt5 + 2400
|
|
1443
|
+
|
|
1444
|
+
Some sanity checks that the ambient volume works correctly::
|
|
1445
|
+
|
|
1446
|
+
sage: (2*cube)._volume_normaliz(measure='ambient')
|
|
1447
|
+
8
|
|
1448
|
+
sage: (1/2*cube)._volume_normaliz(measure='ambient')
|
|
1449
|
+
1/8
|
|
1450
|
+
sage: s._volume_normaliz(measure='ambient')
|
|
1451
|
+
0
|
|
1452
|
+
|
|
1453
|
+
sage: P = polytopes.regular_polygon(3, backend='normaliz') # needs sage.rings.number_field
|
|
1454
|
+
sage: P._volume_normaliz('ambient') == P.volume(engine='internal') # needs sage.rings.number_field
|
|
1455
|
+
True
|
|
1456
|
+
|
|
1457
|
+
sage: P = polytopes.dodecahedron(backend='normaliz') # needs sage.rings.number_field
|
|
1458
|
+
sage: P._volume_normaliz('ambient') == P.volume(engine='internal') # needs sage.rings.number_field
|
|
1459
|
+
True
|
|
1460
|
+
|
|
1461
|
+
sage: P = Polyhedron(rays=[[1]], backend='normaliz')
|
|
1462
|
+
sage: P.volume()
|
|
1463
|
+
+Infinity
|
|
1464
|
+
"""
|
|
1465
|
+
cone = self._normaliz_cone
|
|
1466
|
+
assert cone
|
|
1467
|
+
if measure == 'euclidean':
|
|
1468
|
+
return self._nmz_result(cone, 'EuclideanVolume')
|
|
1469
|
+
|
|
1470
|
+
if measure == 'induced_lattice':
|
|
1471
|
+
if self._internal_base_ring in (ZZ, QQ):
|
|
1472
|
+
return self._nmz_result(cone, 'Volume')
|
|
1473
|
+
return self._nmz_result(cone, 'RenfVolume')
|
|
1474
|
+
|
|
1475
|
+
if measure == 'ambient':
|
|
1476
|
+
if self.dim() < self.ambient_dim():
|
|
1477
|
+
return self.base_ring().zero()
|
|
1478
|
+
if not self.is_compact():
|
|
1479
|
+
from sage.rings.infinity import infinity
|
|
1480
|
+
return infinity
|
|
1481
|
+
|
|
1482
|
+
from sage.arith.misc import factorial
|
|
1483
|
+
return self._volume_normaliz('induced_lattice') / factorial(self.dim())
|
|
1484
|
+
|
|
1485
|
+
raise TypeError("the measure should be `ambient`, `euclidean`, or `induced_lattice`")
|
|
1486
|
+
|
|
1487
|
+
def _triangulate_normaliz(self):
|
|
1488
|
+
r"""
|
|
1489
|
+
Give a triangulation of the polyhedron using normaliz.
|
|
1490
|
+
|
|
1491
|
+
OUTPUT:
|
|
1492
|
+
|
|
1493
|
+
For compact polyhedra a list of simplices
|
|
1494
|
+
each represented by indices of their vertices.
|
|
1495
|
+
|
|
1496
|
+
For cones a list of simplicial cones
|
|
1497
|
+
each represented by indices of their rays.
|
|
1498
|
+
|
|
1499
|
+
.. NOTE::
|
|
1500
|
+
|
|
1501
|
+
This function depends on Normaliz (i.e. the ``pynormaliz`` optional
|
|
1502
|
+
package). See the Normaliz documentation for further details.
|
|
1503
|
+
|
|
1504
|
+
EXAMPLES::
|
|
1505
|
+
|
|
1506
|
+
sage: P = Polyhedron(vertices=[[0,0,1], [1,0,1], [0,1,1], [1,1,1]], backend='normaliz')
|
|
1507
|
+
sage: P._triangulate_normaliz()
|
|
1508
|
+
[(0, 1, 2), (1, 2, 3)]
|
|
1509
|
+
sage: C1 = Polyhedron(rays=[[0,0,1], [1,0,1], [0,1,1], [1,1,1]], backend='normaliz')
|
|
1510
|
+
sage: C1._triangulate_normaliz()
|
|
1511
|
+
[(0, 1, 2), (1, 2, 3)]
|
|
1512
|
+
sage: C2 = Polyhedron(rays=[[1,0,1], [0,0,1], [0,1,1], [1,1,10/9]], backend='normaliz')
|
|
1513
|
+
sage: C2._triangulate_normaliz()
|
|
1514
|
+
[(0, 1, 2), (1, 2, 3)]
|
|
1515
|
+
|
|
1516
|
+
Works only for cones and compact polyhedra::
|
|
1517
|
+
|
|
1518
|
+
sage: P = polytopes.cube(backend='normaliz')
|
|
1519
|
+
sage: Q = Polyhedron(rays=[[0,1]], backend='normaliz')
|
|
1520
|
+
sage: R = Polyhedron(lines=[[0,1]], backend='normaliz')
|
|
1521
|
+
sage: (P*Q)._triangulate_normaliz()
|
|
1522
|
+
Traceback (most recent call last):
|
|
1523
|
+
...
|
|
1524
|
+
NotImplementedError: triangulation of non-compact polyhedra that are not cones is not supported
|
|
1525
|
+
sage: (P*R)._triangulate_normaliz()
|
|
1526
|
+
Traceback (most recent call last):
|
|
1527
|
+
...
|
|
1528
|
+
NotImplementedError: triangulation of non-compact not pointed polyhedron is not supported
|
|
1529
|
+
|
|
1530
|
+
TESTS:
|
|
1531
|
+
|
|
1532
|
+
Check that :issue:`30531` is fixed::
|
|
1533
|
+
|
|
1534
|
+
sage: P = polytopes.cube(backend='normaliz')*AA(2).sqrt()
|
|
1535
|
+
sage: P._triangulate_normaliz()
|
|
1536
|
+
[(0, 1, 2, 4),
|
|
1537
|
+
(1, 2, 4, 3),
|
|
1538
|
+
(1, 3, 4, 5),
|
|
1539
|
+
(3, 5, 6, 7),
|
|
1540
|
+
(6, 2, 4, 3),
|
|
1541
|
+
(6, 3, 4, 5)]
|
|
1542
|
+
|
|
1543
|
+
::
|
|
1544
|
+
|
|
1545
|
+
sage: C1 = Polyhedron(rays=[[0,0,1], [1,0,AA(2).sqrt()], [0,1,1], [1,1,1]],
|
|
1546
|
+
....: backend='normaliz')
|
|
1547
|
+
sage: C1._triangulate_normaliz()
|
|
1548
|
+
[(0, 1, 3), (0, 3, 2)]
|
|
1549
|
+
"""
|
|
1550
|
+
if self.lines():
|
|
1551
|
+
raise NotImplementedError("triangulation of non-compact not pointed polyhedron is not supported")
|
|
1552
|
+
if len(self.vertices_list()) >= 2 and self.rays_list(): # A mix of polytope and cone
|
|
1553
|
+
raise NotImplementedError("triangulation of non-compact polyhedra that are not cones is not supported")
|
|
1554
|
+
|
|
1555
|
+
if self.is_compact():
|
|
1556
|
+
cone = self._normaliz_cone
|
|
1557
|
+
else:
|
|
1558
|
+
# Make a inhomogeneous copy of the cone.
|
|
1559
|
+
cone = self._cone_from_Vrepresentation_and_Hrepresentation(
|
|
1560
|
+
self.vertices(), self.rays(), self.lines(),
|
|
1561
|
+
self.inequalities(), self.equations(), homogeneous=True)
|
|
1562
|
+
|
|
1563
|
+
# Compute the triangulation.
|
|
1564
|
+
assert cone
|
|
1565
|
+
|
|
1566
|
+
# Normaliz does not guarantee that the order of generators is kept during
|
|
1567
|
+
# computation of the triangulation.
|
|
1568
|
+
# Those are the generators that the indices of the triangulation correspond to:
|
|
1569
|
+
nmz_triangulation, nmz_triangulation_generators = self._nmz_result(cone, "Triangulation")
|
|
1570
|
+
|
|
1571
|
+
base_ring = self.base_ring()
|
|
1572
|
+
v_list = self.vertices_list()
|
|
1573
|
+
r_list = self.rays_list()
|
|
1574
|
+
|
|
1575
|
+
new_to_old = {}
|
|
1576
|
+
for i, g in enumerate(nmz_triangulation_generators):
|
|
1577
|
+
if self.is_compact():
|
|
1578
|
+
d = base_ring(g[-1])
|
|
1579
|
+
vertex = [base_ring(x) / d for x in g[:-1]]
|
|
1580
|
+
new_to_old[i] = v_list.index(vertex)
|
|
1581
|
+
else:
|
|
1582
|
+
if g[-1] > 0:
|
|
1583
|
+
new_to_old[i] = None
|
|
1584
|
+
else:
|
|
1585
|
+
try:
|
|
1586
|
+
new_to_old[i] = r_list.index([base_ring(x) for x in g[:-1]])
|
|
1587
|
+
except ValueError:
|
|
1588
|
+
# Rays are only unique up to scaling.
|
|
1589
|
+
new_ray = vector(base_ring, g[:-1])
|
|
1590
|
+
|
|
1591
|
+
for j, r in enumerate(self.rays()):
|
|
1592
|
+
ray = r.vector()
|
|
1593
|
+
try:
|
|
1594
|
+
# Check for colinearity.
|
|
1595
|
+
_ = new_ray / ray
|
|
1596
|
+
new_to_old[i] = j
|
|
1597
|
+
break
|
|
1598
|
+
except (TypeError, ArithmeticError):
|
|
1599
|
+
pass
|
|
1600
|
+
else:
|
|
1601
|
+
raise ValueError("could not match rays after computing triangulation with original rays")
|
|
1602
|
+
|
|
1603
|
+
def new_indices(old_indices):
|
|
1604
|
+
for i in old_indices:
|
|
1605
|
+
if new_to_old[i] is not None:
|
|
1606
|
+
yield new_to_old[i]
|
|
1607
|
+
|
|
1608
|
+
return [tuple(new_indices(x[0])) for x in nmz_triangulation]
|
|
1609
|
+
|
|
1610
|
+
|
|
1611
|
+
#########################################################################
|
|
1612
|
+
class Polyhedron_QQ_normaliz(Polyhedron_normaliz, Polyhedron_QQ):
|
|
1613
|
+
r"""
|
|
1614
|
+
Polyhedra over `\QQ` with normaliz.
|
|
1615
|
+
|
|
1616
|
+
INPUT:
|
|
1617
|
+
|
|
1618
|
+
- ``Vrep`` -- list ``[vertices, rays, lines]`` or ``None``
|
|
1619
|
+
- ``Hrep`` -- list ``[ieqs, eqns]`` or ``None``
|
|
1620
|
+
|
|
1621
|
+
EXAMPLES::
|
|
1622
|
+
|
|
1623
|
+
sage: p = Polyhedron(vertices=[(0,0), (1,0), (0,1)],
|
|
1624
|
+
....: rays=[(1,1)], lines=[],
|
|
1625
|
+
....: backend='normaliz', base_ring=QQ)
|
|
1626
|
+
sage: TestSuite(p).run()
|
|
1627
|
+
"""
|
|
1628
|
+
|
|
1629
|
+
@cached_method(do_pickle=True)
|
|
1630
|
+
def ehrhart_series(self, variable='t'):
|
|
1631
|
+
r"""
|
|
1632
|
+
Return the Ehrhart series of a compact rational polyhedron.
|
|
1633
|
+
|
|
1634
|
+
The Ehrhart series is the generating function where the coefficient of
|
|
1635
|
+
`t^k` is number of integer lattice points inside the `k`-th dilation of
|
|
1636
|
+
the polytope.
|
|
1637
|
+
|
|
1638
|
+
INPUT:
|
|
1639
|
+
|
|
1640
|
+
- ``variable`` -- string (default: ``'t'``)
|
|
1641
|
+
|
|
1642
|
+
OUTPUT: a rational function
|
|
1643
|
+
|
|
1644
|
+
EXAMPLES::
|
|
1645
|
+
|
|
1646
|
+
sage: S = Polyhedron(vertices=[[0,1], [1,0]], backend='normaliz')
|
|
1647
|
+
sage: ES = S.ehrhart_series()
|
|
1648
|
+
sage: ES.numerator()
|
|
1649
|
+
1
|
|
1650
|
+
sage: ES.denominator().factor()
|
|
1651
|
+
(t - 1)^2
|
|
1652
|
+
|
|
1653
|
+
sage: C = Polyhedron(vertices=[[0,0,0], [0,0,1], [0,1,0], [0,1,1],
|
|
1654
|
+
....: [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
|
|
1655
|
+
....: backend='normaliz')
|
|
1656
|
+
sage: ES = C.ehrhart_series()
|
|
1657
|
+
sage: ES.numerator()
|
|
1658
|
+
t^2 + 4*t + 1
|
|
1659
|
+
sage: ES.denominator().factor()
|
|
1660
|
+
(t - 1)^4
|
|
1661
|
+
|
|
1662
|
+
The following example is from the Normaliz manual contained in the file
|
|
1663
|
+
``rational.in``::
|
|
1664
|
+
|
|
1665
|
+
sage: rat_poly = Polyhedron(vertices=[[1/2,1/2], [-1/3,-1/3], [1/4,-1/2]],
|
|
1666
|
+
....: backend='normaliz')
|
|
1667
|
+
sage: ES = rat_poly.ehrhart_series()
|
|
1668
|
+
sage: ES.numerator()
|
|
1669
|
+
2*t^6 + 3*t^5 + 4*t^4 + 3*t^3 + t^2 + t + 1
|
|
1670
|
+
sage: ES.denominator().factor()
|
|
1671
|
+
(-1) * (t + 1)^2 * (t - 1)^3 * (t^2 + 1) * (t^2 + t + 1)
|
|
1672
|
+
|
|
1673
|
+
The polyhedron should be compact::
|
|
1674
|
+
|
|
1675
|
+
sage: C = Polyhedron(rays=[[1,2], [2,1]], backend='normaliz')
|
|
1676
|
+
sage: C.ehrhart_series()
|
|
1677
|
+
Traceback (most recent call last):
|
|
1678
|
+
...
|
|
1679
|
+
NotImplementedError: Ehrhart series can only be computed for compact polyhedron
|
|
1680
|
+
|
|
1681
|
+
.. SEEALSO::
|
|
1682
|
+
|
|
1683
|
+
:meth:`~sage.geometry.polyhedron.backend_normaliz.hilbert_series`
|
|
1684
|
+
|
|
1685
|
+
TESTS:
|
|
1686
|
+
|
|
1687
|
+
Check that the Ehrhart series is pickled::
|
|
1688
|
+
|
|
1689
|
+
sage: new_poly = loads(dumps(rat_poly))
|
|
1690
|
+
sage: new_poly.ehrhart_series.is_in_cache()
|
|
1691
|
+
True
|
|
1692
|
+
"""
|
|
1693
|
+
if self.is_empty():
|
|
1694
|
+
return 0
|
|
1695
|
+
|
|
1696
|
+
if not self.is_compact():
|
|
1697
|
+
raise NotImplementedError("Ehrhart series can only be computed for compact polyhedron")
|
|
1698
|
+
|
|
1699
|
+
cone = self._normaliz_cone
|
|
1700
|
+
e = self._nmz_result(cone, "EhrhartSeries")
|
|
1701
|
+
# The output format of PyNormaliz is a list with 3 things:
|
|
1702
|
+
# 1) the coefficients of the h^*-polynomial
|
|
1703
|
+
# 2) a list of the exponents e such that (1-t^e) appears as a factor in
|
|
1704
|
+
# the denominator
|
|
1705
|
+
# 3) a shifting of the generating function.
|
|
1706
|
+
|
|
1707
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
1708
|
+
poly_ring = PolynomialRing(ZZ, variable).fraction_field()
|
|
1709
|
+
t = poly_ring.gens()[0]
|
|
1710
|
+
es = sum([e[0][i] * t**i for i in range(len(e[0]))])
|
|
1711
|
+
for expo in range(len(e[1])):
|
|
1712
|
+
es = es / (1 - t**e[1][expo])
|
|
1713
|
+
|
|
1714
|
+
# The shift:
|
|
1715
|
+
return es * t**e[2]
|
|
1716
|
+
|
|
1717
|
+
def _ehrhart_quasipolynomial_normaliz(self, variable='t'):
|
|
1718
|
+
r"""
|
|
1719
|
+
Return the Ehrhart quasipolynomial of a compact rational polyhedron
|
|
1720
|
+
using Normaliz.
|
|
1721
|
+
|
|
1722
|
+
If it is a polynomial, returns the polynomial. Otherwise, returns a
|
|
1723
|
+
tuple of rational polynomials whose length is the quasi-period of the
|
|
1724
|
+
quasipolynomial and each rational polynomial describes a residue class.
|
|
1725
|
+
|
|
1726
|
+
INPUT:
|
|
1727
|
+
|
|
1728
|
+
- ``variable`` -- string (default: ``'t'``)
|
|
1729
|
+
|
|
1730
|
+
OUTPUT: a polynomial or tuple of polynomials
|
|
1731
|
+
|
|
1732
|
+
EXAMPLES::
|
|
1733
|
+
|
|
1734
|
+
sage: C = Polyhedron(vertices=[[0,0,0], [0,0,1], [0,1,0], [0,1,1],
|
|
1735
|
+
....: [1,0,0], [1,0,1], [1,1,0], [1,1,1]],
|
|
1736
|
+
....: backend='normaliz')
|
|
1737
|
+
sage: C._ehrhart_quasipolynomial_normaliz()
|
|
1738
|
+
t^3 + 3*t^2 + 3*t + 1
|
|
1739
|
+
|
|
1740
|
+
sage: P = Polyhedron(vertices=[[0,0], [3/2,0], [0,3/2], [1,1]], backend='normaliz')
|
|
1741
|
+
sage: P._ehrhart_quasipolynomial_normaliz()
|
|
1742
|
+
(3/2*t^2 + 2*t + 1, 3/2*t^2 + 2*t + 1/2)
|
|
1743
|
+
sage: P._ehrhart_quasipolynomial_normaliz('x')
|
|
1744
|
+
(3/2*x^2 + 2*x + 1, 3/2*x^2 + 2*x + 1/2)
|
|
1745
|
+
|
|
1746
|
+
The quasipolynomial evaluated at ``i`` counts the integral points
|
|
1747
|
+
in the ``i``-th dilate::
|
|
1748
|
+
|
|
1749
|
+
sage: Q = Polyhedron(vertices=[[-1/3], [2/3]], backend='normaliz')
|
|
1750
|
+
sage: p0,p1,p2 = Q._ehrhart_quasipolynomial_normaliz()
|
|
1751
|
+
sage: r0 = [p0(i) for i in range(15)]
|
|
1752
|
+
sage: r1 = [p1(i) for i in range(15)]
|
|
1753
|
+
sage: r2 = [p2(i) for i in range(15)]
|
|
1754
|
+
sage: result = [None]*15
|
|
1755
|
+
sage: result[::3] = r0[::3]
|
|
1756
|
+
sage: result[1::3] = r1[1::3]
|
|
1757
|
+
sage: result[2::3] = r2[2::3]
|
|
1758
|
+
sage: result == [(i*Q).integral_points_count() for i in range(15)]
|
|
1759
|
+
True
|
|
1760
|
+
|
|
1761
|
+
|
|
1762
|
+
.. SEEALSO::
|
|
1763
|
+
|
|
1764
|
+
:meth:`~sage.geometry.polyhedron.backend_normaliz.hilbert_series`,
|
|
1765
|
+
:meth:`~sage.geometry.polyhedron.backend_normaliz.ehrhart_series`
|
|
1766
|
+
"""
|
|
1767
|
+
cone = self._normaliz_cone
|
|
1768
|
+
# Normaliz needs to compute the EhrhartSeries first
|
|
1769
|
+
assert NmzCompute(cone, ["EhrhartSeries"])
|
|
1770
|
+
e = self._nmz_result(cone, "EhrhartQuasiPolynomial")
|
|
1771
|
+
|
|
1772
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
1773
|
+
poly_ring = PolynomialRing(QQ, variable)
|
|
1774
|
+
t = poly_ring.gens()[0]
|
|
1775
|
+
if len(e) == 2:
|
|
1776
|
+
# It is a polynomial
|
|
1777
|
+
es = sum([e[0][i] * t**i for i in range(len(e[0]))])
|
|
1778
|
+
return es / ZZ(e[1])
|
|
1779
|
+
|
|
1780
|
+
# It is a quasipolynomial
|
|
1781
|
+
polynomials = []
|
|
1782
|
+
for p in e[:-1]:
|
|
1783
|
+
es = sum([p[i] * t**i for i in range(len(p))]) / ZZ(e[-1])
|
|
1784
|
+
polynomials += [es]
|
|
1785
|
+
|
|
1786
|
+
return tuple(polynomials)
|
|
1787
|
+
|
|
1788
|
+
_ehrhart_polynomial_normaliz = _ehrhart_quasipolynomial_normaliz
|
|
1789
|
+
|
|
1790
|
+
@cached_method(do_pickle=True, key=lambda self, g, v: (tuple(g), v))
|
|
1791
|
+
def hilbert_series(self, grading, variable='t'):
|
|
1792
|
+
r"""
|
|
1793
|
+
Return the Hilbert series of the polyhedron with respect to ``grading``.
|
|
1794
|
+
|
|
1795
|
+
INPUT:
|
|
1796
|
+
|
|
1797
|
+
- ``grading`` -- vector. The grading to use to form the Hilbert series
|
|
1798
|
+
|
|
1799
|
+
- ``variable`` -- string (default: ``'t'``)
|
|
1800
|
+
|
|
1801
|
+
OUTPUT: a rational function
|
|
1802
|
+
|
|
1803
|
+
EXAMPLES::
|
|
1804
|
+
|
|
1805
|
+
sage: C = Polyhedron(backend='normaliz',
|
|
1806
|
+
....: rays=[[0,0,1], [0,1,1], [1,0,1], [1,1,1]])
|
|
1807
|
+
sage: HS = C.hilbert_series([1,1,1])
|
|
1808
|
+
sage: HS.numerator()
|
|
1809
|
+
t^2 + 1
|
|
1810
|
+
sage: HS.denominator().factor()
|
|
1811
|
+
(-1) * (t + 1) * (t - 1)^3 * (t^2 + t + 1)
|
|
1812
|
+
|
|
1813
|
+
By changing the grading, you can get the Ehrhart series of the square
|
|
1814
|
+
lifted at height 1::
|
|
1815
|
+
|
|
1816
|
+
sage: C.hilbert_series([0,0,1])
|
|
1817
|
+
(t + 1)/(-t^3 + 3*t^2 - 3*t + 1)
|
|
1818
|
+
|
|
1819
|
+
Here is an example ``2cone.in`` from the Normaliz manual::
|
|
1820
|
+
|
|
1821
|
+
sage: C = Polyhedron(backend='normaliz', rays=[[1,3], [2,1]])
|
|
1822
|
+
sage: HS = C.hilbert_series([1,1])
|
|
1823
|
+
sage: HS.numerator()
|
|
1824
|
+
t^5 + t^4 + t^3 + t^2 + 1
|
|
1825
|
+
sage: HS.denominator().factor()
|
|
1826
|
+
(t + 1) * (t - 1)^2 * (t^2 + 1) * (t^2 + t + 1)
|
|
1827
|
+
|
|
1828
|
+
sage: HS = C.hilbert_series([1,2])
|
|
1829
|
+
sage: HS.numerator()
|
|
1830
|
+
t^8 + t^6 + t^5 + t^3 + 1
|
|
1831
|
+
sage: HS.denominator().factor()
|
|
1832
|
+
(t + 1) * (t - 1)^2 * (t^2 + 1) * (t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)
|
|
1833
|
+
|
|
1834
|
+
Here is the magic square example form the Normaliz manual::
|
|
1835
|
+
|
|
1836
|
+
sage: eq = [[0,1,1,1,-1,-1,-1, 0, 0, 0],
|
|
1837
|
+
....: [0,1,1,1, 0, 0, 0,-1,-1,-1],
|
|
1838
|
+
....: [0,0,1,1,-1, 0, 0,-1, 0, 0],
|
|
1839
|
+
....: [0,1,0,1, 0,-1, 0, 0,-1, 0],
|
|
1840
|
+
....: [0,1,1,0, 0, 0,-1, 0, 0,-1],
|
|
1841
|
+
....: [0,0,1,1, 0,-1, 0, 0, 0,-1],
|
|
1842
|
+
....: [0,1,1,0, 0,-1, 0,-1, 0, 0]]
|
|
1843
|
+
sage: magic_square = (Polyhedron(eqns=eq, backend='normaliz')
|
|
1844
|
+
....: & Polyhedron(rays=identity_matrix(9).rows()))
|
|
1845
|
+
sage: grading = [1,1,1,0,0,0,0,0,0]
|
|
1846
|
+
sage: magic_square.hilbert_series(grading)
|
|
1847
|
+
(t^6 + 2*t^3 + 1)/(-t^9 + 3*t^6 - 3*t^3 + 1)
|
|
1848
|
+
|
|
1849
|
+
.. SEEALSO::
|
|
1850
|
+
|
|
1851
|
+
:meth:`~sage.geometry.polyhedron.backend_normaliz.ehrhart_series`
|
|
1852
|
+
|
|
1853
|
+
TESTS:
|
|
1854
|
+
|
|
1855
|
+
Check that the Hilbert series is pickled::
|
|
1856
|
+
|
|
1857
|
+
sage: new_magic = loads(dumps(magic_square))
|
|
1858
|
+
sage: new_magic.hilbert_series.is_in_cache(grading)
|
|
1859
|
+
True
|
|
1860
|
+
"""
|
|
1861
|
+
if self.is_empty():
|
|
1862
|
+
return 0
|
|
1863
|
+
|
|
1864
|
+
data = self._get_nmzcone_data()
|
|
1865
|
+
data['grading'] = [grading]
|
|
1866
|
+
new_cone = self._make_normaliz_cone(data)
|
|
1867
|
+
h = self._nmz_result(new_cone, "HilbertSeries")
|
|
1868
|
+
|
|
1869
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
1870
|
+
poly_ring = PolynomialRing(ZZ, variable).fraction_field()
|
|
1871
|
+
t = poly_ring.gens()[0]
|
|
1872
|
+
hs = sum([h[0][i] * t**i for i in range(len(h[0]))])
|
|
1873
|
+
for expo in range(len(h[1])):
|
|
1874
|
+
hs = hs / (1 - t**h[1][expo])
|
|
1875
|
+
|
|
1876
|
+
# The shift:
|
|
1877
|
+
return hs * t**h[2]
|
|
1878
|
+
|
|
1879
|
+
def integral_points(self, threshold=10000) -> tuple:
|
|
1880
|
+
r"""
|
|
1881
|
+
Return the integral points in the polyhedron.
|
|
1882
|
+
|
|
1883
|
+
Uses either the naive algorithm (iterate over a rectangular
|
|
1884
|
+
bounding box) or triangulation + Smith form.
|
|
1885
|
+
|
|
1886
|
+
INPUT:
|
|
1887
|
+
|
|
1888
|
+
- ``threshold`` -- integer (default: 10000); use the naïve
|
|
1889
|
+
algorithm as long as the bounding box is smaller than this
|
|
1890
|
+
|
|
1891
|
+
OUTPUT:
|
|
1892
|
+
|
|
1893
|
+
The list of integral points in the polyhedron. If the
|
|
1894
|
+
polyhedron is not compact, a :exc:`ValueError` is raised.
|
|
1895
|
+
|
|
1896
|
+
EXAMPLES::
|
|
1897
|
+
|
|
1898
|
+
sage: Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)],
|
|
1899
|
+
....: backend='normaliz').integral_points()
|
|
1900
|
+
((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))
|
|
1901
|
+
|
|
1902
|
+
sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)],
|
|
1903
|
+
....: backend='normaliz')
|
|
1904
|
+
sage: simplex.integral_points()
|
|
1905
|
+
((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))
|
|
1906
|
+
|
|
1907
|
+
The polyhedron need not be full-dimensional::
|
|
1908
|
+
|
|
1909
|
+
sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)],
|
|
1910
|
+
....: backend='normaliz')
|
|
1911
|
+
sage: simplex.integral_points()
|
|
1912
|
+
((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))
|
|
1913
|
+
|
|
1914
|
+
sage: point = Polyhedron([(2,3,7)],
|
|
1915
|
+
....: backend='normaliz')
|
|
1916
|
+
sage: point.integral_points()
|
|
1917
|
+
((2, 3, 7),)
|
|
1918
|
+
|
|
1919
|
+
sage: empty = Polyhedron(backend='normaliz')
|
|
1920
|
+
sage: empty.integral_points()
|
|
1921
|
+
()
|
|
1922
|
+
|
|
1923
|
+
Here is a simplex where the naive algorithm of running over
|
|
1924
|
+
all points in a rectangular bounding box no longer works fast
|
|
1925
|
+
enough::
|
|
1926
|
+
|
|
1927
|
+
sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
|
|
1928
|
+
sage: simplex = Polyhedron(v, backend='normaliz'); simplex
|
|
1929
|
+
A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
|
|
1930
|
+
sage: len(simplex.integral_points())
|
|
1931
|
+
49
|
|
1932
|
+
|
|
1933
|
+
A rather thin polytope for which the bounding box method would
|
|
1934
|
+
be a very bad idea (note this is a rational (non-lattice)
|
|
1935
|
+
polytope, so the other backends use the bounding box method)::
|
|
1936
|
+
|
|
1937
|
+
sage: P = Polyhedron(vertices=((0, 0), (178933,37121))) + 1/1000*polytopes.hypercube(2)
|
|
1938
|
+
sage: P = Polyhedron(vertices=P.vertices_list(),
|
|
1939
|
+
....: backend='normaliz')
|
|
1940
|
+
sage: len(P.integral_points())
|
|
1941
|
+
434
|
|
1942
|
+
|
|
1943
|
+
Finally, the 3-d reflexive polytope number 4078::
|
|
1944
|
+
|
|
1945
|
+
sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),
|
|
1946
|
+
....: (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]
|
|
1947
|
+
sage: P = Polyhedron(v, backend='normaliz')
|
|
1948
|
+
sage: pts1 = P.integral_points()
|
|
1949
|
+
sage: all(P.contains(p) for p in pts1)
|
|
1950
|
+
True
|
|
1951
|
+
sage: pts2 = LatticePolytope(v).points() # needs palp
|
|
1952
|
+
sage: for p in pts1: p.set_immutable()
|
|
1953
|
+
sage: set(pts1) == set(pts2) # needs palp
|
|
1954
|
+
True
|
|
1955
|
+
|
|
1956
|
+
sage: timeit('Polyhedron(v, backend='normaliz').integral_points()') # not tested - random
|
|
1957
|
+
625 loops, best of 3: 1.41 ms per loop
|
|
1958
|
+
sage: timeit('LatticePolytope(v).points()') # not tested - random
|
|
1959
|
+
25 loops, best of 3: 17.2 ms per loop
|
|
1960
|
+
|
|
1961
|
+
TESTS:
|
|
1962
|
+
|
|
1963
|
+
Test some trivial cases (see :issue:`17937`):
|
|
1964
|
+
|
|
1965
|
+
Empty polyhedron in 1 dimension::
|
|
1966
|
+
|
|
1967
|
+
sage: P = Polyhedron(ambient_dim=1, backend='normaliz')
|
|
1968
|
+
sage: P.integral_points()
|
|
1969
|
+
()
|
|
1970
|
+
|
|
1971
|
+
Empty polyhedron in 0 dimensions::
|
|
1972
|
+
|
|
1973
|
+
sage: P = Polyhedron(ambient_dim=0, backend='normaliz')
|
|
1974
|
+
sage: P.integral_points()
|
|
1975
|
+
()
|
|
1976
|
+
|
|
1977
|
+
Single point in 1 dimension::
|
|
1978
|
+
|
|
1979
|
+
sage: P = Polyhedron([[3]], backend='normaliz')
|
|
1980
|
+
sage: P.integral_points()
|
|
1981
|
+
((3),)
|
|
1982
|
+
|
|
1983
|
+
Single non-integral point in 1 dimension::
|
|
1984
|
+
|
|
1985
|
+
sage: P = Polyhedron([[1/2]], backend='normaliz')
|
|
1986
|
+
sage: P.integral_points()
|
|
1987
|
+
()
|
|
1988
|
+
|
|
1989
|
+
Single point in 0 dimensions::
|
|
1990
|
+
|
|
1991
|
+
sage: P = Polyhedron([[]], backend='normaliz')
|
|
1992
|
+
sage: P.integral_points()
|
|
1993
|
+
((),)
|
|
1994
|
+
|
|
1995
|
+
A polytope with no integral points (:issue:`22938`)::
|
|
1996
|
+
|
|
1997
|
+
sage: ieqs = [[1, 2, -1, 0], [0, -1, 2, -1], [0, 0, -1, 2],
|
|
1998
|
+
....: [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1],
|
|
1999
|
+
....: [-1, -1, -1, -1], [1, 1, 0, 0], [1, 0, 1, 0],
|
|
2000
|
+
....: [1, 0, 0, 1]]
|
|
2001
|
+
sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')
|
|
2002
|
+
sage: P.bounding_box()
|
|
2003
|
+
((-3/4, -1/2, -1/4), (-1/2, -1/4, 0))
|
|
2004
|
+
sage: P.bounding_box(integral_hull=True)
|
|
2005
|
+
(None, None)
|
|
2006
|
+
sage: P.integral_points()
|
|
2007
|
+
()
|
|
2008
|
+
|
|
2009
|
+
Check the polytopes from :issue:`22984`::
|
|
2010
|
+
|
|
2011
|
+
sage: base = [[0, 2, 0, -1, 0, 0, 0, 0, 0],
|
|
2012
|
+
....: [0, 0, 2, 0, -1, 0, 0, 0, 0],
|
|
2013
|
+
....: [1, -1, 0, 2, -1, 0, 0, 0, 0],
|
|
2014
|
+
....: [0, 0, -1, -1, 2, -1, 0, 0, 0],
|
|
2015
|
+
....: [0, 0, 0, 0, -1, 2, -1, 0, 0],
|
|
2016
|
+
....: [0, 0, 0, 0, 0, -1, 2, -1, 0],
|
|
2017
|
+
....: [1, 0, 0, 0, 0, 0, -1, 2, -1],
|
|
2018
|
+
....: [0, 0, 0, 0, 0, 0, 0, -1, 2],
|
|
2019
|
+
....: [0, -1, 0, 0, 0, 0, 0, 0, 0],
|
|
2020
|
+
....: [0, 0, -1, 0, 0, 0, 0, 0, 0],
|
|
2021
|
+
....: [0, 0, 0, -1, 0, 0, 0, 0, 0],
|
|
2022
|
+
....: [0, 0, 0, 0, -1, 0, 0, 0, 0],
|
|
2023
|
+
....: [0, 0, 0, 0, 0, -1, 0, 0, 0],
|
|
2024
|
+
....: [0, 0, 0, 0, 0, 0, -1, 0, 0],
|
|
2025
|
+
....: [0, 0, 0, 0, 0, 0, 0, -1, 0],
|
|
2026
|
+
....: [0, 0, 0, 0, 0, 0, 0, 0, -1],
|
|
2027
|
+
....: [-1, -1, -1, -1, -1, -1, -1, -1, -1]]
|
|
2028
|
+
|
|
2029
|
+
sage: ieqs = base + [
|
|
2030
|
+
....: [2, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
2031
|
+
....: [4, 0, 1, 0, 0, 0, 0, 0, 0],
|
|
2032
|
+
....: [4, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
2033
|
+
....: [7, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
2034
|
+
....: [6, 0, 0, 0, 0, 1, 0, 0, 0],
|
|
2035
|
+
....: [4, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
2036
|
+
....: [2, 0, 0, 0, 0, 0, 0, 1, 0],
|
|
2037
|
+
....: [1, 0, 0, 0, 0, 0, 0, 0, 1]]
|
|
2038
|
+
sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')
|
|
2039
|
+
sage: P.integral_points()
|
|
2040
|
+
((-2, -2, -4, -5, -4, -3, -2, -1),
|
|
2041
|
+
(-2, -2, -4, -5, -4, -3, -2, 0),
|
|
2042
|
+
(-1, -2, -3, -4, -3, -2, -2, -1),
|
|
2043
|
+
(-1, -2, -3, -4, -3, -2, -1, 0),
|
|
2044
|
+
(-1, -1, -2, -2, -2, -2, -2, -1),
|
|
2045
|
+
(-1, -1, -2, -2, -1, -1, -1, 0),
|
|
2046
|
+
(-1, -1, -2, -2, -1, 0, 0, 0),
|
|
2047
|
+
(-1, 0, -2, -2, -2, -2, -2, -1),
|
|
2048
|
+
(0, -1, -1, -2, -2, -2, -2, -1),
|
|
2049
|
+
(0, 0, -1, -1, -1, -1, -1, 0))
|
|
2050
|
+
|
|
2051
|
+
sage: ieqs = base + [
|
|
2052
|
+
....: [3, 1, 0, 0, 0, 0, 0, 0, 0],
|
|
2053
|
+
....: [4, 0, 1, 0, 0, 0, 0, 0, 0],
|
|
2054
|
+
....: [6, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
2055
|
+
....: [8, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
2056
|
+
....: [6, 0, 0, 0, 0, 1, 0, 0, 0],
|
|
2057
|
+
....: [4, 0, 0, 0, 0, 0, 1, 0, 0],
|
|
2058
|
+
....: [2, 0, 0, 0, 0, 0, 0, 1, 0],
|
|
2059
|
+
....: [1, 0, 0, 0, 0, 0, 0, 0, 1]]
|
|
2060
|
+
sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')
|
|
2061
|
+
sage: P.integral_points()
|
|
2062
|
+
((-3, -4, -6, -8, -6, -4, -2, -1),
|
|
2063
|
+
(-3, -4, -6, -8, -6, -4, -2, 0),
|
|
2064
|
+
(-2, -2, -4, -5, -4, -3, -2, -1),
|
|
2065
|
+
(-2, -2, -4, -5, -4, -3, -2, 0),
|
|
2066
|
+
(-1, -2, -3, -4, -3, -2, -2, -1),
|
|
2067
|
+
(-1, -2, -3, -4, -3, -2, -1, 0),
|
|
2068
|
+
(-1, -1, -2, -2, -2, -2, -2, -1),
|
|
2069
|
+
(-1, -1, -2, -2, -1, -1, -1, 0),
|
|
2070
|
+
(-1, -1, -2, -2, -1, 0, 0, 0),
|
|
2071
|
+
(-1, 0, -2, -2, -2, -2, -2, -1),
|
|
2072
|
+
(0, -1, -1, -2, -2, -2, -2, -1),
|
|
2073
|
+
(0, 0, -1, -1, -1, -1, -1, 0))
|
|
2074
|
+
"""
|
|
2075
|
+
if not self.is_compact():
|
|
2076
|
+
raise ValueError('can only enumerate points in a compact polyhedron')
|
|
2077
|
+
# Trivial cases: polyhedron with 0 or 1 vertices
|
|
2078
|
+
if self.n_vertices() == 0:
|
|
2079
|
+
return ()
|
|
2080
|
+
if self.n_vertices() == 1:
|
|
2081
|
+
v = self.vertices_list()[0]
|
|
2082
|
+
try:
|
|
2083
|
+
return (vector(ZZ, v),)
|
|
2084
|
+
except TypeError: # vertex not integral
|
|
2085
|
+
return ()
|
|
2086
|
+
# for small bounding boxes, it is faster to naively iterate over the points of the box
|
|
2087
|
+
if threshold > 1:
|
|
2088
|
+
box_min, box_max = self.bounding_box(integral_hull=True)
|
|
2089
|
+
if box_min is None:
|
|
2090
|
+
return ()
|
|
2091
|
+
box_points = prod(max_coord - min_coord + 1
|
|
2092
|
+
for min_coord, max_coord in zip(box_min, box_max))
|
|
2093
|
+
if box_points < threshold:
|
|
2094
|
+
from sage.geometry.integral_points import rectangular_box_points
|
|
2095
|
+
return rectangular_box_points(list(box_min), list(box_max), self)
|
|
2096
|
+
# Compute with normaliz
|
|
2097
|
+
points = []
|
|
2098
|
+
cone = self._normaliz_cone
|
|
2099
|
+
assert cone
|
|
2100
|
+
for g in self._nmz_result(cone, "ModuleGenerators"):
|
|
2101
|
+
assert g[-1] == 1
|
|
2102
|
+
points.append(vector(ZZ, g[:-1]))
|
|
2103
|
+
return tuple(points)
|
|
2104
|
+
|
|
2105
|
+
def integral_points_generators(self):
|
|
2106
|
+
r"""
|
|
2107
|
+
Return the integral points generators of the polyhedron.
|
|
2108
|
+
|
|
2109
|
+
Every integral point in the polyhedron can be written as a (unique)
|
|
2110
|
+
nonnegative linear combination of integral points contained in the three
|
|
2111
|
+
defining parts of the polyhedron: the integral points (the compact
|
|
2112
|
+
part), the recession cone, and the lineality space.
|
|
2113
|
+
|
|
2114
|
+
OUTPUT:
|
|
2115
|
+
|
|
2116
|
+
A tuple consisting of the integral points, the Hilbert basis of the
|
|
2117
|
+
recession cone, and an integral basis for the lineality space.
|
|
2118
|
+
|
|
2119
|
+
EXAMPLES:
|
|
2120
|
+
|
|
2121
|
+
Normaliz gives a nonnegative integer basis of the lineality space::
|
|
2122
|
+
|
|
2123
|
+
sage: P = Polyhedron(backend='normaliz', lines=[[2,2]])
|
|
2124
|
+
sage: P.integral_points_generators()
|
|
2125
|
+
(((0, 0),), (), ((1, 1),))
|
|
2126
|
+
|
|
2127
|
+
A recession cone generated by two rays::
|
|
2128
|
+
|
|
2129
|
+
sage: C = Polyhedron(backend='normaliz', rays=[[1,2], [2,1]])
|
|
2130
|
+
sage: C.integral_points_generators()
|
|
2131
|
+
(((0, 0),), ((1, 1), (1, 2), (2, 1)), ())
|
|
2132
|
+
|
|
2133
|
+
Empty polyhedron::
|
|
2134
|
+
|
|
2135
|
+
sage: P = Polyhedron(backend='normaliz')
|
|
2136
|
+
sage: P.integral_points_generators()
|
|
2137
|
+
((), (), ())
|
|
2138
|
+
"""
|
|
2139
|
+
# Trivial cases: polyhedron with 0 vertices
|
|
2140
|
+
if self.n_vertices() == 0:
|
|
2141
|
+
return ((), (), ())
|
|
2142
|
+
# Compute with normaliz
|
|
2143
|
+
cone = self._normaliz_cone
|
|
2144
|
+
compact_part = []
|
|
2145
|
+
recession_cone_part = []
|
|
2146
|
+
lineality_part = []
|
|
2147
|
+
assert cone
|
|
2148
|
+
for g in self._nmz_result(cone, "ModuleGenerators"):
|
|
2149
|
+
assert g[-1] == 1
|
|
2150
|
+
compact_part.append(vector(ZZ, g[:-1]))
|
|
2151
|
+
|
|
2152
|
+
for g in self._nmz_result(cone, "HilbertBasis"):
|
|
2153
|
+
assert g[-1] == 0
|
|
2154
|
+
recession_cone_part.append(vector(ZZ, g[:-1]))
|
|
2155
|
+
|
|
2156
|
+
for g in self._nmz_result(cone, "MaximalSubspace"):
|
|
2157
|
+
assert g[-1] == 0
|
|
2158
|
+
lineality_part.append(vector(ZZ, g[:-1]))
|
|
2159
|
+
|
|
2160
|
+
return tuple(compact_part), tuple(recession_cone_part), tuple(lineality_part)
|
|
2161
|
+
|
|
2162
|
+
def _Hstar_function_normaliz(self, acting_group=None, output=None):
|
|
2163
|
+
r"""
|
|
2164
|
+
Return `H^*` as a rational function in `t` with coefficients in
|
|
2165
|
+
the ring of class functions of the ``acting_group`` of the polytope.
|
|
2166
|
+
|
|
2167
|
+
As in [Stap2011]_, when ``self`` is the polytope `P`,
|
|
2168
|
+
`H^*(t) = (\sum_{m \geq 0} \chi_{mP} t^m)(\det(I-\rho(t)))`.
|
|
2169
|
+
The irreducible characters of ``acting_group`` form an orthonormal basis
|
|
2170
|
+
for the ring of class functions with values in `\CC`.
|
|
2171
|
+
The coefficients of `H^*(t)` are expressed in this basis.
|
|
2172
|
+
|
|
2173
|
+
INPUT:
|
|
2174
|
+
|
|
2175
|
+
- ``acting_group`` -- (default=None) a permgroup object. A subgroup of
|
|
2176
|
+
``self``'s ``restricted_automorphism_group`` output as a permutation.
|
|
2177
|
+
If ``None``, it is set to the full ``restricted_automorphism_group``
|
|
2178
|
+
of ``self``. The acting group should always use output='permutation'.
|
|
2179
|
+
|
|
2180
|
+
- ``output`` -- string. an output option. The allowed values are:
|
|
2181
|
+
|
|
2182
|
+
* ``None`` (default): returns the rational function `H^*(t)`. `H^*` is
|
|
2183
|
+
a rational function in `t` with coefficients in the ring of
|
|
2184
|
+
class functions.
|
|
2185
|
+
* ``'e_series_list'``: string. Returns a list of the ehrhart series
|
|
2186
|
+
for the fixed subpolytopes of each conjugacy class representative.
|
|
2187
|
+
* ``'determinant_vec'``: string. Returns a list of the determinants
|
|
2188
|
+
of `Id-\rho*t` for each conjugacy class representative.
|
|
2189
|
+
* ``'Hstar_as_lin_comb'``: string. Returns a vector of the coefficients
|
|
2190
|
+
of the irreducible representations in the expression of `H^*`.
|
|
2191
|
+
* ``'prod_det_es'``: string. Returns a vector of the product of
|
|
2192
|
+
determinants and the Ehrhart series.
|
|
2193
|
+
* ``'complete'``: string. Returns a dictionary with Hstar,
|
|
2194
|
+
Hstar_as_lin_comb, the conjugacy class representatives,
|
|
2195
|
+
the character table of the acting group, and
|
|
2196
|
+
whether Hstar is effective.
|
|
2197
|
+
|
|
2198
|
+
OUTPUT:
|
|
2199
|
+
|
|
2200
|
+
The default output is the rational function `H^*`. `H^*` is a rational
|
|
2201
|
+
function in `t` with coefficients in the ring of class functions.
|
|
2202
|
+
There are several output options to see the intermediary outputs of the
|
|
2203
|
+
function.
|
|
2204
|
+
|
|
2205
|
+
EXAMPLES:
|
|
2206
|
+
|
|
2207
|
+
The `H^*`-polynomial of the standard `d-1` dimensional simplex
|
|
2208
|
+
`S = conv(e_1, \dots, e_d)` under its ``restricted_automorphism_group``
|
|
2209
|
+
is equal to 1 = `\chi_{trivial}` (Prop 6.1 [Stap2011]_).
|
|
2210
|
+
Here is the computation for the 3-dimensional standard simplex::
|
|
2211
|
+
|
|
2212
|
+
sage: # needs sage.groups
|
|
2213
|
+
sage: S = polytopes.simplex(3, backend='normaliz'); S
|
|
2214
|
+
A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices
|
|
2215
|
+
sage: G = S.restricted_automorphism_group(output='permutation')
|
|
2216
|
+
sage: G.is_isomorphic(SymmetricGroup(4))
|
|
2217
|
+
True
|
|
2218
|
+
sage: len(G)
|
|
2219
|
+
24
|
|
2220
|
+
sage: Hstar = S._Hstar_function_normaliz(G); Hstar
|
|
2221
|
+
chi_4
|
|
2222
|
+
sage: G.character_table()
|
|
2223
|
+
[ 1 -1 1 1 -1]
|
|
2224
|
+
[ 3 -1 0 -1 1]
|
|
2225
|
+
[ 2 0 -1 2 0]
|
|
2226
|
+
[ 3 1 0 -1 -1]
|
|
2227
|
+
[ 1 1 1 1 1]
|
|
2228
|
+
|
|
2229
|
+
The next example is Example 7.6 in [Stap2011]_, and shows that `H^*`
|
|
2230
|
+
is not always a polynomial. Let P be the polytope with vertices
|
|
2231
|
+
`\pm(0,0,1),\pm(1,0,1), \pm(0,1,1), \pm(1,1,1)` and let
|
|
2232
|
+
G = `\Zmod{2}` act on P as follows::
|
|
2233
|
+
|
|
2234
|
+
sage: # needs sage.groups
|
|
2235
|
+
sage: P = Polyhedron(vertices=[[0,0,1], [0,0,-1], [1,0,1], [-1,0,-1],
|
|
2236
|
+
....: [0,1,1], [0,-1,-1], [1,1,1], [-1,-1,-1]],
|
|
2237
|
+
....: backend='normaliz')
|
|
2238
|
+
sage: K = P.restricted_automorphism_group(output='permutation')
|
|
2239
|
+
sage: G = K.subgroup(gens=[K([(0,2),(1,3),(4,6),(5,7)])])
|
|
2240
|
+
sage: conj_reps = G.conjugacy_classes_representatives()
|
|
2241
|
+
sage: Dict = P.permutations_to_matrices(conj_reps, acting_group=G)
|
|
2242
|
+
sage: list(Dict.keys())[0]
|
|
2243
|
+
(0,2)(1,3)(4,6)(5,7)
|
|
2244
|
+
sage: list(Dict.values())[0]
|
|
2245
|
+
[-1 0 1 0]
|
|
2246
|
+
[ 0 1 0 0]
|
|
2247
|
+
[ 0 0 1 0]
|
|
2248
|
+
[ 0 0 0 1]
|
|
2249
|
+
sage: len(G)
|
|
2250
|
+
2
|
|
2251
|
+
sage: G.character_table()
|
|
2252
|
+
[ 1 1]
|
|
2253
|
+
[ 1 -1]
|
|
2254
|
+
|
|
2255
|
+
Then we calculate the rational function `H^*(t)`::
|
|
2256
|
+
|
|
2257
|
+
sage: Hst = P._Hstar_function_normaliz(G); Hst # needs sage.groups
|
|
2258
|
+
(chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3
|
|
2259
|
+
+ (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1)
|
|
2260
|
+
|
|
2261
|
+
To see the exact as written in [Stap2011]_, we can format it as
|
|
2262
|
+
``'Hstar_as_lin_comb'``. The first coordinate is the coefficient of the
|
|
2263
|
+
trivial character; the second is the coefficient of the sign character::
|
|
2264
|
+
|
|
2265
|
+
sage: lin = P._Hstar_function_normaliz(G, output='Hstar_as_lin_comb'); lin # needs sage.groups
|
|
2266
|
+
((t^4 + 3*t^3 + 8*t^2 + 3*t + 1)/(t + 1), (3*t^3 + 2*t^2 + 3*t)/(t + 1))
|
|
2267
|
+
"""
|
|
2268
|
+
from sage.groups.conjugacy_classes import ConjugacyClassGAP
|
|
2269
|
+
from sage.rings.number_field.number_field import CyclotomicField
|
|
2270
|
+
from sage.rings.qqbar import QQbar
|
|
2271
|
+
from sage.matrix.matrix_space import MatrixSpace
|
|
2272
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
2273
|
+
from sage.matrix.special import identity_matrix
|
|
2274
|
+
# Setting the group
|
|
2275
|
+
G_perm = self.restricted_automorphism_group(output='permutation')
|
|
2276
|
+
|
|
2277
|
+
if acting_group is not None:
|
|
2278
|
+
if not acting_group.is_subgroup(G_perm):
|
|
2279
|
+
raise TypeError("the 'acting_group' should be a subgroup of the 'restricted_automorphism_group'.")
|
|
2280
|
+
G_perm = acting_group
|
|
2281
|
+
# Create the Gap group one time only (each creation has different conj reps)
|
|
2282
|
+
G_perm_gap = G_perm._libgap_()
|
|
2283
|
+
|
|
2284
|
+
# Fixing the conjugacy classes representatives once and for all
|
|
2285
|
+
cls = G_perm_gap.ConjugacyClasses()
|
|
2286
|
+
L = [cl.Representative() for cl in cls]
|
|
2287
|
+
conj_classes = [ConjugacyClassGAP(G_perm, G_perm.element_class(rep, G_perm, check=False)) for rep in L]
|
|
2288
|
+
conj_reps = [cl[0] for cl in conj_classes]
|
|
2289
|
+
|
|
2290
|
+
# Creating the Character Table
|
|
2291
|
+
n_classes = len(conj_reps)
|
|
2292
|
+
irrG_perm_gap = G_perm_gap.Irr()
|
|
2293
|
+
ct = [[irrG_perm_gap[i, j] for j in range(n_classes)] for i in range(n_classes)]
|
|
2294
|
+
e = irrG_perm_gap.Flat().Conductor()
|
|
2295
|
+
K = CyclotomicField(e)
|
|
2296
|
+
ct = [[K(x) for x in v] for v in ct]
|
|
2297
|
+
# Finally return the result as a matrix.
|
|
2298
|
+
MS = MatrixSpace(K, n_classes)
|
|
2299
|
+
char_initial = MS(ct)
|
|
2300
|
+
|
|
2301
|
+
# A check on whether the character table has permuted columns
|
|
2302
|
+
tbl = G_perm_gap.CharacterTable()
|
|
2303
|
+
perm = tbl.IdentificationOfConjugacyClasses()
|
|
2304
|
+
ident_perm = list(range(1, 1 + n_classes))
|
|
2305
|
+
assert perm == ident_perm, "The conjugacy classes don't match with the character table"
|
|
2306
|
+
|
|
2307
|
+
# Create fixed subpolytopes and their Ehrhart series
|
|
2308
|
+
group_dict = self.permutations_to_matrices(conj_reps, acting_group)
|
|
2309
|
+
fix_polys = self.fixed_subpolytopes(conj_reps)
|
|
2310
|
+
list_es = [fix_polys[g].ehrhart_series() for g in conj_reps]
|
|
2311
|
+
|
|
2312
|
+
# get the list of the denominators det([Id - rho (t)])
|
|
2313
|
+
ring = PolynomialRing(QQbar, 't')
|
|
2314
|
+
det_vector = list()
|
|
2315
|
+
dim = group_dict[G_perm.gens()[0]].dimensions()[0]
|
|
2316
|
+
t = ring.gens()[0]
|
|
2317
|
+
ts_matrix = t * identity_matrix(ring, dim)
|
|
2318
|
+
identity = identity_matrix(ring, dim)
|
|
2319
|
+
|
|
2320
|
+
# ix the determinant if polytope isn't full dimensional
|
|
2321
|
+
codim = self.ambient_dim() - self.dim()
|
|
2322
|
+
for perm in conj_reps:
|
|
2323
|
+
mat = group_dict[perm]
|
|
2324
|
+
mat = mat.change_ring(ring)
|
|
2325
|
+
new_matrix = identity - mat * ts_matrix
|
|
2326
|
+
det = (1 - t)**-codim * (new_matrix.determinant())
|
|
2327
|
+
det_vector.append(det)
|
|
2328
|
+
|
|
2329
|
+
FF = ring.fraction_field()
|
|
2330
|
+
initial_result = vector(FF, [a * b for a, b in zip(det_vector, list_es)])
|
|
2331
|
+
Char = char_initial.change_ring(FF)
|
|
2332
|
+
new_result = Char.solve_left(initial_result)
|
|
2333
|
+
|
|
2334
|
+
new_new_result = self._Hstar_as_rat_fct(new_result)
|
|
2335
|
+
if output is None:
|
|
2336
|
+
return new_new_result
|
|
2337
|
+
if output == 'e_series_list':
|
|
2338
|
+
return list_es
|
|
2339
|
+
if output == 'determinant_vec':
|
|
2340
|
+
return det_vector
|
|
2341
|
+
if output == 'Hstar_as_lin_comb':
|
|
2342
|
+
return new_result
|
|
2343
|
+
if output == 'prod_det_es':
|
|
2344
|
+
return initial_result
|
|
2345
|
+
if output == 'complete':
|
|
2346
|
+
results_dictionary = {}
|
|
2347
|
+
results_dictionary['Hstar'] = new_new_result
|
|
2348
|
+
results_dictionary['Hstar_as_lin_comb'] = new_result
|
|
2349
|
+
results_dictionary['conjugacy_class_reps'] = conj_reps
|
|
2350
|
+
results_dictionary['character_table'] = char_initial
|
|
2351
|
+
results_dictionary['is_effective'] = self._is_effective_normaliz(new_new_result, new_result)
|
|
2352
|
+
return results_dictionary
|
|
2353
|
+
|
|
2354
|
+
def _Hstar_as_rat_fct(self, initial_Hstar):
|
|
2355
|
+
r"""
|
|
2356
|
+
Rewrite the vector representing `H^*(t)` given as a linear combination
|
|
2357
|
+
of the irreducible representations of the acting group as a rational
|
|
2358
|
+
function in `t`.
|
|
2359
|
+
|
|
2360
|
+
INPUT:
|
|
2361
|
+
|
|
2362
|
+
- ``initial_Hstar`` -- a vector of rational functions in `t`
|
|
2363
|
+
|
|
2364
|
+
OUTPUT:
|
|
2365
|
+
|
|
2366
|
+
A rational function in `t` with coefficients in the ring of class functions
|
|
2367
|
+
of ``self.restricted_automorphism_group()``.
|
|
2368
|
+
|
|
2369
|
+
EXAMPLES:
|
|
2370
|
+
|
|
2371
|
+
The expression of `H^*` as a polynomial in `t` for a 3-dimensional simplex
|
|
2372
|
+
is computed as follows::
|
|
2373
|
+
|
|
2374
|
+
sage: simplex = Polyhedron(vertices=[[0,0,0], [1,0,0],
|
|
2375
|
+
....: [0,1,0], [0,0,1]], backend='normaliz')
|
|
2376
|
+
sage: Hstar = simplex.Hstar_function(); Hstar # indirect doctest # needs sage.rings.number_field
|
|
2377
|
+
chi_4
|
|
2378
|
+
|
|
2379
|
+
The polynomial is `\chi_4 \cdot t^0`. We can see which irreducible
|
|
2380
|
+
representation `\chi_4` corresponds to by looking at the character table::
|
|
2381
|
+
|
|
2382
|
+
sage: G = simplex.restricted_automorphism_group(output='permutation') # needs sage.groups
|
|
2383
|
+
sage: char = G.character_table(); char # needs sage.groups
|
|
2384
|
+
[ 1 -1 1 1 -1]
|
|
2385
|
+
[ 3 -1 0 -1 1]
|
|
2386
|
+
[ 2 0 -1 2 0]
|
|
2387
|
+
[ 3 1 0 -1 -1]
|
|
2388
|
+
[ 1 1 1 1 1]
|
|
2389
|
+
|
|
2390
|
+
Thus `\chi_4` corresponds to the trivial representation of the group, and
|
|
2391
|
+
for every element in the group, it evaluates to 1.
|
|
2392
|
+
|
|
2393
|
+
As another example, we can look at `H^*(t)` for the `\pm 1` square::
|
|
2394
|
+
|
|
2395
|
+
sage: square = Polyhedron(vertices=[[1,1], [-1,1], [-1,-1], [1,-1]],
|
|
2396
|
+
....: backend='normaliz')
|
|
2397
|
+
sage: Hstar = square.Hstar_function(); Hstar # needs sage.rings.number_field
|
|
2398
|
+
chi_0*t^2 + (2*chi_0 + chi_2 + chi_3 + chi_4)*t + chi_0
|
|
2399
|
+
|
|
2400
|
+
Plugging in the values from the first column of the character table below
|
|
2401
|
+
yields the `h^*`-polynomial of the square, `t^2+6t+1`::
|
|
2402
|
+
|
|
2403
|
+
sage: G = square.restricted_automorphism_group(output='permutation') # needs sage.groups
|
|
2404
|
+
sage: G.character_table() # needs sage.groups
|
|
2405
|
+
[ 1 1 1 1 1]
|
|
2406
|
+
[ 1 -1 -1 1 1]
|
|
2407
|
+
[ 1 -1 1 -1 1]
|
|
2408
|
+
[ 1 1 -1 -1 1]
|
|
2409
|
+
[ 2 0 0 0 -2]
|
|
2410
|
+
"""
|
|
2411
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
2412
|
+
from sage.rings.qqbar import QQbar
|
|
2413
|
+
chi_vars = ','.join(f'chi_{i}' for i in range(len(initial_Hstar)))
|
|
2414
|
+
Chi_ring = PolynomialRing(QQbar, chi_vars)
|
|
2415
|
+
virtual_ring = PolynomialRing(Chi_ring, initial_Hstar.base_ring().gens())
|
|
2416
|
+
fraction_virtual_ring = virtual_ring.fraction_field()
|
|
2417
|
+
return initial_Hstar.change_ring(fraction_virtual_ring) * vector(fraction_virtual_ring, Chi_ring.gens())
|
|
2418
|
+
|
|
2419
|
+
def _is_effective_normaliz(self, Hstar, Hstar_as_lin_comb):
|
|
2420
|
+
r"""
|
|
2421
|
+
Test for the effectiveness of the ``Hstar`` series of this polytope.
|
|
2422
|
+
|
|
2423
|
+
The ``Hstar`` series of the polytope is determined by the action of a
|
|
2424
|
+
subgroup of the polytope's ``restricted_automorphism_group``. The
|
|
2425
|
+
``Hstar`` series is effective if it is a polynomial in `t` and the
|
|
2426
|
+
coefficient of each `t^i` is an effective character in the ring of
|
|
2427
|
+
class functions of the acting group. A character `\rho` is effective if
|
|
2428
|
+
the coefficients of the irreducible representations in the expression
|
|
2429
|
+
of `\rho` are nonnegative integers.
|
|
2430
|
+
|
|
2431
|
+
INPUT:
|
|
2432
|
+
|
|
2433
|
+
- ``Hstar`` -- a rational function in `t` with coefficients in the ring
|
|
2434
|
+
of class functions
|
|
2435
|
+
|
|
2436
|
+
- ``Hstar_as_lin_comb`` -- vector. The coefficients of the irreducible
|
|
2437
|
+
representations of the acting group in the expression of ``Hstar`` as
|
|
2438
|
+
a linear combination of irreducible representations with coefficients
|
|
2439
|
+
in the field of rational functions in `t`.
|
|
2440
|
+
|
|
2441
|
+
OUTPUT: boolean; whether the ``Hstar`` series is effective
|
|
2442
|
+
|
|
2443
|
+
EXAMPLES:
|
|
2444
|
+
|
|
2445
|
+
The `H^*` series of the two-dimensional permutahedron under the action
|
|
2446
|
+
of the symmetric group is effective::
|
|
2447
|
+
|
|
2448
|
+
sage: # needs sage.groups
|
|
2449
|
+
sage: p3 = polytopes.permutahedron(3, backend='normaliz')
|
|
2450
|
+
sage: G = p3.restricted_automorphism_group(output='permutation')
|
|
2451
|
+
sage: reflection12 = G([(0,2),(1,4),(3,5)])
|
|
2452
|
+
sage: reflection23 = G([(0,1),(2,3),(4,5)])
|
|
2453
|
+
sage: S3 = G.subgroup(gens=[reflection12, reflection23])
|
|
2454
|
+
sage: S3.is_isomorphic(SymmetricGroup(3))
|
|
2455
|
+
True
|
|
2456
|
+
sage: Hstar = p3.Hstar_function(S3) # needs sage.rings.number_field
|
|
2457
|
+
sage: Hlin = p3.Hstar_function(S3, output='Hstar_as_lin_comb') # needs sage.rings.number_field
|
|
2458
|
+
sage: p3._is_effective_normaliz(Hstar, Hlin) # needs sage.rings.number_field
|
|
2459
|
+
True
|
|
2460
|
+
|
|
2461
|
+
If the `H^*`-series is not polynomial, then it is not effective::
|
|
2462
|
+
|
|
2463
|
+
sage: # needs sage.groups
|
|
2464
|
+
sage: P = Polyhedron(vertices=[[0,0,1], [0,0,-1], [1,0,1], [-1,0,-1],
|
|
2465
|
+
....: [0,1,1], [0,-1,-1], [1,1,1], [-1,-1,-1]],
|
|
2466
|
+
....: backend='normaliz')
|
|
2467
|
+
sage: G = P.restricted_automorphism_group(output='permutation')
|
|
2468
|
+
sage: H = G.subgroup(gens = [G([(0,2),(1,3),(4,6),(5,7)])])
|
|
2469
|
+
sage: Hstar = P.Hstar_function(H); Hstar # needs sage.rings.number_field
|
|
2470
|
+
(chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3
|
|
2471
|
+
+ (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1)
|
|
2472
|
+
sage: Hstar_lin = P.Hstar_function(H, output='Hstar_as_lin_comb') # needs sage.rings.number_field
|
|
2473
|
+
sage: P._is_effective_normaliz(Hstar, Hstar_lin) # needs sage.rings.number_field
|
|
2474
|
+
False
|
|
2475
|
+
"""
|
|
2476
|
+
if not Hstar.denominator().is_unit():
|
|
2477
|
+
return False
|
|
2478
|
+
for irrep in range(len(Hstar_as_lin_comb)):
|
|
2479
|
+
coeffs = Hstar_as_lin_comb[irrep].numerator().coefficients()
|
|
2480
|
+
for i in coeffs:
|
|
2481
|
+
if not i.is_integer() or i < 0:
|
|
2482
|
+
return False
|
|
2483
|
+
return True
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
#########################################################################
|
|
2487
|
+
class Polyhedron_ZZ_normaliz(Polyhedron_QQ_normaliz, Polyhedron_ZZ):
|
|
2488
|
+
r"""
|
|
2489
|
+
Polyhedra over `\ZZ` with normaliz.
|
|
2490
|
+
|
|
2491
|
+
INPUT:
|
|
2492
|
+
|
|
2493
|
+
- ``Vrep`` -- list ``[vertices, rays, lines]`` or ``None``
|
|
2494
|
+
- ``Hrep`` -- list ``[ieqs, eqns]`` or ``None``
|
|
2495
|
+
|
|
2496
|
+
EXAMPLES::
|
|
2497
|
+
|
|
2498
|
+
sage: p = Polyhedron(vertices=[(0,0), (1,0), (0,1)],
|
|
2499
|
+
....: rays=[(1,1)], lines=[],
|
|
2500
|
+
....: backend='normaliz', base_ring=ZZ)
|
|
2501
|
+
sage: TestSuite(p).run()
|
|
2502
|
+
"""
|
|
2503
|
+
pass
|