1use crate::neuron_id_manager::{AllocationStats, NeuronIdManager};
20use serde::{Deserialize, Serialize};
21use std::collections::{HashMap, HashSet};
22
23#[derive(Debug, Clone, Copy)]
25pub struct MemoryNeuronLifecycleConfig {
26 pub initial_lifespan: u32,
28
29 pub lifespan_growth_rate: f32,
31
32 pub longterm_threshold: u32,
34
35 pub max_reactivations: u32,
37}
38
39impl Default for MemoryNeuronLifecycleConfig {
40 fn default() -> Self {
41 Self {
42 initial_lifespan: 20,
43 lifespan_growth_rate: 3.0,
44 longterm_threshold: 100,
45 max_reactivations: 1000,
46 }
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52pub struct MemoryNeuronDetail {
53 pub neuron_id: u32,
54 pub cortical_area_idx: u32,
55 pub pattern_hash: Option<u64>,
56 pub is_longterm_memory: bool,
57 pub is_active: bool,
58 pub lifespan_current: u32,
59 pub lifespan_initial: u32,
60 pub lifespan_growth_rate: f32,
61 pub creation_burst: u64,
62 pub last_activation_burst: u64,
63 pub activation_count: u32,
64}
65
66#[derive(Debug, Clone, Default)]
68pub struct MemoryNeuronStats {
69 pub total_capacity: usize,
70 pub active_neurons: usize,
71 pub longterm_neurons: usize,
72 pub dead_neurons: usize,
73 pub reusable_indices: usize,
74 pub memory_usage_bytes: usize,
75 pub avg_lifespan: f64,
76 pub avg_activation_count: f64,
77}
78
79pub struct MemoryNeuronArray {
81 capacity: usize,
82
83 neuron_ids: Vec<u32>,
85 cortical_area_ids: Vec<u32>,
86 is_active: Vec<bool>,
87
88 lifespan_current: Vec<u32>,
90 lifespan_initial: Vec<u32>,
91 lifespan_growth_rate: Vec<f32>,
92 is_longterm_memory: Vec<bool>,
93
94 creation_burst: Vec<u64>,
96 last_activation_burst: Vec<u64>,
97 activation_count: Vec<u32>,
98
99 pattern_hash_to_index: HashMap<u64, usize>,
101 index_to_pattern_hash: HashMap<usize, u64>,
102
103 next_available_index: usize,
105 reusable_indices: HashSet<usize>,
106
107 area_neuron_indices: HashMap<u32, HashSet<usize>>,
109
110 id_manager: NeuronIdManager,
112}
113
114impl MemoryNeuronArray {
115 pub fn new(capacity: usize) -> Self {
117 Self {
118 capacity,
119 neuron_ids: vec![0; capacity],
120 cortical_area_ids: vec![0; capacity],
121 is_active: vec![false; capacity],
122 lifespan_current: vec![0; capacity],
123 lifespan_initial: vec![0; capacity],
124 lifespan_growth_rate: vec![0.0; capacity],
125 is_longterm_memory: vec![false; capacity],
126 creation_burst: vec![0; capacity],
127 last_activation_burst: vec![0; capacity],
128 activation_count: vec![0; capacity],
129 pattern_hash_to_index: HashMap::new(),
130 index_to_pattern_hash: HashMap::new(),
131 next_available_index: 0,
132 reusable_indices: HashSet::new(),
133 area_neuron_indices: HashMap::new(),
134 id_manager: NeuronIdManager::new(),
135 }
136 }
137
138 pub fn create_memory_neuron(
140 &mut self,
141 pattern_hash: u64,
142 cortical_area_id: u32,
143 current_burst: u64,
144 config: &MemoryNeuronLifecycleConfig,
145 ) -> Option<usize> {
146 if let Some(&existing_idx) = self.pattern_hash_to_index.get(&pattern_hash) {
148 if self.is_active[existing_idx] {
149 return self.reactivate_memory_neuron_internal(existing_idx, current_burst);
151 }
152 }
153
154 let neuron_idx = self.get_available_index_internal()?;
156
157 let neuron_id = self.id_manager.allocate_memory_neuron_id()?;
159
160 self.neuron_ids[neuron_idx] = neuron_id;
162 self.cortical_area_ids[neuron_idx] = cortical_area_id;
163 self.is_active[neuron_idx] = true;
164
165 self.lifespan_current[neuron_idx] = config.initial_lifespan;
167 self.lifespan_initial[neuron_idx] = config.initial_lifespan;
168 self.lifespan_growth_rate[neuron_idx] = config.lifespan_growth_rate;
169 self.is_longterm_memory[neuron_idx] = false;
170
171 self.creation_burst[neuron_idx] = current_burst;
173 self.last_activation_burst[neuron_idx] = current_burst;
174 self.activation_count[neuron_idx] = 1;
175
176 self.pattern_hash_to_index.insert(pattern_hash, neuron_idx);
178 self.index_to_pattern_hash.insert(neuron_idx, pattern_hash);
179
180 self.area_neuron_indices
182 .entry(cortical_area_id)
183 .or_default()
184 .insert(neuron_idx);
185
186 Some(neuron_idx)
187 }
188
189 pub fn reactivate_memory_neuron(&mut self, neuron_idx: usize, current_burst: u64) -> bool {
191 self.reactivate_memory_neuron_internal(neuron_idx, current_burst)
192 .is_some()
193 }
194
195 fn reactivate_memory_neuron_internal(
197 &mut self,
198 neuron_idx: usize,
199 current_burst: u64,
200 ) -> Option<usize> {
201 if !self.is_valid_index(neuron_idx) || !self.is_active[neuron_idx] {
202 return None;
203 }
204
205 self.last_activation_burst[neuron_idx] = current_burst;
207 self.activation_count[neuron_idx] += 1;
208
209 if !self.is_longterm_memory[neuron_idx] {
211 let current_lifespan = self.lifespan_current[neuron_idx];
212 let growth = self.lifespan_growth_rate[neuron_idx] as u32;
213 self.lifespan_current[neuron_idx] = current_lifespan.saturating_add(growth);
214 }
215
216 Some(neuron_idx)
217 }
218
219 pub fn age_memory_neurons(&mut self, _current_burst: u64) -> Vec<usize> {
221 let n = self.next_available_index;
222 if n == 0 {
223 return Vec::new();
224 }
225
226 let mut died_indices = Vec::new();
227
228 for i in 0..n {
230 if self.is_active[i] && !self.is_longterm_memory[i] && self.lifespan_current[i] > 0 {
231 self.lifespan_current[i] -= 1;
232
233 if self.lifespan_current[i] == 0 {
235 self.is_active[i] = false;
236 died_indices.push(i);
237 }
238 }
239 }
240
241 for &i in &died_indices {
243 self.cleanup_dead_neuron_internal(i);
244 }
245
246 died_indices
247 }
248
249 pub fn check_longterm_conversion(&mut self, longterm_threshold: u32) -> Vec<usize> {
251 let n = self.next_available_index;
252 if n == 0 {
253 return Vec::new();
254 }
255
256 let mut converted_indices = Vec::new();
257
258 for i in 0..n {
259 if self.is_active[i]
260 && !self.is_longterm_memory[i]
261 && self.lifespan_current[i] >= longterm_threshold
262 {
263 self.is_longterm_memory[i] = true;
264 converted_indices.push(i);
265 }
266 }
267
268 converted_indices
269 }
270
271 pub fn check_longterm_conversion_by_area(
275 &mut self,
276 lifecycle_configs: &HashMap<u32, MemoryNeuronLifecycleConfig>,
277 default_threshold: u32,
278 ) -> Vec<usize> {
279 let n = self.next_available_index;
280 if n == 0 {
281 return Vec::new();
282 }
283
284 let area_indices: Vec<(u32, Vec<usize>)> = self
285 .area_neuron_indices
286 .iter()
287 .map(|(&area, indices)| (area, indices.iter().copied().collect()))
288 .collect();
289
290 let mut converted_indices = Vec::new();
291 for (area_id, indices) in area_indices {
292 let threshold = lifecycle_configs
293 .get(&area_id)
294 .map(|config| config.longterm_threshold)
295 .unwrap_or(default_threshold);
296
297 for neuron_idx in indices {
298 if self.is_active[neuron_idx]
299 && !self.is_longterm_memory[neuron_idx]
300 && self.lifespan_current[neuron_idx] >= threshold
301 {
302 self.is_longterm_memory[neuron_idx] = true;
303 converted_indices.push(neuron_idx);
304 }
305 }
306 }
307
308 converted_indices
309 }
310
311 pub fn get_active_neurons_by_area(&self, cortical_area_id: u32) -> Vec<u32> {
313 if let Some(indices) = self.area_neuron_indices.get(&cortical_area_id) {
314 indices
315 .iter()
316 .filter(|&&idx| self.is_valid_index(idx) && self.is_active[idx])
317 .map(|&idx| self.neuron_ids[idx])
318 .collect()
319 } else {
320 Vec::new()
321 }
322 }
323
324 pub fn count_short_term_in_area(&self, cortical_area_id: u32) -> usize {
326 self.area_neuron_indices
327 .get(&cortical_area_id)
328 .map(|indices| {
329 indices
330 .iter()
331 .filter(|&&idx| {
332 self.is_valid_index(idx)
333 && self.is_active[idx]
334 && !self.is_longterm_memory[idx]
335 })
336 .count()
337 })
338 .unwrap_or(0)
339 }
340
341 pub fn count_long_term_in_area(&self, cortical_area_id: u32) -> usize {
343 self.area_neuron_indices
344 .get(&cortical_area_id)
345 .map(|indices| {
346 indices
347 .iter()
348 .filter(|&&idx| {
349 self.is_valid_index(idx)
350 && self.is_active[idx]
351 && self.is_longterm_memory[idx]
352 })
353 .count()
354 })
355 .unwrap_or(0)
356 }
357
358 pub fn paginated_neuron_ids_in_area(
360 &self,
361 cortical_area_id: u32,
362 offset: usize,
363 limit: usize,
364 ) -> (Vec<u32>, usize) {
365 let mut ids = self.get_active_neurons_by_area(cortical_area_id);
366 ids.sort_unstable();
367 let total = ids.len();
368 let page = ids.into_iter().skip(offset).take(limit).collect();
369 (page, total)
370 }
371
372 fn find_neuron_index_by_global_id(&self, neuron_id: u32) -> Option<usize> {
373 (0..self.next_available_index).find(|&i| self.neuron_ids[i] == neuron_id)
374 }
375
376 pub fn get_memory_neuron_detail(&self, neuron_id: u32) -> Option<MemoryNeuronDetail> {
378 let idx = self.find_neuron_index_by_global_id(neuron_id)?;
379 if !self.is_valid_index(idx) || !self.is_active[idx] {
380 return None;
381 }
382 Some(MemoryNeuronDetail {
383 neuron_id: self.neuron_ids[idx],
384 cortical_area_idx: self.cortical_area_ids[idx],
385 pattern_hash: self.index_to_pattern_hash.get(&idx).copied(),
386 is_longterm_memory: self.is_longterm_memory[idx],
387 is_active: self.is_active[idx],
388 lifespan_current: self.lifespan_current[idx],
389 lifespan_initial: self.lifespan_initial[idx],
390 lifespan_growth_rate: self.lifespan_growth_rate[idx],
391 creation_burst: self.creation_burst[idx],
392 last_activation_burst: self.last_activation_burst[idx],
393 activation_count: self.activation_count[idx],
394 })
395 }
396
397 pub fn find_neuron_by_pattern(&self, pattern_hash: &u64) -> Option<usize> {
399 self.pattern_hash_to_index
400 .get(pattern_hash)
401 .copied()
402 .filter(|&idx| self.is_valid_index(idx) && self.is_active[idx])
403 }
404
405 pub fn get_neuron_id(&self, neuron_idx: usize) -> Option<u32> {
407 if self.is_valid_index(neuron_idx) {
408 Some(self.neuron_ids[neuron_idx])
409 } else {
410 None
411 }
412 }
413
414 pub fn get_cortical_area_id(&self, neuron_idx: usize) -> Option<u32> {
416 if self.is_valid_index(neuron_idx) {
417 Some(self.cortical_area_ids[neuron_idx])
418 } else {
419 None
420 }
421 }
422
423 pub fn get_pattern_hash(&self, neuron_idx: usize) -> Option<u64> {
424 self.index_to_pattern_hash.get(&neuron_idx).copied()
425 }
426
427 pub fn get_stats(&self) -> MemoryNeuronStats {
429 let n = self.next_available_index;
430
431 if n == 0 {
432 return MemoryNeuronStats {
433 total_capacity: self.capacity,
434 ..Default::default()
435 };
436 }
437
438 let active_count = self.is_active[..n].iter().filter(|&&x| x).count();
439 let longterm_count = (0..n)
440 .filter(|&i| self.is_active[i] && self.is_longterm_memory[i])
441 .count();
442 let dead_count = n - active_count;
443
444 let (avg_lifespan, avg_activation_count) = if active_count > 0 {
446 let total_lifespan: u32 = (0..n)
447 .filter(|&i| self.is_active[i])
448 .map(|i| self.lifespan_current[i])
449 .sum();
450 let total_activations: u32 = (0..n)
451 .filter(|&i| self.is_active[i])
452 .map(|i| self.activation_count[i])
453 .sum();
454
455 (
456 total_lifespan as f64 / active_count as f64,
457 total_activations as f64 / active_count as f64,
458 )
459 } else {
460 (0.0, 0.0)
461 };
462
463 let memory_usage = self.capacity * (
465 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 {
473 total_capacity: self.capacity,
474 active_neurons: active_count,
475 longterm_neurons: longterm_count,
476 dead_neurons: dead_count,
477 reusable_indices: self.reusable_indices.len(),
478 memory_usage_bytes: memory_usage,
479 avg_lifespan,
480 avg_activation_count,
481 }
482 }
483
484 pub fn get_id_allocation_stats(&self) -> AllocationStats {
486 self.id_manager.get_allocation_stats()
487 }
488
489 fn get_available_index_internal(&mut self) -> Option<usize> {
491 if let Some(&idx) = self.reusable_indices.iter().next() {
493 self.reusable_indices.remove(&idx);
494 return Some(idx);
495 }
496
497 if self.next_available_index < self.capacity {
499 let idx = self.next_available_index;
500 self.next_available_index += 1;
501 Some(idx)
502 } else {
503 None
504 }
505 }
506
507 fn cleanup_dead_neuron_internal(&mut self, neuron_idx: usize) {
509 let neuron_id = self.neuron_ids[neuron_idx];
511 self.id_manager.deallocate_memory_neuron_id(neuron_id);
512
513 if let Some(pattern_hash) = self.index_to_pattern_hash.remove(&neuron_idx) {
515 self.pattern_hash_to_index.remove(&pattern_hash);
516 }
517
518 let area_id = self.cortical_area_ids[neuron_idx];
520 if let Some(indices) = self.area_neuron_indices.get_mut(&area_id) {
521 indices.remove(&neuron_idx);
522 }
523
524 self.reusable_indices.insert(neuron_idx);
526 }
527
528 fn is_valid_index(&self, neuron_idx: usize) -> bool {
530 neuron_idx < self.next_available_index
531 }
532
533 pub fn reset_cortical_area(&mut self, cortical_area_id: u32) -> usize {
536 let Some(neuron_indices) = self.area_neuron_indices.get(&cortical_area_id) else {
537 return 0;
538 };
539
540 let indices_to_reset: Vec<usize> = neuron_indices.iter().copied().collect();
541 let mut reset_count = 0;
542
543 for neuron_idx in indices_to_reset {
544 if !self.is_valid_index(neuron_idx) {
545 continue;
546 }
547
548 self.is_active[neuron_idx] = false;
550
551 if let Some(pattern_hash) = self.index_to_pattern_hash.remove(&neuron_idx) {
553 self.pattern_hash_to_index.remove(&pattern_hash);
554 }
555
556 self.lifespan_current[neuron_idx] = 0;
558 self.activation_count[neuron_idx] = 0;
559
560 self.reusable_indices.insert(neuron_idx);
562
563 reset_count += 1;
564 }
565
566 self.area_neuron_indices.remove(&cortical_area_id);
568
569 reset_count
570 }
571
572 pub fn reset(&mut self) {
573 self.neuron_ids.fill(0);
574 self.cortical_area_ids.fill(0);
575 self.is_active.fill(false);
576 self.lifespan_current.fill(0);
577 self.lifespan_initial.fill(0);
578 self.lifespan_growth_rate.fill(0.0);
579 self.is_longterm_memory.fill(false);
580 self.creation_burst.fill(0);
581 self.last_activation_burst.fill(0);
582 self.activation_count.fill(0);
583
584 self.pattern_hash_to_index.clear();
585 self.index_to_pattern_hash.clear();
586 self.area_neuron_indices.clear();
587
588 self.next_available_index = 0;
589 self.reusable_indices.clear();
590
591 self.id_manager.reset();
592 }
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598
599 #[test]
600 fn test_lifecycle_config_default() {
601 let config = MemoryNeuronLifecycleConfig::default();
602 assert_eq!(config.initial_lifespan, 20);
603 assert_eq!(config.lifespan_growth_rate, 3.0);
604 assert_eq!(config.longterm_threshold, 100);
605 assert_eq!(config.max_reactivations, 1000);
606 }
607
608 #[test]
609 fn test_create_memory_neuron() {
610 let mut array = MemoryNeuronArray::new(1000);
611 let config = MemoryNeuronLifecycleConfig::default();
612
613 let pattern_hash = 0x0101010101010101u64;
614 let neuron_idx = array.create_memory_neuron(pattern_hash, 100, 0, &config);
615
616 assert!(neuron_idx.is_some());
617 let idx = neuron_idx.unwrap();
618 assert!(array.is_active[idx]);
619 assert_eq!(array.cortical_area_ids[idx], 100);
620 assert_eq!(array.lifespan_current[idx], config.initial_lifespan);
621 assert_eq!(array.activation_count[idx], 1);
622 assert_eq!(array.creation_burst[idx], 0);
623 }
624
625 #[test]
626 fn test_create_duplicate_pattern() {
627 let mut array = MemoryNeuronArray::new(1000);
628 let config = MemoryNeuronLifecycleConfig::default();
629
630 let pattern_hash = 0x0101010101010101u64;
631 let idx1 = array
632 .create_memory_neuron(pattern_hash, 100, 0, &config)
633 .unwrap();
634
635 let idx2 = array
637 .create_memory_neuron(pattern_hash, 100, 1, &config)
638 .unwrap();
639
640 assert_eq!(idx1, idx2);
641 assert_eq!(array.activation_count[idx1], 2); }
643
644 #[test]
645 fn test_multiple_neurons() {
646 let mut array = MemoryNeuronArray::new(1000);
647 let config = MemoryNeuronLifecycleConfig::default();
648
649 let mut neurons = Vec::new();
650 for i in 0..10 {
651 let pattern_hash = i as u64;
652 let idx = array
653 .create_memory_neuron(pattern_hash, 100, 0, &config)
654 .unwrap();
655 neurons.push(idx);
656 }
657
658 assert_eq!(neurons.len(), 10);
659 assert_eq!(array.next_available_index, 10);
660
661 let stats = array.get_stats();
662 assert_eq!(stats.active_neurons, 10);
663 }
664
665 #[test]
666 fn test_reactivate_memory_neuron() {
667 let mut array = MemoryNeuronArray::new(1000);
668 let config = MemoryNeuronLifecycleConfig::default();
669
670 let pattern_hash = 0x0101010101010101u64;
671 let idx = array
672 .create_memory_neuron(pattern_hash, 100, 0, &config)
673 .unwrap();
674
675 let initial_count = array.activation_count[idx];
676 let initial_lifespan = array.lifespan_current[idx];
677
678 assert!(array.reactivate_memory_neuron(idx, 1));
679
680 assert_eq!(array.activation_count[idx], initial_count + 1);
681 assert_eq!(array.last_activation_burst[idx], 1);
682
683 let expected_lifespan = initial_lifespan + config.lifespan_growth_rate as u32;
685 assert_eq!(array.lifespan_current[idx], expected_lifespan);
686 }
687
688 #[test]
689 fn test_reactivate_invalid_neuron() {
690 let mut array = MemoryNeuronArray::new(1000);
691
692 assert!(!array.reactivate_memory_neuron(0, 1));
694 assert!(!array.reactivate_memory_neuron(999, 1));
695 }
696
697 #[test]
698 fn test_age_memory_neurons() {
699 let mut array = MemoryNeuronArray::new(1000);
700 let config = MemoryNeuronLifecycleConfig {
701 initial_lifespan: 2,
702 ..Default::default()
703 };
704
705 let pattern_hash = 0x0101010101010101u64;
706 let idx = array
707 .create_memory_neuron(pattern_hash, 100, 0, &config)
708 .unwrap();
709
710 let died = array.age_memory_neurons(1);
712 assert!(died.is_empty());
713 assert_eq!(array.lifespan_current[idx], 1);
714
715 let died = array.age_memory_neurons(2);
717 assert_eq!(died.len(), 1);
718 assert_eq!(died[0], idx);
719 assert!(!array.is_active[idx]);
720
721 let found = array.find_neuron_by_pattern(&pattern_hash);
723 assert!(found.is_none());
724 }
725
726 #[test]
727 fn test_age_multiple_neurons() {
728 let mut array = MemoryNeuronArray::new(1000);
729 let config = MemoryNeuronLifecycleConfig {
730 initial_lifespan: 5,
731 ..Default::default()
732 };
733
734 let mut neurons = Vec::new();
735 for i in 0..10 {
736 let pattern_hash = i as u64;
737 let idx = array
738 .create_memory_neuron(pattern_hash, 100, 0, &config)
739 .unwrap();
740 neurons.push(idx);
741 }
742
743 for burst in 1..=5 {
745 let died = array.age_memory_neurons(burst);
746 if burst < 5 {
747 assert_eq!(died.len(), 0);
748 } else {
749 assert_eq!(died.len(), 10);
750 }
751 }
752
753 let stats = array.get_stats();
754 assert_eq!(stats.active_neurons, 0);
755 assert_eq!(stats.dead_neurons, 10);
756 }
757
758 #[test]
759 fn test_longterm_memory_no_aging() {
760 let mut array = MemoryNeuronArray::new(1000);
761 let config = MemoryNeuronLifecycleConfig {
762 initial_lifespan: 100,
763 ..Default::default()
764 };
765
766 let pattern_hash = 0x0101010101010101u64;
767 let idx = array
768 .create_memory_neuron(pattern_hash, 100, 0, &config)
769 .unwrap();
770
771 let converted = array.check_longterm_conversion(100);
773 assert_eq!(converted.len(), 1);
774 assert!(array.is_longterm_memory[idx]);
775
776 let initial_lifespan = array.lifespan_current[idx];
777
778 for burst in 1..=50 {
780 array.age_memory_neurons(burst);
781 }
782
783 assert!(array.is_active[idx]);
784 assert_eq!(array.lifespan_current[idx], initial_lifespan); }
786
787 #[test]
788 fn test_longterm_conversion() {
789 let mut array = MemoryNeuronArray::new(1000);
790 let config = MemoryNeuronLifecycleConfig {
791 initial_lifespan: 100,
792 ..Default::default()
793 };
794
795 let pattern_hash = 0x0101010101010101u64;
796 let idx = array
797 .create_memory_neuron(pattern_hash, 100, 0, &config)
798 .unwrap();
799
800 let converted = array.check_longterm_conversion(100);
801 assert_eq!(converted.len(), 1);
802 assert_eq!(converted[0], idx);
803 assert!(array.is_longterm_memory[idx]);
804
805 let converted2 = array.check_longterm_conversion(100);
807 assert_eq!(converted2.len(), 0);
808 }
809
810 #[test]
811 fn test_longterm_conversion_threshold() {
812 let mut array = MemoryNeuronArray::new(1000);
813 let config = MemoryNeuronLifecycleConfig {
814 initial_lifespan: 50,
815 ..Default::default()
816 };
817
818 let pattern_hash = 0x0101010101010101u64;
819 let idx = array
820 .create_memory_neuron(pattern_hash, 100, 0, &config)
821 .unwrap();
822
823 let converted = array.check_longterm_conversion(100);
825 assert_eq!(converted.len(), 0);
826 assert!(!array.is_longterm_memory[idx]);
827
828 for burst in 1..=20 {
830 array.reactivate_memory_neuron(idx, burst);
831 }
832
833 let converted = array.check_longterm_conversion(100);
835 assert_eq!(converted.len(), 1);
836 assert!(array.is_longterm_memory[idx]);
837 }
838
839 #[test]
840 fn test_find_neuron_by_pattern() {
841 let mut array = MemoryNeuronArray::new(1000);
842 let config = MemoryNeuronLifecycleConfig::default();
843
844 let pattern_hash = 0x0101010101010101u64;
845 let idx = array
846 .create_memory_neuron(pattern_hash, 100, 0, &config)
847 .unwrap();
848
849 let found = array.find_neuron_by_pattern(&pattern_hash);
850 assert_eq!(found, Some(idx));
851
852 let pattern_hash2 = 0x0202020202020202u64;
854 let found2 = array.find_neuron_by_pattern(&pattern_hash2);
855 assert_eq!(found2, None);
856 }
857
858 #[test]
859 fn test_get_active_neurons_by_area() {
860 let mut array = MemoryNeuronArray::new(1000);
861 let config = MemoryNeuronLifecycleConfig::default();
862
863 for area in [100, 200] {
865 for i in 0..5 {
866 let pattern_hash = ((area as u64) << 32) | (i as u64);
867 array.create_memory_neuron(pattern_hash, area, 0, &config);
868 }
869 }
870
871 let area100_neurons = array.get_active_neurons_by_area(100);
872 let area200_neurons = array.get_active_neurons_by_area(200);
873
874 assert_eq!(area100_neurons.len(), 5);
875 assert_eq!(area200_neurons.len(), 5);
876
877 let area999_neurons = array.get_active_neurons_by_area(999);
879 assert_eq!(area999_neurons.len(), 0);
880 }
881
882 #[test]
883 fn test_get_neuron_id() {
884 let mut array = MemoryNeuronArray::new(1000);
885 let config = MemoryNeuronLifecycleConfig::default();
886
887 let pattern_hash = 0x0101010101010101u64;
888 let idx = array
889 .create_memory_neuron(pattern_hash, 100, 0, &config)
890 .unwrap();
891
892 let neuron_id = array.get_neuron_id(idx);
893 assert!(neuron_id.is_some());
894
895 let invalid_id = array.get_neuron_id(999);
897 assert!(invalid_id.is_none());
898 }
899
900 #[test]
901 fn test_index_reuse() {
902 let mut array = MemoryNeuronArray::new(1000);
903 let config = MemoryNeuronLifecycleConfig {
904 initial_lifespan: 1,
905 ..Default::default()
906 };
907
908 let pattern_hash = 0x0101010101010101u64;
909 let idx1 = array
910 .create_memory_neuron(pattern_hash, 100, 0, &config)
911 .unwrap();
912 assert_eq!(idx1, 0);
913
914 array.age_memory_neurons(1);
916 assert!(!array.is_active[idx1]);
917
918 let pattern_hash2 = 0x0202020202020202u64;
920 let idx2 = array
921 .create_memory_neuron(pattern_hash2, 100, 2, &config)
922 .unwrap();
923 assert_eq!(idx2, 0); assert_eq!(array.next_available_index, 1); }
926
927 #[test]
928 fn test_get_stats() {
929 let mut array = MemoryNeuronArray::new(1000);
930 let config = MemoryNeuronLifecycleConfig::default();
931
932 for i in 0..10 {
934 let pattern_hash = i as u64;
935 array.create_memory_neuron(pattern_hash, 100, 0, &config);
936 }
937
938 let stats = array.get_stats();
939 assert_eq!(stats.total_capacity, 1000);
940 assert_eq!(stats.active_neurons, 10);
941 assert_eq!(stats.longterm_neurons, 0);
942 assert_eq!(stats.dead_neurons, 0);
943 assert!(stats.avg_lifespan > 0.0);
944 assert!(stats.avg_activation_count >= 1.0);
945 assert!(stats.memory_usage_bytes > 0);
946 }
947
948 #[test]
949 fn test_capacity_exhaustion() {
950 let mut array = MemoryNeuronArray::new(5);
951 let config = MemoryNeuronLifecycleConfig::default();
952
953 for i in 0..5 {
955 let pattern_hash = i as u64;
956 let idx = array.create_memory_neuron(pattern_hash, 100, 0, &config);
957 assert!(idx.is_some());
958 }
959
960 let pattern_hash = 0x6363636363636363u64;
962 let idx = array.create_memory_neuron(pattern_hash, 100, 0, &config);
963 assert!(idx.is_none());
964 }
965
966 #[test]
967 fn test_reset_cortical_area() {
968 let mut array = MemoryNeuronArray::new(1000);
969 let config = MemoryNeuronLifecycleConfig::default();
970
971 let pattern1 = 0x0101010101010101u64;
973 let pattern2 = 0x0202020202020202u64;
974 array.create_memory_neuron(pattern1, 5, 0, &config);
975 array.create_memory_neuron(pattern2, 5, 0, &config);
976
977 let pattern3 = 0x0303030303030303u64;
979 array.create_memory_neuron(pattern3, 6, 0, &config);
980
981 assert_eq!(array.get_active_neurons_by_area(5).len(), 2);
983 assert_eq!(array.get_active_neurons_by_area(6).len(), 1);
984
985 let reset_count = array.reset_cortical_area(5);
987 assert_eq!(reset_count, 2);
988
989 assert_eq!(array.get_active_neurons_by_area(5).len(), 0);
991
992 assert_eq!(array.get_active_neurons_by_area(6).len(), 1);
994
995 assert!(!array.pattern_hash_to_index.contains_key(&pattern1));
997 assert!(!array.pattern_hash_to_index.contains_key(&pattern2));
998
999 assert!(array.pattern_hash_to_index.contains_key(&pattern3));
1001 }
1002
1003 #[test]
1004 fn test_reset() {
1005 let mut array = MemoryNeuronArray::new(1000);
1006 let config = MemoryNeuronLifecycleConfig::default();
1007
1008 for i in 0..5 {
1010 let pattern_hash = i as u64;
1011 array.create_memory_neuron(pattern_hash, 100, 0, &config);
1012 }
1013
1014 assert_eq!(array.next_available_index, 5);
1015
1016 array.reset();
1017
1018 assert_eq!(array.next_available_index, 0);
1019 let stats = array.get_stats();
1020 assert_eq!(stats.active_neurons, 0);
1021 }
1022
1023 #[test]
1024 fn test_lifespan_growth_on_reactivation() {
1025 let mut array = MemoryNeuronArray::new(1000);
1026 let config = MemoryNeuronLifecycleConfig {
1027 initial_lifespan: 10,
1028 lifespan_growth_rate: 5.0,
1029 longterm_threshold: 100,
1030 max_reactivations: 1000,
1031 };
1032
1033 let pattern_hash = 0x0101010101010101u64;
1034 let idx = array
1035 .create_memory_neuron(pattern_hash, 100, 0, &config)
1036 .unwrap();
1037
1038 assert_eq!(array.lifespan_current[idx], 10);
1039
1040 array.reactivate_memory_neuron(idx, 1);
1041 assert_eq!(array.lifespan_current[idx], 15);
1042
1043 array.reactivate_memory_neuron(idx, 2);
1044 assert_eq!(array.lifespan_current[idx], 20);
1045 }
1046
1047 #[test]
1048 fn test_st_ltm_counts_and_pagination_and_detail() {
1049 let mut array = MemoryNeuronArray::new(1000);
1050 let config = MemoryNeuronLifecycleConfig::default();
1051
1052 for i in 0..5 {
1053 array
1054 .create_memory_neuron(i as u64, 7, 0, &config)
1055 .expect("create");
1056 }
1057 assert_eq!(array.count_short_term_in_area(7), 5);
1058 assert_eq!(array.count_long_term_in_area(7), 0);
1059
1060 array.is_longterm_memory[0] = true;
1061 assert_eq!(array.count_short_term_in_area(7), 4);
1062 assert_eq!(array.count_long_term_in_area(7), 1);
1063
1064 let (page0, total) = array.paginated_neuron_ids_in_area(7, 0, 2);
1065 assert_eq!(total, 5);
1066 assert_eq!(page0.len(), 2);
1067
1068 let nid = page0[0];
1069 let detail = array.get_memory_neuron_detail(nid).expect("detail");
1070 assert_eq!(detail.neuron_id, nid);
1071 assert_eq!(detail.cortical_area_idx, 7);
1072 assert!(detail.pattern_hash.is_some());
1073 }
1074}