use super::discovery::ResourceGradient;
use super::topology::{Edge, NodeId, Topology};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct GrowthParams {
pub branching_threshold: f64,
pub pruning_threshold: f64,
pub fusion_distance: f64,
pub base_growth_rate: f64,
pub max_branches: usize,
pub fusion_capacity_boost: f64,
}
impl Default for GrowthParams {
fn default() -> Self {
Self {
branching_threshold: 0.5,
pruning_threshold: 0.1,
fusion_distance: 0.5,
base_growth_rate: 0.1,
max_branches: 2,
fusion_capacity_boost: 2.0,
}
}
}
impl GrowthParams {
pub fn exploration() -> Self {
Self {
branching_threshold: 0.3,
pruning_threshold: 0.05,
max_branches: 3,
..Default::default()
}
}
pub fn transport() -> Self {
Self {
branching_threshold: 0.7,
pruning_threshold: 0.3,
max_branches: 2,
..Default::default()
}
}
pub fn balanced() -> Self {
Self::default()
}
}
pub struct GrowthSimulator {
pub topology: Topology,
pub params: GrowthParams,
pub node_potentials: HashMap<NodeId, f64>,
pub next_node_id: u64,
pub generation: u64,
pub stats: GrowthStats,
}
#[derive(Debug, Clone, Default)]
pub struct GrowthStats {
pub total_branches: usize,
pub total_extensions: usize,
pub total_fusions: usize,
pub total_pruned: usize,
}
impl GrowthSimulator {
pub fn new(params: GrowthParams) -> Self {
Self {
topology: Topology::new(),
params,
node_potentials: HashMap::new(),
next_node_id: 1,
generation: 0,
stats: GrowthStats::default(),
}
}
pub fn with_defaults() -> Self {
Self::new(GrowthParams::default())
}
pub fn spawn_tip(&mut self) -> NodeId {
let id = NodeId(self.next_node_id);
self.next_node_id += 1;
self.topology.add_tip(id);
self.node_potentials.insert(id, 1.0);
id
}
pub fn spawn_connected(&mut self, parent: NodeId) -> NodeId {
let id = NodeId(self.next_node_id);
self.next_node_id += 1;
self.topology.add_tip(id);
self.topology.connect(parent, id, 1.0);
self.node_potentials.insert(id, 0.5);
id
}
pub fn grow(&mut self, gradient: &ResourceGradient) {
self.generation += 1;
let tips: Vec<NodeId> = self.topology.active_tips.iter().copied().collect();
for tip in tips {
let potential = *self.node_potentials.get(&tip).unwrap_or(&0.0);
let resource = gradient.get(tip);
let new_potential = potential + resource * self.params.base_growth_rate;
self.node_potentials.insert(tip, new_potential);
if new_potential >= self.params.branching_threshold {
self.branch(tip);
} else if resource > 0.1 {
self.extend(tip, gradient);
}
}
self.check_fusion();
self.prune();
}
fn branch(&mut self, tip: NodeId) {
let potential = self.node_potentials.get(&tip).copied().unwrap_or(0.0);
let num_branches = self.params.max_branches.min(2);
let potential_per_branch = potential / num_branches as f64;
for _ in 0..num_branches {
let new_tip = NodeId(self.next_node_id);
self.next_node_id += 1;
self.topology.nodes.insert(new_tip);
self.topology.active_tips.insert(new_tip);
self.topology.edges.push(Edge {
source: tip,
target: new_tip,
capacity: 1.0,
latency: 0.1,
});
self.node_potentials.insert(new_tip, potential_per_branch);
}
self.topology.active_tips.remove(&tip);
self.node_potentials.insert(tip, 0.0);
self.stats.total_branches += 1;
}
fn extend(&mut self, tip: NodeId, _gradient: &ResourceGradient) {
let new_tip = NodeId(self.next_node_id);
self.next_node_id += 1;
self.topology.nodes.insert(new_tip);
self.topology.active_tips.insert(new_tip);
self.topology.active_tips.remove(&tip);
self.topology.edges.push(Edge {
source: tip,
target: new_tip,
capacity: 1.0,
latency: 0.1,
});
let potential = self.node_potentials.get(&tip).copied().unwrap_or(0.0);
self.node_potentials.insert(new_tip, potential);
self.node_potentials.insert(tip, 0.0);
self.stats.total_extensions += 1;
}
fn check_fusion(&mut self) {
let tips: Vec<NodeId> = self.topology.active_tips.iter().copied().collect();
let mut fused = Vec::new();
for i in 0..tips.len() {
for j in (i + 1)..tips.len() {
let a = tips[i];
let b = tips[j];
if fused.contains(&a) || fused.contains(&b) {
continue;
}
let pot_a = self.node_potentials.get(&a).unwrap_or(&0.0);
let pot_b = self.node_potentials.get(&b).unwrap_or(&0.0);
if *pot_a > 0.3 && *pot_b > 0.3 {
self.topology.edges.push(Edge {
source: a,
target: b,
capacity: self.params.fusion_capacity_boost,
latency: 0.05,
});
let combined = pot_a + pot_b;
self.node_potentials.insert(a, combined);
self.topology.active_tips.remove(&b);
fused.push(b);
self.stats.total_fusions += 1;
}
}
}
}
fn prune(&mut self) {
let before = self.topology.edges.len();
self.topology.prune(self.params.pruning_threshold);
let after = self.topology.edges.len();
self.stats.total_pruned += before - after;
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn stats(&self) -> &GrowthStats {
&self.stats
}
pub fn total_potential(&self) -> f64 {
self.node_potentials.values().sum()
}
pub fn reset(&mut self) {
self.topology = Topology::new();
self.node_potentials.clear();
self.next_node_id = 1;
self.generation = 0;
self.stats = GrowthStats::default();
}
}
#[derive(Debug, Clone)]
pub enum GrowthEvent {
Branch {
parent: NodeId,
children: Vec<NodeId>,
},
Extension {
from: NodeId,
to: NodeId,
},
Fusion {
tip_a: NodeId,
tip_b: NodeId,
},
Prune {
edge: (NodeId, NodeId),
},
}
#[cfg(test)]
mod tests {
use super::*;
use crate::network::discovery::ResourceType;
#[test]
fn test_growth_cycle() {
let mut sim = GrowthSimulator::with_defaults();
let root = sim.spawn_tip();
let mut gradient = ResourceGradient::new(ResourceType::new("nutrient"));
gradient.set(root, 1.0);
for _ in 0..10 {
sim.grow(&gradient);
}
assert!(sim.topology.nodes.len() > 1);
assert!(sim.generation() == 10);
}
#[test]
fn test_branching() {
let mut sim = GrowthSimulator::new(GrowthParams {
branching_threshold: 0.3,
..Default::default()
});
let root = sim.spawn_tip();
sim.node_potentials.insert(root, 1.0);
let mut gradient = ResourceGradient::new(ResourceType::new("nutrient"));
gradient.set(root, 1.0);
sim.grow(&gradient);
assert!(sim.stats.total_branches >= 1);
}
#[test]
fn test_connected_spawn() {
let mut sim = GrowthSimulator::with_defaults();
let root = sim.spawn_tip();
let child = sim.spawn_connected(root);
assert_eq!(sim.topology.nodes.len(), 2);
assert_eq!(sim.topology.edges.len(), 1);
assert!(sim.topology.active_tips.contains(&child));
}
#[test]
fn test_growth_params_presets() {
let exploration = GrowthParams::exploration();
let transport = GrowthParams::transport();
let balanced = GrowthParams::balanced();
assert!(exploration.branching_threshold < balanced.branching_threshold);
assert!(transport.pruning_threshold > balanced.pruning_threshold);
}
#[test]
fn test_reset() {
let mut sim = GrowthSimulator::with_defaults();
sim.spawn_tip();
sim.generation = 100;
sim.stats.total_branches = 50;
sim.reset();
assert_eq!(sim.topology.nodes.len(), 0);
assert_eq!(sim.generation, 0);
assert_eq!(sim.stats.total_branches, 0);
}
}