archimedes.approximation.ConstrainedBasis¶

class archimedes.approximation.ConstrainedBasis(
base: Basis,
matrix: ndarray,
)¶

A basis whose functions are fixed linear combinations of another’s

Usually built so that every combination satisfies a set of linear constraints on the reference domain, for example to satisfy a boundary condition by construction.

Given a constraint matrix \(A\) (one row per constraint, one column per base function), the new basis functions are

\[\phi_i(x) = \sum_j N_{ji} \, \psi_j(x)\]

where the columns of \(N\) span \(\mathrm{null}(A)\), i.e. \(A N \approx 0\). Every function in the new basis therefore satisfies every constraint row to numerical precision.

Should typically not be constructed directly; use one of the classmethod constructors instead.

Methods

boundary_dofs([order])

Indices of the degrees of freedom at the left/right ends of the domain.

dirichlet(base)

Construct a basis that vanishes at both reference endpoints.

evaluate(x[, deriv, side])

Evaluate all n_basis basis functions at x.

from_constraints(base, constraints)

Construct a basis from the nullspace of a provided matrix.

neumann(base)

Construct a basis whose derivatives vanish at both reference endpoints.

Attributes

Parameters

The target-domain parameters this basis expects.

density

Whether this basis is orthonormal with respect to a probability measure (unit mass) rather than the raw weight of the associated Measure.

n_basis

ndim

Number of independent variables the basis functions take.

base

The basis that is recombined to form the constrained basis.

matrix

Constraint matrix defining the new basis as linear combinations of base.

classmethod dirichlet(
base: Basis,
) → ConstrainedBasis¶

Construct a basis that vanishes at both reference endpoints.

The resulting basis satisfies the Dirichlet boundary condition \(\phi(-1) = \phi(1) = 0\).

Spans the same space as the classical hand-derived combinations (e.g. Shen’s Chebyshev-Galerkin basis \(T_k - T_{k+2}\)), but is not necessarily numerically identical to them. Both are valid bases of \(\mathrm{null}(A)\).

This cannot be constructed to hold nonzero values at the endpoints; see ConcatBasis for combining this with another “vertex” basis to hold nonzero endpoint values.

Examples

>>> import numpy as np
>>> from archimedes.approximation import (
...     ConstrainedBasis,
...     OrthogonalPolynomialBasis,
... )
>>> from archimedes.measure import LegendreMeasure
>>> legendre = OrthogonalPolynomialBasis(LegendreMeasure(), 6)
>>> basis = ConstrainedBasis.dirichlet(legendre)
>>> basis.n_basis
4
>>> x = np.array([-1.0, 1.0])
>>> bool(np.allclose(basis.evaluate(x), 0.0))
True
classmethod from_constraints(
base: Basis,
constraints: Callable[[Basis], ndarray],
) → ConstrainedBasis¶

Construct a basis from the nullspace of a provided matrix.

The constraint matrix is null(A), where A = constraints(base).

A useful way to construct the constraints function is by evaluating the base basis. Basis.evaluate() returns a Vandermonde-like matrix with one row per point and one column per basis function. If A = base.evaluate(x), then A @ c evaluates the function defined by coefficient vector c at x. Hence the condition that the function vanish at x is the same as A @ c = 0; in other words null(A) is the set of coefficient vectors whose function vanishes at x. See example below.

Parameters:
  • base (Basis) – The basis to combine.

  • constraints (callable) – constraints(base) -> ndarray of shape (n_constraints, base.n_basis).

Examples

Build a basis where every function vanishes at the left endpoint only:

>>> import numpy as np
>>> from archimedes.approximation import (
...     ConstrainedBasis,
...     FunctionSpace,
...     OrthogonalPolynomialBasis,
... )
>>> from archimedes.measure import LegendreMeasure, UnitInterval
>>> legendre = OrthogonalPolynomialBasis(LegendreMeasure(), 6)
>>> basis = ConstrainedBasis.from_constraints(
...     legendre, lambda base: base.evaluate(np.array([-1.0]))
... )
>>> basis.n_basis
5

Any combination of the new functions satisfies the constraint:

>>> coeffs = np.random.default_rng(0).standard_normal(basis.n_basis)
>>> x = np.array([-1.0, 0.0, 1.0])
>>> u = basis.evaluate(x) @ coeffs
>>> bool(np.isclose(u[0], 0.0))
True

The constraint is imposed on the reference domain, so it holds at the left endpoint of any target interval:

>>> space = FunctionSpace(basis, UnitInterval.Parameters(a=0.0, b=2.0))
>>> f = space.project(np.sin)
>>> bool(np.isclose(f(np.array([0.0]))[0], 0.0))
True
classmethod neumann(
base: Basis,
) → ConstrainedBasis¶

Construct a basis whose derivatives vanish at both reference endpoints.

The resulting basis satisfies the Neumann boundary condition \(\phi'(-1) = \phi'(1) = 0\).

Examples

>>> import numpy as np
>>> from archimedes.approximation import (
...     ConstrainedBasis,
...     OrthogonalPolynomialBasis,
... )
>>> from archimedes.measure import LegendreMeasure
>>> legendre = OrthogonalPolynomialBasis(LegendreMeasure(), 6)
>>> basis = ConstrainedBasis.neumann(legendre)
>>> basis.n_basis
4
>>> x = np.array([-1.0, 1.0])
>>> dphi = basis.evaluate(x, deriv=1)
>>> bool(np.allclose(dphi, 0.0))
True
__init__(
base: Basis,
matrix: ndarray,
) → None¶
boundary_dofs(order: int = 0) → tuple[int | None, int | None]¶

Indices of the degrees of freedom at the left/right ends of the domain.

Can be used for example to set boundary conditions or enforce continuity at the domain endpoints.

Parameters:

order (int, optional) – Derivative order to look up. Default 0 (the endpoint value).

Returns:

left, right – Index into this basis’s functions, or None where there is no degree of freedom of that order at that end.

Return type:

int or None

Notes

Only meaningful for nodal families (e.g. LagrangeBasis) with nodes at the endpoints. Modal families (e.g. OrthogonalPolynomialBasis) and nodal bases with interior-only nodes (Gauss-Legendre points, for instance) do not have boundary DOFs.

evaluate(x, deriv: int = 0, *, side: str = RIGHT, **domain_kwargs)¶

Evaluate all n_basis basis functions at x.

Parameters:
  • x (array_like) – Evaluation points, shape (npts,).

  • deriv (int, optional) – Order of derivative to evaluate. Default 0.

  • side ({"right", "left"}, optional) – Which one-sided limit to take where the basis is two-valued. Default "right". Irrelevant for smooth bases.

  • **domain_kwargs – Target-domain parameters; see the subclass docstring.

Returns:

phi – Basis values (or deriv-th derivatives), shape (npts, n_basis).

Return type:

ndarray

property Parameters: type¶

The target-domain parameters this basis expects.

A Parameters subclass (e.g. UnitInterval.Parameters). Returns the type, not an instance.

Determines which reference domain the basis is defined on, for example:

base: Basis¶

The basis that is recombined to form the constrained basis.

density: bool = False¶

Whether this basis is orthonormal with respect to a probability measure (unit mass) rather than the raw weight of the associated Measure.

Only meaningful for orthogonal polynomial families based on a Measure (in particular OrthogonalPolynomialBasis); other families should leave this False.

matrix: ndarray¶

Constraint matrix defining the new basis as linear combinations of base.

property n_basis: int¶
ndim: int = 1¶

Number of independent variables the basis functions take.

Typically 1, since most bases are univariate. TensorBasis is the exception, with one variable per tensored factor.