The implementation of isogenies was started in Sage 4.1 by Dan Shumow, then taken up by many authors. This worksheet tries to review the known limits of the current implementation (linking to relevant trac tickets), point out its weaknesses and suggest improvements.
The idea is to have a work plan for the upcoming Sage Days that will take place during the ECC summer school in Nancy on Sept. 12-16.
So, here's the status of morphisms of elliptic curves. Sage implements isomorphisms of Weierstrass models:
Generic morphism: From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 7*x over Number Field in th with defining polynomial x^2 + 3 To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 7/16*x over Number Field in th with defining polynomial x^2 + 3 Via: (u,r,s,t) = (2, 0, 0, 0) Generic morphism: From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 7*x over Number Field in th with defining polynomial x^2 + 3 To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 7/16*x over Number Field in th with defining polynomial x^2 + 3 Via: (u,r,s,t) = (2, 0, 0, 0) |
Sage also implements isogenies. For example, if a generator of the kernel is given, a canonical isogeny is computed via Vélu formulae:
Time: CPU 2.09 s, Wall: 2.20 s Time: CPU 2.09 s, Wall: 2.20 s |
Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 7/16*x over Number Field in th with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 = x^3 + (-7/4)*x over Number Field in th with defining polynomial x^2 + 3 Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 7/16*x over Number Field in th with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 = x^3 + (-7/4)*x over Number Field in th with defining polynomial x^2 + 3 |
Alternatively we can pass the polynomial vanishing on the abscissae of the kernel, Vélu's formulae are applied in this case too. And here's the first thing I hate: notice the difference between the timings. This is because two completely separate implementations of Vélu's formulae are used in the two cases (see Dan Shumow's thesis).
Time: CPU 0.04 s, Wall: 0.05 s Time: CPU 0.04 s, Wall: 0.05 s |
Unfortunately, the implementation of Vélu's formulae from the kernel polynomial is incomplete and does not handle even-degree isogenies other than degree 2 ones and the multiplication-by-2 map.
Traceback (click to the left of this block for traceback) ... NotImplementedError: For basic Kohel's algorithm, if the kernel degree is even then the kernel must be contained in the two torsion. Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "_sage_input_7.py", line 10, in <module>
exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("aCA9IEUyLmRpdmlzaW9uX3BvbHlub21pYWwoNikubW9uaWMoKQpFMi5pc29nZW55KGgp"),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
File "", line 1, in <module>
File "/tmp/tmppWyOFj/___code___.py", line 4, in <module>
exec compile(u'E2.isogeny(h)
File "", line 1, in <module>
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_field.py", line 789, in isogeny
return EllipticCurveIsogeny(self, kernel, codomain, degree, model, check=check)
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_curve_isogeny.py", line 914, in __init__
self.__init_from_kernel_polynomial(kernel, degree)
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_curve_isogeny.py", line 2063, in __init_from_kernel_polynomial
raise NotImplementedError, "For basic Kohel's algorithm, if the kernel degree is even then the kernel must be contained in the two torsion."
NotImplementedError: For basic Kohel's algorithm, if the kernel degree is even then the kernel must be contained in the two torsion.
|
We can test isogenies for equality. Isogenies are not unique objects, though; but elliptic curves aren't either, so I guess that this problem can wait.
(True, False) (True, False) |
however, equality test is buggy: only the __hash__ method takes consideration of the domain and codomain (thus == will work as expected), but the __cmp__ function only compares kernel polynomials, really! I should make a ticket for this.
Because of the poor implementation of the __cmp__ method, I suspect there are other ways of making equality tests fail.
(False, 0) (False, 0) |
Isogenies internally use WeierstrassIsomorphism to reduce to some canonical forms, but here's one of the things I hate the most: isogenies cannot be composed with isomorphisms.
Traceback (click to the left of this block for traceback) ... Via: (u,r,s,t) = (2, 0, 0, 0)) codomain Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "_sage_input_10.py", line 10, in <module>
exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("cGhpICogaQ=="),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
File "", line 1, in <module>
File "/tmp/tmptgPeZN/___code___.py", line 2, in <module>
exec compile(u'phi * i
File "", line 1, in <module>
File "map.pyx", line 642, in sage.categories.map.Map.__mul__ (sage/categories/map.c:4148)
TypeError: self (=Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 7/16*x over Number Field in th with defining polynomial x^2 + 3 to Elliptic Curve defined by y^2 = x^3 + (-7/4)*x over Number Field in th with defining polynomial x^2 + 3) domain must equal right (=Generic morphism:
From: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 7*x over Number Field in th with defining polynomial x^2 + 3
To: Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 7/16*x over Number Field in th with defining polynomial x^2 + 3
Via: (u,r,s,t) = (2, 0, 0, 0)) codomain
|
An important subclass of isogenies are the endomorphisms. Unfortunately what Sage really implements are Vélu isogenies (also called normalized isogenies), and endomorphisms are in general not normalized. I am currently working on this and will soon post a patch that allows to construct non-normalized isogenies.
(False, True) (False, True) |
Partly because of this, there is no endomorphism object for elliptic curves and there are two different functions to compute the multiplication-by-m map. Yes, contraddicting what I said before, the second returns a non-normalized isogeny; the point is that this and EllipticCurveIsogeny.dual() are the only two ways of constructing non-normalized isogenies.
Time: CPU 0.10 s, Wall: 0.10 s [<type 'sage.rings.fraction_field_element.FractionFieldElement'>, <type 'sage.rings.fraction_field_element.FractionFieldElement'>] Time: CPU 0.10 s, Wall: 0.10 s [<type 'sage.rings.fraction_field_element.FractionFieldElement'>, <type 'sage.rings.fraction_field_element.FractionFieldElement'>] |
<class 'sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny'>\ ; Time: CPU 3.94 s, Wall: 4.14 s <class 'sage.schemes.elliptic_curves.ell_curve_isogeny.EllipticCurveIsogeny'> Time: CPU 3.94 s, Wall: 4.14 s |
Also observe the huge difference between the timings (don't even consider computing the multiplication-by-13!). I don't even understand what exactly takes so long, I believe there might be a serious bug in the way Vélu's formulae are implemented. This issue is already pointed out in ticket #8014 and is a serious obstacle to seriously implementing an endomorphism object. The solution would be to store only the information relative to the kernel of the isogeny, and to lazily compute all the other quantities upon request.
It is also worth mentioning that the kernel of a normalized isogeny can be computed from the knowledge of its domain, codomain and degree. Currently only Stark's algorithm is implemented; ticket 11095 adds the BMSS algorithm, which is faster and slightly more general, and does some cleanup.
Time: CPU 0.60 s, Wall: 0.63 s True Time: CPU 0.60 s, Wall: 0.63 s True |
Unfortunately, the situation where the isogeny is not normalized is more complex. As said before, I am currently working on this and shall soon post a patch.
Traceback (click to the left of this block for traceback) ... ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 2 Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "_sage_input_15.py", line 10, in <module>
exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("RS5pc29nZW55KE5vbmUsIHBoaS5jb2RvbWFpbigpLCBkZWdyZWU9Mik="),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
File "", line 1, in <module>
File "/tmp/tmpZrEcOJ/___code___.py", line 3, in <module>
exec compile(u'E.isogeny(None, phi.codomain(), degree=_sage_const_2 )
File "", line 1, in <module>
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_field.py", line 789, in isogeny
return EllipticCurveIsogeny(self, kernel, codomain, degree, model, check=check)
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_curve_isogeny.py", line 903, in __init__
(pre_isom, post_isom, E, codomain, kernel) = compute_sequence_of_maps(E, codomain, degree)
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_curve_isogeny.py", line 3875, in compute_sequence_of_maps
ker_poly = compute_isogeny_kernel_polynomial(E1pr, E2pr, ell)
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_curve_isogeny.py", line 3706, in compute_isogeny_kernel_polynomial
ker_poly = compute_isogeny_starks(E1, E2, ell)
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_curve_isogeny.py", line 3602, in compute_isogeny_starks
raise ValueError, "The two curves are not linked by a cyclic normalized isogeny of degree %s"%ell
ValueError: The two curves are not linked by a cyclic normalized isogeny of degree 2
|
Another major feature missing in sage is unseparable isogenies: there's simply no way of constructing them. Besides that, the multiplication_by_m method also fails to compute unseparable endomorphisms. See also ticket 6413.
Traceback (click to the left of this block for traceback) ... ZeroDivisionError: fraction field element division by zero Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "_sage_input_16.py", line 10, in <module>
exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("SyA9IEdGKDUpCkUgPSBFbGxpcHRpY0N1cnZlKEssIFszLCAyXSkKRS5tdWx0aXBsaWNhdGlvbl9ieV9tKDUp"),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
File "", line 1, in <module>
File "/tmp/tmpzZw7FJ/___code___.py", line 5, in <module>
exec compile(u'E.multiplication_by_m(_sage_const_5 )
File "", line 1, in <module>
File "/home/defeo/sage-4.7.1.rc2/local/lib/python2.6/site-packages/sage/schemes/elliptic_curves/ell_generic.py", line 2155, in multiplication_by_m
my = ((2*y+a1*x+a3)*mx.derivative(x)/m - a1*mx-a3)/2
File "element.pyx", line 1557, in sage.structure.element.RingElement.__div__ (sage/structure/element.c:12803)
File "coerce.pyx", line 714, in sage.structure.coerce.CoercionModel_cache_maps.bin_op (sage/structure/coerce.c:6436)
File "element.pyx", line 1555, in sage.structure.element.RingElement.__div__ (sage/structure/element.c:12777)
File "fraction_field_element.pyx", line 680, in sage.rings.fraction_field_element.FractionFieldElement._div_ (sage/rings/fraction_field_element.c:6099)
ZeroDivisionError: fraction field element division by zero
|
Finally, it should also be mentioned that Sage does not implement any algorithm to compute the kernel polynomial of an isogeny from its domain and codomain (even when the isogeny is normalized) in case the characteristic of the base field is too small. Ticket 11094 aims at solving this problem at least for finite fields, but these algorithms are quite complex and have some big prerequisites; in particular:
Implementing these algorithms is a long-term goal and shouldn't be considered for the next Sage Days.
Now I will go through some proposed improvements to the current situation. Each of these should translate to one or more tickets to be implemented at the Sage Days.
There are many ways of representing (aka computing) an isogeny, but here we are mainly interested in what is usually called explicit isogenies. Given an isogeny between two curves in (affine) Weierstrass form, it is always possible to write it as a pair of rational fractions
If the isogeny is separable, Vélu's formulae show that a monic polynomial h uniquely determines g, p_1, p_2 and q up to isomorphism of the codomain curve. Vélu's formulae are not the only possible canonical choice to represent separable isogenies, but they are historically relevant and have some advantages over other choices. Their main disadvantage is that the Vélu isogeny associated to the m-torsion subgroup is not the multiplication-by-m endomorphism (in general, not even after rescaling).
If the isogeny is unseparable, then it is the composition of a Frobenius map followed by a separable isogeny (Silverman AEC, Corollary II.2.12, I believe this is also true for non-perfect fields, right?). Hence any isogeny can be uniquely decomposed as
So, I propose to implement isogenies with a class looking like this:
|
|
Notice that this representation encompasses well all sort of isogenies:
However, the constant zero isogeny requires a special treatment: we can use h=0, but this does not uniquely define the codomain, which must be given explicitely.
For efficiency, no big computation should be performed in the __init__ method. The rational maps can be computed as lazy attributes using Vélu's formulae; if implemented correctly, this can be almost as fast as computing the multiplication-by-m maps starting from the m-division polynomial.
For a good implementation of Vélu's formulae, I suggest following these guidelines:
Some people may not be happy with my choice of inputs to the __init__ method: while u_degree and urst will probably please anyone, h may not be consensual. The polynomial h, in fact, factors as a product ab^2, where a|(x^3+b_2x^2+b_4x+b_6) vanishes on the abscissae of the 2-torsion part of the kernel, and b vanishes on all the other abscissae with multiplicity exactly one. Following the convention for division polynomials, it is traditional to input the polynomial ab, instead of h to Vélu formulae (this is the input currently expected in Sage, for example). Unfortunately, I don't think there's any formula using this input that can avoid a GCD between ab and x^3+b_2x^2+b_4x+b_6. For maximum compatibility, and to make everyone happy, I suggest the following convention:
We may also allow to input an inseparable polynomial h. For example, if h=H(x^q) for some separable polynomial H, we can consider that the user is asking to compose the q-th power Frobenius with the separable isogeny of E^{(q)} having kernel polynomial H.
For compatibility with division polynomials, we may also accept a non-monic h. If c is the leading coefficient of h, this would be equivalent to create an isogeny with parameters h'=h/c and (u,r,s,t)=(\sqrt{c},0,0,0). If c has no square root, we could extend the base field or move to a quadratic twist, although it is probably better to raise an exception.
The current implementation allows to input a list of kernel points instead of a kernel polynomial to the constructor. Although seldom used in real-world computations, and limited to separable isogenies, this feature is often handy and I suggest keeping it:
The current implementation of EllipticCurveIsogeny inherits from sage.categories.morphism.Morphism. This is not very tight and looses most of the benefits of the coercion model. I'd like some advice from coercion model experts, here. I think that isogenies should inherit at least from
but maybe sage.schemes contains some possibly better superclasses:
By looking at sage.schemes.elliptic_curves.ell_point, it seems that at some point it was considered making EllipticCurvePoint_field inherit from it, but then this has been abandoned in favour AdditiveGroupElement.
Isogenies with given domain and codomain should have a parent. Currently this is
Set of Morphisms from Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x + 2 over Rational Field to Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 2*x + 1 over Rational Field in Category of hom sets in Category of sets Set of Morphisms from Abelian group of points on Elliptic Curve defined by y^2 = x^3 + x + 2 over Rational Field to Abelian group of points on Elliptic Curve defined by y^2 = x^3 + 2*x + 1 over Rational Field in Category of hom sets in Category of sets |
which seems a reasonable choice, but this should be extended to inherit from AbelianGroup at least. Properly speaking, this parent is \mathrm{Hom}_K(E_1,E_2), where K is the base field of the elliptic curves.
I think it would be useful to implement some subclasses of isogenies and their respective parents:
with methods and optimizations specific to each class. For example, the endomorphisms should form a ring, containing the automorphisms as its unit group.
Some more interesting methods could be added to these classes. For example, currently the Frobenius endomorphism is given as the generator of an order in an imaginary quadratic field
Order in Number Field in phi with defining polynomial x^2 + 2*x + 11 Order in Number Field in phi with defining polynomial x^2 + 2*x + 11 |
in this implementation, the Frobenius could be given as an element of the endomorphism ring, instead, and its parent could implement a method 'minimal_order' (I cannot think of a better name right now) returning the order above, and registering a coercion from this order to the endomorphism ring (and maybe a conversion in the other direction, but this is computationally harder). In the future, a method "order" could also be implemented, computing the order isomorphic to End(E).
I hope to have given a sound and practical specification for isogenies. This document probably contains errors and inconstincencies. I'd appreciate feedback, especially on the class hierarchy.
The degree to which this project will be completed during the summer school will largely depend on the programming skills of the students, but I'd be surprised if at the end of the week we had a patch mature enough to be merged. If this project succeeds, I'll take care of coordinating the work after the summer school and uploading the relevant patches.
|
|