/src/alea/math_utils/polynomials/polynomials.py

https://bitbucket.org/aleadev/alea · Python · 194 lines · 131 code · 49 blank · 14 comment · 31 complexity · 39a30a298ef41b164b3a5de7949d7615 MD5 · raw file

  1. import sys
  2. import math
  3. from abc import ABCMeta, abstractmethod, abstractproperty
  4. import numpy as np
  5. import alea.math_utils.polynomials.polynomials_base as _p
  6. import alea.utils.decorators as _dec
  7. if sys.version_info >= (3, 0):
  8. xrange = range
  9. class PolynomialFamily(object):
  10. """Abstract base for families of (orthogonal) polynomials"""
  11. __metaclass__ = ABCMeta
  12. x = np.poly1d([1.0, 0.0])
  13. @abstractmethod
  14. def recurrence_coefficients(self, n):
  15. return NotImplemented
  16. @abstractmethod
  17. def get_structure_coefficient(self, a, b, c):
  18. """Return specific structure coefficient"""
  19. return NotImplemented
  20. def eval(self, n, x, all_degrees=False):
  21. """Evaluate polynomial of degree ``n`` at points ``x``"""
  22. values = _p.compute_poly(self.recurrence_coefficients, n, x)
  23. if all_degrees:
  24. return values
  25. else:
  26. return values[-1]
  27. def __getitem__(self, n):
  28. x = np.poly1d([1, 0])
  29. return self.eval(n, x)
  30. def get_coefficients(self, n):
  31. """Return coefficients of the polynomial with degree ``n`` of
  32. the family."""
  33. x = np.poly1d([1, 0])
  34. l = self.eval(n, x)
  35. return l.coeffs[::-1]
  36. def get_beta(self, n):
  37. """Return the coefficients beta needed to multiply a polynomial by x such that
  38. p.x * p[n] == beta[1] * p[n + 1] - beta[0] * p[n] + beta[-1] * p[n - 1]"""
  39. (a, b, c) = self.recurrence_coefficients(n)
  40. return (a / b, 1 / b, c / b)
  41. def get_structure_coefficients(self, n):
  42. """Return structure coefficients of indices up to ``n``"""
  43. structcoeffs = getattr(self, "_structcoeffs", np.empty((0, 0, 0)))
  44. if n > structcoeffs.shape[0]:
  45. structcoeffs = np.array(
  46. [[[self.get_structure_coefficient(a, b, c)
  47. for a in xrange(n)]
  48. for b in xrange(n)]
  49. for c in xrange(n)])
  50. return structcoeffs[0:n, 0:n, 0:n]
  51. @abstractmethod
  52. def norm(self, n, sqrt=True):
  53. """Return norm of the ``n``-th degree polynomial."""
  54. return NotImplemented
  55. @abstractproperty
  56. def normalised(self):
  57. """True if polynomials are normalised."""
  58. return False
  59. class BasePolynomialFamily(PolynomialFamily):
  60. """ """
  61. def __init__(self, rc_func, sqnorm_func=None, sc_func=None, normalised=False, cache_size=1000):
  62. if cache_size > 0:
  63. cache = _dec.simple_int_cache(cache_size)
  64. else:
  65. cache = lambda func: func
  66. self._rc_func = cache(rc_func)
  67. if sqnorm_func is None:
  68. sqnorm_func = lambda n: _p.sqnorm_from_rc(rc_func, n)
  69. self._sqnorm_func = cache(sqnorm_func)
  70. if sc_func is None:
  71. # needs to be implemented in _polynomials
  72. sc_func = NotImplemented
  73. self._sc_func = sc_func
  74. self._normalised = normalised
  75. def normalise(self):
  76. rc_func = _p.normalise_rc(self._rc_func, self._sqnorm_func)
  77. self._rc_func = rc_func
  78. self._sqnorm_func = None
  79. self._sc_func = NotImplemented
  80. self._normalised = True
  81. def recurrence_coefficients(self, n):
  82. return self._rc_func(n)
  83. def get_structure_coefficient(self, a, b, c):
  84. return self._sc_func(a, b, c)
  85. def norm(self, n, sqrt=True):
  86. """Return the norm of the `n`-th polynomial."""
  87. if self._normalised:
  88. return 1.0
  89. elif sqrt:
  90. return math.sqrt(self._sqnorm_func(n))
  91. else:
  92. return self._sqnorm_func(n)
  93. @property
  94. def normalised(self):
  95. """True if polynomials are normalised."""
  96. return self._normalised
  97. class LegendrePolynomials(BasePolynomialFamily):
  98. def __init__(self, a= -1.0, b=1.0, normalised=True):
  99. rc_func = _p.rc_legendre
  100. sqnorm_func = _p.sqnorm_legendre
  101. if a != -1.0 or b != 1.0:
  102. rc_func = _p.rc_window_trans(rc_func, (-1, 1), (a, b))
  103. sqnorm_func = None
  104. super(self.__class__, self).__init__(rc_func, sqnorm_func)
  105. if normalised:
  106. self.normalise()
  107. class StochasticHermitePolynomials(BasePolynomialFamily):
  108. def __init__(self, mu=0.0, sigma=1.0, normalised=True):
  109. rc_func = _p.rc_stoch_hermite
  110. sqnorm_func = _p.sqnorm_stoch_hermite
  111. if mu != 0.0 or sigma != 1.0:
  112. rc_func = _p.rc_shift_scale(rc_func, mu, sigma)
  113. sqnorm_func = None
  114. super(self.__class__, self).__init__(rc_func, sqnorm_func)
  115. if normalised:
  116. self.normalise()
  117. class JacobiPolynomials(BasePolynomialFamily):
  118. def __init__(self, alpha, beta, a= -1.0, b=1.0, normalised=True):
  119. if alpha <= -1 or beta <= -1:
  120. raise TypeError("alpha and beta must be larger than -1")
  121. rc_func = lambda n: _p.rc_jacobi(n, alpha, beta)
  122. sqnorm_func = None
  123. if a != -1.0 or b != 1.0:
  124. rc_func = _p.rc_window_trans(rc_func, (-1.0, 1.0), (a, b))
  125. super(self.__class__, self).__init__(rc_func, sqnorm_func)
  126. if normalised:
  127. self.normalise()
  128. class ChebyshevT(BasePolynomialFamily):
  129. def __init__(self, a= -1.0, b=1.0, normalised=True):
  130. rc_func = _p.rc_chebyshev_t
  131. sqnorm_func = None
  132. if a != -1.0 or b != 1.0:
  133. rc_func = _p.rc_window_trans(rc_func, (-1, 1), (a, b))
  134. super(self.__class__, self).__init__(rc_func, sqnorm_func)
  135. if normalised:
  136. self.normalise()
  137. class ChebyshevU(BasePolynomialFamily):
  138. def __init__(self, a= -1.0, b=1.0, normalised=True):
  139. rc_func = _p.rc_chebyshev_u
  140. sqnorm_func = None
  141. if a != -1.0 or b != 1.0:
  142. rc_func = _p.rc_window_trans(rc_func, (-1, 1), (a, b))
  143. super(self.__class__, self).__init__(rc_func, sqnorm_func)
  144. if normalised:
  145. self.normalise()