Skip to main content

taco_interval_ta/
lib.rs

1//! Threshold Automaton with applied interval abstraction
2//!
3//! This crate defines `IntervalThresholdAutomaton` which is a type representing
4//! a threshold automaton to which interval abstraction has been applied. Guards
5//! are abstracted to sets of symbolic intervals, depending on the interval
6//! order that is used for the automaton.
7//!
8//! This crate also provides the types for defining and working with intervals
9//! and implements an ordering mechanism for intervals.
10
11use builder::{IntervalTABuilder, static_interval_order::StaticIntervalOrder};
12use interval::{Interval, IntervalOrderError, IntervalOrderFor};
13use log::warn;
14use taco_display_utils::{
15    display_iterator_stable_order, indent_all, join_iterator, join_iterator_and_add_back,
16};
17use taco_model_checker::{ModelCheckerContext, TASpecification, TATrait};
18use taco_smt_encoder::ProvidesSMTSolverBuilder;
19use taco_threshold_automaton::expressions::{And, BooleanConnective, IsDeclared, Or};
20use taco_threshold_automaton::general_threshold_automaton::{
21    Action, GeneralThresholdAutomaton, Rule, UpdateExpression,
22};
23use taco_threshold_automaton::lia_threshold_automaton::integer_thresholds::WeightedSum;
24use taco_threshold_automaton::lia_threshold_automaton::{
25    LIARule, LIATransformationError, LIAVariableConstraint,
26};
27
28use taco_threshold_automaton::{
29    ActionDefinition, LocationConstraint, RuleDefinition, ThresholdAutomaton, VariableConstraint,
30    impl_bitand, impl_bitor,
31};
32use taco_threshold_automaton::{
33    expressions::{BooleanExpression, Location, Parameter, Variable},
34    lia_threshold_automaton::LIAThresholdAutomaton,
35};
36
37use std::collections::HashSet;
38use std::error;
39use std::{
40    collections::HashMap,
41    fmt::{self, Debug, Display},
42    hash,
43};
44
45pub mod builder;
46pub mod interval;
47
48/// Threshold automaton with guards abstracted to sets of intervals
49///
50/// A threshold automaton where a specific order on intervals is defined and
51/// the guards are abstracted to sets of intervals in which they are enabled.
52#[derive(Debug, Clone)]
53pub struct IntervalThresholdAutomaton {
54    /// Underlying linear integer arithmetic threshold automaton
55    lia_ta: LIAThresholdAutomaton,
56    /// Initial variable constraints as intervals
57    initial_variable_constraints: Vec<IntervalConstraint>,
58    /// Abstract rules of the threshold automaton
59    rules: Vec<IntervalTARule>,
60    /// Current interval order
61    order: StaticIntervalOrder,
62    /// Order expression
63    order_expr: Vec<BooleanExpression<Parameter>>,
64}
65
66impl IntervalThresholdAutomaton {
67    /// Get the initial interval for variable `var`
68    pub fn get_initial_interval<'a>(&'a self, var: &Variable) -> Vec<&'a Interval> {
69        let intervals_for_var = self
70            .order
71            .get_all_intervals_of_t(var)
72            .unwrap_or_else(|_| panic!("Failed to get intervals of variable {}", var))
73            .iter()
74            .collect();
75
76        self.initial_variable_constraints
77            .iter()
78            .fold(intervals_for_var, |acc, constr| {
79                constr.get_enabled_intervals(var, acc)
80            })
81    }
82
83    /// Get the zero interval for variable `var`
84    pub fn get_zero_interval<'a>(&'a self, var: &Variable) -> &'a Interval {
85        self.order
86            .get_zero_interval(var)
87            .unwrap_or_else(|_| panic!("Failed to get zero interval for variable {}", var))
88    }
89
90    /// Get intervals for variable `var`
91    pub fn get_intervals(&self, var: &Variable) -> &Vec<Interval> {
92        self.order
93            .get_all_intervals_of_t(var)
94            .unwrap_or_else(|_| panic!("Failed to get intervals for variable {var}"))
95    }
96
97    /// Get the previous interval of `i` for variable `var`
98    ///
99    /// Returns `None` if `i` is the first interval
100    pub fn get_previous_interval(&self, var: &Variable, i: &Interval) -> Option<&Interval> {
101        self.order
102            .get_previous_interval(var, i)
103            .unwrap_or_else(|_| panic!("Failed to get previous interval for variable {var}"))
104    }
105
106    /// Get the next interval of `i` for variable `var`
107    ///
108    /// Returns `None` if `i` is the open interval
109    pub fn get_next_interval(&self, var: &Variable, i: &Interval) -> Option<&Interval> {
110        self.order
111            .get_next_interval(var, i)
112            .unwrap_or_else(|_| panic!("Failed to get next interval for variable {var}"))
113    }
114
115    /// Get the corresponding concrete rule for a given abstract rule
116    pub fn get_derived_rule(&self, abstract_rule: &IntervalTARule) -> &Rule {
117        self.lia_ta
118            .get_ta()
119            .rules()
120            .find(|rule| {
121                rule.id() == abstract_rule.id()
122                    && rule.source() == abstract_rule.source()
123                    && rule.target() == abstract_rule.target()
124            })
125            .unwrap()
126    }
127
128    /// Translate a [LIAVariableConstraint] into an [IntervalConstraint] in the
129    /// interval order of the automaton
130    ///
131    /// This function will return an error if the constraint contains a
132    /// threshold that was not present when the interval order was constructed
133    pub fn get_interval_constraint(
134        &self,
135        constr: &LIAVariableConstraint,
136    ) -> Result<IntervalConstraint, IntervalConstraintConstructionError> {
137        IntervalConstraint::from_lia_constr(constr, &self.order)
138    }
139
140    /// Get the intervals in which the interval constraint is enabled
141    pub fn get_enabled_intervals<'a>(
142        &'a self,
143        constr: &'a IntervalConstraint,
144        var: &Variable,
145    ) -> impl Iterator<Item = &'a Interval> {
146        // TODO: adjust when multivar
147
148        constr
149            .get_enabled_intervals(var, self.get_intervals(var).iter().collect())
150            .into_iter()
151    }
152
153    /// Get all variables for which the interval constraint actually
154    /// constrains the interval a variable can be in
155    pub fn get_variables_constrained(&self, constr: &IntervalConstraint) -> Vec<&Variable> {
156        self.variables()
157            .filter(|var| {
158                self.get_enabled_intervals(constr, var)
159                    .collect::<HashSet<_>>()
160                    != self.get_intervals(var).iter().collect::<HashSet<_>>()
161            })
162            .collect()
163    }
164
165    /// Get the underlying threshold automaton
166    pub fn get_ta(&self) -> &GeneralThresholdAutomaton {
167        self.lia_ta.get_ta()
168    }
169
170    /// Get the interval order for a specific variable
171    ///
172    /// Returns all intervals for variable `var` in the interval order of the
173    /// automaton
174    pub fn get_ordered_intervals(
175        &self,
176        var: &Variable,
177    ) -> Result<&Vec<Interval>, IntervalOrderError<Variable>> {
178        let intervals = self
179            .order
180            .single_var_order()
181            .get(var)
182            .ok_or_else(|| IntervalOrderError::VariableNotFound { var: var.clone() })?;
183        Ok(intervals)
184    }
185
186    /// TODO: Remove when interval order is ready
187    pub fn get_helper_vars_for_sumvars(&self) -> &HashMap<Variable, WeightedSum<Variable>> {
188        self.lia_ta.get_replacement_vars_for_sumvars()
189    }
190}
191
192impl ThresholdAutomaton for IntervalThresholdAutomaton {
193    type Rule = IntervalTARule;
194    type InitialVariableConstraintType = IntervalConstraint;
195
196    fn name(&self) -> &str {
197        self.lia_ta.name()
198    }
199
200    fn parameters(&self) -> impl Iterator<Item = &Parameter> {
201        self.lia_ta.parameters()
202    }
203
204    fn resilience_conditions(&self) -> impl Iterator<Item = &BooleanExpression<Parameter>> {
205        self.lia_ta
206            .resilience_conditions()
207            .chain(self.order_expr.iter())
208    }
209
210    fn variables(&self) -> impl Iterator<Item = &Variable> {
211        self.lia_ta.variables()
212    }
213
214    fn locations(&self) -> impl Iterator<Item = &Location> {
215        self.lia_ta.locations()
216    }
217
218    fn can_be_initial_location(&self, location: &Location) -> bool {
219        self.lia_ta.can_be_initial_location(location)
220    }
221
222    fn rules(&self) -> impl Iterator<Item = &Self::Rule> {
223        self.rules.iter()
224    }
225
226    fn incoming_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
227        self.rules
228            .iter()
229            .filter(move |rule| rule.target() == location)
230    }
231
232    fn outgoing_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
233        self.rules
234            .iter()
235            .filter(move |rule| rule.source() == location)
236    }
237
238    fn initial_location_constraints(&self) -> impl Iterator<Item = &LocationConstraint> {
239        self.lia_ta.initial_location_constraints()
240    }
241
242    fn initial_variable_constraints(&self) -> impl Iterator<Item = &IntervalConstraint> {
243        self.initial_variable_constraints.iter()
244    }
245}
246
247/// Trait marking types that can be abstracted by intervals
248///
249/// This trait is implemented by types that have a set of intervals associated
250/// with them, e.g., `Variable` and `WeightedSum<Variable>` that appear in
251/// guards of the automaton.
252pub trait HasAssociatedIntervals: hash::Hash + Eq + Display + Debug + Clone {}
253
254/// Mark `Variable` as having associated intervals, as they appear in guards
255/// `SingleVariableGuard`s
256impl HasAssociatedIntervals for Variable {}
257
258/// Mark `WeightedSum<Variable>` as having associated intervals as they appear
259/// in guards `MultiVariableGuard`s
260impl HasAssociatedIntervals for WeightedSum<Variable> {}
261
262impl fmt::Display for IntervalThresholdAutomaton {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        let order = self.order.to_string();
265
266        let variables = format!(
267            "shared {};",
268            display_iterator_stable_order(self.variables())
269        );
270
271        let parameters = format!(
272            "parameters {};",
273            display_iterator_stable_order(self.parameters())
274        );
275
276        let rc = format!(
277            "assumptions ({}) {{\n{}}}",
278            self.resilience_conditions().count(),
279            indent_all(join_iterator_and_add_back(
280                self.resilience_conditions(),
281                ";\n"
282            ))
283        );
284
285        let mut location_str = String::new();
286        let mut locations = self.locations().collect::<Vec<_>>();
287        locations.sort();
288
289        for (i, loc) in locations.into_iter().enumerate() {
290            location_str += &format!("{loc}:[{i}];\n");
291        }
292        let locations = format!(
293            "locations ({}) {{\n{}}}",
294            self.locations().count(),
295            indent_all(location_str)
296        );
297
298        let initial_cond = format!(
299            "inits ({}) {{\n{}}}",
300            self.initial_location_constraints().count()
301                + self.initial_variable_constraints().count(),
302            indent_all(
303                join_iterator_and_add_back(self.initial_location_constraints(), ";\n")
304                    + &join_iterator_and_add_back(self.initial_variable_constraints(), ";\n")
305            )
306        );
307
308        let mut sorted_rules_by_id = self.rules().collect::<Vec<_>>();
309        sorted_rules_by_id.sort_by_key(|r| &r.id);
310        let rules = format!(
311            "rules ({}) {{\n{}}}",
312            self.rules().count(),
313            indent_all(join_iterator_and_add_back(
314                sorted_rules_by_id.into_iter(),
315                ";\n"
316            ))
317        );
318
319        let ta_body = format!(
320            "{order}\n\n{variables}\n\n{parameters}\n\n{rc}\n\n{locations}\n\n{initial_cond}\n\n{rules}"
321        );
322
323        write!(
324            f,
325            "thresholdAutomaton {} {{\n{}\n}}\n",
326            self.name(),
327            indent_all(ta_body)
328        )
329    }
330}
331
332impl IsDeclared<Parameter> for IntervalThresholdAutomaton {
333    fn is_declared(&self, obj: &Parameter) -> bool {
334        self.lia_ta.is_declared(obj)
335    }
336}
337
338impl IsDeclared<Location> for IntervalThresholdAutomaton {
339    fn is_declared(&self, obj: &Location) -> bool {
340        self.lia_ta.is_declared(obj)
341    }
342}
343
344impl IsDeclared<Variable> for IntervalThresholdAutomaton {
345    fn is_declared(&self, obj: &Variable) -> bool {
346        self.lia_ta.is_declared(obj)
347    }
348}
349
350impl<C: ModelCheckerContext + ProvidesSMTSolverBuilder, SC: TASpecification> TATrait<C, SC>
351    for IntervalThresholdAutomaton
352{
353    type TransformationError = IntervalTATransformationError;
354
355    fn try_from_general_ta(
356        ta: GeneralThresholdAutomaton,
357        ctx: &C,
358        spec_ctx: &SC,
359    ) -> Result<Vec<Self>, Self::TransformationError> {
360        let solver_builder = ctx.get_solver_builder();
361
362        let lta = LIAThresholdAutomaton::try_from(ta);
363        if let Err(lta_err) = lta {
364            return Err(IntervalTATransformationError::LIATransformationError(
365                Box::new(lta_err),
366            ));
367        }
368
369        // TODO: remove this preprocessing when sum of variables is implemented
370        let lta = lta.unwrap().into_ta_without_sum_vars();
371
372        let target_constrs = spec_ctx.var_constraint().into_iter().cloned().collect();
373
374        let builder = IntervalTABuilder::new(lta, solver_builder, target_constrs);
375
376        let itas = builder.build()?;
377        Ok(itas.collect())
378    }
379}
380
381/// Interval abstracted rule
382///
383/// Rule of the abstract threshold automaton with an interval guard
384#[derive(Debug, Clone, Eq, PartialEq, Hash)]
385pub struct IntervalTARule {
386    /// Id assigned to the rule in the specification
387    id: u32,
388    /// Source location of the rule
389    source: Location,
390    /// Target location of the rule
391    target: Location,
392    /// Guard of the rule
393    guard: IntervalConstraint,
394    /// Effect of the rule
395    actions: Vec<IntervalTAAction>,
396}
397
398impl IntervalTARule {
399    /// Creates a new abstract Rule for testing purposes
400    pub fn new(
401        id: u32,
402        source: Location,
403        target: Location,
404        guard: IntervalConstraint,
405        effect: Vec<IntervalTAAction>,
406    ) -> IntervalTARule {
407        IntervalTARule {
408            id,
409            source,
410            target,
411            guard,
412            actions: effect,
413        }
414    }
415
416    /// Check if the rule updates the given variable
417    pub fn updates_variable(&self, var: &Variable) -> bool {
418        self.actions().any(|action| action.variable() == var)
419    }
420
421    /// Returns the guard of the rule
422    pub fn get_guard(&self) -> &IntervalConstraint {
423        &self.guard
424    }
425
426    /// Check whether the rule is enabled in the given state
427    pub fn is_enabled(
428        &self,
429        state: &IntervalState<Variable>,
430    ) -> Result<bool, IntervalOrderError<Variable>> {
431        self.guard.is_enabled(state)
432    }
433
434    /// Derive an interval rule from a rule of a linear integer arithmetic rule
435    /// given a fixed order
436    fn from_lia_rule<O: IntervalOrderFor<Variable> + IntervalOrderFor<WeightedSum<Variable>>>(
437        rule: &LIARule,
438        order: &O,
439    ) -> IntervalTARule {
440        let guard = IntervalConstraint::from_lia_constr(rule.guard(), order)
441            .expect("Failed to derive guard interval constraint");
442
443        let actions = rule.actions().map(|action| action.clone().into()).collect();
444
445        IntervalTARule::new(
446            rule.id(),
447            rule.source().clone(),
448            rule.target().clone(),
449            guard,
450            actions,
451        )
452    }
453}
454
455impl RuleDefinition for IntervalTARule {
456    type Action = IntervalTAAction;
457    type Guard = IntervalConstraint;
458
459    fn id(&self) -> u32 {
460        self.id
461    }
462
463    fn source(&self) -> &Location {
464        &self.source
465    }
466
467    fn target(&self) -> &Location {
468        &self.target
469    }
470
471    fn guard(&self) -> &Self::Guard {
472        &self.guard
473    }
474
475    fn actions(&self) -> impl Iterator<Item = &Self::Action> {
476        self.actions.iter()
477    }
478}
479
480impl Display for IntervalTARule {
481    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
482        let rule_body = indent_all(format!(
483            "when ( {} )\ndo {{ {} }}",
484            self.guard,
485            join_iterator(self.actions.iter(), "; ")
486        ));
487
488        write!(
489            f,
490            "{}: {} -> {}\n{}",
491            self.id, self.source, self.target, rule_body
492        )
493    }
494}
495
496impl Display for IntervalTAAction {
497    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
498        write!(f, "{} {}", self.variable, self.effect)
499    }
500}
501
502impl Display for IntervalActionEffect {
503    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
504        match self {
505            IntervalActionEffect::Inc(_i) => write!(f, "++"),
506            IntervalActionEffect::Dec(_i) => write!(f, "--"),
507            IntervalActionEffect::Reset => write!(f, "= 0"),
508        }
509    }
510}
511
512impl Display for IntervalConstraint {
513    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
514        match self {
515            IntervalConstraint::True => write!(f, "true"),
516            IntervalConstraint::False => write!(f, "false"),
517            IntervalConstraint::Conj(g1, g2) => write!(f, "{g1} && {g2}"),
518            IntervalConstraint::Disj(g1, g2) => write!(f, "{g1} || {g2}"),
519            IntervalConstraint::SingleVarIntervalConstr(var, intervals) => {
520                write!(
521                    f,
522                    "{} ∈ {{ {} }}",
523                    var,
524                    join_iterator(intervals.iter(), ", ")
525                )
526            }
527            IntervalConstraint::MultiVarIntervalConstr(weighted_sum, intervals) => {
528                write!(
529                    f,
530                    "{}  ∈ {{ {} }}",
531                    weighted_sum,
532                    join_iterator(intervals.iter(), ", ")
533                )
534            }
535        }
536    }
537}
538
539/// Action describing the effect of an action on an interval
540///
541/// The action updates the interval of a variable
542#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
543pub struct IntervalTAAction {
544    /// Variable to be updated
545    variable: Variable,
546    /// Effect of the action
547    effect: IntervalActionEffect,
548}
549
550impl IntervalTAAction {
551    /// Creates a new interval action
552    pub fn new(variable: Variable, effect: IntervalActionEffect) -> IntervalTAAction {
553        IntervalTAAction { variable, effect }
554    }
555
556    /// Returns the effect of the action
557    pub fn effect(&self) -> &IntervalActionEffect {
558        &self.effect
559    }
560}
561
562impl From<Action> for IntervalTAAction {
563    fn from(val: Action) -> Self {
564        let variable = val.variable().clone();
565        let effect = match val.update() {
566            UpdateExpression::Inc(i) => IntervalActionEffect::Inc(*i),
567            UpdateExpression::Dec(d) => IntervalActionEffect::Dec(*d),
568            UpdateExpression::Reset => IntervalActionEffect::Reset,
569            UpdateExpression::Unchanged => unreachable!("Should have been removed"),
570        };
571
572        IntervalTAAction::new(variable, effect)
573    }
574}
575
576/// Describes the effect of an action to the interval
577#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
578pub enum IntervalActionEffect {
579    /// Increment
580    Inc(u32),
581    /// Decrement
582    Dec(u32),
583    /// Reset to the zero interval
584    Reset,
585}
586
587impl ActionDefinition for IntervalTAAction {
588    fn variable(&self) -> &Variable {
589        &self.variable
590    }
591
592    fn is_unchanged(&self) -> bool {
593        false
594    }
595
596    fn is_reset(&self) -> bool {
597        matches!(self.effect, IntervalActionEffect::Reset)
598    }
599
600    fn is_increment(&self) -> bool {
601        matches!(self.effect, IntervalActionEffect::Inc(_))
602    }
603
604    fn is_decrement(&self) -> bool {
605        matches!(self.effect, IntervalActionEffect::Dec(_))
606    }
607}
608
609/// Interval guard constraining the interval of evaluation of a variable
610/// or a sum of variables
611#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
612pub enum IntervalConstraint {
613    /// Guard is always enabled
614    True,
615    /// Guard is always disabled
616    False,
617    /// Conjunction of interval guard
618    Conj(Box<IntervalConstraint>, Box<IntervalConstraint>),
619    /// Disjunction of interval guard
620    Disj(Box<IntervalConstraint>, Box<IntervalConstraint>),
621    /// Interval guard over a single variable
622    ///
623    /// The guard is enabled if the variable is in one of the intervals.
624    SingleVarIntervalConstr(Variable, Vec<Interval>),
625    /// Interval guard over a sum of variables
626    ///
627    /// The guard is enabled if the sum of variables is in one of the intervals.
628    MultiVarIntervalConstr(WeightedSum<Variable>, Vec<Interval>),
629}
630
631/// Interval state for a type `T`
632///
633/// This struct assigns an interval to each element of type `T`. It can be used
634/// to track the abstract state, i.e., the current interval in which `T`
635/// currently used.
636///
637/// On this abstract state, one can check for example whether a guard is enabled.
638pub struct IntervalState<T: HasAssociatedIntervals> {
639    // map t to its current interval
640    t_to_interval: HashMap<T, Interval>,
641}
642
643impl IntervalConstraint {
644    /// Check if the guard is enabled in the given state
645    pub fn is_enabled(
646        &self,
647        state: &IntervalState<Variable>,
648    ) -> Result<bool, IntervalOrderError<Variable>> {
649        match self {
650            IntervalConstraint::True => Ok(true),
651            IntervalConstraint::False => Ok(false),
652            IntervalConstraint::Conj(g1, g2) => Ok(g1.is_enabled(state)? && g2.is_enabled(state)?),
653            IntervalConstraint::Disj(g1, g2) => Ok(g1.is_enabled(state)? || g2.is_enabled(state)?),
654            IntervalConstraint::SingleVarIntervalConstr(var, intervals) => {
655                let interval = state
656                    .t_to_interval
657                    .get(var)
658                    .ok_or_else(|| IntervalOrderError::VariableNotFound { var: var.clone() })?;
659
660                Ok(intervals.contains(interval))
661            }
662            IntervalConstraint::MultiVarIntervalConstr(_multivar, _intervals) => {
663                todo!()
664            }
665        }
666    }
667
668    /// Derive an interval guard from a guard of a linear integer arithmetic
669    /// guard under the current interval order
670    fn from_lia_constr<S: IntervalOrderFor<Variable> + IntervalOrderFor<WeightedSum<Variable>>>(
671        guard: &LIAVariableConstraint,
672        order: &S,
673    ) -> Result<Self, IntervalConstraintConstructionError> {
674        match guard {
675            LIAVariableConstraint::True => Ok(IntervalConstraint::True),
676            LIAVariableConstraint::False => Ok(IntervalConstraint::False),
677            LIAVariableConstraint::ComparisonConstraint(_) => {
678                unreachable!("Comparison guards not supported by this algorithm")
679            }
680            LIAVariableConstraint::SingleVarConstraint(g) => {
681                let enabled_intervals = order.compute_enabled_intervals_of_threshold(
682                    g.get_atom(),
683                    g.get_threshold_constraint(),
684                )?;
685
686                // simplify the expressions
687                if enabled_intervals.is_empty() {
688                    return Ok(IntervalConstraint::False);
689                }
690
691                // simplify the expressions
692                if order
693                    .get_all_intervals_of_t(g.get_atom())?
694                    .iter()
695                    .all(|i| enabled_intervals.contains(i))
696                {
697                    return Ok(IntervalConstraint::True);
698                }
699
700                Ok(IntervalConstraint::SingleVarIntervalConstr(
701                    g.get_atom().clone(),
702                    enabled_intervals,
703                ))
704            }
705            LIAVariableConstraint::SumVarConstraint(g) => {
706                Ok(IntervalConstraint::MultiVarIntervalConstr(
707                    g.get_atoms().clone(),
708                    order
709                        .compute_enabled_intervals_of_threshold(
710                            g.get_atoms(),
711                            g.get_threshold_constraint(),
712                        )
713                        .unwrap(),
714                ))
715            }
716            LIAVariableConstraint::BinaryGuard(lhs, con, rhs) => {
717                let lhs_guard = Self::from_lia_constr(lhs, order)?;
718                let rhs_guard = Self::from_lia_constr(rhs, order)?;
719
720                match con {
721                    BooleanConnective::And => {
722                        // simplify the expressions
723                        if lhs_guard == IntervalConstraint::True {
724                            return Ok(rhs_guard);
725                        }
726                        // simplify the expressions
727                        if rhs_guard == IntervalConstraint::True {
728                            return Ok(lhs_guard);
729                        }
730
731                        Ok(IntervalConstraint::Conj(
732                            Box::new(lhs_guard),
733                            Box::new(rhs_guard),
734                        ))
735                    }
736                    BooleanConnective::Or => {
737                        // simplify the expressions
738                        if lhs_guard == IntervalConstraint::False {
739                            return Ok(rhs_guard);
740                        }
741                        // simplify the expressions
742                        if rhs_guard == IntervalConstraint::False {
743                            return Ok(lhs_guard);
744                        }
745
746                        Ok(IntervalConstraint::Disj(
747                            Box::new(lhs_guard),
748                            Box::new(rhs_guard),
749                        ))
750                    }
751                }
752            }
753        }
754    }
755
756    /// Get the the set of intervals for which the constraint `self` is
757    /// satisfied for variable `var`
758    ///
759    /// This function requires `intervals_of_var` to be the set of all intervals
760    /// for `var` in the automaton. (Which can be obtained from the interval
761    /// order)
762    fn get_enabled_intervals<'a>(
763        &'a self,
764        var: &Variable,
765        mut intervals_of_var: Vec<&'a Interval>,
766    ) -> Vec<&'a Interval> {
767        match self {
768            IntervalConstraint::True => intervals_of_var,
769            IntervalConstraint::False => Vec::new(),
770            IntervalConstraint::Conj(lhs, rhs) => {
771                let lhs_intervals = lhs.get_enabled_intervals(var, intervals_of_var);
772                rhs.get_enabled_intervals(var, lhs_intervals)
773            }
774            IntervalConstraint::Disj(lhs, rhs) => {
775                let mut lhs_intervals = lhs.get_enabled_intervals(var, intervals_of_var.clone());
776                let mut rhs_intervals = rhs.get_enabled_intervals(var, intervals_of_var);
777
778                rhs_intervals.retain(|i| !lhs_intervals.contains(i));
779                lhs_intervals.extend(rhs_intervals);
780
781                lhs_intervals
782            }
783            IntervalConstraint::SingleVarIntervalConstr(variable, intervals) => {
784                if variable == var {
785                    intervals_of_var.retain(|i| intervals.contains(i));
786                }
787                intervals_of_var
788            }
789            IntervalConstraint::MultiVarIntervalConstr(weighted_sum, _intervals) => {
790                // FIXME: Implement the interval arithmetic
791                if weighted_sum.contains(var) {
792                    warn!(
793                        "Interval constraint over a sum of variables not fully implemented ! Tried to get constraints for variable {var}"
794                    );
795                }
796
797                intervals_of_var
798            }
799        }
800    }
801}
802
803impl And for IntervalConstraint {
804    fn and(self, other: Self) -> Self {
805        IntervalConstraint::Conj(Box::new(self), Box::new(other))
806    }
807}
808impl_bitand!(IntervalConstraint);
809
810impl Or for IntervalConstraint {
811    fn or(self, other: Self) -> Self {
812        IntervalConstraint::Disj(Box::new(self), Box::new(other))
813    }
814}
815impl_bitor!(IntervalConstraint);
816
817#[derive(Debug, Clone, PartialEq)]
818/// Error that can occur during the construction of an [IntervalConstraint]
819pub enum IntervalConstraintConstructionError {
820    /// Error while attempting to translate a constraint over a single variable
821    VarError(IntervalOrderError<Variable>),
822    /// Error while attempting to translate a constraint over the sum of variables
823    SumVarError(IntervalOrderError<WeightedSum<Variable>>),
824}
825
826impl error::Error for IntervalConstraintConstructionError {}
827
828impl fmt::Display for IntervalConstraintConstructionError {
829    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
830        match self {
831            IntervalConstraintConstructionError::VarError(err) => {
832                write!(f, "Error while constructing an IntervalConstraint: {err}")
833            }
834            IntervalConstraintConstructionError::SumVarError(err) => {
835                write!(f, "Error while constructing an IntervalConstraint: {err}")
836            }
837        }
838    }
839}
840
841impl From<IntervalOrderError<Variable>> for IntervalConstraintConstructionError {
842    fn from(value: IntervalOrderError<Variable>) -> Self {
843        Self::VarError(value)
844    }
845}
846
847impl From<IntervalOrderError<WeightedSum<Variable>>> for IntervalConstraintConstructionError {
848    fn from(value: IntervalOrderError<WeightedSum<Variable>>) -> Self {
849        Self::SumVarError(value)
850    }
851}
852
853impl VariableConstraint for IntervalConstraint {
854    fn as_boolean_expr(&self) -> BooleanExpression<Variable> {
855        match self {
856            IntervalConstraint::True => BooleanExpression::True,
857            IntervalConstraint::False => BooleanExpression::False,
858            IntervalConstraint::Conj(lhs, rhs) => BooleanExpression::BinaryExpression(
859                Box::new(lhs.as_boolean_expr()),
860                BooleanConnective::And,
861                Box::new(rhs.as_boolean_expr()),
862            ),
863            IntervalConstraint::Disj(lhs, rhs) => BooleanExpression::BinaryExpression(
864                Box::new(lhs.as_boolean_expr()),
865                BooleanConnective::Or,
866                Box::new(rhs.as_boolean_expr()),
867            ),
868            IntervalConstraint::SingleVarIntervalConstr(var, intervals) => {
869                intervals.iter().fold(BooleanExpression::False, |acc, i| {
870                    let b_expr = i.encode_into_boolean_expr(var);
871
872                    BooleanExpression::BinaryExpression(
873                        Box::new(acc),
874                        BooleanConnective::Or,
875                        Box::new(b_expr),
876                    )
877                })
878            }
879            IntervalConstraint::MultiVarIntervalConstr(vars, intervals) => {
880                intervals.iter().fold(BooleanExpression::False, |acc, i| {
881                    let b_expr = i.encode_into_boolean_expr(vars);
882
883                    BooleanExpression::BinaryExpression(
884                        Box::new(acc),
885                        BooleanConnective::Or,
886                        Box::new(b_expr),
887                    )
888                })
889            }
890        }
891    }
892}
893
894/// Errors that can occur when trying to construct an interval automaton from a
895/// general automaton
896#[derive(Debug, PartialEq, Clone)]
897pub enum IntervalTATransformationError {
898    /// Error that occurred during while attempting to translate general ta into
899    /// the linear arithmetic form
900    LIATransformationError(Box<LIATransformationError>),
901    /// Comparison Guard found in Rule during transformation
902    ComparisonGuardFoundRule(Box<LIARule>),
903    /// Comparison Guard found in Initial Variable Constraint during transformation
904    ComparisonGuardFoundInitialVariableConstraint(Box<LIAVariableConstraint>),
905    /// Comparison Guard found as part of the interval target
906    ComparisonGuardFoundVariableTarget(Box<LIAVariableConstraint>),
907}
908
909impl fmt::Display for IntervalTATransformationError {
910    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
911        match self {
912            IntervalTATransformationError::LIATransformationError(e) => write!(
913                f,
914                "Failed to derive interval threshold automaton: Transformation to linear arithmetic automaton failed: Error: {e}"
915            ),
916            IntervalTATransformationError::ComparisonGuardFoundRule(rule) => write!(
917                f,
918                "Failed to derive interval threshold automaton: Found comparison guard which is unsupported. Rule: {rule}"
919            ),
920            IntervalTATransformationError::ComparisonGuardFoundInitialVariableConstraint(
921                constr,
922            ) => {
923                write!(
924                    f,
925                    "Failed to derive interval threshold automaton: Found comparison guard in initial variable constraint which is unsupported. Initial variable constraint: {constr}"
926                )
927            }
928            IntervalTATransformationError::ComparisonGuardFoundVariableTarget(constr) => {
929                write!(
930                    f,
931                    "Failed to derive interval threshold automaton: Found comparison guard as part of a interval target constraint: {constr}"
932                )
933            }
934        }
935    }
936}
937
938impl error::Error for IntervalTATransformationError {}
939
940#[cfg(test)]
941mod tests {
942    use std::collections::{BTreeMap, HashSet};
943
944    use interval::IntervalBoundary;
945    use taco_smt_encoder::SMTSolverBuilder;
946    use taco_threshold_automaton::{
947        expressions::{ComparisonOp, IntegerExpression, fraction::Fraction},
948        general_threshold_automaton::{
949            Action,
950            builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
951        },
952        lia_threshold_automaton::{
953            SingleAtomConstraint, SumAtomConstraint,
954            integer_thresholds::{ThresholdCompOp, ThresholdConstraint},
955        },
956    };
957
958    use super::*;
959
960    #[test]
961    fn test_interval_threshold_automaton_getters() {
962        let var = Variable::new("x");
963        let i1 = Interval::new_constant(0, 1);
964        let i2 = Interval::new_constant(1, 2);
965        let i3 = Interval::new(
966            IntervalBoundary::from_const(2),
967            false,
968            IntervalBoundary::from_const(2),
969            false,
970        );
971        let i4 = Interval::new(
972            IntervalBoundary::from_const(2),
973            true,
974            IntervalBoundary::new_infty(),
975            true,
976        );
977
978        let rc = BooleanExpression::ComparisonExpression(
979            Box::new(IntegerExpression::Param(Parameter::new("n"))),
980            ComparisonOp::Gt,
981            Box::new(IntegerExpression::Const(3)),
982        );
983
984        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
985            .with_variable(var.clone())
986            .unwrap()
987            .with_locations([Location::new("l1"), Location::new("l2")])
988            .unwrap()
989            .with_parameter(Parameter::new("n"))
990            .unwrap()
991            .initialize()
992            .with_resilience_condition(rc.clone())
993            .unwrap()
994            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
995                Box::new(IntegerExpression::Atom(Location::new("l1"))),
996                ComparisonOp::Eq,
997                Box::new(IntegerExpression::Const(0)),
998            ))
999            .unwrap()
1000            .with_rule(
1001                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1002                    .with_guard(BooleanExpression::ComparisonExpression(
1003                        Box::new(IntegerExpression::Atom(var.clone())),
1004                        ComparisonOp::Gt,
1005                        Box::new(IntegerExpression::Const(2)),
1006                    ))
1007                    .with_action(
1008                        Action::new(
1009                            var.clone(),
1010                            IntegerExpression::Const(1) + IntegerExpression::Atom(var.clone()),
1011                        )
1012                        .unwrap(),
1013                    )
1014                    .build(),
1015            )
1016            .unwrap()
1017            .build();
1018
1019        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1020
1021        let rule = IntervalTARule::new(
1022            0,
1023            Location::new("l1"),
1024            Location::new("l2"),
1025            IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i4.clone()]),
1026            vec![IntervalTAAction::new(
1027                var.clone(),
1028                IntervalActionEffect::Inc(1),
1029            )],
1030        );
1031
1032        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1033            .build()
1034            .unwrap();
1035        let interval_threshold_automaton = interval_tas.next().unwrap();
1036        assert!(interval_tas.next().is_none());
1037
1038        assert_eq!(interval_threshold_automaton.name(), "test_ta");
1039        assert_eq!(
1040            interval_threshold_automaton
1041                .parameters()
1042                .map(|p| p.to_string())
1043                .collect::<HashSet<_>>(),
1044            HashSet::from(["n".to_string()])
1045        );
1046        assert_eq!(
1047            interval_threshold_automaton
1048                .resilience_conditions()
1049                .collect::<Vec<_>>(),
1050            vec![
1051                &rc,
1052                &BooleanExpression::ComparisonExpression(
1053                    Box::new(IntegerExpression::Const(0)),
1054                    ComparisonOp::Lt,
1055                    Box::new(IntegerExpression::Const(1)),
1056                ),
1057                &BooleanExpression::ComparisonExpression(
1058                    Box::new(IntegerExpression::Const(1)),
1059                    ComparisonOp::Lt,
1060                    Box::new(IntegerExpression::Const(2)),
1061                )
1062            ]
1063        );
1064
1065        assert!(interval_threshold_automaton.is_declared(&Location::new("l1")));
1066        assert!(interval_threshold_automaton.is_declared(&Variable::new("x")));
1067        assert!(interval_threshold_automaton.is_declared(&Parameter::new("n")));
1068
1069        assert_eq!(
1070            interval_threshold_automaton
1071                .incoming_rules(&Location::new("l2"))
1072                .collect::<Vec<_>>(),
1073            vec![&rule]
1074        );
1075        assert_eq!(
1076            interval_threshold_automaton
1077                .outgoing_rules(&Location::new("l1"))
1078                .collect::<Vec<_>>(),
1079            vec![&rule]
1080        );
1081
1082        assert_eq!(
1083            interval_threshold_automaton.get_initial_interval(&var),
1084            vec![&i1, &i2, &i3, &i4,],
1085            "Got: {}\nExpected: {}",
1086            join_iterator(
1087                interval_threshold_automaton
1088                    .get_initial_interval(&var)
1089                    .iter(),
1090                ","
1091            ),
1092            join_iterator([&i1, &i2, &i3, &i4].iter(), ",")
1093        );
1094        assert_eq!(
1095            interval_threshold_automaton.get_zero_interval(&var),
1096            &Interval::zero()
1097        );
1098        assert_eq!(
1099            interval_threshold_automaton.get_intervals(&var),
1100            &vec![i1.clone(), i2.clone(), i3.clone(), i4.clone()]
1101        );
1102        assert_eq!(
1103            interval_threshold_automaton.get_previous_interval(&var, &i1),
1104            None
1105        );
1106        assert_eq!(
1107            interval_threshold_automaton.get_previous_interval(&var, &i2),
1108            Some(&i1)
1109        );
1110        assert_eq!(
1111            interval_threshold_automaton.get_previous_interval(&var, &i3),
1112            Some(&i2)
1113        );
1114        assert_eq!(
1115            interval_threshold_automaton.get_next_interval(&var, &i1),
1116            Some(&i2)
1117        );
1118        assert_eq!(
1119            interval_threshold_automaton.get_next_interval(&var, &i2),
1120            Some(&i3)
1121        );
1122        assert_eq!(
1123            interval_threshold_automaton.get_next_interval(&var, &i3),
1124            Some(&i4)
1125        );
1126        assert_eq!(
1127            interval_threshold_automaton.get_next_interval(&var, &i4),
1128            None
1129        );
1130
1131        assert_eq!(
1132            interval_threshold_automaton
1133                .initial_location_constraints()
1134                .cloned()
1135                .collect::<Vec<_>>(),
1136            vec![BooleanExpression::ComparisonExpression(
1137                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1138                ComparisonOp::Eq,
1139                Box::new(IntegerExpression::Const(0)),
1140            )]
1141        );
1142    }
1143
1144    #[test]
1145    fn test_rule_getter() {
1146        let var = Variable::new("x");
1147        let i = Interval::new(
1148            IntervalBoundary::from_const(2),
1149            false,
1150            IntervalBoundary::new_infty(),
1151            true,
1152        );
1153        let rule = IntervalTARule::new(
1154            0,
1155            Location::new("l1"),
1156            Location::new("l2"),
1157            IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i.clone()]),
1158            vec![],
1159        );
1160
1161        assert_eq!(rule.id(), 0);
1162        assert_eq!(rule.source(), &Location::new("l1"));
1163        assert_eq!(rule.target(), &Location::new("l2"));
1164        assert_eq!(
1165            rule.guard(),
1166            &IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i.clone()])
1167        );
1168    }
1169
1170    #[test]
1171    fn test_display_abstract_rule() {
1172        let var = Variable::new("x");
1173        let i = Interval::new(
1174            IntervalBoundary::from_const(2),
1175            false,
1176            IntervalBoundary::new_infty(),
1177            true,
1178        );
1179        let rule = IntervalTARule::new(
1180            0,
1181            Location::new("l1"),
1182            Location::new("l2"),
1183            IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i.clone()]),
1184            vec![IntervalTAAction::new(
1185                var.clone(),
1186                IntervalActionEffect::Reset,
1187            )],
1188        );
1189
1190        assert_eq!(
1191            rule.to_string(),
1192            "0: l1 -> l2\n    when ( x ∈ { [2, ∞[ } )\n    do { x = 0 }"
1193        );
1194
1195        let rule = IntervalTARule::new(
1196            1,
1197            Location::new("l1"),
1198            Location::new("l2"),
1199            IntervalConstraint::True,
1200            vec![IntervalTAAction::new(
1201                var.clone(),
1202                IntervalActionEffect::Inc(1),
1203            )],
1204        );
1205
1206        assert_eq!(
1207            rule.to_string(),
1208            "1: l1 -> l2\n    when ( true )\n    do { x ++ }"
1209        );
1210
1211        let var2 = Variable::new("y");
1212        let rule = IntervalTARule::new(
1213            3,
1214            Location::new("l1"),
1215            Location::new("l2"),
1216            IntervalConstraint::MultiVarIntervalConstr(
1217                WeightedSum::new(BTreeMap::from([(var.clone(), 1), (var2.clone(), 1)])),
1218                vec![i.clone()],
1219            ),
1220            vec![IntervalTAAction::new(
1221                var.clone(),
1222                IntervalActionEffect::Dec(2),
1223            )],
1224        );
1225
1226        assert_eq!(
1227            rule.to_string(),
1228            "3: l1 -> l2\n    when ( x + y  ∈ { [2, ∞[ } )\n    do { x -- }"
1229        );
1230    }
1231
1232    #[test]
1233    fn test_get_initial_intervals() {
1234        let var1 = Variable::new("x");
1235        let var2 = Variable::new("y");
1236
1237        let i4 = Interval::new(
1238            IntervalBoundary::from_const(1),
1239            false,
1240            IntervalBoundary::Infty,
1241            true,
1242        );
1243
1244        let rc = BooleanExpression::ComparisonExpression(
1245            Box::new(IntegerExpression::Param(Parameter::new("n"))),
1246            ComparisonOp::Gt,
1247            Box::new(IntegerExpression::Const(3)),
1248        );
1249
1250        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1251            .with_variables([var1.clone(), var2.clone()])
1252            .unwrap()
1253            .with_locations([Location::new("l1"), Location::new("l2")])
1254            .unwrap()
1255            .with_parameter(Parameter::new("n"))
1256            .unwrap()
1257            .initialize()
1258            .with_resilience_condition(rc.clone())
1259            .unwrap()
1260            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1261                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1262                ComparisonOp::Eq,
1263                Box::new(IntegerExpression::Const(0)),
1264            ))
1265            .unwrap()
1266            .with_initial_variable_constraints([BooleanExpression::ComparisonExpression(
1267                Box::new(IntegerExpression::Atom(var1.clone())),
1268                ComparisonOp::Eq,
1269                Box::new(IntegerExpression::Const(0)),
1270            )])
1271            .unwrap()
1272            .with_rule(
1273                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1274                    .with_guard(BooleanExpression::ComparisonExpression(
1275                        Box::new(IntegerExpression::Atom(var1.clone())),
1276                        ComparisonOp::Gt,
1277                        Box::new(IntegerExpression::Const(2)),
1278                    ))
1279                    .with_action(
1280                        Action::new(
1281                            var1.clone(),
1282                            IntegerExpression::Const(1) + IntegerExpression::Atom(var1.clone()),
1283                        )
1284                        .unwrap(),
1285                    )
1286                    .build(),
1287            )
1288            .unwrap()
1289            .build();
1290
1291        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1292
1293        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1294            .build()
1295            .unwrap();
1296        let interval_threshold_automaton = interval_tas.next().unwrap();
1297        assert!(interval_tas.next().is_none());
1298
1299        assert_eq!(
1300            interval_threshold_automaton.get_initial_interval(&var1),
1301            vec![&Interval::zero(),]
1302        );
1303        assert_eq!(
1304            interval_threshold_automaton.get_initial_interval(&var2),
1305            vec![&Interval::zero(), &i4]
1306        );
1307    }
1308
1309    #[test]
1310    fn test_interval_action_getters() {
1311        let var = Variable::new("x");
1312        let action = IntervalTAAction::new(var.clone(), IntervalActionEffect::Inc(1));
1313
1314        assert_eq!(action.variable(), &var);
1315        assert!(!action.is_unchanged());
1316        assert!(!action.is_reset());
1317        assert!(action.is_increment());
1318        assert!(!action.is_decrement());
1319
1320        let action = IntervalTAAction::new(var.clone(), IntervalActionEffect::Reset);
1321
1322        assert_eq!(action.variable(), &var);
1323        assert!(!action.is_unchanged());
1324        assert!(action.is_reset());
1325        assert!(!action.is_increment());
1326        assert!(!action.is_decrement());
1327
1328        let action = IntervalTAAction::new(var.clone(), IntervalActionEffect::Dec(1));
1329
1330        assert_eq!(action.variable(), &var);
1331        assert!(!action.is_unchanged());
1332        assert!(!action.is_reset());
1333        assert!(!action.is_increment());
1334        assert!(action.is_decrement());
1335    }
1336
1337    #[test]
1338    fn test_interval_guard_is_enabled() {
1339        let i1 = Interval::new_constant(0, 1);
1340        let i2 = Interval::new_constant(2, 3);
1341        let i3 = Interval::new_constant(4, 5);
1342
1343        let var = Variable::new("x");
1344        let state = IntervalState {
1345            t_to_interval: HashMap::from([(var.clone(), i1.clone())]),
1346        };
1347
1348        let guard = IntervalConstraint::SingleVarIntervalConstr(
1349            var.clone(),
1350            Vec::from([i1.clone(), i2.clone()]),
1351        );
1352        assert!(guard.is_enabled(&state).unwrap());
1353
1354        let guard = IntervalConstraint::SingleVarIntervalConstr(
1355            var.clone(),
1356            Vec::from([i2.clone(), i3.clone()]),
1357        );
1358        assert!(!guard.is_enabled(&state).unwrap());
1359
1360        let guard = IntervalConstraint::Conj(
1361            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1362                var.clone(),
1363                Vec::from([i1.clone(), i3.clone()]),
1364            )),
1365            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1366                var.clone(),
1367                Vec::from([i1.clone(), i2.clone()]),
1368            )),
1369        );
1370        assert!(guard.is_enabled(&state).unwrap());
1371
1372        let guard = IntervalConstraint::Conj(
1373            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1374                var.clone(),
1375                Vec::from([i1.clone(), i2.clone()]),
1376            )),
1377            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1378                var.clone(),
1379                Vec::from([i3.clone()]),
1380            )),
1381        );
1382        assert!(!guard.is_enabled(&state).unwrap());
1383
1384        let guard = IntervalConstraint::Disj(
1385            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1386                var.clone(),
1387                Vec::from([i1.clone()]),
1388            )),
1389            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1390                var.clone(),
1391                Vec::from([i3.clone()]),
1392            )),
1393        );
1394        assert!(guard.is_enabled(&state).unwrap());
1395
1396        let guard = IntervalConstraint::Disj(
1397            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1398                var.clone(),
1399                Vec::from([i2.clone()]),
1400            )),
1401            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1402                var.clone(),
1403                Vec::from([i3.clone()]),
1404            )),
1405        );
1406        assert!(!guard.is_enabled(&state).unwrap());
1407
1408        let guard = IntervalConstraint::True;
1409        assert!(guard.is_enabled(&state).unwrap());
1410    }
1411
1412    #[test]
1413    fn test_interval_guard_is_enabled_error() {
1414        let i1 = Interval::new_constant(0, 1);
1415
1416        let state: IntervalState<Variable> = IntervalState {
1417            t_to_interval: HashMap::new(),
1418        };
1419
1420        let var = Variable::new("x");
1421        let guard =
1422            IntervalConstraint::SingleVarIntervalConstr(var.clone(), Vec::from([i1.clone()]));
1423
1424        let err = guard.is_enabled(&state);
1425        assert!(matches!(err, Err(IntervalOrderError::VariableNotFound { var: v }) if v == var));
1426    }
1427
1428    #[test]
1429    fn test_guard_to_boolean_expr() {
1430        let guard = IntervalConstraint::True;
1431        assert_eq!(guard.as_boolean_expr(), BooleanExpression::True);
1432
1433        let guard = IntervalConstraint::False;
1434        assert_eq!(guard.as_boolean_expr(), BooleanExpression::False);
1435
1436        let var = Variable::new("x");
1437        let i1 = Interval::new_constant(0, 1);
1438        let i2 = Interval::new_constant(1, 2);
1439
1440        let guard =
1441            IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i1.clone(), i2.clone()]);
1442        assert_eq!(
1443            guard.as_boolean_expr(),
1444            BooleanExpression::BinaryExpression(
1445                Box::new(BooleanExpression::BinaryExpression(
1446                    Box::new(BooleanExpression::False),
1447                    BooleanConnective::Or,
1448                    Box::new(BooleanExpression::BinaryExpression(
1449                        Box::new(BooleanExpression::ComparisonExpression(
1450                            Box::new(IntegerExpression::Atom(var.clone())),
1451                            ComparisonOp::Geq,
1452                            Box::new(IntegerExpression::Const(0))
1453                        )),
1454                        BooleanConnective::And,
1455                        Box::new(BooleanExpression::ComparisonExpression(
1456                            Box::new(IntegerExpression::Atom(var.clone())),
1457                            ComparisonOp::Lt,
1458                            Box::new(IntegerExpression::Const(1))
1459                        ))
1460                    )),
1461                )),
1462                BooleanConnective::Or,
1463                Box::new(BooleanExpression::BinaryExpression(
1464                    Box::new(BooleanExpression::ComparisonExpression(
1465                        Box::new(IntegerExpression::Atom(var.clone())),
1466                        ComparisonOp::Geq,
1467                        Box::new(IntegerExpression::Const(1))
1468                    )),
1469                    BooleanConnective::And,
1470                    Box::new(BooleanExpression::ComparisonExpression(
1471                        Box::new(IntegerExpression::Atom(var.clone())),
1472                        ComparisonOp::Lt,
1473                        Box::new(IntegerExpression::Const(2))
1474                    ))
1475                ))
1476            )
1477        );
1478
1479        let guard = IntervalConstraint::MultiVarIntervalConstr(
1480            WeightedSum::new([(var.clone(), 1)]),
1481            vec![i1.clone(), i2.clone()],
1482        );
1483        assert_eq!(
1484            guard.as_boolean_expr(),
1485            BooleanExpression::BinaryExpression(
1486                Box::new(BooleanExpression::BinaryExpression(
1487                    Box::new(BooleanExpression::False),
1488                    BooleanConnective::Or,
1489                    Box::new(BooleanExpression::BinaryExpression(
1490                        Box::new(BooleanExpression::ComparisonExpression(
1491                            Box::new(IntegerExpression::Atom(var.clone())),
1492                            ComparisonOp::Geq,
1493                            Box::new(IntegerExpression::Const(0))
1494                        )),
1495                        BooleanConnective::And,
1496                        Box::new(BooleanExpression::ComparisonExpression(
1497                            Box::new(IntegerExpression::Atom(var.clone())),
1498                            ComparisonOp::Lt,
1499                            Box::new(IntegerExpression::Const(1))
1500                        ))
1501                    )),
1502                )),
1503                BooleanConnective::Or,
1504                Box::new(BooleanExpression::BinaryExpression(
1505                    Box::new(BooleanExpression::ComparisonExpression(
1506                        Box::new(IntegerExpression::Atom(var.clone())),
1507                        ComparisonOp::Geq,
1508                        Box::new(IntegerExpression::Const(1))
1509                    )),
1510                    BooleanConnective::And,
1511                    Box::new(BooleanExpression::ComparisonExpression(
1512                        Box::new(IntegerExpression::Atom(var.clone())),
1513                        ComparisonOp::Lt,
1514                        Box::new(IntegerExpression::Const(2))
1515                    ))
1516                ))
1517            )
1518        );
1519
1520        let guard = IntervalConstraint::Conj(
1521            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1522                var.clone(),
1523                vec![i1.clone()],
1524            )),
1525            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1526                var.clone(),
1527                vec![i2.clone()],
1528            )),
1529        );
1530        assert_eq!(
1531            guard.as_boolean_expr(),
1532            BooleanExpression::BinaryExpression(
1533                Box::new(BooleanExpression::BinaryExpression(
1534                    Box::new(BooleanExpression::False),
1535                    BooleanConnective::Or,
1536                    Box::new(BooleanExpression::BinaryExpression(
1537                        Box::new(BooleanExpression::ComparisonExpression(
1538                            Box::new(IntegerExpression::Atom(var.clone())),
1539                            ComparisonOp::Geq,
1540                            Box::new(IntegerExpression::Const(0))
1541                        )),
1542                        BooleanConnective::And,
1543                        Box::new(BooleanExpression::ComparisonExpression(
1544                            Box::new(IntegerExpression::Atom(var.clone())),
1545                            ComparisonOp::Lt,
1546                            Box::new(IntegerExpression::Const(1))
1547                        ))
1548                    )),
1549                )),
1550                BooleanConnective::And,
1551                Box::new(BooleanExpression::BinaryExpression(
1552                    Box::new(BooleanExpression::False),
1553                    BooleanConnective::Or,
1554                    Box::new(BooleanExpression::BinaryExpression(
1555                        Box::new(BooleanExpression::ComparisonExpression(
1556                            Box::new(IntegerExpression::Atom(var.clone())),
1557                            ComparisonOp::Geq,
1558                            Box::new(IntegerExpression::Const(1))
1559                        )),
1560                        BooleanConnective::And,
1561                        Box::new(BooleanExpression::ComparisonExpression(
1562                            Box::new(IntegerExpression::Atom(var.clone())),
1563                            ComparisonOp::Lt,
1564                            Box::new(IntegerExpression::Const(2))
1565                        ))
1566                    )),
1567                )),
1568            )
1569        );
1570
1571        let guard = IntervalConstraint::Disj(
1572            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1573                var.clone(),
1574                vec![i1.clone()],
1575            )),
1576            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1577                var.clone(),
1578                vec![i2.clone()],
1579            )),
1580        );
1581
1582        assert_eq!(
1583            guard.as_boolean_expr(),
1584            BooleanExpression::BinaryExpression(
1585                Box::new(BooleanExpression::BinaryExpression(
1586                    Box::new(BooleanExpression::False),
1587                    BooleanConnective::Or,
1588                    Box::new(BooleanExpression::BinaryExpression(
1589                        Box::new(BooleanExpression::ComparisonExpression(
1590                            Box::new(IntegerExpression::Atom(var.clone())),
1591                            ComparisonOp::Geq,
1592                            Box::new(IntegerExpression::Const(0))
1593                        )),
1594                        BooleanConnective::And,
1595                        Box::new(BooleanExpression::ComparisonExpression(
1596                            Box::new(IntegerExpression::Atom(var.clone())),
1597                            ComparisonOp::Lt,
1598                            Box::new(IntegerExpression::Const(1))
1599                        ))
1600                    )),
1601                )),
1602                BooleanConnective::Or,
1603                Box::new(BooleanExpression::BinaryExpression(
1604                    Box::new(BooleanExpression::False),
1605                    BooleanConnective::Or,
1606                    Box::new(BooleanExpression::BinaryExpression(
1607                        Box::new(BooleanExpression::ComparisonExpression(
1608                            Box::new(IntegerExpression::Atom(var.clone())),
1609                            ComparisonOp::Geq,
1610                            Box::new(IntegerExpression::Const(1))
1611                        )),
1612                        BooleanConnective::And,
1613                        Box::new(BooleanExpression::ComparisonExpression(
1614                            Box::new(IntegerExpression::Atom(var.clone())),
1615                            ComparisonOp::Lt,
1616                            Box::new(IntegerExpression::Const(2))
1617                        ))
1618                    )),
1619                )),
1620            )
1621        );
1622    }
1623
1624    #[test]
1625    fn test_display_interval_threshold_automaton() {
1626        let var = Variable::new("x");
1627
1628        let rc = BooleanExpression::ComparisonExpression(
1629            Box::new(IntegerExpression::Param(Parameter::new("n"))),
1630            ComparisonOp::Gt,
1631            Box::new(IntegerExpression::Const(3)),
1632        );
1633
1634        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1635            .with_variable(var.clone())
1636            .unwrap()
1637            .with_locations([Location::new("l1"), Location::new("l2")])
1638            .unwrap()
1639            .with_parameter(Parameter::new("n"))
1640            .unwrap()
1641            .initialize()
1642            .with_resilience_condition(rc.clone())
1643            .unwrap()
1644            .with_rule(
1645                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1646                    .with_guard(BooleanExpression::ComparisonExpression(
1647                        Box::new(IntegerExpression::Atom(var.clone())),
1648                        ComparisonOp::Gt,
1649                        Box::new(IntegerExpression::Const(2)),
1650                    ))
1651                    .with_action(
1652                        Action::new(
1653                            var.clone(),
1654                            IntegerExpression::Const(1) + IntegerExpression::Atom(var.clone()),
1655                        )
1656                        .unwrap(),
1657                    )
1658                    .build(),
1659            )
1660            .unwrap()
1661            .build();
1662
1663        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1664
1665        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1666            .build()
1667            .unwrap();
1668        let interval_threshold_automaton = interval_tas.next().unwrap();
1669        assert!(interval_tas.next().is_none());
1670
1671        let expected = "thresholdAutomaton test_ta {\n    intervalOrder {\n        x: [0, 1[, [1, 2[, [2, 2], ]2, ∞[;\n    }\n\n    shared x;\n\n    parameters n;\n\n    assumptions (3) {\n        n > 3;\n        0 < 1;\n        1 < 2;\n    }\n\n    locations (2) {\n        l1:[0];\n        l2:[1];\n    }\n\n    inits (0) {\n    }\n\n    rules (1) {\n        0: l1 -> l2\n            when ( x ∈ { ]2, ∞[ } )\n            do { x ++ };\n    }\n}\n";
1672
1673        assert_eq!(interval_threshold_automaton.to_string(), expected);
1674    }
1675
1676    #[test]
1677    fn test_interval_constraint_from_lia() {
1678        let i0 = Interval::new_constant(0, 1);
1679        let i1 = Interval::new_constant(1, 2);
1680        let i2 = Interval::new(
1681            IntervalBoundary::from_const(2),
1682            false,
1683            IntervalBoundary::new_bound(
1684                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
1685                Fraction::from(0),
1686            ),
1687            true,
1688        );
1689        let i3 = Interval::new(
1690            IntervalBoundary::new_bound(
1691                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
1692                Fraction::from(0),
1693            ),
1694            true,
1695            IntervalBoundary::Infty,
1696            true,
1697        );
1698
1699        let order = StaticIntervalOrder::new(
1700            HashMap::from([(
1701                Variable::new("x"),
1702                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
1703            )]),
1704            HashMap::from([(
1705                WeightedSum::new([
1706                    (Variable::new("x"), Fraction::from(1)),
1707                    (Variable::new("y"), Fraction::from(1)),
1708                ]),
1709                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
1710            )]),
1711            HashMap::new(),
1712        );
1713
1714        // x > 2
1715        let lguard = LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
1716            Variable::new("x"),
1717            ThresholdConstraint::new(
1718                ThresholdCompOp::Geq,
1719                Vec::<(_, u32)>::new(),
1720                Fraction::from(2),
1721            ),
1722        ));
1723        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
1724        let expected_guard = IntervalConstraint::SingleVarIntervalConstr(
1725            Variable::new("x"),
1726            vec![i2.clone(), i3.clone()],
1727        );
1728        // x ∈ {[2, ∞[}
1729        assert_eq!(
1730            got_guard, expected_guard,
1731            "Got {got_guard} \n Expected {expected_guard}"
1732        );
1733
1734        // x + y < 2
1735        let lguard = LIAVariableConstraint::SumVarConstraint(
1736            SumAtomConstraint::try_new(
1737                [
1738                    (Variable::new("x"), Fraction::from(1)),
1739                    (Variable::new("y"), Fraction::from(1)),
1740                ],
1741                ThresholdConstraint::new(
1742                    ThresholdCompOp::Lt,
1743                    Vec::<(_, u32)>::new(),
1744                    Fraction::from(2),
1745                ),
1746            )
1747            .unwrap(),
1748        );
1749        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
1750        let expected_guard = IntervalConstraint::MultiVarIntervalConstr(
1751            WeightedSum::new([
1752                (Variable::new("x"), Fraction::from(1)),
1753                (Variable::new("y"), Fraction::from(1)),
1754            ]),
1755            vec![i0.clone(), i1.clone()],
1756        );
1757        // x + y ∈ {[0, 1[, [1, 2[}
1758        assert_eq!(
1759            got_guard, expected_guard,
1760            "Got {got_guard} \n Expected {expected_guard}"
1761        );
1762
1763        // x < 2 && x >= 1
1764        let lguard = LIAVariableConstraint::BinaryGuard(
1765            Box::new(LIAVariableConstraint::SingleVarConstraint(
1766                SingleAtomConstraint::new(
1767                    Variable::new("x"),
1768                    ThresholdConstraint::new(
1769                        ThresholdCompOp::Lt,
1770                        Vec::<(_, u32)>::new(),
1771                        Fraction::from(2),
1772                    ),
1773                ),
1774            )),
1775            BooleanConnective::And,
1776            Box::new(LIAVariableConstraint::SingleVarConstraint(
1777                SingleAtomConstraint::new(
1778                    Variable::new("x"),
1779                    ThresholdConstraint::new(
1780                        ThresholdCompOp::Geq,
1781                        Vec::<(_, u32)>::new(),
1782                        Fraction::from(1),
1783                    ),
1784                ),
1785            )),
1786        );
1787        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
1788        let expected_guard = IntervalConstraint::Conj(
1789            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1790                Variable::new("x"),
1791                vec![i0.clone(), i1.clone()],
1792            )),
1793            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1794                Variable::new("x"),
1795                vec![i1.clone(), i2.clone(), i3.clone()],
1796            )),
1797        );
1798        // x ∈ {[0,1[, [1, 2[} && {[1,2[, [2, ∞[}
1799        assert_eq!(
1800            got_guard, expected_guard,
1801            "Got {got_guard} \n Expected {expected_guard}"
1802        );
1803
1804        // x < 2 || x >= 1
1805        let lguard = LIAVariableConstraint::BinaryGuard(
1806            Box::new(LIAVariableConstraint::SingleVarConstraint(
1807                SingleAtomConstraint::new(
1808                    Variable::new("x"),
1809                    ThresholdConstraint::new(
1810                        ThresholdCompOp::Lt,
1811                        Vec::<(_, u32)>::new(),
1812                        Fraction::from(2),
1813                    ),
1814                ),
1815            )),
1816            BooleanConnective::Or,
1817            Box::new(LIAVariableConstraint::SingleVarConstraint(
1818                SingleAtomConstraint::new(
1819                    Variable::new("x"),
1820                    ThresholdConstraint::new(
1821                        ThresholdCompOp::Geq,
1822                        Vec::<(_, u32)>::new(),
1823                        Fraction::from(1),
1824                    ),
1825                ),
1826            )),
1827        );
1828        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
1829        let expected_guard = IntervalConstraint::Disj(
1830            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1831                Variable::new("x"),
1832                vec![i0.clone(), i1.clone()],
1833            )),
1834            Box::new(IntervalConstraint::SingleVarIntervalConstr(
1835                Variable::new("x"),
1836                vec![i1.clone(), i2.clone(), i3.clone()],
1837            )),
1838        );
1839        // x ∈ {[0,1[, [1, 2[} || {[1,2[, [2, ∞[}
1840        assert_eq!(
1841            got_guard, expected_guard,
1842            "Got {got_guard} \n Expected {expected_guard}"
1843        );
1844    }
1845
1846    #[test]
1847    fn test_from_lia_simplify_and() {
1848        let i0 = Interval::new_constant(0, 1);
1849        let i1 = Interval::new_constant(1, 2);
1850        let i2 = Interval::new(
1851            IntervalBoundary::from_const(2),
1852            false,
1853            IntervalBoundary::new_bound(
1854                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
1855                Fraction::from(0),
1856            ),
1857            true,
1858        );
1859        let i3 = Interval::new(
1860            IntervalBoundary::new_bound(
1861                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
1862                Fraction::from(0),
1863            ),
1864            true,
1865            IntervalBoundary::Infty,
1866            true,
1867        );
1868
1869        let order = StaticIntervalOrder::new(
1870            HashMap::from([(
1871                Variable::new("x"),
1872                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
1873            )]),
1874            HashMap::from([(
1875                WeightedSum::new([
1876                    (Variable::new("x"), Fraction::from(1)),
1877                    (Variable::new("y"), Fraction::from(1)),
1878                ]),
1879                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
1880            )]),
1881            HashMap::new(),
1882        );
1883
1884        // (x >= 2) && true
1885        let lguard = LIAVariableConstraint::BinaryGuard(
1886            Box::new(LIAVariableConstraint::SingleVarConstraint(
1887                SingleAtomConstraint::new(
1888                    Variable::new("x"),
1889                    ThresholdConstraint::new(
1890                        ThresholdCompOp::Geq,
1891                        Vec::<(_, u32)>::new(),
1892                        Fraction::from(2),
1893                    ),
1894                ),
1895            )),
1896            BooleanConnective::And,
1897            Box::new(LIAVariableConstraint::True),
1898        );
1899        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
1900        let expected_guard = IntervalConstraint::SingleVarIntervalConstr(
1901            Variable::new("x"),
1902            vec![i2.clone(), i3.clone()],
1903        );
1904        // x ∈ {[2, ∞[}
1905        assert_eq!(
1906            got_guard, expected_guard,
1907            "Got {got_guard} \n Expected {expected_guard}"
1908        );
1909
1910        // true && (x >= 2)
1911        let lguard = LIAVariableConstraint::BinaryGuard(
1912            Box::new(LIAVariableConstraint::True),
1913            BooleanConnective::And,
1914            Box::new(LIAVariableConstraint::SingleVarConstraint(
1915                SingleAtomConstraint::new(
1916                    Variable::new("x"),
1917                    ThresholdConstraint::new(
1918                        ThresholdCompOp::Geq,
1919                        Vec::<(_, u32)>::new(),
1920                        Fraction::from(2),
1921                    ),
1922                ),
1923            )),
1924        );
1925        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
1926        let expected_guard = IntervalConstraint::SingleVarIntervalConstr(
1927            Variable::new("x"),
1928            vec![i2.clone(), i3.clone()],
1929        );
1930        // x ∈ {[2, ∞[}
1931        assert_eq!(
1932            got_guard, expected_guard,
1933            "Got {got_guard} \n Expected {expected_guard}"
1934        );
1935    }
1936
1937    #[test]
1938    fn test_from_lia_simplify_or() {
1939        let i0 = Interval::new_constant(0, 1);
1940        let i1 = Interval::new_constant(1, 2);
1941        let i2 = Interval::new(
1942            IntervalBoundary::from_const(2),
1943            false,
1944            IntervalBoundary::new_bound(
1945                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
1946                Fraction::from(0),
1947            ),
1948            true,
1949        );
1950        let i3 = Interval::new(
1951            IntervalBoundary::new_bound(
1952                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
1953                Fraction::from(0),
1954            ),
1955            true,
1956            IntervalBoundary::Infty,
1957            true,
1958        );
1959
1960        let order = StaticIntervalOrder::new(
1961            HashMap::from([(
1962                Variable::new("x"),
1963                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
1964            )]),
1965            HashMap::from([(
1966                WeightedSum::new([
1967                    (Variable::new("x"), Fraction::from(1)),
1968                    (Variable::new("y"), Fraction::from(1)),
1969                ]),
1970                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
1971            )]),
1972            HashMap::new(),
1973        );
1974
1975        // (x >= 2) || false
1976        let lguard = LIAVariableConstraint::BinaryGuard(
1977            Box::new(LIAVariableConstraint::SingleVarConstraint(
1978                SingleAtomConstraint::new(
1979                    Variable::new("x"),
1980                    ThresholdConstraint::new(
1981                        ThresholdCompOp::Geq,
1982                        Vec::<(_, u32)>::new(),
1983                        Fraction::from(2),
1984                    ),
1985                ),
1986            )),
1987            BooleanConnective::Or,
1988            Box::new(LIAVariableConstraint::False),
1989        );
1990        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
1991        let expected_guard = IntervalConstraint::SingleVarIntervalConstr(
1992            Variable::new("x"),
1993            vec![i2.clone(), i3.clone()],
1994        );
1995        // x ∈ {[2, ∞[}
1996        assert_eq!(
1997            got_guard, expected_guard,
1998            "Got {got_guard} \n Expected {expected_guard}"
1999        );
2000
2001        // false || (x >= 2)
2002        let lguard = LIAVariableConstraint::BinaryGuard(
2003            Box::new(LIAVariableConstraint::False),
2004            BooleanConnective::Or,
2005            Box::new(LIAVariableConstraint::SingleVarConstraint(
2006                SingleAtomConstraint::new(
2007                    Variable::new("x"),
2008                    ThresholdConstraint::new(
2009                        ThresholdCompOp::Geq,
2010                        Vec::<(_, u32)>::new(),
2011                        Fraction::from(2),
2012                    ),
2013                ),
2014            )),
2015        );
2016        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
2017        let expected_guard = IntervalConstraint::SingleVarIntervalConstr(
2018            Variable::new("x"),
2019            vec![i2.clone(), i3.clone()],
2020        );
2021        // x ∈ {[2, ∞[}
2022        assert_eq!(
2023            got_guard, expected_guard,
2024            "Got {got_guard} \n Expected {expected_guard}"
2025        );
2026    }
2027
2028    #[test]
2029    fn test_from_lia_simplify_interval_all_or_none() {
2030        let i0 = Interval::new_constant(0, 1);
2031        let i1 = Interval::new_constant(1, 2);
2032        let i2 = Interval::new(
2033            IntervalBoundary::from_const(2),
2034            false,
2035            IntervalBoundary::new_bound(
2036                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
2037                Fraction::from(0),
2038            ),
2039            true,
2040        );
2041        let i3 = Interval::new(
2042            IntervalBoundary::new_bound(
2043                WeightedSum::new([(Parameter::new("n"), Fraction::from(1))]),
2044                Fraction::from(0),
2045            ),
2046            true,
2047            IntervalBoundary::Infty,
2048            true,
2049        );
2050
2051        let order = StaticIntervalOrder::new(
2052            HashMap::from([(
2053                Variable::new("x"),
2054                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
2055            )]),
2056            HashMap::from([(
2057                WeightedSum::new([
2058                    (Variable::new("x"), Fraction::from(1)),
2059                    (Variable::new("y"), Fraction::from(1)),
2060                ]),
2061                vec![i0.clone(), i1.clone(), i2.clone(), i3.clone()],
2062            )]),
2063            HashMap::new(),
2064        );
2065
2066        // x >= 0
2067        let lguard = LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
2068            Variable::new("x"),
2069            ThresholdConstraint::new(
2070                ThresholdCompOp::Geq,
2071                Vec::<(_, u32)>::new(),
2072                Fraction::from(0),
2073            ),
2074        ));
2075        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
2076        let expected_guard = IntervalConstraint::True;
2077        // true
2078        assert_eq!(
2079            got_guard, expected_guard,
2080            "Got {got_guard} \n Expected {expected_guard}"
2081        );
2082
2083        // x < 0
2084        let lguard = LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
2085            Variable::new("x"),
2086            ThresholdConstraint::new(
2087                ThresholdCompOp::Lt,
2088                Vec::<(_, u32)>::new(),
2089                Fraction::from(0),
2090            ),
2091        ));
2092        let got_guard = IntervalConstraint::from_lia_constr(&lguard, &order).unwrap();
2093        let expected_guard = IntervalConstraint::False;
2094        // true
2095        assert_eq!(
2096            got_guard, expected_guard,
2097            "Got {got_guard} \n Expected {expected_guard}"
2098        );
2099    }
2100
2101    #[test]
2102    fn test_get_interval_constr() {
2103        let var = Variable::new("x");
2104
2105        let rc = BooleanExpression::ComparisonExpression(
2106            Box::new(IntegerExpression::Param(Parameter::new("n"))),
2107            ComparisonOp::Gt,
2108            Box::new(IntegerExpression::Const(3)),
2109        );
2110
2111        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2112            .with_variables([var.clone(), Variable::new("y")])
2113            .unwrap()
2114            .with_locations([Location::new("l1"), Location::new("l2")])
2115            .unwrap()
2116            .with_parameter(Parameter::new("n"))
2117            .unwrap()
2118            .initialize()
2119            .with_resilience_condition(rc.clone())
2120            .unwrap()
2121            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2122                Box::new(IntegerExpression::Atom(Location::new("l1"))),
2123                ComparisonOp::Eq,
2124                Box::new(IntegerExpression::Const(0)),
2125            ))
2126            .unwrap()
2127            .with_rule(
2128                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2129                    .with_guard(BooleanExpression::ComparisonExpression(
2130                        Box::new(IntegerExpression::Atom(var.clone())),
2131                        ComparisonOp::Gt,
2132                        Box::new(IntegerExpression::Const(2)),
2133                    ))
2134                    .with_action(
2135                        Action::new(
2136                            var.clone(),
2137                            IntegerExpression::Const(1) + IntegerExpression::Atom(var.clone()),
2138                        )
2139                        .unwrap(),
2140                    )
2141                    .build(),
2142            )
2143            .unwrap()
2144            .build();
2145
2146        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2147
2148        let lia_constr: LIAVariableConstraint = (&BooleanExpression::ComparisonExpression(
2149            Box::new(IntegerExpression::Atom(Variable::new("x"))),
2150            ComparisonOp::Eq,
2151            Box::new(IntegerExpression::Const(0)),
2152        ))
2153            .try_into()
2154            .unwrap();
2155
2156        let mut interval_tas = IntervalTABuilder::new(
2157            lia_ta,
2158            SMTSolverBuilder::default(),
2159            vec![lia_constr.clone()],
2160        )
2161        .build()
2162        .unwrap();
2163        let interval_ta = interval_tas.next().unwrap();
2164
2165        let expected_interval_constr = IntervalConstraint::SingleVarIntervalConstr(
2166            Variable::new("x"),
2167            vec![Interval::new_constant(0, 1)],
2168        );
2169
2170        assert_eq!(
2171            interval_ta.get_interval_constraint(&lia_constr).unwrap(),
2172            expected_interval_constr
2173        )
2174    }
2175
2176    #[test]
2177    fn test_get_interval_constr_unknown_var() {
2178        let var = Variable::new("x");
2179
2180        let rc = BooleanExpression::ComparisonExpression(
2181            Box::new(IntegerExpression::Param(Parameter::new("n"))),
2182            ComparisonOp::Gt,
2183            Box::new(IntegerExpression::Const(3)),
2184        );
2185
2186        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2187            .with_variables([var.clone(), Variable::new("y")])
2188            .unwrap()
2189            .with_locations([Location::new("l1"), Location::new("l2")])
2190            .unwrap()
2191            .with_parameter(Parameter::new("n"))
2192            .unwrap()
2193            .initialize()
2194            .with_resilience_condition(rc.clone())
2195            .unwrap()
2196            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2197                Box::new(IntegerExpression::Atom(Location::new("l1"))),
2198                ComparisonOp::Eq,
2199                Box::new(IntegerExpression::Const(0)),
2200            ))
2201            .unwrap()
2202            .with_rule(
2203                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2204                    .with_guard(BooleanExpression::ComparisonExpression(
2205                        Box::new(IntegerExpression::Atom(var.clone())),
2206                        ComparisonOp::Gt,
2207                        Box::new(IntegerExpression::Const(2)),
2208                    ))
2209                    .with_action(
2210                        Action::new(
2211                            var.clone(),
2212                            IntegerExpression::Const(1) + IntegerExpression::Atom(var.clone()),
2213                        )
2214                        .unwrap(),
2215                    )
2216                    .build(),
2217            )
2218            .unwrap()
2219            .build();
2220
2221        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2222
2223        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2224            .build()
2225            .unwrap();
2226        let interval_ta = interval_tas.next().unwrap();
2227
2228        let lia_constr: LIAVariableConstraint = (&BooleanExpression::ComparisonExpression(
2229            Box::new(IntegerExpression::Atom(Variable::new("unknown"))),
2230            ComparisonOp::Eq,
2231            Box::new(IntegerExpression::Const(0)),
2232        ))
2233            .try_into()
2234            .unwrap();
2235
2236        let got_interval_constr = interval_ta.get_interval_constraint(&lia_constr);
2237
2238        assert!(got_interval_constr.is_err());
2239        assert_eq!(
2240            got_interval_constr.unwrap_err(),
2241            IntervalConstraintConstructionError::VarError(IntervalOrderError::VariableNotFound {
2242                var: Variable::new("unknown")
2243            })
2244        )
2245    }
2246
2247    #[test]
2248    fn test_get_constrained_variables() {
2249        let var = Variable::new("x");
2250
2251        let rc = BooleanExpression::ComparisonExpression(
2252            Box::new(IntegerExpression::Param(Parameter::new("n"))),
2253            ComparisonOp::Gt,
2254            Box::new(IntegerExpression::Const(3)),
2255        );
2256
2257        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2258            .with_variables([var.clone(), Variable::new("y")])
2259            .unwrap()
2260            .with_locations([Location::new("l1"), Location::new("l2")])
2261            .unwrap()
2262            .with_parameter(Parameter::new("n"))
2263            .unwrap()
2264            .initialize()
2265            .with_resilience_condition(rc.clone())
2266            .unwrap()
2267            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2268                Box::new(IntegerExpression::Atom(Location::new("l1"))),
2269                ComparisonOp::Eq,
2270                Box::new(IntegerExpression::Const(0)),
2271            ))
2272            .unwrap()
2273            .with_rule(
2274                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2275                    .with_guard(BooleanExpression::ComparisonExpression(
2276                        Box::new(IntegerExpression::Atom(var.clone())),
2277                        ComparisonOp::Gt,
2278                        Box::new(IntegerExpression::Const(2)),
2279                    ))
2280                    .with_action(
2281                        Action::new(
2282                            var.clone(),
2283                            IntegerExpression::Const(1) + IntegerExpression::Atom(var.clone()),
2284                        )
2285                        .unwrap(),
2286                    )
2287                    .build(),
2288            )
2289            .unwrap()
2290            .build();
2291
2292        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2293
2294        let lia_constr: LIAVariableConstraint = (&BooleanExpression::ComparisonExpression(
2295            Box::new(IntegerExpression::Atom(Variable::new("x"))),
2296            ComparisonOp::Eq,
2297            Box::new(IntegerExpression::Const(10)),
2298        ))
2299            .try_into()
2300            .unwrap();
2301
2302        let mut interval_tas = IntervalTABuilder::new(
2303            lia_ta,
2304            SMTSolverBuilder::default(),
2305            vec![lia_constr.clone()],
2306        )
2307        .build()
2308        .unwrap();
2309        let interval_ta = interval_tas.next().unwrap();
2310
2311        let interval_constr = interval_ta.get_interval_constraint(&lia_constr).unwrap();
2312
2313        assert_eq!(
2314            interval_ta.get_variables_constrained(&interval_constr),
2315            vec![&Variable::new("x")]
2316        )
2317    }
2318
2319    #[test]
2320    fn test_display_interval_constraint_construction_error() {
2321        let var_error: IntervalConstraintConstructionError = IntervalOrderError::VariableNotFound {
2322            var: Variable::new("x"),
2323        }
2324        .into();
2325        assert!(
2326            var_error
2327                .to_string()
2328                .contains("Error while constructing an IntervalConstraint:"),
2329        );
2330
2331        let sum_var_error: IntervalConstraintConstructionError =
2332            IntervalOrderError::VariableNotFound {
2333                var: WeightedSum::new([(Variable::new("x"), 1), (Variable::new("y"), 1)]),
2334            }
2335            .into();
2336        assert!(
2337            sum_var_error
2338                .to_string()
2339                .contains("Error while constructing an IntervalConstraint:"),
2340        );
2341    }
2342
2343    #[test]
2344    fn test_display_interval_ta_transformation_error() {
2345        let var = Variable::new("x");
2346        let rule = RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2347            .with_guard(BooleanExpression::ComparisonExpression(
2348                Box::new(IntegerExpression::Atom(var.clone())),
2349                ComparisonOp::Gt,
2350                Box::new(IntegerExpression::Atom(Variable::new("y"))),
2351            ))
2352            .build()
2353            .try_into()
2354            .unwrap();
2355        let error = IntervalTATransformationError::ComparisonGuardFoundRule(Box::new(rule));
2356        assert!(
2357            error
2358                .to_string()
2359                .contains("Found comparison guard which is unsupported")
2360        );
2361
2362        let constr: LIAVariableConstraint = BooleanExpression::ComparisonExpression(
2363            Box::new(IntegerExpression::Atom(var.clone())),
2364            ComparisonOp::Gt,
2365            Box::new(IntegerExpression::Atom(Variable::new("y"))),
2366        )
2367        .try_into()
2368        .unwrap();
2369        let error = IntervalTATransformationError::ComparisonGuardFoundInitialVariableConstraint(
2370            Box::new(constr.clone()),
2371        );
2372        assert!(
2373            error
2374                .to_string()
2375                .contains("Found comparison guard in initial variable constraint")
2376        );
2377
2378        let error =
2379            IntervalTATransformationError::ComparisonGuardFoundVariableTarget(Box::new(constr));
2380        assert!(
2381            error
2382                .to_string()
2383                .contains("Found comparison guard as part of a interval target constraint")
2384        );
2385    }
2386
2387    #[test]
2388    fn test_and_trait_and_bitand_operator() {
2389        let var = Variable::new("x");
2390        let i1 = Interval::new_constant(0, 1);
2391        let i2 = Interval::new_constant(1, 2);
2392
2393        let lhs = IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i1.clone()]);
2394        let rhs = IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i2.clone()]);
2395
2396        let lhs_clone = lhs.clone();
2397        let expected = IntervalConstraint::Conj(Box::new(lhs_clone.clone()), Box::new(rhs.clone()));
2398
2399        // The `and` method of the `And` trait combines both constraints with a
2400        // conjunction, keeping the operand order
2401        assert_eq!(lhs_clone.and(rhs.clone()), expected);
2402
2403        // The `&` operator delegates to `And::and`
2404        assert_eq!(lhs & rhs, expected);
2405    }
2406
2407    #[test]
2408    fn test_or_trait_and_bitor_operator() {
2409        let var = Variable::new("x");
2410        let i1 = Interval::new_constant(0, 1);
2411        let i2 = Interval::new_constant(1, 2);
2412
2413        let lhs = IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i1.clone()]);
2414        let rhs = IntervalConstraint::SingleVarIntervalConstr(var.clone(), vec![i2.clone()]);
2415
2416        let lhs_clone = lhs.clone();
2417        let expected = IntervalConstraint::Disj(Box::new(lhs_clone.clone()), Box::new(rhs.clone()));
2418
2419        // The `or` method of the `Or` trait combines both constraints with a
2420        // disjunction, keeping the operand order
2421        assert_eq!(lhs_clone.or(rhs.clone()), expected);
2422
2423        // The `|` operator delegates to `Or::or`
2424        assert_eq!(lhs | rhs, expected);
2425    }
2426}