//! # Application 11: Extropic Intelligence Substrate //! //! The complete substrate for bounded, self-improving intelligence: //! - Autonomous goal mutation under coherence constraints //! - Native agent lifecycles at the memory layer //! - Hardware-enforced spike/silence semantics //! //! ## The Three Missing Pieces //! //! 1. **Goal Mutation**: Goals are not static—they evolve as attractors //! that the system discovers and refines while preserving coherence. //! //! 2. **Agent Lifecycles in Memory**: Agents are born, grow, decay, and die //! within the vector space itself. Memory IS the agent. //! //! 3. **Spike Semantics**: Communication follows neural spike patterns— //! silence is the default, spikes are costly, and hardware enforces this. //! //! ## Why This Matters //! This is the difference between a system that *uses* intelligence //! and a system that *is* intelligence. use std::collections::HashMap; use std::sync::atomic::{AtomicU64, Ordering}; /// Maximum goal history entries to retain (prevents unbounded memory growth) const MAX_GOAL_HISTORY: usize = 100; // ============================================================================= // Part 1: Autonomous Goal Mutation // ============================================================================= /// A goal that can mutate autonomously while preserving coherence #[derive(Clone, Debug)] pub struct MutableGoal { /// Current goal state (as a vector in goal-space) pub state: Vec, /// Goal coherence with the system coherence_with_system: f64, /// Mutation rate (how quickly goals can change) mutation_rate: f64, /// Stability (resistance to mutation) stability: f64, /// History of goal states history: Vec>, /// Attractors discovered in goal-space discovered_attractors: Vec, } #[derive(Clone, Debug)] pub struct GoalAttractor { pub center: Vec, pub strength: f64, pub radius: f64, } impl MutableGoal { pub fn new(initial: Vec) -> Self { Self { state: initial.clone(), coherence_with_system: 1.0, mutation_rate: 0.1, stability: 0.5, history: vec![initial], discovered_attractors: Vec::new(), } } /// Attempt to mutate the goal based on feedback pub fn mutate(&mut self, feedback: &GoalFeedback, system_coherence: f64) -> MutationResult { // Goals cannot mutate if system coherence is too low if system_coherence < 0.3 { return MutationResult::Blocked { reason: "System coherence too low for goal mutation".to_string(), }; } // Calculate mutation pressure from feedback let pressure = feedback.calculate_pressure(); // Stability resists mutation let effective_rate = self.mutation_rate * pressure * (1.0 - self.stability); if effective_rate < 0.01 { return MutationResult::NoChange; } // Calculate mutation direction (toward better coherence) let direction = self.calculate_mutation_direction(feedback); // Apply mutation with coherence constraint let mut new_state = self.state.clone(); for (i, d) in direction.iter().enumerate() { if i < new_state.len() { new_state[i] += d * effective_rate; } } // Check if mutation preserves coherence let new_coherence = self.calculate_coherence(&new_state, system_coherence); if new_coherence < self.coherence_with_system * 0.9 { // Mutation would hurt coherence too much let dampened: Vec = direction.iter().map(|d| d * 0.1).collect(); return MutationResult::Dampened { original_delta: direction, actual_delta: dampened, }; } // Apply mutation let old_state = self.state.clone(); self.state = new_state; self.coherence_with_system = new_coherence; // Bounded history to prevent memory growth if self.history.len() >= MAX_GOAL_HISTORY { self.history.remove(0); } self.history.push(self.state.clone()); // Check for attractor discovery self.check_attractor_discovery(); MutationResult::Mutated { from: old_state, to: self.state.clone(), coherence_delta: new_coherence - self.coherence_with_system, } } fn calculate_mutation_direction(&self, feedback: &GoalFeedback) -> Vec { let mut direction = vec![0.0; self.state.len()]; // Pull toward successful outcomes for (outcome, weight) in &feedback.outcome_weights { for (i, v) in outcome.iter().enumerate() { if i < direction.len() { direction[i] += (v - self.state[i]) * weight; } } } // Pull toward discovered attractors for attractor in &self.discovered_attractors { let dist = self.distance_to(&attractor.center); if dist < attractor.radius * 2.0 { let pull = attractor.strength / (dist + 0.1); for (i, c) in attractor.center.iter().enumerate() { if i < direction.len() { direction[i] += (c - self.state[i]) * pull * 0.1; } } } } // Normalize let mag: f64 = direction.iter().map(|d| d * d).sum::().sqrt(); if mag > 0.01 { direction.iter_mut().for_each(|d| *d /= mag); } direction } fn calculate_coherence(&self, state: &[f64], system_coherence: f64) -> f64 { // Coherence is based on: // 1. Consistency with history (not changing too fast) // 2. Alignment with discovered attractors // 3. System-wide coherence let history_consistency = if let Some(prev) = self.history.last() { let change: f64 = state.iter() .zip(prev) .map(|(a, b)| (a - b).abs()) .sum(); 1.0 / (1.0 + change) } else { 1.0 }; let attractor_alignment = if !self.discovered_attractors.is_empty() { let min_dist = self.discovered_attractors.iter() .map(|a| self.distance_to(&a.center)) .fold(f64::INFINITY, f64::min); 1.0 / (1.0 + min_dist * 0.1) } else { 0.5 }; (history_consistency * 0.3 + attractor_alignment * 0.3 + system_coherence * 0.4) .clamp(0.0, 1.0) } fn check_attractor_discovery(&mut self) { // If we've been near the same point for a while, it's an attractor if self.history.len() < 10 { return; } let recent: Vec<_> = self.history.iter().rev().take(10).collect(); let centroid = self.compute_centroid(&recent); let variance: f64 = recent.iter() .map(|s| self.distance_to_vec(s, ¢roid)) .sum::() / recent.len() as f64; if variance < 0.1 { // Low variance = potential attractor let already_known = self.discovered_attractors.iter() .any(|a| self.distance_to(&a.center) < a.radius); if !already_known { self.discovered_attractors.push(GoalAttractor { center: centroid, strength: 1.0 / (variance + 0.01), radius: variance.sqrt() * 2.0 + 0.1, }); } } } fn compute_centroid(&self, points: &[&Vec]) -> Vec { if points.is_empty() { return self.state.clone(); } let dim = points[0].len(); let mut centroid = vec![0.0; dim]; for p in points { for (i, v) in p.iter().enumerate() { centroid[i] += v; } } centroid.iter_mut().for_each(|c| *c /= points.len() as f64); centroid } fn distance_to(&self, target: &[f64]) -> f64 { self.distance_to_vec(&self.state, target) } fn distance_to_vec(&self, a: &[f64], b: &[f64]) -> f64 { a.iter() .zip(b) .map(|(x, y)| (x - y).powi(2)) .sum::() .sqrt() } } pub struct GoalFeedback { /// Outcomes and their weights (positive = good, negative = bad) pub outcome_weights: Vec<(Vec, f64)>, } impl GoalFeedback { pub fn calculate_pressure(&self) -> f64 { let total_weight: f64 = self.outcome_weights.iter() .map(|(_, w)| w.abs()) .sum(); (total_weight / self.outcome_weights.len().max(1) as f64).min(1.0) } } #[derive(Debug)] pub enum MutationResult { Mutated { from: Vec, to: Vec, coherence_delta: f64, }, Dampened { original_delta: Vec, actual_delta: Vec, }, Blocked { reason: String, }, NoChange, } // ============================================================================= // Part 2: Native Agent Lifecycles at Memory Layer // ============================================================================= /// An agent that exists AS memory, not IN memory pub struct MemoryAgent { /// Unique identifier pub id: u64, /// The agent's state IS its memory vector memory_vector: Vec, /// Lifecycle stage lifecycle: LifecycleStage, /// Age in ticks age: u64, /// Metabolic rate (how fast it processes/decays) metabolism: f64, /// Coherence with environment coherence: f64, /// Spike history (for communication) spike_buffer: SpikeBuffer, /// Goals that can mutate goals: Vec, } #[derive(Clone, Debug, PartialEq)] pub enum LifecycleStage { /// Just created, forming initial structure Embryonic { formation_progress: f64 }, /// Growing and learning Growing { growth_rate: f64 }, /// Mature and stable Mature { stability: f64 }, /// Beginning to decay Senescent { decay_rate: f64 }, /// Final dissolution Dying { dissolution_progress: f64 }, /// No longer exists Dead, } impl MemoryAgent { /// Birth a new agent from seed memory pub fn birth(id: u64, seed: Vec) -> Self { Self { id, memory_vector: seed, lifecycle: LifecycleStage::Embryonic { formation_progress: 0.0 }, age: 0, metabolism: 1.0, coherence: 0.5, // Starts with partial coherence spike_buffer: SpikeBuffer::new(100), goals: Vec::new(), } } /// Tick the agent's lifecycle pub fn tick(&mut self, environment_coherence: f64) -> LifecycleEvent { self.age += 1; self.coherence = self.calculate_coherence(environment_coherence); // Extract values needed for operations to avoid borrow conflicts let current_coherence = self.coherence; let current_age = self.age; let memory_str = self.memory_strength(); // Progress through lifecycle stages match self.lifecycle.clone() { LifecycleStage::Embryonic { formation_progress } => { let new_progress = formation_progress + 0.1 * current_coherence; if new_progress >= 1.0 { self.lifecycle = LifecycleStage::Growing { growth_rate: 0.05 }; return LifecycleEvent::StageTransition { from: "Embryonic".to_string(), to: "Growing".to_string(), }; } self.lifecycle = LifecycleStage::Embryonic { formation_progress: new_progress }; } LifecycleStage::Growing { growth_rate } => { // Grow memory vector (add dimensions or strengthen existing) self.grow(growth_rate); // Transition to mature when growth slows if current_age > 100 && growth_rate < 0.01 { self.lifecycle = LifecycleStage::Mature { stability: current_coherence }; return LifecycleEvent::StageTransition { from: "Growing".to_string(), to: "Mature".to_string(), }; } // Adjust growth rate based on coherence let new_rate = growth_rate * if current_coherence > 0.7 { 1.01 } else { 0.99 }; self.lifecycle = LifecycleStage::Growing { growth_rate: new_rate }; } LifecycleStage::Mature { stability } => { // Mature agents maintain stability let new_stability = (stability * 0.99 + current_coherence * 0.01).clamp(0.0, 1.0); // Begin senescence if stability drops or age is high if new_stability < 0.4 || current_age > 1000 { self.lifecycle = LifecycleStage::Senescent { decay_rate: 0.01 }; return LifecycleEvent::StageTransition { from: "Mature".to_string(), to: "Senescent".to_string(), }; } self.lifecycle = LifecycleStage::Mature { stability: new_stability }; } LifecycleStage::Senescent { decay_rate } => { // Memory begins to decay self.decay(decay_rate); // Accelerate decay with low coherence let new_rate = if current_coherence < 0.3 { decay_rate * 1.1 } else { decay_rate }; // Begin dying when too decayed if memory_str < 0.2 { self.lifecycle = LifecycleStage::Dying { dissolution_progress: 0.0 }; return LifecycleEvent::StageTransition { from: "Senescent".to_string(), to: "Dying".to_string(), }; } self.lifecycle = LifecycleStage::Senescent { decay_rate: new_rate }; } LifecycleStage::Dying { dissolution_progress } => { let new_progress = dissolution_progress + 0.1; self.dissolve(new_progress); if new_progress >= 1.0 { self.lifecycle = LifecycleStage::Dead; return LifecycleEvent::Death { age: current_age }; } self.lifecycle = LifecycleStage::Dying { dissolution_progress: new_progress }; } LifecycleStage::Dead => { return LifecycleEvent::AlreadyDead; } } LifecycleEvent::None } fn calculate_coherence(&self, environment_coherence: f64) -> f64 { // Coherence based on memory vector structure let internal_coherence = self.memory_strength(); // Blend with environment (internal_coherence * 0.6 + environment_coherence * 0.4).clamp(0.0, 1.0) } fn memory_strength(&self) -> f64 { if self.memory_vector.is_empty() { return 0.0; } let magnitude: f64 = self.memory_vector.iter().map(|v| v * v).sum::().sqrt(); let dim = self.memory_vector.len() as f64; (magnitude / dim.sqrt()).min(1.0) } fn grow(&mut self, rate: f64) { // Strengthen existing memories for v in &mut self.memory_vector { *v *= 1.0 + rate * 0.1; } } fn decay(&mut self, rate: f64) { // Weaken memories for v in &mut self.memory_vector { *v *= 1.0 - rate; } } fn dissolve(&mut self, progress: f64) { // Zero out memory proportionally let threshold = progress; for v in &mut self.memory_vector { if v.abs() < threshold { *v = 0.0; } } } /// Attempt to reproduce (create offspring agent) pub fn reproduce(&self) -> Option { // Can only reproduce when mature and coherent if !matches!(self.lifecycle, LifecycleStage::Mature { stability } if stability > 0.6) { return None; } if self.coherence < 0.7 { return None; } // Create offspring with mutated memory let mut offspring_memory = self.memory_vector.clone(); for v in &mut offspring_memory { *v *= 0.9 + pseudo_random_f64() * 0.2; // Small mutation } Some(MemoryAgent::birth( self.id * 1000 + self.age, offspring_memory, )) } pub fn is_alive(&self) -> bool { !matches!(self.lifecycle, LifecycleStage::Dead) } } #[derive(Debug)] pub enum LifecycleEvent { None, StageTransition { from: String, to: String }, Death { age: u64 }, AlreadyDead, } // ============================================================================= // Part 3: Hardware-Enforced Spike/Silence Semantics // ============================================================================= /// A spike buffer that enforces spike/silence semantics pub struct SpikeBuffer { /// Spike times (as tick numbers) spikes: Vec, /// Maximum spikes in buffer capacity: usize, /// Current tick current_tick: u64, /// Refractory period (minimum ticks between spikes) refractory_period: u64, /// Last spike time last_spike: u64, /// Energy cost per spike spike_cost: f64, /// Current energy energy: f64, /// Silence counter (ticks since last spike) silence_duration: u64, } impl SpikeBuffer { pub fn new(capacity: usize) -> Self { Self { spikes: Vec::with_capacity(capacity), capacity, current_tick: 0, refractory_period: 3, last_spike: 0, spike_cost: 1.0, energy: 100.0, silence_duration: 0, } } /// Attempt to emit a spike pub fn spike(&mut self, strength: f64) -> SpikeResult { self.current_tick += 1; // Check refractory period if self.current_tick - self.last_spike < self.refractory_period { self.silence_duration += 1; return SpikeResult::Refractory { ticks_remaining: self.refractory_period - (self.current_tick - self.last_spike), }; } // Check energy let cost = self.spike_cost * strength; if self.energy < cost { self.silence_duration += 1; return SpikeResult::InsufficientEnergy { required: cost, available: self.energy, }; } // Emit spike self.energy -= cost; self.last_spike = self.current_tick; self.spikes.push(self.current_tick); // Maintain capacity if self.spikes.len() > self.capacity { self.spikes.remove(0); } let silence_was = self.silence_duration; self.silence_duration = 0; SpikeResult::Emitted { tick: self.current_tick, strength, silence_before: silence_was, } } /// Advance time without spiking (silence) pub fn silence(&mut self) { self.current_tick += 1; self.silence_duration += 1; // Energy slowly regenerates during silence self.energy = (self.energy + 0.5).min(100.0); } /// Get spike rate (spikes per tick in recent window) pub fn spike_rate(&self, window: u64) -> f64 { let min_tick = self.current_tick.saturating_sub(window); let recent_spikes = self.spikes.iter() .filter(|&&t| t >= min_tick) .count(); recent_spikes as f64 / window as f64 } /// Check if in silence (no recent spikes) pub fn is_silent(&self, threshold: u64) -> bool { self.silence_duration >= threshold } } #[derive(Debug)] pub enum SpikeResult { /// Spike successfully emitted Emitted { tick: u64, strength: f64, silence_before: u64, }, /// In refractory period, cannot spike Refractory { ticks_remaining: u64 }, /// Not enough energy to spike InsufficientEnergy { required: f64, available: f64 }, } // ============================================================================= // Part 4: The Complete Extropic Substrate // ============================================================================= /// The complete extropic intelligence substrate pub struct ExtropicSubstrate { /// All agents in the substrate agents: HashMap, /// Global coherence coherence: f64, /// Spike bus for inter-agent communication spike_bus: SpikeBus, /// Current tick tick: u64, /// Next agent ID next_agent_id: AtomicU64, /// Configuration config: SubstrateConfig, } struct SpikeBus { /// Recent spikes from all agents spikes: Vec<(u64, u64, f64)>, // (agent_id, tick, strength) /// Maximum bus capacity capacity: usize, } struct SubstrateConfig { /// Maximum agents max_agents: usize, /// Minimum global coherence min_coherence: f64, /// Birth rate control birth_rate_limit: f64, } impl ExtropicSubstrate { pub fn new(max_agents: usize) -> Self { Self { agents: HashMap::new(), coherence: 1.0, spike_bus: SpikeBus { spikes: Vec::new(), capacity: 1000, }, tick: 0, next_agent_id: AtomicU64::new(1), config: SubstrateConfig { max_agents, min_coherence: 0.3, birth_rate_limit: 0.1, }, } } /// Spawn a new agent into the substrate pub fn spawn(&mut self, seed: Vec) -> Option { if self.agents.len() >= self.config.max_agents { return None; } if self.coherence < self.config.min_coherence { return None; // Too incoherent to spawn } let id = self.next_agent_id.fetch_add(1, Ordering::SeqCst); let agent = MemoryAgent::birth(id, seed); self.agents.insert(id, agent); Some(id) } /// Tick the entire substrate pub fn tick(&mut self) -> SubstrateTick { self.tick += 1; let mut events = Vec::new(); let mut births = Vec::new(); let mut deaths = Vec::new(); // Get agent count for birth rate calculation let agent_count = self.agents.len(); let current_coherence = self.coherence; // Tick all agents for (id, agent) in &mut self.agents { let event = agent.tick(current_coherence); match &event { LifecycleEvent::Death { age } => { deaths.push(*id); events.push((*id, format!("Death at age {}", age))); } LifecycleEvent::StageTransition { from, to } => { events.push((*id, format!("Transition: {} -> {}", from, to))); } _ => {} } // Check for reproduction if agent_count > 0 { if let Some(offspring) = agent.reproduce() { if births.len() as f64 / agent_count as f64 <= self.config.birth_rate_limit { births.push(offspring); } } } } // Remove dead agents for id in &deaths { self.agents.remove(id); } // Count births before consuming the vector let birth_count = births.len(); // Add offspring for offspring in births { let id = offspring.id; if self.agents.len() < self.config.max_agents { self.agents.insert(id, offspring); events.push((id, "Born".to_string())); } } // Update global coherence self.coherence = self.calculate_global_coherence(); SubstrateTick { tick: self.tick, agent_count: self.agents.len(), coherence: self.coherence, births: birth_count, deaths: deaths.len(), events, } } fn calculate_global_coherence(&self) -> f64 { if self.agents.is_empty() { return 1.0; } let total: f64 = self.agents.values() .filter(|a| a.is_alive()) .map(|a| a.coherence) .sum(); let alive_count = self.agents.values().filter(|a| a.is_alive()).count(); if alive_count == 0 { return 1.0; } total / alive_count as f64 } pub fn agent_count(&self) -> usize { self.agents.len() } pub fn coherence(&self) -> f64 { self.coherence } pub fn status(&self) -> String { let alive = self.agents.values().filter(|a| a.is_alive()).count(); let stages: HashMap = self.agents.values() .map(|a| match &a.lifecycle { LifecycleStage::Embryonic { .. } => "Embryonic", LifecycleStage::Growing { .. } => "Growing", LifecycleStage::Mature { .. } => "Mature", LifecycleStage::Senescent { .. } => "Senescent", LifecycleStage::Dying { .. } => "Dying", LifecycleStage::Dead => "Dead", }) .fold(HashMap::new(), |mut acc, s| { *acc.entry(s.to_string()).or_insert(0) += 1; acc }); format!( "Tick {} | Coherence: {:.3} | Alive: {} | Stages: {:?}", self.tick, self.coherence, alive, stages ) } } #[derive(Debug)] pub struct SubstrateTick { pub tick: u64, pub agent_count: usize, pub coherence: f64, pub births: usize, pub deaths: usize, pub events: Vec<(u64, String)>, } // Simple pseudo-random using atomic counter fn pseudo_random_f64() -> f64 { static SEED: AtomicU64 = AtomicU64::new(42); let s = SEED.fetch_add(1, Ordering::Relaxed); let x = s.wrapping_mul(0x5DEECE66D).wrapping_add(0xB); ((x >> 16) & 0xFFFF) as f64 / 65536.0 } #[cfg(test)] mod tests { use super::*; #[test] fn test_goal_mutation() { let mut goal = MutableGoal::new(vec![1.0, 0.0, 0.0]); let feedback = GoalFeedback { outcome_weights: vec![ (vec![0.5, 0.5, 0.0], 0.8), // Good outcome nearby (vec![0.0, 1.0, 0.0], -0.3), // Bad outcome to avoid ], }; println!("Initial goal: {:?}", goal.state); for i in 0..20 { let result = goal.mutate(&feedback, 0.8); println!("Mutation {}: {:?}", i, result); println!(" State: {:?}", goal.state); println!(" Attractors discovered: {}", goal.discovered_attractors.len()); } // Goal should have moved assert!(goal.state[0] != 1.0 || goal.state[1] != 0.0, "Goal should have mutated"); } #[test] fn test_agent_lifecycle() { let mut agent = MemoryAgent::birth(1, vec![1.0, 1.0, 1.0, 1.0]); println!("Initial: {:?}", agent.lifecycle); let mut stage_changes = 0; for tick in 0..2000 { let event = agent.tick(0.8); if let LifecycleEvent::StageTransition { from, to } = &event { println!("Tick {}: {} -> {}", tick, from, to); stage_changes += 1; } if let LifecycleEvent::Death { age } = &event { println!("Agent died at age {}", age); break; } } assert!(stage_changes >= 2, "Should have gone through multiple stages"); } #[test] fn test_spike_buffer() { let mut buffer = SpikeBuffer::new(10); // Try to spike rapidly let mut emitted = 0; let mut blocked = 0; for _ in 0..20 { match buffer.spike(1.0) { SpikeResult::Emitted { silence_before, .. } => { println!("Spike! Silence before: {}", silence_before); emitted += 1; } SpikeResult::Refractory { ticks_remaining } => { println!("Refractory: {} ticks remaining", ticks_remaining); blocked += 1; buffer.silence(); // Advance time } SpikeResult::InsufficientEnergy { .. } => { println!("No energy"); blocked += 1; buffer.silence(); } } } println!("Emitted: {}, Blocked: {}", emitted, blocked); assert!(blocked > 0, "Refractory period should block some spikes"); } #[test] fn test_extropic_substrate() { let mut substrate = ExtropicSubstrate::new(50); // Spawn initial agents for i in 0..10 { let seed = vec![1.0, (i as f64) * 0.1, 0.5, 0.5]; substrate.spawn(seed); } println!("Initial: {}", substrate.status()); // Run simulation for tick in 0..500 { let result = substrate.tick(); if tick % 50 == 0 || result.births > 0 || result.deaths > 0 { println!("Tick {}: births={}, deaths={}, agents={}", tick, result.births, result.deaths, result.agent_count); println!(" {}", substrate.status()); } for (agent_id, event) in &result.events { if !event.is_empty() { println!(" Agent {}: {}", agent_id, event); } } } println!("\nFinal: {}", substrate.status()); // Substrate should still be coherent assert!(substrate.coherence() > 0.3, "Substrate should maintain coherence"); } #[test] fn test_reproduction() { let mut substrate = ExtropicSubstrate::new(100); // Spawn a few agents for _ in 0..5 { substrate.spawn(vec![1.0, 1.0, 1.0, 1.0]); } let initial_count = substrate.agent_count(); // Run until reproduction happens let mut reproductions = 0; for _ in 0..1000 { let result = substrate.tick(); reproductions += result.births; if reproductions > 0 { break; } } // May or may not reproduce depending on lifecycle timing println!("Reproductions: {}", reproductions); println!("Final count: {} (started with {})", substrate.agent_count(), initial_count); } }