Understanding the implementation of the $p$-adic(?) sigma function in SageMath

I'm trying to understand the (pretty undocumented) method .sigma() method for formal groups of elliptic curves, as listed here. The source code looks like this:

def sigma(self, prec=10):
"""
EXAMPLES::

sage: E = EllipticCurve('14a')
sage: F = E.formal_group()
sage: F.sigma(5)
t + 1/2*t^2 + (1/2*c + 1/3)*t^3 + (3/4*c + 3/4)*t^4 + O(t^5)
"""
a1,a2,a3,a4,a6 = self.curve().ainvs()

k = self.curve().base_ring()
fl = self.log(prec)
R = rings.PolynomialRing(k,'c'); c = R.gen()
F = fl.reverse()

S = rings.LaurentSeriesRing(R,'z')
c = S(c)
z = S.gen()
F = F(z + O(z**prec))
wp = self.x()(F)
e2 = 12*c - a1**2 - 4*a2
g = (1/z**2 - wp + e2/12).power_series()
h = g.integral().integral()
sigma_of_z = z.power_series() * h.exp()

T = rings.PowerSeriesRing(R,'t')
fl = fl(T.gen()+O(T.gen()**prec))
sigma_of_t = sigma_of_z(fl)
return sigma_of_t


For context, an elliptic curve object in Sage is specified by an actual Weierstrass form $$y^2+a_1xy+a_3y=x^3+a_2x^2+a_4x+a_6$$ with paramaters $$[a_1,a_2,\ldots, a_6]$$, and not just an isomorphism class. The only really important part of the above code is the second to last stanza, as the rest is just converting between the parameter $$z$$ (from the complex uniformization) and the customary formal group parameter $$t=-x/y$$.

Mathematical context

Anyway, this code is apparently supposed to return some form of the Weierstrass $$\sigma$$-function, or more accurately I think it's supposed to return something like the $$p$$-adic $$\sigma$$-function as studied by Mazur and Tate in their paper of that name (and many others since). Recall that the classical Weierstrass $$\sigma$$-function satisfies (among many other identities) $$d(d\log \sigma(z;\Lambda)) = - \wp(z;\Lambda).$$ where $$\Lambda$$ is the (fixed, for our purposes) auxiliary data of a lattice in $$\mathbb{C}$$, equivalently a rigidified elliptic curve.

The $$p$$-adic $$\sigma$$-function according to Mazur and Tate is usually put in terms of the formal parameter $$t$$, but for our purposes, we'll just write it in terms of $$z$$. In that case it can be given by $$\sigma_p(z;\Lambda) := \exp(-E^{(p)}_2(\Lambda)z^2/24)\sigma(z;\Lambda)$$ where $$E^{(p)}$$ is the $$p$$-stabilized Eisenstein series of weight $$2$$, normalized to have residue $$1$$ at the cusp at infinity. In particular, by taking the second log derivative of both sides we get $$d(d\log\sigma_p(z)) = -\wp -E^{(p)}_2(\Lambda)/12$$.

(Compare this to the formula $$X-\wp = E^{(p)}_2(\Lambda)/12$$ on page 2 of the Mazur-Tate article.)

The source of my confusion

The code above almost, but not quite, looks like it does this. Indeed, we have the Weierstrass $$\wp$$-function initialized as its power series in terms of $$z$$

wp = self.x()(F)


using the fact that the $$x$$-coordinate in Weierstrass form is said function. This is followed by this mysterious line:

e2 = 12*c - a1**2 - 4*a2


which appears to say that we should have something like "$$E_2^{(p)}(\Lambda) = 12c -a_1^2-4a_2$$" (probably up to a scalar since e2 might be normalized differently), where $$c$$ is a free variable whose "meaning" is not clear to me.

The rest of the relevant code does this:

g = (1/z**2 - wp + e2/12).power_series()
h = g.integral().integral()
sigma_of_z = z.power_series() * h.exp()


which is all very much as expected: indeed, if we apply $$d\circ d\log$$ to the equation in the last line, we get

$$d(d\log \text{"sigma of z"}(z)) = -\frac{1}{z^2}+ d(d\text{"h"}) = -\frac{1}{z^2}+\text{"g"} = -\wp + \frac{\text{"e2"}}{12}$$

This makes it indeed look like e2 is some form of $$E_2^{(p)}$$, but it appears to be off by a factor of $$-2$$. Could just be a weird normalization, or (as likely) a mistake on my part.

(One thing which suggests that it's not a mistake and in fact is just a different of a factor of $$-2$$ is that in the case that $$a_1=a_2=0$$, e.g. reduced Weierstrass form), c is then $$-\frac{1}{24}E_2^{(p)}$$, which is a pretty standard normalization often denoted $$G_2^{(p)}$$.)

So my question generally is, what's going on here? More specifically,

1. Am I correct that this is supposed to be something like the $$p$$-adic $$\sigma$$-function? (It has to be somewhat more subtle that that, I guess, since it's not like it can be all of them for different $$p$$ at the same time, but I'm failing to see how it works.)

2. What's going on with the e2 variable and its relationship to the c variable? What precise relationship is the former supposed to have to $$E_2^{(p)}$$ as above? What are you supposed to plug in'' for c to actually get particular values of $$\sigma_p$$ for different $$p$$? Can you use this variable to evaluate it at a different rigidification (i.e. choice of invariant differential) without changing the isomorphism class of the elliptic curve? And how does this relate to the weird equation e2 = 12*c - a1**2 - 4*a2?

• I fear this is a question for me and not for the forum, though I don't mind if someone else answers it. I doubt I will have time to look into it for a while but I will. Feel free to move it to an email conversation. Feb 7, 2021 at 8:43
• Oh, are you the author of this code? I'll shoot you an email soon!
– xir
Feb 7, 2021 at 16:13
• Oh, I would never publically admit to be the author of such badly documented code - even if it is 15 years or older. I'll answer by email and will improve the documentation at some point..... Feb 7, 2021 at 21:33
• From a mathematical perspective, the answer to this question is spectacularly boring: This code is outdated, originally used in now deleted code. One should use E.padic_sigma(p) to obtain the canonical sigma function. I will change the function in question and its documentation soon. Mar 28, 2021 at 12:05
• trac.sagemath.org/ticket/31573 is the ticket that fixes this. It is likely to enter sage for version 9.4. Apr 9, 2021 at 8:24

This answer was started some long time ago, i also tried to understand in some depth the related mathematical structure. Although it may have become obsolete after the comment of Chris Wuthrich to the OP, i completed it and submit it now.

Among all related papers delivered by the search engine, the reference that suits closest in notation seems to be:

and the notations / relations $$(1.4)$$, $$(1.2)$$, $$(1.3)$$, $$(1.5)$$, $$(1.6)$$, $$(1.7)$$ in [MST] are inserted below for the convenience of the reader and for simpler reference. [MST] is based on an earlier paper, [MT91], The $$p$$-adic sigma function, Duke Math. J., 62 (1991).

We work with the elliptic curve of (affine) equation $$\tag{1.4} y^2+a_1xy+a_3 = x^3 + a_2x^2+a_4x+a_6\ .$$ Let $$x(t)$$ be the Laurent series of the shape $$\tag{1.2} x(t) =\frac 1{t^2}+\dots\in\Bbb Z_p((t)) \ ,$$ that expresses $$x$$ in terms of the parameter $$t=-x/y$$.

(MST) Theorem 1.3: There is exactly one odd function $$\sigma(t) = t+\dots \in\Bbb Z_p[[t]]$$ and constant $$c\in \Bbb Z_p$$ that together satisfy the differential equation $$\tag{1.3} x(t) +c = −\frac d\omega\left(\frac 1\sigma\;\frac{d\sigma}\omega\right)\ ,$$ where $$\omega$$ is the invariant differential $$dx/(2y+a_1x+a_3)$$ associated with the Weierstraß equation for E.

(MST) Remark 1.4. The condition that $$\sigma$$ is odd and that the coefficient of $$t$$ is 1 are essential.

Using $$x(t)$$ as in $$(1.2)$$ the series $$\tag{1.5} \wp(t) = x(t) +\frac 1{12}(a_1^2+4a_2)\in \Bbb Q((t))$$ satisfies $$(\wp')^2=4\wp^3-g_2\wp-g_3$$.

Then $$(1.6)$$ is cited from [MT91] with a sign self-correction: $$\tag{1.6} x(t) + c =\wp(t) -\frac 1{12}{\mathbf E}_2(E,\omega)\ .$$ This leads to (a slightly changed version of $$(1.7)$$): $$\tag{1.7'} 12c =(a_1^2+4a_2)-{\mathbf E}_2(E,\omega)\ .$$

The code seems to have notations inspired from the above. Let's see the parallel, if any, best in some ad-hoc generic example:

prec = 5
A.<a1,a2,a3,a4,a6,c> = PolynomialRing(QQ)
E = EllipticCurve(A, [a1, a2, a3, a4, a6])
EFormalGroup = E.formal_group()
elog = EFormalGroup.log(prec)
eexp = elog.reverse()
S.<z> = LaurentSeriesRing(A)

F = eexp( z + O(z^prec) )


This gives the following value for F:

sage: F
z - 1/2*a1*z^2 + (1/6*a1^2 - 1/3*a2)*z^3 + (-1/24*a1^3 + 1/3*a1*a2 - 1/2*a3)*z^4 + O(z^5)
sage: latex(F)


$$z - \frac{1}{2} a_{1}z^{2} + \left(\frac{1}{6} a_{1}^{2} - \frac{1}{3} a_{2}\right)z^{3} + \left(-\frac{1}{24} a_{1}^{3} + \frac{1}{3} a_{1} a_{2} - \frac{1}{2} a_{3}\right)z^{4} + O(z^{5})$$

This $$F$$ or F above is the "same one" as in the code, in the line F = F(z + O(z**prec)). (My elog is the fl from the sage code, my eexp is the exponential of the formal group, F = fl.reverse().)

Then the code associates the $$x$$-component, computed in F, in the line wp = self.x()(F) - and this maps the additive group generator / uniformizer z near zero into...

The line e2 = 12*c - a1**2 - 4*a2 corresponds up to sign to the above $$(1.7')$$, if e2 is a sort of $${\mathbf E}_2(E,\omega)$$. (The sign correction is an other issue...) Then the code considers the power series of the object

(1/z**2 - wp + e2/12)


I suppose that wp - e2/12 implements $$\wp(t) -\frac 1{12}{\mathbf E}_2(E,\omega)$$ from $$(1.6)$$. We also get rid of the pole $$1/z^2$$ since we know how to double integrate it, and in the code the double integration should avoid logarithmic terms. So in the code, the RHS of $$(1.6)$$ is assumed to be the RHS of $$(1.3)$$, staying for $$x(t)+c$$, we undo the RHS of $$(1.3)$$ by double integration and exponentiation so h = g.integral().integral() followed by h.exp() gets the part in $$\sigma$$ without the pole contribution in $$-1/z^2$$. Its double integration, $$\log z$$, followed by exponentiation, is then inserted as correction in the line sigma_of_z = z.power_series() * h.exp().

We come back by undoing the passage from the aditive formal group to the elliptic curve formal group, i.e. from z to F (or eexp), by plugging in the fl (or elog).

I suppose, the code of the formal group sigma method was designed to support some very special experiments for some very special purposes. The above generic curve does not have the method padic_sigma from the comment to the OP of Chris Wuthrich, but some elliptic curve defined over $$\Bbb Q$$ has it. As a final comment, the two methods seem to do different things (even for the same value of the constant $$c$$). Let us compare.

I will use the prime $$p=17$$, and the curve '14a'.

For the "old code" in the implementation of sigma and for the computed value of $$c$$ plugged in, we have:

p = 7
prec = 6
E = EllipticCurve('14a')
FG = E.formal_group()
cval = (E.a1()^2 + 4*E.a2() - E.padic_E2(p)) / 12
for coeff in FG.sigma().coefficients()[:7]:
print(coeff.subs({c: cval}) + O(p^prec))


This gives the first few coefficients in FG.sigma():

1 + O(7^6)
4 + 3*7 + 3*7^2 + 3*7^3 + 3*7^4 + 3*7^5 + O(7^6)
6*7 + 2*7^2 + 5*7^3 + 7^4 + 3*7^5 + O(7^6)
2 + 6*7^2 + 2*7^3 + 4*7^4 + 6*7^5 + O(7^6)
2 + 3*7 + 3*7^2 + 7^3 + 2*7^4 + 5*7^5 + O(7^6)
4 + 5*7 + 5*7^2 + 7^3 + 3*7^5 + O(7^6)
3 + 6*7 + 5*7^2 + 3*7^3 + 3*7^4 + 2*7^5 + O(7^6)


For the method padic_sigma named in the comment of Chris Wuthrich we have an ArithmeticError: p must be a good ordinary prime for the code:

for coeff in EllipticCurve('14a').padic_sigma(p)[:8]:
print(coeff + O(p^prec))


Using some other prime, $$p=13$$ for instance instead, we get different results. The coefficients of FG.sigma() are...

1 + O(13^6)
7 + 6*13 + 6*13^2 + 6*13^3 + 6*13^4 + 6*13^5 + O(13^6)
2 + 5*13^2 + 6*13^3 + 5*13^4 + 3*13^5 + O(13^6)
10*13 + 10*13^2 + 12*13^3 + 4*13^4 + 8*13^5 + O(13^6)
7*13 + 6*13^3 + 5*13^4 + 13^5 + O(13^6)
10 + 10*13 + 2*13^2 + 2*13^3 + 8*13^4 + 6*13^5 + O(13^6)
5 + 9*13 + 10*13^2 + 10*13^3 + 3*13^4 + 9*13^5 + O(13^6)


and on the other side...

sage:
....:         print(coeff + O(p^prec))
....:
O(13^6)
1 + O(13^6)
7 + 6*13 + 6*13^2 + 6*13^3 + 6*13^4 + 6*13^5 + O(13^6)
9 + 3*13 + 5*13^2 + 10*13^3 + 4*13^4 + O(13^6)
4 + 2*13 + 11*13^2 + 5*13^3 + 10*13^4 + 3*13^5 + O(13^6)
4 + 7*13 + 13^2 + 6*13^3 + 10*13^4 + 12*13^5 + O(13^6)
8*13 + 11*13^2 + 4*13^3 + 8*13^4 + 5*13^5 + O(13^6)
12 + 2*13 + 13^2 + 5*13^3 + 2*13^4 + O(13^6)


The first two coefficients agree, but starting with the term in third degree, the constant $$c$$ appears effectively:

sage: t = FG.sigma().parent().gen()
sage: FG.sigma() + O(t^5)
t + 1/2*t^2 + (1/2*c + 1/3)*t^3 + (3/4*c + 3/4)*t^4 + O(t^5)

• In short : If the reduction is good ordinary, use E.padic_sigma(p); it is the unique canonical $p$-adic sigma function in that situation. If the reduction is split multiplicative, there is way to get the square of it from E.tate_curve(p). Ask if needed. The non-split multiplicative case is not implemented. In the supersingular case there is no unique canonical choice. In sage the $p$-adic height is implemented as a function that takes values in the Dieudonne module $D_p(E)$, as Perrin-Riou does it in her work. These are methods of E.padic_lseries(p) for such primes. Apr 9, 2021 at 7:58
• Historic: The method described in the question was used before Mazur-Stein-Tate found a quick way to evaluate E2 using Kedlaya's algorithm. Instead I treated it as a power series with one unknown $c$ and used the fact that the canonical one for an ordinary prime has integer coefficients to determine congruences on the unknown $c$. That code stayed in sage for a while because it also worked for $p=2$ and $3$, while Kedlaya's algorithm wasn't (isn't ?) implemented there for these primes. Apr 9, 2021 at 8:01
• References for the ordinary case: Mazur-Tate The p-adic sigma function. Duke Math. J. 62 (1991), no. 3, 663–688. For the supersingular case : Perrin-Riou, Bernadette Arithmétique des courbes elliptiques à réduction supersingulière en p Experiment. Math. 12 (2003), no. 2, 155–186. And the older Bernardi, Dominique; Perrin-Riou, Bernadette Variante p-adique de la conjecture de Birch et Swinnerton-Dyer (le cas supersingulier) C. R. Acad. Sci. Paris Sér. I Math. 317 (1993), no. 3, 227–232.. Apr 9, 2021 at 8:27