diff --git a/benchmarks/polys.py b/benchmarks/polys.py index 60318c2..274d924 100644 --- a/benchmarks/polys.py +++ b/benchmarks/polys.py @@ -1,4 +1,4 @@ -from sympy import symbols, prod, prem, rem, degree, LC +from sympy import symbols, prod, prem, rem, degree, LC, subresultants from sympy.polys import QQ, Poly @@ -52,7 +52,27 @@ def as_ring(self): class _LinearDenseQuadraticGCD(_GCDExample): - """A pair of linearly dense quartic inputs with quadratic GCDs""" + """A pair of linearly dense quartic inputs with quadratic GCDs. + + This class generates benchmark examples with two polynomials, ``f`` and + ``g``, that are linearly dense quartic polynomials with quadratic GCDs. The + polynomials are constructed based on the input parameter ``n`. + + Examples + ======== + + >>> example = _LinearDenseQuadraticGCD(3) + >>> f, g, d, syms = example.as_expr() + >>> f + (x - y1 - y2 - y3 - 2)**2*(x + y1 + y2 + y3 + 1)**2 + >>> g + (x + y1 + y2 + y3 + 1)**2*(x + y1 + y2 + y3 + 2)**2 + >>> d + (x + y1 + y2 + y3 + 1)**2 + >>> syms + (x, y1, y2, y3) + + """ def make_poly(self, n): x, *y = syms = symbols("x, y1:{}".format(n+1)) @@ -61,8 +81,29 @@ def make_poly(self, n): g = d * (2 + x + sum(y[:n])) ** 2 return f, g, d, syms + class _SparseGCDHighDegree(_GCDExample): - """A pair of polynomials in n symbols with a high degree sparse GCD.""" + """A pair of polynomials in n symbols with a high degree sparse GCD. + + This class generates benchmark examples with two polynomials, ``f`` and + ``g``, that have a high degree sparse GCD. The polynomials are constructed + based on the input parameter ``n``. + + Examples + ======== + + >>> example = _SparseGCDHighDegree(3) + >>> f, g, d, syms = example.as_expr() + >>> f + (x**4 + y1**4 + y2**4 + y3**4 - 2)*(x**4 + y1**4 + y2**4 + y3**4 + 1) + >>> g + (x**4 + y1**4 + y2**4 + y3**4 + 1)*(x**4 + y1**4 + y2**4 + y3**4 + 2) + >>> d + x**4 + y1**4 + y2**4 + y3**4 + 1 + >>> syms + (x, y1, y2, y3) + + """ def make_poly(self, n): x, *y = syms = symbols("x, y1:{}".format(n+1)) @@ -73,7 +114,27 @@ def make_poly(self, n): class _QuadraticNonMonicGCD(_GCDExample): - """A pair of quadratic polynomials with a non-monic GCD.""" + """A pair of quadratic polynomials with a non-monic GCD. + + This class generates benchmark examples with two quadratic polynomials, + ``f`` and ``g``, that have a non-monic GCD. The polynomials are constructed + based on the input parameter ``n``. + + Examples + ======== + + >>> example = _QuadraticNonMonicGCD(3) + >>> f, g, d, syms = example.as_expr() + >>> f + (x**2*y1**2 + y2**2 + y3**2 + 1)*(x**2 - y1**2 + y2**2 + y3**2 - 1) + >>> g + (x*y1 + y2 + y3 + 2)**2*(x**2*y1**2 + y2**2 + y3**2 + 1) + >>> d + x**2*y1**2 + y2**2 + y3**2 + 1 + >>> syms + (x, y1, y2, y3) + + """ def make_poly(self, n): x, *y = syms = symbols("x, y1:{}".format(n+1)) @@ -84,7 +145,27 @@ def make_poly(self, n): class _SparseNonMonicQuadratic(_GCDExample): - """A pair of sparse non-monic quadratic polynomials with linear GCDs.""" + """A pair of sparse non-monic quadratic polynomials with linear GCDs. + + This class generates benchmark examples with two sparse non-monic quadratic + polynomials, ``f`` and ``g``, that have a linear GCD. The polynomials are + constructed based on the input parameter ``n``. + + Examples + ======== + + >>> example = _SparseNonMonicQuadratic(3) + >>> f, g, d, syms = example.as_expr() + >>> f + (x*y1*y2*y3 - 1)*(x*y1*y2*y3 + 3) + >>> g + (x*y1*y2*y3 - 3)*(x*y1*y2*y3 - 1) + >>> d + x*y1*y2*y3 - 1 + >>> syms + (x, y1, y2, y3) + + """ def make_poly(self, n): x, *y = syms = symbols("x, y1:{}".format(n+1)) @@ -93,6 +174,7 @@ def make_poly(self, n): g = d * (-3 + x * prod(y[:n])) return f, g, d, syms + class _TimeOP: """ Benchmarks comparing Poly implementations of a given operation. @@ -111,12 +193,22 @@ def setup(self, n, impl): 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) + if isinstance(expected, list): + # some methods output a list of polynomials + expected = [examples.to_poly(p) for p in expected] + else: + # others output only a single polynomial. + expected = examples.to_poly(expected) + elif impl == 'sparse': func = self.get_func_sparse(*examples.as_ring()) - expected = examples.to_ring(expected) + if isinstance(expected, list): + expected = [examples.to_ring(p) for p in expected] + else: + expected = examples.to_ring(expected) self.func = func self.expected_result = expected @@ -147,20 +239,64 @@ def get_func_sparse(self, f, g, d, ring): class TimePREM_LinearDenseQuadraticGCD(_TimePREM): + """This case involves linearly dense quartic inputs with quadratic GCDs. + The quadratic GCD suggests that the pseudo remainder method could be + applicable and potentially efficient for computing the GCD of these + polynomials.""" + + GCDExampleCLS = _LinearDenseQuadraticGCD + # This case is slow for n>5. + params = [(1, 3, 5), ('expr', 'dense', 'sparse')] + + +class TimePREM_QuadraticNonMonicGCD(_TimePREM): + """This case deals with quadratic polynomials having a non-monic GCD. The + non-monic aspect may introduce additional complexities, but the quadratic + nature suggests that the pseudo remainder method could be useful. + """ + + GCDExampleCLS = _QuadraticNonMonicGCD + # This case is slow for n>5. + params = [(1, 3, 5), ('expr', 'dense', 'sparse')] + + +class _TimeSUBRESULTANTS(_TimeOP): + """Benchmarks for subresultants PRS method""" + + def expected(self, f, g, d, syms): + x = syms[0] + subresultant = subresultants(f, g, x) + + return subresultant + + def get_func_expr(self, f, g, d, syms): + x = syms[0] + return lambda: subresultants(f, g, x) + + def get_func_poly(self, f, g, d): + return lambda: f.subresultants(g) + + def get_func_sparse(self, f, g, d, ring): + return lambda: f.subresultants(g) + + +class TimeSUBRESULTANTS_LinearDenseQuadraticGCD(_TimeSUBRESULTANTS): GCDExampleCLS = _LinearDenseQuadraticGCD - params = [(1, 3, 5), ('expr', 'dense', 'sparse')] # This case is slow for n=8. + # This case is slow for n>3. + params = [(1, 2, 3), ('expr', 'dense', 'sparse')] -class TimePREM_SparseGCDHighDegree(_TimePREM): +class TimeSUBRESULTANTS_SparseGCDHighDegree(_TimeSUBRESULTANTS): GCDExampleCLS = _SparseGCDHighDegree - params = [(1, 3, 5, 8), ('expr', 'dense', 'sparse')] + params = [(1, 3, 5), ('expr', 'dense', 'sparse')] -class TimePREM_QuadraticNonMonicGCD(_TimePREM): +class TimeSUBRESULTANTS_QuadraticNonMonicGCD(_TimeSUBRESULTANTS): GCDExampleCLS = _QuadraticNonMonicGCD - params = [(1, 3, 5), ('expr', 'dense', 'sparse')] # This case is slow for n=8. + # This case is slow for n>3. + params = [(1, 2, 3), ('expr', 'dense', 'sparse')] -class TimePREM_SparseNonMonicQuadratic(_TimePREM): +class TimeSUBRESULTANTS_SparseNonMonicQuadratic(_TimeSUBRESULTANTS): GCDExampleCLS = _SparseNonMonicQuadratic - params = [(1, 3, 5, 8), ('expr', 'dense', 'sparse')] \ No newline at end of file + params = [(1, 3, 5), ('expr', 'dense', 'sparse')]