summaryrefslogtreecommitdiff
path: root/neurobench_testing
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
parent30dc9553e8a7a7d603077a80238782298d0b9349 (diff)
Removed Neurobench because of incompatibility problems
Diffstat (limited to 'neurobench_testing')
-rw-r--r--neurobench_testing/README.md1
-rw-r--r--neurobench_testing/custom_memristor_model.py18
-rw-r--r--neurobench_testing/kaevin_memristor.py134
-rw-r--r--neurobench_testing/memTorch_testing.py89
-rw-r--r--neurobench_testing/memTorch_testing_custom_memristor.py160
5 files changed, 189 insertions, 213 deletions
diff --git a/neurobench_testing/README.md b/neurobench_testing/README.md
index 0e3ad72..60d3488 100644
--- a/neurobench_testing/README.md
+++ b/neurobench_testing/README.md
@@ -5,6 +5,7 @@ except for memtorch which needs to be cloned and compiled locally on linux syste
I am not sure for windows or mac.
```
+python3 -m venv .venv
pip install -r requirements.txt
git clone --recursive https://github.com/coreylammie/MemTorch
diff --git a/neurobench_testing/custom_memristor_model.py b/neurobench_testing/custom_memristor_model.py
index 8bdcde0..eb91b99 100644
--- a/neurobench_testing/custom_memristor_model.py
+++ b/neurobench_testing/custom_memristor_model.py
@@ -1,5 +1,7 @@
import torch
+import memtorch
from memtorch.bh.memristor.Memristor import Memristor
+from memtorch.utils import clip, convert_range #idk if ill need this
class MemtorchMemristor(Memristor):
def __init__(
@@ -27,16 +29,8 @@ class MemtorchMemristor(Memristor):
self.i_off = i_off
self.p = p
- # makes sure w starts in valid state
- if not hasattr(self, 'w'):
- self.w = torch.tensor(0.5)
-
-
- """
- Updates w and computes new resistance
-
- """
- def step(self, v, dt):
- i = v / self.r_curr
-
+ #state variables
+ self.w = 0.5
+ self.g = 1/self.r_on
+ def window()
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
+
+
diff --git a/neurobench_testing/memTorch_testing.py b/neurobench_testing/memTorch_testing.py
index 5c3d828..1992e05 100644
--- a/neurobench_testing/memTorch_testing.py
+++ b/neurobench_testing/memTorch_testing.py
@@ -1,15 +1,10 @@
-"""
-This is a program to test out using memTorch with neurobench.
-June 17th, 2026
-Author: Tanner Robison,
-Teuscher Lab
-"""
+import sys
import torch
import torch.nn as nn
import snntorch as snn
from snntorch import surrogate
-from torch.utils.data import DataLoader
+from torch.utils.data import DataLoader, Subset
from neurobench.models import SNNTorchModel
from neurobench.benchmarks import Benchmark, benchmark
@@ -32,31 +27,9 @@ import copy
from memtorch.mn.Module import patch_model
from memtorch.map.Parameter import naive_map
from memtorch.bh.memristor import VTEAM
+from memtorch.map.Input import naive_scale
beta = 0.9
-class SNN(nn.Module):
- def __init__(self):
- super().__init__()
-
- #standard layers
- self.fc1 = nn.Linear(20, 128)
- self.fc2 = nn.Linear(128, 35)
-
- #Spiking neurons
- self.lif1 = snn.Leaky(beta=beta, init_hidden=True)
- self.lif2 = snn.Leaky(beta=beta, init_hidden=True, output=True)
-
- def forward(self, x):
- x = x.view(x.size(0), -1)
-
- cur1 = self.fc1(x)
- spk1 = self.lif1(cur1)
-
- cur2 = self.fc2(spk1)
- spk2, mem2 = self.lif2(cur2)
-
- return spk2, mem2
-
device = torch.device("cpu")
spike_grad = surrogate.fast_sigmoid()
net = nn.Sequential(
@@ -71,28 +44,38 @@ net = nn.Sequential(
snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True, output=True),
)
-#memristor patch
-reference_memristor = VTEAM()
+vteam_params = {
+ 'time_series_resolution': 1e-3,
+ 'r_on': 50,
+ 'r_off': 1000,
+}
net.load_state_dict(torch.load("examples/gsc/model_data/s2s_gsc_snntorch", map_location=device))
patched_net = patch_model(
copy.deepcopy(net),
- memristor_model=reference_memristor,
- memristor_model_params={'time_series_resolution': 1e-8},
+ memristor_model=VTEAM,
+ memristor_model_params=vteam_params,
+ module_parameters_to_patch=[torch.nn.Linear],
mapping_routine=naive_map,
transistor=True,
tile_shape=(128, 128),
- ADC_resolution=8,
- use_bindings=True
+ max_input_voltage=0.3,
+ scaling_routine=naive_scale,
+ ADC_resolution=16,
+ use_bindings=True,
+ verbose=True,
)
static_metrics = [Footprint, ConnectionSparsity]
workload_metrics = [ActivationSparsity, SynapticOperations, ClassificationAccuracy]
-# data loader here maybe??
test_set = SpeechCommands(path="data/SpeechCommands/", subset="testing")
-test_set_loader = DataLoader(test_set, batch_size=500, shuffle=True)
+
+#shorten data set so I can actually run it lol
+tiny_indices = list(range(10))
+tiny_test_set = Subset(test_set, tiny_indices)
+test_set_loader = DataLoader(tiny_test_set, batch_size=16, shuffle=True)
pre_processor = [S2SPreProcessor(device=device)]
post_processor = [ChooseMaxCount()]
@@ -106,13 +89,36 @@ benchmark = Benchmark(
[static_metrics, workload_metrics]
)
+print("\n --Checking signal strength--")
+dummy_input = torch.randn(2, 20).to(device)
+
+try:
+ raw_signal = patched_net(dummy_input)
+
+ if isinstance(raw_signal, tuple) and len(raw_signal) > 1:
+ voltage_signal = raw_signal[0]
+ print("Checking Neuron voltages")
+ else:
+ voltage_signal = raw_signal
+ print("Checking RAW OUTPUTS")
+
+
+ print(f"Signal Max: {raw_signal.max().item():.8f}")
+ print(f"Signal Min: {raw_signal.min().item():.8f}")
+ print(f"Signal Mean: {raw_signal.mean().item():.8f}")
+
+except Exception as e:
+ print("Error getting signal:", e)
+
+sys.exit()
+
results = benchmark.run()
print("\n\n----- IDEAL BENCHMARK -----")
print(f"Footprint: {results['Footprint']}")
print(f"Connection Sparsity: {results['ConnectionSparsity']}")
print(f"Activation Sparsity: {results['ActivationSparsity']}")
print(f"Synaptic Operations: {results['SynapticOperations']}")
-print(f"Classification Accuracy: {results['ClassificationAccuracy']}")
+print(f"Classification Accuracy: {results['ClassificationAccuracy']}\n")
#energy calculations
ENERGY_PER_MAC = 0.9e-12
@@ -125,7 +131,7 @@ total_energy = (macs * ENERGY_PER_MAC) + (acs * ENERGY_PER_AC)
print("Energy Report:")
print(f"Total Operations: {macs} MACS, {acs} acs ")
-print(f"Calculated Energy cost: {total_energy} joules per batch")
+print(f"Calculated Energy cost: {total_energy} joules per batch\n\n")
model = SNNTorchModel(patched_net)
@@ -137,8 +143,9 @@ benchmark = Benchmark(
[static_metrics, workload_metrics]
)
+
with torch.no_grad(): #makes sure its in inference mode
- #otherwise you get memory leaks
+ #otherwise you get memory leaks : (
results = benchmark.run()
print("\n\n----- MEMRISTOR BENCHMARK -----")
diff --git a/neurobench_testing/memTorch_testing_custom_memristor.py b/neurobench_testing/memTorch_testing_custom_memristor.py
deleted file mode 100644
index 64f338e..0000000
--- a/neurobench_testing/memTorch_testing_custom_memristor.py
+++ /dev/null
@@ -1,160 +0,0 @@
-from memtorch import memristor
-import torch
-import torch.nn as nn
-import snntorch as snn
-from snntorch import surrogate
-
-from torch.utils.data import DataLoader
-
-from neurobench.models import SNNTorchModel
-from neurobench.benchmarks import Benchmark, benchmark
-from neurobench.datasets import SpeechCommands
-
-from neurobench.metrics.workload import (
- ActivationSparsity,
- SynapticOperations,
- ClassificationAccuracy,
-)
-
-from neurobench.metrics.static import (
- Footprint,
- ConnectionSparsity,
-)
-
-from neurobench.processors.preprocessors import S2SPreProcessor
-from neurobench.processors.postprocessors import ChooseMaxCount
-
-import copy
-from memtorch.mn.Module import patch_model
-from memtorch.map.Parameter import naive_map
-from memtorch.bh.memristor import VTEAM
-
-from kaevin_memristor import TEAMMemristor
-
-beta = 0.9
-class SNN(nn.Module):
- def __init__(self):
- super().__init__()
-
- #standard layers
- self.fc1 = nn.Linear(20, 128)
- self.fc2 = nn.Linear(128, 35)
-
- #Spiking neurons
- self.lif1 = snn.Leaky(beta=beta, init_hidden=True)
- self.lif2 = snn.Leaky(beta=beta, init_hidden=True, output=True)
-
- def forward(self, x):
- x = x.view(x.size(0), -1)
-
- cur1 = self.fc1(x)
- spk1 = self.lif1(cur1)
-
- cur2 = self.fc2(spk1)
- spk2, mem2 = self.lif2(cur2)
-
- return spk2, mem2
-
-device = torch.device("cpu")
-spike_grad = surrogate.fast_sigmoid()
-net = nn.Sequential(
- nn.Flatten(),
- nn.Linear(20, 256),
- snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True),
- nn.Linear(256, 256),
- snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True),
- nn.Linear(256, 256),
- snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True),
- nn.Linear(256, 35),
- snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True, output=True),
-)
-
-#memristor patch
-reference_memristor = TEAMMemristor
-
-net.load_state_dict(torch.load("examples/gsc/model_data/s2s_gsc_snntorch", map_location=device))
-
-patched_net = patch_model(
- copy.deepcopy(net),
- memristor_model=reference_memristor,
- memristor_model_params={'time_series_resolution': 1e-8},
- mapping_routine=naive_map,
- transistor=True,
- tile_shape=(128, 128),
- ADC_resolution=8,
- use_bindings=True
-)
-
-static_metrics = [Footprint, ConnectionSparsity]
-workload_metrics = [ActivationSparsity, SynapticOperations, ClassificationAccuracy]
-
-# data loader here maybe??
-test_set = SpeechCommands(path="data/SpeechCommands/", subset="testing")
-test_set_loader = DataLoader(test_set, batch_size=500, shuffle=True)
-
-pre_processor = [S2SPreProcessor(device=device)]
-post_processor = [ChooseMaxCount()]
-
-model = SNNTorchModel(net)
-benchmark = Benchmark(
- model,
- test_set_loader,
- pre_processor,
- post_processor,
- [static_metrics, workload_metrics]
-)
-
-results = benchmark.run()
-print("\n\n----- IDEAL BENCHMARK -----")
-print(f"Footprint: {results['Footprint']}")
-print(f"Connection Sparsity: {results['ConnectionSparsity']}")
-print(f"Activation Sparsity: {results['ActivationSparsity']}")
-print(f"Synaptic Operations: {results['SynapticOperations']}")
-print(f"Classification Accuracy: {results['ClassificationAccuracy']}")
-
-#energy calculations
-ENERGY_PER_MAC = 0.9e-12
-ENERGY_PER_AC = 0.1e-12
-
-macs = results['SynapticOperations']['Effective_MACs']
-acs = results['SynapticOperations']['Effective_ACs']
-
-total_energy = (macs * ENERGY_PER_MAC) + (acs * ENERGY_PER_AC)
-
-print("Energy Report:")
-print(f"Total Operations: {macs} MACS, {acs} acs ")
-print(f"Calculated Energy cost: {total_energy} joules per batch")
-
-
-model = SNNTorchModel(patched_net)
-benchmark = Benchmark(
- model,
- test_set_loader,
- pre_processor,
- post_processor,
- [static_metrics, workload_metrics]
-)
-
-with torch.no_grad(): #makes sure its in inference mode
- #otherwise you get memory leaks
- results = benchmark.run()
-
-print("\n\n----- MEMRISTOR BENCHMARK -----")
-print(f"Footprint: {results['Footprint']}")
-print(f"Connection Sparsity: {results['ConnectionSparsity']}")
-print(f"Activation Sparsity: {results['ActivationSparsity']}")
-print(f"Synaptic Operations: {results['SynapticOperations']}")
-print(f"Classification Accuracy: {results['ClassificationAccuracy']}")
-
-#energy calculations
-ENERGY_PER_MAC = 0.9e-12
-ENERGY_PER_AC = 0.1e-12
-
-macs = results['SynapticOperations']['Effective_MACs']
-acs = results['SynapticOperations']['Effective_ACs']
-
-total_energy = (macs * ENERGY_PER_MAC) + (acs * ENERGY_PER_AC)
-
-print("Energy Report:")
-print(f"Total Operations: {macs} MACS, {acs} acs ")
-print(f"Calculated Energy cost: {total_energy} joules per batch")