about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/numpy/polynomial/_polybase.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/numpy/polynomial/_polybase.py')
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/polynomial/_polybase.py1206
1 files changed, 1206 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/numpy/polynomial/_polybase.py b/.venv/lib/python3.12/site-packages/numpy/polynomial/_polybase.py
new file mode 100644
index 00000000..9730574c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/polynomial/_polybase.py
@@ -0,0 +1,1206 @@
+"""
+Abstract base class for the various polynomial Classes.
+
+The ABCPolyBase class provides the methods needed to implement the common API
+for the various polynomial classes. It operates as a mixin, but uses the
+abc module from the stdlib, hence it is only available for Python >= 2.6.
+
+"""
+import os
+import abc
+import numbers
+
+import numpy as np
+from . import polyutils as pu
+
+__all__ = ['ABCPolyBase']
+
+class ABCPolyBase(abc.ABC):
+    """An abstract base class for immutable series classes.
+
+    ABCPolyBase provides the standard Python numerical methods
+    '+', '-', '*', '//', '%', 'divmod', '**', and '()' along with the
+    methods listed below.
+
+    .. versionadded:: 1.9.0
+
+    Parameters
+    ----------
+    coef : array_like
+        Series coefficients in order of increasing degree, i.e.,
+        ``(1, 2, 3)`` gives ``1*P_0(x) + 2*P_1(x) + 3*P_2(x)``, where
+        ``P_i`` is the basis polynomials of degree ``i``.
+    domain : (2,) array_like, optional
+        Domain to use. The interval ``[domain[0], domain[1]]`` is mapped
+        to the interval ``[window[0], window[1]]`` by shifting and scaling.
+        The default value is the derived class domain.
+    window : (2,) array_like, optional
+        Window, see domain for its use. The default value is the
+        derived class window.
+    symbol : str, optional
+        Symbol used to represent the independent variable in string 
+        representations of the polynomial expression, e.g. for printing.
+        The symbol must be a valid Python identifier. Default value is 'x'.
+
+        .. versionadded:: 1.24
+
+    Attributes
+    ----------
+    coef : (N,) ndarray
+        Series coefficients in order of increasing degree.
+    domain : (2,) ndarray
+        Domain that is mapped to window.
+    window : (2,) ndarray
+        Window that domain is mapped to.
+    symbol : str
+        Symbol representing the independent variable.
+
+    Class Attributes
+    ----------------
+    maxpower : int
+        Maximum power allowed, i.e., the largest number ``n`` such that
+        ``p(x)**n`` is allowed. This is to limit runaway polynomial size.
+    domain : (2,) ndarray
+        Default domain of the class.
+    window : (2,) ndarray
+        Default window of the class.
+
+    """
+
+    # Not hashable
+    __hash__ = None
+
+    # Opt out of numpy ufuncs and Python ops with ndarray subclasses.
+    __array_ufunc__ = None
+
+    # Limit runaway size. T_n^m has degree n*m
+    maxpower = 100
+
+    # Unicode character mappings for improved __str__
+    _superscript_mapping = str.maketrans({
+        "0": "⁰",
+        "1": "¹",
+        "2": "²",
+        "3": "³",
+        "4": "⁴",
+        "5": "⁵",
+        "6": "⁶",
+        "7": "⁷",
+        "8": "⁸",
+        "9": "⁹"
+    })
+    _subscript_mapping = str.maketrans({
+        "0": "₀",
+        "1": "₁",
+        "2": "₂",
+        "3": "₃",
+        "4": "₄",
+        "5": "₅",
+        "6": "₆",
+        "7": "₇",
+        "8": "₈",
+        "9": "₉"
+    })
+    # Some fonts don't support full unicode character ranges necessary for
+    # the full set of superscripts and subscripts, including common/default
+    # fonts in Windows shells/terminals. Therefore, default to ascii-only
+    # printing on windows.
+    _use_unicode = not os.name == 'nt'
+
+    @property
+    def symbol(self):
+        return self._symbol
+
+    @property
+    @abc.abstractmethod
+    def domain(self):
+        pass
+
+    @property
+    @abc.abstractmethod
+    def window(self):
+        pass
+
+    @property
+    @abc.abstractmethod
+    def basis_name(self):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _add(c1, c2):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _sub(c1, c2):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _mul(c1, c2):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _div(c1, c2):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _pow(c, pow, maxpower=None):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _val(x, c):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _int(c, m, k, lbnd, scl):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _der(c, m, scl):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _fit(x, y, deg, rcond, full):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _line(off, scl):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _roots(c):
+        pass
+
+    @staticmethod
+    @abc.abstractmethod
+    def _fromroots(r):
+        pass
+
+    def has_samecoef(self, other):
+        """Check if coefficients match.
+
+        .. versionadded:: 1.6.0
+
+        Parameters
+        ----------
+        other : class instance
+            The other class must have the ``coef`` attribute.
+
+        Returns
+        -------
+        bool : boolean
+            True if the coefficients are the same, False otherwise.
+
+        """
+        if len(self.coef) != len(other.coef):
+            return False
+        elif not np.all(self.coef == other.coef):
+            return False
+        else:
+            return True
+
+    def has_samedomain(self, other):
+        """Check if domains match.
+
+        .. versionadded:: 1.6.0
+
+        Parameters
+        ----------
+        other : class instance
+            The other class must have the ``domain`` attribute.
+
+        Returns
+        -------
+        bool : boolean
+            True if the domains are the same, False otherwise.
+
+        """
+        return np.all(self.domain == other.domain)
+
+    def has_samewindow(self, other):
+        """Check if windows match.
+
+        .. versionadded:: 1.6.0
+
+        Parameters
+        ----------
+        other : class instance
+            The other class must have the ``window`` attribute.
+
+        Returns
+        -------
+        bool : boolean
+            True if the windows are the same, False otherwise.
+
+        """
+        return np.all(self.window == other.window)
+
+    def has_sametype(self, other):
+        """Check if types match.
+
+        .. versionadded:: 1.7.0
+
+        Parameters
+        ----------
+        other : object
+            Class instance.
+
+        Returns
+        -------
+        bool : boolean
+            True if other is same class as self
+
+        """
+        return isinstance(other, self.__class__)
+
+    def _get_coefficients(self, other):
+        """Interpret other as polynomial coefficients.
+
+        The `other` argument is checked to see if it is of the same
+        class as self with identical domain and window. If so,
+        return its coefficients, otherwise return `other`.
+
+        .. versionadded:: 1.9.0
+
+        Parameters
+        ----------
+        other : anything
+            Object to be checked.
+
+        Returns
+        -------
+        coef
+            The coefficients of`other` if it is a compatible instance,
+            of ABCPolyBase, otherwise `other`.
+
+        Raises
+        ------
+        TypeError
+            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):
+                raise TypeError("Domains differ")
+            elif not np.all(self.window == other.window):
+                raise TypeError("Windows differ")
+            elif self.symbol != other.symbol:
+                raise ValueError("Polynomial symbols differ")
+            return other.coef
+        return other
+
+    def __init__(self, coef, domain=None, window=None, symbol='x'):
+        [coef] = pu.as_series([coef], trim=False)
+        self.coef = coef
+
+        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
+
+        # 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")
+
+        self._symbol = symbol
+
+    def __repr__(self):
+        coef = repr(self.coef)[6:-1]
+        domain = repr(self.domain)[6:-1]
+        window = repr(self.window)[6:-1]
+        name = self.__class__.__name__
+        return (f"{name}({coef}, domain={domain}, window={window}, "
+                f"symbol='{self.symbol}')")
+
+    def __format__(self, fmt_str):
+        if fmt_str == '':
+            return self.__str__()
+        if fmt_str not in ('ascii', 'unicode'):
+            raise ValueError(
+                f"Unsupported format string '{fmt_str}' passed to "
+                f"{self.__class__}.__format__. Valid options are "
+                f"'ascii' and 'unicode'"
+            )
+        if fmt_str == 'ascii':
+            return self._generate_string(self._str_term_ascii)
+        return self._generate_string(self._str_term_unicode)
+
+    def __str__(self):
+        if self._use_unicode:
+            return self._generate_string(self._str_term_unicode)
+        return self._generate_string(self._str_term_ascii)
+
+    def _generate_string(self, term_method):
+        """
+        Generate the full string representation of the polynomial, using
+        ``term_method`` to generate each polynomial term.
+        """
+        # Get configuration for line breaks
+        linewidth = np.get_printoptions().get('linewidth', 75)
+        if linewidth < 1:
+            linewidth = 1
+        out = pu.format_float(self.coef[0])
+        for i, coef in enumerate(self.coef[1:]):
+            out += " "
+            power = str(i + 1)
+            # Polynomial coefficient
+            # The coefficient array can be an object array with elements that
+            # will raise a TypeError with >= 0 (e.g. strings or Python
+            # complex). In this case, represent the coefficient as-is.
+            try:
+                if coef >= 0:
+                    next_term = f"+ " + pu.format_float(coef, parens=True)
+                else:
+                    next_term = f"- " + pu.format_float(-coef, parens=True)
+            except TypeError:
+                next_term = f"+ {coef}"
+            # Polynomial term
+            next_term += term_method(power, self.symbol)
+            # Length of the current line with next term added
+            line_len = len(out.split('\n')[-1]) + len(next_term)
+            # If not the last term in the polynomial, it will be two
+            # characters longer due to the +/- with the next term
+            if i < len(self.coef[1:]) - 1:
+                line_len += 2
+            # Handle linebreaking
+            if line_len >= linewidth:
+                next_term = next_term.replace(" ", "\n", 1)
+            out += next_term
+        return out
+
+    @classmethod
+    def _str_term_unicode(cls, i, arg_str):
+        """
+        String representation of single polynomial term using unicode
+        characters for superscripts and subscripts.
+        """
+        if cls.basis_name is None:
+            raise NotImplementedError(
+                "Subclasses must define either a basis_name, or override "
+                "_str_term_unicode(cls, i, arg_str)"
+            )
+        return (f"·{cls.basis_name}{i.translate(cls._subscript_mapping)}"
+                f"({arg_str})")
+
+    @classmethod
+    def _str_term_ascii(cls, i, arg_str):
+        """
+        String representation of a single polynomial term using ** and _ to
+        represent superscripts and subscripts, respectively.
+        """
+        if cls.basis_name is None:
+            raise NotImplementedError(
+                "Subclasses must define either a basis_name, or override "
+                "_str_term_ascii(cls, i, arg_str)"
+            )
+        return f" {cls.basis_name}_{i}({arg_str})"
+
+    @classmethod
+    def _repr_latex_term(cls, i, arg_str, needs_parens):
+        if cls.basis_name is None:
+            raise NotImplementedError(
+                "Subclasses must define either a basis name, or override "
+                "_repr_latex_term(i, arg_str, needs_parens)")
+        # since we always add parens, we don't care if the expression needs them
+        return f"{{{cls.basis_name}}}_{{{i}}}({arg_str})"
+
+    @staticmethod
+    def _repr_latex_scalar(x, parens=False):
+        # TODO: we're stuck with disabling math formatting until we handle
+        # exponents in this function
+        return r'\text{{{}}}'.format(pu.format_float(x, parens=parens))
+
+    def _repr_latex_(self):
+        # get the scaled argument string to the basis functions
+        off, scale = self.mapparms()
+        if off == 0 and scale == 1:
+            term = self.symbol
+            needs_parens = False
+        elif scale == 1:
+            term = f"{self._repr_latex_scalar(off)} + {self.symbol}"
+            needs_parens = True
+        elif off == 0:
+            term = f"{self._repr_latex_scalar(scale)}{self.symbol}"
+            needs_parens = True
+        else:
+            term = (
+                f"{self._repr_latex_scalar(off)} + "
+                f"{self._repr_latex_scalar(scale)}{self.symbol}"
+            )
+            needs_parens = True
+
+        mute = r"\color{{LightGray}}{{{}}}".format
+
+        parts = []
+        for i, c in enumerate(self.coef):
+            # prevent duplication of + and - signs
+            if i == 0:
+                coef_str = f"{self._repr_latex_scalar(c)}"
+            elif not isinstance(c, numbers.Real):
+                coef_str = f" + ({self._repr_latex_scalar(c)})"
+            elif not np.signbit(c):
+                coef_str = f" + {self._repr_latex_scalar(c, parens=True)}"
+            else:
+                coef_str = f" - {self._repr_latex_scalar(-c, parens=True)}"
+
+            # produce the string for the term
+            term_str = self._repr_latex_term(i, term, needs_parens)
+            if term_str == '1':
+                part = coef_str
+            else:
+                part = rf"{coef_str}\,{term_str}"
+
+            if c == 0:
+                part = mute(part)
+
+            parts.append(part)
+
+        if parts:
+            body = ''.join(parts)
+        else:
+            # in case somehow there are no coefficients at all
+            body = '0'
+
+        return rf"${self.symbol} \mapsto {body}$"
+
+
+
+    # Pickle and copy
+
+    def __getstate__(self):
+        ret = self.__dict__.copy()
+        ret['coef'] = self.coef.copy()
+        ret['domain'] = self.domain.copy()
+        ret['window'] = self.window.copy()
+        ret['symbol'] = self.symbol
+        return ret
+
+    def __setstate__(self, dict):
+        self.__dict__ = dict
+
+    # Call
+
+    def __call__(self, arg):
+        off, scl = pu.mapparms(self.domain, self.window)
+        arg = off + scl*arg
+        return self._val(arg, self.coef)
+
+    def __iter__(self):
+        return iter(self.coef)
+
+    def __len__(self):
+        return len(self.coef)
+
+    # Numeric properties.
+
+    def __neg__(self):
+        return self.__class__(
+            -self.coef, self.domain, self.window, self.symbol
+        )
+
+    def __pos__(self):
+        return self
+
+    def __add__(self, other):
+        othercoef = self._get_coefficients(other)
+        try:
+            coef = self._add(self.coef, othercoef)
+        except Exception:
+            return NotImplemented
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    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)
+
+    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)
+
+    def __truediv__(self, other):
+        # there is no true divide if the rhs is not a Number, although it
+        # could return the first n elements of an infinite series.
+        # It is hard to see where n would come from, though.
+        if not isinstance(other, numbers.Number) or isinstance(other, bool):
+            raise TypeError(
+                f"unsupported types for true division: "
+                f"'{type(self)}', '{type(other)}'"
+            )
+        return self.__floordiv__(other)
+
+    def __floordiv__(self, other):
+        res = self.__divmod__(other)
+        if res is NotImplemented:
+            return res
+        return res[0]
+
+    def __mod__(self, other):
+        res = self.__divmod__(other)
+        if res is NotImplemented:
+            return res
+        return res[1]
+
+    def __divmod__(self, other):
+        othercoef = self._get_coefficients(other)
+        try:
+            quo, rem = self._div(self.coef, othercoef)
+        except ZeroDivisionError:
+            raise
+        except Exception:
+            return NotImplemented
+        quo = self.__class__(quo, self.domain, self.window, self.symbol)
+        rem = self.__class__(rem, self.domain, self.window, self.symbol)
+        return quo, rem
+
+    def __pow__(self, other):
+        coef = self._pow(self.coef, other, maxpower=self.maxpower)
+        res = self.__class__(coef, self.domain, self.window, self.symbol)
+        return res
+
+    def __radd__(self, other):
+        try:
+            coef = self._add(other, self.coef)
+        except Exception:
+            return NotImplemented
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    def __rsub__(self, other):
+        try:
+            coef = self._sub(other, self.coef)
+        except Exception:
+            return NotImplemented
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    def __rmul__(self, other):
+        try:
+            coef = self._mul(other, self.coef)
+        except Exception:
+            return NotImplemented
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    def __rdiv__(self, other):
+        # set to __floordiv__ /.
+        return self.__rfloordiv__(other)
+
+    def __rtruediv__(self, other):
+        # An instance of ABCPolyBase is not considered a
+        # Number.
+        return NotImplemented
+
+    def __rfloordiv__(self, other):
+        res = self.__rdivmod__(other)
+        if res is NotImplemented:
+            return res
+        return res[0]
+
+    def __rmod__(self, other):
+        res = self.__rdivmod__(other)
+        if res is NotImplemented:
+            return res
+        return res[1]
+
+    def __rdivmod__(self, other):
+        try:
+            quo, rem = self._div(other, self.coef)
+        except ZeroDivisionError:
+            raise
+        except Exception:
+            return NotImplemented
+        quo = self.__class__(quo, self.domain, self.window, self.symbol)
+        rem = self.__class__(rem, self.domain, self.window, self.symbol)
+        return quo, rem
+
+    def __eq__(self, other):
+        res = (isinstance(other, self.__class__) and
+               np.all(self.domain == other.domain) and
+               np.all(self.window == other.window) and
+               (self.coef.shape == other.coef.shape) and
+               np.all(self.coef == other.coef) and
+               (self.symbol == other.symbol))
+        return res
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    #
+    # Extra methods.
+    #
+
+    def copy(self):
+        """Return a copy.
+
+        Returns
+        -------
+        new_series : series
+            Copy of self.
+
+        """
+        return self.__class__(self.coef, self.domain, self.window, self.symbol)
+
+    def degree(self):
+        """The degree of the series.
+
+        .. versionadded:: 1.5.0
+
+        Returns
+        -------
+        degree : int
+            Degree of the series, one less than the number of coefficients.
+
+        Examples
+        --------
+
+        Create a polynomial object for ``1 + 7*x + 4*x**2``:
+
+        >>> poly = np.polynomial.Polynomial([1, 7, 4])
+        >>> print(poly)
+        1.0 + 7.0·x + 4.0·x²
+        >>> poly.degree()
+        2
+
+        Note that this method does not check for non-zero coefficients.
+        You must trim the polynomial to remove any trailing zeroes:
+
+        >>> poly = np.polynomial.Polynomial([1, 7, 0])
+        >>> print(poly)
+        1.0 + 7.0·x + 0.0·x²
+        >>> poly.degree()
+        2
+        >>> poly.trim().degree()
+        1
+
+        """
+        return len(self) - 1
+
+    def cutdeg(self, deg):
+        """Truncate series to the given degree.
+
+        Reduce the degree of the series to `deg` by discarding the
+        high order terms. If `deg` is greater than the current degree a
+        copy of the current series is returned. This can be useful in least
+        squares where the coefficients of the high degree terms may be very
+        small.
+
+        .. versionadded:: 1.5.0
+
+        Parameters
+        ----------
+        deg : non-negative int
+            The series is reduced to degree `deg` by discarding the high
+            order terms. The value of `deg` must be a non-negative integer.
+
+        Returns
+        -------
+        new_series : series
+            New instance of series with reduced degree.
+
+        """
+        return self.truncate(deg + 1)
+
+    def trim(self, tol=0):
+        """Remove trailing coefficients
+
+        Remove trailing coefficients until a coefficient is reached whose
+        absolute value greater than `tol` or the beginning of the series is
+        reached. If all the coefficients would be removed the series is set
+        to ``[0]``. A new series instance is returned with the new
+        coefficients.  The current instance remains unchanged.
+
+        Parameters
+        ----------
+        tol : non-negative number.
+            All trailing coefficients less than `tol` will be removed.
+
+        Returns
+        -------
+        new_series : series
+            New instance of series with trimmed coefficients.
+
+        """
+        coef = pu.trimcoef(self.coef, tol)
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    def truncate(self, size):
+        """Truncate series to length `size`.
+
+        Reduce the series to length `size` by discarding the high
+        degree terms. The value of `size` must be a positive integer. This
+        can be useful in least squares where the coefficients of the
+        high degree terms may be very small.
+
+        Parameters
+        ----------
+        size : positive int
+            The series is reduced to length `size` by discarding the high
+            degree terms. The value of `size` must be a positive integer.
+
+        Returns
+        -------
+        new_series : series
+            New instance of series with truncated coefficients.
+
+        """
+        isize = int(size)
+        if isize != size or isize < 1:
+            raise ValueError("size must be a positive integer")
+        if isize >= len(self.coef):
+            coef = self.coef
+        else:
+            coef = self.coef[:isize]
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    def convert(self, domain=None, kind=None, window=None):
+        """Convert series to a different kind and/or domain and/or window.
+
+        Parameters
+        ----------
+        domain : array_like, optional
+            The domain of the converted series. If the value is None,
+            the default domain of `kind` is used.
+        kind : class, optional
+            The polynomial series type class to which the current instance
+            should be converted. If kind is None, then the class of the
+            current instance is used.
+        window : array_like, optional
+            The window of the converted series. If the value is None,
+            the default window of `kind` is used.
+
+        Returns
+        -------
+        new_series : series
+            The returned class can be of different type than the current
+            instance and/or have a different domain and/or different
+            window.
+
+        Notes
+        -----
+        Conversion between domains and class types can result in
+        numerically ill defined series.
+
+        """
+        if kind is None:
+            kind = self.__class__
+        if domain is None:
+            domain = kind.domain
+        if window is None:
+            window = kind.window
+        return self(kind.identity(domain, window=window, symbol=self.symbol))
+
+    def mapparms(self):
+        """Return the mapping parameters.
+
+        The returned values define a linear map ``off + scl*x`` that is
+        applied to the input arguments before the series is evaluated. The
+        map depends on the ``domain`` and ``window``; if the current
+        ``domain`` is equal to the ``window`` the resulting map is the
+        identity.  If the coefficients of the series instance are to be
+        used by themselves outside this class, then the linear function
+        must be substituted for the ``x`` in the standard representation of
+        the base polynomials.
+
+        Returns
+        -------
+        off, scl : float or complex
+            The mapping function is defined by ``off + scl*x``.
+
+        Notes
+        -----
+        If the current domain is the interval ``[l1, r1]`` and the window
+        is ``[l2, r2]``, then the linear mapping function ``L`` is
+        defined by the equations::
+
+            L(l1) = l2
+            L(r1) = r2
+
+        """
+        return pu.mapparms(self.domain, self.window)
+
+    def integ(self, m=1, k=[], lbnd=None):
+        """Integrate.
+
+        Return a series instance that is the definite integral of the
+        current series.
+
+        Parameters
+        ----------
+        m : non-negative int
+            The number of integrations to perform.
+        k : array_like
+            Integration constants. The first constant is applied to the
+            first integration, the second to the second, and so on. The
+            list of values must less than or equal to `m` in length and any
+            missing values are set to zero.
+        lbnd : Scalar
+            The lower bound of the definite integral.
+
+        Returns
+        -------
+        new_series : series
+            A new series representing the integral. The domain is the same
+            as the domain of the integrated series.
+
+        """
+        off, scl = self.mapparms()
+        if lbnd is None:
+            lbnd = 0
+        else:
+            lbnd = off + scl*lbnd
+        coef = self._int(self.coef, m, k, lbnd, 1./scl)
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    def deriv(self, m=1):
+        """Differentiate.
+
+        Return a series instance of that is the derivative of the current
+        series.
+
+        Parameters
+        ----------
+        m : non-negative int
+            Find the derivative of order `m`.
+
+        Returns
+        -------
+        new_series : series
+            A new series representing the derivative. The domain is the same
+            as the domain of the differentiated series.
+
+        """
+        off, scl = self.mapparms()
+        coef = self._der(self.coef, m, scl)
+        return self.__class__(coef, self.domain, self.window, self.symbol)
+
+    def roots(self):
+        """Return the roots of the series polynomial.
+
+        Compute the roots for the series. Note that the accuracy of the
+        roots decreases the further outside the `domain` they lie.
+
+        Returns
+        -------
+        roots : ndarray
+            Array containing the roots of the series.
+
+        """
+        roots = self._roots(self.coef)
+        return pu.mapdomain(roots, self.window, self.domain)
+
+    def linspace(self, n=100, domain=None):
+        """Return x, y values at equally spaced points in domain.
+
+        Returns the x, y values at `n` linearly spaced points across the
+        domain.  Here y is the value of the polynomial at the points x. By
+        default the domain is the same as that of the series instance.
+        This method is intended mostly as a plotting aid.
+
+        .. versionadded:: 1.5.0
+
+        Parameters
+        ----------
+        n : int, optional
+            Number of point pairs to return. The default value is 100.
+        domain : {None, array_like}, optional
+            If not None, the specified domain is used instead of that of
+            the calling instance. It should be of the form ``[beg,end]``.
+            The default is None which case the class domain is used.
+
+        Returns
+        -------
+        x, y : ndarray
+            x is equal to linspace(self.domain[0], self.domain[1], n) and
+            y is the series evaluated at element of x.
+
+        """
+        if domain is None:
+            domain = self.domain
+        x = np.linspace(domain[0], domain[1], n)
+        y = self(x)
+        return x, y
+
+    @classmethod
+    def fit(cls, x, y, deg, domain=None, rcond=None, full=False, w=None,
+        window=None, symbol='x'):
+        """Least squares fit to data.
+
+        Return a series instance that is the least squares fit to the data
+        `y` sampled at `x`. The domain of the returned instance can be
+        specified and this will often result in a superior fit with less
+        chance of ill conditioning.
+
+        Parameters
+        ----------
+        x : array_like, shape (M,)
+            x-coordinates of the M sample points ``(x[i], y[i])``.
+        y : array_like, shape (M,)
+            y-coordinates of the M sample points ``(x[i], y[i])``.
+        deg : int or 1-D array_like
+            Degree(s) of the fitting polynomials. If `deg` is a single integer
+            all terms up to and including the `deg`'th term are included in the
+            fit. For NumPy versions >= 1.11.0 a list of integers specifying the
+            degrees of the terms to include may be used instead.
+        domain : {None, [beg, end], []}, optional
+            Domain to use for the returned series. If ``None``,
+            then a minimal domain that covers the points `x` is chosen.  If
+            ``[]`` the class domain is used. The default value was the
+            class domain in NumPy 1.4 and ``None`` in later versions.
+            The ``[]`` option was added in numpy 1.5.0.
+        rcond : float, optional
+            Relative condition number of the fit. Singular values smaller
+            than this relative to the largest singular value will be
+            ignored. The default value is len(x)*eps, where eps is the
+            relative precision of the float type, about 2e-16 in most
+            cases.
+        full : bool, optional
+            Switch determining nature of return value. When it is False
+            (the default) just the coefficients are returned, when True
+            diagnostic information from the singular value decomposition is
+            also returned.
+        w : array_like, shape (M,), optional
+            Weights. If not None, the weight ``w[i]`` applies to the unsquared
+            residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are
+            chosen so that the errors of the products ``w[i]*y[i]`` all have
+            the same variance.  When using inverse-variance weighting, use
+            ``w[i] = 1/sigma(y[i])``.  The default value is None.
+
+            .. versionadded:: 1.5.0
+        window : {[beg, end]}, optional
+            Window to use for the returned series. The default
+            value is the default class domain
+
+            .. versionadded:: 1.6.0
+        symbol : str, optional
+            Symbol representing the independent variable. Default is 'x'.
+
+        Returns
+        -------
+        new_series : series
+            A series that represents the least squares fit to the data and
+            has the domain and window specified in the call. If the
+            coefficients for the unscaled and unshifted basis polynomials are
+            of interest, do ``new_series.convert().coef``.
+
+        [resid, rank, sv, rcond] : list
+            These values are only returned if ``full == True``
+
+            - resid -- sum of squared residuals of the least squares fit
+            - rank -- the numerical rank of the scaled Vandermonde matrix
+            - sv -- singular values of the scaled Vandermonde matrix
+            - rcond -- value of `rcond`.
+
+            For more details, see `linalg.lstsq`.
+
+        """
+        if domain is None:
+            domain = pu.getdomain(x)
+        elif type(domain) is list and len(domain) == 0:
+            domain = cls.domain
+
+        if window is None:
+            window = cls.window
+
+        xnew = pu.mapdomain(x, domain, window)
+        res = cls._fit(xnew, y, deg, w=w, rcond=rcond, full=full)
+        if full:
+            [coef, status] = res
+            return (
+                cls(coef, domain=domain, window=window, symbol=symbol), status
+            )
+        else:
+            coef = res
+            return cls(coef, domain=domain, window=window, symbol=symbol)
+
+    @classmethod
+    def fromroots(cls, roots, domain=[], window=None, symbol='x'):
+        """Return series instance that has the specified roots.
+
+        Returns a series representing the product
+        ``(x - r[0])*(x - r[1])*...*(x - r[n-1])``, where ``r`` is a
+        list of roots.
+
+        Parameters
+        ----------
+        roots : array_like
+            List of roots.
+        domain : {[], None, array_like}, optional
+            Domain for the resulting series. If None the domain is the
+            interval from the smallest root to the largest. If [] the
+            domain is the class domain. The default is [].
+        window : {None, array_like}, optional
+            Window for the returned series. If None the class window is
+            used. The default is None.
+        symbol : str, optional
+            Symbol representing the independent variable. Default is 'x'.
+
+        Returns
+        -------
+        new_series : series
+            Series with the specified roots.
+
+        """
+        [roots] = pu.as_series([roots], trim=False)
+        if domain is None:
+            domain = pu.getdomain(roots)
+        elif type(domain) is list and len(domain) == 0:
+            domain = cls.domain
+
+        if window is None:
+            window = cls.window
+
+        deg = len(roots)
+        off, scl = pu.mapparms(domain, window)
+        rnew = off + scl*roots
+        coef = cls._fromroots(rnew) / scl**deg
+        return cls(coef, domain=domain, window=window, symbol=symbol)
+
+    @classmethod
+    def identity(cls, domain=None, window=None, symbol='x'):
+        """Identity function.
+
+        If ``p`` is the returned series, then ``p(x) == x`` for all
+        values of x.
+
+        Parameters
+        ----------
+        domain : {None, array_like}, optional
+            If given, the array must be of the form ``[beg, end]``, where
+            ``beg`` and ``end`` are the endpoints of the domain. If None is
+            given then the class domain is used. The default is None.
+        window : {None, array_like}, optional
+            If given, the resulting array must be if the form
+            ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of
+            the window. If None is given then the class window is used. The
+            default is None.
+        symbol : str, optional
+            Symbol representing the independent variable. Default is 'x'.
+
+        Returns
+        -------
+        new_series : series
+             Series of representing the identity.
+
+        """
+        if domain is None:
+            domain = cls.domain
+        if window is None:
+            window = cls.window
+        off, scl = pu.mapparms(window, domain)
+        coef = cls._line(off, scl)
+        return cls(coef, domain, window, symbol)
+
+    @classmethod
+    def basis(cls, deg, domain=None, window=None, symbol='x'):
+        """Series basis polynomial of degree `deg`.
+
+        Returns the series representing the basis polynomial of degree `deg`.
+
+        .. versionadded:: 1.7.0
+
+        Parameters
+        ----------
+        deg : int
+            Degree of the basis polynomial for the series. Must be >= 0.
+        domain : {None, array_like}, optional
+            If given, the array must be of the form ``[beg, end]``, where
+            ``beg`` and ``end`` are the endpoints of the domain. If None is
+            given then the class domain is used. The default is None.
+        window : {None, array_like}, optional
+            If given, the resulting array must be if the form
+            ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of
+            the window. If None is given then the class window is used. The
+            default is None.
+        symbol : str, optional
+            Symbol representing the independent variable. Default is 'x'.
+
+        Returns
+        -------
+        new_series : series
+            A series with the coefficient of the `deg` term set to one and
+            all others zero.
+
+        """
+        if domain is None:
+            domain = cls.domain
+        if window is None:
+            window = cls.window
+        ideg = int(deg)
+
+        if ideg != deg or ideg < 0:
+            raise ValueError("deg must be non-negative integer")
+        return cls([0]*ideg + [1], domain, window, symbol)
+
+    @classmethod
+    def cast(cls, series, domain=None, window=None):
+        """Convert series to series of this class.
+
+        The `series` is expected to be an instance of some polynomial
+        series of one of the types supported by by the numpy.polynomial
+        module, but could be some other class that supports the convert
+        method.
+
+        .. versionadded:: 1.7.0
+
+        Parameters
+        ----------
+        series : series
+            The series instance to be converted.
+        domain : {None, array_like}, optional
+            If given, the array must be of the form ``[beg, end]``, where
+            ``beg`` and ``end`` are the endpoints of the domain. If None is
+            given then the class domain is used. The default is None.
+        window : {None, array_like}, optional
+            If given, the resulting array must be if the form
+            ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of
+            the window. If None is given then the class window is used. The
+            default is None.
+
+        Returns
+        -------
+        new_series : series
+            A series of the same kind as the calling class and equal to
+            `series` when evaluated.
+
+        See Also
+        --------
+        convert : similar instance method
+
+        """
+        if domain is None:
+            domain = cls.domain
+        if window is None:
+            window = cls.window
+        return series.convert(domain, cls, window)