1use std::cmp::min;
6use std::fmt::{Debug, Display};
7use std::rc::Rc;
8use taco_display_utils::join_iterator;
9use taco_threshold_automaton::lia_threshold_automaton::integer_thresholds::IntoNoDivBooleanExpr;
10
11use easy_smt::Response::Sat;
12use std::collections::HashMap;
13use taco_interval_ta::IntervalThresholdAutomaton;
14use taco_interval_ta::interval::Interval;
15use taco_model_checker::reachability_specification::DisjunctionTargetConfig;
16use taco_smt_encoder::SMTSolverBuilder;
17use taco_smt_encoder::expression_encoding::DeclaresVariable;
18use taco_smt_encoder::expression_encoding::EncodeToSMT;
19use taco_smt_encoder::expression_encoding::config_ctx::{ConfigCtx, ConfigFromSMT};
20use taco_smt_encoder::expression_encoding::{GetAssignment, SMTSolverError, SMTVariableContext};
21use taco_smt_encoder::{SMTExpr, SMTSolverContext};
22use taco_smt_encoder::{SMTSolution, SMTSolver};
23use taco_threshold_automaton::ActionDefinition;
24use taco_threshold_automaton::RuleDefinition;
25use taco_threshold_automaton::ThresholdAutomaton;
26use taco_threshold_automaton::VariableConstraint;
27use taco_threshold_automaton::expressions::BooleanExpression;
28use taco_threshold_automaton::expressions::{Location, Parameter, Variable};
29use taco_threshold_automaton::general_threshold_automaton::Rule;
30use taco_threshold_automaton::general_threshold_automaton::UpdateExpression;
31use taco_threshold_automaton::general_threshold_automaton::UpdateExpression::*;
32use taco_threshold_automaton::path::PathBuilder;
33use taco_threshold_automaton::path::{Configuration, Path, Transition};
34
35use crate::paths::{SteadyErrorPath, SteadyPath};
36
37pub struct SMTEncoderSteady<'a> {
41 ta: &'a IntervalThresholdAutomaton,
43 rule_counter_to_smt: Vec<(Rule, SMTExpr)>,
45 ctx: IndexedSMTContext,
47 steady_error_path: &'a SteadyErrorPath<'a>,
49}
50impl<'a> SMTEncoderSteady<'a> {
51 pub fn new(
55 smt: SMTSolverBuilder,
56 aut: &'a IntervalThresholdAutomaton,
57 steady_error_path: &'a SteadyErrorPath<'a>,
58 ) -> Self {
59 let indexed_ctx = IndexedSMTContext::new(
61 smt,
62 (steady_error_path.length_configurations() * 2)
65 .try_into()
66 .unwrap(),
67 aut,
68 );
69
70 let mut encoder = SMTEncoderSteady {
71 rule_counter_to_smt: Vec::new(),
72 ctx: indexed_ctx,
73 ta: aut,
74 steady_error_path,
75 };
76
77 encoder.initialize_rule_counters(steady_error_path.concrete_rules_ordered(aut));
79
80 let rc_cond = encoder.encode_resilience_conditions(aut.resilience_conditions());
82 encoder
83 .ctx
84 .smt_solver
85 .assert(rc_cond)
86 .expect("error for asserting resilience condition");
87
88 let loc_cond = encoder.encode_loc_conditions(aut.initial_location_constraints());
89 encoder
90 .ctx
91 .smt_solver
92 .assert(loc_cond)
93 .expect("error for asserting initial location condition");
94
95 let var_cond = aut
96 .initial_variable_constraints()
97 .map(|constr| constr.as_boolean_expr())
98 .collect::<Vec<_>>();
99 let var_cond = encoder.encode_var_conditions(var_cond.iter());
100 encoder
101 .ctx
102 .smt_solver
103 .assert(var_cond)
104 .expect("error for asserting initial variable condition");
105
106 encoder
107 }
108
109 fn initialize_rule_counters<'b, I>(&mut self, rules_it: I)
112 where
113 I: Iterator<Item = &'b Rule>,
114 {
115 for (i, rule) in rules_it.enumerate() {
116 let rule_smt = rule
117 .declare_variable(&mut self.ctx.smt_solver, i.try_into().unwrap())
118 .expect("Error creating shared smt variable");
119 self.rule_counter_to_smt.push((rule.clone(), rule_smt));
120 }
121 }
122
123 fn encode_resilience_conditions<'b, I>(&mut self, rc: I) -> SMTExpr
125 where
126 I: Iterator<Item = &'b BooleanExpression<Parameter>>,
127 {
128 self.ctx.set_curr_index(0);
129 let mut rc_encoding = self.ctx.smt_solver.true_();
130
131 for param_constr in rc {
132 let encoding = param_constr
133 .encode_to_smt_with_ctx(&self.ctx.smt_solver, &self.ctx)
134 .expect("error constructing the resilience condition");
135 rc_encoding = self.ctx.smt_solver.and(rc_encoding, encoding);
136 }
137
138 rc_encoding
139 }
140
141 fn encode_loc_conditions<'b, I>(&mut self, cond: I) -> SMTExpr
143 where
144 I: Iterator<Item = &'b BooleanExpression<Location>>,
145 {
146 self.ctx.set_curr_index(0);
147 let mut initial_loc_encoding = self.ctx.smt_solver.true_();
148
149 for loc_constr in cond {
150 let loc_encoding = loc_constr
151 .encode_to_smt_with_ctx(&self.ctx.smt_solver, self.ctx.get_current_config_ctx())
152 .expect("error constructing the initial location condition");
153 initial_loc_encoding = self.ctx.smt_solver.and(initial_loc_encoding, loc_encoding);
154 }
155
156 initial_loc_encoding
157 }
158
159 fn encode_var_conditions<'b, I>(&mut self, cond: I) -> SMTExpr
161 where
162 I: Iterator<Item = &'b BooleanExpression<Variable>>,
163 {
164 self.ctx.set_curr_index(0);
165 let mut initial_var_encoding = self.ctx.smt_solver.true_();
166
167 for var_constr in cond {
168 let var_encoding = var_constr
169 .encode_to_smt_with_ctx(&self.ctx.smt_solver, self.ctx.get_current_config_ctx())
170 .expect("error constructing the initial shared variable condition");
171 initial_var_encoding = self.ctx.smt_solver.and(initial_var_encoding, var_encoding);
172 }
173
174 initial_var_encoding
175 }
176
177 pub fn steady_is_non_spurious(
190 &mut self,
191 spec: Option<&DisjunctionTargetConfig>,
192 ) -> SpuriousResult {
193 for param_smt in self.ctx.param_to_smt.values() {
195 let param_encoding = self
196 .ctx
197 .smt_solver
198 .gte(*param_smt, self.ctx.smt_solver.numeral(0));
199 self.ctx
200 .smt_solver
201 .assert(param_encoding)
202 .expect("error for asserting a non-negative indexed location");
203 }
204 for loc in self.ta.locations() {
205 for i in 0..self.steady_error_path.length_configurations() * 2 {
206 let loc_smt = self.ctx.get_indexed_loc_smt(loc, i as usize);
207 let loc_encoding = self
208 .ctx
209 .smt_solver
210 .gte(loc_smt, self.ctx.smt_solver.numeral(0));
211 self.ctx
212 .smt_solver
213 .assert(loc_encoding)
214 .expect("error for asserting a non-negative indexed location");
215 }
216 }
217 for (_, rule_counter) in self.rule_counter_to_smt.iter() {
218 let rule_encoding = self
219 .ctx
220 .smt_solver
221 .gte(*rule_counter, self.ctx.smt_solver.numeral(0));
222 self.ctx
223 .smt_solver
224 .assert(rule_encoding)
225 .expect("error for asserting a non-negative rule counter");
226 }
227
228 let abs = self.encode_steady_error_path();
230 self.ctx
231 .smt_solver
232 .assert(abs)
233 .expect("error for asserting encoded abstract path");
234
235 let mut from_index: u32 = 0; let mut rule_index = 0;
238 for path in self.steady_error_path.steady_paths() {
239 self.ctx
240 .smt_solver
241 .assert(self.encode_steady_path(from_index, rule_index, path))
242 .expect("error for asserting steady path encoding");
243 from_index += 2; rule_index += path.length_transitions();
245 if rule_index < self.rule_counter_to_smt.len() as u32 {
246 self.ctx
247 .smt_solver
248 .assert(self.encode_step_transition(from_index, rule_index))
249 .expect("error for asserting step encoding");
250 rule_index += 1; }
252 }
253
254 if let Some(specification) = spec {
256 self.ctx
257 .smt_solver
258 .assert(self.encode_error_states(specification))
259 .expect("error for asserting error states");
260 }
261 let response = self
263 .ctx
264 .smt_solver
265 .check()
266 .expect("Error checking smt encoding");
267 if response == Sat {
268 if spec.is_some() {
270 let p = self
272 .extract_non_spurious_concrete_path(SMTSolution::SAT)
273 .expect("error extracting error path");
274 return SpuriousResult::new(true, Some(p));
275 }
276 return SpuriousResult::new(true, None);
277 }
278 SpuriousResult::new(false, None)
280 }
281
282 pub fn encode_step_transition(&self, from_index: u32, rule_index: u32) -> SMTExpr {
288 let last_config_index = from_index - 1;
289 let next_config_index = from_index;
290 let (rule, rule_smt) = &self.rule_counter_to_smt[rule_index as usize];
291
292 let mut step_encoding = self
293 .ctx
294 .smt_solver
295 .eq(*rule_smt, self.ctx.smt_solver.numeral(1));
296
297 let from_location = rule.source();
299 let to_location = rule.target();
300
301 let from_loc_smt = self
302 .ctx
303 .get_indexed_loc_smt(from_location, last_config_index as usize);
304 let to_loc_smt = self
305 .ctx
306 .get_indexed_loc_smt(to_location, last_config_index as usize);
307 let next_from_loc_smt = self
308 .ctx
309 .get_indexed_loc_smt(from_location, next_config_index as usize);
310 let next_to_loc_smt = self
311 .ctx
312 .get_indexed_loc_smt(to_location, next_config_index as usize);
313 if from_location == to_location {
314 step_encoding = self.ctx.smt_solver.and(
316 step_encoding,
317 self.ctx.smt_solver.eq(next_from_loc_smt, from_loc_smt),
318 );
319 step_encoding = self.ctx.smt_solver.and(
320 step_encoding,
321 self.ctx
322 .smt_solver
323 .gt(from_loc_smt, self.ctx.smt_solver.numeral(0)),
324 );
325 } else {
326 step_encoding = self.ctx.smt_solver.and(
328 step_encoding,
329 self.ctx.smt_solver.eq(
330 next_from_loc_smt,
331 self.ctx.smt_solver.sub(from_loc_smt, *rule_smt),
332 ),
333 );
334
335 step_encoding = self.ctx.smt_solver.and(
337 step_encoding,
338 self.ctx.smt_solver.eq(
339 next_to_loc_smt,
340 self.ctx.smt_solver.plus(to_loc_smt, *rule_smt),
341 ),
342 );
343 }
344
345 for loc in self.ta.locations() {
347 if loc != from_location && loc != to_location {
348 let loc_smt = self
349 .ctx
350 .get_indexed_loc_smt(loc, last_config_index as usize);
351 let next_loc_smt = self
352 .ctx
353 .get_indexed_loc_smt(loc, next_config_index as usize);
354 step_encoding = self
355 .ctx
356 .smt_solver
357 .and(step_encoding, self.ctx.smt_solver.eq(next_loc_smt, loc_smt));
358 }
359 }
360
361 let action_map = rule
363 .actions()
364 .map(|action| (action.variable(), action.update().clone()))
365 .collect::<HashMap<_, _>>();
366
367 for shared_var in self.ta.variables() {
368 let shared_smt = self
369 .ctx
370 .get_indexed_shared_smt(shared_var, last_config_index as usize);
371 let next_shared_smt = self
372 .ctx
373 .get_indexed_shared_smt(shared_var, next_config_index as usize);
374 let update = action_map
375 .get(shared_var)
376 .unwrap_or(&UpdateExpression::Unchanged);
377 step_encoding = self.ctx.smt_solver.and(
378 step_encoding,
379 self.encode_update_shared_var(&shared_smt, &next_shared_smt, *rule_smt, update),
380 );
381 }
382
383 step_encoding
384 }
385
386 fn encode_update_shared_var(
394 &self,
395 shared_smt: &SMTExpr,
396 next_shared_smt: &SMTExpr,
397 rule_smt: SMTExpr,
398 update: &UpdateExpression,
399 ) -> SMTExpr {
400 match update {
401 Inc(x) => self.ctx.smt_solver.eq(
403 *next_shared_smt,
404 self.ctx.smt_solver.plus(
405 *shared_smt,
406 self.ctx
407 .smt_solver
408 .times(rule_smt, self.ctx.smt_solver.numeral(*x)),
409 ),
410 ),
411 Dec(x) => self.ctx.smt_solver.eq(
413 *next_shared_smt,
414 self.ctx.smt_solver.sub(
415 *shared_smt,
416 self.ctx
417 .smt_solver
418 .times(rule_smt, self.ctx.smt_solver.numeral(*x)),
419 ),
420 ),
421 UpdateExpression::Reset => self
423 .ctx
424 .smt_solver
425 .eq(*next_shared_smt, self.ctx.smt_solver.numeral(0)),
426 UpdateExpression::Unchanged => self.ctx.smt_solver.eq(*next_shared_smt, *shared_smt),
428 }
429 }
430
431 fn encode_steady_error_path(&mut self) -> SMTExpr {
435 let mut encoding = self.ctx.smt_solver.true_();
436 let mut index = 0;
437 for steady_path in self.steady_error_path.steady_paths() {
438 for (shared, interval) in steady_path
439 .variable_assignment()
440 .assignments()
441 .filter(|(v, _)| !self.ta.get_helper_vars_for_sumvars().contains_key(*v))
442 {
444 self.ctx.set_curr_index(index);
445 encoding = self.ctx.smt_solver.and(
446 encoding,
447 self.encode_shared_interval(shared.clone(), interval.clone()),
448 );
449 self.ctx.set_curr_index(index + 1);
450 encoding = self.ctx.smt_solver.and(
451 encoding,
452 self.encode_shared_interval(shared.clone(), interval.clone()),
453 );
454 }
455 for (shared, interval) in self.ta.get_helper_vars_for_sumvars().iter().map(|(v, sv)| {
458 let interval = steady_path
459 .variable_assignment()
460 .assignments()
461 .find(|(var, _)| *var == v)
462 .expect("All variables should have an interval assignment")
463 .1;
464 (sv, interval)
465 }) {
466 self.ctx.set_curr_index(index);
467 encoding = self.ctx.smt_solver.and(
468 encoding,
469 self.encode_shared_interval(shared.clone(), interval.clone()),
470 );
471 self.ctx.set_curr_index(index + 1);
472 encoding = self.ctx.smt_solver.and(
473 encoding,
474 self.encode_shared_interval(shared.clone(), interval.clone()),
475 );
476 }
477
478 index += 2;
479 }
480
481 encoding
482 }
483
484 fn encode_shared_interval<S>(&self, shared: S, interval: Interval) -> SMTExpr
487 where
488 S: IntoNoDivBooleanExpr<Variable> + Clone,
489 {
490 let interval_expression = interval.encode_into_boolean_expr(&shared);
491 interval_expression
492 .encode_to_smt_with_ctx(&self.ctx.smt_solver, self.ctx.get_current_config_ctx())
493 .expect("error encoding interval expression")
494 }
495
496 fn encode_error_states(&self, spec: &DisjunctionTargetConfig) -> SMTExpr {
501 spec.encode_to_smt_with_ctx(&self.ctx.smt_solver, self.ctx.config_ctxs.last().unwrap())
503 .expect("Failed to encode error conditions")
504 }
505
506 fn encode_steady_path(
517 &self,
518 from_index: u32,
519 rule_index: u32,
520 steady_path: &SteadyPath,
521 ) -> SMTExpr {
522 let mut steady_path_encoding =
523 self.encode_steady_path_move_processes(from_index, rule_index, steady_path);
524 steady_path_encoding = self.ctx.smt_solver.and(
525 steady_path_encoding,
526 self.encode_steady_path_update_shared_variables(from_index, rule_index, steady_path),
527 );
528 steady_path_encoding = self.ctx.smt_solver.and(
529 steady_path_encoding,
530 self.encode_steady_path_fireable_chain(from_index, rule_index, steady_path),
531 );
532 if self.ta.has_decrements() {
533 steady_path_encoding = self.ctx.smt_solver.and(
534 steady_path_encoding,
535 self.encode_monotonicity_constraints(rule_index, steady_path),
536 );
537 }
538 steady_path_encoding
539 }
540
541 fn encode_steady_path_move_processes(
550 &self,
551 from_index: u32,
552 rule_index: u32,
553 steady_path: &SteadyPath,
554 ) -> SMTExpr {
555 let mut move_encoding = self.ctx.smt_solver.true_();
556 for loc in self.ta.locations() {
557 let loc_smt = self.ctx.get_indexed_loc_smt(loc, from_index as usize);
558 let next_loc_smt = self.ctx.get_indexed_loc_smt(loc, (from_index + 1) as usize);
559 let mut rhs = loc_smt;
560 for i in 0..steady_path.length_transitions() {
561 let (rule, rule_smt) = self.rule_counter_to_smt[(rule_index + i) as usize].clone();
562 if rule.target() == rule.source() {
563 continue;
565 }
566 if rule.target() == loc {
567 rhs = self.ctx.smt_solver.plus(rhs, rule_smt);
569 }
570 if rule.source() == loc {
571 rhs = self.ctx.smt_solver.sub(rhs, rule_smt);
573 }
574 }
575 move_encoding = self
576 .ctx
577 .smt_solver
578 .and(move_encoding, self.ctx.smt_solver.eq(next_loc_smt, rhs));
579 }
580
581 move_encoding
582 }
583
584 fn encode_steady_path_update_shared_variables(
596 &self,
597 from_index: u32,
598 rule_index: u32,
599 steady_path: &SteadyPath,
600 ) -> SMTExpr {
601 let mut update_encoding = self.ctx.smt_solver.true_();
602 for shared in self.ta.variables() {
603 let shared_smt = self.ctx.get_indexed_shared_smt(shared, from_index as usize);
604 let mut reset_shared = None;
605 let next_shared_smt = self
606 .ctx
607 .get_indexed_shared_smt(shared, (from_index + 1) as usize);
608 let mut rhs = shared_smt;
609 for i in 0..steady_path.length_transitions() {
610 let (rule, rule_smt) = self.rule_counter_to_smt[(rule_index + i) as usize].clone();
611 let action_map = rule
612 .actions()
613 .map(|action| (action.variable(), action.update().clone()))
614 .collect::<HashMap<_, _>>();
615 let update = action_map
616 .get(shared)
617 .unwrap_or(&UpdateExpression::Unchanged);
618 match update {
619 Inc(x) => {
620 rhs = self.ctx.smt_solver.plus(
622 rhs,
623 self.ctx
624 .smt_solver
625 .times(rule_smt, self.ctx.smt_solver.numeral(*x)),
626 );
627 }
628 Dec(x) => {
629 rhs = self.ctx.smt_solver.sub(
631 rhs,
632 self.ctx
633 .smt_solver
634 .times(rule_smt, self.ctx.smt_solver.numeral(*x)),
635 );
636 }
637 UpdateExpression::Reset => {
639 reset_shared = Some(
640 self.ctx
641 .smt_solver
642 .eq(next_shared_smt, self.ctx.smt_solver.numeral(0)),
643 );
644 }
645 UpdateExpression::Unchanged => {
646 continue;
647 }
648 }
649 }
650 update_encoding = self.ctx.smt_solver.and(
651 update_encoding,
652 self.ctx.smt_solver.eq(next_shared_smt, rhs),
653 );
654 if let Some(reset_encoding) = reset_shared {
655 update_encoding = self.ctx.smt_solver.and(update_encoding, reset_encoding);
656 }
657 }
658 update_encoding
659 }
660
661 fn encode_steady_path_fireable_chain(
672 &self,
673 from_index: u32,
674 rule_index: u32,
675 steady_path: &SteadyPath,
676 ) -> SMTExpr {
677 if rule_index == self.rule_counter_to_smt.len() as u32
678 || steady_path.length_transitions() == 0
679 {
680 return self.ctx.smt_solver.true_();
682 }
683 let vector_rules = self.rule_counter_to_smt
684 [(rule_index as usize)..((rule_index + steady_path.length_transitions()) as usize)]
685 .to_vec();
686 let vector_cloned = vector_rules
687 .iter()
688 .map(|(rule, rule_smt)| (rule, rule_smt))
689 .collect::<Vec<_>>();
690 self.ctx
691 .smt_solver
692 .and_many(vector_rules.clone().into_iter().map(|(rule, rule_smt)| {
693 let xr_gt_0 = self
695 .ctx
696 .smt_solver
697 .gt(rule_smt, self.ctx.smt_solver.numeral(0));
698
699 let all_possible_s =
701 self.compute_s_for_r_for_steady_path(&vector_cloned, &rule, &rule_smt);
702
703 if all_possible_s.is_empty() {
705 return self.ctx.smt_solver.false_();
706 }
707
708 let disj_s = self.ctx.smt_solver.or_many(
710 all_possible_s
711 .iter()
712 .map(|s| self.encode_phi_chain_for_steady_path(from_index, s)),
713 );
714
715 self.ctx.smt_solver.imp(xr_gt_0, disj_s)
717 }))
718 }
719
720 fn encode_monotonicity_constraints(
732 &self,
733 rule_index: u32,
734 steady_path: &SteadyPath,
735 ) -> SMTExpr {
736 let mut monotonicity_encoding_constraints = Vec::new();
737 for shared in self.ta.variables() {
738 let mut inc_rules = Vec::new();
739 let mut dec_rules = Vec::new();
740 for i in 0..steady_path.length_transitions() {
741 let (rule, rule_smt) = self.rule_counter_to_smt[(rule_index + i) as usize].clone();
742 let action_map = rule
743 .actions()
744 .map(|action| (action.variable(), action.update().clone()))
745 .collect::<HashMap<_, _>>();
746 let update = action_map
747 .get(shared)
748 .unwrap_or(&UpdateExpression::Unchanged);
749 match update {
750 Inc(_) => inc_rules.push(rule_smt),
751 Dec(_) => dec_rules.push(rule_smt),
752 UpdateExpression::Reset | UpdateExpression::Unchanged => {}
753 }
754 }
755 if inc_rules.is_empty() || dec_rules.is_empty() {
756 continue;
758 } else {
759 let sum_inc = self.ctx.smt_solver.plus_many(inc_rules.iter().cloned());
760 let sum_dec = self.ctx.smt_solver.plus_many(dec_rules.iter().cloned());
761 let inc_eq_0 = self
762 .ctx
763 .smt_solver
764 .eq(sum_inc, self.ctx.smt_solver.numeral(0));
765 let dec_eq_0 = self
766 .ctx
767 .smt_solver
768 .eq(sum_dec, self.ctx.smt_solver.numeral(0));
769 monotonicity_encoding_constraints.push(self.ctx.smt_solver.or(inc_eq_0, dec_eq_0));
771 }
772 }
773 if monotonicity_encoding_constraints.is_empty() {
774 self.ctx.smt_solver.true_()
776 } else {
777 self.ctx
778 .smt_solver
779 .and_many(monotonicity_encoding_constraints)
780 }
781 }
782
783 fn encode_phi_chain_for_steady_path(
795 &self,
796 from_index: u32,
797 s: &[(&Rule, &SMTExpr)],
798 ) -> SMTExpr {
799 if s.is_empty() {
800 return self.ctx.smt_solver.true_();
801 }
802
803 let loc = s.last().unwrap().0.source(); let loc_var = self.ctx.get_indexed_loc_smt(loc, from_index as usize);
806 let loc_gt_0 = self
807 .ctx
808 .smt_solver
809 .gt(loc_var, self.ctx.smt_solver.numeral(0));
810
811 self.ctx.smt_solver.and_many(
812 s.iter()
813 .map(|(_, rule_smt)| {
814 self.ctx
815 .smt_solver
816 .gt(**rule_smt, self.ctx.smt_solver.numeral(0))
817 })
818 .chain([loc_gt_0]),
819 )
820 }
821
822 fn compute_s_for_r_for_steady_path(
837 &'a self,
838 vector_rules: &'a Vec<(&'a Rule, &'a SMTExpr)>,
839 r: &'a Rule,
840 rule_smt: &'a SMTExpr,
841 ) -> Vec<Vec<(&'a Rule, &'a SMTExpr)>> {
842 let loc = r.source();
843 Self::compute_s_with_backtracking_recursive_for_steady_path(
844 loc,
845 vector_rules,
846 vec![(r, rule_smt)],
847 )
848 }
849
850 fn compute_s_with_backtracking_recursive_for_steady_path(
860 loc: &'a Location,
861 vector_rules: &[(&'a Rule, &'a SMTExpr)],
862 rules_applied: Vec<(&'a Rule, &'a SMTExpr)>,
863 ) -> Vec<Vec<(&'a Rule, &'a SMTExpr)>> {
864 let mut s = vector_rules
866 .iter()
867 .filter(|r| !rules_applied.contains(r) && r.0.target() == loc)
868 .flat_map(|r| {
869 let loc = r.0.source();
870 let mut rules_applied: Vec<_> = rules_applied.clone();
871 rules_applied.push(*r);
872
873 Self::compute_s_with_backtracking_recursive_for_steady_path(
874 loc,
875 vector_rules,
876 rules_applied,
877 )
878 })
879 .collect::<Vec<_>>();
880
881 s.push(rules_applied);
883
884 s
885 }
886
887 fn extract_non_spurious_concrete_path(
889 &mut self,
890 res: SMTSolution,
891 ) -> Result<Path, SMTEncoderError> {
892 let path_builder = PathBuilder::new(self.ta.get_ta().clone());
894
895 let mut param_assignment = HashMap::<Parameter, u32>::new();
897 for param in self.ta.parameters() {
898 if !res.is_sat() {
899 return Err(SMTEncoderError::NoSatParamAssign(param.clone()));
900 }
901
902 let param_value = self
903 .ctx
904 .get_assignment(res, param)
905 .expect("error getting assignment for parameter");
906 match param_value {
907 Some(value) => {
908 param_assignment.insert(param.clone(), value.try_into().unwrap());
909 }
910 None => {
911 return Err(SMTEncoderError::NoSatParamAssign(param.clone()));
912 }
913 }
914 }
915
916 let mut initialized_path_builder = path_builder
917 .add_parameter_assignment(param_assignment)
918 .expect("error adding parameter assignment");
919
920 let configs = self.ctx.get_assigned_configs(res);
921
922 let mut concrete_configs = Vec::from([configs[0].clone()]);
923 let mut concrete_transitions = vec![];
924
925 let mut config_index = 0;
926 let mut rule_index = 0;
927 for path in self.steady_error_path.steady_paths() {
928 let (transitions, updated_configs) = self.extract_steady_path(
929 res,
930 path,
931 &configs,
932 concrete_configs.last().unwrap().clone(),
933 config_index,
934 rule_index,
935 )?;
936 concrete_configs.extend(updated_configs);
937 concrete_transitions.extend(transitions);
938
939 config_index += 2; rule_index += path.length_transitions();
941 if config_index < configs.len() as u32 {
942 let (config, transition) =
944 self.extract_step_path(res, &configs, config_index, rule_index)?;
945 concrete_configs.push(config);
946 concrete_transitions.push(transition);
947 rule_index += 1; }
949 }
950
951 initialized_path_builder = initialized_path_builder
952 .add_configurations(concrete_configs)
953 .expect("error adding configurations to path builder");
954 initialized_path_builder = initialized_path_builder
955 .add_transitions(concrete_transitions)
956 .expect("error adding transitions to path builder");
957
958 Ok(initialized_path_builder
959 .build()
960 .expect("error building the path"))
961 }
962
963 fn extract_step_path(
965 &mut self,
966 res: SMTSolution,
967 configurations: &[Configuration],
968 config_index: u32,
969 rule_index: u32,
970 ) -> Result<(Configuration, Transition), SMTEncoderError> {
971 let config = configurations[config_index as usize].clone();
973
974 let rule_counter = self.rule_counter_to_smt[rule_index as usize].clone();
976 let counter_value = self
977 .get_rule_counter_assignment(res, &rule_counter.1)
978 .expect("error getting rule counter assignment");
979
980 match counter_value {
981 Some(value) => {
982 assert!(
983 value == 1,
984 "Step transition should be applied exactly once, but was applied {value} times!"
985 );
986 let transition = Transition::new(rule_counter.0, value.try_into().unwrap());
987 Ok((config, transition))
988 }
989 None => Err(SMTEncoderError::NoSatRuleCounterAssign(
990 rule_counter.0.clone(),
991 rule_index,
992 )),
993 }
994 }
995
996 fn extract_steady_path(
998 &mut self,
999 res: SMTSolution,
1000 steady_path: &SteadyPath,
1001 configurations: &[Configuration],
1002 last_config: Configuration,
1003 config_index: u32,
1004 rule_index: u32,
1005 ) -> Result<(Vec<Transition>, Vec<Configuration>), SMTEncoderError> {
1006 let mut rules = Vec::<(Rule, u32)>::new();
1008 for i in 0..steady_path.length_transitions() {
1009 let index = (i + rule_index) as usize;
1010 let rule_counter = self.rule_counter_to_smt[index].clone();
1011 let counter_value = self
1012 .get_rule_counter_assignment(res, &rule_counter.1)
1013 .expect("error getting rule counter assignment");
1014 match counter_value {
1015 Some(value) => {
1016 rules.push((rule_counter.0, value.try_into().unwrap()));
1017 }
1018 None => {
1019 return Err(SMTEncoderError::NoSatRuleCounterAssign(
1020 rule_counter.0.clone(),
1021 index.try_into().unwrap(),
1022 ));
1023 }
1024 }
1025 }
1026
1027 rules = rules
1028 .into_iter()
1029 .filter(|(_, n)| *n > 0)
1030 .collect::<Vec<_>>();
1031
1032 let mut curr_config = last_config.clone();
1033
1034 let mut concrete_configs = vec![];
1035 let mut concrete_transitions = vec![];
1036
1037 let old_rules = rules.clone();
1038 let old_config = curr_config.clone();
1039
1040 while !rules.is_empty() {
1042 let mut n_apply = 0;
1043
1044 for (rule, n_to_apply) in rules.iter_mut().filter(|(r, _)| r.source() == r.target()) {
1046 if curr_config.get_processes_in_location(rule.source()) > 0 {
1047 n_apply = *n_to_apply;
1049 *n_to_apply = 0;
1050
1051 let (transition, config) = Self::extract_transition_and_updated_config(
1052 curr_config.clone(),
1053 rule.clone(),
1054 n_apply,
1055 );
1056 curr_config = config;
1057 concrete_configs.push(curr_config.clone());
1058 concrete_transitions.push(transition);
1059 }
1060 }
1061
1062 for (rule, n_to_apply) in rules.iter_mut().filter(|(_, n)| *n > 0) {
1063 if curr_config.get_processes_in_location(rule.source()) > 0 {
1065 n_apply = min(
1067 curr_config.get_processes_in_location(rule.source()),
1068 *n_to_apply,
1069 );
1070
1071 *n_to_apply -= n_apply;
1072
1073 let (transition, config) = Self::extract_transition_and_updated_config(
1074 curr_config,
1075 rule.clone(),
1076 n_apply,
1077 );
1078 curr_config = config;
1079 concrete_configs.push(curr_config.clone());
1080 concrete_transitions.push(transition);
1081
1082 break;
1085 }
1086 }
1087
1088 assert!(
1089 n_apply > 0,
1090 "Failed to order rules into chainable order! Configuration that rules must be applied on:\n{}\n\nRules left to apply:\n{}\n\nSet of rules to be ordered in this steady step:\n{}\n\nInitial configuration of the steady step:\n{}\n\n ",
1091 curr_config,
1092 join_iterator(rules.iter().map(|(r, i)| format!("{i} x {r}")), ",\n"),
1093 join_iterator(old_rules.iter().map(|(r, i)| format!("{i} x {r}")), ",\n"),
1094 old_config,
1095 );
1096
1097 rules.retain(|(_, n)| *n > 0);
1098 }
1099
1100 let i = config_index + 1;
1102
1103 let correct_config = configurations[i as usize].clone();
1104
1105 if correct_config != last_config {
1106 assert!(concrete_configs.last().unwrap().clone() == correct_config);
1107 }
1108
1109 Ok((concrete_transitions, concrete_configs))
1110 }
1111
1112 fn extract_transition_and_updated_config(
1115 config: Configuration,
1116 rule: Rule,
1117 n: u32,
1118 ) -> (Transition, Configuration) {
1119 let transition = Transition::new(rule, n);
1120 let curr_config = config
1121 .apply_rule(&transition)
1122 .expect("Constructed Transition invalid!");
1123
1124 (transition, curr_config)
1125 }
1126
1127 fn get_rule_counter_assignment(
1129 &mut self,
1130 res: SMTSolution,
1131 expr: &SMTExpr,
1132 ) -> Result<Option<u64>, SMTSolverError> {
1133 match res {
1134 SMTSolution::SAT => {
1135 let solver = self.ctx.get_smt_solver_mut();
1136
1137 let solution = solver.get_value(vec![*expr])?;
1138 debug_assert!(solution.len() == 1);
1139 debug_assert!(solution[0].0 == *expr);
1140
1141 let solution_val = solver.get(solution[0].1);
1142
1143 Ok(Some(u64::try_from(solution_val).unwrap()))
1144 }
1145 SMTSolution::UNSAT => Ok(None),
1146 }
1147 }
1148}
1149
1150pub struct SpuriousResult {
1152 is_non_spurious: bool,
1154 non_spurious_path: Option<Path>,
1156}
1157impl SpuriousResult {
1158 pub fn new(is_non_spurious: bool, non_spurious_path: Option<Path>) -> Self {
1160 SpuriousResult {
1161 is_non_spurious,
1162 non_spurious_path,
1163 }
1164 }
1165 pub fn new_spurious() -> Self {
1167 SpuriousResult {
1168 is_non_spurious: false,
1169 non_spurious_path: None,
1170 }
1171 }
1172 pub fn is_non_spurious(&self) -> bool {
1174 self.is_non_spurious
1175 }
1176
1177 pub fn non_spurious_path(&self) -> Option<&Path> {
1179 self.non_spurious_path.as_ref()
1180 }
1181}
1182
1183#[derive(Debug, Clone)]
1186#[allow(clippy::enum_variant_names)]
1187enum SMTEncoderError {
1188 NoSatParamAssign(Parameter),
1190 NoSatRuleCounterAssign(Rule, u32),
1192}
1193
1194impl std::error::Error for SMTEncoderError {}
1195
1196impl Display for SMTEncoderError {
1197 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1198 match self {
1199 SMTEncoderError::NoSatParamAssign(param) => write!(
1200 f,
1201 "There exists no satisfying assignment for parameter {param}"
1202 ),
1203 SMTEncoderError::NoSatRuleCounterAssign(rule, i) => write!(
1204 f,
1205 "There exists no satisfying assignment for rule {rule} at index {i}",
1206 ),
1207 }
1208 }
1209}
1210
1211struct IndexedSMTContext {
1216 smt_solver: SMTSolver,
1218 config_ctxs: Vec<ConfigCtx>,
1220 param_to_smt: Rc<HashMap<Parameter, SMTExpr>>,
1222 curr_index: usize,
1224}
1225impl IndexedSMTContext {
1226 fn new<T: ThresholdAutomaton>(solver_builder: SMTSolverBuilder, len: usize, ta: &T) -> Self {
1230 let mut solver = solver_builder.new_solver();
1231
1232 let param_to_smt = ta
1234 .parameters()
1235 .map(|p| {
1236 let smt = p
1237 .declare_variable(&mut solver, 0)
1238 .expect("Failed to declare parameter");
1239 (p.clone(), smt)
1240 })
1241 .collect::<HashMap<_, _>>();
1242 let param_to_smt = Rc::new(param_to_smt);
1243
1244 let config_ctxs = (0..len)
1245 .map(|index| ConfigCtx::new(&mut solver, ta, param_to_smt.clone(), index))
1246 .collect();
1247
1248 IndexedSMTContext {
1249 smt_solver: solver,
1250 config_ctxs,
1251 param_to_smt,
1252 curr_index: 0,
1253 }
1254 }
1255
1256 fn set_curr_index(&mut self, i: usize) {
1258 self.curr_index = i
1259 }
1260
1261 fn get_current_config_ctx(&self) -> &ConfigCtx {
1264 &self.config_ctxs[self.curr_index]
1265 }
1266 fn get_indexed_shared_smt(&self, var: &Variable, i: usize) -> SMTExpr {
1269 self.config_ctxs[i]
1270 .get_expr_for(var)
1271 .expect("Failed to encode variable")
1272 }
1273 fn get_indexed_loc_smt(&self, loc: &Location, i: usize) -> SMTExpr {
1276 self.config_ctxs[i]
1277 .get_expr_for(loc)
1278 .expect("Failed to encode location")
1279 }
1280
1281 fn get_assigned_configs(&mut self, res: SMTSolution) -> Vec<Configuration> {
1283 self.config_ctxs
1284 .iter()
1285 .map(|cfg_ctx| {
1286 cfg_ctx
1287 .get_assigned_configuration(&mut self.smt_solver, res)
1288 .expect("Failed to extract configuration assignment")
1289 })
1290 .collect()
1291 }
1292}
1293
1294impl SMTSolverContext for IndexedSMTContext {
1295 fn get_smt_solver(&self) -> &SMTSolver {
1296 &self.smt_solver
1297 }
1298
1299 fn get_smt_solver_mut(&mut self) -> &mut SMTSolver {
1300 &mut self.smt_solver
1301 }
1302}
1303
1304impl SMTVariableContext<Parameter> for IndexedSMTContext {
1305 fn get_expr_for(&self, param: &Parameter) -> Result<SMTExpr, SMTSolverError> {
1306 self.param_to_smt
1307 .get(param)
1308 .cloned()
1309 .ok_or_else(|| SMTSolverError::UndeclaredParameter(param.clone()))
1310 }
1311
1312 fn get_exprs<'a>(&'a self) -> impl IntoIterator<Item = &'a Parameter>
1313 where
1314 Parameter: 'a,
1315 {
1316 self.param_to_smt.keys()
1317 }
1318}
1319impl GetAssignment<Parameter> for IndexedSMTContext {}
1320
1321struct _ReplacingContext {
1324 var_to_smt: HashMap<Variable, SMTExpr>,
1325 param: Rc<HashMap<Parameter, SMTExpr>>,
1326}
1327
1328impl _ReplacingContext {
1329 fn _new(
1331 var_to_smt: HashMap<Variable, SMTExpr>,
1332 param: Rc<HashMap<Parameter, SMTExpr>>,
1333 ) -> Self {
1334 Self { var_to_smt, param }
1335 }
1336}
1337
1338impl SMTVariableContext<Variable> for _ReplacingContext {
1339 fn get_expr_for(&self, expr: &Variable) -> Result<SMTExpr, SMTSolverError> {
1340 self.var_to_smt
1341 .get(expr)
1342 .cloned()
1343 .ok_or_else(|| SMTSolverError::UndeclaredVariable(expr.clone()))
1344 }
1345
1346 fn get_exprs<'a>(&'a self) -> impl IntoIterator<Item = &'a Variable>
1347 where
1348 Variable: 'a,
1349 {
1350 self.var_to_smt.keys()
1351 }
1352}
1353
1354impl SMTVariableContext<Parameter> for _ReplacingContext {
1355 fn get_expr_for(&self, expr: &Parameter) -> Result<SMTExpr, SMTSolverError> {
1356 self.param
1357 .get(expr)
1358 .cloned()
1359 .ok_or_else(|| SMTSolverError::UndeclaredParameter(expr.clone()))
1360 }
1361
1362 fn get_exprs<'a>(&'a self) -> impl IntoIterator<Item = &'a Parameter>
1363 where
1364 Parameter: 'a,
1365 {
1366 self.param.keys()
1367 }
1368}
1369
1370#[cfg(test)]
1371mod tests {
1372 use crate::paths::SteadyErrorPath;
1373 use crate::paths::SteadyPath;
1374 use crate::paths::VariableAssignment;
1375 use crate::zcs::SymbolicVariableAssignment;
1376 use crate::zcs::builder::ZCSBuilder;
1377 use crate::zcs_error_graph::ZCSErrorGraph;
1378 use crate::zcs_error_graph::builder::ZCSErrorGraphBuilder;
1379 use taco_interval_ta::builder::IntervalTABuilder;
1380
1381 use taco_interval_ta::IntervalThresholdAutomaton;
1382 use taco_smt_encoder::SMTSolverBuilder;
1383
1384 use taco_threshold_automaton::BooleanVarConstraint;
1385 use taco_threshold_automaton::LocationConstraint;
1386 use taco_threshold_automaton::ParameterConstraint;
1387
1388 use crate::ZCSStates;
1389 use crate::smt_encoder_steady::SMTEncoderError;
1390 use crate::smt_encoder_steady::SMTEncoderSteady;
1391 use crate::smt_encoder_steady::SMTSolution;
1392 use crate::zcs::SymbolicTransition;
1393 use std::collections::HashMap;
1394 use std::collections::HashSet;
1395 use taco_interval_ta::interval::Interval;
1396 use taco_interval_ta::interval::IntervalBoundary;
1397 use taco_model_checker::reachability_specification::TargetConfig;
1398 use taco_threshold_automaton::RuleDefinition;
1399 use taco_threshold_automaton::ThresholdAutomaton;
1400 use taco_threshold_automaton::expressions::ComparisonOp;
1401 use taco_threshold_automaton::expressions::IntegerExpression;
1402 use taco_threshold_automaton::expressions::fraction::Fraction;
1403 use taco_threshold_automaton::expressions::{Location, Parameter, Variable};
1404 use taco_threshold_automaton::general_threshold_automaton::Action;
1405 use taco_threshold_automaton::general_threshold_automaton::UpdateExpression;
1406 use taco_threshold_automaton::general_threshold_automaton::builder::RuleBuilder;
1407 use taco_threshold_automaton::lia_threshold_automaton::integer_thresholds::Threshold;
1408
1409 fn get_test_automaton() -> IntervalThresholdAutomaton {
1436 let ta_builder =
1437 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
1438 .with_locations(vec![
1439 Location::new("l0"),
1440 Location::new("l1"),
1441 Location::new("l2"),
1442 ])
1443 .unwrap()
1444 .with_variable(Variable::new("x"))
1445 .unwrap()
1446 .with_parameters(vec![
1447 Parameter::new("n"),
1448 Parameter::new("t"),
1449 Parameter::new("f"),
1450 ])
1451 .unwrap()
1452 .initialize()
1453 .with_resilience_conditions(vec![
1454 ParameterConstraint::ComparisonExpression(
1455 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1456 ComparisonOp::Gt,
1457 Box::new(
1458 IntegerExpression::Const(3)
1459 * IntegerExpression::Param(Parameter::new("t")),
1460 ),
1461 ),
1462 ParameterConstraint::ComparisonExpression(
1463 Box::new(IntegerExpression::Atom(Parameter::new("t"))),
1464 ComparisonOp::Geq,
1465 Box::new(IntegerExpression::Param(Parameter::new("f"))),
1466 ),
1467 ParameterConstraint::ComparisonExpression(
1468 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1469 ComparisonOp::Gt,
1470 Box::new(IntegerExpression::Const(0)),
1471 ),
1472 ParameterConstraint::ComparisonExpression(
1473 Box::new(IntegerExpression::Atom(Parameter::new("t"))),
1474 ComparisonOp::Geq,
1475 Box::new(IntegerExpression::Const(0)),
1476 ),
1477 ParameterConstraint::ComparisonExpression(
1478 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
1479 ComparisonOp::Geq,
1480 Box::new(IntegerExpression::Const(0)),
1481 ),
1482 ParameterConstraint::ComparisonExpression(
1483 Box::new(IntegerExpression::Atom(Parameter::new("n")) - IntegerExpression::Atom(Parameter::new("t"))),
1484 ComparisonOp::Gt,
1485 Box::new(IntegerExpression::Const(1)),
1486 ),
1487 ])
1488 .unwrap()
1489 .with_initial_location_constraints(vec![
1490 LocationConstraint::ComparisonExpression(
1491 Box::new(IntegerExpression::Atom(Location::new("l0"))),
1492 ComparisonOp::Eq,
1493 Box::new(
1494 IntegerExpression::Param(Parameter::new("n"))
1495 - IntegerExpression::Param(Parameter::new("t")),
1496 ),
1497 ),
1498 LocationConstraint::ComparisonExpression(
1499 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1500 ComparisonOp::Eq,
1501 Box::new(IntegerExpression::Const(0)),
1502 ),
1503 LocationConstraint::ComparisonExpression(
1504 Box::new(IntegerExpression::Atom(Location::new("l2"))),
1505 ComparisonOp::Eq,
1506 Box::new(IntegerExpression::Const(0)),
1507 ),
1508 ])
1509 .unwrap()
1510 .with_rules(vec![
1511 RuleBuilder::new(0, Location::new("l0"), Location::new("l1"))
1512 .with_action(
1513 Action::new(
1514 Variable::new("x"),
1515 IntegerExpression::Atom(Variable::new("x"))
1516 + IntegerExpression::Const(1),
1517 )
1518 .unwrap(),
1519 )
1520 .build(),
1521 RuleBuilder::new(1, Location::new("l0"), Location::new("l1"))
1522 .with_action(
1523 Action::new(
1524 Variable::new("x"),
1525 IntegerExpression::Atom(Variable::new("x"))
1526 - IntegerExpression::Const(1),
1527 )
1528 .unwrap(),
1529 )
1530 .build(),
1531 RuleBuilder::new(2, Location::new("l0"), Location::new("l1"))
1532 .with_action(Action::new_reset(Variable::new("x")))
1533 .build(),
1534 RuleBuilder::new(3, Location::new("l1"), Location::new("l2"))
1535 .with_guard(BooleanVarConstraint::ComparisonExpression(
1536 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1537 ComparisonOp::Geq,
1538 Box::new(
1539 IntegerExpression::Param(Parameter::new("n"))
1540 - IntegerExpression::Param(Parameter::new("t")),
1541 ),
1542 ))
1543 .build(),
1544 RuleBuilder::new(4, Location::new("l1"), Location::new("l2"))
1545 .with_guard(BooleanVarConstraint::ComparisonExpression(
1546 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1547 ComparisonOp::Geq,
1548 Box::new(
1549 IntegerExpression::Param(Parameter::new("n"))
1550 - IntegerExpression::Param(Parameter::new("t")),
1551 ),
1552 ))
1553 .with_action(
1554 Action::new(
1555 Variable::new("x"),
1556 IntegerExpression::Atom(Variable::new("x"))
1557 + IntegerExpression::Const(1),
1558 )
1559 .unwrap(),
1560 )
1561 .build(),
1562 ])
1563 .unwrap();
1564 let ta = ta_builder.build();
1565 let lia =
1566 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
1567 ta.clone(),
1568 )
1569 .unwrap();
1570
1571 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
1572 .build()
1573 .unwrap();
1574 let ata = interval_tas.next().unwrap();
1575
1576 let nxt = interval_tas.next();
1577 assert!(nxt.is_none(), "Got additional ta {}", nxt.unwrap());
1578 ata
1579 }
1580
1581 fn get_test_error_graph(ata: &'_ IntervalThresholdAutomaton) -> ZCSErrorGraph<'_> {
1582 let mgr = taco_bdd::BDDManager::default();
1583 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, ata);
1584 let cs = builder.build();
1585
1586 let l0 = cs.get_sym_state_for_loc(&Location::new("l0"));
1587 let l1 = cs.get_sym_state_for_loc(&Location::new("l1"));
1588 let l2 = cs.get_sym_state_for_loc(&Location::new("l2"));
1589 let error_states = l0
1590 .complement()
1591 .intersection(&l1.complement())
1592 .intersection(&l2);
1593
1594 let error_graph_builder = ZCSErrorGraphBuilder::new(cs, error_states);
1595 error_graph_builder.build()
1596 }
1597
1598 fn get_test_steady_error_path<'a>(
1602 aut: &'a IntervalThresholdAutomaton,
1603 error_graph: &'a ZCSErrorGraph,
1604 ) -> SteadyErrorPath<'a> {
1605 let full_state = error_graph.new_empty_sym_state().complement();
1606 let true_bdd = full_state.set_of_states();
1607 let idle_sym_state = ZCSStates::new(true_bdd.clone());
1608
1609 let r_0 = aut.rules().find(|r| r.id() == 0).unwrap();
1610 let r_1 = aut.rules().find(|r| r.id() == 1).unwrap();
1611 let r_2 = aut.rules().find(|r| r.id() == 2).unwrap();
1612 let r_3 = aut.rules().find(|r| r.id() == 3).unwrap();
1613 let r_4 = aut.rules().find(|r| r.id() == 4).unwrap();
1614
1615 let interval_0 = Interval::new(
1617 IntervalBoundary::from_const(0),
1618 false,
1619 IntervalBoundary::from_const(1),
1620 true,
1621 );
1622
1623 let interval_1 = Interval::new(
1625 IntervalBoundary::from_const(1),
1626 false,
1627 IntervalBoundary::Bound(Threshold::new(
1628 [
1629 (Parameter::new("n"), 1.into()),
1630 (Parameter::new("t"), Fraction::new(1, 1, true)),
1631 ],
1632 0,
1633 )),
1634 true,
1635 );
1636 let interval_2 = Interval::new(
1638 IntervalBoundary::Bound(Threshold::new(
1639 [
1640 (Parameter::new("n"), 1.into()),
1641 (Parameter::new("t"), Fraction::new(1, 1, true)),
1642 ],
1643 0,
1644 )),
1645 false,
1646 IntervalBoundary::new_infty(),
1647 true,
1648 );
1649
1650 let intial_assignment = VariableAssignment::new_for_testing(HashMap::from([(
1651 Variable::new("x"),
1652 interval_0.clone(),
1653 )]));
1654 let sym_initial_assignment =
1655 SymbolicVariableAssignment::new(true_bdd.clone(), intial_assignment);
1656 let mut steady_error_path = SteadyErrorPath::new(
1657 SteadyPath::new(
1658 vec![&SymbolicTransition::new(true_bdd.clone(), r_0.clone())],
1659 idle_sym_state.clone(),
1660 sym_initial_assignment,
1661 ),
1662 error_graph,
1663 );
1664
1665 let sym_assignment = SymbolicVariableAssignment::new(
1666 true_bdd.clone(),
1667 VariableAssignment::new_for_testing(HashMap::from([(
1668 Variable::new("x"),
1669 interval_1.clone(),
1670 )])),
1671 );
1672 steady_error_path.add_successor(
1673 &SymbolicTransition::new(true_bdd.clone(), r_0.clone()),
1674 SteadyPath::new(
1675 vec![&SymbolicTransition::new(true_bdd.clone(), r_0.clone())],
1676 idle_sym_state.clone(),
1677 sym_assignment.clone(),
1678 ),
1679 );
1680
1681 steady_error_path.add_successor(
1682 &SymbolicTransition::new(true_bdd.clone(), r_1.clone()),
1683 SteadyPath::new(
1684 vec![&SymbolicTransition::new(true_bdd.clone(), r_0.clone())],
1685 idle_sym_state.clone(),
1686 sym_assignment.clone(),
1687 ),
1688 );
1689
1690 let sym_assignment = SymbolicVariableAssignment::new(
1691 true_bdd.clone(),
1692 VariableAssignment::new_for_testing(HashMap::from([(
1693 Variable::new("x"),
1694 interval_2.clone(),
1695 )])),
1696 );
1697
1698 steady_error_path.add_successor(
1699 &SymbolicTransition::new(true_bdd.clone(), r_0.clone()),
1700 SteadyPath::new(
1701 vec![&SymbolicTransition::new(true_bdd.clone(), r_0.clone())],
1702 idle_sym_state.clone(),
1703 sym_assignment.clone(),
1704 ),
1705 );
1706
1707 steady_error_path.add_successor(
1708 &SymbolicTransition::new(true_bdd.clone(), r_3.clone()),
1709 SteadyPath::new(vec![], idle_sym_state.clone(), sym_assignment),
1710 );
1711
1712 let sym_assignment = SymbolicVariableAssignment::new(
1713 true_bdd.clone(),
1714 VariableAssignment::new_for_testing(HashMap::from([(
1715 Variable::new("x"),
1716 interval_0.clone(),
1717 )])),
1718 );
1719
1720 steady_error_path.add_successor(
1721 &SymbolicTransition::new(true_bdd.clone(), r_2.clone()),
1722 SteadyPath::new(vec![], idle_sym_state.clone(), sym_assignment.clone()),
1723 );
1724
1725 steady_error_path.add_successor(
1726 &SymbolicTransition::new(true_bdd.clone(), r_4.clone()),
1727 SteadyPath::new(vec![], idle_sym_state, sym_assignment),
1728 );
1729
1730 steady_error_path
1731 }
1732
1733 fn get_initialized_test_smt_encoder<'a>(
1740 aut: &'a IntervalThresholdAutomaton,
1741 path: &'a SteadyErrorPath,
1742 ) -> SMTEncoderSteady<'a> {
1743 let solver = SMTSolverBuilder::default();
1744 SMTEncoderSteady::new(solver, aut, path)
1745 }
1746
1747 fn get_test_steady_error_path_one_step<'a>(
1750 aut: &'a IntervalThresholdAutomaton,
1751 error_graph: &'a ZCSErrorGraph,
1752 ) -> SteadyErrorPath<'a> {
1753 let full_state = error_graph.new_empty_sym_state().complement();
1754 let true_bdd = full_state.set_of_states();
1755 let idle_sym_state = ZCSStates::new(true_bdd.clone());
1756
1757 let r_0 = aut.rules().find(|r| r.id() == 0).unwrap();
1758
1759 let interval_0 = Interval::new(
1761 IntervalBoundary::from_const(0),
1762 false,
1763 IntervalBoundary::from_const(1),
1764 true,
1765 );
1766
1767 let interval_1 = Interval::new(
1769 IntervalBoundary::from_const(1),
1770 false,
1771 IntervalBoundary::Bound(Threshold::new(
1772 [
1773 (Parameter::new("n"), 1.into()),
1774 (Parameter::new("t"), Fraction::new(1, 1, true)),
1775 ],
1776 0,
1777 )),
1778 true,
1779 );
1780
1781 let intial_assignment = VariableAssignment::new_for_testing(HashMap::from([(
1782 Variable::new("x"),
1783 interval_0.clone(),
1784 )]));
1785 let sym_initial_assignment =
1786 SymbolicVariableAssignment::new(true_bdd.clone(), intial_assignment);
1787 let mut steady_error_path = SteadyErrorPath::new(
1788 SteadyPath::new(
1789 vec![&SymbolicTransition::new(true_bdd.clone(), r_0.clone())],
1790 idle_sym_state.clone(),
1791 sym_initial_assignment,
1792 ),
1793 error_graph,
1794 );
1795
1796 let sym_assignment = SymbolicVariableAssignment::new(
1797 true_bdd.clone(),
1798 VariableAssignment::new_for_testing(HashMap::from([(
1799 Variable::new("x"),
1800 interval_1.clone(),
1801 )])),
1802 );
1803 steady_error_path.add_successor(
1804 &SymbolicTransition::new(true_bdd.clone(), r_0.clone()),
1805 SteadyPath::new(
1806 vec![&SymbolicTransition::new(true_bdd.clone(), r_0.clone())],
1807 idle_sym_state.clone(),
1808 sym_assignment.clone(),
1809 ),
1810 );
1811
1812 steady_error_path
1813 }
1814
1815 #[test]
1816 fn test_new_rule_counter() {
1817 let solver = SMTSolverBuilder::default();
1818
1819 let r_0 = RuleBuilder::new(8, Location::new("l_0"), Location::new("l_1")).build();
1820 let r_1 = RuleBuilder::new(9, Location::new("l_1"), Location::new("l_0")).build();
1821
1822 let aut = get_test_automaton();
1823 let error_graph = get_test_error_graph(&aut);
1824 let path = get_test_steady_error_path(&aut, &error_graph);
1825 let mut encoder = SMTEncoderSteady::new(solver, &aut, &path);
1826
1827 let rules = vec![&r_0, &r_1, &r_0];
1828 encoder.rule_counter_to_smt.clear();
1829 encoder.initialize_rule_counters(rules.into_iter());
1830
1831 let r_0_0 = encoder.rule_counter_to_smt[0].1;
1832 let r_1_1 = encoder.rule_counter_to_smt[1].1;
1833 let r_0_2 = encoder.rule_counter_to_smt[2].1;
1834
1835 assert_eq!(
1836 format!("{}", encoder.ctx.smt_solver.display(r_0_0)),
1837 "rule_8_0"
1838 );
1839 assert_eq!(
1840 format!("{}", encoder.ctx.smt_solver.display(r_1_1)),
1841 "rule_9_1"
1842 );
1843 assert_eq!(
1844 format!("{}", encoder.ctx.smt_solver.display(r_0_2)),
1845 "rule_8_2"
1846 );
1847 }
1848
1849 #[test]
1850 fn test_initialize_rule_counters() {
1851 let solver = SMTSolverBuilder::default();
1852
1853 let r_0 = RuleBuilder::new(8, Location::new("l_0"), Location::new("l_1")).build();
1854 let r_1 = RuleBuilder::new(9, Location::new("l_1"), Location::new("l_0")).build();
1855
1856 let aut = get_test_automaton();
1857 let error_graph = get_test_error_graph(&aut);
1858 let path = get_test_steady_error_path(&aut, &error_graph);
1859 let mut encoder = SMTEncoderSteady::new(solver, &aut, &path);
1860
1861 let vec = vec![&r_0, &r_1, &r_0];
1862
1863 encoder.rule_counter_to_smt.clear();
1864
1865 encoder.initialize_rule_counters(vec.into_iter());
1866
1867 assert_eq!(
1868 format!(
1869 "{}",
1870 encoder
1871 .ctx
1872 .smt_solver
1873 .display(encoder.rule_counter_to_smt.first().unwrap().1)
1874 ),
1875 "rule_8_0"
1876 );
1877 assert_eq!(
1878 format!(
1879 "{}",
1880 encoder
1881 .ctx
1882 .smt_solver
1883 .display(encoder.rule_counter_to_smt.get(1).unwrap().1)
1884 ),
1885 "rule_9_1"
1886 );
1887 assert_eq!(
1888 format!(
1889 "{}",
1890 encoder
1891 .ctx
1892 .smt_solver
1893 .display(encoder.rule_counter_to_smt.get(2).unwrap().1)
1894 ),
1895 "rule_8_2"
1896 );
1897 }
1898
1899 #[test]
1900 fn test_encode_step_transition() {
1901 let aut = get_test_automaton();
1902 let error_graph = get_test_error_graph(&aut);
1903 let path = get_test_steady_error_path(&aut, &error_graph);
1904
1905 let encoder = get_initialized_test_smt_encoder(&aut, &path);
1906 let encoding = encoder.encode_step_transition(2, 1);
1908
1909 let smt = format!("{}", encoder.ctx.smt_solver.display(encoding));
1910
1911 assert!(smt.contains("(= rule_0_1 1)"));
1912 assert!(smt.contains("(= loc_l0_2 (- loc_l0_1 rule_0_1))"));
1913 assert!(smt.contains("(= loc_l1_2 (+ loc_l1_1 rule_0_1))"));
1914 assert!(smt.contains("(= loc_l2_2 loc_l2_1)"));
1915 assert!(smt.contains("(= var_x_2 (+ var_x_1 (* rule_0_1 1)))"));
1916
1917 let encoding = encoder.encode_step_transition(4, 3);
1919
1920 let smt = format!("{}", encoder.ctx.smt_solver.display(encoding));
1921 assert!(smt.contains("(= rule_1_3 1)"));
1922 assert!(smt.contains("(= loc_l0_4 (- loc_l0_3 rule_1_3))"));
1923 assert!(smt.contains("(= loc_l1_4 (+ loc_l1_3 rule_1_3))"));
1924 assert!(smt.contains("(= loc_l2_4 loc_l2_3)"));
1925 assert!(smt.contains("(= var_x_4 (- var_x_3 (* rule_1_3 1)))"));
1926
1927 let encoding = encoder.encode_step_transition(10, 8);
1929
1930 let smt = format!("{}", encoder.ctx.smt_solver.display(encoding));
1931 assert!(smt.contains("(= rule_2_8 1)"));
1932 assert!(smt.contains("(= loc_l0_10 (- loc_l0_9 rule_2_8))"));
1933 assert!(smt.contains("(= loc_l1_10 (+ loc_l1_9 rule_2_8))"));
1934 assert!(smt.contains("(= loc_l2_10 loc_l2_9))"));
1935 assert!(smt.contains("(= var_x_10 0)"));
1936 }
1937
1938 #[test]
1939 fn test_encode_update_shared_vars_inc() {
1940 let aut = get_test_automaton();
1941 let error_graph = get_test_error_graph(&aut);
1942 let path = get_test_steady_error_path(&aut, &error_graph);
1943 let encoder = SMTEncoderSteady::new(SMTSolverBuilder::default(), &aut, &path);
1944 let shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 1);
1945 let next_shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 2);
1946 let rule_smt = encoder.rule_counter_to_smt.get(1).unwrap().1;
1947 let encoding = encoder.encode_update_shared_var(
1948 &shared_smt,
1949 &next_shared_smt,
1950 rule_smt,
1951 &UpdateExpression::Inc(1),
1952 );
1953
1954 let correct_encoding = "(= var_x_2 (+ var_x_1 (* rule_0_1 1)))";
1955
1956 assert_eq!(
1957 format!("{}", encoder.ctx.smt_solver.display(encoding)),
1958 correct_encoding
1959 );
1960 }
1961
1962 #[test]
1963 fn test_encode_update_shared_vars_dec() {
1964 let aut = get_test_automaton();
1965 let error_graph = get_test_error_graph(&aut);
1966 let path = get_test_steady_error_path(&aut, &error_graph);
1967 let encoder = SMTEncoderSteady::new(SMTSolverBuilder::default(), &aut, &path);
1968 let shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 1);
1969 let next_shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 2);
1970 let rule_smt = encoder.rule_counter_to_smt.get(1).unwrap().1;
1971 let encoding = encoder.encode_update_shared_var(
1972 &shared_smt,
1973 &next_shared_smt,
1974 rule_smt,
1975 &UpdateExpression::Dec(1),
1976 );
1977
1978 let correct_encoding = "(= var_x_2 (- var_x_1 (* rule_0_1 1)))";
1979
1980 assert_eq!(
1981 format!("{}", encoder.ctx.smt_solver.display(encoding)),
1982 correct_encoding
1983 );
1984 }
1985
1986 #[test]
1987 fn test_encode_update_shared_vars_reset() {
1988 let aut = get_test_automaton();
1989 let error_graph = get_test_error_graph(&aut);
1990 let path = get_test_steady_error_path(&aut, &error_graph);
1991 let encoder = SMTEncoderSteady::new(SMTSolverBuilder::default(), &aut, &path);
1992 let shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 1);
1993 let next_shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 2);
1994 let rule_smt = encoder.rule_counter_to_smt.get(1).unwrap().1;
1995 let encoding = encoder.encode_update_shared_var(
1996 &shared_smt,
1997 &next_shared_smt,
1998 rule_smt,
1999 &UpdateExpression::Reset,
2000 );
2001
2002 let correct_encoding = "(= var_x_2 0)";
2003
2004 assert_eq!(
2005 format!("{}", encoder.ctx.smt_solver.display(encoding)),
2006 correct_encoding
2007 );
2008 }
2009
2010 #[test]
2011 fn test_encode_update_shared_vars_unchanged() {
2012 let aut = get_test_automaton();
2013 let error_graph = get_test_error_graph(&aut);
2014 let path = get_test_steady_error_path(&aut, &error_graph);
2015 let encoder = SMTEncoderSteady::new(SMTSolverBuilder::default(), &aut, &path);
2016 let shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 1);
2017 let next_shared_smt = encoder.ctx.get_indexed_shared_smt(&Variable::new("x"), 2);
2018 let rule_smt = encoder.rule_counter_to_smt.get(1).unwrap().1;
2019 let encoding = encoder.encode_update_shared_var(
2020 &shared_smt,
2021 &next_shared_smt,
2022 rule_smt,
2023 &UpdateExpression::Unchanged,
2024 );
2025
2026 let correct_encoding = "(= var_x_2 var_x_1)";
2027
2028 assert_eq!(
2029 format!("{}", encoder.ctx.smt_solver.display(encoding)),
2030 correct_encoding
2031 );
2032 }
2033
2034 #[test]
2035 fn test_encode_resilience_conditions() {
2036 let solver = SMTSolverBuilder::default();
2037 let aut = get_test_automaton();
2038 let error_graph = get_test_error_graph(&aut);
2039 let path = get_test_steady_error_path(&aut, &error_graph);
2040 let mut encoder = SMTEncoderSteady::new(solver, &aut, &path);
2041
2042 let rc_1 = ParameterConstraint::ComparisonExpression(
2043 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
2044 ComparisonOp::Gt,
2045 Box::new(IntegerExpression::Const(3) * IntegerExpression::Param(Parameter::new("t"))),
2046 );
2047
2048 let rc_2 = ParameterConstraint::ComparisonExpression(
2049 Box::new(IntegerExpression::Atom(Parameter::new("t"))),
2050 ComparisonOp::Geq,
2051 Box::new(IntegerExpression::Param(Parameter::new("f"))),
2052 );
2053 let rcs = vec![&rc_1, &rc_2];
2054
2055 let res = encoder.encode_resilience_conditions(rcs.into_iter());
2056
2057 let correct_encoding = "(and (and true (> param_n (* 3 param_t))) (>= param_t param_f))";
2058
2059 assert_eq!(
2060 format!("{}", encoder.ctx.smt_solver.display(res)),
2061 correct_encoding
2062 );
2063 }
2064
2065 #[test]
2066 fn test_encode_loc_conditions() {
2067 let solver = SMTSolverBuilder::default();
2068 let aut = get_test_automaton();
2069 let error_graph = get_test_error_graph(&aut);
2070 let path = get_test_steady_error_path(&aut, &error_graph);
2071 let mut encoder = SMTEncoderSteady::new(solver, &aut, &path);
2072
2073 let loc_constr_1 = LocationConstraint::ComparisonExpression(
2074 Box::new(IntegerExpression::Atom(Location::new("l0"))),
2075 ComparisonOp::Eq,
2076 Box::new(
2077 IntegerExpression::Param(Parameter::new("n"))
2078 - IntegerExpression::Param(Parameter::new("t")),
2079 ),
2080 );
2081 let loc_constr_2 = LocationConstraint::ComparisonExpression(
2082 Box::new(IntegerExpression::Atom(Location::new("l1"))),
2083 ComparisonOp::Eq,
2084 Box::new(IntegerExpression::Const(0)),
2085 );
2086 let loc_constr_3 = LocationConstraint::ComparisonExpression(
2087 Box::new(IntegerExpression::Atom(Location::new("l2"))),
2088 ComparisonOp::Eq,
2089 Box::new(IntegerExpression::Const(0)),
2090 );
2091
2092 let init_loc_constrs = vec![&loc_constr_1, &loc_constr_2, &loc_constr_3];
2093
2094 let res = encoder.encode_loc_conditions(init_loc_constrs.into_iter());
2095
2096 let correct_encoding =
2097 "(and (and (and true (= loc_l0_0 (- param_n param_t))) (= loc_l1_0 0)) (= loc_l2_0 0))";
2098
2099 assert_eq!(
2100 format!("{}", encoder.ctx.smt_solver.display(res)),
2101 correct_encoding
2102 );
2103 }
2104
2105 #[test]
2106 fn test_encode_var_conditions() {
2107 let aut = get_test_automaton();
2108 let error_graph = get_test_error_graph(&aut);
2109 let path = get_test_steady_error_path(&aut, &error_graph);
2110 let mut encoder = SMTEncoderSteady::new(SMTSolverBuilder::default(), &aut, &path);
2111
2112 let init_var_constr = BooleanVarConstraint::ComparisonExpression(
2113 Box::new(IntegerExpression::Atom(Variable::new("x"))),
2114 ComparisonOp::Eq,
2115 Box::new(IntegerExpression::Const(0)),
2116 );
2117
2118 let res = encoder.encode_var_conditions(vec![&init_var_constr].into_iter());
2119
2120 let correct_encoding = "(and true (= var_x_0 0))";
2121
2122 assert_eq!(
2123 format!("{}", encoder.ctx.smt_solver.display(res)),
2124 correct_encoding
2125 );
2126 }
2127
2128 #[test]
2129 fn test_encode_shared_interval_left_infty() {
2130 let aut = get_test_automaton();
2131 let error_graph = get_test_error_graph(&aut);
2132 let path = get_test_steady_error_path(&aut, &error_graph);
2133 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2134
2135 let interval = Interval::new(
2137 IntervalBoundary::new_infty(),
2138 true,
2139 IntervalBoundary::new_infty(),
2140 true,
2141 );
2142 encoder.ctx.set_curr_index(1);
2143 let res = encoder.encode_shared_interval(Variable::new("x"), interval);
2144
2145 assert_eq!(format!("{}", encoder.ctx.smt_solver.display(res)), "false");
2146 }
2147
2148 #[test]
2149 fn test_encode_shared_interval_right_infty() {
2150 let aut = get_test_automaton();
2151 let error_graph = get_test_error_graph(&aut);
2152 let path = get_test_steady_error_path(&aut, &error_graph);
2153 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2154
2155 let interval = Interval::new(
2157 IntervalBoundary::Bound(Threshold::new(
2158 [
2159 (Parameter::new("n"), 1.into()),
2160 (Parameter::new("t"), Fraction::new(1, 1, true)),
2161 ],
2162 0,
2163 )),
2164 false,
2165 IntervalBoundary::new_infty(),
2166 true,
2167 );
2168
2169 encoder.ctx.set_curr_index(1);
2170 let res = encoder.encode_shared_interval(Variable::new("x"), interval);
2171
2172 assert_eq!(
2173 format!("{}", encoder.ctx.smt_solver.display(res)),
2174 "(and (>= var_x_1 (+ param_n (* (- 1) param_t))) true)"
2175 );
2176 }
2177
2178 #[test]
2179 fn test_encode_shared_interval_interval_left_closed_right_closed() {
2180 let aut = get_test_automaton();
2181 let error_graph = get_test_error_graph(&aut);
2182 let path = get_test_steady_error_path(&aut, &error_graph);
2183 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2184
2185 let interval = Interval::new(
2187 IntervalBoundary::from_const(0),
2188 false,
2189 IntervalBoundary::Bound(Threshold::new(
2190 [
2191 (Parameter::new("n"), 1.into()),
2192 (Parameter::new("t"), Fraction::new(1, 1, true)),
2193 ],
2194 0,
2195 )),
2196 false,
2197 );
2198
2199 encoder.ctx.set_curr_index(1);
2200 let res = encoder.encode_shared_interval(Variable::new("x"), interval);
2201
2202 assert_eq!(
2203 format!("{}", encoder.ctx.smt_solver.display(res)),
2204 "(and (>= var_x_1 0) (<= var_x_1 (+ param_n (* (- 1) param_t))))"
2205 );
2206 }
2207
2208 #[test]
2209 fn test_encode_shared_interval_interval_left_open_right_closed() {
2210 let aut = get_test_automaton();
2211 let error_graph = get_test_error_graph(&aut);
2212 let path = get_test_steady_error_path(&aut, &error_graph);
2213 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2214
2215 let interval = Interval::new(
2217 IntervalBoundary::from_const(0),
2218 true,
2219 IntervalBoundary::Bound(Threshold::new(
2220 [
2221 (Parameter::new("n"), 1.into()),
2222 (Parameter::new("t"), Fraction::new(1, 1, true)),
2223 ],
2224 0,
2225 )),
2226 false,
2227 );
2228
2229 encoder.ctx.set_curr_index(1);
2230 let res = encoder.encode_shared_interval(Variable::new("x"), interval);
2231
2232 assert_eq!(
2233 format!("{}", encoder.ctx.smt_solver.display(res)),
2234 "(and (> var_x_1 0) (<= var_x_1 (+ param_n (* (- 1) param_t))))"
2235 );
2236 }
2237
2238 #[test]
2239 fn test_encode_shared_interval_interval_left_closed_right_open() {
2240 let aut = get_test_automaton();
2241 let error_graph = get_test_error_graph(&aut);
2242 let path = get_test_steady_error_path(&aut, &error_graph);
2243 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2244
2245 let interval = Interval::new(
2247 IntervalBoundary::from_const(0),
2248 false,
2249 IntervalBoundary::Bound(Threshold::new(
2250 [
2251 (Parameter::new("n"), 1.into()),
2252 (Parameter::new("t"), Fraction::new(1, 1, true)),
2253 ],
2254 0,
2255 )),
2256 true,
2257 );
2258
2259 encoder.ctx.set_curr_index(1);
2260 let res = encoder.encode_shared_interval(Variable::new("x"), interval);
2261
2262 assert_eq!(
2263 format!("{}", encoder.ctx.smt_solver.display(res)),
2264 "(and (>= var_x_1 0) (< var_x_1 (+ param_n (* (- 1) param_t))))"
2265 );
2266 }
2267
2268 #[test]
2269 fn test_encode_shared_interval_interval_left_open_right_open() {
2270 let aut = get_test_automaton();
2271 let error_graph = get_test_error_graph(&aut);
2272 let path = get_test_steady_error_path(&aut, &error_graph);
2273 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2274
2275 let interval = Interval::new(
2277 IntervalBoundary::from_const(0),
2278 true,
2279 IntervalBoundary::Bound(Threshold::new(
2280 [
2281 (Parameter::new("n"), 1.into()),
2282 (Parameter::new("t"), Fraction::new(1, 1, true)),
2283 ],
2284 0,
2285 )),
2286 true,
2287 );
2288
2289 encoder.ctx.set_curr_index(1);
2290 let res = encoder.encode_shared_interval(Variable::new("x"), interval);
2291
2292 assert_eq!(
2293 format!("{}", encoder.ctx.smt_solver.display(res)),
2294 "(and (> var_x_1 0) (< var_x_1 (+ param_n (* (- 1) param_t))))"
2295 );
2296 }
2297
2298 #[test]
2299 fn test_encode_abstract_path() {
2300 let aut = get_test_automaton();
2301 let error_graph = get_test_error_graph(&aut);
2302 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2303 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2304
2305 let res = encoder.encode_steady_error_path();
2306
2307 assert_eq!(
2308 format!("{}", encoder.ctx.smt_solver.display(res)),
2309 "(and (and (and (and true (and (>= var_x_0 0) (< var_x_0 1))) (and (>= var_x_1 0) (< var_x_1 1))) (and (>= var_x_2 1) (< var_x_2 (+ param_n (* (- 1) param_t))))) (and (>= var_x_3 1) (< var_x_3 (+ param_n (* (- 1) param_t)))))"
2310 );
2311 }
2312
2313 #[test]
2314 fn test_set_curr_index() {
2315 let aut = get_test_automaton();
2316 let error_graph = get_test_error_graph(&aut);
2317 let path = get_test_steady_error_path(&aut, &error_graph);
2318 let mut ctx = get_initialized_test_smt_encoder(&aut, &path).ctx;
2319
2320 assert_eq!(ctx.curr_index, 0);
2321
2322 ctx.set_curr_index(3);
2323
2324 assert_eq!(ctx.curr_index, 3);
2325 }
2326
2327 #[test]
2328 fn test_encode_step_processes_self_loop() {
2329 let solver = SMTSolverBuilder::default();
2330
2331 let ta_builder =
2332 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
2333 .with_locations(vec![Location::new("l0"), Location::new("l1"), Location::new("l2")])
2334 .unwrap()
2335 .initialize()
2336 .with_rules([RuleBuilder::new(0, Location::new("l0"), Location::new("l0")).build(), RuleBuilder::new(1, Location::new("l0"), Location::new("l1")).build(), RuleBuilder::new(2, Location::new("l0"), Location::new("l2")).build()])
2337 .unwrap();
2338 let ta = ta_builder.build();
2339 let lia =
2340 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
2341 ta.clone(),
2342 )
2343 .unwrap();
2344
2345 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
2346 .build()
2347 .unwrap();
2348 let ata = interval_tas.next().unwrap();
2349
2350 let nxt = interval_tas.next();
2351
2352 assert!(nxt.is_none(), "Got additional ta {}", nxt.unwrap());
2353
2354 let error_graph = get_test_error_graph(&ata);
2355 let path = get_test_steady_error_path_one_step(&ata, &error_graph);
2356
2357 let encoder = SMTEncoderSteady::new(solver, &ata, &path);
2358
2359 let res = encoder.encode_step_transition(2, 1);
2360
2361 let smt = format!("{}", encoder.ctx.smt_solver.display(res));
2362 assert!(smt.contains("(= rule_0_1 1)"));
2363 assert!(smt.contains("(= loc_l0_2 loc_l0_1))"));
2364 assert!(smt.contains("(= loc_l1_2 loc_l1_1))"));
2365 assert!(smt.contains("(= loc_l2_2 loc_l2_1))"));
2366 }
2367
2368 #[test]
2369 fn test_encode_steady_path() {
2370 let aut = get_test_automaton();
2371 let error_graph = get_test_error_graph(&aut);
2372 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2373 let encoder = get_initialized_test_smt_encoder(&aut, &path);
2374
2375 let res = encoder.encode_steady_path(0, 0, path.steady_paths().next().unwrap());
2376
2377 let smt = format!("{}", encoder.ctx.smt_solver.display(res));
2378
2379 assert!(smt.contains("(= loc_l2_1 loc_l2_0)"));
2380 assert!(smt.contains("(= loc_l0_1 (- loc_l0_0 rule_0_0))"));
2381 assert!(smt.contains("(= loc_l1_1 (+ loc_l1_0 rule_0_0))"));
2382 assert!(smt.contains("(= var_x_1 (+ var_x_0 (* rule_0_0 1)))"));
2383 assert!(smt.contains("(=> (> rule_0_0 0) (and (> rule_0_0 0) (> loc_l0_0 0)))"));
2384 }
2385
2386 #[test]
2387 fn test_encode_steady_path_move_processes() {
2388 let aut = get_test_automaton();
2389 let error_graph = get_test_error_graph(&aut);
2390 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2391 let encoder = get_initialized_test_smt_encoder(&aut, &path);
2392
2393 let res =
2394 encoder.encode_steady_path_move_processes(0, 0, path.steady_paths().next().unwrap());
2395
2396 let smt = format!("{}", encoder.ctx.smt_solver.display(res));
2397 assert!(smt.contains("(= loc_l1_1 (+ loc_l1_0 rule_0_0))"));
2398 assert!(smt.contains("(= loc_l2_1 loc_l2_0)"));
2399 assert!(smt.contains("(= loc_l0_1 (- loc_l0_0 rule_0_0))"));
2400 }
2401
2402 #[test]
2403 fn test_encode_steady_path_update_shared_variables() {
2404 let aut = get_test_automaton();
2405 let error_graph = get_test_error_graph(&aut);
2406 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2407 let encoder = get_initialized_test_smt_encoder(&aut, &path);
2408
2409 let res = encoder.encode_steady_path_update_shared_variables(
2410 0,
2411 0,
2412 path.steady_paths().next().unwrap(),
2413 );
2414
2415 assert_eq!(
2416 format!("{}", encoder.ctx.smt_solver.display(res)),
2417 "(and true (= var_x_1 (+ var_x_0 (* rule_0_0 1))))"
2418 );
2419 }
2420
2421 #[test]
2422 fn test_abstract_is_non_spurious_without_spec() {
2423 let aut = get_test_automaton();
2424 let error_graph = get_test_error_graph(&aut);
2425 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2426 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2427
2428 let res = encoder.steady_is_non_spurious(None);
2429
2430 assert!(res.is_non_spurious());
2431 }
2432
2433 #[test]
2434 fn test_abstract_is_non_spurious_with_spec() {
2435 let aut = get_test_automaton();
2436 let error_graph = get_test_error_graph(&aut);
2437 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2438 let mut encoder = get_initialized_test_smt_encoder(&aut, &path);
2439
2440 let res = encoder.steady_is_non_spurious(Some(
2441 &TargetConfig::new_cover(HashSet::from([Location::new("l1"), Location::new("l2")]))
2442 .unwrap()
2443 .into_disjunct_with_name("cover"),
2444 ));
2445 assert!(!res.is_non_spurious());
2446 }
2447
2448 #[test]
2449 fn test_extract_non_spurious_concrete_path_no_param_assignment() {
2450 let aut = get_test_automaton();
2451 let error_graph = get_test_error_graph(&aut);
2452 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2453 let mut smt_encoder = get_initialized_test_smt_encoder(&aut, &path);
2454
2455 let res = smt_encoder.extract_non_spurious_concrete_path(SMTSolution::UNSAT);
2456
2457 assert!(res.is_err());
2458
2459 assert!(matches!(
2460 res.clone().unwrap_err(),
2461 SMTEncoderError::NoSatParamAssign(_),
2462 ));
2463 }
2464
2465 #[test]
2466 fn test_extract_non_spurious_concrete_path() {
2467 let aut = get_test_automaton();
2468 let error_graph = get_test_error_graph(&aut);
2469 let path = get_test_steady_error_path_one_step(&aut, &error_graph);
2470 let mut smt_encoder = get_initialized_test_smt_encoder(&aut, &path);
2471
2472 smt_encoder.steady_is_non_spurious(None);
2473
2474 let res = smt_encoder.extract_non_spurious_concrete_path(SMTSolution::SAT);
2475
2476 assert!(res.is_ok());
2477
2478 let path = res.unwrap();
2479
2480 assert!((path.parameter_values().count() != 0));
2481 assert!(path.get_parameter_value(&Parameter::new("n")).unwrap() > 0);
2482
2483 assert_eq!(path.transitions().count(), 1);
2484 assert_eq!(path.transitions().next().unwrap().rule().id(), 0);
2485
2486 assert_eq!(path.configurations().count(), 2);
2487
2488 let configs = path.configurations().collect::<Vec<_>>();
2489
2490 let conf_0 = configs.first().unwrap();
2491 let conf_1 = configs.get(1).unwrap();
2492
2493 assert_eq!(conf_0.get_variable_value(&Variable::new("x")), 0);
2494 assert!(conf_1.get_variable_value(&Variable::new("x")) > 0);
2495
2496 assert_eq!(conf_0.get_processes_in_location(&Location::new("l2")), 0);
2497 assert_eq!(conf_0.get_processes_in_location(&Location::new("l1")), 0);
2498 assert_eq!(conf_1.get_processes_in_location(&Location::new("l2")), 0);
2499 assert!(conf_1.get_processes_in_location(&Location::new("l1")) > 0);
2500 assert_eq!(
2501 conf_1.get_processes_in_location(&Location::new("l0")),
2502 conf_0.get_processes_in_location(&Location::new("l0"))
2503 - conf_1.get_processes_in_location(&Location::new("l1"))
2504 );
2505 }
2506}