Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix category of ModularFormsRing and documentation cleanup #37821

Merged
merged 10 commits into from
Sep 15, 2024
141 changes: 83 additions & 58 deletions src/sage/modular/modform/ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,17 +243,18 @@ def __init__(self, group, base_ring=QQ):
self.__cached_gens = []
self.__cached_cusp_maxweight = ZZ(-1)
self.__cached_cusp_gens = []
Parent.__init__(self, base=base_ring, category=GradedAlgebras(base_ring))
cat = GradedAlgebras(base_ring).Commutative()
Parent.__init__(self, base=base_ring, category=cat)

def change_ring(self, base_ring):
r"""
Return the ring of modular forms over the given base ring and the same
group as ``self``.
Return a ring of modular forms over a new base ring of the same
congruence subgroup.

INPUT:

- ``base_ring`` -- a base ring, which should be `\QQ`, `\ZZ`, or the
integers mod `p` for some prime `p`.
- ``base_ring`` -- a base ring, which should be `\QQ`, `\ZZ`, or
the integers mod `p` for some prime `p`.

EXAMPLES::

Expand All @@ -268,7 +269,7 @@ def change_ring(self, base_ring):

def some_elements(self):
r"""
Return a list of generators of ``self``.
Return some elements of this ring.

EXAMPLES::

Expand All @@ -280,7 +281,7 @@ def some_elements(self):

def group(self):
r"""
Return the congruence subgroup for which this is the ring of modular forms.
Return the congruence subgroup of this ring of modular forms.

EXAMPLES::

Expand All @@ -292,14 +293,15 @@ def group(self):

def gen(self, i):
r"""
Return the `i`-th generator of ``self``.
Return the `i`-th generator of this ring.

INPUT:

- ``i`` (Integer) -- correspond to the `i`-th modular form generating
the ring of modular forms.
- ``i`` (Integer) -- correspond to the `i`-th modular form
generating the ring of modular forms.

kwankyu marked this conversation as resolved.
Show resolved Hide resolved
OUTPUT: A ``GradedModularFormElement``
OUTPUT: A generator of this ring, which is an instance of
:class:`~sage.modular.modform.GradedModularFormElement`

EXAMPLES::

Expand All @@ -315,7 +317,7 @@ def gen(self, i):

def ngens(self):
r"""
Return the number of generators of ``self``
Return the number of generators of this ring.

EXAMPLES::

Expand All @@ -335,17 +337,22 @@ def ngens(self):

def polynomial_ring(self, names, gens=None):
r"""
Return a polynomial ring of which ``self`` is a quotient.
Return a polynomial ring of which this ring of modular forms is
a quotient.

INPUT:

- ``names`` -- a list or tuple of names (strings), or a comma separated string
- ``gens`` (default: None) -- (list) a list of generator of ``self``. If ``gens`` is
``None`` then the generators returned by :meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms`
- ``names`` -- a list or tuple of names (strings), or a comma
separated string
- ``gens`` (default: None) -- (list) a list of generator of
``self``. If ``gens`` is ``None`` then the generators returned by
:meth:`~sage.modular.modform.find_generator.ModularFormsRing.gen_forms`
is used instead.
kwankyu marked this conversation as resolved.
Show resolved Hide resolved

kwankyu marked this conversation as resolved.
Show resolved Hide resolved
OUTPUT: A multivariate polynomial ring in the variable ``names``. Each variable of the
polynomial ring correspond to a generator given in gens (following the ordering of the list).
OUTPUT: A multivariate polynomial ring in the variable
``names``. Each variable of the polynomial ring correspond to a
generator given in the list ``gens`` (following the ordering of
the list).

EXAMPLES::

Expand All @@ -358,7 +365,8 @@ def polynomial_ring(self, names, gens=None):
sage: M.polynomial_ring('g', gens)
Multivariate Polynomial Ring in g0, g1, g2 over Rational Field

The degrees of the variables are the weights of the corresponding forms::
The degrees of the variables are the weights of the
corresponding forms::

sage: M = ModularFormsRing(1)
sage: P.<E4, E6> = M.polynomial_ring()
Expand All @@ -372,12 +380,13 @@ def polynomial_ring(self, names, gens=None):
if gens is None:
gens = self.gen_forms()
degs = [f.weight() for f in gens]
return PolynomialRing(self.base_ring(), len(gens), names, order=TermOrder('wdeglex', degs)) # Should we remove the deg lexicographic ordering here?
return PolynomialRing(self.base_ring(), len(gens), names,
order=TermOrder('wdeglex', degs))

def _generators_variables_dictionnary(self, poly_parent, gens):
r"""
Utility function that returns a dictionary giving an association between
polynomial ring generators and generators of modular forms ring.
Return a dictionary giving an association between polynomial
ring generators and generators of modular forms ring.

INPUT:

Expand All @@ -398,23 +407,26 @@ def _generators_variables_dictionnary(self, poly_parent, gens):
nb_var = poly_parent.ngens()
nb_gens = self.ngens()
if nb_var != nb_gens:
raise ValueError('the number of variables (%s) must be equal to the number of generators of the modular forms ring (%s)' % (nb_var, self.ngens()))
raise ValueError('the number of variables (%s) must be equal to'
' the number of generators of the modular forms'
' ring (%s)' % (nb_var, self.ngens()))
return {poly_parent.gen(i): self(gens[i]) for i in range(0, nb_var)}

def from_polynomial(self, polynomial, gens=None):
r"""
Convert the given polynomial to a graded form living in ``self``. If
``gens`` is ``None`` then the list of generators given by the method
:meth:`gen_forms` will be used. Otherwise, ``gens`` should be a list of
generators.
Return a graded modular form constructed by evaluating a given
multivariate polynomial at a set of generators.

INPUT:

- ``polynomial`` -- A multivariate polynomial. The variables names of
the polynomial should be different from ``'q'``. The number of
variable of this polynomial should equal the number of generators
- ``gens`` -- list (default: ``None``) of generators of the modular
forms ring
- ``polynomial`` -- A multivariate polynomial. The variables
names of the polynomial should be different from ``'q'``. The
number of variable of this polynomial should equal the number
of given generators.
- ``gens`` (default: ``None``) -- a list of generators of this
ring. If this parameter is ``None``, then the generators given
by the method :meth:`gen_forms` will be used. Note that we do
not check if the list is indeed a generating set.

kwankyu marked this conversation as resolved.
Show resolved Hide resolved
OUTPUT: A ``GradedModularFormElement`` given by the polynomial
relation ``polynomial``.
Expand All @@ -437,7 +449,8 @@ def from_polynomial(self, polynomial, gens=None):
sage: M.from_polynomial(P(1/2))
1/2

Note that the number of variables must be equal to the number of generators::
Note that the number of variables must be equal to the number of
generators::

sage: x, y = polygens(QQ, 'x, y')
sage: M(x + y)
Expand Down Expand Up @@ -470,7 +483,7 @@ def from_polynomial(self, polynomial, gens=None):

def _element_constructor_(self, forms_datum):
r"""
The call method of self.
Return the graded modular form corresponding to the given data.

INPUT:

Expand Down Expand Up @@ -524,7 +537,7 @@ def _element_constructor_(self, forms_datum):
else:
raise ValueError('the group (%s) and/or the base ring (%s) of the given modular form is not consistant with the base space: %s' % (forms_datum.group(), forms_datum.base_ring(), self))
elif forms_datum in self.base_ring():
forms_dictionary = {0:forms_datum}
forms_dictionary = {0: forms_datum}
elif isinstance(forms_datum, MPolynomial):
return self.from_polynomial(forms_datum)
elif isinstance(forms_datum, PowerSeries_poly):
Expand Down Expand Up @@ -577,7 +590,8 @@ def one(self):

def _coerce_map_from_(self, M):
r"""
Code to make ModularFormRing work well with coercion framework.
Return ``True`` if there is a coercion map from ``M`` to this
ring.

TESTS::

Expand Down Expand Up @@ -623,7 +637,7 @@ def __richcmp__(self, other, op):

def _repr_(self):
r"""
String representation of self.
Return the string representation of self.

EXAMPLES::

Expand All @@ -636,7 +650,8 @@ def _repr_(self):

def modular_forms_of_weight(self, weight):
"""
Return the space of modular forms on this group of the given weight.
Return the space of modular forms of the given weight and the
same congruence subgroup.

EXAMPLES::

Expand All @@ -650,9 +665,15 @@ def modular_forms_of_weight(self, weight):

def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2):
r"""
If `R` is the base ring of self, then this function calculates a set of
modular forms which generate the `R`-algebra of all modular forms of
weight up to ``maxweight`` with coefficients in `R`.
Return a list of generator of this ring as a list of pairs
`(k, f)` where `k` is an integer and `f` is a univariate power
series in `q` corresponding to the `q`-expansion of a modular
form of weight `k`.

More precisely, if `R` is the base ring of self, then this
function calculates a set of modular forms which generate the
`R`-algebra of all modular forms of weight up to ``maxweight``
with coefficients in `R`.

INPUT:

Expand All @@ -666,7 +687,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2):
triples `(k, f, F)`, where:

- `k` is an integer,
- `f` is the `q`-expansion of a modular form of weight `k`, as a power series over the base ring of self,
- `f` is the `q`-expansion of a modular form of weight `k`,
as a power series over the base ring of self,
- `F` (if provided) is a modular form object corresponding to F.

If this list is nonempty, we find a minimal generating set containing
Expand All @@ -680,8 +702,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2):

OUTPUT:

a list of pairs (k, f), where f is the q-expansion to precision
``prec`` of a modular form of weight k.
a list of pairs `(k, f)`, where `f` is the `q`-expansion to precision
``prec`` of a modular form of weight `k`.

.. SEEALSO::

Expand Down Expand Up @@ -787,7 +809,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2):
for x in start_gens:
if len(x) == 2:
if x[1].prec() < prec:
raise ValueError("Requested precision cannot be higher than precision of approximate starting generators!")
raise ValueError("Requested precision cannot be higher"
" than precision of approximate starting generators!")
sgs.append((x[0], x[1], None))
else:
sgs.append(x)
Expand All @@ -807,8 +830,8 @@ def generators(self, maxweight=8, prec=10, start_gens=[], start_weight=2):

def gen_forms(self, maxweight=8, start_gens=[], start_weight=2):
r"""
Return a list of modular forms generating this ring (as an algebra over
the appropriate base ring).
Return a list of modular forms generating this ring (as an algebra
over the appropriate base ring).

This method differs from :meth:`generators` only in that it returns
graded modular form objects, rather than bare `q`-expansions.
Expand Down Expand Up @@ -843,19 +866,21 @@ def gen_forms(self, maxweight=8, start_gens=[], start_weight=2):
Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(11) of weight 2 over Rational Field

"""
sgs = tuple( (F.weight(), None, F) for F in start_gens )
sgs = tuple((F.weight(), None, F) for F in start_gens)
G = self._find_generators(maxweight, sgs, start_weight)
return [F for k,f,F in G]
return [F for k, f, F in G]

gens = gen_forms

def _find_generators(self, maxweight, start_gens, start_weight):
r"""
For internal use. This function is called by :meth:`generators` and
:meth:`gen_forms`: it returns a list of triples `(k, f, F)` where `F`
Returns a list of triples `(k, f, F)` where `F`
is a modular form of weight `k` and `f` is its `q`-expansion coerced
into the base ring of self.

kwankyu marked this conversation as resolved.
Show resolved Hide resolved
For internal use. This function is called by :meth:`generators` and
:meth:`gen_forms`.

INPUT:

- ``maxweight`` -- maximum weight to try
Expand Down Expand Up @@ -976,7 +1001,7 @@ def _find_generators(self, maxweight, start_gens, start_weight):
@cached_method
def q_expansion_basis(self, weight, prec=None, use_random=True):
r"""
Calculate a basis of q-expansions for the space of modular forms of the
Return a basis of q-expansions for the space of modular forms of the
given weight for this group, calculated using the ring generators given
kwankyu marked this conversation as resolved.
Show resolved Hide resolved
by ``find_generators``.

Expand Down Expand Up @@ -1044,8 +1069,8 @@ def q_expansion_basis(self, weight, prec=None, use_random=True):

def cuspidal_ideal_generators(self, maxweight=8, prec=None):
r"""
Calculate generators for the ideal of cuspidal forms in this ring, as a
module over the whole ring.
Return a set of generators for the ideal of cuspidal forms in
this ring, as a module over the whole ring.

EXAMPLES::

Expand All @@ -1066,7 +1091,7 @@ def cuspidal_ideal_generators(self, maxweight=8, prec=None):
for j,f,F in self.__cached_cusp_gens:
if f.prec() >= working_prec:
f = F.qexp(working_prec).change_ring(self.base_ring())
G.append( (j,f,F) )
G.append((j, f, F))
else:
k = 2
G = []
Expand Down Expand Up @@ -1117,16 +1142,16 @@ def cuspidal_ideal_generators(self, maxweight=8, prec=None):
if prec is None:
return G
elif prec <= working_prec:
return [ (k, f.truncate_powerseries(prec), F) for k,f,F in G]
return [(k, f.truncate_powerseries(prec), F) for k,f,F in G]
else:
# user wants increased precision, so we may as well cache that
Gnew = [ (k, F.qexp(prec).change_ring(self.base_ring()), F) for k,f,F in G]
Gnew = [(k, F.qexp(prec).change_ring(self.base_ring()), F) for k, f, F in G]
self.__cached_cusp_gens = Gnew
return Gnew

def cuspidal_submodule_q_expansion_basis(self, weight, prec=None):
r"""
Calculate a basis of `q`-expansions for the space of cusp forms of
Return a basis of `q`-expansions for the space of cusp forms of
weight ``weight`` for this group.

INPUT:
Expand Down
Loading