Skip to content

Commit

Permalink
Linting code
Browse files Browse the repository at this point in the history
  • Loading branch information
monkin77 committed Aug 24, 2024
1 parent 2729cae commit db2fd35
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 121 deletions.
18 changes: 11 additions & 7 deletions src/lava/proc/io/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,9 @@ def validate_channel_config(channel_config: ChannelConfig) -> None:
"to be of type ReceiveNotEmpty. Got "
"<channel_config>.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
Expand All @@ -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
45 changes: 24 additions & 21 deletions src/lava/proc/lif/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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
Expand All @@ -661,15 +665,15 @@ 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"]

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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -722,8 +726,7 @@ def run_spk(self):

self.subthr_dynamics(activation_in=a_in_data)
spike_vector = self.spiking_activation()
""" 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)
120 changes: 73 additions & 47 deletions src/lava/proc/lif/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__(
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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,
*,
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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,
*,
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit db2fd35

Please sign in to comment.