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)