-
-
Notifications
You must be signed in to change notification settings - Fork 33
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
Add benchmarks for pseudo remainder method #89
Conversation
@oscarbenjamin if anything you want to modify in the code, then please tell me. Also, I little bit confused about the timings that how it should be calculated. |
I think this needs to be changed to 1.12:
|
make a separate PR for this? |
It could be. The tests won't run on this one until that change is made. |
there are almost 40 values of F and G from these benchmarks, should I include all ? |
Maybe just pick some representative ones. It is good to time with smaller examples and also larger examples from each group. Also benchmarks should not take longer than around 1 second so don't use really large examples. We can still include the code to generate the examples of any size but we don't need to have the benchmark suite run all sizes every time it runs. |
Code should be added that can generate pairs of polynomials of different sizes as well as their gcd and what the prem should be. We can reuse this for other benchmarks like gcd later. The Time class should have a params attribute that says which sizes to use for timings when running the benchmarks suite and the setup method should take a parameter that will be the values from params. The purpose of storing the results in values is so that the tear down method can check that the result is correct (there is no point reporting timings if a wrong result is computed). There should be Many of these things are demonstrated by the example I linked before: sympy_benchmarks/benchmarks/integrate.py Line 46 in 3680cc1
|
If you look in the CI output under "Run benchmarks" you can find this output which shows the other polys benchmarks being run:
I don't see any output for the Do you see any output for the prem benchmarks when running asv locally? |
I'm not sure why the benchmark doesn't seem to run in CI. |
I am currently experiencing certain challenges while running asv locally, but I am determined to find solutions to overcome them. Do I need to use Python version 3.10 specifically for executing the benchmarks? |
I think that it should be possible with any version of Python. The problem might be that the |
Let's see if the added benchmarks run now. |
The asv output indicates that this is not working:
The table looks nice but we need numbers rather than "failed" :) |
fix errors fix errors
Sorry, I am trying to squash the commits but there is a problem with the extension. |
ea6340f
to
87549c2
Compare
Why the asv output is structured in this way?
It should be like this
Should I use tuple of a tuple for |
Possibly there are too many cases for a horizontal layout. It make a difference to make the names shorter e.g. It might be that asv tries to detect the "terminal width" or something. Maybe there is an option to asv to control that. |
Should this problem be resolved?
|
I don't know if it is an easy thing to change but if not then that is fine. It looks like I don't know if there is anything in the docs about changing the layout. |
This is looking a lot better but the structure is still not quite right for what we want to do when adding more cases later. I reorganised it a bit like this: from sympy import *
class _GCDExample:
"""A benchmark example with two polynomials and their gcd."""
def __init__(self, n):
f, g, d, syms = self.make_poly(n)
self.f = f
self.g = g
self.d = d
self.x = syms[0]
self.y = syms[1:]
self.syms = syms
self.ring = QQ[syms]
def make_poly(self, n):
raise NotImplementedError("Subclass should implement this.")
def to_poly(self, expr):
return Poly(expr, self.syms)
def to_ring(self, expr):
return self.ring(expr)
def as_expr(self):
return (self.f, self.g, self.d, self.syms)
def as_poly(self):
return (self.to_poly(self.f), self.to_poly(self.g), self.to_poly(self.d))
def as_ring(self):
return (self.to_ring(self.f), self.to_ring(self.g), self.to_ring(self.d), self.ring)
class _SparseGCDHighDegree(_GCDExample):
"""A pair of polynomials in n symbols with a high degree sparse GCD.
>>> for n in [1, 2, 3, 4, 5]:
... f, g, d, syms = _SparseGCDHighDegree(n).as_expr()
... print(d)
x**2 + y1**2 + 1
x**3 + y1**3 + y2**3 + 1
x**4 + y1**4 + y2**4 + y3**4 + 1
x**5 + y1**5 + y2**5 + y3**5 + y4**5 + 1
x**6 + y1**6 + y2**6 + y3**6 + y4**6 + y5**6 + 1
"""
def make_poly(self, n):
x, *y = syms = symbols("x, y1:{}".format(n+1))
d = 1 + x ** (n + 1) + sum([y[i] ** (n + 1) for i in range(n)])
f = d * (-2 + x ** (n + 1) + sum([y[i] ** (n + 1) for i in range(n)]))
g = d * (2 + x ** (n + 1) + sum([y[i] ** (n + 1) for i in range(n)]))
return f, g, d, syms
class _QuadraticNonMonicGCD(_GCDExample):
"""A pair of quadratic polynomials with a non-monic GCD.
>>> for n in [1, 2, 3, 4, 5]:
... f, g, d, syms = _QuadraticNonMonicGCD(n).as_expr()
... print(d)
x**2*y1**2 + 1
x**2*y1**2 + y2**2 + 1
x**2*y1**2 + y2**2 + y3**2 + 1
x**2*y1**2 + y2**2 + y3**2 + y4**2 + 1
x**2*y1**2 + y2**2 + y3**2 + y4**2 + y5**2 + 1
"""
def make_poly(self, n):
x, *y = syms = symbols("x, y1:{}".format(n+1))
d = 1 + x ** 2 * y[0] ** 2 + sum([y[i] ** 2 for i in range(1, n)])
f = d * (-1 + x ** 2 - y[0] ** 2 + sum([y[i] ** 2 for i in range(1, n)]))
g = d * (2 + x * y[0] + sum(y[1:n])) ** 2
return f, g, d, syms
class _TimeOP:
"""
Benchmarks comparing Poly implementations of a given operation.
"""
param_names = [
'size',
'impl',
]
def setup(self, n, impl):
examples = self.GCDExampleCLS(n)
expected = self.expected(*examples.as_expr())
if impl == 'expr':
func = self.get_func_expr(*examples.as_expr())
expected = examples.to_expr(expected)
elif impl == 'dense':
func = self.get_func_poly(*examples.as_poly())
expected = examples.to_poly(expected)
elif impl == 'sparse':
func = self.get_func_sparse(*examples.as_ring())
expected = examples.to_ring(expected)
self.func = func
self.expected = expected
def time_op(self, n, impl):
self.returned = self.func()
def teardown(self, n, impl):
assert self.expected == self.returned
class _TimePREM(_TimeOP):
def expected(self, f, g, d, syms):
prem_f_g_x = rem(f * LC(g, x) ** (degree(f, x) - degree(g, x) + 1), g, x)
return prem(f, g, x)
def get_func_expr(self, f, g, d, syms):
x = syms[0]
return lambda: prem(f, g, x)
def get_func_poly(self, f, g, d):
return lambda: f.prem(g)
def get_func_sparse(self, f, g, d, ring):
return lambda: f.prem(g)
class TimePREM_SparseGCDHighDegree(_TimePREM):
GCDExampleCLS = _SparseGCDHighDegree
params = [(1, 3, 5), ('expr', 'dense', 'sparse')] The key point about this is that if we want to test something else like class _TimeGCD(_TimeOP):
def expected(self, f, g, d, syms):
return d
def get_func_expr(self, f, g, d, syms):
return lambda: gcd(f, g)
def get_func_poly(self, f, g, d):
return lambda: f.gcd(g)
def get_func_sparse(self, f, g, d, ring):
return lambda: f.gcd(g) Note how this |
So in the end the output looks like:
|
I think this looks good. Thanks, and let's get it merged. |
Thanks a lot! |
This PR adds the benchmarks for the pseudo-remainder method.
Related Issue:
#88
See sympy/sympy#25278