Skip to content

Benchmarks

KAN vs MLP at equal FLOPs

KAN vs MLP at equal FLOPs

benchmarks/fair_comparison.py, following KANbeFair: same optimiser, steps and data, FLOPs counted by kanforge.flops; CPU, float32, seed 0, 18.10.2026.

Summary (best of each family within 10⁵ FLOPs per sample):

task best KAN best MLP
exp(sin(πx₁) + x₂²) RMSE 1.5e-3 (B-spline) 9.0e-2 KAN ~60x more accurate
x₁ · x₂ 4.3e-4 (B-spline) 5.0e-3 ~12x
sin(x₁)e^(−x₂²) + x₃/(1 + x₄²) 9.9e-4 (B-spline) 5.4e-3 ~5x
Friedman-1 (smooth tabular) 3.8e-3 (B-spline) 2.1e-2 ~5x
digits (classification) accuracy 0.974 0.978 MLP slightly better, clearly better at small budgets

KANs win on smooth, low-dimensional functions — symbolic formulas and physics — and are not better on high-dimensional classification, the same picture as KANbeFair. One optimiser setting (Adam 3e-3, 3000 steps) for all models: a tuned MLP narrows the regression gaps; the comparison is about equal effort, not best possible results.

Full tables:

Adam lr 3e-3, 3000 steps, seed 0; metric: test RMSE (lower is better) or accuracy (higher).

exp(sin(pi x1) + x2^2)

model width params FLOPs RMSE
MLP 4 17 57 0.7365
MLP 8 33 113 0.8418
MLP 16 65 225 0.1653
MLP 32 129 449 0.1221
MLP 64 257 897 0.09461
MLP 128 513 1793 0.09223
MLP 256 1025 3585 0.09047
KAN B-spline G5 k3 1 29 824 0.04104
KAN B-spline G5 k3 2 57 1135 0.01068
KAN B-spline G5 k3 4 113 1757 0.003746
KAN B-spline G5 k3 8 225 3001 0.00178
KAN B-spline G5 k3 16 449 5489 0.001933
KAN B-spline G5 k3 32 897 10465 0.001498
KAN Chebyshev d4 1 20 101 0.06972
KAN Chebyshev d4 2 39 159 0.1051
KAN Chebyshev d4 4 77 275 0.02533
KAN Chebyshev d4 8 153 507 0.01795
KAN Chebyshev d4 16 305 971 0.01237
KAN Chebyshev d4 32 609 1899 0.008207

Best within a FLOP budget:

FLOPs <= KAN B-spline G5 k3 KAN Chebyshev d4 MLP
1e+02 — — 0.7365
1e+03 0.04104 0.01237 0.09461
1e+04 0.00178 0.008207 0.09047
1e+05 0.001498 0.008207 0.09047

x1 * x2

model width params FLOPs RMSE
MLP 4 17 57 0.02179
MLP 8 33 113 0.01013
MLP 16 65 225 0.007712
MLP 32 129 449 0.00605
MLP 64 257 897 0.006227
MLP 128 513 1793 0.004981
MLP 256 1025 3585 0.01809
KAN B-spline G5 k3 1 29 824 0.1234
KAN B-spline G5 k3 2 57 1135 0.00069
KAN B-spline G5 k3 4 113 1757 0.001973
KAN B-spline G5 k3 8 225 3001 0.0005986
KAN B-spline G5 k3 16 449 5489 0.0004267
KAN B-spline G5 k3 32 897 10465 0.0008165
KAN Chebyshev d4 1 20 101 0.1516
KAN Chebyshev d4 2 39 159 0.008885
KAN Chebyshev d4 4 77 275 0.004261
KAN Chebyshev d4 8 153 507 0.002174
KAN Chebyshev d4 16 305 971 0.001584
KAN Chebyshev d4 32 609 1899 0.001747

Best within a FLOP budget:

FLOPs <= KAN B-spline G5 k3 KAN Chebyshev d4 MLP
1e+02 — — 0.02179
1e+03 0.1234 0.001584 0.00605
1e+04 0.0004267 0.001584 0.004981
1e+05 0.0004267 0.001584 0.004981

sin(x1) exp(-x2^2) + x3 / (1 + x4^2)

model width params FLOPs RMSE
MLP 4 25 73 0.1081
MLP 8 49 145 0.06752
MLP 16 97 289 0.01212
MLP 32 193 577 0.007397
MLP 64 385 1153 0.006718
MLP 128 769 2305 0.00694
MLP 256 1537 4609 0.005414
KAN B-spline G5 k3 1 47 1372 0.1419
KAN B-spline G5 k3 2 93 1719 0.0789
KAN B-spline G5 k3 4 185 2413 0.001661
KAN B-spline G5 k3 8 369 3801 0.0009948
KAN B-spline G5 k3 16 737 6577 0.001119
KAN B-spline G5 k3 32 1473 12129 0.001408
KAN Chebyshev d4 1 32 167 0.1416
KAN Chebyshev d4 2 63 249 0.09131
KAN Chebyshev d4 4 125 413 0.007006
KAN Chebyshev d4 8 249 741 0.007707
KAN Chebyshev d4 16 497 1397 0.006881
KAN Chebyshev d4 32 993 2709 0.00445

Best within a FLOP budget:

FLOPs <= KAN B-spline G5 k3 KAN Chebyshev d4 MLP
1e+02 — — 0.1081
1e+03 — 0.007006 0.007397
1e+04 0.0009948 0.00445 0.005414
1e+05 0.0009948 0.00445 0.005414

Friedman-1 (10 inputs)

model width params FLOPs RMSE
MLP 4 49 121 0.1092
MLP 8 97 241 0.07352
MLP 16 193 481 0.02965
MLP 32 385 961 0.03041
MLP 64 769 1921 0.02607
MLP 128 1537 3841 0.02244
MLP 256 3073 7681 0.0209
KAN B-spline G5 k3 1 101 3016 0.2539
KAN B-spline G5 k3 2 201 3471 0.1581
KAN B-spline G5 k3 4 401 4381 0.01306
KAN B-spline G5 k3 8 801 6201 0.003787
KAN B-spline G5 k3 16 1601 9841 0.006346
KAN B-spline G5 k3 32 3201 17121 0.007644
KAN Chebyshev d4 1 68 365 0.2523
KAN Chebyshev d4 2 135 519 0.2114
KAN Chebyshev d4 4 269 827 0.02568
KAN Chebyshev d4 8 537 1443 0.02147
KAN Chebyshev d4 16 1073 2675 0.01336
KAN Chebyshev d4 32 2145 5139 0.02354

Best within a FLOP budget:

FLOPs <= KAN B-spline G5 k3 KAN Chebyshev d4 MLP
1e+03 — 0.02568 0.02965
1e+04 0.003787 0.01336 0.0209
1e+05 0.003787 0.01336 0.0209

digits (64 inputs, 10 classes)

model width params FLOPs accuracy
MLP 4 310 634 0.908
MLP 8 610 1258 0.962
MLP 16 1210 2506 0.96
MLP 32 2410 5002 0.974
MLP 64 4810 9994 0.974
MLP 128 9610 19978 0.974
MLP 256 19210 39946 0.978
KAN B-spline G5 k3 1 677 17983 0.432
KAN B-spline G5 k3 2 1344 19572 0.758
KAN B-spline G5 k3 4 2678 22750 0.898
KAN B-spline G5 k3 8 5346 29106 0.968
KAN B-spline G5 k3 16 10682 41818 0.972
KAN B-spline G5 k3 32 21354 67242 0.974
KAN Chebyshev d4 1 455 2264 0.504
KAN Chebyshev d4 2 900 3174 0.744
KAN Chebyshev d4 4 1790 4994 0.852
KAN Chebyshev d4 8 3570 8634 0.962
KAN Chebyshev d4 16 7130 15914 0.962
KAN Chebyshev d4 32 14250 30474 0.972

Best within a FLOP budget:

FLOPs <= KAN B-spline G5 k3 KAN Chebyshev d4 MLP
1e+03 — — 0.908
1e+04 — 0.962 0.974
1e+05 0.974 0.972 0.978

Speed of the fused kernels

Benchmarks

benchmarks/bench_kernels.py — one KANLinear layer, median of 20 runs after warm-up, float32. "fwd+bwd" is forward plus backward into all parameters (and into the input for PyTorch). Plain ops = basis tensor + matrix product; kernel = fused kernel of kanforge.kernels.

Machine: Apple M5, 16 GB, PyTorch 2.14 (MPS), MLX 0.32 — 18.10.2026. Timings on this machine vary by ~±15 % between runs.

PyTorch MPS

basis batch x in x out fwd ops fwd kernel fwd+bwd ops fwd+bwd kernel
bspline G8 k3 1024x64x64 3.57 ms 0.90 ms 6.69 ms 2.04 ms
bspline G8 k3 8192x128x128 109.44 ms 19.43 ms 204.61 ms 69.31 ms
bspline G8 k3 32768x64x64 185.66 ms 18.71 ms 361.65 ms 44.76 ms
chebyshev d8 1024x64x64 0.73 ms 0.88 ms 1.36 ms 1.77 ms
chebyshev d8 8192x128x128 11.89 ms 13.65 ms 26.50 ms 29.96 ms
chebyshev d8 32768x64x64 26.20 ms 14.00 ms 56.71 ms 25.93 ms
rbf 8 1024x64x64 1.18 ms 0.48 ms 1.99 ms 0.92 ms
rbf 8 8192x128x128 17.87 ms 6.12 ms 38.37 ms 17.71 ms
rbf 8 32768x64x64 32.54 ms 7.98 ms 71.94 ms 17.59 ms
fourier 4 1024x64x64 0.85 ms 0.78 ms 1.32 ms 1.52 ms
fourier 4 8192x128x128 10.23 ms 6.58 ms 22.69 ms 19.09 ms
fourier 4 32768x64x64 20.34 ms 7.96 ms 41.50 ms 18.09 ms

MLX (Metal)

basis batch x in x out fwd ops fwd kernel fwd+bwd ops fwd+bwd kernel
bspline G8 k3 1024x64x64 1.80 ms 0.62 ms 1.25 ms 1.16 ms
bspline G8 k3 8192x128x128 30.31 ms 17.49 ms 35.14 ms 29.86 ms
bspline G8 k3 32768x64x64 64.72 ms 18.08 ms 69.33 ms 28.09 ms
chebyshev d8 1024x64x64 0.57 ms 0.78 ms 0.64 ms 1.04 ms
chebyshev d8 8192x128x128 11.12 ms 12.66 ms 11.77 ms 20.27 ms
chebyshev d8 32768x64x64 23.74 ms 13.67 ms 24.83 ms 17.15 ms
rbf 8 1024x64x64 0.56 ms 0.45 ms 0.65 ms 0.51 ms
rbf 8 8192x128x128 6.48 ms 7.87 ms 8.81 ms 12.96 ms
rbf 8 32768x64x64 15.62 ms 7.25 ms 19.13 ms 10.91 ms
fourier 4 1024x64x64 0.51 ms 0.60 ms 0.63 ms 0.91 ms
fourier 4 8192x128x128 4.67 ms 5.62 ms 7.83 ms 12.53 ms
fourier 4 32768x64x64 9.61 ms 9.08 ms 11.46 ms 12.59 ms

PyTorch CUDA (Triton)

RTX 3060 12 GB, driver 535, PyTorch 2.6.0+cu124, Triton 3.2.0, idle GPU — 18.10.2026. tl.dot runs with IEEE float32 (no TF32), like the plain-ops reference.

basis batch x in x out fwd ops fwd kernel fwd+bwd ops fwd+bwd kernel
bspline G8 k3 1024x64x64 0.74 ms 0.50 ms 1.72 ms 0.94 ms
bspline G8 k3 8192x128x128 8.46 ms 8.22 ms 21.59 ms 17.39 ms
bspline G8 k3 32768x64x64 16.46 ms 8.24 ms 41.50 ms 18.87 ms
chebyshev d8 1024x64x64 0.26 ms 0.33 ms 0.91 ms 0.86 ms
chebyshev d8 8192x128x128 2.63 ms 4.33 ms 6.46 ms 8.78 ms
chebyshev d8 32768x64x64 5.02 ms 4.44 ms 11.89 ms 9.62 ms
rbf 8 1024x64x64 0.24 ms 0.27 ms 0.72 ms 0.63 ms
rbf 8 8192x128x128 2.64 ms 3.91 ms 7.07 ms 8.38 ms
rbf 8 32768x64x64 4.96 ms 4.01 ms 12.89 ms 9.23 ms
fourier 4 1024x64x64 0.17 ms 0.40 ms 0.59 ms 0.98 ms
fourier 4 8192x128x128 1.41 ms 4.40 ms 3.76 ms 10.64 ms
fourier 4 32768x64x64 2.53 ms 4.46 ms 6.30 ms 11.57 ms

JAX (Pallas)

RTX 3060, JAX 0.6.2 (CUDA 12), idle GPU, 18.10.2026 — benchmarks/bench_jax.py, both paths jit-compiled with IEEE float32 products. fwd+bwd = gradient with respect to coefficients and input.

basis batch x in x out fwd ops fwd kernel fwd+bwd ops fwd+bwd kernel
bspline G8 k3 1024x64x64 0.15 ms 0.43 ms 0.39 ms 1.00 ms
bspline G8 k3 8192x128x128 1.20 ms 8.02 ms 4.78 ms 20.68 ms
bspline G8 k3 32768x64x64 1.90 ms 7.52 ms 8.30 ms 21.74 ms
chebyshev d8 1024x64x64 0.10 ms 0.15 ms 0.17 ms 0.45 ms
chebyshev d8 8192x128x128 0.60 ms 1.88 ms 1.72 ms 10.82 ms
chebyshev d8 32768x64x64 0.95 ms 1.85 ms 2.16 ms 10.99 ms
rbf 8 1024x64x64 0.09 ms 0.17 ms 0.23 ms 0.49 ms
rbf 8 8192x128x128 0.51 ms 2.53 ms 1.49 ms 12.24 ms
rbf 8 32768x64x64 0.75 ms 2.44 ms 1.74 ms 12.15 ms
fourier 4 1024x64x64 0.09 ms 0.39 ms 0.17 ms 0.89 ms
fourier 4 8192x128x128 0.51 ms 7.65 ms 1.73 ms 19.30 ms
fourier 4 32768x64x64 0.72 ms 7.21 ms 2.40 ms 20.20 ms

XLA fuses the plain path very well (B-splines: 1.9 ms here vs 16 ms for plain PyTorch on the same GPU); the first Pallas kernels (a loop over inputs with scalar knot loads) are 2–10x slower, so auto never uses them in JAX.

What backend="auto" does with this

kanforge.kernels.plan.AUTO_KINDS: the kernel only where it won both columns.

  • PyTorch MPS: B-splines (3–9x on fwd+bwd) and bumps — RBF, Mexican hat, DoG, RSWAF (2–4x).
  • MLX: B-splines (1.1–2.5x). RBF is mixed, Chebyshev and Fourier are slower: MLX's own fused graph is already fast for them.
  • Chebyshev-family polynomials and Fourier: plain ops by default; on MPS the kernel is about even for small batches and 2x faster for 32k rows, choose it with backend="kernel".
  • JAX: never (plain XLA is faster, see above); backend="kernel" still runs Pallas.
  • CUDA (Triton): B-splines (1.2–2.2x on fwd+bwd). Chebyshev and RBF win only for large batches, Fourier is 2–3x slower than cuBLAS + fused cos/sin. Correctness: the full suite passes on the RTX 3060 (and in CI under the Triton interpreter). ROCm is untested.