From 165bb5fdf4ced09a6780999b42c633ace8bc5e50 Mon Sep 17 00:00:00 2001 From: Monkin Date: Sat, 24 Aug 2024 13:46:37 +0100 Subject: [PATCH] Linting code --- src/lava/proc/io/utils.py | 18 +++-- src/lava/proc/lif/models.py | 43 ++++++----- src/lava/proc/lif/process.py | 120 ++++++++++++++++++----------- tests/lava/proc/lif/test_models.py | 113 ++++++++++++++++----------- 4 files changed, 175 insertions(+), 119 deletions(-) diff --git a/src/lava/proc/io/utils.py b/src/lava/proc/io/utils.py index 23fc00cab..c2d55ad02 100644 --- a/src/lava/proc/io/utils.py +++ b/src/lava/proc/io/utils.py @@ -280,8 +280,9 @@ def validate_channel_config(channel_config: ChannelConfig) -> None: "to be of type ReceiveNotEmpty. Got " ".receive_not_empty = " f"{channel_config.receive_not_empty}.") - -def convert_to_numpy_array(val, shape, name = "value", verbose=False): + + +def convert_to_numpy_array(val, shape, name="value", verbose=False): """ Converts a given value to a numpy array if it is not already @@ -306,14 +307,17 @@ def convert_to_numpy_array(val, shape, name = "value", verbose=False): if np.isscalar(val): if verbose: print(f"{name} is scalar, converting to numpy array") - # If val is a scalar, create an array filled with that value with shape (n_neurons) + # If val is a scalar, create an array filled with that value + # with shape (n_neurons) val = np.full(shape, val) elif not isinstance(val, np.ndarray): - # If val is not a scalar and not a numpy array, try to convert it to a numpy array + # If val is not a scalar and not a numpy array, try to convert + # it to a numpy array try: val = np.array(val) except Exception as e: - raise ValueError(f"Failed to convert {name} to a numpy array. Please ensure it is either a scalar, list, or numpy array.") from e - - return val + raise ValueError( + f"""Failed to convert {name} to a numpy array. Please ensure it + is either a scalar, list, or numpy array.""") from e + return val diff --git a/src/lava/proc/lif/models.py b/src/lava/proc/lif/models.py index f5aa66590..da09238ac 100644 --- a/src/lava/proc/lif/models.py +++ b/src/lava/proc/lif/models.py @@ -575,7 +575,8 @@ class AbstractPyEILifModelFloat(PyLoihiProcessModel): s_out = None # This will be an OutPort of different LavaPyTypes u_exc: np.ndarray = LavaPyType(np.ndarray, float) u_inh: np.ndarray = LavaPyType(np.ndarray, float) - u: np.ndarray = LavaPyType(np.ndarray, float) # Net current (u_exc + u_inh) + # Net current (u_exc + u_inh) + u: np.ndarray = LavaPyType(np.ndarray, float) v: np.ndarray = LavaPyType(np.ndarray, float) bias_mant: np.ndarray = LavaPyType(np.ndarray, float) bias_exp: np.ndarray = LavaPyType(np.ndarray, float) @@ -594,7 +595,7 @@ def spiking_activation(self): def subthr_dynamics(self, activation_in: np.ndarray): """Common sub-threshold dynamics of current and voltage variables for - all Configurable Time Constants LIF models. + all Configurable Time Constants LIF models. This is where the 'leaky integration' happens. """ # Get the excitatory input from a_in -- Positive values increase u_exc @@ -610,7 +611,8 @@ def subthr_dynamics(self, activation_in: np.ndarray): self.u_inh[:] += inh_a_in # Update the voltage - # Calculate the net current by adding the excitatory and inhibitory currents + # Calculate the net current by adding the + # excitatory and inhibitory currents self.u = self.u_exc + self.u_inh # u_inh is negative self.v[:] = self.v * (1 - self.dv) + self.u + self.bias_mant @@ -632,27 +634,29 @@ def run_spk(self): self.reset_voltage(spike_vector=self.s_out_buff) self.s_out.send(self.s_out_buff) + @implements(proc=EILIF, protocol=LoihiProtocol) @requires(CPU) @tag("floating_pt") class PyEILifFloat(AbstractPyEILifModelFloat): """Implementation of Excitatory/Inhibitory Leaky-Integrate-and-Fire - neural process in floating point precision. This short and simple + neural process in floating point precision. This short and simple ProcessModel can be used for quick algorithmic prototyping, without engaging with the nuances of a fixed point implementation. """ s_out: PyOutPort = LavaPyType(PyOutPort.VEC_DENSE, float) vth: float = LavaPyType(float, float) - + def spiking_activation(self): """Spiking activation function for LIF.""" return self.v > self.vth - + + @implements(proc=EILIFRefractory, protocol=LoihiProtocol) @requires(CPU) @tag("floating_pt") class PyEILifRefractoryFloat(AbstractPyEILifModelFloat): - """Implementation of Excitatory/Inhibitory Refractory + """Implementation of Excitatory/Inhibitory Refractory Leaky-Integrate-and-Fire neural process in floating point precision. This short and simple ProcessModel can be used for quick algorithmic prototyping, without engaging with the nuances of a fixed @@ -661,7 +665,7 @@ class PyEILifRefractoryFloat(AbstractPyEILifModelFloat): s_out: PyOutPort = LavaPyType(PyOutPort.VEC_DENSE, float) vth: float = LavaPyType(float, float) refractory_period_end: np.ndarray = LavaPyType(np.ndarray, int) - + def __init__(self, proc_params): super(PyEILifRefractoryFloat, self).__init__(proc_params) self.refractory_period = proc_params["refractory_period"] @@ -669,7 +673,7 @@ def __init__(self, proc_params): def spiking_activation(self): """Spiking activation function for LIF.""" return self.v > self.vth - + def subthr_dynamics(self, activation_in: np.ndarray): """Sub-threshold dynamics of current and voltage variables for ConfigTimeConstantsLIF @@ -692,18 +696,18 @@ def subthr_dynamics(self, activation_in: np.ndarray): # Check which neurons are not in refractory period non_refractory = self.refractory_period_end < self.time_step - """ non_refrac_idx = np.where(non_refractory == False)[0] - if len(non_refrac_idx) > 0: - print(f"Time step: {self.time_step} has neurons in refractory period -> {non_refrac_idx}") - print(f"{self.u[non_refrac_idx[0]]} {self.v[non_refrac_idx[0]]}") """ # Update the voltage of the non-refractory neurons - # Calculate the net current by adding the excitatory and inhibitory currents + # Calculate the net current by adding the excitatory + # and inhibitory currents self.u = self.u_exc + self.u_inh # u_inh is negative - self.v[non_refractory] = self.v[non_refractory] * (1 - self.dv[non_refractory]) + ( - self.u[non_refractory] + self.bias_mant[non_refractory]) - + self.v[non_refractory] = ( + self.v[non_refractory] * (1 - self.dv[non_refractory]) + ( + self.u[non_refractory] + self.bias_mant[non_refractory] + ) + ) + def process_spikes(self, spike_vector: np.ndarray): """ Set the refractory_period_end for the neurons that spiked and @@ -712,7 +716,7 @@ def process_spikes(self, spike_vector: np.ndarray): self.refractory_period_end[spike_vector] = (self.time_step + self.refractory_period) super().reset_voltage(spike_vector) - + def run_spk(self): """The run function that performs the actual computation during execution orchestrated by a PyLoihiProcessModel using the @@ -725,5 +729,6 @@ def run_spk(self): """ if np.max(spike_vector) > 0: print(f"Time step: {self.time_step} has a neuron spike.") """ - self.process_spikes(spike_vector=spike_vector) # Reset voltage of spiked neurons to 0 and update refractory period + # Reset voltage of spiked neurons to 0 and update refractory period + self.process_spikes(spike_vector=spike_vector) self.s_out.send(spike_vector) diff --git a/src/lava/proc/lif/process.py b/src/lava/proc/lif/process.py index 24507851d..43a40e084 100644 --- a/src/lava/proc/lif/process.py +++ b/src/lava/proc/lif/process.py @@ -420,7 +420,7 @@ def __init__( class AbstractEILIF(AbstractProcess): - """Abstract class for variables common to all neurons with Excitatory/Inhibitory + """Abstract class for variables common to all neurons with Excitatory/Inhibitory leaky integrator dynamics and configurable time constants""" def __init__( @@ -458,26 +458,37 @@ def __init__( self.s_out = OutPort(shape=shape) self.u_exc = Var(shape=shape, init=u_exc) self.u_inh = Var(shape=shape, init=u_inh) - self.u = Var(shape=shape, init=u_exc + u_inh) # neuron total current (u_inh is negative) + # neuron total current (u_inh is negative) + self.u = Var(shape=shape, init=u_exc + u_inh) self.v = Var(shape=shape, init=v) - self.du_exc = Var(shape=shape, init=du_exc) # Shape of du_exc must match the shape of the neurons - self.du_inh = Var(shape=shape, init=du_inh) # Shape of du_inh must match the shape of the neurons - self.dv = Var(shape=shape, init=dv) # Shape of dv must match the shape of the neurons + # Shape of du_exc must match the shape of the neurons + self.du_exc = Var(shape=shape, init=du_exc) + # Shape of du_inh must match the shape of the neurons + self.du_inh = Var(shape=shape, init=du_inh) + # Shape of dv must match the shape of the neurons + self.dv = Var(shape=shape, init=dv) self.bias_exp = Var(shape=shape, init=bias_exp) self.bias_mant = Var(shape=shape, init=bias_mant) class EILIF(AbstractEILIF): - """Exctitatory/Inhibitory Leaky-Integrate-and-Fire (LIF) neural Process. - 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. + """Excitatory/Inhibitory Leaky-Integrate-and-Fire (LIF) neural Process. + 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. LIF dynamics abstracts to: - u_exc[t] = u_exc[t-1] * (1-du_exc) + a_in (excitatory spike) # neuron excitatory current - u_inh[t] = u_inh[t-1] * (1-du_inh) + a_in (inhibitory spike) # neuron inhibitory current + # neuron excitatory current + u_exc[t] = u_exc[t-1] * (1-du_exc) + a_in (excitatory spike) + # neuron inhibitory current + u_inh[t] = u_inh[t-1] * (1-du_inh) + a_in (inhibitory spike) + + # neuron total current (u_inh[t] is negative) + u[t] = u_exc[t] + u_inh[t] - u[t] = u_exc[t] + u_inh[t] # neuron total current (u_inh[t] is negative) v[t] = v[t-1] * (1-dv) + u[t] + bias # neuron voltage s_out = v[t] > vth # spike if threshold is exceeded v[t] = 0 # reset at spike @@ -493,17 +504,17 @@ class EILIF(AbstractEILIF): v : float, list, numpy.ndarray, optional Initial value of the neurons' voltage (membrane potential). du_exc : float, list, numpy.ndarray, optional - Inverse of decay time-constant for excitatory current decay. This can be a scalar, list, - or numpy array. Anyhow, it will be converted to a np array representing the - time-constants of each neuron. + Inverse of decay time-constant for excitatory current decay. + This can be a scalar, list, or numpy array. Anyhow, it will be converted + to a np array representing the time-constants of each neuron. du_inh : float, list, numpy.ndarray, optional - Inverse of decay time-constant for inhibitory current decay. This can be a scalar, list, - or numpy array. Anyhow, it will be converted to a np array representing the - time-constants of each neuron. + Inverse of decay time-constant for inhibitory current decay. + This can be a scalar, list, or numpy array. Anyhow, it will be converted + to a np array representing the time-constants of each neuron. dv : float, list, numpy.ndarray, optional - Inverse of decay time-constant for voltage decay. This can be a scalar, list, - or numpy array. Anyhow, it will be converted to a np array representing the - time-constants of each neuron. + Inverse of decay time-constant for voltage decay. This can be a scalar, + list, or numpy array. Anyhow, it will be converted to a np array + representing the time-constants of each neuron. bias_mant : float, list, numpy.ndarray, optional Mantissa part of neuron bias. bias_exp : float, list, numpy.ndarray, optional @@ -517,9 +528,11 @@ class EILIF(AbstractEILIF): Example ------- >>> ei_lif = EILIF(shape=(200, 15), du_exc=0.1, du_inh=0.2, dv=5) - This will create 200x15 EILIF neurons that all have the same excitatory and - inhibitory current decays (0.1 and 0.2, respectively) and voltage decay of 5. + This will create 200x15 EILIF neurons that all have the same excitatory + and inhibitory current decays (0.1 and 0.2, respectively) + and voltage decay of 5. """ + def __init__( self, *, @@ -538,12 +551,14 @@ def __init__( 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=verbose) - du_inh = convert_to_numpy_array(du_inh, shape, "du_inh", verbose=verbose) + # 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=verbose) + du_inh = convert_to_numpy_array( + du_inh, shape, "du_inh", verbose=verbose) dv = convert_to_numpy_array(dv, shape, "dv", verbose=verbose) - + super().__init__( shape=shape, u_exc=u_exc, @@ -563,17 +578,26 @@ def __init__( # Add the vth variable to the process self.vth = Var(shape=(1,), init=vth) + class EILIFRefractory(EILIF): - """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. + """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. LIF dynamics abstracts to: - u_exc[t] = u_exc[t-1] * (1-du_exc) + a_in (excitatory spike) # neuron excitatory current - u_inh[t] = u_inh[t-1] * (1-du_inh) + a_in (inhibitory spike) # neuron inhibitory current + # neuron excitatory current + u_exc[t] = u_exc[t-1] * (1-du_exc) + a_in (excitatory spike) + # neuron inhibitory current + u_inh[t] = u_inh[t-1] * (1-du_inh) + a_in (inhibitory spike) + + # neuron total current (u_inh[t] is negative) + u[t] = u_exc[t] + u_inh[t] - u[t] = u_exc[t] + u_inh[t] # neuron total current (u_inh[t] is negative) v[t] = v[t-1] * (1-dv) + u[t] + bias # neuron voltage s_out = v[t] > vth # spike if threshold is exceeded v[t] = 0 # reset at spike @@ -589,17 +613,17 @@ class EILIFRefractory(EILIF): v : float, list, numpy.ndarray, optional Initial value of the neurons' voltage (membrane potential). du_exc : float, list, numpy.ndarray, optional - Inverse of decay time-constant for excitatory current decay. This can be a scalar, list, - or numpy array. Anyhow, it will be converted to a np array representing the - time-constants of each neuron. + Inverse of decay time-constant for excitatory current decay. + This can be a scalar, list, or numpy array. Anyhow, it will be converted + to a np array representing the time-constants of each neuron. du_inh : float, list, numpy.ndarray, optional - Inverse of decay time-constant for inhibitory current decay. This can be a scalar, list, - or numpy array. Anyhow, it will be converted to a np array representing the - time-constants of each neuron. + Inverse of decay time-constant for inhibitory current decay. + This can be a scalar, list, or numpy array. Anyhow, it will be converted + to a np array representing the time-constants of each neuron. dv : float, list, numpy.ndarray, optional - Inverse of decay time-constant for voltage decay. This can be a scalar, list, - or numpy array. Anyhow, it will be converted to a np array representing the - time-constants of each neuron. + Inverse of decay time-constant for voltage decay. This can be a scalar, + list, or numpy array. Anyhow, it will be converted to a np array + representing the time-constants of each neuron. bias_mant : float, list, numpy.ndarray, optional Mantissa part of neuron bias. bias_exp : float, list, numpy.ndarray, optional @@ -614,11 +638,13 @@ class EILIFRefractory(EILIF): Example ------- - >>> refrac_ei_lif = EILIFRefractory(shape=(200, 15), du_exc=0.1, du_inh=0.2, dv=5) - This will create 200x15 EILIF neurons that all have the same excitatory and + >>> refrac_ei_lif = EILIFRefractory(shape=(200, 15), du_exc=0.1, + du_inh=0.2, dv=5) + This will create 200x15 EILIF neurons that all have the same excitatory and inhibitory current decays (0.1 and 0.2, respectively), voltage decay of 5. and refractory period of 1 timestep. """ + def __init__( self, *, @@ -661,7 +687,7 @@ def __init__( # Check if the refractory period is a float if isinstance(refractory_period, float): if verbose: - print("Refractory period must be an integer. Converting to integer...") + print("Converting the Refractory period to integer...") refractory_period = int(refractory_period) self.proc_params["refractory_period"] = refractory_period diff --git a/tests/lava/proc/lif/test_models.py b/tests/lava/proc/lif/test_models.py index c83000db8..3597f2fbb 100644 --- a/tests/lava/proc/lif/test_models.py +++ b/tests/lava/proc/lif/test_models.py @@ -17,13 +17,16 @@ 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, EILIFRefractory +from lava.proc.lif.process import ( + LIF, LIFReset, TernaryLIF, LIFRefractory, EILIF, EILIFRefractory +) from lava.proc import io class LifRunConfig(RunConfig): """Run configuration selects appropriate LIF ProcessModel based on tag: floating point precision or Loihi bit-accurate fixed point precision""" + def __init__(self, custom_sync_domains=None, select_tag='fixed_pt'): super().__init__(custom_sync_domains=custom_sync_domains) self.select_tag = select_tag @@ -46,6 +49,7 @@ class VecSendProcess(AbstractProcess): send_at_times: np.ndarray, vector bools. Send the `vec_to_send` at times when there is a True """ + def __init__(self, **kwargs): super().__init__() shape = kwargs.pop("shape", (1,)) @@ -67,6 +71,7 @@ class VecRecvProcess(AbstractProcess): ---------- shape: tuple, shape of the process """ + def __init__(self, **kwargs): super().__init__() shape = kwargs.get("shape", (1,)) @@ -143,6 +148,7 @@ def run_spk(self): class TestLIFProcessModelsFloat(unittest.TestCase): """Tests for floating point ProcessModels of LIF""" + def test_float_pm_no_decay(self): """ Tests floating point LIF ProcessModel with no current or voltage @@ -251,13 +257,15 @@ def test_float_pm_impulse_dv(self): lif.stop() # Gold standard for testing: voltage decay of 0.5 should integrate # the voltage from 128. to 255., with steps of 64., 32., 16., etc. - expected_v_timeseries = [128., 192., 224., 240., 248., 252., 254., 255.] + expected_v_timeseries = [128., 192., + 224., 240., 248., 252., 254., 255.] self.assertListEqual(expected_v_timeseries, lif_v) class TestLIFProcessModelsFixed(unittest.TestCase): """Tests for fixed point, ProcessModels of LIF, which are bit-accurate with Loihi hardware""" + def test_bitacc_pm_no_decay(self): """ Tests fixed point LIF ProcessModel (bit-accurate @@ -418,6 +426,7 @@ def test_bitacc_pm_scaling_of_bias(self): class TestTLIFProcessModelsFloat(unittest.TestCase): """Tests for ternary LIF floating point neuron model""" + def test_float_pm_neg_no_decay_1(self): """Tests floating point ternary LIF model with negative bias driving a neuron without any decay of current and voltage states.""" @@ -566,6 +575,7 @@ def test_float_pm_neg_impulse_dv(self): class TestTLIFProcessModelsFixed(unittest.TestCase): """Tests for ternary LIF fixed point neuron model""" + def test_fixed_pm_neg_no_decay_1(self): """Tests fixed point ProcessModel for ternary LIF neurons without any current or voltage decay, solely driven by (negative) bias""" @@ -861,6 +871,7 @@ def test_float_model(self): assert_almost_equal(v, v_expected) + class TestEILIFFloat(unittest.TestCase): """Test EILIF process model""" @@ -872,18 +883,18 @@ def test_no_decays(self): # Two neurons with different biases # No Input current provided to make the voltage dependent on the bias ei_lif = EILIF(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,) + 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,) 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")) + run_cfg=Loihi2SimCfg(select_tag="floating_pt")) v = v_logger.data.get() ei_lif.stop() @@ -905,20 +916,23 @@ def test_different_decays(self): # Two neurons with different biases # No Input current provided to make the voltage dependent on the bias ei_lif = EILIF(shape=(num_neurons,), - du_exc=du_exc_arr, - du_inh=du_inh_arr, - vth=6,) - + du_exc=du_exc_arr, + du_inh=du_inh_arr, + vth=6,) + # 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) - + 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) - - + 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) @@ -927,17 +941,19 @@ def test_different_decays(self): v_logger.connect_var(ei_lif.v) ei_lif.run(condition=RunSteps(num_steps), - run_cfg=Loihi2SimCfg(select_tag="floating_pt")) + 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, 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) + [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""" @@ -950,19 +966,19 @@ def test_no_decays(self): # 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) + 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")) + run_cfg=Loihi2SimCfg(select_tag="floating_pt")) v = v_logger.data.get() ei_lif.stop() @@ -982,23 +998,27 @@ def test_different_decays(self): 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. + # 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) - + 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) - + 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) - - + 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) @@ -1007,13 +1027,14 @@ def test_different_decays(self): v_logger.connect_var(ei_lif.v) ei_lif.run(condition=RunSteps(num_steps), - run_cfg=Loihi2SimCfg(select_tag="floating_pt")) + 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) + [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) \ No newline at end of file + assert_almost_equal(v, v_expected, decimal=2)