proof_engine/ecology/
food_web.rs1use std::collections::{HashMap, HashSet};
4
5#[derive(Debug, Clone)]
7pub struct WebNode {
8 pub species_id: u32,
9 pub name: String,
10 pub trophic_level: f32,
11 pub biomass: f64,
12}
13
14#[derive(Debug, Clone)]
16pub struct WebEdge {
17 pub prey: u32,
18 pub predator: u32,
19 pub transfer_efficiency: f64,
20}
21
22#[derive(Debug, Clone)]
24pub struct FoodWeb {
25 pub nodes: Vec<WebNode>,
26 pub edges: Vec<WebEdge>,
27}
28
29impl FoodWeb {
30 pub fn new() -> Self { Self { nodes: Vec::new(), edges: Vec::new() } }
31
32 pub fn add_node(&mut self, node: WebNode) { self.nodes.push(node); }
33 pub fn add_edge(&mut self, edge: WebEdge) { self.edges.push(edge); }
34
35 pub fn connectance(&self) -> f64 {
37 let n = self.nodes.len() as f64;
38 if n < 2.0 { return 0.0; }
39 self.edges.len() as f64 / (n * (n - 1.0))
40 }
41
42 pub fn prey_of(&self, predator_id: u32) -> Vec<u32> {
44 self.edges.iter().filter(|e| e.predator == predator_id).map(|e| e.prey).collect()
45 }
46
47 pub fn predators_of(&self, prey_id: u32) -> Vec<u32> {
49 self.edges.iter().filter(|e| e.prey == prey_id).map(|e| e.predator).collect()
50 }
51
52 pub fn compute_trophic_levels(&mut self) {
54 let producers: Vec<u32> = self.nodes.iter()
55 .filter(|n| self.prey_of(n.species_id).is_empty())
56 .map(|n| n.species_id)
57 .collect();
58
59 for node in &mut self.nodes {
60 if producers.contains(&node.species_id) {
61 node.trophic_level = 1.0;
62 }
63 }
64
65 for _ in 0..10 {
67 for i in 0..self.nodes.len() {
68 let id = self.nodes[i].species_id;
69 let prey_levels: Vec<f32> = self.prey_of(id).iter()
70 .filter_map(|&pid| self.nodes.iter().find(|n| n.species_id == pid))
71 .map(|n| n.trophic_level)
72 .collect();
73 if !prey_levels.is_empty() {
74 let avg: f32 = prey_levels.iter().sum::<f32>() / prey_levels.len() as f32;
75 self.nodes[i].trophic_level = avg + 1.0;
76 }
77 }
78 }
79 }
80
81 pub fn total_energy_flow(&self) -> f64 {
83 self.edges.iter().map(|e| {
84 let prey_biomass = self.nodes.iter()
85 .find(|n| n.species_id == e.prey)
86 .map(|n| n.biomass)
87 .unwrap_or(0.0);
88 prey_biomass * e.transfer_efficiency
89 }).sum()
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn test_food_web() {
99 let mut web = FoodWeb::new();
100 web.add_node(WebNode { species_id: 0, name: "Grass".into(), trophic_level: 1.0, biomass: 1000.0 });
101 web.add_node(WebNode { species_id: 1, name: "Rabbit".into(), trophic_level: 2.0, biomass: 100.0 });
102 web.add_node(WebNode { species_id: 2, name: "Fox".into(), trophic_level: 3.0, biomass: 10.0 });
103 web.add_edge(WebEdge { prey: 0, predator: 1, transfer_efficiency: 0.1 });
104 web.add_edge(WebEdge { prey: 1, predator: 2, transfer_efficiency: 0.1 });
105
106 assert_eq!(web.prey_of(1), vec![0]);
107 assert_eq!(web.predators_of(1), vec![2]);
108 assert!(web.connectance() > 0.0);
109 }
110}