Skip to content
This repository was archived by the owner on Feb 1, 2023. It is now read-only.

Commit 064de7e

Browse files
author
Release Manager
committed
Trac #18957: ehrhart_polynomial should be made available for polytopes defined over QQ
... because they can happen to be lattice polytopes. An exception should be raised when they are not. See http://trac.sagemath.org/ticket/18812#comment:33 URL: https://trac.sagemath.org/18957 Reported by: mkoeppe Ticket author(s): Sophia Elia Reviewer(s): Jean-Philippe Labbé, Frédéric Chapoton
2 parents 4c40aae + b373a3b commit 064de7e

File tree

3 files changed

+895
-127
lines changed

3 files changed

+895
-127
lines changed

src/sage/geometry/polyhedron/backend_normaliz.py

Lines changed: 75 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,90 +1017,6 @@ def integral_hull(self):
10171017
return self.parent().element_class._from_normaliz_cone(parent=self.parent(),
10181018
normaliz_cone=cone)
10191019

1020-
def ehrhart_quasipolynomial(self, variable='t'):
1021-
r"""
1022-
Return the Ehrhart quasi-polynomial of a compact rational polyhedron
1023-
using Normaliz.
1024-
1025-
INPUT:
1026-
1027-
- ``variable`` -- string (default: ``'t'``)
1028-
1029-
OUTPUT:
1030-
1031-
If it is a polynomial, returns the polynomial. Otherwise, returns a
1032-
tuple of rational polynomials whose length is the quasi-period of the
1033-
quasi-polynomial and each rational polynomial describes a residue class.
1034-
1035-
EXAMPLES::
1036-
1037-
sage: C = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]],backend='normaliz') # optional - pynormaliz
1038-
sage: C.ehrhart_quasipolynomial() # optional - pynormaliz
1039-
t^3 + 3*t^2 + 3*t + 1
1040-
1041-
sage: P = Polyhedron(vertices=[[0,0],[3/2,0],[0,3/2],[1,1]],backend='normaliz') # optional - pynormaliz
1042-
sage: P.ehrhart_quasipolynomial() # optional - pynormaliz
1043-
(3/2*t^2 + 2*t + 1, 3/2*t^2 + 2*t + 1/2)
1044-
sage: P.ehrhart_quasipolynomial('x') # optional - pynormaliz
1045-
(3/2*x^2 + 2*x + 1, 3/2*x^2 + 2*x + 1/2)
1046-
1047-
The quasi-polynomial evaluated at ``i`` counts the integral points
1048-
in the ``i``-th dilate::
1049-
1050-
sage: Q = Polyhedron(vertices = [[-1/3],[2/3]],backend='normaliz') # optional - pynormaliz
1051-
sage: p0,p1,p2 = Q.ehrhart_quasipolynomial() # optional - pynormaliz
1052-
sage: r0 = [p0(i) for i in range(15)] # optional - pynormaliz
1053-
sage: r1 = [p1(i) for i in range(15)] # optional - pynormaliz
1054-
sage: r2 = [p2(i) for i in range(15)] # optional - pynormaliz
1055-
sage: result = [None]*15 # optional - pynormaliz
1056-
sage: result[::3] = r0[::3] # optional - pynormaliz
1057-
sage: result[1::3] = r1[1::3] # optional - pynormaliz
1058-
sage: result[2::3] = r2[2::3] # optional - pynormaliz
1059-
sage: result == [(i*Q).integral_points_count() for i in range(15)] # optional - pynormaliz
1060-
True
1061-
1062-
The polyhedron should be compact::
1063-
1064-
sage: C = Polyhedron(backend='normaliz',rays=[[1,2],[2,1]]) # optional - pynormaliz
1065-
sage: C.ehrhart_quasipolynomial() # optional - pynormaliz
1066-
Traceback (most recent call last):
1067-
...
1068-
NotImplementedError: Ehrhart quasi-polynomial can only be computed for compact polyhedron
1069-
1070-
.. SEEALSO::
1071-
1072-
:meth:`~sage.geometry.polyhedron.backend_normaliz.hilbert_series`,
1073-
:meth:`~sage.geometry.polyhedron.backend_normaliz.ehrhart_series`
1074-
"""
1075-
if self.is_empty():
1076-
return 0
1077-
1078-
if not self.is_compact():
1079-
raise NotImplementedError("Ehrhart quasi-polynomial can only be computed for compact polyhedron")
1080-
1081-
cone = self._normaliz_cone
1082-
# Normaliz needs to compute the EhrhartSeries first
1083-
PythonModule("PyNormaliz", spkg="pynormaliz").require()
1084-
import PyNormaliz
1085-
assert PyNormaliz.NmzCompute(cone, ["EhrhartSeries"])
1086-
e = self._nmz_result(cone, "EhrhartQuasiPolynomial")
1087-
1088-
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1089-
poly_ring = PolynomialRing(QQ, variable)
1090-
t = poly_ring.gens()[0]
1091-
if len(e) == 2:
1092-
# It is a polynomial
1093-
es = sum([e[0][i]*t**i for i in range(len(e[0]))])
1094-
return es / ZZ(e[1])
1095-
else:
1096-
# It is a quasi-polynomial
1097-
polynomials = []
1098-
for p in e[:-1]:
1099-
es = sum([p[i]*t**i for i in range(len(p))]) / ZZ(e[-1])
1100-
polynomials += [es]
1101-
1102-
return tuple(polynomials)
1103-
11041020
def _volume_normaliz(self, measure='euclidean'):
11051021
r"""
11061022
Computes the volume of a polytope using normaliz.
@@ -1316,6 +1232,81 @@ def ehrhart_series(self, variable='t'):
13161232

13171233
return es
13181234

1235+
def _ehrhart_quasipolynomial_normaliz(self, variable='t'):
1236+
r"""
1237+
Return the Ehrhart quasipolynomial of a compact rational polyhedron
1238+
using Normaliz.
1239+
1240+
If it is a polynomial, returns the polynomial. Otherwise, returns a
1241+
tuple of rational polynomials whose length is the quasi-period of the
1242+
quasipolynomial and each rational polynomial describes a residue class.
1243+
1244+
INPUT:
1245+
1246+
- ``variable`` -- string (default: ``'t'``)
1247+
1248+
OUTPUT:
1249+
1250+
A polynomial or tuple of polynomials.
1251+
1252+
EXAMPLES::
1253+
1254+
sage: C = Polyhedron(vertices = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]],backend='normaliz') # optional - pynormaliz
1255+
sage: C._ehrhart_quasipolynomial_normaliz() # optional - pynormaliz
1256+
t^3 + 3*t^2 + 3*t + 1
1257+
1258+
sage: P = Polyhedron(vertices=[[0,0],[3/2,0],[0,3/2],[1,1]],backend='normaliz') # optional - pynormaliz
1259+
sage: P._ehrhart_quasipolynomial_normaliz() # optional - pynormaliz
1260+
(3/2*t^2 + 2*t + 1, 3/2*t^2 + 2*t + 1/2)
1261+
sage: P._ehrhart_quasipolynomial_normaliz('x') # optional - pynormaliz
1262+
(3/2*x^2 + 2*x + 1, 3/2*x^2 + 2*x + 1/2)
1263+
1264+
The quasipolynomial evaluated at ``i`` counts the integral points
1265+
in the ``i``-th dilate::
1266+
1267+
sage: Q = Polyhedron(vertices = [[-1/3],[2/3]],backend='normaliz') # optional - pynormaliz
1268+
sage: p0,p1,p2 = Q._ehrhart_quasipolynomial_normaliz() # optional - pynormaliz
1269+
sage: r0 = [p0(i) for i in range(15)] # optional - pynormaliz
1270+
sage: r1 = [p1(i) for i in range(15)] # optional - pynormaliz
1271+
sage: r2 = [p2(i) for i in range(15)] # optional - pynormaliz
1272+
sage: result = [None]*15 # optional - pynormaliz
1273+
sage: result[::3] = r0[::3] # optional - pynormaliz
1274+
sage: result[1::3] = r1[1::3] # optional - pynormaliz
1275+
sage: result[2::3] = r2[2::3] # optional - pynormaliz
1276+
sage: result == [(i*Q).integral_points_count() for i in range(15)] # optional - pynormaliz
1277+
True
1278+
1279+
1280+
.. SEEALSO::
1281+
1282+
:meth:`~sage.geometry.polyhedron.backend_normaliz.hilbert_series`,
1283+
:meth:`~sage.geometry.polyhedron.backend_normaliz.ehrhart_series`
1284+
"""
1285+
cone = self._normaliz_cone
1286+
# Normaliz needs to compute the EhrhartSeries first
1287+
PythonModule("PyNormaliz", spkg="pynormaliz").require()
1288+
import PyNormaliz
1289+
assert PyNormaliz.NmzCompute(cone, ["EhrhartSeries"])
1290+
e = self._nmz_result(cone, "EhrhartQuasiPolynomial")
1291+
1292+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1293+
poly_ring = PolynomialRing(QQ, variable)
1294+
t = poly_ring.gens()[0]
1295+
if len(e) == 2:
1296+
# It is a polynomial
1297+
es = sum([e[0][i]*t**i for i in range(len(e[0]))])
1298+
return es / ZZ(e[1])
1299+
else:
1300+
# It is a quasipolynomial
1301+
polynomials = []
1302+
for p in e[:-1]:
1303+
es = sum([p[i]*t**i for i in range(len(p))]) / ZZ(e[-1])
1304+
polynomials += [es]
1305+
1306+
return tuple(polynomials)
1307+
1308+
_ehrhart_polynomial_normaliz = _ehrhart_quasipolynomial_normaliz
1309+
13191310
def hilbert_series(self, grading, variable='t'):
13201311
r"""
13211312
Return the Hilbert series of the polyhedron with respect to ``grading``.

0 commit comments

Comments
 (0)