1pub mod builder;
5
6use log::info;
7
8use std::collections::VecDeque;
9
10use taco_interval_ta::IntervalTARule;
11use taco_interval_ta::interval::Interval;
12use taco_threshold_automaton::expressions::{Location, Variable};
13use zcs::*;
14
15use crate::paths::{SteadyErrorPath, SteadyPath, VariableAssignment};
16use crate::smt_encoder_steady::SpuriousResult;
17use crate::{ZCSModelCheckerContext, zcs};
18
19#[derive(Debug, Clone)]
21pub struct ZCSErrorGraph<'a> {
22 error_states: ZCSStates,
24 explored_states: Vec<ZCSStates>,
27 initial_states: ZCSStates,
29 cs: ZCS<'a>,
31}
32impl ZCSErrorGraph<'_> {
33 pub fn is_empty(&self) -> bool {
35 self.initial_states.is_empty()
36 }
37
38 pub fn initial_states(&self) -> ZCSStates {
40 self.initial_states.clone()
41 }
42
43 pub fn compute_successors(&self, from: ZCSStates, with: SymbolicTransition) -> ZCSStates {
45 let from = self.cs.abstract_rule_vars(from);
47 let mut succ = self.cs.compute_successor(from, with.clone());
48 succ = self.cs.abstract_rule_vars(succ);
50 let mut intersection = self.cs.new_empty_sym_state();
52 for i in 0..self.explored_states.len() {
53 intersection = intersection.union(&(self.explored_states[i].intersection(&succ)));
54 }
55 intersection
56 }
57
58 pub fn get_non_error_intersection(&self, states: ZCSStates) -> (bool, ZCSStates) {
61 let intersection = self.explored_states[0].complement().intersection(&states);
62 if !intersection.is_empty() {
63 (true, intersection)
64 } else {
65 (false, intersection)
66 }
67 }
68
69 pub fn get_error_intersection(&self, states: ZCSStates) -> (bool, ZCSStates) {
72 let intersection = self.error_states.intersection(&states);
73 if !intersection.is_empty() {
74 (true, intersection)
75 } else {
76 (false, intersection)
77 }
78 }
79
80 pub fn get_sym_state_for_loc(&self, loc: &Location) -> ZCSStates {
82 self.cs.get_sym_state_for_loc(loc)
83 }
84
85 pub fn get_sym_state_for_shared_interval(
87 &self,
88 shared: &Variable,
89 interval: &Interval,
90 ) -> ZCSStates {
91 self.cs.get_sym_state_for_shared_interval(shared, interval)
92 }
93
94 pub fn get_sym_state_for_rule(&self, rule: &IntervalTARule) -> ZCSStates {
96 self.cs.get_sym_state_for_rule(rule)
97 }
98
99 pub fn transitions(&self) -> impl Iterator<Item = &SymbolicTransition> {
101 self.cs.transitions()
102 }
103
104 pub fn new_empty_sym_state(&self) -> ZCSStates {
106 self.cs.new_empty_sym_state()
107 }
108
109 pub fn contains_non_spurious_error_path(
160 &self,
161 smc_ctx: &ZCSModelCheckerContext,
162 ) -> SpuriousResult {
163 let reachable_assignments = self.compute_reachable_variable_assignments();
165 let mut queue: VecDeque<SteadyErrorPath> = VecDeque::new();
166 for assignment in reachable_assignments.clone() {
167 if self.initial_states().contains_sym_assignment(&assignment) {
168 let initial_states_for_assignment =
169 self.initial_states.intersect_assignment(&assignment);
170 let initial_steady_path =
171 self.construct_steady_path(initial_states_for_assignment, assignment);
172 queue.push_back(SteadyErrorPath::new(initial_steady_path, self));
173 }
174 }
175
176 let mut spurious_ce_count = 0;
179 let mut next_to_print = 1;
180
181 let all_possible_assignments = self.get_all_possible_variable_assignments();
183
184 while !queue.is_empty() {
185 let steady_error_path = queue.pop_front().unwrap();
186 let last_states = steady_error_path.get_last_states();
187 let last_assignment = steady_error_path.get_last_assignment();
188 for rule in self.cs.transitions() {
189 let successor_states = self.compute_successors(last_states.clone(), rule.clone());
190
191 let (non_empty, error_intersection) =
193 self.get_error_intersection(successor_states.clone());
194 if non_empty {
195 for assignment in all_possible_assignments.clone() {
197 if error_intersection.contains_sym_assignment(&assignment) {
198 let mut updated_steady_path = steady_error_path.clone();
199 let error_steady_path =
200 SteadyPath::new(vec![], error_intersection.clone(), assignment);
201 updated_steady_path.add_successor(rule, error_steady_path);
202
203 let res = updated_steady_path.is_non_spurious(true, smc_ctx);
204 if res.is_non_spurious() {
205 return res;
206 } else if smc_ctx.print_spurious_ce() {
207 info!("Spurious counterexample found: {updated_steady_path}");
208 }
209 }
210 }
211 }
212 let (non_empty, mut non_error_intersection) =
214 self.get_non_error_intersection(successor_states.clone());
215 if non_empty {
216 if smc_ctx.heuristics().is_monotonic()
217 || steady_error_path.last_is_monotonic(smc_ctx.ta())
218 {
219 non_error_intersection =
220 non_error_intersection.remove_assignment(&last_assignment);
221 }
222 if non_error_intersection.is_empty() {
223 continue;
224 }
225 for assignment in reachable_assignments.iter() {
226 if non_error_intersection.contains_sym_assignment(assignment) {
227 let mut updated_steady_path = steady_error_path.clone();
228 let non_error_states_for_assignment =
229 non_error_intersection.intersect_assignment(assignment);
230 let successor_steady_path = self.construct_steady_path(
231 non_error_states_for_assignment,
232 assignment.clone(),
233 );
234 updated_steady_path.add_successor(rule, successor_steady_path);
235 let res = updated_steady_path.is_non_spurious(false, smc_ctx);
236 if res.is_non_spurious() {
237 queue.push_back(updated_steady_path);
238 } else if smc_ctx.print_spurious_ce() {
239 spurious_ce_count += 1;
240 if spurious_ce_count == next_to_print {
241 info!("Spurious subpath found: {updated_steady_path}");
242 next_to_print *= 10;
243 }
244 }
245 }
246 }
247 }
248 }
249 }
250 SpuriousResult::new_spurious()
252 }
253
254 fn get_all_possible_variable_assignments(&self) -> Vec<SymbolicVariableAssignment> {
256 let mut possible_assignments = vec![VariableAssignment::new()];
257 for shared in self.variables() {
258 let mut updated_assignments = Vec::new();
259 for assignment in possible_assignments {
260 let intervals = self.get_ordered_intervals_for_shared(shared);
261 for interval in intervals {
262 let mut updated_assignment = assignment.clone();
263 updated_assignment
264 .add_shared_var_interval(shared.clone(), interval.clone())
265 .expect("failed to update variable_assignment");
266 updated_assignments.push(updated_assignment);
267 }
268 }
269 possible_assignments = updated_assignments;
270 }
271
272 possible_assignments
273 .iter()
274 .map(|assign| self.cs.get_sym_var_assignment(assign.clone()))
275 .collect()
276 }
277
278 fn compute_reachable_variable_assignments(&self) -> Vec<SymbolicVariableAssignment> {
280 let possible_assignments = self.get_all_possible_variable_assignments();
281
282 possible_assignments
283 .into_iter()
284 .filter(|assign| {
285 self.explored_states[1..self.explored_states.len()]
286 .iter()
287 .any(|state| state.contains_sym_assignment(assign))
288 })
289 .collect()
290 }
291
292 fn construct_steady_path(
298 &self,
299 sym_state: ZCSStates,
300 assignment: SymbolicVariableAssignment,
301 ) -> SteadyPath {
302 let mut reachable_states = sym_state;
303 let mut applied_rules = Vec::new();
304 let mut reachable_states_changed = true;
305 while reachable_states_changed {
306 reachable_states_changed = false;
307 for rule in self.cs.transitions() {
308 let mut succ = self
309 .cs
310 .compute_successor(reachable_states.clone(), rule.clone());
311 succ = succ.intersect_assignment(&assignment);
313 let (not_empty, non_error_succ) = self.get_non_error_intersection(succ.clone());
315 if not_empty {
316 if !applied_rules.contains(&rule) {
317 applied_rules.push(rule);
318 }
319 let updated_reachable_states = reachable_states.union(&non_error_succ);
320 if !updated_reachable_states.equal(&reachable_states) {
321 reachable_states = updated_reachable_states;
323 reachable_states_changed = true;
324 break;
325 }
326 }
327 }
328 }
329 SteadyPath::new(applied_rules, reachable_states, assignment)
330 }
331
332 pub fn locations(&self) -> impl Iterator<Item = &Location> {
334 self.cs.locations()
335 }
336
337 pub fn variables(&self) -> impl Iterator<Item = &Variable> {
339 self.cs.variables()
340 }
341
342 pub fn get_ordered_intervals_for_shared(&self, shared: &Variable) -> &Vec<Interval> {
344 self.cs.get_ordered_intervals_for_shared(shared)
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use crate::IntervalThresholdAutomaton;
351 use crate::ZCSErrorGraphBuilder;
352 use crate::zcs;
353 use crate::zcs::ZCS;
354 use crate::zcs::ZCSStates;
355 use taco_interval_ta::IntervalTAAction;
356 use taco_interval_ta::IntervalTARule;
357 use taco_interval_ta::builder::IntervalTABuilder;
358 use taco_interval_ta::interval::Interval;
359 use taco_interval_ta::interval::IntervalBoundary;
360 use taco_interval_ta::{IntervalActionEffect, IntervalConstraint};
361 use taco_model_checker::ModelCheckerContext;
362 use taco_model_checker::reachability_specification::TargetConfig;
363 use taco_smt_encoder::SMTSolverBuilder;
364 use taco_threshold_automaton::ParameterConstraint;
365 use taco_threshold_automaton::expressions::fraction::Fraction;
366 use taco_threshold_automaton::lia_threshold_automaton::integer_thresholds::Threshold;
367
368 use crate::ZCSModelCheckerContext;
369 use crate::ZCSModelCheckerHeuristics;
370 use taco_model_checker::SMTBddContext;
371
372 use crate::zcs_error_graph::VariableAssignment;
373 use taco_threshold_automaton::RuleDefinition;
374 use taco_threshold_automaton::expressions::BooleanConnective;
375 use taco_threshold_automaton::expressions::{Location, Variable};
376 use taco_threshold_automaton::lia_threshold_automaton::integer_thresholds::WeightedSum;
377 use taco_threshold_automaton::{
378 BooleanVarConstraint, LocationConstraint,
379 expressions::{ComparisonOp, IntegerExpression, Parameter},
380 general_threshold_automaton::{Action, builder::RuleBuilder},
381 };
382 use zcs::builder::ZCSBuilder;
383
384 fn get_test_ata() -> IntervalThresholdAutomaton {
426 let ta_builder =
427 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
428 .with_locations(vec![
429 Location::new("l0"),
430 Location::new("l1"),
431 Location::new("l2"),
432 ])
433 .unwrap()
434 .with_variables(vec![Variable::new("x"), Variable::new("y")])
435 .unwrap()
436 .with_parameters(vec![
437 Parameter::new("n"),
438 Parameter::new("t"),
439 Parameter::new("f"),
440 ])
441 .unwrap()
442 .initialize()
443 .with_initial_location_constraints(vec![
444 LocationConstraint::ComparisonExpression(
445 Box::new(IntegerExpression::Atom(Location::new("l0"))),
446 ComparisonOp::Eq,
447 Box::new(
448 IntegerExpression::Param(Parameter::new("n"))
449 - IntegerExpression::Param(Parameter::new("t")),
450 ),
451 ),
452 LocationConstraint::ComparisonExpression(
453 Box::new(IntegerExpression::Atom(Location::new("l1"))),
454 ComparisonOp::Eq,
455 Box::new(IntegerExpression::Const(0)),
456 ),
457 LocationConstraint::ComparisonExpression(
458 Box::new(IntegerExpression::Atom(Location::new("l2"))),
459 ComparisonOp::Eq,
460 Box::new(IntegerExpression::Const(0)),
461 ),
462 ])
463 .unwrap()
464 .with_resilience_conditions([ParameterConstraint::ComparisonExpression(
465 Box::new(IntegerExpression::Atom(Parameter::new("n")) - IntegerExpression::Atom(Parameter::new("t"))),
466 ComparisonOp::Gt,
467 Box::new(IntegerExpression::Const(1)),
468 )])
469 .unwrap()
470 .with_initial_variable_constraints([BooleanVarConstraint::ComparisonExpression(Box::new(IntegerExpression::Atom(Variable::new("x"))), ComparisonOp::Eq, Box::new(IntegerExpression::Const(0))), BooleanVarConstraint::ComparisonExpression(Box::new(IntegerExpression::Atom(Variable::new("y"))), ComparisonOp::Eq, Box::new(IntegerExpression::Const(0)))])
471 .unwrap()
472 .with_rules(vec![
473 RuleBuilder::new(0, Location::new("l0"), Location::new("l1"))
474 .with_action(
475 Action::new(
476 Variable::new("x"),
477 IntegerExpression::Atom(Variable::new("x"))
478 + IntegerExpression::Const(1),
479 )
480 .unwrap(),
481 )
482 .build(),
483 RuleBuilder::new(1, Location::new("l0"), Location::new("l2"))
484 .with_guard(
485 BooleanVarConstraint::BinaryExpression(
486 Box::new(BooleanVarConstraint::ComparisonExpression(
487 Box::new(IntegerExpression::Atom(Variable::new("x"))),
488 ComparisonOp::Geq,
489 Box::new(
490 IntegerExpression::Param(Parameter::new("n"))
491 - IntegerExpression::Param(Parameter::new("t")),
492 ))),
493 BooleanConnective::And,
494 Box::new(BooleanVarConstraint::ComparisonExpression(
495 Box::new(IntegerExpression::Atom(Variable::new("y"))),
496 ComparisonOp::Eq,
497 Box::new(IntegerExpression::Const(0)),
498 )
499 )
500 ))
501 .build(),
502 RuleBuilder::new(2, Location::new("l1"), Location::new("l2"))
503 .with_guard(
504 BooleanVarConstraint::BinaryExpression(
505 Box::new(BooleanVarConstraint::ComparisonExpression(
506 Box::new(IntegerExpression::Atom(Variable::new("x"))),
507 ComparisonOp::Geq,
508 Box::new(
509 IntegerExpression::Param(Parameter::new("n"))
510 - IntegerExpression::Param(Parameter::new("t")),
511 ))),
512 BooleanConnective::And,
513 Box::new(BooleanVarConstraint::ComparisonExpression(
514 Box::new(IntegerExpression::Atom(Variable::new("y"))),
515 ComparisonOp::Eq,
516 Box::new(IntegerExpression::Const(0)),
517 )
518 )
519 ))
520 .build(),
521 ])
522 .unwrap();
523 let ta = ta_builder.build();
524 let lia =
525 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
526 ta.clone(),
527 )
528 .unwrap();
529
530 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
531 .build()
532 .unwrap();
533 interval_tas.next().unwrap()
534 }
535
536 fn get_error_states(cs: &ZCS) -> ZCSStates {
537 let l0 = cs.get_sym_state_for_loc(&Location::new("l0"));
538 let l1 = cs.get_sym_state_for_loc(&Location::new("l1"));
539 let l2 = cs.get_sym_state_for_loc(&Location::new("l2"));
540 l0.complement()
541 .intersection(&l1.complement())
542 .intersection(&l2)
543 }
544
545 #[test]
546 fn test_is_empty() {
547 let ata = get_test_ata();
548 let mgr = taco_bdd::BDDManager::default();
549 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
550 let cs = builder.build();
551 let error_states = get_error_states(&cs);
552
553 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
554 let error_graph = error_graph_builder.build();
555
556 assert!(!error_graph.is_empty());
557 }
558
559 #[test]
560 fn test_initial_states() {
561 let ata = get_test_ata();
562 let mgr = taco_bdd::BDDManager::default();
563 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
564 let cs = builder.build();
565 let error_states = get_error_states(&cs);
566
567 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
568 let error_graph = error_graph_builder.build();
569
570 let l0 = error_graph.cs.get_sym_state_for_loc(&Location::new("l0"));
571 let l1 = error_graph.cs.get_sym_state_for_loc(&Location::new("l1"));
572 let l2 = error_graph.cs.get_sym_state_for_loc(&Location::new("l2"));
573
574 let interval_0 =
576 Interval::new_constant(Fraction::new(0, 1, false), Fraction::new(1, 1, false));
577
578 let i0 = error_graph
579 .cs
580 .get_sym_state_for_shared_interval(&Variable::new("x"), &interval_0);
581 let i0 = i0.intersection(
582 &error_graph
583 .cs
584 .get_sym_state_for_shared_interval(&Variable::new("y"), &interval_0),
585 );
586 let abstract_rule_0 = IntervalTARule::new(
588 0,
589 Location::new("l0"),
590 Location::new("l1"),
591 IntervalConstraint::True,
592 vec![IntervalTAAction::new(
593 Variable::new("x"),
594 IntervalActionEffect::Inc(1),
595 )],
596 );
597
598 let r0 = error_graph.cs.get_sym_state_for_rule(&abstract_rule_0);
599
600 let initial_states = l0
601 .intersection(&l1.complement())
602 .intersection(&l2.complement())
603 .intersection(&i0)
604 .intersection(&r0);
605
606 assert!(initial_states.equal(&error_graph.initial_states()));
607 }
608
609 #[test]
610 fn test_get_non_error_intersection_empty() {
611 let ata = get_test_ata();
612 let mgr = taco_bdd::BDDManager::default();
613 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
614 let cs = builder.build();
615 let error_states = get_error_states(&cs);
616
617 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
618 let error_graph = error_graph_builder.build();
619
620 let l0 = error_graph.cs.get_sym_state_for_loc(&Location::new("l0"));
621 let l1 = error_graph.cs.get_sym_state_for_loc(&Location::new("l1"));
622 let l2 = error_graph.cs.get_sym_state_for_loc(&Location::new("l2"));
623 let error_states = l0
624 .complement()
625 .intersection(&l1.complement())
626 .intersection(&l2);
627
628 let res = error_graph.get_non_error_intersection(error_states);
629
630 assert!(!res.0);
631 assert!(res.1.is_empty());
632 }
633
634 #[test]
635 fn test_get_non_error_intersection() {
636 let ata = get_test_ata();
637 let mgr = taco_bdd::BDDManager::default();
638 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
639 let cs = builder.build();
640 let error_states = get_error_states(&cs);
641
642 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
643 let error_graph = error_graph_builder.build();
644
645 let l0 = error_graph.cs.get_sym_state_for_loc(&Location::new("l0"));
646 let l1 = error_graph.cs.get_sym_state_for_loc(&Location::new("l1"));
647 let l2 = error_graph.cs.get_sym_state_for_loc(&Location::new("l2"));
648 let error_states = l0
649 .complement()
650 .intersection(&l1.complement())
651 .intersection(&l2);
652
653 let interval_0 =
655 Interval::new_constant(Fraction::new(0, 1, false), Fraction::new(1, 1, false));
656
657 let i0 = error_graph
658 .cs
659 .get_sym_state_for_shared_interval(&Variable::new("x"), &interval_0);
660
661 let initial_states = l0
662 .intersection(&l1.complement())
663 .intersection(&l2.complement())
664 .intersection(&i0);
665
666 let res = error_graph.get_non_error_intersection(error_states.union(&initial_states));
667
668 assert!(res.0);
669 assert!(res.1.equal(&initial_states));
670 }
671
672 #[test]
673 fn test_get_error_intersection_empty() {
674 let ata = get_test_ata();
675 let mgr = taco_bdd::BDDManager::default();
676 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
677 let cs = builder.build();
678 let error_states = get_error_states(&cs);
679
680 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
681 let error_graph = error_graph_builder.build();
682
683 let l0 = error_graph.cs.get_sym_state_for_loc(&Location::new("l0"));
684
685 let res = error_graph.get_error_intersection(l0);
686
687 assert!(!res.0);
688 assert!(res.1.is_empty());
689 }
690
691 #[test]
692 fn test_get_error_intersection() {
693 let ata = get_test_ata();
694 let mgr = taco_bdd::BDDManager::default();
695 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
696 let cs = builder.build();
697 let error_states = get_error_states(&cs);
698
699 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
700 let error_graph = error_graph_builder.build();
701
702 let l0 = error_graph.cs.get_sym_state_for_loc(&Location::new("l0"));
703 let l1 = error_graph.cs.get_sym_state_for_loc(&Location::new("l1"));
704 let l2 = error_graph.cs.get_sym_state_for_loc(&Location::new("l2"));
705
706 let interval_2 = Interval::new(
708 IntervalBoundary::Bound(Threshold::new(
709 [
710 (Parameter::new("n"), 1.into()),
711 (Parameter::new("t"), Fraction::new(1, 1, true)),
712 ],
713 0,
714 )),
715 false,
716 IntervalBoundary::new_infty(),
717 true,
718 );
719
720 let i2 = error_graph
721 .cs
722 .get_sym_state_for_shared_interval(&Variable::new("x"), &interval_2);
723
724 let error_states = l0
725 .complement()
726 .intersection(&l1.complement())
727 .intersection(&l2)
728 .intersection(&i2);
729
730 let builder = ZCSErrorGraphBuilder::new(error_graph.cs.clone(), error_states.clone());
731 let error_graph = builder.build();
732
733 let abstract_rule_0 = IntervalTARule::new(
735 0,
736 Location::new("l0"),
737 Location::new("l1"),
738 IntervalConstraint::True,
739 vec![IntervalTAAction::new(
740 Variable::new("x"),
741 IntervalActionEffect::Inc(1),
742 )],
743 );
744
745 let r0 = error_graph.cs.get_sym_state_for_rule(&abstract_rule_0);
746
747 let res = error_graph
748 .get_error_intersection(l0.clone().union(&l2).intersection(&i2).intersection(&r0));
749
750 assert!(res.0);
751 assert!(
752 res.1.equal(
753 &l0.complement()
754 .intersection(&l1.complement())
755 .intersection(&l2)
756 .intersection(&i2)
757 .intersection(&r0)
758 )
759 );
760 }
761
762 #[test]
763 fn test_contains_non_spurious_error_path() {
764 let mgr = taco_bdd::BDDManager::default();
765 let ata = get_test_ata();
766 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
767 let cs = builder.build();
768 let error_states = get_error_states(&cs);
769
770 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
771 let sym_err_graph = error_graph_builder.build();
772
773 let spec = TargetConfig::new_reach(
774 [Location::new("l2")],
775 vec![Location::new("l0"), Location::new("l1")],
776 )
777 .unwrap()
778 .into_disjunct_with_name("test");
779
780 let ctx = SMTBddContext::try_new(None).unwrap();
781 let smc_ctx = ZCSModelCheckerContext::new(
782 &ctx,
783 ZCSModelCheckerHeuristics::DecrementAndIncrementHeuristics,
784 &ata,
785 &spec,
786 );
787
788 let res = sym_err_graph.contains_non_spurious_error_path(&smc_ctx);
789
790 assert!(res.is_non_spurious());
791 }
792
793 #[test]
794 fn test_get_sym_state_for_loc() {
795 let ata = get_test_ata();
796 let mgr = taco_bdd::BDDManager::default();
797 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
798 let cs = builder.build();
799 let error_states = get_error_states(&cs);
800
801 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
802 let error_graph = error_graph_builder.build();
803
804 let l0 = error_graph.cs.get_sym_state_for_loc(&Location::new("l0"));
805 let l1 = error_graph.cs.get_sym_state_for_loc(&Location::new("l1"));
806 let l2 = error_graph.cs.get_sym_state_for_loc(&Location::new("l2"));
807
808 assert!(
809 error_graph
810 .get_sym_state_for_loc(&Location::new("l0"))
811 .equal(&l0)
812 );
813 assert!(
814 error_graph
815 .get_sym_state_for_loc(&Location::new("l1"))
816 .equal(&l1)
817 );
818 assert!(
819 error_graph
820 .get_sym_state_for_loc(&Location::new("l2"))
821 .equal(&l2)
822 );
823 }
824
825 #[test]
826 fn test_get_sym_state_for_shared_interval() {
827 let ata = get_test_ata();
828 let mgr = taco_bdd::BDDManager::default();
829 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
830 let cs = builder.build();
831 let error_states = get_error_states(&cs);
832
833 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
834 let error_graph = error_graph_builder.build();
835
836 let interval_0 =
838 Interval::new_constant(Fraction::new(0, 1, false), Fraction::new(1, 1, false));
839 let i0 = error_graph
840 .cs
841 .get_sym_state_for_shared_interval(&Variable::new("x"), &interval_0);
842
843 assert!(
844 error_graph
845 .get_sym_state_for_shared_interval(&Variable::new("x"), &interval_0)
846 .equal(&i0)
847 );
848 }
849
850 #[test]
851 fn test_get_sym_state_for_rule() {
852 let ata = get_test_ata();
853 let mgr = taco_bdd::BDDManager::default();
854 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
855 let cs = builder.build();
856 let error_states = get_error_states(&cs);
857
858 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
859 let error_graph = error_graph_builder.build();
860
861 let abstract_rule_0 = IntervalTARule::new(
863 0,
864 Location::new("l0"),
865 Location::new("l1"),
866 IntervalConstraint::True,
867 vec![IntervalTAAction::new(
868 Variable::new("x"),
869 IntervalActionEffect::Inc(1),
870 )],
871 );
872
873 let r0 = error_graph.cs.get_sym_state_for_rule(&abstract_rule_0);
874
875 assert!(
876 error_graph
877 .cs
878 .get_sym_state_for_rule(&abstract_rule_0)
879 .equal(&r0)
880 );
881 }
882
883 #[test]
884 fn test_transitions() {
885 let ata = get_test_ata();
886 let mgr = taco_bdd::BDDManager::default();
887 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
888 let cs = builder.build();
889 let error_states = get_error_states(&cs);
890 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
891 let error_graph = error_graph_builder.build();
892
893 let transitions = error_graph
894 .transitions()
895 .map(|r| r.abstract_rule().id())
896 .collect::<Vec<_>>();
897 assert_eq!(transitions.len(), 3);
898 assert!(transitions.contains(&0));
899 assert!(transitions.contains(&1));
900 assert!(transitions.contains(&2));
901 }
902
903 #[test]
904 fn test_new_empty_sym_state() {
905 let ata = get_test_ata();
906 let mgr = taco_bdd::BDDManager::default();
907 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
908 let cs = builder.build();
909 let error_states = get_error_states(&cs);
910
911 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
912 let error_graph = error_graph_builder.build();
913
914 let empty_state = error_graph.new_empty_sym_state();
915
916 assert!(empty_state.is_empty());
917 }
918
919 #[test]
920 fn test_compute_reachable_variable_assignments() {
921 let ata = get_test_ata();
922 let mgr = taco_bdd::BDDManager::default();
923 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
924 let cs = builder.build();
925 let error_states = get_error_states(&cs);
926
927 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
928 let error_graph = error_graph_builder.build();
929
930 let assignments = error_graph.compute_reachable_variable_assignments();
931
932 assert_eq!(assignments.len(), 3);
937
938 let interval_0 =
940 Interval::new_constant(Fraction::new(0, 1, false), Fraction::new(1, 1, false));
941
942 let interval_2 = Interval::new(
944 IntervalBoundary::Bound(Threshold::new(
945 [
946 (Parameter::new("n"), 1.into()),
947 (Parameter::new("t"), Fraction::new(1, 1, true)),
948 ],
949 0,
950 )),
951 false,
952 IntervalBoundary::new_infty(),
953 true,
954 );
955 let mut assign2 = VariableAssignment::new();
956 assign2
957 .add_shared_var_interval(Variable::new("x"), interval_2.clone())
958 .unwrap();
959
960 let mut reached_i_0 = false;
961 let mut reached_i_1 = false;
962 let mut reached_i_2 = false;
963 for assign in assignments {
964 for (var, interval) in assign.assignment().assignments() {
965 if *var == Variable::new("y") {
966 assert_eq!(interval, &interval_0);
967 }
968 if *var == Variable::new("x") {
969 if interval == &interval_0 {
970 reached_i_0 = true;
971 } else if interval == &interval_2 {
972 reached_i_2 = true;
973 } else {
974 reached_i_1 = true;
975 }
976 }
977 }
978 }
979 assert!(reached_i_0);
980 assert!(reached_i_1);
981 assert!(reached_i_2);
982
983 let interval_1 = Interval::new(
985 IntervalBoundary::new_bound(WeightedSum::new_empty(), Fraction::new(1, 1, false)),
986 false,
987 IntervalBoundary::new_infty(),
988 true,
989 );
990
991 let all_possible_assignments = error_graph.get_all_possible_variable_assignments();
992 assert_eq!(all_possible_assignments.len(), 6);
993 assert!(all_possible_assignments.iter().any(|assign| {
994 assign
995 .assignment()
996 .assignments()
997 .any(|(var, interval)| *var == Variable::new("y") && *interval == interval_1)
998 }));
999 }
1000
1001 #[test]
1002 fn test_construct_steady_path() {
1003 let ata = get_test_ata();
1004 let mgr = taco_bdd::BDDManager::default();
1005 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
1006 let cs = builder.build();
1007 let error_states = get_error_states(&cs);
1008
1009 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
1010 let error_graph = error_graph_builder.build();
1011
1012 let interval_0 =
1014 Interval::new_constant(Fraction::new(0, 1, false), Fraction::new(1, 1, false));
1015 let mut assignment = VariableAssignment::new();
1016 assignment
1017 .add_shared_var_interval(Variable::new("x"), interval_0.clone())
1018 .unwrap();
1019 assignment
1020 .add_shared_var_interval(Variable::new("y"), interval_0.clone())
1021 .unwrap();
1022 let assignment = error_graph.cs.get_sym_var_assignment(assignment);
1023
1024 let initial_states = error_graph.initial_states();
1025
1026 let steady_path =
1027 error_graph.construct_steady_path(initial_states.clone(), assignment.clone());
1028
1029 println!("{:?}", steady_path._reachable_states());
1030 println!("{:?}", initial_states);
1031
1032 assert!(steady_path._reachable_states().equal(&initial_states));
1033 assert!(
1034 steady_path
1035 ._reachable_states()
1036 .remove_assignment(&assignment)
1037 .is_empty()
1038 );
1039 }
1040
1041 fn get_test_cycle_ata() -> IntervalThresholdAutomaton {
1056 let ta_builder =
1057 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
1058 .with_locations(vec![
1059 Location::new("l0"),
1060 Location::new("l1"),
1061 Location::new("l2"),
1062 Location::new("l3"),
1063 ])
1064 .unwrap()
1065 .with_variables(vec![Variable::new("x"), Variable::new("y")])
1066 .unwrap()
1067 .with_parameters(vec![
1068 Parameter::new("n"),
1069 ])
1070 .unwrap()
1071 .initialize()
1072 .with_initial_location_constraints(vec![
1073 LocationConstraint::ComparisonExpression(
1074 Box::new(IntegerExpression::Atom(Location::new("l0"))),
1075 ComparisonOp::Eq,
1076 Box::new(
1077 IntegerExpression::Param(Parameter::new("n"))
1078 ),
1079 ),
1080 LocationConstraint::ComparisonExpression(
1081 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1082 ComparisonOp::Eq,
1083 Box::new(IntegerExpression::Const(0)),
1084 ),
1085 LocationConstraint::ComparisonExpression(
1086 Box::new(IntegerExpression::Atom(Location::new("l2"))),
1087 ComparisonOp::Eq,
1088 Box::new(IntegerExpression::Const(0)),
1089 ),
1090 LocationConstraint::ComparisonExpression(
1091 Box::new(IntegerExpression::Atom(Location::new("l3"))),
1092 ComparisonOp::Eq,
1093 Box::new(IntegerExpression::Const(0)),
1094 ),
1095 ])
1096 .unwrap()
1097 .with_resilience_conditions([ParameterConstraint::ComparisonExpression(
1098 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1099 ComparisonOp::Gt,
1100 Box::new(IntegerExpression::Const(1)),
1101 )])
1102 .unwrap()
1103 .with_initial_variable_constraints([BooleanVarConstraint::ComparisonExpression(Box::new(IntegerExpression::Atom(Variable::new("x"))), ComparisonOp::Eq, Box::new(IntegerExpression::Const(0))), BooleanVarConstraint::ComparisonExpression(Box::new(IntegerExpression::Atom(Variable::new("y"))), ComparisonOp::Eq, Box::new(IntegerExpression::Const(0)))])
1104 .unwrap()
1105 .with_rules(vec![
1106 RuleBuilder::new(0, Location::new("l0"), Location::new("l1"))
1107 .with_guard(
1108 BooleanVarConstraint::ComparisonExpression(
1109 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1110 ComparisonOp::Lt,
1111 Box::new(IntegerExpression::Const(1)),
1112 )
1113 )
1114 .with_action(
1115 Action::new(
1116 Variable::new("x"),
1117 IntegerExpression::Atom(Variable::new("x"))
1118 + IntegerExpression::Const(1),
1119 )
1120 .unwrap(),
1121 )
1122 .build(),
1123 RuleBuilder::new(1, Location::new("l1"), Location::new("l2"))
1124 .build(),
1125 RuleBuilder::new(2, Location::new("l2"), Location::new("l1"))
1126 .with_action(
1127 Action::new(
1128 Variable::new("y"),
1129 IntegerExpression::Atom(Variable::new("y"))
1130 + IntegerExpression::Const(1),
1131 )
1132 .unwrap(),
1133 )
1134 .build(),
1135 RuleBuilder::new(3, Location::new("l0"), Location::new("l3"))
1136 .with_guard(
1137 BooleanVarConstraint::ComparisonExpression(
1138 Box::new(IntegerExpression::Atom(Variable::new("y"))),
1139 ComparisonOp::Geq,
1140 Box::new(IntegerExpression::Const(5)),
1141 )
1142 )
1143 .build(),
1144 ])
1145 .unwrap();
1146 let ta = ta_builder.build();
1147 let lia =
1148 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
1149 ta.clone(),
1150 )
1151 .unwrap();
1152
1153 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
1154 .build()
1155 .unwrap();
1156 interval_tas.next().unwrap()
1157 }
1158
1159 #[test]
1160 fn test_cycle_ta_for_spuriousness() {
1161 let ata = get_test_cycle_ata();
1162 let mgr = taco_bdd::BDDManager::default();
1163 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
1164 let cs = builder.build();
1165 let l3 = cs.get_sym_state_for_loc(&Location::new("l3"));
1166 let error_states = l3;
1168
1169 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
1170 let sym_err_graph = error_graph_builder.build();
1171
1172 let spec = TargetConfig::new_cover([Location::new("l3")])
1173 .unwrap()
1174 .into_disjunct_with_name("test");
1175
1176 let ctx = SMTBddContext::try_new(None).unwrap();
1177 let smc_ctx = ZCSModelCheckerContext::new(
1178 &ctx,
1179 ZCSModelCheckerHeuristics::DecrementAndIncrementHeuristics,
1180 &ata,
1181 &spec,
1182 );
1183
1184 let res = sym_err_graph.contains_non_spurious_error_path(&smc_ctx);
1185
1186 assert!(res.is_non_spurious());
1187 }
1188}