Skip to content

Commit

Permalink
sagemathgh-36994: Use PARI when computing the Tate pairing
Browse files Browse the repository at this point in the history
    
To match `weil_pairing()` this pull request now uses PARI to compute the
non-reduced Tate pairing for elliptic curves over finite fields.

This has been included for efficiency, with PARI offering a significant
performance speed up:

```py
sage: GF(65537^2).inject_variables()
Defining z2
sage: E = EllipticCurve(GF(65537^2), [0,1])
sage: P = E(22, 28891)
sage: Q = E.random_point()
sage: # Old timing
sage: %timeit P.tate_pairing(Q, 7282, 2)
1.4 ms ± 66 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
sage: # New timing
sage: %timeit P.tate_pairing(Q, 7282, 2)
50.8 µs ± 674 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops
each)
```

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [ ] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.
    
URL: sagemath#36994
Reported by: Giacomo Pope
Reviewer(s): John Cremona
  • Loading branch information
Release Manager committed Jan 16, 2024
2 parents 2bc8b5a + d118f1c commit c6aff5e
Showing 1 changed file with 58 additions and 23 deletions.
81 changes: 58 additions & 23 deletions src/sage/schemes/elliptic_curves/ell_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
from sage.rings.integer import Integer
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.finite_rings.integer_mod import Mod
from sage.rings.real_mpfr import RealField
from sage.rings.real_mpfr import RR
import sage.groups.generic as generic
Expand Down Expand Up @@ -2013,18 +2014,52 @@ def tate_pairing(self, Q, n, k, q=None):
sage: Px.weil_pairing(Qx, 41)^e == num/den
True
.. NOTE::
TESTS:
Check that the PARI output matches the original Sage implementation::
sage: # needs sage.rings.finite_rings
sage: GF(65537^2).inject_variables()
Defining z2
sage: E = EllipticCurve(GF(65537^2), [0,1])
sage: P = E(22, 28891)
sage: Q = E(-93, 40438*z2 + 31573)
sage: P.tate_pairing(Q, 7282, 2)
34585*z2 + 4063
The point ``P (self)`` must have ``n`` torsion::
sage: P.tate_pairing(Q, 163, 2)
Traceback (most recent call last):
...
ValueError: The point P must be n-torsion
We must have that ``n`` divides ``q^k - 1``, this is only checked when q is supplied::
This function uses Miller's algorithm, followed by a naive
exponentiation. It does not do anything fancy. In the case
that there is an issue with `Q` being on one of the lines
generated in the `r*P` calculation, `Q` is offset by a random
point `R` and ``P.tate_pairing(Q+R,n,k)/P.tate_pairing(R,n,k)``
is returned.
sage: P.tate_pairing(Q, 7282, 2, q=123)
Traceback (most recent call last):
...
ValueError: n does not divide (q^k - 1) for the supplied value of q
The Tate pairing is only defined for points on curves defined over finite fields::
sage: E = EllipticCurve([0,1])
sage: P = E(2,3)
sage: P.tate_pairing(P, 6, 1)
Traceback (most recent call last):
...
NotImplementedError: Reduced Tate pairing is currently only implemented for finite fields
ALGORITHM:
- :pari:`elltatepairing` computes the
non-reduced tate pairing and the exponentiation is handled by
Sage using user input for `k` (and optionally `q`).
AUTHORS:
- Mariah Lenox (2011-03-07)
- Giacomo Pope (2024): Use of PARI for the non-reduced Tate pairing
"""
P = self
E = P.curve()
Expand All @@ -2033,6 +2068,9 @@ def tate_pairing(self, Q, n, k, q=None):
raise ValueError("Points must both be on the same curve")

K = E.base_ring()
if not K.is_finite():
raise NotImplementedError("Reduced Tate pairing is currently only implemented for finite fields")

d = K.degree()
if q is None:
if d == 1:
Expand All @@ -2041,22 +2079,19 @@ def tate_pairing(self, Q, n, k, q=None):
q = K.base_ring().order()
else:
raise ValueError("Unexpected field degree: set keyword argument q equal to the size of the base field (big field is GF(q^%s))." % k)

if n*P != E(0):
raise ValueError('This point is not of order n=%s' % n)

# In small cases, or in the case of pairing an element with
# itself, Q could be on one of the lines in the Miller
# algorithm. If this happens we try again, with an offset of a
# random point.
try:
ret = self._miller_(Q, n)
e = Integer((q**k - 1)/n)
ret = ret**e
except (ZeroDivisionError, ValueError):
R = E.random_point()
ret = self.tate_pairing(Q + R, n, k)/self.tate_pairing(R, n, k)
return ret
# The user has supplied q, so we check here that it's a sensible value
elif Mod(q, n)**k != 1:
raise ValueError("n does not divide (q^k - 1) for the supplied value of q")

if pari.ellmul(E, P, n) != [0]:
raise ValueError("The point P must be n-torsion")

# NOTE: Pari returns the non-reduced Tate pairing, so we
# must perform the exponentation ourselves using the supplied
# k value
ePQ = pari.elltatepairing(E, P, Q, n)
exp = Integer((q**k - 1)/n)
return K(ePQ**exp) # Cast the PARI type back to the base ring

def ate_pairing(self, Q, n, k, t, q=None):
r"""
Expand Down

0 comments on commit c6aff5e

Please sign in to comment.