1use std::collections::VecDeque;
4
5use log::{debug, info};
6use taco_model_checker::reachability_specification::DisjunctionTargetConfig;
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: &DisjunctionTargetConfig,
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 use taco_model_checker::reachability_specification::{DisjunctionTargetConfig, TargetConfig};
311 use taco_smt_encoder::SMTSolverBuilder;
312 use taco_threshold_automaton::{
313 expressions::{
314 BooleanExpression, ComparisonOp, IntegerExpression, Location, Parameter, Variable,
315 },
316 general_threshold_automaton::{
317 Action,
318 builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
319 },
320 lia_threshold_automaton::LIAThresholdAutomaton,
321 };
322
323 use crate::{
324 acs_threshold_automaton::{
325 ACSInterval, ACSLocation, ACSThresholdAutomaton, CSIntervalConstraint, CSRule,
326 configuration::{ACSIntervalState, ACSLocState, ACSTAConfig},
327 },
328 error_graph::{NodeRef, edge::ErrorGraphEdge, node::ErrorGraphNode},
329 };
330
331 fn get_test_ta() -> ACSThresholdAutomaton {
332 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
333 .with_variables([Variable::new("x"), Variable::new("y")])
334 .unwrap()
335 .with_locations([
336 Location::new("l1"),
337 Location::new("l2"),
338 Location::new("l3"),
339 ])
340 .unwrap()
341 .with_parameter(Parameter::new("n"))
342 .unwrap()
343 .initialize()
344 .with_resilience_condition(BooleanExpression::ComparisonExpression(
345 Box::new(IntegerExpression::Param(Parameter::new("n"))),
346 ComparisonOp::Gt,
347 Box::new(IntegerExpression::Const(3)),
348 ))
349 .unwrap()
350 .with_initial_location_constraints([
351 BooleanExpression::ComparisonExpression(
352 Box::new(IntegerExpression::Atom(Location::new("l2"))),
353 ComparisonOp::Eq,
354 Box::new(IntegerExpression::Const(0)),
355 ),
356 BooleanExpression::ComparisonExpression(
357 Box::new(IntegerExpression::Atom(Location::new("l3"))),
358 ComparisonOp::Eq,
359 Box::new(IntegerExpression::Const(0)),
360 ),
361 ])
362 .unwrap()
363 .with_initial_variable_constraints([
364 BooleanExpression::ComparisonExpression(
365 Box::new(IntegerExpression::Atom(Variable::new("x"))),
366 ComparisonOp::Eq,
367 Box::new(IntegerExpression::Const(0)),
368 ),
369 BooleanExpression::ComparisonExpression(
370 Box::new(IntegerExpression::Atom(Variable::new("y"))),
371 ComparisonOp::Eq,
372 Box::new(IntegerExpression::Const(0)),
373 ),
374 ])
375 .unwrap()
376 .with_rule(
377 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
378 .with_action(
379 Action::new(
380 Variable::new("x"),
381 IntegerExpression::Const(1)
382 + IntegerExpression::Atom(Variable::new("x")),
383 )
384 .unwrap(),
385 )
386 .build(),
387 )
388 .unwrap()
389 .build();
390 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
391 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
392 .build()
393 .unwrap();
394 let interval_ta = interval_tas.next().unwrap();
395 assert!(interval_tas.next().is_none());
396 ACSThresholdAutomaton::from(interval_ta)
397 }
398
399 #[test]
400 fn test_getters() {
401 let edge1 = ErrorGraphEdge::new(
402 &CSRule::new_mock(
403 0,
404 ACSLocation::new_mock(1),
405 ACSLocation::new_mock(2),
406 CSIntervalConstraint::True,
407 Vec::new(),
408 ),
409 Rc::new(RefCell::new(ErrorGraphNode {
410 error_level: 0,
411 config: ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new()),
412 parents: Vec::new(),
413 self_loops: Vec::new(),
414 back_edges: Vec::new(),
415 })),
416 );
417
418 let edge2 = ErrorGraphEdge::new(
419 &CSRule::new_mock(
420 3,
421 ACSLocation::new_mock(1),
422 ACSLocation::new_mock(2),
423 CSIntervalConstraint::True,
424 Vec::new(),
425 ),
426 Rc::new(RefCell::new(ErrorGraphNode {
427 error_level: 0,
428 config: ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new()),
429 parents: Vec::new(),
430 self_loops: Vec::new(),
431 back_edges: Vec::new(),
432 })),
433 );
434
435 let node = ErrorGraphNode {
436 error_level: 42,
437 config: ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new()),
438 parents: vec![edge1.clone()],
439 self_loops: vec![1, 2, 3],
440 back_edges: vec![edge2.clone()],
441 };
442
443 assert_eq!(node.error_level(), 42);
444 assert_eq!(
445 node.config(),
446 &ACSTAConfig::new_mock_from_vecs(Vec::new(), Vec::new())
447 );
448 assert_eq!(
449 node.self_loops().cloned().collect::<Vec<_>>(),
450 vec![1, 2, 3]
451 );
452 assert_eq!(node.parents().collect::<Vec<_>>(), vec![&edge1]);
453 assert_eq!(node.back_edges().collect::<Vec<_>>(), vec![&edge2]);
454 }
455
456 #[test]
457 fn test_new_roots_from_spec_single() {
458 let ta = get_test_ta();
459
460 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
461
462 let spec = TargetConfig::new_cover([Location::new("l3")])
463 .unwrap()
464 .into_disjunct_with_name("test");
465
466 let created_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
467 let created_nodes = created_node_map
468 .get_all_values()
469 .map(|n| n.borrow().clone())
470 .collect::<Vec<_>>();
471
472 let mut loc_state = ACSLocState::new_empty(&ta);
473 loc_state[loc_l3] = 1;
474
475 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
476 .into_iter()
477 .map(|is| ErrorGraphNode {
478 error_level: 0,
479 config: ACSTAConfig::new_mock(loc_state.clone(), is),
480 parents: Vec::new(),
481 self_loops: Vec::new(),
482 back_edges: Vec::new(),
483 })
484 .collect::<Vec<_>>();
485
486 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
487 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
488 }
489
490 #[test]
491 fn test_new_roots_from_spec_all_cover() {
492 let ta = get_test_ta();
493
494 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
495 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
496 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
497
498 let spec = TargetConfig::new_cover([
499 Location::new("l3"),
500 Location::new("l2"),
501 Location::new("l1"),
502 ])
503 .unwrap()
504 .into_disjunct_with_name("test");
505
506 let created_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
507 let created_nodes = created_node_map
508 .get_all_values()
509 .map(|n| n.borrow().clone())
510 .collect::<Vec<_>>();
511
512 let mut loc_state = ACSLocState::new_empty(&ta);
513 loc_state[loc_l3] = 1;
514 loc_state[loc_l2] = 1;
515 loc_state[loc_l1] = 1;
516
517 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
518 .into_iter()
519 .map(|is| ErrorGraphNode {
520 error_level: 0,
521 config: ACSTAConfig::new_mock(loc_state.clone(), is),
522 parents: Vec::new(),
523 self_loops: Vec::new(),
524 back_edges: Vec::new(),
525 })
526 .collect::<Vec<_>>();
527
528 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
529 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
530 }
531
532 #[test]
533 fn test_new_roots_from_spec_multi_cover() {
534 let ta = get_test_ta();
535
536 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
537 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
538 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
539
540 let spec = DisjunctionTargetConfig::new_from_targets(
541 "test".into(),
542 [
543 TargetConfig::new_cover([Location::new("l3")]).unwrap(),
544 TargetConfig::new_cover([Location::new("l2")]).unwrap(),
545 TargetConfig::new_cover([Location::new("l1")]).unwrap(),
546 ],
547 );
548
549 let created_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
550 let created_nodes = created_node_map
551 .get_all_values()
552 .map(|n| n.borrow().clone())
553 .collect::<Vec<_>>();
554
555 let mut loc_state_3 = ACSLocState::new_empty(&ta);
556 loc_state_3[loc_l3] = 1;
557
558 let mut loc_state_2 = ACSLocState::new_empty(&ta);
559 loc_state_2[loc_l2] = 1;
560
561 let mut loc_state_1 = ACSLocState::new_empty(&ta);
562 loc_state_1[loc_l1] = 1;
563
564 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
565 .into_iter()
566 .flat_map(|is| {
567 [
568 ErrorGraphNode {
569 error_level: 0,
570 config: ACSTAConfig::new_mock(loc_state_3.clone(), is.clone()),
571 parents: Vec::new(),
572 self_loops: Vec::new(),
573 back_edges: Vec::new(),
574 },
575 ErrorGraphNode {
576 error_level: 0,
577 config: ACSTAConfig::new_mock(loc_state_2.clone(), is.clone()),
578 parents: Vec::new(),
579 self_loops: Vec::new(),
580 back_edges: Vec::new(),
581 },
582 ErrorGraphNode {
583 error_level: 0,
584 config: ACSTAConfig::new_mock(loc_state_1.clone(), is),
585 parents: Vec::new(),
586 self_loops: Vec::new(),
587 back_edges: Vec::new(),
588 },
589 ]
590 })
591 .collect::<Vec<_>>();
592
593 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
594 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
595 }
596
597 #[test]
598 fn test_new_compute_predecessor_ta_rule_not_exec_inc_all_cover() {
599 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
600 .with_variables([Variable::new("x"), Variable::new("y")])
601 .unwrap()
602 .with_locations([
603 Location::new("l1"),
604 Location::new("l2"),
605 Location::new("l3"),
606 ])
607 .unwrap()
608 .with_parameter(Parameter::new("n"))
609 .unwrap()
610 .initialize()
611 .with_resilience_condition(BooleanExpression::ComparisonExpression(
612 Box::new(IntegerExpression::Param(Parameter::new("n"))),
613 ComparisonOp::Gt,
614 Box::new(IntegerExpression::Const(3)),
615 ))
616 .unwrap()
617 .with_initial_location_constraints([
618 BooleanExpression::ComparisonExpression(
619 Box::new(IntegerExpression::Atom(Location::new("l2"))),
620 ComparisonOp::Eq,
621 Box::new(IntegerExpression::Const(0)),
622 ),
623 BooleanExpression::ComparisonExpression(
624 Box::new(IntegerExpression::Atom(Location::new("l3"))),
625 ComparisonOp::Eq,
626 Box::new(IntegerExpression::Const(0)),
627 ),
628 ])
629 .unwrap()
630 .with_initial_variable_constraints([
631 BooleanExpression::ComparisonExpression(
632 Box::new(IntegerExpression::Atom(Variable::new("x"))),
633 ComparisonOp::Eq,
634 Box::new(IntegerExpression::Const(0)),
635 ),
636 BooleanExpression::ComparisonExpression(
637 Box::new(IntegerExpression::Atom(Variable::new("y"))),
638 ComparisonOp::Eq,
639 Box::new(IntegerExpression::Const(0)),
640 ),
641 ])
642 .unwrap()
643 .with_rule(
644 RuleBuilder::new(0, Location::new("l1"), Location::new("l1"))
645 .with_guard(BooleanExpression::False) .with_action(
647 Action::new(
648 Variable::new("x"),
649 IntegerExpression::Const(1)
650 + IntegerExpression::Atom(Variable::new("x")),
651 )
652 .unwrap(),
653 )
654 .build(),
655 )
656 .unwrap()
657 .build();
658 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
659 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
660 .build()
661 .unwrap();
662 let interval_ta = interval_tas.next().unwrap();
663 assert!(interval_tas.next().is_none());
664 let ta = ACSThresholdAutomaton::from(interval_ta);
665
666 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
667 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
668 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
669
670 let var_x = ta.to_cs_var(&Variable::new("x"));
671
672 let spec = TargetConfig::new_cover([
673 Location::new("l3"),
674 Location::new("l2"),
675 Location::new("l1"),
676 ])
677 .unwrap()
678 .into_disjunct_with_name("test");
679
680 let mut cfg_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
683 let mut to_explore_queue: VecDeque<NodeRef> = VecDeque::new();
684
685 let mut loc_state = ACSLocState::new_empty(&ta);
686 loc_state[loc_l3] = 1;
687 loc_state[loc_l2] = 1;
688 loc_state[loc_l1] = 1;
689
690 let mut explored_is = ACSIntervalState::new_mock_zero(&ta);
691 explored_is[var_x] = ACSInterval::new_mock(1);
692
693 let node_to_explore = cfg_node_map
695 .values()
696 .find(|n| *n.borrow().config.interval_state() == explored_is)
697 .unwrap()
698 .clone();
699
700 ErrorGraphNode::compute_predecessors_and_insert_to_graph(
701 &mut cfg_node_map,
702 &mut to_explore_queue,
703 &node_to_explore,
704 &ta,
705 );
706
707 assert_eq!(
708 to_explore_queue.len(),
709 0,
710 "Got configs {:?}",
711 join_iterator(
712 to_explore_queue
713 .iter()
714 .map(|n| n.borrow().config.display(&ta)),
715 ", "
716 )
717 );
718
719 let created_nodes = cfg_node_map
720 .get_all_values()
721 .map(|n| n.borrow().clone())
722 .collect::<Vec<_>>();
723
724 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
725 .into_iter()
726 .map(|is| ErrorGraphNode {
727 error_level: 0,
728 config: ACSTAConfig::new_mock(loc_state.clone(), is),
729 parents: Vec::new(),
730 self_loops: vec![],
731 back_edges: Vec::new(),
732 })
733 .collect::<Vec<_>>();
734
735 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
736 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
737 }
738
739 #[test]
740 fn test_new_compute_predecessor_ta_only_loc_update_all_cover() {
741 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
742 .with_variables([Variable::new("x"), Variable::new("y")])
743 .unwrap()
744 .with_locations([
745 Location::new("l1"),
746 Location::new("l2"),
747 Location::new("l3"),
748 ])
749 .unwrap()
750 .with_parameter(Parameter::new("n"))
751 .unwrap()
752 .initialize()
753 .with_resilience_condition(BooleanExpression::ComparisonExpression(
754 Box::new(IntegerExpression::Param(Parameter::new("n"))),
755 ComparisonOp::Gt,
756 Box::new(IntegerExpression::Const(3)),
757 ))
758 .unwrap()
759 .with_initial_location_constraints([
760 BooleanExpression::ComparisonExpression(
761 Box::new(IntegerExpression::Atom(Location::new("l2"))),
762 ComparisonOp::Eq,
763 Box::new(IntegerExpression::Const(0)),
764 ),
765 BooleanExpression::ComparisonExpression(
766 Box::new(IntegerExpression::Atom(Location::new("l3"))),
767 ComparisonOp::Eq,
768 Box::new(IntegerExpression::Const(0)),
769 ),
770 ])
771 .unwrap()
772 .with_initial_variable_constraints([
773 BooleanExpression::ComparisonExpression(
774 Box::new(IntegerExpression::Atom(Variable::new("x"))),
775 ComparisonOp::Eq,
776 Box::new(IntegerExpression::Const(0)),
777 ),
778 BooleanExpression::ComparisonExpression(
779 Box::new(IntegerExpression::Atom(Variable::new("y"))),
780 ComparisonOp::Eq,
781 Box::new(IntegerExpression::Const(0)),
782 ),
783 ])
784 .unwrap()
785 .with_rule(RuleBuilder::new(0, Location::new("l1"), Location::new("l2")).build())
786 .unwrap()
787 .build();
788 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
789 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
790 .build()
791 .unwrap();
792 let interval_ta = interval_tas.next().unwrap();
793 assert!(interval_tas.next().is_none());
794 let ta = ACSThresholdAutomaton::from(interval_ta);
795
796 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
797 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
798 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
799
800 let var_x = ta.to_cs_var(&Variable::new("x"));
801
802 let spec = TargetConfig::new_cover([
803 Location::new("l3"),
804 Location::new("l2"),
805 Location::new("l1"),
806 ])
807 .unwrap()
808 .into_disjunct_with_name("test");
809
810 let mut cfg_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
813 let mut to_explore_queue: VecDeque<NodeRef> = VecDeque::new();
814
815 let mut loc_state = ACSLocState::new_empty(&ta);
816 loc_state[loc_l3] = 1;
817 loc_state[loc_l2] = 1;
818 loc_state[loc_l1] = 1;
819
820 let mut is_to_explore = ACSIntervalState::new_mock_zero(&ta);
821 is_to_explore[var_x] = ACSInterval::new_mock(1);
822
823 let node_to_explore = cfg_node_map
825 .values()
826 .find(|n| *n.borrow().config.interval_state() == is_to_explore.clone())
827 .unwrap()
828 .clone();
829
830 ErrorGraphNode::compute_predecessors_and_insert_to_graph(
831 &mut cfg_node_map,
832 &mut to_explore_queue,
833 &node_to_explore,
834 &ta,
835 );
836
837 assert_eq!(
838 to_explore_queue.len(),
839 1,
840 "Got configs {}",
841 join_iterator(
842 to_explore_queue
843 .iter()
844 .map(|n| n.borrow().config.display(&ta)),
845 ", "
846 )
847 );
848
849 let mut new_loc_state = ACSLocState::new_empty(&ta);
850 new_loc_state[loc_l3] = 1;
851 new_loc_state[loc_l2] = 0;
852 new_loc_state[loc_l1] = 2;
853
854 let expected_node_to_explore = ErrorGraphNode {
855 error_level: 1,
856 config: ACSTAConfig::new_mock(new_loc_state, is_to_explore.clone()),
857 parents: vec![ErrorGraphEdge::new_mock_with_id(0, node_to_explore.clone())],
858 self_loops: Vec::new(),
859 back_edges: Vec::new(),
860 };
861
862 assert_eq!(
863 to_explore_queue,
864 VecDeque::from([expected_node_to_explore.clone().into()])
865 );
866
867 let created_nodes = cfg_node_map
868 .get_all_values()
869 .map(|n| n.borrow().clone())
870 .collect::<Vec<_>>();
871
872 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
873 .into_iter()
874 .map(|is| ErrorGraphNode {
875 error_level: 0,
876 config: ACSTAConfig::new_mock(loc_state.clone(), is),
877 parents: Vec::new(),
878 self_loops: vec![],
879 back_edges: Vec::new(),
880 })
881 .chain([expected_node_to_explore])
882 .collect::<Vec<_>>();
883
884 assert!(created_nodes.iter().all(|n| expected_nodes.contains(n)));
885 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
886 }
887
888 #[test]
889 fn test_new_compute_predecessor_ta_only_self_loop_with_inc_all_cover() {
890 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
891 .with_variables([Variable::new("x"), Variable::new("y")])
892 .unwrap()
893 .with_locations([
894 Location::new("l1"),
895 Location::new("l2"),
896 Location::new("l3"),
897 ])
898 .unwrap()
899 .with_parameter(Parameter::new("n"))
900 .unwrap()
901 .initialize()
902 .with_resilience_condition(BooleanExpression::ComparisonExpression(
903 Box::new(IntegerExpression::Param(Parameter::new("n"))),
904 ComparisonOp::Gt,
905 Box::new(IntegerExpression::Const(3)),
906 ))
907 .unwrap()
908 .with_initial_location_constraints([
909 BooleanExpression::ComparisonExpression(
910 Box::new(IntegerExpression::Atom(Location::new("l2"))),
911 ComparisonOp::Eq,
912 Box::new(IntegerExpression::Const(0)),
913 ),
914 BooleanExpression::ComparisonExpression(
915 Box::new(IntegerExpression::Atom(Location::new("l3"))),
916 ComparisonOp::Eq,
917 Box::new(IntegerExpression::Const(0)),
918 ),
919 ])
920 .unwrap()
921 .with_initial_variable_constraints([
922 BooleanExpression::ComparisonExpression(
923 Box::new(IntegerExpression::Atom(Variable::new("x"))),
924 ComparisonOp::Eq,
925 Box::new(IntegerExpression::Const(0)),
926 ),
927 BooleanExpression::ComparisonExpression(
928 Box::new(IntegerExpression::Atom(Variable::new("y"))),
929 ComparisonOp::Eq,
930 Box::new(IntegerExpression::Const(0)),
931 ),
932 ])
933 .unwrap()
934 .with_rule(
935 RuleBuilder::new(0, Location::new("l1"), Location::new("l1"))
936 .with_action(
937 Action::new(
938 Variable::new("x"),
939 IntegerExpression::Const(1)
940 + IntegerExpression::Atom(Variable::new("x")),
941 )
942 .unwrap(),
943 )
944 .build(),
945 )
946 .unwrap()
947 .build();
948 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
949 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
950 .build()
951 .unwrap();
952 let interval_ta = interval_tas.next().unwrap();
953 assert!(interval_tas.next().is_none());
954 let ta = ACSThresholdAutomaton::from(interval_ta);
955
956 let loc_l1 = ta.to_cs_loc(&Location::new("l1"));
957 let loc_l2 = ta.to_cs_loc(&Location::new("l2"));
958 let loc_l3 = ta.to_cs_loc(&Location::new("l3"));
959
960 let var_x = ta.to_cs_var(&Variable::new("x"));
961
962 let spec = TargetConfig::new_cover([
963 Location::new("l3"),
964 Location::new("l2"),
965 Location::new("l1"),
966 ])
967 .unwrap()
968 .into_disjunct_with_name("test");
969
970 let mut cfg_node_map = ErrorGraphNode::new_roots_from_spec(&spec, &ta);
973 let mut to_explore_queue: VecDeque<NodeRef> = VecDeque::new();
974
975 let mut loc_state = ACSLocState::new_empty(&ta);
976 loc_state[loc_l3] = 1;
977 loc_state[loc_l2] = 1;
978 loc_state[loc_l1] = 1;
979
980 let mut explored_is = ACSIntervalState::new_mock_zero(&ta);
981 explored_is[var_x] = ACSInterval::new_mock(1);
982
983 let node_to_explore = cfg_node_map
985 .values()
986 .find(|n| *n.borrow().config.interval_state() == explored_is)
987 .unwrap()
988 .clone();
989
990 ErrorGraphNode::compute_predecessors_and_insert_to_graph(
991 &mut cfg_node_map,
992 &mut to_explore_queue,
993 &node_to_explore,
994 &ta,
995 );
996
997 assert_eq!(
998 to_explore_queue.len(),
999 0,
1000 "Got configs {:?}",
1001 join_iterator(
1002 to_explore_queue
1003 .iter()
1004 .map(|n| n.borrow().config.display(&ta)),
1005 ", "
1006 )
1007 );
1008
1009 let created_nodes = cfg_node_map
1010 .get_all_values()
1011 .map(|n| n.borrow().clone())
1012 .collect::<Vec<_>>();
1013
1014 let expected_nodes = ACSIntervalState::all_possible_interval_configs(&ta)
1016 .into_iter()
1017 .map(|is| {
1018 if is == explored_is {
1022 return ErrorGraphNode {
1023 error_level: 0,
1024 config: ACSTAConfig::new_mock(loc_state.clone(), is),
1025 parents: Vec::new(),
1026 self_loops: vec![0],
1027 back_edges: Vec::new(),
1028 };
1029 }
1030
1031 if is == ACSIntervalState::new_mock_zero(&ta) {
1032 return ErrorGraphNode {
1033 error_level: 0,
1034 config: ACSTAConfig::new_mock(
1035 loc_state.clone(),
1036 ACSIntervalState::new_mock_zero(&ta),
1037 ),
1038 parents: vec![ErrorGraphEdge::new_mock_with_id(
1039 0,
1040 ErrorGraphNode {
1041 error_level: 0,
1042 config: ACSTAConfig::new_mock(
1043 loc_state.clone(),
1044 explored_is.clone(),
1045 ),
1046 parents: Vec::new(),
1047 self_loops: vec![0],
1048 back_edges: Vec::new(),
1049 }
1050 .into(),
1051 )],
1052 self_loops: vec![],
1053 back_edges: Vec::new(),
1054 };
1055 }
1056
1057 ErrorGraphNode {
1060 error_level: 0,
1061 config: ACSTAConfig::new_mock(loc_state.clone(), is),
1062 parents: Vec::new(),
1063 self_loops: vec![],
1064 back_edges: Vec::new(),
1065 }
1066 })
1067 .collect::<Vec<_>>();
1068
1069 assert!(
1070 created_nodes.iter().all(|n| expected_nodes.contains(n)),
1071 "got: {}\n expected: {}\n missing: {:?}",
1072 join_iterator(
1073 created_nodes
1074 .iter()
1075 .map(|n| { n.config.display_compact(&ta) }),
1076 ","
1077 ),
1078 join_iterator(
1079 expected_nodes
1080 .iter()
1081 .map(|n| { n.config.display_compact(&ta) }),
1082 ","
1083 ),
1084 created_nodes
1085 .iter()
1086 .find(|n| !expected_nodes.contains(n))
1087 .unwrap()
1088 );
1089 assert!(expected_nodes.iter().all(|n| created_nodes.contains(n)));
1090 }
1091}