From d70b6e75bcb862782c1a597b6d2a772a7e43a144 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 31 Aug 2023 20:35:45 -0400 Subject: [PATCH 01/12] plateau- fixed point implementation of Plateau neuron model Signed-off-by: kevin --- src/lava/proc/plateau/models.py | 130 +++++++++++++++++ src/lava/proc/plateau/process.py | 70 ++++++++++ tests/lava/proc/plateau/test_models.py | 178 ++++++++++++++++++++++++ tests/lava/proc/plateau/test_process.py | 30 ++++ 4 files changed, 408 insertions(+) create mode 100644 src/lava/proc/plateau/models.py create mode 100644 src/lava/proc/plateau/process.py create mode 100644 tests/lava/proc/plateau/test_models.py create mode 100644 tests/lava/proc/plateau/test_process.py diff --git a/src/lava/proc/plateau/models.py b/src/lava/proc/plateau/models.py new file mode 100644 index 000000000..1195d4305 --- /dev/null +++ b/src/lava/proc/plateau/models.py @@ -0,0 +1,130 @@ +# Copyright (C) 2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# See: https://spdx.org/licenses/ + + +import numpy as np +from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol +from lava.magma.core.model.py.ports import PyInPort, PyOutPort +from lava.magma.core.model.py.type import LavaPyType +from lava.magma.core.resources import CPU +from lava.magma.core.decorator import implements, requires, tag +from lava.magma.core.model.py.model import PyLoihiProcessModel +from lava.proc.plateau.process import Plateau + + +@implements(proc=Plateau, protocol=LoihiProtocol) +@requires(CPU) +@tag("fixed_pt") +class PyPlateauModelFixed(PyLoihiProcessModel): + """ Implementation of Plateau neuron process in fixed point precision. + + Precisions of state variables + + - du_dend : unsigned 12-bit integer (0 to 4095) + - du_soma : unsigned 12-bit integer (0 to 4095) + - vth_dend : unsigned 17-bit integer (0 to 131071) + - vth_soma : unsigned 17-bit integer (0 to 131071) + - up_dur : unsigned 8-bit integer (0 to 255) + """ + + a_dend_in: PyInPort = LavaPyType(PyInPort.VEC_DENSE, np.int16, precision=16) + a_soma_in: PyInPort = LavaPyType(PyInPort.VEC_DENSE, np.int16, precision=16) + s_out: PyOutPort = LavaPyType(PyOutPort.VEC_DENSE, np.int32, precision=24) + v_dend: np.ndarray = LavaPyType(np.ndarray, np.int32, precision=24) + v_soma: np.ndarray = LavaPyType(np.ndarray, np.int32, precision=24) + dv_dend: int = LavaPyType(int, np.uint16, precision=12) + dv_soma: int = LavaPyType(int, np.uint16, precision=12) + vth_dend: int = LavaPyType(int, np.int32, precision=17) + vth_soma: int = LavaPyType(int, np.int32, precision=17) + up_dur: int = LavaPyType(int, np.uint16, precision=8) + up_state: int = LavaPyType(np.ndarray, np.uint16, precision=8) + + def __init__(self, proc_params): + super(PyPlateauModelFixed, self).__init__(proc_params) + self.uv_bitwidth = 24 + self.max_uv_val = 2 ** (self.uv_bitwidth - 1) + self.decay_shift = 12 + self.decay_unity = 2 ** self.decay_shift + self.vth_shift = 6 + self.act_shift = 6 + self.isthrscaled = False + + def scale_threshold(self): + self.effective_vth_dend = np.left_shift(self.vth_dend, self.vth_shift) + self.effective_vth_soma = np.left_shift(self.vth_soma, self.vth_shift) + self.isthrscaled = True + + def subthr_dynamics( + self, + activation_dend_in: np.ndarray, + activation_soma_in: np.ndarray + ): + """Run the sub-threshold dynamics for both the dendrite and soma of the + neuron. Both use 'leaky integration'. + """ + for v, dv, a_in in [ + (self.v_dend, self.dv_dend, activation_dend_in), + (self.v_soma, self.dv_soma, activation_soma_in), + ]: + decayed_volt = np.int64(v) * (self.decay_unity - dv) + decayed_volt = np.sign(decayed_volt) * np.right_shift( + np.abs(decayed_volt), 12 + ) + decayed_volt = np.int32(decayed_volt) + updated_volt = decayed_volt + np.left_shift(a_in, self.act_shift) + + neg_voltage_limit = -np.int32(self.max_uv_val) + 1 + pos_voltage_limit = np.int32(self.max_uv_val) - 1 + + v[:] = np.clip( + updated_volt, neg_voltage_limit, pos_voltage_limit + ) + + def update_up_state(self): + """Decrements the up state (if necessary) and checks v_dend to see if + up state needs to be (re)set. If up state is (re)set, then v_dend is + reset to 0. + """ + self.up_state[self.up_state > 0] -= 1 + self.up_state[self.v_dend > self.effective_vth_dend] = self.up_dur + self.v_dend[self.v_dend > self.effective_vth_dend] = 0 + + def soma_spike_and_reset(self): + """Check the spiking conditions for the plateau soma. Checks if: + v_soma > v_th_soma + up_state > 0 + + For any neurons n that satisfy both conditions, sets: + s_out_buff[n] = True + v_soma = 0 + """ + s_out_buff = np.logical_and( + self.v_soma > self.effective_vth_soma, + self.up_state > 0 + ) + self.v_soma[s_out_buff] = 0 + + return s_out_buff + + def run_spk(self): + """The run function that performs the actual computation during + execution orchestrated bgy a PyLoihiProcessModel using the + LoihiProtocol. + """ + + # Receive synaptic input + a_dend_in_data = self.a_dend_in.recv() + a_soma_in_data = self.a_soma_in.recv() + + # Check threshold scaling + if not self.isthrscaled: + self.scale_threshold() + + self.subthr_dynamics(a_dend_in_data, a_soma_in_data) + + self.update_up_state() + + self.s_out_buff = self.soma_spike_and_reset() + + self.s_out.send(self.s_out_buff) diff --git a/src/lava/proc/plateau/process.py b/src/lava/proc/plateau/process.py new file mode 100644 index 000000000..f3e63c8f8 --- /dev/null +++ b/src/lava/proc/plateau/process.py @@ -0,0 +1,70 @@ +# Copyright (C) 2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# See: https://spdx.org/licenses/ + + +import typing as ty +from lava.magma.core.process.process import AbstractProcess +from lava.magma.core.process.variable import Var +from lava.magma.core.process.ports.ports import InPort, OutPort + + +class Plateau(AbstractProcess): + """Plateau Neuron Process. + + Couples two modified LIF dynamics. The neuron posesses two potentials, + v_dend and v_soma. Both follow sub-threshold LIF dynamics. When v_dend + crosses v_th_dend, it resets and sets the up_state to the value up_dur. + The supra-threshold behavior of v_soma depends on up_state: + if up_state == 0: + v_soma follows sub-threshold dynamics + if up_state > 0: + v_soma resets and the neuron sends out a spike + + Parameters + ---------- + shape : tuple(int) + Number and topology of Plateau neurons. + dv_dend : float + Inverse of the decay time-constant for the dendrite potential. + dv_soma : float + Inverse of the decay time-constant for the soma potential. + vth_dend : float + Dendrite threshold voltage, exceeding which, the neuron will enter the + UP state. + vth_soma : float + Soma threshold voltage, exceeding which, the neuron will spike if it is + also in the UP state. + up_dur : int + The duration, in timesteps, of the UP state. + """ + def __init__( + self, + shape: ty.Tuple[int, ...], + dv_dend: float, + dv_soma: float, + vth_dend: float, + vth_soma: float, + up_dur: int, + name: ty.Optional[str] = None, + ): + super().__init__( + shape=shape, + dv_dend=dv_dend, + dv_soma=dv_soma, + name=name, + up_dur=up_dur, + vth_dend=vth_dend, + vth_soma=vth_soma + ) + self.a_dend_in = InPort(shape=shape) + self.a_soma_in = InPort(shape=shape) + self.s_out = OutPort(shape=shape) + self.v_dend = Var(shape=shape, init=0) + self.v_soma = Var(shape=shape, init=0) + self.dv_dend = Var(shape=(1,), init=dv_dend) + self.dv_soma = Var(shape=(1,), init=dv_soma) + self.vth_dend = Var(shape=(1,), init=vth_dend) + self.vth_soma = Var(shape=(1,), init=vth_soma) + self.up_dur = Var(shape=(1,), init=up_dur) + self.up_state = Var(shape=shape, init=0) diff --git a/tests/lava/proc/plateau/test_models.py b/tests/lava/proc/plateau/test_models.py new file mode 100644 index 000000000..fa00a385b --- /dev/null +++ b/tests/lava/proc/plateau/test_models.py @@ -0,0 +1,178 @@ +# Copyright (C) 2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# See: https://spdx.org/licenses/ + + +import unittest +import numpy as np +from numpy.testing import assert_almost_equal + +from lava.magma.core.decorator import implements, requires, tag +from lava.magma.core.model.py.model import PyLoihiProcessModel +from lava.magma.core.model.py.ports import PyOutPort, PyInPort +from lava.magma.core.model.py.type import LavaPyType +from lava.magma.core.process.ports.ports import OutPort, InPort +from lava.magma.core.process.process import AbstractProcess +from lava.magma.core.process.variable import Var +from lava.magma.core.resources import CPU +from lava.proc.plateau.process import Plateau +from lava.proc.dense.process import Dense +from lava.magma.core.run_configs import Loihi2SimCfg, RunConfig +from lava.magma.core.run_conditions import RunSteps +from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol +from lava.tests.lava.proc.lif.test_models import VecSendProcess, VecRecvProcess + + +class SpikeGen(AbstractProcess): + """Process for sending spikes at user-supplied time steps. + + Parameters + ---------- + spikes_in: list[list], list of lists containing spike times + runtime: int, number of timesteps for the generator to store spikes + """ + def __init__(self, spikes_in, runtime): + super().__init__() + n = len(spikes_in) + self.shape = (n,) + spike_data = np.zeros(shape=(n, runtime)) + for i in range(n): + for t in range(1, runtime + 1): + if t in spikes_in[i]: + spike_data[i, t - 1] = 1 + self.s_out = OutPort(shape=self.shape) + self.spike_data = Var(shape=(n, runtime), init=spike_data) + + +@implements(proc=SpikeGen, protocol=LoihiProtocol) +@requires(CPU) +@tag('fixed_pt') +class PySpikeGenModel(PyLoihiProcessModel): + s_out: PyOutPort = LavaPyType(PyOutPort.VEC_DENSE, float) + spike_data: np.ndarray = LavaPyType(np.ndarray, float) + + def run_spk(self): + """Send the appropriate spikes for the given time step + """ + self.s_out.send(self.spike_data[:, self.time_step - 1]) + + +class TestPlateauProcessModelsFixed(unittest.TestCase): + """Tests for the fixed point Plateau process models.""" + def test_fixed_max_decay(self): + """ + Tests fixed point Plateau with max voltage decays. + """ + shape = (3,) + num_steps = 20 + spikes_in_dend = [ + [5], + [5], + [5], + ] + spikes_in_soma = [ + [3], + [10], + [17] + ] + sg_dend = SpikeGen(spikes_in=spikes_in_dend, runtime=num_steps) + sg_soma = SpikeGen(spikes_in=spikes_in_soma, runtime=num_steps) + dense_dend = Dense(weights=2 * np.diag(np.ones(shape=shape))) + dense_soma = Dense(weights=2 * np.diag(np.ones(shape=shape))) + plat = Plateau( + shape=shape, + dv_dend=4096, + dv_soma=4096, + vth_soma=1, + vth_dend=1, + up_dur=10 + ) + vr = VecRecvProcess(shape=(num_steps, shape[0])) + sg_dend.s_out.connect(dense_dend.s_in) + sg_soma.s_out.connect(dense_soma.s_in) + dense_dend.a_out.connect(plat.a_dend_in) + dense_soma.a_out.connect(plat.a_soma_in) + plat.s_out.connect(vr.s_in) + # run model + plat.run(RunSteps(num_steps), Loihi2SimCfg(select_tag='fixed_pt')) + test_spk_data = vr.spk_data.get() + plat.stop() + # Gold standard for the test + expected_spk_data = np.zeros((num_steps, shape[0])) + # Neuron 2 should spike when receiving soma input + expected_spk_data[10, 1] = 1 + self.assertTrue(np.all(expected_spk_data == test_spk_data)) + + def test_up_dur(self): + """ + Tests that the UP state lasts for the time specified by the model. + Checks that up_state decreases by one each time step after activation. + """ + shape = (1,) + num_steps = 10 + spikes_in_dend = [[3]] + sg_dend = SpikeGen(spikes_in=spikes_in_dend, runtime=num_steps) + dense_dend = Dense(weights=2 * (np.diag(np.ones(shape=shape)))) + plat = Plateau( + shape=shape, + dv_dend=4096, + dv_soma=4096, + vth_soma=1, + vth_dend=1, + up_dur=5 + ) + sg_dend.s_out.connect(dense_dend.s_in) + dense_dend.a_out.connect(plat.a_dend_in) + # run model + test_up_state = [] + for t in range(num_steps): + plat.run(RunSteps(1), Loihi2SimCfg(select_tag='fixed_pt')) + test_up_state.append(plat.up_state.get().astype(int)[0]) + plat.stop() + # Gold standard for the test + # UP state active time steps 4 - 9 (5 timesteps) + # this is delayed by one b.c. of the Dense process + expected_up_state = [0, 0, 0, 5, 4, 3, 2, 1, 0, 0] + self.assertListEqual(expected_up_state, test_up_state) + + def test_fixed_dvs(self): + """ + Tests fixed point Plateau voltage decays. + """ + shape = (1,) + num_steps = 10 + spikes_in = [[1]] + sg_dend = SpikeGen(spikes_in=spikes_in, runtime=num_steps) + sg_soma = SpikeGen(spikes_in=spikes_in, runtime=num_steps) + dense_dend = Dense(weights=100 * np.diag(np.ones(shape=shape))) + dense_soma = Dense(weights=100 * np.diag(np.ones(shape=shape))) + plat = Plateau( + shape=shape, + dv_dend=2048, + dv_soma=1024, + vth_soma=100, + vth_dend=100, + up_dur=10 + ) + sg_dend.s_out.connect(dense_dend.s_in) + sg_soma.s_out.connect(dense_soma.s_in) + dense_dend.a_out.connect(plat.a_dend_in) + dense_soma.a_out.connect(plat.a_soma_in) + # run model + test_v_dend = [] + test_v_soma = [] + for t in range(num_steps): + plat.run(RunSteps(1), Loihi2SimCfg(select_tag='fixed_pt')) + test_v_dend.append(plat.v_dend.get().astype(int)[0]) + test_v_soma.append(plat.v_soma.get().astype(int)[0]) + plat.stop() + # Gold standard for the test + # 100<<6 = 6400 -- initial value at time step 2 + expected_v_dend = [ + 0, 6400, 3200, 1600, 800, 400, 200, 100, 50, 25 + ] + expected_v_soma = [ + 0, 6400, 4800, 3600, 2700, 2025, 1518, 1138, 853, 639 + ] + self.assertListEqual(expected_v_dend, test_v_dend) + self.assertListEqual(expected_v_soma, test_v_soma) diff --git a/tests/lava/proc/plateau/test_process.py b/tests/lava/proc/plateau/test_process.py new file mode 100644 index 000000000..d902fa270 --- /dev/null +++ b/tests/lava/proc/plateau/test_process.py @@ -0,0 +1,30 @@ +# Copyright (C) 2023 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# See: https://spdx.org/licenses/ + + +import unittest +import numpy as np +from lava.proc.plateau.process import Plateau + + +class TestPlateauProcess(unittest.TestCase): + """Tests for Plateau class""" + def test_init(self): + """Tests instantiation of Plateau""" + plat = Plateau( + shape=(100,), + dv_dend=100., + dv_soma=1., + vth_dend=10., + vth_soma=1., + up_dur=10, + name="Plat" + ) + + self.assertEqual(plat.name, "Plat") + self.assertEqual(plat.dv_dend.init, 100.) + self.assertEqual(plat.dv_soma.init, 1.) + self.assertEqual(plat.vth_dend.init, 10.) + self.assertEqual(plat.vth_soma.init, 1.) + self.assertEqual(plat.up_dur.init, 10) From 5db90d736308b2ac72c1e3c0d798d1c365d65286 Mon Sep 17 00:00:00 2001 From: Marcus G K Williams Date: Tue, 17 Oct 2023 23:40:42 -0700 Subject: [PATCH 02/12] Fix codacy issues Signed-off-by: Marcus G K Williams --- src/lava/proc/plateau/models.py | 3 +++ tests/lava/proc/plateau/test_models.py | 11 +++++------ tests/lava/proc/plateau/test_process.py | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lava/proc/plateau/models.py b/src/lava/proc/plateau/models.py index 1195d4305..5b200c210 100644 --- a/src/lava/proc/plateau/models.py +++ b/src/lava/proc/plateau/models.py @@ -49,6 +49,9 @@ def __init__(self, proc_params): self.vth_shift = 6 self.act_shift = 6 self.isthrscaled = False + self.effective_vth_dend = None + self.effective_vth_soma = None + self.s_out_buff = None def scale_threshold(self): self.effective_vth_dend = np.left_shift(self.vth_dend, self.vth_shift) diff --git a/tests/lava/proc/plateau/test_models.py b/tests/lava/proc/plateau/test_models.py index fa00a385b..ea0a8088b 100644 --- a/tests/lava/proc/plateau/test_models.py +++ b/tests/lava/proc/plateau/test_models.py @@ -5,22 +5,21 @@ import unittest import numpy as np -from numpy.testing import assert_almost_equal from lava.magma.core.decorator import implements, requires, tag from lava.magma.core.model.py.model import PyLoihiProcessModel -from lava.magma.core.model.py.ports import PyOutPort, PyInPort +from lava.magma.core.model.py.ports import PyOutPort from lava.magma.core.model.py.type import LavaPyType -from lava.magma.core.process.ports.ports import OutPort, InPort +from lava.magma.core.process.ports.ports import OutPort from lava.magma.core.process.process import AbstractProcess from lava.magma.core.process.variable import Var from lava.magma.core.resources import CPU from lava.proc.plateau.process import Plateau from lava.proc.dense.process import Dense -from lava.magma.core.run_configs import Loihi2SimCfg, RunConfig +from lava.magma.core.run_configs import Loihi2SimCfg from lava.magma.core.run_conditions import RunSteps from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol -from lava.tests.lava.proc.lif.test_models import VecSendProcess, VecRecvProcess +from tests.lava.proc.lif.test_models import VecRecvProcess class SpikeGen(AbstractProcess): @@ -125,7 +124,7 @@ def test_up_dur(self): dense_dend.a_out.connect(plat.a_dend_in) # run model test_up_state = [] - for t in range(num_steps): + for _ in range(num_steps): plat.run(RunSteps(1), Loihi2SimCfg(select_tag='fixed_pt')) test_up_state.append(plat.up_state.get().astype(int)[0]) plat.stop() diff --git a/tests/lava/proc/plateau/test_process.py b/tests/lava/proc/plateau/test_process.py index d902fa270..333d08ea4 100644 --- a/tests/lava/proc/plateau/test_process.py +++ b/tests/lava/proc/plateau/test_process.py @@ -4,7 +4,6 @@ import unittest -import numpy as np from lava.proc.plateau.process import Plateau From bab1fd1dc7a79b3fbd0e70548fda25b26deff69c Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 12:19:57 -0400 Subject: [PATCH 03/12] fixed typo 'bgy' -> 'by' in PyPlateauModelFixed.run_spk docstring Signed-off-by: kevin --- src/lava/proc/plateau/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lava/proc/plateau/models.py b/src/lava/proc/plateau/models.py index 1195d4305..0f790f590 100644 --- a/src/lava/proc/plateau/models.py +++ b/src/lava/proc/plateau/models.py @@ -109,7 +109,7 @@ def soma_spike_and_reset(self): def run_spk(self): """The run function that performs the actual computation during - execution orchestrated bgy a PyLoihiProcessModel using the + execution orchestrated by a PyLoihiProcessModel using the LoihiProtocol. """ From 5d8b10c4e75c00b515037d0d2cb78e26d03d01ca Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 12:22:55 -0400 Subject: [PATCH 04/12] changed 'potential' to 'voltage' in the Plateau class docstring Signed-off-by: kevin --- src/lava/proc/plateau/process.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lava/proc/plateau/process.py b/src/lava/proc/plateau/process.py index f3e63c8f8..554781c39 100644 --- a/src/lava/proc/plateau/process.py +++ b/src/lava/proc/plateau/process.py @@ -12,7 +12,7 @@ class Plateau(AbstractProcess): """Plateau Neuron Process. - Couples two modified LIF dynamics. The neuron posesses two potentials, + Couples two modified LIF dynamics. The neuron posesses two voltages, v_dend and v_soma. Both follow sub-threshold LIF dynamics. When v_dend crosses v_th_dend, it resets and sets the up_state to the value up_dur. The supra-threshold behavior of v_soma depends on up_state: @@ -26,9 +26,9 @@ class Plateau(AbstractProcess): shape : tuple(int) Number and topology of Plateau neurons. dv_dend : float - Inverse of the decay time-constant for the dendrite potential. + Inverse of the decay time-constant for the dendrite voltage. dv_soma : float - Inverse of the decay time-constant for the soma potential. + Inverse of the decay time-constant for the soma voltage. vth_dend : float Dendrite threshold voltage, exceeding which, the neuron will enter the UP state. From 1961780b3f75654e8d586535252585be28740f3e Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 12:42:44 -0400 Subject: [PATCH 05/12] changed variable types from float to int for dv_dend, dv_soma, vth_dend, and vth_soma Signed-off-by: kevin --- src/lava/proc/plateau/process.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lava/proc/plateau/process.py b/src/lava/proc/plateau/process.py index 554781c39..83fa04fa8 100644 --- a/src/lava/proc/plateau/process.py +++ b/src/lava/proc/plateau/process.py @@ -25,14 +25,14 @@ class Plateau(AbstractProcess): ---------- shape : tuple(int) Number and topology of Plateau neurons. - dv_dend : float + dv_dend : int Inverse of the decay time-constant for the dendrite voltage. - dv_soma : float + dv_soma : int Inverse of the decay time-constant for the soma voltage. - vth_dend : float + vth_dend : int Dendrite threshold voltage, exceeding which, the neuron will enter the UP state. - vth_soma : float + vth_soma : int Soma threshold voltage, exceeding which, the neuron will spike if it is also in the UP state. up_dur : int @@ -41,10 +41,10 @@ class Plateau(AbstractProcess): def __init__( self, shape: ty.Tuple[int, ...], - dv_dend: float, - dv_soma: float, - vth_dend: float, - vth_soma: float, + dv_dend: int, + dv_soma: int, + vth_dend: int, + vth_soma: int, up_dur: int, name: ty.Optional[str] = None, ): From 4be49183a3825101fd91fa7aa51bbda747b528df Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 12:44:02 -0400 Subject: [PATCH 06/12] updated Plateau process unit test to specify integer inputs for dv_dend, dv_soma, vth_dend, and vth_soma Signed-off-by: kevin --- tests/lava/proc/plateau/test_process.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/lava/proc/plateau/test_process.py b/tests/lava/proc/plateau/test_process.py index d902fa270..79f023bfb 100644 --- a/tests/lava/proc/plateau/test_process.py +++ b/tests/lava/proc/plateau/test_process.py @@ -14,17 +14,17 @@ def test_init(self): """Tests instantiation of Plateau""" plat = Plateau( shape=(100,), - dv_dend=100., - dv_soma=1., - vth_dend=10., - vth_soma=1., + dv_dend=100, + dv_soma=1, + vth_dend=10, + vth_soma=1, up_dur=10, name="Plat" ) self.assertEqual(plat.name, "Plat") - self.assertEqual(plat.dv_dend.init, 100.) - self.assertEqual(plat.dv_soma.init, 1.) - self.assertEqual(plat.vth_dend.init, 10.) - self.assertEqual(plat.vth_soma.init, 1.) + self.assertEqual(plat.dv_dend.init, 100) + self.assertEqual(plat.dv_soma.init, 1) + self.assertEqual(plat.vth_dend.init, 10) + self.assertEqual(plat.vth_soma.init, 1) self.assertEqual(plat.up_dur.init, 10) From 70433d42f8c7a4b1f5f6de9fc174ab87b08fc99d Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 13:07:32 -0400 Subject: [PATCH 07/12] added input validation for dv_dend, dv_soma, vth_dend, vth_soma, up_dur Signed-off-by: kevin --- src/lava/proc/plateau/models.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lava/proc/plateau/models.py b/src/lava/proc/plateau/models.py index 0f790f590..d033e3aa9 100644 --- a/src/lava/proc/plateau/models.py +++ b/src/lava/proc/plateau/models.py @@ -42,6 +42,7 @@ class PyPlateauModelFixed(PyLoihiProcessModel): def __init__(self, proc_params): super(PyPlateauModelFixed, self).__init__(proc_params) + self._validate_inputs(proc_params) self.uv_bitwidth = 24 self.max_uv_val = 2 ** (self.uv_bitwidth - 1) self.decay_shift = 12 @@ -50,6 +51,21 @@ def __init__(self, proc_params): self.act_shift = 6 self.isthrscaled = False + def _validate_var(self, var, var_type, min_val, max_val, var_name): + if type(var) is not var_type: + raise ValueError(f"'{var_name}' must have type {var_type}") + if var < min_val or var > max_val: + raise ValueError( + f"'{var_name}' must be in range [{min_val}, {max_val}]" + ) + + def _validate_inputs(self, proc_params): + self._validate_var(proc_params['dv_dend'], int, 0, 4095, 'dv_dend') + self._validate_var(proc_params['dv_soma'], int, 0, 4095, 'dv_soma') + self._validate_var(proc_params['vth_dend'], int, 0, 131071, 'vth_dend') + self._validate_var(proc_params['vth_soma'], int, 0, 131071, 'vth_soma') + self._validate_var(proc_params['up_dur'], int, 0, 255, 'up_dur') + def scale_threshold(self): self.effective_vth_dend = np.left_shift(self.vth_dend, self.vth_shift) self.effective_vth_soma = np.left_shift(self.vth_soma, self.vth_shift) From 4b2648030dacddd541169bef64f4b78279381cea Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 13:08:42 -0400 Subject: [PATCH 08/12] fixed typos 'du_dend' -> 'dv_dend' and 'du_soma' -> 'dv_soma' in PyPlateauModelFixed docstring Signed-off-by: kevin --- src/lava/proc/plateau/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lava/proc/plateau/models.py b/src/lava/proc/plateau/models.py index d033e3aa9..99a92689f 100644 --- a/src/lava/proc/plateau/models.py +++ b/src/lava/proc/plateau/models.py @@ -21,8 +21,8 @@ class PyPlateauModelFixed(PyLoihiProcessModel): Precisions of state variables - - du_dend : unsigned 12-bit integer (0 to 4095) - - du_soma : unsigned 12-bit integer (0 to 4095) + - dv_dend : unsigned 12-bit integer (0 to 4095) + - dv_soma : unsigned 12-bit integer (0 to 4095) - vth_dend : unsigned 17-bit integer (0 to 131071) - vth_soma : unsigned 17-bit integer (0 to 131071) - up_dur : unsigned 8-bit integer (0 to 255) From fb217baa213674df17c5caf0d0b5078da9b423ac Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 14:22:35 -0400 Subject: [PATCH 09/12] reduced decay_unity by 1 (4096 -> 4095) to agree with limits on dv_dend and dv_soma (0, 4095) Signed-off-by: kevin --- src/lava/proc/plateau/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lava/proc/plateau/models.py b/src/lava/proc/plateau/models.py index 99a92689f..6b8b6b66e 100644 --- a/src/lava/proc/plateau/models.py +++ b/src/lava/proc/plateau/models.py @@ -46,7 +46,7 @@ def __init__(self, proc_params): self.uv_bitwidth = 24 self.max_uv_val = 2 ** (self.uv_bitwidth - 1) self.decay_shift = 12 - self.decay_unity = 2 ** self.decay_shift + self.decay_unity = 2 ** self.decay_shift - 1 self.vth_shift = 6 self.act_shift = 6 self.isthrscaled = False From d1c66f02ffbb3994f518193e481d9fcc958b10fa Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 14:23:21 -0400 Subject: [PATCH 10/12] updated tests to agree with changes to dv_dend, dv_soma allowed range Signed-off-by: kevin --- tests/lava/proc/plateau/test_models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/lava/proc/plateau/test_models.py b/tests/lava/proc/plateau/test_models.py index fa00a385b..e3d94348f 100644 --- a/tests/lava/proc/plateau/test_models.py +++ b/tests/lava/proc/plateau/test_models.py @@ -81,8 +81,8 @@ def test_fixed_max_decay(self): dense_soma = Dense(weights=2 * np.diag(np.ones(shape=shape))) plat = Plateau( shape=shape, - dv_dend=4096, - dv_soma=4096, + dv_dend=4095, + dv_soma=4095, vth_soma=1, vth_dend=1, up_dur=10 @@ -115,8 +115,8 @@ def test_up_dur(self): dense_dend = Dense(weights=2 * (np.diag(np.ones(shape=shape)))) plat = Plateau( shape=shape, - dv_dend=4096, - dv_soma=4096, + dv_dend=4095, + dv_soma=4095, vth_soma=1, vth_dend=1, up_dur=5 @@ -169,10 +169,10 @@ def test_fixed_dvs(self): # Gold standard for the test # 100<<6 = 6400 -- initial value at time step 2 expected_v_dend = [ - 0, 6400, 3200, 1600, 800, 400, 200, 100, 50, 25 + 0, 6400, 3198, 1598, 798, 398, 198, 98, 48, 23 ] expected_v_soma = [ - 0, 6400, 4800, 3600, 2700, 2025, 1518, 1138, 853, 639 + 0, 6400, 4798, 3597, 2696, 2021, 1515, 1135, 850, 637 ] self.assertListEqual(expected_v_dend, test_v_dend) self.assertListEqual(expected_v_soma, test_v_soma) From c469bd8860112776fa27ef742e675751de1e6da3 Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 19 Oct 2023 14:29:44 -0400 Subject: [PATCH 11/12] Removed custom SpikeGen process and replaced with source RingBuffers Signed-off-by: kevin --- tests/lava/proc/plateau/test_models.py | 80 +++++++------------------- 1 file changed, 20 insertions(+), 60 deletions(-) diff --git a/tests/lava/proc/plateau/test_models.py b/tests/lava/proc/plateau/test_models.py index e3d94348f..2e2003b95 100644 --- a/tests/lava/proc/plateau/test_models.py +++ b/tests/lava/proc/plateau/test_models.py @@ -5,56 +5,24 @@ import unittest import numpy as np -from numpy.testing import assert_almost_equal -from lava.magma.core.decorator import implements, requires, tag -from lava.magma.core.model.py.model import PyLoihiProcessModel -from lava.magma.core.model.py.ports import PyOutPort, PyInPort -from lava.magma.core.model.py.type import LavaPyType -from lava.magma.core.process.ports.ports import OutPort, InPort -from lava.magma.core.process.process import AbstractProcess -from lava.magma.core.process.variable import Var -from lava.magma.core.resources import CPU from lava.proc.plateau.process import Plateau from lava.proc.dense.process import Dense -from lava.magma.core.run_configs import Loihi2SimCfg, RunConfig +from lava.proc.io.source import RingBuffer as Source +from lava.magma.core.run_configs import Loihi2SimCfg from lava.magma.core.run_conditions import RunSteps -from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol -from lava.tests.lava.proc.lif.test_models import VecSendProcess, VecRecvProcess +from lava.tests.lava.proc.lif.test_models import VecRecvProcess -class SpikeGen(AbstractProcess): - """Process for sending spikes at user-supplied time steps. - - Parameters - ---------- - spikes_in: list[list], list of lists containing spike times - runtime: int, number of timesteps for the generator to store spikes +def create_spike_source(spike_list, n_indices, n_timesteps): + """Use list of spikes [(idx, timestep), ...] to create a RingBuffer source + with data shape (n_indices, n_timesteps) and spikes at all specified points + in the spike_list. """ - def __init__(self, spikes_in, runtime): - super().__init__() - n = len(spikes_in) - self.shape = (n,) - spike_data = np.zeros(shape=(n, runtime)) - for i in range(n): - for t in range(1, runtime + 1): - if t in spikes_in[i]: - spike_data[i, t - 1] = 1 - self.s_out = OutPort(shape=self.shape) - self.spike_data = Var(shape=(n, runtime), init=spike_data) - - -@implements(proc=SpikeGen, protocol=LoihiProtocol) -@requires(CPU) -@tag('fixed_pt') -class PySpikeGenModel(PyLoihiProcessModel): - s_out: PyOutPort = LavaPyType(PyOutPort.VEC_DENSE, float) - spike_data: np.ndarray = LavaPyType(np.ndarray, float) - - def run_spk(self): - """Send the appropriate spikes for the given time step - """ - self.s_out.send(self.spike_data[:, self.time_step - 1]) + data = np.zeros(shape=(n_indices, n_timesteps)) + for idx, timestep in spike_list: + data[idx, timestep - 1] = 1 + return Source(data=data) class TestPlateauProcessModelsFixed(unittest.TestCase): @@ -65,18 +33,10 @@ def test_fixed_max_decay(self): """ shape = (3,) num_steps = 20 - spikes_in_dend = [ - [5], - [5], - [5], - ] - spikes_in_soma = [ - [3], - [10], - [17] - ] - sg_dend = SpikeGen(spikes_in=spikes_in_dend, runtime=num_steps) - sg_soma = SpikeGen(spikes_in=spikes_in_soma, runtime=num_steps) + spikes_in_dend = [(0, 5), (1, 5), (2, 5)] + spikes_in_soma = [(0, 3), (1, 10), (2, 17)] + sg_dend = create_spike_source(spikes_in_dend, shape[0], num_steps) + sg_soma = create_spike_source(spikes_in_soma, shape[0], num_steps) dense_dend = Dense(weights=2 * np.diag(np.ones(shape=shape))) dense_soma = Dense(weights=2 * np.diag(np.ones(shape=shape))) plat = Plateau( @@ -110,8 +70,8 @@ def test_up_dur(self): """ shape = (1,) num_steps = 10 - spikes_in_dend = [[3]] - sg_dend = SpikeGen(spikes_in=spikes_in_dend, runtime=num_steps) + spikes_in_dend = [(0, 3)] + sg_dend = create_spike_source(spikes_in_dend, shape[0], num_steps) dense_dend = Dense(weights=2 * (np.diag(np.ones(shape=shape)))) plat = Plateau( shape=shape, @@ -141,9 +101,9 @@ def test_fixed_dvs(self): """ shape = (1,) num_steps = 10 - spikes_in = [[1]] - sg_dend = SpikeGen(spikes_in=spikes_in, runtime=num_steps) - sg_soma = SpikeGen(spikes_in=spikes_in, runtime=num_steps) + spikes_in = [(0, 1)] + sg_dend = create_spike_source(spikes_in, shape[0], num_steps) + sg_soma = create_spike_source(spikes_in, shape[0], num_steps) dense_dend = Dense(weights=100 * np.diag(np.ones(shape=shape))) dense_soma = Dense(weights=100 * np.diag(np.ones(shape=shape))) plat = Plateau( From cea6296c43ca3ff907c45c2ff8082744516ffd52 Mon Sep 17 00:00:00 2001 From: kevin Date: Sun, 22 Oct 2023 15:58:06 -0400 Subject: [PATCH 12/12] fixed codacy issues Signed-off-by: kevin --- src/lava/proc/plateau/models.py | 2 +- tests/lava/proc/plateau/test_models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lava/proc/plateau/models.py b/src/lava/proc/plateau/models.py index 6b8b6b66e..e122f3db8 100644 --- a/src/lava/proc/plateau/models.py +++ b/src/lava/proc/plateau/models.py @@ -52,7 +52,7 @@ def __init__(self, proc_params): self.isthrscaled = False def _validate_var(self, var, var_type, min_val, max_val, var_name): - if type(var) is not var_type: + if not isinstance(var, var_type): raise ValueError(f"'{var_name}' must have type {var_type}") if var < min_val or var > max_val: raise ValueError( diff --git a/tests/lava/proc/plateau/test_models.py b/tests/lava/proc/plateau/test_models.py index 2e2003b95..03105f009 100644 --- a/tests/lava/proc/plateau/test_models.py +++ b/tests/lava/proc/plateau/test_models.py @@ -121,7 +121,7 @@ def test_fixed_dvs(self): # run model test_v_dend = [] test_v_soma = [] - for t in range(num_steps): + for _ in range(num_steps): plat.run(RunSteps(1), Loihi2SimCfg(select_tag='fixed_pt')) test_v_dend.append(plat.v_dend.get().astype(int)[0]) test_v_soma.append(plat.v_soma.get().astype(int)[0])