Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

agriconnect / numpy   python

Repository URL to install this package:

/ polynomial / _polybase.py

"""
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.

"""
from __future__ import division, absolute_import, print_function

from abc import ABCMeta, abstractmethod, abstractproperty
import numbers

import numpy as np
from . import polyutils as pu

__all__ = ['ABCPolyBase']

class ABCPolyBase(object):
    """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.

    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.

    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.

    """
    __metaclass__ = ABCMeta

    # 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

    @abstractproperty
    def domain(self):
        pass

    @abstractproperty
    def window(self):
        pass

    @abstractproperty
    def nickname(self):
        pass

    @abstractproperty
    def basis_name(self):
        pass

    @abstractmethod
    def _add(self):
        pass

    @abstractmethod
    def _sub(self):
        pass

    @abstractmethod
    def _mul(self):
        pass

    @abstractmethod
    def _div(self):
        pass

    @abstractmethod
    def _pow(self):
        pass

    @abstractmethod
    def _val(self):
        pass

    @abstractmethod
    def _int(self):
        pass

    @abstractmethod
    def _der(self):
        pass

    @abstractmethod
    def _fit(self):
        pass

    @abstractmethod
    def _line(self):
        pass

    @abstractmethod
    def _roots(self):
        pass

    @abstractmethod
    def _fromroots(self):
        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")
            return other.coef
        return other

    def __init__(self, coef, domain=None, window=None):
        [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

    def __repr__(self):
        format = "%s(%s, domain=%s, window=%s)"
        coef = repr(self.coef)[6:-1]
        domain = repr(self.domain)[6:-1]
        window = repr(self.window)[6:-1]
        name = self.__class__.__name__
        return format % (name, coef, domain, window)

    def __str__(self):
        format = "%s(%s)"
        coef = str(self.coef)
        name = self.nickname
        return format % (name, coef)

    @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 "{{{basis}}}_{{{i}}}({arg_str})".format(
            basis=cls.basis_name, i=i, arg_str=arg_str
        )

    @staticmethod
    def _repr_latex_scalar(x):
        # TODO: we're stuck with disabling math formatting until we handle
        # exponents in this function
        return r'\text{{{}}}'.format(x)

    def _repr_latex_(self):
        # get the scaled argument string to the basis functions
        off, scale = self.mapparms()
        if off == 0 and scale == 1:
            term = 'x'
            needs_parens = False
        elif scale == 1:
            term = '{} + x'.format(
                self._repr_latex_scalar(off)
            )
            needs_parens = True
        elif off == 0:
            term = '{}x'.format(
                self._repr_latex_scalar(scale)
            )
            needs_parens = True
        else:
            term = '{} + {}x'.format(
                self._repr_latex_scalar(off),
                self._repr_latex_scalar(scale)
            )
            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 = '{}'.format(self._repr_latex_scalar(c))
            elif not isinstance(c, numbers.Real):
                coef_str = ' + ({})'.format(self._repr_latex_scalar(c))
            elif not np.signbit(c):
                coef_str = ' + {}'.format(self._repr_latex_scalar(c))
            else:
                coef_str = ' - {}'.format(self._repr_latex_scalar(-c))

            # 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 = r'{}\,{}'.format(coef_str, term_str)

            if c == 0:
                part = mute(part)

            parts.append(part)
Loading ...