summaryrefslogtreecommitdiff
path: root/neurobench_testing/kaevin_memristor.py
diff options
context:
space:
mode:
authorTanner Robison <[email protected]>2026-06-22 14:26:09 -0700
committerTanner Robison <[email protected]>2026-06-23 20:31:09 -0700
commitc53c13b873ea1e69ed260844268f11708d4b4a5b (patch)
tree1b9f4d6ae9bccff1c52cf8bfe7c1f953056eaecf /neurobench_testing/kaevin_memristor.py
parent30dc9553e8a7a7d603077a80238782298d0b9349 (diff)
Removed Neurobench because of incompatibility problems
Diffstat (limited to 'neurobench_testing/kaevin_memristor.py')
-rw-r--r--neurobench_testing/kaevin_memristor.py134
1 files changed, 134 insertions, 0 deletions
diff --git a/neurobench_testing/kaevin_memristor.py b/neurobench_testing/kaevin_memristor.py
new file mode 100644
index 0000000..b4a4364
--- /dev/null
+++ b/neurobench_testing/kaevin_memristor.py
@@ -0,0 +1,134 @@
+import numpy as np
+from scipy.integrate import solve_ivp
+
+# TEAM (Threshold Adaptive Memristor) model as a reusable class
+class TEAMMemristor:
+
+ def __init__(
+ self,
+ k_off=1, # switching rate for off-state
+ k_on=-1, # switching rate for on-state
+ alpha_off=5, # exponent controlling nonlinearity when switching off
+ alpha_on=5, # exponent controlling nonlinearity when switching on
+ i_off=0.5e-3, # threshold current to trigger off state switching
+ i_on=-0.5e-3, # threshold current to trigger on state switching
+ g_on=1/1e3, # maximum conductance (1 kohm = 1 ms)
+ g_off=1/10e3, # minimum conductance (10 kohm = 0.1 ms)
+ w_init=0.5, # initial state variable (0=off, 1=on)
+ p=2 # window function exponent
+ ):
+ self.k_off = k_off
+ self.k_on = k_on
+ self.alpha_off = alpha_off
+ self.alpha_on = alpha_on
+ self.i_off = i_off
+ self.i_on = i_on
+ self.G_on = G_on
+ self.G_off = G_off
+ self.w_init = w_init
+ self.p = p
+
+ def set_state(self, w):
+ # Update initial condition for next simulation
+ self.w_init = np.clip(w, 0, 1)
+
+ def window(self, w, i):
+ # Nonlinear window function: reduces switching rate near the boundaries
+ w = np.clip(w, 0.0, 1.0)
+ if i >= 0:
+ return 1 - w**(2*self.p) # switching off: slower near w=1
+ else:
+ return 1 - (1-w)**(2*self.p) # switching on: slower near w=0
+
+ def conductance(self, w):
+ # Linear interpolation between off and on conductance based on state w
+ w = np.clip(w, 0.0, 1.0)
+ return self.G_off + w*(self.G_on - self.G_off)
+
+ def dw_dt(self, w, i):
+ # TEAM state dynamics: dw/dt depends on current magnitude and direction
+ w = np.clip(w, 0.0, 1.0)
+
+ if i >= self.i_off: # positive current above threshold = switch off
+ dw = (
+ self.k_off
+ * ((i/self.i_off)-1)**self.alpha_off
+ * self.window(w, i)
+ )
+ elif i <= self.i_on: # negative current below threshold = switch on
+ dw = (
+ self.k_on
+ * (((-i)/abs(self.i_on))-1)**self.alpha_on
+ * self.window(w, i)
+ )
+ else: # between thresholds = no switching
+ dw = 0.0
+
+ # Enforce physical bounds: prevent state from leaving [0,1]
+ if w <= 0 and dw < 0:
+ dw = 0
+ if w >= 1 and dw > 0:
+ dw = 0
+
+ return dw
+
+ def simulate(self,
+ freq=1, # excitation frequency (Hz)
+ V_amp=1.5, # sinusoid amplitude (V)
+ cycles=3): # number of periods to simulate
+ # Solve the memristor ODE for given frequency and voltage amplitude
+
+ def voltage(t): # sinusoidal excitation signal
+ return V_amp*np.sin(2*np.pi*freq*t)
+
+ def ode(t, y): # dy/dt: current through memristor
+ w = y[0]
+ v = voltage(t)
+ G = self.conductance(w)
+ i = G*v # Ohm's law: i = G*v
+ return [self.dw_dt(w, i)]
+
+ T = 1/freq # period
+ t_end = cycles*T
+ t_eval = np.linspace(0, t_end, 10000) # dense time grid for smooth curves
+
+ # Solve with RK45, small max step
+ sol = solve_ivp(
+ ode,
+ [0, t_end],
+ [self.w_init],
+ t_eval=t_eval,
+ method='RK45',
+ max_step=T/1000, # max step keeps resolution within one period
+ rtol=1e-8,
+ atol=1e-10
+ )
+
+ raw_w = sol.y[0]
+
+ # Check if numerical solver violated physical bounds
+ eps = 1e-6
+ if np.any(raw_w < -eps) or np.any(raw_w > 1+eps):
+ print(
+ "WARNING: solver left bounds "
+ f"min={raw_w.min():.12f}, "
+ f"max={raw_w.max():.12f}"
+ )
+
+ w = np.clip(raw_w, 0, 1) # enforce bounds just in case
+ t = sol.t
+ v = voltage(t)
+ G = self.conductance(w)
+ i = G*v # current throughout simulation
+
+ return t, w, v, i
+
+ def resistance(self, w):
+ # Compute resistance as reciprocal of conductance
+ return 1/self.conductance(w)
+
+ def reset(self):
+ # Reset state to default initial condition
+ self.w_init = 0.5
+
+