Source code for minterpy.polynomials.lagrange_polynomial
"""
This module contains the `LagrangePolynomial` class.
The `LagrangePolynomial` class is a concrete implementation of the abstract
base class :py:class:`MultivariatePolynomialSingleABC
<.core.ABC.multivariate_polynomial_abstract.MultivariatePolynomialSingleABC>`
for polynomials in the Lagrange basis.
Background information
----------------------
The relevant section of the documentation on
:ref:`fundamentals/polynomial-bases:Lagrange basis` contains a more
detailed explanation regarding the polynomials in the Lagrange form.
Implementation details
----------------------
`LagrangePolynomial` is currently designed to be a bare concrete
implementation of the abstract base class
:py:class:`MultivariatePolynomialSingleABC
<.core.ABC.multivariate_polynomial_abstract.MultivariatePolynomialSingleABC>`.
In other words, most (if not all) concrete implementation of the abstract
methods are left undefined and will raise an exception when called or invoked.
`LagrangePolynomial` serves as an entry point to Minterpy polynomials
especially in the context of function approximations because the intuitiveness
of the corresponding coefficients (i.e., they are the function values at
the grid points). However, the polynomial itself is not fully featured
(e.g., no addition, multiplication, etc.) as compared to polynomials in
the other basis.
----
"""
from __future__ import annotations
import copy
import numpy as np
from minterpy.global_settings import SCALAR
from minterpy.core.ABC import MultivariatePolynomialSingleABC
from minterpy.utils.polynomials.lagrange import integrate_monomials_lagrange
from minterpy.utils.verification import dummy, verify_domain
__all__ = ["LagrangePolynomial"]
def scalar_add_lagrange(
poly: "LagrangePolynomial",
scalar: SCALAR,
) -> "LagrangePolynomial":
"""Add an instance of polynomial in the Lagrange basis with a real scalar.
This is the concrete implementation of ``_scalar_add`` method in the
``MultivariatePolynomialSingleABC`` for handling expressions like
``poly + x``, where ``x`` is a real scalar number and ``poly`` is
an instance of ``LagrangePolynomial``.
Parameters
----------
poly : LagrangePolynomial
A polynomial instance in the Lagrange basis to be added with a scalar.
scalar : SCALAR
The real scalar number to be added to the polynomial instance.
Returns
-------
LagrangePolynomial
The summed polynomial in the Lagrange basis; the polynomial is a new
instance.
"""
# Create a copy of the polynomial
poly_sum = copy.deepcopy(poly)
# Add **all** the coefficients with the real scalar number
poly_sum.coeffs += scalar
return poly_sum
def integrate_over_lagrange(
poly: "LagrangePolynomial",
bounds: np.ndarray,
) -> np.ndarray:
"""Compute the definite integral of a polynomial in the Lagrange basis.
Parameters
----------
poly : LagrangePolynomial
The polynomial of which the integration is carried out.
bounds : :class:`numpy:numpy.ndarray`
The bounds (lower and upper) of the definite integration, an ``(M, 2)``
array, where ``M`` is the number of spatial dimensions.
Returns
-------
:class:`numpy:numpy.ndarray`
The integral value of the polynomial over the given domain.
"""
quad_weights = _compute_quad_weights(poly, bounds)
return quad_weights @ poly.coeffs
# TODO redundant
lagrange_generate_internal_domain = verify_domain
lagrange_generate_user_domain = verify_domain
[docs]
class LagrangePolynomial(MultivariatePolynomialSingleABC):
"""Concrete implementation of polynomials in the Lagrange basis.
A polynomial in the Lagrange basis is the sum of so-called Lagrange
polynomials, each of which is multiplied with a coefficient.
The value a *single* Lagrange monomial is per definition :math:`1`
at one of the grid points and :math:`0` on all the other points.
Notes
-----
- The Lagrange polynomials commonly appear in the Wikipedia article is
in Minterpy considered the "monomial". In other words, a polynomial in
the Lagrange basis is the sum of Lagrange monomials each of which is
multiplied with a coefficient.
- A polynomial in the Lagrange basis may also be defined also
for multi-indices of exponents which are not downward-closed.
In such cases, the corresponding Lagrange monomials also form a basis.
These mononomials still possess their special property of being :math:`1`
at a single grid point and :math:`0` at all the other points,
with respect to the given grid.
"""
# --- Virtual Functions
# Evaluation
_eval = staticmethod(dummy) # type: ignore
# Arithmetics (polynomial-polynomial)
_add = staticmethod(dummy) # type: ignore
_sub = staticmethod(dummy) # type: ignore
_mul = staticmethod(dummy) # type: ignore
_div = staticmethod(dummy) # type: ignore
_pow = staticmethod(dummy) # type: ignore
# Arithmetics (polynomial-scalar)
_scalar_add = staticmethod(scalar_add_lagrange) # type: ignore
# Calculus
_partial_diff = staticmethod(dummy) # type: ignore
_diff = staticmethod(dummy) # type: ignore
_integrate_over = staticmethod(integrate_over_lagrange)
# Domain generation
generate_internal_domain = staticmethod(lagrange_generate_internal_domain)
generate_user_domain = staticmethod(lagrange_generate_user_domain)
# --- Internal utility functions
def _compute_quad_weights(
poly: LagrangePolynomial,
bounds: np.ndarray,
) -> np.ndarray:
"""Compute the quadrature weights of a polynomial in the Lagrange basis.
"""
# Get the relevant data from the polynomial instance
exponents = poly.multi_index.exponents
generating_points = poly.grid.generating_points
# ...from the MultiIndexTree
tree = poly.grid.tree
split_positions = tree.split_positions
subtree_sizes = tree.subtree_sizes
masks = tree.stored_masks
quad_weights = integrate_monomials_lagrange(
exponents,
generating_points,
split_positions,
subtree_sizes,
masks,
bounds,
)
return quad_weights