Internal numerical contracts#

SplineOps keeps its public modules independent. Internal reuse is accepted only when the participating algorithms have the same mathematical contract; similar vocabulary is not enough. This page records those decisions so that future cleanup does not silently change results.

Compatibility matrix#

Primitive compatibility#

Primitive

Current consumers

Reuse decision

Reason

Cubic mirror prefilter

TensorSpline, differentials

Shared implementation

Both require cardinal cubic coefficients on a whole-sample mirror extension. Differentials uses the batched last-axis implementation and independently applies derivative filters.

Basis evaluation

TensorSpline, resize, affine

Partly shared

Affine evaluates through TensorSpline. Resize projection has a different scale-dependent cross-Gram contract and remains specialized. NumPy point supports contract directly with their weights; CuPy retains its existing broadcast-and-reduce path until GPU evidence exists.

Boundary mapping

Interpolation, resize, pyramids

Not unified

Mirror names do not imply identical reflection centers, sample grids, or projection behavior. Each public contract keeps its own mapping.

Coordinate generation

TensorSpline, affine, resize

Shared policy, separate geometry

Evaluation is tiled to bound temporaries. Affine pull-back coordinates and endpoint-aligned resize coordinates are purpose-specific.

Array backend detection

TensorSpline

Centralized inside interpolation

NumPy is the package-wide backend. CuPy remains experimental and must not leak into modules that have no GPU contract.

Plan/cache objects

Resize, TensorSpline, affine, smoothing, regression, differentials

Separate public concepts

ResizePlan represents reusable regular-grid projection work. TensorSplineGeometryPlan retains fixed query support independently of compatible sample values, and AffinePlan composes detached geometry plans. Smoothing retains a frequency response, regression a sparse factorization, and differentials a fixed shape/spacing contract. These purpose-specific lifetimes are not forced into one abstraction.

Array-role contract#

batch and channel are roles assigned by a public API, not inferred from array rank:

Spatial and non-spatial axes#

Module

Spatial axes

Batch/channel behavior

Resize

Explicit axes; all axes when omitted

Unselected axes are preserved exactly.

Affine

Exactly two or three spatial_axes

Remaining slices are mathematically independent and share one batched prefilter/support contraction with memory-bounded query tiles. Tagged persistence is atomic numeric-plus-JSON data, not object serialization.

TensorSpline

Every construction-data axis is a spline dimension

Query coordinates may be batched; sample-value channel axes are not inferred.

Smoothing

Explicit axes; every axis when omitted

Unselected batch/channel slices share one batched real-FFT execution.

Differentials

Two or three explicit spatial_axes through DifferentialPlan

Remaining slices are mathematically independent but execute through one batched coefficient/derivative workspace; the legacy Differentials object remains scalar. Structured outputs may not alias the source or one another.

Multiscale

Two explicit spatial_axes for 2-D pyramids and wavelets

Small planes use cache-sized vectorized groups; cache-filling planes are dispatched directly to avoid full-array transpose copies. Axes are never inferred from rank.

Rules for shared internals#

  • Public classes and functions do not move merely to reduce duplicate code.

  • A shared primitive needs parity tests in every consumer before replacement.

  • Dtype promotion, complex values, backend ownership, boundaries, singleton behavior, and mutation are part of a primitive’s contract.

  • Internal APIs may change between releases. Public modules must not expose private helper objects as accidental compatibility promises.

  • Interpolation prefiltering is shared only by TensorSpline and differentials, which use the same cardinal-spline coefficient contract. Resize’s scale-dependent projection filters remain specialized.

  • Future legacy ports retain source-level attribution and method records even though the maintainer has cleared the current distribution’s provenance; see Method and source provenance.

This conservative structure is intentional. It lets SplineOps earn reuse through evidence while preserving TensorSpline, resize, affine, differentials, smoothing, regression, and multiscale tools as separate public capabilities.