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