1use std::{
5 cmp::Reverse,
6 collections::{BTreeMap, BinaryHeap, HashSet},
7};
8
9use crate::interface::catalog::flow::{FlowEdge, FlowEdgeId, OperatorId};
10
11#[derive(Debug, Clone, PartialEq)]
12pub struct DirectedGraph<NodeData> {
13 nodes: BTreeMap<OperatorId, NodeData>,
14 edges: Vec<FlowEdge>,
15 outgoing: BTreeMap<OperatorId, Vec<OperatorId>>,
16 incoming: BTreeMap<OperatorId, Vec<OperatorId>>,
17}
18
19impl<NodeData> DirectedGraph<NodeData> {
20 pub fn new() -> Self {
21 Self {
22 nodes: BTreeMap::new(),
23 edges: Vec::new(),
24 outgoing: BTreeMap::new(),
25 incoming: BTreeMap::new(),
26 }
27 }
28
29 pub fn add_node(&mut self, node_id: OperatorId, data: NodeData) -> OperatorId {
30 self.nodes.insert(node_id, data);
31 self.outgoing.entry(node_id).or_default();
32 self.incoming.entry(node_id).or_default();
33 node_id
34 }
35
36 pub fn add_edge(&mut self, edge: FlowEdge) -> FlowEdgeId {
37 let source = edge.source;
38 let target = edge.target;
39
40 let result = edge.id;
41
42 if !self.nodes.contains_key(&source) {
43 panic!("Source operator {:?} does not exist", source);
44 }
45 if !self.nodes.contains_key(&target) {
46 panic!("Target operator {:?} does not exist", target);
47 }
48
49 if self.creates_cycle(&source, &target) {
50 panic!("Adding edge would create a cycle");
51 }
52
53 self.edges.push(edge);
54
55 self.outgoing.entry(source).or_default().push(target);
56
57 self.incoming.entry(target).or_default().push(source);
58
59 result
60 }
61
62 pub fn get_node(&self, node_id: &OperatorId) -> Option<&NodeData> {
63 self.nodes.get(node_id)
64 }
65
66 pub fn get_node_mut(&mut self, node_id: &OperatorId) -> Option<&mut NodeData> {
67 self.nodes.get_mut(node_id)
68 }
69
70 pub fn node_count(&self) -> usize {
71 self.nodes.len()
72 }
73
74 pub fn edge_count(&self) -> usize {
75 self.edges.len()
76 }
77
78 pub fn neighbors(&self, node_id: &OperatorId) -> Vec<OperatorId> {
79 self.outgoing.get(node_id).cloned().unwrap_or_default()
80 }
81
82 pub fn predecessors(&self, node_id: &OperatorId) -> Vec<OperatorId> {
83 self.incoming.get(node_id).cloned().unwrap_or_default()
84 }
85
86 pub fn topological_sort(&self) -> Vec<OperatorId> {
87 let mut in_degree = BTreeMap::new();
88
89 let mut heap: BinaryHeap<Reverse<OperatorId>> = BinaryHeap::new();
90 let mut result = Vec::new();
91
92 for node_id in self.nodes.keys() {
93 in_degree.insert(*node_id, 0);
94 }
95
96 for edge in &self.edges {
97 *in_degree.get_mut(&edge.target).unwrap() += 1;
98 }
99
100 for (node_id, °ree) in &in_degree {
101 if degree == 0 {
102 heap.push(Reverse(*node_id));
103 }
104 }
105
106 while let Some(Reverse(node_id)) = heap.pop() {
107 result.push(node_id);
108
109 if let Some(neighbors) = self.outgoing.get(&node_id) {
110 for neighbor in neighbors {
111 let degree = in_degree.get_mut(neighbor).unwrap();
112 *degree -= 1;
113 if *degree == 0 {
114 heap.push(Reverse(*neighbor));
115 }
116 }
117 }
118 }
119
120 if result.len() != self.nodes.len() {
121 panic!("Graph contains cycles");
122 }
123
124 result
125 }
126
127 pub fn dfs_from(&self, start: &OperatorId) -> Vec<OperatorId> {
128 let mut visited = HashSet::new();
129 let mut result = Vec::new();
130 let mut stack = vec![*start];
131
132 while let Some(node_id) = stack.pop() {
133 if visited.insert(node_id) {
134 result.push(node_id);
135
136 if let Some(neighbors) = self.outgoing.get(&node_id) {
137 for neighbor in neighbors.iter().rev() {
138 if !visited.contains(neighbor) {
139 stack.push(*neighbor);
140 }
141 }
142 }
143 }
144 }
145
146 result
147 }
148
149 fn creates_cycle(&self, source: &OperatorId, target: &OperatorId) -> bool {
150 let reachable = self.dfs_from(target);
151 reachable.contains(source)
152 }
153
154 pub fn nodes(&self) -> impl Iterator<Item = (&OperatorId, &NodeData)> {
155 self.nodes.iter()
156 }
157
158 pub fn edges(&self) -> impl Iterator<Item = &FlowEdge> {
159 self.edges.iter()
160 }
161
162 pub fn is_empty(&self) -> bool {
163 self.nodes.is_empty()
164 }
165
166 pub fn clear(&mut self) {
167 self.nodes.clear();
168 self.edges.clear();
169 self.outgoing.clear();
170 self.incoming.clear();
171 }
172}
173
174impl<NodeData> Default for DirectedGraph<NodeData> {
175 fn default() -> Self {
176 Self::new()
177 }
178}
179
180#[cfg(test)]
181pub mod tests {
182 use super::*;
183 use crate::interface::catalog::flow::FlowId;
184
185 const FLOW: FlowId = FlowId(1);
186
187 #[test]
188 fn test_basic_graph_operations() {
189 let mut graph = DirectedGraph::new();
190
191 let node1 = graph.add_node(OperatorId(1), "Node 1");
192 let node2 = graph.add_node(OperatorId(2), "Node 2");
193 let node3 = graph.add_node(OperatorId(3), "Node 3");
194
195 assert_eq!(graph.node_count(), 3);
196 assert_eq!(graph.edge_count(), 0);
197
198 graph.add_edge(FlowEdge::new(1, FLOW, node1, node2));
199 graph.add_edge(FlowEdge::new(2, FLOW, node2, node3));
200
201 assert_eq!(graph.edge_count(), 2);
202 assert_eq!(graph.neighbors(&node1), vec![OperatorId(2)]);
203 assert_eq!(graph.neighbors(&node2), vec![OperatorId(3)]);
204 assert_eq!(graph.predecessors(&node3), vec![OperatorId(2)]);
205 }
206
207 #[test]
208 #[should_panic(expected = "Adding edge would create a cycle")]
209 fn test_cycle_detection() {
210 let mut graph = DirectedGraph::new();
211
212 let node1 = graph.add_node(OperatorId(1), "Node 1");
213 let node2 = graph.add_node(OperatorId(2), "Node 2");
214 let node3 = graph.add_node(OperatorId(3), "Node 3");
215
216 graph.add_edge(FlowEdge::new(1, FLOW, node1, node2));
217 graph.add_edge(FlowEdge::new(2, FLOW, node2, node3));
218
219 graph.add_edge(FlowEdge::new(3, FLOW, node3, node1));
220 }
221
222 #[test]
223 fn test_topological_sort() {
224 let mut graph = DirectedGraph::new();
225
226 let node1 = graph.add_node(OperatorId(1), "Node 1");
227 let node2 = graph.add_node(OperatorId(2), "Node 2");
228 let node3 = graph.add_node(OperatorId(3), "Node 3");
229
230 graph.add_edge(FlowEdge::new(1, FLOW, node1, node2));
231 graph.add_edge(FlowEdge::new(2, FLOW, node2, node3));
232
233 let sorted = graph.topological_sort();
234 assert_eq!(sorted, vec![OperatorId(1), OperatorId(2), OperatorId(3)]);
235 }
236
237 #[test]
238 fn test_dfs_traversal() {
239 let mut graph = DirectedGraph::new();
240
241 let node1 = graph.add_node(OperatorId(1), "Node 1");
242 let node2 = graph.add_node(OperatorId(2), "Node 2");
243 let node3 = graph.add_node(OperatorId(3), "Node 3");
244 let node4 = graph.add_node(OperatorId(4), "Node 4");
245
246 graph.add_edge(FlowEdge::new(1, FLOW, node1, node2));
247 graph.add_edge(FlowEdge::new(2, FLOW, node1, node3));
248 graph.add_edge(FlowEdge::new(3, FLOW, node2, node4));
249
250 let dfs_result = graph.dfs_from(&node1);
251 assert!(dfs_result.contains(&OperatorId(1)));
252 assert!(dfs_result.contains(&OperatorId(2)));
253 assert!(dfs_result.contains(&OperatorId(3)));
254 assert!(dfs_result.contains(&OperatorId(4)));
255 assert_eq!(dfs_result.len(), 4);
256 }
257}