Skip to content

Commit

Permalink
Add: Tests for EILifRefractory
Browse files Browse the repository at this point in the history
  • Loading branch information
monkin77 committed Aug 24, 2024
1 parent 72d6aac commit 2729cae
Show file tree
Hide file tree
Showing 4 changed files with 675 additions and 14 deletions.
22 changes: 17 additions & 5 deletions src/lava/proc/io/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,24 @@ def validate_channel_config(channel_config: ChannelConfig) -> None:
def convert_to_numpy_array(val, shape, name = "value", verbose=False):
"""
Converts a given value to a numpy array if it is not already
@param val: The value to convert. Can be a scalar, list, or numpy array
@param shape: The shape of the array to convert to
@param verbose: Whether to print debug messages
@return: The value as a numpy array
@raises ValueError: If the value cannot be converted to a numpy array
Parameters
----------
val : scalar | list | np.ndarray
The value to convert. Can be a scalar, list, or numpy array
shape : tuple
The shape of the array to convert to
verbose : bool
Whether to print debug messages
Returns
----------
value: np.ndarray
The value as a numpy array
Raises
----------
ValueError: If the value cannot be converted to a numpy array
"""
if np.isscalar(val):
if verbose:
Expand Down
16 changes: 10 additions & 6 deletions src/lava/proc/lif/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,13 +535,14 @@ def __init__(
vth: ty.Optional[float] = 10,
name: ty.Optional[str] = None,
log_config: ty.Optional[LogConfig] = None,
verbose: ty.Optional[bool] = False,
**kwargs,
) -> None:
# Try to convert du_exc, du_inh and dv to numpy arrays if they are not already
# If unsuccessful, it will raise a ValueError
du_exc = convert_to_numpy_array(du_exc, shape, "du_exc", verbose=True)
du_inh = convert_to_numpy_array(du_inh, shape, "du_inh", verbose=True)
dv = convert_to_numpy_array(dv, shape, "dv", verbose=True)
du_exc = convert_to_numpy_array(du_exc, shape, "du_exc", verbose=verbose)
du_inh = convert_to_numpy_array(du_inh, shape, "du_inh", verbose=verbose)
dv = convert_to_numpy_array(dv, shape, "dv", verbose=verbose)

Check notice on line 546 in src/lava/proc/lif/process.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/lava/proc/lif/process.py#L546

Trailing whitespace
super().__init__(
shape=shape,
Expand All @@ -563,7 +564,7 @@ def __init__(
self.vth = Var(shape=(1,), init=vth)

class EILIFRefractory(EILIF):
"""Exctitatory/Inhibitory Leaky-Integrate-and-Fire (LIF) neural Process with refractory period.
"""Excitatory/Inhibitory Leaky-Integrate-and-Fire (LIF) neural Process with refractory period.
This neuron model receives 2 input currents, one excitatory and one inhibitory.
The neuron's total current is the sum of the excitatory and inhibitory currents.
Each current has its own decay time-constant and it is independent on a neuron-to-neuron basis.
Expand Down Expand Up @@ -622,7 +623,7 @@ def __init__(
self,
*,
shape: ty.Tuple[int, ...],
v: ty.Union[float, list, np.ndarray],
v: ty.Optional[ty.Union[float, list, np.ndarray]] = 0,
u_exc: ty.Union[float, list, np.ndarray] = 0,
u_inh: ty.Union[float, list, np.ndarray] = 0,
du_exc: ty.Optional[ty.Union[float, list, np.ndarray]] = 0,
Expand All @@ -634,6 +635,7 @@ def __init__(
refractory_period: ty.Optional[int] = 1,
name: ty.Optional[str] = None,
log_config: ty.Optional[LogConfig] = None,
verbose: ty.Optional[bool] = False,
**kwargs,
) -> None:
super().__init__(
Expand All @@ -649,6 +651,7 @@ def __init__(
vth=vth,
name=name,
log_config=log_config,
verbose=verbose,
**kwargs,
)

Expand All @@ -657,7 +660,8 @@ def __init__(
raise ValueError("Refractory period must be > 0.")
# Check if the refractory period is a float
if isinstance(refractory_period, float):
print("Refractory period must be an integer. Converting to integer...")
if verbose:
print("Refractory period must be an integer. Converting to integer...")
refractory_period = int(refractory_period)

self.proc_params["refractory_period"] = refractory_period
Expand Down
84 changes: 81 additions & 3 deletions tests/lava/proc/lif/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
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.proc.lif.process import LIF, LIFReset, TernaryLIF, LIFRefractory, EILIF
from lava.proc.lif.process import LIF, LIFReset, TernaryLIF, LIFRefractory, EILIF, EILIFRefractory
from lava.proc import io


Expand Down Expand Up @@ -932,10 +932,88 @@ def test_different_decays(self):
v = v_logger.data.get()
ei_lif.stop()

print("V: ", v)

# Voltage is expected to remain at reset level for two time steps
v_expected = np.array([[1, 2.9, 4.61, 5.149, 4.73, 3.54, 2.71, 2.16],
[1, 2.8, 4.24, 4.39, 3.61, 2.16, 1.22, 0.62]], dtype=float)

assert_almost_equal(v, v_expected, decimal=2)

class TestEILIFRefractoryFloat(unittest.TestCase):
"""Test EILIFRefractory process model"""

def test_no_decays(self):
"""Test float model"""
num_neurons = 2
num_steps = 8
refractory_period = 1

# Two neurons with different biases
# No Input current provided to make the voltage dependent on the bias
ei_lif = EILIFRefractory(shape=(num_neurons,),
u_exc=np.zeros(num_neurons),
u_inh=np.zeros(num_neurons),
bias_mant=np.arange(num_neurons) + 1,
bias_exp=np.ones(
(num_neurons,), dtype=float),
vth=4,
refractory_period=refractory_period)

v_logger = io.sink.Read(buffer=num_steps)
v_logger.connect_var(ei_lif.v)

ei_lif.run(condition=RunSteps(num_steps),
run_cfg=Loihi2SimCfg(select_tag="floating_pt"))

v = v_logger.data.get()
ei_lif.stop()

# Voltage is expected to remain at reset level for two time steps
v_expected = np.array([[1, 2, 3, 4, 0, 0, 1, 2],
[2, 4, 0, 0, 2, 4, 0, 0]], dtype=float)

assert_almost_equal(v, v_expected)

def test_different_decays(self):
"""Test float model with different decays per neuron"""
num_neurons = 2
num_steps = 8
refractory_period = 1

du_exc_arr = np.array([0.1, 0.2])
du_inh_arr = np.array([0.2, 0.3])

# Neuron 1 will spike, while neuron 2 will not due to different decay rates.
ei_lif = EILIFRefractory(shape=(num_neurons,),
du_exc=du_exc_arr,
du_inh=du_inh_arr,
vth=5,
refractory_period=refractory_period)

# Setup external input
positive_sps = VecSendProcess(shape=(num_neurons,), num_steps=num_steps,
vec_to_send=np.full(shape=(num_neurons), fill_value=1),
send_at_times=[1, 1, 0, 0, 0, 0, 0 , 0], dtype=bool)

negative_sps = VecSendProcess(shape=(num_neurons,), num_steps=num_steps,
vec_to_send=np.full(shape=(num_neurons), fill_value=-1),
send_at_times=[0, 0, 0, 1, 1, 1, 0 , 0], dtype=bool)


# Connect external input to the EILIF model
positive_sps.s_out.connect(ei_lif.a_in)
negative_sps.s_out.connect(ei_lif.a_in)

v_logger = io.sink.Read(buffer=num_steps)
v_logger.connect_var(ei_lif.v)

ei_lif.run(condition=RunSteps(num_steps),
run_cfg=Loihi2SimCfg(select_tag="floating_pt"))

v = v_logger.data.get()
ei_lif.stop()

# Voltage is expected to remain at reset level for two time steps
v_expected = np.array([[1, 2.9, 4.61, 0, 0, -1.19341, -2.02, -2.57],
[1, 2.8, 4.24, 4.39, 3.61, 2.16, 1.22, 0.62]], dtype=float)

assert_almost_equal(v, v_expected, decimal=2)
Loading

0 comments on commit 2729cae

Please sign in to comment.