Files
wifi-densepose/crates/ruvector-nervous-system/examples/tiers/t3_synthetic_nervous.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

703 lines
21 KiB
Rust

//! # Tier 3: Synthetic Nervous Systems for Environments
//!
//! Buildings, factories, cities.
//!
//! ## What Changes
//! - Infrastructure becomes a sensing fabric
//! - Reflexes manage local events
//! - Policy emerges from patterns, not rules
//!
//! ## Why This Matters
//! - Environments respond like organisms
//! - Energy, safety, and flow self-regulate
//! - Central planning gives way to distributed intelligence
//!
//! This is exotic but inevitable.
use std::collections::{HashMap, VecDeque};
use std::f32::consts::PI;
/// A location in the environment
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct LocationId(pub String);
/// A zone grouping multiple locations
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ZoneId(pub String);
/// Environmental sensor reading
#[derive(Clone, Debug)]
pub struct EnvironmentReading {
pub timestamp: u64,
pub location: LocationId,
pub sensor_type: EnvironmentSensor,
pub value: f32,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum EnvironmentSensor {
Temperature,
Humidity,
Light,
Occupancy,
AirQuality,
Noise,
Motion,
Energy,
Water,
}
/// Environmental actuator command
#[derive(Clone, Debug)]
pub struct EnvironmentAction {
pub location: LocationId,
pub actuator: EnvironmentActuator,
pub value: f32,
pub priority: u8,
}
#[derive(Clone, Debug)]
pub enum EnvironmentActuator {
HVAC { mode: HVACMode },
Lighting { brightness: f32 },
Ventilation { flow_rate: f32 },
Shading { position: f32 },
DoorLock { locked: bool },
Alarm { active: bool },
}
#[derive(Clone, Debug)]
pub enum HVACMode {
Off,
Heating(f32),
Cooling(f32),
Ventilation,
}
/// Local reflex for immediate environmental response
pub struct LocalEnvironmentReflex {
pub location: LocationId,
pub sensor_type: EnvironmentSensor,
pub threshold_low: f32,
pub threshold_high: f32,
pub action_low: EnvironmentActuator,
pub action_high: EnvironmentActuator,
pub hysteresis: f32,
pub last_state: i8, // -1 = below, 0 = normal, 1 = above
}
impl LocalEnvironmentReflex {
pub fn new(
location: LocationId,
sensor_type: EnvironmentSensor,
threshold_low: f32,
threshold_high: f32,
action_low: EnvironmentActuator,
action_high: EnvironmentActuator,
) -> Self {
Self {
location,
sensor_type,
threshold_low,
threshold_high,
action_low,
action_high,
hysteresis: 0.5,
last_state: 0,
}
}
/// Check if reflex should fire
pub fn check(&mut self, reading: &EnvironmentReading) -> Option<EnvironmentAction> {
if reading.location != self.location || reading.sensor_type != self.sensor_type {
return None;
}
// Apply hysteresis
let effective_low = if self.last_state == -1 {
self.threshold_low + self.hysteresis
} else {
self.threshold_low
};
let effective_high = if self.last_state == 1 {
self.threshold_high - self.hysteresis
} else {
self.threshold_high
};
if reading.value < effective_low && self.last_state != -1 {
self.last_state = -1;
Some(EnvironmentAction {
location: self.location.clone(),
actuator: self.action_low.clone(),
value: reading.value,
priority: 1,
})
} else if reading.value > effective_high && self.last_state != 1 {
self.last_state = 1;
Some(EnvironmentAction {
location: self.location.clone(),
actuator: self.action_high.clone(),
value: reading.value,
priority: 1,
})
} else if reading.value >= effective_low && reading.value <= effective_high {
self.last_state = 0;
None
} else {
None
}
}
}
/// Zone-level homeostasis controller
pub struct ZoneHomeostasis {
pub zone: ZoneId,
pub locations: Vec<LocationId>,
pub target_temperature: f32,
pub target_humidity: f32,
pub target_light: f32,
pub adaptation_rate: f32,
/// Learned occupancy pattern (24 hours)
pub occupancy_pattern: [f32; 24],
pub learning_enabled: bool,
}
impl ZoneHomeostasis {
pub fn new(zone: ZoneId, locations: Vec<LocationId>) -> Self {
Self {
zone,
locations,
target_temperature: 22.0,
target_humidity: 50.0,
target_light: 500.0,
adaptation_rate: 0.1,
occupancy_pattern: [0.0; 24],
learning_enabled: true,
}
}
/// Learn from occupancy patterns
pub fn learn_occupancy(&mut self, hour: usize, occupancy: f32) {
if self.learning_enabled && hour < 24 {
self.occupancy_pattern[hour] = self.occupancy_pattern[hour]
* (1.0 - self.adaptation_rate)
+ occupancy * self.adaptation_rate;
}
}
/// Predict occupancy for pre-conditioning
pub fn predict_occupancy(&self, hour: usize) -> f32 {
if hour < 24 {
self.occupancy_pattern[hour]
} else {
0.0
}
}
/// Compute zone-level action based on aggregate readings
pub fn compute_action(
&self,
readings: &[EnvironmentReading],
hour: usize,
) -> Vec<EnvironmentAction> {
let mut actions = Vec::new();
// Filter readings for this zone
let zone_readings: Vec<_> = readings
.iter()
.filter(|r| self.locations.contains(&r.location))
.collect();
if zone_readings.is_empty() {
return actions;
}
// Average temperature
let temp_readings: Vec<_> = zone_readings
.iter()
.filter(|r| r.sensor_type == EnvironmentSensor::Temperature)
.collect();
if !temp_readings.is_empty() {
let avg_temp: f32 =
temp_readings.iter().map(|r| r.value).sum::<f32>() / temp_readings.len() as f32;
// Adjust target based on predicted occupancy
let predicted_occ = self.predict_occupancy(hour);
let effective_target = if predicted_occ > 0.5 {
self.target_temperature
} else {
// Setback when unoccupied
self.target_temperature - 2.0
};
let temp_error = avg_temp - effective_target;
if temp_error.abs() > 1.0 {
let mode = if temp_error > 0.0 {
HVACMode::Cooling(temp_error.abs().min(5.0))
} else {
HVACMode::Heating(temp_error.abs().min(5.0))
};
for loc in &self.locations {
actions.push(EnvironmentAction {
location: loc.clone(),
actuator: EnvironmentActuator::HVAC { mode: mode.clone() },
value: temp_error,
priority: 2,
});
}
}
}
// Light based on occupancy
let occupancy_readings: Vec<_> = zone_readings
.iter()
.filter(|r| r.sensor_type == EnvironmentSensor::Occupancy)
.collect();
if !occupancy_readings.is_empty() {
let occupied = occupancy_readings.iter().any(|r| r.value > 0.5);
for loc in &self.locations {
let brightness = if occupied { 1.0 } else { 0.1 };
actions.push(EnvironmentAction {
location: loc.clone(),
actuator: EnvironmentActuator::Lighting { brightness },
value: brightness,
priority: 3,
});
}
}
actions
}
}
/// Global workspace for environment-wide coordination
pub struct EnvironmentWorkspace {
pub capacity: usize,
pub items: VecDeque<WorkspaceItem>,
pub policies: Vec<EmergentPolicy>,
}
#[derive(Clone, Debug)]
pub struct WorkspaceItem {
pub zone: ZoneId,
pub observation: String,
pub salience: f32,
pub timestamp: u64,
}
#[derive(Clone, Debug)]
pub struct EmergentPolicy {
pub name: String,
pub trigger_pattern: String,
pub action_pattern: String,
pub confidence: f32,
pub occurrences: u64,
}
impl EnvironmentWorkspace {
pub fn new(capacity: usize) -> Self {
Self {
capacity,
items: VecDeque::new(),
policies: Vec::new(),
}
}
/// Broadcast observation to workspace
pub fn broadcast(&mut self, item: WorkspaceItem) {
if self.items.len() >= self.capacity {
// Remove lowest salience
if let Some(min_idx) = self
.items
.iter()
.enumerate()
.min_by(|(_, a), (_, b)| a.salience.partial_cmp(&b.salience).unwrap())
.map(|(i, _)| i)
{
self.items.remove(min_idx);
}
}
self.items.push_back(item);
}
/// Detect emergent patterns
pub fn detect_patterns(&mut self) -> Option<EmergentPolicy> {
// Look for repeated sequences in workspace
let observations: Vec<_> = self.items.iter().map(|i| i.observation.clone()).collect();
if observations.len() < 3 {
return None;
}
// Simple pattern: if same observation repeats
let last = observations.last()?;
let count = observations.iter().filter(|o| *o == last).count();
if count >= 3 {
let policy = EmergentPolicy {
name: format!("Pattern_{}", self.policies.len()),
trigger_pattern: last.clone(),
action_pattern: "coordinate_response".to_string(),
confidence: count as f32 / observations.len() as f32,
occurrences: 1,
};
// Check if already known
if !self
.policies
.iter()
.any(|p| p.trigger_pattern == last.clone())
{
self.policies.push(policy.clone());
return Some(policy);
}
}
None
}
}
/// Complete synthetic nervous system for an environment
pub struct SyntheticNervousSystem {
pub name: String,
/// Local reflexes (fast, location-specific)
pub reflexes: Vec<LocalEnvironmentReflex>,
/// Zone homeostasis (medium, zone-level)
pub zones: HashMap<ZoneId, ZoneHomeostasis>,
/// Global workspace (slow, environment-wide)
pub workspace: EnvironmentWorkspace,
/// Current time
pub timestamp: u64,
/// Action history
pub action_log: Vec<(u64, EnvironmentAction)>,
}
impl SyntheticNervousSystem {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
reflexes: Vec::new(),
zones: HashMap::new(),
workspace: EnvironmentWorkspace::new(7),
timestamp: 0,
action_log: Vec::new(),
}
}
/// Add a zone
pub fn add_zone(&mut self, zone_id: &str, locations: Vec<&str>) {
let zone = ZoneId(zone_id.to_string());
let locs: Vec<_> = locations
.iter()
.map(|l| LocationId(l.to_string()))
.collect();
self.zones
.insert(zone.clone(), ZoneHomeostasis::new(zone, locs));
}
/// Add a local reflex
pub fn add_reflex(&mut self, reflex: LocalEnvironmentReflex) {
self.reflexes.push(reflex);
}
/// Process sensor readings through the nervous system
pub fn process(&mut self, readings: Vec<EnvironmentReading>) -> Vec<EnvironmentAction> {
self.timestamp += 1;
let hour = ((self.timestamp / 60) % 24) as usize;
let mut actions = Vec::new();
// 1. Local reflexes (fastest)
for reflex in &mut self.reflexes {
for reading in &readings {
if let Some(action) = reflex.check(reading) {
actions.push(action);
}
}
}
// If reflexes fired, skip higher levels
if !actions.is_empty() {
for action in &actions {
self.action_log.push((self.timestamp, action.clone()));
}
return actions;
}
// 2. Zone homeostasis (medium)
for (_, zone) in &mut self.zones {
// Learn occupancy
for reading in &readings {
if reading.sensor_type == EnvironmentSensor::Occupancy
&& zone.locations.contains(&reading.location)
{
zone.learn_occupancy(hour, reading.value);
}
}
// Compute zone actions
let zone_actions = zone.compute_action(&readings, hour);
actions.extend(zone_actions);
}
// 3. Global workspace (slowest, pattern detection)
for reading in &readings {
if reading.value > 0.8 || reading.value < 0.2 {
// Significant observation
self.workspace.broadcast(WorkspaceItem {
zone: ZoneId("global".to_string()),
observation: format!(
"{:?}_{}",
reading.sensor_type,
if reading.value > 0.5 { "high" } else { "low" }
),
salience: reading.value.abs(),
timestamp: self.timestamp,
});
}
}
// Detect emergent patterns
if let Some(policy) = self.workspace.detect_patterns() {
println!(
" [EMERGENT] New policy: {} (confidence: {:.2})",
policy.name, policy.confidence
);
}
for action in &actions {
self.action_log.push((self.timestamp, action.clone()));
}
actions
}
/// Get system status
pub fn status(&self) -> EnvironmentStatus {
let learned_patterns = self.workspace.policies.len();
let zone_states: HashMap<_, _> = self
.zones
.iter()
.map(|(id, zone)| {
(
id.clone(),
ZoneState {
target_temp: zone.target_temperature,
occupancy_learned: zone.occupancy_pattern.iter().sum::<f32>() > 0.0,
},
)
})
.collect();
EnvironmentStatus {
timestamp: self.timestamp,
active_reflexes: self.reflexes.len(),
zones: self.zones.len(),
learned_patterns,
zone_states,
recent_actions: self.action_log.len(),
}
}
}
#[derive(Debug)]
pub struct EnvironmentStatus {
pub timestamp: u64,
pub active_reflexes: usize,
pub zones: usize,
pub learned_patterns: usize,
pub zone_states: HashMap<ZoneId, ZoneState>,
pub recent_actions: usize,
}
#[derive(Debug)]
pub struct ZoneState {
pub target_temp: f32,
pub occupancy_learned: bool,
}
fn main() {
println!("=== Tier 3: Synthetic Nervous Systems for Environments ===\n");
let mut building = SyntheticNervousSystem::new("Smart Building");
// Add zones
building.add_zone("office_north", vec!["room_101", "room_102", "room_103"]);
building.add_zone("office_south", vec!["room_201", "room_202"]);
building.add_zone("lobby", vec!["entrance", "reception"]);
// Add local reflexes
building.add_reflex(LocalEnvironmentReflex::new(
LocationId("room_101".to_string()),
EnvironmentSensor::Temperature,
18.0,
28.0,
EnvironmentActuator::HVAC {
mode: HVACMode::Heating(3.0),
},
EnvironmentActuator::HVAC {
mode: HVACMode::Cooling(3.0),
},
));
building.add_reflex(LocalEnvironmentReflex::new(
LocationId("entrance".to_string()),
EnvironmentSensor::Motion,
0.0,
0.5,
EnvironmentActuator::Lighting { brightness: 0.2 },
EnvironmentActuator::Lighting { brightness: 1.0 },
));
println!("Building initialized:");
let status = building.status();
println!(" Zones: {}", status.zones);
println!(" Active reflexes: {}", status.active_reflexes);
// Simulate a day
println!("\nSimulating 24 hours...");
for hour in 0..24 {
for minute in 0..60 {
let timestamp = hour * 60 + minute;
// Generate readings based on time of day
let occupied = (hour >= 8 && hour <= 18) && (minute % 5 == 0);
let temp = 20.0 + 4.0 * ((hour as f32 / 24.0) * PI).sin();
let readings = vec![
EnvironmentReading {
timestamp,
location: LocationId("room_101".to_string()),
sensor_type: EnvironmentSensor::Temperature,
value: temp,
},
EnvironmentReading {
timestamp,
location: LocationId("room_101".to_string()),
sensor_type: EnvironmentSensor::Occupancy,
value: if occupied { 1.0 } else { 0.0 },
},
EnvironmentReading {
timestamp,
location: LocationId("entrance".to_string()),
sensor_type: EnvironmentSensor::Motion,
value: if occupied && minute % 15 == 0 {
1.0
} else {
0.0
},
},
];
let actions = building.process(readings);
if hour % 4 == 0 && minute == 0 {
println!(
" Hour {}: {} actions, temp={:.1}°C, occupied={}",
hour,
actions.len(),
temp,
occupied
);
}
}
}
// Summary
let status = building.status();
println!("\n=== End of Day Status ===");
println!(" Total actions taken: {}", status.recent_actions);
println!(" Emergent policies learned: {}", status.learned_patterns);
println!(" Zone states:");
for (zone, state) in &status.zone_states {
println!(
" {:?}: target={:.1}°C, occupancy_learned={}",
zone.0, state.target_temp, state.occupancy_learned
);
}
println!("\n=== Key Benefits ===");
println!("- Infrastructure becomes a sensing fabric");
println!("- Local reflexes handle immediate events");
println!("- Zone homeostasis manages comfort autonomously");
println!("- Policies emerge from patterns, not rules");
println!("- Energy, safety, and flow self-regulate");
println!("\nThis is exotic but inevitable.");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_local_reflex() {
let mut reflex = LocalEnvironmentReflex::new(
LocationId("test".to_string()),
EnvironmentSensor::Temperature,
18.0,
28.0,
EnvironmentActuator::HVAC {
mode: HVACMode::Heating(1.0),
},
EnvironmentActuator::HVAC {
mode: HVACMode::Cooling(1.0),
},
);
// Cold triggers heating
let reading = EnvironmentReading {
timestamp: 0,
location: LocationId("test".to_string()),
sensor_type: EnvironmentSensor::Temperature,
value: 15.0,
};
let action = reflex.check(&reading);
assert!(action.is_some());
}
#[test]
fn test_zone_homeostasis() {
let mut zone = ZoneHomeostasis::new(
ZoneId("test".to_string()),
vec![LocationId("room1".to_string())],
);
// Learn occupancy pattern
for _ in 0..10 {
zone.learn_occupancy(10, 1.0); // 10am occupied
zone.learn_occupancy(22, 0.0); // 10pm empty
}
assert!(zone.predict_occupancy(10) > 0.5);
assert!(zone.predict_occupancy(22) < 0.5);
}
#[test]
fn test_workspace_patterns() {
let mut workspace = EnvironmentWorkspace::new(7);
// Add repeated observation
for _ in 0..5 {
workspace.broadcast(WorkspaceItem {
zone: ZoneId("test".to_string()),
observation: "Temperature_high".to_string(),
salience: 1.0,
timestamp: 0,
});
}
let pattern = workspace.detect_patterns();
assert!(pattern.is_some());
}
}