Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Singular maMapPoly to avoid quadratic complexity in polynomial evaluation #35381

Merged
merged 1 commit into from
Apr 6, 2023

Conversation

remyoudompheng
Copy link
Contributor

@remyoudompheng remyoudompheng commented Mar 29, 2023

📚 Description

Currently evaluation of mutivariate polynomials using the Singular implementation is dominated by a quadratic algorithm for large polynomials, making it less efficient than the generic implementation.

Another Singular API does not have this problem and performs a more classical computation.

Fixes issue #35374

Example:

sage: p = next_prime(2**24)
sage: x, y = polygens(GF(p), list("xy"))
sage: Rgeneric = PolynomialRing(GF(p), names=["x","y"], implementation="generic")
sage: pol = (x+y+1)^300
sage: polg = Rgeneric(pol)
# Generic
sage: %timeit polg(12,34)
86.5 ms ± 828 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# Before patch
sage: %time pol(12,34)
CPU times: user 6.76 s, sys: 30 ms, total: 6.79 s
# After patch
sage: %timeit pol(12,34)
12 ms ± 635 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

📝 Checklist

  • The title is concise, informative, and self-explanatory.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.
  • I have updated the documentation accordingly.

…aluation

The function fast_map_common_subexp tries to sort monomials
using a quadratic algorithm, making it unusable for large polynomials.

Function maMapPoly does not have this issue.
@remyoudompheng
Copy link
Contributor Author

remyoudompheng commented Mar 29, 2023

Please note that I am not particularly comfortable with the Singular API. In particular I kept the existing call when arguments are not constants (in case the fast_maps have some optimizations for composition of polynomials?)
Tests were performed on Archlinux using system libSingular (version 4.3.2)

Crude benchmarks:

from sage.all import *
import time

p = next_prime(2**24)
R = GF(p)["x","y"]
x, y = R.gens()
Rgeneric = PolynomialRing(GF(p), names=["x","y"], implementation="generic")
z = mod(57, p)
for degree in [10, 20, 40, 80, 100, 150, 200, 300, 400, 500, 600, 700, 800]:
    pol = (x+y+1)**degree + (2*x+2*y-1)**degree
    size = len(pol.dict())

    t = time.time()
    vals = pol(z, z)
    ts = time.time()-t

    polg = Rgeneric(pol)
    t = time.time()
    valg = polg(z, z)
    tg = time.time()-t

    assert vals == valg
    print(f"{degree=} {size=} singular={1000*ts:.1f}ms generic={1000*tg:.1f}ms")

Results (amended manually with %timeit for small degrees)

Before (Sage 9.8):

degree=10 size=66 singular=0.060ms generic=0.095ms
degree=20 size=231 singular=0.180ms generic=0.370ms
degree=40 size=861 singular=1.47ms generic=0.41ms
degree=80 size=3321 singular=19.2ms generic=5.9ms
degree=100 size=5151 singular=47.5ms generic=7.3ms
degree=150 size=11476 singular=209.5ms generic=16.7ms
degree=200 size=20301 singular=633.3ms generic=28.3ms
degree=300 size=45451 singular=3806.5ms generic=66.4ms
degree=400 size=80601 singular=26039.9ms generic=150.5ms
degree=500 size=125751 singular=63312.4ms generic=185.3ms
degree=600 size=180901 singular=129740.4ms generic=269.3ms
degree=700 size=246051 singular=265180.8ms generic=372.8ms
degree=800 size=321201 singular=490258.8ms generic=515.8ms

After (Sage 10.0beta6 + patch):

degree=10 size=66 singular=0.010ms generic=0.095ms
degree=20 size=231 singular=0.026ms generic=0.370ms
degree=40 size=861 singular=0.108ms generic=0.41ms
degree=80 size=3321 singular=0.44ms generic=5.9ms
degree=100 size=5151 singular=0.7ms generic=7.5ms
degree=150 size=11476 singular=2.0ms generic=17.2ms
degree=200 size=20301 singular=4.1ms generic=30.5ms
degree=300 size=45451 singular=12.5ms generic=69.6ms
degree=400 size=80601 singular=23.9ms generic=128.7ms
degree=500 size=125751 singular=44.5ms generic=201.1ms
degree=600 size=180901 singular=66.6ms generic=299.3ms
degree=700 size=246051 singular=93.2ms generic=410.3ms
degree=800 size=321201 singular=121.0ms generic=509.3ms

@videlec
Copy link
Contributor

videlec commented Apr 2, 2023

Definitely an improvement.

@vbraun vbraun merged commit 7cd86d0 into sagemath:develop Apr 6, 2023
@mkoeppe mkoeppe added this to the sage-10.0 milestone Apr 7, 2023
@remyoudompheng remyoudompheng deleted the singular-eval branch April 7, 2023 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants