Source code for seemps.analysis.expansion.legendre
from __future__ import annotations
import numpy as np
from ...typing import Vector
from ..mesh import array_affine
from .expansion import OrthogonalExpansion, ScalarFunction
class LegendreExpansion(OrthogonalExpansion):
"""
Expansion in the Legendre basis.
The polynomials are orthogonal on [−1, 1] with uniform weight.
Recurrence:
(k+1) Pₖ₊₁(x) = (2k+1) x Pₖ(x) − k Pₖ₋₁(x).
"""
canonical_domain = (-1, 1)
def __init__(self, coeffs: Vector, domain: tuple[float, float]):
super().__init__(coeffs, domain)
[docs]
def get_recurrence(self, k: int) -> tuple[float, float, float]:
"""Legendre recurrence: (k+1) P_{k+1}(x) = (2k+1) x P_k(x) - k P_{k-1}(x)"""
α_k = (2 * k + 1) / (k + 1)
β_k = 0.0
γ_k = k / (k + 1)
return (α_k, β_k, γ_k)
@property
def p1_factor(self) -> float:
return 1.0
[docs]
@classmethod
def project(
cls,
func: ScalarFunction,
start: float = -1.0,
stop: float = 1.0,
order: int | None = None,
) -> LegendreExpansion:
if order is None:
order = cls.estimate_order(func, start, stop)
nodes, weights = np.polynomial.legendre.leggauss(order)
nodes_affine = array_affine(
nodes, orig=LegendreExpansion.canonical_domain, dest=(start, stop)
)
P_matrix = np.vstack(
[np.polynomial.legendre.legval(nodes, [0] * k + [1]) for k in range(order)]
)
coeffs = (P_matrix * func(nodes_affine)).dot(weights)
coeffs *= (2 * np.arange(order) + 1) / 2
return cls(coeffs, domain=(start, stop))