Jekyll2022-10-20T14:31:07-04:00https://davidayotte.github.io/feed.xmlDavid AyottePh. D. Student in mathematicsDavid Ayottedavid.ayotte@mail.concordia.caGoogle Summer of Code 2021 summary2021-08-18T00:00:00-04:002021-08-18T00:00:00-04:00https://davidayotte.github.io/posts/2021/08/gsoc-final<p>In this post, I will give an overview of my work for the SageMath open-source mathematical software. If you are new to this blog, I suggest that you start reading from the <a href="/posts/2021/05/gsoc2021/">first post</a>.</p> <p>I will list here a brief overview of the work I did and explain the new features that will be available in SageMath in a future release of the software (probably SageMath 9.5). A more detailed list can be found <a href="https://trac.sagemath.org/ticket/31560">here</a>.</p> <h2 id="work-done-during-the-summer">Work done during the summer</h2> <ul> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/31559">#31559</a>: The class <code class="language-plaintext highlighter-rouge">ModularFormsRing</code> now manipulates formal object.</p> <p>The old implementation of the ring of modular forms was a little bit outdated. Now, the elements of this ring in SageMath are instances of the class <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code> which inherit from the class <code class="language-plaintext highlighter-rouge">Element</code>:</p> </li> </ul> <figure class="highlight"><pre><code class="language-text" data-lang="text"> sage: M = ModularFormsRing(1) sage: f = M.0; f 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6) sage: type(f) &lt;class 'sage.modular.modform.ring.ModularFormsRing_with_category.element_class'&gt; sage: f.parent() Ring of Modular Forms for Modular Group SL(2,Z) over Rational Field </code></pre></figure> <ul> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/32168">#32168</a>: Fixed conversion between modular forms spaces.</p> <p>It is well known that a modular form $$f$$ of weight $$k$$, level $$N$$ and nebentypus $$\chi$$ is modular over $$\Gamma_1(N)$$. However, it was not always possible to convert a modular form between different spaces:</p> </li> </ul> <figure class="highlight"><pre><code class="language-text" data-lang="text"> sage: chi = DirichletGroup(13).0 sage: Mchi = ModularForms(chi, 5); Mchi Modular Forms space of dimension 6, character [zeta12] and weight 5 over Cyclotomic Field of order 12 and degree 4 sage: M = ModularForms(Gamma1(13), 5, CyclotomicField(12)); M Modular Forms space of dimension 34 for Congruence Subgroup Gamma1(13) of weight 5 over Cyclotomic Field of order 12 and degree 4 sage: f = Mchi.0; f q + (-3*zeta12^3 - zeta12^2 + zeta12 + 3)*q^5 + O(q^6) sage: M(f) # convert bug Traceback (most recent call last): ... TypeError: entries must be a list of length 34 </code></pre></figure> <p>This bug is now fixed:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text"> sage: f = Mchi.0; f q + (-3*zeta12^3 - zeta12^2 + zeta12 + 3)*q^5 + O(q^6) sage: f.parent() Modular Forms space of dimension 6, character [zeta12] and weight 5 over Cyclotomic Field of order 12 and degree 4 sage: f = M(f); f q + (-3*zeta12^3 - zeta12^2 + zeta12 + 3)*q^5 + O(q^6) sage: f.parent() # bug fixed Modular Forms space of dimension 34 for Congruence Subgroup Gamma1(13) of weight 5 over Cyclotomic Field of order 12 and degree 4 </code></pre></figure> <ul> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/32135">#32135</a>: implemented <code class="language-plaintext highlighter-rouge">to_polynomial</code> and <code class="language-plaintext highlighter-rouge">from_polynomial</code> for the ring of modular forms (see <a href="/posts/2021/06/relation/">this blog post</a> for more info).</p> </li> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/31512">#31512</a>: implemented the ring of quasimodular forms.</p> <p>A new parent class was implemented, named <code class="language-plaintext highlighter-rouge">QuasiModularForms</code>. This class is similar to the class <code class="language-plaintext highlighter-rouge">ModularFormsRing</code>. See this <a href="/posts/2021/05/quasimodform/">blog post</a> for more info about quasimodular forms.</p> </li> </ul> <figure class="highlight"><pre><code class="language-text" data-lang="text"> sage: QM = QuasiModularForms(1); QM Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field sage: E2, E4, E6 = QM.gens() sage: E2 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) sage: E4 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6) sage: E6 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6) sage: f = E2 * E4 + E6; f 2 - 288*q - 20304*q^2 - 185472*q^3 - 855216*q^4 - 2697408*q^5 + O(q^6) sage: f.is_modular_form() False </code></pre></figure> <ul> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/32336">#32336</a>: implemented <code class="language-plaintext highlighter-rouge">to_polynomial</code> and <code class="language-plaintext highlighter-rouge">from_polynomial</code> for quasimodular forms.</p> <p>These two methods are similar to the ones implemented for the ring of modular forms.</p> </li> </ul> <figure class="highlight"><pre><code class="language-text" data-lang="text"> sage: QM = QuasiModularForms(1) sage: f = QM.0 + QM.1 sage: f.to_polynomial() E2 + E4 sage: P.&lt;E2, E4, E6&gt; = QQ[] sage: g = QM.from_polynomial(E2 + E4); g 2 + 216*q + 2088*q^2 + 6624*q^3 + 17352*q^4 + 30096*q^5 + O(q^6) sage: f == g True </code></pre></figure> <ul> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/32343">#32343</a>: implemented the Serre derivative of modular forms.</p> <p>The Serre derivative of a modular form is an operator that sends a weight $$k$$ modular form to a weight $$k+2$$ modular form. It is defined by $$f \mapsto q\frac{df}{dq} - \frac{k}{12}E_2 f$$.</p> </li> </ul> <figure class="highlight"><pre><code class="language-text" data-lang="text"> sage: M = ModularForms(Gamma1(7), 6); M Modular Forms space of dimension 13 for Congruence Subgroup Gamma1(7) of weight 6 over Rational Field sage: f = M.0; f q + O(q^6) sage: f.weight() 6 sage: F = f.serre_derivative(); F 1/2*q + 12*q^2 + 36*q^3 + 48*q^4 + 84*q^5 + O(q^6) sage: F.weight() 8 sage: F.parent() Modular Forms space of dimension 17 for Congruence Subgroup Gamma1(7) of weight 8 over Rational Field </code></pre></figure> <ul> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/32357">#32357</a>: implemented derivative of quasimodular forms and graded modular forms.</p> <p>The derivative of a modular form $$f \mapsto q\tfrac{df}{dq}$$ is not necessarily a modular form. However, it is a quasimodular form. Using the Serre derivative, it was possible to implement this derivative of a graded modular form and a quasiform:</p> </li> </ul> <figure class="highlight"><pre><code class="language-text" data-lang="text"> sage: QM = QuasiModularForms(1) sage: E2, E4, E6 = QM.gens() sage: dE2 = E2.derivative(); dE2 -24*q - 144*q^2 - 288*q^3 - 672*q^4 - 720*q^5 + O(q^6) sage: dE2 == (E2^2 - E4)/12 # Ramanujan identity True sage: dE4 = E4.derivative(); dE4 240*q + 4320*q^2 + 20160*q^3 + 70080*q^4 + 151200*q^5 + O(q^6) sage: dE4 == (E2 * E4 - E6)/3 # Ramanujan identity True sage: dE6 = E6.derivative(); dE6 -504*q - 33264*q^2 - 368928*q^3 - 2130912*q^4 - 7877520*q^5 + O(q^6) sage: dE6 == (E2 * E6 - E4^2)/2 # Ramanujan identity True </code></pre></figure> <ul> <li> <p>Ticket <a href="https://trac.sagemath.org/ticket/32366">32366</a>: implement the q-bracket.</p> <p>This ticket is still a work in progress, but its goal is to implement the q-bracket using the tools provided in the paper of Zagier: <a href="https://people.mpim-bonn.mpg.de/zagier/files/doi/10.1007/s11139-015-9730-8/bloch-okounkov.pdf">Partitions, quasimodular forms and the Bloch-Okounkov theorem</a>. More details about this $$q$$-bracket is given in <a href="/posts/2021/08/okounkov/">this post</a>.</p> </li> </ul> <p>More details about this project can be found in the task ticket <a href="https://trac.sagemath.org/ticket/31560">#31560</a>. This ticket list all the work done so far, and what is to be done in the future.</p> <h2 id="reviewed-tickets">Reviewed tickets</h2> <p>In addition to these new feature, I also reviewed some tickets. Reviewing tickets is a really important part of SageMath development as everything must be peer reviewed before being officially included in the software. In other words, no reviewers $$=$$ no new feature/bug fix. Here’s the list of tickets I reviewed during the summer:</p> <ul> <li><a href="https://trac.sagemath.org/ticket/30775">#30775</a></li> <li><a href="https://trac.sagemath.org/ticket/31414">#31414</a></li> <li><a href="https://trac.sagemath.org/ticket/31775">#31775</a></li> <li><a href="https://trac.sagemath.org/ticket/32165">#32165</a></li> <li><a href="https://trac.sagemath.org/ticket/32193">#32193</a></li> <li><a href="https://trac.sagemath.org/ticket/32195">#32195</a></li> <li><a href="https://trac.sagemath.org/ticket/32316">#32316</a></li> <li><a href="https://trac.sagemath.org/ticket/32350">#32350</a></li> </ul> <h2 id="last-words">Last words</h2> <p>The GSoC 2021 program is already finished and it was an enriching and captivating experience. I perfected my Python programming skills while working on a subject that I’m passionated about: modular forms. Moreover, I thoroughly enjoyed working in collaboration with my mentor as I learned a lot from him. Thank you for reading this blog. If you have any questions or suggestions do not hesitate to contact me via email.</p>David Ayottedavid.ayotte@mail.concordia.caIn this post, I will give an overview of my work for the SageMath open-source mathematical software. If you are new to this blog, I suggest that you start reading from the first post.Partitions and the Bloch-Okounkov Theorem2021-08-01T00:00:00-04:002021-08-01T00:00:00-04:00https://davidayotte.github.io/posts/2021/08/okounkov<p>In this post I will briefly explain what is a partition of a positive integer and how it is possible to relate this theory to quasimodular forms via the Bloch-Okounkov theorem.</p> <h2 id="partitions">Partitions</h2> <p>In mathematics, a <em>partition of a number $$n$$</em> is simply a finite sequence of integers $$\lambda_1, \lambda_2, \ldots, \lambda_m$$ such that $$\lambda_i \geq \lambda_{i+1}$$ and</p> $n = \lambda_1 + \lambda_2 + \cdots + \lambda_m,\quad m\geq 1.$ <p>Equivalently, a <em>partition</em> is a sequence of integers $$\lambda = (\lambda_1, \lambda_2, \ldots, \lambda_i, \ldots)$$ such that $$\lambda_i \geq \lambda_{i+1}$$ and $$\lambda_i = 0$$ for all but finitely many indices $$i$$. We denote by $$\vert \lambda \vert$$ the sum of its entries: $$\vert \lambda \vert = \lambda_1 + \cdots + \lambda_m$$.</p> <p>For example, $$(5,3,2)$$ is a partition of the number $$10$$ and $$(3,1,1)$$ is a partition of the number $$5$$.</p> <p><strong>Exercise.</strong> Find all the seven partitions of the number $$5$$.</p> <p>We will denotes by $$\mathcal{P}$$ the set of all partitions. For our purpose, we will be working with function $$f : \mathcal{P} \rightarrow \mathbb{Q}$$. An example of such function is $$f(\lambda) = \vert \lambda \vert$$. Given a function $$f$$ defined over $$\mathcal{P}$$, it is possible to construct a $$q$$-power series via the $$q$$-bracket:</p> $\langle f \rangle_q := \frac{\sum_{\lambda \in \mathcal{P}} f(\lambda) q^{\vert \lambda \vert}}{\sum_{\lambda \in \mathcal{P}}q^{\vert \lambda \vert}} \in \mathbb{Q}[[q]].$ <p>The theorem of Bloch and Okounkov asserts that this $$q$$-bracket correspond to the $$q$$-expansion of a quasimodular form for a certain class of functions $$f:\mathcal{P} \rightarrow \mathbb{Q}$$ (called <em>shifted symmetric polynomials</em>). To defines these shifted symmetric polynomials, we will need the notion of Young diagram and the Frobenius coordinates of a partition.</p> <h2 id="young-diagrams-and-frobenius-coordinates">Young Diagrams and Frobenius Coordinates</h2> <p>One way of representing a partition visually is by the use of <em>Young diagrams</em>. The <em>Young diagram of a partition $$\lambda = (\lambda_1, \ldots, \lambda_m)$$</em> is given by a series of squares:</p> <p><img src="/images/posts/young_diagram.jpg" alt="Young diagram of (5, 3, 1)" width="250" /></p> <p>In the example above, the top row of squares correspond to $$\lambda_1 = 5$$, the second row correspond $$\lambda_2 = 3$$ and so on.</p> <p><strong>Exercise.</strong> Draw the Young diagram of $$(5,4,3,3,2,1)$$.</p> <p>Now, using the Young diagram of a partition, it is now possible to define the Frobenius coordinates of a partition. Let $$\lambda$$ be a partition. The <em>Frobenius coordinates of $$\lambda$$</em> is the numbers</p> $(r; a_1, a_2, \ldots, a_r; b_1, b_2, \ldots, b_r)$ <p>such that</p> <ul> <li>$$r$$ is the length of the longest principal diagonal in the Young diagram of $$\lambda$$;</li> <li>$$a_1 &gt; \ldots &gt; a_i &gt;\ldots &gt; a_r \geq 0$$ are the number of cells to the right of the $$i$$-th cell of this diagonal;</li> <li>$$b_1 &gt; \ldots &gt; b_i&gt; \ldots &gt; b_r \geq 0$$ are the number of cells below the $$i$$-th cell of this diagonal.</li> </ul> <p>There is nothing better than an example to illustrate this definition. Consider the partition $$\lambda = (5, 3, 1)$$ with Young diagram:</p> <p><img src="/images/posts/young_diagram.jpg" alt="Young diagram of (5, 3, 1)" width="250" /></p> <p>By drawing the longest principal diagonal and counting the number of cells we get:</p> <p><img src="/images/posts/Inkedyoung_diagram_frob_coord_LI.jpg" alt="frobenius coordinates" width="250" /></p> <p>Thus, the Frobenius coordinates of $$\lambda = (5, 3, 1)$$ is</p> $(2; 4, 1; 2; 0)$ <p><strong>Exercise.</strong> Find the Frobenius coordinates of $$\lambda = (5,5,4,1)$$.</p> <h2 id="shifted-symmetric-polynomials">Shifted Symmetric Polynomials</h2> <p>In this section, I will give a brief overview of the shifted symmetric polynomials. First, we need to define two invariants of a partition: $$P_k(\lambda)$$ and $$Q_k(\lambda)$$ for an integer $$k\geq 0$$.</p> <p><strong>Definition.</strong> Let $$\lambda$$ be a partition with Frobenius coordinates $$(r; a_1, \ldots, a_r; b_1,\ldots, b_r)$$ we define</p> $P_k(\lambda) := \sum_{i = 1}^r \left[ (a_i + 1/2)^k - (-b_i - 1/2)^k \right], \quad k\geq 0;$ $Q_k(\lambda) := \frac{P_{k-1}(\lambda)}{(k-1)!} + \beta_k,\quad k\geq 1;$ <p>where $$\beta_k$$ is the $$k$$-th coefficients of the expansion of $$\frac{z/2}{\mathrm{sinh(z/2)}}$$ around $$z=0$$.</p> <p><strong>Definition.</strong> A <em>shifted symmetric polynomial</em> is a function $$f:\mathcal{P} \rightarrow \mathbb{Q}$$ living in $$\mathcal{R} := \mathbb{Q}[Q_1, Q_2, \ldots, Q_k, \ldots]$$. We define $$\mathcal{R}_k \subset \mathcal{R}$$ to be the weight $$k$$ graded component of $$\mathcal{R}$$, that is the subring generated by the weight $$k$$ homogenenous monomials:</p> $\mathcal{R}_k = \langle Q_{k_1}^{m_1}\cdots Q_{k_N}^{m_N} : k_i, m_i\geq 1, k_1 m_1 + \cdots + k_N m_N = k \rangle$ <p><strong>Theorem</strong> (Bloch-Okounkov). For any $$f\in \mathcal{R}_k$$, the $$q$$-bracket $$\langle f \rangle_q$$ correspond to the $$q$$-expansion of a quasimodular forms of weight $$k$$ for $$\mathrm{SL}_2(\mathbb{Z})$$.</p> <p>This theorem is interesting as it relates two apparently different concept: partitions and (quasi)modular forms. In particular, the Bloch-Okounkov theorem can be seen as a tool for studying partitions via quasimodular forms and vice versa.</p> <p>A proof of this theorem can be found in the the main reference for this post:</p> <p><a href="https://people.mpim-bonn.mpg.de/zagier/files/doi/10.1007/s11139-015-9730-8/bloch-okounkov.pdf">Don Zagier, Partitions, quasimodular forms and the Bloch-Okounkov theorem</a></p> <p>In the next weeks, I will be working on the SageMath implementation of the objects described above. In a later version of Sage, it should be possible to do computations with quasimodular forms and to compute the $$q$$-bracket of a shifted symmetric polynomial.</p> <p>Thanks for reading!</p>David Ayottedavid.ayotte@mail.concordia.caIn this post I will briefly explain what is a partition of a positive integer and how it is possible to relate this theory to quasimodular forms via the Bloch-Okounkov theorem.How to Write a Modular Form In Terms of a Set of Generators?2021-07-14T00:00:00-04:002021-07-14T00:00:00-04:00https://davidayotte.github.io/posts/2021/06/modform-relations<p>In this post, I will explain an algorithmic way to write any modular form $$f \in \mathcal{M}_*(\Gamma)$$ as a polynomial in the generators of the graded ring $$\mathcal{M}_*(\Gamma)$$ ($$\Gamma = \Gamma_0(N), \Gamma_1(N)$$ or $$\mathrm{SL}_2(\mathbb{Z})$$).</p> <p>First it is important to mention that for any congruence subgroup $$\Gamma \leq \mathrm{SL}_2(\mathbb{Z})$$, the graded ring of modular forms $$\mathcal{M}_*(\Gamma)$$ is finitely generated (but not necessarily freely). This is not trivial and it was proven in the paper <em>Les schémas de modules de courbes elliptiques</em> [<a href="https://www.springer.com/gp/book/9783540065586">DR73</a>, Thm 3.4]. In fact, they prove that it is finitely generated as a $$\mathbb{Z}$$-algebra.</p> <p>Let’s start with an (easy) example. Consider the weight 12 normalized Eisenstein serie for $$\mathrm{SL}_2(\mathbb{Z})$$ denoted $$E_{12}$$ (by “normalized” I mean that the first Fourier coefficient is $$1$$). This modular form lives in the graded ring of modular forms for the full modular group:</p> $E_{12} \in \mathcal{M}_*(\mathrm{SL}_2(\mathbb{Z})) \cong \mathbb{C}[E_4, E_6].$ <p>Thus, there exist a unique polynomial $$P(X, Y) \in \mathbb{C}[X, Y]$$ such that</p> $\label{poly} E_{12} = P(E_4, E_6)$ <p>Next, we claim that $$P$$ must be homogeneous of weight $$12$$ in $$E_4$$ and $$E_6$$. In other words, if $$X = x^4$$ and $$Y = y^6$$ then $$P(x^4, y^6)$$ must be of degree $$12$$ and respect the following relation: $$P((\lambda x)^4, (\lambda y)^6) = \lambda^{12}P(x^4, y^6)$$ for every $$\lambda\in \mathbb{C}$$. This is simply because $$E_{12}$$ is of weight $$12$$ and the product of two modular forms of weight $$k_1$$ and $$k_2$$ respectively is a modular form of weight $$k_1 + k_2$$. Since the only homogeneous monomials of weight $$12$$ are $$E_4^3$$ and $$E_6^2$$ the problem reduces to finding two complex numbers $$a$$ and $$b$$ such that</p> $\label{E12_pol} E_{12} = a E_4^3 + b E_6^2.$ <p>To determine $$a$$ and $$b$$, we will need a tool called the <em>Sturm bound</em> of a modular forms space. This bound tell us that a modular form is determined only by a finite number of coefficients in its $$q$$-expansion. Here’s the general statement:</p> <h2 id="theorem">Theorem.</h2> <p>Let $$f\in \mathcal{M}_k(\mathrm{SL}_2(\mathbb{Z}))$$ with $$q$$-expansion $$\sum_{n\geq 0} a_n q^n$$. Suppose that $$a_i = 0$$ for $$0 \leq i \leq \lfloor k/12 \rfloor$$. Then $$f = 0$$. The number $$\mathrm{SB}_k := \lfloor k/12 \rfloor$$ is called the <em>Sturm bound</em> of $$\mathcal{M}_k(\mathrm{SL}_2(\mathbb{Z}))$$</p> <p>This theorem is a corollary of the valence formula. Moreover, a more general version for congruences subgroups also exist (see for example this <a href="http://www.lmfdb.org/knowledge/show/cmf.sturm_bound">page</a>)</p> <p>Using the Sturm bound, we can now solve our initial problem by following these steps:</p> <ol> <li>Compute the sturm bound of $$\mathcal{M}_{12}(\mathrm{SL}_2(\mathbb{Z}))$$: $$~~ \mathrm{SB}_{12} = \lfloor 12/12 \rfloor = 1$$</li> <li> <p>Compute the $$q$$-expansion of $$E_{12}$$, $$E_4^3$$ and $$E_6^2$$ up to precision $$q^{\mathrm{SB}_{12}+1}$$:</p> $E_{12} = 1 + \frac{65520}{691}q + O(q^2);$ $E_4^3 = 1 + 720q + O(q^2);$ $E_6^2 = 1 - 1008q + O(q^2).$ </li> <li> <p>Solve the resulting linear system given by $$C_{E_{12}} = a C_{E_{4}^3} + b C_{E_6^2}$$ where $$C_{E_k^n}$$ is the vector formed by the coefficients of $$E_k^n$$ up to precision $$q^{\mathrm{SB}_{12}+1}$$:</p> $\begin{pmatrix} 1 \\ \frac{65520}{691} \end{pmatrix} = \begin{pmatrix} 1 &amp; 1 \\ 720 &amp; -1008 \end{pmatrix} \begin{pmatrix} a \\ b \end{pmatrix}$ </li> </ol> <p>The unique solution to this linear system is given by $$a = 441/691$$ and $$b = 250/691$$. Therefore, the solution to our initial problem is:</p> $E_{12} = \frac{441}{691}E_{4}^3 + \frac{250}{691} E_{6}^{2}$ <p>The same approach can be used for any given modular forms $$f\in \mathcal{M}_k$$. An advantage of this method is that it is computational in the sense that it can be implemented into a computer program.</p> <h2 id="sagemath-implementation">Sagemath Implementation</h2> <p>The algorithm showcased in the example above was implemented into SageMath. The idea was to create two new methods: <code class="language-plaintext highlighter-rouge">to_polynomial</code> and <code class="language-plaintext highlighter-rouge">from_polynomial</code>. The first method takes a <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code> and returns its polynomial form:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(1) sage: e12 = EisensteinForms(1, 12).0 sage: E12 = M(e12) # the form must be of type GradedModularFormElement sage: E12.to_polynomial('E4, E6') 441/691*E4^3 + 250/691*E6^2 sage: f = ModularForms(1, 40).0 sage: M(f).to_polynomial() 463/5308416*E4^10 + 1811/5308416*E4^7*E6^2 - 1939/5308416*E4^4*E6^4 - 335/5308416*E4*E6^6</code></pre></figure> <p>The second method takes a polynomial and returns the associated <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(1) sage: P.&lt;E4, E6&gt; = M.polynomial_ring() sage: M.from_polynomial(E4) 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6) sage: M.from_polynomial(E6) 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6) sage: M.from_polynomial(441/691*E4^3 + 250/691*E6^2) 1 + 65520/691*q + 134250480/691*q^2 + 11606736960/691*q^3 + 274945048560/691*q^4 + 3199218815520/691*q^5 + O(q^6) sage: M.from_polynomial(441/691*E4^3 + 250/691*E6^2).to_polynomial('E4, E6') 441/691*E4^3 + 250/691*E6^2</code></pre></figure> <p>The implementation also works for congruence subgroups as SageMath knows how to compute the generators for these rings.</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(Gamma0(6)) sage: M.gen_forms() [1 + 24*q^3 + O(q^6), q + 5*q^3 - 2*q^4 + 6*q^5 + O(q^6), q^2 - 2*q^3 + 3*q^4 + O(q^6)] sage: P.&lt;g1, g2, g3&gt; = M.polynomial_ring() sage: M.from_polynomial(g1) 1 + 24*q^3 + O(q^6) sage: M.from_polynomial(g2) q + 5*q^3 - 2*q^4 + 6*q^5 + O(q^6) sage: M.from_polynomial(g3) q^2 - 2*q^3 + 3*q^4 + O(q^6) sage: f = ModularForms(Gamma0(6), 8).0 sage: M(f).to_polynomial('g') g0^3*g1 - 5*g0^2*g1*g2 - 80*g0^2*g2^2 - 286*g0*g1*g2^2 - 324*g0*g2^3 + 2664*g1*g2^3 + 6648*g2^4</code></pre></figure> <p>It is important to note here that, in the case where the group is not $$\mathrm{SL}_2(\mathbb{Z})$$, then there might be some relations between the generators. So the methods <code class="language-plaintext highlighter-rouge">to_polynomial</code> and <code class="language-plaintext highlighter-rouge">from_polynomial</code> are not necessarily the inverses of each other. This is illustrated by this example:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(Gamma0(6)) sage: P.&lt;g0, g1, g2&gt; = M.polynomial_ring() sage: M.from_polynomial(g1^2).to_polynomial() g0*g2 + 2*g1*g2 + 11*g2^2</code></pre></figure> <h2 id="source-code">Source Code</h2> <p>Here is the SageMath implemenation of the method <code class="language-plaintext highlighter-rouge">to_polynomial</code>:</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"> <span class="k">def</span> <span class="nf">_homogeneous_to_polynomial</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">names</span><span class="p">,</span> <span class="n">gens</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" If self is a homogeneous form, return a polynomial P(x_0,..., x_n) corresponding to self. Each variable x_i of the returned polynomial correspond to a generator g_i of the list gens (following the order of the list) INPUT: - names -- a list or tuple of names (strings), or a comma separated string; - gens -- (list) a list of generator of self. OUTPUT: A polynomial in the variables names """</span> <span class="n">M</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">parent</span><span class="p">()</span> <span class="n">k</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">weight</span><span class="p">()</span> <span class="c1">#only if self is homogeneous </span> <span class="n">poly_parent</span> <span class="o">=</span> <span class="n">M</span><span class="p">.</span><span class="n">polynomial_ring</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">gens</span><span class="p">)</span> <span class="n">monomials</span> <span class="o">=</span> <span class="n">M</span><span class="p">.</span><span class="n">_monomials_of_weight</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">gens</span><span class="p">,</span> <span class="n">poly_parent</span><span class="p">)</span> <span class="c1"># initialize the matrix of coefficients </span> <span class="n">matrix_data</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">monomials</span><span class="p">.</span><span class="n">values</span><span class="p">():</span> <span class="n">matrix_data</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">f</span><span class="p">[</span><span class="n">k</span><span class="p">].</span><span class="n">coefficients</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="n">f</span><span class="p">[</span><span class="n">k</span><span class="p">].</span><span class="n">parent</span><span class="p">().</span><span class="n">sturm_bound</span><span class="p">())))</span> <span class="n">mat</span> <span class="o">=</span> <span class="n">Matrix</span><span class="p">(</span><span class="n">matrix_data</span><span class="p">).</span><span class="n">transpose</span><span class="p">()</span> <span class="c1"># initialize the column vector of the coefficients of self </span> <span class="n">coef_self</span> <span class="o">=</span> <span class="n">vector</span><span class="p">(</span><span class="bp">self</span><span class="p">[</span><span class="n">k</span><span class="p">].</span><span class="n">coefficients</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="p">[</span><span class="n">k</span><span class="p">].</span><span class="n">parent</span><span class="p">().</span><span class="n">sturm_bound</span><span class="p">()))).</span><span class="n">column</span><span class="p">()</span> <span class="c1"># solve the linear system: mat * X = coef_self </span> <span class="n">soln</span> <span class="o">=</span> <span class="n">mat</span><span class="p">.</span><span class="n">solve_right</span><span class="p">(</span><span class="n">coef_self</span><span class="p">)</span> <span class="c1"># initialize the polynomial associated to self </span> <span class="n">itr</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">poly</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">monomials</span><span class="p">.</span><span class="n">keys</span><span class="p">():</span> <span class="n">poly</span> <span class="o">+=</span> <span class="n">soln</span><span class="p">[</span><span class="n">itr</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">p</span> <span class="n">itr</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">return</span> <span class="n">poly</span> <span class="k">def</span> <span class="nf">to_polynomial</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">names</span><span class="o">=</span><span class="s">'x'</span><span class="p">,</span> <span class="n">gens</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" Return a polynomial P(x_0,..., x_n) such that P(g_0,..., g_n) is equal to self where g_0, ..., g_n is a list of generators of the parent. INPUT: - names -- a list or tuple of names (strings), or a comma separated string. Correspond to the names of the variables; - gens -- (default: None) a list of generator of the parent of self. If set to None, the list returned by :meth:~sage.modular.modform.find_generator.ModularFormsRing.gen_forms is used instead OUTPUT: A polynomial in the variables names """</span> <span class="n">M</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">parent</span><span class="p">()</span> <span class="k">if</span> <span class="n">gens</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="n">gens</span> <span class="o">=</span> <span class="n">M</span><span class="p">.</span><span class="n">gen_forms</span><span class="p">()</span> <span class="c1"># sum the polynomial of each homogeneous part </span> <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">M</span><span class="p">(</span><span class="bp">self</span><span class="p">[</span><span class="n">k</span><span class="p">]).</span><span class="n">_homogeneous_to_polynomial</span><span class="p">(</span><span class="n">names</span><span class="p">,</span> <span class="n">gens</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">weights_list</span><span class="p">())</span></code></pre></figure>David Ayottedavid.ayotte@mail.concordia.caIn this post, I will explain an algorithmic way to write any modular form $$f \in \mathcal{M}_*(\Gamma)$$ as a polynomial in the generators of the graded ring $$\mathcal{M}_*(\Gamma)$$ ($$\Gamma = \Gamma_0(N), \Gamma_1(N)$$ or $$\mathrm{SL}_2(\mathbb{Z})$$).The Pushout of Two Modular Forms Spaces2021-07-01T00:00:00-04:002021-07-01T00:00:00-04:00https://davidayotte.github.io/posts/2021/06/pushout<p>In the previous post, I discussed about some changes made in the code for the graded ring of modular forms in SageMath. However, there was one lacking feature and it was the <em>pushout</em> of two modular forms space. I will explain this feature in this post.</p> <p>Recall that our goal here was to give the class <code class="language-plaintext highlighter-rouge">ModularFormsRing</code> a <em>ring structure</em>. In other words, we want to be able to <em>add</em>, <em>substract</em> and <em>multiply</em> two given modular forms. This was implemented in SageMath by creating a new element class called <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(1) sage: E4 = M.0 # GradedModularFormElement sage: E6 = M.1 # GradedModularFormElement sage: E4 + E6 2 - 264*q - 14472*q^2 - 116256*q^3 - 515208*q^4 - 1545264*q^5 + O(q^6) sage: (E4 + E6).parent() Ring of modular forms for Modular Group SL(2,Z) with coefficients in Rational Field sage: E4 - E6 744*q + 18792*q^2 + 129696*q^3 + 550248*q^4 + 1605744*q^5 + O(q^6) sage: E4 * E6 1 - 264*q - 135432*q^2 - 5196576*q^3 - 69341448*q^4 - 515625264*q^5 + O(q^6)</code></pre></figure> <p>To make the above example consistent with the actual implementation of modular forms in SageMath, we needed to implement the <em>pushout</em> of two modular forms spaces. In SageMath, the pushout of two parents $$P_1$$ and $$P_2$$ is a third parent $$Q$$ such that $$P_1 \hookrightarrow Q$$ and $$P_2 \hookrightarrow Q$$. In our case, let’s take for example the following parents:</p> $P_1 := \mathcal{M}_4(\mathrm{SL}_2(\mathbb{Z})) = \mathbb{C}E_4;$ $P_2 := \mathcal{M}_6(\mathrm{SL}_2(\mathbb{Z})) = \mathbb{C}E_6;$ $Q:= \mathcal{M}_*(\mathrm{SL}_2(\mathbb{Z})) = \bigoplus_{k\in \mathbb{Z}} \mathcal{M}_k(\mathrm{SL}_2(\mathbb{Z})).$ <p>We observe that $$P_1$$ and $$P_2$$ can both be embbeded into the ring $$Q$$. So, it would be desirable to have that the sum of an element of $$P_1$$ with an element of $$P_2$$ would return and element of $$Q$$. In SageMath, $$P_1$$ and $$P_2$$ are two instances of the class <code class="language-plaintext highlighter-rouge">ModularFormsSpace</code>. Hence, to make the pushout feature works, we simply added the following method:</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"> <span class="k">def</span> <span class="nf">_pushout_</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" Implement the pushout of self and other. INPUT: - other -- ModularFormSpace or a ModularFormRing OUTPUT: If self and other have the same groups and base rings, then this method returns self if the weights of the two spaces are equal, otherwise it returns a ModularFormsRing. """</span> <span class="kn">from</span> <span class="nn">.find_generators</span> <span class="kn">import</span> <span class="n">ModularFormsRing</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">ModularFormsSpace</span><span class="p">):</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">group</span><span class="p">()</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">group</span><span class="p">()</span> <span class="ow">and</span> <span class="bp">self</span><span class="p">.</span><span class="n">base_ring</span><span class="p">()</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">base_ring</span><span class="p">():</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">weight</span><span class="p">()</span> <span class="o">==</span> <span class="n">other</span><span class="p">.</span><span class="n">weight</span><span class="p">():</span> <span class="k">return</span> <span class="bp">self</span> <span class="k">else</span><span class="p">:</span> <span class="k">return</span> <span class="n">ModularFormsRing</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">group</span><span class="p">(),</span> <span class="n">base_ring</span><span class="o">=</span><span class="bp">self</span><span class="p">.</span><span class="n">base_ring</span><span class="p">())</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">ModularFormsRing</span><span class="p">)</span> <span class="ow">and</span> <span class="n">other</span><span class="p">.</span><span class="n">has_coerce_map_from</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="n">other</span></code></pre></figure> <p>Thus, the code above makes the following example works:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: e4 = ModularForms(1,4).0; e6 = ModularForms(1,6).0; sage: e4.parent() Modular Forms space of dimension 1 for Modular Group SL(2,Z) of weight 4 over Rational Field sage: e4 + e6 2 - 264*q - 14472*q^2 - 116256*q^3 - 515208*q^4 - 1545264*q^5 + O(q^6) sage: (e4 + e6).parent() Ring of modular forms for Modular Group SL(2,Z) with coefficients in Rational Field</code></pre></figure> <p>Thanks for reading!</p>David Ayottedavid.ayotte@mail.concordia.caIn the previous post, I discussed about some changes made in the code for the graded ring of modular forms in SageMath. However, there was one lacking feature and it was the pushout of two modular forms space. I will explain this feature in this post.The Graded Ring of Modular Forms in SageMath2021-06-21T00:00:00-04:002021-06-21T00:00:00-04:00https://davidayotte.github.io/posts/2021/06/modformsage<p>The goal of this post is to go over some of the new features that are currently in developement for the graded ring of modular forms in SageMath.</p> <p>Currently in SageMath (version 9.3), it is possible to create the graded ring of modular forms using the class <a href="https://doc.sagemath.org/html/en/reference/modfrm/sage/modular/modform/find_generators.html">ModularFormRing</a>:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(1); M Ring of modular forms for Modular Group SL(2,Z) with coefficients in Rational Field</code></pre></figure> <p>However, this class does not follows the parent structure offered by SageMath. This feature is useful when implementing a new algebraic structure in SageMath and I will explain why here. Some knowledge of the python programming language will be assumed.</p> <h2 id="some-mathematics">Some mathematics</h2> <p>First of all, the graded ring of modular forms is the set of all modular forms for a given group. More precisely, it consist of the direct sum</p> $\mathcal{M}_*(\Gamma) := \bigoplus_{k\in \mathbb{Z}} \mathcal{M}_k(\Gamma),\quad \Gamma \leq \mathrm{SL}_2(\mathbb{Z}).$ <p>Note that we have</p> $\mathcal{M}_{k_1}(\Gamma) \mathcal{M}_{k_2}(\Gamma) \subseteq \mathcal{M}_{k_1+k_2}(\Gamma), k_1, k_2\in \mathbb{Z}.$ <p>We will view an element $$F$$ of $$\mathcal{M}_*(\Gamma)$$ as a sum</p> $F = f_0 + f_1 + f_2 + \ldots + f_r, \quad r\geq 0,$ <p>where each $$f_k$$ is a weight $$k$$ modular forms. Such an element $$F$$ will be called a <em>graded modular form</em> (or sometimes simply a <em>graded form</em>). Note that it is not garanteed that a graded form is a modular form as it is not always invariant under the slash-$$k$$ operator. For example, take the function</p> $F := E_4 + E_6$ <p>where $$E_4$$ and $$E_6$$ are the weight $$4$$ and $$6$$ normalized Eisenstein series.</p> <p>Given a graded form $$F = f_0 + f_1 + f_2 + \ldots + f_r$$, each modular forms $$f_k$$ will be called the <em>weight $$k$$ homogeneous component of $$F$$</em>.</p> <h2 id="some-programming">Some programming</h2> <p>In SageMath, there is a notion of <em>elements</em> and <em>parents</em>. In short, an <em>element</em> is a member contained in a structure called <em>parent</em>. An example of a parent in SageMath is the ring of integer $$\mathbb{Z}$$ with elements the integers. To implement a new parent structure in SageMath, one need to define a class that inherit of the subclass <a href="https://doc.sagemath.org/html/en/reference/structure/sage/structure/parent.html">Parent</a>. This was not the case for the initial implementation of the class ModularFormRing.</p> <p>Moreover, when implementing a new structure, one needs to also define a new class representing the elements of our parent class. This class of elements must inherit of the subclass <a href="https://doc.sagemath.org/html/en/reference/structure/sage/structure/element.html">Element</a>. The base methods of this subclass provides useful features. For example, when implemented correctly, it is possible to define the classical operations (addition, multiplication,…) between our elements.</p> <p>Implementing this Parent/Element framework is exactly what we want to do here with the class <code class="language-plaintext highlighter-rouge">ModularFormsRing</code>.</p> <h3 id="new-features">New features</h3> <p>It is now possible to work with a graded modular forms element:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormRing(1) sage: e4 = ModularForms(1,4).0 #Weight 4 eisenstein serie sage: e4 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6) sage: e4.parent() Modular Forms space of dimension 1 for Modular Group SL(2,Z) of weight 4 over Rational Field sage: E4 = M(e4); F 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6) sage: E4.parent() Ring of modular forms for Modular Group SL(2,Z) with coefficients in Rational Field</code></pre></figure> <p>We observe here that <code class="language-plaintext highlighter-rouge">E4</code> and <code class="language-plaintext highlighter-rouge">e4</code> are the same mathematical object but does not lives in the same parent. An interaction between these two parents can be implemented and this is what we call <em>coercion</em>. This will be explained a little bit later in the current post.</p> <p>Here’s an overview of what was done in order to make the above example works:</p> <ul> <li>Make the class <code class="language-plaintext highlighter-rouge">ModularFormRing</code> inherit from <code class="language-plaintext highlighter-rouge">Parent</code>;</li> <li>Define a specific element class (<code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>);</li> <li>Define the <code class="language-plaintext highlighter-rouge">_element_constructor_</code> method;</li> <li>Define coercions between <code class="language-plaintext highlighter-rouge">ModularFormSpace</code> and <code class="language-plaintext highlighter-rouge">ModularFormRing</code></li> </ul> <p>Below is a summary of the class <code class="language-plaintext highlighter-rouge">ModularFormRing</code> with its <code class="language-plaintext highlighter-rouge">_element_constructor_</code> method.</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">class</span> <span class="nc">ModularFormsRing</span><span class="p">(</span><span class="n">Parent</span><span class="p">):</span> <span class="n">Element</span> <span class="o">=</span> <span class="n">GradedModularFormElement</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">group</span><span class="p">,</span> <span class="n">base_ring</span><span class="o">=</span><span class="n">QQ</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" The ring of modular forms (of weights 0 or at least 2) for a congruence subgroup of {\rm SL}_2(\ZZ), with coefficients in a specified base ring. INPUT: - group -- a congruence subgroup of {\rm 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. [...] """</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">group</span><span class="p">,</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">Integer</span><span class="p">)):</span> <span class="n">group</span> <span class="o">=</span> <span class="n">Gamma0</span><span class="p">(</span><span class="n">group</span><span class="p">)</span> <span class="k">elif</span> <span class="ow">not</span> <span class="n">is_CongruenceSubgroup</span><span class="p">(</span><span class="n">group</span><span class="p">):</span> <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"Group (=%s) should be a congruence subgroup"</span> <span class="o">%</span> <span class="n">group</span><span class="p">)</span> <span class="k">if</span> <span class="n">base_ring</span> <span class="o">!=</span> <span class="n">ZZ</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">base_ring</span><span class="p">.</span><span class="n">is_field</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">base_ring</span><span class="p">.</span><span class="n">is_finite</span><span class="p">():</span> <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">"Base ring (=%s) should be QQ, ZZ or a finite prime field"</span> <span class="o">%</span> <span class="n">base_ring</span><span class="p">)</span> <span class="bp">self</span><span class="p">.</span><span class="n">__group</span> <span class="o">=</span> <span class="n">group</span> <span class="bp">self</span><span class="p">.</span><span class="n">__base_ring</span> <span class="o">=</span> <span class="n">base_ring</span> <span class="bp">self</span><span class="p">.</span><span class="n">__cached_maxweight</span> <span class="o">=</span> <span class="n">ZZ</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="bp">self</span><span class="p">.</span><span class="n">__cached_gens</span> <span class="o">=</span> <span class="p">[]</span> <span class="bp">self</span><span class="p">.</span><span class="n">__cached_cusp_maxweight</span> <span class="o">=</span> <span class="n">ZZ</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="bp">self</span><span class="p">.</span><span class="n">__cached_cusp_gens</span> <span class="o">=</span> <span class="p">[]</span> <span class="n">Parent</span><span class="p">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">base</span><span class="o">=</span><span class="n">base_ring</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="n">GradedAlgebras</span><span class="p">(</span><span class="n">base_ring</span><span class="p">))</span> <span class="p">[...]</span> <span class="k">def</span> <span class="nf">_element_constructor_</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">):</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">GradedModularFormElement</span><span class="p">):</span> <span class="n">mf_dict</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">_dictionnary</span><span class="p">()</span> <span class="k">if</span> <span class="n">is_ModularFormElement</span><span class="p">(</span><span class="n">f</span><span class="p">):</span> <span class="k">if</span> <span class="n">f</span><span class="p">.</span><span class="n">group</span><span class="p">()</span> <span class="o">==</span> <span class="bp">self</span><span class="p">.</span><span class="n">group</span><span class="p">()</span> <span class="ow">and</span> <span class="bp">self</span><span class="p">.</span><span class="n">base_ring</span><span class="p">().</span><span class="n">has_coerce_map_from</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">base_ring</span><span class="p">()):</span> <span class="n">mf_dict</span> <span class="o">=</span> <span class="p">{</span><span class="n">f</span><span class="p">.</span><span class="n">weight</span><span class="p">():</span><span class="n">f</span><span class="p">}</span> <span class="k">else</span><span class="p">:</span> <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'The modular form must have the same group and the same base ring as self'</span><span class="p">)</span> <span class="k">if</span> <span class="n">f</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">base_ring</span><span class="p">():</span> <span class="n">mf_dict</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span><span class="n">f</span><span class="p">}</span> <span class="k">return</span> <span class="n">GradedModularFormElement</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">mf_dict</span><span class="p">)</span></code></pre></figure> <p>Here is a brief summary of the element class <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>:</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="k">class</span> <span class="nc">GradedModularFormElement</span><span class="p">(</span><span class="n">Element</span><span class="p">):</span> <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">mf_dict</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" An element of a graded ring of modular forms. INPUTS: - parents - an object of the class ModularFormsRing - mf_dict - a dictionary {k_1:f_1, k_2:f_2, ..., k_n:f_n} where f_i is a modular form of weight k_i OUTPUT: A GradedModularFormElement corresponding to f_1 + f_2 + ... f_n """</span> <span class="bp">self</span><span class="p">.</span><span class="n">__mf_dict</span> <span class="o">=</span> <span class="n">mf_dict</span> <span class="n">Element</span><span class="p">.</span><span class="n">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">)</span></code></pre></figure> <p>We have chosen here to represent a graded modular form as a dictionnary because this makes it easier to access the weight-$$k$$ component of the graded form (keys = weights, values = modular forms).</p> <p>Now that the Parent/Element framework is implemented, it is also desirable to define operation between two <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>. This is done by defining the three special methods: <code class="language-plaintext highlighter-rouge">_add_</code>, <code class="language-plaintext highlighter-rouge">_neg_</code> and <code class="language-plaintext highlighter-rouge">_mul_</code>.</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"> <span class="k">def</span> <span class="nf">_add_</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" Addition of two GradedModularFormElement. """</span> <span class="n">GM</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">__class__</span> <span class="n">f_self</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_dictionnary</span><span class="p">()</span> <span class="n">f_other</span> <span class="o">=</span> <span class="n">other</span><span class="p">.</span><span class="n">_dictionnary</span><span class="p">()</span> <span class="n">f_sum</span> <span class="o">=</span> <span class="p">{</span> <span class="n">k</span> <span class="p">:</span> <span class="bp">self</span><span class="p">.</span><span class="n">__getitem__</span><span class="p">(</span><span class="n">k</span><span class="p">)</span> <span class="o">+</span> <span class="n">other</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">f_self</span><span class="p">)</span> <span class="o">|</span> <span class="nb">set</span><span class="p">(</span><span class="n">f_other</span><span class="p">)</span> <span class="p">}</span> <span class="k">return</span> <span class="n">GM</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">parent</span><span class="p">(),</span> <span class="n">f_sum</span><span class="p">)</span> <span class="k">def</span> <span class="nf">_neg_</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" The negation of self. """</span> <span class="n">GM</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">__class__</span> <span class="n">minus_self</span> <span class="o">=</span> <span class="p">{</span><span class="n">k</span><span class="p">:</span><span class="o">-</span><span class="bp">self</span><span class="p">.</span><span class="n">_dictionnary</span><span class="p">()[</span><span class="n">k</span><span class="p">]</span> <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">_dictionnary</span><span class="p">()}</span> <span class="k">return</span> <span class="n">GM</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">parent</span><span class="p">(),</span> <span class="n">minus_self</span><span class="p">)</span> <span class="k">def</span> <span class="nf">_mul_</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span> <span class="sa">r</span><span class="s">""" Multiplication of two GradedModularFormElement. """</span> <span class="n">GM</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">__class__</span> <span class="n">f_self</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_dictionnary</span><span class="p">()</span> <span class="n">f_other</span> <span class="o">=</span> <span class="n">other</span><span class="p">.</span><span class="n">_dictionnary</span><span class="p">()</span> <span class="n">f_mul</span> <span class="o">=</span> <span class="p">{}</span> <span class="k">for</span> <span class="n">k_self</span> <span class="ow">in</span> <span class="n">f_self</span><span class="p">.</span><span class="n">keys</span><span class="p">():</span> <span class="k">for</span> <span class="n">k_other</span> <span class="ow">in</span> <span class="n">f_other</span><span class="p">.</span><span class="n">keys</span><span class="p">():</span> <span class="n">f_mul</span><span class="p">[</span><span class="n">k_self</span> <span class="o">+</span> <span class="n">k_other</span><span class="p">]</span> <span class="o">=</span> <span class="n">f_self</span><span class="p">[</span><span class="n">k_self</span><span class="p">]</span><span class="o">*</span><span class="n">f_other</span><span class="p">[</span><span class="n">k_other</span><span class="p">]</span> <span class="k">return</span> <span class="n">GM</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">parent</span><span class="p">(),</span> <span class="n">f_mul</span><span class="p">)</span></code></pre></figure> <p>These three methods provides now the possibility of adding, substracting and multiplying two graded forms in SageMath:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(1) sage: e4 = ModularForms(1,4).0 #weight 4 eisenstein serie sage: e6 = ModularForms(1,6).0 #weight 6 eisenstein serie sage: E4 = M(e4); #GradedModularFormElement sage: E6 = M(e6); #GradedModularFormElement sage: E4 + E6 2 - 264*q - 14472*q^2 - 116256*q^3 - 515208*q^4 - 1545264*q^5 + O(q^6) sage:(E4 + E6).parent() Ring of modular forms for Modular Group SL(2,Z) with coefficients in Rational Field</code></pre></figure> <p>For the last part of this post, I will cover briefly the notion of “coercion” which is an important part of this implementation. Thanks to the coercion framework in SageMath, it is possible to implement some interactions between two different parents. For example, one could want to perform an operation between a <code class="language-plaintext highlighter-rouge">ModularFormElement</code> and a <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>.</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(1) sage: e4 = ModularForms(1,4).0 #ModularFormElement sage: e6 = ModularForms(1,6).0 #ModularFormElement sage: E4 = M(e4); #GradedModularFormElement sage: E6 = M(e6); #GradedModularFormElement sage: E4 + e6 + e4 + E6 4 - 528*q - 28944*q^2 - 232512*q^3 - 1030416*q^4 - 3090528*q^5 + O(q^6) sage: (E4 + e6 + e4 + E6).parent() Ring of modular forms for Modular Group SL(2,Z) with coefficients in Rational Field</code></pre></figure> <p>To implement this useful feature it is sufficient to implement the method <code class="language-plaintext highlighter-rouge">_coerce_map_from_</code> to the class <code class="language-plaintext highlighter-rouge">ModularFormRing</code> and the coercion framework of SageMath will take care of the rest.</p> <figure class="highlight"><pre><code class="language-python" data-lang="python"> <span class="k">def</span> <span class="nf">_coerce_map_from_</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">M</span><span class="p">):</span> <span class="k">if</span> <span class="n">is_ModularFormsSpace</span><span class="p">(</span><span class="n">M</span><span class="p">):</span> <span class="k">if</span> <span class="n">M</span><span class="p">.</span><span class="n">group</span><span class="p">()</span> <span class="o">==</span> <span class="bp">self</span><span class="p">.</span><span class="n">group</span><span class="p">()</span> <span class="ow">and</span> <span class="bp">self</span><span class="p">.</span><span class="n">has_coerce_map_from</span><span class="p">(</span><span class="n">M</span><span class="p">.</span><span class="n">base_ring</span><span class="p">()):</span> <span class="k">return</span> <span class="bp">True</span> <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">base_ring</span><span class="p">().</span><span class="n">has_coerce_map_from</span><span class="p">(</span><span class="n">M</span><span class="p">):</span> <span class="k">return</span> <span class="bp">True</span> <span class="k">return</span> <span class="bp">False</span></code></pre></figure> <p>Finally, in the near future, I plan to implement the “pushout” of two different <code class="language-plaintext highlighter-rouge">ModularFormsSpace</code>. In other words, this functionality would add the possibility to sum two <code class="language-plaintext highlighter-rouge">ModularFormElement</code> of different weight and create directly a <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>. For example, we currently (v. 9.3) have the following behavior:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text">sage: M = ModularFormsRing(1) sage: e4 = ModularForms(1,4).0 #ModularFormElement sage: e6 = ModularForms(1,6).0 #ModularFormElement sage e4 + e6 Traceback (most recent call last) ... TypeError: unsupported operand parent(s) for +: 'Modular Forms space of dimension 1 for Modular Group SL(2,Z) of weight 4 over Rational Field' and 'Modular Forms space of dimension 1 for Modular Group SL(2,Z) of weight 6 over Rational Field'</code></pre></figure> <p>Instead of an error here, SageMath would return a <code class="language-plaintext highlighter-rouge">GradedModularFormElement</code>. This feature is still under developpement.</p> <p>Thanks for reading!</p>David Ayottedavid.ayotte@mail.concordia.caThe goal of this post is to go over some of the new features that are currently in developement for the graded ring of modular forms in SageMath.The Algebraic Structure of the Space of Quasimodular Forms2021-06-02T00:00:00-04:002021-06-02T00:00:00-04:00https://davidayotte.github.io/posts/2021/06/alg_structure<p>This post is a direct follow up of the <a href="/posts/2021/05/quasimodform/">last post</a>. In particular, I want to explain what is the algebraic structure of the space of quasimodular forms and how it will be used in our SageMath implementation.</p> <p>Before this, let me recall the structure of the classical modular forms. We will denote by $$\mathcal{M}_k$$ the space of all modular forms of weight $$k$$ for $$\mathrm{SL}_2(\mathbb{Z})$$. Observe that this space is in fact a $$\mathbb{C}$$-vector space. Indeed, for any $$f,g\in \mathcal{M}_k$$ and $$\lambda\in\mathbb{C}$$ we will have $$f + \lambda g \in \mathcal{M}_k$$ (it is in fact finite dimensional over $$\mathbb{C}$$, but this is non-trivial). Moreover, we observe that if $$f\in \mathcal{M}_{k_1}$$ and $$g\in \mathcal{M}_{k_2}$$ for $$k_1, k_2 \in \mathbb{Z}$$ then $$fg\in \mathcal{M}_{k_1 + k_2}$$. Thus the set of <em>all</em> modular forms (whitout any fixed weight), denoted $$\mathcal{M}_*$$, is in fact a graded ring:</p> $\mathcal{M}_* = \bigoplus_{k\geq 0} \mathcal{M}_k,\quad \mathcal{M}_{k_1}\mathcal{M}_{k_2} \subseteq \mathcal{M}_{k_1 + k_2}$ <p>It turns out that this ring has a simple structure which is very convenient for computations:</p> <h2 id="proposition">Proposition.</h2> <p>We have the isomorphism of ring $$\mathcal{M}_* \cong \mathbb{C}[G_4, G_6]$$, where $$G_4$$ and $$G_6$$ are the weight 4 and weight 6 Eisenstein series respectively. $$\diamond$$</p> <p>A similar relation can be found for the space of quasimodular forms. Let’s write some notations:</p> <ul> <li> <p>$$\widetilde{\mathcal{M}}_k^{\leq p} :=$$ set of quasimodular forms of weight $$k$$ and depth $$\leq p$$ for $$\mathrm{SL}_2(\mathbb{Z})$$;</p> </li> <li> <p>$$\widetilde{\mathcal{M}}_k := \bigcup_{p\geq 0} \widetilde{\mathcal{M}}_k^{\leq p} =$$ set of quasimodular forms of weight $$k$$ for $$\mathrm{SL}_2(\mathbb{Z})$$;</p> </li> <li> <p>$$\widetilde{\mathcal{M}}_* := \bigoplus_{k\geq 0} \widetilde{\mathcal{M}}_k =$$ graded ring of quasimodular forms for $$\mathrm{SL}_2(\mathbb{Z})$$.</p> </li> </ul> <p>An general element of a quasimodular form space will be called a <em>quasiform</em>.</p> <h2 id="proposition-1">Proposition.</h2> <p>We have the following isomorphism of ring:</p> $\widetilde{\mathcal{M}}_* \cong \mathbb{C}[G_2, G_4, G_6].$ <p>In particular, we have $$\widetilde{\mathcal{M}}_* \cong \mathcal{M}_*[G_2]$$. $$\diamond$$</p> <p>This isomorphism will be the main point for our implementation of the graded ring of quasimodular forms into SageMath. Indeed, a general quasimodular form $$F\in \widetilde{\mathcal{M}}_*$$ will be implemented as a polynomial in $$G_2$$ with coefficient in $$\mathcal{M}_*$$:</p> $\label{eq:pol_G2} F = f_0 + f_1 G_2 + f_2 G_2^2 + \ldots f_r G_2^r,\quad f_i \in \mathcal{M}_*, r\geq 0.$ <p>Hence, to encode a quasiform in SageMath it <em>should</em> be enough to simply specify a list modular forms:</p> <figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="p">[</span> <span class="n">f_0</span><span class="p">,</span> <span class="n">f_1</span><span class="p">,</span> <span class="n">f_2</span><span class="p">,</span> <span class="o">...</span><span class="p">,</span> <span class="n">f_r</span> <span class="p">]</span></code></pre></figure> <p>where each f_i correspond to a coefficient of (\ref{eq:pol_G2}). A useful feature of SageMath is the fact that the classical modular forms are already implemented. Thus, during the implementation of the quasimodular forms, it would be all to our advantage to <em>reuse</em> what has already been implemented.</p> <p>In addition to the implementation of classical modular forms into SageMath, there is also the software PARI/GP that can perform computation with modular forms. <a href="https://pari.math.u-bordeaux.fr/">PARI/GP</a> is also a free mathematical software, but it is more centered around number theory. What is more important to us is the fact that PARI/GP is included in SageMath! This means that when using SageMath, it is possible to explicitly call PARI/GP in order to perform a certain computation. Therefore, we will have access to both of the implementations which will be very useful.</p>David Ayottedavid.ayotte@mail.concordia.caThis post is a direct follow up of the last post. In particular, I want to explain what is the algebraic structure of the space of quasimodular forms and how it will be used in our SageMath implementation.A Quick Introduction to the Theory of (Quasi)modular Forms2021-05-26T00:00:00-04:002021-05-26T00:00:00-04:00https://davidayotte.github.io/posts/2021/05/quasimodforms<p>In this post, I shall explain what is a quasimodular forms, which are the main mathematical objects for this GSoC project.</p> <p>Basic knowledge of classical modular forms will be assumed throughougt this text, but let’s recall the definition. First we define an action of the group of $$2\times 2$$ matrices with coefficient in $$\mathbb{Z}$$ and determinant $$1$$, denoted $$\mathrm{SL}_2(\mathbb{Z})$$ (called the <em>modular group</em>), on the upper half plane $$\mathbb{H} := \lbrace z \in \mathbb{C} : \mathrm{im}(z)&gt;0 \rbrace$$ by</p> $\begin{pmatrix} a &amp; b \\ c &amp; d \end{pmatrix} z := \frac{az+b}{cz+d}$ <h2 id="definition">Definition.</h2> <p>A <em>weak modular form of weight $$k$$ for $$\mathrm{SL}_2(\mathbb{Z})$$</em> is an holomorphic function $$f:\mathbb{H} \rightarrow \mathbb{C}$$ such that</p> $\label{eq_mod_form} f\left( \frac{az+b}{cz+d}\right) = (cz+d)^k f(z)$ <p>for every $$z\in \mathbb{H}$$ and every $$\bigl(\begin{smallmatrix} a &amp; b\\ c &amp; d \end{smallmatrix}\bigr) \in \mathrm{SL}_2(\mathbb{Z})$$. This invariance property is called the <em>modularity condition</em>. $$\diamond$$</p> <p>If we define the operator $$f\mid_k\gamma(z) := (cz+d)^{-k} f(\gamma z)$$, then the notion of being weakly holomorphic is the same as being $$\mathrm{SL}_2(\mathbb{Z})$$-invariant under the $$\mid_k$$ operator.</p> <p>By equation (\ref{eq_mod_form}), we deduce that $$f$$ is a periodic function $$f(z+1) = f(z)$$ (check the modularity condition with the matrix $$\bigl(\begin{smallmatrix} 1 &amp; 1\\ 0 &amp; 1 \end{smallmatrix}\bigr)$$. Thus, $$f$$ possesses a Fourier expansion:</p> $\label{eq_fourier} f(z) = \sum_{n=-\infty}^{\infty} a_n(f) q^n, \qquad q:=e^{2i\pi z}.$ <h2 id="definition-1">Definition.</h2> <p>A <em>modular form of weight $$k$$ for $$\mathrm{SL}_2(\mathbb{Z})$$</em> is a weak modular form such that $$a_n(f) = 0$$ for every $$n&lt;0$$. Furthermore, if $$a_0(f) = 0$$, then $$f$$ is called a <em>cusp form</em>. The expansion (\ref{eq_fourier}) of a modular form is called its <em>$$q$$-expansion</em>. $$\diamond$$</p> <p>The main example one encounter when studying modular forms is the <em>weight-$$k$$ Eisenstein series</em> which is defined by:</p> $\label{eq_eis_ser} G_{k}(z) := \sum_{\substack{ c,d\in \mathbb{Z} \\ (c,d) \neq (0,0) }} \frac{1}{(cz +d)^{k}}.$ <p>It turns out that if $$k\geq 4$$ is even, then $$G_k$$ is absolutely and uniformely convergent for $$z\in \mathbb{H}$$ and defines a modular form with $$q$$-expansion</p> $G_k(z) = 2\zeta(k) + 2\frac{(2\pi i)^k}{(k-1)!} \sum_{n=1}^{\infty} \sigma_{k-1}(n)q^n$ <p>where $$\zeta(s) := \sum_{n=1}^{\infty}\tfrac{1}{n^s}$$ is the <em>Riemann Zeta function</em> and $$\sigma_k(n):=\sum_{d\mid n}d^k$$. From here, one question arises: what happens if we take $$k=2$$? The answer is that we would loose the absolute convergence of (\ref{eq_eis_ser}). However, we can still choose a specific arrangement of the sum and define</p> $G_2(z) := \sum_{c\in \mathbb{Z}} \sum_{d\in \mathbb{Z}_c} \frac{1}{(cz+d)^2}$ <p>where</p> $\mathbb{Z}_c = \begin{cases} \mathbb{Z}\setminus \lbrace 0 \rbrace, &amp; \text{ if }c = 0\\ \mathbb{Z}, &amp; \text{ otherwise.} \end{cases}$ <p>Using this definition, it is possible to show that</p> $G_2(z) = 2\zeta(2) - 8 \pi^2 \sum_{n=1}^{\infty} \sigma_1(n) q^n.$ <p>Moreover, we have that</p> $G_2|_2\gamma(z) = G_2(z) - 2\pi i\frac{c}{cz+d},\quad \forall \gamma = \bigl(\begin{smallmatrix} a &amp; b\\ c &amp; d \end{smallmatrix}\bigr)\in \mathrm{SL}_2(\mathbb{Z}).$ <p>and so we observe that $$G_2$$ is <em>almost</em> a modular form. It is so close of being a modular form that we call it a <em>quasimodular form</em>.</p> <p>Let $$\mathrm{Hol}_0(\mathbb{H})$$ be the set of holomorphic function on $$\mathbb{H}$$ having a Fourier expansion of the form $$\sum_{n\geq 0} a_n e^{2i\pi n}$$.</p> <h2 id="definition-2">Definition.</h2> <p>Let $$k$$ and $$p$$ be two positives integers. A <em>quasimodular form of weight $$k$$ and depth $$\leq p$$ for $$\mathrm{SL}_2(\mathbb{Z})$$</em> is a holomorphic function $$f\in \mathrm{Hol}_0(\mathbb{H})$$ such that for $$z\in \mathbb{H}$$ and $$\gamma = \bigl(\begin{smallmatrix} a &amp; b\\ c &amp; d \end{smallmatrix}\bigr)\in \mathrm{SL}_2(\mathbb{Z})$$ there exists $$f_0,\ldots, f_p \in \mathrm{Hol}_0(\mathbb{H})$$ such that</p> $(f \mid_k \gamma)(z) = \sum_{r = 0}^{p} f_r(z) \left( \frac{c}{cz+d} \right)^r.$ <p>In other words, $$(f \mid_k \gamma)(z)$$ is a polynomial of degree at most $$p$$ in $$\tfrac{c}{cz+d}$$ with coefficient in $$\mathrm{Hol}_0(\mathbb{H})$$. $$\diamond$$</p> <h2 id="references">References</h2> <p>Here are some references for the theory of classical modular forms:</p> <ul> <li> <p>Diamond, Fred and Shurman, Jerry, <em>A first course in modular forms.</em> Graduate Texts in Mathematics, Vol. 228, Springer-Verlag, New York, 2005</p> </li> <li> <p>Serre, Jean-Pierre, <em>Cours d’arithmétique.</em> Deuxième édition revue et corrigée. Le Mathématicien, No 2. Presses Universitaires de France, Paris, 1977.</p> </li> </ul> <p>A good exposition for the theory of quasimodular form is given in section 5.3 (page 58) of:</p> <ul> <li>Zagier, Don, <em>Elliptic modular forms and their applications.</em> The 1-2-3 of modular forms, 1–103, Universitext, Springer, Berlin, 2008.</li> </ul>David Ayottedavid.ayotte@mail.concordia.caIn this post, I shall explain what is a quasimodular forms, which are the main mathematical objects for this GSoC project.Google Summer of Code 2021 program2021-05-25T00:00:00-04:002021-05-25T00:00:00-04:00https://davidayotte.github.io/posts/2021/05/gsoc<p>I will explain in this post a brief summary of what is the <a href="https://summerofcode.withgoogle.com/">Google Summer of Code</a> (abreviated GSoC). In short, the GSoC 2021 program is a 10-weeks program by Google where students all around the world are paired with mentors and work as developpers for an open source organization.</p> <p>The organization that I will be working for this summer is <a href="https://www.sagemath.org/">SageMath</a> together with the mentor <a href="https://www.labri.fr/perso/vdelecro/">Vincent Delecroix</a>. SageMath is a mathematical software that can perform multiple computing task and is very useful for a wide variety of students and researchers. I personally used a lot this software in the past during my study, so I am very enthusiastic of being a part of this for the summer 2021.</p> <p>My main goal will be to implement a new feature into this software, which is the “ring of quasimodular forms”. A more precise description of my project can be found <a href="/files/GSoC_project.pdf">here</a>.</p> <p>The 10-weeks coding period of GSoC will begin in June 7. Hence, every two weeks during the program, I will document my journey here on my blog by explaining my work and sharing my ideas. Before this coding period, I will be in a preparatory phase where I will study the mathematical side of quasimodular forms and explain what is the current state of SageMath regarding modular forms.</p> <p>If you read this blog and find any mistakes, or have any question, do not hesitate to contact me via email. My email address can be found below my picture at the left of this page.</p>David Ayottedavid.ayotte@mail.concordia.caI will explain in this post a brief summary of what is the Google Summer of Code (abreviated GSoC). In short, the GSoC 2021 program is a 10-weeks program by Google where students all around the world are paired with mentors and work as developpers for an open source organization.