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

MAINT: Improve performance of polynomial operations (2) #24531

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
MAINT: Improve performance of internal polynomial operations
  • Loading branch information
eendebakpt committed Dec 1, 2023
commit 57a5c8715fef6568f64151227eee07b38e9ae3b3
71 changes: 38 additions & 33 deletions numpy/polynomial/_polybase.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,47 +289,52 @@ class as self with identical domain and window. If so,
When `other` is an incompatible instance of ABCPolyBase.

"""
if isinstance(other, ABCPolyBase):
if not isinstance(other, self.__class__):
raise TypeError("Polynomial types differ")
elif not np.all(self.domain == other.domain):
if isinstance(other, self.__class__):
if not (self.domain == other.domain).all():
raise TypeError("Domains differ")
elif not np.all(self.window == other.window):
elif not (self.window == other.window).all():
raise TypeError("Windows differ")
elif self.symbol != other.symbol:
raise ValueError("Polynomial symbols differ")
return other.coef
elif isinstance(other, ABCPolyBase):
raise TypeError("Polynomial types differ")
return other

def __init__(self, coef, domain=None, window=None, symbol='x'):
[coef] = pu.as_series([coef], trim=False)
self.coef = coef
def __init__(self, coef, domain=None, window=None, symbol='x', _validate_input = True):
if _validate_input:
[coef] = pu.as_series([coef], trim=False)

if domain is not None:
[domain] = pu.as_series([domain], trim=False)
if len(domain) != 2:
raise ValueError("Domain has wrong number of elements.")
self.domain = domain
if domain is not None:
[domain] = pu.as_series([domain], trim=False)
if len(domain) != 2:
raise ValueError("Domain has wrong number of elements.")
self.domain = domain

if window is not None:
[window] = pu.as_series([window], trim=False)
if len(window) != 2:
raise ValueError("Window has wrong number of elements.")
self.window = window
if window is not None:
[window] = pu.as_series([window], trim=False)
if len(window) != 2:
raise ValueError("Window has wrong number of elements.")
self.window = window

# Validation for symbol
try:
if not symbol.isidentifier():
raise ValueError(
"Symbol string must be a valid Python identifier"
)
# If a user passes in something other than a string, the above
# results in an AttributeError. Catch this and raise a more
# informative exception
except AttributeError:
raise TypeError("Symbol must be a non-empty string")
# Validation for symbol
try:
if not symbol.isidentifier():
raise ValueError(
"Symbol string must be a valid Python identifier"
)
# If a user passes in something other than a string, the above
# results in an AttributeError. Catch this and raise a more
# informative exception
except AttributeError:
raise TypeError("Symbol must be a non-empty string")
else:
coef = np.array(coef)
self.domain = np.array(domain)
self.window = np.array(window)

self._symbol = symbol
self.coef = coef

def __repr__(self):
coef = repr(self.coef)[6:-1]
Expand Down Expand Up @@ -521,7 +526,7 @@ def __len__(self):

def __neg__(self):
return self.__class__(
-self.coef, self.domain, self.window, self.symbol
-self.coef, self.domain, self.window, self.symbol, _validate_input=False,
)

def __pos__(self):
Expand All @@ -533,23 +538,23 @@ def __add__(self, other):
coef = self._add(self.coef, othercoef)
except Exception:
return NotImplemented
return self.__class__(coef, self.domain, self.window, self.symbol)
return self.__class__(coef, self.domain, self.window, self.symbol, _validate_input = False)

def __sub__(self, other):
othercoef = self._get_coefficients(other)
try:
coef = self._sub(self.coef, othercoef)
except Exception:
return NotImplemented
return self.__class__(coef, self.domain, self.window, self.symbol)
return self.__class__(coef, self.domain, self.window, self.symbol, _validate_input = False)

def __mul__(self, other):
othercoef = self._get_coefficients(other)
try:
coef = self._mul(self.coef, othercoef)
except Exception:
return NotImplemented
return self.__class__(coef, self.domain, self.window, self.symbol)
return self.__class__(coef, self.domain, self.window, self.symbol, _validate_input = False)

def __truediv__(self, other):
# there is no true divide if the rhs is not a Number, although it
Expand Down
6 changes: 3 additions & 3 deletions numpy/polynomial/polynomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,10 @@ def polymulx(c):

"""
# c is a trimmed copy
[c] = pu.as_series([c])
[c] = pu.as_series([c], copy=False)
# The zero series needs special treatment
if len(c) == 1 and c[0] == 0:
return c
return np.array(c)

prd = np.empty(len(c) + 1, dtype=c.dtype)
prd[0] = c[0]*0
Expand Down Expand Up @@ -358,7 +358,7 @@ def polymul(c1, c2):

"""
# c1, c2 are trimmed copies
[c1, c2] = pu.as_series([c1, c2])
[c1, c2] = pu.as_series([c1, c2], copy=False)
ret = np.convolve(c1, c2)
return pu.trimseq(ret)

Expand Down
26 changes: 16 additions & 10 deletions numpy/polynomial/polyutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

from numpy._core.multiarray import dragon4_positional, dragon4_scientific
from numpy.exceptions import RankWarning
from numpy.dtypes import ObjectDType

_object_dtype = ObjectDType()

__all__ = [
'as_series', 'trimseq', 'trimcoef', 'getdomain', 'mapdomain', 'mapparms',
Expand Down Expand Up @@ -63,7 +66,7 @@ def trimseq(seq):
return seq[:i+1]


def as_series(alist, trim=True):
def as_series(alist, trim=True, _validate_input=True, copy=True):
"""
Return argument as a list of 1-d arrays.

Expand Down Expand Up @@ -113,19 +116,22 @@ def as_series(alist, trim=True):
[array([2.]), array([1.1, 0. ])]

"""
arrays = [np.array(a, ndmin=1, copy=False) for a in alist]
if min([a.size for a in arrays]) == 0:
raise ValueError("Coefficient array is empty")
if any(a.ndim != 1 for a in arrays):
raise ValueError("Coefficient array is not 1-d")
if _validate_input:
arrays = [np.array(a, ndmin=1, copy=False) for a in alist]
if min([a.size for a in arrays]) == 0:
raise ValueError("Coefficient array is empty")
if any(a.ndim != 1 for a in arrays):
raise ValueError("Coefficient array is not 1-d")
else:
arrays = alist
if trim:
arrays = [trimseq(a) for a in arrays]

if any(a.dtype == np.dtype(object) for a in arrays):
if any(a.dtype == _object_dtype for a in arrays):
ret = []
for a in arrays:
if a.dtype != np.dtype(object):
tmp = np.empty(len(a), dtype=np.dtype(object))
if a.dtype != _object_dtype:
tmp = np.empty(len(a), dtype=_object_dtype)
tmp[:] = a[:]
ret.append(tmp)
else:
Expand All @@ -135,7 +141,7 @@ def as_series(alist, trim=True):
dtype = np.common_type(*arrays)
except Exception as e:
raise ValueError("Coefficient arrays have no common type") from e
ret = [np.array(a, copy=True, dtype=dtype) for a in arrays]
ret = [np.array(a, copy=copy, dtype=dtype) for a in arrays]
return ret


Expand Down