scijit.interpolate.RectBivariateSpline

scijit.interpolate.RectBivariateSpline(x, y, z, bbox=None, kx=3, ky=3, s=0.0, maxit=20)

Build a bivariate spline over gridded data.

Parameters:
x1-D float64 ndarray, length mx

Grid abscissae, strictly increasing.

y1-D float64 ndarray, length my

Grid ordinates, strictly increasing.

z2-D float64 ndarray, shape (mx, my)

Grid values. An F-ordered or strided array is copied first, so it is read correctly.

bbox(4,) array_like of float, optional

The rectangle the fit is made over, [xb, xe, yb, ye]. None, in place of the four or in one slot, means min(x), max(x), min(y), max(y), and is the default. It may only WIDEN the data rectangle; a narrower one gives FITPACK’s ier = 10, and raises. The literal default [None, None, None, None] is accepted, from Python and from inside @njit.

kxint, optional

Degree in x, 1 <= kx <= 5. Default 3.

kyint, optional

Degree in y, 1 <= ky <= 5. Default 3.

sfloat, optional

Smoothing factor; 0 gives the interpolating surface. Default 0.0. None raises TypeError.

maxitint, optional

Cap on FITPACK’s knot-search iterations; reaching it is ier = 3, which raises. Default 20. Any value below 1 means “run no iterations” and is accepted; with s = 0 no iteration is needed and the surface is unaffected. See the Notes for the one case where it still raises.

Attributes:
tx, ty1-D float64 ndarray

Knot vectors in x and y. Together with c these are the tck.

c1-D float64 ndarray

Coefficients in FITPACK’s flat bivariate layout.

kx, kyint

The two degrees.

fpfloat

Sum of squared residuals, also returned by get_residual().

ierint

FITPACK status: 0 = smoothing achieved, -1 = interpolating surface, -2 = least-squares polynomial. Any other value has already raised, so this is 0, -1 or -2 on every instance that exists.

Methods

eval_grid(x, y, dx, dy)

Grid evaluation, the equivalent of spl(x, y).

ev(xi, yi, dx, dy), ev_one(x, y, dx, dy),

partial_derivative(x, y, dx, dy), integral(xa, xb, ya, yb),

get_knots(), get_coeffs(), get_residual()

Returns:
spl_RectBivariateSpline

A jitclass instance carrying the attributes and methods below.

Raises:
ValueError

Non-increasing x or y, a z whose two dimensions do not match x and y, a bbox that is not length 4, s < 0, mx <= kx, my <= ky, kx or ky outside 1..5, and every FITPACK status outside {0, -1, -2}.

TypeError

s = None, with the message must be real number, not NoneType.

See also

scipy.interpolate.RectBivariateSpline

The scipy class this mirrors.

Notes

  • spl(x, y) runs eval_grid, scipy’s grid=True default, for every pair of scalars and arrays, so two scalars give a (1, 1) grid. ev(xi, yi) is scipy’s grid=False and ev_one(x, y) the single-point scalar spelling, both reached by name.

  • A complex z raises TypeError. scipy 1.18 accepts it, emits a ComplexWarning and discards the imaginary part, returning a float64 spline.

  • maxit is honoured as the knot-search iteration cap. maxit=5 on a smoothing fit turns a converged answer into ier = 3, which raises, and a larger cap converges where 20 does not.

  • maxit < 1 means “run no knot-search iterations”, scipy’s reading, and is accepted. Where the fit needs no iteration the result is scipy’s, measured over four grids from 6x7 to 20x18: with s = 0 (status ier = -1, interpolating) 4 of 4 cases agree with scipy at 0.000e+00 on knots, coefficients and fp, and with a large s (ier = -2, least-squares polynomial) 3 of 3 do.

  • WHERE IT STILL RAISES. If the knot search was needed and the cap stopped it, FITPACK reports ier = 3 and the fit raises, as it does for any other iteration cap. The surface FITPACK has at that point is unconverged. Raise the cap, or call fitters.regrid directly and read ier.

  • A SMOOTHING FIT MAY WARN THAT IT IS RANK DEFICIENT. With a small s on noisy data, FITPACK’s knot search can place knots so that one B-spline coefficient is not determined by the data at all. The surface still passes through the data as asked, and fp and ier report success, but between the data points it carries an arbitrary component and can swing far outside the range of the data.

    A UserWarning naming the number of undetermined coefficients is issued when this happens. The fix is a LARGER s: the condition appears when s is small enough to drive the knot count to its maximum, and disappears once the fit has room. scipy issues no warning for it.

  • .partial_derivative(dx, dy) returns a new _DerivedBivariateSpline in scipy; here it evaluates on a grid directly. A jitclass cannot construct one of itself.

  • ev_one returns a SCALAR where scipy’s spl.ev(0.5, 0.5) returns a length-1 array.

  • z as a list of lists is rejected: numba cannot reflect a list of lists. A 2-D array, including F-ordered and strided, is accepted.

Defaults work in both worlds. RectBivariateSpline is a plain @njit factory, not the jitclass itself, so RectBivariateSpline(x, y, z) compiles and runs inside @njit as well as from Python. The class it returns, _RectBivariateSpline, takes every argument explicitly, because a jitclass constructor’s defaults are Python-only.

Accuracy against scipy.interpolate.RectBivariateSpline on scipy 1.18.0. INTERPOLATING fits (s = 0) are exactly 0.0 in knots, coefficients, fp, grid values, .ev, .integral, every dx/dy pair and both get_* methods, for kx, ky = 1..5 and with bbox widened.

A SMOOTHING fit is a different matter. scipy 1.18 reaches FITPACK through a C translation whose smoothing iteration rounds differently, so on data where the knot search is sensitive the two libraries place knots in DIFFERENT POSITIONS. The surface gap is then set by the data rather than by floating point: measured up to 1.8e-02 on a 13x14 noisy grid at s = 0.01, with both sides reporting ier = 0 and both inside FITPACK’s own |fp-s|/s <= 1e-3 test. Two valid smoothing splines, not one right and one wrong. Where the knot search agrees the surfaces agree. Compare knot counts before comparing values.

prange-safe: yes.

Examples

>>> import numpy as np
>>> from numba import njit
>>> from scijit.interpolate import RectBivariateSpline
>>> x = np.linspace(0, 1, 12)
>>> y = np.linspace(0, 1, 15)
>>> z = np.outer(np.sin(3 * x), np.cos(2 * y))
>>> spl = RectBivariateSpline(x, y, z)     # fit once
>>> float(np.round(spl(0.5, 0.5)[0, 0], 8))     # spl(x, y) is the grid form; [0, 0] takes the point
0.53894085

Inside compiled code, the largest value over a coarse query grid:

>>> @njit
... def gridmax(spl):
...     return np.max(spl(np.linspace(0.0, 1.0, 5), np.linspace(0.0, 1.0, 5)))
>>> float(np.round(gridmax(spl), 6))
0.99748