Skip to main content

ebi_objects/ebi_objects/
stochastic_process_tree.rs

1use super::process_tree::{Node, Operator};
2use crate::{
3    Activity, ActivityKey, EbiObject, Exportable, Importable,
4    ebi_objects::{
5        labelled_petri_net::TransitionIndex,
6        process_tree::{NodeState, TreeMarking},
7    },
8    line_reader::LineReader,
9    traits::{
10        graphable,
11        importable::{ImporterParameter, ImporterParameterValues, from_string},
12    },
13    tree_semantics,
14};
15#[cfg(any(test, feature = "testactivities"))]
16use ebi_activity_key::TestActivityKey;
17use ebi_arithmetic::anyhow::{Context, Error, Result, anyhow};
18use ebi_arithmetic::{Fraction, Signed, Zero};
19use ebi_derive::ActivityKey;
20use layout::{adt::dag::NodeHandle, topo::layout::VisualGraph};
21use std::fmt::Display;
22
23pub const HEADER: &str = "stochastic process tree";
24
25#[derive(Debug, ActivityKey, Clone)]
26pub struct StochasticProcessTree {
27    pub activity_key: ActivityKey,
28    pub tree: Vec<Node>,
29    pub transition2node: Vec<usize>,
30    pub weights: Vec<Fraction>, //weights must be strictly positive; no deadlocks or livelocks in trees. Index are transitions, not nodes.
31    pub termination_weight: Fraction,
32}
33
34impl StochasticProcessTree {
35    pub fn number_of_leaves(&self) -> usize {
36        self.tree.iter().filter(|node| node.is_leaf()).count() + 1
37    }
38
39    pub fn node_to_string(
40        &self,
41        indent: usize,
42        node: usize,
43        f: &mut std::fmt::Formatter<'_>,
44    ) -> Result<usize> {
45        let id = "\t".repeat(indent);
46        match &self.tree[node] {
47            Node::Tau => {
48                writeln!(
49                    f,
50                    "{}tau\n{}# weight node {}\n{}{}",
51                    id,
52                    id,
53                    node,
54                    id,
55                    self.weights[self.node_to_transition(node).unwrap()]
56                )?;
57                Ok(node + 1)
58            }
59            Node::Activity(activity) => {
60                writeln!(
61                    f,
62                    "{}activity {}\n{}# weight node {}\n{}{}",
63                    id,
64                    self.activity_key.get_activity_label(&activity),
65                    id,
66                    node,
67                    id,
68                    self.weights[self.node_to_transition(node).unwrap()]
69                )?;
70                Ok(node + 1)
71            }
72            Node::Operator(operator, number_of_children) => {
73                writeln!(f, "{}{}", id, operator.to_string())?;
74                writeln!(
75                    f,
76                    "{}# number of children\n{}{}",
77                    id, id, number_of_children
78                )?;
79                let mut child = node + 1;
80                for _ in 0..*number_of_children {
81                    child = self.node_to_string(indent + 1, child, f)?;
82                }
83                Ok(child)
84            }
85        }
86    }
87
88    ///read one node, recursively
89    fn string_to_tree(
90        lreader: &mut LineReader<'_>,
91        tree: &mut Vec<Node>,
92        weights: &mut Vec<Fraction>,
93        activity_key: &mut ActivityKey,
94        root: bool,
95    ) -> Result<()> {
96        let node_type_line = match lreader.next_line_string().with_context(|| {
97            format!(
98                "Failed to read node {} at line {}",
99                tree.len(),
100                lreader.get_last_line_number()
101            )
102        }) {
103            Ok(x) => x,
104            Err(e) => {
105                if root {
106                    //The root may be missing: then, we have an empty tree.
107                    return Ok(());
108                } else {
109                    return Err(e);
110                }
111            }
112        };
113
114        if node_type_line.trim_start().starts_with("tau") {
115            let weight = lreader.next_line_weight().with_context(|| {
116                format!(
117                    "failed to read weight of node {} at line {}",
118                    tree.len(),
119                    lreader.get_last_line_number()
120                )
121            })?;
122            if !weight.is_positive() {
123                return Err(anyhow!(
124                    "weight of node {} at line {} is not positive",
125                    tree.len(),
126                    lreader.get_last_line_number()
127                ));
128            }
129            weights.push(weight);
130            tree.push(Node::Tau);
131        } else if node_type_line.trim_start().starts_with("activity ") {
132            let label = node_type_line.trim_start()[9..].to_string();
133            let activity = activity_key.process_activity(&label);
134
135            let weight = lreader.next_line_weight().with_context(|| {
136                format!(
137                    "failed to read weight of node {} at line {}",
138                    tree.len(),
139                    lreader.get_last_line_number()
140                )
141            })?;
142            if !weight.is_positive() {
143                return Err(anyhow!(
144                    "weight of node {} at line {} is not positive",
145                    tree.len(),
146                    lreader.get_last_line_number()
147                ));
148            }
149            weights.push(weight);
150
151            tree.push(Node::Activity(activity));
152        } else if let Ok(operator) = node_type_line.trim_start().trim_end().parse::<Operator>() {
153            let number_of_children = lreader.next_line_index().with_context(|| {
154                format!(
155                    "failed to read number of children for node {} at line {}",
156                    tree.len(),
157                    lreader.get_last_line_number()
158                )
159            })?;
160            if number_of_children < 1 {
161                return Err(anyhow!(
162                    "loop node ending at node {} at line {} has no children",
163                    tree.len(),
164                    lreader.get_last_line_number()
165                ));
166            }
167            tree.push(Node::Operator(operator, number_of_children));
168            for _ in 0..number_of_children {
169                Self::string_to_tree(lreader, tree, weights, activity_key, false)?;
170            }
171        } else if root && node_type_line.trim_start().is_empty() {
172            //empty tree
173            return Ok(());
174        } else {
175            return Err(anyhow!(
176                "could not parse type of node {} at line {}; Expected `tau`, `activity`, `concurrent`, `interleaved`, `or`, `sequence` or `xor`",
177                tree.len(),
178                lreader.get_last_line_number()
179            ));
180        }
181
182        Ok(())
183    }
184
185    pub fn node_to_dot(
186        &self,
187        graph: &mut VisualGraph,
188        node: usize,
189        entry: &NodeHandle,
190        exit: &NodeHandle,
191    ) -> usize {
192        match self.tree[node] {
193            Node::Tau => {
194                graphable::create_edge(graph, entry, exit, "");
195                node + 1
196            }
197            Node::Activity(activity) => {
198                let transition = graphable::create_transition(
199                    graph,
200                    self.activity_key.get_activity_label(&activity),
201                    "",
202                );
203                graphable::create_edge(graph, entry, &transition, "");
204                graphable::create_edge(graph, &transition, exit, "");
205                node + 1
206            }
207            Node::Operator(Operator::Xor, number_of_children) => {
208                let mut child = node + 1;
209                for _ in 0..number_of_children {
210                    child = StochasticProcessTree::node_to_dot(&self, graph, child, entry, exit);
211                }
212                child
213            }
214            Node::Operator(Operator::Sequence, number_of_children) => {
215                let intermediate_nodes = (0..(number_of_children - 1))
216                    .map(|_| graphable::create_dot(graph))
217                    .collect::<Vec<_>>();
218
219                let mut child = node + 1;
220                for i in 0..number_of_children {
221                    let child_entry = if i == 0 {
222                        entry
223                    } else {
224                        &intermediate_nodes[i - 1]
225                    };
226                    let child_exit = if i == number_of_children - 1 {
227                        exit
228                    } else {
229                        &intermediate_nodes[i]
230                    };
231
232                    child = StochasticProcessTree::node_to_dot(
233                        &self,
234                        graph,
235                        child,
236                        child_entry,
237                        child_exit,
238                    );
239                }
240                child
241            }
242            Node::Operator(Operator::Concurrent, number_of_children) => {
243                let split = graphable::create_gateway(graph, "+");
244                graphable::create_edge(graph, entry, &split, "");
245                let join = graphable::create_gateway(graph, "+");
246                graphable::create_edge(graph, &join, exit, "");
247
248                let mut child = node + 1;
249                for _ in 0..number_of_children {
250                    child = StochasticProcessTree::node_to_dot(&self, graph, child, &split, &join);
251                }
252                child
253            }
254            Node::Operator(Operator::Or, number_of_children) => {
255                let split = graphable::create_gateway(graph, "o");
256                graphable::create_edge(graph, entry, &split, "");
257                let join = graphable::create_gateway(graph, "o");
258                graphable::create_edge(graph, &join, exit, "");
259
260                let mut child = node + 1;
261                for _ in 0..number_of_children {
262                    child = StochasticProcessTree::node_to_dot(&self, graph, child, &split, &join);
263                }
264                child
265            }
266            Node::Operator(Operator::Interleaved, number_of_children) => {
267                let split = graphable::create_gateway(graph, "↔");
268                graphable::create_edge(graph, entry, &split, "");
269                let join = graphable::create_gateway(graph, "↔");
270                graphable::create_edge(graph, &join, exit, "");
271
272                let mut child = node + 1;
273                for _ in 0..number_of_children {
274                    child = StochasticProcessTree::node_to_dot(&self, graph, child, &split, &join);
275                }
276                child
277            }
278            Node::Operator(Operator::Loop, number_of_children) => {
279                let split = graphable::create_dot(graph);
280                graphable::create_edge(graph, entry, &split, "");
281                let join = graphable::create_dot(graph);
282                graphable::create_edge(graph, &join, exit, "");
283
284                let mut child = node + 1;
285
286                child = StochasticProcessTree::node_to_dot(&self, graph, child, &split, &join);
287
288                if number_of_children == 1 {
289                    graphable::create_edge(graph, &join, &split, "");
290                } else {
291                    for _ in 1..number_of_children {
292                        child =
293                            StochasticProcessTree::node_to_dot(&self, graph, child, &join, &split);
294                    }
295                }
296                child
297            }
298        }
299    }
300}
301
302impl Display for StochasticProcessTree {
303    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304        writeln!(f, "{}", HEADER)?;
305        if !self.tree.is_empty() {
306            let _ = self.node_to_string(0, 0, f);
307        };
308        writeln!(f, "# termination weight\n{}", self.termination_weight)
309    }
310}
311
312impl Importable for StochasticProcessTree {
313    const FILE_FORMAT_SPECIFICATION_LATEX: &str = "A stochastic process tree is a line-based structure. Lines starting with a \\# are ignored.
314    This first line is exactly `stochastic process tree'.
315    The subsequent lines contain the nodes:
316    Each node is either:
317    \\begin{itemize}
318        \\item A line with the word `activity' followed on the same line by a space and the label of the activity leaf. The next line contains the weight of the activity;
319        \\item The word `tau', followed on the next line by the weight of the leaf;
320        \\item The name of an operator (`sequence', `xor', `concurrent', `loop', `interleaved', or `or') on its own line.
321        The line thereafter contains the number of children of the node, after which the nodes are given.
322        An operator node must have at least one child.
323    \\end{itemize}
324    Indentation of nodes is allowed, but not mandatory.\\
325    The last line of the file contains the weight of termination.
326    
327    For instance:
328    \\lstinputlisting[language=ebilines, style=boxed]{../testfiles/all_operators.sptree}";
329
330    const IMPORTER_PARAMETERS: &[ImporterParameter] = &[];
331
332    fn import_as_object(
333        reader: &mut dyn std::io::BufRead,
334        parameter_values: &ImporterParameterValues,
335    ) -> Result<EbiObject> {
336        Ok(EbiObject::StochasticProcessTree(Self::import(
337            reader,
338            parameter_values,
339        )?))
340    }
341
342    fn import(reader: &mut dyn std::io::BufRead, _: &ImporterParameterValues) -> Result<Self>
343    where
344        Self: Sized,
345    {
346        let mut lreader = LineReader::new(reader);
347
348        let head = lreader
349            .next_line_string()
350            .with_context(|| format!("failed to read header, which should be {}", HEADER))?;
351        if head != HEADER {
352            return Err(anyhow!(
353                "first line should be exactly `{}`, but found `{}` on line `{}`",
354                HEADER,
355                lreader.get_last_line(),
356                lreader.get_last_line_number()
357            ));
358        }
359
360        let mut activity_key = ActivityKey::new();
361        let mut tree = vec![];
362        let mut weights = vec![];
363        Self::string_to_tree(
364            &mut lreader,
365            &mut tree,
366            &mut weights,
367            &mut activity_key,
368            true,
369        )?;
370
371        let termination_weight = lreader
372            .next_line_weight()
373            .with_context(|| format!("could not read termination weight at end of file"))?;
374        if !termination_weight.is_positive() {
375            return Err(anyhow!(
376                "termination weight ({}) is not positive",
377                termination_weight
378            ));
379        }
380
381        Ok(StochasticProcessTree::from((
382            activity_key,
383            tree,
384            weights,
385            termination_weight,
386        )))
387    }
388}
389from_string!(StochasticProcessTree);
390
391impl Exportable for StochasticProcessTree {
392    fn export_from_object(object: EbiObject, f: &mut dyn std::io::Write) -> Result<()> {
393        match object {
394            EbiObject::StochasticProcessTree(lpn) => lpn.export(f),
395            _ => Err(anyhow!(
396                "cannot export {} {} as a stochastic process tree",
397                object.get_type().get_article(),
398                object.get_type()
399            )),
400        }
401    }
402
403    fn export(&self, f: &mut dyn std::io::Write) -> Result<()> {
404        Ok(write!(f, "{}", self)?)
405    }
406}
407
408tree_semantics!(StochasticProcessTree);
409
410pub fn get_transition_weight<'a>(
411    tree: &'a StochasticProcessTree,
412    _state: &TreeMarking,
413    transition: TransitionIndex,
414) -> &'a Fraction {
415    if transition < tree.transition2node.len() {
416        &tree.weights[transition]
417    } else {
418        &tree.termination_weight
419    }
420}
421
422pub fn get_total_weight_of_enabled_transitions(
423    tree: &StochasticProcessTree,
424    state: &TreeMarking,
425) -> Fraction {
426    let mut sum = if !state.terminated && can_terminate(tree, state, tree.root()) {
427        tree.termination_weight.clone()
428    } else {
429        Fraction::zero()
430    };
431
432    for (transition, node) in tree.transition2node.iter().enumerate() {
433        if can_execute(tree, state, *node) {
434            sum += &tree.weights[transition];
435        }
436    }
437
438    sum
439}
440
441#[cfg(any(test, feature = "testactivities"))]
442impl TestActivityKey for StochasticProcessTree {
443    fn test_activity_key(&self) {
444        self.tree.iter().for_each(|node| {
445            if let Node::Activity(a) = node {
446                use crate::HasActivityKey;
447
448                self.activity_key().assert_activity_is_of_key(a);
449            }
450        });
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use crate::{
457        HasActivityKey, StochasticProcessTree,
458        ebi_objects::stochastic_process_tree::{
459            can_execute, can_terminate, execute_transition, get_enabled_transitions,
460            get_initial_state, get_total_weight_of_enabled_transitions, get_transition_activity,
461        },
462    };
463    use ebi_arithmetic::Fraction;
464    use std::fs;
465
466    #[test]
467    fn sptree_semantics_loop() {
468        let fin1 =
469            fs::read_to_string("testfiles/seq(a,xor(seq(f,and(c,b)),seq(f,loop(d,e))).sptree")
470                .unwrap();
471        let tree = fin1.parse::<StochasticProcessTree>().unwrap();
472
473        let ta = 0;
474        let tf1 = 1;
475        let tf2 = 4;
476        let td = 5;
477        let te = 6;
478        let ttau = 7;
479        let tfin = 8;
480
481        assert_eq!(
482            tree.activity_key()
483                .deprocess_activity(&get_transition_activity(&tree, td).unwrap()),
484            "d"
485        );
486        assert_eq!(
487            tree.activity_key()
488                .deprocess_activity(&get_transition_activity(&tree, te).unwrap()),
489            "e"
490        );
491        assert!(get_transition_activity(&tree, ttau).is_none());
492
493        let mut state = get_initial_state(&tree).unwrap();
494        println!("{}\n", state);
495        assert_eq!(get_enabled_transitions(&tree, &state), [ta]);
496        assert_eq!(
497            get_total_weight_of_enabled_transitions(&tree, &state),
498            Fraction::from(1)
499        );
500
501        println!("execute a {}", ta);
502        execute_transition(&tree, &mut state, ta).unwrap();
503        println!("{}\n", state);
504        assert_eq!(get_enabled_transitions(&tree, &state), [tf1, tf2]);
505        assert_eq!(
506            get_total_weight_of_enabled_transitions(&tree, &state),
507            Fraction::from(4)
508        );
509
510        println!("execute f2 {}", tf2);
511        execute_transition(&tree, &mut state, tf2).unwrap();
512        println!("{}\n", state);
513        assert_eq!(get_enabled_transitions(&tree, &state), [td]);
514
515        println!("execute d {}", td);
516        execute_transition(&tree, &mut state, td).unwrap();
517        println!("{}\n", state);
518        assert_eq!(get_enabled_transitions(&tree, &state), [te, ttau]);
519        assert_eq!(
520            get_total_weight_of_enabled_transitions(&tree, &state),
521            Fraction::from(2)
522        );
523
524        println!("execute e {}", te);
525        execute_transition(&tree, &mut state, te).unwrap();
526        println!("{}\n", state);
527        assert_eq!(get_enabled_transitions(&tree, &state), [td]);
528
529        println!("execute d {}", td);
530        execute_transition(&tree, &mut state, td).unwrap();
531        println!("{}\n", state);
532        assert_eq!(get_enabled_transitions(&tree, &state), [te, ttau]);
533        assert_eq!(
534            get_total_weight_of_enabled_transitions(&tree, &state),
535            Fraction::from(2)
536        );
537
538        println!("execute tau {}", ttau);
539        execute_transition(&tree, &mut state, ttau).unwrap();
540        println!("{}\n", state);
541        assert_eq!(get_enabled_transitions(&tree, &state), [tfin]);
542        assert_eq!(
543            get_total_weight_of_enabled_transitions(&tree, &state),
544            Fraction::from(1)
545        );
546
547        println!("terminate {}", tfin);
548        execute_transition(&tree, &mut state, tfin).unwrap();
549        println!("{}\n", state);
550        assert_eq!(get_enabled_transitions(&tree, &state).len(), 0);
551        assert_eq!(
552            get_total_weight_of_enabled_transitions(&tree, &state),
553            Fraction::from(0)
554        );
555    }
556
557    #[test]
558    fn sptree_semantics_concurrent_loop_termination() {
559        let fin1 = fs::read_to_string("testfiles/seq(a,and(b,loop(c,d))).sptree").unwrap();
560        let tree = fin1.parse::<StochasticProcessTree>().unwrap();
561
562        let ta = 0;
563        let tb = 1;
564        let tc = 2;
565        let td = 3;
566        let tfin = 4;
567
568        assert_eq!(
569            tree.activity_key()
570                .deprocess_activity(&get_transition_activity(&tree, ta).unwrap()),
571            "a"
572        );
573        assert_eq!(
574            tree.activity_key()
575                .deprocess_activity(&get_transition_activity(&tree, tb).unwrap()),
576            "b"
577        );
578        assert_eq!(
579            tree.activity_key()
580                .deprocess_activity(&get_transition_activity(&tree, tc).unwrap()),
581            "c"
582        );
583        assert_eq!(
584            tree.activity_key()
585                .deprocess_activity(&get_transition_activity(&tree, td).unwrap()),
586            "d"
587        );
588        assert!(get_transition_activity(&tree, tfin).is_none());
589
590        let mut state = get_initial_state(&tree).unwrap();
591        println!("{}\n", state);
592        assert_eq!(get_enabled_transitions(&tree, &state), [ta]);
593        assert_eq!(
594            get_total_weight_of_enabled_transitions(&tree, &state),
595            Fraction::from(1)
596        );
597
598        println!("execute a {}", ta);
599        execute_transition(&tree, &mut state, ta).unwrap();
600        println!("{}\n", state);
601        assert_eq!(get_enabled_transitions(&tree, &state), [tb, tc]);
602        assert_eq!(
603            get_total_weight_of_enabled_transitions(&tree, &state),
604            Fraction::from(5)
605        );
606
607        println!("execute c {}", tc);
608        execute_transition(&tree, &mut state, tc).unwrap();
609        println!("{}\n", state);
610        assert_eq!(get_enabled_transitions(&tree, &state), [tb, td]);
611        assert_eq!(
612            get_total_weight_of_enabled_transitions(&tree, &state),
613            Fraction::from(6)
614        );
615
616        println!("execute b {}", tb);
617        execute_transition(&tree, &mut state, tb).unwrap();
618        println!("{}\n", state);
619        assert_eq!(get_enabled_transitions(&tree, &state), [td, tfin]);
620        assert_eq!(
621            get_total_weight_of_enabled_transitions(&tree, &state),
622            Fraction::from(9)
623        );
624
625        println!("execute d {}", td);
626        execute_transition(&tree, &mut state, td).unwrap();
627        println!("{}\n", state);
628        assert_eq!(get_enabled_transitions(&tree, &state), [tc]);
629        assert_eq!(
630            get_total_weight_of_enabled_transitions(&tree, &state),
631            Fraction::from(3)
632        );
633
634        println!("execute c {}", tc);
635        execute_transition(&tree, &mut state, tc).unwrap();
636        println!("{}\n", state);
637        assert_eq!(get_enabled_transitions(&tree, &state), [td, tfin]);
638        assert_eq!(
639            get_total_weight_of_enabled_transitions(&tree, &state),
640            Fraction::from(9)
641        );
642
643        println!("terminate {}", tfin);
644        execute_transition(&tree, &mut state, tfin).unwrap();
645        println!("{}\n", state);
646        assert_eq!(get_enabled_transitions(&tree, &state).len(), 0);
647        assert_eq!(
648            get_total_weight_of_enabled_transitions(&tree, &state),
649            Fraction::from(0)
650        );
651    }
652
653    #[test]
654    fn sptree_semantics_loop_loop_termination() {
655        let fin1 = fs::read_to_string("testfiles/loop(a,loop(b,c)).sptree").unwrap();
656        let tree = fin1.parse::<StochasticProcessTree>().unwrap();
657
658        let ta = 0;
659        let tb = 1;
660        let tc = 2;
661        let tfin = 3;
662
663        assert_eq!(
664            tree.activity_key()
665                .deprocess_activity(&get_transition_activity(&tree, ta).unwrap()),
666            "a"
667        );
668        assert_eq!(
669            tree.activity_key()
670                .deprocess_activity(&get_transition_activity(&tree, tb).unwrap()),
671            "b"
672        );
673        assert_eq!(
674            tree.activity_key()
675                .deprocess_activity(&get_transition_activity(&tree, tc).unwrap()),
676            "c"
677        );
678        assert!(get_transition_activity(&tree, tfin).is_none());
679
680        let mut state = get_initial_state(&tree).unwrap();
681        println!("{}\n", state);
682        assert_eq!(get_enabled_transitions(&tree, &state), [ta]);
683        assert_eq!(
684            get_total_weight_of_enabled_transitions(&tree, &state),
685            Fraction::from(1)
686        );
687
688        println!("execute a {}", ta);
689        execute_transition(&tree, &mut state, ta).unwrap();
690        println!("{}\n", state);
691        assert_eq!(get_enabled_transitions(&tree, &state), [tb, tfin]);
692        assert_eq!(
693            get_total_weight_of_enabled_transitions(&tree, &state),
694            Fraction::from(6)
695        );
696
697        println!("execute b {}", tb);
698        execute_transition(&tree, &mut state, tb).unwrap();
699        println!("{}\n", state);
700        assert_eq!(get_enabled_transitions(&tree, &state), [ta, tc]);
701        assert_eq!(
702            get_total_weight_of_enabled_transitions(&tree, &state),
703            Fraction::from(4)
704        );
705
706        println!("execute a {}", ta);
707        execute_transition(&tree, &mut state, ta).unwrap();
708        println!("{}\n", state);
709        assert_eq!(get_enabled_transitions(&tree, &state), [tb, tfin]);
710        assert_eq!(
711            get_total_weight_of_enabled_transitions(&tree, &state),
712            Fraction::from(6)
713        );
714
715        println!("execute b {}", tb);
716        execute_transition(&tree, &mut state, tb).unwrap();
717        println!("{}\n", state);
718        assert_eq!(get_enabled_transitions(&tree, &state), [ta, tc]);
719        assert_eq!(
720            get_total_weight_of_enabled_transitions(&tree, &state),
721            Fraction::from(4)
722        );
723
724        println!("execute c {}", tc);
725        execute_transition(&tree, &mut state, tc).unwrap();
726        println!("{}\n", state);
727        assert_eq!(get_enabled_transitions(&tree, &state), [tb]);
728        assert_eq!(
729            get_total_weight_of_enabled_transitions(&tree, &state),
730            Fraction::from(2)
731        );
732
733        println!("execute b {}", tb);
734        execute_transition(&tree, &mut state, tb).unwrap();
735        println!("{}\n", state);
736        assert_eq!(get_enabled_transitions(&tree, &state), [ta, tc]);
737        assert_eq!(
738            get_total_weight_of_enabled_transitions(&tree, &state),
739            Fraction::from(4)
740        );
741
742        println!("execute a {}", ta);
743        execute_transition(&tree, &mut state, ta).unwrap();
744        println!("{}\n", state);
745        assert_eq!(get_enabled_transitions(&tree, &state), [tb, tfin]);
746        assert_eq!(
747            get_total_weight_of_enabled_transitions(&tree, &state),
748            Fraction::from(6)
749        );
750
751        println!("terminate {}", tfin);
752        execute_transition(&tree, &mut state, tfin).unwrap();
753        println!("{}\n", state);
754        assert_eq!(get_enabled_transitions(&tree, &state).len(), 0);
755        assert_eq!(
756            get_total_weight_of_enabled_transitions(&tree, &state),
757            Fraction::from(0)
758        );
759    }
760
761    #[test]
762    fn sptree_semantics_loop_right_loop_termination() {
763        let fin1 = fs::read_to_string("testfiles/loop(loop(a,b),c).sptree").unwrap();
764        let tree = fin1.parse::<StochasticProcessTree>().unwrap();
765
766        let ta = 0;
767        let tb = 1;
768        let tc = 2;
769        let tfin = 3;
770
771        assert_eq!(
772            tree.activity_key()
773                .deprocess_activity(&get_transition_activity(&tree, ta).unwrap()),
774            "a"
775        );
776        assert_eq!(
777            tree.activity_key()
778                .deprocess_activity(&get_transition_activity(&tree, tb).unwrap()),
779            "b"
780        );
781        assert_eq!(
782            tree.activity_key()
783                .deprocess_activity(&get_transition_activity(&tree, tc).unwrap()),
784            "c"
785        );
786        assert!(get_transition_activity(&tree, tfin).is_none());
787
788        let mut state = get_initial_state(&tree).unwrap();
789        println!("{}\n", state);
790        assert_eq!(get_enabled_transitions(&tree, &state), [ta]);
791        assert_eq!(
792            get_total_weight_of_enabled_transitions(&tree, &state),
793            Fraction::from(1)
794        );
795
796        println!("execute a {}", ta);
797        execute_transition(&tree, &mut state, ta).unwrap();
798        println!("{}\n", state);
799        assert_eq!(get_enabled_transitions(&tree, &state), [tb, tc, tfin]);
800        assert_eq!(
801            get_total_weight_of_enabled_transitions(&tree, &state),
802            Fraction::from(9)
803        );
804
805        println!("execute c {}", tc);
806        execute_transition(&tree, &mut state, tc).unwrap();
807        println!("{}\n", state);
808        assert_eq!(get_enabled_transitions(&tree, &state), [ta]);
809        assert_eq!(
810            get_total_weight_of_enabled_transitions(&tree, &state),
811            Fraction::from(1)
812        );
813
814        println!("execute a {}", ta);
815        execute_transition(&tree, &mut state, ta).unwrap();
816        println!("{}\n", state);
817        assert_eq!(get_enabled_transitions(&tree, &state), [tb, tc, tfin]);
818        assert_eq!(
819            get_total_weight_of_enabled_transitions(&tree, &state),
820            Fraction::from(9)
821        );
822
823        println!("execute b {}", tb);
824        execute_transition(&tree, &mut state, tb).unwrap();
825        println!("{}\n", state);
826        assert_eq!(get_enabled_transitions(&tree, &state), [ta]);
827        assert_eq!(
828            get_total_weight_of_enabled_transitions(&tree, &state),
829            Fraction::from(1)
830        );
831
832        println!("execute a {}", ta);
833        execute_transition(&tree, &mut state, ta).unwrap();
834        println!("{}\n", state);
835        assert_eq!(get_enabled_transitions(&tree, &state), [tb, tc, tfin]);
836        assert_eq!(
837            get_total_weight_of_enabled_transitions(&tree, &state),
838            Fraction::from(9)
839        );
840
841        println!("terminate {}", tfin);
842        execute_transition(&tree, &mut state, tfin).unwrap();
843        println!("{}\n", state);
844        assert_eq!(get_enabled_transitions(&tree, &state).len(), 0);
845        assert_eq!(
846            get_total_weight_of_enabled_transitions(&tree, &state),
847            Fraction::from(0)
848        );
849    }
850
851    macro_rules! assert_execute_expect {
852        ($tree:ident, $state:ident, $t:ident, $e:expr) => {
853            println!("execute {} {}", ::std::stringify!($t), $t);
854            assert!(can_execute(&$tree, &$state, $tree.transition2node[$t]));
855            execute_transition(&$tree, &mut $state, $t).unwrap();
856            println!("{}\n", $state);
857            assert_eq!(get_enabled_transitions(&$tree, &$state), $e);
858        };
859    }
860
861    macro_rules! assert_terminate {
862        ($tree:ident, $state:ident, $tfin:ident) => {
863            println!("terminate {}", $tfin);
864            assert!(can_terminate(&$tree, &$state, $tree.root()));
865            execute_transition(&$tree, &mut $state, $tfin).unwrap();
866            println!("{}\n", $state);
867            assert_eq!(get_enabled_transitions(&$tree, &$state).len(), 0);
868            assert_eq!(
869                get_total_weight_of_enabled_transitions(&$tree, &$state),
870                Fraction::from(0)
871            );
872        };
873    }
874
875    #[test]
876    fn sptree_semantics_xor_loop_termination() {
877        let fin1 = fs::read_to_string("testfiles/loop(xor(a,loop(b,c),d).sptree").unwrap();
878        let tree = fin1.parse::<StochasticProcessTree>().unwrap();
879
880        let ta = 0;
881        let tb = 1;
882        let tc = 2;
883        let td = 3;
884        let tfin = 4;
885
886        assert_eq!(
887            tree.activity_key()
888                .deprocess_activity(&get_transition_activity(&tree, ta).unwrap()),
889            "a"
890        );
891        assert_eq!(
892            tree.activity_key()
893                .deprocess_activity(&get_transition_activity(&tree, tb).unwrap()),
894            "b"
895        );
896        assert_eq!(
897            tree.activity_key()
898                .deprocess_activity(&get_transition_activity(&tree, tc).unwrap()),
899            "c"
900        );
901        assert_eq!(
902            tree.activity_key()
903                .deprocess_activity(&get_transition_activity(&tree, td).unwrap()),
904            "d"
905        );
906        assert!(get_transition_activity(&tree, tfin).is_none());
907
908        let mut state = get_initial_state(&tree).unwrap();
909        println!("{}\n", state);
910        assert_eq!(get_enabled_transitions(&tree, &state), [ta, tb]);
911
912        assert_execute_expect!(tree, state, ta, [td, tfin]);
913
914        assert_execute_expect!(tree, state, td, [ta, tb]);
915
916        assert_execute_expect!(tree, state, tb, [tc, td, tfin]);
917
918        assert_execute_expect!(tree, state, td, [ta, tb]);
919
920        assert_execute_expect!(tree, state, ta, [td, tfin]);
921
922        assert_execute_expect!(tree, state, td, [ta, tb]); //reset
923
924        assert_execute_expect!(tree, state, tb, [tc, td, tfin]);
925
926        assert_execute_expect!(tree, state, tc, [tb]);
927
928        assert_execute_expect!(tree, state, tb, [tc, td, tfin]);
929
930        assert_terminate!(tree, state, tfin);
931    }
932
933    #[test]
934    fn sptree_semantics_or_loop_termination() {
935        let fin1 = fs::read_to_string("testfiles/loop(or(a,loop(b,c),d).sptree").unwrap();
936        let tree = fin1.parse::<StochasticProcessTree>().unwrap();
937
938        let ta = 0;
939        let tb = 1;
940        let tc = 2;
941        let td = 3;
942        let tfin = 4;
943
944        assert_eq!(
945            tree.activity_key()
946                .deprocess_activity(&get_transition_activity(&tree, ta).unwrap()),
947            "a"
948        );
949        assert_eq!(
950            tree.activity_key()
951                .deprocess_activity(&get_transition_activity(&tree, tb).unwrap()),
952            "b"
953        );
954        assert_eq!(
955            tree.activity_key()
956                .deprocess_activity(&get_transition_activity(&tree, tc).unwrap()),
957            "c"
958        );
959        assert_eq!(
960            tree.activity_key()
961                .deprocess_activity(&get_transition_activity(&tree, td).unwrap()),
962            "d"
963        );
964        assert!(get_transition_activity(&tree, tfin).is_none());
965
966        let mut state = get_initial_state(&tree).unwrap();
967        println!("{}\n", state);
968        assert_eq!(get_enabled_transitions(&tree, &state), [ta, tb]);
969
970        assert_execute_expect!(tree, state, ta, [tb, td, tfin]);
971
972        assert_execute_expect!(tree, state, td, [ta, tb]);
973
974        assert_execute_expect!(tree, state, ta, [tb, td, tfin]);
975
976        assert_execute_expect!(tree, state, tb, [tc, td, tfin]);
977
978        assert_execute_expect!(tree, state, tc, [tb]);
979
980        assert_execute_expect!(tree, state, tb, [tc, td, tfin]);
981
982        assert_execute_expect!(tree, state, td, [ta, tb]);
983
984        assert_execute_expect!(tree, state, tb, [ta, tc, td, tfin]);
985
986        assert_execute_expect!(tree, state, tc, [ta, tb]);
987
988        assert_execute_expect!(tree, state, tb, [ta, tc, td, tfin]);
989
990        assert_execute_expect!(tree, state, ta, [tc, td, tfin]);
991
992        assert_terminate!(tree, state, tfin);
993    }
994
995    #[test]
996    fn sptree_semantics_interleaved_loop_termination() {
997        let fin1 =
998            fs::read_to_string("testfiles/loop(interleaved(seq(a,b),loop(c,d),e).sptree").unwrap();
999        let tree = fin1.parse::<StochasticProcessTree>().unwrap();
1000
1001        let ta = 0;
1002        let tb = 1;
1003        let tc = 2;
1004        let td = 3;
1005        let te = 4;
1006        let tfin = 5;
1007
1008        assert_eq!(
1009            tree.activity_key()
1010                .deprocess_activity(&get_transition_activity(&tree, ta).unwrap()),
1011            "a"
1012        );
1013        assert_eq!(
1014            tree.activity_key()
1015                .deprocess_activity(&get_transition_activity(&tree, tb).unwrap()),
1016            "b"
1017        );
1018        assert_eq!(
1019            tree.activity_key()
1020                .deprocess_activity(&get_transition_activity(&tree, tc).unwrap()),
1021            "c"
1022        );
1023        assert_eq!(
1024            tree.activity_key()
1025                .deprocess_activity(&get_transition_activity(&tree, td).unwrap()),
1026            "d"
1027        );
1028        assert_eq!(
1029            tree.activity_key()
1030                .deprocess_activity(&get_transition_activity(&tree, te).unwrap()),
1031            "e"
1032        );
1033        assert!(get_transition_activity(&tree, tfin).is_none());
1034
1035        let mut state = get_initial_state(&tree).unwrap();
1036        println!("{}\n", state);
1037        assert_eq!(get_enabled_transitions(&tree, &state), [ta, tc]);
1038
1039        assert_execute_expect!(tree, state, ta, [tb]);
1040
1041        assert_execute_expect!(tree, state, tb, [tc]);
1042
1043        assert_execute_expect!(tree, state, tc, [td, te, tfin]);
1044
1045        assert_execute_expect!(tree, state, td, [tc]);
1046
1047        assert_execute_expect!(tree, state, tc, [td, te, tfin]);
1048
1049        assert_execute_expect!(tree, state, te, [ta, tc]); //reset
1050
1051        assert_execute_expect!(tree, state, tc, [ta, td]);
1052
1053        assert_execute_expect!(tree, state, td, [tc]);
1054
1055        assert_execute_expect!(tree, state, tc, [td, te, tfin]);
1056
1057        assert_execute_expect!(tree, state, td, [tc]);
1058
1059        assert_execute_expect!(tree, state, tc, [td, te, tfin]);
1060
1061        assert_terminate!(tree, state, tfin);
1062    }
1063}