1use crate::topology_impl::PetTopologyGraph;
10use phago_core::substrate::Substrate;
11use phago_core::topology::TopologyGraph;
12use phago_core::types::*;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15
16pub struct SubstrateImpl {
23 signals: Vec<Signal>,
24 graph: PetTopologyGraph,
25 traces: HashMap<TraceLocationKey, Vec<Trace>>,
26 documents: HashMap<DocumentId, Document>,
27 tick: Tick,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
32enum TraceLocationKey {
33 Spatial(OrderedPosition),
34 GraphNode(NodeId),
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
39struct OrderedPosition {
40 x: i64,
42 y: i64,
43}
44
45impl From<&Position> for OrderedPosition {
46 fn from(p: &Position) -> Self {
47 Self {
48 x: (p.x * 10.0).round() as i64,
49 y: (p.y * 10.0).round() as i64,
50 }
51 }
52}
53
54impl From<&SubstrateLocation> for TraceLocationKey {
55 fn from(loc: &SubstrateLocation) -> Self {
56 match loc {
57 SubstrateLocation::Spatial(pos) => TraceLocationKey::Spatial(OrderedPosition::from(pos)),
58 SubstrateLocation::GraphNode(id) => TraceLocationKey::GraphNode(*id),
59 }
60 }
61}
62
63impl SubstrateImpl {
64 pub fn new() -> Self {
65 Self {
66 signals: Vec::new(),
67 graph: PetTopologyGraph::new(),
68 traces: HashMap::new(),
69 documents: HashMap::new(),
70 tick: 0,
71 }
72 }
73
74 pub fn get_document(&self, id: &DocumentId) -> Option<&Document> {
76 self.documents.get(id)
77 }
78
79 pub fn all_documents(&self) -> Vec<&Document> {
81 self.documents.values().collect()
82 }
83
84 pub fn graph(&self) -> &PetTopologyGraph {
86 &self.graph
87 }
88
89 pub fn graph_mut(&mut self) -> &mut PetTopologyGraph {
91 &mut self.graph
92 }
93
94 pub fn all_signals(&self) -> &[Signal] {
96 &self.signals
97 }
98
99 pub fn total_trace_count(&self) -> usize {
101 self.traces.values().map(|v| v.len()).sum()
102 }
103
104 pub fn traces_near(&self, position: &Position, radius: f64, trace_type: &TraceType) -> Vec<&Trace> {
106 let r_grid = (radius * 10.0).ceil() as i64;
107 let cx = (position.x * 10.0).round() as i64;
108 let cy = (position.y * 10.0).round() as i64;
109
110 let mut results = Vec::new();
111 for dx in -r_grid..=r_grid {
113 for dy in -r_grid..=r_grid {
114 let key = TraceLocationKey::Spatial(OrderedPosition {
115 x: cx + dx,
116 y: cy + dy,
117 });
118 if let Some(traces) = self.traces.get(&key) {
119 for trace in traces {
120 if &trace.trace_type == trace_type {
121 results.push(trace);
122 }
123 }
124 }
125 }
126 }
127 results
128 }
129}
130
131impl Default for SubstrateImpl {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl Substrate for SubstrateImpl {
138 fn signals_near(&self, position: &Position, radius: f64) -> Vec<&Signal> {
141 let r2 = radius * radius;
142 self.signals
143 .iter()
144 .filter(|s| {
145 let dx = s.position.x - position.x;
146 let dy = s.position.y - position.y;
147 dx * dx + dy * dy <= r2
148 })
149 .collect()
150 }
151
152 fn emit_signal(&mut self, signal: Signal) {
153 self.signals.push(signal);
154 }
155
156 fn decay_signals(&mut self, rate: f64, removal_threshold: f64) {
157 for signal in &mut self.signals {
158 signal.decay(rate);
159 }
160 self.signals
161 .retain(|s| !s.is_below_threshold(removal_threshold));
162 }
163
164 fn add_node(&mut self, data: NodeData) -> NodeId {
167 self.graph.add_node(data)
168 }
169
170 fn get_node(&self, id: &NodeId) -> Option<&NodeData> {
171 self.graph.get_node(id)
172 }
173
174 fn set_edge(&mut self, from: NodeId, to: NodeId, data: EdgeData) {
175 self.graph.set_edge(from, to, data);
176 }
177
178 fn get_edge(&self, from: &NodeId, to: &NodeId) -> Option<&EdgeData> {
179 self.graph.get_edge(from, to)
180 }
181
182 fn neighbors(&self, node: &NodeId) -> Vec<(NodeId, &EdgeData)> {
183 self.graph.neighbors(node)
184 }
185
186 fn remove_edge(&mut self, from: &NodeId, to: &NodeId) {
187 self.graph.remove_edge(from, to);
188 }
189
190 fn all_nodes(&self) -> Vec<NodeId> {
191 self.graph.all_nodes()
192 }
193
194 fn all_edges(&self) -> Vec<(NodeId, NodeId, &EdgeData)> {
195 self.graph.all_edges()
196 }
197
198 fn node_count(&self) -> usize {
199 self.graph.node_count()
200 }
201
202 fn edge_count(&self) -> usize {
203 self.graph.edge_count()
204 }
205
206 fn deposit_trace(&mut self, location: &SubstrateLocation, trace: Trace) {
209 let key = TraceLocationKey::from(location);
210 self.traces.entry(key).or_default().push(trace);
211 }
212
213 fn traces_at(&self, location: &SubstrateLocation) -> Vec<&Trace> {
214 let key = TraceLocationKey::from(location);
215 self.traces
216 .get(&key)
217 .map(|traces| traces.iter().collect())
218 .unwrap_or_default()
219 }
220
221 fn decay_traces(&mut self, rate: f64, removal_threshold: f64) {
222 for traces in self.traces.values_mut() {
223 for trace in traces.iter_mut() {
224 trace.intensity *= 1.0 - rate;
225 }
226 traces.retain(|t| t.intensity >= removal_threshold);
227 }
228 self.traces.retain(|_, v| !v.is_empty());
230 }
231
232 fn add_document(&mut self, doc: Document) {
235 self.documents.insert(doc.id, doc);
236 }
237
238 fn get_document(&self, id: &DocumentId) -> Option<&Document> {
239 self.documents.get(id)
240 }
241
242 fn undigested_documents(&self) -> Vec<&Document> {
243 self.documents.values().filter(|d| !d.digested).collect()
244 }
245
246 fn consume_document(&mut self, id: &DocumentId) -> Option<String> {
247 if let Some(doc) = self.documents.get_mut(id) {
248 if !doc.digested {
249 doc.digested = true;
250 return Some(doc.content.clone());
251 }
252 }
253 None
254 }
255
256 fn all_documents(&self) -> Vec<&Document> {
257 self.documents.values().collect()
258 }
259
260 fn current_tick(&self) -> Tick {
263 self.tick
264 }
265
266 fn advance_tick(&mut self) {
267 self.tick += 1;
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 fn make_signal(x: f64, y: f64, intensity: f64) -> Signal {
276 Signal::new(
277 SignalType::Input,
278 intensity,
279 Position::new(x, y),
280 AgentId::new(),
281 0,
282 )
283 }
284
285 #[test]
286 fn signals_near_filters_by_distance() {
287 let mut sub = SubstrateImpl::new();
288 sub.emit_signal(make_signal(1.0, 1.0, 1.0)); sub.emit_signal(make_signal(100.0, 100.0, 1.0)); let near = sub.signals_near(&Position::new(0.0, 0.0), 5.0);
292 assert_eq!(near.len(), 1);
293 }
294
295 #[test]
296 fn signal_decay_removes_weak_signals() {
297 let mut sub = SubstrateImpl::new();
298 sub.emit_signal(make_signal(0.0, 0.0, 1.0));
299 sub.emit_signal(make_signal(1.0, 1.0, 0.05));
300
301 sub.decay_signals(0.5, 0.04);
303 assert_eq!(sub.all_signals().len(), 1); }
305
306 #[test]
307 fn trace_deposit_and_retrieve() {
308 let mut sub = SubstrateImpl::new();
309 let loc = SubstrateLocation::Spatial(Position::new(5.0, 5.0));
310 let trace = Trace {
311 agent_id: AgentId::new(),
312 trace_type: TraceType::Digestion,
313 intensity: 1.0,
314 tick: 0,
315 payload: vec![],
316 };
317 sub.deposit_trace(&loc, trace);
318
319 let traces = sub.traces_at(&loc);
320 assert_eq!(traces.len(), 1);
321 assert_eq!(traces[0].trace_type, TraceType::Digestion);
322 }
323
324 #[test]
325 fn trace_decay_removes_weak_traces() {
326 let mut sub = SubstrateImpl::new();
327 let loc = SubstrateLocation::Spatial(Position::new(0.0, 0.0));
328 sub.deposit_trace(&loc, Trace {
329 agent_id: AgentId::new(),
330 trace_type: TraceType::Visit,
331 intensity: 1.0,
332 tick: 0,
333 payload: vec![],
334 });
335 sub.deposit_trace(&loc, Trace {
336 agent_id: AgentId::new(),
337 trace_type: TraceType::Visit,
338 intensity: 0.02,
339 tick: 0,
340 payload: vec![],
341 });
342
343 sub.decay_traces(0.5, 0.02);
344 assert_eq!(sub.traces_at(&loc).len(), 1);
346 }
347
348 #[test]
349 fn graph_operations_through_substrate() {
350 let mut sub = SubstrateImpl::new();
351
352 let n1 = sub.add_node(NodeData {
353 id: NodeId::new(),
354 label: "cell".to_string(),
355 node_type: NodeType::Concept,
356 position: Position::new(0.0, 0.0),
357 access_count: 0,
358 created_tick: 0,
359 });
360 let n2 = sub.add_node(NodeData {
361 id: NodeId::new(),
362 label: "membrane".to_string(),
363 node_type: NodeType::Concept,
364 position: Position::new(1.0, 0.0),
365 access_count: 0,
366 created_tick: 0,
367 });
368
369 sub.set_edge(n1, n2, EdgeData {
370 weight: 0.8,
371 co_activations: 1,
372 created_tick: 0,
373 last_activated_tick: 0,
374 });
375
376 assert_eq!(sub.node_count(), 2);
377 assert_eq!(sub.edge_count(), 1);
378 assert_eq!(sub.get_node(&n1).unwrap().label, "cell");
379 }
380
381 #[test]
382 fn tick_advances() {
383 let mut sub = SubstrateImpl::new();
384 assert_eq!(sub.current_tick(), 0);
385 sub.advance_tick();
386 sub.advance_tick();
387 assert_eq!(sub.current_tick(), 2);
388 }
389}