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