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, 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#[derive(Debug, Clone)]
53pub struct IntervalThresholdAutomaton {
54 lia_ta: LIAThresholdAutomaton,
56 initial_variable_constraints: Vec<IntervalConstraint>,
58 rules: Vec<IntervalTARule>,
60 order: StaticIntervalOrder,
62 order_expr: Vec<BooleanExpression<Parameter>>,
64}
65
66impl IntervalThresholdAutomaton {
67 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 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 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 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 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 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 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 pub fn get_enabled_intervals<'a>(
142 &'a self,
143 constr: &'a IntervalConstraint,
144 var: &Variable,
145 ) -> impl Iterator<Item = &'a Interval> {
146 constr
149 .get_enabled_intervals(var, self.get_intervals(var).iter().collect())
150 .into_iter()
151 }
152
153 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 pub fn get_ta(&self) -> &GeneralThresholdAutomaton {
167 self.lia_ta.get_ta()
168 }
169
170 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 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
247pub trait HasAssociatedIntervals: hash::Hash + Eq + Display + Debug + Clone {}
253
254impl HasAssociatedIntervals for Variable {}
257
258impl 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 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#[derive(Debug, Clone, Eq, PartialEq, Hash)]
385pub struct IntervalTARule {
386 id: u32,
388 source: Location,
390 target: Location,
392 guard: IntervalConstraint,
394 actions: Vec<IntervalTAAction>,
396}
397
398impl IntervalTARule {
399 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 pub fn updates_variable(&self, var: &Variable) -> bool {
418 self.actions().any(|action| action.variable() == var)
419 }
420
421 pub fn get_guard(&self) -> &IntervalConstraint {
423 &self.guard
424 }
425
426 pub fn is_enabled(
428 &self,
429 state: &IntervalState<Variable>,
430 ) -> Result<bool, IntervalOrderError<Variable>> {
431 self.guard.is_enabled(state)
432 }
433
434 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#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
543pub struct IntervalTAAction {
544 variable: Variable,
546 effect: IntervalActionEffect,
548}
549
550impl IntervalTAAction {
551 pub fn new(variable: Variable, effect: IntervalActionEffect) -> IntervalTAAction {
553 IntervalTAAction { variable, effect }
554 }
555
556 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#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
578pub enum IntervalActionEffect {
579 Inc(u32),
581 Dec(u32),
583 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#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
612pub enum IntervalConstraint {
613 True,
615 False,
617 Conj(Box<IntervalConstraint>, Box<IntervalConstraint>),
619 Disj(Box<IntervalConstraint>, Box<IntervalConstraint>),
621 SingleVarIntervalConstr(Variable, Vec<Interval>),
625 MultiVarIntervalConstr(WeightedSum<Variable>, Vec<Interval>),
629}
630
631pub struct IntervalState<T: HasAssociatedIntervals> {
639 t_to_interval: HashMap<T, Interval>,
641}
642
643impl IntervalConstraint {
644 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 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 if enabled_intervals.is_empty() {
688 return Ok(IntervalConstraint::False);
689 }
690
691 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 if lhs_guard == IntervalConstraint::True {
724 return Ok(rhs_guard);
725 }
726 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 if lhs_guard == IntervalConstraint::False {
739 return Ok(rhs_guard);
740 }
741 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 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 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)]
818pub enum IntervalConstraintConstructionError {
820 VarError(IntervalOrderError<Variable>),
822 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#[derive(Debug, PartialEq, Clone)]
897pub enum IntervalTATransformationError {
898 LIATransformationError(Box<LIATransformationError>),
901 ComparisonGuardFoundRule(Box<LIARule>),
903 ComparisonGuardFoundInitialVariableConstraint(Box<LIAVariableConstraint>),
905 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 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 assert_eq!(
1730 got_guard, expected_guard,
1731 "Got {got_guard} \n Expected {expected_guard}"
1732 );
1733
1734 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 assert_eq!(
1759 got_guard, expected_guard,
1760 "Got {got_guard} \n Expected {expected_guard}"
1761 );
1762
1763 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 assert_eq!(
1800 got_guard, expected_guard,
1801 "Got {got_guard} \n Expected {expected_guard}"
1802 );
1803
1804 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 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 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 assert_eq!(
1906 got_guard, expected_guard,
1907 "Got {got_guard} \n Expected {expected_guard}"
1908 );
1909
1910 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 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 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 assert_eq!(
1997 got_guard, expected_guard,
1998 "Got {got_guard} \n Expected {expected_guard}"
1999 );
2000
2001 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 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 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 assert_eq!(
2079 got_guard, expected_guard,
2080 "Got {got_guard} \n Expected {expected_guard}"
2081 );
2082
2083 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 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 assert_eq!(lhs_clone.and(rhs.clone()), expected);
2402
2403 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 assert_eq!(lhs_clone.or(rhs.clone()), expected);
2422
2423 assert_eq!(lhs | rhs, expected);
2425 }
2426}