Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
372
crates/ruvector-dag/examples/exotic/living_simulation.rs
Normal file
372
crates/ruvector-dag/examples/exotic/living_simulation.rs
Normal file
@@ -0,0 +1,372 @@
|
||||
//! # Living Simulation
|
||||
//!
|
||||
//! Not simulations that predict outcomes.
|
||||
//! Simulations that maintain internal stability while being perturbed.
|
||||
//!
|
||||
//! Examples:
|
||||
//! - Economic simulations that resist collapse and show where stress accumulates
|
||||
//! - Climate models that expose fragile boundaries rather than forecasts
|
||||
//! - Social simulations that surface tipping points before they happen
|
||||
//!
|
||||
//! You are no longer modeling reality. You are modeling fragility.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A node in the living simulation - responds to stress, not commands
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SimNode {
|
||||
pub id: usize,
|
||||
/// Current stress level (0-1)
|
||||
pub stress: f64,
|
||||
/// Resilience - ability to absorb stress without propagating
|
||||
pub resilience: f64,
|
||||
/// Threshold at which node becomes fragile
|
||||
pub fragility_threshold: f64,
|
||||
/// Whether this node is currently a fragility point
|
||||
pub is_fragile: bool,
|
||||
/// Accumulated damage from sustained stress
|
||||
pub damage: f64,
|
||||
}
|
||||
|
||||
/// An edge representing stress transmission
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SimEdge {
|
||||
pub from: usize,
|
||||
pub to: usize,
|
||||
/// How much stress transmits across this edge (0-1)
|
||||
pub transmission: f64,
|
||||
/// Current load on this edge
|
||||
pub load: f64,
|
||||
/// Breaking point - edge fails above this load
|
||||
pub breaking_point: f64,
|
||||
pub broken: bool,
|
||||
}
|
||||
|
||||
/// A living simulation that reveals fragility through perturbation
|
||||
pub struct LivingSimulation {
|
||||
nodes: HashMap<usize, SimNode>,
|
||||
edges: Vec<SimEdge>,
|
||||
|
||||
/// Global tension (mincut-derived)
|
||||
tension: f64,
|
||||
|
||||
/// History of fragility points
|
||||
fragility_history: Vec<FragilityEvent>,
|
||||
|
||||
/// Simulation time
|
||||
tick: usize,
|
||||
|
||||
/// Stability threshold - below this, system is stable
|
||||
stability_threshold: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FragilityEvent {
|
||||
pub tick: usize,
|
||||
pub node_id: usize,
|
||||
pub stress_level: f64,
|
||||
pub was_cascade: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SimulationState {
|
||||
pub tick: usize,
|
||||
pub tension: f64,
|
||||
pub fragile_nodes: Vec<usize>,
|
||||
pub broken_edges: usize,
|
||||
pub avg_stress: f64,
|
||||
pub max_stress: f64,
|
||||
pub stability: f64,
|
||||
}
|
||||
|
||||
impl LivingSimulation {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
nodes: HashMap::new(),
|
||||
edges: Vec::new(),
|
||||
tension: 0.0,
|
||||
fragility_history: Vec::new(),
|
||||
tick: 0,
|
||||
stability_threshold: 0.3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Build an economic simulation
|
||||
pub fn economic(num_sectors: usize) -> Self {
|
||||
let mut sim = Self::new();
|
||||
|
||||
// Create sectors as nodes
|
||||
for i in 0..num_sectors {
|
||||
sim.nodes.insert(
|
||||
i,
|
||||
SimNode {
|
||||
id: i,
|
||||
stress: 0.0,
|
||||
resilience: 0.3 + (i as f64 * 0.1).min(0.5),
|
||||
fragility_threshold: 0.6,
|
||||
is_fragile: false,
|
||||
damage: 0.0,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Create interconnections (supply chains)
|
||||
for i in 0..num_sectors {
|
||||
for j in (i + 1)..num_sectors {
|
||||
if (i + j) % 3 == 0 {
|
||||
// Selective connections
|
||||
sim.edges.push(SimEdge {
|
||||
from: i,
|
||||
to: j,
|
||||
transmission: 0.4,
|
||||
load: 0.0,
|
||||
breaking_point: 0.8,
|
||||
broken: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sim
|
||||
}
|
||||
|
||||
/// Apply external perturbation to a node
|
||||
pub fn perturb(&mut self, node_id: usize, stress_delta: f64) {
|
||||
if let Some(node) = self.nodes.get_mut(&node_id) {
|
||||
node.stress = (node.stress + stress_delta).clamp(0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Advance simulation one tick - stress propagates, fragility emerges
|
||||
pub fn tick(&mut self) -> SimulationState {
|
||||
self.tick += 1;
|
||||
|
||||
// Phase 1: Propagate stress through edges
|
||||
let mut stress_deltas: HashMap<usize, f64> = HashMap::new();
|
||||
|
||||
for edge in &mut self.edges {
|
||||
if edge.broken {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let (Some(from_node), Some(to_node)) =
|
||||
(self.nodes.get(&edge.from), self.nodes.get(&edge.to))
|
||||
{
|
||||
let stress_diff = from_node.stress - to_node.stress;
|
||||
let transmitted = stress_diff * edge.transmission;
|
||||
|
||||
edge.load = transmitted.abs();
|
||||
|
||||
if edge.load > edge.breaking_point {
|
||||
edge.broken = true;
|
||||
} else {
|
||||
*stress_deltas.entry(edge.to).or_insert(0.0) += transmitted;
|
||||
*stress_deltas.entry(edge.from).or_insert(0.0) -= transmitted * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Apply stress deltas with resilience
|
||||
for (node_id, delta) in stress_deltas {
|
||||
if let Some(node) = self.nodes.get_mut(&node_id) {
|
||||
let absorbed = delta * (1.0 - node.resilience);
|
||||
node.stress = (node.stress + absorbed).clamp(0.0, 1.0);
|
||||
|
||||
// Accumulate damage from sustained stress
|
||||
if node.stress > node.fragility_threshold {
|
||||
node.damage += 0.01;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Update fragility status
|
||||
let mut cascade_detected = false;
|
||||
for node in self.nodes.values_mut() {
|
||||
let was_fragile = node.is_fragile;
|
||||
node.is_fragile = node.stress > node.fragility_threshold;
|
||||
|
||||
if node.is_fragile && !was_fragile {
|
||||
cascade_detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4: Record fragility events
|
||||
for node in self.nodes.values() {
|
||||
if node.is_fragile {
|
||||
self.fragility_history.push(FragilityEvent {
|
||||
tick: self.tick,
|
||||
node_id: node.id,
|
||||
stress_level: node.stress,
|
||||
was_cascade: cascade_detected,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 5: Compute global tension
|
||||
self.tension = self.compute_tension();
|
||||
|
||||
// Phase 6: Self-healing attempt
|
||||
self.attempt_healing();
|
||||
|
||||
self.state()
|
||||
}
|
||||
|
||||
/// Get current state
|
||||
pub fn state(&self) -> SimulationState {
|
||||
let stresses: Vec<f64> = self.nodes.values().map(|n| n.stress).collect();
|
||||
let fragile: Vec<usize> = self
|
||||
.nodes
|
||||
.values()
|
||||
.filter(|n| n.is_fragile)
|
||||
.map(|n| n.id)
|
||||
.collect();
|
||||
let broken_edges = self.edges.iter().filter(|e| e.broken).count();
|
||||
|
||||
SimulationState {
|
||||
tick: self.tick,
|
||||
tension: self.tension,
|
||||
fragile_nodes: fragile,
|
||||
broken_edges,
|
||||
avg_stress: stresses.iter().sum::<f64>() / stresses.len().max(1) as f64,
|
||||
max_stress: stresses.iter().cloned().fold(0.0, f64::max),
|
||||
stability: 1.0 - self.tension,
|
||||
}
|
||||
}
|
||||
|
||||
/// Identify tipping points - nodes near fragility threshold
|
||||
pub fn tipping_points(&self) -> Vec<(usize, f64)> {
|
||||
let mut points: Vec<(usize, f64)> = self
|
||||
.nodes
|
||||
.values()
|
||||
.filter(|n| !n.is_fragile)
|
||||
.map(|n| {
|
||||
let distance_to_fragility = n.fragility_threshold - n.stress;
|
||||
(n.id, distance_to_fragility)
|
||||
})
|
||||
.collect();
|
||||
|
||||
points.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||
points.into_iter().take(3).collect()
|
||||
}
|
||||
|
||||
/// Find stress accumulation zones
|
||||
pub fn stress_accumulation_zones(&self) -> Vec<(usize, f64)> {
|
||||
let mut zones: Vec<(usize, f64)> = self
|
||||
.nodes
|
||||
.values()
|
||||
.map(|n| (n.id, n.stress + n.damage))
|
||||
.collect();
|
||||
|
||||
zones.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
zones.into_iter().take(3).collect()
|
||||
}
|
||||
|
||||
fn compute_tension(&self) -> f64 {
|
||||
// Tension based on fragility spread and edge stress
|
||||
let fragile_ratio = self.nodes.values().filter(|n| n.is_fragile).count() as f64
|
||||
/ self.nodes.len().max(1) as f64;
|
||||
|
||||
let edge_stress: f64 = self
|
||||
.edges
|
||||
.iter()
|
||||
.filter(|e| !e.broken)
|
||||
.map(|e| e.load / e.breaking_point)
|
||||
.sum::<f64>()
|
||||
/ self.edges.len().max(1) as f64;
|
||||
|
||||
let broken_ratio =
|
||||
self.edges.iter().filter(|e| e.broken).count() as f64 / self.edges.len().max(1) as f64;
|
||||
|
||||
(fragile_ratio * 0.4 + edge_stress * 0.3 + broken_ratio * 0.3).min(1.0)
|
||||
}
|
||||
|
||||
fn attempt_healing(&mut self) {
|
||||
// Only heal when tension is low enough
|
||||
if self.tension > self.stability_threshold {
|
||||
return;
|
||||
}
|
||||
|
||||
// Gradually reduce stress in non-fragile nodes
|
||||
for node in self.nodes.values_mut() {
|
||||
if !node.is_fragile {
|
||||
node.stress *= 0.95;
|
||||
node.damage *= 0.99;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("=== Living Simulation ===\n");
|
||||
println!("You are no longer modeling reality. You are modeling fragility.\n");
|
||||
|
||||
let mut sim = LivingSimulation::economic(8);
|
||||
|
||||
println!("Economic simulation: 8 sectors, interconnected supply chains\n");
|
||||
|
||||
// Run baseline
|
||||
println!("Phase 1: Baseline stability");
|
||||
for _ in 0..5 {
|
||||
sim.tick();
|
||||
}
|
||||
let baseline = sim.state();
|
||||
println!(
|
||||
" Tension: {:.2}, Avg stress: {:.2}\n",
|
||||
baseline.tension, baseline.avg_stress
|
||||
);
|
||||
|
||||
// Apply perturbation
|
||||
println!("Phase 2: Supply shock to sector 0");
|
||||
sim.perturb(0, 0.7);
|
||||
|
||||
println!("Tick | Tension | Fragile | Broken | Tipping Points");
|
||||
println!("-----|---------|---------|--------|---------------");
|
||||
|
||||
for _ in 0..20 {
|
||||
let state = sim.tick();
|
||||
let tipping = sim.tipping_points();
|
||||
let tipping_str: String = tipping
|
||||
.iter()
|
||||
.map(|(id, dist)| format!("{}:{:.2}", id, dist))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
println!(
|
||||
"{:4} | {:.2} | {:7} | {:6} | {}",
|
||||
state.tick,
|
||||
state.tension,
|
||||
state.fragile_nodes.len(),
|
||||
state.broken_edges,
|
||||
tipping_str
|
||||
);
|
||||
|
||||
// Additional perturbation mid-crisis
|
||||
if state.tick == 12 {
|
||||
println!(" >>> Additional shock to sector 3");
|
||||
sim.perturb(3, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
let final_state = sim.state();
|
||||
|
||||
println!("\n=== Fragility Analysis ===");
|
||||
println!("Stress accumulation zones:");
|
||||
for (id, stress) in sim.stress_accumulation_zones() {
|
||||
println!(" Sector {}: cumulative stress {:.2}", id, stress);
|
||||
}
|
||||
|
||||
println!("\nFinal tipping points (nodes nearest to fragility):");
|
||||
for (id, distance) in sim.tipping_points() {
|
||||
println!(" Sector {}: {:.2} from threshold", id, distance);
|
||||
}
|
||||
|
||||
println!("\nFragility events: {}", sim.fragility_history.len());
|
||||
let cascades = sim
|
||||
.fragility_history
|
||||
.iter()
|
||||
.filter(|e| e.was_cascade)
|
||||
.count();
|
||||
println!("Cascade events: {}", cascades);
|
||||
|
||||
println!("\n\"Not predicting outcomes. Exposing fragile boundaries.\"");
|
||||
}
|
||||
Reference in New Issue
Block a user