1use std::collections::BTreeMap;
7
8use sim_kernel::{Cx, Result, Symbol};
9
10use crate::{EdgeId, Graph, NodeId, PortMode, PortRef, validate::validate_graph};
11
12#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct CompiledGraph {
15 pub name: Symbol,
17 pub nodes: Vec<CompiledNode>,
19 pub edges: Vec<CompiledEdge>,
21 pub node_index_by_id: BTreeMap<NodeId, usize>,
23 pub edge_index_by_id: BTreeMap<EdgeId, usize>,
25 pub input_nodes: Vec<usize>,
27 pub output_nodes: Vec<usize>,
29 pub incoming_edges: Vec<Vec<usize>>,
31 pub outgoing_edges: Vec<Vec<usize>>,
33 pub reachable_from_inputs: Vec<bool>,
35 pub cyclic_nodes: Vec<bool>,
37 pub cycle_edges: Vec<bool>,
39}
40
41#[derive(Clone, Debug, PartialEq, Eq)]
43pub struct CompiledNode {
44 pub source_index: usize,
46 pub id: NodeId,
48 pub verb: Symbol,
50 pub has_stream_ports: bool,
52}
53
54#[derive(Clone, Debug, PartialEq, Eq)]
56pub struct CompiledEdge {
57 pub source_index: usize,
59 pub id: EdgeId,
61 pub from: PortRef,
63 pub to: PortRef,
65 pub from_node: usize,
67 pub to_node: usize,
69 pub priority: i64,
71 pub max_visits: Option<u32>,
73}
74
75pub fn compile_graph(cx: &mut Cx, graph: &Graph) -> Result<CompiledGraph> {
81 validate_graph(cx, graph)?;
82
83 let node_index_by_id = node_indexes(graph);
84 let input_nodes = boundary_nodes(graph, "in");
85 let output_nodes = boundary_nodes(graph, "out");
86 let nodes = compile_nodes(graph);
87 let (edges, edge_index_by_id) = compile_edges(graph, &node_index_by_id);
88 let (incoming_edges, outgoing_edges) = edge_lists(graph.nodes.len(), &edges);
89 let reachable_from_inputs =
90 reachable_from_inputs(graph.nodes.len(), &edges, &outgoing_edges, &input_nodes);
91 let (cyclic_nodes, cycle_edges) = cycle_metadata(graph.nodes.len(), &edges, &outgoing_edges);
92
93 Ok(CompiledGraph {
94 name: graph.name.clone(),
95 nodes,
96 edges,
97 node_index_by_id,
98 edge_index_by_id,
99 input_nodes,
100 output_nodes,
101 incoming_edges,
102 outgoing_edges,
103 reachable_from_inputs,
104 cyclic_nodes,
105 cycle_edges,
106 })
107}
108
109fn node_indexes(graph: &Graph) -> BTreeMap<NodeId, usize> {
110 graph
111 .nodes
112 .iter()
113 .enumerate()
114 .map(|(index, node)| (node.id.clone(), index))
115 .collect()
116}
117
118fn boundary_nodes(graph: &Graph, verb: &str) -> Vec<usize> {
119 graph
120 .nodes
121 .iter()
122 .enumerate()
123 .filter_map(|(index, node)| (node.verb.name.as_ref() == verb).then_some(index))
124 .collect()
125}
126
127fn compile_nodes(graph: &Graph) -> Vec<CompiledNode> {
128 graph
129 .nodes
130 .iter()
131 .enumerate()
132 .map(|(source_index, node)| CompiledNode {
133 source_index,
134 id: node.id.clone(),
135 verb: node.verb.clone(),
136 has_stream_ports: node
137 .inputs
138 .iter()
139 .chain(node.outputs.iter())
140 .any(|port| port.mode == PortMode::Stream),
141 })
142 .collect()
143}
144
145fn compile_edges(
146 graph: &Graph,
147 node_index_by_id: &BTreeMap<NodeId, usize>,
148) -> (Vec<CompiledEdge>, BTreeMap<EdgeId, usize>) {
149 let mut edge_index_by_id = BTreeMap::new();
150 let edges = graph
151 .edges
152 .iter()
153 .enumerate()
154 .map(|(source_index, edge)| {
155 edge_index_by_id.insert(edge.id, source_index);
156 CompiledEdge {
157 source_index,
158 id: edge.id,
159 from: edge.from.clone(),
160 to: edge.to.clone(),
161 from_node: node_index_by_id[&edge.from.node],
162 to_node: node_index_by_id[&edge.to.node],
163 priority: edge.priority,
164 max_visits: edge.max_visits,
165 }
166 })
167 .collect();
168 (edges, edge_index_by_id)
169}
170
171fn edge_lists(node_count: usize, edges: &[CompiledEdge]) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
172 let mut incoming = vec![Vec::new(); node_count];
173 let mut outgoing = vec![Vec::new(); node_count];
174
175 for edge in edges {
176 incoming[edge.to_node].push(edge.source_index);
177 outgoing[edge.from_node].push(edge.source_index);
178 }
179
180 for edge_list in &mut outgoing {
181 edge_list.sort_by_key(|edge_index| (edges[*edge_index].priority, *edge_index));
182 }
183
184 (incoming, outgoing)
185}
186
187fn reachable_from_inputs(
188 node_count: usize,
189 edges: &[CompiledEdge],
190 outgoing_edges: &[Vec<usize>],
191 input_nodes: &[usize],
192) -> Vec<bool> {
193 let mut reachable = vec![false; node_count];
194 for input in input_nodes {
195 visit_reachable(*input, edges, outgoing_edges, &mut reachable);
196 }
197 reachable
198}
199
200fn visit_reachable(
201 node: usize,
202 edges: &[CompiledEdge],
203 outgoing_edges: &[Vec<usize>],
204 reachable: &mut [bool],
205) {
206 if reachable[node] {
207 return;
208 }
209 reachable[node] = true;
210 for edge_index in &outgoing_edges[node] {
211 visit_reachable(edges[*edge_index].to_node, edges, outgoing_edges, reachable);
212 }
213}
214
215fn cycle_metadata(
216 node_count: usize,
217 edges: &[CompiledEdge],
218 outgoing_edges: &[Vec<usize>],
219) -> (Vec<bool>, Vec<bool>) {
220 let reachability = transitive_reachability(node_count, edges, outgoing_edges);
221 let mut cyclic_nodes = vec![false; node_count];
222 let mut cycle_edges = vec![false; edges.len()];
223
224 for edge in edges {
225 if edge.from_node == edge.to_node || reachability[edge.to_node][edge.from_node] {
226 cycle_edges[edge.source_index] = true;
227 cyclic_nodes[edge.from_node] = true;
228 cyclic_nodes[edge.to_node] = true;
229 }
230 }
231
232 (cyclic_nodes, cycle_edges)
233}
234
235fn transitive_reachability(
236 node_count: usize,
237 edges: &[CompiledEdge],
238 outgoing_edges: &[Vec<usize>],
239) -> Vec<Vec<bool>> {
240 let mut reachability = vec![vec![false; node_count]; node_count];
241 for start in 0..node_count {
242 visit_from(start, start, edges, outgoing_edges, &mut reachability);
243 }
244 reachability
245}
246
247fn visit_from(
248 start: usize,
249 node: usize,
250 edges: &[CompiledEdge],
251 outgoing_edges: &[Vec<usize>],
252 reachability: &mut [Vec<bool>],
253) {
254 for edge_index in &outgoing_edges[node] {
255 let next = edges[*edge_index].to_node;
256 if reachability[start][next] {
257 continue;
258 }
259 reachability[start][next] = true;
260 visit_from(start, next, edges, outgoing_edges, reachability);
261 }
262}