Files
wifi-densepose/examples/delta-behavior/applications/04-self-stabilizing-world-model.rs
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

554 lines
18 KiB
Rust

//! # Application 4: Self-Stabilizing World Models
//!
//! The world model is allowed to update only if the global structure remains intact.
//!
//! ## What Breaks Today
//! World models drift until they are no longer useful.
//!
//! ## Exotic Effect
//! The model stops learning when the world becomes incoherent
//! instead of hallucinating structure.
//!
//! ## Critical For
//! - Always-on perception
//! - Autonomous exploration
//! - Robotics in unknown environments
use std::collections::HashMap;
/// A world model that refuses to learn incoherent updates
pub struct SelfStabilizingWorldModel {
/// Entities in the world
entities: HashMap<EntityId, Entity>,
/// Relationships between entities
relationships: Vec<Relationship>,
/// Physical laws the model believes
laws: Vec<PhysicalLaw>,
/// Current coherence of the model
coherence: f64,
/// History of coherence for trend detection
coherence_history: Vec<f64>,
/// Learning rate (decreases under low coherence)
base_learning_rate: f64,
/// Minimum coherence to allow updates
min_update_coherence: f64,
/// Updates that were rejected
rejected_updates: Vec<RejectedUpdate>,
}
type EntityId = u64;
#[derive(Clone, Debug)]
pub struct Entity {
pub id: EntityId,
pub properties: HashMap<String, PropertyValue>,
pub position: Option<(f64, f64, f64)>,
pub last_observed: u64,
pub confidence: f64,
}
#[derive(Clone, Debug)]
pub enum PropertyValue {
Boolean(bool),
Number(f64),
String(String),
Vector(Vec<f64>),
}
#[derive(Clone, Debug)]
pub struct Relationship {
pub subject: EntityId,
pub predicate: String,
pub object: EntityId,
pub confidence: f64,
}
#[derive(Clone, Debug)]
pub struct PhysicalLaw {
pub name: String,
pub confidence: f64,
/// Number of observations supporting this law
pub support_count: u64,
/// Number of observations violating this law
pub violation_count: u64,
}
#[derive(Debug)]
pub struct Observation {
pub entity_id: EntityId,
pub properties: HashMap<String, PropertyValue>,
pub position: Option<(f64, f64, f64)>,
pub timestamp: u64,
pub source_confidence: f64,
}
#[derive(Debug)]
pub enum UpdateResult {
/// Update applied successfully
Applied { coherence_change: f64 },
/// Update rejected to preserve coherence
Rejected { reason: RejectionReason },
/// Update partially applied with modifications
Modified { changes: Vec<String>, coherence_change: f64 },
/// Model entered "uncertain" mode - no updates allowed
Frozen { coherence: f64, threshold: f64 },
}
#[derive(Debug, Clone)]
pub struct RejectedUpdate {
pub observation: String,
pub reason: RejectionReason,
pub timestamp: u64,
pub coherence_at_rejection: f64,
}
#[derive(Debug, Clone)]
pub enum RejectionReason {
/// Would violate established physical laws
ViolatesPhysicalLaw(String),
/// Would create logical contradiction
LogicalContradiction(String),
/// Would cause excessive coherence drop
ExcessiveCoherenceDrop { predicted: f64, threshold: f64 },
/// Source confidence too low for this change
InsufficientConfidence { required: f64, provided: f64 },
/// Model is in frozen state
ModelFrozen,
/// Would fragment world structure
StructuralFragmentation,
}
impl SelfStabilizingWorldModel {
pub fn new() -> Self {
Self {
entities: HashMap::new(),
relationships: Vec::new(),
laws: vec![
PhysicalLaw {
name: "conservation_of_matter".to_string(),
confidence: 0.99,
support_count: 1000,
violation_count: 0,
},
PhysicalLaw {
name: "locality".to_string(),
confidence: 0.95,
support_count: 500,
violation_count: 5,
},
PhysicalLaw {
name: "temporal_consistency".to_string(),
confidence: 0.98,
support_count: 800,
violation_count: 2,
},
],
coherence: 1.0,
coherence_history: vec![1.0],
base_learning_rate: 0.1,
min_update_coherence: 0.4,
rejected_updates: Vec::new(),
}
}
/// Current effective learning rate (decreases with low coherence)
pub fn effective_learning_rate(&self) -> f64 {
self.base_learning_rate * self.coherence.powi(2)
}
/// Is the model currently accepting updates?
pub fn is_learning(&self) -> bool {
self.coherence >= self.min_update_coherence
}
/// Attempt to integrate an observation into the world model
pub fn observe(&mut self, observation: Observation, timestamp: u64) -> UpdateResult {
// Check if model is frozen
if !self.is_learning() {
return UpdateResult::Frozen {
coherence: self.coherence,
threshold: self.min_update_coherence,
};
}
// Predict coherence impact
let predicted_coherence = self.predict_coherence_after(&observation);
// Would this drop coherence too much?
let coherence_drop = self.coherence - predicted_coherence;
if coherence_drop > 0.2 {
self.rejected_updates.push(RejectedUpdate {
observation: format!("Entity {} update", observation.entity_id),
reason: RejectionReason::ExcessiveCoherenceDrop {
predicted: predicted_coherence,
threshold: self.coherence - 0.2,
},
timestamp,
coherence_at_rejection: self.coherence,
});
return UpdateResult::Rejected {
reason: RejectionReason::ExcessiveCoherenceDrop {
predicted: predicted_coherence,
threshold: self.coherence - 0.2,
},
};
}
// Check physical law violations
if let Some(violation) = self.check_law_violations(&observation) {
self.rejected_updates.push(RejectedUpdate {
observation: format!("Entity {} update", observation.entity_id),
reason: violation.clone(),
timestamp,
coherence_at_rejection: self.coherence,
});
return UpdateResult::Rejected { reason: violation };
}
// Check logical consistency
if let Some(contradiction) = self.check_contradictions(&observation) {
self.rejected_updates.push(RejectedUpdate {
observation: format!("Entity {} update", observation.entity_id),
reason: contradiction.clone(),
timestamp,
coherence_at_rejection: self.coherence,
});
return UpdateResult::Rejected { reason: contradiction };
}
// Apply the update
self.apply_observation(observation, timestamp);
// Recalculate coherence
let old_coherence = self.coherence;
self.coherence = self.calculate_coherence();
self.coherence_history.push(self.coherence);
// Trim history
if self.coherence_history.len() > 100 {
self.coherence_history.remove(0);
}
UpdateResult::Applied {
coherence_change: self.coherence - old_coherence,
}
}
fn predict_coherence_after(&self, observation: &Observation) -> f64 {
// Simulate the update's impact on coherence
let mut consistency_score = 1.0;
if let Some(existing) = self.entities.get(&observation.entity_id) {
// How much does this differ from existing knowledge?
for (key, new_value) in &observation.properties {
if let Some(old_value) = existing.properties.get(key) {
let diff = self.property_difference(old_value, new_value);
consistency_score *= 1.0 - (diff * 0.5);
}
}
// Position change check (locality)
if let (Some(old_pos), Some(new_pos)) = (&existing.position, &observation.position) {
let distance = ((new_pos.0 - old_pos.0).powi(2)
+ (new_pos.1 - old_pos.1).powi(2)
+ (new_pos.2 - old_pos.2).powi(2))
.sqrt();
// Large sudden movements are suspicious
if distance > 10.0 {
consistency_score *= 0.7;
}
}
}
self.coherence * consistency_score
}
fn property_difference(&self, old: &PropertyValue, new: &PropertyValue) -> f64 {
match (old, new) {
(PropertyValue::Number(a), PropertyValue::Number(b)) => {
let max = a.abs().max(b.abs()).max(1.0);
((a - b).abs() / max).min(1.0)
}
(PropertyValue::Boolean(a), PropertyValue::Boolean(b)) => {
if a == b { 0.0 } else { 1.0 }
}
(PropertyValue::String(a), PropertyValue::String(b)) => {
if a == b { 0.0 } else { 0.5 }
}
_ => 0.5, // Different types
}
}
fn check_law_violations(&self, observation: &Observation) -> Option<RejectionReason> {
if let Some(existing) = self.entities.get(&observation.entity_id) {
// Check locality violation (teleportation)
if let (Some(old_pos), Some(new_pos)) = (&existing.position, &observation.position) {
let distance = ((new_pos.0 - old_pos.0).powi(2)
+ (new_pos.1 - old_pos.1).powi(2)
+ (new_pos.2 - old_pos.2).powi(2))
.sqrt();
// If object moved impossibly fast
let max_speed = 100.0; // units per timestamp
if distance > max_speed {
return Some(RejectionReason::ViolatesPhysicalLaw(
format!("locality: object moved {} units instantaneously", distance)
));
}
}
}
None
}
fn check_contradictions(&self, observation: &Observation) -> Option<RejectionReason> {
// Check for direct contradictions with high-confidence existing data
if let Some(existing) = self.entities.get(&observation.entity_id) {
if existing.confidence > 0.9 {
for (key, new_value) in &observation.properties {
if let Some(old_value) = existing.properties.get(key) {
let diff = self.property_difference(old_value, new_value);
if diff > 0.9 && observation.source_confidence < existing.confidence {
return Some(RejectionReason::LogicalContradiction(
format!("Property {} contradicts high-confidence existing data", key)
));
}
}
}
}
}
None
}
fn apply_observation(&mut self, observation: Observation, timestamp: u64) {
let learning_rate = self.effective_learning_rate();
// Pre-compute blended values to avoid borrow conflict
let blended_properties: Vec<(String, PropertyValue)> = observation.properties
.into_iter()
.map(|(key, new_value)| {
let blended = if let Some(entity) = self.entities.get(&observation.entity_id) {
if let Some(old_value) = entity.properties.get(&key) {
self.blend_values(old_value, &new_value, learning_rate)
} else {
new_value
}
} else {
new_value
};
(key, blended)
})
.collect();
let entity = self.entities.entry(observation.entity_id).or_insert(Entity {
id: observation.entity_id,
properties: HashMap::new(),
position: None,
last_observed: 0,
confidence: 0.5,
});
// Apply pre-computed blended values
for (key, blended) in blended_properties {
entity.properties.insert(key, blended);
}
// Update position
if let Some(new_pos) = observation.position {
if let Some(old_pos) = entity.position {
// Smooth position update
entity.position = Some((
old_pos.0 + learning_rate * (new_pos.0 - old_pos.0),
old_pos.1 + learning_rate * (new_pos.1 - old_pos.1),
old_pos.2 + learning_rate * (new_pos.2 - old_pos.2),
));
} else {
entity.position = Some(new_pos);
}
}
entity.last_observed = timestamp;
// Update confidence
entity.confidence = entity.confidence * 0.9 + observation.source_confidence * 0.1;
}
fn blend_values(&self, old: &PropertyValue, new: &PropertyValue, rate: f64) -> PropertyValue {
match (old, new) {
(PropertyValue::Number(a), PropertyValue::Number(b)) => {
PropertyValue::Number(a + rate * (b - a))
}
_ => new.clone(), // For non-numeric, just use new if rate > 0.5
}
}
fn calculate_coherence(&self) -> f64 {
if self.entities.is_empty() {
return 1.0;
}
let mut scores = Vec::new();
// 1. Internal consistency of entities
for entity in self.entities.values() {
scores.push(entity.confidence);
}
// 2. Relationship consistency
for rel in &self.relationships {
if self.entities.contains_key(&rel.subject) && self.entities.contains_key(&rel.object) {
scores.push(rel.confidence);
} else {
scores.push(0.0); // Dangling relationship
}
}
// 3. Physical law confidence
for law in &self.laws {
scores.push(law.confidence);
}
// 4. Temporal coherence (recent observations should be consistent)
let recent_variance = self.calculate_recent_variance();
scores.push(1.0 - recent_variance);
// Geometric mean of all scores
if scores.is_empty() {
1.0
} else {
let product: f64 = scores.iter().product();
product.powf(1.0 / scores.len() as f64)
}
}
fn calculate_recent_variance(&self) -> f64 {
if self.coherence_history.len() < 2 {
return 0.0;
}
let recent: Vec<f64> = self.coherence_history.iter().rev().take(10).cloned().collect();
let mean: f64 = recent.iter().sum::<f64>() / recent.len() as f64;
let variance: f64 = recent.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / recent.len() as f64;
variance.sqrt().min(1.0)
}
/// Get count of rejected updates
pub fn rejection_count(&self) -> usize {
self.rejected_updates.len()
}
/// Get model status
pub fn status(&self) -> String {
format!(
"WorldModel | Coherence: {:.3} | Entities: {} | Learning: {} | Rejections: {}",
self.coherence,
self.entities.len(),
if self.is_learning() { "ON" } else { "FROZEN" },
self.rejected_updates.len()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coherent_learning() {
let mut model = SelfStabilizingWorldModel::new();
// Feed consistent observations
for i in 0..10 {
let obs = Observation {
entity_id: 1,
properties: [("temperature".to_string(), PropertyValue::Number(20.0 + i as f64 * 0.1))].into(),
position: Some((i as f64, 0.0, 0.0)),
timestamp: i as u64,
source_confidence: 0.9,
};
let result = model.observe(obs, i as u64);
assert!(matches!(result, UpdateResult::Applied { .. }));
}
println!("{}", model.status());
assert!(model.coherence > 0.8);
}
#[test]
fn test_rejects_incoherent_update() {
let mut model = SelfStabilizingWorldModel::new();
// Establish entity at position
let obs1 = Observation {
entity_id: 1,
properties: HashMap::new(),
position: Some((0.0, 0.0, 0.0)),
timestamp: 0,
source_confidence: 0.95,
};
model.observe(obs1, 0);
// Try to teleport it (violates locality)
let obs2 = Observation {
entity_id: 1,
properties: HashMap::new(),
position: Some((1000.0, 0.0, 0.0)), // Impossibly far
timestamp: 1,
source_confidence: 0.5,
};
let result = model.observe(obs2, 1);
println!("Teleport result: {:?}", result);
// Should be rejected
assert!(matches!(result, UpdateResult::Rejected { .. }));
println!("{}", model.status());
}
#[test]
fn test_freezes_under_chaos() {
let mut model = SelfStabilizingWorldModel::new();
// Feed chaotic, contradictory observations
for i in 0..100 {
let obs = Observation {
entity_id: (i % 5) as u64,
properties: [
("value".to_string(), PropertyValue::Number(if i % 2 == 0 { 100.0 } else { -100.0 }))
].into(),
position: Some((
(i as f64 * 10.0) % 50.0 - 25.0,
(i as f64 * 7.0) % 50.0 - 25.0,
0.0
)),
timestamp: i as u64,
source_confidence: 0.3,
};
let result = model.observe(obs, i as u64);
if matches!(result, UpdateResult::Frozen { .. }) {
println!("Model FROZE at step {} - stopped hallucinating!", i);
println!("{}", model.status());
return; // Test passes - model stopped itself
}
}
println!("Final: {}", model.status());
// Model should have either frozen or heavily rejected updates
assert!(
model.rejection_count() > 20 || model.coherence < 0.5,
"Model should resist chaotic input"
);
}
}