1use std::{
13 collections::HashSet,
14 ops::{Index, IndexMut},
15 vec,
16};
17
18use taco_display_utils::display_iterator_stable_order;
19use taco_interval_ta::{IntervalActionEffect, IntervalConstraint};
20
21use taco_model_checker::internal_spec::upwards_closed_set::{
22 UpwardsClosedClause, UpwardsClosedSet,
23};
24use taco_smt_encoder::{
25 SMTExpr, SMTSolver,
26 expression_encoding::{EncodeToSMT, SMTVariableContext},
27};
28use taco_threshold_automaton::expressions::{Location, Parameter, Variable};
29
30use crate::{
31 acs_threshold_automaton::{
32 ACSInterval, ACSLocation, ACSThresholdAutomaton, CSIntervalAction, CSRule, CSVariable,
33 },
34 partial_ord::{PartialOrdCompResult, PartialOrder},
35};
36
37pub(crate) mod partially_ordered_cfg_map;
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
48pub struct ACSTAConfig {
49 loc_state: ACSLocState,
51 interval_state: ACSIntervalState,
53}
54
55impl ACSTAConfig {
56 pub fn interval_state(&self) -> &ACSIntervalState {
58 &self.interval_state
59 }
60
61 pub fn location_state(&self) -> &ACSLocState {
63 &self.loc_state
64 }
65
66 pub fn is_initial(&self, ta: &ACSThresholdAutomaton) -> bool {
69 let locs_all_init = self
70 .loc_state
71 .into_iterator_loc_n_procs()
72 .filter(|(_, n_procs)| *n_procs > 0)
73 .all(|(loc, _)| ta.is_initial_loc(&loc));
74
75 let intervals_all_init = self
76 .interval_state
77 .into_iterator_variable_interval()
78 .all(|(var, interval)| ta.is_initial_interval(&var, &interval));
79
80 locs_all_init && intervals_all_init
81 }
82
83 pub fn compute_potential_predecessors(
92 &self,
93 rule: &CSRule,
94 ta: &ACSThresholdAutomaton,
95 ) -> Option<impl Iterator<Item = Self>> {
96 let res_acts = rule
97 .actions()
98 .filter(|a| a.effect() == &IntervalActionEffect::Reset)
99 .collect::<Vec<_>>();
100 if !res_acts.is_empty() {
101 for res_act in res_acts.into_iter() {
102 let reset_var = res_act.variable();
103 if self.interval_state[reset_var] != ta.get_zero_interval(reset_var) {
104 return None;
105 }
106 }
107 }
108
109 let location_state = self.loc_state.compute_predecessors_min_basis(rule);
110
111 Some(
112 self.interval_state
113 .compute_all_predecessor_configs(rule, ta)
114 .into_iter()
115 .filter(|is| rule.guard().is_satisfied(is))
116 .map(move |interval_cfg| Self {
117 loc_state: location_state.clone(),
118 interval_state: interval_cfg,
119 }),
120 )
121 }
122
123 pub fn from_disjunct_target(
126 spec: &UpwardsClosedSet,
127 ta: &ACSThresholdAutomaton,
128 ) -> impl Iterator<Item = ACSTAConfig> {
129 spec.upwards_closed_clauses()
130 .flat_map(|target| Self::from_target_config(target, ta))
131 }
132
133 pub fn from_target_config(
135 spec: &UpwardsClosedClause,
136 ta: &ACSThresholdAutomaton,
137 ) -> HashSet<Self> {
138 let interval_constr = ta
139 .interval_ta
140 .get_interval_constraint(spec.variable_constr())
141 .expect("Failed to derive interval constraint for target");
142
143 let interval_cfgs = ACSIntervalState::get_target_interval_configs(interval_constr, ta);
144
145 let location_cfg = ACSLocState::compute_target_cfg(spec, ta);
146
147 interval_cfgs
148 .into_iter()
149 .map(|interval_cfg| Self {
150 loc_state: location_cfg.clone(),
151 interval_state: interval_cfg,
152 })
153 .collect()
154 }
155
156 pub fn display(&self, ta: &ACSThresholdAutomaton) -> String {
158 format!(
159 "locations: ({}), variables: ({})",
160 self.loc_state.display(ta),
161 self.interval_state.display(ta)
162 )
163 }
164
165 pub fn display_compact(&self, ta: &ACSThresholdAutomaton) -> String {
168 format!(
169 "locations: ({}), variables: ({})",
170 self.loc_state.display_compact(ta),
171 self.interval_state.display_compact(ta)
172 )
173 }
174
175 pub fn encode_config_constraints_to_smt<
178 C: SMTVariableContext<Parameter> + SMTVariableContext<Variable> + SMTVariableContext<Location>,
179 >(
180 &self,
181 ta: &ACSThresholdAutomaton,
182 solver: &SMTSolver,
183 ctx: &C,
184 ) -> SMTExpr {
185 let loc_constr = self
187 .location_state()
188 .into_iterator_loc_n_procs()
189 .filter(|(_, n)| *n > 0)
190 .map(|(loc, n)| {
191 let loc = ctx
192 .get_expr_for(ta.idx_ctx.get_loc(&loc))
193 .expect("Expected location SMT variable to be declared");
194
195 solver.gte(loc, solver.numeral(n))
196 });
197
198 let interval_constr = self
199 .interval_state()
200 .into_iterator_variable_interval()
201 .filter(|(v, _)| {
202 !ta.interval_ta
211 .get_helper_vars_for_sumvars()
212 .contains_key(ta.idx_ctx.get_var(v))
213 })
214 .map(|(var, i)| {
215 let i = ta.idx_ctx.get_interval(&var, &i);
216 let var = ta.idx_ctx.get_var(&var);
217
218 let interval_expr = i.encode_into_boolean_expr(var);
219
220 interval_expr
221 .encode_to_smt_with_ctx(solver, ctx)
222 .expect("Failed to encode interval constraint")
223 });
224
225 solver.and_many(loc_constr.chain(interval_constr))
226 }
227}
228
229impl PartialOrder for ACSTAConfig {
230 #[inline(always)]
231 fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
232 let interval_comp = self.interval_state.part_cmp(&other.interval_state);
233 if interval_comp == PartialOrdCompResult::Incomparable {
234 return PartialOrdCompResult::Incomparable;
235 }
236
237 let loc_comp = self.loc_state.part_cmp(&other.loc_state);
238 interval_comp.combine(loc_comp)
239 }
240}
241
242#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
250pub struct ACSIntervalState {
251 v_to_interval: Vec<ACSInterval>,
254}
255
256impl ACSIntervalState {
257 pub fn into_iterator_variable_interval(
258 &self,
259 ) -> impl Iterator<Item = (CSVariable, ACSInterval)> {
260 self.v_to_interval
261 .iter()
262 .enumerate()
263 .map(|(var, interval)| (CSVariable(var), *interval))
264 }
265
266 fn new_cfg_all_zero_interval(ta: &ACSThresholdAutomaton) -> Self {
273 Self {
274 v_to_interval: vec![ACSInterval(0); ta.variables().count()],
275 }
276 }
277
278 pub fn all_possible_interval_configs(ta: &ACSThresholdAutomaton) -> Vec<ACSIntervalState> {
280 let zero_interval = Vec::from([Self::new_cfg_all_zero_interval(ta)]);
281
282 ta.variables().fold(zero_interval, |cfgs, var| {
283 cfgs.iter()
284 .flat_map(|cfg| {
285 ta.get_all_intervals(var).map(|interval| {
286 let mut new_cfg = cfg.clone();
287 new_cfg[var] = *interval;
288 new_cfg
289 })
290 })
291 .collect()
292 })
293 }
294
295 pub fn get_target_interval_configs(
298 constr: IntervalConstraint,
299 ta: &ACSThresholdAutomaton,
300 ) -> Vec<ACSIntervalState> {
301 if constr == IntervalConstraint::True {
302 return Self::all_possible_interval_configs(ta);
303 }
304
305 let zero_interval = Vec::from([Self::new_cfg_all_zero_interval(ta)]);
306
307 ta.variables().fold(zero_interval, |cfgs, var| {
308 cfgs.iter()
309 .flat_map(|cfg| {
310 let org_var = ta.idx_ctx.get_var(var);
311
312 ta.get_interval_ta()
313 .get_enabled_intervals(&constr, org_var)
314 .map(|interval| {
315 let interval = ta.idx_ctx.to_cs_interval(var, interval);
316 let mut new_cfg = cfg.clone();
317 new_cfg[var] = interval;
318 new_cfg
319 })
320 })
321 .collect()
322 })
323 }
324
325 pub fn compute_all_predecessor_configs(
327 &self,
328 rule: &CSRule,
329 ta: &ACSThresholdAutomaton,
330 ) -> Vec<Self> {
331 rule.actions()
334 .fold(Vec::from([self.clone()]), |predecessors, act| {
335 predecessors
337 .into_iter()
338 .flat_map(|interval_cfg| {
339 Self::compute_possible_interval_state_before_application_of_act(
340 &interval_cfg,
341 act,
342 ta,
343 )
344 })
345 .collect()
346 })
347 .into_iter()
348 .filter(|is| rule.is_guard_enabled(is))
349 .collect()
350 }
351
352 fn compute_possible_interval_state_before_application_of_act(
362 &self,
363 act: &CSIntervalAction,
364 ta: &ACSThresholdAutomaton,
365 ) -> HashSet<Self> {
366 let mut possible_pred_intervals = HashSet::new();
367
368 match act.effect() {
369 IntervalActionEffect::Inc(i) => {
370 let prev_interval = ta.get_prev_interval(act.variable(), &self[act.variable()]);
371
372 if !ta
375 .idx_ctx
376 .get_interval(act.variable(), &self[act.variable()])
377 .check_sub_always_out_of_interval(*i)
378 {
379 possible_pred_intervals.insert(self.clone());
380 }
381
382 if let Some(prev_interval) = prev_interval {
385 let mut prev_interval_cfg = self.clone();
386 prev_interval_cfg[act.variable()] = prev_interval;
387
388 possible_pred_intervals.insert(prev_interval_cfg);
389 }
390 }
391 IntervalActionEffect::Dec(d) => {
392 let next_interval = ta.get_next_interval(act.variable(), &self[act.variable()]);
393
394 if !ta
397 .idx_ctx
398 .get_interval(act.variable(), &self[act.variable()])
399 .check_add_always_out_of_interval(*d)
400 {
401 possible_pred_intervals.insert(self.clone());
402 }
403
404 if let Some(nxt_interval) = next_interval {
407 let mut nxt_interval_cfg = self.clone();
408 nxt_interval_cfg[act.variable()] = nxt_interval;
409
410 possible_pred_intervals.insert(nxt_interval_cfg);
411 }
412 }
413 IntervalActionEffect::Reset => {
414 let potential_intervals = ta.get_all_intervals(act.variable()).map(|interval| {
416 let mut new_cfg = self.clone();
417 new_cfg[act.variable()] = *interval;
418
419 new_cfg
420 });
421
422 possible_pred_intervals.extend(potential_intervals);
423 }
424 }
425
426 possible_pred_intervals
427 }
428
429 pub fn display(&self, ta: &ACSThresholdAutomaton) -> String {
431 display_iterator_stable_order(
432 self.into_iterator_variable_interval()
433 .map(|(var, interval)| {
434 format!("{} : {}", var.display(ta), interval.display(&var, ta))
435 }),
436 )
437 }
438
439 pub fn display_compact(&self, ta: &ACSThresholdAutomaton) -> String {
442 display_iterator_stable_order(
443 self.into_iterator_variable_interval()
444 .filter(|(var, interval)| ta.get_zero_interval(var) != *interval)
445 .map(|(var, interval)| {
446 format!("{} : {}", var.display(ta), interval.display(&var, ta))
447 }),
448 )
449 }
450}
451
452impl Index<&CSVariable> for ACSIntervalState {
453 type Output = ACSInterval;
454
455 fn index(&self, var: &CSVariable) -> &Self::Output {
456 &self.v_to_interval[var.0]
457 }
458}
459
460impl IndexMut<&CSVariable> for ACSIntervalState {
461 fn index_mut(&mut self, var: &CSVariable) -> &mut Self::Output {
462 &mut self.v_to_interval[var.0]
463 }
464}
465
466impl Index<CSVariable> for ACSIntervalState {
467 type Output = ACSInterval;
468
469 fn index(&self, var: CSVariable) -> &Self::Output {
470 &self.v_to_interval[var.0]
471 }
472}
473
474impl IndexMut<CSVariable> for ACSIntervalState {
475 fn index_mut(&mut self, var: CSVariable) -> &mut Self::Output {
476 &mut self.v_to_interval[var.0]
477 }
478}
479
480impl PartialOrder for ACSInterval {
481 #[inline(always)]
482 fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
483 if self == other {
484 return PartialOrdCompResult::Equal;
485 }
486 PartialOrdCompResult::Incomparable
487 }
488}
489
490impl PartialOrder for ACSIntervalState {
491 fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
492 self.v_to_interval.part_cmp(&other.v_to_interval)
493 }
494}
495
496#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
504pub struct ACSLocState {
505 loc_state: Vec<u32>,
507}
508
509impl ACSLocState {
510 pub fn into_iterator_loc_n_procs(&self) -> impl Iterator<Item = (ACSLocation, u32)> {
512 self.loc_state
513 .iter()
514 .enumerate()
515 .map(|(i, n)| (ACSLocation(i), *n))
516 }
517
518 pub fn compute_predecessors_min_basis(&self, rule: &CSRule) -> Self {
527 let mut new_loc_state = self.clone();
528
529 if self[rule.target()] > 0 {
530 new_loc_state[rule.target()] -= 1;
531 }
532
533 new_loc_state[rule.source()] += 1;
534
535 new_loc_state
536 }
537
538 fn new_all_zero(ta: &ACSThresholdAutomaton) -> Self {
539 Self {
540 loc_state: vec![0; ta.locations().count()],
541 }
542 }
543
544 pub fn compute_target_cfg(spec: &UpwardsClosedClause, ta: &ACSThresholdAutomaton) -> Self {
546 debug_assert!(
547 spec.locs_to_uncover().count() == 0,
548 "This model checker currently does not support reachability constraints, this should have been caught"
549 );
550
551 let mut target = Self::new_all_zero(ta);
552
553 for (loc, n) in spec.locs_to_cover() {
554 target[ta.idx_ctx.to_cs_loc(loc)] = *n;
555 }
556
557 target
558 }
559
560 pub fn display(&self, ta: &ACSThresholdAutomaton) -> String {
562 display_iterator_stable_order(
563 self.into_iterator_loc_n_procs()
564 .map(|(l, n)| format!("{} : {}", l.display(ta), n)),
565 )
566 }
567
568 pub fn display_compact(&self, ta: &ACSThresholdAutomaton) -> String {
571 display_iterator_stable_order(
572 self.into_iterator_loc_n_procs()
573 .filter(|(_, n)| *n > 0)
574 .map(|(l, n)| format!("{} : {}", l.display(ta), n)),
575 )
576 }
577}
578
579impl Index<&ACSLocation> for ACSLocState {
580 type Output = u32;
581
582 fn index(&self, loc: &ACSLocation) -> &Self::Output {
583 &self.loc_state[loc.0]
584 }
585}
586
587impl IndexMut<&ACSLocation> for ACSLocState {
588 fn index_mut(&mut self, loc: &ACSLocation) -> &mut Self::Output {
589 &mut self.loc_state[loc.0]
590 }
591}
592
593impl Index<ACSLocation> for ACSLocState {
594 type Output = u32;
595
596 fn index(&self, loc: ACSLocation) -> &Self::Output {
597 &self.loc_state[loc.0]
598 }
599}
600
601impl IndexMut<ACSLocation> for ACSLocState {
602 fn index_mut(&mut self, loc: ACSLocation) -> &mut Self::Output {
603 &mut self.loc_state[loc.0]
604 }
605}
606
607impl PartialOrder for ACSLocState {
608 fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
609 self.loc_state.part_cmp(&other.loc_state)
610 }
611}
612
613#[cfg(test)]
614mod mock_objects {
615 use crate::acs_threshold_automaton::{
616 ACSInterval, ACSThresholdAutomaton,
617 configuration::{ACSIntervalState, ACSLocState, ACSTAConfig},
618 };
619
620 impl ACSTAConfig {
621 pub fn new_mock_from_vecs(loc: Vec<u32>, intervals: Vec<ACSInterval>) -> Self {
622 ACSTAConfig {
623 loc_state: super::ACSLocState { loc_state: loc },
624 interval_state: super::ACSIntervalState {
625 v_to_interval: intervals,
626 },
627 }
628 }
629
630 pub fn new_mock(loc: ACSLocState, int: ACSIntervalState) -> Self {
631 Self {
632 loc_state: loc,
633 interval_state: int,
634 }
635 }
636 }
637
638 impl ACSLocState {
639 pub fn new_empty(ta: &ACSThresholdAutomaton) -> Self {
640 Self::new_all_zero(ta)
641 }
642 }
643
644 impl ACSIntervalState {
645 pub fn new_mock_from_vec(intervals: Vec<ACSInterval>) -> Self {
646 ACSIntervalState {
647 v_to_interval: intervals,
648 }
649 }
650
651 pub fn new_mock_zero(ta: &ACSThresholdAutomaton) -> Self {
652 Self::new_cfg_all_zero_interval(ta)
653 }
654
655 pub fn get_all_possible_intervals(ta: &ACSThresholdAutomaton) -> Vec<ACSIntervalState> {
656 Self::all_possible_interval_configs(ta)
657 }
658 }
659}
660
661#[cfg(test)]
662mod tests {
663 use std::collections::{HashMap, HashSet};
664
665 use taco_display_utils::join_iterator;
666 use taco_interval_ta::{
667 IntervalActionEffect, IntervalConstraint, builder::IntervalTABuilder, interval::Interval,
668 };
669 use taco_model_checker::internal_spec::upwards_closed_set::UpwardsClosedSet;
670 use taco_smt_encoder::SMTSolverBuilder;
671 use taco_threshold_automaton::{
672 expressions::{
673 BooleanExpression, ComparisonOp, IntegerExpression, Location, Parameter, Variable,
674 },
675 general_threshold_automaton::{
676 Action,
677 builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
678 },
679 lia_threshold_automaton::LIAThresholdAutomaton,
680 };
681
682 use crate::{
683 acs_threshold_automaton::{
684 ACSInterval, ACSLocation, ACSThresholdAutomaton, CSIntervalAction,
685 CSIntervalConstraint, CSRule, CSVariable,
686 configuration::{ACSIntervalState, ACSLocState, ACSTAConfig},
687 },
688 partial_ord::{PartialOrdCompResult, PartialOrder},
689 };
690
691 #[test]
692 fn test_cstaconfig_getters() {
693 let config = ACSTAConfig {
694 loc_state: ACSLocState {
695 loc_state: vec![1, 2, 3],
696 },
697 interval_state: ACSIntervalState {
698 v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
699 },
700 };
701 assert_eq!(
702 config.location_state(),
703 &ACSLocState {
704 loc_state: vec![1, 2, 3],
705 }
706 );
707 assert_eq!(
708 config.interval_state(),
709 &ACSIntervalState {
710 v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
711 }
712 );
713 }
714
715 #[test]
716 fn test_cstaconfig_is_initial() {
717 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
718 .with_variables([Variable::new("x"), Variable::new("y")])
719 .unwrap()
720 .with_locations([
721 Location::new("l1"),
722 Location::new("l2"),
723 Location::new("l3"),
724 ])
725 .unwrap()
726 .with_parameter(Parameter::new("n"))
727 .unwrap()
728 .initialize()
729 .with_resilience_condition(BooleanExpression::ComparisonExpression(
730 Box::new(IntegerExpression::Param(Parameter::new("n"))),
731 ComparisonOp::Gt,
732 Box::new(IntegerExpression::Const(3)),
733 ))
734 .unwrap()
735 .with_initial_location_constraints([
736 BooleanExpression::ComparisonExpression(
737 Box::new(IntegerExpression::Atom(Location::new("l2"))),
738 ComparisonOp::Eq,
739 Box::new(IntegerExpression::Const(0)),
740 ),
741 BooleanExpression::ComparisonExpression(
742 Box::new(IntegerExpression::Atom(Location::new("l3"))),
743 ComparisonOp::Eq,
744 Box::new(IntegerExpression::Const(0)),
745 ),
746 ])
747 .unwrap()
748 .with_initial_variable_constraints([
749 BooleanExpression::ComparisonExpression(
750 Box::new(IntegerExpression::Atom(Variable::new("x"))),
751 ComparisonOp::Eq,
752 Box::new(IntegerExpression::Const(0)),
753 ),
754 BooleanExpression::ComparisonExpression(
755 Box::new(IntegerExpression::Atom(Variable::new("y"))),
756 ComparisonOp::Eq,
757 Box::new(IntegerExpression::Const(0)),
758 ),
759 ])
760 .unwrap()
761 .with_rule(
762 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
763 .with_guard(BooleanExpression::ComparisonExpression(
764 Box::new(IntegerExpression::Atom(Variable::new("x"))),
765 ComparisonOp::Gt,
766 Box::new(IntegerExpression::Const(2)),
767 ))
768 .with_action(
769 Action::new(
770 Variable::new("x"),
771 IntegerExpression::Const(1)
772 + IntegerExpression::Atom(Variable::new("x")),
773 )
774 .unwrap(),
775 )
776 .build(),
777 )
778 .unwrap()
779 .build();
780 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
781 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
782 .build()
783 .unwrap();
784 let interval_ta = interval_tas.next().unwrap();
785 assert!(interval_tas.next().is_none());
786 let ta = ACSThresholdAutomaton::new(interval_ta);
788 let l1 = ta.idx_ctx.to_cs_loc(&Location::new("l1"));
789 let l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
790
791 let mut loc_state = ACSLocState::new_all_zero(&ta);
792 loc_state[l1] = 1;
793
794 let config1 = ACSTAConfig {
795 loc_state,
796 interval_state: ACSIntervalState {
797 v_to_interval: vec![ACSInterval(0), ACSInterval(0)],
798 },
799 };
800
801 assert!(config1.is_initial(&ta));
802
803 let mut loc_state = ACSLocState::new_all_zero(&ta);
804 loc_state[l1] = 420;
805
806 let config2 = ACSTAConfig {
807 loc_state,
808 interval_state: ACSIntervalState {
809 v_to_interval: vec![ACSInterval(0), ACSInterval(0)],
810 },
811 };
812
813 assert!(config2.is_initial(&ta));
814
815 let mut loc_state = ACSLocState::new_all_zero(&ta);
816 loc_state[l1] = 1;
817 loc_state[l2] = 2;
818
819 let config2 = ACSTAConfig {
820 loc_state,
821 interval_state: ACSIntervalState {
822 v_to_interval: vec![ACSInterval(0), ACSInterval(0)],
823 },
824 };
825
826 assert!(!config2.is_initial(&ta));
827
828 let mut loc_state = ACSLocState::new_all_zero(&ta);
829 loc_state[l1] = 1;
830
831 let config2 = ACSTAConfig {
832 loc_state,
833 interval_state: ACSIntervalState {
834 v_to_interval: vec![ACSInterval(0), ACSInterval(1)],
835 },
836 };
837
838 assert!(!config2.is_initial(&ta));
839
840 let mut loc_state = ACSLocState::new_all_zero(&ta);
841 loc_state[l1] = 1;
842 loc_state[l2] = 2;
843
844 let config2 = ACSTAConfig {
845 loc_state,
846 interval_state: ACSIntervalState {
847 v_to_interval: vec![ACSInterval(1), ACSInterval(1)],
848 },
849 };
850
851 assert!(!config2.is_initial(&ta));
852 }
853
854 #[test]
855 fn test_cstaconfig_compute_min_predecessor() {
856 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
857 .with_variables([Variable::new("x"), Variable::new("y")])
858 .unwrap()
859 .with_locations([
860 Location::new("l1"),
861 Location::new("l2"),
862 Location::new("l3"),
863 ])
864 .unwrap()
865 .with_parameter(Parameter::new("n"))
866 .unwrap()
867 .initialize()
868 .with_resilience_condition(BooleanExpression::ComparisonExpression(
869 Box::new(IntegerExpression::Param(Parameter::new("n"))),
870 ComparisonOp::Gt,
871 Box::new(IntegerExpression::Const(3)),
872 ))
873 .unwrap()
874 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
875 Box::new(IntegerExpression::Atom(Location::new("l1"))),
876 ComparisonOp::Eq,
877 Box::new(IntegerExpression::Const(0)),
878 ))
879 .unwrap()
880 .with_rule(
881 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
882 .with_guard(BooleanExpression::ComparisonExpression(
883 Box::new(IntegerExpression::Atom(Variable::new("x"))),
884 ComparisonOp::Geq,
885 Box::new(IntegerExpression::Const(3)),
886 ))
887 .with_action(
888 Action::new(
889 Variable::new("x"),
890 IntegerExpression::Const(1)
891 + IntegerExpression::Atom(Variable::new("x")),
892 )
893 .unwrap(),
894 )
895 .build(),
896 )
897 .unwrap()
898 .build();
899 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
900 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
901 .build()
902 .unwrap();
903 let interval_ta = interval_tas.next().unwrap();
904 assert!(interval_tas.next().is_none());
905 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
906 let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
907 let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
908
909 let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
910 interval_state[var_x] = ACSInterval(1);
911 interval_state[var_y] = ACSInterval(0);
912
913 let rule = CSRule {
914 id: 0,
915 source: ACSLocation(0),
916 target: ACSLocation(1),
917 guard: CSIntervalConstraint::True,
918 actions: vec![CSIntervalAction {
919 var: var_x,
920 effect: IntervalActionEffect::Inc(1),
921 }],
922 };
923
924 let config = ACSTAConfig {
925 loc_state: ACSLocState {
926 loc_state: vec![0, 1, 0],
927 },
928 interval_state: interval_state.clone(),
929 };
930
931 let mut pred_1 = config.clone();
932 pred_1.loc_state.loc_state = vec![1, 0, 0];
933
934 let mut pred_2 = config.clone();
935 pred_2.loc_state.loc_state = vec![1, 0, 0];
936 pred_2.interval_state[var_x] = ACSInterval(0);
937
938 let got_min_basis = config
939 .compute_potential_predecessors(&rule, &cs_ta)
940 .unwrap()
941 .collect::<HashSet<_>>();
942 let expected_min_basis = HashSet::from([pred_1, pred_2]);
943
944 assert_eq!(got_min_basis, expected_min_basis);
945 }
946
947 #[test]
948 fn test_compute_pred_none_if_var_of_reset_not_in_zero() {
949 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
950 .with_variables([Variable::new("x"), Variable::new("y")])
951 .unwrap()
952 .with_locations([
953 Location::new("l1"),
954 Location::new("l2"),
955 Location::new("l3"),
956 ])
957 .unwrap()
958 .with_parameter(Parameter::new("n"))
959 .unwrap()
960 .initialize()
961 .with_resilience_condition(BooleanExpression::ComparisonExpression(
962 Box::new(IntegerExpression::Param(Parameter::new("n"))),
963 ComparisonOp::Gt,
964 Box::new(IntegerExpression::Const(3)),
965 ))
966 .unwrap()
967 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
968 Box::new(IntegerExpression::Atom(Location::new("l1"))),
969 ComparisonOp::Eq,
970 Box::new(IntegerExpression::Const(0)),
971 ))
972 .unwrap()
973 .with_rule(
974 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
975 .with_guard(BooleanExpression::ComparisonExpression(
976 Box::new(IntegerExpression::Atom(Variable::new("x"))),
977 ComparisonOp::Gt,
978 Box::new(IntegerExpression::Const(2)),
979 ))
980 .with_action(
981 Action::new(Variable::new("x"), IntegerExpression::Const(0)).unwrap(),
982 )
983 .build(),
984 )
985 .unwrap()
986 .build();
987 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
988 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
989 .build()
990 .unwrap();
991 let interval_ta = interval_tas.next().unwrap();
992 assert!(interval_tas.next().is_none());
993 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
994 let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
995 let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
996
997 let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
998 interval_state[var_x] = ACSInterval(1);
999 interval_state[var_y] = ACSInterval(0);
1000
1001 let rule = CSRule {
1002 id: 0,
1003 source: ACSLocation(0),
1004 target: ACSLocation(1),
1005 guard: CSIntervalConstraint::True,
1006 actions: vec![CSIntervalAction {
1007 var: var_x,
1008 effect: IntervalActionEffect::Reset,
1009 }],
1010 };
1011
1012 let config = ACSTAConfig {
1013 loc_state: ACSLocState {
1014 loc_state: vec![0, 1, 0],
1015 },
1016 interval_state: interval_state.clone(),
1017 };
1018
1019 let got_pred = config.compute_potential_predecessors(&rule, &cs_ta);
1020 assert!(got_pred.is_none())
1021 }
1022
1023 #[test]
1024 fn test_castaconfig_from_disjunct_target() {
1025 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1026 .with_variables([Variable::new("x"), Variable::new("y")])
1027 .unwrap()
1028 .with_locations([
1029 Location::new("l1"),
1030 Location::new("l2"),
1031 Location::new("l3"),
1032 ])
1033 .unwrap()
1034 .with_parameter(Parameter::new("n"))
1035 .unwrap()
1036 .initialize()
1037 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1038 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1039 ComparisonOp::Gt,
1040 Box::new(IntegerExpression::Const(3)),
1041 ))
1042 .unwrap()
1043 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1044 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1045 ComparisonOp::Eq,
1046 Box::new(IntegerExpression::Const(0)),
1047 ))
1048 .unwrap()
1049 .with_rule(
1050 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1051 .with_guard(BooleanExpression::ComparisonExpression(
1052 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1053 ComparisonOp::Gt,
1054 Box::new(IntegerExpression::Const(2)),
1055 ))
1056 .with_action(
1057 Action::new(
1058 Variable::new("x"),
1059 IntegerExpression::Const(1)
1060 + IntegerExpression::Atom(Variable::new("x")),
1061 )
1062 .unwrap(),
1063 )
1064 .build(),
1065 )
1066 .unwrap()
1067 .build();
1068 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1069 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1070 .build()
1071 .unwrap();
1072 let interval_ta = interval_tas.next().unwrap();
1073 assert!(interval_tas.next().is_none());
1074 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1075 let loc_l2 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1076 let loc_l3 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1077
1078 let target_1 = UpwardsClosedSet::new_cover([Location::new("l3")]);
1079 let target_2 = UpwardsClosedSet::new_cover([Location::new("l2")]);
1080 let disj = target_1 | target_2;
1081
1082 let got_configs = ACSTAConfig::from_disjunct_target(&disj, &cs_ta).collect::<HashSet<_>>();
1083
1084 let interval_states = ACSIntervalState::all_possible_interval_configs(&cs_ta);
1085 let configs_l2 = interval_states.iter().map(|is| {
1086 let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1087 loc_state[loc_l2] = 1;
1088
1089 ACSTAConfig {
1090 loc_state,
1091 interval_state: is.clone(),
1092 }
1093 });
1094 let configs_l3 = interval_states.iter().map(|is| {
1095 let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1096 loc_state[loc_l3] = 1;
1097
1098 ACSTAConfig {
1099 loc_state,
1100 interval_state: is.clone(),
1101 }
1102 });
1103 let expected_configs = configs_l2.chain(configs_l3).collect::<HashSet<_>>();
1104
1105 assert_eq!(got_configs, expected_configs)
1106 }
1107
1108 #[test]
1109 fn test_castaconfig_from_disjunct_target_with_var_constr() {
1110 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1111 .with_variables([Variable::new("x"), Variable::new("y")])
1112 .unwrap()
1113 .with_locations([
1114 Location::new("l1"),
1115 Location::new("l2"),
1116 Location::new("l3"),
1117 ])
1118 .unwrap()
1119 .with_parameter(Parameter::new("n"))
1120 .unwrap()
1121 .initialize()
1122 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1123 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1124 ComparisonOp::Gt,
1125 Box::new(IntegerExpression::Const(3)),
1126 ))
1127 .unwrap()
1128 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1129 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1130 ComparisonOp::Eq,
1131 Box::new(IntegerExpression::Const(0)),
1132 ))
1133 .unwrap()
1134 .with_rule(
1135 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1136 .with_guard(BooleanExpression::ComparisonExpression(
1137 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1138 ComparisonOp::Geq,
1139 Box::new(IntegerExpression::Const(3)),
1140 ))
1141 .with_action(
1142 Action::new(
1143 Variable::new("x"),
1144 IntegerExpression::Const(1)
1145 + IntegerExpression::Atom(Variable::new("x")),
1146 )
1147 .unwrap(),
1148 )
1149 .build(),
1150 )
1151 .unwrap()
1152 .build();
1153 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1154 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1155 .build()
1156 .unwrap();
1157 let interval_ta = interval_tas.next().unwrap();
1158 assert!(interval_tas.next().is_none());
1159
1160 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1161 let loc_l2 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1162 let loc_l3 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1163
1164 let cstr = BooleanExpression::ComparisonExpression(
1165 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1166 ComparisonOp::Eq,
1167 Box::new(IntegerExpression::Const(0)),
1168 );
1169
1170 let target_1 = UpwardsClosedSet::new_var_constraint(cstr.clone().try_into().unwrap())
1172 & UpwardsClosedSet::new_cover_int([(Location::new("l3"), 1)]);
1173
1174 let target_2 = UpwardsClosedSet::new_var_constraint(cstr.clone().try_into().unwrap())
1176 & UpwardsClosedSet::new_cover_int([(Location::new("l2"), 1)]);
1177 let disj = target_1 | target_2;
1178
1179 let got_configs = ACSTAConfig::from_disjunct_target(&disj, &cs_ta).collect::<HashSet<_>>();
1180
1181 let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1182 let mut state2 = interval_state.clone();
1183 state2[cs_ta.to_cs_var(&Variable::new("y"))] = ACSInterval(1);
1184
1185 let interval_states = [interval_state, state2];
1186 let configs_l2 = interval_states.iter().map(|is| {
1187 let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1188 loc_state[loc_l2] = 1;
1189
1190 ACSTAConfig {
1191 loc_state,
1192 interval_state: is.clone(),
1193 }
1194 });
1195 let configs_l3 = interval_states.iter().map(|is| {
1196 let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1197 loc_state[loc_l3] = 1;
1198
1199 ACSTAConfig {
1200 loc_state,
1201 interval_state: is.clone(),
1202 }
1203 });
1204 let expected_configs = configs_l2.chain(configs_l3).collect::<HashSet<_>>();
1205
1206 assert_eq!(
1207 got_configs,
1208 expected_configs,
1209 "Got: {}\nExpected: {}\n",
1210 join_iterator(got_configs.iter().map(|c| c.display(&cs_ta)), "; "),
1211 join_iterator(expected_configs.iter().map(|c| c.display(&cs_ta)), "; ")
1212 )
1213 }
1214
1215 #[test]
1216 fn test_cstaconfig_partial_order() {
1217 let config1 = ACSTAConfig {
1218 loc_state: ACSLocState {
1219 loc_state: vec![1, 2, 3],
1220 },
1221 interval_state: ACSIntervalState {
1222 v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
1223 },
1224 };
1225 let config2 = ACSTAConfig {
1226 loc_state: ACSLocState {
1227 loc_state: vec![2, 2, 4],
1228 },
1229 interval_state: ACSIntervalState {
1230 v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
1231 },
1232 };
1233
1234 assert_eq!(config1.part_cmp(&config1), PartialOrdCompResult::Equal);
1235 assert_eq!(config1.part_cmp(&config2), PartialOrdCompResult::Smaller);
1236 assert_eq!(config2.part_cmp(&config1), PartialOrdCompResult::Greater);
1237
1238 let config3 = ACSTAConfig {
1239 loc_state: ACSLocState {
1240 loc_state: vec![0, 2, 6],
1241 },
1242 interval_state: ACSIntervalState {
1243 v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
1244 },
1245 };
1246 assert_eq!(
1247 config1.part_cmp(&config3),
1248 PartialOrdCompResult::Incomparable
1249 );
1250 assert_eq!(
1251 config3.part_cmp(&config1),
1252 PartialOrdCompResult::Incomparable
1253 );
1254
1255 let config4 = ACSTAConfig {
1256 loc_state: ACSLocState {
1257 loc_state: vec![1, 2, 3],
1258 },
1259 interval_state: ACSIntervalState {
1260 v_to_interval: vec![ACSInterval(0), ACSInterval(2), ACSInterval(1)],
1261 },
1262 };
1263 assert_eq!(
1264 config1.part_cmp(&config4),
1265 PartialOrdCompResult::Incomparable
1266 );
1267 assert_eq!(
1268 config4.part_cmp(&config1),
1269 PartialOrdCompResult::Incomparable
1270 );
1271 }
1272
1273 #[test]
1274 fn test_cstaconfig_display() {
1275 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1276 .with_variables([Variable::new("x"), Variable::new("y")])
1277 .unwrap()
1278 .with_locations([
1279 Location::new("l1"),
1280 Location::new("l2"),
1281 Location::new("l3"),
1282 ])
1283 .unwrap()
1284 .with_parameter(Parameter::new("n"))
1285 .unwrap()
1286 .initialize()
1287 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1288 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1289 ComparisonOp::Gt,
1290 Box::new(IntegerExpression::Const(3)),
1291 ))
1292 .unwrap()
1293 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1294 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1295 ComparisonOp::Eq,
1296 Box::new(IntegerExpression::Const(0)),
1297 ))
1298 .unwrap()
1299 .with_rule(
1300 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1301 .with_guard(BooleanExpression::ComparisonExpression(
1302 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1303 ComparisonOp::Gt,
1304 Box::new(IntegerExpression::Const(2)),
1305 ))
1306 .with_action(
1307 Action::new(
1308 Variable::new("x"),
1309 IntegerExpression::Const(1)
1310 + IntegerExpression::Atom(Variable::new("x")),
1311 )
1312 .unwrap(),
1313 )
1314 .build(),
1315 )
1316 .unwrap()
1317 .build();
1318 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1319 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1320 .build()
1321 .unwrap();
1322 let interval_ta = interval_tas.next().unwrap();
1323 assert!(interval_tas.next().is_none());
1324 let ta = ACSThresholdAutomaton::new(interval_ta);
1325
1326 let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
1327
1328 let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
1329 interval_state[var_x] = ACSInterval(3);
1330
1331 let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1332 let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1333
1334 let mut loc_state = ACSLocState::new_all_zero(&ta);
1335 loc_state[loc_l2] = 1;
1336 loc_state[loc_l3] = 42;
1337
1338 let config = ACSTAConfig {
1339 loc_state,
1340 interval_state,
1341 };
1342
1343 let got_string = config.display(&ta);
1344 let expected_string =
1345 "locations: (l1 : 0, l2 : 1, l3 : 42), variables: (x : ]2, ∞[, y : [0, 1[)";
1346
1347 assert_eq!(&got_string, expected_string);
1348 }
1349
1350 #[test]
1351 fn test_cstaconfig_display_compact() {
1352 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1353 .with_variables([Variable::new("x"), Variable::new("y")])
1354 .unwrap()
1355 .with_locations([
1356 Location::new("l1"),
1357 Location::new("l2"),
1358 Location::new("l3"),
1359 ])
1360 .unwrap()
1361 .with_parameter(Parameter::new("n"))
1362 .unwrap()
1363 .initialize()
1364 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1365 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1366 ComparisonOp::Gt,
1367 Box::new(IntegerExpression::Const(3)),
1368 ))
1369 .unwrap()
1370 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1371 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1372 ComparisonOp::Eq,
1373 Box::new(IntegerExpression::Const(0)),
1374 ))
1375 .unwrap()
1376 .with_rule(
1377 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1378 .with_guard(BooleanExpression::ComparisonExpression(
1379 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1380 ComparisonOp::Gt,
1381 Box::new(IntegerExpression::Const(2)),
1382 ))
1383 .with_action(
1384 Action::new(
1385 Variable::new("x"),
1386 IntegerExpression::Const(1)
1387 + IntegerExpression::Atom(Variable::new("x")),
1388 )
1389 .unwrap(),
1390 )
1391 .build(),
1392 )
1393 .unwrap()
1394 .build();
1395 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1396 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1397 .build()
1398 .unwrap();
1399 let interval_ta = interval_tas.next().unwrap();
1400 assert!(interval_tas.next().is_none());
1401 let ta = ACSThresholdAutomaton::new(interval_ta);
1402
1403 let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
1404
1405 let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
1406 interval_state[var_x] = ACSInterval(3);
1407
1408 let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1409 let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1410
1411 let mut loc_state = ACSLocState::new_all_zero(&ta);
1412 loc_state[loc_l2] = 1;
1413 loc_state[loc_l3] = 42;
1414
1415 let config = ACSTAConfig {
1416 loc_state,
1417 interval_state,
1418 };
1419
1420 let got_string = config.display_compact(&ta);
1421 let expected_string = "locations: (l2 : 1, l3 : 42), variables: (x : ]2, ∞[)";
1422
1423 assert_eq!(&got_string, expected_string);
1424 }
1425
1426 #[test]
1427 fn test_interval_state_getters() {
1428 let mut interval_state = ACSIntervalState {
1429 v_to_interval: vec![ACSInterval(0), ACSInterval(1), ACSInterval(2)],
1430 };
1431
1432 let var_to_interval = interval_state
1433 .into_iterator_variable_interval()
1434 .collect::<HashMap<_, _>>();
1435
1436 assert_eq!(
1437 var_to_interval,
1438 HashMap::from([
1439 (CSVariable(0), ACSInterval(0)),
1440 (CSVariable(1), ACSInterval(1)),
1441 (CSVariable(2), ACSInterval(2))
1442 ])
1443 );
1444
1445 assert_eq!(interval_state[CSVariable(1)], ACSInterval(1));
1446 assert_eq!(interval_state[&CSVariable(1)], ACSInterval(1));
1447
1448 interval_state[CSVariable(1)] = ACSInterval(0);
1449 assert_eq!(interval_state[CSVariable(1)], ACSInterval(0));
1450 assert_eq!(interval_state[&CSVariable(1)], ACSInterval(0));
1451 }
1452
1453 #[test]
1454 fn test_interval_state_partial_order() {
1455 let interval_state1 = ACSIntervalState {
1456 v_to_interval: vec![ACSInterval(0), ACSInterval(1), ACSInterval(2)],
1457 };
1458 let interval_state2 = ACSIntervalState {
1459 v_to_interval: vec![ACSInterval(0), ACSInterval(2), ACSInterval(2)],
1460 };
1461
1462 assert_eq!(
1463 interval_state1.part_cmp(&interval_state1),
1464 PartialOrdCompResult::Equal
1465 );
1466
1467 assert_eq!(
1468 interval_state1.part_cmp(&interval_state2),
1469 PartialOrdCompResult::Incomparable
1470 );
1471 assert_eq!(
1472 interval_state2.part_cmp(&interval_state1),
1473 PartialOrdCompResult::Incomparable
1474 );
1475 }
1476
1477 #[test]
1478 fn test_interval_state_all_possible_interval_configs() {
1479 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1480 .with_variables([Variable::new("x"), Variable::new("y")])
1481 .unwrap()
1482 .with_locations([
1483 Location::new("l1"),
1484 Location::new("l2"),
1485 Location::new("l3"),
1486 ])
1487 .unwrap()
1488 .with_parameter(Parameter::new("n"))
1489 .unwrap()
1490 .initialize()
1491 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1492 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1493 ComparisonOp::Gt,
1494 Box::new(IntegerExpression::Const(3)),
1495 ))
1496 .unwrap()
1497 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1498 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1499 ComparisonOp::Eq,
1500 Box::new(IntegerExpression::Const(0)),
1501 ))
1502 .unwrap()
1503 .with_rule(
1504 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1505 .with_guard(BooleanExpression::ComparisonExpression(
1506 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1507 ComparisonOp::Gt,
1508 Box::new(IntegerExpression::Const(2)),
1509 ))
1510 .with_action(
1511 Action::new(
1512 Variable::new("x"),
1513 IntegerExpression::Const(1)
1514 + IntegerExpression::Atom(Variable::new("x")),
1515 )
1516 .unwrap(),
1517 )
1518 .build(),
1519 )
1520 .unwrap()
1521 .build();
1522 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1523 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1524 .build()
1525 .unwrap();
1526 let interval_ta = interval_tas.next().unwrap();
1527 assert!(interval_tas.next().is_none());
1528 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1529 let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
1530 let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1531
1532 let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1533
1534 let got_states = ACSIntervalState::all_possible_interval_configs(&cs_ta);
1536
1537 let mut state1 = interval_state.clone();
1538 state1[var_x] = ACSInterval(1);
1539
1540 let mut state2 = interval_state.clone();
1541 state2[var_x] = ACSInterval(2);
1542
1543 let mut state6 = interval_state.clone();
1544 state6[var_x] = ACSInterval(3);
1545
1546 let mut state3 = interval_state.clone();
1547 state3[var_y] = ACSInterval(1);
1548
1549 let mut state4 = interval_state.clone();
1550 state4[var_x] = ACSInterval(1);
1551 state4[var_y] = ACSInterval(1);
1552
1553 let mut state5 = interval_state.clone();
1554 state5[var_x] = ACSInterval(2);
1555 state5[var_y] = ACSInterval(1);
1556
1557 let mut state7 = interval_state.clone();
1558 state7[var_x] = ACSInterval(3);
1559 state7[var_y] = ACSInterval(1);
1560
1561 let expected_states = HashSet::from([
1562 interval_state.clone(),
1563 state1,
1564 state2,
1565 state3,
1566 state4,
1567 state5,
1568 state6,
1569 state7,
1570 ]);
1571 assert_eq!(
1572 got_states.into_iter().collect::<HashSet<_>>(),
1573 expected_states
1574 );
1575 }
1576
1577 #[test]
1578 fn test_interval_state_all_possible_interval_configs_true() {
1579 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1580 .with_variables([Variable::new("x"), Variable::new("y")])
1581 .unwrap()
1582 .with_locations([
1583 Location::new("l1"),
1584 Location::new("l2"),
1585 Location::new("l3"),
1586 ])
1587 .unwrap()
1588 .with_parameter(Parameter::new("n"))
1589 .unwrap()
1590 .initialize()
1591 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1592 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1593 ComparisonOp::Gt,
1594 Box::new(IntegerExpression::Const(3)),
1595 ))
1596 .unwrap()
1597 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1598 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1599 ComparisonOp::Eq,
1600 Box::new(IntegerExpression::Const(0)),
1601 ))
1602 .unwrap()
1603 .with_rule(
1604 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1605 .with_guard(BooleanExpression::ComparisonExpression(
1606 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1607 ComparisonOp::Geq,
1608 Box::new(IntegerExpression::Const(3)),
1609 ))
1610 .with_action(
1611 Action::new(
1612 Variable::new("x"),
1613 IntegerExpression::Const(1)
1614 + IntegerExpression::Atom(Variable::new("x")),
1615 )
1616 .unwrap(),
1617 )
1618 .build(),
1619 )
1620 .unwrap()
1621 .build();
1622 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1623 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1624 .build()
1625 .unwrap();
1626 let interval_ta = interval_tas.next().unwrap();
1627 assert!(interval_tas.next().is_none());
1628 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1629 let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
1630 let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1631
1632 let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1633
1634 let cstr = IntervalConstraint::True;
1635
1636 let got_states = ACSIntervalState::get_target_interval_configs(cstr, &cs_ta);
1637
1638 let mut state1 = interval_state.clone();
1639 state1[var_x] = ACSInterval(1);
1640
1641 let mut state2 = interval_state.clone();
1642 state2[var_x] = ACSInterval(2);
1643
1644 let mut state3 = interval_state.clone();
1645 state3[var_y] = ACSInterval(1);
1646
1647 let mut state4 = interval_state.clone();
1648 state4[var_x] = ACSInterval(1);
1649 state4[var_y] = ACSInterval(1);
1650
1651 let mut state5 = interval_state.clone();
1652 state5[var_x] = ACSInterval(2);
1653 state5[var_y] = ACSInterval(1);
1654
1655 let expected_states = HashSet::from([
1656 interval_state.clone(),
1657 state1,
1658 state2,
1659 state3,
1660 state4,
1661 state5,
1662 ]);
1663 assert_eq!(
1664 got_states.into_iter().collect::<HashSet<_>>(),
1665 expected_states
1666 );
1667 }
1668
1669 #[test]
1670 fn test_target_interval_configs() {
1671 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1672 .with_variables([Variable::new("x"), Variable::new("y")])
1673 .unwrap()
1674 .with_locations([
1675 Location::new("l1"),
1676 Location::new("l2"),
1677 Location::new("l3"),
1678 ])
1679 .unwrap()
1680 .with_parameter(Parameter::new("n"))
1681 .unwrap()
1682 .initialize()
1683 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1684 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1685 ComparisonOp::Gt,
1686 Box::new(IntegerExpression::Const(3)),
1687 ))
1688 .unwrap()
1689 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1690 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1691 ComparisonOp::Eq,
1692 Box::new(IntegerExpression::Const(0)),
1693 ))
1694 .unwrap()
1695 .with_rule(
1696 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1697 .with_guard(BooleanExpression::ComparisonExpression(
1698 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1699 ComparisonOp::Gt,
1700 Box::new(IntegerExpression::Const(2)),
1701 ))
1702 .with_action(
1703 Action::new(
1704 Variable::new("x"),
1705 IntegerExpression::Const(1)
1706 + IntegerExpression::Atom(Variable::new("x")),
1707 )
1708 .unwrap(),
1709 )
1710 .build(),
1711 )
1712 .unwrap()
1713 .build();
1714 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1715 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1716 .build()
1717 .unwrap();
1718 let interval_ta = interval_tas.next().unwrap();
1719 assert!(interval_tas.next().is_none());
1720 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1721 let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1722
1723 let cstr = IntervalConstraint::SingleVarIntervalConstr(
1724 Variable::new("x"),
1725 vec![Interval::new_constant(0, 1)],
1726 );
1727 let got_states = ACSIntervalState::get_target_interval_configs(cstr, &cs_ta);
1728
1729 let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1730 let mut state2 = interval_state.clone();
1731 state2[var_y] = ACSInterval(1);
1732
1733 let expected_states = HashSet::from([interval_state.clone(), state2]);
1734 assert_eq!(
1735 got_states.into_iter().collect::<HashSet<_>>(),
1736 expected_states
1737 );
1738 }
1739
1740 #[test]
1741 fn test_interval_state_compute_all_predecessor_configs() {
1742 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1743 .with_variables([Variable::new("x"), Variable::new("y")])
1744 .unwrap()
1745 .with_locations([
1746 Location::new("l1"),
1747 Location::new("l2"),
1748 Location::new("l3"),
1749 ])
1750 .unwrap()
1751 .with_parameter(Parameter::new("n"))
1752 .unwrap()
1753 .initialize()
1754 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1755 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1756 ComparisonOp::Gt,
1757 Box::new(IntegerExpression::Const(3)),
1758 ))
1759 .unwrap()
1760 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1761 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1762 ComparisonOp::Eq,
1763 Box::new(IntegerExpression::Const(0)),
1764 ))
1765 .unwrap()
1766 .with_rule(
1767 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1768 .with_guard(BooleanExpression::ComparisonExpression(
1769 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1770 ComparisonOp::Gt,
1771 Box::new(IntegerExpression::Const(2)),
1772 ))
1773 .with_action(
1774 Action::new(
1775 Variable::new("x"),
1776 IntegerExpression::Const(1)
1777 + IntegerExpression::Atom(Variable::new("x")),
1778 )
1779 .unwrap(),
1780 )
1781 .build(),
1782 )
1783 .unwrap()
1784 .build();
1785 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1786 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1787 .build()
1788 .unwrap();
1789
1790 let interval_ta = interval_tas.next().unwrap();
1791
1792 assert!(interval_tas.next().is_none());
1793 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1794
1795 let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
1796 let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1797
1798 let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1800 let rule = CSRule {
1801 id: 0,
1802 source: ACSLocation(0),
1803 target: ACSLocation(1),
1804 guard: CSIntervalConstraint::True,
1805 actions: vec![CSIntervalAction {
1806 var: var_x,
1807 effect: IntervalActionEffect::Inc(1),
1808 }],
1809 };
1810 let got_preds = interval_state.compute_all_predecessor_configs(&rule, &cs_ta);
1811 let expected_preds = HashSet::from([]);
1812 assert_eq!(
1813 got_preds.into_iter().collect::<HashSet<_>>(),
1814 expected_preds
1815 );
1816
1817 let mut is_exact_int = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1819 is_exact_int[var_x] = ACSInterval(2); is_exact_int[var_y] = ACSInterval(0);
1821
1822 let mut is_open_int = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1823 is_open_int[var_x] = ACSInterval(3); is_open_int[var_y] = ACSInterval(0);
1825
1826 let rule = CSRule {
1827 id: 0,
1828 source: ACSLocation(0),
1829 target: ACSLocation(1),
1830 guard: CSIntervalConstraint::True,
1831 actions: vec![CSIntervalAction {
1832 var: var_x,
1833 effect: IntervalActionEffect::Inc(1),
1834 }],
1835 };
1836
1837 let got_preds_exact = is_exact_int.compute_all_predecessor_configs(&rule, &cs_ta);
1838
1839 let mut pred_1 = is_exact_int.clone();
1840 pred_1[var_x] = ACSInterval(1);
1841
1842 let expected_preds_exact = HashSet::from([
1843 pred_1.clone(),
1845 ]);
1846 assert_eq!(
1847 got_preds_exact.clone().into_iter().collect::<HashSet<_>>(),
1848 expected_preds_exact,
1849 "Got: {}\nExpected: {}",
1850 join_iterator(got_preds_exact.iter().map(|c| c.display(&cs_ta)), ";"),
1851 join_iterator(expected_preds_exact.iter().map(|c| c.display(&cs_ta)), ";"),
1852 );
1853
1854 let got_preds_open = is_open_int.compute_all_predecessor_configs(&rule, &cs_ta);
1855 let expected_preds_open = HashSet::from([
1856 is_open_int.clone(),
1857 is_exact_int.clone(),
1859 ]);
1860 assert_eq!(
1861 got_preds_open.clone().into_iter().collect::<HashSet<_>>(),
1862 expected_preds_open,
1863 "Got: {}\nExpected: {}",
1864 join_iterator(got_preds_open.iter().map(|c| c.display(&cs_ta)), ";"),
1865 join_iterator(expected_preds_open.iter().map(|c| c.display(&cs_ta)), ";"),
1866 );
1867
1868 let rule = CSRule {
1869 id: 0,
1870 source: ACSLocation(0),
1871 target: ACSLocation(1),
1872 guard: CSIntervalConstraint::True,
1873 actions: vec![CSIntervalAction {
1874 var: var_x,
1875 effect: IntervalActionEffect::Dec(1),
1876 }],
1877 };
1878
1879 let got_preds = is_open_int.compute_all_predecessor_configs(&rule, &cs_ta);
1880
1881 let mut pred_1 = is_open_int.clone();
1882 pred_1[var_x] = ACSInterval(3);
1883 let expected_preds = HashSet::from([pred_1.clone()]);
1884 assert_eq!(
1885 got_preds.into_iter().collect::<HashSet<_>>(),
1886 expected_preds
1887 );
1888
1889 let rule = CSRule {
1890 id: 0,
1891 source: ACSLocation(0),
1892 target: ACSLocation(1),
1893 guard: CSIntervalConstraint::True,
1894 actions: vec![CSIntervalAction {
1895 var: var_y,
1896 effect: IntervalActionEffect::Reset,
1897 }],
1898 };
1899
1900 let got_preds = is_open_int.compute_all_predecessor_configs(&rule, &cs_ta);
1901 let mut pred_1 = is_open_int.clone();
1902 pred_1[var_y] = ACSInterval(1);
1903 let expected_preds = HashSet::from([is_open_int.clone(), pred_1]);
1904 assert_eq!(
1905 got_preds.into_iter().collect::<HashSet<_>>(),
1906 expected_preds
1907 );
1908 assert_eq!(cs_ta.get_all_intervals(&var_x).count(), 4);
1909 }
1910
1911 #[test]
1912 fn test_interval_state_display() {
1913 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1914 .with_variables([Variable::new("x"), Variable::new("y")])
1915 .unwrap()
1916 .with_locations([
1917 Location::new("l1"),
1918 Location::new("l2"),
1919 Location::new("l3"),
1920 ])
1921 .unwrap()
1922 .with_parameter(Parameter::new("n"))
1923 .unwrap()
1924 .initialize()
1925 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1926 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1927 ComparisonOp::Gt,
1928 Box::new(IntegerExpression::Const(3)),
1929 ))
1930 .unwrap()
1931 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1932 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1933 ComparisonOp::Eq,
1934 Box::new(IntegerExpression::Const(0)),
1935 ))
1936 .unwrap()
1937 .with_rule(
1938 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1939 .with_guard(BooleanExpression::ComparisonExpression(
1940 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1941 ComparisonOp::Gt,
1942 Box::new(IntegerExpression::Const(2)),
1943 ))
1944 .with_action(
1945 Action::new(
1946 Variable::new("x"),
1947 IntegerExpression::Const(1)
1948 + IntegerExpression::Atom(Variable::new("x")),
1949 )
1950 .unwrap(),
1951 )
1952 .build(),
1953 )
1954 .unwrap()
1955 .build();
1956 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1957 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1958 .build()
1959 .unwrap();
1960 let interval_ta = interval_tas.next().unwrap();
1961 assert!(interval_tas.next().is_none());
1962 let ta = ACSThresholdAutomaton::new(interval_ta);
1963
1964 let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
1965
1966 let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
1967 interval_state[var_x] = ACSInterval(3);
1968
1969 let got_string = interval_state.display(&ta);
1970 let expected_string = "x : ]2, ∞[, y : [0, 1[";
1971
1972 assert_eq!(&got_string, expected_string);
1973 }
1974
1975 #[test]
1976 fn test_interval_state_display_compact() {
1977 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1978 .with_variables([Variable::new("x"), Variable::new("y")])
1979 .unwrap()
1980 .with_locations([
1981 Location::new("l1"),
1982 Location::new("l2"),
1983 Location::new("l3"),
1984 ])
1985 .unwrap()
1986 .with_parameter(Parameter::new("n"))
1987 .unwrap()
1988 .initialize()
1989 .with_resilience_condition(BooleanExpression::ComparisonExpression(
1990 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1991 ComparisonOp::Gt,
1992 Box::new(IntegerExpression::Const(3)),
1993 ))
1994 .unwrap()
1995 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1996 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1997 ComparisonOp::Eq,
1998 Box::new(IntegerExpression::Const(0)),
1999 ))
2000 .unwrap()
2001 .with_rule(
2002 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2003 .with_guard(BooleanExpression::ComparisonExpression(
2004 Box::new(IntegerExpression::Atom(Variable::new("x"))),
2005 ComparisonOp::Gt,
2006 Box::new(IntegerExpression::Const(2)),
2007 ))
2008 .with_action(
2009 Action::new(
2010 Variable::new("x"),
2011 IntegerExpression::Const(1)
2012 + IntegerExpression::Atom(Variable::new("x")),
2013 )
2014 .unwrap(),
2015 )
2016 .build(),
2017 )
2018 .unwrap()
2019 .build();
2020 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2021 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2022 .build()
2023 .unwrap();
2024 let interval_ta = interval_tas.next().unwrap();
2025 assert!(interval_tas.next().is_none());
2026 let ta = ACSThresholdAutomaton::new(interval_ta);
2027
2028 let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
2029
2030 let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
2031 interval_state[var_x] = ACSInterval(2);
2032
2033 let got_string = interval_state.display_compact(&ta);
2034 let expected_string = "x : [2, 2]";
2035
2036 assert_eq!(&got_string, expected_string);
2037 }
2038
2039 #[test]
2040 fn test_loc_state_getters() {
2041 let mut loc_state = ACSLocState {
2042 loc_state: vec![1, 2, 3, 4, 5],
2043 };
2044
2045 let state_n = loc_state
2046 .into_iterator_loc_n_procs()
2047 .collect::<HashMap<_, _>>();
2048
2049 assert_eq!(
2050 state_n,
2051 HashMap::from([
2052 (ACSLocation(0), 1),
2053 (ACSLocation(1), 2),
2054 (ACSLocation(2), 3),
2055 (ACSLocation(3), 4),
2056 (ACSLocation(4), 5)
2057 ])
2058 );
2059
2060 assert_eq!(loc_state[ACSLocation(1)], 2);
2061 assert_eq!(loc_state[&ACSLocation(1)], 2);
2062
2063 loc_state[ACSLocation(0)] += 1;
2064 assert_eq!(loc_state[ACSLocation(0)], 2);
2065 }
2066
2067 #[test]
2068 fn test_loc_state_part_ord() {
2069 let loc_state1 = ACSLocState {
2070 loc_state: vec![1, 2, 3, 4, 5],
2071 };
2072
2073 let loc_state2 = ACSLocState {
2074 loc_state: vec![1, 2, 3, 5, 5],
2075 };
2076
2077 assert_eq!(
2078 loc_state1.part_cmp(&loc_state2),
2079 PartialOrdCompResult::Smaller
2080 );
2081 assert_eq!(
2082 loc_state2.part_cmp(&loc_state1),
2083 PartialOrdCompResult::Greater
2084 );
2085 assert_eq!(
2086 loc_state1.part_cmp(&loc_state1),
2087 PartialOrdCompResult::Equal
2088 );
2089
2090 let loc_state3 = ACSLocState {
2091 loc_state: vec![1, 2, 3, 0, 6],
2092 };
2093
2094 assert_eq!(
2095 loc_state1.part_cmp(&loc_state3),
2096 PartialOrdCompResult::Incomparable
2097 );
2098
2099 let loc_state4 = ACSLocState {
2100 loc_state: vec![1, 2, 3, 6, 0],
2101 };
2102
2103 assert_eq!(
2104 loc_state1.part_cmp(&loc_state4),
2105 PartialOrdCompResult::Incomparable
2106 );
2107 }
2108
2109 #[test]
2110 fn test_loc_state_compute_predecessor_min_basis() {
2111 let loc_state = ACSLocState {
2112 loc_state: vec![0, 1, 2],
2113 };
2114
2115 let rule = CSRule {
2116 id: 0,
2117 source: ACSLocation(0),
2118 target: ACSLocation(1),
2119 guard: CSIntervalConstraint::True,
2120 actions: vec![],
2121 };
2122
2123 let pred = loc_state.compute_predecessors_min_basis(&rule);
2124
2125 let expected_pred = ACSLocState {
2126 loc_state: vec![1, 0, 2],
2127 };
2128
2129 assert_eq!(pred, expected_pred);
2130
2131 let loc_state = ACSLocState {
2132 loc_state: vec![0, 0, 2],
2133 };
2134
2135 let rule = CSRule {
2136 id: 0,
2137 source: ACSLocation(0),
2138 target: ACSLocation(1),
2139 guard: CSIntervalConstraint::True,
2140 actions: vec![],
2141 };
2142
2143 let pred = loc_state.compute_predecessors_min_basis(&rule);
2144
2145 let expected_pred = ACSLocState {
2146 loc_state: vec![1, 0, 2],
2147 };
2148
2149 assert_eq!(pred, expected_pred);
2150 }
2151
2152 #[test]
2153 fn test_loc_state_compute_target_cfg() {
2154 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2155 .with_variables([Variable::new("x"), Variable::new("y")])
2156 .unwrap()
2157 .with_locations([
2158 Location::new("l1"),
2159 Location::new("l2"),
2160 Location::new("l3"),
2161 ])
2162 .unwrap()
2163 .with_parameter(Parameter::new("n"))
2164 .unwrap()
2165 .initialize()
2166 .with_resilience_condition(BooleanExpression::ComparisonExpression(
2167 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2168 ComparisonOp::Gt,
2169 Box::new(IntegerExpression::Const(3)),
2170 ))
2171 .unwrap()
2172 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2173 Box::new(IntegerExpression::Atom(Location::new("l1"))),
2174 ComparisonOp::Eq,
2175 Box::new(IntegerExpression::Const(0)),
2176 ))
2177 .unwrap()
2178 .with_rule(
2179 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2180 .with_guard(BooleanExpression::ComparisonExpression(
2181 Box::new(IntegerExpression::Atom(Variable::new("x"))),
2182 ComparisonOp::Gt,
2183 Box::new(IntegerExpression::Const(2)),
2184 ))
2185 .with_action(
2186 Action::new(
2187 Variable::new("x"),
2188 IntegerExpression::Const(1)
2189 + IntegerExpression::Atom(Variable::new("x")),
2190 )
2191 .unwrap(),
2192 )
2193 .build(),
2194 )
2195 .unwrap()
2196 .build();
2197 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2198 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2199 .build()
2200 .unwrap();
2201 let interval_ta = interval_tas.next().unwrap();
2202 assert!(interval_tas.next().is_none());
2203 let cs_ta = ACSThresholdAutomaton::new(interval_ta);
2204
2205 let spec = UpwardsClosedSet::new_cover([Location::new("l3")]);
2207 let spec = spec.upwards_closed_clauses().next().unwrap();
2208 let got_loc_state = ACSLocState::compute_target_cfg(spec, &cs_ta);
2209
2210 let cs_loc = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2211 let mut expected_loc_state = ACSLocState {
2212 loc_state: vec![0, 0, 0],
2213 };
2214 expected_loc_state[cs_loc] = 1;
2215
2216 assert_eq!(got_loc_state, expected_loc_state);
2217
2218 let spec = UpwardsClosedSet::new_cover([Location::new("l1")]);
2220 let spec = spec.upwards_closed_clauses().next().unwrap();
2221 let got_loc_state = ACSLocState::compute_target_cfg(spec, &cs_ta);
2222
2223 let loc_l1 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l1"));
2224 let mut expected_loc_state = ACSLocState {
2225 loc_state: vec![0, 0, 0],
2226 };
2227 expected_loc_state[loc_l1] = 1;
2228 assert_eq!(got_loc_state, expected_loc_state);
2229
2230 let spec = UpwardsClosedSet::new_cover([Location::new("l1"), Location::new("l2")]);
2232 let spec = spec.upwards_closed_clauses().next().unwrap();
2233 let got_loc_state = ACSLocState::compute_target_cfg(spec, &cs_ta);
2234
2235 let mut expected_loc_state = ACSLocState {
2236 loc_state: vec![0, 0, 0],
2237 };
2238 let cs_loc1 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l1"));
2239 expected_loc_state[cs_loc1] = 1;
2240 let cs_loc2 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l2"));
2241 expected_loc_state[cs_loc2] = 1;
2242
2243 assert_eq!(got_loc_state, expected_loc_state);
2244
2245 let spec = UpwardsClosedSet::new_cover([
2247 Location::new("l1"),
2248 Location::new("l2"),
2249 Location::new("l3"),
2250 ]);
2251 let spec = spec.upwards_closed_clauses().next().unwrap();
2252 let got_loc_state = ACSLocState::compute_target_cfg(spec, &cs_ta);
2253
2254 let expected_loc_state = ACSLocState {
2255 loc_state: vec![1, 1, 1],
2256 };
2257
2258 assert_eq!(got_loc_state, expected_loc_state);
2259
2260 let spec = UpwardsClosedSet::new_cover_int([(Location::new("l3"), 42)]);
2262 let spec = spec.upwards_closed_clauses().next().unwrap();
2263 let got_loc_state = ACSLocState::compute_target_cfg(spec, &cs_ta);
2264
2265 let cs_loc = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2266 let mut expected_loc_state = ACSLocState {
2267 loc_state: vec![0, 0, 0],
2268 };
2269 expected_loc_state[cs_loc] = 42;
2270
2271 assert_eq!(got_loc_state, expected_loc_state);
2272
2273 let spec = UpwardsClosedSet::new_cover_int([
2275 (Location::new("l1"), 42),
2276 (Location::new("l2"), 42),
2277 (Location::new("l3"), 42),
2278 ]);
2279 let spec = spec.upwards_closed_clauses().next().unwrap();
2280 let got_loc_state = ACSLocState::compute_target_cfg(spec, &cs_ta);
2281
2282 let expected_loc_state = ACSLocState {
2283 loc_state: vec![42, 42, 42],
2284 };
2285
2286 assert_eq!(got_loc_state, expected_loc_state);
2287 }
2288
2289 #[test]
2290 fn test_loc_state_display() {
2291 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2292 .with_variables([Variable::new("x"), Variable::new("y")])
2293 .unwrap()
2294 .with_locations([
2295 Location::new("l1"),
2296 Location::new("l2"),
2297 Location::new("l3"),
2298 ])
2299 .unwrap()
2300 .with_parameter(Parameter::new("n"))
2301 .unwrap()
2302 .initialize()
2303 .with_resilience_condition(BooleanExpression::ComparisonExpression(
2304 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2305 ComparisonOp::Gt,
2306 Box::new(IntegerExpression::Const(3)),
2307 ))
2308 .unwrap()
2309 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2310 Box::new(IntegerExpression::Atom(Location::new("l1"))),
2311 ComparisonOp::Eq,
2312 Box::new(IntegerExpression::Const(0)),
2313 ))
2314 .unwrap()
2315 .with_rule(
2316 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2317 .with_guard(BooleanExpression::ComparisonExpression(
2318 Box::new(IntegerExpression::Atom(Variable::new("x"))),
2319 ComparisonOp::Gt,
2320 Box::new(IntegerExpression::Const(2)),
2321 ))
2322 .with_action(
2323 Action::new(
2324 Variable::new("x"),
2325 IntegerExpression::Const(1)
2326 + IntegerExpression::Atom(Variable::new("x")),
2327 )
2328 .unwrap(),
2329 )
2330 .build(),
2331 )
2332 .unwrap()
2333 .build();
2334 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2335 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2336 .build()
2337 .unwrap();
2338 let interval_ta = interval_tas.next().unwrap();
2339 assert!(interval_tas.next().is_none());
2340 let ta = ACSThresholdAutomaton::new(interval_ta);
2341
2342 let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2343 let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
2344
2345 let mut loc_state = ACSLocState::new_all_zero(&ta);
2346 loc_state[loc_l2] = 1;
2347 loc_state[loc_l3] = 42;
2348
2349 let got_string = loc_state.display(&ta);
2350 let expected_string = "l1 : 0, l2 : 1, l3 : 42";
2351
2352 assert_eq!(&got_string, expected_string);
2353 }
2354
2355 #[test]
2356 fn test_loc_state_display_compact() {
2357 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2358 .with_variables([Variable::new("x"), Variable::new("y")])
2359 .unwrap()
2360 .with_locations([
2361 Location::new("l1"),
2362 Location::new("l2"),
2363 Location::new("l3"),
2364 ])
2365 .unwrap()
2366 .with_parameter(Parameter::new("n"))
2367 .unwrap()
2368 .initialize()
2369 .with_resilience_condition(BooleanExpression::ComparisonExpression(
2370 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2371 ComparisonOp::Gt,
2372 Box::new(IntegerExpression::Const(3)),
2373 ))
2374 .unwrap()
2375 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2376 Box::new(IntegerExpression::Atom(Location::new("l1"))),
2377 ComparisonOp::Eq,
2378 Box::new(IntegerExpression::Const(0)),
2379 ))
2380 .unwrap()
2381 .with_rule(
2382 RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2383 .with_guard(BooleanExpression::ComparisonExpression(
2384 Box::new(IntegerExpression::Atom(Variable::new("x"))),
2385 ComparisonOp::Gt,
2386 Box::new(IntegerExpression::Const(2)),
2387 ))
2388 .with_action(
2389 Action::new(
2390 Variable::new("x"),
2391 IntegerExpression::Const(1)
2392 + IntegerExpression::Atom(Variable::new("x")),
2393 )
2394 .unwrap(),
2395 )
2396 .build(),
2397 )
2398 .unwrap()
2399 .build();
2400 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2401 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2402 .build()
2403 .unwrap();
2404 let interval_ta = interval_tas.next().unwrap();
2405 assert!(interval_tas.next().is_none());
2406 let ta = ACSThresholdAutomaton::new(interval_ta);
2407
2408 let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2409 let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
2410
2411 let mut loc_state = ACSLocState::new_all_zero(&ta);
2412 loc_state[loc_l2] = 1;
2413 loc_state[loc_l3] = 42;
2414
2415 let got_string = loc_state.display_compact(&ta);
2416 let expected_string = "l2 : 1, l3 : 42";
2417
2418 assert_eq!(&got_string, expected_string);
2419 }
2420}