1use sim_kernel::{Expr, Symbol};
6
7pub const TOPOLOGY_API: &str = "sim.topology.v3";
9
10pub const DEFAULT_GRAPH_VERSION: &str = "0.1.0";
12
13pub const DEFAULT_MAX_STEPS: u32 = 256;
15
16pub const DEFAULT_MAX_NODE_VISITS: u32 = 64;
18
19pub const DEFAULT_MAX_EDGE_VISITS: u32 = 64;
21
22#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub struct NodeId(pub Symbol);
25
26impl NodeId {
27 pub fn new(name: impl Into<String>) -> Self {
29 Self(Symbol::new(name.into()))
30 }
31
32 pub fn as_symbol(&self) -> &Symbol {
34 &self.0
35 }
36}
37
38impl From<Symbol> for NodeId {
39 fn from(value: Symbol) -> Self {
40 Self(value)
41 }
42}
43
44impl From<&str> for NodeId {
45 fn from(value: &str) -> Self {
46 Self::new(value)
47 }
48}
49
50impl From<String> for NodeId {
51 fn from(value: String) -> Self {
52 Self::new(value)
53 }
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
58pub struct EdgeId(pub u32);
59
60impl EdgeId {
61 pub const fn new(index: u32) -> Self {
63 Self(index)
64 }
65}
66
67impl From<u32> for EdgeId {
68 fn from(value: u32) -> Self {
69 Self(value)
70 }
71}
72
73#[derive(Clone, Debug, PartialEq, Eq, Hash)]
75pub struct PortRef {
76 pub node: NodeId,
78 pub port: Symbol,
80}
81
82impl PortRef {
83 pub fn new(node: impl Into<NodeId>, port: Symbol) -> Self {
85 Self {
86 node: node.into(),
87 port,
88 }
89 }
90
91 pub fn named(node: impl Into<NodeId>, port: impl Into<String>) -> Self {
93 Self::new(node, Symbol::new(port.into()))
94 }
95
96 pub fn input(node: impl Into<NodeId>) -> Self {
98 Self::named(node, "in")
99 }
100
101 pub fn output(node: impl Into<NodeId>) -> Self {
103 Self::named(node, "out")
104 }
105}
106
107#[derive(Clone, Debug)]
109pub struct Graph {
110 pub name: Symbol,
112 pub version: String,
114 pub api: String,
116 pub input: Option<Expr>,
118 pub output: Option<Expr>,
120 pub nodes: Vec<Node>,
122 pub edges: Vec<Edge>,
124 pub cells: Vec<Cell>,
126 pub scheduler: Scheduler,
128 pub budget: Budget,
130 pub capabilities: Vec<Symbol>,
132 pub metadata: Vec<(Symbol, Expr)>,
134 pub tests: Vec<GraphTest>,
136}
137
138impl Graph {
139 pub fn new(name: Symbol) -> Self {
141 Self {
142 name,
143 version: DEFAULT_GRAPH_VERSION.to_owned(),
144 api: TOPOLOGY_API.to_owned(),
145 input: None,
146 output: None,
147 nodes: Vec::new(),
148 edges: Vec::new(),
149 cells: Vec::new(),
150 scheduler: Scheduler::default(),
151 budget: Budget::default(),
152 capabilities: Vec::new(),
153 metadata: Vec::new(),
154 tests: Vec::new(),
155 }
156 }
157
158 pub fn minimal(name: impl Into<String>) -> Self {
160 Self::new(Symbol::new(name.into()))
161 }
162
163 pub fn placeholder() -> Self {
165 Self::minimal("topology")
166 }
167}
168
169impl Default for Graph {
170 fn default() -> Self {
171 Self::minimal("topology")
172 }
173}
174
175#[derive(Clone, Debug)]
177pub struct Node {
178 pub id: NodeId,
180 pub verb: Symbol,
182 pub inputs: Vec<Port>,
184 pub outputs: Vec<Port>,
186 pub target: Option<Expr>,
188 pub role: Option<Symbol>,
190 pub input: Option<Expr>,
192 pub output: Option<Expr>,
194 pub options: Vec<(Symbol, Expr)>,
196}
197
198impl Node {
199 pub fn new(id: impl Into<NodeId>, verb: Symbol) -> Self {
201 let (inputs, outputs) = default_ports_for_verb(&verb);
202 Self {
203 id: id.into(),
204 verb,
205 inputs,
206 outputs,
207 target: None,
208 role: None,
209 input: None,
210 output: None,
211 options: Vec::new(),
212 }
213 }
214
215 pub fn named(id: impl Into<NodeId>, verb: impl Into<String>) -> Self {
217 Self::new(id, Symbol::new(verb.into()))
218 }
219
220 pub fn with_ports(
222 id: impl Into<NodeId>,
223 verb: Symbol,
224 inputs: Vec<Port>,
225 outputs: Vec<Port>,
226 ) -> Self {
227 Self {
228 id: id.into(),
229 verb,
230 inputs,
231 outputs,
232 target: None,
233 role: None,
234 input: None,
235 output: None,
236 options: Vec::new(),
237 }
238 }
239}
240
241#[derive(Clone, Debug)]
243pub struct Port {
244 pub name: Symbol,
246 pub shape: Option<Expr>,
248 pub mode: PortMode,
250 pub required: bool,
252}
253
254impl Port {
255 pub fn new(name: Symbol, mode: PortMode, required: bool) -> Self {
257 Self {
258 name,
259 shape: None,
260 mode,
261 required,
262 }
263 }
264
265 pub fn value(name: impl Into<String>, required: bool) -> Self {
267 Self::new(Symbol::new(name.into()), PortMode::Value, required)
268 }
269
270 pub fn stream(name: impl Into<String>, required: bool) -> Self {
272 Self::new(Symbol::new(name.into()), PortMode::Stream, required)
273 }
274}
275
276#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
278pub enum PortMode {
279 #[default]
281 Value,
282 Stream,
284}
285
286#[derive(Clone, Debug)]
288pub struct Edge {
289 pub id: EdgeId,
291 pub from: PortRef,
293 pub to: PortRef,
295 pub when: Option<Expr>,
297 pub transform: Option<Expr>,
299 pub as_name: Option<Symbol>,
301 pub priority: i64,
303 pub max_visits: Option<u32>,
305 pub buffer: Option<Expr>,
307 pub metadata: Vec<(Symbol, Expr)>,
309}
310
311impl Edge {
312 pub fn new(id: impl Into<EdgeId>, from: PortRef, to: PortRef) -> Self {
314 Self {
315 id: id.into(),
316 from,
317 to,
318 when: None,
319 transform: None,
320 as_name: None,
321 priority: 0,
322 max_visits: None,
323 buffer: None,
324 metadata: Vec::new(),
325 }
326 }
327}
328
329#[derive(Clone, Debug)]
331pub struct Cell {
332 pub name: Symbol,
334 pub shape: Option<Expr>,
336 pub initial: Expr,
338 pub merge: Option<Symbol>,
340 pub private: bool,
342}
343
344impl Cell {
345 pub fn new(name: Symbol, initial: Expr) -> Self {
347 Self {
348 name,
349 shape: None,
350 initial,
351 merge: None,
352 private: false,
353 }
354 }
355}
356
357#[derive(Clone, Debug, PartialEq, Eq)]
359pub struct Scheduler {
360 pub mode: SchedulerMode,
362 pub seed: Option<u64>,
364 pub max_concurrency: u32,
366 pub deterministic: bool,
368}
369
370impl Default for Scheduler {
371 fn default() -> Self {
372 Self {
373 mode: SchedulerMode::Sequential,
374 seed: None,
375 max_concurrency: 1,
376 deterministic: true,
377 }
378 }
379}
380
381#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
383pub enum SchedulerMode {
384 #[default]
386 Sequential,
387}
388
389#[derive(Clone, Debug, PartialEq, Eq)]
391pub struct Budget {
392 pub max_steps: u32,
394 pub max_node_visits: u32,
396 pub max_edge_visits: u32,
398 pub max_outputs: u32,
400 pub max_child_runs: u32,
402 pub deadline_ms: Option<u64>,
404 pub on_exhausted: BudgetExhausted,
406}
407
408impl Default for Budget {
409 fn default() -> Self {
410 Self {
411 max_steps: DEFAULT_MAX_STEPS,
412 max_node_visits: DEFAULT_MAX_NODE_VISITS,
413 max_edge_visits: DEFAULT_MAX_EDGE_VISITS,
414 max_outputs: 64,
415 max_child_runs: 16,
416 deadline_ms: None,
417 on_exhausted: BudgetExhausted::Fail,
418 }
419 }
420}
421
422#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
424pub enum BudgetExhausted {
425 #[default]
427 Fail,
428 Partial,
430}
431
432#[derive(Clone, Debug)]
434pub struct GraphTest {
435 pub name: Symbol,
437 pub input: Expr,
439 pub expect: Expr,
441 pub fixtures: Vec<(Symbol, Expr)>,
443}
444
445impl GraphTest {
446 pub fn new(name: Symbol, input: Expr, expect: Expr) -> Self {
448 Self {
449 name,
450 input,
451 expect,
452 fixtures: Vec::new(),
453 }
454 }
455}
456
457fn default_ports_for_verb(verb: &Symbol) -> (Vec<Port>, Vec<Port>) {
458 match verb.name.as_ref() {
459 "in" => (Vec::new(), vec![Port::value("out", true)]),
460 "out" => (vec![Port::value("in", true)], Vec::new()),
461 "call" => (
462 vec![Port::value("in", true)],
463 vec![Port::value("out", true), Port::value("error", false)],
464 ),
465 "branch" => (
466 vec![Port::value("in", true)],
467 vec![
468 Port::value("true", false),
469 Port::value("false", false),
470 Port::value("else", false),
471 ],
472 ),
473 "merge" => (
474 vec![Port::value("in", true)],
475 vec![Port::value("out", true)],
476 ),
477 "tee" => (
478 vec![Port::value("in", true)],
479 vec![Port::value("out", true)],
480 ),
481 _ => (
482 vec![Port::value("in", true)],
483 vec![Port::value("out", true)],
484 ),
485 }
486}