Source code for rustmatrix.quadrature

"""Gaussian quadrature points/weights for arbitrary weighting functions.

Direct port of ``pytmatrix.quadrature.quadrature`` — used by
:func:`rustmatrix.orientation.orient_averaged_fixed` to build an
integration rule for the orientation PDF in beta.

Pure Python on top of numpy/scipy; no Rust involvement.
"""

from __future__ import annotations

import numpy as np


[docs] def discrete_gautschi(z: np.ndarray, w: np.ndarray, n_iter: int): """Discrete Gautschi / Stieltjes procedure. Builds three-term recurrence coefficients ``(a, b)`` for the orthogonal polynomials with respect to the discrete inner product defined by abscissas ``z`` and weights ``w``. Parameters ---------- z : ndarray, shape (n,) Abscissas sampling the underlying interval. w : ndarray, shape (n,) Weights at each abscissa (including the integration differential). n_iter : int Number of recurrence levels to compute. Returns ------- a : ndarray, shape (n_iter,) Diagonal coefficients. b : ndarray, shape (n_iter - 1,) Subdiagonal coefficients. """ p = np.ones(z.shape) p /= np.sqrt(np.dot(p, p)) p_prev = np.zeros(z.shape) wz = z * w a = np.empty(n_iter) b = np.empty(n_iter) for j in range(n_iter): p_norm = np.dot(w * p, p) a[j] = np.dot(wz * p, p) / p_norm b[j] = 0.0 if j == 0 else p_norm / np.dot(w * p_prev, p_prev) p_new = (z - a[j]) * p - b[j] * p_prev p_prev = p p = p_new return a, b[1:]
[docs] def get_points_and_weights(w_func=None, left=-1.0, right=1.0, num_points=5, n=4096): r"""Quadrature points and weights for a custom weighting function. Approximates :math:`\int_{left}^{right} f(x)\,w(x)\,dx \approx \sum_i w_i f(x_i)` by building the orthogonal-polynomial recurrence for ``w`` from a dense midpoint sample, diagonalising the resulting tridiagonal Jacobi matrix, and reading off the points and weights. Parameters ---------- w_func : callable, optional Weighting function ``w(x)``. Defaults to the constant 1 (i.e. ordinary Gauss-Legendre on ``[left, right]``). left, right : float Integration interval. num_points : int Number of quadrature points to return. n : int Resolution of the midpoint sample used to approximate ``w``. Returns ------- points, weights : ndarray, shape (num_points,) Quadrature nodes and their weights, sorted by node. Notes ----- Used by :func:`orientation.orient_averaged_fixed` to build a rule that integrates over the orientation PDF in β. """ if w_func is None: w_func = lambda x: np.ones(x.shape) # noqa: E731 dx = (float(right) - left) / n z = np.linspace(left + 0.5 * dx, right - 0.5 * dx, n) w = dx * w_func(z) a, b = discrete_gautschi(z, w, num_points) alpha = a beta = np.sqrt(b) J = np.diag(alpha) J += np.diag(beta, k=-1) J += np.diag(beta, k=1) points, v = np.linalg.eigh(J) ind = points.argsort() points = points[ind] weights = v[0, :] ** 2 * w.sum() weights = weights[ind] return points, weights