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