axsdb.math

Fast interpolation with Numba.

This module provides high-performance interpolation functions implemented using Numba’s guvectorize decorator. These functions are designed to replace xarray’s interpolation for specific use cases where performance is critical.

Functions:

interp1d(x, y, xnew[, bounds, fill_value])

Fast 1D linear interpolation.

lerp(y, indices, weights)

Linear interpolation using precomputed indices and weights.

lerp_indices(x, xnew[, bounds])

Precompute left-indices and interpolation weights for linear interpolation.

axsdb.math.interp1d(x: ndarray, y: ndarray, xnew: ndarray, bounds: Literal['fill', 'clamp', 'raise'] = 'fill', fill_value: float | tuple[float, float] = nan) ndarray[source]

Fast 1D linear interpolation.

This function provides high-performance linear interpolation that broadcasts over leading dimensions. It powers a drop-in replacement for cases where xarray’s interpolation is too slow.

Parameters:
  • x (array-like) – X coordinates of the data points. Must be sorted in ascending order along the last axis. Results are undefined for unsorted x. Shape (…, n).

  • y (array-like) – Y coordinates of the data points. Shape (…, n).

  • xnew (array-like) – X coordinates at which to evaluate the interpolation. Shape (…, m).

  • bounds ({"fill", "clamp", "raise"}, default: "fill") –

    How to handle out-of-bounds query points:

    • "fill": use fill_value for points outside the data range.

    • "clamp": use the nearest boundary value (y[0] or y[-1]).

    • "raise": raise a ValueError if any query point is out of bounds.

  • fill_value (float or tuple of (float, float), default: np.nan) –

    Value(s) to use for out-of-bounds points when bounds="fill":

    • if a single float, use for both lower and upper bounds;

    • if a 2-tuple, use (fill_lower, fill_upper).

Returns:

Interpolated values at the query points. The output shape is determined by numpy broadcasting rules applied to x, y, and xnew. Shape (…, m).

Return type:

ndarray

Raises:

ValueError

  • If bounds="raise" and any query point is outside the data range. * If bounds is not one of “fill”, “clamp”, or “raise”. * If fill_value is a tuple with length != 2.

Notes

  • The implementation uses a Numba gufunc with signature (n),(n),(m)->(m) for the core interpolation, enabling efficient broadcasting over arbitrary leading dimensions.

  • The function assumes x is sorted in ascending order along the last axis. Results are undefined if this assumption is violated.

  • NaN values in xnew are passed through (output will be NaN).

Examples

Basic interpolation:

>>> x = np.array([0.0, 1.0, 2.0, 3.0])
>>> y = np.array([0.0, 1.0, 4.0, 9.0])
>>> xnew = np.array([0.5, 1.5, 2.5])
>>> interp1d(x, y, xnew)
array([0.5, 2.5, 6.5])

With fill values for out-of-bounds:

>>> xnew = np.array([-1.0, 1.5, 5.0])
>>> interp1d(x, y, xnew, bounds="fill", fill_value=(-999.0, 999.0))
array([-999. ,    2.5,  999. ])

Clamping to boundary values:

>>> interp1d(x, y, xnew, bounds="clamp")
array([0. , 2.5, 9. ])

Broadcasting over multiple curves:

>>> x = np.array([0.0, 1.0, 2.0])
>>> y = np.array([[0.0, 1.0, 2.0],    # Linear
...               [0.0, 1.0, 4.0]])   # Quadratic
>>> xnew = np.array([0.5, 1.5])
>>> interp1d(x, y, xnew)
array([[0.5, 1.5],
       [0.5, 2.5]])
axsdb.math.lerp(y: ndarray, indices: ndarray, weights: ndarray) ndarray[source]

Linear interpolation using precomputed indices and weights.

This is the fast inner loop for the case where many y arrays share the same x grid and query points. The binary search is done once via lerp_indices(); this function executes only the linear combination y[i] + t * (y[i+1] - y[i]).

Parameters:
  • y (ndarray) – Data values. The last axis must correspond to the x grid used in lerp_indices(). Broadcasting over leading dimensions is handled by the underlying gufunc. Shape (…, n).

  • indices (ndarray) – Left-bin indices from lerp_indices(). IMPORTANT: Indices must be in the range [0, n-2] where n = y.shape[-1]. This invariant is enforced by lerp_indices() but is not validated here for performance reasons. Shape (m,).

  • weights (ndarray) – Interpolation weights from lerp_indices(). Shape (m,).

Returns:

Interpolated values. NaN weights (from bounds="fill") propagate as NaN in the output. Shape (…, m).

Return type:

ndarray

Notes

This function does not perform bounds checking on indices for performance. The caller must ensure indices are valid (in [0, n-2]). Using lerp_indices() guarantees this invariant.

axsdb.math.lerp_indices(x: ndarray, xnew: ndarray, bounds: Literal['fill', 'clamp', 'raise'] = 'fill') tuple[ndarray, ndarray][source]

Precompute left-indices and interpolation weights for linear interpolation.

When the same query points xnew will be applied to many different y arrays sharing the same x grid, it is far cheaper to run the binary search once here and then call lerp() for each y. That function skips the search entirely and executes only the y[left] + t*(y[left+1] - y[left]) step.

Parameters:
  • x (ndarray) – Sorted coordinate grid (1-D). Shape (n,).

  • xnew (ndarray) – Query points (1-D). Shape (m,).

  • bounds ({"fill", "clamp", "raise"}, default: "fill") –

    Out-of-bounds handling, same semantics as interp1d().

    • "fill": out-of-bounds indices are set to 0 with weight NaN so that lerp() will produce NaN there. The caller can replace those NaNs after the fact if a different fill value is needed.

    • "clamp": out-of-bounds queries are clamped to the nearest boundary index with weight 0 (reproducing y[0] or y[-1]).

    • "raise": raises immediately if any query is out of bounds.

Returns:

  • indices (ndarray) – Left-bin indices as floats (required by the gufunc signature). Shape (m,), dtype float64.

  • weights (ndarray) – Fractional position within each bin: t = (xnew - x[i]) / (x[i+1] - x[i]). Shape (m,), dtype float64.

Raises:

ValueError – If bounds="raise" and any query point is outside [x[0], x[-1]].