Interpretation¶
kanforge.interpret.analysis
¶
Tabulation, attribution and pruning of KAN models (PyTorch or MLX).
EdgeTable(x, values)
dataclass
¶
Edge functions phi_oi sampled on a per-input grid of points.
input_ranges(layer, x=None, margin=0.0)
¶
Per-input (lo, hi): from samples x, else the basis grid, else [-1, 1].
tabulate(layer, x=None, n=201, margin=0.0)
¶
Sample every edge of layer (any basis, any backend) on n points
per input over the range of x (or of the grid).
layer_activations(model, x)
¶
Inputs of every KAN layer of the model, in order (backend arrays).
tabulate_model(model, x, n=201)
¶
Tabulate every KAN layer over the activations it receives for inputs x.
attribution(model, x, eps=1e-12)
¶
Importance of edges and nodes, propagated from the outputs (pykan-style).
E_oi = std over the data of phi_oi(x_i); an output node passes its score to
its inputs in proportion to E_oi; a product node passes its full score to
each factor. Per layer: edge (out, in), input (in,), output
(out,) scores; output nodes of the model score 1.
feature_importance(model, x)
¶
Importance of every model input (first-layer input scores).
prune_edges(model, x, threshold=0.01)
¶
Mask edges whose attribution score is below threshold; returns how many.
prune_nodes(model, x, threshold=0.01)
¶
A smaller model without hidden nodes scoring below threshold.
Works for KAN and sequences of KANLinear; inputs and outputs stay.
prune(model, x, node_threshold=0.01, edge_threshold=0.01)
¶
prune_nodes then prune_edges (pykan's prune).
kanforge.interpret.fit
¶
Symbolic regression of one edge: fit c * f(a * x + b) + d and rank candidates.
For each library function a coarse grid over (a, b) followed by two zoom rounds is searched; for every (a, b) the outer c, d are the exact least-squares solution (variable projection), so the fit is deterministic and needs no gradient. Ranking follows pykan: score = weight_simple * complexity + (1 - weight_simple) * log2(1 + 1e-5 - R^2), lower is better, plus a penalty for cancelling fits: a function used far from where it bends, scaled up and offset back (38 cos(0.2 x) - 38 ~ x^2) also reaches R^2 ~ 1. Its size |c| * max|f| / (half range of y) is ~1 for a real match and large for a Taylor-like imitation.
Suggestion(name, r2, complexity, params, score)
dataclass
¶
expr(u)
¶
SymPy expression of the edge for input expression u.
fit_affine(x, y, fn, a_max=10.0, b_max=10.0, grid=41)
¶
Best (a, b, c, d) and R^2 of y ~ c * fn(a * x + b) + d.
suggest_symbolic(x, y, functions=None, weight_simple=0.8, topk=5)
¶
Candidate formulas for samples (x, y) of one edge, best first.
kanforge.interpret.symbolic
¶
Symbolic edges and closed-form formulas of whole models (SymPy).
suggest_edge(layer, o, i, x=None, functions=None, weight_simple=0.8, topk=5, n=201)
¶
Candidate formulas for edge (o, i) of layer over the range of x.
fix_symbolic(layer, o, i, name, x=None, n=201)
¶
Fit c * f(a * x + b) + d to edge (o, i), make it symbolic; returns R^2.
auto_symbolic(model, x, functions=None, weight_simple=0.8, r2_threshold=0.9, n=201)
¶
Make every learned edge symbolic with its best candidate (pykan's
auto_symbolic). Ranges come from the activations each layer receives for
x. Edges whose best R^2 is below r2_threshold stay learned, so
symbolic_formula reports them instead of returning a wrong formula.
The zero function is not a candidate by default — with its zero
complexity it would win over every mediocre fit; remove negligible edges
with prune_edges instead (or pass functions including "0").
symbolic_formula(model, names=None, digits=4, simplify=False, tol=0.0)
¶
SymPy expressions of every output.
Every edge must be symbolic or pruned (spline and base weights zero up to
tol count as zero); otherwise ValueError lists the remaining ones.
Also handles MultKAN product nodes and plain Linear layers (models from
from_mlp).
kanforge.interpret.kanpiler
¶
kanpiler: compile SymPy formulas into MultKAN models (KAN 2.0).
The expression tree becomes a graph of nodes:
- a sum (
Add) is an addition node, numbers go to its bias; - a product of several factors is a binary multiplication node (
Mulwith more factors is split into nested binary products); - a function of an affine argument,
c * f(a * u + b) + dwithffrom the symbolic library (powers, sqrt, exp, log, sin, ...), is an edge; - anything else reaching an edge goes through the identity edge.
Nodes are placed on layers by depth; edges that skip layers get identity
nodes in between, and an input feeding the same node twice is routed once
through an identity node. mode="symbolic" makes every edge symbolic
(the model equals the formula); mode="fit" fits the layer's basis to
every edge by least squares over the variable ranges (a learnable
starting point, like TruKAN's compiler).
kanpile(exprs, variables, backend='torch', basis='bspline', mode='symbolic', ranges=None, samples=1000, seed=0)
¶
Compile SymPy expression(s) of variables into a MultKAN.
Returns (model, outputs) where outputs[k] is the output column of
exprs[k] (sum nodes come before product nodes in MultKAN layers).
ranges ({variable: (lo, hi)}, default [-1, 1]) are used by
mode="fit" to sample the edges and to place the per-input grids.
kanforge.interpret.library
¶
Library of 1-D functions for symbolic edges: c * f(a * x + b) + d.
Every function is written with operations NumPy, PyTorch and MLX share, so
the same entry evaluates symbolic edges on any backend; sympy gives its
formula and complexity follows pykan's scale (used to prefer simpler
candidates). Codes stored in layers are positions in NAMES: append new
functions at the end to keep saved models valid.
kanforge.interpret.plot
¶
Plots of edge functions (matplotlib, optional dependency).
plot_layer(layer, x=None, n=201, scores=None, ax_size=1.6)
¶
Grid of every edge function phi_oi (rows: outputs, columns: inputs).
Pruned edges are blank, symbolic ones are titled with their function;
with scores (attribution(...)[k]["edge"]) the line opacity shows
edge importance. Returns the matplotlib figure.
plot_model(model, x, n=201)
¶
plot_layer for every KAN layer, with attribution opacities; list of figures.
kanforge.interpret.contrastive
¶
pykan's unsupervised mode: find dependent features with a contrastive task.
Real samples (label 1) are mixed with samples whose columns are shuffled
independently (label 0), which destroys every dependence between features
but keeps their marginals. A KAN trained to tell them apart can only use
relations between features; pruning and feature_importance then show
which features are related, and auto_symbolic how.
contrastive_samples(x, seed=0)
¶
(inputs, labels) NumPy arrays: x with label 1 and a column-shuffled copy with label 0.