Van der Pol radial-stable recurrent cell / mini_compare.py

✓✓ Beats tuned baseline

Raw ⬇ ZIP
 1import json, numpy as np
 2from vdp_cell import rk4_step
 3
 4rng=np.random.default_rng(11)
 5N=700; d=16; delay=80; wash=20
 6# Binary delayed-memory task: predict input from the state at the current time.
 7u=rng.uniform(-1,1,size=N)
 8target=u.copy()
 9B=rng.normal(0,.7,size=(d,))
10W=rng.normal(0,1/np.sqrt(d),size=(d,d)); W/=max(1.0,np.linalg.eigvals(W).real.max()/0.9)
11
12def ridge(X,y,l=1e-3):
13    A=X.T@X+l*np.eye(X.shape[1]); return np.linalg.solve(A,X.T@y)
14def score(X,y):
15    cut=int(.65*len(y)); w=ridge(X[:cut],y[:cut]); pred=X[cut:]@w
16    return float(np.mean((pred-y[cut:])**2)),float(np.corrcoef(pred,y[cut:])[0,1])
17# Same scalar drive mapped into every cell; compare tanh Elman and VdP oscillator.
18for kind in ['tanh','vdp']:
19    h=np.zeros(d); feats=[]
20    for t in range(N):
21        if kind=='tanh': h=np.tanh(W@h+B*u[t])
22        else:
23            drive=np.zeros(d); drive[:d//2]=B[:d//2]*u[t]; drive[d//2:]=B[d//2:]*u[t]
24            h=rk4_step(h,.05,np.full(d//2,2.),1.,1.,drive)
25        # state must carry a delay; train target at t-delay from current state
26        if t>=delay: feats.append((h.copy(),target[t-delay]))
27    X=np.array([x for x,y in feats]); y=np.array([y for x,y in feats])
28    mse,corr=score(np.c_[X,np.ones(len(X))],y)
29    print(kind, {'test_mse':mse,'test_corr':corr})