#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum NeuronType {
Computational = 0b000,
Sensory = 0b001,
Motor = 0b010,
MemoryReader = 0b011,
MemoryMatcher = 0b100,
Gate = 0b101,
Relay = 0b110,
Oscillator = 0b111,
}
impl NeuronType {
#[inline]
pub fn from_flags(flags: u8) -> Self {
match (flags >> 3) & 0b111 {
0b000 => Self::Computational,
0b001 => Self::Sensory,
0b010 => Self::Motor,
0b011 => Self::MemoryReader,
0b100 => Self::MemoryMatcher,
0b101 => Self::Gate,
0b110 => Self::Relay,
0b111 => Self::Oscillator,
_ => unreachable!(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum NeuronProfile {
RegularSpiking = 0b00,
FastSpiking = 0b01,
IntrinsicBursting = 0b10,
Reserved = 0b11,
}
impl NeuronProfile {
#[inline]
pub fn from_flags(flags: u8) -> Self {
match (flags >> 1) & 0b11 {
0b00 => Self::RegularSpiking,
0b01 => Self::FastSpiking,
0b10 => Self::IntrinsicBursting,
_ => Self::Reserved,
}
}
#[inline]
pub fn default_leak(self) -> u8 {
match self {
Self::RegularSpiking => 4, Self::FastSpiking => 8, Self::IntrinsicBursting => 2, Self::Reserved => 4,
}
}
#[inline]
pub fn default_refractory(self) -> u8 {
match self {
Self::RegularSpiking => 2,
Self::FastSpiking => 1, Self::IntrinsicBursting => 3, Self::Reserved => 2,
}
}
}
pub mod flags {
pub const INHIBITORY_BIT: u8 = 0x01;
#[inline]
pub fn is_inhibitory(f: u8) -> bool {
f & INHIBITORY_BIT != 0
}
#[inline]
pub fn is_excitatory(f: u8) -> bool {
!is_inhibitory(f)
}
#[inline]
pub fn neuron_type(f: u8) -> super::NeuronType {
super::NeuronType::from_flags(f)
}
#[inline]
pub fn encode(inhibitory: bool, profile: super::NeuronProfile) -> u8 {
encode_full(inhibitory, profile, super::NeuronType::Computational)
}
#[inline]
pub fn encode_full(inhibitory: bool, profile: super::NeuronProfile, ntype: super::NeuronType) -> u8 {
let mut f = 0u8;
if inhibitory {
f |= INHIBITORY_BIT;
}
f |= (profile as u8) << 1;
f |= (ntype as u8) << 3;
f
}
}
pub struct NeuronArrays {
pub membrane: Vec<i16>,
pub threshold: Vec<i16>,
pub leak: Vec<u8>,
pub refract_remaining: Vec<u8>,
pub flags: Vec<u8>,
pub trace: Vec<i8>,
pub spike_out: Vec<bool>,
pub binding_slot: Vec<u8>,
}
impl NeuronArrays {
pub fn new(n: u32, n_excitatory: u32, resting: i16, threshold: i16) -> Self {
let n = n as usize;
let n_exc = n_excitatory as usize;
let mut flags_vec = vec![0u8; n];
for i in 0..n {
let inhibitory = i >= n_exc;
let profile = if inhibitory {
NeuronProfile::FastSpiking
} else {
NeuronProfile::RegularSpiking
};
flags_vec[i] = flags::encode(inhibitory, profile);
}
let mut leak_vec = vec![0u8; n];
for i in 0..n {
leak_vec[i] = NeuronProfile::from_flags(flags_vec[i]).default_leak();
}
Self {
membrane: vec![resting; n],
threshold: vec![threshold; n],
leak: leak_vec,
refract_remaining: vec![0; n],
flags: flags_vec,
trace: vec![0i8; n],
spike_out: vec![false; n],
binding_slot: vec![0u8; n],
}
}
#[inline]
pub fn len(&self) -> usize {
self.membrane.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.membrane.is_empty()
}
pub fn extend(&mut self, additional: u32, n_new_excitatory: u32, resting: i16, threshold: i16) {
let n = additional as usize;
let n_exc = n_new_excitatory as usize;
for i in 0..n {
let inhibitory = i >= n_exc;
let profile = if inhibitory {
NeuronProfile::FastSpiking
} else {
NeuronProfile::RegularSpiking
};
let f = flags::encode(inhibitory, profile);
self.membrane.push(resting);
self.threshold.push(threshold);
self.leak.push(NeuronProfile::from_flags(f).default_leak());
self.refract_remaining.push(0);
self.flags.push(f);
self.trace.push(0);
self.spike_out.push(false);
self.binding_slot.push(0);
}
}
pub fn remove_descending(&mut self, indices: &[usize]) -> usize {
let mut removed = 0;
for &idx in indices {
if idx >= self.membrane.len() { continue; }
self.membrane.swap_remove(idx);
self.threshold.swap_remove(idx);
self.leak.swap_remove(idx);
self.refract_remaining.swap_remove(idx);
self.flags.swap_remove(idx);
self.trace.swap_remove(idx);
self.spike_out.swap_remove(idx);
self.binding_slot.swap_remove(idx);
removed += 1;
}
removed
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn neuron_arrays_dale_law() {
let arr = NeuronArrays::new(100, 80, -17920, -14080);
assert_eq!(arr.len(), 100);
for i in 0..80 {
assert!(flags::is_excitatory(arr.flags[i]), "neuron {i} should be excitatory");
}
for i in 80..100 {
assert!(flags::is_inhibitory(arr.flags[i]), "neuron {i} should be inhibitory");
}
}
#[test]
fn profile_encoding() {
let f = flags::encode(false, NeuronProfile::IntrinsicBursting);
assert!(flags::is_excitatory(f));
assert_eq!(NeuronProfile::from_flags(f), NeuronProfile::IntrinsicBursting);
let f2 = flags::encode(true, NeuronProfile::FastSpiking);
assert!(flags::is_inhibitory(f2));
assert_eq!(NeuronProfile::from_flags(f2), NeuronProfile::FastSpiking);
}
#[test]
fn resting_state() {
let arr = NeuronArrays::new(10, 8, -17920, -14080);
for &m in &arr.membrane {
assert_eq!(m, -17920);
}
for &t in &arr.threshold {
assert_eq!(t, -14080);
}
for &r in &arr.refract_remaining {
assert_eq!(r, 0);
}
for &tr in &arr.trace {
assert_eq!(tr, 0);
}
}
}