Skip to content

Bases

kanforge.bases.base

The Basis contract shared by every backend.

Every edge function is phi(x) = sum_b c_b * B_b(x; theta). A basis maps an input of shape (..., in) to (..., in, size) and can return any derivative order of the same shape.

theta are optional basis parameters: trainable ones (rational coefficients, wavelet scales, knot positions, ...) and fixed state such as the grid of a spline, which differs per input and is updated from data. A basis declares them with param_specs(): one default value per parameter, shaped for a single input. Layers store them per input, per group of inputs (GR-KAN) or once per layer and pass them in shaped (in, *shape) or (*shape); both broadcast against x[..., None].

local marks bases with compact support (at most support non-zero values per input); fused kernels will use it to pick a sparse gather.

ParamSpec(value, trainable=True) dataclass

Default value of one basis parameter for a single input.

trainable=False marks state such as grids: layers keep it as a buffer, always one copy per input, and grid tools update it from data.

Basis

support property

Max number of non-zero basis values per input.

grid_names()

Names of the grid buffers ("grid", or "{i}_grid" in Concat).

derivative(x, order=1, params=None)

d^order/dx^order of every basis function, shape (..., in, size).

make_basis(spec='bspline', **kwargs)

Build a basis from a name, a config() dict or an existing instance.

kanforge.bases.spline

Piecewise bases: B-splines, NURBS, truncated powers, ReLU-KAN.

Their grid is a per-input buffer grid of increasing points; by default it is uniform over grid_range and grid tools (kanforge.grid) adapt it to the data. Splines extend it by order points on each side with the mean grid step, as pykan does.

BSpline(grid_size=5, order=3, grid_range=(-1.0, 1.0))

Bases: _Gridded

B-splines of order on grid_size intervals (pykan / efficient-kan).

There are grid_size + order functions; they sum to one inside the grid. Outside the extended grid all values are zero and the layer falls back to its base (SiLU) branch.

NURBS(grid_size=5, order=3, grid_range=(-1.0, 1.0))

Bases: Basis

Rational B-splines R_j = w_j N_j / sum_k w_k N_k with trainable weights.

Weights are stored as logs, so they stay positive. With all weights equal this is exactly BSpline. Used by Double-KANs for signals with jumps.

TruncatedPower(degree=3, num_knots=6, grid_range=(-1.0, 1.0), learn_knots=False)

Bases: Basis

TruKAN basis: Legendre P_0..P_degree of the grid-normalised input plus (x - t_j)_+^degree / (hi - t_j)^degree.

The grid holds num_knots + 2 points: the ends of the range and the interior knots. With learn_knots the interior knots are trainable per input; they stay ordered because they are cumulative sums of softplus increments (as in TruKAN).

ReLUKAN(grid_size=5, order=3, grid_range=(-1.0, 1.0))

Bases: _Gridded

ReLU-KAN: R_i(x) = [relu(e_i - x) * relu(x - s_i)]^2 * 16 / (e_i - s_i)^4.

Same layout as BSpline: bump i spans knots t_i .. t_{i+order+1} of the extended grid and peaks at 1 on a uniform grid. Only additions, products and ReLU, so it is cheap on any accelerator.

extend_grid(grid, k)

Add k points on each side of grid ((..., G+1)) with the mean step.

kanforge.bases.polynomial

Polynomial bases: Chebyshev, Legendre, Jacobi, Gegenbauer, Hermite, Laguerre, Bernstein, Gram, Taylor and fractional Jacobi (fKAN).

Every family is stored as a matrix A of Chebyshev coefficients, so values are always computed with the stable Chebyshev recurrence: B(z) = [T_0(z) .. T_D(z)] @ A. Derivatives in z are chebder(A); the input map z = g(x) (tanh, identity, fractional power) is applied with Faà di Bruno. For fused kernels A can be folded into the layer coefficients.

PolynomialBasis(degree=5, input_map=None)

Bases: Basis

Base class: subclasses define _family(z) on NumPy float64 points.

Chebyshev(degree=5, input_map=None)

Bases: PolynomialBasis

Chebyshev polynomials of the first kind T_n(tanh x), as in ChebyKAN.

Chebyshev2(degree=5, input_map=None)

Bases: PolynomialBasis

Chebyshev polynomials of the second kind U_n.

Jacobi(degree=5, alpha=1.0, beta=1.0, input_map=None)

Bases: PolynomialBasis

Jacobi polynomials P_n^(alpha, beta), as in JacobiKAN.

FractionalJacobi(degree=5, alpha=1.0, beta=1.0)

Bases: Jacobi

fKAN: Jacobi polynomials of 2 * sigmoid(x)^gamma - 1 with trainable gamma in (0, 1).

Gegenbauer(degree=5, lam=1.0, input_map=None)

Bases: PolynomialBasis

Gegenbauer (ultraspherical) polynomials C_n^(lam).

Hermite(degree=5, input_map=None)

Bases: PolynomialBasis

Physicists' Hermite polynomials H_n.

Bernstein(degree=5, input_map=None)

Bases: PolynomialBasis

Bernstein polynomials C(n, j) t^j (1 - t)^(n - j) of t = (z + 1) / 2.

Gram(degree=5, points=None, input_map=None)

Bases: PolynomialBasis

Gram polynomials: orthonormal on points equally spaced points of [-1, 1].

Normalised so that the mean of each squared polynomial over the points is 1.

Taylor(degree=5, input_map=None)

Bases: PolynomialBasis

Monomials 1, x, ..., x^degree (TaylorKAN); identity input map by default.

kanforge.bases.radial

Bump-shaped bases: Gaussian RBF (FastKAN), RSWAF (FasterKAN) and wavelets (Wav-KAN: Mexican hat, Morlet, derivative of Gaussian).

All Gaussian-envelope functions have the form psi(u) = (p(u) cos(w u) + q(u) sin(w u)) * exp(-u^2 / 2), which is closed under differentiation, so every derivative order is exact.

RBF(count=8, grid_range=(-1.0, 1.0), learnable=False)

Bases: _Envelope

Gaussian RBF exp(-((x - c) / h)^2) on a uniform grid of centres (FastKAN).

MexicanHat(count=8, grid_range=(-1.0, 1.0), learnable=True)

Bases: _Envelope

Mexican hat wavelet 2 / (sqrt(3) pi^(1/4)) (1 - u^2) exp(-u^2 / 2).

Morlet(count=8, grid_range=(-1.0, 1.0), learnable=True, omega=5.0)

Bases: _Envelope

Real Morlet wavelet cos(omega u) exp(-u^2 / 2).

DoG(count=8, grid_range=(-1.0, 1.0), learnable=True)

Bases: _Envelope

Derivative-of-Gaussian wavelet -u exp(-u^2 / 2).

RSWAF(count=8, grid_range=(-1.0, 1.0))

Bases: Basis

Reflectional switch activation 1 - tanh((x - c) / h)^2 (FasterKAN).

kanforge.bases.fourier

Periodic bases: Fourier series (FourierKAN) and random Fourier features (KAF).

Fourier(frequencies=5, period=2 * math.pi, learnable_frequency=False)

Bases: Basis

cos(w_m x) and sin(w_m x) for w_m = 2pim/period, m = 1..frequencies.

The constant term is left to the layer bias, as in FourierKAN. With learnable_frequency every input gets a trainable frequency scale.

RandomFourier(features=16, sigma=1.0, seed=0)

Bases: Basis

Random Fourier features cos(w_b x + phi_b), w ~ N(0, sigma^2), phi ~ U(0, 2 pi) (KAF).

Frequencies and phases are fixed and reproducible from seed.

kanforge.bases.rational

Rational activation P(x) / (1 + |Q(x)|), the GR-KAN basis of KAT.

A single trainable function per input (size == 1); in a layer with groups the coefficients are shared inside each group of inputs, and the layer coefficients play the role of KAT's linear weight.

fit_rational(target, num_degree, den_degree, lim=3.0) cached

Coefficients (a, b) of P/(1+|Q|) that best fit target on [-lim, lim].

Linear least squares for P - f*Q = f gives the start, Levenberg–Marquardt then minimises the true residual. Exact for identity.

kanforge.bases.neural

Tiny-network edges (in the spirit of FCN-KAN): B_b(x) = act(a_b * (x - c_b)).

With the layer coefficients on top this is a one-hidden-layer MLP on every edge whose first layer is shared by the edges leaving the same input.

kanforge.bases.combine

Concatenation of bases, e.g. BSRBF-KAN = B-splines + Gaussian RBF.

Concat(bases)

Bases: Basis

All functions of several bases side by side.

Parameters of part i are exposed as "{i}_{name}".

BSRBF(grid_size=5, order=3, grid_range=(-1.0, 1.0))

Bases: Concat

BSRBF-KAN: B-splines and Gaussian RBF on the same grid.