Autocatalytic Hysteresis Memory Cell / experiment.py
Mechanism failed
1import json, math
2import numpy as np
3
4SEED=1349
5alpha,beta=4.0,0.2
6
7def drift(x,g): return alpha*x*x+g-x**3-beta*x
8def ddrift(x): return 2*alpha*x-3*x*x-beta
9
10def extrema(): return np.sort(np.roots([3.,-2*alpha,beta]).real)
11
12def fold_bounds():
13 e=extrema(); q=lambda x: alpha*x*x-x**3-beta*x
14 # gamma values at which a pair of fixed points coalesces
15 return float(-q(e[1])),float(-q(e[0])),e
16
17def roots(g):
18 z=np.roots([1.,-alpha,beta,-g])
19 return np.sort(z[np.abs(z.imag)<1e-7].real)
20
21def integrate(gfun,omega,dt=0.004,cycles=6,noise=0.,x0=None):
22 T=2*np.pi/omega; n=min(max(int(cycles*T/dt),4000),300000)
23 dt=cycles*T/n; x=3.9 if x0 is None else x0
24 rng=np.random.default_rng(SEED+int(1000*omega))
25 xs=[]; gs=[]
26 for k in range(n):
27 t=k*dt
28 x=max(0.,x+dt*drift(x,gfun(t))+math.sqrt(2*noise*dt)*rng.normal())
29 if k>=n//2: xs.append(x); gs.append(gfun(t+dt))
30 xs,gs=np.asarray(xs),np.asarray(gs)
31 return float(abs(np.sum(.5*(xs[1:]+xs[:-1])*(gs[1:]-gs[:-1])))),float(dt)
32
33def frequency_sweep(g0,amp):
34 # Estimate tau on the high stable branch at the center forcing.
35 rr=roots(g0); stable=rr[ddrift(rr)<0]
36 tau=float(-1/ddrift(stable[-1]))
37 omegas=np.logspace(math.log10(.03/tau),math.log10(30/tau),13)
38 rows=[]
39 for w in omegas:
40 a,dt=integrate(lambda t:g0+amp*math.sin(w*t),w)
41 rows.append({'omega':float(w),'omega_tau':float(w*tau),'area':a})
42 return tau,rows
43
44def classification():
45 rng=np.random.default_rng(SEED); N,L=320,40
46 X=np.zeros((N,L)); y=np.arange(N)%2
47 for i in range(N): X[i]=(-5.0 if y[i]==0 else -3.9)+.08*rng.normal(size=L)
48 tr=np.arange(240); te=np.arange(240,320)
49 def run(kind):
50 st=[]
51 for seq in X:
52 if kind=='cell':
53 x=3.9
54 for u in seq:
55 for _ in range(4): x=max(0.,x+.05*drift(x,u))
56 else:
57 x=0.
58 for u in seq: x=.9*x+.1*u
59 st.append(x)
60 st=np.asarray(st); th=(st[tr[y[tr]==0]].mean()+st[tr[y[tr]==1]].mean())/2
61 return float(np.mean((st[te]>th)==y[te])),float(st[y==0].mean()),float(st[y==1].mean())
62 return run('linear'),run('cell')
63
64def main():
65 lo,hi,e=fold_bounds(); gmid=(lo+hi)/2
66 rr=roots(gmid)
67 math_check={'extrema':e.tolist(),'predicted_fold_interval':[lo,hi],
68 'midpoint_gamma':gmid,'all_roots':rr.tolist(),
69 'derivatives':[float(ddrift(x)) for x in rr],
70 'stable_root_count':int(np.sum(ddrift(rr)<0))}
71 # Sweep spans both folds, so deterministic state switching is possible.
72 tau,freq=frequency_sweep(gmid,(hi-lo)*.56)
73 wstar=1/tau
74 noises=[0.,1e-4,5e-4,1e-3,5e-3,2e-2]
75 noise=[]
76 for d in noises:
77 a,_=integrate(lambda t:gmid+(hi-lo)*.56*math.sin(wstar*t),wstar,noise=d)
78 noise.append([d,a])
79 result={'parameters':{'alpha':alpha,'beta':beta,'seed':SEED},'math_check':math_check,
80 'tau_high':tau,'frequency_sweep':freq,'noise_sweep':noise,
81 'classification':{'linear':classification()[0],'cell':classification()[1]}}
82 with open('results.json','w') as f: json.dump(result,f,indent=2)
83 print(json.dumps(result,indent=2))
84if __name__=='__main__': main()