Changeset 3190:b1427bf737c5
- Timestamp:
- 02/25/07 21:00:36 (6 years ago)
- Branch:
- default
- Location:
- sage/schemes/elliptic_curves
- Files:
-
- 2 edited
-
ell_rational_field.py (modified) (3 diffs)
-
monsky_washnitzer.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
sage/schemes/elliptic_curves/ell_rational_field.py
r2836 r3190 40 40 from sage.misc.functional import log 41 41 42 # Use some interval arithmetic to guarant ycorrectness. We assume42 # Use some interval arithmetic to guarantee correctness. We assume 43 43 # that alpha is computed to the precision of a float. 44 44 IR = rings.RIF … … 4388 4388 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) 4389 4389 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 4390 4394 """ 4391 4395 if check_hypotheses: … … 4428 4432 " at p." 4429 4433 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 4437 4460 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 4447 4466 4448 4467 if check: -
sage/schemes/elliptic_curves/monsky_washnitzer.py
r2874 r3190 3 3 4 4 The most interesting functions to be exported here are matrix_of_frobenius() 5 and adjusted_prec() .5 and adjusted_prec(), and matrix_of_frobenius_alternate(). 6 6 7 7 Currently this code is limited to the case $p \geq 5$ (no $GF(p^n)$ … … 15 15 -- Edixhoven, B., ``Point counting after Kedlaya'', EIDMA-Stieltjes graduate 16 16 course, Lieden (lecture notes?). 17 -- Harvey, D. ``Kedlaya's algorithm in larger characteristic'', 18 http://arxiv.org/abs/math.NT/0610973 17 19 18 20 AUTHORS: … … 23 25 more documentation, added Newton iteration method, added more complete 24 26 "trace trick", integrated better into SAGE. 27 -- David Harvey (Feb 2007): added algorithm with sqrt(p) complexity 25 28 26 29 """ … … 40 43 from sage.rings.ring import CommutativeAlgebra 41 44 from sage.structure.element import CommutativeAlgebraElement 42 43 from sage.rings.arith import binomial 44 from sage.misc.functional import log, ceil 45 from sage.matrix.matrix_space import MatrixSpace 46 47 from sage.rings.arith import binomial, floor 48 from sage.misc.functional import log, ceil, sqrt 45 49 46 50 … … 1256 1260 1257 1261 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 1282 def 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 1455 def 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 1476 def 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 1580 def 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 1600 def 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 1623 def 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 1735 def 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 1794 def 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 1258 1894 ### end of file
Note: See TracChangeset
for help on using the changeset viewer.
