spread: simultaneous wavefront instead of independent BFS
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@bcachefs.org>
This commit is contained in:
parent
c13a9da81c
commit
05c7d55949
1 changed files with 28 additions and 17 deletions
|
|
@ -177,7 +177,7 @@ fn run_spread(
|
||||||
let store_params = store.params();
|
let store_params = store.params();
|
||||||
let max_hops = stage.param_u32("max_hops", store_params.max_hops);
|
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 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)
|
spreading_activation(seeds, graph, store, max_hops, edge_decay, min_activation)
|
||||||
}
|
}
|
||||||
|
|
@ -632,6 +632,12 @@ fn run_manifold(
|
||||||
results
|
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(
|
fn spreading_activation(
|
||||||
seeds: &[(String, f64)],
|
seeds: &[(String, f64)],
|
||||||
graph: &Graph,
|
graph: &Graph,
|
||||||
|
|
@ -641,30 +647,35 @@ fn spreading_activation(
|
||||||
min_activation: f64,
|
min_activation: f64,
|
||||||
) -> Vec<(String, f64)> {
|
) -> Vec<(String, f64)> {
|
||||||
let mut activation: HashMap<String, f64> = HashMap::new();
|
let mut activation: HashMap<String, f64> = HashMap::new();
|
||||||
let mut queue: VecDeque<(String, f64, u32)> = VecDeque::new();
|
|
||||||
|
|
||||||
|
// Initialize wavefront from all seeds
|
||||||
|
let mut frontier: HashMap<String, f64> = HashMap::new();
|
||||||
for (key, act) in seeds {
|
for (key, act) in seeds {
|
||||||
let current = activation.entry(key.clone()).or_insert(0.0);
|
*frontier.entry(key.clone()).or_insert(0.0) += act;
|
||||||
if *act > *current {
|
*activation.entry(key.clone()).or_insert(0.0) += act;
|
||||||
*current = *act;
|
|
||||||
queue.push_back((key.clone(), *act, 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some((key, act, depth)) = queue.pop_front() {
|
// Propagate hop by hop — all sources simultaneously
|
||||||
if depth >= max_hops { continue; }
|
for _hop in 0..max_hops {
|
||||||
|
let mut next_frontier: HashMap<String, f64> = HashMap::new();
|
||||||
|
|
||||||
for (neighbor, strength) in graph.neighbors(&key) {
|
for (key, act) in &frontier {
|
||||||
let neighbor_weight = store.node_weight(neighbor.as_str());
|
for (neighbor, strength) in graph.neighbors(key) {
|
||||||
let propagated = act * edge_decay * neighbor_weight * strength as f64;
|
let neighbor_weight = store.node_weight(neighbor.as_str());
|
||||||
if propagated < min_activation { continue; }
|
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);
|
*next_frontier.entry(neighbor.clone()).or_insert(0.0) += propagated;
|
||||||
if propagated > *current {
|
|
||||||
*current = propagated;
|
|
||||||
queue.push_back((neighbor.clone(), propagated, depth + 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
let mut results: Vec<_> = activation.into_iter().collect();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue