1use std::{
19 collections::{BTreeMap, HashMap},
20 fmt,
21 rc::Rc,
22};
23
24use taco_threshold_automaton::{
25 ActionDefinition, RuleDefinition, ThresholdAutomaton, VariableConstraint,
26 expressions::{Location, Parameter, Variable},
27 general_threshold_automaton::{Rule, UpdateExpression},
28};
29
30use crate::{
31 SMTExpr, SMTSolution, SMTSolver,
32 expression_encoding::{DeclaresVariable, EncodeToSMT, SMTSolverError, SMTVariableContext},
33};
34
35pub trait StepSMT: SMTVariableContext<Rule> {
37 fn get_rules_applied(
40 &self,
41 solver: &mut SMTSolver,
42 res: SMTSolution,
43 ) -> Result<impl Iterator<Item = (Rule, u32)>, SMTSolverError> {
44 let rules_used = self.get_solution(solver, res)?;
45
46 Ok(rules_used.into_iter().filter(|(_, n)| *n > 0))
47 }
48}
49
50#[derive(Debug, Clone)]
59pub struct LazyStepContext<TA, C>
60where
61 TA: ThresholdAutomaton,
62 C: SMTVariableContext<Parameter> + SMTVariableContext<Location> + SMTVariableContext<Variable>,
63{
64 index: usize,
66 rule_vars: BTreeMap<Rule, SMTExpr>,
68 ta: Rc<TA>,
70 prev_config: Rc<C>,
72 curr_config: Rc<C>,
74}
75
76impl<TA, C> SMTVariableContext<Rule> for LazyStepContext<TA, C>
77where
78 TA: ThresholdAutomaton,
79 C: SMTVariableContext<Parameter> + SMTVariableContext<Location> + SMTVariableContext<Variable>,
80{
81 fn get_expr_for(&self, expr: &Rule) -> Result<SMTExpr, SMTSolverError> {
82 self.rule_vars
83 .get(expr)
84 .cloned()
85 .ok_or_else(|| SMTSolverError::UndeclaredRule(expr.clone()))
86 }
87
88 fn get_exprs<'a>(&'a self) -> impl IntoIterator<Item = &'a Rule>
89 where
90 Rule: 'a,
91 {
92 self.rule_vars.keys()
93 }
94
95 fn get_solution<'a>(
96 &'a self,
97 solver: &mut crate::SMTSolver,
98 res: crate::SMTSolution,
99 ) -> Result<HashMap<Rule, u32>, super::SMTSolverError>
100 where
101 Rule: 'a + Eq + std::hash::Hash + Clone,
102 {
103 match res {
104 crate::SMTSolution::UNSAT => Err(super::SMTSolverError::ExtractionFromUnsat),
105 crate::SMTSolution::SAT => {
106 let expr_vec = self
107 .get_exprs()
108 .into_iter()
109 .map(|t| self.get_expr_for(t))
110 .collect::<Result<Vec<_>, _>>()?;
111
112 let solution = solver.get_value(expr_vec)?;
113
114 solution
115 .into_iter()
116 .zip(self.get_exprs())
117 .map(|((_, sol), t)| {
118 let solution_value = solver.get_u32(sol).ok_or_else(|| {
119 let sol = solver.display(sol);
120 super::SMTSolverError::SolutionExtractionParseIntError(sol.to_string())
121 })?;
122
123 Ok((t.clone(), solution_value))
124 })
125 .collect::<Result<HashMap<Rule, u32>, super::SMTSolverError>>()
126 }
127 }
128 }
129}
130
131impl<TA, C> LazyStepContext<TA, C>
132where
133 TA: ThresholdAutomaton,
134 C: SMTVariableContext<Parameter> + SMTVariableContext<Location> + SMTVariableContext<Variable>,
135{
136 pub fn new(
142 rules_to_appl: impl Iterator<Item = Rule>,
143 ta: Rc<TA>,
144 prev_config: Rc<C>,
145 curr_config: Rc<C>,
146 index: usize,
147 solver: &mut SMTSolver,
148 ) -> LazyStepContext<TA, C> {
149 let rule_vars = rules_to_appl
150 .into_iter()
151 .map(|r| {
152 let rule = r
153 .declare_variable(solver, index as u32)
154 .expect("Failed to declare rule variable");
155 solver
156 .assert(solver.gte(rule, solver.numeral(0)))
157 .expect("Failed to assert rule application >= 0");
158
159 (r.clone(), rule)
160 })
161 .collect();
162
163 Self {
164 index,
165 ta,
166 rule_vars,
167 prev_config,
168 curr_config,
169 }
170 }
171
172 fn encode_phi_base(&self, solver: &SMTSolver) -> SMTExpr {
185 if self.rule_vars.is_empty() {
186 return solver.true_();
187 }
188
189 solver.and_many(
191 self.rule_vars
192 .keys()
193 .map(|rule| rule.guard().as_boolean_expr())
194 .map(|guard| {
195 let prev_sat_guard = guard
196 .encode_to_smt_with_ctx(solver, self.prev_config.as_ref())
197 .expect("Failed to encode guard with previous configuration");
198 let curr_sat_guard = guard
199 .encode_to_smt_with_ctx(solver, self.curr_config.as_ref())
200 .expect("Failed to encode guard with current configuration");
201
202 solver.eq(prev_sat_guard, curr_sat_guard)
203 }),
204 )
205 }
206
207 fn encode_phi_l(&self, solver: &SMTSolver) -> SMTExpr {
209 if self.ta.locations().count() == 0 {
210 return solver.true_();
211 }
212
213 solver.and_many(self.ta.locations().map(|loc| {
214 let prev_loc = self
216 .prev_config
217 .get_expr_for(loc)
218 .expect("Failed to get location for ϕ_L");
219 let curr_loc = self
221 .curr_config
222 .get_expr_for(loc)
223 .expect("Failed to get location for ϕ_L");
224
225 let diff_loc_state = solver.sub(curr_loc, prev_loc);
227
228 let in_sum = solver.plus_many(
230 self.rule_vars
231 .iter()
232 .filter(|(r, _)| r.target() == loc) .map(|(_, x_r)| *x_r)
234 .chain([solver.numeral(0)]), );
236
237 let out_sum = solver.plus_many(
239 self.rule_vars
240 .iter()
241 .filter(|(r, _)| r.source() == loc) .map(|(_, x_r)| *x_r)
243 .chain([solver.numeral(0)]), );
245
246 let diff_rule_appl = solver.sub(in_sum, out_sum);
248
249 solver.eq(diff_loc_state, diff_rule_appl)
250 }))
251 }
252
253 fn encode_phi_gamma(&self, solver: &SMTSolver) -> SMTExpr {
255 if self.ta.variables().count() == 0 {
256 return solver.true_();
257 }
258
259 solver.and_many(self.ta.variables().map(|z| {
261 let prev_var = self
263 .prev_config
264 .get_expr_for(z)
265 .expect("Failed to get variable for ϕ_Γ");
266 let curr_var = self
268 .curr_config
269 .get_expr_for(z)
270 .expect("Failed to get variable for ϕ_Γ");
271
272 let diff_var_state = solver.sub(curr_var, prev_var);
274
275 let update_acc = solver.plus_many(
277 self.rule_vars
278 .iter()
279 .map(|(r, x_r)| {
280 let mut acts = r.actions().filter(|a| a.variable() == z && !a.is_reset());
281
282 let act = acts.next();
283 if act.is_none() {
284 return solver.numeral(0);
285 }
286
287 debug_assert!(
288 acts.next().is_none(),
289 "Multiple actions on same variable should have been rejected !"
290 );
291
292 let act = act.unwrap();
293
294 debug_assert!(
295 !act.is_reset(),
296 "This model checker does not support resets !"
297 );
298
299 let update = Self::update_vec_to_smt(act.update(), solver);
301
302 solver.times(*x_r, update)
304 })
305 .chain([solver.numeral(0)]), );
307
308 let mut update = solver.eq(diff_var_state, update_acc);
310
311 let reset_effect = self
314 .rule_vars
315 .iter()
316 .filter(|(r, _)| r.actions().any(|a| a.variable() == z && a.is_reset()))
317 .map(|(_, x_r)| {
318 let x_r_gt_0 = solver.gt(*x_r, solver.numeral(0));
319 let curr_var_eq_0 = solver.eq(curr_var, solver.numeral(0));
320
321 solver.and(x_r_gt_0, curr_var_eq_0)
322 })
323 .collect::<Vec<_>>();
324
325 if !reset_effect.is_empty() {
327 let no_reset_applied = solver.eq(
329 solver.plus_many(
330 self.rule_vars
331 .iter()
332 .filter(|(r, _)| r.actions().any(|a| a.variable() == z && a.is_reset()))
333 .map(|(_, x_r)| *x_r),
334 ),
335 solver.numeral(0),
336 );
337
338 update = solver.or(
339 solver.and(update, no_reset_applied), solver.or_many(reset_effect), );
342 }
343
344 update
345 }))
346 }
347
348 fn encode_phi_r(&self, solver: &SMTSolver) -> SMTExpr {
350 if self.rule_vars.is_empty() {
351 return solver.true_();
352 }
353
354 solver.and_many(self.rule_vars.iter().map(|(rule, x_r)| {
355 let rule_applied = solver.gt(*x_r, solver.numeral(0));
357
358 let guard_sat = rule
360 .guard()
361 .encode_to_smt_with_ctx(solver, self.prev_config.as_ref())
362 .expect("Failed to encode guard with previous configuration");
363
364 solver.imp(rule_applied, guard_sat)
366 }))
367 }
368
369 fn exponential_encode_phi_appl(&self, solver: &SMTSolver) -> SMTExpr {
386 if self.rule_vars.is_empty() {
387 return solver.true_();
388 }
389
390 solver.and_many(self.rule_vars.iter().map(|(rule, x_r)| {
391 let xr_gt_0 = solver.gt(*x_r, solver.numeral(0));
393
394 let all_possible_s = self.compute_s_for_r(rule);
396
397 if all_possible_s.is_empty() {
399 return solver.false_();
400 }
401
402 let disj_s = solver.or_many(
404 all_possible_s
405 .iter()
406 .map(|s| self.encode_phi_chain(solver, s)),
407 );
408
409 let mut res = solver.imp(xr_gt_0, disj_s);
411
412 if rule.actions().any(|a| a.is_reset()) {
416 let reset_var_already_zero =
417 solver.and_many(rule.actions().filter(|a| a.is_reset()).map(|a| {
418 let v = self.prev_config.get_expr_for(a.variable()).unwrap();
419 solver.eq(v, solver.numeral(0))
420 }));
421 let reset_applied_imp_0 = solver.imp(xr_gt_0, reset_var_already_zero);
422
423 res = solver.and(res, reset_applied_imp_0)
424 }
425
426 if self
430 .rule_vars
431 .iter()
432 .any(|(r, _)| r.actions().any(|a| a.is_decrement()))
433 && self
434 .rule_vars
435 .iter()
436 .any(|(r, _)| r.actions().any(|a| a.is_increment()))
437 {
438 let r_dec = solver.plus_many(
440 self.rule_vars
441 .iter()
442 .filter(|(r, _)| r.actions().any(|a| a.is_decrement()))
443 .map(|(_, x_r)| *x_r),
444 );
445 let r_inc = solver.plus_many(
447 self.rule_vars
448 .iter()
449 .filter(|(r, _)| r.actions().any(|a| a.is_increment()))
450 .map(|(_, x_r)| *x_r),
451 );
452
453 let r_inc_gt_0 = solver.gt(r_inc, solver.numeral(0));
455 let r_dec_gt_0 = solver.gt(r_dec, solver.numeral(0));
457
458 let dec_xor_inc = solver.and(
460 solver.imp(r_inc_gt_0, solver.not(r_dec_gt_0)),
461 solver.imp(r_dec_gt_0, solver.not(r_inc_gt_0)),
462 );
463
464 res = solver.and(res, dec_xor_inc);
465 }
466
467 res
468 }))
469 }
470
471 fn encode_phi_chain(&self, solver: &SMTSolver, s: &[&Rule]) -> SMTExpr {
480 if s.is_empty() {
481 return solver.true_();
482 }
483
484 let loc = s.last().unwrap().source(); let loc_var = self
487 .prev_config
488 .get_expr_for(loc)
489 .expect("Failed to get location for ϕ_chain");
490 let loc_gt_0 = solver.gt(loc_var, solver.numeral(0));
491
492 solver.and_many(
493 s.iter()
494 .map(|rule| {
495 let x_r = self.get_expr_for(rule).expect("Rule variable not found");
496
497 solver.gt(x_r, solver.numeral(0))
498 })
499 .chain([loc_gt_0]),
500 )
501 }
502
503 fn compute_s_for_r<'a>(&'a self, r: &'a Rule) -> Vec<Vec<&'a Rule>> {
517 let loc = r.source();
518 self.compute_s_with_backtracking_recursive(loc, vec![r])
519 }
520
521 fn compute_s_with_backtracking_recursive<'a>(
530 &'a self,
531 loc: &'a Location,
532 rules_applied: Vec<&'a Rule>,
533 ) -> Vec<Vec<&'a Rule>> {
534 let mut s = self
536 .rule_vars
537 .iter()
538 .filter(|(r, _)| r.target() == loc) .filter(|(r, _)| !rules_applied.contains(r))
540 .flat_map(|(r, _)| {
541 let loc = r.source();
542 let mut rules_applied: Vec<_> = rules_applied.clone();
543 rules_applied.push(r);
544
545 self.compute_s_with_backtracking_recursive(loc, rules_applied)
546 })
547 .collect::<Vec<_>>();
548
549 s.push(rules_applied);
551
552 s
553 }
554
555 pub fn encode_phi_steady(&self, solver: &SMTSolver) -> SMTExpr {
557 let phi_base = self.encode_phi_base(solver);
558 let phi_l = self.encode_phi_l(solver);
559 let phi_gamma = self.encode_phi_gamma(solver);
560 let phi_r = self.encode_phi_r(solver);
561 let phi_appl = self.exponential_encode_phi_appl(solver);
562
563 solver.and_many([phi_base, phi_l, phi_gamma, phi_r, phi_appl])
564 }
565
566 fn encode_appl_for_step_step(&self, solver: &SMTSolver) -> SMTExpr {
571 if self
572 .rule_vars
573 .iter()
574 .filter(|(r, _)| r.source() == r.target())
575 .count()
576 == 0
577 {
578 return solver.true_();
579 }
580
581 solver.and_many(
582 self.rule_vars
583 .iter()
584 .filter(|(r, _)| r.source() == r.target())
585 .map(|(rule, x_r)| {
586 let xr_gt_0 = solver.gt(*x_r, solver.numeral(0));
588
589 let src_loc_procs = self
590 .prev_config
591 .get_expr_for(rule.source())
592 .expect("Failed to get SMT variable for source location");
593
594 let src_loc_has_procs = solver.gt(src_loc_procs, solver.numeral(0));
596
597 solver.imp(xr_gt_0, src_loc_has_procs)
599 }),
600 )
601 }
602
603 pub fn encode_phi_step(&self, solver: &SMTSolver) -> SMTExpr {
624 let phi_l = self.encode_phi_l(solver);
625 let phi_gamma = self.encode_phi_gamma(solver);
626 let phi_r = self.encode_phi_r(solver);
627 let phi_appl = self.encode_appl_for_step_step(solver);
628
629 let at_most_one_rule_applied = solver.lte(
631 solver.plus_many(self.rule_vars.values().cloned().chain([solver.numeral(0)])),
632 solver.numeral(1),
633 );
634
635 solver.and_many([phi_l, phi_gamma, phi_r, at_most_one_rule_applied, phi_appl])
636 }
637
638 fn update_vec_to_smt(upd: &UpdateExpression, solver: &SMTSolver) -> SMTExpr {
643 match upd {
644 UpdateExpression::Inc(i) => solver.numeral(*i),
645 UpdateExpression::Dec(d) => {
646 let dec = solver.numeral(*d);
647 solver.negate(dec)
648 }
649 UpdateExpression::Reset => unreachable!("Resets should be handled before"),
650 UpdateExpression::Unchanged => solver.numeral(0),
651 }
652 }
653}
654
655impl<TA, C> StepSMT for LazyStepContext<TA, C>
656where
657 TA: ThresholdAutomaton,
658 C: SMTVariableContext<Parameter> + SMTVariableContext<Location> + SMTVariableContext<Variable>,
659{
660}
661
662impl<TA, C> fmt::Display for LazyStepContext<TA, C>
663where
664 TA: ThresholdAutomaton,
665 C: SMTVariableContext<Parameter> + SMTVariableContext<Location> + SMTVariableContext<Variable>,
666{
667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668 write!(f, "StepContext: {}", self.index)
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use std::{collections::HashMap, rc::Rc};
675
676 use taco_threshold_automaton::{
677 ThresholdAutomaton,
678 expressions::{
679 BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression, Location,
680 Variable,
681 },
682 general_threshold_automaton::{
683 Action, UpdateExpression,
684 builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
685 },
686 };
687
688 use crate::{
689 SMTSolverBuilder, SMTSolverContext,
690 expression_encoding::{
691 SMTVariableContext,
692 config_ctx::ConfigCtx,
693 step_ctx::{LazyStepContext, StepSMT},
694 },
695 };
696
697 #[test]
698 fn test_encode_phi_base() {
699 let mut solver = SMTSolverBuilder::default().new_solver();
700
701 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
702 .with_guard(BooleanExpression::ComparisonExpression(
703 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
704 ComparisonOp::Gt,
705 Box::new(IntegerExpression::Const(1)),
706 ))
707 .build();
708 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
709 .with_guard(BooleanExpression::BinaryExpression(
710 Box::new(BooleanExpression::False),
711 BooleanConnective::Or,
712 Box::new(BooleanExpression::BinaryExpression(
713 Box::new(BooleanExpression::True),
714 BooleanConnective::And,
715 Box::new(BooleanExpression::ComparisonExpression(
716 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
717 ComparisonOp::Leq,
718 Box::new(IntegerExpression::Const(5)),
719 )),
720 )),
721 ))
722 .build();
723
724 let r3 = RuleBuilder::new(2, Location::new("loc2"), Location::new("loc3")).build();
726
727 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
728 .with_locations([
729 Location::new("loc1"),
730 Location::new("loc2"),
731 Location::new("loc3"),
732 ])
733 .unwrap()
734 .with_variables([
735 Variable::new("var1"),
736 Variable::new("var2"),
737 Variable::new("var3"),
738 ])
739 .unwrap()
740 .initialize()
741 .with_rules([r1.clone(), r2.clone(), r3.clone()])
742 .unwrap()
743 .build();
744
745 let config_0 = Rc::new(ConfigCtx::new(
746 &mut solver,
747 &test_ta,
748 Rc::new(HashMap::new()),
749 0,
750 ));
751 let config_1 = Rc::new(ConfigCtx::new(
752 &mut solver,
753 &test_ta,
754 Rc::new(HashMap::new()),
755 1,
756 ));
757
758 let step_context = LazyStepContext::new(
759 test_ta.rules().cloned(),
760 Rc::new(test_ta.clone()),
761 config_0.clone(),
762 config_1.clone(),
763 0,
764 &mut solver,
765 );
766
767 let got_smt_expr = step_context.encode_phi_base(&solver);
768
769 let expected_smt = solver.and_many([
770 solver.imp(
772 solver.gt(
773 config_0.get_expr_for(&Variable::new("var1")).unwrap(),
774 solver.numeral(1),
775 ),
776 solver.gt(
777 config_1.get_expr_for(&Variable::new("var1")).unwrap(),
778 solver.numeral(1),
779 ),
780 ),
781 solver.imp(
783 solver.gt(
784 config_1.get_expr_for(&Variable::new("var1")).unwrap(),
785 solver.numeral(1),
786 ),
787 solver.gt(
788 config_0.get_expr_for(&Variable::new("var1")).unwrap(),
789 solver.numeral(1),
790 ),
791 ),
792 solver.imp(
794 solver.lte(
795 config_0.get_expr_for(&Variable::new("var2")).unwrap(),
796 solver.numeral(5),
797 ),
798 solver.lte(
799 config_1.get_expr_for(&Variable::new("var2")).unwrap(),
800 solver.numeral(5),
801 ),
802 ),
803 solver.imp(
805 solver.lte(
806 config_1.get_expr_for(&Variable::new("var2")).unwrap(),
807 solver.numeral(5),
808 ),
809 solver.lte(
810 config_0.get_expr_for(&Variable::new("var2")).unwrap(),
811 solver.numeral(5),
812 ),
813 ),
814 ]);
815
816 let check_equivalent = solver.not(solver.and(
819 solver.imp(got_smt_expr, expected_smt),
820 solver.imp(expected_smt, got_smt_expr),
821 ));
822
823 assert!(
824 !solver
825 .assert_and_check_expr(check_equivalent)
826 .unwrap()
827 .is_sat(),
828 "got : {}\nexpected: {}",
829 solver.display(got_smt_expr),
830 solver.display(expected_smt)
831 );
832 }
833
834 #[test]
835 fn test_encode_phi_base_no_panics() {
836 let mut solver = SMTSolverBuilder::default().new_solver();
837
838 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build();
839 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3")).build();
840
841 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
842 .with_locations([
843 Location::new("loc1"),
844 Location::new("loc2"),
845 Location::new("loc3"),
846 ])
847 .unwrap()
848 .initialize()
849 .with_rules([r1.clone(), r2.clone()])
850 .unwrap()
851 .build();
852
853 let config_0 = Rc::new(ConfigCtx::new(
854 &mut solver,
855 &test_ta,
856 Rc::new(HashMap::new()),
857 0,
858 ));
859 let config_1 = Rc::new(ConfigCtx::new(
860 &mut solver,
861 &test_ta,
862 Rc::new(HashMap::new()),
863 1,
864 ));
865
866 let step_context = LazyStepContext::new(
867 test_ta.rules().cloned(),
868 Rc::new(test_ta.clone()),
869 config_0.clone(),
870 config_1.clone(),
871 0,
872 &mut solver,
873 );
874
875 let got_smt_expr = step_context.encode_phi_base(&solver);
876
877 let expected_smt = solver.true_();
878
879 let check_equivalent = solver.not(solver.and(
882 solver.imp(got_smt_expr, expected_smt),
883 solver.imp(expected_smt, got_smt_expr),
884 ));
885
886 assert!(
887 !solver
888 .assert_and_check_expr(check_equivalent)
889 .unwrap()
890 .is_sat(),
891 "got : {}\nexpected: {}",
892 solver.display(got_smt_expr),
893 solver.display(expected_smt)
894 );
895 }
896
897 #[test]
898 fn test_encode_phi_l() {
899 let mut solver = SMTSolverBuilder::default().new_solver();
900
901 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build();
902 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3")).build();
903
904 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
905 .with_locations([
906 Location::new("loc1"),
907 Location::new("loc2"),
908 Location::new("loc3"),
909 ])
910 .unwrap()
911 .initialize()
912 .with_rules([r1.clone(), r2.clone()])
913 .unwrap()
914 .build();
915
916 let config_0 = Rc::new(ConfigCtx::new(
917 &mut solver,
918 &test_ta,
919 Rc::new(HashMap::new()),
920 0,
921 ));
922 let config_1 = Rc::new(ConfigCtx::new(
923 &mut solver,
924 &test_ta,
925 Rc::new(HashMap::new()),
926 1,
927 ));
928
929 let step_context = LazyStepContext::new(
930 test_ta.rules().cloned(),
931 Rc::new(test_ta.clone()),
932 config_0.clone(),
933 config_1.clone(),
934 0,
935 &mut solver,
936 );
937
938 let got_smt_expr = step_context.encode_phi_l(&solver);
939
940 let expected_smt = solver.and_many([
941 solver.eq(
943 solver.sub(
944 config_1.get_expr_for(&Location::new("loc1")).unwrap(),
945 config_0.get_expr_for(&Location::new("loc1")).unwrap(),
946 ),
947 solver.sub(solver.numeral(0), step_context.get_expr_for(&r1).unwrap()),
948 ),
949 solver.eq(
951 solver.sub(
952 config_1.get_expr_for(&Location::new("loc2")).unwrap(),
953 config_0.get_expr_for(&Location::new("loc2")).unwrap(),
954 ),
955 solver.sub(
956 step_context.get_expr_for(&r1).unwrap(),
957 step_context.get_expr_for(&r2).unwrap(),
958 ),
959 ),
960 solver.eq(
962 solver.sub(
963 config_1.get_expr_for(&Location::new("loc3")).unwrap(),
964 config_0.get_expr_for(&Location::new("loc3")).unwrap(),
965 ),
966 solver.sub(step_context.get_expr_for(&r2).unwrap(), solver.numeral(0)),
967 ),
968 ]);
969
970 let check_equivalent = solver.not(solver.and(
973 solver.imp(got_smt_expr, expected_smt),
974 solver.imp(expected_smt, got_smt_expr),
975 ));
976
977 assert!(
978 !solver
979 .assert_and_check_expr(check_equivalent)
980 .unwrap()
981 .is_sat(),
982 "got : {}\nexpected: {}",
983 solver.display(got_smt_expr),
984 solver.display(expected_smt)
985 );
986 }
987
988 #[test]
989 fn test_phi_l_no_locations_does_not_panic() {
990 let mut solver = SMTSolverBuilder::default().new_solver();
991
992 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
993 .initialize()
994 .build();
995
996 let config_0 = Rc::new(ConfigCtx::new(
997 &mut solver,
998 &test_ta,
999 Rc::new(HashMap::new()),
1000 0,
1001 ));
1002 let config_1 = Rc::new(ConfigCtx::new(
1003 &mut solver,
1004 &test_ta,
1005 Rc::new(HashMap::new()),
1006 1,
1007 ));
1008
1009 let step_context = LazyStepContext::new(
1010 test_ta.rules().cloned(),
1011 Rc::new(test_ta.clone()),
1012 config_0.clone(),
1013 config_1.clone(),
1014 0,
1015 &mut solver,
1016 );
1017 let got_smt_expr = step_context.encode_phi_l(&solver);
1018
1019 let expected_smt = solver.true_();
1020
1021 let check_equivalent = solver.not(solver.and(
1024 solver.imp(got_smt_expr, expected_smt),
1025 solver.imp(expected_smt, got_smt_expr),
1026 ));
1027
1028 assert!(
1029 !solver
1030 .assert_and_check_expr(check_equivalent)
1031 .unwrap()
1032 .is_sat(),
1033 "got : {}\nexpected: {}",
1034 solver.display(got_smt_expr),
1035 solver.display(expected_smt)
1036 );
1037 }
1038
1039 #[test]
1040 fn test_encode_phi_gamma() {
1041 let mut solver = SMTSolverBuilder::default().new_solver();
1042
1043 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
1044 .with_actions([Action::new_with_update(
1045 Variable::new("var1"),
1046 UpdateExpression::Inc(1),
1047 )])
1048 .build();
1049 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1050 .with_actions([
1051 Action::new_with_update(Variable::new("var1"), UpdateExpression::Dec(1)),
1052 Action::new_with_update(Variable::new("var2"), UpdateExpression::Inc(3)),
1053 Action::new_with_update(Variable::new("var3"), UpdateExpression::Unchanged),
1054 ])
1055 .build();
1056
1057 let r3 = RuleBuilder::new(2, Location::new("loc2"), Location::new("loc3")).build();
1059
1060 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1061 .with_locations([
1062 Location::new("loc1"),
1063 Location::new("loc2"),
1064 Location::new("loc3"),
1065 ])
1066 .unwrap()
1067 .with_variables([
1068 Variable::new("var1"),
1069 Variable::new("var2"),
1070 Variable::new("var3"),
1071 ])
1072 .unwrap()
1073 .initialize()
1074 .with_rules([r1.clone(), r2.clone(), r3.clone()])
1075 .unwrap()
1076 .build();
1077
1078 let config_0 = Rc::new(ConfigCtx::new(
1079 &mut solver,
1080 &test_ta,
1081 Rc::new(HashMap::new()),
1082 0,
1083 ));
1084 let config_1 = Rc::new(ConfigCtx::new(
1085 &mut solver,
1086 &test_ta,
1087 Rc::new(HashMap::new()),
1088 1,
1089 ));
1090
1091 let step_context = LazyStepContext::new(
1092 test_ta.rules().cloned(),
1093 Rc::new(test_ta.clone()),
1094 config_0.clone(),
1095 config_1.clone(),
1096 0,
1097 &mut solver,
1098 );
1099
1100 let got_smt_expr = step_context.encode_phi_gamma(&solver);
1101
1102 let expected_smt = solver.and_many([
1103 solver.eq(
1105 solver.plus_many([
1106 step_context.get_expr_for(&r1).unwrap(),
1108 solver.times(
1110 solver.negate(solver.numeral(1)),
1111 step_context.get_expr_for(&r2).unwrap(),
1112 ),
1113 ]),
1114 solver.sub(
1115 config_1.get_expr_for(&Variable::new("var1")).unwrap(),
1116 config_0.get_expr_for(&Variable::new("var1")).unwrap(),
1117 ),
1118 ),
1119 solver.eq(
1121 solver.plus_many([
1122 solver.times(solver.numeral(3), step_context.get_expr_for(&r2).unwrap()),
1124 ]),
1125 solver.sub(
1126 config_1.get_expr_for(&Variable::new("var2")).unwrap(),
1127 config_0.get_expr_for(&Variable::new("var2")).unwrap(),
1128 ),
1129 ),
1130 solver.eq(
1132 solver.numeral(0),
1133 solver.sub(
1134 config_1.get_expr_for(&Variable::new("var3")).unwrap(),
1135 config_0.get_expr_for(&Variable::new("var3")).unwrap(),
1136 ),
1137 ),
1138 ]);
1139
1140 let check_equivalent = solver.not(solver.and(
1143 solver.imp(got_smt_expr, expected_smt),
1144 solver.imp(expected_smt, got_smt_expr),
1145 ));
1146
1147 assert!(
1148 !solver
1149 .assert_and_check_expr(check_equivalent)
1150 .unwrap()
1151 .is_sat(),
1152 "got : {}\nexpected: {}",
1153 solver.display(got_smt_expr),
1154 solver.display(expected_smt)
1155 );
1156 }
1157
1158 #[test]
1159 fn test_encode_phi_gamma_no_variables_does_not_panic() {
1160 let mut solver = SMTSolverBuilder::default().new_solver();
1161
1162 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1163 .initialize()
1164 .build();
1165
1166 let config_0 = Rc::new(ConfigCtx::new(
1167 &mut solver,
1168 &test_ta,
1169 Rc::new(HashMap::new()),
1170 0,
1171 ));
1172 let config_1 = Rc::new(ConfigCtx::new(
1173 &mut solver,
1174 &test_ta,
1175 Rc::new(HashMap::new()),
1176 1,
1177 ));
1178
1179 let step_context = LazyStepContext::new(
1180 test_ta.rules().cloned(),
1181 Rc::new(test_ta.clone()),
1182 config_0.clone(),
1183 config_1.clone(),
1184 0,
1185 &mut solver,
1186 );
1187
1188 let got_smt_expr = step_context.encode_phi_gamma(&solver);
1189
1190 let expected_smt = solver.true_();
1191
1192 let check_equivalent = solver.not(solver.and(
1195 solver.imp(got_smt_expr, expected_smt),
1196 solver.imp(expected_smt, got_smt_expr),
1197 ));
1198
1199 assert!(
1200 !solver
1201 .assert_and_check_expr(check_equivalent)
1202 .unwrap()
1203 .is_sat(),
1204 "got : {}\nexpected: {}",
1205 solver.display(got_smt_expr),
1206 solver.display(expected_smt)
1207 );
1208 }
1209
1210 #[test]
1211 fn test_encode_phi_r() {
1212 let mut solver = SMTSolverBuilder::default().new_solver();
1213
1214 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
1215 .with_guard(BooleanExpression::ComparisonExpression(
1216 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1217 ComparisonOp::Gt,
1218 Box::new(IntegerExpression::Const(1)),
1219 ))
1220 .build();
1221 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1222 .with_guard(BooleanExpression::BinaryExpression(
1223 Box::new(BooleanExpression::False),
1224 BooleanConnective::Or,
1225 Box::new(BooleanExpression::BinaryExpression(
1226 Box::new(BooleanExpression::True),
1227 BooleanConnective::And,
1228 Box::new(BooleanExpression::ComparisonExpression(
1229 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1230 ComparisonOp::Leq,
1231 Box::new(IntegerExpression::Const(5)),
1232 )),
1233 )),
1234 ))
1235 .build();
1236
1237 let r3 = RuleBuilder::new(2, Location::new("loc2"), Location::new("loc3")).build();
1239
1240 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1241 .with_locations([
1242 Location::new("loc1"),
1243 Location::new("loc2"),
1244 Location::new("loc3"),
1245 ])
1246 .unwrap()
1247 .with_variables([
1248 Variable::new("var1"),
1249 Variable::new("var2"),
1250 Variable::new("var3"),
1251 ])
1252 .unwrap()
1253 .initialize()
1254 .with_rules([r1.clone(), r2.clone(), r3.clone()])
1255 .unwrap()
1256 .build();
1257
1258 let config_0 = Rc::new(ConfigCtx::new(
1259 &mut solver,
1260 &test_ta,
1261 Rc::new(HashMap::new()),
1262 0,
1263 ));
1264 let config_1 = Rc::new(ConfigCtx::new(
1265 &mut solver,
1266 &test_ta,
1267 Rc::new(HashMap::new()),
1268 1,
1269 ));
1270
1271 let step_context = LazyStepContext::new(
1272 test_ta.rules().cloned(),
1273 Rc::new(test_ta.clone()),
1274 config_0.clone(),
1275 config_1.clone(),
1276 0,
1277 &mut solver,
1278 );
1279
1280 let got_smt_expr = step_context.encode_phi_r(&solver);
1281
1282 let expected_smt = solver.and_many([
1283 solver.imp(
1285 solver.gte(step_context.get_expr_for(&r1).unwrap(), solver.numeral(1)),
1286 solver.gt(
1287 config_0.get_expr_for(&Variable::new("var1")).unwrap(),
1288 solver.numeral(1),
1289 ),
1290 ),
1291 solver.imp(
1293 solver.gt(step_context.get_expr_for(&r2).unwrap(), solver.numeral(0)),
1294 solver.lte(
1295 config_0.get_expr_for(&Variable::new("var2")).unwrap(),
1296 solver.numeral(5),
1297 ),
1298 ),
1299 ]);
1300
1301 let check_equivalent = solver.not(solver.and(
1304 solver.imp(got_smt_expr, expected_smt),
1305 solver.imp(expected_smt, got_smt_expr),
1306 ));
1307
1308 assert!(
1309 !solver
1310 .assert_and_check_expr(check_equivalent)
1311 .unwrap()
1312 .is_sat(),
1313 "got : {}\nexpected: {}",
1314 solver.display(got_smt_expr),
1315 solver.display(expected_smt)
1316 );
1317 }
1318
1319 #[test]
1320 fn test_encode_phi_r_does_not_panic() {
1321 let mut solver = SMTSolverBuilder::default().new_solver();
1322
1323 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1324 .initialize()
1325 .build();
1326
1327 let config_0 = Rc::new(ConfigCtx::new(
1328 &mut solver,
1329 &test_ta,
1330 Rc::new(HashMap::new()),
1331 0,
1332 ));
1333 let config_1 = Rc::new(ConfigCtx::new(
1334 &mut solver,
1335 &test_ta,
1336 Rc::new(HashMap::new()),
1337 1,
1338 ));
1339
1340 let step_context = LazyStepContext::new(
1341 test_ta.rules().cloned(),
1342 Rc::new(test_ta.clone()),
1343 config_0.clone(),
1344 config_1.clone(),
1345 0,
1346 &mut solver,
1347 );
1348
1349 let got_smt_expr = step_context.encode_phi_gamma(&solver);
1350
1351 let expected_smt = solver.true_();
1352
1353 let check_equivalent = solver.not(solver.and(
1356 solver.imp(got_smt_expr, expected_smt),
1357 solver.imp(expected_smt, got_smt_expr),
1358 ));
1359
1360 assert!(
1361 !solver
1362 .assert_and_check_expr(check_equivalent)
1363 .unwrap()
1364 .is_sat(),
1365 "got : {}\nexpected: {}",
1366 solver.display(got_smt_expr),
1367 solver.display(expected_smt)
1368 );
1369 }
1370
1371 #[test]
1372 fn test_encode_phi_appl() {
1373 let mut solver = SMTSolverBuilder::default().new_solver();
1374
1375 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build();
1376 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3")).build();
1377
1378 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1379 .with_locations([
1380 Location::new("loc1"),
1381 Location::new("loc2"),
1382 Location::new("loc3"),
1383 ])
1384 .unwrap()
1385 .initialize()
1386 .with_rules([r1.clone(), r2.clone()])
1387 .unwrap()
1388 .build();
1389
1390 let config_0 = Rc::new(ConfigCtx::new(
1391 &mut solver,
1392 &test_ta,
1393 Rc::new(HashMap::new()),
1394 0,
1395 ));
1396 let config_1 = Rc::new(ConfigCtx::new(
1397 &mut solver,
1398 &test_ta,
1399 Rc::new(HashMap::new()),
1400 1,
1401 ));
1402
1403 let step_context = LazyStepContext::new(
1404 test_ta.rules().cloned(),
1405 Rc::new(test_ta.clone()),
1406 config_0.clone(),
1407 config_1.clone(),
1408 0,
1409 &mut solver,
1410 );
1411
1412 let got_smt_expr = step_context.exponential_encode_phi_appl(&solver);
1413
1414 let r1 = step_context.get_expr_for(&r1).unwrap();
1415 let r2 = step_context.get_expr_for(&r2).unwrap();
1416
1417 let expected_smt = solver.and_many([
1418 solver.imp(
1420 solver.gt(r1, solver.numeral(0)),
1421 solver.gt(
1422 config_0.get_expr_for(&Location::new("loc1")).unwrap(),
1423 solver.numeral(0),
1424 ),
1425 ),
1426 solver.imp(
1428 solver.gt(r2, solver.numeral(0)),
1429 solver.or_many([
1430 solver.gt(
1431 config_0.get_expr_for(&Location::new("loc2")).unwrap(),
1432 solver.numeral(0),
1433 ),
1434 solver.and_many([
1435 solver.gt(r1, solver.numeral(0)),
1436 solver.gt(
1437 config_0.get_expr_for(&Location::new("loc1")).unwrap(),
1438 solver.numeral(0),
1439 ),
1440 ]),
1441 ]),
1442 ),
1443 ]);
1444
1445 let check_equivalent = solver.not(solver.and(
1448 solver.imp(got_smt_expr, expected_smt),
1449 solver.imp(expected_smt, got_smt_expr),
1450 ));
1451
1452 assert!(
1453 !solver
1454 .assert_and_check_expr(check_equivalent)
1455 .unwrap()
1456 .is_sat(),
1457 "got : {}\nexpected: {}",
1458 solver.display(got_smt_expr),
1459 solver.display(expected_smt)
1460 );
1461 }
1462
1463 #[test]
1464 fn test_compute_s() {
1465 let mut solver = SMTSolverBuilder::default().new_solver();
1466
1467 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build();
1468 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3")).build();
1469 let r3 = RuleBuilder::new(2, Location::new("loc3"), Location::new("loc4")).build();
1470
1471 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1472 .with_locations([
1473 Location::new("loc1"),
1474 Location::new("loc2"),
1475 Location::new("loc3"),
1476 Location::new("loc4"),
1477 ])
1478 .unwrap()
1479 .initialize()
1480 .with_rules([r1.clone(), r2.clone(), r3.clone()])
1481 .unwrap()
1482 .build();
1483
1484 let config_0 = Rc::new(ConfigCtx::new(
1485 &mut solver,
1486 &test_ta,
1487 Rc::new(HashMap::new()),
1488 0,
1489 ));
1490 let config_1 = Rc::new(ConfigCtx::new(
1491 &mut solver,
1492 &test_ta,
1493 Rc::new(HashMap::new()),
1494 1,
1495 ));
1496
1497 let step_context = LazyStepContext::new(
1498 test_ta.rules().cloned(),
1499 Rc::new(test_ta.clone()),
1500 config_0.clone(),
1501 config_1.clone(),
1502 0,
1503 &mut solver,
1504 );
1505
1506 let s = step_context.compute_s_for_r(&r3);
1507 assert_eq!(s.len(), 3, "{s:?}");
1508 assert!(s.contains(&vec![&r3]), "{s:?}");
1509 assert!(s.contains(&vec![&r3, &r2,]), "{s:?}");
1510 assert!(s.contains(&vec![&r3, &r2, &r1]), "{s:?}");
1511 }
1512
1513 #[test]
1514 fn test_encode_phi_step() {
1515 let mut solver = SMTSolverBuilder::default().new_solver();
1516
1517 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
1518 .with_guard(BooleanExpression::ComparisonExpression(
1519 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1520 ComparisonOp::Gt,
1521 Box::new(IntegerExpression::Const(1)),
1522 ))
1523 .build();
1524 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1525 .with_guard(BooleanExpression::ComparisonExpression(
1526 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1527 ComparisonOp::Lt,
1528 Box::new(IntegerExpression::Const(42)),
1529 ))
1530 .build();
1531
1532 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1533 .with_locations([
1534 Location::new("loc1"),
1535 Location::new("loc2"),
1536 Location::new("loc3"),
1537 ])
1538 .unwrap()
1539 .with_variables([Variable::new("var1"), Variable::new("var2")])
1540 .unwrap()
1541 .initialize()
1542 .with_rules([r1.clone(), r2.clone()])
1543 .unwrap()
1544 .build();
1545
1546 let config_0 = Rc::new(ConfigCtx::new(
1547 &mut solver,
1548 &test_ta,
1549 Rc::new(HashMap::new()),
1550 0,
1551 ));
1552 let config_1 = Rc::new(ConfigCtx::new(
1553 &mut solver,
1554 &test_ta,
1555 Rc::new(HashMap::new()),
1556 1,
1557 ));
1558
1559 let step_context = LazyStepContext::new(
1560 test_ta.rules().cloned(),
1561 Rc::new(test_ta.clone()),
1562 config_0.clone(),
1563 config_1.clone(),
1564 0,
1565 &mut solver,
1566 );
1567
1568 let got_smt_expr = step_context.encode_phi_step(&solver);
1569
1570 let phi_l = step_context.encode_phi_l(&solver);
1572 let phi_gamma = step_context.encode_phi_gamma(&solver);
1573 let phi_r = step_context.encode_phi_r(&solver);
1574
1575 let r1 = step_context.get_expr_for(&r1).unwrap();
1576 let r2 = step_context.get_expr_for(&r2).unwrap();
1577
1578 let expected_smt = solver.and_many([
1579 phi_l,
1580 phi_gamma,
1581 phi_r,
1582 solver.lte(solver.plus(r1, r2), solver.numeral(1)),
1584 ]);
1585
1586 let check_equivalent = solver.not(solver.and(
1589 solver.imp(got_smt_expr, expected_smt),
1590 solver.imp(expected_smt, got_smt_expr),
1591 ));
1592
1593 assert!(
1594 !solver
1595 .assert_and_check_expr(check_equivalent)
1596 .unwrap()
1597 .is_sat(),
1598 "got : {}\nexpected: {}",
1599 solver.display(got_smt_expr),
1600 solver.display(expected_smt)
1601 );
1602 }
1603
1604 #[test]
1605 fn test_get_rules_applied() {
1606 let mut solver = SMTSolverBuilder::default().new_solver();
1607
1608 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
1609 .with_guard(BooleanExpression::ComparisonExpression(
1610 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1611 ComparisonOp::Gt,
1612 Box::new(IntegerExpression::Const(1)),
1613 ))
1614 .build();
1615 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1616 .with_guard(BooleanExpression::ComparisonExpression(
1617 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1618 ComparisonOp::Lt,
1619 Box::new(IntegerExpression::Const(42)),
1620 ))
1621 .build();
1622
1623 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1624 .with_locations([
1625 Location::new("loc1"),
1626 Location::new("loc2"),
1627 Location::new("loc3"),
1628 ])
1629 .unwrap()
1630 .with_variables([Variable::new("var1"), Variable::new("var2")])
1631 .unwrap()
1632 .initialize()
1633 .with_rules([r1.clone(), r2.clone()])
1634 .unwrap()
1635 .build();
1636
1637 let config_0 = Rc::new(ConfigCtx::new(
1638 &mut solver,
1639 &test_ta,
1640 Rc::new(HashMap::new()),
1641 0,
1642 ));
1643 let config_1 = Rc::new(ConfigCtx::new(
1644 &mut solver,
1645 &test_ta,
1646 Rc::new(HashMap::new()),
1647 1,
1648 ));
1649
1650 let step_context = LazyStepContext::new(
1651 test_ta.rules().cloned(),
1652 Rc::new(test_ta.clone()),
1653 config_0.clone(),
1654 config_1.clone(),
1655 0,
1656 &mut solver,
1657 );
1658
1659 let smt_expr = solver.and_many([
1660 solver.eq(step_context.get_expr_for(&r1).unwrap(), solver.numeral(4)),
1661 solver.eq(step_context.get_expr_for(&r2).unwrap(), solver.numeral(42)),
1662 ]);
1663
1664 let res = solver.assert_and_check_expr(smt_expr).unwrap();
1665 let rules = step_context
1666 .get_rules_applied(&mut solver, res)
1667 .unwrap()
1668 .collect::<Vec<_>>();
1669
1670 assert!(rules.contains(&(r1.clone(), 4)));
1671 assert!(rules.contains(&(r2.clone(), 42)));
1672 }
1673
1674 #[test]
1675 fn test_encode_phi_gamma_with_reset() {
1676 let mut solver = SMTSolverBuilder::default().new_solver();
1677
1678 let r1 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
1679 .with_actions([Action::new_with_update(
1680 Variable::new("var1"),
1681 UpdateExpression::Inc(1),
1682 )])
1683 .build();
1684 let r2 = RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1685 .with_actions([
1686 Action::new_with_update(Variable::new("var1"), UpdateExpression::Dec(1)),
1687 Action::new_with_update(Variable::new("var2"), UpdateExpression::Inc(3)),
1688 Action::new_with_update(Variable::new("var3"), UpdateExpression::Reset),
1689 ])
1690 .build();
1691
1692 let r3 = RuleBuilder::new(2, Location::new("loc2"), Location::new("loc3")).build();
1694
1695 let test_ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1696 .with_locations([
1697 Location::new("loc1"),
1698 Location::new("loc2"),
1699 Location::new("loc3"),
1700 ])
1701 .unwrap()
1702 .with_variables([
1703 Variable::new("var1"),
1704 Variable::new("var2"),
1705 Variable::new("var3"),
1706 ])
1707 .unwrap()
1708 .initialize()
1709 .with_rules([r1.clone(), r2.clone(), r3.clone()])
1710 .unwrap()
1711 .build();
1712
1713 let config_0 = Rc::new(ConfigCtx::new(
1714 &mut solver,
1715 &test_ta,
1716 Rc::new(HashMap::new()),
1717 0,
1718 ));
1719 let config_1 = Rc::new(ConfigCtx::new(
1720 &mut solver,
1721 &test_ta,
1722 Rc::new(HashMap::new()),
1723 1,
1724 ));
1725
1726 let step_context = LazyStepContext::new(
1727 test_ta.rules().cloned(),
1728 Rc::new(test_ta.clone()),
1729 config_0.clone(),
1730 config_1.clone(),
1731 0,
1732 &mut solver,
1733 );
1734
1735 let got_smt_expr = step_context.encode_phi_gamma(&solver);
1736
1737 let expected_smt = solver.and_many([
1738 solver.eq(
1740 solver.plus_many([
1741 step_context.get_expr_for(&r1).unwrap(),
1743 solver.times(
1745 solver.negate(solver.numeral(1)),
1746 step_context.get_expr_for(&r2).unwrap(),
1747 ),
1748 ]),
1749 solver.sub(
1750 config_1.get_expr_for(&Variable::new("var1")).unwrap(),
1751 config_0.get_expr_for(&Variable::new("var1")).unwrap(),
1752 ),
1753 ),
1754 solver.eq(
1756 solver.plus_many([
1757 solver.times(solver.numeral(3), step_context.get_expr_for(&r2).unwrap()),
1759 ]),
1760 solver.sub(
1761 config_1.get_expr_for(&Variable::new("var2")).unwrap(),
1762 config_0.get_expr_for(&Variable::new("var2")).unwrap(),
1763 ),
1764 ),
1765 solver.or(
1767 solver.and(
1768 solver.eq(step_context.get_expr_for(&r2).unwrap(), solver.numeral(0)),
1769 solver.eq(
1770 solver.numeral(0),
1771 solver.sub(
1772 config_1.get_expr_for(&Variable::new("var3")).unwrap(),
1773 config_0.get_expr_for(&Variable::new("var3")).unwrap(),
1774 ),
1775 ),
1776 ),
1777 solver.and(
1778 solver.gt(step_context.get_expr_for(&r2).unwrap(), solver.numeral(0)),
1779 solver.eq(
1780 config_1.get_expr_for(&Variable::new("var3")).unwrap(),
1781 solver.numeral(0),
1782 ),
1783 ),
1784 ),
1785 ]);
1786
1787 let check_equivalent = solver.not(solver.and(
1790 solver.imp(got_smt_expr, expected_smt),
1791 solver.imp(expected_smt, got_smt_expr),
1792 ));
1793
1794 assert!(
1795 !solver
1796 .assert_and_check_expr(check_equivalent)
1797 .unwrap()
1798 .is_sat(),
1799 "got : {}\nexpected: {}",
1800 solver.display(got_smt_expr),
1801 solver.display(expected_smt)
1802 );
1803 }
1804
1805 }