Skip to main content

taco_acs_model_checker/acs_threshold_automaton/
configuration.rs

1//! Configurations of a [`ACSThresholdAutomaton`]
2//!
3//! This module contains the type definitions for configurations of a
4//! [ `ACSThresholdAutomaton`]. These types are designed to have a small memory
5//! footprint and should be easily computable.
6//! This also means that when working with configurations, there is no extensive
7//! validation that is performed. In particular, one must ensure that
8//! configurations, locations, and intervals etc. from different threshold
9//! automata are not mixed. Otherwise panics can arise, and the results will not
10//! have any meaning.
11
12use 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/// Configuration of a [`ACSThresholdAutomaton`]
40///
41/// This struct represents a configuration, i.e. an assignment of intervals for
42/// all shared variables and a number of processes in each location.
43///
44/// Use these configurations with care. Configurations of different automata
45/// should never be indexed by locations, variables or intervals of other
46/// automata.
47#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
48pub struct ACSTAConfig {
49    /// Location state specifying how many processes are in each location
50    loc_state: ACSLocState,
51    /// Interval state specifying the current interval for each variable
52    interval_state: ACSIntervalState,
53}
54
55impl ACSTAConfig {
56    /// Get the interval state of the configuration
57    pub fn interval_state(&self) -> &ACSIntervalState {
58        &self.interval_state
59    }
60
61    /// Get the location state of the configuration
62    pub fn location_state(&self) -> &ACSLocState {
63        &self.loc_state
64    }
65
66    /// Check whether the current configuration is an initial configuration of
67    /// the threshold automaton
68    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    /// Compute all predecessors of the configuration that could stem from
84    /// rule `r` being applied
85    ///
86    /// Note that the iterator returned here might contain comparable elements.
87    /// Computing the *minimal* basis must be handled when collecting this
88    /// iterator
89    /// This function returns `None` if the rule contains a reset, and the
90    /// variable the reset has been applied to is not in its 0 interval.
91    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    /// Compute all configurations that correspond to an error state in the
124    /// given disjunction over target configurations
125    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    /// Compute a goal configuration from a single [`TargetConfig`]
134    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    /// Get a string representation of the configuration
157    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    /// Get a compact string representation of the configuration without
166    /// locations that are not covered by processes or variables that are zero
167    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    /// Encode the conditions this abstract configuration places on the
176    /// corresponding concrete configurations into an SMT expression
177    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        // encode at least required number of procs
186        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                // We do not encode intervals of the replacement variables as
203                // these variables do not appear in the original automaton but
204                // we use the original automaton in the encoding.
205                // Correctness of the interval abstraction is guaranteed because
206                // the single variable behaves the same as the sum of two
207                // variables and the correctness of steps is ensured by encoding
208                // the rule guards
209                // TODO: remove once ordering support is ready
210                !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/// Assignment of variables to their current value
243///
244/// An interval state represents a valuation of the shared variables of a
245/// threshold automaton with interval abstraction.
246///
247/// Use this type with care. `IntervalState`s of different automata
248/// should never be indexed by variables or intervals of other automata.
249#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
250pub struct ACSIntervalState {
251    /// Intervals of the configuration
252    /// Index of the entry represents the variable
253    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    /// Create a new interval configuration where all variables are in the
267    /// interval mapped to zero
268    ///
269    /// The resulting configuration of this function should not be used directly
270    /// as it does not necessarily correspond to a valid configuration. It
271    /// should only be used for initialization purposes.
272    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    /// Compute all interval states that are possible in the threshold automaton
279    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    /// Compute all interval states that are allowed by the given interval
296    /// constraint in the threshold automaton
297    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    /// Compute all potential predecessor interval configurations of one rule application
326    pub fn compute_all_predecessor_configs(
327        &self,
328        rule: &CSRule,
329        ta: &ACSThresholdAutomaton,
330    ) -> Vec<Self> {
331        // for each action add the predecessor where the action had an affect on
332        // the interval, but also the interval state, where the interval was unaffected
333        rule.actions()
334            .fold(Vec::from([self.clone()]), |predecessors, act| {
335                // for each computed predecessor compute intervals before action
336                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    /// Compute all possible interval configurations before the application of
353    /// the action
354    ///
355    /// Concretely, for
356    /// - Increment actions: The increment could have lead to jumping to the
357    ///   next interval, or could have stayed in the current interval.
358    /// - Decrement actions: The decrement could have lead to a jump to a
359    ///   smaller interval or could have stayed in the current interval.
360    /// - Reset actions: A reset will always go to the 0 interval.
361    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                // check whether we could have stayed in the interval
373                // if yes, add back the current interval state
374                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                // check if a smaller interval exists, if yes insert the interval
383                // state with `act.variable()` is in that interval
384                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                // check whether we could have stayed in the interval
395                // if yes, add back the current interval state
396                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                // check if a bigger interval exists, if yes insert the interval
405                // state with `act.variable()` is in that interval
406                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                // the previous
415                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    /// Get a string representation of the interval state
430    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    /// Get a string representation of the interval state, leaving out variables
440    /// in their zero interval
441    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/// Assignment of process counts to a location
497///
498/// The LocationState tracks how many processes are in each location of the
499/// threshold automaton.
500///
501/// Use this type with care. `LocationState`s of different automata
502/// should never be indexed by locations of other automata.
503#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
504pub struct ACSLocState {
505    /// Vector counting how many processes are in each location
506    loc_state: Vec<u32>,
507}
508
509impl ACSLocState {
510    /// Iterate over the configurations and the number of processes in these locations
511    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    /// Compute the minimal basis of the predecessor
519    ///
520    /// This function assumes that `self` is the minimal basis of an upwards
521    /// closed set of locations and computes the minimal basis of a state from
522    /// which an element in `self` can be reached using `CSRule`.
523    ///
524    /// In particular this means that the predecessor generated here, can have
525    /// more processes than `self`.
526    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    /// Get the [`ACSLocState`] that corresponds to the target configuration
545    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    /// Get a string representation of the location state
561    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    /// Get a string representation of the location state, leaving out locations
569    /// with zero processes in them
570    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        // l2 == 0, l3 == 0, x == 0, y == 0
787        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        // l3 > 1 && x == 0
1171        let target_1 = UpwardsClosedSet::new_var_constraint(cstr.clone().try_into().unwrap())
1172            & UpwardsClosedSet::new_cover_int([(Location::new("l3"), 1)]);
1173
1174        // x == 0
1175        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        // x: [0,1[ [1, 2[ [2,2] ]2,...
1535        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        // all zero -> no predecessor because no inc possible
1799        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        // Interval state: x [2,2], y: [0,1]
1818        let mut is_exact_int = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1819        is_exact_int[var_x] = ACSInterval(2); // 2,2
1820        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); // 2,\infty
1824        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            // previous interval
1844            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            // previous interval
1858            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        // Cover l3
2206        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        // Cover l1
2219        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        // Cover l1 + l2
2231        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        // Cover l1 + l2 + l3
2246        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        // GeneralCover l3
2261        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        // GeneralCover l1 + l2 + l3
2274        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}