#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SignalType {
SensoryRaw = 0,
VisualRetinal = 1,
VisualEdge = 2,
VisualShape = 3,
VisualObject = 4,
AuditoryRaw = 10,
AuditoryOnset = 11,
AuditoryPitch = 12,
AuditoryRhythm = 13,
MotorCommand = 20,
MotorProprioception = 21,
MotorEfference = 22,
CognitiveAttention = 30,
CognitiveWorkingMem = 31,
CognitiveDecision = 32,
ChemicalModulator = 40,
TextRaw = 50,
WordPattern = 51,
PhonemeSequence = 52,
SemanticEmbedding = 53,
MemoryAddress = 60,
MemoryRecall = 61,
EpisodicContext = 62,
SemanticAssociation = 63,
Custom(u8) = 255,
}
impl SignalType {
pub fn from_u8(v: u8) -> Self {
match v {
0 => SignalType::SensoryRaw,
1 => SignalType::VisualRetinal,
2 => SignalType::VisualEdge,
3 => SignalType::VisualShape,
4 => SignalType::VisualObject,
10 => SignalType::AuditoryRaw,
11 => SignalType::AuditoryOnset,
12 => SignalType::AuditoryPitch,
13 => SignalType::AuditoryRhythm,
20 => SignalType::MotorCommand,
21 => SignalType::MotorProprioception,
22 => SignalType::MotorEfference,
30 => SignalType::CognitiveAttention,
31 => SignalType::CognitiveWorkingMem,
32 => SignalType::CognitiveDecision,
40 => SignalType::ChemicalModulator,
50 => SignalType::TextRaw,
51 => SignalType::WordPattern,
52 => SignalType::PhonemeSequence,
53 => SignalType::SemanticEmbedding,
60 => SignalType::MemoryAddress,
61 => SignalType::MemoryRecall,
62 => SignalType::EpisodicContext,
63 => SignalType::SemanticAssociation,
_ => SignalType::Custom(v),
}
}
pub fn to_u8(self) -> u8 {
match self {
SignalType::SensoryRaw => 0,
SignalType::VisualRetinal => 1,
SignalType::VisualEdge => 2,
SignalType::VisualShape => 3,
SignalType::VisualObject => 4,
SignalType::AuditoryRaw => 10,
SignalType::AuditoryOnset => 11,
SignalType::AuditoryPitch => 12,
SignalType::AuditoryRhythm => 13,
SignalType::MotorCommand => 20,
SignalType::MotorProprioception => 21,
SignalType::MotorEfference => 22,
SignalType::CognitiveAttention => 30,
SignalType::CognitiveWorkingMem => 31,
SignalType::CognitiveDecision => 32,
SignalType::ChemicalModulator => 40,
SignalType::TextRaw => 50,
SignalType::WordPattern => 51,
SignalType::PhonemeSequence => 52,
SignalType::SemanticEmbedding => 53,
SignalType::MemoryAddress => 60,
SignalType::MemoryRecall => 61,
SignalType::EpisodicContext => 62,
SignalType::SemanticAssociation => 63,
SignalType::Custom(v) => v,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TemplateType {
LateralInhibition {
scale: u16,
surround_ratio: u8,
},
AttractorMemory {
capacity: u16,
},
TemporalChain {
length: u16,
},
OscillatorNetwork {
pacemaker_hz: u8,
follower_count: u16,
},
DisinhibitionGate,
WinnerTakeAll {
competitors: u16,
},
SensoryArray {
dimensions: u16,
},
}
impl TemplateType {
pub fn neuron_count(&self) -> usize {
match self {
TemplateType::LateralInhibition { scale, surround_ratio } => {
*scale as usize * (1 + *surround_ratio as usize)
}
TemplateType::AttractorMemory { capacity } => *capacity as usize,
TemplateType::TemporalChain { length } => *length as usize + 1, TemplateType::OscillatorNetwork { follower_count, .. } => {
1 + *follower_count as usize }
TemplateType::DisinhibitionGate => 2,
TemplateType::WinnerTakeAll { competitors } => {
*competitors as usize * 2
}
TemplateType::SensoryArray { dimensions } => *dimensions as usize,
}
}
pub fn type_distribution(&self) -> (usize, usize, usize, usize, usize, usize) {
match self {
TemplateType::LateralInhibition { scale, surround_ratio } => {
let comp = *scale as usize;
let gate = *scale as usize * *surround_ratio as usize;
(comp, gate, 0, 0, 0, 0)
}
TemplateType::AttractorMemory { capacity } => {
(0, 0, 0, *capacity as usize, 0, 0)
}
TemplateType::TemporalChain { length } => {
(*length as usize, 1, 0, 0, 0, 0) }
TemplateType::OscillatorNetwork { follower_count, .. } => {
(*follower_count as usize, 0, 1, 0, 0, 0) }
TemplateType::DisinhibitionGate => (0, 2, 0, 0, 0, 0),
TemplateType::WinnerTakeAll { competitors } => {
(*competitors as usize, *competitors as usize, 0, 0, 0, 0)
}
TemplateType::SensoryArray { dimensions } => {
(0, 0, 0, 0, *dimensions as usize, 0)
}
}
}
}
#[derive(Debug, Clone)]
pub struct TemplateRequest {
pub template_type: TemplateType,
pub input_signal: SignalType,
pub output_signal: SignalType,
pub position_hint: Option<[f32; 3]>,
}
#[derive(Debug, Clone)]
pub struct TemplateInstance {
pub id: u32,
pub template_type: TemplateType,
pub neuron_indices: Vec<usize>,
pub centroid: [f32; 3],
pub input_signal: SignalType,
pub output_signal: SignalType,
pub fitness: f32,
pub age_ticks: u64,
pub cumulative_activity: u64,
}
impl TemplateInstance {
pub fn new(
id: u32,
template_type: TemplateType,
neuron_indices: Vec<usize>,
centroid: [f32; 3],
input_signal: SignalType,
output_signal: SignalType,
) -> Self {
Self {
id,
template_type,
neuron_indices,
centroid,
input_signal,
output_signal,
fitness: 0.5, age_ticks: 0,
cumulative_activity: 0,
}
}
pub fn update_fitness(&mut self, measured_correlation: f32, alpha: f32) {
self.fitness = self.fitness * (1.0 - alpha) + measured_correlation * alpha;
}
pub fn should_prune(&self, fitness_threshold: f32, grace_period: u64) -> bool {
self.age_ticks > grace_period && self.fitness < fitness_threshold
}
pub fn tick(&mut self) {
self.age_ticks += 1;
}
}
#[derive(Debug, Default)]
pub struct TemplateRegistry {
instances: Vec<TemplateInstance>,
next_id: u32,
}
impl TemplateRegistry {
pub fn new() -> Self {
Self {
instances: Vec::new(),
next_id: 1,
}
}
pub fn register(&mut self, mut instance: TemplateInstance) -> u32 {
let id = self.next_id;
instance.id = id;
self.next_id += 1;
self.instances.push(instance);
id
}
pub fn get(&self, id: u32) -> Option<&TemplateInstance> {
self.instances.iter().find(|t| t.id == id)
}
pub fn get_mut(&mut self, id: u32) -> Option<&mut TemplateInstance> {
self.instances.iter_mut().find(|t| t.id == id)
}
pub fn remove(&mut self, id: u32) -> Option<TemplateInstance> {
if let Some(pos) = self.instances.iter().position(|t| t.id == id) {
Some(self.instances.swap_remove(pos))
} else {
None
}
}
pub fn all(&self) -> &[TemplateInstance] {
&self.instances
}
pub fn all_mut(&mut self) -> &mut [TemplateInstance] {
&mut self.instances
}
pub fn templates_needing(&self, signal: SignalType) -> Vec<u32> {
self.instances
.iter()
.filter(|t| t.input_signal == signal)
.map(|t| t.id)
.collect()
}
pub fn templates_offering(&self, signal: SignalType) -> Vec<u32> {
self.instances
.iter()
.filter(|t| t.output_signal == signal)
.map(|t| t.id)
.collect()
}
pub fn prune_unfit(&mut self, fitness_threshold: f32, grace_period: u64) -> Vec<u32> {
let mut pruned = Vec::new();
self.instances.retain(|t| {
if t.should_prune(fitness_threshold, grace_period) {
pruned.push(t.id);
false
} else {
true
}
});
pruned
}
pub fn tick_all(&mut self) {
for t in &mut self.instances {
t.tick();
}
}
pub fn len(&self) -> usize {
self.instances.len()
}
pub fn is_empty(&self) -> bool {
self.instances.is_empty()
}
}
#[derive(Debug, Clone, Copy)]
pub enum SpatialArrangement {
Random { radius: f32 },
Grid { spacing: f32 },
Ring { radius: f32 },
Chain { step: f32, direction: [f32; 3] },
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lateral_inhibition_neuron_count() {
let t = TemplateType::LateralInhibition {
scale: 16,
surround_ratio: 4,
};
assert_eq!(t.neuron_count(), 16 * 5); }
#[test]
fn test_attractor_memory_count() {
let t = TemplateType::AttractorMemory { capacity: 32 };
assert_eq!(t.neuron_count(), 32);
}
#[test]
fn test_temporal_chain_count() {
let t = TemplateType::TemporalChain { length: 10 };
assert_eq!(t.neuron_count(), 11); }
#[test]
fn test_oscillator_network_count() {
let t = TemplateType::OscillatorNetwork {
pacemaker_hz: 40,
follower_count: 8,
};
assert_eq!(t.neuron_count(), 9); }
#[test]
fn test_template_registry() {
let mut reg = TemplateRegistry::new();
let instance = TemplateInstance::new(
0,
TemplateType::LateralInhibition {
scale: 4,
surround_ratio: 4,
},
vec![0, 1, 2, 3],
[1.0, 2.0, 3.0],
SignalType::VisualRetinal,
SignalType::VisualEdge,
);
let id = reg.register(instance);
assert_eq!(id, 1);
assert_eq!(reg.len(), 1);
let t = reg.get(id).unwrap();
assert_eq!(t.input_signal, SignalType::VisualRetinal);
assert_eq!(t.output_signal, SignalType::VisualEdge);
}
#[test]
fn test_fitness_pruning() {
let mut reg = TemplateRegistry::new();
let mut instance = TemplateInstance::new(
0,
TemplateType::DisinhibitionGate,
vec![0, 1],
[0.0, 0.0, 0.0],
SignalType::CognitiveAttention,
SignalType::CognitiveDecision,
);
instance.age_ticks = 1000;
instance.fitness = 0.1;
let id = reg.register(instance);
let pruned = reg.prune_unfit(0.2, 500);
assert_eq!(pruned.len(), 1);
assert_eq!(pruned[0], id);
assert!(reg.is_empty());
}
#[test]
fn test_signal_type_roundtrip() {
for v in [0u8, 1, 2, 10, 20, 30, 40, 100] {
let s = SignalType::from_u8(v);
let back = s.to_u8();
if v > 40 {
assert_eq!(back, v);
}
}
}
}