Changeset 3190:b1427bf737c5


Ignore:
Timestamp:
02/25/07 21:00:36 (6 years ago)
Author:
dmharvey@…
Branch:
default
Message:

algorithm for computing padic E2 in time sqrt(p)

Location:
sage/schemes/elliptic_curves
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • sage/schemes/elliptic_curves/ell_rational_field.py

    r2836 r3190  
    4040from sage.misc.functional import log 
    4141 
    42 # Use some interval arithmetic to guaranty correctness.  We assume 
     42# Use some interval arithmetic to guarantee correctness.  We assume 
    4343# that alpha is computed to the precision of a float. 
    4444IR = rings.RIF 
     
    43884388            2 + 4*5 + 2*5^3 + 5^4 + 3*5^5 + 2*5^6 + 5^8 + 3*5^9 + 4*5^10 + 2*5^11 + 2*5^12 + 2*5^14 + 3*5^15 + 3*5^16 + 3*5^17 + 4*5^18 + 2*5^19 + 4*5^20 + 5^21 + 4*5^22 + 2*5^23 + 3*5^24 + 3*5^26 + 2*5^27 + 3*5^28 + O(5^30) 
    43894389 
     4390        Here's one using the $p^{1/2}$ algorithm: 
     4391            sage: EllipticCurve([-1, 1/4]).padic_E2(3001, 1, check=True)  # long time (< 10s) 
     4392            1907 + 2819*3001 + 1124*3001^2 + O(3001^3) 
     4393 
    43904394        """ 
    43914395        if check_hypotheses: 
     
    44284432               " at p." 
    44294433 
    4430         # Need to increase precision a little to compensate for precision 
    4431         # losses during the computation. (See monsky_washnitzer.py 
    4432         # for more details.) 
    4433         adjusted_prec = monsky_washnitzer.adjusted_prec(p, prec) 
    4434          
    4435         if check: 
    4436             trace = None 
     4434        # If p is large, we use the matrix_of_frobenius_alternate 
     4435        # function, which has better asymptotic performance for large p. 
     4436        # The right cutoff should depend on N, but this has not been 
     4437        # investigated properly yet, so for now we have an arbitrary 
     4438        # cutoff at p = 3000, which works decently on sage.math when 
     4439        # the precision is very low. 
     4440 
     4441        if p < 3000: 
     4442            # Need to increase precision a little to compensate for precision 
     4443            # losses during the computation. (See monsky_washnitzer.py 
     4444            # for more details.) 
     4445            adjusted_prec = monsky_washnitzer.adjusted_prec(p, prec) 
     4446         
     4447            if check: 
     4448                trace = None 
     4449            else: 
     4450                trace = self.ap(p) 
     4451 
     4452            base_ring = rings.Integers(p**adjusted_prec) 
     4453            output_ring = rings.pAdicField(p, prec) 
     4454         
     4455            R, x = rings.PolynomialRing(base_ring, 'x').objgen() 
     4456            Q = x**3 + base_ring(X.a4()) * x + base_ring(X.a6()) 
     4457            frob_p = monsky_washnitzer.matrix_of_frobenius( 
     4458                             Q, p, adjusted_prec, trace) 
     4459 
    44374460        else: 
    4438             trace = self.ap(p) 
    4439  
    4440         base_ring = rings.Integers(p**adjusted_prec) 
    4441         output_ring = rings.pAdicField(p, prec) 
    4442          
    4443         R, x = rings.PolynomialRing(base_ring, 'x').objgen() 
    4444         Q = x**3 + base_ring(X.a4()) * x + base_ring(X.a6()) 
    4445         frob_p = monsky_washnitzer.matrix_of_frobenius( 
    4446                         Q, p, adjusted_prec, trace) 
     4461            base_ring = rings.Integers(p**prec) 
     4462            output_ring = rings.pAdicField(p, prec) 
     4463            frob_p = monsky_washnitzer.matrix_of_frobenius_alternate( 
     4464                          X.a4(), X.a6(), p, prec) 
     4465             
    44474466 
    44484467        if check: 
  • sage/schemes/elliptic_curves/monsky_washnitzer.py

    r2874 r3190  
    33 
    44The most interesting functions to be exported here are matrix_of_frobenius() 
    5 and adjusted_prec(). 
     5and adjusted_prec(), and matrix_of_frobenius_alternate(). 
    66 
    77Currently this code is limited to the case $p \geq 5$ (no $GF(p^n)$ 
     
    1515  -- Edixhoven, B., ``Point counting after Kedlaya'', EIDMA-Stieltjes graduate 
    1616     course, Lieden (lecture notes?). 
     17  -- Harvey, D. ``Kedlaya's algorithm in larger characteristic'', 
     18     http://arxiv.org/abs/math.NT/0610973 
    1719 
    1820AUTHORS: 
     
    2325       more documentation, added Newton iteration method, added more complete 
    2426       "trace trick", integrated better into SAGE. 
     27    -- David Harvey (Feb 2007): added algorithm with sqrt(p) complexity 
    2528 
    2629""" 
     
    4043from sage.rings.ring import CommutativeAlgebra 
    4144from sage.structure.element import CommutativeAlgebraElement 
    42  
    43 from sage.rings.arith import binomial 
    44 from sage.misc.functional import log, ceil 
     45from sage.matrix.matrix_space import MatrixSpace 
     46 
     47from sage.rings.arith import binomial, floor 
     48from sage.misc.functional import log, ceil, sqrt 
    4549 
    4650 
     
    12561260 
    12571261 
     1262 
     1263#***************************************************************************** 
     1264# From here on is an implementation of D. Harvey's modification of Kedlaya's 
     1265# algorithm, ``Kedlaya's algorithm in larger characteristic'' (see arXiv). 
     1266# Actually the algorithm implemented here is better than the one in the paper; 
     1267# the $N^3$ contribution is reduced to $N^{5/2}$, and incorporates some ideas 
     1268# from ``Linear recurrences with polynomial coefficients and application to 
     1269# integer factorization and Cartier-Manin operator'' (Bostan, Gaudry, Schost) 
     1270# to reduce the running time by another factor of $\log p$. 
     1271# 
     1272# todo: I haven't yet done the precision analysis (i.e. the analogue of 
     1273# lemmas 2 and 3 from Kedlaya's original paper), so for now I'm playing it 
     1274# safe, using more precision than is presumably necessary. 
     1275# 
     1276# AUTHOR: 
     1277#    -- David Harvey (2007-02) 
     1278# 
     1279#***************************************************************************** 
     1280 
     1281 
     1282def shift_evaluation_points(R, values, b, r, p, cache): 
     1283    r""" 
     1284    Given the values of a polynomial $P(x)$ on an arithmetic progression, 
     1285    this function computes the values of $P(x)$ on a shift of that 
     1286    arithmetic progression. 
     1287     
     1288    INPUT: 
     1289      p -- a prime 
     1290      R -- coefficient ring, of the form $\ZZ/p^m \ZZ$ 
     1291      values -- a list of $d+1$ values of a polynomial $P(x)$ of degree $d$, 
     1292          at the $d+1$ points $x = 0, r, 2r, \ldots, dr$. 
     1293      b -- an integer 
     1294      r -- a nonzero integer 
     1295      cache -- a dictionary 
     1296 
     1297    OUTPUT: 
     1298      A list of $d+1$ values $P(b)$, $P(b+r)$, ..., $P(b+dr)$. 
     1299      (Also the cache might get updated.) 
     1300 
     1301    NOTE: The polynomial $P$ is *not* part of the input. 
     1302 
     1303    PRECONDITIONS: $d$ must be less than $p$ 
     1304       
     1305    EXAMPLES: 
     1306    Run it on some random input, and compare to a naive implementation: 
     1307        sage: from sage.schemes.elliptic_curves.monsky_washnitzer import shift_evaluation_points 
     1308        sage: R = Integers(7**3) 
     1309        sage: S.<x> = PolynomialRing(R) 
     1310        sage: r = 3 
     1311        sage: for d in range(7): 
     1312        ...      for b in range(50): 
     1313        ...         p = S([R.random_element() for i in range(d+1)]) 
     1314        ...         L = [p(i*r) for i in range(d+1)] 
     1315        ...         shifted = shift_evaluation_points(R, L, b, r, 7, {}) 
     1316        ...         if shifted != [p(b + i*r) for i in range(d+1)]: 
     1317        ...            print "failed" 
     1318 
     1319    ALGORITHM: 
     1320    The basic idea (as described in the Bostan/Gaudry/Schost paper) is as 
     1321    follows. The Lagrange interpolation formula says that 
     1322      $$ P(x) = \sum_{i=0}^d P(ir) \prod_{j \neq i} (x-jr)/(ir-jr). $$ 
     1323    We substitute $x = b+kr$, and rearrange to eventually obtain 
     1324      $$ P(b+kr) = r^{-d} \prod_{j=0}^d (b+(k-j)r) 
     1325        \sum_{i=0}^d [P(ir) \prod_{j \neq i} (i-j)^{-1}] \frac{1}{b+(k-i)r}. $$ 
     1326    The point is that the latter sum is a convolution and so can be 
     1327    evaluated for all $k$ simultaneously by a single polynomial multiplication. 
     1328 
     1329    There are some complications related to non-invertible elements of $R$, 
     1330    but we just deal with it :-) 
     1331         
     1332    """ 
     1333 
     1334    d = len(values) - 1 
     1335    assert p > d 
     1336 
     1337    try: 
     1338        # try to retrieve some precomputed stuff from the cache 
     1339        bad, input_scale, S, kernel, output_scale = cache[d, b, r] 
     1340 
     1341    except KeyError: 
     1342        # that didn't work; need to compute all of it 
     1343         
     1344        # figure out for which i in [-d, d] is b+ir not invertible; 
     1345        # put these i into a "bad" list 
     1346        i = (-R(b)/r).lift() % p 
     1347        bad = [] 
     1348        if i <= d: 
     1349            bad.append(i) 
     1350        if i >= p-d: 
     1351            bad.append(i-p) 
     1352 
     1353        # compute inverse factorials [1/0!, 1/1!, 1/2!, ..., 1/d!] in R 
     1354        total = R(1) 
     1355        for k in range(2, d+1): 
     1356            total = total * R(k) 
     1357        inv = total**(-1) 
     1358        inv_fac = [inv] 
     1359        for k in range(d, 1, -1): 
     1360            inv_fac.append(inv_fac[-1] * R(k)) 
     1361        inv_fac.append(R(1)) 
     1362        inv_fac.reverse() 
     1363 
     1364        # compute some coefficients to be used in the preprocessing step 
     1365        scale = R(r)**(-d) 
     1366        input_scale = [scale * inv_fac[i] * inv_fac[d-i] * \ 
     1367                       (-1 if (d-i) & 1 else 1) for i in range(d+1)] 
     1368 
     1369        # Let B_i = b+ir for each i in [-d, d], except we declare B_i = 1 
     1370        # if i is "bad". 
     1371        B = [(R(1) if i in bad else R(b + i*r)) for i in range(-d, d+1)] 
     1372 
     1373        # compute partial products of B_i 
     1374        products = [R(1)] 
     1375        for z in B: 
     1376            products.append(products[-1] * z) 
     1377 
     1378        # compute partial products of inverses of B_i 
     1379        inverse = ~(products[-1]) 
     1380        inv_products = [inverse] 
     1381        for z in reversed(B): 
     1382            inv_products.append(inv_products[-1] * z) 
     1383        inv_products.reverse() 
     1384         
     1385        # compute inverses of each B_i 
     1386        B_inv = [inv_products[i] * products[i-1] \ 
     1387                 for i in range(1, len(products))] 
     1388 
     1389        # replace the "bad" B_i with 0, so they don't contribute to the 
     1390        # convolution at all 
     1391        for i in bad: 
     1392            B_inv[i+d] = R(0) 
     1393 
     1394        # kernel is the polynomial we need to multiply by in the convolution 
     1395        S = PolynomialRing(R, "x") 
     1396        kernel = S(B_inv, check=False) 
     1397 
     1398        # coefficients for postprocessing step 
     1399        output_scale = [products[k+d+1] * inv_products[k] for k in range(d+1)] 
     1400 
     1401        # save everything useful to the cache 
     1402        cache[d, b, r] = (bad, input_scale, S, kernel, output_scale) 
     1403     
     1404 
     1405    # preprocessing step: 
     1406    # multiply each P(ir) by \prod_{j \neq i} r (i-j)^{-1} 
     1407    values = [values[i] * input_scale[i] for i in range(d+1)] 
     1408 
     1409    # do the convolution 
     1410    H = S(values, check=False) * kernel 
     1411    output = H.list()[d:(2*d+1)] 
     1412    # (need to zero-pad output up to length d+1) 
     1413    output.extend([R(0)] * (d+1 - len(output))) 
     1414 
     1415    # postprocessing: 
     1416    # multiply each output by \prod_{j=0}^d B_{k-j} 
     1417    output = [output[k] * output_scale[k] for k in range(d+1)] 
     1418 
     1419 
     1420    # Now need to deal with "bad" indices. This shouldn't happen too often, 
     1421    # especially when $d$ is much smaller than $p$. 
     1422 
     1423    # The first problem is that in the postprocessing step we left out 
     1424    # multiplying by the bad factors. Now we put them back in. 
     1425    for i in bad: 
     1426        factor = b + i*r 
     1427        for k in range(max(i, 0), min(d+i, d) + 1): 
     1428            output[k] = output[k] * factor 
     1429 
     1430    # The second problem is that in the polynomial S(B_inv) the monomials 
     1431    # corresponding to the bad i were completely wrong. 
     1432    for i in bad: 
     1433        if i >= 0: 
     1434            correction = ([R(0)] * i) + [values[k-i] * output_scale[k] \ 
     1435                          for k in range(i, d+1)] 
     1436        else: 
     1437            correction = [values[k-i] * output_scale[k] \ 
     1438                          for k in range(0, i+d+1)] + ([R(0)] * (-i)) 
     1439 
     1440        # again, we left out all the bad factors; put them back in 
     1441        for ii in bad: 
     1442            if ii != i: 
     1443                factor = b + ii*r 
     1444                for k in range(max(ii, 0), min(d+ii, d) + 1): 
     1445                    correction[k] = correction[k] * factor 
     1446 
     1447        # add correction term to output 
     1448        for k in range(d+1): 
     1449            output[k] = output[k] + correction[k] 
     1450 
     1451    return output 
     1452 
     1453 
     1454 
     1455def naive_shift_evaluation_points(R, values, b, r, p): 
     1456    r""" 
     1457    Same as shift_evaluation_points, but uses naive algorithm (naive 
     1458    Lagrange interpolation, followed by naive evaluation). 
     1459 
     1460    For testing/debugging purposes only. 
     1461    """ 
     1462    d = len(values) - 1 
     1463    S, x = PolynomialRing(R, "x").objgen() 
     1464    total = S(0) 
     1465    for j in range(d+1): 
     1466        prod = S(1) 
     1467        for i in range(d+1): 
     1468            if i != j: 
     1469                prod = prod * (x - i*r) / (r*(j-i)) 
     1470        total = total + values[j] * prod 
     1471 
     1472    return [total(b + i*r) for i in range(d+1)] 
     1473 
     1474 
     1475 
     1476def matrix_polys(A, R, n, b, r, q, p, cache): 
     1477    r""" 
     1478    INPUT: 
     1479      p -- a prime 
     1480      R -- coefficient ring, of the form $\ZZ/p^m \ZZ$ 
     1481      n -- a positive integer 
     1482      A -- an $n \times n$ matrix of linear polys over $R$ (stored as a list 
     1483           of lists) 
     1484      b -- an integer 
     1485      r -- a positive integer 
     1486      q -- a positive integer 
     1487      cache -- dictionary (passed through to shift_evaluation_points) 
     1488 
     1489    OUTPUT: 
     1490      Let $B(x) = A(x+1) A(x+2) ... A(x+r-1) A(x+r)$, a matrix of degree 
     1491      $r$ polynomials. 
     1492 
     1493      Output an $n \times n$ matrix, the $(i, j)$-entry is a list of the 
     1494      $(i, j)$-entries of the evaluations $B(b), B(b+q), \ldots, B(b+rq)$; 
     1495      i.e. it evaluates $B(x)$ at $r+1$ points spaced out by $q$. 
     1496 
     1497    ALGORITHM: 
     1498      Umm, it's probably the one described in the Bostan/Gaudry/Schost paper, 
     1499      although I haven't looked at that part of the paper too closely yet. 
     1500 
     1501    EXAMPLES: 
     1502    Run it on some random input, and compare to a naive implementation: 
     1503        sage: from sage.schemes.elliptic_curves.monsky_washnitzer import matrix_polys, naive_matrix_polys 
     1504        sage: R = Integers(7**3) 
     1505        sage: S.<x> = PolynomialRing(R, "x") 
     1506        sage: b = 5 
     1507        sage: for r in range(1, 14): 
     1508        ...       A = [[R.random_element() * x + R.random_element() \ 
     1509        ...             for i in range(3)] for j in range(3)] 
     1510        ...       X = matrix_polys(A, R, 3, b, r, 3, 7, {}) 
     1511        ...       Y = naive_matrix_polys(A, R, 3, b, r, 3, 7) 
     1512        ...       if X != Y: print "failed" 
     1513 
     1514    """ 
     1515    assert r >= 1 
     1516    assert r < 2*p 
     1517 
     1518    if r == 1: 
     1519        # base case; i.e. evaluate original polys at x = b+1, b+q+1 
     1520        return [[[A[j][i](b+1), A[j][i](b+q+1)] for i in range(n)] 
     1521                for j in range(n)] 
     1522 
     1523    # let r' = floor(r/2) 
     1524    half_r = r // 2 
     1525     
     1526    # evaluate A(x+1) A(x+2) ... A(x+r') 
     1527    # at x = b, b + q, ..., b + r'q. 
     1528    X = matrix_polys(A, R, n, b, half_r, q, p, cache) 
     1529 
     1530    # shift to obtain evaluations of A(x+r'+1) A(x+r'+2) ... A(x+2r') 
     1531    # at x = b, b + q, ..., b + r'q. 
     1532    shift1 = [[shift_evaluation_points(R, values, half_r, q, p, cache) \ 
     1533               for values in row] for row in X] 
     1534 
     1535    # shift to obtain evaluations of A(x+1) A(x+2) ... A(x+r') 
     1536    # at x = b + (r'+1)q, b + (r'+2)q, ..., b + (2r'+1)q 
     1537    shift2 = [[shift_evaluation_points(R, values, (half_r+1)*q, q, p, cache) \ 
     1538               for values in row] for row in X] 
     1539 
     1540    # shift to obtain evaluations of A(x+r'+1) A(x+r'+2) ... A(x+2r') 
     1541    # at x = b + (r'+1)q, b + (r'+2)q, ..., b + (2r'+1)q 
     1542    shift3 = [[shift_evaluation_points(R, values, half_r, q, p, cache) \ 
     1543               for values in row] for row in shift2] 
     1544 
     1545    # multiply matrices to obtain evaluations of A(x+1) A(x+2) ... A(x+2r') 
     1546    # at x = b, b + q, ..., b + r'q. 
     1547    prod1 = [[[sum([X[i][m][k] * shift1[m][j][k] for m in range(n)]) \ 
     1548               for k in range(half_r+1)] for j in range(n)] for i in range(n)] 
     1549                
     1550    # multiply matrices to obtain evaluations of A(x+1) A(x+2) ... A(x+2r') 
     1551    # at x = b + (r'+1)q, ..., b + (2r'+1)q. 
     1552    prod2 = [[[sum([shift2[i][m][k] * shift3[m][j][k] for m in range(n)]) \ 
     1553               for k in range(half_r+1)] for j in range(n)] for i in range(n)] 
     1554                
     1555    if r & 1 == 0: 
     1556        # if r is even, then the polys are the right degree, but we have one 
     1557        # extra evaluation point (namely x = b + (r+1)q). Throw it away. 
     1558        for i in range(n): 
     1559            for j in range(n): 
     1560                prod2[i][j].pop() 
     1561                prod1[i][j].extend(prod2[i][j]) 
     1562        output = prod1 
     1563         
     1564    else: 
     1565        # if r is odd, then we have precisely the right number of evaluation 
     1566        # points, but our polynomials are short by one term, namely A(x+r). 
     1567        # Add in the effect of that term. 
     1568        for i in range(n): 
     1569            for j in range(n): 
     1570                prod1[i][j].extend(prod2[i][j]) 
     1571 
     1572        output = \ 
     1573               [[[sum([prod1[i][m][k] * A[m][j](b+k*q+r) for m in range(n)]) \ 
     1574                  for k in range(r+1)] for j in range(n)] for i in range(n)] 
     1575 
     1576    return output 
     1577 
     1578 
     1579 
     1580def naive_matrix_polys(A, R, n, b, r, q, p): 
     1581    r""" 
     1582    Same as matrix_polys, but uses a naive algorithm. 
     1583 
     1584    For testing/debugging purposes only. 
     1585    """ 
     1586    result = [[[] for _ in range(n)] for _ in range(n)] 
     1587    for i in range(r+1): 
     1588        prod = MatrixSpace(R, n, n).identity_matrix() 
     1589        for j in range(1, r+1): 
     1590            A_eval = matrix(R, [[f(b+q*i+j) for f in row] for row in A]) 
     1591            prod = prod * A_eval 
     1592        for k1 in range(n): 
     1593            for k2 in range(n): 
     1594                result[k1][k2].append(prod[k1][k2]) 
     1595    return result 
     1596             
     1597 
     1598 
     1599 
     1600def reduce_one(T, s, n, M, D, R): 
     1601    r""" 
     1602    A single step reduction. 
     1603     
     1604    INPUT: 
     1605      R -- coefficient ring, of the form $\ZZ/p^m \ZZ$ 
     1606      n -- a positive integer 
     1607      T -- an $n$-tuple of elements of $R$. 
     1608      M -- an $n \times n$ matrix of linear polys over $R$ (stored as a list 
     1609           of lists) 
     1610      D -- a linear polynomial over $R$. 
     1611 
     1612    OUTPUT: 
     1613      Let $A(x) = M(x)/D(x)$. 
     1614      Return value is the matrix-vector product $A(s) T$, as a list. 
     1615       
     1616    """ 
     1617    d = D(s).lift() 
     1618    return [R(sum([M[i][j](s) * T[j] for j in range(n)]).lift() / d) \ 
     1619            for i in range(n)] 
     1620 
     1621 
     1622 
     1623def reduce_sequence(T, d, s, n, M, D, p, R): 
     1624    r""" 
     1625    Reduces a sequence of terms. 
     1626     
     1627    INPUT: 
     1628      p -- a prime 
     1629      R -- coefficient ring, of the form $\ZZ/p^m \ZZ$ 
     1630      T -- a nonempty list of $n$-tuples of elements of $R$. Denote by $L$ 
     1631           the length of $T$. 
     1632      d -- a list of positive integers, of length $L$; they must be in 
     1633           strictly ascending order 
     1634      s -- an integer >= 0 
     1635      n -- an integer >= 1 
     1636      M -- an $n \times n$ matrix of linear polynomials over $R$, 
     1637           stored as a list of lists 
     1638      D -- a linear polynomial over $R$ 
     1639 
     1640    PRECONDITIONS: 
     1641       The largest $d_i$ must be less than $\sqrt p$. 
     1642       (or something like that... todo: check this...) 
     1643 
     1644    OUTPUT: 
     1645       Let $A(x) = M(x)/D(x)$, and let $B(t)$ denote the matrix product 
     1646         $$ A(s+1) A(s+2) \cdots A(s+t). $$ 
     1647 
     1648       This function returns the sum 
     1649         $$ \sum_{i=1}^L  B(d_i) T_i. $$ 
     1650 
     1651       In other words, treating each term $T_i$ as appearing in position 
     1652       $s + d_i$, it applies the reduction matrices to reduce them all down to 
     1653       a single $n$-tuple in position $s$. 
     1654 
     1655       Running time is something like 
     1656          $O(M(e^{1/2}) + L M(e^{1/4}))$ 
     1657       where $e = \max(d_i)$, and where $M(k)$ is the time for degree $k$ 
     1658       polynomial multiplication over $R$. 
     1659 
     1660    """ 
     1661    # L = number of terms to reduce 
     1662    L = len(T) 
     1663    # e = total distance to reduce 
     1664    e = d[-1] 
     1665 
     1666    if e <= 30: 
     1667        # naive algorithm when the reduction length is small enough 
     1668        output = [R(0)] * n 
     1669        for i in range(L): 
     1670            term = T[i] 
     1671            for t in range(d[i], 0, -1): 
     1672                term = reduce_one(term, s + t, n, M, D, R) 
     1673            for j in range(n): 
     1674                output[j] = output[j] + term[j] 
     1675        return output 
     1676 
     1677    # We do the reduction by splitting into segments. There are $w+1$ 
     1678    # segments of length $w$, where $w$ is the smallest integer such 
     1679    # that $w(w+1) \geq e$ (so in particular $w \approx \sqrt e$). 
     1680    w = ceil(sqrt(e)) 
     1681    while w*(w+1) >= e: 
     1682        w = w - 1 
     1683    if w*(w+1) < e: 
     1684        w = w + 1 
     1685 
     1686    # The BGS method only works if the terms are on segment boundaries. 
     1687    # So first we (recursively) reduce each term separately to make it 
     1688    # lie on a segment boundary. 
     1689    # This is the step that takes time $O(L M(e^{1/4}))$. 
     1690    new_T = [] 
     1691    new_d = [] 
     1692    for i in range(L): 
     1693        this_d = d[i] 
     1694        reduced_d = w * (this_d // w) 
     1695        if reduced_d == this_d: 
     1696            reduced_T = T[i] 
     1697        else: 
     1698            reduced_T = reduce_sequence([T[i]], [this_d - reduced_d], 
     1699                                        s + reduced_d, n, M, D, p, R) 
     1700        new_T.append(reduced_T) 
     1701        new_d.append(reduced_d) 
     1702    T = new_T 
     1703    d = new_d 
     1704 
     1705    # Now all terms are on segment boundaries. Next we compute a reduction 
     1706    # matrix for each segment, using BGS method. 
     1707    cache = {} 
     1708    M_segments = matrix_polys([[M[i][j] for j in range(n)] for i in range(n)], 
     1709                              R, n, s, w, w, p, cache) 
     1710    D_segments = matrix_polys([[D]], R, 1, s, w, w, p, cache)[0][0] 
     1711 
     1712    # Now we use the reduction matrices to sweep up all the terms. 
     1713    total = [R(0), R(0), R(0)] 
     1714    for segment in range(w+1, -1, -1): 
     1715        # add in terms at the current segment boundary 
     1716        while len(d) and (d[-1] == segment*w): 
     1717            for j in range(n): 
     1718                total[j] = total[j] + T[-1][j] 
     1719            T.pop() 
     1720            d.pop() 
     1721 
     1722        # apply reduction matrix 
     1723        if segment > 0: 
     1724            M_here = [[M_segments[i][j][segment-1] \ 
     1725                       for j in range(n)] for i in range(n)] 
     1726            D_here = D_segments[segment-1].lift() 
     1727            total = [R(sum([M_here[i][j] * total[j] 
     1728                            for j in range(n)]).lift() / D_here) \ 
     1729                     for i in range(n)] 
     1730     
     1731    return total 
     1732 
     1733 
     1734 
     1735def starting_differentials(R, a, b, p, M): 
     1736    r""" 
     1737    Computes the initial terms in the expansion of $\sigma(x^i dx/y)$ 
     1738    (where $\sigma$ is frobenius) to which the cohomology reduction 
     1739    algorithm will be applied. 
     1740     
     1741    INPUT: 
     1742      p -- a prime $\geq 5$ 
     1743      R -- coefficient ring, of the form $\ZZ/p^m \ZZ$ 
     1744      a, b -- elements of $R$, coefficients of an elliptic curve 
     1745              $y^2 = x^3 + ax + b$ 
     1746      M -- number of terms being computed (see ``Kedlaya's algorithm in 
     1747           larger characteristic'' for the exact meaning) 
     1748 
     1749    OUTPUT: 
     1750      A list of length $M$; the $j$th entry is a list of length $3j+1$; 
     1751      its $m$th entry is the coefficient of 
     1752         $ x^{p(m+i+1)-1} y^{-p(2j+1)+1} dx/y $ 
     1753      in the expansion of $\sigma(x^i dx/y)$. 
     1754 
     1755      Complexity is $O(M^2)$ operations in $R$. 
     1756 
     1757    """ 
     1758    # compute B[k][j] = binomial(k, j) in R, for 0 <= j <= k < 2M 
     1759    B = [[R(1)]] 
     1760    for k in range(1, 2*M): 
     1761        B.append([R(1)] + [B[-1][j] + B[-1][j-1] for j in range(1,k)] + [R(1)]) 
     1762 
     1763    # compute temp[k] = binomial(2k, k) / 4^k in R, for 0 <= k < M 
     1764    four_to_minus_1 = R(4)**(-1) 
     1765    four_to_minus_k = R(1) 
     1766    temp = [R(1)] 
     1767    for k in range(1, M): 
     1768        four_to_minus_k = four_to_minus_k * four_to_minus_1 
     1769        temp.append(four_to_minus_k * B[2*k][k]) 
     1770 
     1771    # compute constants 
     1772    # C[j] = (-1)^j p \sum_{k=j}^{M-1} 4^{-k} (2k \choose k) (k \choose j) 
     1773    # in R, for 0 <= j < M 
     1774    C = [p * sum([temp[k] * B[k][j] for k in range(j, M)]) for j in range(M)] 
     1775    for j in range(1, M, 2): 
     1776        C[j] = -C[j] 
     1777 
     1778    # compute A[j][m] = u^m coefficient of (u^3 + a*u + b)^j 
     1779    # for 0 <= j < M 
     1780    Ru, u = PolynomialRing(R, "u").objgen() 
     1781    Q = u**3 + a*u + b 
     1782    Q_to_j = Ru(1) 
     1783    A = [[R(1)]] 
     1784    for j in range(1, M): 
     1785        Q_to_j = Q_to_j * Q 
     1786        A.append(Q_to_j.list()) 
     1787 
     1788    # compute products C[j] * A[j][m] 
     1789    return [[C[j] * x for x in A[j]] for j in range(M)] 
     1790 
     1791 
     1792 
     1793 
     1794def matrix_of_frobenius_alternate(a, b, p, N): 
     1795    r""" 
     1796    Computes the matrix of Frobenius on Monsky-Washnitzer cohomology, 
     1797    with respect to the basis $(dx/y, x dx/y)$. 
     1798 
     1799    INPUT: 
     1800        a, b -- rational numbers, coefficients of elliptic curve 
     1801                $y^2 = x^3 + ax + b$ 
     1802        p -- a prime for which the curve has good reduction 
     1803        N -- desired output precision for matrix (i.e. modulo $p^N$). 
     1804 
     1805    OUTPUT: 
     1806        2x2 matrix of frobenius, with coefficients in Integers(p^N) 
     1807 
     1808    TODO: 
     1809    Haven't yet checked yet exactly which combinations of $p$ and $N$ are 
     1810    valid. If $p$ is much larger than $N$, there shouldn't be a problem. 
     1811 
     1812    EXAMPLES: 
     1813        sage: from monsky_washnitzer import matrix_of_frobenius_alternate 
     1814        sage: M = matrix_of_frobenius_alternate(-1, 1/4, 101, 3); M 
     1815        [824362 606695] 
     1816        [641451 205942] 
     1817        sage: M.det() 
     1818        101 
     1819        sage: M.trace() 
     1820        3 
     1821     
     1822    """ 
     1823    assert p > N    # todo: check this is the right condition.... 
     1824 
     1825    # choose minimal M such that M >= floor(log_p(2M+1)) + N 
     1826    M = N 
     1827    while M < floor(log(2*M+1, p)) + N: 
     1828      M = M + 1 
     1829 
     1830    # I haven't done the precision analysis properly yet, so for now 
     1831    # we work to precision 3M, with M digits at the bottom and M spare 
     1832    # digits at the top, which should be pretty safe. 
     1833    R = Integers(p**(3*M)) 
     1834    R_output = Integers(p**N) 
     1835    S, x = PolynomialRing(R, "x").objgen() 
     1836    a = R(a) 
     1837    b = R(b) 
     1838     
     1839    differentials = starting_differentials(R, a, b, p, M) 
     1840 
     1841    # multiply through by p^M for safety, need to come back and fix this... 
     1842    differentials = [[item * p**M for item in row] for row in differentials] 
     1843 
     1844    output = [] 
     1845 
     1846    for i in range(2): 
     1847        column = [] 
     1848         
     1849        for j in range(M): 
     1850            t = p*(2*j+1)-1 
     1851 
     1852            terms = [R(0)] if i == 1 else [] 
     1853            terms.extend(differentials[j]) 
     1854            terms = [[y, R(0), R(0)] for y in terms] 
     1855 
     1856            # reduce this row to the left 
     1857            triple = reduce_sequence( 
     1858              terms, [p*(m+1)-1 for m in range(len(terms))], 
     1859              0, 3, 
     1860              [[S(0),            S(0),            -2*b*x          ], 
     1861               [2*x - 3*t + 3,   S(0),            -a*(2*x - t + 1)], 
     1862               [S(0),            2*x - 3*t + 3,   S(0)            ]], 
     1863              2*x - 3*t + 3, 
     1864              p, R) 
     1865 
     1866            # do one reduction to make it only two terms instead of three 
     1867            triple = reduce_one(triple, t//2, 3, 
     1868              [[ 9*b*(6*x-5), 2*a*a*(6*x-5), -3*a*b*(6*x-5)], 
     1869               [-6*a*(6*x-7),   9*b*(6*x-7),  2*a*a*(6*x-7)], 
     1870               [        S(0),          S(0),           S(0)]], 
     1871              (27*b**2 + 4*a**3)*(2*x-1), R) 
     1872 
     1873            column.append([triple[0], triple[1]]) 
     1874 
     1875        # reduce the column to the bottom 
     1876        result = reduce_sequence( 
     1877              column, [(p*(2*m+1)-3)//2 for m in range(len(column))], 
     1878              0, 2, 
     1879              [[ 9*b*(6*x-5), 2*a*a*(6*x-5)], 
     1880               [-6*a*(6*x-7),   9*b*(6*x-7)]], 
     1881              (27*b**2 + 4*a**3)*(2*x-1), 
     1882              p, R) 
     1883 
     1884        # undo stupid p^M adjustment, and reduce down to Z/p^N 
     1885        for k in range(2): 
     1886            result[k] = R_output(result[k].lift() / p**M) 
     1887 
     1888        output.append(result) 
     1889 
     1890    return matrix(R_output, 2, 2, [output[0][0], output[1][0], 
     1891                                   output[0][1], output[1][1]]) 
     1892             
     1893 
    12581894### end of file 
Note: See TracChangeset for help on using the changeset viewer.