Zeta – computing zeta functions of groups, algebras, and modules

Introduction

Zeta provides methods for computing local and topological zeta functions arising from the enumeration of subalgebras, ideals, submodules, and representations of suitable algebraic structures as well as some other types of zeta functions. For theoretical background and descriptions of the methods used, see References.

This package is an experimental fork of Zeta, turning it into a pip-installable SageMath package.

Zeta is distributed as a Python-package for the computer algebra system SageMath. In addition to Singular and other software included with Sage, Zeta also relies on LattE integrale and Normaliz.

This work is supported by the Alexander von Humboldt-Foundation. From 2013–2016, the development of Zeta was supported by the DFG Priority Programme “Algorithmic and Experimental Methods in Algebra, Geometry and Number Theory”.

Please also check the original homepage of Zeta by Tobias Rossmann.

Installation

Dependencies

We assume SageMath version 8.3, or higher, is used.

The wheel packaging standard is needed at installation time. It can be installed by running:

$ sage -pip install wheel

Zeta will try to invoke the programs count (a part of LattE integrale) and normaliz (a part of Normaliz). They can be installed by running:

$ sage -i latte_int
$ sage -i normaliz

If you want to use your own versions of these progrmas, you can set the variables zetalib.common.count and zetalib.common.normaliz for the desired paths, respectively.

Older versions of Zeta required a patched version of count. To that end, the file latte-int-1.7.3/code/latte/genFunction/maple.cpp in the sources of LattE integrale 1.7.3 should be replaced by the file maple.cpp included with Zeta. In order to compile the patched version of LattE integrale 1.7.3 from scratch, you may want to use this modified version (26M) of the LattE integrale 1.7.3 bundle. Compiled versions of normaliz, count and scdd_gmp were included in the bin directory for linux-x86_64 until version 0.4.0.

Install from PyPI

The easiest way to obtain Zeta is to run:

$ sage -pip install zetalib

Local install from source

Download the source from the git repository:

$ git clone https://gitlab.com/mathzeta2/zetalib.git

For convenience this package contains a Makefile with some often used commands. To build the C extensions, install and test you should change to the root directory and run:

$ make

Alternatively, you can do it in separate steps:

$ make build
$ make test
$ sage -pip install --upgrade --no-index -v . # or `make install`

To uninstall you can run:

$ sage -pip uninstall zetalib # or `make uninstall`

If you want to use another version of SageMath you have installed, you can modify the SAGE variable when calling make:

$ make SAGE=/path/to/sage build

Build documentation

The source files of the documentation are located in the docs/source directory, and are written in Sage’s Sphinx format.

Generate the HTML documentation by running:

$ cd docs
$ sage -sh -c "make html"

Or using the shorthand:

$ make doc

Then open docs/build/html/index.html in your browser.

Packaging

All packaging setup is internally done through setup.py. To create a “source package” run:

$ sage setup.py sdist

To create a binary wheel package run:

$ sage setup.py bdist_wheel

Or use the shorthand:

$ make build_wheel

Basic usage

Creating algebras

By an algebra, we mean a free \(\mathbf{Z}\)-module of finite rank endowed with a biadditive multiplication; we do not require this multiplication to be associative or Lie. Given a \(\mathbf Z\)-basis \(x_1,\dotsc,x_d\) of an algebra \(L\), define \(\alpha_{ije}\in \mathbf Z\) by

\[x_i x_j = \sum_{e=1}^d \alpha_{ije} x_e.\]

The numbers \(\alpha_{ije}\) are the structure constants of \(L\) with respect to the chosen basis \((x_1,\dotsc,x_d)\). The principal method for specifying an algebra in Zeta is to provide structure constants as a nested list

\[\begin{split}\begin{matrix} [[ (\alpha_{111},\dotsc,\alpha_{11d}), & \dotsc & (\alpha_{1d1},\dotsc,\alpha_{1dd}) ]\phantom], \\ \vdots & & \vdots \\ \phantom[[ (\alpha_{d11},\dotsc,\alpha_{d1d}), & \dotsc & (\alpha_{dd1},\dotsc,\alpha_{ddd}) ]] \\ \end{matrix}\end{split}\]

as the first argument of zetalib.Algebra. (We note that the table of structure constants of an instance of zetalib.Algebra is stored in the table attribute.)

Computing topological zeta functions

Given an algebra obtained via zetalib.Algebra, the function zetalib.topological_zeta_function can be used to attempt to compute an associated topological zeta function. Specifically, zetalib.topological_zeta_function(L, 'subalgebras') will attempt to compute the topological subalgebra zeta function of \(L\) as a rational function in \(s\), while zetalib.topological_zeta_function(L, 'ideals') will do the same for ideals. If \(L\) is a nilpotent Lie algebra, then zetalib.topological_zeta_function(L, 'reps') will attempt to compute the topological representation zeta function of the unipotent algebraic group over \(\mathbf Q\) corresponding to \(L\otimes_{\mathbf Z} \mathbf Q\).

In general, such computations are not guaranteed to succeed. If the method for computing topological zeta functions from [Ro2015a, Ro2015b] (for subalgebras and ideals) or [Ro2016] (for representations) fails, zetalib.topological_zeta_function will raise an exception of type zetalib.ReductionError. Disregarding bugs in Zeta, Sage, or elsewhere, whenever zetalib.topological_zeta_function does finish successfully, its output is supposed to be correct.

Example (subalgebras and ideals)

To illustrate the computation of topological subobject zeta functions, consider the commutative algebra \(L = \mathbf Z[X]/X^3\). As a \(\mathbf Z\)-basis of \(L\), we choose \((1,x,x^2)\), where \(x\) is the image of \(X\) in \(L\). The associated nested list of structure constants is

\[\begin{split}\begin{matrix} [[(1, 0, 0), & (0, 1, 0), & (0, 0, 1)]\phantom],\\ \phantom[ [(0, 1, 0), & (0, 0, 1), & (0, 0, 0)]\phantom],\\ \phantom[[(0, 0, 1), & (0, 0, 0), & (0, 0, 0)]]. \end{matrix}\end{split}\]

The following documents a complete Sage session leading to the computation of the topological subalgebra and ideal zeta functions of \(L\).

sage: import zetalib
sage: L = zetalib.Algebra([[(1, 0, 0), (0, 1, 0), (0, 0, 1)], [(0, 1, 0), (0, 0,1), (0, 0, 0)], [(0, 0, 1), (0, 0, 0), (0, 0, 0)]])
sage: zetalib.topological_zeta_function(L, 'subalgebras')
2*(15*s - 8)/((5*s - 4)*(3*s - 2)^2*s)
sage: zetalib.topological_zeta_function(L, 'ideals')
1/((3*s - 2)*(2*s - 1)*s)

Example (representations)

We illustrate the computation of topological representation zeta functions of unipotent algebraic groups (over \(\mathbf Q\)) using the familiar example of the Heisenberg group \(\mathbf H\). The first step is to construct a \(\mathbf Z\)-form of its Lie algebra. We choose the natural \(\mathbf Z\)-form \(L = \mathbf Z x_1 \oplus \mathbf Z x_2 \oplus \mathbf Z x_3\) with \([x_1,x_2] = x_3\), \([x_2,x_1] = -x_3\) and \([x_i,x_j] = 0\) in the remaining cases. The list of structure constants of \(L\) with respect to the basis \((x_1,x_2,x_3)\) is

\[\begin{split}\begin{matrix} [[(0, 0, \phantom-0), & (0, 0, 1), & (0, 0, 0)]\phantom],\\ \phantom[ [(0, 0, -1), & (0, 0, 0), & (0, 0,0)]\phantom],\\ \phantom[[(0, 0, \phantom-0), & (0, 0, 0), & (0, 0, 0)]]. \end{matrix}\end{split}\]

The following documents a complete Sage session leading to the computation of the topological representation zeta function of \(\mathbf H\).

sage: import zetalib
sage: L = zetalib.Algebra([[(0, 0, 0), (0, 0, 1), (0, 0, 0)], [(0, 0,-1), (0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0), (0, 0, 0)]])
sage: zetalib.topological_zeta_function(L, 'reps')
s/(s - 1)

Computing local zeta functions

Uniform zeta functions

Using most of the same arguments as zetalib.topological_zeta_function from Computing topological zeta functions, the function zetalib.local_zeta_function can be used to attempt to compute generic local subalgebra, ideal, or representation zeta functions—that is to say, computed zeta functions will be valid for all but finitely many primes \(p\) and arbitrary finite extensions of \(\mathbf Q_p\) as in [Ro2015a, §5.2] and [Ro2016, §2.2]. If the method from [Ro2018a] is unable to compute a specific zeta function, an exception of type zetalib.ReductionError will be raised.

By default, zetalib.local_zeta_function will attempt to construct a single rational function, \(W(q,t)\) say, in \((q,t)\) such that for almost all primes \(p\) and all \(q = p^f\) (\(f \ge 1\)), the local zeta function in question obtained after base extension from \(\mathbf Q_p\) to a degree \(f\) extension is given by \(W(q,q^{-s})\). Crucially, such a rational function \(W(q,t)\) need not exist and even if it does, Zeta may be unable to compute it.

Example (uniform local zeta functions)

Let \(L\) be the Heisenberg Lie algebra as above. The following computes the associated generic local subalgebra, ideal, and representation zeta functions.

sage: import zetalib
sage: L = zetalib.Algebra([[(0, 0, 0), (0, 0, 1), (0, 0, 0)], [(0, 0,-1), (0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0), (0, 0, 0)]])
sage: zetalib.local_zeta_function(L, 'subalgebras')
-(q^2*t^2 + q*t + 1)/((q^3*t^2 - 1)*(q*t + 1)*(q*t - 1)*(t - 1))
sage: zetalib.local_zeta_function(L, 'ideals')
-1/((q^2*t^3 - 1)*(q*t - 1)*(t - 1))
sage: zetalib.local_zeta_function(L, 'reps')
(t - 1)/(q*t - 1)

That is, for almost all primes \(p\) and all finite extensions \(K/\mathbf Q_p\), the subalgebra and ideal zeta functions of \(L \otimes \mathfrak O_K\) are exactly the first two rational functions in \(q\) and \(t = q^{-s}\); here, \(\mathfrak O_K\) denotes the valuation ring of \(K\) and \(q\) the residue field size. These results are due to Grunewald, Segal, and Smith and in fact valid for arbitrary \(p\); the restriction to \(K = \mathbf Q_p\) in their work is not essential. Similarly, the above computation using Zeta shows that if \(H \leqslant \mathrm{GL}_3\) is the Heisenberg group scheme, then for almost all primes \(p\) and all finite extensions \(K/\mathbf Q_p\), the representation zeta function of \(H(\mathfrak O_K)\) is \((q^{-s}-1)/(q^{1-s}-1)\), as proved (for all \(p\)) by Stasinski and Voll.

Non-uniform zeta functions: the symbolic mode

Assuming the method from [Ro2018a] applies, Zeta supports limited computations of non-uniform generic local zeta functions—that is, instances where no rational function \(W(q,t)\) as above exists. For that purpose, symbolic=True needs to be passed to zetalib.local_zeta_function. If successful, the output will then be given by a rational function in \(q\), \(t\), and finitely many variables of the form sc_i, each corresponding to the number of rational points over the residue field of \(K\) of (the reduction modulo \(p\) of) the subvariety zetalib.common.symbolic_count_varieties[i] of some algebraic torus.

Example (non-uniform local zeta functions)

Let \(L\) be the Lie algebra with \(\mathbf Z\)-basis \((x_1,\dotsc,x_6)\) and non-trivial commutators \([x_1,x_2] = x_3\), \([x_1,x_3] = x_5\), \([x_1,x_4] = 3x_6\), \([x_2,x_3] = x_6\), and \([x_2,x_4] = x_5\); this algebra is called \(L_{6,24}(3)\) in de Graaf’s classification. We may compute the generic local representation zeta functions associated with \(L\) as follows.

sage: import zetalib
sage: table = [zero_matrix(6,6) for _ in range(6)]
sage: table[0][1,2] = 1; table[1][0,2] = -1
sage: table[0][2,4] = 1; table[2][0,4] = -1
sage: table[0][3,5] = 3; table[3][0,5] = -3
sage: table[1][2,5] = 1; table[2][1,5] = -1
sage: table[1][3,4] = 1; table[3][1,4] = -1
sage: L = zetalib.Algebra(table)
sage: zetalib.local_zeta_function(L, 'reps', symbolic=True)
-(q*sc_0*t - q*t^2 - sc_0*t + 1)*(t - 1)/((q^3*t^2 - 1)*(q*t - 1))
sage: zetalib.common.symbolic_count_varieties[0]
Subvariety of 1-dimensional torus defined by [x^2 - 3]

We thus see how the generic local representation zeta functions associated with \(L\) depend on whether \(3\) is a square in the residue field of \(K\). Calling zetalib.local_zeta_function(L, 'reps') without symbolic=True will result in an error. As computations with symbolic=True are generally substantially more computationally demanding, they should only be attempted as a last resort.

Computing Igusa-type zeta functions

Zeta also provides rudimentary support for the computation of local and topological zeta functions associated with polynomials and polynomial mappings under the non-degeneracy assumptions from [Ro2015a]. Given \(f_1,\dotsc,f_r \in \mathbf Q[X_1,\dotsc,X_n]\), Zeta can be used to attempt to compute the generic local zeta functions (in the sense discussed above) defined by

\[\int_{\mathfrak O_K^n} \lVert f_1(x),\dotsc, f_r(x) \rVert^s_K \mathrm d\mu_K(x)\]

or the associated topological zeta function; here, \(\mu_K\) denotes the Haar measure and \(\lVert \cdotp \rVert_K\) the maximum norm, both normalised as usual.

For a single polynomial, the method used by Zeta is very closely related to combinatorial formulae of Denef and Loeser and Denef and Hoornaert. In order to attempt to compute topological or generic local zeta functions associated with a polynomial (or a polynomial mapping), simply pass a multivariate polynomial (or a list of these) to zetalib.topological_zeta_function or zetalib.local_zeta_function, respectively.

Example (Igusa-type zeta functions)

The following computes the local and topological zeta functions associated with \(f\) and \((f,g)\), where \(f = X^3 - XYZ\) and \(g = X^2 - Y^2\).

sage: import zetalib
sage: R.<x,y,z> = QQ[]
sage: f = x^3 -x*y*z
sage: g = x^2 - y^2
sage: zetalib.local_zeta_function(f)
(q^4 + q^2*t^2 - q^3 - 2*q^2*t - q*t^2 + q^2 + t^2)*(q - 1)/((q^2 + q*t + t^2)*(q - t)^3)
sage: zetalib.topological_zeta_function(f)
1/3*(s^2 + 2*s + 3)/(s + 1)^3
sage: zetalib.local_zeta_function([f,g])
(q^2 + 2*q + t)*(q - 1)^2/((q^2 - t)*(q + t)*(q - t))
sage: zetalib.topological_zeta_function([f,g])
2/((s + 2)*(s + 1))

Non-uniform examples can be handled as in Non-uniform zeta functions: the symbolic mode.

Modules and algebras with operators

In [Ro2015a, Ro2015b], (topological) ideal zeta functions were treated as special cases of submodule zeta functions. In Zeta, we regard modules as special cases of algebras with operators. Namely, each algebra \(L\) in Zeta is endowed with a possibly empty set \(\Omega\) of operators, i.e. \(\Omega\) consists of additive endomorphisms of \(L\). The topological and local subalgebra and ideal zeta functions of \(L\) are always understood to be those arising from the enumeration of \(\Omega\)-invariant subalgebras or ideals, respectively. Thus, if the multiplication of \(L\) is trivial, then the \(\Omega\)-invariant subalgebras (and ideals) of \(L\) are precisely the submodules of \(L\) under the action of the enveloping associative unital ring of \(\Omega\) within \(\mathrm{End}(L)\).

In practice, \(\Omega\) is given by a finite list of matrices (or nested lists of integers representing those matrices) corresponding to the defining basis of \(L\). This list is then supplied to zetalib.Algebra using the keyword parameter operators. For algebras with zero multiplication, instead of entering structure constants, you can provide a keyword argument rank to zetalib.Algebra which initialises all structure constants to zero.

Example (operators)

We illustrate the computation of the topological submodule zeta function arising from the enumeration of sublattices within \(\mathbf Z^3\) invariant under the matrix

\[\begin{split}\begin{bmatrix} 1 & 1 & -1 \\ 0 & 1 & 1 \\ 0 & 0 & 1 \end{bmatrix}\end{split}\]
sage: import zetalib
sage: M = zetalib.Algebra(rank=3, operators=[ [[1,1,-1],[0,1,1],[0,0,1]] ])
sage: zetalib.topological_zeta_function(M)
1/((3*s - 2)*(2*s - 1)*s)

In the database included with Zeta, for examples of algebras with trivial multiplication but non-empty lists of operators, we did not include ideal zeta functions; they coincide with the corresponding subalgebra and submodule zeta functions.

Average sizes of kernels

Subject to the same restrictions as above, Zeta supports the computation of the (local) “ask zeta functions” defined and studied in [Ro2018b].

Let \(\mathfrak{O}\) be a compact discrete valuation ring with maximal ideal \(\mathfrak{P}\). Let \(M \subset \mathrm{M}_{d\times e}(\mathfrak{O})\) be a submodule. Let \(M_n \subset \mathrm{M}_{d\times e}(\mathfrak{O}/\mathfrak{P}^n)\) denote the image of \(M\) under the natural map \(\mathrm{M}_{d\times e}(\mathfrak{O}) \to \mathrm{M}_{d\times e}(\mathfrak{O}/\mathfrak{P}^n)\). The ask zeta function of \(M\) is

\[\mathsf{Z}_M(t) = \sum_{n=0}^\infty \mathrm{ask}(M_n) t^n,\]

where \(\mathrm{ask}(M_n)\) denotes the average size of the kernels of the elements of \(M_n\) acting by right-multiplication on \((\mathfrak{O}/\mathfrak{P}^n)^d\).

Zeta can be used to attempt to compute generic local ask zeta functions in the following global setting. Let \(M \subset \mathrm{M}_{d\times e}(\mathbf{Z})\) be a submodule of rank \(\ell\). Let \(A\) be an integral \(d \times e\) matrix of linear forms in \(\ell\) variables such that \(M\) is precisely the module of specialisations of \(A\). Then zetalib.local_zeta_function(A, 'ask') attempts to compute \(\mathsf{Z}_{M \otimes \mathfrak{O}_K}(t)\) for almost all primes \(p\) and all finite extensions \(K/\mathbf{Q}_p\) in the same sense as in Computing local zeta functions. The optional keyword parameter mode determines whether Zeta attempts to compute ask zeta functions using the functions \(\mathrm{K}_M\) (mode='K') or \(\mathrm{O}_M\) (mode='O') from [Ro2018b, §4], respectively; the default is mode='O'.

Example (ask zeta function)

We compute the generic local ask zeta functions associated with \(\mathrm{M}_{2\times 3}(\mathbf{Z})\).

sage: import zetalib
sage: R.<a,b,c,d,e,f> = QQ[]
sage: A = matrix([[a,b,c],[d,e,f]])
sage: zetalib.local_zeta_function(A, 'ask')
-(q^3 - t)/((q - t)*q^2*(t - 1))

Conjugacy class zeta functions

Let \(L\) be a nilpotent Lie algebra constructed as in Creating algebras. Then zetalib.local_zeta_function(L, 'cc') attempts to compute the generic local conjugacy class zeta functions associated with the unipotent algebraic group corresponding to \(L \otimes \mathbf{Q}\); see [Ro2018b, §7.5]. The optional keyword parameter mode has the same interpretation as in Average sizes of kernels.

Example

We compute the generic local conjugacy class zeta functions of the Heisenberg group.

sage: import zetalib
sage: L = zetalib.Algebra([[(0, 0, 0), (0, 0, 1), (0, 0, 0)], [(0, 0,-1), (0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0), (0, 0, 0)]])
sage: zetalib.local_zeta_function(L, 'cc')
-(t - 1)/((q^2*t - 1)*(q*t - 1))

The built-in database of examples

Accessing the database

Zeta includes a “database” of algebras. When topological or local zeta functions associated with an algebra in the database have been successfully computed using Zeta, these are stored as well.

Each algebra stored in Zeta can be referred to using its unique identification number or one of finitely many names; identification numbers may change between versions of Zeta. Access to these algebras is provided using the function zetalib.lookup.

If zetalib.lookup is called with precisely one argument entry, then entry should be either an identification number or a name of an algebra, \(L\) say, in the database. In this case, zetalib.lookup will return \(L\). Optional further arguments to zetalib.lookup can be used to access other information about \(L\):

  • If the second argument is 'subalgebras', 'ideals', or 'reps' and the third argument is 'local' or 'topological', then zetalib.lookup will return the local or topological subalgebra, ideal, or representation zeta function of \(L\), respectively, if it is known, and None otherwise.
  • If the second argument is 'id', then zetalib.lookup returns the identification number of \(L\).
  • If the second argument is 'names', then zetalib.lookup returns a list of the stored names of \(L\).

When called without arguments, zetalib.lookup returns a list of pairs (i,names), where i ranges over the identification numbers of all algebras in the database and names is a possibly empty list of names associated with the ith algebra.

Example

The algebra \(L = \mathbf Z[X]/X^3\) from Example (subalgebras and ideals) is known to Zeta under the name 'ZZ[X]/X^3'; it can be retrieved via L = zetalib.lookup('ZZ[X]/X^3'). We may recover the pre-computed topological zeta functions of \(L\) as follows:

sage: import zetalib
sage: zetalib.lookup('ZZ[X]/X^3', 'subalgebras', 'topological')
2*(15*s - 8)/((5*s - 4)*(3*s - 2)^2*s)
sage: zetalib.lookup('ZZ[X]/X^3', 'ideals', 'topological')
1/((3*s - 2)*(2*s - 1)*s)

Algebras and their names

Apart from self-explanatory names such as 'sl(2,ZZ)' and 'gl(2,ZZ)', Zeta also includes algebras \(L_{d,i}\), \(L_{d,i}(\varepsilon)\), \(L^i\), \(L^i_a\), \(M^i\), and \(M^i_a\) taken from de Graaf’s tables of nilpotent and soluble Lie algebras; their corresponding names in Zeta are of the form 'L(d,i)', 'L(d,i;eps)', 'L^i', 'L^i(a)', 'M^i', and 'M^i(a)'. For the infinite families among these algebras, we only included selected specialisations of the parameters. Recall [Ro2015a, Prop. 5.19(ii)] that the topological subalgebra and ideal zeta functions of an algebra \(L\) (over \(\mathbf Z\)) only depend on the \(\mathbf C\)-isomorphism type of \(L\otimes_{\mathbf Z}\mathbf C\); a similar statement holds for topological representation zeta functions by [Ro2016, Prop. 4.3].

Similar to Woodward’s tables, we use the notation 'g(...)' to refer to \(\mathbf Z\)-forms of algebras from Seeley’s list of 7-dimensional nilpotent Lie algebras over \(\mathbf C\); for example 'g(147A)' is a \(\mathbf Z\)-form of the algebra \(1,4,7_A\) in Seeley’s list.

The algebras 'N_i^(8,d)' are taken from the lists of Ren and Zhu, and Yan and Deng.

The algebras called 'C(d,i)' and 'C(d,i;eps)' in Zeta are “commutative versions” of the nilpotent Lie rings 'L(d,i)' and 'L(d,i;eps)' respectively: they were obtained by inverting the signs of all entries underneath the diagonal in the matrices of structure constants.

An algebra called 'name[eps]' in Zeta is obtained by tensoring 'name' with the dual numbers as in [Ro2016, §6].

Listing algebras, topological zeta functions, and their properties

The function zetalib.examples.printall generates a text-based list of

  • algebras known to Zeta,
  • structural information about each algebra,
  • known associated topological zeta functions,
  • numerical invariants of these zeta functions (degree, complex roots, …)

and writes these to an optional file-like object (which defaults to stdout). The output of this function is also available for download.

By the essential value of a rational function \(Z\in \mathbf Q(s)\) at a point \(w\in \mathbf C\), we mean the value of \(Z/(s-w)^m\) at \(s = w\), where \(m\) is the order of \(Z\) at \(w\); similarly, for \(w = \infty\). The output of zetalib.examples.printall (and hence the content of the file linked to above) contains the essential values of topological zeta functions at \(0\) and \(\infty\); these are related to Conjectures IV–V from [Ro2015a, Ro2015b].

Advanced usage

More on the creation of algebras

As an integral version of terminology used by Evseev, we say that a \(\mathbf Z\)-basis \((x_1,\dotsc,x_d)\) of an algebra \(L\) is simple if each product \(x_ix_j\) is of the form \(\varepsilon_{ij} x_{a_{ij}}\) for \(\varepsilon_{ij} \in \{-1,0,1\}\). In this case, the structure constants of \(L\) with respect to \((x_1,\dotsc,x_d)\) are determined by the matrix \(A = [\varepsilon_{ij} a_{ij}]_{i,j=1,\dotsc,d}\). Zeta supports the creation of algebras from such a matrix \(A\) by passing simple_basis=True and matrix=\(A\) as arguments to zetalib.Algebra.

For example, the Heisenberg Lie ring with \(\mathbf Z\)-basis \((x_1,x_2,x_3)\) and non-trivial products \([x_1,x_2] = x_3\) and \([x_2,x_1] = -x_3\) from above can be defined in Zeta via zetalib.Algebra(simple_basis=True, matrix=[[0,3,0], [-3,0,0], [0,0,0] ]).

TODO: Add documentation of the bilinear argument.

Additive gradings: blocks

Zeta supports the computation of graded subalgebra and ideal zeta functions as in [Ro2018a]. These zeta functions enumerate homogeneous subobjects with respect to a given additive decomposition of the underlying module. Such decompositions are specified using the keyword argument blocks of zetalib.Algebra. To that end, blocks should be assigned a list \((\beta_1,\dotsc,\beta_r)\) of positive integers summing up to the rank of the algebra \(L\) in question. If \((x_1,\dotsc,x_d)\) is the defining basis of \(L\), then the associated additive decomposition is \(L = L_1 \oplus \dotsb \oplus L_r\) for \(L_j = \bigoplus_{i=\sigma_{j-1}+1}^{\sigma_j} \mathbf Z x_i\) and \(\sigma_i = \sum_{e=1}^i \beta_e\).

Example (graded zeta functions)

Let \(L\) be the Heisenberg Lie algebra with \(\mathbf Z\)-basis \((x_1,x_2,x_3)\) and \([x_1,x_2] = x_3\) as above. Then \(L = L_1 \oplus L_2\) with \(L_1 = \mathbf Z x_1 \oplus \mathbf Z x_2\) and \(L_2 = \mathbf Z x_3\) is the associated graded Lie algebra and the following computes the generic graded local zeta functions arising from the enumeration of its homogeneous subalgebras.

sage: import zetalib
sage: L = zetalib.Algebra([[(0, 0, 0), (0, 0, 1), (0, 0, 0)], [(0, 0,-1), (0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0), (0, 0, 0)]], blocks=[2,1])
sage: zetalib.local_zeta_function(L, 'subalgebras')
-(q*t^3 - 1)/((q*t^2 - 1)*(q*t - 1)*(t + 1)*(t - 1)^2)

Changing bases

(The following only applies to the computation of subalgebra and ideal zeta functions and not to representation or Igusa-type zeta functions.) Computations using Zeta are usually very sensitive to the choice of the basis used to define the structure constants of the algebra under consideration. If a particular zeta function cannot be directly computed using Zeta, it might be useful to consider different bases. Given an algebra L of rank \(d\) and an invertible \(d\times d\) matrix A over \(\mathbf Z\), the algebra obtained from \(L\) by taking the rows of A as a basis (relative to the original one) can be constructed via L.change_basis(A). In the presence of a non-trivial grading, the latter is required to be respected by A.

Unless zetalib.local_zeta_function or zetalib.topological_zeta_function is called with the keyword argument optimise_basis=False, Zeta will attempt to find a basis of the algebra, \(L\) say, in question such that the associated toric datum (see [Ro2015b]) is “small”. Currently, Zeta simply loops over permutations of the defining basis of \(L\).

Verbosity

If zetalib.local_zeta_function or zetalib.topological_zeta_function is called with the keyword argument verbose=True, then detailed information on the various stages of computations will be displayed. Apart from illustrating the key steps explained in [Ro2015a, Ro2015b, Ro2016, Ro2018a], this can often be helpful when it comes to estimating the feasibility of the intended computation.

Computational resources

An upper bound on the number of CPUs used by zetalib.local_zeta_function and zetalib.topological_zeta_function can be enforced by providing a numerical value for the keyword parameter ncpus.

During computations of zeta functions, Zeta uses various temporary files. Be warned that for some computations carried out by the author, the combined size of these files exceeded 50G.

Zeta can be equally demanding when it comes to system memory, in particular when computing local zeta functions. If computations run out of memory, you can try reducing the number of CPUs used as indicated above or try setting the keyword parameter profile to zetalib.Profile.SAVE_MEMORY. Setting profile=zetalib.Profile.SPEED will result in slightly better performance at the cost of increased memory use.

Reduction strategies

(The following only applies to the computation of subalgebra and ideal zeta functions.) The reduction step explained in [Ro2015b] depends on a strategy for choosing “reduction candidates”. A particular strategy can be chosen using the keyword parameter strategy of zetalib.local_zeta_function or zetalib.topological_zeta_function. In particular, setting strategy=zetalib.Strategy.NONE disables reduction completely while strategy=zetalib.Strategy.NORMAL yields the strategy used in the paper. Passing strategy=zetalib.Strategy.PREEMPTIVE will result in a more aggressive reduction strategy which tries to anticipate and remove causes of singularity in advance. While often slower than the zetalib.Strategy.NORMAL, this strategy is needed to reproduce some of the computations recorded in the database (The built-in database of examples).

Acknowledgements

References

[Ro2015a]T. Rossmann, Computing topological zeta functions of groups, algebras, and modules, I, Proc. Lond. Math. Soc. (3) 110 (2015), no. 5, 1099–1134. doi:10.1112/plms/pdv012, preprint.
[Ro2015b](1, 2) T. Rossmann, Computing topological zeta functions of groups, algebras, and modules, II, J. Algebra 444 (2015), 567–605. doi:10.1016/j.jalgebra.2015.07.039, preprint.
[Ro2016]T. Rossmann, Topological representation zeta functions of unipotent groups, J. Algebra 448 (2016), 210–237. doi:10.1016/j.jalgebra.2015.09.050, preprint.
[Ro2018a](1, 2, 3) T. Rossmann, Computing local zeta functions of groups, algebras, and modules. preprint.
[Ro2018b]T. Rossmann, The average size of the kernel of a matrix and orbits of linear groups. preprint.

License

See the LICENSE file. This fork of Zeta is released under GPL-3.0-or-later, like the original version, as quoted in the original documentation:

Copyright 2014, 2015, 2016, 2017 Tobias Rossmann.

Zeta 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 3 of the License, or (at your option) any later version.

Zeta 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 Zeta. If not, see http://www.gnu.org/licenses.

Individual modules documentation

There is also a TODO list. Contributions are welcomed!

Indices and Tables