source: sage/libs/mwrank/interface.py @ 5825:fbdd730a0fa0

Revision 5825:fbdd730a0fa0, 18.2 KB checked in by William Stein <wstein@…>, 6 years ago (diff)

Fix trac #434 -- but in mwrank interface.

Line 
1r"""
2Cremona's MWRANK Library
3
4This is the SAGE interface to John Cremona's MWRANK C++ library for
5arithmetic on elliptic curves.  The classes defined in this module
6give SAGE interpreter-level to access much of the functionality of
7MWRANK.  For most purposes it is not necessary to directly use these
8classes; instead, one can create an \class{EllipticCurve} and call
9methods that are implemented using this module.
10
11\note{This interface is a direct library-level interface to mwrank.}
12"""
13
14from sage.structure.sage_object import SageObject
15
16def set_precision(n):
17    """
18    Set the global NTL real number precision.  This has a massive
19    effect on the speed of mwrank calculations.  The default is n=15,
20    but it might have to be increased if a computation fails.  In this
21    case, one must recreate the mwrank curve from scratch after
22    resetting this precision.
23
24    NOTE -- this change is global and affects all of SAGE.
25   
26    INPUT:
27        n -- long
28
29    EXAMPLES:
30        sage: mwrank_set_precision(20)
31    """
32    # don't want to load mwrank every time SAGE starts up, so we do
33    # the import here.
34    from sage.libs.mwrank.mwrank import set_precision 
35    set_precision(n)
36
37class mwrank_EllipticCurve(SageObject):
38    r"""
39    The \class{mwrank_EllipticCurve} class represents an MWRANK
40    elliptic curve.
41    """
42    def __init__(self, ainvs, verbose=False):
43        r"""
44        Create the mwrank elliptic curve with invariants
45        \code{a_invs}, which is a list of $\leq 5$ \emph{integers}  $a_1$,
46        $a_2$, $a_3$, $a_4$, and $a_6$.
47
48        If strictly less than 5 invariants are given, then the first
49        ones are set to 0, so, e.g., \code{[3,4]} means $a_1=a_2=a_3=0$ and
50        $a_4=3$, $a_6=4$.
51
52        INPUT:
53            ainvs  --  a list of <= 5 integers
54            verbose -- if True, then all Selmer group computations
55                       will be verbose. (default: False).
56                       
57        EXAMPLES:
58        We create the elliptic curve $y^2 + y = x^3 + x^2 - 2x$.
59
60            sage: e = mwrank_EllipticCurve([0, 1, 1, -2, 0])
61            sage: e.ainvs()
62            [0, 1, 1, -2, 0]
63
64        This example illustrates that omitted $a$-invariants default to $0$:
65            sage: e = mwrank_EllipticCurve([3, -4])
66            sage: e
67            y^2 = x^3 + 3*x - 4
68            sage: e.ainvs()
69            [0, 0, 0, 3, -4]
70
71        The entries of the input list are coerced to \class{int}.
72        This has the effect that for float input, the integer parts of
73        the input coefficients are taken.
74            sage: e = mwrank_EllipticCurve([3, float(-4.8)]); e
75            y^2 = x^3 + 3*x - 4
76
77        When you enter a singular model you get an exception:
78            sage: e = mwrank_EllipticCurve([0, 0])
79            Traceback (most recent call last):
80            ...
81            ArithmeticError: Invariants (= [0, 0, 0, 0, 0]) do not describe an elliptic curve.
82        """
83        # import here to save time during startup (mwrank takes a while to init)
84        from sage.libs.mwrank.mwrank import _Curvedata
85       
86        if not isinstance(ainvs, list) and len(ainvs) <= 5:
87            raise TypeError, "ainvs must be a list of length at most 5."
88
89        # Pad ainvs on the beginning by 0's, so e.g.
90        # [a4,a6] works.
91        ainvs = [0]*(5-len(ainvs)) + ainvs
92
93        # Convert each entry to an int
94        try:
95            a_int = [int(x) for x in ainvs]
96        except (TypeError, ValueError):
97            raise TypeError, "ainvs must be a list of integers."
98        self.__ainvs = a_int
99        self.__curve = _Curvedata(a_int[0], a_int[1], a_int[2],
100                                  a_int[3], a_int[4])
101
102        if verbose:
103            self.__verbose = True
104        else:
105            self.__verbose = False
106
107        # place holders
108        self.__saturate = -2  # not yet saturated
109
110    def __reduce__(self):
111        return mwrank_EllipticCurve, (self.__ainvs, self.__verbose)
112       
113
114    def set_verbose(self, verbose):
115        """
116        Set the verbosity of printing of output by the 2-descent and
117        other functions.
118        INPUT:
119            verbosity -- bool; if True print lots of output
120        """
121        self.__verbose = verbose
122
123
124    def _curve_data(self):
125        return self.__curve
126
127    def ainvs(self):
128        return self.__ainvs
129
130    def isogeny_class(self, verbose=False):
131        return self.__curve.isogeny_class(verbose)
132
133    def __repr__(self):
134        a = self.ainvs()
135        s = "y^2"
136        if a[0] == -1:
137            s += "- x*y "
138        elif a[0] == 1:
139            s += "+ x*y "
140        elif a[0] != 0:
141            s += "+ %s*x*y "%a[0]
142        if a[2] == -1:
143            s += " - y"
144        elif a[2] == 1:
145            s += "+ y"
146        elif a[2] != 0:
147            s += "+ %s*y"%a[2]
148        s += " = x^3 "
149        if a[1] == -1:
150            s += "- x^2 "
151        elif a[1] == 1:
152            s += "+ x^2 "
153        elif a[1] != 0:
154            s += "+ %s*x^2 "%a[1]
155        if a[3] == -1:
156            s += "- x "
157        elif a[3] == 1:
158            s += "+ x "
159        elif a[3] != 0:
160            s += "+ %s*x "%a[3]
161        if a[4] == -1:
162            s += "-1"
163        elif a[4] == 1:
164            s += "+1"
165        elif a[4] != 0:
166            s += "+ %s"%a[4]
167        s = s.replace("+ -","- ")
168        return s
169
170
171    def two_descent(self,
172                    verbose = True,
173                    selmer_only = False,
174                    first_limit = 20,
175                    second_limit = 8,
176                    n_aux = -1,
177                    second_descent = True):
178        """
179        Compute 2-descent data for this curve.
180
181        INPUT:
182            verbose     -- (default: True) print what mwrank is doing
183            selmer_only -- (default: False) selmer_only switch
184            first_limit -- (default: 20) firstlim is bound on |x|+|z|
185            second_limit-- (default: 5)  secondlim is bound on log max {|x|,|z| },
186                                         i.e. logarithmic
187            n_aux       -- (default: -1) n_aux only relevant for general
188                           2-descent when 2-torsion trivial; n_aux=-1 causes default
189                           to be used (depends on method)
190            second_descent -- (default: True) second_descent only relevant for
191                           descent via 2-isogeny
192        OUTPUT:
193            Nothing -- nothing is returned
194        """
195        from sage.libs.mwrank.mwrank import _two_descent # import here to save time       
196        first_limit = int(first_limit)
197        second_limit = int(second_limit)
198        n_aux = int(n_aux)
199        second_descent = int(second_descent)
200        # TODO:  Don't allow limits above some value...???
201        #   (since otherwise mwrank just sets limit tiny)
202        self.__descent = _two_descent()
203        self.__descent.do_descent(self.__curve,
204                                      verbose,
205                                      selmer_only,
206                                      first_limit,
207                                      second_limit,
208                                      n_aux,
209                                      second_descent)
210        if not self.__two_descent_data().ok():
211            raise RuntimeError, "A 2-descent didn't not complete successfully."
212
213    def __two_descent_data(self):
214        try:
215            return self.__descent
216        except AttributeError:
217            self.two_descent(self.__verbose)
218            return self.__descent
219
220    def conductor(self):
221        """
222        Return the conductor of this curve, computed using Cremona's
223        implementation of Tate's algorithm.
224
225        NOTE: This is independent of PARI's.
226        """
227        return self.__curve.conductor()
228
229    def rank(self):
230        """
231        Returns the rank of this curve, computed using 2-descent.
232        """
233        return self.__two_descent_data().getrank()
234
235    def selmer_rank_bound(self):
236        r"""
237        Bound on the rank of the curve, computed using the 2-selmer
238        group.  This is the rank of the curve minus the rank of the
239        2-torsion, minus a number determined by whatever mwrank was
240        able to determine related to $\Sha(E)[2]$ (e.g., using a
241        second descent or if there is a rational $2$-torsion point,
242        then there may be an isogeny to a curve with trivial
243        $\Sha(E)$).  In many cases, this is the actual rank of the
244        curve, but in general it is just $\geq$ the true rank.
245
246        EXAMPLES:
247        The following is the curve 960D1, which has rank 0,
248        but Sha of order 4.
249       
250            sage: E = mwrank_EllipticCurve([0, -1, 0, -900, -10098])
251            sage: E.selmer_rank_bound()
252            0
253
254        In this case this was resolved using a second descent.
255
256            sage: E = mwrank_EllipticCurve([0, -1, 0, -900, -10098])
257            sage: E.two_descent(second_descent = False, verbose=False)
258            sage: E.selmer_rank_bound()
259            2
260           
261        Above, the \method{selmer_rank_bound} gives 0 instead of 2,
262        because it knows Sha is nontrivial.  In contrast, for the
263        curve 571A, also with rank 0 and $\Sha$ of order 4, we obtain
264        a worse bound:
265       
266            sage: E = mwrank_EllipticCurve([0, -1, 1, -929, -10595])
267            sage: E.selmer_rank_bound()
268            2
269            sage: E.rank()
270            0
271        """
272        return self.__two_descent_data().getselmer()
273
274    def regulator(self):
275        """
276        Return the regulator of the saturated Mordell-Weil group.
277
278            sage: E = mwrank_EllipticCurve([0, 0, 1, -1, 0])
279            sage: E.regulator()
280            0.05111140823996884
281
282        """
283        self.saturate()
284        if not self.certain():
285            raise RuntimeError, "Unable to saturate Mordell-Weil group."
286        R = self.__two_descent_data().regulator()
287        return float(R)
288
289    def saturate(self, bound=-1):
290        """
291        Compute the saturation of the Mordell-Weil group at all
292        primes up to bound.
293
294        INPUT:
295            bound -- int (default: -1)   -1 saturate at *all* primes,
296                                          0 -- do not saturate
297                                          n -- saturate at least at all primes <= n.
298        """
299        bound = int(bound)
300        if self.__saturate < bound:
301            self.__two_descent_data().saturate(bound)
302            self.__saturate = bound
303
304    def gens(self):
305        """
306        Return a list of the generators for the Mordell-Weil group. 
307        """
308        self.saturate()
309        return eval(self.__two_descent_data().getbasis().replace(":",","))
310
311    def certain(self):
312        r"""
313        True if the last \method{two_descent} call provably correctly
314        computed the rank.  If \method{two_descent} hasn't been
315        called, then it is first called by \method{certain}
316        using the default parameters.
317       
318        EXAMPLES:
319        A $2$-descent does not determine $E(\Q)$ with certainty
320        for the curve $y^2 + y = x^3 - x^2 - 120x - 2183$.
321       
322            sage: E = mwrank_EllipticCurve([0, -1, 1, -120, -2183])
323            sage: E.two_descent(False)
324            ...
325            sage: E.certain()
326            False
327            sage: E.rank()   
328            0
329
330        The rank of $E$is actually 0 (as one could see by computing
331        the L-function), but $\Sha$ has order 4 and the $2$-torsion is
332        trivial, so mwrank does not conclusively determine the rank.
333
334            sage: E.selmer_rank_bound()
335            2
336        """
337        return bool(self.__two_descent_data().getcertain())
338                       
339    #def fullmw(self):
340    #    return self.__two_descent_data().getfullmw()
341
342    def CPS_height_bound(self):
343        r"""
344        Return the Cremona-Prickett-Siksek height bound.  This is a
345        floating point number $B$ such that if $P$ is a point on the curve,
346        then the naive logarithmetic height of $P$ is off from the
347        canonical height by at most $B$.
348
349        \begin{notice}
350        We assume the model is minimal!
351        \end{notice}
352        """
353        return self.__curve.cps_bound()
354
355    def silverman_bound(self):
356        r"""
357        Return the Silverman height bound.  This is a number $B$ such
358        that if $P$ is a point on the curve, then the naive
359        logarithmetic height of $P$ is off from the canonical height by
360        at most $B$.
361
362        \begin{notice}
363        We assume the model is minimal!
364        \end{notice}
365        """
366        return self.__curve.silverman_bound()
367
368
369class mwrank_MordellWeil(SageObject):
370    r"""
371    The \class{mwrank_MordellWeil} class represents a subgroup of a
372    Mordell-Weil group.  Use this class to saturate a specified list
373    of points on an \class{mwrank_EllipticCurve}, or to search for
374    points up to some bound.
375    """
376    def __init__(self, curve, verbose=True, pp=1, maxr=999):
377        r"""
378        Create a \class{mwrank_MordellWeil} instance.
379
380        INPUT:
381            curve -- \class{mwrank_EllipticCurve} instance
382            verbose -- bool
383            pp -- int
384            maxr -- int
385        """
386        if not isinstance(curve, mwrank_EllipticCurve):
387            raise TypeError, "curve (=%s) must be an mwrank_EllipticCurve"%curve
388        self.__curve = curve
389        self.__verbose = verbose
390        self.__pp = pp
391        self.__maxr = maxr
392        if verbose:
393            verb = 1
394        else:
395            verb = 0
396        from sage.libs.mwrank.mwrank import _mw # import here to save time
397        self.__mw = _mw(curve._curve_data(), verb, pp, maxr)
398
399    def __reduce__(self):
400        return mwrank_MordellWeil, (self.__curve, self.__verbose, self.__pp, self.__maxr)
401
402    def __repr__(self):
403        return "Subgroup of Mordell Weil group: %s"%self.__mw
404
405    def process(self, v, sat=0):
406        """
407        This function allows one to add points to a mwrank_MordellWeil object.
408
409        Process points in the list v, with saturation at primes up to
410        sat.  If sat = 0 (the default), then saturate at all primes.
411
412        INPUT:
413       
414            v -- a point (3-tuple of ints), or a
415                 list of 3-tuples of integers, which define points on the curve.
416                 
417            sat -- int, saturate at primes up to sat, or at all primes if sat=0.
418           
419        """
420        if not isinstance(v, list):
421            raise TypeError, "v (=%s) must be a list"%v
422        sat = int(sat)
423        for P in v:
424            if not isinstance(P, (list,tuple)) or len(P) != 3:
425                raise TypeError, "v (=%s) must be a list of 3-tuples of ints"%v
426            self.__mw.process(P, sat)
427
428    def regulator(self):
429        """
430        Return the regulator of the points in this subgroup of
431        the Mordell-Weil group.
432        """
433        return self.__mw.regulator()
434
435    def rank(self):
436        """
437        Return the rank of this subgroup of the Mordell-Weil group.
438        """
439        return self.__mw.getrank()
440
441    def saturate(self, max_prime=-1, odd_primes_only=False):
442        r"""
443        Saturate this subgroup of the Mordell-Weil group.
444       
445        INPUT:
446            max_prime (int) -- (default: 97), saturation is performed
447                               for all primes up to max_prime
448                               
449            odd_primes_only (bool) -- only do saturation at odd primes
450
451        OUTPUT:
452            ok (bool) -- True if and only if the saturation
453                         is provably correct at \emph{all} primes.
454            index (int) -- The index of the group generated by
455                           points in their saturation
456            saturation (list) -- list of points that form
457                                 a basis for the saturation
458
459        \begin{notice}
460        We emphasize that if this function returns True as the first
461        return argument, then the points it found are saturated at
462        \emph{all} primes, i.e., saturating at the primes up to
463        \code{max_prime} are sufficient to saturate at all primes.
464        Note that the function might not have needed to saturate at
465        all primes up to \code{max_prime}.
466        It has worked out what prime you need to saturate up to,
467        and that prime is $\leq $ \code{max_prime}.
468       
469        \end{notice}
470
471        \begin{notice}
472        Currently (July 2005), this does not remember the result of
473        calling search.  So calling search up to height 20 then
474        calling saturate results in another search up to height 18.
475        \end{notice}
476
477           
478        """
479        ok, index, unsat = self.__mw.saturate(int(max_prime), odd_primes_only)
480        return bool(ok), str(index), unsat
481
482    def search(self, height_limit=18, verbose=False):
483        r"""
484        Search for new points, and add them to this subgroup of the
485        Mordell-Weil group.
486
487        INPUT:
488            height_limit -- float (default: 18) search up to
489                            this logarithmetic height.
490                   On 32-bit machines, h_lim MUST be < 21.48 else
491                   exp(h_lim)>2^31 and overflows.
492        """
493        # On 64-bit machines, h_lim must be < 43.668, but
494        #           it's unlikely (in 2005) that you would ever
495        #          search higher than 21.
496        height_limit = float(height_limit)
497        if height_limit >= 21.4:
498            raise ValueError, "The height limit must be < 21.4."
499
500        moduli_option = 0  # Use Stoll's sieving program... see strategies in ratpoints-1.4.c
501       
502        ##            moduli_option -- int (default: 0); if > 0; a flag used to determine
503        ##                    the moduli that are used in sieving
504        ##                       1 -- first 10 odd primes; the first one you
505        ##                            would think of.
506        ##                       2 -- three composites; $2^6\cdot 3^4$, ... TODO
507        ##                             (from German mathematician J. Gebel;
508        ##                               personal conversation about SIMATH)
509        ##                       3 -- nine prime powers; $2^5, \ldots$;
510        ##                            like 1 but includes powers of small primes
511        ##                    TODO: Extract the meaning from mwprocs.cc; line 776 etc.
512       
513        verbose == bool(verbose)
514        self.__mw.search(height_limit, moduli_option, verbose)
515
516    def points(self):
517        """
518        Return a list of the generating points in this Mordell-Weil
519        group.
520        """
521        return eval(self.__mw.getbasis().replace(":",","))
522       
523       
Note: See TracBrowser for help on using the repository browser.