Skip to content

Module polynomial.frozen

This module defines the Freezable interface and subclasses.

View Source
"""This module defines the Freezable interface and subclasses."""

from math import inf

from polynomial.core import (

    Constant,

    Polynomial,

    _extract_polynomial,

)

class Freezable:

    """An interface for freezable objects."""

    def _freeze(self):

        """Prevent further modification of self."""

        if not self._is_frozen():

            self._frozen = True

    def _is_frozen(self):

        """Return true if self is frozen."""

        return getattr(self, "_frozen", False)

    def __setitem__(self, key, value):

        """Implement self[x] = y; disallows setting item if frozen."""

        if self._is_frozen():

            raise AttributeError("Can not modify items of frozen object.")

        super().__setitem__(key, value)

    def __setattr__(self, key, value):

        """Implement self.x; disallows setting attr if frozen."""

        if not self._is_frozen():

            object.__setattr__(self, key, value)

        else:

            raise AttributeError("Can not modify frozen object.")

    def _no_op(self):

        """Do nothing. Used as a dummy method."""

class FrozenPolynomial(Freezable, Polynomial):

    """A polynomial which can not be directly modified."""

    def __init__(self, *args, **kwargs):

        """Create a polynomial from the args, and then freeze it."""

        Polynomial.__init__(self, *args, **kwargs)

        self._vector = tuple(self._vector)

        self._trim = self._no_op

        self._freeze()

    @classmethod

    def zero_instance(cls):

        """Return the zero FrozenPolynomial."""

        return FrozenPolynomial()

    @classmethod

    def from_polynomial(cls, polynomial):

        """Create a frozen copy of the polynomial."""

        return cls(polynomial)

    def __repr__(self):

        """Return repr(self)."""

        return "Frozen" + super().__repr__()

    def __hash__(self):

        """Return hash(self).

        Equal to the hash of a tuple with the coefficients sorted by their

        degree descendingly.

        """

        return hash(self._vector)

class ZeroPolynomial(Freezable, Constant, valid_degrees=-inf):

    """The zero polynomial."""

    # Never used, since we would raise errors due to Freezable

    # anyways.

    valid_term_counts = (0, )

    def __init__(self):

        """Equivalent to Polynomial()."""

        Constant.__init__(self, 0)

        self._trim = self._no_op

        self._freeze()

    @property

    def _vector(self):

        """Return self._vector."""

        return (0, )

    @property

    def degree(self):

        """Return self.degree."""

        return -inf

    @classmethod

    def zero_instance(cls):

        """Return an instance of the ZeroPolynomial."""

        return ZeroPolynomial()

    @property

    def const(self):

        """Return self.const, which is always 0."""

        return 0

    @_extract_polynomial

    def __mul__(self, other):

        """Return self * other."""

        return other.zero_instance()

    @_extract_polynomial

    def __rmul__(self, other):

        """Return other * self."""

        return other.zero_instance()

    def __ipow__(self, other):

        """Return self **= power.

        Does not mutate self.

        """

        if other == 0:

            return Constant(1)

        # This call simply enforces other >= 0 and is int.

        # Could be moved out into a decorator.

        super().__ipow__(other)

        return ZeroPolynomial()

    def __repr__(self):

        """Return repr(self)."""

        return "ZeroPolynomial()"

    def __hash__(self):

        """Return hash(self). Equal to the hash of an empty tuple."""

        return hash(tuple())

Variables

inf

Classes

Freezable

class Freezable(
    /,
    *args,
    **kwargs
)

An interface for freezable objects.

View Source
class Freezable:

    """An interface for freezable objects."""

    def _freeze(self):

        """Prevent further modification of self."""

        if not self._is_frozen():

            self._frozen = True

    def _is_frozen(self):

        """Return true if self is frozen."""

        return getattr(self, "_frozen", False)

    def __setitem__(self, key, value):

        """Implement self[x] = y; disallows setting item if frozen."""

        if self._is_frozen():

            raise AttributeError("Can not modify items of frozen object.")

        super().__setitem__(key, value)

    def __setattr__(self, key, value):

        """Implement self.x; disallows setting attr if frozen."""

        if not self._is_frozen():

            object.__setattr__(self, key, value)

        else:

            raise AttributeError("Can not modify frozen object.")

    def _no_op(self):

        """Do nothing. Used as a dummy method."""

Descendants

  • polynomial.frozen.FrozenPolynomial
  • polynomial.frozen.ZeroPolynomial

FrozenPolynomial

class FrozenPolynomial(
    *args,
    **kwargs
)

A polynomial which can not be directly modified.

View Source
class FrozenPolynomial(Freezable, Polynomial):

    """A polynomial which can not be directly modified."""

    def __init__(self, *args, **kwargs):

        """Create a polynomial from the args, and then freeze it."""

        Polynomial.__init__(self, *args, **kwargs)

        self._vector = tuple(self._vector)

        self._trim = self._no_op

        self._freeze()

    @classmethod

    def zero_instance(cls):

        """Return the zero FrozenPolynomial."""

        return FrozenPolynomial()

    @classmethod

    def from_polynomial(cls, polynomial):

        """Create a frozen copy of the polynomial."""

        return cls(polynomial)

    def __repr__(self):

        """Return repr(self)."""

        return "Frozen" + super().__repr__()

    def __hash__(self):

        """Return hash(self).

        Equal to the hash of a tuple with the coefficients sorted by their

        degree descendingly.

        """

        return hash(self._vector)

Ancestors (in MRO)

  • polynomial.frozen.Freezable
  • polynomial.core.Polynomial

Static methods

from_polynomial
def from_polynomial(
    polynomial
)

Create a frozen copy of the polynomial.

View Source
    @classmethod

    def from_polynomial(cls, polynomial):

        """Create a frozen copy of the polynomial."""

        return cls(polynomial)
zero_instance
def zero_instance(

)

Return the zero FrozenPolynomial.

View Source
    @classmethod

    def zero_instance(cls):

        """Return the zero FrozenPolynomial."""

        return FrozenPolynomial()

Instance variables

degree

Return the degree of the polynomial.

derivative

Return a polynomial object which is the derivative of self.

monomials

Return a list with all terms in the form of monomials.

List is sorted from the highest degree term to the lowest.

terms

Get the terms of self as a list of tuples in coeff, deg form.

Terms are returned from largest degree to smallest degree, excluding any terms with a zero coefficient.

Methods

calculate
def calculate(
    self,
    x
)

Calculate the value of the polynomial at a given point.

View Source
    def calculate(self, x):

        """Calculate the value of the polynomial at a given point."""

        if self.degree < 0:

            return 0

        return sum(ak * (x ** k) for ak, k in self.terms)
nth_derivative
def nth_derivative(
    self,
    n=1
)

Return the polynomial object which is the nth derivative of self.

View Source
    def nth_derivative(self, n=1):

        """Return the polynomial object which is the nth derivative of self."""

        if not isinstance(n, int) or n < 0:

            raise ValueError(

                "n must be a non-negative integer (got {0})".format(n)

            )

        if not self or n > self.degree:

            # Short circuit since the result would be zero.

            return self.zero_instance()

        if n == 0:

            return deepcopy(self)

        if n == 1:

            factors = range(1, self.degree + 1)

        else:

            d = self.degree - n + 1

            factorial_term = n + 1

            factors = [1] * d

            # Calculate n! for base term.

            for i in range(1, factorial_term):

                factors[0] *= i

            for i in range(1, d):

                # The last number is n * (n-1) * (n-2) * ... * i

                # The next number is (n+1) * n * (n-1) * ... * i + 1

                # To get the next number, we multiply the last number by

                # n + 1 and divide by i.

                factors[i] = (factors[i - 1] // i) * factorial_term

                factorial_term += 1

        return Polynomial(

            [c * x for c, x

             in zip(self, reversed(factors))]

        )
terms_are_valid
def terms_are_valid(
    self,
    terms
)

Return true if the terms are valid.

View Source
    def terms_are_valid(self, terms):

        """Return true if the terms are valid."""

        return True
try_set_self
def try_set_self(
    self,
    terms
)

Try applying terms to self if possible.

If not possible, returns a Polynomial with the terms.

View Source
    def try_set_self(self, terms):

        """Try applying terms to self if possible.

        If not possible, returns a Polynomial with the terms.

        """

        if self.terms_are_valid(terms):

            self.terms = terms

            return self

        return Polynomial(terms, from_monomials=True)

ZeroPolynomial

class ZeroPolynomial(

)

The zero polynomial.

View Source
class ZeroPolynomial(Freezable, Constant, valid_degrees=-inf):

    """The zero polynomial."""

    # Never used, since we would raise errors due to Freezable

    # anyways.

    valid_term_counts = (0, )

    def __init__(self):

        """Equivalent to Polynomial()."""

        Constant.__init__(self, 0)

        self._trim = self._no_op

        self._freeze()

    @property

    def _vector(self):

        """Return self._vector."""

        return (0, )

    @property

    def degree(self):

        """Return self.degree."""

        return -inf

    @classmethod

    def zero_instance(cls):

        """Return an instance of the ZeroPolynomial."""

        return ZeroPolynomial()

    @property

    def const(self):

        """Return self.const, which is always 0."""

        return 0

    @_extract_polynomial

    def __mul__(self, other):

        """Return self * other."""

        return other.zero_instance()

    @_extract_polynomial

    def __rmul__(self, other):

        """Return other * self."""

        return other.zero_instance()

    def __ipow__(self, other):

        """Return self **= power.

        Does not mutate self.

        """

        if other == 0:

            return Constant(1)

        # This call simply enforces other >= 0 and is int.

        # Could be moved out into a decorator.

        super().__ipow__(other)

        return ZeroPolynomial()

    def __repr__(self):

        """Return repr(self)."""

        return "ZeroPolynomial()"

    def __hash__(self):

        """Return hash(self). Equal to the hash of an empty tuple."""

        return hash(tuple())

Ancestors (in MRO)

  • polynomial.frozen.Freezable
  • polynomial.core.Constant
  • polynomial.core.FixedDegreePolynomial
  • polynomial.core.Monomial
  • polynomial.core.FixedTermPolynomial
  • polynomial.core.Polynomial

Class variables

valid_degrees
valid_term_counts

Static methods

zero_instance
def zero_instance(

)

Return an instance of the ZeroPolynomial.

View Source
    @classmethod

    def zero_instance(cls):

        """Return an instance of the ZeroPolynomial."""

        return ZeroPolynomial()

Instance variables

coefficient

Return the coefficient of the monomial.

const

Return self.const, which is always 0.

degree

Return self.degree.

derivative

Return a polynomial object which is the derivative of self.

monomials

Return a list with all terms in the form of monomials.

List is sorted from the highest degree term to the lowest.

terms

Get the terms of self as a list of tuples in coeff, deg form.

Terms are returned from largest degree to smallest degree, excluding any terms with a zero coefficient.

Methods

calculate
def calculate(
    self,
    x
)

Calculate the value of the polynomial at a given point.

View Source
    def calculate(self, x):

        """Calculate the value of the polynomial at a given point."""

        if self.degree < 0:

            return 0

        return sum(ak * (x ** k) for ak, k in self.terms)
nth_derivative
def nth_derivative(
    self,
    n=1
)

Return the polynomial object which is the nth derivative of self.

View Source
    def nth_derivative(self, n=1):

        """Return the polynomial object which is the nth derivative of self."""

        if not isinstance(n, int) or n < 0:

            raise ValueError(

                "n must be a non-negative integer (got {0})".format(n)

            )

        if not self or n > self.degree:

            # Short circuit since the result would be zero.

            return self.zero_instance()

        if n == 0:

            return deepcopy(self)

        if n == 1:

            factors = range(1, self.degree + 1)

        else:

            d = self.degree - n + 1

            factorial_term = n + 1

            factors = [1] * d

            # Calculate n! for base term.

            for i in range(1, factorial_term):

                factors[0] *= i

            for i in range(1, d):

                # The last number is n * (n-1) * (n-2) * ... * i

                # The next number is (n+1) * n * (n-1) * ... * i + 1

                # To get the next number, we multiply the last number by

                # n + 1 and divide by i.

                factors[i] = (factors[i - 1] // i) * factorial_term

                factorial_term += 1

        return Polynomial(

            [c * x for c, x

             in zip(self, reversed(factors))]

        )
terms_are_valid
def terms_are_valid(
    self,
    terms
)
View Source
        def terms_are_valid(self, terms):

            return (

                _terms_are_valid(self, terms)

                and orig_terms_are_valid(self, terms)

            )
try_set_self
def try_set_self(
    self,
    terms
)

Try applying terms to self if possible.

If not possible, returns a Polynomial with the terms.

View Source
    def try_set_self(self, terms):

        """Try applying terms to self if possible.

        If not possible, returns a Polynomial with the terms.

        """

        if self.terms_are_valid(terms):

            self.terms = terms

            return self

        return Polynomial(terms, from_monomials=True)