diff options
| author | Tanner Robison <[email protected]> | 2026-06-22 14:26:09 -0700 |
|---|---|---|
| committer | Tanner Robison <[email protected]> | 2026-06-23 20:31:09 -0700 |
| commit | c53c13b873ea1e69ed260844268f11708d4b4a5b (patch) | |
| tree | 1b9f4d6ae9bccff1c52cf8bfe7c1f953056eaecf /neurobench_testing | |
| parent | 30dc9553e8a7a7d603077a80238782298d0b9349 (diff) | |
Removed Neurobench because of incompatibility problems
Diffstat (limited to 'neurobench_testing')
| -rw-r--r-- | neurobench_testing/README.md | 1 | ||||
| -rw-r--r-- | neurobench_testing/custom_memristor_model.py | 18 | ||||
| -rw-r--r-- | neurobench_testing/kaevin_memristor.py | 134 | ||||
| -rw-r--r-- | neurobench_testing/memTorch_testing.py | 89 | ||||
| -rw-r--r-- | neurobench_testing/memTorch_testing_custom_memristor.py | 160 |
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") |
