weavatrix_graph/algo/components/
condensation.rs1use super::scc;
2use crate::Vec;
3use crate::{EdgeEndpoints, GraphError, IndexGraphView, NodeIndex, Result, Topology};
4
5#[derive(Clone, Debug)]
6pub struct Condensation<Node> {
7 components: Vec<Vec<Node>>,
8 component_by_node: Vec<(Node, NodeIndex)>,
9 topology: Topology,
10}
11
12impl<Node> Condensation<Node>
13where
14 Node: Copy + Eq,
15{
16 #[must_use]
17 pub fn components(&self) -> &[Vec<Node>] {
18 &self.components
19 }
20
21 #[must_use]
22 pub fn component(&self, index: NodeIndex) -> Option<&[Node]> {
23 self.components.get(index.index()).map(Vec::as_slice)
24 }
25
26 #[must_use]
27 pub fn component_of(&self, node: Node) -> Option<NodeIndex> {
28 self.component_by_node
29 .iter()
30 .find_map(|&(candidate, index)| (candidate == node).then_some(index))
31 }
32
33 #[must_use]
34 pub const fn topology(&self) -> &Topology {
35 &self.topology
36 }
37
38 #[must_use]
39 pub fn into_parts(self) -> (Vec<Vec<Node>>, Topology) {
40 (self.components, self.topology)
41 }
42}
43
44pub fn condensation<G>(graph: &G) -> Result<Condensation<G::Node>>
50where
51 G: IndexGraphView,
52{
53 let allows_edge = |_| true;
54 let components = scc::with_filter(graph, &allows_edge);
55 build(graph, components, &allows_edge)
56}
57
58pub fn condensation_filtered<G, F>(graph: &G, allows_edge: F) -> Result<Condensation<G::Node>>
64where
65 G: IndexGraphView,
66 F: Fn(G::Edge) -> bool,
67{
68 let components = scc::with_filter(graph, &allows_edge);
69 build(graph, components, &allows_edge)
70}
71
72fn build<G, F>(
73 graph: &G,
74 components: Vec<Vec<G::Node>>,
75 allows_edge: &F,
76) -> Result<Condensation<G::Node>>
77where
78 G: IndexGraphView,
79 F: Fn(G::Edge) -> bool,
80{
81 let mut component_by_node = Vec::with_capacity(graph.node_count());
82 let mut component_by_slot = vec![None; graph.node_bound()];
83 for (position, component) in components.iter().enumerate() {
84 let compact = u32::try_from(position).map_err(|_| GraphError::IndexCapacityExceeded {
85 category: "components",
86 count: components.len(),
87 })?;
88 for &node in component {
89 let index = NodeIndex::new(compact);
90 component_by_node.push((node, index));
91 if let Some(slot) = component_by_slot.get_mut(G::node_slot(node)) {
92 *slot = Some(index);
93 }
94 }
95 }
96
97 let mut edges = Vec::new();
98 for (edge, endpoints) in graph.edge_references() {
99 if !allows_edge(edge) {
100 continue;
101 }
102 let (Some(Some(source)), Some(Some(target))) = (
103 component_by_slot.get(G::node_slot(endpoints.source())),
104 component_by_slot.get(G::node_slot(endpoints.target())),
105 ) else {
106 continue;
107 };
108 if source != target {
109 edges.push(EdgeEndpoints::new(*source, *target));
110 }
111 }
112 edges.sort_unstable_by_key(|edge| (edge.source().index(), edge.target().index()));
113 edges.dedup();
114 let topology = Topology::try_from_edges(components.len(), edges)?;
115 Ok(Condensation {
116 components,
117 component_by_node,
118 topology,
119 })
120}