Van der Pol radial-stable recurrent cell / mini_compare.py
Beats tuned baseline
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})