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>, 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 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 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 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]); 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]); 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}