1844 lines
69 KiB
Python
1844 lines
69 KiB
Python
"""
|
|
COMPUTING (LOCAL) IGUSA AND TOPOLOGICAL ZETA FUNCTIONS
|
|
FOR NEWTON NON-DEGENERATED POLYNOMIALS
|
|
"""
|
|
|
|
#######################################################################
|
|
# #
|
|
# COMPUTING (LOCAL) IGUSA AND TOPOLOGICAL ZETA FUNCTIONS OF A #
|
|
# NON-DEGENERATED POLYNOMIAL WITH RESPECT TO ITS NEWTON'S POLYHEDRON. #
|
|
# For Sagemath #
|
|
# #
|
|
# Last update: 18-07-2022 #
|
|
# #
|
|
# These functions are based on the work of K. Hoornaert and D. Loots: #
|
|
# "Computer program written in Maple for the calculation #
|
|
# of Igusa local zeta #
|
|
# function". #
|
|
# http://www.wis.kuleuven.ac.be/algebra/kathleen.htm, 2000. #
|
|
# #
|
|
# For any bug or commentary, please contact me. #
|
|
# #
|
|
# Juan Viu-Sos #
|
|
# Universidad Politecnica de Madrid #
|
|
# https://jviusos.github.io/ #
|
|
# juan.viusos@upm.es #
|
|
# #
|
|
# #
|
|
# This program is free software; you can redistribute it and/or modify#
|
|
# it under the terms of the GNU General Public License as published by#
|
|
# the Free Software Foundation; either version 2 of the License, or #
|
|
# (at your option) any later version. #
|
|
# #
|
|
# This program is distributed in the hope that it will be useful, #
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
# GNU General Public License for more details. #
|
|
# #
|
|
# You should have received a copy of the GNU General Public License #
|
|
# along with this program; if not, write to the Free Software #
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, #
|
|
# MA 02110-1301, USA. #
|
|
# #
|
|
#######################################################################
|
|
|
|
# TODO
|
|
# [ ] Set the local case by default
|
|
# [ ] Implementation of formula for germs on quotient singularities
|
|
# [ ] Improve topological and monodromy zeta functions outputs
|
|
# [ ] Implementation of the motivic zeta function
|
|
|
|
from sage.arith.misc import gcd
|
|
from sage.calculus.var import var
|
|
from sage.combinat.tuple import Tuples
|
|
from sage.geometry.cone import Cone
|
|
from sage.geometry.fan import Fan
|
|
from sage.geometry.polyhedron.constructor import Polyhedron
|
|
from sage.geometry.triangulation.point_configuration import PointConfiguration
|
|
from sage.matrix.constructor import matrix
|
|
from sage.misc.flatten import flatten
|
|
from sage.misc.misc_c import prod
|
|
from sage.misc.mrange import mrange
|
|
from sage.modules.free_module import VectorSpace
|
|
from sage.modules.free_module_element import vector
|
|
from sage.plot.plot3d.shapes2 import point3d
|
|
from sage.plot.point import point
|
|
from sage.rings.finite_rings.finite_field_constructor import GF
|
|
from sage.rings.integer import Integer
|
|
from sage.rings.integer_ring import ZZ
|
|
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
from sage.rings.rational_field import QQ
|
|
from sage.symbolic.ring import SR
|
|
|
|
|
|
class ZetaFunctions():
|
|
r"""
|
|
The class ``Zetafunctions`` takes a multivariate polynomial as
|
|
argument and allows to calculate its associated (local) Igusa and
|
|
Topological zeta functions.
|
|
|
|
This class allows us to get information about the associated
|
|
Newton's polyhedron, their faces, the associated cones, ...
|
|
|
|
This class is composed by a multivariate polynomial `f` of degree
|
|
`n` with non-constant term and its associated Newton's polyhedron
|
|
`\Gamma(f)`.
|
|
|
|
Methods in ZetaFunctions:
|
|
|
|
- ``cones_plot(self, **kwargs)``
|
|
- ``actual_faces(self, d=1, compact=False)``
|
|
- ``dict_info_poles(self, d=1, weights=None, local=False)``
|
|
- ``get_newton_polyhedron(self)``
|
|
- ``get_polyfaces_dictionary(self, keys = 'polynomials', compact=False)``
|
|
- ``give_expected_pole_info(self, d=1, local=False, weights=None)``
|
|
- ``give_info_facets(self, compact = False)``
|
|
- ``give_info_newton(self, faces=False, cones=False, compact = False)``
|
|
- ``is_newton_non_degenerated(self, p=None, local=False, topological=False, method='default', info=False)``
|
|
- ``newton_plot(self, support=False, point_size = 30, **kwargs)``
|
|
- ``igusa_zeta(self, p=None, local=False, weights=None, info=False, check='ideals', dict_Ntau={},)``
|
|
- ``topological_zeta(self, d=1, local=False, weights=None, info=False, check='ideals')``
|
|
- ``monodromy_zeta(self, char=False, info=False, cyclo_info=False, check='ideals')``
|
|
- ``Mtaus(self)``
|
|
- ``Jtaus(self, ring_s, weights=None)``
|
|
- ``ntaus(self)``
|
|
|
|
.. WARNING::
|
|
|
|
These formulas for the Igusa and Topological zeta functions
|
|
only work when the given polynomial is NOT DEGENERATED with
|
|
respect to his associated Newton Polyhedron (see [DH01]_,
|
|
[DL92]_ and [Var76]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: zex = ZetaFunctions(x^2 + y*z)
|
|
sage: zex.give_info_newton()
|
|
Newton's polyhedron of x^2 + y*z:
|
|
support points = [(2, 0, 0), (0, 1, 1)]
|
|
vertices = [(0, 1, 1), (2, 0, 0)]
|
|
number of proper faces = 13
|
|
Facet 0: x + 2*y - 2 >= 0
|
|
Facet 1: x + 2*z - 2 >= 0
|
|
Facet 2: z >= 0
|
|
Facet 3: y >= 0
|
|
Facet 4: x >= 0
|
|
sage: zex.topological_zeta().factor()
|
|
(1/2) * (s + 1)^-1 * (s + 3/2)^-1 * (s + 3)
|
|
sage: zex.give_expected_pole_info()
|
|
The candidate poles of the (local) topological zeta function
|
|
(with d = 1) of x^2 + y*z in function of s are:
|
|
======
|
|
-3/2 with expected order: 2
|
|
The responsible face of maximal contribution is
|
|
``tau_0`` = minimal face who intersects with the
|
|
diagonal of ambient space:
|
|
tau4: dim 1, vertices = [(0, 1, 1), (2, 0, 0)], rays = [],
|
|
cone generators = [(1, 0, 2), (1, 2, 0)],
|
|
partition into simplicial
|
|
cones = [[(1, 0, 2), (1, 2, 0)]]
|
|
======
|
|
-1 with expected order: 1
|
|
(If all Vol(tau) are 0, where tau runs through the selected faces
|
|
that are no vertices, then the expected order of -1 is 0).
|
|
|
|
|
|
REFERENCES:
|
|
|
|
.. [DH01] Denef, J. and Hoornaert, K., Newton Polyhedra and Igusa's Local
|
|
Zeta Function, 2001. J. Number Theory 89 (2001), no. 1, 31-64.
|
|
|
|
.. [DL92] Denef, J. and Loeser, F., Caractéristiques d'Euler-Poincaré,
|
|
fonctions zêta locales et modifications analytiques,
|
|
J. Amer. Math. Soc. 5 (1992), no. 4, 05-720.
|
|
|
|
.. [HL00] Hoornaert, K. and Loots, D., Computer program written in Maple
|
|
for the calculation of Igusa's local zeta function, (2000).
|
|
http://www.wis.kuleuven.ac.be/algebra/kathleen.htm
|
|
|
|
.. [Var76] Varchenko, A. N., Zeta-function of monodromy and Newton's
|
|
diagram. Invent. Math. 37 (1976), no. 3, 253-262.
|
|
|
|
.. [Viu12] Viu-Sos, J., Funciones zeta y poliedros de Newton: Aspectos
|
|
teoricos y computacionales, Master Thesis (2012).
|
|
https://zaguan.unizar.es/record/8916/files/TAZ-TFM-2012-749.pdf
|
|
|
|
AUTHORS:
|
|
|
|
- Kathleen Hoornaert (2000): initial version for Maple
|
|
|
|
- Juan Viu-Sos (2012): initial version for Sage
|
|
|
|
- Frédéric Chapoton (2017): improved outputs, handling errors
|
|
and symbolic variables
|
|
|
|
- Juan Viu-Sos (2022): improved methods and new functions
|
|
in the class to visualize data
|
|
"""
|
|
|
|
def __init__(self, poly):
|
|
# Polynomial
|
|
self._f = poly
|
|
# Newton's polyhedron
|
|
self._Gammaf = newton_polyhedron(poly)
|
|
|
|
def cones_plot(self, **kwargs):
|
|
r"""
|
|
Plots the Fan of cones associated to Newton's polyhedron
|
|
(for `n = 2, 3`).
|
|
|
|
Options:
|
|
|
|
- Keyword options for ``plot()``.
|
|
"""
|
|
return fan_all_cones(self._Gammaf).plot(**kwargs)
|
|
|
|
def actual_faces(self, d=1, compact=False):
|
|
r"""
|
|
Return the list of faces taking ``d`` and ``local`` into account.
|
|
|
|
INPUT:
|
|
|
|
- ``d`` -- positive integer (default: ``1``), integer to decide
|
|
which character is used to compute the Zeta functions
|
|
|
|
- ``local`` -- boolean (default: ``False``), looks only for info about
|
|
compact faces
|
|
"""
|
|
P = self._Gammaf
|
|
if compact:
|
|
faces_set = compact_faces(P)
|
|
else:
|
|
faces_set = proper_faces(P)
|
|
return face_divisors(d, faces_set)
|
|
|
|
def dict_info_poles(self, d=1, weights=None, local=False):
|
|
r"""
|
|
Return a dictionary where the keys are the candidate real poles of
|
|
the chosen zeta function.
|
|
|
|
INPUT:
|
|
|
|
- ``d`` -- positive integer (default: ``1``), integer to decide
|
|
which character is used to compute the Zeta functions
|
|
|
|
- ``weights`` -- list (default: ``None``),
|
|
an `n`-list of positive integers
|
|
`[w_1,\ldots,w_n]` representing the volume form
|
|
`x_1^{w_1-1}\cdots x_n^{w_n-1}\ dx_1\wedge\cdots\wedge dx_n`;
|
|
if set to ``None``, ``w_i = 1``
|
|
|
|
- ``local`` -- boolean (default: ``False``), looks only for info about
|
|
compact faces
|
|
|
|
OUTPUT:
|
|
|
|
Information about poles of the topological zeta function:
|
|
|
|
- The list of perpendicular vectors to the facets that are
|
|
responsible for the candidate real pole
|
|
|
|
- A list of the faces of maximal dimension that are responsible for
|
|
the expected order
|
|
|
|
- The expected order
|
|
|
|
- A boolean: for the candidate pole `-1`
|
|
the factor ``L_tau`` or `s/(s+1)` can contribute to the order.
|
|
If this is is the case, we increase the expected order due to
|
|
the ``S_Delta_tau`` by `1` and this record gets the value ``True``.
|
|
In all other cases we do not increase this expected order and this
|
|
record gets the value ``False``
|
|
"""
|
|
f = self._f
|
|
P = self._Gammaf
|
|
PL = P.face_lattice()
|
|
# We need to keep the face lattice in order to
|
|
# compare faces
|
|
faces_set = self.actual_faces(d=d, compact=local)
|
|
|
|
all_prim_vect = set()
|
|
for tau in faces_set:
|
|
prim = set(map(tuple, primitive_vectors(tau)))
|
|
all_prim_vect = all_prim_vect.union(prim)
|
|
|
|
dict_poles = {}
|
|
for v in all_prim_vect:
|
|
if m_vect(v, P):
|
|
realpole = -sigma_vect(v, weights) / m_vect(v, P)
|
|
# We initialize a list of attributes if the pole is
|
|
# not detected yet
|
|
if realpole not in dict_poles:
|
|
dict_poles[realpole] = [set([v]), [], 0, False]
|
|
else:
|
|
dict_poles[realpole][0].add(v)
|
|
|
|
# We calculate the maximal expected of each pole and the faces
|
|
# of higher dimension
|
|
# responsibles of this order
|
|
poles_set = list(dict_poles.keys())
|
|
# If d=1, we have the face tau_0
|
|
# (tau_0 is the smallest face which contains the intersecion
|
|
# between diagonal and polyhedron)
|
|
if d == 1 and not local:
|
|
max_pole = max(poles_set)
|
|
for tau in faces_set:
|
|
gens_cone = [tuple(v) for v in primitive_vectors(tau)]
|
|
if set(gens_cone) == dict_poles[max_pole][0]:
|
|
dict_poles[max_pole][1] = [tau]
|
|
dict_poles[max_pole][2] = matrix(gens_cone).rank()
|
|
dict_poles[max_pole][3] = False
|
|
break
|
|
poles_set.remove(max_pole)
|
|
for pole in poles_set:
|
|
vects_pole = dict_poles[pole][0] # added
|
|
interscones = []
|
|
for tau in faces_set:
|
|
prim = set(tuple(v) for v in primitive_vectors(tau))
|
|
interscones.append(prim.intersection(vects_pole))
|
|
orders = [matrix(QQ, list(ic)).rank() for ic in interscones]
|
|
maxorder = max(orders)
|
|
max_faces = set(faces_set[i] for i in range(len(faces_set))
|
|
if orders[i] == maxorder)
|
|
# We find the maximal elements in set of responsible faces
|
|
for face in faces_set:
|
|
if face in max_faces:
|
|
if any(PL.lt(face, tau) for tau in max_faces):
|
|
# BUG: "face < tau" did not compare well
|
|
max_faces.remove(face)
|
|
dict_poles[pole][1] = list(max_faces)
|
|
# Max order of pole is max dim of the associated cones
|
|
dict_poles[pole][2] = maxorder
|
|
# We convert the set of vectors into a list
|
|
dict_poles[pole][0] = list(map(vector, dict_poles[pole][0]))
|
|
|
|
# Special pole -1 has sometimes a larger order
|
|
if -1 in dict_poles:
|
|
faces_minus_one = dict_poles[-1][1]
|
|
if max(len(support_points_in_face(f, tau))
|
|
for tau in faces_minus_one) > 1:
|
|
dict_poles[-1][2] = dict_poles[-1][2] + 1
|
|
dict_poles[-1][3] = True
|
|
return dict_poles
|
|
|
|
def get_newton_polyhedron(self):
|
|
r"""
|
|
Return Newton's polyhedron.
|
|
"""
|
|
return self._Gammaf
|
|
|
|
def get_polyfaces_dictionary(self, keys='polynomials', compact=False):
|
|
r"""
|
|
Return a dictionary with the polynomials associated to each face
|
|
of the Newton's polyhedron.
|
|
|
|
INPUT:
|
|
|
|
- ``keys`` -- string (default: 'polynomials'), either
|
|
'faces' or 'polynomials', to decide the type of keys
|
|
|
|
- ``compact`` -- boolean (default: ``False``), if
|
|
set to ``True``, consider only the compact faces.
|
|
"""
|
|
faces_set = self.actual_faces(compact=compact)
|
|
if keys == 'polynomials':
|
|
return {ftau(self._f, tau): tau for tau in faces_set}
|
|
if keys == 'faces':
|
|
return {tau: ftau(self._f, tau) for tau in faces_set}
|
|
raise ValueError("Not recognized option for 'keys'.")
|
|
|
|
def give_expected_pole_info(self, d=1, local=False, weights=None):
|
|
r"""
|
|
Print information about the candidate real poles of the
|
|
topological zeta function `Z_{top, f}^{(d)}(s)` like order of
|
|
poles and responsible faces of highest dimension.
|
|
|
|
INPUT:
|
|
|
|
|
|
- ``d`` -- positive integer (default: ``1``), integer to decide
|
|
which character is used to compute the Zeta functions
|
|
|
|
- ``local`` -- boolean (default: ``False``), if ``True`` it
|
|
calculates the local (at the origin) topological Zeta function.
|
|
|
|
- ``weights`` -- list (default: ``None``),
|
|
an `n`-list of positive integers
|
|
`[w_1,\ldots,w_n]` representing the volume form
|
|
`x_1^{w_1-1}\cdots x_n^{w_n-1}\ dx_1\wedge\cdots\wedge dx_n`;
|
|
if set to ``None``, ``w_i = 1``
|
|
"""
|
|
f = self._f
|
|
P = self._Gammaf
|
|
faces_set = self.actual_faces(d=d, compact=local)
|
|
dict_poles = self.dict_info_poles(d=d, weights=weights, local=local)
|
|
|
|
n_supp_by_face = [len(support_points_in_face(f, tau))
|
|
for tau in faces_set]
|
|
if not dict_poles:
|
|
if (d == 1 and max(n_supp_by_face) == 1) or d != 1:
|
|
print("There will be no poles for the (local) topological " +
|
|
"zeta function " + "(with d = " + str(d) + ") of "
|
|
+ str(f) + ".")
|
|
else:
|
|
print("The candidate poles of the (local) topological " +
|
|
"zeta function (with d = " + str(d) + ") of " +
|
|
str(f) + " in function of s are:")
|
|
print("=" * 6)
|
|
print("-1 with expected order: 1")
|
|
print("(If all Vol(tau) are 0, where tau runs through " +
|
|
"the selected faces that are no vertices, then " +
|
|
"the expected order of -1 is 0)")
|
|
else:
|
|
poles_set = list(dict_poles.keys())
|
|
poles_set.sort(reverse=True)
|
|
# We reconstruct the list of all faces accessing to element
|
|
# some_face = dict_poles[poles_set[0]][1][0]
|
|
# list_all_faces =
|
|
# list(some_face.polyhedron().face_lattice())[1:-1]
|
|
list_all_faces = proper_faces(P) # debug
|
|
if d == 1 and not local:
|
|
print("The candidate poles of the (local) topological " +
|
|
"zeta function (with d = " + str(d) + ") of " +
|
|
str(f) + " in function of s are:")
|
|
max_pole = max(poles_set)
|
|
print("=" * 6)
|
|
print(str(max_pole) + " with expected order: "
|
|
+ str(dict_poles[max_pole][2]))
|
|
if max_pole == -1:
|
|
if dict_poles[-1][3]:
|
|
print("(If all the Vol(tau) of the faces tau that" +
|
|
"are no vertices and contained in Gamma " +
|
|
"are 0, then the expected order of -1 is " +
|
|
str(dict_poles[-1][3] - 1) + ").")
|
|
tau_0 = dict_poles[max_pole][1][0]
|
|
print("The responsible face of maximal contribution is " +
|
|
"``tau_0`` = minimal face who intersects " +
|
|
"with the diagonal of ambient space:")
|
|
i = list_all_faces.index(tau_0)
|
|
print(" tau" + str(i) + ": " + face_info_output(tau_0) +
|
|
", " + cone_info_output(cone_from_face(tau_0)))
|
|
poles_set.remove(max_pole)
|
|
if -1 not in poles_set:
|
|
print("=" * 6)
|
|
print("-1 with expected order: 1")
|
|
print("(If all Vol(tau) are 0, where tau runs through " +
|
|
"the selected faces that are no vertices, then " +
|
|
"the expected order of -1 is 0).")
|
|
elif local:
|
|
print("The candidate poles of the local topological " +
|
|
"zeta function (with d = " + str(d) + ") of " +
|
|
str(f) + " in function of s are:")
|
|
if max(n_supp_by_face) > 1 and -1 not in poles_set:
|
|
print("=" * 6)
|
|
print("-1 with expected order: 1")
|
|
print("(If all Vol(tau) are 0, where tau runs " +
|
|
"through the selected faces that are no vertices, " +
|
|
"then the expected order of -1 is 0).")
|
|
for pole in poles_set:
|
|
print("=" * 6)
|
|
print(str(pole) + " with expected order: " +
|
|
str(dict_poles[pole][2]))
|
|
if dict_poles[pole][3]:
|
|
print("(If all the Vol(tau) of the faces that are no " +
|
|
"vertices and contained")
|
|
print("one or more of the faces below are 0, then the " +
|
|
"expected order of -1 is " +
|
|
str(dict_poles[pole][3] - 1) + ").")
|
|
print("The responsible face(s) of maximal dimension is/are:")
|
|
for tau in dict_poles[pole][1]:
|
|
i = list_all_faces.index(tau)
|
|
print("\t" + "=" * 6)
|
|
print("\t tau" + str(i) + ": " + face_info_output(tau) +
|
|
", " + cone_info_output(cone_from_face(tau)))
|
|
|
|
def give_info_facets(self, compact=False):
|
|
r"""
|
|
Print a relation of facets in Newton's polyhedron and the
|
|
inequalities which define them.
|
|
"""
|
|
give_all_facets_info(self._f, self._Gammaf, compact)
|
|
|
|
def give_info_newton(self, faces=False, cones=False, compact=False):
|
|
r"""
|
|
Print information about the the Newton's polyhedron of ``f``:
|
|
|
|
- Support points of f.
|
|
- Vertices of Newton's polyhedron.
|
|
- Number of proper faces.
|
|
- Inequalities defining facets.
|
|
|
|
INPUT:
|
|
|
|
- ``faces`` -- boolean (default: ``False``), if ``True``
|
|
prints information about each face in polyhedron
|
|
|
|
- ``cones`` -- boolean (default: ``False``), if ``True``
|
|
prints information about each cone associated
|
|
to faces in polyhedron
|
|
|
|
- ``compact`` -- boolean (default: ``False``), if ``True``
|
|
consider only the compact faces
|
|
"""
|
|
faces_set = self.actual_faces(compact=compact)
|
|
print("Newton's polyhedron of " + str(self._f) + ":")
|
|
print(" support points = " + str(self._f.exponents()))
|
|
print(" vertices = " +
|
|
str(list(map(tuple, self._Gammaf.vertices()))))
|
|
if compact:
|
|
print(" number of compact faces = {}".format(len(faces_set)))
|
|
else:
|
|
print(" number of proper faces = {}".format(len(faces_set)))
|
|
give_all_facets_info(self._f, self._Gammaf, compact)
|
|
if faces or cones:
|
|
print("Information about faces:")
|
|
for tau in faces_set:
|
|
face_info, cone_info = "", ""
|
|
i = faces_set.index(tau)
|
|
if faces:
|
|
face_info = face_info_output(tau) + "\n"
|
|
if cones:
|
|
cone_info = cone_info_output(cone_from_face(tau)) + "\n"
|
|
print("tau" + str(i) + ": " + face_info + cone_info)
|
|
|
|
def is_newton_non_degenerated(self, p=None, local=False, info=False,
|
|
topological=False, method='default'):
|
|
r"""Checks if the polynomial ``f`` is degenerated over
|
|
`\mathbb{F}_p` (`p` prime) with respect \
|
|
the faces of the polyhedron ``P`` (see [DH01]_).
|
|
|
|
INPUT:
|
|
|
|
- ``p`` -- a primer number or ``None`` (default: ''None``),
|
|
if ``None`` it checks degeneration over `\CC` (which is
|
|
equivalent to be degenerated over `\mathbb{F}_p` with `p\gg 0`)
|
|
|
|
- ``local`` -- boolean (default: ``False``), if ``True`` it checks
|
|
degeneration for local case (only with respect the compact faces)
|
|
|
|
- ``topological`` -- boolean (default: ``False``), if ``True`` it
|
|
checks degeneration for the faces of the global polyhedron
|
|
|
|
- ``info`` -- boolean (default: ``False``), if ``True`` it prints the
|
|
first face for which the polynomial is degenerated
|
|
|
|
For finite fields (``p`` is a given prime):
|
|
|
|
- ``method`` -- string (default: 'default')
|
|
- if 'default', it checks the condition using evaluation
|
|
over `(\mathbb{F}_p\setminus\{0\})^n` in the system of equations
|
|
|
|
- if 'ideals' it checks the condition using ideals over the
|
|
finite field.
|
|
"""
|
|
f = self._f
|
|
if topological and not local:
|
|
return not is_global_degenerated(f, info=info, method=method)
|
|
P = self._Gammaf
|
|
if local:
|
|
faces_set = compact_faces(P)
|
|
else:
|
|
faces_set = P.face_lattice()[1:]
|
|
|
|
for tau in faces_set:
|
|
f_tau = ftau(f, tau)
|
|
if is_degenerated(f_tau, p, method):
|
|
if info:
|
|
# print("The formula for the Igusa zeta function is " +
|
|
# "not valid:")
|
|
if not p:
|
|
print("The polynomial is degenerated at least with " +
|
|
"respect to the face tau = {" +
|
|
face_info_output(tau) +
|
|
"} over the complex numbers!")
|
|
else:
|
|
print("The polynomial is degenerated at least with " +
|
|
"respect to the face tau = {"
|
|
+ face_info_output(tau)
|
|
+ "} over GF("
|
|
+ str(p) + ")!")
|
|
return False
|
|
return True
|
|
|
|
def newton_plot(self, support=False, point_size=30, **kwargs):
|
|
r"""
|
|
Returns the graphics of the associated Newton's polyhedron
|
|
(for `n = 2, 3`) together with the support of `f`.
|
|
In 3D, the origin is plotted as a black point.
|
|
|
|
INPUT:
|
|
|
|
- ``support`` -- boolean (default: False), whether it plots the support
|
|
points outside the compact part of the Newton polyhedron
|
|
|
|
- ``point_size`` -- number (default: 30), size of the ponits in plot.
|
|
|
|
- Other keyword options for ``plot()``.
|
|
"""
|
|
n = self._f.parent().ngens()
|
|
boundary = self._Gammaf.faces(n - 1)
|
|
pts = self._f.exponents()
|
|
if not support:
|
|
pts = [p for p in pts
|
|
if any(tuple(p) in [tuple(v) for v in e.vertices()]
|
|
for e in boundary)]
|
|
if n == 2:
|
|
plot_pts = sum([point(p, color='red', size=point_size)
|
|
for p in pts])
|
|
elif n == 3:
|
|
plot_pts = sum([point3d(p, color='red', size=point_size)
|
|
for p in pts])
|
|
plot_pts += point3d(tuple([0] * 3), color='black', size=point_size)
|
|
# print(plot_pts)
|
|
else:
|
|
raise TypeError('Dimension different from 2 and 3.')
|
|
return (self._Gammaf.plot(**kwargs) + plot_pts)
|
|
|
|
def igusa_zeta(self, p=None, local=False, weights=None,
|
|
info=False, check='ideals', dict_Ntau={}):
|
|
r"""
|
|
Return the expression of the Igusa zeta function for `p` a
|
|
prime number (explicit or abstract), in terms of a symbolic
|
|
variable `s`.
|
|
|
|
INPUT:
|
|
|
|
- ``p`` -- a primer number or ``None`` (default: ``None``),
|
|
``None`` stands for the abstract case
|
|
|
|
- ``local`` -- boolean (default: ``False``), if ``True`` it calculates
|
|
the local Igusa zeta function (at the origin)
|
|
|
|
- ``weights`` -- list (default: ``None``),
|
|
an `n`-list of positive integers
|
|
`[w_1,\ldots,w_n]` representing the volume form
|
|
`x_1^{w_1-1}\cdots x_n^{w_n-1}\ dx_1\wedge\cdots\wedge dx_n`;
|
|
if set to ``None``, ``w_i = 1``
|
|
|
|
- ``info`` -- boolean (default: ``False``), if ``True`` it gives
|
|
information of each face `\tau`, the associated cone of `\tau`,
|
|
and the values ``L_tau`` and ``S_tau`` in the process
|
|
|
|
- ``check`` -- string (default: ``'default'``), choose the method to
|
|
check the non-degeneracy condition ('default' or 'ideals').
|
|
If ``check = 'no_check'``, degeneracy checking is omitted
|
|
|
|
- ``dict_Ntau`` -- dictionary (default: ``{}``), meainingful only in
|
|
the abstract case ``p = None``:
|
|
|
|
- The keys are the polynomials `f_{\tau}` associated of each
|
|
face `\tau` of the Newton Polyhedron.
|
|
|
|
- The values are the associated abstract values
|
|
|
|
.. math::
|
|
N_{\tau}=\#\{a\in(\mathbb{F}_p\setminus\{0\})^d\mid
|
|
f^*_{\tau}(a)=0\}
|
|
|
|
with `f^*_{\tau}=\mathbb{F}_p(f_{\tau})`,
|
|
depending of a symbolic variable `p`
|
|
|
|
If the value associated to a face `\tau_k` is not in the
|
|
dictionary, the method introduces a new symbolic variable
|
|
``N_tauk`` to represent `N_{\tau_k}`
|
|
|
|
.. WARNING::
|
|
|
|
This formula is only valid when the the given polynomial
|
|
is NOT DEGENERATED for `p` with respect to its
|
|
associated Newton Polyhedron (see [DH01]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: p, s = var('p, s')
|
|
sage: zex1 = ZetaFunctions(x^2 - y^2 + z^3)
|
|
|
|
For `p=3` given::
|
|
|
|
sage: zex1.igusa_zeta(p = 3)
|
|
2*3^(2*s)*(3^(2*s + 4) - 3^(s + 1) + 2)/((3^(3*s + 4) - 1)*(3^(s + 1) - 1))
|
|
|
|
For `p` arbitrary, we can give the number of solutions over the faces::
|
|
|
|
sage: dNtau1 = {x^2-y^2+z^3: (p-1)*(p-3), -y^2+z^3: (p-1)^2, x^2+z^3: (p-1)^2, x^2-y^2: 2*(p-1)^2}
|
|
sage: z1 = zex1.igusa_zeta(p = None, dict_Ntau = dNtau1)
|
|
sage: numer = (p - 1)*p^(2*s)*(p^(2*s + 4) + p - p^(s + 1) - 1)
|
|
sage: denom = (p^(3*s + 4) - 1)*(p^(s + 1) - 1)
|
|
sage: bool(z1 == numer / denom)
|
|
True
|
|
|
|
sage: zex2 = ZetaFunctions(x^2 + y*z + z^2)
|
|
|
|
For `p=3 \bmod 4`, we can give the number of solutions over the faces::
|
|
|
|
sage: dNtau2 = {x^2+y*z+z^2: (p-1)^2,y*z+z^2: (p-1)^2, x^2+y*z: (p-1)^2,x^2+z^2 : 0}
|
|
sage: zex2.igusa_zeta(p = None, dict_Ntau = dNtau2)
|
|
(p - 1)*p^(2*s)*(p^(s + 3) - 1)/((p^(2*s + 3) - 1)*(p^(s + 1) - 1))
|
|
|
|
For `p=1 \bmod 4`::
|
|
|
|
sage: dNtau2bis = {x^2+y*z+z^2: (p-1)*(p-3), y*z+z^2: (p-1)^2, x^2+y*z: (p-1)^2, x^2+z^2: 2*(p-1)^2}
|
|
sage: zex2.igusa_zeta(p = None, dict_Ntau = dNtau2bis)
|
|
(p - 1)*p^(2*s)*(p^(s + 3) - 1)/((p^(2*s + 3) - 1)*(p^(s + 1) - 1))
|
|
"""
|
|
f = self._f
|
|
if not isinstance(p, (int, Integer)):
|
|
p = var('p')
|
|
s = var('s')
|
|
P = self._Gammaf
|
|
abs_Ngamma = None
|
|
abs_Ntau = None
|
|
if check != 'no_check':
|
|
if not self.is_newton_non_degenerated(p, local,
|
|
method=check, info=info):
|
|
raise TypeError('degenerated wrt Newton')
|
|
else:
|
|
print("Warning: not checking the non-degeneracy condition!")
|
|
if local:
|
|
faces_set = compact_faces(P)
|
|
result = 0
|
|
else:
|
|
faces_set = proper_faces(P)
|
|
if p not in ZZ:
|
|
abs_Ngamma = dict_Ntau.get(f)
|
|
if abs_Ngamma is None:
|
|
abs_Ngamma = var('N_Gamma')
|
|
result = Lgamma(f, p, abs_Ngamma, s)
|
|
if info:
|
|
print("Gamma: total polyhedron\nL_gamma = " +
|
|
str(result) + "\n")
|
|
for tau in faces_set:
|
|
i = proper_faces(P).index(tau)
|
|
if p not in ZZ:
|
|
f_tau = ftau(f, tau)
|
|
if len(f_tau.exponents()) == 1:
|
|
abs_Ntau = 0 # If f_tau is a monomial
|
|
else:
|
|
abs_Ntau = dict_Ntau.get(f_tau)
|
|
if abs_Ntau is None:
|
|
abs_Ntau = var('N_tau' + str(i))
|
|
L_tau, N_tau = Ltau(f, tau, p, abs_Ntau, s)
|
|
S_tau, cone_info = Stau(f, tau, p, weights, s)
|
|
if info:
|
|
print("tau" + str(i) + ":")
|
|
print(face_info_output(tau))
|
|
print(cone_info)
|
|
print("N_tau = " + str(N_tau))
|
|
print("L_tau = " + str(L_tau))
|
|
print("S_tau = " + str(S_tau))
|
|
print()
|
|
result += L_tau * S_tau
|
|
return result.factor()
|
|
|
|
def topological_zeta(self, d=1, local=False, weights=None, info=False,
|
|
check='ideals'):
|
|
r"""
|
|
Return the expression of the Topological zeta function
|
|
`Z_{top, f}^{(d)}` for `d\geq 1`, as a rational function
|
|
in the variable ``s``:
|
|
|
|
INPUT:
|
|
|
|
- ``d`` -- a positive integer (default: ``1``), only the divisors whose
|
|
multiplicity is a multiple of ``d`` are considered (see [DL92]_).
|
|
|
|
- ``local`` -- boolean (default: ``False``), if ``True`` it calculates
|
|
the local topological zeta function (at the origin)
|
|
|
|
- ``weights`` -- list (default: ``None``),
|
|
an `n`-list of positive integers
|
|
`[w_1,\ldots,w_n]` representing the volume form
|
|
`x_1^{w_1-1}\cdots x_n^{w_n-1}\ dx_1\wedge\cdots\wedge dx_n`;
|
|
if set to ``None``, ``w_i = 1``
|
|
|
|
- ``info`` -- boolean (default: ``False``), if ``True`` it gives
|
|
information of each face `\tau`, the associated cone of `\tau`,
|
|
and the values `J_\tau` and
|
|
`\dim(\tau)!\cdot\operatorname{Vol}(\tau)`
|
|
in the process (see [DL92]_)
|
|
|
|
- ``check`` -- string (default: ``'default'``), choose the method to
|
|
check the non-degeneracy condition ('default' or 'ideals').
|
|
If ``check = 'no_check'``, degeneracy checking is omitted
|
|
|
|
.. WARNING::
|
|
|
|
This formula is only valid when the the given polynomial
|
|
is NOT DEGENERATED with respect to its associated Newton
|
|
Polyhedron (see [DL92]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: zex1 = ZetaFunctions(x^2 + y*z)
|
|
sage: zex1.topological_zeta().factor()
|
|
(1/2) * (s + 1)^-1 * (s + 3/2)^-1 * (s + 3)
|
|
|
|
For `d = 2`::
|
|
|
|
sage: zex1.topological_zeta(d = 2).factor()
|
|
(1/2) * (s + 3/2)^-1
|
|
"""
|
|
f = self._f
|
|
ring_s = PolynomialRing(QQ, 's')
|
|
s = ring_s.gen(0)
|
|
P = self._Gammaf
|
|
if check != 'no_check':
|
|
if not self.is_newton_non_degenerated(local=True,
|
|
topological=True,
|
|
method=check, info=info):
|
|
raise TypeError('degenerated wrt Newton')
|
|
else:
|
|
print("Warning: not checking the non-degeneracy condition!")
|
|
faces_set = self.actual_faces(d=d, compact=local)
|
|
if local or d > 1:
|
|
result = ring_s.zero()
|
|
else:
|
|
total_face = P.face_lattice()[-1]
|
|
dim_gamma = total_face.dim()
|
|
vol_gamma = face_volume(f, total_face)
|
|
result = (s / (s + 1)) * (-1) ** dim_gamma * vol_gamma
|
|
if info:
|
|
print("Gamma: total polyhedron")
|
|
print("J_gamma = 1")
|
|
print("dim_Gamma!*Vol(Gamma) = " + str(vol_gamma))
|
|
print()
|
|
|
|
for tau in faces_set:
|
|
J_tau, cone_info = Jtau(tau, weights, ring_s)
|
|
dim_tau = tau.dim()
|
|
vol_tau = face_volume(f, tau)
|
|
if info:
|
|
i = proper_faces(P).index(tau)
|
|
print("tau" + str(i) + ":")
|
|
print(face_info_output(tau))
|
|
print(cone_info)
|
|
print("J_tau = " + str(J_tau))
|
|
print("dim_tau!*Vol(tau) = " + str(vol_tau))
|
|
print()
|
|
if d == 1:
|
|
if dim_tau == 0:
|
|
term = J_tau
|
|
else:
|
|
term = (s / (s + 1)) * ((-1) ** dim_tau) * vol_tau * J_tau
|
|
else:
|
|
term = ((-1) ** dim_tau) * vol_tau * J_tau
|
|
result += term
|
|
return result
|
|
|
|
def monodromy_zeta(self, char=False, info=False, cyclo_info=False,
|
|
check='ideals'):
|
|
r"""
|
|
Return the expression of the Monodromy zeta function at the
|
|
origin, in terms of the symbolic variable ``s``.
|
|
|
|
INPUT:
|
|
|
|
- ``char`` -- boolean (default: ``False``), if ``True`` prints the
|
|
characteristic polynomial of the monodromy (only if `f` has an
|
|
isolated singularity at the origin)
|
|
|
|
- ``info`` -- boolean (default: ``False``), if ``True`` gives
|
|
information of each face `\tau`, the associated cone of `\tau`, and
|
|
the values `J_\tau` and `\dim(\tau)! \operatorname{Vol}(\tau)`
|
|
in the process (see [Var76]_).
|
|
|
|
- ``cyclo_info`` -- boolean (default: ``False``), if ``True`` prints
|
|
the cyclotomic factors with their multiplicity
|
|
|
|
- ``check`` -- string (default: ``'default'``), choose the method to
|
|
check the non-degeneracy condition ('default' or 'ideals').
|
|
If ``check = 'no_check'``, degeneracy checking is omitted
|
|
|
|
.. WARNING::
|
|
|
|
This formula is only valid when the the given polynomial
|
|
is NOT DEGENERATED with respect to its associated Newton
|
|
Polyhedron (see [Var76]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: R.<x,y> = QQ[]
|
|
sage: zex1 = ZetaFunctions(y^7+x^2*y^5+x^5*y^3)
|
|
sage: zex1.monodromy_zeta(char = True, cyclo_info=True).factor()
|
|
The characteristic polynomial of the monodromy is (t - 1)^3 *
|
|
(t^6 + t^5 + t^4 + t^3 + t^2 + t + 1) * (t^18 + t^17 + t^16 +
|
|
t^15 + t^14 + t^13 + t^12 + t^11 + t^10 + t^9 + t^8 + t^7 +
|
|
t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)
|
|
======
|
|
The decomposition in cyclotomic product is:
|
|
1-cyclotomic polynomial with multiplicity -2
|
|
7-cyclotomic polynomial with multiplicity -1
|
|
19-cyclotomic polynomial with multiplicity -1
|
|
======
|
|
(t - 1)^-2 * (t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)^-1 *
|
|
(t^18 + t^17 + t^16 + t^15 + t^14 + t^13 + t^12 + t^11 +
|
|
t^10 + t^9 + t^8 + t^7 + t^6 + t^5 + t^4 + t^3 + t^2 + t + 1)^-1
|
|
|
|
======
|
|
-1/(-t^26 + t^19 + t^7 - 1)
|
|
|
|
sage: S.<x,y,z> = QQ[]
|
|
sage: zex2 = ZetaFunctions(x*y + z^3)
|
|
sage: zex2.monodromy_zeta(char = True, cyclo_info=True).factor()
|
|
The characteristic polynomial of the monodromy is t^2 + t + 1
|
|
======
|
|
The decomposition in cyclotomic product is:
|
|
1-cyclotomic polynomial with multiplicity 1
|
|
3-cyclotomic polynomial with multiplicity 1
|
|
======
|
|
(-1) * (t - 1) * (t^2 + t + 1)
|
|
"""
|
|
f = self._f
|
|
n = f.parent().ngens()
|
|
P = self._Gammaf
|
|
ring_t = PolynomialRing(QQ, 't')
|
|
t = ring_t.gen(0)
|
|
|
|
if check != 'no_check':
|
|
# if is_global_degenerated(f, method=check):
|
|
if not self.is_newton_non_degenerated(local=True,
|
|
method=check, info=info):
|
|
raise TypeError('degenerated wrt Newton')
|
|
else:
|
|
print("Warning: not checking the non-degeneracy condition!")
|
|
|
|
result = ring_t.one()
|
|
for i, tau in enumerate(compact_faces(P)):
|
|
zeta_tau = Mtau(tau)
|
|
dim_tau = tau.dim()
|
|
vol_tau = face_volume(f, tau)
|
|
if info:
|
|
print("tau{}: {}".format(i, tau.ambient_Vrepresentation()))
|
|
print("M_tau = {}".format(zeta_tau))
|
|
print("dim_tau!*Vol(tau) = {}".format(vol_tau))
|
|
print()
|
|
result *= zeta_tau ** ((-1) ** dim_tau * vol_tau)
|
|
if char:
|
|
mn = (-1) ** (n - 1)
|
|
num_deg = ring_t(result.numerator()).degree()
|
|
den_deg = ring_t(result.denominator()).degree()
|
|
mu = mn * (num_deg - den_deg - 1)
|
|
aux = result(t=~t)
|
|
charpoly = (t ** mu * (t / (t - 1) * aux) ** mn).factor()
|
|
print("The characteristic polynomial of the monodromy is " +
|
|
"{}".format(charpoly))
|
|
print("=" * 6)
|
|
if cyclo_info:
|
|
cyclotomic = {}
|
|
for f, m in result.factor():
|
|
c = f.is_cyclotomic(certificate=True)
|
|
cyclotomic[c] = m
|
|
cyclo = list(cyclotomic.keys())
|
|
cyclo.sort()
|
|
cyclo_str = "The decomposition in cyclotomic product is:"
|
|
for c in cyclo:
|
|
cyclo_str += "\n" + str(c) + "-cyclotomic polynomial "
|
|
cyclo_str += " with multiplicity " + str(cyclotomic[c])
|
|
cyclo_str += "\n======"
|
|
print(cyclo_str)
|
|
return result
|
|
|
|
def Mtaus(self):
|
|
r"""
|
|
Return a dictionary assigning to each `\tau`
|
|
the value `M_\tau` as a rational function in `s`.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: R.<x,y> = QQ[]
|
|
sage: f = y^7 + x^2 * y^5 + x^5 * y^3
|
|
sage: zex = ZetaFunctions(f)
|
|
sage: zex.Mtaus()
|
|
{(A vertex at (0, 7),): -t^7 + 1,
|
|
(A vertex at (0, 7), A vertex at (2, 5)): -t^7 + 1,
|
|
(A vertex at (2, 5),): 1,
|
|
(A vertex at (2, 5), A vertex at (5, 3)): -t^19 + 1,
|
|
(A vertex at (5, 3),): 1}
|
|
"""
|
|
return {tuple(tau.vertices()): Mtau(tau)
|
|
for tau in compact_faces(self._Gammaf)}
|
|
|
|
def Jtaus(self, ring_s, weights=None):
|
|
r"""
|
|
Return a dictionary assigning to each `\tau`
|
|
the value `M_\tau` as a rational function in `s`.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: R.<x,y> = QQ[]
|
|
sage: S.<s> = QQ[]
|
|
sage: f = y^7 + x^2 * y^5 + x^5 * y^3
|
|
sage: zex = ZetaFunctions(f)
|
|
sage: zex.Jtaus(S)
|
|
{(A vertex at (0, 7),): (1/7) * (s + 2/7)^-1,
|
|
(A vertex at (0, 7), A vertex at (2, 5)): (1/7) * (s + 2/7)^-1,
|
|
(A vertex at (2, 5),): (1/133) * (s + 5/19)^-1 * (s + 2/7)^-1,
|
|
(A vertex at (2, 5), A vertex at (5, 3)): (1/19) * (s + 5/19)^-1,
|
|
(A vertex at (5, 3),): (2/57) * (s + 5/19)^-1 * (s + 1/3)^-1}
|
|
"""
|
|
return {tuple(tau.vertices()): Jtau(tau, weights, ring_s)[0]
|
|
for tau in compact_faces(self._Gammaf)}
|
|
|
|
def ntaus(self):
|
|
r"""
|
|
Return a dictionary assigning to each `\tau`
|
|
the value `n_\tau`.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: R.<x,y> = QQ[]
|
|
sage: f = y^7 + x^2 * y^5 + x^5 * y^3
|
|
sage: zex = ZetaFunctions(f)
|
|
sage: zex.ntaus()
|
|
{(A vertex at (0, 7),): 7,
|
|
(A vertex at (0, 7), A vertex at (2, 5)): 7,
|
|
(A vertex at (2, 5),): 1,
|
|
(A vertex at (2, 5), A vertex at (5, 3)): 19,
|
|
(A vertex at (5, 3),): 1}
|
|
"""
|
|
return {tuple(tau.vertices()): ntau(tau)
|
|
for tau in compact_faces(self._Gammaf)}
|
|
|
|
# ------------------------AUXILIARY FUNCTIONS------------------------
|
|
|
|
|
|
# NEWTON'S POLYHEDRON
|
|
def newton_polyhedron(f):
|
|
r"""
|
|
Construct an object ``Polyhedra`` that represents the local Newton's
|
|
Polyhedron `\Gamma(f)` of the polynomial `f`.
|
|
"""
|
|
return Polyhedron(vertices=f.exponents(),
|
|
rays=VectorSpace(QQ, f.parent().ngens()).basis())
|
|
|
|
|
|
def proper_faces(P):
|
|
r"""
|
|
Return a list with the proper faces of the polyhedron ``P`` sorted
|
|
in increasing order dimension.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, proper_faces
|
|
sage: R.<x, y, z> = QQ[]
|
|
sage: f1 = x^3 + y^3 + z^4
|
|
sage: P = newton_polyhedron(f1)
|
|
sage: proper_faces(P)
|
|
[A 0-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex,
|
|
A 0-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex,
|
|
A 0-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex and 1 ray,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex and 1 ray,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex and 1 ray,
|
|
A 2-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices and 2 rays,
|
|
A 2-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 3 vertices,
|
|
A 2-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices and 2 rays,
|
|
A 2-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices and 2 rays]
|
|
"""
|
|
return flatten(P.face_lattice().level_sets()[1:-1])
|
|
|
|
|
|
# Information about faces
|
|
def compact_faces(P):
|
|
r"""
|
|
Return a list with the compact faces of the polyhedron ``P``
|
|
sorted in increasing dimension order.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, compact_faces
|
|
sage: R.<x, y, z> = QQ[]
|
|
sage: f1 = x^3 + y^3 + z^4
|
|
sage: P = newton_polyhedron(f1)
|
|
sage: compact_faces(P)
|
|
[A 0-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex,
|
|
A 0-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex,
|
|
A 0-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 2-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 3 vertices]
|
|
"""
|
|
return [face for face in proper_faces(P) if not face.rays()]
|
|
|
|
|
|
def vertices(tau):
|
|
r"""
|
|
Return a list with the vertices of the face ``tau``.
|
|
"""
|
|
return [i.vector() for i in tau.ambient_Vrepresentation() if i.is_vertex()]
|
|
|
|
|
|
def rays(tau):
|
|
r"""
|
|
Return a list with the rays of the face ``tau``.
|
|
"""
|
|
return [i.vector() for i in tau.ambient_Vrepresentation() if i.is_ray()]
|
|
|
|
|
|
def translate_points(points_list):
|
|
r"""
|
|
Return a list of re-parametrized points by affine translation
|
|
consider the first point in ``points_list`` as the origin.
|
|
"""
|
|
origin = points_list[0]
|
|
return [v - origin for v in points_list]
|
|
|
|
|
|
def facet_info(f, facet):
|
|
r"""
|
|
Return a string with the inequality which define the facet,
|
|
written in the form
|
|
|
|
.. math::
|
|
|
|
a_1 x_1 + a_2 x_2 + ... + a_n x_n + b \geq 0.
|
|
|
|
"""
|
|
rep = facet.ambient_Hrepresentation()[0]
|
|
message = str(vector(rep.A()).dot_product(vector(f.parent().gens())) +
|
|
rep.b())
|
|
message += " >= 0"
|
|
return message
|
|
|
|
|
|
def give_all_facets_info(f, P, compact=False):
|
|
r"""
|
|
Print a relation of facets in ``P`` and their associated inequalities.
|
|
"""
|
|
for i, facet in enumerate(P.faces(P.dim() - 1)):
|
|
if not (compact and facet.rays()):
|
|
print(" Facet {}: {}".format(i, facet_info(f, facet)))
|
|
|
|
|
|
def face_info_output(tau):
|
|
r"""
|
|
Return a string containing a description of vertices and rays in
|
|
face `\tau`.
|
|
"""
|
|
info = "dim " + str(tau.dim()) + ", vertices = "
|
|
info += str(vertices(tau)) + ", rays = " + str(rays(tau))
|
|
return info
|
|
|
|
|
|
# Relations Polyhedron-points
|
|
def support_points_in_face(f, tau):
|
|
r"""
|
|
Return a list of support points of ``f`` contained in the face ``tau``.
|
|
"""
|
|
return [i for i in f.exponents() if tau.as_polyhedron().contains(i)]
|
|
|
|
|
|
# CONES, FANS AND SIMPLE CONES
|
|
def primitivize(v):
|
|
r"""
|
|
Return a linearly dependent vector with ``v`` whose components are
|
|
integers whose greatest common divisor is 1.
|
|
"""
|
|
return v / gcd(v)
|
|
|
|
|
|
def primitive_vectors(tau):
|
|
r"""
|
|
Return a list of primitive vectors of a face: primitive normal
|
|
vectors to the hyperplanes defining `\tau`.
|
|
"""
|
|
return [primitivize(i.A()) for i in tau.ambient_Hrepresentation()]
|
|
|
|
|
|
def cone_from_face(tau):
|
|
r"""
|
|
Construct an object ``Cone`` which represents the dual cone of the
|
|
face ``tau``.
|
|
|
|
.. NOTE::
|
|
|
|
For the total face, it gives a cone generated by the zero vector.
|
|
"""
|
|
gens = primitive_vectors(tau)
|
|
if not gens:
|
|
return Cone([vertices(tau)[0].parent()(0)])
|
|
return Cone(gens)
|
|
|
|
|
|
def primitive_vectors_cone(cone):
|
|
r"""
|
|
Return a list of primitive rays generators of ``cone``.
|
|
"""
|
|
return [primitivize(i.sparse_vector()) for i in cone.rays()]
|
|
|
|
|
|
def all_cones(P):
|
|
r"""
|
|
Return a list with all the cones generated by the faces of ``P``.
|
|
"""
|
|
return [cone_from_face(f) for f in P.face_lattice()[1:]]
|
|
|
|
|
|
def fan_all_cones(P):
|
|
r"""
|
|
Construct an object ``Fan`` representing the fan of all associated
|
|
cones of ``P``.
|
|
"""
|
|
return Fan(all_cones(P), discard_faces=True)
|
|
|
|
|
|
def same_facet(lcone_gens, p, bcone_gens):
|
|
r"""
|
|
Check if ``lcone_gens`` (a cone represented by its generators)
|
|
and the fixed point ``p`` belongs to the same facet of
|
|
``bcone_gens``.
|
|
"""
|
|
for face_cone in Cone(bcone_gens).facets():
|
|
rays_lcone = set(map(tuple, lcone_gens))
|
|
rays_face = set(map(tuple, primitive_vectors_cone(face_cone)))
|
|
if ({tuple(p)}.union(rays_lcone)).issubset(rays_face):
|
|
return True
|
|
return False
|
|
|
|
|
|
def simplicial_partition(cone):
|
|
r"""
|
|
Return a list with the subcones which forms the simplicial
|
|
partition of ``cone``.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, compact_faces, cone_from_face
|
|
sage: from sage.schemes.generic.igusa_top_zeta import simplicial_partition, proper_faces, face_divisors
|
|
sage: R.<x, y, z> = QQ[]
|
|
sage: g = x*y + z^3
|
|
sage: P = newton_polyhedron(g)
|
|
sage: tau = compact_faces(P)[0]
|
|
sage: c = cone_from_face(tau)
|
|
sage: simplicial_partition(c)
|
|
[2-d cone in 3-d lattice N,
|
|
3-d cone in 3-d lattice N,
|
|
3-d cone in 3-d lattice N]
|
|
|
|
sage: x,y,z = polygens(QQ,'x,y,z')
|
|
sage: f=x^2 + y*z
|
|
sage: P=newton_polyhedron(f)
|
|
sage: faces_set = proper_faces(P)
|
|
sage: faces_set = face_divisors(1, faces_set)
|
|
sage: tau = faces_set[1]
|
|
sage: c = cone_from_face(tau)
|
|
sage: simplicial_partition(c) # BUG HERE
|
|
[2-d cone in 3-d lattice N,
|
|
3-d cone in 3-d lattice N,
|
|
3-d cone in 3-d lattice N]
|
|
"""
|
|
if cone.is_simplicial():
|
|
return [cone]
|
|
|
|
dict_ = {}
|
|
F = Fan([cone])
|
|
P = F.cone_lattice()
|
|
|
|
# Ordered list of subcones by increasing dimension
|
|
list_subcones = [ls for i in range(1, F.dim() + 1)
|
|
for ls in F.cones(i)]
|
|
|
|
for subcone in list_subcones:
|
|
if subcone.is_simplicial():
|
|
dict_[subcone] = [set(subcone.rays())]
|
|
else:
|
|
partition = []
|
|
fixpoint = subcone.rays()[0]
|
|
for subsubcone in list_subcones:
|
|
if (P.is_less_than(subsubcone, subcone) and
|
|
not same_facet(subsubcone.rays(), fixpoint,
|
|
subcone.rays())):
|
|
for part in dict_[subsubcone]:
|
|
partition += [part.union({fixpoint})]
|
|
dict_[subcone] = partition
|
|
return [Cone(c) for c in dict_[list_subcones[-1]]]
|
|
|
|
|
|
def cone_info_output(cone, fan_simplicial=None):
|
|
r"""
|
|
Return a string containing information about the generators of the
|
|
cone and its simplicial partition.
|
|
|
|
- ``fan_simplicial`` -- a fan with the simplicial partition of ``cone``
|
|
(if there is already calculated).
|
|
"""
|
|
F = fan_simplicial
|
|
if F is None:
|
|
F = simplicial_partition(cone)
|
|
info = "cone generators = " + str(primitive_vectors_cone(cone))
|
|
info += ", partition into simplicial cones = "
|
|
info += str(list(map(primitive_vectors_cone, F)))
|
|
return info
|
|
|
|
|
|
def integral_vectors(scone):
|
|
r"""
|
|
Return a list of integral vectors contained in
|
|
|
|
.. MATH::
|
|
|
|
\left\{\sum \lambda_j a_j \mid 0\leq \lambda_j <1\right\}
|
|
|
|
where `\{a_j\}` is a basis of the simple cone ``scone``.
|
|
"""
|
|
origin = VectorSpace(QQ, scone.lattice_dim()).zero()
|
|
if not scone.dim():
|
|
integrals = [origin]
|
|
else:
|
|
cone_gens = primitive_vectors_cone(scone)
|
|
ngens = len(cone_gens)
|
|
A = matrix(ZZ, cone_gens).transpose()
|
|
D, _, V = A.smith_form()
|
|
diag = D.diagonal()
|
|
coords = mrange(diag, vector)
|
|
|
|
# Aux function to scale the vectors on the list
|
|
def escale(v):
|
|
v = vector(QQ, v)
|
|
for i in range(ngens):
|
|
if diag[i] != 0:
|
|
v[i] = v[i] / diag[i]
|
|
else:
|
|
v[i] = 0
|
|
return v
|
|
|
|
# Aux function 'floor' for vectors component by component
|
|
def floor(v):
|
|
for i in range(ngens):
|
|
v[i] = v[i] - v[i].floor()
|
|
return v
|
|
# Now, we scale and we return to the canonical basis
|
|
L = map(lambda v: V * v, map(escale, coords))
|
|
# Finally, we find the integral vectors of own region
|
|
integrals = map(lambda v: matrix(QQ, A) * v, list(map(floor, L)))
|
|
return list(integrals)
|
|
|
|
|
|
def multiplicity(scone):
|
|
r"""
|
|
Return the multiplicity of a simple cone.
|
|
"""
|
|
L = primitive_vectors_cone(scone)
|
|
A = matrix(ZZ, L)
|
|
S = A.smith_form()[0]
|
|
return prod(S[i, i] for i in range(len(L)))
|
|
|
|
|
|
# Sigma and m functions defined in the loc. cit. article
|
|
def sigma_vect(v, weights=None):
|
|
r"""
|
|
Return the weighted sum of the components of ``v`` by ``weights``.
|
|
"""
|
|
if weights is None:
|
|
return sum(v)
|
|
return vector(v).dot_product(vector(weights))
|
|
|
|
|
|
def m_vect(v, P):
|
|
r"""
|
|
Return `m(v):=\min\{v\cdot x \mid x\in P\}` where ``v`` is
|
|
a vector and ``P`` is a ``Polyhedra`` in the affine space.
|
|
"""
|
|
vrat = vector(QQ, v)
|
|
return min(vrat.dot_product(x.vector()) for x in P.vertices())
|
|
|
|
|
|
# MONOMIALS ASSOCIATED TO A FACE
|
|
def ftau(f, tau):
|
|
r"""
|
|
Return the polynomial `f_{\tau}` associated to the face `\tau` of
|
|
the Newton's Polyhedron of `f`.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, ftau
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f1 = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f1)
|
|
sage: fa = P.faces(2)[1]
|
|
sage: ftau(f1, fa)
|
|
z^3 - y^2
|
|
sage: [ftau(f1, tau) for tau in P.faces(2)]
|
|
[z^3 + x^2 - y^2, z^3 - y^2, x^2 - y^2, z^3 + x^2]
|
|
"""
|
|
from sage.rings.polynomial.polydict import ETuple
|
|
# We take a dictionary between the exponents and the coefficients
|
|
# of monomials.
|
|
D = f.dict()
|
|
ring = f.parent()
|
|
vars_ = ring.gens()
|
|
g = ring.zero()
|
|
for v in support_points_in_face(f, tau):
|
|
g += D[ETuple(v)] * prod(var ** v[i] for i, var in enumerate(vars_))
|
|
return g
|
|
|
|
|
|
def solve_in_Fp_x(f, p):
|
|
r"""
|
|
For ``f`` be an integral polynomial, returns the list `[ \{a\in
|
|
(\mathbb{F}_p\setminus\{0\})^d \mid f^*(a)=0\}, \{` vars of `f^*\}]` where
|
|
`f^*` is `f` with coefficients in \ `\mathbb{F}_p`, for a
|
|
given prime number ``p``.
|
|
"""
|
|
g = f.change_ring(GF(p)) # We can lose variables in GF(p)
|
|
vars_ = g.variables()
|
|
nvars = g.nvariables()
|
|
h = (GF(p)[vars_])(g)
|
|
if len(h.exponents()) == 1:
|
|
sols = [] # If f_tau is a monomial
|
|
else:
|
|
Fp_x_nvars = list(Tuples(range(1, p), nvars)) # (Fp-0)^nvars
|
|
if h == 0:
|
|
return [Fp_x_nvars, vars_]
|
|
sols = [a for a in Fp_x_nvars if h(tuple(a)) == 0]
|
|
return sols, vars_
|
|
|
|
|
|
def is_degenerated(f_tau, p=None, method='default'):
|
|
r"""
|
|
Checks if the polynomial ``f_tau`` is degenerated over
|
|
`\mathbb{F}_p`, for `p` a given prime number (see [DH01]_).
|
|
|
|
If ``p = None``, checks degeneration over `\CC` (which is
|
|
equivalent to be degenerated over `\mathbb{F}_p` with `p\gg 0`).
|
|
|
|
For finite fields (``p`` is a given prime):
|
|
|
|
- ``method = 'default'`` checks the condition using evaluation over the \
|
|
`(\mathbb{F}_p\setminus\{0\})^n` in the system of equations.
|
|
|
|
- ``method = 'ideals'`` checks the condition using ideals over
|
|
the finite field.
|
|
"""
|
|
bool_ = False
|
|
S = f_tau.parent()
|
|
id = f_tau.jacobian_ideal() + S.ideal(f_tau)
|
|
if p not in ZZ:
|
|
bool_ = prod(S.gens()) not in id.radical()
|
|
else:
|
|
if method == 'ideals':
|
|
for xi in S.gens():
|
|
id += S * (xi ** (p - 1) - 1)
|
|
# xi unity in Fp iff xi^{(p-1)-1}=0
|
|
bool_ = 1 not in id
|
|
# True if id is NOT the ring (ie, sist. has a solution)
|
|
else:
|
|
candidates, vars_ = solve_in_Fp_x(f_tau, p)
|
|
if not vars_:
|
|
bool_ = True
|
|
else:
|
|
S = GF(p)[vars_]
|
|
g = f_tau.change_ring(GF(p))
|
|
for xi in S.gens():
|
|
df_tau = S(g).derivative(xi)
|
|
candidates = [a for a in candidates
|
|
if df_tau(tuple(a)) == 0]
|
|
if candidates:
|
|
bool_ = True
|
|
return bool_
|
|
|
|
|
|
# IGUSA ZETA FUNCTION
|
|
# Values Ntau, Ltau and Stau defined in paper [DH01]
|
|
def Ntau(f, tau, p):
|
|
r"""
|
|
Return the number
|
|
|
|
.. math::
|
|
N_{\tau} = \#\{a\in(\mathbb{F}_p \setminus \{0\})^d
|
|
\mid f^*_{\tau}(a)=0\}
|
|
|
|
with `f^*_{\tau}=\mathbb{F}_p(f_{\tau})`
|
|
for a given face `\tau` and `p` a given prime number (see [DH01]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, Ntau
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: [Ntau(f,tau,5) for tau in P.faces(1)]
|
|
[16, 32, 16, 0, 0, 0]
|
|
"""
|
|
n = f.parent().ngens()
|
|
f_tau = ftau(f, tau)
|
|
if p not in ZZ:
|
|
print("You must to give a 'Dictionary' with the number of " +
|
|
"solutions in GF(" + str(p) + ")^" + str(n) +
|
|
" associated to each face.")
|
|
else:
|
|
sols, vars = solve_in_Fp_x(f_tau, p)
|
|
return len(sols) * (p - 1) ** (n - len(vars))
|
|
|
|
|
|
def Ltau(f, tau, p, abs_Ntau, s):
|
|
r"""
|
|
Return a list `[L_{\tau}, N_{\tau}]` in terms of a symbolic
|
|
variable `s`.
|
|
|
|
If `p` is an abstract prime number, ``abs_Ntau`` is the given
|
|
symbolic expression of `N_{\tau}` in this case (see [DH01]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, Ltau
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: s = var('s')
|
|
sage: [Ltau(f,tau,5,0,s) for tau in P.faces(1)]
|
|
[(16/125*(3*5^(s + 1) + 1)/(5^(s + 1) - 1), 16),
|
|
(32/125*(5^(s + 1) + 3)/(5^(s + 1) - 1), 32),
|
|
(16/125*(3*5^(s + 1) + 1)/(5^(s + 1) - 1), 16),
|
|
(64/125, 0),
|
|
(64/125, 0),
|
|
(64/125, 0)]
|
|
"""
|
|
if p not in ZZ:
|
|
N_tau = abs_Ntau
|
|
else:
|
|
N_tau = Ntau(f, tau, p)
|
|
|
|
n = f.parent().ngens()
|
|
p_power_s = p ** s
|
|
# p_power_s = var('Z')
|
|
u = ((p - 1) ** n - p * N_tau * ((p_power_s - 1) / (p_power_s * p - 1)))
|
|
result = p ** (-n) * u
|
|
return result.factor(), N_tau
|
|
|
|
|
|
def Lgamma(f, p, abs_Ngamma, s):
|
|
r"""
|
|
Return the value `L_{\Gamma}` for the total polyhedron `\Gamma` in
|
|
terms of a symbolic variable `s`.
|
|
|
|
``abs_Ngamma`` is the corresponding ``Ngamma`` value for abstract
|
|
prime ``p`` (see [DH01]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, Lgamma
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: s = var('s')
|
|
sage: Lgamma(f,7,0,s)
|
|
-24/49*(7^s - 1)/(7*7^s - 1) + 216/343
|
|
"""
|
|
n = f.parent().ngens()
|
|
s = SR(s)
|
|
if p not in ZZ:
|
|
N_gamma = abs_Ngamma
|
|
p = SR(p)
|
|
else:
|
|
sols, vars_ = solve_in_Fp_x(f, p)
|
|
N_gamma = len(sols) * (p - 1) ** (n - len(vars_))
|
|
p_power_s = p ** s
|
|
# p_power_s = var('Z')
|
|
u = ((p - 1) ** n - p * N_gamma * ((p_power_s - 1) / (p_power_s * p - 1)))
|
|
return p ** (-n) * u
|
|
|
|
|
|
def Stau(f, tau, p, weights, s):
|
|
r"""
|
|
Return a list ``[S_tau, cone_info]`` with ``cone_info`` containing
|
|
a string of information about the cones, simplicial partition,
|
|
multiplicity and integral points (see [DH01]_).
|
|
|
|
Value ``S_tau`` is expressed in terms of a symbolic variable ``s``.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, Stau
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: p, s = var('p, s')
|
|
sage: r = Stau(f, P.faces(1)[0], p, 3 * [1], s)[0]
|
|
sage: bool(r == 1/((p - 1)*(p^(6*s + 8) - 1)))
|
|
True
|
|
sage: Stau(f, P.faces(1)[0], 7, 3 * [1], s)[0]
|
|
1/6/(7^(6*s + 8) - 1)
|
|
"""
|
|
c = cone_from_face(tau)
|
|
F = simplicial_partition(c)
|
|
result = 0
|
|
for scone in F:
|
|
num = 0
|
|
den = 1
|
|
for h in integral_vectors(scone):
|
|
num += p ** (sigma_vect(h, weights) + m_vect(h, tau) * s)
|
|
for a in primitive_vectors_cone(scone):
|
|
den *= (p ** (sigma_vect(a, weights) + m_vect(a, tau) * s) - 1)
|
|
result += num / den
|
|
# result = factor(simplify(expand(result + num/den)))
|
|
info = cone_info_output(c, F) + "\n" + "multiplicities = "
|
|
info += str(list(map(multiplicity, F))) + ", integral points = "
|
|
info += str(list(map(integral_vectors, F)))
|
|
return result, info
|
|
|
|
|
|
# TOPOLOGICAL ZETA FUNCTION
|
|
# Calculation of the expression Jtau defined in paper [DL92]
|
|
def Jtau(tau, weights, ring_s):
|
|
r"""
|
|
Return a list ``[J_tau, cone_info]`` with ``cone_info`` containing a
|
|
string of information about the cones, simplicial partition,
|
|
multiplicity and integral points. (see [DL92]_)
|
|
|
|
Value ``J_tau`` is a rational function in ``s``.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, proper_faces, face_divisors, Jtau
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f=x^2 + y*z
|
|
sage: S.<s> = QQ[]
|
|
sage: P=newton_polyhedron(f)
|
|
sage: faces_set = proper_faces(P)
|
|
sage: faces_set = face_divisors(1, faces_set)
|
|
sage: Jtau(faces_set[1], None, S)[0]
|
|
(1/2) * (s + 3/2)^-2 * (s + 5/2)
|
|
"""
|
|
s = ring_s.gen(0)
|
|
c = cone_from_face(tau)
|
|
dim_cone = c.dim()
|
|
F = simplicial_partition(c)
|
|
if dim_cone == 0:
|
|
result = 1
|
|
else:
|
|
result = 0
|
|
for scone in F:
|
|
if scone.dim() == dim_cone:
|
|
num = multiplicity(scone)
|
|
den = 1
|
|
for a in primitive_vectors_cone(scone):
|
|
den *= (m_vect(a, tau) * s + sigma_vect(a, weights))
|
|
result = result + num / den
|
|
result = result.factor()
|
|
cone_info = cone_info_output(c, F) + "\n" + "multiplicities = "
|
|
cone_info += str(list(map(multiplicity, F))) + ", integral points = "
|
|
cone_info += str(list(map(integral_vectors, F)))
|
|
return result, cone_info
|
|
|
|
|
|
# MONODROMY ZETA FUNCTION
|
|
# Calculation of the expression Mtau defined in [Var76]
|
|
def Mtau(tau):
|
|
r"""
|
|
Return the value `M_{\tau}` (the monodromy zeta factor associated
|
|
to a face) for `\tau` a face in `P` as a polynomial in `t`.
|
|
|
|
INPUT:
|
|
|
|
- ``tau`` -- a face of a Newton polyhedron ``P``
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, Mtau, compact_faces
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: [Mtau(tau) for tau in P.faces(1)]
|
|
[-t^6 + 1, -t^2 + 1, -t^6 + 1, 1, 1, 1]
|
|
|
|
sage: g = x*y + z^3
|
|
sage: P = newton_polyhedron(g)
|
|
sage: [Mtau(tau) for tau in P.faces(1)]
|
|
[1, -t + 1, -t^3 + 1, -t + 1, -t^3 + 1, 1]
|
|
sage: [Mtau(tau) for tau in compact_faces(P)]
|
|
[-t^3 + 1, 1, 1]
|
|
"""
|
|
ring_t = PolynomialRing(QQ, 't')
|
|
t = ring_t.gen(0)
|
|
ring_s = PolynomialRing(QQ, 's')
|
|
s = ring_s.gen(0)
|
|
|
|
c = cone_from_face(tau)
|
|
dim_cone = c.dim()
|
|
|
|
result = ring_t.one()
|
|
for scone in simplicial_partition(c):
|
|
if scone.dim() == dim_cone:
|
|
mult = multiplicity(scone)
|
|
den = ring_s.one()
|
|
for a in primitive_vectors_cone(scone):
|
|
den *= (m_vect(a, tau) * s + sum(a))
|
|
if den.degree() == 1:
|
|
M = s * den.subs(s=~s)
|
|
result *= (1 - t ** (M.subs(s=0) / mult))
|
|
return result
|
|
|
|
|
|
def face_volume(f, tau):
|
|
r"""
|
|
Return the value `\operatorname{Vol}(\tau)\cdot(\dim\tau)!`, for
|
|
a given face ``tau`` .
|
|
|
|
`\operatorname{Vol}(\tau)` is defined as follows:
|
|
|
|
Let `\omega_\tau` be the volume form over `\operatorname{Aff}(\tau)`,
|
|
the affine space generated \ by `\tau` such that the
|
|
parallelepiped spanned by a lattice basis of
|
|
`\operatorname{Aff}(\tau)\cap\ZZ^n` has volume 1. Then
|
|
`\operatorname{Vol}(\tau)` is the volume of `\tau` intersection the
|
|
Global Newton \ Polyhedron of `f` with respect to `\omega_\tau`
|
|
(see [DL92]_).
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, face_volume
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: [face_volume(f, tau) for tau in P.faces(1)]
|
|
[1, 2, 1, 0, 0, 0]
|
|
"""
|
|
n = f.parent().ngens()
|
|
dim_tau = tau.dim()
|
|
result = 0
|
|
if dim_tau:
|
|
tau_in_global = Polyhedron(vertices=support_points_in_face(f, tau))
|
|
vertices_in_global = [vector(v) for v in tau_in_global.vertices()]
|
|
trans_vertices = translate_points(vertices_in_global)
|
|
if matrix(ZZ, trans_vertices).rank() == dim_tau:
|
|
V = QQ ** n
|
|
aff = V.submodule(trans_vertices).intersection(ZZ ** n)
|
|
basis_aff = aff.basis()
|
|
W = V.submodule_with_basis(basis_aff)
|
|
coords_list = list(map(W.coordinate_vector, trans_vertices))
|
|
p = PointConfiguration(coords_list)
|
|
result = p.volume() # Return dimtau!*n-volume of tau
|
|
else:
|
|
result = 1
|
|
return result
|
|
|
|
|
|
def ntau(tau):
|
|
r"""
|
|
Return `m(\Delta_\tau) = \gcd\{m(a) \mid a\in\Delta_\tau\cap\ZZ^n\}`
|
|
where `\Delta_\tau` is the associated cone of `\tau`.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, ntau
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: tau = P.faces(1)[0]
|
|
sage: ntau(tau)
|
|
6
|
|
"""
|
|
c = cone_from_face(tau)
|
|
F = simplicial_partition(c)
|
|
L_vectors = []
|
|
# We need to evaluate m over the basis of the cone and the
|
|
# integral points views above.
|
|
for scone in F:
|
|
L_vectors += integral_vectors(scone)
|
|
L_vectors += primitive_vectors_cone(scone)
|
|
return gcd(m_vect(i, tau) for i in L_vectors)
|
|
|
|
|
|
def face_divisors(d, faces_set):
|
|
r"""
|
|
Return a list of faces `\tau` in ``faces_set`` such that ``d`` divides
|
|
`m(\Delta_\tau) = \gcd\{m(a) \mid a\in\Delta_\tau\cap\ZZ^n\}`
|
|
where `\Delta_\tau` is the associated cone of `\tau`.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import newton_polyhedron, face_divisors
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: P = newton_polyhedron(f)
|
|
sage: face_divisors(3, P.faces(1))
|
|
[A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 2 vertices,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex and 1 ray,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex and 1 ray,
|
|
A 1-dimensional face of a Polyhedron in QQ^3 defined
|
|
as the convex hull of 1 vertex and 1 ray]
|
|
"""
|
|
if d == 1:
|
|
return faces_set
|
|
return [tau for tau in faces_set if not ntau(tau) % d]
|
|
|
|
|
|
def is_global_degenerated(f, p=None, info=False, method='default'):
|
|
r"""
|
|
Check if the polynomial ``f`` is degenerated over `\mathbb{F}_p`
|
|
(`p` prime) with respect to the faces of the Global Newton
|
|
Polyhedron of ``f`` (see [DL92]_).
|
|
|
|
If ``p = None``, checks degeneration over `\CC` (which is equivalent to be
|
|
degenerated over `\mathbb{F}_p` with `p\gg 0`).
|
|
|
|
``local = True`` checks degeneration for local case (only with respect the
|
|
compact faces).
|
|
|
|
For finite fields (`p` is a given prime):
|
|
|
|
- ``method = 'default'`` checks the condition using evaluation over
|
|
`(\mathbb{F}_p\setminus\{0\})^n` in the system of equations.
|
|
|
|
- ``method = 'ideals'`` checks the condition using ideals over the
|
|
finite field.
|
|
|
|
EXAMPLES::
|
|
|
|
sage: from sage.schemes.generic.igusa_top_zeta import is_global_degenerated
|
|
sage: R.<x,y,z> = QQ[]
|
|
sage: f = x^2 - y^2 + z^3
|
|
sage: is_global_degenerated(f)
|
|
False
|
|
"""
|
|
Q = f.newton_polytope() # Global Newton Polyhedron of f
|
|
|
|
for tau in Q.face_lattice()[1:]:
|
|
f_tau = ftau(f, tau)
|
|
if is_degenerated(f_tau, p, method):
|
|
if info:
|
|
if p not in ZZ:
|
|
print("The polynomial is degenerated at least " +
|
|
"with respect to the face tau = {" +
|
|
face_info_output(tau) +
|
|
"} over the complex numbers!")
|
|
else:
|
|
print("The polynomial is degenerated at least " +
|
|
"with respect to the face tau = {" +
|
|
face_info_output(tau) + "} over GF(" + str(p) + ")!")
|
|
return True
|
|
return False
|