Skip to content

Neural ODEs

Learn the dynamics of a damped oscillator with dy/dt = KAN(y) and a differentiable RK4.

python examples/09_neural_ode.py

Set KANFORGE_QUICK=1 for a few-second run (what the test suite does).

"""Learn the dynamics of a damped oscillator with a KAN-ODE (dy/dt = KAN(y))."""

import os

import torch

from kanforge.torch import KANODE

QUICK = os.environ.get("KANFORGE_QUICK") == "1"
torch.manual_seed(0)


def true_rhs(y):
    return torch.stack([y[:, 1], -y[:, 0] - 0.2 * y[:, 1]], 1)


t = torch.linspace(0, 4, 41)
y0 = torch.randn(32, 2)
with torch.no_grad():  # reference trajectories (RK4 on the true system)
    ref = [y0]
    for k in range(len(t) - 1):
        h, y = t[k + 1] - t[k], ref[-1]
        k1 = true_rhs(y)
        k2 = true_rhs(y + h / 2 * k1)
        k3 = true_rhs(y + h / 2 * k2)
        ref.append(y + h / 6 * (k1 + 2 * k2 + 2 * k3 + true_rhs(y + h * k3)))
    ref = torch.stack(ref)

ode = KANODE(2, hidden=(8,), basis="chebyshev")
opt = torch.optim.Adam(ode.parameters(), lr=5e-3)
for _ in range(10 if QUICK else 400):
    opt.zero_grad()
    loss = ((ode(y0, t) - ref) ** 2).mean()
    loss.backward()
    opt.step()
print(f"trajectory RMSE: {loss.sqrt().item():.3e}")