1use std::collections::VecDeque;
4
5use log::{debug, info};
6use taco_model_checker::internal_spec::upwards_closed_set::UpwardsClosedSet;
7
8use crate::{
9 acs_threshold_automaton::{
10 ACSThresholdAutomaton,
11 configuration::{ACSTAConfig, partially_ordered_cfg_map::PartiallyOrderedConfigMap},
12 },
13 error_graph::{NodeRef, edge::ErrorGraphEdge},
14};
15
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
18pub struct ErrorGraphNode {
19 error_level: usize,
21 config: ACSTAConfig,
23 parents: Vec<ErrorGraphEdge>,
26 self_loops: Vec<u32>,
28 back_edges: Vec<ErrorGraphEdge>,
31}
32
33impl ErrorGraphNode {
34 pub fn parents(&self) -> impl Iterator<Item = &ErrorGraphEdge> {
39 self.parents.iter()
40 }
41
42 pub fn self_loops(&self) -> impl Iterator<Item = &u32> {
44 self.self_loops.iter()
45 }
46
47 pub fn back_edges(&self) -> impl Iterator<Item = &ErrorGraphEdge> {
49 self.back_edges.iter()
50 }
51
52 pub fn config(&self) -> &ACSTAConfig {
54 &self.config
55 }
56
57 pub fn error_level(&self) -> usize {
59 self.error_level
60 }
61
62 pub fn new_roots_from_spec(
69 spec: &UpwardsClosedSet,
70 ta: &ACSThresholdAutomaton,
71 ) -> PartiallyOrderedConfigMap<NodeRef> {
72 let error_level = 0;
73
74 let configs = ACSTAConfig::from_disjunct_target(spec, ta);
75
76 let mut res: PartiallyOrderedConfigMap<NodeRef> = PartiallyOrderedConfigMap::new();
77
78 configs
79 .into_iter()
80 .map(|config| {
81 let node: NodeRef = Self {
82 error_level,
83 config: config.clone(),
84 self_loops: Vec::new(),
85 parents: Vec::new(),
86 back_edges: Vec::new(),
87 }
88 .into();
89
90 (config, node)
91 })
92 .for_each(|(config, node)| {
93 let (smaller, bigger) = res.get_leq_or_bigger(&config);
94
95 if smaller.is_some() {
96 return;
97 }
98
99 if let Some(bigger_cfg) = bigger {
100 let bigger_cfg = bigger_cfg.borrow().config().clone();
101 res.remove_entries_with_config(&bigger_cfg);
102 }
103
104 res.insert(config, node);
105 });
106
107 debug_assert!(res.len() >= 1);
108 info!("Found {} potentially reachable error configs", res.len());
109
110 res
111 }
112
113 pub fn compute_predecessors_and_insert_to_graph(
123 cfg_node_map: &mut PartiallyOrderedConfigMap<NodeRef>,
124 to_explore_queue: &mut VecDeque<NodeRef>,
125 node_to_explore: &NodeRef,
126 ta: &ACSThresholdAutomaton,
127 ) {
128 let error_level = node_to_explore.borrow().error_level + 1;
130 let config_node_to_explore = node_to_explore.borrow().config.clone();
131
132 for rule in ta.rules() {
134 let predecessors = config_node_to_explore.compute_potential_predecessors(rule, ta);
135 if predecessors.is_none() {
136 continue;
137 }
138 let predecessors = predecessors.unwrap();
139
140 for cfg in predecessors {
141 if cfg == config_node_to_explore {
142 node_to_explore.borrow_mut().self_loops.push(rule.id());
144 continue;
145 }
146
147 let new_edge = ErrorGraphEdge::new(rule, node_to_explore.clone());
149
150 let (smaller_or_eq_node, bigger_node) = cfg_node_map.get_leq_or_bigger(&cfg);
151
152 if let Some(small_or_eq_node) = smaller_or_eq_node {
155 Self::insert_existing_smaller_node(
156 small_or_eq_node,
157 node_to_explore,
158 new_edge.clone(),
159 rule.id(),
160 );
161 continue;
162 }
163
164 if let Some(bigger_node) = bigger_node {
166 let bigger_node = bigger_node.clone();
167 let bigger_node_cfg = bigger_node.borrow().config().clone();
168
169 debug_assert!(
170 bigger_node_cfg != cfg,
171 "Equal configurations should have been caught beforehand: bigger {}, cfg {}",
172 bigger_node_cfg.display_compact(ta),
173 cfg.display_compact(ta)
174 );
175
176 Self::insert_existing_bigger_node(&bigger_node, cfg.clone(), new_edge);
177
178 cfg_node_map.remove_entries_with_config(&bigger_node_cfg);
180
181 to_explore_queue.push_back(bigger_node.clone());
183 cfg_node_map.insert(cfg, bigger_node);
185 continue;
186 }
187
188 let node = Self {
191 error_level,
192 config: cfg.clone(),
193 parents: vec![new_edge],
194 self_loops: vec![],
195 back_edges: vec![],
196 };
197
198 debug!("Found new node {}", node.config.display_compact(ta));
199 let node: NodeRef = node.into();
200
201 cfg_node_map.insert(cfg, node.clone());
202 to_explore_queue.push_back(node);
203 }
204 }
205 }
206
207 fn insert_existing_smaller_node(
213 smaller_or_eq_node: &NodeRef,
214 node_to_explore: &NodeRef,
215 edge_to_insert: ErrorGraphEdge,
216 rule_id: u32,
217 ) {
218 if smaller_or_eq_node == node_to_explore {
221 smaller_or_eq_node.borrow_mut().self_loops.push(rule_id);
223 return;
224 }
225
226 let error_level = node_to_explore.borrow().error_level();
227
228 if smaller_or_eq_node.borrow().error_level >= error_level {
232 ErrorGraphEdge::insert_to_edge_collection(
235 &mut smaller_or_eq_node.borrow_mut().parents,
236 edge_to_insert,
237 );
238 return;
239 }
240
241 ErrorGraphEdge::insert_to_edge_collection(
244 &mut smaller_or_eq_node.borrow_mut().back_edges,
245 edge_to_insert,
246 );
247 }
248
249 fn insert_existing_bigger_node(
255 bigger_node: &NodeRef,
256 cfg_to_insert: ACSTAConfig,
257 edge_to_insert: ErrorGraphEdge,
258 ) {
259 if bigger_node == edge_to_insert.node() {
260 bigger_node
261 .borrow_mut()
262 .self_loops
263 .extend(edge_to_insert.get_ids_of_rules());
264
265 bigger_node.borrow_mut().config = cfg_to_insert;
266 return;
267 }
268
269 let mut bigger_node = bigger_node.borrow_mut();
270 bigger_node.config = cfg_to_insert;
272
273 ErrorGraphEdge::insert_to_edge_collection(&mut bigger_node.back_edges, edge_to_insert);
274 }
275}
276
277#[cfg(test)]
278mod mock_objects {
279 use crate::{
280 acs_threshold_automaton::configuration::ACSTAConfig,
281 error_graph::{edge::ErrorGraphEdge, node::ErrorGraphNode},
282 };
283
284 impl ErrorGraphNode {
285 pub fn new_mock(
287 error_level: usize,
288 config: ACSTAConfig,
289 parents: Vec<ErrorGraphEdge>,
290 self_loops: Vec<u32>,
291 back_edges: Vec<ErrorGraphEdge>,
292 ) -> Self {
293 Self {
294 error_level,
295 config,
296 parents,
297 self_loops,
298 back_edges,
299 }
300 }
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use std::{cell::RefCell, collections::VecDeque, rc::Rc};
307
308 use taco_display_utils::join_iterator;
309 use taco_interval_ta::builder::IntervalTABuilder;
310
311 use taco_model_checker::internal_spec::upwards_closed_set::UpwardsClosedSet;
312 use taco_smt_encoder::SMTSolverBuilder;
313 use taco_threshold_automaton::{
314 expressions::{
315 BooleanExpression, ComparisonOp, IntegerExpression, Location, Parameter, Variable,
316 },
317 general_threshold_automaton::{
318 Action,
319 builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
320 },
321 lia_threshold_automaton::LIAThresholdAutomaton,
322 };
323
324 use crate::{
325 acs_threshold_automaton::{
326 ACSInterval, ACSLocation, ACSThresholdAutomaton, CSIntervalConstraint, CSRule,
327 configuration::{ACSIntervalState, ACSLocState, ACSTAConfig},
328 },
329 error_graph::{NodeRef, edge::ErrorGraphEdge, node::ErrorGraphNode},
330 };
331
332 fn get_test_ta() -> ACSThresholdAutomaton {
333 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
334 .with_variables([Variable::new("x"), Variable::new("y")])
335 .unwrap()
336 .with_locations([
337 Location::new("l1"),
338 Location::new("l2"),
339 Location::new("l3"),
340 ])
341 .unwrap()
342 .with_parameter(Parameter::new("n"))
343 .unwrap()
344 .initialize()
345 .with_resilience_condition(BooleanExpression::ComparisonExpression(
346 Box::new(IntegerExpression::Param(Parameter::new("n"))),
347 ComparisonOp::Gt,
348 Box::new(IntegerExpression::Const(3)),
349 ))
350 .unwrap()
351 .with_initial_location_constraints([
352 BooleanExpression::ComparisonExpression(
353 Box::new(IntegerExpression::Atom(Location::new("l2"))),
354 ComparisonOp::Eq,
355 Box::new(IntegerExpression::Const(0)),
356 ),
357 BooleanExpression::ComparisonExpression(
358 Box::new(IntegerExpression::Atom(Location::new("l3"))),
359 ComparisonOp::Eq,
360 Box::new(IntegerExpression::Const(0)),
361 ),
362 ])
363 .unwrap()
364 .with_initial_variable_constraints([
365 BooleanExpression::ComparisonExpression(
366 Box::new(IntegerExpression::Atom(Variable::new("x"))),
367 ComparisonOp::Eq,
368 Box::new(IntegerExpression::Const(0)),
369 ),
370 BooleanExpression::ComparisonExpression(
371 Box::new(IntegerExpression::Atom(Variable::new("y"))),
372 ComparisonOp::Eq,
373 Box::new(IntegerExpression::Const(0)),
374 ),
375 ])
376 .unwrap()
377 .with_rule(
378 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
379 .with_action(
380 Action::new(
381 Variable::new("x"),
382 IntegerExpression::Const(1)
383 + IntegerExpression::Atom(Variable::new("x")),
384 )
385 .unwrap(),
386 )
387 .build(),
388 )
389 .unwrap()
390 .build();
391 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
392 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
393 .build()
394 .unwrap();
395 let interval_ta = interval_tas.next().unwrap();
396 assert!(interval_tas.next().is_none());
397 ACSThresholdAutomaton::from(interval_ta)
398 }
399
400 #[test]
401 fn test_getters() {
402 let edge1 = ErrorGraphEdge::new(
403 &CSRule::new_mock(
404 0,
405 ACSLocation::new_mock(1),
406 ACSLocation::new_mock(2),
407 CSIntervalConstraint::True,
408 Vec::new(),
409 ),
410 Rc::new(RefCell::new(ErrorGraphNode {
411 error_level: 0,
412 config: ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new()),
413 parents: Vec::new(),
414 self_loops: Vec::new(),
415 back_edges: Vec::new(),
416 })),
417 );
418
419 let edge2 = ErrorGraphEdge::new(
420 &CSRule::new_mock(
421 3,
422 ACSLocation::new_mock(1),
423 ACSLocation::new_mock(2),
424 CSIntervalConstraint::True,
425 Vec::new(),
426 ),
427 Rc::new(RefCell::new(ErrorGraphNode {
428 error_level: 0,
429 config: ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new()),
430 parents: Vec::new(),
431 self_loops: Vec::new(),
432 back_edges: Vec::new(),
433 })),
434 );
435
436 let node = ErrorGraphNode {
437 error_level: 42,
438 config: ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new()),
439 parents: vec![edge1.clone()],
440 self_loops: vec![1, 2, 3],
441 back_edges: vec![edge2.clone()],
442 };
443
444 assert_eq!(node.error_level(), 42);
445 assert_eq!(
446 node.config(),
447 &ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new())
448 );
449 assert_eq!(
450 node.self_loops().cloned().collect::<Vec<_>>(),
451 vec![1, 2, 3]
452 );
453 assert_eq!(node.parents().collect::<Vec<_>>(), vec![&edge1]);
454 assert_eq!(node.back_edges().collect::<Vec<_>>(), vec![&edge2]);
455 }
456
457 #[test]
458 fn test_new_roots_from_spec_single() {
459 let ta = get_test_ta();
460
461 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
462
463 let spec = UpwardsClosedSet::new_cover([Location::new("l3")]);
464
465 let created_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
466 let created_nodes = created_node_map
467 .get_all_values()
468 .map(|n| n.borrow().clone())
469 .collect::<Vec<_>>();
470
471 let mut loc_state = ACSLocState::new_empty(&ta);
472 loc_state[loc_l3] = 1;
473
474 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
475 .into_iter()
476 .map(|is| ErrorGraphNode {
477 error_level: 0,
478 config: ACSTAConfig::new_mock(loc_state.clone(), is),
479 parents: Vec::new(),
480 self_loops: Vec::new(),
481 back_edges: Vec::new(),
482 })
483 .collect::<Vec<_>>();
484
485 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
486 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
487 }
488
489 #[test]
490 fn test_new_roots_from_spec_all_cover() {
491 let ta = get_test_ta();
492
493 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
494 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
495 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
496
497 let spec = UpwardsClosedSet::new_cover([
498 Location::new("l3"),
499 Location::new("l2"),
500 Location::new("l1"),
501 ]);
502
503 let created_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
504 let created_nodes = created_node_map
505 .get_all_values()
506 .map(|n| n.borrow().clone())
507 .collect::<Vec<_>>();
508
509 let mut loc_state = ACSLocState::new_empty(&ta);
510 loc_state[loc_l3] = 1;
511 loc_state[loc_l2] = 1;
512 loc_state[loc_l1] = 1;
513
514 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
515 .into_iter()
516 .map(|is| ErrorGraphNode {
517 error_level: 0,
518 config: ACSTAConfig::new_mock(loc_state.clone(), is),
519 parents: Vec::new(),
520 self_loops: Vec::new(),
521 back_edges: Vec::new(),
522 })
523 .collect::<Vec<_>>();
524
525 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
526 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
527 }
528
529 #[test]
530 fn test_new_roots_from_spec_multi_cover() {
531 let ta = get_test_ta();
532
533 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
534 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
535 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
536
537 let spec = UpwardsClosedSet::new_cover([Location::new("l3")])
538 | UpwardsClosedSet::new_cover([Location::new("l2")])
539 | UpwardsClosedSet::new_cover([Location::new("l1")]);
540
541 let created_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
542 let created_nodes = created_node_map
543 .get_all_values()
544 .map(|n| n.borrow().clone())
545 .collect::<Vec<_>>();
546
547 let mut loc_state_3 = ACSLocState::new_empty(&ta);
548 loc_state_3[loc_l3] = 1;
549
550 let mut loc_state_2 = ACSLocState::new_empty(&ta);
551 loc_state_2[loc_l2] = 1;
552
553 let mut loc_state_1 = ACSLocState::new_empty(&ta);
554 loc_state_1[loc_l1] = 1;
555
556 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
557 .into_iter()
558 .flat_map(|is| {
559 [
560 ErrorGraphNode {
561 error_level: 0,
562 config: ACSTAConfig::new_mock(loc_state_3.clone(), is.clone()),
563 parents: Vec::new(),
564 self_loops: Vec::new(),
565 back_edges: Vec::new(),
566 },
567 ErrorGraphNode {
568 error_level: 0,
569 config: ACSTAConfig::new_mock(loc_state_2.clone(), is.clone()),
570 parents: Vec::new(),
571 self_loops: Vec::new(),
572 back_edges: Vec::new(),
573 },
574 ErrorGraphNode {
575 error_level: 0,
576 config: ACSTAConfig::new_mock(loc_state_1.clone(), is),
577 parents: Vec::new(),
578 self_loops: Vec::new(),
579 back_edges: Vec::new(),
580 },
581 ]
582 })
583 .collect::<Vec<_>>();
584
585 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
586 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
587 }
588
589 #[test]
590 fn test_new_compute_predecessor_ta_rule_not_exec_inc_all_cover() {
591 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
592 .with_variables([Variable::new("x"), Variable::new("y")])
593 .unwrap()
594 .with_locations([
595 Location::new("l1"),
596 Location::new("l2"),
597 Location::new("l3"),
598 ])
599 .unwrap()
600 .with_parameter(Parameter::new("n"))
601 .unwrap()
602 .initialize()
603 .with_resilience_condition(BooleanExpression::ComparisonExpression(
604 Box::new(IntegerExpression::Param(Parameter::new("n"))),
605 ComparisonOp::Gt,
606 Box::new(IntegerExpression::Const(3)),
607 ))
608 .unwrap()
609 .with_initial_location_constraints([
610 BooleanExpression::ComparisonExpression(
611 Box::new(IntegerExpression::Atom(Location::new("l2"))),
612 ComparisonOp::Eq,
613 Box::new(IntegerExpression::Const(0)),
614 ),
615 BooleanExpression::ComparisonExpression(
616 Box::new(IntegerExpression::Atom(Location::new("l3"))),
617 ComparisonOp::Eq,
618 Box::new(IntegerExpression::Const(0)),
619 ),
620 ])
621 .unwrap()
622 .with_initial_variable_constraints([
623 BooleanExpression::ComparisonExpression(
624 Box::new(IntegerExpression::Atom(Variable::new("x"))),
625 ComparisonOp::Eq,
626 Box::new(IntegerExpression::Const(0)),
627 ),
628 BooleanExpression::ComparisonExpression(
629 Box::new(IntegerExpression::Atom(Variable::new("y"))),
630 ComparisonOp::Eq,
631 Box::new(IntegerExpression::Const(0)),
632 ),
633 ])
634 .unwrap()
635 .with_rule(
636 RuleBuilder::new(0, Location::new("l1"), Location::new("l1"))
637 .with_guard(BooleanExpression::False) .with_action(
639 Action::new(
640 Variable::new("x"),
641 IntegerExpression::Const(1)
642 + IntegerExpression::Atom(Variable::new("x")),
643 )
644 .unwrap(),
645 )
646 .build(),
647 )
648 .unwrap()
649 .build();
650 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
651 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
652 .build()
653 .unwrap();
654 let interval_ta = interval_tas.next().unwrap();
655 assert!(interval_tas.next().is_none());
656 let ta = ACSThresholdAutomaton::from(interval_ta);
657
658 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
659 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
660 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
661
662 let var_x = ta.to_cs_var(&Variable::new("x"));
663
664 let spec = UpwardsClosedSet::new_cover([
665 Location::new("l3"),
666 Location::new("l2"),
667 Location::new("l1"),
668 ]);
669
670 let mut cfg_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
673 let mut to_explore_queue: VecDeque<NodeRef> = VecDeque::new();
674
675 let mut loc_state = ACSLocState::new_empty(&ta);
676 loc_state[loc_l3] = 1;
677 loc_state[loc_l2] = 1;
678 loc_state[loc_l1] = 1;
679
680 let mut explored_is = ACSIntervalState::new_mock_zero(&ta);
681 explored_is[var_x] = ACSInterval::new_mock(1);
682
683 let node_to_explore = cfg_node_map
685 .values()
686 .find(|n| *n.borrow().config.interval_state() == explored_is)
687 .unwrap()
688 .clone();
689
690 ErrorGraphNode::compute_predecessors_and_insert_to_graph(
691 &mut cfg_node_map,
692 &mut to_explore_queue,
693 &node_to_explore,
694 &ta,
695 );
696
697 assert_eq!(
698 to_explore_queue.len(),
699 0,
700 "Got configs {:?}",
701 join_iterator(
702 to_explore_queue
703 .iter()
704 .map(|n| n.borrow().config.display(&ta)),
705 ", "
706 )
707 );
708
709 let created_nodes = cfg_node_map
710 .get_all_values()
711 .map(|n| n.borrow().clone())
712 .collect::<Vec<_>>();
713
714 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
715 .into_iter()
716 .map(|is| ErrorGraphNode {
717 error_level: 0,
718 config: ACSTAConfig::new_mock(loc_state.clone(), is),
719 parents: Vec::new(),
720 self_loops: vec![],
721 back_edges: Vec::new(),
722 })
723 .collect::<Vec<_>>();
724
725 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
726 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
727 }
728
729 #[test]
730 fn test_new_compute_predecessor_ta_only_loc_update_all_cover() {
731 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
732 .with_variables([Variable::new("x"), Variable::new("y")])
733 .unwrap()
734 .with_locations([
735 Location::new("l1"),
736 Location::new("l2"),
737 Location::new("l3"),
738 ])
739 .unwrap()
740 .with_parameter(Parameter::new("n"))
741 .unwrap()
742 .initialize()
743 .with_resilience_condition(BooleanExpression::ComparisonExpression(
744 Box::new(IntegerExpression::Param(Parameter::new("n"))),
745 ComparisonOp::Gt,
746 Box::new(IntegerExpression::Const(3)),
747 ))
748 .unwrap()
749 .with_initial_location_constraints([
750 BooleanExpression::ComparisonExpression(
751 Box::new(IntegerExpression::Atom(Location::new("l2"))),
752 ComparisonOp::Eq,
753 Box::new(IntegerExpression::Const(0)),
754 ),
755 BooleanExpression::ComparisonExpression(
756 Box::new(IntegerExpression::Atom(Location::new("l3"))),
757 ComparisonOp::Eq,
758 Box::new(IntegerExpression::Const(0)),
759 ),
760 ])
761 .unwrap()
762 .with_initial_variable_constraints([
763 BooleanExpression::ComparisonExpression(
764 Box::new(IntegerExpression::Atom(Variable::new("x"))),
765 ComparisonOp::Eq,
766 Box::new(IntegerExpression::Const(0)),
767 ),
768 BooleanExpression::ComparisonExpression(
769 Box::new(IntegerExpression::Atom(Variable::new("y"))),
770 ComparisonOp::Eq,
771 Box::new(IntegerExpression::Const(0)),
772 ),
773 ])
774 .unwrap()
775 .with_rule(RuleBuilder::new(0, Location::new("l1"), Location::new("l2")).build())
776 .unwrap()
777 .build();
778 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
779 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
780 .build()
781 .unwrap();
782 let interval_ta = interval_tas.next().unwrap();
783 assert!(interval_tas.next().is_none());
784 let ta = ACSThresholdAutomaton::from(interval_ta);
785
786 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
787 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
788 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
789
790 let var_x = ta.to_cs_var(&Variable::new("x"));
791
792 let spec = UpwardsClosedSet::new_cover([
793 Location::new("l3"),
794 Location::new("l2"),
795 Location::new("l1"),
796 ]);
797
798 let mut cfg_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
801 let mut to_explore_queue: VecDeque<NodeRef> = VecDeque::new();
802
803 let mut loc_state = ACSLocState::new_empty(&ta);
804 loc_state[loc_l3] = 1;
805 loc_state[loc_l2] = 1;
806 loc_state[loc_l1] = 1;
807
808 let mut is_to_explore = ACSIntervalState::new_mock_zero(&ta);
809 is_to_explore[var_x] = ACSInterval::new_mock(1);
810
811 let node_to_explore = cfg_node_map
813 .values()
814 .find(|n| *n.borrow().config.interval_state() == is_to_explore.clone())
815 .unwrap()
816 .clone();
817
818 ErrorGraphNode::compute_predecessors_and_insert_to_graph(
819 &mut cfg_node_map,
820 &mut to_explore_queue,
821 &node_to_explore,
822 &ta,
823 );
824
825 assert_eq!(
826 to_explore_queue.len(),
827 1,
828 "Got configs {}",
829 join_iterator(
830 to_explore_queue
831 .iter()
832 .map(|n| n.borrow().config.display(&ta)),
833 ", "
834 )
835 );
836
837 let mut new_loc_state = ACSLocState::new_empty(&ta);
838 new_loc_state[loc_l3] = 1;
839 new_loc_state[loc_l2] = 0;
840 new_loc_state[loc_l1] = 2;
841
842 let expected_node_to_explore = ErrorGraphNode {
843 error_level: 1,
844 config: ACSTAConfig::new_mock(new_loc_state, is_to_explore.clone()),
845 parents: vec![ErrorGraphEdge::new_mock_with_id(0, node_to_explore.clone())],
846 self_loops: Vec::new(),
847 back_edges: Vec::new(),
848 };
849
850 assert_eq!(
851 to_explore_queue,
852 VecDeque::from([expected_node_to_explore.clone().into()])
853 );
854
855 let created_nodes = cfg_node_map
856 .get_all_values()
857 .map(|n| n.borrow().clone())
858 .collect::<Vec<_>>();
859
860 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
861 .into_iter()
862 .map(|is| ErrorGraphNode {
863 error_level: 0,
864 config: ACSTAConfig::new_mock(loc_state.clone(), is),
865 parents: Vec::new(),
866 self_loops: vec![],
867 back_edges: Vec::new(),
868 })
869 .chain([expected_node_to_explore])
870 .collect::<Vec<_>>();
871
872 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
873 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
874 }
875
876 #[test]
877 fn test_new_compute_predecessor_ta_only_self_loop_with_inc_all_cover() {
878 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
879 .with_variables([Variable::new("x"), Variable::new("y")])
880 .unwrap()
881 .with_locations([
882 Location::new("l1"),
883 Location::new("l2"),
884 Location::new("l3"),
885 ])
886 .unwrap()
887 .with_parameter(Parameter::new("n"))
888 .unwrap()
889 .initialize()
890 .with_resilience_condition(BooleanExpression::ComparisonExpression(
891 Box::new(IntegerExpression::Param(Parameter::new("n"))),
892 ComparisonOp::Gt,
893 Box::new(IntegerExpression::Const(3)),
894 ))
895 .unwrap()
896 .with_initial_location_constraints([
897 BooleanExpression::ComparisonExpression(
898 Box::new(IntegerExpression::Atom(Location::new("l2"))),
899 ComparisonOp::Eq,
900 Box::new(IntegerExpression::Const(0)),
901 ),
902 BooleanExpression::ComparisonExpression(
903 Box::new(IntegerExpression::Atom(Location::new("l3"))),
904 ComparisonOp::Eq,
905 Box::new(IntegerExpression::Const(0)),
906 ),
907 ])
908 .unwrap()
909 .with_initial_variable_constraints([
910 BooleanExpression::ComparisonExpression(
911 Box::new(IntegerExpression::Atom(Variable::new("x"))),
912 ComparisonOp::Lt,
913 Box::new(IntegerExpression::Const(1)),
914 ),
915 BooleanExpression::ComparisonExpression(
916 Box::new(IntegerExpression::Atom(Variable::new("y"))),
917 ComparisonOp::Lt,
918 Box::new(IntegerExpression::Const(1)),
919 ),
920 ])
921 .unwrap()
922 .with_rule(
923 RuleBuilder::new(0, Location::new("l1"), Location::new("l1"))
924 .with_action(
925 Action::new(
926 Variable::new("x"),
927 IntegerExpression::Const(1)
928 + IntegerExpression::Atom(Variable::new("x")),
929 )
930 .unwrap(),
931 )
932 .build(),
933 )
934 .unwrap()
935 .build();
936 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
937 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
938 .build()
939 .unwrap();
940 let interval_ta = interval_tas.next().unwrap();
941 assert!(interval_tas.next().is_none());
942 let ta = ACSThresholdAutomaton::from(interval_ta);
943
944 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
945 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
946 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
947
948 let var_x = ta.to_cs_var(&Variable::new("x"));
949
950 let spec = UpwardsClosedSet::new_cover([
951 Location::new("l3"),
952 Location::new("l2"),
953 Location::new("l1"),
954 ]);
955
956 let mut cfg_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
959 let mut to_explore_queue: VecDeque<NodeRef> = VecDeque::new();
960
961 let mut loc_state = ACSLocState::new_empty(&ta);
962 loc_state[loc_l3] = 1;
963 loc_state[loc_l2] = 1;
964 loc_state[loc_l1] = 1;
965
966 let mut explored_is = ACSIntervalState::new_mock_zero(&ta);
967 explored_is[var_x] = ACSInterval::new_mock(1);
968
969 let node_to_explore = cfg_node_map
971 .values()
972 .find(|n| *n.borrow().config.interval_state() == explored_is)
973 .unwrap()
974 .clone();
975
976 ErrorGraphNode::compute_predecessors_and_insert_to_graph(
977 &mut cfg_node_map,
978 &mut to_explore_queue,
979 &node_to_explore,
980 &ta,
981 );
982
983 assert_eq!(
984 to_explore_queue.len(),
985 0,
986 "Got configs {:?}",
987 join_iterator(
988 to_explore_queue
989 .iter()
990 .map(|n| n.borrow().config.display(&ta)),
991 ", "
992 )
993 );
994
995 let created_nodes = cfg_node_map
996 .get_all_values()
997 .map(|n| n.borrow().clone())
998 .collect::<Vec<_>>();
999
1000 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
1002 .into_iter()
1003 .map(|is| {
1004 if is == explored_is {
1008 return ErrorGraphNode {
1009 error_level: 0,
1010 config: ACSTAConfig::new_mock(loc_state.clone(), is),
1011 parents: Vec::new(),
1012 self_loops: vec![0],
1013 back_edges: Vec::new(),
1014 };
1015 }
1016
1017 if is == ACSIntervalState::new_mock_zero(&ta) {
1018 return ErrorGraphNode {
1019 error_level: 0,
1020 config: ACSTAConfig::new_mock(
1021 loc_state.clone(),
1022 ACSIntervalState::new_mock_zero(&ta),
1023 ),
1024 parents: vec![ErrorGraphEdge::new_mock_with_id(
1025 0,
1026 ErrorGraphNode {
1027 error_level: 0,
1028 config: ACSTAConfig::new_mock(
1029 loc_state.clone(),
1030 explored_is.clone(),
1031 ),
1032 parents: Vec::new(),
1033 self_loops: vec![0],
1034 back_edges: Vec::new(),
1035 }
1036 .into(),
1037 )],
1038 self_loops: vec![],
1039 back_edges: Vec::new(),
1040 };
1041 }
1042
1043 ErrorGraphNode {
1046 error_level: 0,
1047 config: ACSTAConfig::new_mock(loc_state.clone(), is),
1048 parents: Vec::new(),
1049 self_loops: vec![],
1050 back_edges: Vec::new(),
1051 }
1052 })
1053 .collect::<Vec<_>>();
1054
1055 assert!(
1056 created_nodes.iter().all(|n| expected_nodes.contains(n)),
1057 "got: {}\n expected: {}\n additional: {}",
1058 join_iterator(
1059 created_nodes
1060 .iter()
1061 .map(|n| { n.config.display_compact(&ta) }),
1062 ","
1063 ),
1064 join_iterator(
1065 expected_nodes
1066 .iter()
1067 .map(|n| { n.config.display_compact(&ta) }),
1068 ","
1069 ),
1070 join_iterator(
1071 created_nodes
1072 .iter()
1073 .filter(|n| !expected_nodes.contains(n))
1074 .map(|n| { n.config.display_compact(&ta) }),
1075 "; "
1076 )
1077 );
1078 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
1079 }
1080}