Commit 17a8ebd
Release Manager
gh-37369: Faster scalar multiplication for python types
There is an annoying problem at the moment. If you compute the scalar
multiplication of a point on an elliptic curve over a finite field, a
fast call to Pari is made when the scalar is a Sage type, but when the
scalar is a Python `int` then the multiplication for `_acted_upon_` is
skipped by the coercion system and `__mul__` is called instead which is
a much slower Sage implementation using double and add.
As an example of this see the following run times.
```py
┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 10.2, Release Date: 2023-12-03 │
│ Using Python 3.11.4. Type "help()" for help. │
└────────────────────────────────────────────────────────────────────┘
sage: F = GF(2**127 - 1)
sage: E = EllipticCurve(F, [0,6,0,1,0])
sage: P = E.random_point()
sage: k = randint(0, 2**127)
sage:
sage: %timeit int(k) * P
3.77 ms ± 98.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sage: %timeit P * int(k)
3.74 ms ± 12.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sage: %timeit ZZ(k) * P
235 µs ± 2.62 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
sage: %timeit P * ZZ(k)
234 µs ± 4.81 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
```
This is particularly annoying if you write Sagemath code in a `.py` file
and write
`2**100 * P`, which will be parsed such that `2**100` is an `int` and
the very slow method is used. For this 127-bit characteristic example
above, the slowdown is more than a factor of 10.
This PR writes small methods for `__mul__` and `__rmul__` which wrap
`_acted_upon_` and allow the speed up for Python `int` values. This
results in the following times for the same example:
```py
┌────────────────────────────────────────────────────────────────────┐
│ SageMath version 10.3.beta8, Release Date: 2024-02-13 │
│ Using Python 3.11.4. Type "help()" for help. │
└────────────────────────────────────────────────────────────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Warning: this is a prerelease version, and it may be unstable. ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
sage: F = GF(2**127 - 1)
sage: E = EllipticCurve(F, [0,6,0,1,0])
sage: P = E.random_point()
sage: k = randint(0, 2**127)
sage:
sage: %timeit int(k) * P
222 µs ± 1.6 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
sage: %timeit P * int(k)
225 µs ± 4.54 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
sage: %timeit ZZ(k) * P
219 µs ± 1.88 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
sage: %timeit P * ZZ(k)
230 µs ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
```
URL: #37369
Reported by: Giacomo Pope
Reviewer(s): John Cremona, nbruin
1 file changed
+46
-0
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2635 | 2635 | | |
2636 | 2636 | | |
2637 | 2637 | | |
| 2638 | + | |
| 2639 | + | |
| 2640 | + | |
| 2641 | + | |
| 2642 | + | |
| 2643 | + | |
| 2644 | + | |
| 2645 | + | |
| 2646 | + | |
| 2647 | + | |
| 2648 | + | |
| 2649 | + | |
| 2650 | + | |
| 2651 | + | |
| 2652 | + | |
| 2653 | + | |
| 2654 | + | |
| 2655 | + | |
| 2656 | + | |
2638 | 2657 | | |
2639 | 2658 | | |
2640 | 2659 | | |
| |||
2678 | 2697 | | |
2679 | 2698 | | |
2680 | 2699 | | |
| 2700 | + | |
| 2701 | + | |
| 2702 | + | |
| 2703 | + | |
| 2704 | + | |
| 2705 | + | |
| 2706 | + | |
| 2707 | + | |
| 2708 | + | |
| 2709 | + | |
| 2710 | + | |
| 2711 | + | |
| 2712 | + | |
| 2713 | + | |
| 2714 | + | |
| 2715 | + | |
| 2716 | + | |
| 2717 | + | |
| 2718 | + | |
| 2719 | + | |
| 2720 | + | |
| 2721 | + | |
| 2722 | + | |
| 2723 | + | |
| 2724 | + | |
| 2725 | + | |
| 2726 | + | |
2681 | 2727 | | |
2682 | 2728 | | |
2683 | 2729 | | |
| |||
0 commit comments