pub fn impact_radius(
graph: &SqliteGraph,
source: i64,
config: &ImpactRadiusConfig<'_>,
) -> Result<ImpactRadiusResult, SqliteGraphError>Expand description
Computes the impact radius from a source node using bounded weighted BFS.
Impact radius computes the “blast zone” - all nodes within a specified weighted distance from a source node. This is useful for:
- Failure impact analysis: What could be affected if this node fails?
- Change propagation: What might need testing after this change?
- Security blast zone: What’s the maximum potential damage radius?
- Network lateral movement: How far could an attacker propagate?
§Arguments
graph- The graph to analyzesource- The source node ID (center of blast zone)config- Configuration for radius computation
§Returns
Ok(ImpactRadiusResult) containing:
blast_zone- All nodes within max_distancedistances- Distance from source to each nodeboundary- Nodes at exactly max_distancesize- Total nodes in blast zone
§Algorithm
Bounded breadth-first search with distance tracking:
- Initialize: distances[source] = 0.0, queue = [(source, 0 hops)]
- While queue not empty:
- Pop (node, hops)
- Skip if dist > max_distance (early termination)
- Skip if max_hops Some() and hops >= max_hops
- Add node to blast_zone
- For each neighbor via fetch_outgoing:
- Compute weight via weight_fn
- Validate weight.is_finite()
- new_dist = dist + weight
- If new_dist <= max_distance AND (not visited OR shorter path):
- Update distances, enqueue (neighbor, hops + 1)
- Extract boundary: nodes where |dist - max_distance| < epsilon
- Return result
§Complexity
- Time: O(|V| + |E|) within the blast zone
- Space: O(|V|) for distances and blast zone
§Edge Cases
- Empty graph: Returns {source} with distance 0.0
- Source not in graph: Returns {source} with distance 0.0
- Disconnected: Only reaches nodes in source’s component
- Zero max_distance: Returns {source} only
- Invalid weights: Returns error if weight_fn returns non-finite value
§Example
ⓘ
use sqlitegraph::{SqliteGraph, algo::observability::{impact_radius, ImpactRadiusConfig, default_weight_fn}};
let graph = SqliteGraph::open_in_memory()?;
// ... build graph ...
// Find all nodes within 3 hops of source
let config = ImpactRadiusConfig {
max_distance: 3.0,
max_hops: None,
weight_fn: &default_weight_fn,
};
let result = impact_radius(&graph, source, &config)?;
println!("Blast zone: {} nodes", result.size);
for &node in &result.blast_zone {
let dist = result.distances[&node];
println!(" Node {}: distance {:.2}", node, dist);
}
println!("Boundary: {:?}", result.boundary);