use crate::neuron_id_manager::{AllocationStats, NeuronIdManager};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy)]
pub struct MemoryNeuronLifecycleConfig {
pub initial_lifespan: u32,
pub lifespan_growth_rate: f32,
pub longterm_threshold: u32,
pub max_reactivations: u32,
}
impl Default for MemoryNeuronLifecycleConfig {
fn default() -> Self {
Self {
initial_lifespan: 20,
lifespan_growth_rate: 3.0,
longterm_threshold: 100,
max_reactivations: 1000,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MemoryNeuronDetail {
pub neuron_id: u32,
pub cortical_area_idx: u32,
pub pattern_hash: Option<u64>,
pub is_longterm_memory: bool,
pub is_active: bool,
pub lifespan_current: u32,
pub lifespan_initial: u32,
pub lifespan_growth_rate: f32,
pub creation_burst: u64,
pub last_activation_burst: u64,
pub activation_count: u32,
}
#[derive(Debug, Clone, Default)]
pub struct MemoryNeuronStats {
pub total_capacity: usize,
pub active_neurons: usize,
pub longterm_neurons: usize,
pub dead_neurons: usize,
pub reusable_indices: usize,
pub memory_usage_bytes: usize,
pub avg_lifespan: f64,
pub avg_activation_count: f64,
}
pub struct MemoryNeuronArray {
capacity: usize,
neuron_ids: Vec<u32>,
cortical_area_ids: Vec<u32>,
is_active: Vec<bool>,
lifespan_current: Vec<u32>,
lifespan_initial: Vec<u32>,
lifespan_growth_rate: Vec<f32>,
is_longterm_memory: Vec<bool>,
creation_burst: Vec<u64>,
last_activation_burst: Vec<u64>,
activation_count: Vec<u32>,
pattern_hash_to_index: HashMap<u64, usize>,
index_to_pattern_hash: HashMap<usize, u64>,
next_available_index: usize,
reusable_indices: HashSet<usize>,
area_neuron_indices: HashMap<u32, HashSet<usize>>,
id_manager: NeuronIdManager,
}
impl MemoryNeuronArray {
pub fn new(capacity: usize) -> Self {
Self {
capacity,
neuron_ids: vec![0; capacity],
cortical_area_ids: vec![0; capacity],
is_active: vec![false; capacity],
lifespan_current: vec![0; capacity],
lifespan_initial: vec![0; capacity],
lifespan_growth_rate: vec![0.0; capacity],
is_longterm_memory: vec![false; capacity],
creation_burst: vec![0; capacity],
last_activation_burst: vec![0; capacity],
activation_count: vec![0; capacity],
pattern_hash_to_index: HashMap::new(),
index_to_pattern_hash: HashMap::new(),
next_available_index: 0,
reusable_indices: HashSet::new(),
area_neuron_indices: HashMap::new(),
id_manager: NeuronIdManager::new(),
}
}
pub fn create_memory_neuron(
&mut self,
pattern_hash: u64,
cortical_area_id: u32,
current_burst: u64,
config: &MemoryNeuronLifecycleConfig,
) -> Option<usize> {
if let Some(&existing_idx) = self.pattern_hash_to_index.get(&pattern_hash) {
if self.is_active[existing_idx] {
return self.reactivate_memory_neuron_internal(existing_idx, current_burst);
}
}
let neuron_idx = self.get_available_index_internal()?;
let neuron_id = self.id_manager.allocate_memory_neuron_id()?;
self.neuron_ids[neuron_idx] = neuron_id;
self.cortical_area_ids[neuron_idx] = cortical_area_id;
self.is_active[neuron_idx] = true;
self.lifespan_current[neuron_idx] = config.initial_lifespan;
self.lifespan_initial[neuron_idx] = config.initial_lifespan;
self.lifespan_growth_rate[neuron_idx] = config.lifespan_growth_rate;
self.is_longterm_memory[neuron_idx] = false;
self.creation_burst[neuron_idx] = current_burst;
self.last_activation_burst[neuron_idx] = current_burst;
self.activation_count[neuron_idx] = 1;
self.pattern_hash_to_index.insert(pattern_hash, neuron_idx);
self.index_to_pattern_hash.insert(neuron_idx, pattern_hash);
self.area_neuron_indices
.entry(cortical_area_id)
.or_default()
.insert(neuron_idx);
Some(neuron_idx)
}
pub fn reactivate_memory_neuron(&mut self, neuron_idx: usize, current_burst: u64) -> bool {
self.reactivate_memory_neuron_internal(neuron_idx, current_burst)
.is_some()
}
fn reactivate_memory_neuron_internal(
&mut self,
neuron_idx: usize,
current_burst: u64,
) -> Option<usize> {
if !self.is_valid_index(neuron_idx) || !self.is_active[neuron_idx] {
return None;
}
self.last_activation_burst[neuron_idx] = current_burst;
self.activation_count[neuron_idx] += 1;
if !self.is_longterm_memory[neuron_idx] {
let current_lifespan = self.lifespan_current[neuron_idx];
let growth = self.lifespan_growth_rate[neuron_idx] as u32;
self.lifespan_current[neuron_idx] = current_lifespan.saturating_add(growth);
}
Some(neuron_idx)
}
pub fn age_memory_neurons(&mut self, _current_burst: u64) -> Vec<usize> {
let n = self.next_available_index;
if n == 0 {
return Vec::new();
}
let mut died_indices = Vec::new();
for i in 0..n {
if self.is_active[i] && !self.is_longterm_memory[i] && self.lifespan_current[i] > 0 {
self.lifespan_current[i] -= 1;
if self.lifespan_current[i] == 0 {
self.is_active[i] = false;
died_indices.push(i);
}
}
}
for &i in &died_indices {
self.cleanup_dead_neuron_internal(i);
}
died_indices
}
pub fn check_longterm_conversion(&mut self, longterm_threshold: u32) -> Vec<usize> {
let n = self.next_available_index;
if n == 0 {
return Vec::new();
}
let mut converted_indices = Vec::new();
for i in 0..n {
if self.is_active[i]
&& !self.is_longterm_memory[i]
&& self.lifespan_current[i] >= longterm_threshold
{
self.is_longterm_memory[i] = true;
converted_indices.push(i);
}
}
converted_indices
}
pub fn check_longterm_conversion_by_area(
&mut self,
lifecycle_configs: &HashMap<u32, MemoryNeuronLifecycleConfig>,
default_threshold: u32,
) -> Vec<usize> {
let n = self.next_available_index;
if n == 0 {
return Vec::new();
}
let area_indices: Vec<(u32, Vec<usize>)> = self
.area_neuron_indices
.iter()
.map(|(&area, indices)| (area, indices.iter().copied().collect()))
.collect();
let mut converted_indices = Vec::new();
for (area_id, indices) in area_indices {
let threshold = lifecycle_configs
.get(&area_id)
.map(|config| config.longterm_threshold)
.unwrap_or(default_threshold);
for neuron_idx in indices {
if self.is_active[neuron_idx]
&& !self.is_longterm_memory[neuron_idx]
&& self.lifespan_current[neuron_idx] >= threshold
{
self.is_longterm_memory[neuron_idx] = true;
converted_indices.push(neuron_idx);
}
}
}
converted_indices
}
pub fn get_active_neurons_by_area(&self, cortical_area_id: u32) -> Vec<u32> {
if let Some(indices) = self.area_neuron_indices.get(&cortical_area_id) {
indices
.iter()
.filter(|&&idx| self.is_valid_index(idx) && self.is_active[idx])
.map(|&idx| self.neuron_ids[idx])
.collect()
} else {
Vec::new()
}
}
pub fn count_short_term_in_area(&self, cortical_area_id: u32) -> usize {
self.area_neuron_indices
.get(&cortical_area_id)
.map(|indices| {
indices
.iter()
.filter(|&&idx| {
self.is_valid_index(idx)
&& self.is_active[idx]
&& !self.is_longterm_memory[idx]
})
.count()
})
.unwrap_or(0)
}
pub fn count_long_term_in_area(&self, cortical_area_id: u32) -> usize {
self.area_neuron_indices
.get(&cortical_area_id)
.map(|indices| {
indices
.iter()
.filter(|&&idx| {
self.is_valid_index(idx)
&& self.is_active[idx]
&& self.is_longterm_memory[idx]
})
.count()
})
.unwrap_or(0)
}
pub fn paginated_neuron_ids_in_area(
&self,
cortical_area_id: u32,
offset: usize,
limit: usize,
) -> (Vec<u32>, usize) {
let mut ids = self.get_active_neurons_by_area(cortical_area_id);
ids.sort_unstable();
let total = ids.len();
let page = ids.into_iter().skip(offset).take(limit).collect();
(page, total)
}
fn find_neuron_index_by_global_id(&self, neuron_id: u32) -> Option<usize> {
(0..self.next_available_index).find(|&i| self.neuron_ids[i] == neuron_id)
}
pub fn get_memory_neuron_detail(&self, neuron_id: u32) -> Option<MemoryNeuronDetail> {
let idx = self.find_neuron_index_by_global_id(neuron_id)?;
if !self.is_valid_index(idx) || !self.is_active[idx] {
return None;
}
Some(MemoryNeuronDetail {
neuron_id: self.neuron_ids[idx],
cortical_area_idx: self.cortical_area_ids[idx],
pattern_hash: self.index_to_pattern_hash.get(&idx).copied(),
is_longterm_memory: self.is_longterm_memory[idx],
is_active: self.is_active[idx],
lifespan_current: self.lifespan_current[idx],
lifespan_initial: self.lifespan_initial[idx],
lifespan_growth_rate: self.lifespan_growth_rate[idx],
creation_burst: self.creation_burst[idx],
last_activation_burst: self.last_activation_burst[idx],
activation_count: self.activation_count[idx],
})
}
pub fn find_neuron_by_pattern(&self, pattern_hash: &u64) -> Option<usize> {
self.pattern_hash_to_index
.get(pattern_hash)
.copied()
.filter(|&idx| self.is_valid_index(idx) && self.is_active[idx])
}
pub fn get_neuron_id(&self, neuron_idx: usize) -> Option<u32> {
if self.is_valid_index(neuron_idx) {
Some(self.neuron_ids[neuron_idx])
} else {
None
}
}
pub fn get_cortical_area_id(&self, neuron_idx: usize) -> Option<u32> {
if self.is_valid_index(neuron_idx) {
Some(self.cortical_area_ids[neuron_idx])
} else {
None
}
}
pub fn get_pattern_hash(&self, neuron_idx: usize) -> Option<u64> {
self.index_to_pattern_hash.get(&neuron_idx).copied()
}
pub fn get_stats(&self) -> MemoryNeuronStats {
let n = self.next_available_index;
if n == 0 {
return MemoryNeuronStats {
total_capacity: self.capacity,
..Default::default()
};
}
let active_count = self.is_active[..n].iter().filter(|&&x| x).count();
let longterm_count = (0..n)
.filter(|&i| self.is_active[i] && self.is_longterm_memory[i])
.count();
let dead_count = n - active_count;
let (avg_lifespan, avg_activation_count) = if active_count > 0 {
let total_lifespan: u32 = (0..n)
.filter(|&i| self.is_active[i])
.map(|i| self.lifespan_current[i])
.sum();
let total_activations: u32 = (0..n)
.filter(|&i| self.is_active[i])
.map(|i| self.activation_count[i])
.sum();
(
total_lifespan as f64 / active_count as f64,
total_activations as f64 / active_count as f64,
)
} else {
(0.0, 0.0)
};
let memory_usage = self.capacity * (
std::mem::size_of::<u32>() * 4 + std::mem::size_of::<f32>() + std::mem::size_of::<u64>() * 2 + std::mem::size_of::<bool>() * 2 ) + self.pattern_hash_to_index.len() * (8 + 8) + self.area_neuron_indices.len() * 64;
MemoryNeuronStats {
total_capacity: self.capacity,
active_neurons: active_count,
longterm_neurons: longterm_count,
dead_neurons: dead_count,
reusable_indices: self.reusable_indices.len(),
memory_usage_bytes: memory_usage,
avg_lifespan,
avg_activation_count,
}
}
pub fn get_id_allocation_stats(&self) -> AllocationStats {
self.id_manager.get_allocation_stats()
}
fn get_available_index_internal(&mut self) -> Option<usize> {
if let Some(&idx) = self.reusable_indices.iter().next() {
self.reusable_indices.remove(&idx);
return Some(idx);
}
if self.next_available_index < self.capacity {
let idx = self.next_available_index;
self.next_available_index += 1;
Some(idx)
} else {
None
}
}
fn cleanup_dead_neuron_internal(&mut self, neuron_idx: usize) {
let neuron_id = self.neuron_ids[neuron_idx];
self.id_manager.deallocate_memory_neuron_id(neuron_id);
if let Some(pattern_hash) = self.index_to_pattern_hash.remove(&neuron_idx) {
self.pattern_hash_to_index.remove(&pattern_hash);
}
let area_id = self.cortical_area_ids[neuron_idx];
if let Some(indices) = self.area_neuron_indices.get_mut(&area_id) {
indices.remove(&neuron_idx);
}
self.reusable_indices.insert(neuron_idx);
}
fn is_valid_index(&self, neuron_idx: usize) -> bool {
neuron_idx < self.next_available_index
}
pub fn reset_cortical_area(&mut self, cortical_area_id: u32) -> usize {
let Some(neuron_indices) = self.area_neuron_indices.get(&cortical_area_id) else {
return 0;
};
let indices_to_reset: Vec<usize> = neuron_indices.iter().copied().collect();
let mut reset_count = 0;
for neuron_idx in indices_to_reset {
if !self.is_valid_index(neuron_idx) {
continue;
}
self.is_active[neuron_idx] = false;
if let Some(pattern_hash) = self.index_to_pattern_hash.remove(&neuron_idx) {
self.pattern_hash_to_index.remove(&pattern_hash);
}
self.lifespan_current[neuron_idx] = 0;
self.activation_count[neuron_idx] = 0;
self.reusable_indices.insert(neuron_idx);
reset_count += 1;
}
self.area_neuron_indices.remove(&cortical_area_id);
reset_count
}
pub fn reset(&mut self) {
self.neuron_ids.fill(0);
self.cortical_area_ids.fill(0);
self.is_active.fill(false);
self.lifespan_current.fill(0);
self.lifespan_initial.fill(0);
self.lifespan_growth_rate.fill(0.0);
self.is_longterm_memory.fill(false);
self.creation_burst.fill(0);
self.last_activation_burst.fill(0);
self.activation_count.fill(0);
self.pattern_hash_to_index.clear();
self.index_to_pattern_hash.clear();
self.area_neuron_indices.clear();
self.next_available_index = 0;
self.reusable_indices.clear();
self.id_manager.reset();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lifecycle_config_default() {
let config = MemoryNeuronLifecycleConfig::default();
assert_eq!(config.initial_lifespan, 20);
assert_eq!(config.lifespan_growth_rate, 3.0);
assert_eq!(config.longterm_threshold, 100);
assert_eq!(config.max_reactivations, 1000);
}
#[test]
fn test_create_memory_neuron() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
let pattern_hash = 0x0101010101010101u64;
let neuron_idx = array.create_memory_neuron(pattern_hash, 100, 0, &config);
assert!(neuron_idx.is_some());
let idx = neuron_idx.unwrap();
assert!(array.is_active[idx]);
assert_eq!(array.cortical_area_ids[idx], 100);
assert_eq!(array.lifespan_current[idx], config.initial_lifespan);
assert_eq!(array.activation_count[idx], 1);
assert_eq!(array.creation_burst[idx], 0);
}
#[test]
fn test_create_duplicate_pattern() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
let pattern_hash = 0x0101010101010101u64;
let idx1 = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let idx2 = array
.create_memory_neuron(pattern_hash, 100, 1, &config)
.unwrap();
assert_eq!(idx1, idx2);
assert_eq!(array.activation_count[idx1], 2); }
#[test]
fn test_multiple_neurons() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
let mut neurons = Vec::new();
for i in 0..10 {
let pattern_hash = i as u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
neurons.push(idx);
}
assert_eq!(neurons.len(), 10);
assert_eq!(array.next_available_index, 10);
let stats = array.get_stats();
assert_eq!(stats.active_neurons, 10);
}
#[test]
fn test_reactivate_memory_neuron() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let initial_count = array.activation_count[idx];
let initial_lifespan = array.lifespan_current[idx];
assert!(array.reactivate_memory_neuron(idx, 1));
assert_eq!(array.activation_count[idx], initial_count + 1);
assert_eq!(array.last_activation_burst[idx], 1);
let expected_lifespan = initial_lifespan + config.lifespan_growth_rate as u32;
assert_eq!(array.lifespan_current[idx], expected_lifespan);
}
#[test]
fn test_reactivate_invalid_neuron() {
let mut array = MemoryNeuronArray::new(1000);
assert!(!array.reactivate_memory_neuron(0, 1));
assert!(!array.reactivate_memory_neuron(999, 1));
}
#[test]
fn test_age_memory_neurons() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig {
initial_lifespan: 2,
..Default::default()
};
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let died = array.age_memory_neurons(1);
assert!(died.is_empty());
assert_eq!(array.lifespan_current[idx], 1);
let died = array.age_memory_neurons(2);
assert_eq!(died.len(), 1);
assert_eq!(died[0], idx);
assert!(!array.is_active[idx]);
let found = array.find_neuron_by_pattern(&pattern_hash);
assert!(found.is_none());
}
#[test]
fn test_age_multiple_neurons() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig {
initial_lifespan: 5,
..Default::default()
};
let mut neurons = Vec::new();
for i in 0..10 {
let pattern_hash = i as u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
neurons.push(idx);
}
for burst in 1..=5 {
let died = array.age_memory_neurons(burst);
if burst < 5 {
assert_eq!(died.len(), 0);
} else {
assert_eq!(died.len(), 10);
}
}
let stats = array.get_stats();
assert_eq!(stats.active_neurons, 0);
assert_eq!(stats.dead_neurons, 10);
}
#[test]
fn test_longterm_memory_no_aging() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig {
initial_lifespan: 100,
..Default::default()
};
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let converted = array.check_longterm_conversion(100);
assert_eq!(converted.len(), 1);
assert!(array.is_longterm_memory[idx]);
let initial_lifespan = array.lifespan_current[idx];
for burst in 1..=50 {
array.age_memory_neurons(burst);
}
assert!(array.is_active[idx]);
assert_eq!(array.lifespan_current[idx], initial_lifespan); }
#[test]
fn test_longterm_conversion() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig {
initial_lifespan: 100,
..Default::default()
};
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let converted = array.check_longterm_conversion(100);
assert_eq!(converted.len(), 1);
assert_eq!(converted[0], idx);
assert!(array.is_longterm_memory[idx]);
let converted2 = array.check_longterm_conversion(100);
assert_eq!(converted2.len(), 0);
}
#[test]
fn test_longterm_conversion_threshold() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig {
initial_lifespan: 50,
..Default::default()
};
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let converted = array.check_longterm_conversion(100);
assert_eq!(converted.len(), 0);
assert!(!array.is_longterm_memory[idx]);
for burst in 1..=20 {
array.reactivate_memory_neuron(idx, burst);
}
let converted = array.check_longterm_conversion(100);
assert_eq!(converted.len(), 1);
assert!(array.is_longterm_memory[idx]);
}
#[test]
fn test_find_neuron_by_pattern() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let found = array.find_neuron_by_pattern(&pattern_hash);
assert_eq!(found, Some(idx));
let pattern_hash2 = 0x0202020202020202u64;
let found2 = array.find_neuron_by_pattern(&pattern_hash2);
assert_eq!(found2, None);
}
#[test]
fn test_get_active_neurons_by_area() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
for area in [100, 200] {
for i in 0..5 {
let pattern_hash = ((area as u64) << 32) | (i as u64);
array.create_memory_neuron(pattern_hash, area, 0, &config);
}
}
let area100_neurons = array.get_active_neurons_by_area(100);
let area200_neurons = array.get_active_neurons_by_area(200);
assert_eq!(area100_neurons.len(), 5);
assert_eq!(area200_neurons.len(), 5);
let area999_neurons = array.get_active_neurons_by_area(999);
assert_eq!(area999_neurons.len(), 0);
}
#[test]
fn test_get_neuron_id() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
let neuron_id = array.get_neuron_id(idx);
assert!(neuron_id.is_some());
let invalid_id = array.get_neuron_id(999);
assert!(invalid_id.is_none());
}
#[test]
fn test_index_reuse() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig {
initial_lifespan: 1,
..Default::default()
};
let pattern_hash = 0x0101010101010101u64;
let idx1 = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
assert_eq!(idx1, 0);
array.age_memory_neurons(1);
assert!(!array.is_active[idx1]);
let pattern_hash2 = 0x0202020202020202u64;
let idx2 = array
.create_memory_neuron(pattern_hash2, 100, 2, &config)
.unwrap();
assert_eq!(idx2, 0); assert_eq!(array.next_available_index, 1); }
#[test]
fn test_get_stats() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
for i in 0..10 {
let pattern_hash = i as u64;
array.create_memory_neuron(pattern_hash, 100, 0, &config);
}
let stats = array.get_stats();
assert_eq!(stats.total_capacity, 1000);
assert_eq!(stats.active_neurons, 10);
assert_eq!(stats.longterm_neurons, 0);
assert_eq!(stats.dead_neurons, 0);
assert!(stats.avg_lifespan > 0.0);
assert!(stats.avg_activation_count >= 1.0);
assert!(stats.memory_usage_bytes > 0);
}
#[test]
fn test_capacity_exhaustion() {
let mut array = MemoryNeuronArray::new(5);
let config = MemoryNeuronLifecycleConfig::default();
for i in 0..5 {
let pattern_hash = i as u64;
let idx = array.create_memory_neuron(pattern_hash, 100, 0, &config);
assert!(idx.is_some());
}
let pattern_hash = 0x6363636363636363u64;
let idx = array.create_memory_neuron(pattern_hash, 100, 0, &config);
assert!(idx.is_none());
}
#[test]
fn test_reset_cortical_area() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
let pattern1 = 0x0101010101010101u64;
let pattern2 = 0x0202020202020202u64;
array.create_memory_neuron(pattern1, 5, 0, &config);
array.create_memory_neuron(pattern2, 5, 0, &config);
let pattern3 = 0x0303030303030303u64;
array.create_memory_neuron(pattern3, 6, 0, &config);
assert_eq!(array.get_active_neurons_by_area(5).len(), 2);
assert_eq!(array.get_active_neurons_by_area(6).len(), 1);
let reset_count = array.reset_cortical_area(5);
assert_eq!(reset_count, 2);
assert_eq!(array.get_active_neurons_by_area(5).len(), 0);
assert_eq!(array.get_active_neurons_by_area(6).len(), 1);
assert!(!array.pattern_hash_to_index.contains_key(&pattern1));
assert!(!array.pattern_hash_to_index.contains_key(&pattern2));
assert!(array.pattern_hash_to_index.contains_key(&pattern3));
}
#[test]
fn test_reset() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
for i in 0..5 {
let pattern_hash = i as u64;
array.create_memory_neuron(pattern_hash, 100, 0, &config);
}
assert_eq!(array.next_available_index, 5);
array.reset();
assert_eq!(array.next_available_index, 0);
let stats = array.get_stats();
assert_eq!(stats.active_neurons, 0);
}
#[test]
fn test_lifespan_growth_on_reactivation() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig {
initial_lifespan: 10,
lifespan_growth_rate: 5.0,
longterm_threshold: 100,
max_reactivations: 1000,
};
let pattern_hash = 0x0101010101010101u64;
let idx = array
.create_memory_neuron(pattern_hash, 100, 0, &config)
.unwrap();
assert_eq!(array.lifespan_current[idx], 10);
array.reactivate_memory_neuron(idx, 1);
assert_eq!(array.lifespan_current[idx], 15);
array.reactivate_memory_neuron(idx, 2);
assert_eq!(array.lifespan_current[idx], 20);
}
#[test]
fn test_st_ltm_counts_and_pagination_and_detail() {
let mut array = MemoryNeuronArray::new(1000);
let config = MemoryNeuronLifecycleConfig::default();
for i in 0..5 {
array
.create_memory_neuron(i as u64, 7, 0, &config)
.expect("create");
}
assert_eq!(array.count_short_term_in_area(7), 5);
assert_eq!(array.count_long_term_in_area(7), 0);
array.is_longterm_memory[0] = true;
assert_eq!(array.count_short_term_in_area(7), 4);
assert_eq!(array.count_long_term_in_area(7), 1);
let (page0, total) = array.paginated_neuron_ids_in_area(7, 0, 2);
assert_eq!(total, 5);
assert_eq!(page0.len(), 2);
let nid = page0[0];
let detail = array.get_memory_neuron_detail(nid).expect("detail");
assert_eq!(detail.neuron_id, nid);
assert_eq!(detail.cortical_area_idx, 7);
assert!(detail.pattern_hash.is_some());
}
}