From 05c7d55949cf01567b6177b292cc21ebb37ba85c Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 9 Mar 2026 01:35:27 -0400 Subject: [PATCH] spread: simultaneous wavefront instead of independent BFS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All seeds emit at once. At each hop, activations from all sources sum at each node, and the combined map propagates on the next hop. Nodes where multiple wavefronts overlap get reinforced and radiate stronger — natural interference patterns. Lower default min_activation threshold (×0.1) since individual contributions are smaller in additive mode. Co-Authored-By: ProofOfConcept --- poc-memory/src/search.rs | 45 +++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/poc-memory/src/search.rs b/poc-memory/src/search.rs index a334643..ad867a9 100644 --- a/poc-memory/src/search.rs +++ b/poc-memory/src/search.rs @@ -177,7 +177,7 @@ fn run_spread( let store_params = store.params(); let max_hops = stage.param_u32("max_hops", store_params.max_hops); let edge_decay = stage.param_f64("edge_decay", store_params.edge_decay); - let min_activation = stage.param_f64("min_activation", store_params.min_activation); + let min_activation = stage.param_f64("min_activation", store_params.min_activation * 0.1); spreading_activation(seeds, graph, store, max_hops, edge_decay, min_activation) } @@ -632,6 +632,12 @@ fn run_manifold( results } +/// Simultaneous wavefront spreading activation. +/// +/// All seeds emit at once. At each hop, activations from all sources +/// sum at each node, and the combined activation map propagates on +/// the next hop. This creates interference patterns — nodes where +/// multiple wavefronts overlap get reinforced and radiate stronger. fn spreading_activation( seeds: &[(String, f64)], graph: &Graph, @@ -641,30 +647,35 @@ fn spreading_activation( min_activation: f64, ) -> Vec<(String, f64)> { let mut activation: HashMap = HashMap::new(); - let mut queue: VecDeque<(String, f64, u32)> = VecDeque::new(); + // Initialize wavefront from all seeds + let mut frontier: HashMap = HashMap::new(); for (key, act) in seeds { - let current = activation.entry(key.clone()).or_insert(0.0); - if *act > *current { - *current = *act; - queue.push_back((key.clone(), *act, 0)); - } + *frontier.entry(key.clone()).or_insert(0.0) += act; + *activation.entry(key.clone()).or_insert(0.0) += act; } - while let Some((key, act, depth)) = queue.pop_front() { - if depth >= max_hops { continue; } + // Propagate hop by hop — all sources simultaneously + for _hop in 0..max_hops { + let mut next_frontier: HashMap = HashMap::new(); - for (neighbor, strength) in graph.neighbors(&key) { - let neighbor_weight = store.node_weight(neighbor.as_str()); - let propagated = act * edge_decay * neighbor_weight * strength as f64; - if propagated < min_activation { continue; } + for (key, act) in &frontier { + for (neighbor, strength) in graph.neighbors(key) { + let neighbor_weight = store.node_weight(neighbor.as_str()); + let propagated = act * edge_decay * neighbor_weight * strength as f64; + if propagated < min_activation { continue; } - let current = activation.entry(neighbor.clone()).or_insert(0.0); - if propagated > *current { - *current = propagated; - queue.push_back((neighbor.clone(), propagated, depth + 1)); + *next_frontier.entry(neighbor.clone()).or_insert(0.0) += propagated; } } + + if next_frontier.is_empty() { break; } + + // Merge into total activation and advance frontier + for (key, act) in &next_frontier { + *activation.entry(key.clone()).or_insert(0.0) += act; + } + frontier = next_frontier; } let mut results: Vec<_> = activation.into_iter().collect();