Skip to content

Commit

Permalink
Continuing to work on documentation
Browse files Browse the repository at this point in the history
* refactored and renamed basis() to vandermonde_X for clarity
* replaced @lru_cache & @Property with cached_property
  • Loading branch information
jeffrey-hokanson committed Oct 26, 2020
1 parent 14af011 commit 8fdee38
Show file tree
Hide file tree
Showing 15 changed files with 484 additions and 97 deletions.
119 changes: 111 additions & 8 deletions doc/basis.rst
Original file line number Diff line number Diff line change
@@ -1,31 +1,134 @@
================
Polynomial Bases
================

The foundation of this library is a set of *polynomial basis* classes.
Each class defines a set of functions :math:`\lbrace \phi_j \rbrace_j`
that form a basis for the desired polynomial space;
i.e., a mononomial basis for polynomials of degree 3 of one variable
has basis functions

.. math::
\phi_0(x) = 1, \quad \phi_1(x) = x, \quad \phi_2(x) = x^2, \quad \phi_3(x) = x^3.
Here we consider two types of polynomial bases:
*total degree* polynomial bases
and *maximum degree* polynomial bases:

.. math::
\mathcal P_{m}^{\text{tot}}
& := \text{Span} \left\lbrace f:
f(\mathbf x) = \prod_{i=1}^d x_i^{\alpha_i}
\right\rbrace_{|\boldsymbol \alpha| \le m}, \quad
& \text{where} & \quad |\boldsymbol \alpha| = \sum_{i=1}^d \alpha_i; \\
\label{eq:max}
\mathcal P_{\mathbf m}^{\text{max}}
& :=
\text{Span} \left \lbrace f:
f(\mathbf x) = \prod_{i=1}^d x_i^{\alpha_i}
\right\rbrace_{ \boldsymbol \alpha \le \mathbf m},
& \text{where} & \quad \boldsymbol \alpha \le \mathbf m \Leftrightarrow
\alpha_i \le m_i.
Most bases are initialized using a set of input coordinates :math:`\mathbf x_i`
and a degree.
We require the input coordinates to determine a scaling to make the
basis well conditioned;
i.e., for the Legendre polynomial basis
we perform an affine transformation (which does not alter polynomial basis)
such that these input coordinates are in the :math:`[-1,1]^m` hypercube.


.. note::
Although these polynomial bases ideally represent the same quantities,
depending on the input coordinates their numerical conditioning can be very different.
The most robust approach, although expensive, is to use :class:`~polyrat.ArnoldiPolynomialBasis`.
If performance is desired, choose a tensor product polynomial basis
for which the input coordinates (after scaling) approximately sample the measure under which the polynomials
are orthogonal.
As a rule of thumb:

* if :math:`x_i` is uniformly distributed on :math:`[a,b]` use :class:`~polyrat.LegendrePolynomialBasis`;
* if :math:`x_i` is normally distributed with mean :math:`\mu` and standard deviation :math:`\sigma` use :class:`~polyrat.HermitePolynomialBasis`;
* if :math:`\mathbf x_i \in \mathbb{C}^d` for :math:`d\ge 2` and degree greater than 10, use :class:`~polyrat.ArnoldiPolynomialBasis`.



.. autoclass:: polyrat.PolynomialBasis
:members:

.. automethod:: __init__





Tensor Product Bases
====================

Several polynomial bases are constructed
from univariate polynomial bases defined in Numpy
:func:`~numpy:numpy.polynomial`.
These bases are each constructed by subclassing
:class:`polyrat.TensorProdutPolynomialBasis`
which performs two tasks:

* multiplying univariate polynomials in each coordinate to construct the desired multivariate basis and
* using an affine transformation in each coordinate to improve conditioning.

Here we provide an overview of the internals of this class to show
how new bases could be implemented in the same way.

.. autoclass:: polyrat.TensorProductPolynomialBasis

.. automethod:: _vander

.. automethod:: _der

.. automethod:: roots

Numpy-based Bases
=================
.. automethod:: _set_scale

.. automethod:: _scale

.. automethod:: _inv_scale


Monomial Basis
--------------

.. autoclass:: polyrat.MonomialPolynomialBasis
:members:

Legendre Basis
--------------

.. autoclass:: polyrat.LegendrePolynomialBasis
:members:

Chebyshev Basis
---------------

.. autoclass:: polyrat.ChebyshevPolynomialBasis
:members:

Hermite Basis
-------------

.. autoclass:: polyrat.HermitePolynomialBasis
:members:

Laguerre Basis
--------------

.. autoclass:: polyrat.LaguerrePolynomialBasis
:members:


Arnoldi Polynomial Basis
========================
.. autoclass:: polyrat.ArnoldiPolynomialBasis
:members:




Lagrange Polynomial Basis
=========================

.. autoclass:: polyrat.LagrangePolynomialBasis
6 changes: 5 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

# -- General configuration ---------------------------------------------------

#autoclass_content = 'both'

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
Expand Down Expand Up @@ -184,4 +186,6 @@
# -- Options for intersphinx extension ---------------------------------------

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
intersphinx_mapping = {'https://docs.python.org/': None,
'numpy': ('http://docs.scipy.org/doc/numpy/', None),
}
26 changes: 6 additions & 20 deletions polyrat/arnoldi.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,28 +156,14 @@ def vandermonde_arnoldi_eval_der(X, R, indices, mode, weight = None, V = None):
class ArnoldiPolynomialBasis(PolynomialBasis):
r""" A polynomial basis constructed using Vandermonde with Arnoldi
Parameters
----------
X: array-like (M,m)
Input coordinates
degree: int or list of ints
Polynomial degree. If an int, a total degree polynomial is constructed;
if a list, the list must be length m and a maximum degree polynomial is
constructed.
"""
def __init__(self, X, degree, weight = None):
self.X = np.copy(np.atleast_2d(X))
self.dim = self.X.shape[1]
try:
self.degree = int(degree)
self.mode = 'total'
except (TypeError, ValueError):
self.degree = np.copy(degree).astype(np.int)
self.mode = 'max'

PolynomialBasis.__init__(self, X, degree)
self._Q, self._R, self._indices = vandermonde_arnoldi_CGS(self.X, self.degree, weight = weight)

def basis(self):
self._Q.flags.writeable = False

@property
def vandermonde_X(self):
return self._Q

def vandermonde(self, X, weight = None):
Expand All @@ -192,7 +178,7 @@ def vandermonde_derivative(self, X, weight = None):
def roots(self, coef, *args, **kwargs):
from .basis import LegendrePolynomialBasis
from .polynomial import PolynomialApproximation
y = self.basis() @ coef
y = self.vandermonde_X @ coef
poly = PolynomialApproximation(self.degree, Basis = LegendrePolynomialBasis)
poly.fit(self.X, y)
roots = poly.roots()
Expand Down
Loading

0 comments on commit 8fdee38

Please sign in to comment.