# Meaning of the names of generators of ring of (quasi)modular forms

Recently, I used Sage to do computations on (quasi)modular forms and I found some mysterious namings of them. The following code

QM2 = QuasiModularForms(Gamma0(2))

for qm in QM2.gens():
print(qm.polynomial(), qm)


gives

E2 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6)
E2_0 1 + 24*q + 24*q^2 + 96*q^3 + 24*q^4 + 144*q^5 + O(q^6)
E4_0 1 + 240*q^2 + 2160*q^4 + O(q^6)


and I know that the first one is the weight 2 and level 1 Eisenstein series, but it is not clear to me what the E2_0 and E4_0 stand for. Based on q-series, I guess that those are 2E2(2z) - E2(z) and E4(2z), but I wonder how it works in general. I tried to find the corresponding code in GitHub but failed. Any helps appreciated.

edit retag close merge delete

Sort by » oldest newest most voted

Let us take a look at the constructor called in order to build the instance QM2.

First of all, it is mathematically a ring (with further structure),

sage: QM2.category()
Category of graded algebras over Rational Field


If we ask via ?QM2 for more information on the instance, we get...

Type:           QuasiModularForms_with_category
String form:    Ring of Quasimodular Forms for Congruence Subgroup Gamma0(2) over Rational Field
File:           /usr/lib/python3.11/site-packages/sage/modular/quasimodform/ring.py
Docstring:
The graded ring of quasimodular forms for the full modular group
\SL_2(\ZZ), with coefficients in a ring.

EXAMPLES:

sage: QM = QuasiModularForms(1); QM
Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field
sage: QM.gens()
[1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6),
1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6),
1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)]

It is possible to access the weight 2 Eisenstein series:

sage: QM.weight_2_eisenstein_series()
1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6)

Currently, the only supported base ring is the rational numbers:

sage: QuasiModularForms(1, GF(5))
Traceback (most recent call last):
...
NotImplementedError: base ring other than Q are not yet supported for quasimodular forms ring
Init docstring:
INPUT:

* "group" (default: \SL_2(\ZZ)) -- a congruence subgroup of
\SL_2(\ZZ), or a positive integer N (interpreted as \Gamma_0(N)).

* "base_ring" (ring, default: \QQ) -- a base ring, which should be
\QQ, \ZZ, or the integers mod p for some prime p.

* "name" (str, default: "'E2'") -- a variable name corresponding to
the weight 2 Eisenstein series.


And so on.

We have above also the location of the corresponding py-module on the hard disk, so we have access to the code. This is the open source world. To have the code of the class (starting with the above doc string) also in the interpreter, just ask for ??QM2 instead.

So we have a ring in our hands. When we explicitly ask for this ring, forgetting about everything else...

sage: QM2.polynomial_ring()
Multivariate Polynomial Ring in E2, E2_0, E4_0 over Rational Field


So the ring has three generators,

sage: QM2.polynomial_ring().ngens()
3


and sage uses for them the names E2, E2_0, E4_0. Let us consider the following code:

N = 2
QM = QuasiModularForms(Gamma0(N))

for qm in QM.gens():
print(f"The q-modular form {qm} has:\n\t"
f"weight     = {qm.weight()}\n\t"
f"polynomial = {qm.polynomial()}\n\t"
f"is_modular = {qm.is_modular_form()}")


It gives:

The q-modular form 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) has:
weight     = 2
polynomial = E2
is_modular = False
The q-modular form 1 + 24*q + 24*q^2 + 96*q^3 + 24*q^4 + 144*q^5 + O(q^6) has:
weight     = 2
polynomial = E2_0
is_modular = True
The q-modular form 1 + 240*q^2 + 2160*q^4 + O(q^6) has:
weight     = 4
polynomial = E4_0
is_modular = True


The names are... see also https://doc.sagemath.org/html/en/reference/modfrm/sage/modular/quasimodform/element.html, when the method polynomial is mentioned:

If the group is not the full modular group, the default names of the generators are given by Ek_i and Sk_i to denote the $i$-th basis element of the weight $k$ Eisenstein subspace, and cuspidal subspace respectively (for more details, see the documentation of polynomial_ring())

And in this last documentation...

sage: QM.polynomial_ring?

Signature:      QM.polynomial_ring(names=None)
Docstring:
Return a multivariate polynomial ring of which the quasimodular
forms ring is a quotient.

In the case of the full modular group, this ring is R[E_2, E_4,
E_6] where E_2, E_4 and E_6 have degrees 2, 4 and 6 respectively.

INPUT:

* "names" (str, default: "None") -- a list or tuple of names
(strings), or a comma separated string. Defines the names for the
generators of the multivariate polynomial ring. The default names
are of the following form:

* "E2" denotes the weight 2 Eisenstein series;

* "Ek_i" and "Sk_i" denote the i-th basis element of the weight k
Eisenstein subspace and cuspidal subspace respectively;

* If the level is one, the default names are "E2", "E4" and "E6";

* In any other cases, we use the letters "Fk", "Gk", "Hk", ...,
"FFk", "FGk", ... to denote any generator of weight k.

OUTPUT: A multivariate polynomial ring in the variables "names"

EXAMPLES:
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


there is also a mention about the default convention of denoting elements of the associated polynomial ring.

In fact, replacing above N = 2 and letting the definition of QM use the new N,

N = 24
QM = QuasiModularForms(Gamma0(N))


we obtain a new list of default names:

The q-modular form 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) has:
weight     = 2
polynomial = E2
is_modular = False
The q-modular form 1 + O(q^6) has:
weight     = 2
polynomial = E2_0
is_modular = True
The q-modular form q + O(q^6) has:
weight     = 2
polynomial = F2
is_modular = True
The q-modular form q^2 + O(q^6) has:
weight     = 2
polynomial = E2_2
is_modular = True
The q-modular form q^3 + O(q^6) has:
weight     = 2
polynomial = E2_3
is_modular = True
The q-modular form q^4 + O(q^6) has:
weight     = 2
polynomial = E2_4
is_modular = True
The q-modular form q^5 + O(q^6) has:
weight     = 2
polynomial = G2
is_modular = True
The q-modular form O(q^6) has:
weight     = 2
polynomial = E2_5
is_modular = True
The q-modular form O(q^6) has:
weight     = 2
polynomial = E2_6
is_modular = True


sage: QM.polynomial_ring()
Multivariate Polynomial Ring in E2, E2_0, F2, E2_2, E2_3, E2_4, G2, E2_5, E2_6 over Rational Field


And let us decide, we want our own names, using the letter U uniformly.

sage: R = QM.polynomial_ring(names='U')
sage: R
Multivariate Polynomial Ring in U0, U1, U2, U3, U4, U5, U6, U7, U8 over Rational Field
sage: R.inject_variables()
Defining U0, U1, U2, U3, U4, U5, U6, U7, U8


And now we can work with U0, U1, ... , U8 instead.

more

Thank you for your very detailed answer. However, I want to confirm that my guess for the E2_0 and E4_0 are correct, and it seems that I need to check the actual implementation of QM.polynomial_ring() to see how Ek_i are actually defined/determined.

( 2024-02-14 23:38:40 +0200 )edit

## Stats

Seen: 263 times

Last updated: Feb 12