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

Add benchmarks for subresultants PRS method #94

Merged
merged 7 commits into from
Jul 20, 2023
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 143 additions & 13 deletions benchmarks/polys.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down Expand Up @@ -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))
Expand All @@ -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))
Expand All @@ -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))
Expand All @@ -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))
Expand All @@ -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.
Expand All @@ -111,12 +193,20 @@ 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):
expected = [examples.to_poly(polynomial) for polynomial in expected] # for the subresultants type methods the output is in form of list.
else:
expected = examples.to_poly(expected) # for those methods whose output is only a polynomial not a tuple or list.
1e9abhi1e10 marked this conversation as resolved.
Show resolved Hide resolved

elif impl == 'sparse':
func = self.get_func_sparse(*examples.as_ring())
expected = examples.to_ring(expected)
if isinstance(expected, list):
expected = [examples.to_ring(polynomial) for polynomial in expected]
else:
expected = examples.to_ring(expected)

self.func = func
self.expected_result = expected
Expand Down Expand Up @@ -147,20 +237,60 @@ 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
params = [(1, 3, 5), ('expr', 'dense', 'sparse')] # This case is slow for n=8.


class TimePREM_SparseGCDHighDegree(_TimePREM):
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
params = [(1, 3, 5), ('expr', 'dense', 'sparse')] # This case is slow for n=8.


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, 2, 3), ('expr', 'dense', 'sparse')] # This case is slow for n>3.


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.
params = [(1, 2, 3), ('expr', 'dense', 'sparse')] # This case is slow for n>3.


class TimePREM_SparseNonMonicQuadratic(_TimePREM):
class TimeSUBRESULTANTS_SparseNonMonicQuadratic(_TimeSUBRESULTANTS):
GCDExampleCLS = _SparseNonMonicQuadratic
params = [(1, 3, 5, 8), ('expr', 'dense', 'sparse')]
params = [(1, 3, 5), ('expr', 'dense', 'sparse')]