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
```

Let us ask for...

```
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.