rtlola_hir/modes/
ordering.rs

1use std::collections::HashMap;
2
3use petgraph::algo::is_cyclic_directed;
4use petgraph::Outgoing;
5use serde::{Deserialize, Serialize};
6
7use super::{Ordered, OrderedTrait, TypedTrait};
8use crate::hir::{Hir, SRef};
9use crate::modes::dependencies::ExtendedDepGraph;
10use crate::modes::{DepAnaTrait, DependencyGraph, HirMode};
11
12impl OrderedTrait for Ordered {
13    fn stream_layers(&self, sr: SRef) -> StreamLayers {
14        self.stream_layers[&sr]
15    }
16}
17
18/// Represents a layer indicating the position when an expression can be evaluated
19#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
20pub struct Layer(usize);
21
22/// Wrapper to collect the layer when a stream instance is spawned and evaluated
23#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
24pub struct StreamLayers {
25    spawn: Layer,
26    #[cfg(feature = "shift_layer")]
27    shift: Layer,
28    evaluation: Layer,
29}
30
31impl StreamLayers {
32    #[cfg(not(feature = "shift_layer"))]
33    /// Produces the wrapper [StreamLayers] for a given spawn and evaluation layer
34    pub(crate) fn new(spawn_layer: Layer, evaluation_layer: Layer) -> StreamLayers {
35        StreamLayers {
36            spawn: spawn_layer,
37            evaluation: evaluation_layer,
38        }
39    }
40
41    #[cfg(feature = "shift_layer")]
42    /// Produces the wrapper [StreamLayers] for a given spawn, shift and evaluation layer
43    pub(crate) fn new(
44        spawn_layer: Layer,
45        shift_layer: Layer,
46        evaluation_layer: Layer,
47    ) -> StreamLayers {
48        StreamLayers {
49            spawn: spawn_layer,
50            shift: shift_layer,
51            evaluation: evaluation_layer,
52        }
53    }
54
55    /// Returns the layer when a stream is spawned
56    pub fn spawn_layer(&self) -> Layer {
57        self.spawn
58    }
59
60    #[cfg(feature = "shift_layer")]
61    /// Returns the layer when a stream is shifted
62    pub fn shift_layer(&self) -> Layer {
63        self.shift
64    }
65
66    /// Returns the layer when a new stream value is produced
67    pub fn evaluation_layer(&self) -> Layer {
68        self.evaluation
69    }
70}
71
72impl From<Layer> for usize {
73    fn from(layer: Layer) -> usize {
74        layer.0
75    }
76}
77
78impl Layer {
79    /// Produces a [Layer]
80    pub fn new(layer: usize) -> Self {
81        Layer(layer)
82    }
83
84    /// Returns the [Layer] as `usize`
85    pub fn inner(self) -> usize {
86        self.0
87    }
88}
89
90impl Ordered {
91    /// Returns the spawn and evaluation layer of each stream
92    ///
93    /// This function analyzes the `spec` and returns the spawn and evaluation layer of each stream.
94    /// The analysis splits the dependency graph into two subgraphs: one with the event-based streams and one with the periodic streams.
95    /// From these two graphs, it computed the spawn and evaluation layer for event-based and periodic streams separately.
96    pub(crate) fn analyze<M>(spec: &Hir<M>) -> Ordered
97    where
98        M: HirMode + DepAnaTrait + TypedTrait,
99    {
100        // split graph in periodic and event-based
101        let stream_layers = Self::compute_layers(spec, spec.graph());
102        Ordered { stream_layers }
103    }
104
105    /// Returns the spawn and evaluation layer of for either each event-based stream or periodic stream
106    ///
107    /// This function splits the dependency graph into two subgraphs and returns the spawn and evaluation layer of each stream.
108    /// The first graph only contains the lookups occurring in the spawn part of the stream template.
109    /// The analysis computes from this graph the spawn layers of each stream.
110    /// The function computes from the second graph the evaluation layers of each stream.
111    fn compute_layers<M>(spec: &Hir<M>, graph: &DependencyGraph) -> HashMap<SRef, StreamLayers>
112    where
113        M: HirMode + DepAnaTrait + TypedTrait,
114    {
115        // Prepare graphs
116        let graph = &graph
117            .clone()
118            .without_negative_offset_edges()
119            .without_close()
120            .without_different_pacing(spec);
121        let spawn_graph = &graph.clone().only_spawn();
122        #[cfg(feature = "shift_layer")]
123        let shift_graph = &graph.clone().only_filter();
124
125        debug_assert!(
126            !is_cyclic_directed(graph),
127            "This should be already checked in the dependency analysis."
128        );
129
130        // start analysis
131        let mut evaluation_layers = spec
132            .inputs()
133            .map(|i| {
134                (
135                    i.sr,
136                    Layer::new(if cfg!(feature = "shift_layer") { 1 } else { 0 }),
137                )
138            })
139            .collect::<HashMap<SRef, Layer>>();
140        let mut spawn_layers = spec
141            .inputs()
142            .map(|i| (i.sr, Layer::new(0)))
143            .collect::<HashMap<SRef, Layer>>();
144        #[cfg(feature = "shift_layer")]
145        let mut shift_layers = spec
146            .inputs()
147            .map(|i| (i.sr, Layer::new(0)))
148            .collect::<HashMap<SRef, Layer>>();
149
150        while graph.node_count() != evaluation_layers.len() {
151            // build spawn layers
152            spawn_graph.node_indices().for_each(|node| {
153                let sref = spawn_graph.node_weight(node).unwrap();
154                // If we dont know the spawn layer yet
155                if !spawn_layers.contains_key(sref) {
156                    // get evaluation layer of successors
157                    let neighbor_layers: Vec<_> = spawn_graph
158                        .neighbors_directed(node, Outgoing)
159                        .map(|outgoing_neighbor| {
160                            evaluation_layers
161                                .get(spawn_graph.node_weight(outgoing_neighbor).unwrap())
162                                .copied()
163                        })
164                        .collect();
165                    let computed_spawn_layer = if neighbor_layers.is_empty() {
166                        Some(Layer::new(0))
167                    } else {
168                        neighbor_layers
169                            .into_iter()
170                            .try_fold(Layer::new(0), |cur_res_layer, neighbor_layer| {
171                                neighbor_layer.map(|nl| std::cmp::max(cur_res_layer, nl))
172                            })
173                            .map(|layer| Layer::new(layer.inner() + 1))
174                    };
175                    if let Some(layer) = computed_spawn_layer {
176                        spawn_layers.insert(*sref, layer);
177                    }
178                }
179            });
180            #[cfg(feature = "shift_layer")]
181            shift_graph.node_indices().for_each(|node| {
182                let sref = shift_graph.node_weight(node).unwrap();
183                // if we dont know the shift layer and evaluation layer, but the spawn layer is known
184                if !shift_layers.contains_key(sref)
185                    && !evaluation_layers.contains_key(sref)
186                    && spawn_layers.contains_key(sref)
187                {
188                    // Get evaluation layer of successors
189                    let neighbor_layers: Vec<_> = shift_graph
190                        .neighbors_directed(node, Outgoing)
191                        .flat_map(|outgoing_neighbor| {
192                            //ignore selfloops
193                            (outgoing_neighbor != node).then_some(outgoing_neighbor)
194                        })
195                        .map(|outgoing_neighbor| {
196                            evaluation_layers
197                                .get(shift_graph.node_weight(outgoing_neighbor).unwrap())
198                                .copied()
199                        })
200                        .collect();
201                    let computed_shift_layer = if neighbor_layers.is_empty() {
202                        // There are no successors
203                        Some(Layer::new(1))
204                    } else {
205                        neighbor_layers
206                            .into_iter()
207                            .try_fold(Layer::new(0), |cur_res_layer, neighbor_layer| {
208                                neighbor_layer.map(|nl| std::cmp::max(cur_res_layer, nl))
209                            })
210                            .map(|layer| Layer::new(layer.inner() + 1))
211                    };
212                    if let Some(layer) = computed_shift_layer {
213                        // Shift layer has to be greater than spawn layer
214                        let layer = if spawn_layers[sref] < layer {
215                            layer
216                        } else {
217                            Layer::new(spawn_layers[sref].inner() + 1)
218                        };
219                        shift_layers.insert(*sref, layer);
220                    }
221                }
222            });
223
224            #[cfg(feature = "shift_layer")]
225            let previous_layers = &shift_layers;
226            #[cfg(not(feature = "shift_layer"))]
227            let previous_layers = &spawn_layers;
228
229            graph.node_indices().for_each(|node| {
230                let sref = graph.node_weight(node).unwrap();
231                // if we dont know the evaluation layer, but the spawn layer is known
232                if !evaluation_layers.contains_key(sref) && previous_layers.contains_key(sref) {
233                    // Get evaluation layer of successors
234                    let neighbor_layers: Vec<_> = graph
235                        .neighbors_directed(node, Outgoing)
236                        .flat_map(|outgoing_neighbor| {
237                            //ignore selfloops
238                            (outgoing_neighbor != node).then_some(outgoing_neighbor)
239                        })
240                        .map(|outgoing_neighbor| {
241                            evaluation_layers
242                                .get(graph.node_weight(outgoing_neighbor).unwrap())
243                                .copied()
244                        })
245                        .collect();
246                    let computed_evaluation_layer = if neighbor_layers.is_empty() {
247                        // There are no successors
248                        Some(Layer::new(if cfg!(feature = "shift_layer") {
249                            2
250                        } else {
251                            1
252                        }))
253                    } else {
254                        // eval_layer = max(successor_eval_layers) + 1
255                        neighbor_layers
256                            .into_iter()
257                            .try_fold(Layer::new(0), |cur_res_layer, neighbor_layer| {
258                                neighbor_layer.map(|nl| std::cmp::max(cur_res_layer, nl))
259                            })
260                            .map(|layer| Layer::new(layer.inner() + 1))
261                    };
262                    if let Some(layer) = computed_evaluation_layer {
263                        // Evaluation layer has to be greater than spawn layer
264                        let layer = if previous_layers[sref] < layer {
265                            layer
266                        } else {
267                            Layer::new(previous_layers[sref].inner() + 1)
268                        };
269                        evaluation_layers.insert(*sref, layer);
270                    }
271                }
272            });
273        }
274        evaluation_layers
275            .into_iter()
276            .map(|(key, evaluation_layer)| {
277                #[cfg(feature = "shift_layer")]
278                let layer = (
279                    key,
280                    StreamLayers::new(spawn_layers[&key], shift_layers[&key], evaluation_layer),
281                );
282                #[cfg(not(feature = "shift_layer"))]
283                let layer = (key, StreamLayers::new(spawn_layers[&key], evaluation_layer));
284                layer
285            })
286            .collect::<HashMap<SRef, StreamLayers>>()
287    }
288}
289
290#[cfg(test)]
291fn check_eval_order_for_spec(spec: &str, ref_layers: HashMap<SRef, StreamLayers>) {
292    use crate::{config::FrontendConfig, BaseMode};
293    use rtlola_parser::{parse, ParserConfig};
294
295    let parser_config = ParserConfig::for_string(spec.to_string());
296    let frontend_config = FrontendConfig::from(&parser_config);
297    let ast = parse(&parser_config).unwrap_or_else(|e| panic!("{:?}", e));
298    let hir = Hir::<BaseMode>::from_ast(ast)
299        .unwrap()
300        .check_types(&frontend_config)
301        .unwrap()
302        .analyze_dependencies(&frontend_config)
303        .unwrap();
304    let order = Ordered::analyze(&hir);
305    let Ordered { stream_layers } = order;
306    assert_eq!(stream_layers.len(), ref_layers.len());
307    stream_layers.iter().for_each(|(sr, layers)| {
308        let ref_layers = &ref_layers[sr];
309        assert_eq!(ref_layers.spawn_layer(), layers.spawn_layer());
310        assert_eq!(ref_layers.evaluation_layer(), layers.evaluation_layer());
311        #[cfg(feature = "shift_layer")]
312        assert_eq!(ref_layers.shift_layer(), layers.shift_layer());
313    });
314}
315
316#[cfg(test)]
317#[cfg(not(feature = "shift_layer"))]
318mod tests {
319    use super::*;
320
321    #[test]
322    fn synchronous_lookup() {
323        let spec = "input a: UInt8\noutput b: UInt8 := a\noutput c:UInt8 := b";
324        let sname_to_sref = vec![("a", SRef::In(0)), ("b", SRef::Out(0)), ("c", SRef::Out(1))]
325            .into_iter()
326            .collect::<HashMap<&str, SRef>>();
327        let event_layers = vec![
328            (
329                sname_to_sref["a"],
330                StreamLayers::new(Layer::new(0), Layer::new(0)),
331            ),
332            (
333                sname_to_sref["b"],
334                StreamLayers::new(Layer::new(0), Layer::new(1)),
335            ),
336            (
337                sname_to_sref["c"],
338                StreamLayers::new(Layer::new(0), Layer::new(2)),
339            ),
340        ]
341        .into_iter()
342        .collect();
343        check_eval_order_for_spec(spec, event_layers)
344    }
345
346    #[test]
347    fn hold_lookup() {
348        let spec =
349            "input a: UInt8\ninput b:UInt8\noutput c: UInt8 := a.hold().defaults(to: 0) + b\noutput d: UInt8 := c.hold().defaults(to: 0) + a";
350        let sname_to_sref = vec![
351            ("a", SRef::In(0)),
352            ("b", SRef::In(1)),
353            ("c", SRef::Out(0)),
354            ("d", SRef::Out(1)),
355        ]
356        .into_iter()
357        .collect::<HashMap<&str, SRef>>();
358        let event_layers = vec![
359            (
360                sname_to_sref["a"],
361                StreamLayers::new(Layer::new(0), Layer::new(0)),
362            ),
363            (
364                sname_to_sref["b"],
365                StreamLayers::new(Layer::new(0), Layer::new(0)),
366            ),
367            (
368                sname_to_sref["c"],
369                StreamLayers::new(Layer::new(0), Layer::new(1)),
370            ),
371            (
372                sname_to_sref["d"],
373                StreamLayers::new(Layer::new(0), Layer::new(2)),
374            ),
375        ]
376        .into_iter()
377        .collect();
378        check_eval_order_for_spec(spec, event_layers)
379    }
380
381    #[test]
382    fn offset_lookup() {
383        let spec = "input a: UInt8\noutput b: UInt8 := a.offset(by: -1).defaults(to: 0)\noutput c: UInt8 := b.offset(by: -1).defaults(to: 0)";
384        let sname_to_sref = vec![("a", SRef::In(0)), ("b", SRef::Out(0)), ("c", SRef::Out(1))]
385            .into_iter()
386            .collect::<HashMap<&str, SRef>>();
387        let event_layers = vec![
388            (
389                sname_to_sref["a"],
390                StreamLayers::new(Layer::new(0), Layer::new(0)),
391            ),
392            (
393                sname_to_sref["b"],
394                StreamLayers::new(Layer::new(0), Layer::new(1)),
395            ),
396            (
397                sname_to_sref["c"],
398                StreamLayers::new(Layer::new(0), Layer::new(1)),
399            ),
400        ]
401        .into_iter()
402        .collect();
403        check_eval_order_for_spec(spec, event_layers)
404    }
405
406    #[test]
407    fn sliding_window_lookup() {
408        let spec = "input a: UInt8\noutput b: UInt8 @1Hz := a.aggregate(over: 1s, using: sum)\noutput c: UInt8 := a + 3\noutput d: UInt8 @1Hz := c.aggregate(over: 1s, using: sum)\noutput e: UInt8 := b + d\noutput f: UInt8 @2Hz := e.hold().defaults(to: 0)";
409        let sname_to_sref = vec![
410            ("a", SRef::In(0)),
411            ("b", SRef::Out(0)),
412            ("c", SRef::Out(1)),
413            ("d", SRef::Out(2)),
414            ("e", SRef::Out(3)),
415            ("f", SRef::Out(4)),
416        ]
417        .into_iter()
418        .collect::<HashMap<&str, SRef>>();
419        let ref_layers = vec![
420            (
421                sname_to_sref["a"],
422                StreamLayers::new(Layer::new(0), Layer::new(0)),
423            ),
424            (
425                sname_to_sref["c"],
426                StreamLayers::new(Layer::new(0), Layer::new(1)),
427            ),
428            (
429                sname_to_sref["b"],
430                StreamLayers::new(Layer::new(0), Layer::new(1)),
431            ),
432            (
433                sname_to_sref["d"],
434                StreamLayers::new(Layer::new(0), Layer::new(1)),
435            ),
436            (
437                sname_to_sref["e"],
438                StreamLayers::new(Layer::new(0), Layer::new(2)),
439            ),
440            (
441                sname_to_sref["f"],
442                StreamLayers::new(Layer::new(0), Layer::new(3)),
443            ),
444        ]
445        .into_iter()
446        .collect();
447        check_eval_order_for_spec(spec, ref_layers)
448    }
449
450    #[test]
451    fn discrete_window_lookup() {
452        let spec = "input a: UInt8\n\
453        output b: UInt8 := a.aggregate(over_discrete: 5, using: sum)\n\
454        output c: UInt8 := a + 3\n\
455        output d: UInt8 := c.aggregate(over_discrete: 5, using: sum)";
456
457        let sname_to_sref = vec![
458            ("a", SRef::In(0)),
459            ("b", SRef::Out(0)),
460            ("c", SRef::Out(1)),
461            ("d", SRef::Out(2)),
462        ]
463        .into_iter()
464        .collect::<HashMap<&str, SRef>>();
465        let ref_layers = vec![
466            (
467                sname_to_sref["a"],
468                StreamLayers::new(Layer::new(0), Layer::new(0)),
469            ),
470            (
471                sname_to_sref["b"],
472                StreamLayers::new(Layer::new(0), Layer::new(1)),
473            ),
474            (
475                sname_to_sref["c"],
476                StreamLayers::new(Layer::new(0), Layer::new(1)),
477            ),
478            (
479                sname_to_sref["d"],
480                StreamLayers::new(Layer::new(0), Layer::new(2)),
481            ),
482        ]
483        .into_iter()
484        .collect();
485        check_eval_order_for_spec(spec, ref_layers)
486    }
487
488    #[test]
489    fn offset_lookups() {
490        let spec = "input a: UInt8\noutput b: UInt8 := a.offset(by:-1).defaults(to: 0)\noutput c: UInt8 := a.offset(by:-2).defaults(to: 0)\noutput d: UInt8 := a.offset(by:-3).defaults(to: 0)\noutput e: UInt8 := a.offset(by:-4).defaults(to: 0)";
491        let sname_to_sref = vec![
492            ("a", SRef::In(0)),
493            ("b", SRef::Out(0)),
494            ("c", SRef::Out(1)),
495            ("d", SRef::Out(2)),
496            ("e", SRef::Out(3)),
497        ]
498        .into_iter()
499        .collect::<HashMap<&str, SRef>>();
500        let event_layers = vec![
501            (
502                sname_to_sref["a"],
503                StreamLayers::new(Layer::new(0), Layer::new(0)),
504            ),
505            (
506                sname_to_sref["b"],
507                StreamLayers::new(Layer::new(0), Layer::new(1)),
508            ),
509            (
510                sname_to_sref["c"],
511                StreamLayers::new(Layer::new(0), Layer::new(1)),
512            ),
513            (
514                sname_to_sref["d"],
515                StreamLayers::new(Layer::new(0), Layer::new(1)),
516            ),
517            (
518                sname_to_sref["e"],
519                StreamLayers::new(Layer::new(0), Layer::new(1)),
520            ),
521        ]
522        .into_iter()
523        .collect();
524        check_eval_order_for_spec(spec, event_layers)
525    }
526    #[test]
527    fn negative_loop_different_offsets() {
528        let spec = "input a: Int8\noutput b: Int8 := a.offset(by: -1).defaults(to: 0) + d.offset(by:-2).defaults(to:0)\noutput c: Int8 := b.offset(by:-3).defaults(to: 0)\noutput d: Int8 := c.offset(by:-4).defaults(to: 0)";
529        let sname_to_sref = vec![
530            ("a", SRef::In(0)),
531            ("b", SRef::Out(0)),
532            ("c", SRef::Out(1)),
533            ("d", SRef::Out(2)),
534        ]
535        .into_iter()
536        .collect::<HashMap<&str, SRef>>();
537        let event_layers = vec![
538            (
539                sname_to_sref["a"],
540                StreamLayers::new(Layer::new(0), Layer::new(0)),
541            ),
542            (
543                sname_to_sref["b"],
544                StreamLayers::new(Layer::new(0), Layer::new(1)),
545            ),
546            (
547                sname_to_sref["c"],
548                StreamLayers::new(Layer::new(0), Layer::new(1)),
549            ),
550            (
551                sname_to_sref["d"],
552                StreamLayers::new(Layer::new(0), Layer::new(1)),
553            ),
554        ]
555        .into_iter()
556        .collect();
557        check_eval_order_for_spec(spec, event_layers)
558    }
559
560    #[test]
561    fn lookup_chain() {
562        let spec = "input a: Int8\noutput b: Int8 := a + d.hold().defaults(to:0)\noutput c: Int8 := b\noutput d: Int8 := c.offset(by:-4).defaults(to: 0)";
563        let sname_to_sref = vec![
564            ("a", SRef::In(0)),
565            ("b", SRef::Out(0)),
566            ("c", SRef::Out(1)),
567            ("d", SRef::Out(2)),
568        ]
569        .into_iter()
570        .collect::<HashMap<&str, SRef>>();
571        let event_layers = vec![
572            (
573                sname_to_sref["a"],
574                StreamLayers::new(Layer::new(0), Layer::new(0)),
575            ),
576            (
577                sname_to_sref["b"],
578                StreamLayers::new(Layer::new(0), Layer::new(2)),
579            ),
580            (
581                sname_to_sref["c"],
582                StreamLayers::new(Layer::new(0), Layer::new(3)),
583            ),
584            (
585                sname_to_sref["d"],
586                StreamLayers::new(Layer::new(0), Layer::new(1)),
587            ),
588        ]
589        .into_iter()
590        .collect();
591        check_eval_order_for_spec(spec, event_layers)
592    }
593
594    #[test]
595    fn multiple_input_stream() {
596        let spec = "input a: Int8\ninput b: Int8\noutput c: Int8 := a + b.hold().defaults(to:0)\noutput d: Int8 := a + c.offset(by: -1).defaults(to: 0)\noutput e: Int8 := c + 3\noutput f: Int8 := c + 6\noutput g: Int8 := b + 3\noutput h: Int8 := g + f";
597        let sname_to_sref = vec![
598            ("a", SRef::In(0)),
599            ("b", SRef::In(1)),
600            ("c", SRef::Out(0)),
601            ("d", SRef::Out(1)),
602            ("e", SRef::Out(2)),
603            ("f", SRef::Out(3)),
604            ("g", SRef::Out(4)),
605            ("h", SRef::Out(5)),
606        ]
607        .into_iter()
608        .collect::<HashMap<&str, SRef>>();
609        let event_layers = vec![
610            (
611                sname_to_sref["a"],
612                StreamLayers::new(Layer::new(0), Layer::new(0)),
613            ),
614            (
615                sname_to_sref["b"],
616                StreamLayers::new(Layer::new(0), Layer::new(0)),
617            ),
618            (
619                sname_to_sref["c"],
620                StreamLayers::new(Layer::new(0), Layer::new(1)),
621            ),
622            (
623                sname_to_sref["d"],
624                StreamLayers::new(Layer::new(0), Layer::new(1)),
625            ),
626            (
627                sname_to_sref["e"],
628                StreamLayers::new(Layer::new(0), Layer::new(2)),
629            ),
630            (
631                sname_to_sref["f"],
632                StreamLayers::new(Layer::new(0), Layer::new(2)),
633            ),
634            (
635                sname_to_sref["g"],
636                StreamLayers::new(Layer::new(0), Layer::new(1)),
637            ),
638            (
639                sname_to_sref["h"],
640                StreamLayers::new(Layer::new(0), Layer::new(3)),
641            ),
642        ]
643        .into_iter()
644        .collect();
645        check_eval_order_for_spec(spec, event_layers)
646    }
647
648    #[test]
649    fn event_and_periodic_stream_mix() {
650        let spec =
651            "input a : Int8 \ninput b :Int8\noutput c @2Hz := a.hold().defaults(to: 0) + 3\noutput d @1Hz := a.hold().defaults(to: 0) + c\noutput e := a + b";
652        let sname_to_sref = vec![
653            ("a", SRef::In(0)),
654            ("b", SRef::In(1)),
655            ("c", SRef::Out(0)),
656            ("d", SRef::Out(1)),
657            ("e", SRef::Out(2)),
658        ]
659        .into_iter()
660        .collect::<HashMap<&str, SRef>>();
661        let ref_layers = vec![
662            (
663                sname_to_sref["a"],
664                StreamLayers::new(Layer::new(0), Layer::new(0)),
665            ),
666            (
667                sname_to_sref["b"],
668                StreamLayers::new(Layer::new(0), Layer::new(0)),
669            ),
670            (
671                sname_to_sref["e"],
672                StreamLayers::new(Layer::new(0), Layer::new(1)),
673            ),
674            (
675                sname_to_sref["c"],
676                StreamLayers::new(Layer::new(0), Layer::new(1)),
677            ),
678            (
679                sname_to_sref["d"],
680                StreamLayers::new(Layer::new(0), Layer::new(2)),
681            ),
682        ]
683        .into_iter()
684        .collect();
685        check_eval_order_for_spec(spec, ref_layers)
686    }
687
688    #[test]
689    fn negative_and_postive_lookups_as_loop() {
690        let spec = "input a: Int8\noutput b: Int8 := a + d.offset(by:-1).defaults(to:0)\noutput c: Int8 := b\noutput d: Int8 := c";
691        let sname_to_sref = vec![
692            ("a", SRef::In(0)),
693            ("b", SRef::Out(0)),
694            ("c", SRef::Out(1)),
695            ("d", SRef::Out(2)),
696        ]
697        .into_iter()
698        .collect::<HashMap<&str, SRef>>();
699        let event_layers = vec![
700            (
701                sname_to_sref["a"],
702                StreamLayers::new(Layer::new(0), Layer::new(0)),
703            ),
704            (
705                sname_to_sref["b"],
706                StreamLayers::new(Layer::new(0), Layer::new(1)),
707            ),
708            (
709                sname_to_sref["c"],
710                StreamLayers::new(Layer::new(0), Layer::new(2)),
711            ),
712            (
713                sname_to_sref["d"],
714                StreamLayers::new(Layer::new(0), Layer::new(3)),
715            ),
716        ]
717        .into_iter()
718        .collect();
719        check_eval_order_for_spec(spec, event_layers)
720    }
721    #[test]
722    fn sliding_windows_chain_and_hold_lookup() {
723        let spec = "input a: Int8\noutput b@1Hz := a.aggregate(over: 1s, using: sum) + d.offset(by: -1).defaults(to: 0)\noutput c@2Hz := b.aggregate(over: 1s, using: sum)\noutput d@2Hz := b.hold().defaults(to: 0)";
724        let sname_to_sref = vec![
725            ("a", SRef::In(0)),
726            ("b", SRef::Out(0)),
727            ("c", SRef::Out(1)),
728            ("d", SRef::Out(2)),
729        ]
730        .into_iter()
731        .collect::<HashMap<&str, SRef>>();
732        let ref_layers = vec![
733            (
734                sname_to_sref["b"],
735                StreamLayers::new(Layer::new(0), Layer::new(1)),
736            ),
737            (
738                sname_to_sref["c"],
739                StreamLayers::new(Layer::new(0), Layer::new(2)),
740            ),
741            (
742                sname_to_sref["d"],
743                StreamLayers::new(Layer::new(0), Layer::new(2)),
744            ),
745            (
746                sname_to_sref["a"],
747                StreamLayers::new(Layer::new(0), Layer::new(0)),
748            ),
749        ]
750        .into_iter()
751        .collect();
752        check_eval_order_for_spec(spec, ref_layers)
753    }
754
755    #[test]
756    fn simple_chain_with_parameter() {
757        let spec =
758            "input a: Int8\noutput b := a + 5\noutput c(para) spawn with b eval with para + a";
759        let sname_to_sref = vec![("a", SRef::In(0)), ("b", SRef::Out(0)), ("c", SRef::Out(1))]
760            .into_iter()
761            .collect::<HashMap<&str, SRef>>();
762        let event_layers = vec![
763            (
764                sname_to_sref["a"],
765                StreamLayers::new(Layer::new(0), Layer::new(0)),
766            ),
767            (
768                sname_to_sref["b"],
769                StreamLayers::new(Layer::new(0), Layer::new(1)),
770            ),
771            (
772                sname_to_sref["c"],
773                StreamLayers::new(Layer::new(2), Layer::new(3)),
774            ),
775        ]
776        .into_iter()
777        .collect();
778        check_eval_order_for_spec(spec, event_layers)
779    }
780
781    #[test]
782    fn lookup_chain_with_parametrization() {
783        let spec = "input a: Int8\noutput b(para) spawn with a when a > 6 eval with a + para\noutput c(para) spawn with a when a > 6 eval with a + b(para)\noutput d(para) spawn with a when a > 6 eval with a + c(para)";
784        let sname_to_sref = vec![
785            ("a", SRef::In(0)),
786            ("b", SRef::Out(0)),
787            ("c", SRef::Out(1)),
788            ("d", SRef::Out(2)),
789        ]
790        .into_iter()
791        .collect::<HashMap<&str, SRef>>();
792        let event_layers = vec![
793            (
794                sname_to_sref["a"],
795                StreamLayers::new(Layer::new(0), Layer::new(0)),
796            ),
797            (
798                sname_to_sref["b"],
799                StreamLayers::new(Layer::new(1), Layer::new(2)),
800            ),
801            (
802                sname_to_sref["c"],
803                StreamLayers::new(Layer::new(1), Layer::new(3)),
804            ),
805            (
806                sname_to_sref["d"],
807                StreamLayers::new(Layer::new(1), Layer::new(4)),
808            ),
809        ]
810        .into_iter()
811        .collect();
812        check_eval_order_for_spec(spec, event_layers)
813    }
814
815    #[test]
816    fn parameter_loop_with_lookup_in_close() {
817        let spec = "\
818        input a: Int8\n\
819        input b: Int8\n\
820        output c(p) \n\
821            spawn with a when a < b\n\
822            eval with p + b + g(p).hold().defaults(to: 0)\n\
823        output d(p) \n\
824            spawn with b when c(4).hold().defaults(to: 0) < 4\n\
825            eval with b + 5\n\
826        output e(p)\n\
827            spawn with b\n\
828            eval with d(p).hold().defaults(to: 0) + b\n\
829        output f(p)\n\
830            spawn with b\n\
831            eval when e(p).hold().defaults(to: 0) < 6 with b + 5\n\
832        output g(p)\n\
833            spawn with b close @true when f(p).hold().defaults(to: 0) < 6\n\
834            eval with b + 5";
835        let sname_to_sref = vec![
836            ("a", SRef::In(0)),
837            ("b", SRef::In(1)),
838            ("c", SRef::Out(0)),
839            ("d", SRef::Out(1)),
840            ("e", SRef::Out(2)),
841            ("f", SRef::Out(3)),
842            ("g", SRef::Out(4)),
843        ]
844        .into_iter()
845        .collect::<HashMap<&str, SRef>>();
846        let event_layers = vec![
847            (
848                sname_to_sref["a"],
849                StreamLayers::new(Layer::new(0), Layer::new(0)),
850            ),
851            (
852                sname_to_sref["b"],
853                StreamLayers::new(Layer::new(0), Layer::new(0)),
854            ),
855            (
856                sname_to_sref["c"],
857                StreamLayers::new(Layer::new(1), Layer::new(3)),
858            ),
859            (
860                sname_to_sref["d"],
861                StreamLayers::new(Layer::new(4), Layer::new(5)),
862            ),
863            (
864                sname_to_sref["e"],
865                StreamLayers::new(Layer::new(1), Layer::new(6)),
866            ),
867            (
868                sname_to_sref["f"],
869                StreamLayers::new(Layer::new(1), Layer::new(7)),
870            ),
871            (
872                sname_to_sref["g"],
873                StreamLayers::new(Layer::new(1), Layer::new(2)),
874            ),
875        ]
876        .into_iter()
877        .collect();
878        check_eval_order_for_spec(spec, event_layers)
879    }
880
881    #[test]
882    fn parameter_nested_lookup_implicit() {
883        let spec = "input a: Int8\n input b: Int8\n output c(p) spawn with a eval with p + b\noutput d := c(c(b).hold().defaults(to: 0)).hold().defaults(to: 0)";
884        let sname_to_sref = vec![
885            ("a", SRef::In(0)),
886            ("b", SRef::In(1)),
887            ("c", SRef::Out(0)),
888            ("d", SRef::Out(1)),
889        ]
890        .into_iter()
891        .collect::<HashMap<&str, SRef>>();
892        let event_layers = vec![
893            (
894                sname_to_sref["a"],
895                StreamLayers::new(Layer::new(0), Layer::new(0)),
896            ),
897            (
898                sname_to_sref["b"],
899                StreamLayers::new(Layer::new(0), Layer::new(0)),
900            ),
901            (
902                sname_to_sref["c"],
903                StreamLayers::new(Layer::new(1), Layer::new(2)),
904            ),
905            (
906                sname_to_sref["d"],
907                StreamLayers::new(Layer::new(0), Layer::new(3)),
908            ),
909        ]
910        .into_iter()
911        .collect();
912        check_eval_order_for_spec(spec, event_layers)
913    }
914
915    #[test]
916    fn parameter_nested_lookup_explicit() {
917        let spec = "input a: Int8\n input b: Int8\n output c(p) spawn with a eval with p + b\noutput d := c(b).hold().defaults(to: 0)\noutput e := c(d).hold().defaults(to: 0)";
918        let sname_to_sref = vec![
919            ("a", SRef::In(0)),
920            ("b", SRef::In(1)),
921            ("c", SRef::Out(0)),
922            ("d", SRef::Out(1)),
923            ("e", SRef::Out(2)),
924        ]
925        .into_iter()
926        .collect::<HashMap<&str, SRef>>();
927        let event_layers = vec![
928            (
929                sname_to_sref["a"],
930                StreamLayers::new(Layer::new(0), Layer::new(0)),
931            ),
932            (
933                sname_to_sref["b"],
934                StreamLayers::new(Layer::new(0), Layer::new(0)),
935            ),
936            (
937                sname_to_sref["c"],
938                StreamLayers::new(Layer::new(1), Layer::new(2)),
939            ),
940            (
941                sname_to_sref["d"],
942                StreamLayers::new(Layer::new(0), Layer::new(3)),
943            ),
944            (
945                sname_to_sref["e"],
946                StreamLayers::new(Layer::new(0), Layer::new(4)),
947            ),
948        ]
949        .into_iter()
950        .collect();
951        check_eval_order_for_spec(spec, event_layers)
952    }
953
954    #[test]
955    fn test_spawn_eventbased() {
956        let spec = "input a: Int32\n\
957                  output b(x: Int32) spawn with a eval with x + a";
958        let sname_to_sref = vec![("a", SRef::In(0)), ("b", SRef::Out(0))]
959            .into_iter()
960            .collect::<HashMap<&str, SRef>>();
961        let event_layers = vec![
962            (
963                sname_to_sref["a"],
964                StreamLayers::new(Layer::new(0), Layer::new(0)),
965            ),
966            (
967                sname_to_sref["b"],
968                StreamLayers::new(Layer::new(1), Layer::new(2)),
969            ),
970        ]
971        .into_iter()
972        .collect();
973        check_eval_order_for_spec(spec, event_layers)
974    }
975
976    #[test]
977    fn test_delay() {
978        let spec = "input a: UInt64\n\
979                            output a_counter: UInt64 @a := a_counter.offset(by: -1).defaults(to: 0) + 1\n\
980                            output b(p: UInt64) spawn with a_counter when a = 1 close when if true then true else b(p) eval @1Hz with a.hold(or: 0) == 2";
981        let sname_to_sref = vec![
982            ("a", SRef::In(0)),
983            ("a_counter", SRef::Out(0)),
984            ("b", SRef::Out(1)),
985        ]
986        .into_iter()
987        .collect::<HashMap<&str, SRef>>();
988        let ref_layers = vec![
989            (
990                sname_to_sref["a"],
991                StreamLayers::new(Layer::new(0), Layer::new(0)),
992            ),
993            (
994                sname_to_sref["a_counter"],
995                StreamLayers::new(Layer::new(0), Layer::new(1)),
996            ),
997            (
998                sname_to_sref["b"],
999                StreamLayers::new(Layer::new(2), Layer::new(3)),
1000            ),
1001        ]
1002        .into_iter()
1003        .collect();
1004        check_eval_order_for_spec(spec, ref_layers)
1005    }
1006
1007    #[test]
1008    fn test_instance_aggregation() {
1009        let spec = "input a: Int32\n\
1010        output b (p) spawn with a eval when a > 5 with b(p).offset(by: -1).defaults(to: 0) + a\n\
1011        output c eval with b.aggregate(over_instances: fresh, using: Σ)\n";
1012        let sname_to_sref = vec![("a", SRef::In(0)), ("b", SRef::Out(0)), ("c", SRef::Out(1))]
1013            .into_iter()
1014            .collect::<HashMap<&str, SRef>>();
1015        let event_layers = vec![
1016            (
1017                sname_to_sref["a"],
1018                StreamLayers::new(Layer::new(0), Layer::new(0)),
1019            ),
1020            (
1021                sname_to_sref["b"],
1022                StreamLayers::new(Layer::new(1), Layer::new(2)),
1023            ),
1024            (
1025                sname_to_sref["c"],
1026                StreamLayers::new(Layer::new(0), Layer::new(3)),
1027            ),
1028        ]
1029        .into_iter()
1030        .collect();
1031        check_eval_order_for_spec(spec, event_layers)
1032    }
1033}
1034
1035#[cfg(test)]
1036#[cfg(feature = "shift_layer")]
1037mod tests {
1038    use std::collections::HashMap;
1039
1040    use crate::hir::SRef;
1041
1042    use super::{check_eval_order_for_spec, Layer, StreamLayers};
1043
1044    #[test]
1045    fn synchronous_lookup() {
1046        let spec = "input a: UInt8\noutput b: UInt8 := a\noutput c:UInt8 := b";
1047        let sname_to_sref = vec![("a", SRef::In(0)), ("b", SRef::Out(0)), ("c", SRef::Out(1))]
1048            .into_iter()
1049            .collect::<HashMap<&str, SRef>>();
1050        let event_layers = vec![
1051            (
1052                sname_to_sref["a"],
1053                StreamLayers::new(Layer::new(0), Layer::new(0), Layer::new(1)),
1054            ),
1055            (
1056                sname_to_sref["b"],
1057                StreamLayers::new(Layer::new(0), Layer::new(1), Layer::new(2)),
1058            ),
1059            (
1060                sname_to_sref["c"],
1061                StreamLayers::new(Layer::new(0), Layer::new(1), Layer::new(3)),
1062            ),
1063        ]
1064        .into_iter()
1065        .collect();
1066        check_eval_order_for_spec(spec, event_layers)
1067    }
1068
1069    #[test]
1070    fn filtered_spec() {
1071        let spec = "input a: UInt8
1072        output b := a + 1
1073        output c eval when b == 0 with a
1074        output d eval when a == 0 with c.hold(or: 0)";
1075        let sname_to_sref = vec![
1076            ("a", SRef::In(0)),
1077            ("b", SRef::Out(0)),
1078            ("c", SRef::Out(1)),
1079            ("d", SRef::Out(2)),
1080        ]
1081        .into_iter()
1082        .collect::<HashMap<&str, SRef>>();
1083        let event_layers = vec![
1084            (
1085                sname_to_sref["a"],
1086                StreamLayers::new(Layer::new(0), Layer::new(0), Layer::new(1)),
1087            ),
1088            (
1089                sname_to_sref["b"],
1090                StreamLayers::new(Layer::new(0), Layer::new(1), Layer::new(2)),
1091            ),
1092            (
1093                sname_to_sref["c"],
1094                StreamLayers::new(Layer::new(0), Layer::new(3), Layer::new(4)),
1095            ),
1096            (
1097                sname_to_sref["d"],
1098                StreamLayers::new(Layer::new(0), Layer::new(2), Layer::new(5)),
1099            ),
1100        ]
1101        .into_iter()
1102        .collect();
1103        check_eval_order_for_spec(spec, event_layers)
1104    }
1105
1106    #[test]
1107    fn filtered_spec_offset() {
1108        let spec = "input a: UInt8
1109        output b := a + 1
1110        output c eval when b == 0 with a
1111        output d eval when b == 0 with c.last(or: 0)";
1112        let sname_to_sref = vec![
1113            ("a", SRef::In(0)),
1114            ("b", SRef::Out(0)),
1115            ("c", SRef::Out(1)),
1116            ("d", SRef::Out(2)),
1117        ]
1118        .into_iter()
1119        .collect::<HashMap<&str, SRef>>();
1120        let event_layers = vec![
1121            (
1122                sname_to_sref["a"],
1123                StreamLayers::new(Layer::new(0), Layer::new(0), Layer::new(1)),
1124            ),
1125            (
1126                sname_to_sref["b"],
1127                StreamLayers::new(Layer::new(0), Layer::new(1), Layer::new(2)),
1128            ),
1129            (
1130                sname_to_sref["c"],
1131                StreamLayers::new(Layer::new(0), Layer::new(3), Layer::new(4)),
1132            ),
1133            (
1134                sname_to_sref["d"],
1135                StreamLayers::new(Layer::new(0), Layer::new(3), Layer::new(4)),
1136            ),
1137        ]
1138        .into_iter()
1139        .collect();
1140        check_eval_order_for_spec(spec, event_layers)
1141    }
1142}