$\DeclareMathOperator{\lcm}{lcm} \newcommand{\mt}[4]{\begin{bmatrix}#1\\#3\end{bmatrix}} \newcommand{\Set}[1]{\left\{#1\right\}} \newcommand{\SetDef}[2]{\Set{{#1}\left|\,\vphantom{#1}{#2}\right.}} \newcommand{\abs}[1]{\left\lvert#1\right\rvert} \newcommand{\setQ}{\mathbb{Q}} $ Via jupytext this file can be shown as a jupyter notebook.
Deriving the Relation between $j$ and $\lambda$¶
This file derives the equation \begin{align*} j(\tau) &= \frac{256 (1 - \lambda(\tau) + \lambda(\tau)^2)^3} {\lambda(\tau)^2 (1 - \lambda(\tau))^2} \end{align*} between the Klein $j$ invariant and the modular $\lambda$ function by using their $q$-expansions ($q=e^{\pi i \tau}$) at the cusps of $\Gamma(2)$ and applying the multi-samba algorithm to them.
\begin{align*} \lambda(\tau) &= \frac{16 \eta\left(\frac{\tau}{2}\right)^8 \eta(2\tau)^{16}} {\eta(\tau)^{24}} \\ j(\tau) &= \left( \left(\frac{\eta(\tau)}{\eta(2\tau)}\right)^8 + 2^8 \left(\frac{\eta(2\tau)}{\eta(\tau)}\right)^{16} \right)^3 \end{align*} https://fungrim.org/topic/Modular_j-invariant/#Connection_formulas
https://fungrim.org/topic/Modular_lambda_function/#Dedekind_eta_function_representations
https://fungrim.org/topic/Modular_lambda_function/#Connection_to_the_j-invariant
)cd ..
)read input/jfricas-test-support.input )quiet
The current FriCAS default directory is /home/hemmecke/backup/git/qeta All user variables and function definitions have been cleared. All )browse facility databases have been cleared. Internally cached functions and constructors have been cleared. )clear completely is finished. The current FriCAS default directory is /home/hemmecke/backup/git/qeta/tmp
We want to show the variable lambda
nicely in the output,
so we translate any symbol lambda
into the TeX-expression
\lambda
. That is just technical boilerplate.
setHandler!(operatorHandlers()$FormatMathJax, 0 ,_
"lambda", formatConstant("\lambda")$FormatMathJax)_
$ OperatorHandlers((Integer, List OutputForm) -> OutputBox);
Do no execute the following if you are in a jFriCAS notebook.
)set output formatted off
)set output algebra on
-------------------------------------------------------------------
--test:modular-equation-j-lambda
-------------------------------------------------------------------
Setting up the stage¶
At the expansions we can work with rational numbers. The following is currently the way to set this up in QEta.
)read convenience )quiet
C ==> QQ
xiord := 2;
EXTENDEDCOEFFICIENTRING(C, xiord, CX, xi);
The cusps and corresponding transformation matrices of $\Gamma(2)$. We have chosen here to work with expansions at the cusps in the order $\infty , 0, 1$. Any other order of the cusps does work with MultiSamba, but, of course, the basis may look different, since the reduction steps may be different. Nevertheless, the modular polynomial does, of course, not depend on which cusp order is chosen for MultiSamba.
spitzen := cusps()$GAMMAN(2);
spitzen := [spitzen.i for i in [3,1,2]]
trfs := [cuspToMatrix(x)$GAMMAN(2) for x in spitzen]
Expansion of $j(\tau)$ at the cusps of $\Gamma(2)$ in terms of $q=e^{\pi i \tau}$.
jinf := multiplyExponents(kleinJInvariant()$QFunctions(C, L1 C), 2)
Note that the expansions of $j(\tau)$ at all cusps are identical, i.e., $j_\infty=j_0=j_1$.
htj := table()$XHashTable(SL2Z, L1 C);
for gamma in trfs repeat htj.gamma := jinf
j := htj :: An(C);
Expansion of $\lambda(\tau)$ at the cusps of $\Gamma(2)$ in terms of $q=e^{\pi i \tau}$.
RFC ==> Fraction SparseUnivariatePolynomial C -- rational function over C
QMLTOOL ==> QEtaModularLambdaTools
transformLambda(gamma) ==> _
transformAtModularLambda(x::RFC, gamma, unitSL2Z, 1)$QMLTOOL(C)
htl := table()$XHashTable(SL2Z, L1 C);
for gamma in trfs repeat htl.gamma := transformLambda(gamma)
l := htl :: An(C)
Compute $p(j,\lambda)=0$ with the function call "modularPolynomial"¶
The easiest way in QEta to compute a modular polynomial is by
calling a specially tailored function from the
package QEtaModularEquation
.
mp := modularPolynomial([j,l]) $ QEtaModularEquation(C, An C)
In the above polynomial $x$ appears only with exponent 1. So we can solve for $x$ and get the following.
sol := solve(mp,x)
Written in a nicer form it gives the known representation of $j$ in terms of $\lambda$.
symj := "j"::Symbol;
symlambda := "lambda"::Symbol;
ratfun := rhs(sol.1);
symj = factor(eval(numer ratfun, y=symlambda))_
/ _
factor(eval(denom ratfun, y=symlambda))
assertEquals(ratfun, 256*(1 - y + y^2)^3 / (y*(1-y))^2)
Let's check whether our series j
and l
really fulfil the relation
for all cusps of $\Gamma(2)$.
e := 1$An(C); -- that is [1,1,1]
z := j*(l*(e - l))^2 - 256*(e - l + l^2)^3
assertTrue(zero? z)
We can also just replace the variables of mp
by the
respective vectors of $q$-expansions at the cusps.
zz := eval(mp, c+->c*1$An(C),[x,y],[j,l])$PolynomialEvaluation(C, An C)
assertTrue(zero? zz)
Compute $p(j,\lambda)=0$ with MultiSamba and an additional reduction¶
Above we used a variant of multisamba that was specifically tailored for the computation of a relation between two modular functions. In this section, we determine the modular polynomial of two modular function with the multisamba algorithm that computes a module basis for the input.
This is a two step process.
- Determine $B=\{b_0,b_1,\ldots,b_5\}$ such that \begin{gather*} \setQ[j,\lambda]=\langle B \rangle_{\setQ[j]}, \end{gather*}
- then reduce $\lambda^6$ by $j$ and $B$ to 0 and extract from this reduction a representation of $\lambda^6$ in terms of the basis elements.
Since each basis element has a representation in terms of $j$ and $\lambda$, we eventually get the modular polynomial such that $p(j,\lambda)=0$.
There is some boilerplate to setup the input and the respective types for multisamba to work.
Xn C ==> QEtaExtendedAlgebra(C, An C, Polynomial C)
toXn(C, a, b) ==> embed(a, b::Polynomial(C))$Xn(C)
xnPol(C, x) ==> second x -- extract the polynomial part from x
xj := toXn(C, j, "x"::Symbol)
xl := toXn(C, l, "y"::Symbol)
xab := samba([xj, xl]) $ QMSAMBA(C,Xn,QMRED);
bas := basis xab;
# bas
The variable xab
represents $\langle B \rangle_{\setQ[j]}$.
The computation does not take long and yields the following basis elements. Note that $1$ is silently assumed to be in the bases, but multisamba does not compute or show it explicitly.
bas.1
bas.2
bas.3
bas.4
bas.5
The pole orders of the input and the basis elements is given by the following list.
[qetaGrades x for x in concat([xj,xl],bas)]
Now we reduce to 0 a high enough power of the lambda function by the algebra basis. Since the highest power of $\lambda$ occuring in the representation of the basis is 5, reducing $\lambda^6$ is enough.
Reduction of $\lambda$ modulo $j$ and the basis only gives a the trivial polynomial 0 in the "representation component". That is not useful.
xr := reduce(xl, xab)$QMRED(C,Xn)
mp1 := xnPol(C,xr)
Also reduction of $\lambda^k$ for $k=2,3,4,5$ finds no relation.
[reduce(xl^k, xab)$QMRED(C,Xn) for k in [2,3,4,5]]
However, reducing $\lambda^6$ is enough.
xr := reduce(xl^6, xab)$QMRED(C,Xn)
mp2 := xnPol(C,xr)
The polynomial mp2
equals modular polynomial mp
from above.
assertEquals(mp2, mp)
In the following we demonstrate the reduction steps that
QEta is actually doing in the command
reduce(xl^6, xab)$QMRED(C,Xn)
from above.
We only perform here the reduction on the series vectors
and hide the "representation part".
From the pole orders of the expansion of $\lambda^6$, we see that the first reduction step will be done with $j$. Let us compute in terms of the basis elements, how the reduction proceeds.
)set stream calc 1
bs := [first x for x in bas];
b0:=1$An(C);b1:=bs.1;b2:=bs.2;b3:=bs.3;b4:=bs.4;b5:=bs.5;
[qetaGrades x for x in [j, l, l^6]]
[qetaGrades x for x in bs]
To facilitate the notation, let us introduce an abbreviation for the function that extracts the leading coefficient of the $k$-th component.
lc ==> qetaLeadingCoefficient
)set stream calc 6
Looking at the orders of $\lambda^6$, it is clear that the first step is a reduction by $j^3$ in the third component.
qetaGrades(l^6)
r1 := l^6 - lc(l^6,3) * j^3
qetaGrades r1
The result must be reduced in the second component.
We must choose b4
.
r2 := r1 - lc(r1,1)/lc(b4,1) * j^2*b4
qetaGrades r2
Again, we must reduce in the third component and use the
basis element b5
.
r3 := r2 - lc(r2,3)/lc(b5,3) * j^2*b5
qetaGrades r3
r3
must be reduced in the third component by b0
.
r4 := r3 - lc(r3,3)/lc(b0,3) * j^2*b0
qetaGrades r4
r4
must be reduced in the first component by b3
.
r5 := r4 - lc(r4,1)/lc(b3,1) * j*b3
qetaGrades r5
r5
must be reduced in the third component by b5
.
r6 := r5 - lc(r5,3)/lc(b5,3) * j*b5
qetaGrades r6
r6
must be reduced in the second component by b2
.
r7 := r6 - lc(r6,2)/lc(b2,2) * j*b2
qetaGrades r7
r7
must be reduced in the third component by b0
.
r8 := r7 - lc(r7,3)/lc(b0,3) * j*b0
qetaGrades r8
r8
must be reduced in the second component by b4
.
r9 := r8 - lc(r8,2)/lc(b4,2) * b4
qetaGrades r9
r9
must be reduced in the first component by b3
.
r10 := r9 - lc(r9,1)/lc(b3,1) * b3
qetaGrades r10
r10
must be reduced in the third component by b5
.
r11 := r10 - lc(r10,3)/lc(b5,3) * b5
qetaGrades r11
r11
must be reduced in the second component by b2
.
r12 := r11 - lc(r11,2)/lc(b2,2) * b2
qetaGrades r12
r12
must be reduced in the first component by b1
.
r13 := r12 - lc(r12,1)/lc(b1,1) * b1
qetaGrades r13
In the final step, we only need to add b0
.
r14 := r13 + b0
assertTrue(zero? r14)
Details of the computations done in the function "modularPolynomial"¶
In this section we give an extended demonstration of what the
function modularPolynomial
, mentioned at the beginning of this
notebook is actually doing.
The $q$-expansions of $j$ at the cusps $\{\infty,0,1\}$ of
$\Gamma(2)$ are identical and of order -2.
For that reason, we take $j$ as the special element.
Since its orders are equal to -2 in each component, it is easy
to determine the component in which an element can be reduced.
We must just look for its highest pole order (the negation of the
order of the respective Laurent series).
In QEta the function qetaGrades
is the name for the function
that returns the vector of pole orders.
The $q$-expansions of $\lambda$ at the cusps $\infty$, 0, 1 are of order 1, 0, -1, respectively.
For the following computation, we form vectors that are stored in
the variables xj
for for the
expansions of $j$ and xl
for the expansions of $\lambda$ at the
cusps $\infty$, 0, 1 (in exactly this order).
The vectors in these variables xj
and xl
already have
an additional component added, the "representation part", namely,
the symbol $x$ at xj
and the symbol $y$ at xl
.
All arithmetic operations are not only performed on the
$q$-expansions, but simultaneously also on the
representation part.
This last component is used for tracing the
steps in the reduction algorithm and eventually contains the
modular polynomial between $j$ and $\lambda$.
Again note that in FriCAS, the Laurent series contain a method to
compute any coefficient. The O-notation only appears at output time
and can be controlled by the )set stream calculate
command.
)set stream calculate 2
Let us show again the expansions of $j(\tau)$ and $\lambda(\tau)$ at the cusps $\{\infty,0,1\}$ of $\Gamma(2)$
xj
xl
The multisamba algorithm (adapted to the computation of a modular polynomial) works as follows.
We start with the set $B=\{1\}$ as an initial basis and then roughly do the following steps.
(1) Take $\lambda$ and reduce it wrt. $j$ and $B$ to the element $b_1$ (which is then put into $B$).
(2) Reduce $\lambda \cdot b_1$ wrt. $j$ and $B$ to the element $b_2$ (which is then put into $B$).
(3) Reduce $\lambda \cdot b_2$ wrt. $j$ and $B$ to the element $b_3$ (which is then put into $B$).
...
(s) Reduce $\lambda \cdot b_{s-1}$ wrt. $j$ and $B$ to the element $b_s$ (which is then put into $B$).
(s+1) Stop the above process if $b_{s+1}=0$ and read off the modular polynomial from the trace of this reduction process.
The grades (or pole orders) are the negation of the orders.
[qetaGrades xj, qetaGrades xl]
Let us start the variant of the multisamba process for the computation of a modular polynomial.
1. Iteration¶
Clearly $\lambda$ is not reducible by $j$, because the highest
pole order of $\lambda$ is in the last component (cusp 1) and
is smaller in absolute value then the pole order of $j$ at cusp 1.
Therefore, we simply add xl
as $b_1$ to the basis.
b0 := toXn(C, 1$An(C), 1);
b1 := xl; B := [b0,b1]; [qetaGrades b for b in B]
2. Iteration¶
Now we consider $\lambda \cdot b_1$.
z2 := xl*b1
qetaGrades(z2)
It has pole order 2 in the last component and can be reduced by subtracting $j/256$ from it.
[lc(z2,3), lc(xj*b0,3)]
z2a := z2 - 1/256 * xj*b0
qetaGrades z2a
Although that reduction step looks like creating an element with a worse pole order vector than that of $z_2$, it nevertheless holds $z_{2} >_j z_{2a}$.
We must find a reducer for the second component. However, none of the basis elements have their highest pole order in the second component, i.e. $z_{2a}$ is irreducible and enters the basis as $b_2$.
b2 := z2a
B := [b0, b1, b2]; [qetaGrades b for b in B]
3. Iteration¶
Now we consider $\lambda\cdot b_2$.
z3 := xl*b2
qetaGrades z3
That product must be reduced in the third component by subtracting an appropriate multiple of $j b_0$ from it.
[lc(z3,3), lc(xj*b0,3)]
z3a := z3 - 1/256*xj*b0
qetaGrades z3a
Now, however, we can use $b_2$ to reduce $z_{3a}$ in the second component.
[lc(z3a,2), lc(b2,2)]
z3b := z3a - 2*b2
qetaGrades z3b
We must find a reducer for the first component, since there is the highest pole order. However, none of the basis elements has their highest pole order in the first component, i.e. $z_{3b}$ is irreducible and enters the basis as $b_3$.
b3 := z3b
B := [b0, b1, b2, b3]; [qetaGrades b for b in B]
4. Iteration¶
Now we consider $\lambda\cdot b_3$.
z4 := xl*b3
qetaGrades z4
The element $z_4$ must be reduced in the third component. This can be done by subtracting a suitable multiple of $j b_0$.
[lc(z4,3), lc(xj*b0,3)]
z4a := z4 + 1/64*xj*b0
qetaGrades z4a
Clearly, the element can be reduced in the second component by $b_2$.
[lc(z4a,2), lc(b2,2)]
z4b := z4a + 4*b2
qetaGrades z4b
$z_{4b}$ would have to be reduced in the third component and it can be done by subtracting a constant multiple of $b_1=\lambda$.
[lc(z4b,3), lc(b1,3)]
z4c := z4b - 3*b1
qetaGrades z4c
$z_{4c}$ would have to be reduced in the second component, but that can obviously not be done by any of the current basis elements. Therefore, $z_{4c}$ is added to the basis $B$ as $b_4$.
b4 := z4c
B := [b0,b1,b2,b3,b4]; [qetaGrades b for b in B]
5. Iteration¶
Now we consider $\lambda\cdot b_4$.
z5 := xl*b4
qetaGrades z5
This element can be reduced (in the third component) by subtracting a constant multiple of $b_1$.
[lc(z5,3), lc(b1,3)]
z5a := z5 + 3*b1
qetaGrades z5a
The element $z_{5a}$ can be reduced in the second component by subtracting a constant multiple of $b_4$.
[lc(z5a,2), lc(b4,2)]
z5b := z5a - b4
qetaGrades z5b
We obtain an element that obviously cannot be reduced in the first component by any existing basis element. It therefore enters the basis as $b_5$.
b5 := z5b
B := [b0,b1,b2,b3,b4,b5]; [qetaGrades b for b in B]
If we look at the pole orders of all the basis elements, we see that relative to $j$ (with pole order 2 in each component), we can reduce any element. In other words, the basis $B$ is already complete and we expect a zero reduction in the next interation.
6. Iteration and Stop¶
Now we consider $\lambda\cdot b_5$.
z6 := xl*b5
qetaGrades z6
This element can be reduced (in the third component) by subtracting a constant multiple of $b_1$.
[lc(z6,3), lc(b1,3)]
z6a := z6 - 3*b1
qetaGrades z6a
Adding the vector for $b_0=1$ to $z_{6a}$ gives exactly zero in each of the first 3 components.
z := z6a + b0
assertTrue(zero? z)
The last component (where the representation of the element is stored) gives rise to the relation between $j$ and $\lambda$.
Indeed it is equal to the modular polynomial that we computed and checked already above.
xnPol(C, z)
assertEquals(xnPol(C, z), mp)
This is how the modular polynomial $p(x,y)$ is computed such that $p(j(\tau),\lambda(\tau))=0$ for every $\tau$ in the complex upper half plane.
-------------------------------------------------------------------
--endtest
-------------------------------------------------------------------