1use std::collections::HashSet;
8
9use std::error::Error;
10use std::fmt::Display;
11
12use taco_display_utils::{
13 display_iterator_stable_order, indent_all, join_iterator, join_iterator_and_add_back,
14};
15
16use crate::expressions::fraction::Fraction;
17use crate::expressions::{
18 And, Atomic, BooleanConnective, BooleanExpression, IntegerExpression, Or, Parameter, Variable,
19};
20use crate::expressions::{IsDeclared, Location};
21use crate::general_threshold_automaton::{Action, GeneralThresholdAutomaton, Rule};
22use crate::lia_threshold_automaton::integer_thresholds::{
23 IntoNoDivBooleanExpr, Threshold, ThresholdConstraint, ThresholdConstraintOver, WeightedSum,
24};
25use crate::{
26 LocationConstraint, RuleDefinition, ThresholdAutomaton, VariableConstraint, impl_bitand,
27 impl_bitor,
28};
29
30use super::{
31 ComparisonConstraint, ComparisonConstraintCreationError, LIARule, LIAThresholdAutomaton,
32 LIATransformationError, LIAVariableConstraint, SingleAtomConstraint, SumAtomConstraint,
33 SumVarConstraintCreationError,
34};
35
36impl LIAThresholdAutomaton {
37 pub fn get_max_number_of_thresholds_per_rule(&self) -> u32 {
40 self.rules
41 .values()
42 .flatten()
43 .map(|rule| rule.number_of_thresholds())
44 .max()
45 .unwrap_or(0)
46 }
47
48 pub fn get_ta(&self) -> &GeneralThresholdAutomaton {
51 &self.ta
52 }
53
54 pub fn has_sum_var_threshold_guard(&self) -> bool {
57 self.rules
58 .values()
59 .flatten()
60 .any(|r| r.guard().has_sum_var())
61 }
62
63 pub fn has_comparison_guard(&self) -> bool {
66 self.rules
67 .values()
68 .flatten()
69 .any(|r| r.guard().has_comparison_guard())
70 }
71
72 pub fn get_single_var_constraints_rules(&self) -> HashSet<SingleAtomConstraint<Variable>> {
77 self.rules()
78 .flat_map(|r| r.guard().get_single_var_constraints())
79 .collect()
80 }
81
82 pub fn get_sum_var_constraints(&self) -> HashSet<SumAtomConstraint<Variable>> {
87 self.rules()
88 .flat_map(|r| r.guard().get_multi_variable_guards())
89 .collect()
90 }
91
92 pub fn get_comparison_guards(&self) -> HashSet<ComparisonConstraint<Variable>> {
97 self.rules()
98 .flat_map(|r| r.guard().get_comparison_guards())
99 .collect()
100 }
101
102 pub fn get_thresholds(&self) -> HashSet<Threshold> {
105 self.rules()
106 .flat_map(|r| r.get_distinct_thresholds())
107 .collect()
108 }
109
110 pub fn get_derived_rule(&self, r: &LIARule) -> Rule {
112 self.ta
113 .rules()
114 .find(|rule| {
115 rule.id() == r.id() && rule.source() == r.source() && rule.target() == r.target()
116 })
117 .expect("Could not find derived rule")
118 .clone()
119 }
120}
121
122impl ThresholdAutomaton for LIAThresholdAutomaton {
123 type Rule = LIARule;
124 type InitialVariableConstraintType = LIAVariableConstraint;
125
126 fn name(&self) -> &str {
127 self.ta.name()
128 }
129
130 fn variables(&self) -> impl Iterator<Item = &Variable> {
131 self.ta
133 .variables()
134 .chain(self.additional_vars_for_sums.keys())
135 }
136
137 fn parameters(&self) -> impl Iterator<Item = &Parameter> {
138 self.ta.parameters()
139 }
140
141 fn locations(&self) -> impl Iterator<Item = &Location> {
142 self.ta.locations()
143 }
144
145 fn resilience_conditions(
146 &self,
147 ) -> impl Iterator<Item = &crate::expressions::BooleanExpression<Parameter>> {
148 self.ta.resilience_conditions()
149 }
150
151 fn can_be_initial_location(&self, location: &Location) -> bool {
152 self.ta.can_be_initial_location(location)
153 }
154
155 fn rules(&self) -> impl Iterator<Item = &Self::Rule> {
156 self.rules.values().flatten()
157 }
158
159 fn incoming_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
160 self.rules().filter(move |r| r.target() == location)
161 }
162
163 fn outgoing_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
164 self.rules.get(location).into_iter().flatten()
165 }
166
167 fn initial_location_constraints(&self) -> impl Iterator<Item = &LocationConstraint> {
168 self.ta.initial_location_conditions()
169 }
170
171 fn initial_variable_constraints(&self) -> impl Iterator<Item = &LIAVariableConstraint> {
172 self.init_variable_constr.iter()
173 }
174}
175
176impl Display for LIAThresholdAutomaton {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 let variables = format!(
181 "shared {};",
182 display_iterator_stable_order(self.ta.variables())
183 );
184 let parameters = format!(
185 "parameters {};",
186 display_iterator_stable_order(self.ta.parameters())
187 );
188
189 let rc = format!(
190 "assumptions ({}) {{\n{}}}",
191 self.ta.resilience_conditions().count(),
192 indent_all(join_iterator_and_add_back(
193 self.ta.resilience_conditions(),
194 ";\n"
195 ))
196 );
197
198 let mut loc_str = String::new();
199 let mut locations = self.ta.locations().collect::<Vec<_>>();
200 locations.sort();
201 for (i, loc) in locations.into_iter().enumerate() {
202 loc_str += &format!("{loc}:[{i}];\n");
203 }
204 let locations = format!(
205 "locations ({}) {{\n{}}}",
206 self.ta.locations().count(),
207 indent_all(loc_str)
208 );
209
210 let initial_cond = format!(
211 "inits ({}) {{\n{}}}",
212 self.ta.initial_location_conditions().count() + self.init_variable_constr.len(),
213 indent_all(
214 join_iterator_and_add_back(self.ta.initial_location_conditions(), ";\n")
215 + &join_iterator_and_add_back(
216 self.init_variable_constr
217 .iter()
218 .map(|cstr| cstr.as_boolean_expr()),
219 ";\n"
220 )
221 )
222 );
223
224 let mut sorted_rules_by_id = self.rules.values().flatten().collect::<Vec<_>>();
225 sorted_rules_by_id.sort_by_key(|r| &r.id);
226 let rules = format!(
227 "rules ({}) {{\n{}}}",
228 self.rules.len(),
229 indent_all(join_iterator_and_add_back(
230 sorted_rules_by_id.into_iter(),
231 ";\n"
232 ))
233 );
234
235 let ta_body = format!(
236 "{variables}\n\n{parameters}\n\n{rc}\n\n{locations}\n\n{initial_cond}\n\n{rules}"
237 );
238
239 write!(
240 f,
241 "thresholdAutomaton {} {{\n{}\n}}\n",
242 self.ta.name(),
243 indent_all(ta_body)
244 )
245 }
246}
247
248impl IsDeclared<Parameter> for LIAThresholdAutomaton {
249 fn is_declared(&self, obj: &Parameter) -> bool {
250 self.ta.is_declared(obj)
251 }
252}
253
254impl IsDeclared<Variable> for LIAThresholdAutomaton {
255 fn is_declared(&self, obj: &Variable) -> bool {
256 self.ta.is_declared(obj)
257 }
258}
259
260impl IsDeclared<Location> for LIAThresholdAutomaton {
261 fn is_declared(&self, obj: &Location) -> bool {
262 self.ta.is_declared(obj)
263 }
264}
265
266impl LIARule {
267 fn get_distinct_thresholds(&self) -> HashSet<Threshold> {
269 self.guard.get_distinct_thresholds()
270 }
271
272 pub fn number_of_thresholds(&self) -> u32 {
274 self.guard.count_number_of_thresholds()
275 }
276
277 pub fn number_of_updates(&self) -> u32 {
279 self.actions.len() as u32
280 }
281}
282
283impl RuleDefinition for LIARule {
284 type Action = Action;
285
286 type Guard = LIAVariableConstraint;
287
288 fn id(&self) -> u32 {
289 self.id
290 }
291
292 fn source(&self) -> &Location {
293 &self.source
294 }
295
296 fn target(&self) -> &Location {
297 &self.target
298 }
299
300 fn guard(&self) -> &Self::Guard {
301 &self.guard
302 }
303
304 fn actions(&self) -> impl Iterator<Item = &Self::Action> {
305 self.actions.iter()
306 }
307}
308
309impl Display for LIARule {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 let guard_string = self.guard.to_string();
312 let update_string = join_iterator(self.actions.iter(), "; ");
313
314 let rule_body = indent_all(format!("when ( {guard_string} )\ndo {{ {update_string} }}"));
315
316 write!(
317 f,
318 "{}: {} -> {}\n{}",
319 self.id, self.source, self.target, rule_body
320 )
321 }
322}
323
324impl LIAVariableConstraint {
325 pub fn is_top(&self) -> bool {
340 match self {
341 LIAVariableConstraint::False
342 | LIAVariableConstraint::ComparisonConstraint(_)
343 | LIAVariableConstraint::SingleVarConstraint(_)
344 | LIAVariableConstraint::SumVarConstraint(_) => false,
345 LIAVariableConstraint::BinaryGuard(lhs, BooleanConnective::And, rhs) => {
346 lhs.is_top() && rhs.is_top()
347 }
348 LIAVariableConstraint::BinaryGuard(lhs, BooleanConnective::Or, rhs) => {
349 lhs.is_top() || rhs.is_top()
350 }
351 LIAVariableConstraint::True => true,
352 }
353 }
354
355 pub fn is_bot(&self) -> bool {
370 match self {
371 LIAVariableConstraint::True
372 | LIAVariableConstraint::ComparisonConstraint(_)
373 | LIAVariableConstraint::SingleVarConstraint(_)
374 | LIAVariableConstraint::SumVarConstraint(_) => false,
375 LIAVariableConstraint::BinaryGuard(lhs, BooleanConnective::And, rhs) => {
376 lhs.is_bot() || rhs.is_bot()
377 }
378 LIAVariableConstraint::BinaryGuard(lhs, BooleanConnective::Or, rhs) => {
379 lhs.is_bot() && rhs.is_bot()
380 }
381 LIAVariableConstraint::False => true,
382 }
383 }
384
385 fn get_distinct_thresholds(&self) -> HashSet<Threshold> {
387 match self {
388 LIAVariableConstraint::ComparisonConstraint(cg) => {
389 HashSet::from([cg.get_threshold().clone()])
390 }
391 LIAVariableConstraint::SingleVarConstraint(svg) => {
392 HashSet::from([svg.get_threshold().clone()])
393 }
394 LIAVariableConstraint::SumVarConstraint(mvg) => {
395 HashSet::from([mvg.get_threshold().clone()])
396 }
397 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
398 let mut l_thr = lhs.get_distinct_thresholds();
399 let r_thr = rhs.get_distinct_thresholds();
400 l_thr.extend(r_thr);
401 l_thr
402 }
403 LIAVariableConstraint::True | LIAVariableConstraint::False => HashSet::new(),
404 }
405 }
406
407 pub fn count_number_of_thresholds(&self) -> u32 {
409 match self {
410 LIAVariableConstraint::ComparisonConstraint(_)
411 | LIAVariableConstraint::SingleVarConstraint(_)
412 | LIAVariableConstraint::SumVarConstraint(_) => 1,
413 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
414 lhs.count_number_of_thresholds() + rhs.count_number_of_thresholds()
415 }
416 LIAVariableConstraint::True => 0,
417 LIAVariableConstraint::False => 0,
418 }
419 }
420
421 pub fn has_sum_var(&self) -> bool {
423 match self {
424 LIAVariableConstraint::SumVarConstraint(_) => true,
425 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
426 lhs.has_sum_var() || rhs.has_sum_var()
427 }
428 _ => false,
429 }
430 }
431
432 pub fn has_comparison_guard(&self) -> bool {
435 match self {
436 LIAVariableConstraint::ComparisonConstraint(_) => true,
437 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
438 lhs.has_comparison_guard() || rhs.has_comparison_guard()
439 }
440 _ => false,
441 }
442 }
443
444 pub fn get_single_var_constraints(&self) -> Vec<SingleAtomConstraint<Variable>> {
446 match self {
447 LIAVariableConstraint::SingleVarConstraint(s) => vec![s.clone()],
448 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
449 let mut guards = lhs.get_single_var_constraints();
450 guards.extend(rhs.get_single_var_constraints());
451 guards
452 }
453 LIAVariableConstraint::SumVarConstraint(_)
454 | LIAVariableConstraint::ComparisonConstraint(_)
455 | LIAVariableConstraint::True
456 | LIAVariableConstraint::False => vec![],
457 }
458 }
459
460 pub fn get_multi_variable_guards(&self) -> Vec<SumAtomConstraint<Variable>> {
462 match self {
463 LIAVariableConstraint::SumVarConstraint(m) => vec![m.clone()],
464 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
465 let mut guards = lhs.get_multi_variable_guards();
466 guards.extend(rhs.get_multi_variable_guards());
467 guards
468 }
469 LIAVariableConstraint::SingleVarConstraint(_)
470 | LIAVariableConstraint::ComparisonConstraint(_)
471 | LIAVariableConstraint::True
472 | LIAVariableConstraint::False => vec![],
473 }
474 }
475
476 pub fn get_comparison_guards(&self) -> Vec<ComparisonConstraint<Variable>> {
478 match self {
479 LIAVariableConstraint::ComparisonConstraint(c) => vec![c.clone()],
480 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
481 let mut guards = lhs.get_comparison_guards();
482 guards.extend(rhs.get_comparison_guards());
483 guards
484 }
485 LIAVariableConstraint::SingleVarConstraint(_)
486 | LIAVariableConstraint::SumVarConstraint(_)
487 | LIAVariableConstraint::True
488 | LIAVariableConstraint::False => vec![],
489 }
490 }
491
492 pub fn is_upper_guard(&self) -> bool {
494 match self {
495 LIAVariableConstraint::ComparisonConstraint(c) => c.is_upper_guard(),
496 LIAVariableConstraint::SingleVarConstraint(s) => s.is_upper_guard(),
497 LIAVariableConstraint::SumVarConstraint(m) => m.is_upper_guard(),
498 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
499 lhs.is_upper_guard() || rhs.is_upper_guard()
500 }
501 LIAVariableConstraint::True | LIAVariableConstraint::False => false,
502 }
503 }
504
505 pub fn is_lower_guard(&self) -> bool {
507 match self {
508 LIAVariableConstraint::ComparisonConstraint(c) => c.is_lower_guard(),
509 LIAVariableConstraint::SingleVarConstraint(s) => s.is_lower_guard(),
510 LIAVariableConstraint::SumVarConstraint(m) => m.is_lower_guard(),
511 LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
512 lhs.is_lower_guard() || rhs.is_lower_guard()
513 }
514 LIAVariableConstraint::True | LIAVariableConstraint::False => false,
515 }
516 }
517}
518
519impl And for LIAVariableConstraint {
520 fn and(self, other: Self) -> Self {
521 LIAVariableConstraint::BinaryGuard(Box::new(self), BooleanConnective::And, Box::new(other))
522 }
523}
524impl_bitand!(LIAVariableConstraint);
525
526impl Or for LIAVariableConstraint {
527 fn or(self, other: Self) -> Self {
528 LIAVariableConstraint::BinaryGuard(Box::new(self), BooleanConnective::Or, Box::new(other))
529 }
530}
531impl_bitor!(LIAVariableConstraint);
532
533impl VariableConstraint for LIAVariableConstraint {
534 fn as_boolean_expr(&self) -> crate::expressions::BooleanExpression<Variable> {
535 match self {
536 LIAVariableConstraint::ComparisonConstraint(guard) => guard.to_boolean_expr(),
537 LIAVariableConstraint::SingleVarConstraint(guard) => guard.to_boolean_expr(),
538 LIAVariableConstraint::SumVarConstraint(guard) => guard.to_boolean_expr(),
539 LIAVariableConstraint::BinaryGuard(lhs, boolean_connective, rhs) => {
540 let lhs = lhs.as_boolean_expr();
541 let rhs = rhs.as_boolean_expr();
542 BooleanExpression::BinaryExpression(
543 Box::new(lhs),
544 *boolean_connective,
545 Box::new(rhs),
546 )
547 }
548 LIAVariableConstraint::True => BooleanExpression::True,
549 LIAVariableConstraint::False => BooleanExpression::False,
550 }
551 }
552}
553
554impl<T: Atomic> SingleAtomConstraint<T> {
555 pub fn new(atom: T, thr: ThresholdConstraint) -> Self {
557 Self(ThresholdConstraintOver::new(atom, thr))
558 }
559
560 pub fn is_upper_guard(&self) -> bool {
562 self.0.is_upper_guard()
563 }
564
565 pub fn is_lower_guard(&self) -> bool {
567 self.0.is_lower_guard()
568 }
569
570 pub fn get_atom(&self) -> &T {
572 self.0.get_t()
573 }
574
575 pub fn to_boolean_expr<S>(&self) -> BooleanExpression<S>
577 where
578 T: IntoNoDivBooleanExpr<S>,
579 IntegerExpression<S>: From<T>,
580 S: Atomic,
581 {
582 self.0.encode_to_boolean_expr()
583 }
584
585 pub fn get_threshold(&self) -> &Threshold {
587 self.0.get_threshold()
588 }
589
590 pub fn get_threshold_constraint(&self) -> &ThresholdConstraint {
592 self.0.get_threshold_constraint()
593 }
594}
595
596impl<T: Atomic> Display for SingleAtomConstraint<T> {
597 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
598 write!(f, "{}", self.0)
599 }
600}
601
602impl<T: Atomic> SumAtomConstraint<T> {
603 pub fn try_new<F: Into<Fraction>, I: IntoIterator<Item = (T, F)> + Clone>(
614 atoms: I,
615 mut thr: ThresholdConstraint,
616 ) -> Result<Self, SumVarConstraintCreationError> {
617 let mut variables = WeightedSum::new(atoms);
618
619 if (&variables).into_iter().count() == 1 {
620 return Err(SumVarConstraintCreationError::IsSingleAtomConstraint);
621 }
622
623 if (&variables).into_iter().any(|(_, f)| f.is_negative())
624 && (&variables).into_iter().any(|(_, f)| !f.is_negative())
625 {
626 return Err(SumVarConstraintCreationError::IsComparisonConstraint);
627 }
628
629 if (&variables).into_iter().all(|(_, f)| f.is_negative()) {
630 variables.scale(-Fraction::from(1));
631 thr.scale(-Fraction::from(1));
632 }
633
634 let constr = ThresholdConstraintOver::new(variables, thr);
635
636 Ok(Self(constr))
637 }
638
639 pub fn is_upper_guard(&self) -> bool {
641 self.0.is_upper_guard()
642 }
643
644 pub fn is_lower_guard(&self) -> bool {
646 self.0.is_lower_guard()
647 }
648
649 pub fn get_atoms(&self) -> &WeightedSum<T> {
651 self.0.get_t()
652 }
653
654 pub fn to_boolean_expr<S>(&self) -> BooleanExpression<S>
656 where
657 T: IntoNoDivBooleanExpr<S>,
658 IntegerExpression<S>: From<T>,
659 S: Atomic,
660 {
661 self.0.encode_to_boolean_expr()
662 }
663
664 pub fn get_threshold(&self) -> &Threshold {
666 self.0.get_threshold()
667 }
668
669 pub fn get_threshold_constraint(&self) -> &ThresholdConstraint {
671 self.0.get_threshold_constraint()
672 }
673}
674
675impl<T: Atomic> Display for SumAtomConstraint<T> {
676 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
677 write!(f, "{}", self.0)
678 }
679}
680
681impl Error for SumVarConstraintCreationError {}
682
683impl Display for SumVarConstraintCreationError {
684 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
685 match self {
686 SumVarConstraintCreationError::IsComparisonConstraint => {
687 write!(f, "Guard is a ComparisonGuard not a MultiVarThresholdGuard")
688 }
689 SumVarConstraintCreationError::IsSingleAtomConstraint => {
690 write!(
691 f,
692 "Guard is a SingleVarThresholdGuard not a MultiVarThresholdGuard"
693 )
694 }
695 }
696 }
697}
698
699impl<T: Atomic> ComparisonConstraint<T> {
700 pub fn try_new<I: IntoIterator<Item = (T, Fraction)> + Clone>(
709 atoms: I,
710 thr: ThresholdConstraint,
711 ) -> Result<Self, ComparisonConstraintCreationError> {
712 if atoms.clone().into_iter().all(|(_, f)| f.is_negative())
713 || atoms.clone().into_iter().all(|(_, f)| !f.is_negative())
714 {
715 return Err(ComparisonConstraintCreationError::IsSumVarConstraint);
716 }
717
718 let atoms = WeightedSum::new(atoms);
719 let symbolic_constraint = ThresholdConstraintOver::new(atoms, thr);
720
721 Ok(Self(symbolic_constraint))
722 }
723
724 pub fn is_upper_guard(&self) -> bool {
726 self.0.is_upper_guard()
727 }
728
729 pub fn is_lower_guard(&self) -> bool {
731 self.0.is_lower_guard()
732 }
733
734 pub fn to_boolean_expr<S>(&self) -> BooleanExpression<S>
736 where
737 T: IntoNoDivBooleanExpr<S>,
738 IntegerExpression<S>: From<T>,
739 S: Atomic,
740 {
741 self.0.encode_to_boolean_expr()
742 }
743
744 pub fn get_threshold(&self) -> &Threshold {
746 self.0.get_threshold()
747 }
748
749 pub fn get_threshold_constraint(&self) -> &ThresholdConstraint {
751 self.0.get_threshold_constraint()
752 }
753}
754
755impl<T: Atomic> Display for ComparisonConstraint<T> {
756 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
757 write!(f, "{}", self.0)?;
758
759 Ok(())
760 }
761}
762
763impl Error for ComparisonConstraintCreationError {}
764
765impl Display for ComparisonConstraintCreationError {
766 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
767 match self {
768 ComparisonConstraintCreationError::IsSumVarConstraint => {
769 write!(f, "Guard is a MultiVarThresholdGuard not a ComparisonGuard")
770 }
771 }
772 }
773}
774
775impl Display for LIAVariableConstraint {
776 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
777 match self {
778 LIAVariableConstraint::SumVarConstraint(g) => write!(f, "{g}"),
779 LIAVariableConstraint::BinaryGuard(lhs, op, rhs) => {
780 write!(f, "({lhs}) {op} ({rhs})")
781 }
782 LIAVariableConstraint::True => write!(f, "true"),
783 LIAVariableConstraint::False => write!(f, "false"),
784 LIAVariableConstraint::ComparisonConstraint(g) => write!(f, "{g}"),
785 LIAVariableConstraint::SingleVarConstraint(g) => write!(f, "{g}"),
786 }
787 }
788}
789
790impl Error for LIATransformationError {}
791
792impl Display for LIATransformationError {
793 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
794 match self {
795 LIATransformationError::GuardError(rule, guard, err) => write!(
796 f,
797 "Error while transforming guard {guard} of rule {rule}: {err}"
798 ),
799 LIATransformationError::InitialConstraintError(
800 boolean_expression,
801 guard_rewrite_error,
802 ) => {
803 write!(
804 f,
805 "Error while transforming initial constraint {boolean_expression} into linear arithmetic: {guard_rewrite_error}"
806 )
807 }
808 }
809 }
810}
811
812#[cfg(test)]
813mod tests {
814 use std::{
815 collections::{BTreeMap, HashMap, HashSet},
816 vec,
817 };
818
819 use crate::{
820 BooleanVarConstraint, LocationConstraint, ParameterConstraint, RuleDefinition,
821 ThresholdAutomaton, VariableConstraint,
822 expressions::{
823 BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression, IntegerOp,
824 Location, Parameter, Variable, fraction::Fraction,
825 },
826 general_threshold_automaton::{
827 Action,
828 builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
829 },
830 lia_threshold_automaton::{
831 ComparisonConstraint, ComparisonConstraintCreationError, ConstraintRewriteError,
832 LIARule, LIAThresholdAutomaton, LIATransformationError, LIAVariableConstraint,
833 SingleAtomConstraint, SumAtomConstraint, SumVarConstraintCreationError, WeightedSum,
834 integer_thresholds::{
835 Threshold, ThresholdCompOp, ThresholdConstraint, ThresholdConstraintOver,
836 },
837 },
838 };
839
840 #[test]
841 fn test_lia_threshold_automaton_getters() {
842 let rule = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build();
843
844 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
845 .with_parameters(vec![
846 Parameter::new("n"),
847 Parameter::new("t"),
848 Parameter::new("f"),
849 ])
850 .unwrap()
851 .with_variables(vec![
852 Variable::new("var1"),
853 Variable::new("var2"),
854 Variable::new("var3"),
855 ])
856 .unwrap()
857 .with_locations(vec![
858 Location::new("loc1"),
859 Location::new("loc2"),
860 Location::new("loc3"),
861 ])
862 .unwrap()
863 .initialize()
864 .with_rules(vec![
865 rule.clone(),
866 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
867 .with_guard(
868 BooleanVarConstraint::ComparisonExpression(
869 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
870 ComparisonOp::Eq,
871 Box::new(IntegerExpression::Const(1)),
872 ) & BooleanVarConstraint::ComparisonExpression(
873 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
874 ComparisonOp::Eq,
875 Box::new(IntegerExpression::Param(Parameter::new("n"))),
876 ),
877 )
878 .with_action(Action::new_reset(Variable::new("var3")))
879 .with_action(
880 Action::new(
881 Variable::new("var1"),
882 IntegerExpression::Atom(Variable::new("var1"))
883 + IntegerExpression::Const(1),
884 )
885 .unwrap(),
886 )
887 .build(),
888 ])
889 .unwrap()
890 .with_initial_location_constraint(
891 LocationConstraint::ComparisonExpression(
892 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
893 ComparisonOp::Eq,
894 Box::new(
895 IntegerExpression::Param(Parameter::new("n"))
896 - IntegerExpression::Param(Parameter::new("f")),
897 ),
898 ) & LocationConstraint::ComparisonExpression(
899 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
900 ComparisonOp::Eq,
901 Box::new(IntegerExpression::Const(0)),
902 ),
903 )
904 .unwrap()
905 .with_initial_variable_constraint(
906 BooleanVarConstraint::ComparisonExpression(
907 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
908 ComparisonOp::Eq,
909 Box::new(IntegerExpression::Const(0)),
910 ) & BooleanVarConstraint::ComparisonExpression(
911 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
912 ComparisonOp::Eq,
913 Box::new(IntegerExpression::Const(0)),
914 ),
915 )
916 .unwrap()
917 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
918 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
919 ComparisonOp::Gt,
920 Box::new(
921 IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
922 ),
923 ))
924 .unwrap()
925 .build();
926
927 let abstract_rule = LIARule {
928 id: 0,
929 source: Location::new("loc1"),
930 target: Location::new("loc2"),
931 guard: LIAVariableConstraint::True,
932 actions: vec![],
933 };
934
935 let lta = LIAThresholdAutomaton {
936 ta: ta.clone(),
937 rules: HashMap::from([
938 (Location::new("loc1"), vec![abstract_rule.clone()]),
939 (
940 Location::new("loc2"),
941 vec![LIARule {
942 id: 1,
943 source: Location::new("loc2"),
944 target: Location::new("loc3"),
945 guard: (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
946 ThresholdConstraintOver::new(
947 WeightedSum::new([(Variable::new("var1"), Fraction::from(1))]),
948 ThresholdConstraint::new(
949 ThresholdCompOp::Lt,
950 Vec::<(Parameter, Fraction)>::new(),
951 2,
952 ),
953 ),
954 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
955 ThresholdConstraintOver::new(
956 WeightedSum::new([(Variable::new("var1"), Fraction::from(1))]),
957 ThresholdConstraint::new(
958 ThresholdCompOp::Geq,
959 Vec::<(Parameter, Fraction)>::new(),
960 1,
961 ),
962 ),
963 ))) & (LIAVariableConstraint::SingleVarConstraint(
964 SingleAtomConstraint(ThresholdConstraintOver::new(
965 Variable::new("var2"),
966 ThresholdConstraint::new(
967 ThresholdCompOp::Lt,
968 [(Parameter::new("n"), 1)],
969 1,
970 ),
971 )),
972 ) & LIAVariableConstraint::SingleVarConstraint(
973 SingleAtomConstraint(ThresholdConstraintOver::new(
974 Variable::new("var2"),
975 ThresholdConstraint::new(
976 ThresholdCompOp::Geq,
977 [(Parameter::new("n"), 1)],
978 0,
979 ),
980 )),
981 )) | (LIAVariableConstraint::ComparisonConstraint(
982 ComparisonConstraint(ThresholdConstraintOver::new(
983 WeightedSum::new([
984 (Variable::new("var1"), Fraction::from(1)),
985 (Variable::new("var2"), -Fraction::from(1)),
986 ]),
987 ThresholdConstraint::new(
988 ThresholdCompOp::Lt,
989 Vec::<(Parameter, Fraction)>::new(),
990 2,
991 ),
992 )),
993 ) & LIAVariableConstraint::ComparisonConstraint(
994 ComparisonConstraint(ThresholdConstraintOver::new(
995 WeightedSum::new([
996 (Variable::new("var1"), Fraction::from(1)),
997 (Variable::new("var2"), -Fraction::from(1)),
998 ]),
999 ThresholdConstraint::new(
1000 ThresholdCompOp::Geq,
1001 Vec::<(Parameter, Fraction)>::new(),
1002 1,
1003 ),
1004 )),
1005 )),
1006 actions: vec![
1007 Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1008 .unwrap(),
1009 Action::new(
1010 Variable::new("var1"),
1011 IntegerExpression::BinaryExpr(
1012 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1013 IntegerOp::Add,
1014 Box::new(IntegerExpression::Const(1)),
1015 ),
1016 )
1017 .unwrap(),
1018 ],
1019 }],
1020 ),
1021 ]),
1022 init_variable_constr: vec![LIAVariableConstraint::BinaryGuard(
1023 Box::new(
1024 (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
1025 Variable::new("var1"),
1026 ThresholdConstraint::new(
1027 ThresholdCompOp::Leq,
1028 Vec::<(Parameter, Fraction)>::new(),
1029 0,
1030 ),
1031 ))) & LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
1032 Variable::new("var1"),
1033 ThresholdConstraint::new(
1034 ThresholdCompOp::Geq,
1035 Vec::<(Parameter, Fraction)>::new(),
1036 0,
1037 ),
1038 )),
1039 ),
1040 BooleanConnective::And,
1041 Box::new(
1042 LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
1043 Variable::new("var2"),
1044 ThresholdConstraint::new(
1045 ThresholdCompOp::Leq,
1046 Vec::<(Parameter, Fraction)>::new(),
1047 0,
1048 ),
1049 )) & LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
1050 Variable::new("var2"),
1051 ThresholdConstraint::new(
1052 ThresholdCompOp::Geq,
1053 Vec::<(Parameter, Fraction)>::new(),
1054 0,
1055 ),
1056 )),
1057 ),
1058 )],
1059 additional_vars_for_sums: HashMap::new(),
1060 };
1061
1062 assert_eq!(lta.get_ta(), &ta);
1063 assert_eq!(lta.get_max_number_of_thresholds_per_rule(), 6);
1064
1065 assert_eq!(lta.name(), "test_ta1");
1066 assert_eq!(lta.variables().count(), 3);
1067 assert!(lta.variables().all(|v| ta.variables().any(|tv| tv == v)));
1068
1069 assert_eq!(lta.parameters().count(), 3);
1070 assert!(lta.parameters().all(|p| ta.parameters().any(|tp| tp == p)));
1071
1072 assert_eq!(lta.locations().count(), 3);
1073 assert!(lta.locations().all(|l| ta.locations().any(|tl| tl == l)));
1074
1075 assert_eq!(lta.resilience_conditions().count(), 1);
1076 assert!(
1077 lta.resilience_conditions()
1078 .all(|rc| ta.resilience_conditions().any(|trc| trc == rc))
1079 );
1080
1081 assert!(lta.can_be_initial_location(&Location::new("loc1")));
1082 assert!(!lta.can_be_initial_location(&Location::new("loc2")));
1083
1084 assert!(lta.outgoing_rules(&Location::new("loc3")).next().is_none());
1085 assert_eq!(lta.outgoing_rules(&Location::new("loc2")).count(), 1);
1086 assert_eq!(lta.incoming_rules(&Location::new("loc2")).count(), 1);
1087 assert_eq!(lta.incoming_rules(&Location::new("loc1")).count(), 0);
1088
1089 let mut sorted_rules = lta.rules().cloned().collect::<Vec<_>>();
1090 sorted_rules.sort_by_key(|r| r.id());
1091
1092 assert_eq!(
1093 lta.get_thresholds(),
1094 HashSet::from([
1095 Threshold::new(Vec::<(Parameter, Fraction)>::new(), 1),
1096 Threshold::new(Vec::<(Parameter, Fraction)>::new(), 2),
1097 Threshold::new([(Parameter::new("n"), 1)], 0),
1098 Threshold::new([(Parameter::new("n"), 1)], 1),
1099 ])
1100 );
1101
1102 assert_eq!(
1103 sorted_rules,
1104 vec![
1105 LIARule {
1106 id: 0,
1107 source: Location::new("loc1"),
1108 target: Location::new("loc2"),
1109 guard: LIAVariableConstraint::True,
1110 actions: vec![],
1111 },
1112 LIARule {
1113 id: 1,
1114 source: Location::new("loc2"),
1115 target: Location::new("loc3"),
1116 guard: (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1117 ThresholdConstraintOver::new(
1118 WeightedSum::new([(Variable::new("var1"), 1,)]),
1119 ThresholdConstraint::new(
1120 ThresholdCompOp::Lt,
1121 Vec::<(Parameter, Fraction)>::new(),
1122 2
1123 ),
1124 )
1125 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1126 ThresholdConstraintOver::new(
1127 WeightedSum::new([(Variable::new("var1"), 1,)]),
1128 ThresholdConstraint::new(
1129 ThresholdCompOp::Geq,
1130 Vec::<(Parameter, Fraction)>::new(),
1131 1
1132 ),
1133 )
1134 ))) & (LIAVariableConstraint::SingleVarConstraint(
1135 SingleAtomConstraint(ThresholdConstraintOver::new(
1136 Variable::new("var2"),
1137 ThresholdConstraint::new(
1138 ThresholdCompOp::Lt,
1139 [(Parameter::new("n"), 1)],
1140 1
1141 ),
1142 ))
1143 ) & LIAVariableConstraint::SingleVarConstraint(
1144 SingleAtomConstraint(ThresholdConstraintOver::new(
1145 Variable::new("var2"),
1146 ThresholdConstraint::new(
1147 ThresholdCompOp::Geq,
1148 [(Parameter::new("n"), 1)],
1149 0
1150 ),
1151 ))
1152 )) | (LIAVariableConstraint::ComparisonConstraint(
1153 ComparisonConstraint(ThresholdConstraintOver::new(
1154 WeightedSum::new([
1155 (Variable::new("var1"), Fraction::from(1)),
1156 (Variable::new("var2"), -Fraction::from(1))
1157 ]),
1158 ThresholdConstraint::new(
1159 ThresholdCompOp::Lt,
1160 Vec::<(Parameter, Fraction)>::new(),
1161 2,
1162 ),
1163 ))
1164 ) & LIAVariableConstraint::ComparisonConstraint(
1165 ComparisonConstraint(ThresholdConstraintOver::new(
1166 WeightedSum::new([
1167 (Variable::new("var1"), Fraction::from(1)),
1168 (Variable::new("var2"), -Fraction::from(1))
1169 ]),
1170 ThresholdConstraint::new(
1171 ThresholdCompOp::Geq,
1172 Vec::<(Parameter, Fraction)>::new(),
1173 1,
1174 ),
1175 ))
1176 )),
1177 actions: vec![
1178 Action::new(Variable::new("var3"), IntegerExpression::Const(0)).unwrap(),
1179 Action::new(
1180 Variable::new("var1"),
1181 IntegerExpression::BinaryExpr(
1182 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1183 IntegerOp::Add,
1184 Box::new(IntegerExpression::Const(1)),
1185 ),
1186 )
1187 .unwrap(),
1188 ],
1189 }
1190 ]
1191 );
1192
1193 assert_eq!(lta.get_derived_rule(&abstract_rule), rule);
1194
1195 assert_eq!(lta.initial_location_constraints().count(), 1);
1196 assert!(
1197 lta.initial_location_constraints()
1198 .any(|ilc| ta.initial_location_constraints().any(|tilc| tilc == ilc))
1199 );
1200
1201 assert_eq!(lta.initial_variable_constraints().count(), 1);
1202 assert!(lta.initial_variable_constraints().any(|ivc| {
1203 ta.initial_variable_constraints()
1204 .map(|c| LIAVariableConstraint::try_from(c.remove_boolean_negations()).unwrap())
1205 .any(|tivc| tivc == *ivc)
1206 }));
1207 }
1208
1209 #[test]
1210 fn test_lia_get_single_var_threshold() {
1211 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1212 .with_parameters(vec![
1213 Parameter::new("n"),
1214 Parameter::new("t"),
1215 Parameter::new("f"),
1216 ])
1217 .unwrap()
1218 .with_variables(vec![
1219 Variable::new("var1"),
1220 Variable::new("var2"),
1221 Variable::new("var3"),
1222 ])
1223 .unwrap()
1224 .with_locations(vec![
1225 Location::new("loc1"),
1226 Location::new("loc2"),
1227 Location::new("loc3"),
1228 ])
1229 .unwrap()
1230 .initialize()
1231 .with_rules(vec![
1232 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1233 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1234 .with_guard(
1235 BooleanVarConstraint::ComparisonExpression(
1236 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1237 ComparisonOp::Eq,
1238 Box::new(IntegerExpression::Const(1)),
1239 ) & BooleanVarConstraint::ComparisonExpression(
1240 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1241 ComparisonOp::Eq,
1242 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1243 ),
1244 )
1245 .with_action(Action::new_reset(Variable::new("var3")))
1246 .with_action(
1247 Action::new(
1248 Variable::new("var1"),
1249 IntegerExpression::Atom(Variable::new("var1"))
1250 + IntegerExpression::Const(1),
1251 )
1252 .unwrap(),
1253 )
1254 .build(),
1255 ])
1256 .unwrap()
1257 .with_initial_location_constraint(
1258 LocationConstraint::ComparisonExpression(
1259 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1260 ComparisonOp::Eq,
1261 Box::new(
1262 IntegerExpression::Param(Parameter::new("n"))
1263 - IntegerExpression::Param(Parameter::new("f")),
1264 ),
1265 ) | LocationConstraint::ComparisonExpression(
1266 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1267 ComparisonOp::Eq,
1268 Box::new(IntegerExpression::Const(0)),
1269 ),
1270 )
1271 .unwrap()
1272 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1273 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1274 ComparisonOp::Gt,
1275 Box::new(
1276 IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1277 ),
1278 ))
1279 .unwrap()
1280 .build();
1281
1282 let lta = LIAThresholdAutomaton {
1283 ta,
1284 rules: HashMap::from([
1285 (
1286 Location::new("loc1"),
1287 vec![LIARule {
1288 id: 0,
1289 source: Location::new("loc1"),
1290 target: Location::new("loc2"),
1291 guard: LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1292 ThresholdConstraintOver::new(
1293 Variable::new("var1"),
1294 ThresholdConstraint::new(
1295 ThresholdCompOp::Lt,
1296 Vec::<(Parameter, Fraction)>::new(),
1297 2,
1298 ),
1299 ),
1300 )) & LIAVariableConstraint::SingleVarConstraint(
1301 SingleAtomConstraint(ThresholdConstraintOver::new(
1302 Variable::new("var1"),
1303 ThresholdConstraint::new(
1304 ThresholdCompOp::Geq,
1305 Vec::<(Parameter, Fraction)>::new(),
1306 1,
1307 ),
1308 )),
1309 ),
1310 actions: vec![],
1311 }],
1312 ),
1313 (
1314 Location::new("loc2"),
1315 vec![LIARule {
1316 id: 1,
1317 source: Location::new("loc2"),
1318 target: Location::new("loc3"),
1319 guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1320 ThresholdConstraintOver::new(
1321 Variable::new("var1"),
1322 ThresholdConstraint::new(
1323 ThresholdCompOp::Lt,
1324 Vec::<(Parameter, Fraction)>::new(),
1325 2,
1326 ),
1327 ),
1328 )) & LIAVariableConstraint::SingleVarConstraint(
1329 SingleAtomConstraint(ThresholdConstraintOver::new(
1330 Variable::new("var1"),
1331 ThresholdConstraint::new(
1332 ThresholdCompOp::Geq,
1333 Vec::<(Parameter, Fraction)>::new(),
1334 1,
1335 ),
1336 )),
1337 )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1338 ThresholdConstraintOver::new(
1339 WeightedSum::new(BTreeMap::from([
1340 (Variable::new("var2"), 1),
1341 (Variable::new("var1"), 1),
1342 ])),
1343 ThresholdConstraint::new(
1344 ThresholdCompOp::Lt,
1345 BTreeMap::from([(Parameter::new("n"), 1)]),
1346 1,
1347 ),
1348 ),
1349 )) & LIAVariableConstraint::SumVarConstraint(
1350 SumAtomConstraint(ThresholdConstraintOver::new(
1351 WeightedSum::new(BTreeMap::from([
1352 (Variable::new("var2"), 1),
1353 (Variable::new("var1"), 1),
1354 ])),
1355 ThresholdConstraint::new(
1356 ThresholdCompOp::Geq,
1357 BTreeMap::from([(Parameter::new("n"), 1)]),
1358 0,
1359 ),
1360 )),
1361 )) | (LIAVariableConstraint::ComparisonConstraint(
1362 ComparisonConstraint(ThresholdConstraintOver::new(
1363 WeightedSum::new(BTreeMap::from([
1364 (Variable::new("var3"), 1.into()),
1365 (Variable::new("var3"), -Fraction::from(1)),
1366 ])),
1367 ThresholdConstraint::new(
1368 ThresholdCompOp::Lt,
1369 BTreeMap::from([(Parameter::new("t"), 3)]),
1370 1,
1371 ),
1372 )),
1373 ) & LIAVariableConstraint::ComparisonConstraint(
1374 ComparisonConstraint(ThresholdConstraintOver::new(
1375 WeightedSum::new(BTreeMap::from([
1376 (Variable::new("var3"), 1.into()),
1377 (Variable::new("var3"), -Fraction::from(1)),
1378 ])),
1379 ThresholdConstraint::new(
1380 ThresholdCompOp::Geq,
1381 BTreeMap::from([(Parameter::new("t"), 3)]),
1382 0,
1383 ),
1384 )),
1385 )),
1386 actions: vec![
1387 Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1388 .unwrap(),
1389 Action::new(
1390 Variable::new("var1"),
1391 IntegerExpression::BinaryExpr(
1392 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1393 IntegerOp::Add,
1394 Box::new(IntegerExpression::Const(1)),
1395 ),
1396 )
1397 .unwrap(),
1398 ],
1399 }],
1400 ),
1401 ]),
1402 init_variable_constr: vec![],
1403 additional_vars_for_sums: HashMap::new(),
1404 };
1405
1406 let expected = HashSet::from([
1407 SingleAtomConstraint(ThresholdConstraintOver::new(
1408 Variable::new("var1"),
1409 ThresholdConstraint::new(
1410 ThresholdCompOp::Lt,
1411 Vec::<(Parameter, Fraction)>::new(),
1412 2,
1413 ),
1414 )),
1415 SingleAtomConstraint(ThresholdConstraintOver::new(
1416 Variable::new("var1"),
1417 ThresholdConstraint::new(
1418 ThresholdCompOp::Geq,
1419 Vec::<(Parameter, Fraction)>::new(),
1420 1,
1421 ),
1422 )),
1423 ]);
1424 assert_eq!(lta.get_single_var_constraints_rules(), expected)
1425 }
1426
1427 #[test]
1428 fn test_lia_ta_has_comparison_guard() {
1429 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1430 .with_parameters(vec![
1431 Parameter::new("n"),
1432 Parameter::new("t"),
1433 Parameter::new("f"),
1434 ])
1435 .unwrap()
1436 .with_variables(vec![
1437 Variable::new("var1"),
1438 Variable::new("var2"),
1439 Variable::new("var3"),
1440 ])
1441 .unwrap()
1442 .with_locations(vec![
1443 Location::new("loc1"),
1444 Location::new("loc2"),
1445 Location::new("loc3"),
1446 ])
1447 .unwrap()
1448 .initialize()
1449 .with_rules(vec![
1450 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1451 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1452 .with_guard(
1453 BooleanVarConstraint::ComparisonExpression(
1454 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1455 ComparisonOp::Eq,
1456 Box::new(IntegerExpression::Const(1)),
1457 ) & BooleanVarConstraint::ComparisonExpression(
1458 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1459 ComparisonOp::Eq,
1460 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1461 ),
1462 )
1463 .with_action(Action::new_reset(Variable::new("var3")))
1464 .with_action(
1465 Action::new(
1466 Variable::new("var1"),
1467 IntegerExpression::Atom(Variable::new("var1"))
1468 + IntegerExpression::Const(1),
1469 )
1470 .unwrap(),
1471 )
1472 .build(),
1473 ])
1474 .unwrap()
1475 .with_initial_location_constraint(
1476 LocationConstraint::ComparisonExpression(
1477 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1478 ComparisonOp::Eq,
1479 Box::new(
1480 IntegerExpression::Param(Parameter::new("n"))
1481 - IntegerExpression::Param(Parameter::new("f")),
1482 ),
1483 ) | LocationConstraint::ComparisonExpression(
1484 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1485 ComparisonOp::Eq,
1486 Box::new(IntegerExpression::Const(0)),
1487 ),
1488 )
1489 .unwrap()
1490 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1491 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1492 ComparisonOp::Gt,
1493 Box::new(
1494 IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1495 ),
1496 ))
1497 .unwrap()
1498 .build();
1499
1500 let lta = LIAThresholdAutomaton {
1501 ta,
1502 rules: HashMap::from([
1503 (
1504 Location::new("loc1"),
1505 vec![LIARule {
1506 id: 0,
1507 source: Location::new("loc1"),
1508 target: Location::new("loc2"),
1509 guard: LIAVariableConstraint::True,
1510 actions: vec![],
1511 }],
1512 ),
1513 (
1514 Location::new("loc2"),
1515 vec![LIARule {
1516 id: 1,
1517 source: Location::new("loc2"),
1518 target: Location::new("loc3"),
1519 guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1520 ThresholdConstraintOver::new(
1521 Variable::new("var1"),
1522 ThresholdConstraint::new(
1523 ThresholdCompOp::Lt,
1524 Vec::<(Parameter, Fraction)>::new(),
1525 2,
1526 ),
1527 ),
1528 )) & LIAVariableConstraint::SingleVarConstraint(
1529 SingleAtomConstraint(ThresholdConstraintOver::new(
1530 Variable::new("var1"),
1531 ThresholdConstraint::new(
1532 ThresholdCompOp::Geq,
1533 Vec::<(Parameter, Fraction)>::new(),
1534 1,
1535 ),
1536 )),
1537 )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1538 ThresholdConstraintOver::new(
1539 WeightedSum::new(BTreeMap::from([
1540 (Variable::new("var2"), 1),
1541 (Variable::new("var1"), 1),
1542 ])),
1543 ThresholdConstraint::new(
1544 ThresholdCompOp::Lt,
1545 BTreeMap::from([(Parameter::new("n"), 1)]),
1546 1,
1547 ),
1548 ),
1549 )) & LIAVariableConstraint::SumVarConstraint(
1550 SumAtomConstraint(ThresholdConstraintOver::new(
1551 WeightedSum::new(BTreeMap::from([
1552 (Variable::new("var2"), 1),
1553 (Variable::new("var1"), 1),
1554 ])),
1555 ThresholdConstraint::new(
1556 ThresholdCompOp::Geq,
1557 BTreeMap::from([(Parameter::new("n"), 1)]),
1558 0,
1559 ),
1560 )),
1561 )) | (LIAVariableConstraint::ComparisonConstraint(
1562 ComparisonConstraint(ThresholdConstraintOver::new(
1563 WeightedSum::new(BTreeMap::from([
1564 (Variable::new("var3"), 1.into()),
1565 (Variable::new("var3"), -Fraction::from(1)),
1566 ])),
1567 ThresholdConstraint::new(
1568 ThresholdCompOp::Lt,
1569 BTreeMap::from([(Parameter::new("t"), 3)]),
1570 1,
1571 ),
1572 )),
1573 ) & LIAVariableConstraint::ComparisonConstraint(
1574 ComparisonConstraint(ThresholdConstraintOver::new(
1575 WeightedSum::new(BTreeMap::from([
1576 (Variable::new("var3"), 1.into()),
1577 (Variable::new("var3"), -Fraction::from(1)),
1578 ])),
1579 ThresholdConstraint::new(
1580 ThresholdCompOp::Geq,
1581 BTreeMap::from([(Parameter::new("t"), 3)]),
1582 0,
1583 ),
1584 )),
1585 )),
1586 actions: vec![
1587 Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1588 .unwrap(),
1589 Action::new(
1590 Variable::new("var1"),
1591 IntegerExpression::BinaryExpr(
1592 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1593 IntegerOp::Add,
1594 Box::new(IntegerExpression::Const(1)),
1595 ),
1596 )
1597 .unwrap(),
1598 ],
1599 }],
1600 ),
1601 ]),
1602 init_variable_constr: vec![],
1603 additional_vars_for_sums: HashMap::new(),
1604 };
1605
1606 assert!(lta.has_comparison_guard());
1607 let expected = HashSet::from([
1608 ComparisonConstraint(ThresholdConstraintOver::new(
1609 WeightedSum::new(BTreeMap::from([
1610 (Variable::new("var3"), 1.into()),
1611 (Variable::new("var3"), -Fraction::from(1)),
1612 ])),
1613 ThresholdConstraint::new(
1614 ThresholdCompOp::Lt,
1615 BTreeMap::from([(Parameter::new("t"), 3)]),
1616 1,
1617 ),
1618 )),
1619 ComparisonConstraint(ThresholdConstraintOver::new(
1620 WeightedSum::new(BTreeMap::from([
1621 (Variable::new("var3"), 1.into()),
1622 (Variable::new("var3"), -Fraction::from(1)),
1623 ])),
1624 ThresholdConstraint::new(
1625 ThresholdCompOp::Geq,
1626 BTreeMap::from([(Parameter::new("t"), 3)]),
1627 0,
1628 ),
1629 )),
1630 ]);
1631 assert_eq!(lta.get_comparison_guards(), expected);
1632
1633 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1634 .with_parameters(vec![
1635 Parameter::new("n"),
1636 Parameter::new("t"),
1637 Parameter::new("f"),
1638 ])
1639 .unwrap()
1640 .with_variables(vec![
1641 Variable::new("var1"),
1642 Variable::new("var2"),
1643 Variable::new("var3"),
1644 ])
1645 .unwrap()
1646 .with_locations(vec![
1647 Location::new("loc1"),
1648 Location::new("loc2"),
1649 Location::new("loc3"),
1650 ])
1651 .unwrap()
1652 .initialize()
1653 .with_rules(vec![
1654 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1655 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1656 .with_guard(
1657 BooleanVarConstraint::ComparisonExpression(
1658 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1659 ComparisonOp::Eq,
1660 Box::new(IntegerExpression::Const(1)),
1661 ) & BooleanVarConstraint::ComparisonExpression(
1662 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1663 ComparisonOp::Eq,
1664 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1665 ),
1666 )
1667 .with_action(Action::new_reset(Variable::new("var3")))
1668 .with_action(
1669 Action::new(
1670 Variable::new("var1"),
1671 IntegerExpression::Atom(Variable::new("var1"))
1672 + IntegerExpression::Const(1),
1673 )
1674 .unwrap(),
1675 )
1676 .build(),
1677 ])
1678 .unwrap()
1679 .with_initial_location_constraint(
1680 LocationConstraint::ComparisonExpression(
1681 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1682 ComparisonOp::Eq,
1683 Box::new(
1684 IntegerExpression::Param(Parameter::new("n"))
1685 - IntegerExpression::Param(Parameter::new("f")),
1686 ),
1687 ) | LocationConstraint::ComparisonExpression(
1688 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1689 ComparisonOp::Eq,
1690 Box::new(IntegerExpression::Const(0)),
1691 ),
1692 )
1693 .unwrap()
1694 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1695 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1696 ComparisonOp::Gt,
1697 Box::new(
1698 IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1699 ),
1700 ))
1701 .unwrap()
1702 .build();
1703
1704 let lta = LIAThresholdAutomaton {
1705 ta,
1706 rules: HashMap::from([
1707 (
1708 Location::new("loc1"),
1709 vec![LIARule {
1710 id: 0,
1711 source: Location::new("loc1"),
1712 target: Location::new("loc2"),
1713 guard: LIAVariableConstraint::True,
1714 actions: vec![],
1715 }],
1716 ),
1717 (
1718 Location::new("loc2"),
1719 vec![LIARule {
1720 id: 1,
1721 source: Location::new("loc2"),
1722 target: Location::new("loc3"),
1723 guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1724 ThresholdConstraintOver::new(
1725 Variable::new("var1"),
1726 ThresholdConstraint::new(
1727 ThresholdCompOp::Lt,
1728 Vec::<(Parameter, Fraction)>::new(),
1729 2,
1730 ),
1731 ),
1732 )) & LIAVariableConstraint::SingleVarConstraint(
1733 SingleAtomConstraint(ThresholdConstraintOver::new(
1734 Variable::new("var1"),
1735 ThresholdConstraint::new(
1736 ThresholdCompOp::Geq,
1737 Vec::<(Parameter, Fraction)>::new(),
1738 1,
1739 ),
1740 )),
1741 )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1742 ThresholdConstraintOver::new(
1743 WeightedSum::new(BTreeMap::from([
1744 (Variable::new("var2"), 1),
1745 (Variable::new("var1"), 1),
1746 ])),
1747 ThresholdConstraint::new(
1748 ThresholdCompOp::Lt,
1749 [(Parameter::new("n"), 1)],
1750 1,
1751 ),
1752 ),
1753 )) & LIAVariableConstraint::SumVarConstraint(
1754 SumAtomConstraint(ThresholdConstraintOver::new(
1755 WeightedSum::new(BTreeMap::from([
1756 (Variable::new("var2"), 1),
1757 (Variable::new("var1"), 1),
1758 ])),
1759 ThresholdConstraint::new(
1760 ThresholdCompOp::Geq,
1761 [(Parameter::new("n"), 1)],
1762 0,
1763 ),
1764 )),
1765 )),
1766 actions: vec![
1767 Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1768 .unwrap(),
1769 Action::new(
1770 Variable::new("var1"),
1771 IntegerExpression::BinaryExpr(
1772 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1773 IntegerOp::Add,
1774 Box::new(IntegerExpression::Const(1)),
1775 ),
1776 )
1777 .unwrap(),
1778 ],
1779 }],
1780 ),
1781 ]),
1782 init_variable_constr: vec![],
1783 additional_vars_for_sums: HashMap::new(),
1784 };
1785
1786 assert!(!lta.has_comparison_guard());
1787 assert!(lta.get_comparison_guards().is_empty());
1788 }
1789
1790 #[test]
1791 fn test_lia_has_multi_var() {
1792 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1793 .with_parameters(vec![
1794 Parameter::new("n"),
1795 Parameter::new("t"),
1796 Parameter::new("f"),
1797 ])
1798 .unwrap()
1799 .with_variables(vec![
1800 Variable::new("var1"),
1801 Variable::new("var2"),
1802 Variable::new("var3"),
1803 ])
1804 .unwrap()
1805 .with_locations(vec![
1806 Location::new("loc1"),
1807 Location::new("loc2"),
1808 Location::new("loc3"),
1809 ])
1810 .unwrap()
1811 .initialize()
1812 .with_rules(vec![
1813 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1814 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1815 .with_guard(
1816 BooleanVarConstraint::ComparisonExpression(
1817 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1818 ComparisonOp::Eq,
1819 Box::new(IntegerExpression::Const(1)),
1820 ) & BooleanVarConstraint::ComparisonExpression(
1821 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1822 ComparisonOp::Eq,
1823 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1824 ),
1825 )
1826 .with_action(Action::new_reset(Variable::new("var3")))
1827 .with_action(
1828 Action::new(
1829 Variable::new("var1"),
1830 IntegerExpression::Atom(Variable::new("var1"))
1831 + IntegerExpression::Const(1),
1832 )
1833 .unwrap(),
1834 )
1835 .build(),
1836 ])
1837 .unwrap()
1838 .with_initial_location_constraint(
1839 LocationConstraint::ComparisonExpression(
1840 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1841 ComparisonOp::Eq,
1842 Box::new(
1843 IntegerExpression::Param(Parameter::new("n"))
1844 - IntegerExpression::Param(Parameter::new("f")),
1845 ),
1846 ) | LocationConstraint::ComparisonExpression(
1847 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1848 ComparisonOp::Eq,
1849 Box::new(IntegerExpression::Const(0)),
1850 ),
1851 )
1852 .unwrap()
1853 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1854 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1855 ComparisonOp::Gt,
1856 Box::new(
1857 IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1858 ),
1859 ))
1860 .unwrap()
1861 .build();
1862
1863 let lta = LIAThresholdAutomaton {
1864 ta,
1865 rules: HashMap::from([
1866 (
1867 Location::new("loc1"),
1868 vec![LIARule {
1869 id: 0,
1870 source: Location::new("loc1"),
1871 target: Location::new("loc2"),
1872 guard: LIAVariableConstraint::True,
1873 actions: vec![],
1874 }],
1875 ),
1876 (
1877 Location::new("loc2"),
1878 vec![LIARule {
1879 id: 1,
1880 source: Location::new("loc2"),
1881 target: Location::new("loc3"),
1882 guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1883 ThresholdConstraintOver::new(
1884 Variable::new("var1"),
1885 ThresholdConstraint::new(
1886 ThresholdCompOp::Lt,
1887 Vec::<(Parameter, Fraction)>::new(),
1888 2,
1889 ),
1890 ),
1891 )) & LIAVariableConstraint::SingleVarConstraint(
1892 SingleAtomConstraint(ThresholdConstraintOver::new(
1893 Variable::new("var1"),
1894 ThresholdConstraint::new(
1895 ThresholdCompOp::Geq,
1896 Vec::<(Parameter, Fraction)>::new(),
1897 1,
1898 ),
1899 )),
1900 )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1901 ThresholdConstraintOver::new(
1902 WeightedSum::new(BTreeMap::from([
1903 (Variable::new("var2"), 1),
1904 (Variable::new("var1"), 1),
1905 ])),
1906 ThresholdConstraint::new(
1907 ThresholdCompOp::Lt,
1908 BTreeMap::from([(Parameter::new("n"), 1)]),
1909 1,
1910 ),
1911 ),
1912 )) & LIAVariableConstraint::SumVarConstraint(
1913 SumAtomConstraint(ThresholdConstraintOver::new(
1914 WeightedSum::new(BTreeMap::from([
1915 (Variable::new("var2"), 1),
1916 (Variable::new("var1"), 1),
1917 ])),
1918 ThresholdConstraint::new(
1919 ThresholdCompOp::Geq,
1920 BTreeMap::from([(Parameter::new("n"), 1)]),
1921 0,
1922 ),
1923 )),
1924 )) | (LIAVariableConstraint::ComparisonConstraint(
1925 ComparisonConstraint(ThresholdConstraintOver::new(
1926 WeightedSum::new(BTreeMap::from([
1927 (Variable::new("var3"), 1.into()),
1928 (Variable::new("var3"), -Fraction::from(1)),
1929 ])),
1930 ThresholdConstraint::new(
1931 ThresholdCompOp::Lt,
1932 BTreeMap::from([(Parameter::new("t"), 3)]),
1933 1,
1934 ),
1935 )),
1936 ) & LIAVariableConstraint::ComparisonConstraint(
1937 ComparisonConstraint(ThresholdConstraintOver::new(
1938 WeightedSum::new(BTreeMap::from([
1939 (Variable::new("var3"), 1.into()),
1940 (Variable::new("var3"), -Fraction::from(1)),
1941 ])),
1942 ThresholdConstraint::new(
1943 ThresholdCompOp::Geq,
1944 BTreeMap::from([(Parameter::new("t"), 3)]),
1945 0,
1946 ),
1947 )),
1948 )),
1949 actions: vec![
1950 Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1951 .unwrap(),
1952 Action::new(
1953 Variable::new("var1"),
1954 IntegerExpression::BinaryExpr(
1955 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1956 IntegerOp::Add,
1957 Box::new(IntegerExpression::Const(1)),
1958 ),
1959 )
1960 .unwrap(),
1961 ],
1962 }],
1963 ),
1964 ]),
1965 init_variable_constr: vec![],
1966 additional_vars_for_sums: HashMap::new(),
1967 };
1968
1969 assert!(lta.has_sum_var_threshold_guard());
1970
1971 let expected = HashSet::from([
1972 SumAtomConstraint(ThresholdConstraintOver::new(
1973 WeightedSum::new(BTreeMap::from([
1974 (Variable::new("var2"), 1),
1975 (Variable::new("var1"), 1),
1976 ])),
1977 ThresholdConstraint::new(
1978 ThresholdCompOp::Geq,
1979 BTreeMap::from([(Parameter::new("n"), 1)]),
1980 0,
1981 ),
1982 )),
1983 SumAtomConstraint(ThresholdConstraintOver::new(
1984 WeightedSum::new(BTreeMap::from([
1985 (Variable::new("var2"), 1),
1986 (Variable::new("var1"), 1),
1987 ])),
1988 ThresholdConstraint::new(
1989 ThresholdCompOp::Lt,
1990 BTreeMap::from([(Parameter::new("n"), 1)]),
1991 1,
1992 ),
1993 )),
1994 ]);
1995 assert_eq!(lta.get_sum_var_constraints(), expected);
1996
1997 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1998 .with_parameters(vec![
1999 Parameter::new("n"),
2000 Parameter::new("t"),
2001 Parameter::new("f"),
2002 ])
2003 .unwrap()
2004 .with_variables(vec![
2005 Variable::new("var1"),
2006 Variable::new("var2"),
2007 Variable::new("var3"),
2008 ])
2009 .unwrap()
2010 .with_locations(vec![
2011 Location::new("loc1"),
2012 Location::new("loc2"),
2013 Location::new("loc3"),
2014 ])
2015 .unwrap()
2016 .initialize()
2017 .with_rules(vec![
2018 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
2019 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
2020 .with_guard(
2021 BooleanVarConstraint::ComparisonExpression(
2022 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2023 ComparisonOp::Eq,
2024 Box::new(IntegerExpression::Const(1)),
2025 ) & BooleanVarConstraint::ComparisonExpression(
2026 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2027 ComparisonOp::Eq,
2028 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2029 ),
2030 )
2031 .with_action(Action::new_reset(Variable::new("var3")))
2032 .with_action(
2033 Action::new(
2034 Variable::new("var1"),
2035 IntegerExpression::Atom(Variable::new("var1"))
2036 + IntegerExpression::Const(1),
2037 )
2038 .unwrap(),
2039 )
2040 .build(),
2041 ])
2042 .unwrap()
2043 .with_initial_location_constraint(
2044 LocationConstraint::ComparisonExpression(
2045 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
2046 ComparisonOp::Eq,
2047 Box::new(
2048 IntegerExpression::Param(Parameter::new("n"))
2049 - IntegerExpression::Param(Parameter::new("f")),
2050 ),
2051 ) | LocationConstraint::ComparisonExpression(
2052 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
2053 ComparisonOp::Eq,
2054 Box::new(IntegerExpression::Const(0)),
2055 ),
2056 )
2057 .unwrap()
2058 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
2059 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
2060 ComparisonOp::Gt,
2061 Box::new(
2062 IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
2063 ),
2064 ))
2065 .unwrap()
2066 .build();
2067
2068 let lta = LIAThresholdAutomaton {
2069 ta,
2070 rules: HashMap::from([
2071 (
2072 Location::new("loc1"),
2073 vec![LIARule {
2074 id: 0,
2075 source: Location::new("loc1"),
2076 target: Location::new("loc2"),
2077 guard: LIAVariableConstraint::True,
2078 actions: vec![],
2079 }],
2080 ),
2081 (
2082 Location::new("loc2"),
2083 vec![LIARule {
2084 id: 1,
2085 source: Location::new("loc2"),
2086 target: Location::new("loc3"),
2087 guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2088 ThresholdConstraintOver::new(
2089 Variable::new("var1"),
2090 ThresholdConstraint::new(
2091 ThresholdCompOp::Lt,
2092 Vec::<(Parameter, Fraction)>::new(),
2093 2,
2094 ),
2095 ),
2096 )) & LIAVariableConstraint::SingleVarConstraint(
2097 SingleAtomConstraint(ThresholdConstraintOver::new(
2098 Variable::new("var1"),
2099 ThresholdConstraint::new(
2100 ThresholdCompOp::Geq,
2101 Vec::<(Parameter, Fraction)>::new(),
2102 1,
2103 ),
2104 )),
2105 )) | (LIAVariableConstraint::ComparisonConstraint(
2106 ComparisonConstraint(ThresholdConstraintOver::new(
2107 WeightedSum::new(BTreeMap::from([
2108 (Variable::new("var3"), 1.into()),
2109 (Variable::new("var3"), -Fraction::from(1)),
2110 ])),
2111 ThresholdConstraint::new(
2112 ThresholdCompOp::Lt,
2113 BTreeMap::from([(Parameter::new("t"), 3)]),
2114 1,
2115 ),
2116 )),
2117 ) & LIAVariableConstraint::ComparisonConstraint(
2118 ComparisonConstraint(ThresholdConstraintOver::new(
2119 WeightedSum::new(BTreeMap::from([
2120 (Variable::new("var3"), 1.into()),
2121 (Variable::new("var3"), -Fraction::from(1)),
2122 ])),
2123 ThresholdConstraint::new(
2124 ThresholdCompOp::Geq,
2125 BTreeMap::from([(Parameter::new("t"), 3)]),
2126 0,
2127 ),
2128 )),
2129 )),
2130 actions: vec![
2131 Action::new(Variable::new("var3"), IntegerExpression::Const(0))
2132 .unwrap(),
2133 Action::new(
2134 Variable::new("var1"),
2135 IntegerExpression::BinaryExpr(
2136 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2137 IntegerOp::Add,
2138 Box::new(IntegerExpression::Const(1)),
2139 ),
2140 )
2141 .unwrap(),
2142 ],
2143 }],
2144 ),
2145 ]),
2146 init_variable_constr: vec![],
2147 additional_vars_for_sums: HashMap::new(),
2148 };
2149
2150 assert!(!lta.has_sum_var_threshold_guard());
2151 assert!(lta.get_sum_var_constraints().is_empty());
2152 }
2153
2154 #[test]
2155 fn test_lia_display() {
2156 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
2157 .with_parameters(vec![
2158 Parameter::new("n"),
2159 Parameter::new("t"),
2160 Parameter::new("f"),
2161 ])
2162 .unwrap()
2163 .with_variables(vec![
2164 Variable::new("var1"),
2165 Variable::new("var2"),
2166 Variable::new("var3"),
2167 ])
2168 .unwrap()
2169 .with_locations(vec![
2170 Location::new("loc1"),
2171 Location::new("loc2"),
2172 Location::new("loc3"),
2173 ])
2174 .unwrap()
2175 .initialize()
2176 .with_rules(vec![
2177 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
2178 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
2179 .with_guard(
2180 BooleanVarConstraint::ComparisonExpression(
2181 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2182 ComparisonOp::Eq,
2183 Box::new(IntegerExpression::Const(1)),
2184 ) & BooleanVarConstraint::ComparisonExpression(
2185 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2186 ComparisonOp::Eq,
2187 Box::new(IntegerExpression::Param(Parameter::new("n"))),
2188 ),
2189 )
2190 .with_action(Action::new_reset(Variable::new("var3")))
2191 .with_action(
2192 Action::new(
2193 Variable::new("var1"),
2194 IntegerExpression::Atom(Variable::new("var1"))
2195 + IntegerExpression::Const(1),
2196 )
2197 .unwrap(),
2198 )
2199 .build(),
2200 ])
2201 .unwrap()
2202 .with_initial_location_constraint(
2203 LocationConstraint::ComparisonExpression(
2204 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
2205 ComparisonOp::Eq,
2206 Box::new(
2207 IntegerExpression::Param(Parameter::new("n"))
2208 - IntegerExpression::Param(Parameter::new("f")),
2209 ),
2210 ) | LocationConstraint::ComparisonExpression(
2211 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
2212 ComparisonOp::Eq,
2213 Box::new(IntegerExpression::Const(0)),
2214 ),
2215 )
2216 .unwrap()
2217 .with_initial_variable_constraint(BooleanVarConstraint::ComparisonExpression(
2218 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2219 ComparisonOp::Eq,
2220 Box::new(IntegerExpression::Const(1)),
2221 ))
2222 .unwrap()
2223 .with_resilience_condition(ParameterConstraint::ComparisonExpression(
2224 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
2225 ComparisonOp::Gt,
2226 Box::new(
2227 IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
2228 ),
2229 ))
2230 .unwrap()
2231 .build();
2232
2233 let lta = LIAThresholdAutomaton {
2234 ta,
2235 rules: HashMap::from([
2236 (
2237 Location::new("loc1"),
2238 vec![LIARule {
2239 id: 0,
2240 source: Location::new("loc1"),
2241 target: Location::new("loc2"),
2242 guard: LIAVariableConstraint::True,
2243 actions: vec![],
2244 }],
2245 ),
2246 (
2247 Location::new("loc2"),
2248 vec![LIARule {
2249 id: 1,
2250 source: Location::new("loc2"),
2251 target: Location::new("loc3"),
2252 guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2253 ThresholdConstraintOver::new(
2254 Variable::new("var1"),
2255 ThresholdConstraint::new(
2256 ThresholdCompOp::Lt,
2257 Vec::<(Parameter, Fraction)>::new(),
2258 2,
2259 ),
2260 ),
2261 )) & LIAVariableConstraint::SingleVarConstraint(
2262 SingleAtomConstraint(ThresholdConstraintOver::new(
2263 Variable::new("var1"),
2264 ThresholdConstraint::new(
2265 ThresholdCompOp::Geq,
2266 Vec::<(Parameter, Fraction)>::new(),
2267 1,
2268 ),
2269 )),
2270 )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2271 ThresholdConstraintOver::new(
2272 WeightedSum::new(BTreeMap::from([
2273 (Variable::new("var2"), 1),
2274 (Variable::new("var1"), 1),
2275 ])),
2276 ThresholdConstraint::new(
2277 ThresholdCompOp::Lt,
2278 BTreeMap::from([(Parameter::new("n"), 1)]),
2279 1,
2280 ),
2281 ),
2282 )) & LIAVariableConstraint::SumVarConstraint(
2283 SumAtomConstraint(ThresholdConstraintOver::new(
2284 WeightedSum::new(BTreeMap::from([
2285 (Variable::new("var2"), 1),
2286 (Variable::new("var1"), 1),
2287 ])),
2288 ThresholdConstraint::new(
2289 ThresholdCompOp::Geq,
2290 BTreeMap::from([(Parameter::new("n"), 1)]),
2291 0,
2292 ),
2293 )),
2294 )) | (LIAVariableConstraint::ComparisonConstraint(
2295 ComparisonConstraint(ThresholdConstraintOver::new(
2296 WeightedSum::new(BTreeMap::from([
2297 (Variable::new("var3"), 1.into()),
2298 (Variable::new("var3"), -Fraction::from(1)),
2299 ])),
2300 ThresholdConstraint::new(
2301 ThresholdCompOp::Lt,
2302 BTreeMap::from([(Parameter::new("t"), 3)]),
2303 1,
2304 ),
2305 )),
2306 ) & LIAVariableConstraint::ComparisonConstraint(
2307 ComparisonConstraint(ThresholdConstraintOver::new(
2308 WeightedSum::new(BTreeMap::from([
2309 (Variable::new("var3"), 1.into()),
2310 (Variable::new("var3"), -Fraction::from(1)),
2311 ])),
2312 ThresholdConstraint::new(
2313 ThresholdCompOp::Geq,
2314 BTreeMap::from([(Parameter::new("t"), 3)]),
2315 0,
2316 ),
2317 )),
2318 )),
2319
2320 actions: vec![
2321 Action::new(Variable::new("var3"), IntegerExpression::Const(0))
2322 .unwrap(),
2323 Action::new(
2324 Variable::new("var1"),
2325 IntegerExpression::BinaryExpr(
2326 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2327 IntegerOp::Add,
2328 Box::new(IntegerExpression::Const(1)),
2329 ),
2330 )
2331 .unwrap(),
2332 ],
2333 }],
2334 ),
2335 ]),
2336 init_variable_constr: vec![
2337 LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2338 ThresholdConstraintOver::new(
2339 Variable::new("var1"),
2340 ThresholdConstraint::new(
2341 ThresholdCompOp::Lt,
2342 Vec::<(Parameter, Fraction)>::new(),
2343 Fraction::from(2),
2344 ),
2345 ),
2346 )),
2347 LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2348 ThresholdConstraintOver::new(
2349 Variable::new("var1"),
2350 ThresholdConstraint::new(
2351 ThresholdCompOp::Geq,
2352 Vec::<(Parameter, Fraction)>::new(),
2353 Fraction::from(1),
2354 ),
2355 ),
2356 )),
2357 ],
2358 additional_vars_for_sums: HashMap::new(),
2359 };
2360
2361 let expected = "thresholdAutomaton test_ta1 {\n shared var1, var2, var3;\n\n parameters f, n, t;\n\n assumptions (1) {\n n > (3 * f);\n }\n\n locations (3) {\n loc1:[0];\n loc2:[1];\n loc3:[2];\n }\n\n inits (3) {\n (loc1 == (n - f) || loc2 == 0);\n var1 < 2;\n var1 >= 1;\n }\n\n rules (2) {\n 0: loc1 -> loc2\n when ( true )\n do { };\n 1: loc2 -> loc3\n when ( (((var1 < 2) && (var1 >= 1)) && ((var1 + var2 < n + 1) && (var1 + var2 >= n))) || ((-1 * var3 < 3 * t + 1) && (-1 * var3 >= 3 * t)) )\n do { var3' == 0; var1' == var1 + 1 };\n }\n}\n";
2362
2363 assert_eq!(lta.to_string(), expected)
2364 }
2365
2366 #[test]
2367 fn test_lia_rule_getters() {
2368 let rule = LIARule {
2369 id: 0,
2370 source: Location::new("loc1"),
2371 target: Location::new("loc2"),
2372 guard: LIAVariableConstraint::True,
2373 actions: vec![],
2374 };
2375
2376 assert_eq!(rule.id(), 0);
2377 assert_eq!(rule.source(), &Location::new("loc1"));
2378 assert_eq!(rule.target(), &Location::new("loc2"));
2379 assert_eq!(rule.guard(), &LIAVariableConstraint::True);
2380 assert_eq!(rule.actions().count(), 0);
2381 assert_eq!(rule.number_of_updates(), 0);
2382 assert!(!rule.has_resets());
2383 assert!(!rule.has_decrements());
2384
2385 let rule = LIARule {
2386 id: 0,
2387 source: Location::new("loc1"),
2388 target: Location::new("loc2"),
2389 guard: LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2390 ThresholdConstraintOver::new(
2391 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2392 ThresholdConstraint::new(
2393 ThresholdCompOp::Lt,
2394 Vec::<(Parameter, Fraction)>::new(),
2395 2,
2396 ),
2397 ),
2398 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2399 ThresholdConstraintOver::new(
2400 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2401 ThresholdConstraint::new(
2402 ThresholdCompOp::Geq,
2403 Vec::<(Parameter, Fraction)>::new(),
2404 1,
2405 ),
2406 ),
2407 )),
2408 actions: vec![
2409 Action::new(
2410 Variable::new("var1"),
2411 IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1),
2412 )
2413 .unwrap(),
2414 ],
2415 };
2416
2417 assert_eq!(rule.id(), 0);
2418 assert_eq!(rule.source(), &Location::new("loc1"));
2419 assert_eq!(rule.target(), &Location::new("loc2"));
2420 assert_eq!(
2421 rule.guard(),
2422 &(LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2423 ThresholdConstraintOver::new(
2424 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2425 ThresholdConstraint::new(
2426 ThresholdCompOp::Lt,
2427 Vec::<(Parameter, Fraction)>::new(),
2428 2,
2429 ),
2430 ),
2431 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2432 ThresholdConstraintOver::new(
2433 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2434 ThresholdConstraint::new(
2435 ThresholdCompOp::Geq,
2436 Vec::<(Parameter, Fraction)>::new(),
2437 1,
2438 ),
2439 ),
2440 ))),
2441 );
2442 assert_eq!(
2443 rule.actions().cloned().collect::<Vec<_>>(),
2444 vec![
2445 Action::new(
2446 Variable::new("var1"),
2447 IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1)
2448 )
2449 .unwrap()
2450 ]
2451 );
2452 assert_eq!(rule.number_of_updates(), 1);
2453 assert!(!rule.has_resets());
2454 assert!(rule.has_decrements());
2455
2456 let rule = LIARule {
2457 id: 2,
2458 source: Location::new("loc1"),
2459 target: Location::new("loc2"),
2460 guard: (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2461 ThresholdConstraintOver::new(
2462 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2463 ThresholdConstraint::new(
2464 ThresholdCompOp::Lt,
2465 Vec::<(Parameter, Fraction)>::new(),
2466 2,
2467 ),
2468 ),
2469 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2470 ThresholdConstraintOver::new(
2471 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2472 ThresholdConstraint::new(
2473 ThresholdCompOp::Geq,
2474 Vec::<(Parameter, Fraction)>::new(),
2475 1,
2476 ),
2477 ),
2478 ))),
2479 actions: vec![Action::new(Variable::new("var1"), IntegerExpression::Const(0)).unwrap()],
2480 };
2481
2482 assert_eq!(rule.id(), 2);
2483 assert_eq!(rule.source(), &Location::new("loc1"));
2484 assert_eq!(rule.target(), &Location::new("loc2"));
2485 assert_eq!(
2486 rule.guard(),
2487 &(LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2488 ThresholdConstraintOver::new(
2489 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2490 ThresholdConstraint::new(
2491 ThresholdCompOp::Lt,
2492 Vec::<(Parameter, Fraction)>::new(),
2493 2,
2494 ),
2495 ),
2496 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2497 ThresholdConstraintOver::new(
2498 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2499 ThresholdConstraint::new(
2500 ThresholdCompOp::Geq,
2501 Vec::<(Parameter, Fraction)>::new(),
2502 1,
2503 ),
2504 ),
2505 )))
2506 );
2507 assert_eq!(
2508 rule.actions().cloned().collect::<Vec<_>>(),
2509 vec![Action::new(Variable::new("var1"), IntegerExpression::Const(0)).unwrap()]
2510 );
2511 assert_eq!(rule.number_of_updates(), 1);
2512 assert!(rule.has_resets());
2513 assert!(!rule.has_decrements());
2514 }
2515
2516 #[test]
2517 fn test_count_number_of_thresholds() {
2518 let thr = LIAVariableConstraint::False;
2519 assert_eq!(thr.count_number_of_thresholds(), 0);
2520
2521 let thr = LIAVariableConstraint::True;
2522 assert_eq!(thr.count_number_of_thresholds(), 0);
2523
2524 let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2525 ThresholdConstraintOver::new(
2526 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2527 ThresholdConstraint::new(
2528 ThresholdCompOp::Lt,
2529 Vec::<(Parameter, Fraction)>::new(),
2530 2,
2531 ),
2532 ),
2533 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2534 ThresholdConstraintOver::new(
2535 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2536 ThresholdConstraint::new(
2537 ThresholdCompOp::Geq,
2538 Vec::<(Parameter, Fraction)>::new(),
2539 1,
2540 ),
2541 ),
2542 ));
2543 assert_eq!(thr.count_number_of_thresholds(), 2);
2544
2545 let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2546 ThresholdConstraintOver::new(
2547 WeightedSum::new(BTreeMap::from([
2548 (Variable::new("var1"), 1),
2549 (Variable::new("var2"), 2),
2550 ])),
2551 ThresholdConstraint::new(
2552 ThresholdCompOp::Lt,
2553 BTreeMap::from([(Parameter::new("n"), 3)]),
2554 6,
2555 ),
2556 ),
2557 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2558 ThresholdConstraintOver::new(
2559 WeightedSum::new(BTreeMap::from([
2560 (Variable::new("var1"), 1),
2561 (Variable::new("var2"), 2),
2562 ])),
2563 ThresholdConstraint::new(
2564 ThresholdCompOp::Geq,
2565 BTreeMap::from([(Parameter::new("n"), 3)]),
2566 5,
2567 ),
2568 ),
2569 ));
2570 assert_eq!(thr.count_number_of_thresholds(), 2);
2571
2572 let thr = (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2573 ThresholdConstraintOver::new(
2574 WeightedSum::new(BTreeMap::from([
2575 (Variable::new("var1"), 1),
2576 (Variable::new("var2"), 2),
2577 ])),
2578 ThresholdConstraint::new(
2579 ThresholdCompOp::Lt,
2580 BTreeMap::from([(Parameter::new("n"), 3)]),
2581 1,
2582 ),
2583 ),
2584 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2585 ThresholdConstraintOver::new(
2586 WeightedSum::new(BTreeMap::from([
2587 (Variable::new("var1"), 1),
2588 (Variable::new("var2"), 2),
2589 ])),
2590 ThresholdConstraint::new(
2591 ThresholdCompOp::Geq,
2592 BTreeMap::from([(Parameter::new("n"), 3)]),
2593 0,
2594 ),
2595 ),
2596 ))) | (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2597 ThresholdConstraintOver::new(
2598 WeightedSum::new(BTreeMap::from([(Variable::new("var3"), 1)])),
2599 ThresholdConstraint::new(
2600 ThresholdCompOp::Lt,
2601 BTreeMap::from([(Parameter::new("m"), 3)]),
2602 1,
2603 ),
2604 ),
2605 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2606 ThresholdConstraintOver::new(
2607 WeightedSum::new(BTreeMap::from([(Variable::new("var3"), 1)])),
2608 ThresholdConstraint::new(
2609 ThresholdCompOp::Geq,
2610 BTreeMap::from([(Parameter::new("m"), 3)]),
2611 0,
2612 ),
2613 ),
2614 )));
2615 assert_eq!(thr.count_number_of_thresholds(), 4);
2616 }
2617
2618 #[test]
2619 fn test_bin_op_threshold_guard() {
2620 let thr = LIAVariableConstraint::False & LIAVariableConstraint::True;
2621 assert_eq!(
2622 thr,
2623 LIAVariableConstraint::BinaryGuard(
2624 Box::new(LIAVariableConstraint::False),
2625 BooleanConnective::And,
2626 Box::new(LIAVariableConstraint::True)
2627 )
2628 );
2629
2630 let thr = LIAVariableConstraint::False | LIAVariableConstraint::True;
2631 assert_eq!(
2632 thr,
2633 LIAVariableConstraint::BinaryGuard(
2634 Box::new(LIAVariableConstraint::False),
2635 BooleanConnective::Or,
2636 Box::new(LIAVariableConstraint::True)
2637 )
2638 );
2639 }
2640
2641 #[test]
2642 fn test_is_top() {
2643 assert!(LIAVariableConstraint::True.is_top());
2644 assert!(!LIAVariableConstraint::False.is_top());
2645
2646 let guard = LIAVariableConstraint::True & LIAVariableConstraint::True;
2648 assert!(guard.is_top());
2649
2650 let guard = LIAVariableConstraint::True & LIAVariableConstraint::False;
2651 assert!(!guard.is_top());
2652
2653 let guard = LIAVariableConstraint::False | LIAVariableConstraint::True;
2655 assert!(guard.is_top());
2656
2657 let guard = LIAVariableConstraint::False | LIAVariableConstraint::False;
2658 assert!(!guard.is_top());
2659
2660 let guard = (LIAVariableConstraint::True & LIAVariableConstraint::False)
2662 | LIAVariableConstraint::True;
2663 assert!(guard.is_top());
2664
2665 let guard = LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
2667 Variable::new("var1"),
2668 ThresholdConstraint::new(ThresholdCompOp::Geq, Vec::<(Parameter, Fraction)>::new(), 0),
2669 ));
2670 assert!(!guard.is_top());
2671 }
2672
2673 #[test]
2674 fn test_is_bot() {
2675 assert!(LIAVariableConstraint::False.is_bot());
2676 assert!(!LIAVariableConstraint::True.is_bot());
2677
2678 let guard = LIAVariableConstraint::False & LIAVariableConstraint::True;
2680 assert!(guard.is_bot());
2681
2682 let guard = LIAVariableConstraint::True & LIAVariableConstraint::True;
2683 assert!(!guard.is_bot());
2684
2685 let guard = LIAVariableConstraint::False | LIAVariableConstraint::False;
2687 assert!(guard.is_bot());
2688
2689 let guard = LIAVariableConstraint::False | LIAVariableConstraint::True;
2690 assert!(!guard.is_bot());
2691
2692 let guard = (LIAVariableConstraint::True | LIAVariableConstraint::False)
2694 & LIAVariableConstraint::False;
2695 assert!(guard.is_bot());
2696
2697 let guard = LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
2699 Variable::new("var1"),
2700 ThresholdConstraint::new(ThresholdCompOp::Geq, Vec::<(Parameter, Fraction)>::new(), 0),
2701 ));
2702 assert!(!guard.is_bot());
2703 }
2704
2705 #[test]
2706 fn test_guards_to_boolean() {
2707 let guard = LIAVariableConstraint::True;
2708 assert_eq!(guard.as_boolean_expr(), BooleanExpression::True);
2709
2710 let guard = LIAVariableConstraint::False;
2711 assert_eq!(guard.as_boolean_expr(), BooleanExpression::False);
2712
2713 let guard = LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2714 ThresholdConstraintOver::new(
2715 Variable::new("var1"),
2716 ThresholdConstraint::new(
2717 ThresholdCompOp::Lt,
2718 Vec::<(Parameter, Fraction)>::new(),
2719 1,
2720 ),
2721 ),
2722 ));
2723 assert_eq!(
2724 guard.as_boolean_expr(),
2725 BooleanExpression::ComparisonExpression(
2726 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2727 ComparisonOp::Lt,
2728 Box::new(IntegerExpression::Const(1))
2729 )
2730 );
2731
2732 let guard = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2733 ThresholdConstraintOver::new(
2734 WeightedSum::new([(Variable::new("var1"), 1), (Variable::new("var2"), 2)]),
2735 ThresholdConstraint::new(
2736 ThresholdCompOp::Lt,
2737 BTreeMap::from([(Parameter::new("n"), 3)]),
2738 5,
2739 ),
2740 ),
2741 ));
2742 assert_eq!(
2743 guard.as_boolean_expr(),
2744 BooleanExpression::ComparisonExpression(
2745 Box::new(IntegerExpression::BinaryExpr(
2746 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2747 IntegerOp::Add,
2748 Box::new(IntegerExpression::BinaryExpr(
2749 Box::new(IntegerExpression::Const(2)),
2750 IntegerOp::Mul,
2751 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2752 ))
2753 )),
2754 ComparisonOp::Lt,
2755 Box::new(
2756 IntegerExpression::BinaryExpr(
2757 Box::new(IntegerExpression::Const(3)),
2758 IntegerOp::Mul,
2759 Box::new(IntegerExpression::Param(Parameter::new("n")))
2760 ) + IntegerExpression::Const(5)
2761 )
2762 )
2763 );
2764
2765 let guard = LIAVariableConstraint::ComparisonConstraint(ComparisonConstraint(
2766 ThresholdConstraintOver::new(
2767 WeightedSum::new([
2768 (Variable::new("var1"), 1.into()),
2769 (Variable::new("var2"), Fraction::from(2)),
2770 ]),
2771 ThresholdConstraint::new(
2772 ThresholdCompOp::Lt,
2773 BTreeMap::from([(Parameter::new("n"), 3)]),
2774 1,
2775 ),
2776 ),
2777 ));
2778 assert_eq!(
2779 guard.as_boolean_expr(),
2780 BooleanExpression::ComparisonExpression(
2781 Box::new(IntegerExpression::BinaryExpr(
2782 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2783 IntegerOp::Add,
2784 Box::new(IntegerExpression::BinaryExpr(
2785 Box::new(IntegerExpression::Const(2)),
2786 IntegerOp::Mul,
2787 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2788 ))
2789 )),
2790 ComparisonOp::Lt,
2791 Box::new(
2792 IntegerExpression::BinaryExpr(
2793 Box::new(IntegerExpression::Const(3)),
2794 IntegerOp::Mul,
2795 Box::new(IntegerExpression::Param(Parameter::new("n")))
2796 ) + IntegerExpression::Const(1)
2797 )
2798 )
2799 );
2800
2801 let guard = LIAVariableConstraint::BinaryGuard(
2802 Box::new(LIAVariableConstraint::True),
2803 BooleanConnective::And,
2804 Box::new(LIAVariableConstraint::False),
2805 );
2806 assert_eq!(
2807 guard.as_boolean_expr(),
2808 BooleanExpression::BinaryExpression(
2809 Box::new(BooleanExpression::True),
2810 BooleanConnective::And,
2811 Box::new(BooleanExpression::False)
2812 )
2813 );
2814
2815 let guard = LIAVariableConstraint::BinaryGuard(
2816 Box::new(LIAVariableConstraint::True),
2817 BooleanConnective::Or,
2818 Box::new(LIAVariableConstraint::False),
2819 );
2820 assert_eq!(
2821 guard.as_boolean_expr(),
2822 BooleanExpression::BinaryExpression(
2823 Box::new(BooleanExpression::True),
2824 BooleanConnective::Or,
2825 Box::new(BooleanExpression::False)
2826 )
2827 );
2828 }
2829
2830 #[test]
2831 fn test_threshold_guard_display() {
2832 let thr = LIAVariableConstraint::False;
2833 let expected = "false";
2834 assert_eq!(thr.to_string(), expected);
2835
2836 let thr = LIAVariableConstraint::True;
2837 let expected = "true";
2838 assert_eq!(thr.to_string(), expected);
2839
2840 let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2841 ThresholdConstraintOver::new(
2842 WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2843 ThresholdConstraint::new(
2844 ThresholdCompOp::Lt,
2845 Vec::<(Parameter, Fraction)>::new(),
2846 1,
2847 ),
2848 ),
2849 ));
2850 let expected = "var1 < 1";
2851 assert_eq!(thr.to_string(), expected);
2852
2853 let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2854 ThresholdConstraintOver::new(
2855 WeightedSum::new(BTreeMap::from([
2856 (Variable::new("var1"), 1),
2857 (Variable::new("var2"), 2),
2858 ])),
2859 ThresholdConstraint::new(
2860 ThresholdCompOp::Lt,
2861 BTreeMap::from([(Parameter::new("n"), 3)]),
2862 5,
2863 ),
2864 ),
2865 ));
2866 let expected = "var1 + 2 * var2 < 3 * n + 5";
2867 assert_eq!(thr.to_string(), expected);
2868
2869 let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2870 ThresholdConstraintOver::new(
2871 WeightedSum::new(BTreeMap::from([
2872 (Variable::new("var1"), 1),
2873 (Variable::new("var2"), 2),
2874 ])),
2875 ThresholdConstraint::new(
2876 ThresholdCompOp::Lt,
2877 BTreeMap::from([(Parameter::new("n"), 3)]),
2878 0,
2879 ),
2880 ),
2881 )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2882 ThresholdConstraintOver::new(
2883 WeightedSum::new(BTreeMap::from([(Variable::new("var3"), 1)])),
2884 ThresholdConstraint::new(
2885 ThresholdCompOp::Lt,
2886 BTreeMap::from([(Parameter::new("m"), 3), (Parameter::new("o"), 4)]),
2887 0,
2888 ),
2889 ),
2890 ));
2891 let expected = "(var1 + 2 * var2 < 3 * n) && (var3 < 3 * m + 4 * o)";
2892 assert_eq!(thr.to_string(), expected);
2893 }
2894
2895 #[test]
2896 fn test_display_single_var_threshold_guard() {
2897 let guard = SingleAtomConstraint::new(
2898 Variable::new("var1"),
2899 ThresholdConstraint::new(ThresholdCompOp::Geq, Vec::<(Parameter, Fraction)>::new(), 5),
2900 );
2901 let expected = "var1 >= 5";
2902 assert_eq!(guard.to_string(), expected);
2903 }
2904
2905 #[test]
2906 fn test_new_multi_var_threshold() {
2907 let guard = SumAtomConstraint::try_new(
2908 [(Variable::new("var1"), 1), (Variable::new("var2"), 2)],
2909 ThresholdConstraint::new(
2910 ThresholdCompOp::Geq,
2911 BTreeMap::from([(Parameter::new("n"), 3)]),
2912 5,
2913 ),
2914 )
2915 .unwrap();
2916
2917 let expected = SumAtomConstraint(ThresholdConstraintOver::new(
2918 WeightedSum::new(BTreeMap::from([
2919 (Variable::new("var1"), 1),
2920 (Variable::new("var2"), 2),
2921 ])),
2922 ThresholdConstraint::new(ThresholdCompOp::Geq, [(Parameter::new("n"), 3)], 5),
2923 ));
2924
2925 assert_eq!(guard, expected);
2926
2927 let expected_thr = Threshold::new([(Parameter::new("n"), 3)], 5);
2928
2929 assert_eq!(guard.get_threshold(), &expected_thr);
2930 assert_eq!(
2931 guard.get_atoms(),
2932 &WeightedSum::new(BTreeMap::from([
2933 (Variable::new("var1"), 1),
2934 (Variable::new("var2"), 2),
2935 ]))
2936 );
2937 }
2938
2939 #[test]
2940 fn test_new_multi_var_threshold_all_vars_neg() {
2941 let guard = SumAtomConstraint::try_new(
2943 BTreeMap::from([
2944 (Variable::new("var1"), -Fraction::from(1)),
2945 (Variable::new("var2"), -Fraction::from(2)),
2946 ]),
2947 ThresholdConstraint::new(
2948 ThresholdCompOp::Geq,
2949 BTreeMap::from([(Parameter::new("n"), 3)]),
2950 5,
2951 ),
2952 )
2953 .unwrap();
2954
2955 let expected = SumAtomConstraint(ThresholdConstraintOver::new(
2957 WeightedSum::new(BTreeMap::from([
2958 (Variable::new("var1"), 1),
2959 (Variable::new("var2"), 2),
2960 ])),
2961 ThresholdConstraint::new(
2962 ThresholdCompOp::Leq,
2963 BTreeMap::from([(Parameter::new("n"), -Fraction::from(3))]),
2964 -Fraction::from(5),
2965 ),
2966 ));
2967
2968 assert_eq!(guard, expected);
2969 }
2970
2971 #[test]
2972 fn test_new_multi_var_threshold_err_single() {
2973 let guard = SumAtomConstraint::try_new(
2974 BTreeMap::from([(Variable::new("var1"), 1)]),
2975 ThresholdConstraint::new(
2976 ThresholdCompOp::Geq,
2977 BTreeMap::from([(Parameter::new("n"), 3)]),
2978 5,
2979 ),
2980 );
2981
2982 assert!(matches!(
2983 guard.clone(),
2984 Err(SumVarConstraintCreationError::IsSingleAtomConstraint)
2985 ));
2986 assert!(
2987 guard
2988 .unwrap_err()
2989 .to_string()
2990 .contains("SingleVarThresholdGuard")
2991 );
2992 }
2993
2994 #[test]
2995 fn test_new_multi_var_threshold_err_mixed() {
2996 let guard = SumAtomConstraint::try_new(
2997 BTreeMap::from([
2998 (Variable::new("var1"), 1.into()),
2999 (Variable::new("var2"), -Fraction::from(2)),
3000 ]),
3001 ThresholdConstraint::new(
3002 ThresholdCompOp::Geq,
3003 BTreeMap::from([(Parameter::new("n"), 3)]),
3004 5,
3005 ),
3006 );
3007
3008 assert!(matches!(
3009 guard.clone(),
3010 Err(SumVarConstraintCreationError::IsComparisonConstraint)
3011 ));
3012 assert!(guard.unwrap_err().to_string().contains("ComparisonGuard"));
3013 }
3014
3015 #[test]
3016 fn test_display_multi_var_threshold_guard() {
3017 let guard = SumAtomConstraint::try_new(
3018 BTreeMap::from([(Variable::new("var1"), 1), (Variable::new("var2"), 2)]),
3019 ThresholdConstraint::new(
3020 ThresholdCompOp::Geq,
3021 BTreeMap::from([(Parameter::new("n"), 3)]),
3022 5,
3023 ),
3024 )
3025 .unwrap();
3026 let expected = "var1 + 2 * var2 >= 3 * n + 5";
3027 assert_eq!(guard.to_string(), expected);
3028 }
3029
3030 #[test]
3031 fn test_new_comparison_guard() {
3032 let guard = ComparisonConstraint::try_new(
3033 BTreeMap::from([
3034 (Variable::new("var1"), 1.into()),
3035 (Variable::new("var2"), -Fraction::from(2)),
3036 ]),
3037 ThresholdConstraint::new(
3038 ThresholdCompOp::Geq,
3039 BTreeMap::from([(Parameter::new("n"), 3)]),
3040 5,
3041 ),
3042 )
3043 .unwrap();
3044
3045 let expected = ComparisonConstraint(ThresholdConstraintOver::new(
3046 WeightedSum::new(BTreeMap::from([
3047 (Variable::new("var1"), 1.into()),
3048 (Variable::new("var2"), -Fraction::from(2)),
3049 ])),
3050 ThresholdConstraint::new(
3051 ThresholdCompOp::Geq,
3052 BTreeMap::from([(Parameter::new("n"), 3)]),
3053 5,
3054 ),
3055 ));
3056
3057 assert_eq!(guard, expected);
3058 }
3059
3060 #[test]
3061 fn test_comparison_guard_get_threshold_constr() {
3062 let guard = ComparisonConstraint(ThresholdConstraintOver::new(
3063 WeightedSum::from([
3064 (Variable::new("var1"), 1.into()),
3065 (Variable::new("var2"), -Fraction::from(2)),
3066 ]),
3067 ThresholdConstraint::new(ThresholdCompOp::Geq, [(Parameter::new("n"), 3)], 5),
3068 ));
3069
3070 assert_eq!(
3071 guard.get_threshold_constraint(),
3072 &ThresholdConstraint::new(ThresholdCompOp::Geq, [(Parameter::new("n"), 3)], 5)
3073 )
3074 }
3075
3076 #[test]
3077 fn test_new_comparison_guard_err_multi() {
3078 let guard = ComparisonConstraint::try_new(
3079 BTreeMap::from([
3080 (Variable::new("var1"), 1.into()),
3081 (Variable::new("var2"), 2.into()),
3082 ]),
3083 ThresholdConstraint::new(
3084 ThresholdCompOp::Geq,
3085 BTreeMap::from([(Parameter::new("n"), 3)]),
3086 5,
3087 ),
3088 );
3089
3090 assert!(matches!(
3091 guard.clone(),
3092 Err(ComparisonConstraintCreationError::IsSumVarConstraint)
3093 ));
3094 assert!(
3095 guard
3096 .unwrap_err()
3097 .to_string()
3098 .contains("MultiVarThresholdGuard")
3099 );
3100 }
3101
3102 #[test]
3103 fn test_display_comparison_guard() {
3104 let guard = ComparisonConstraint::try_new(
3105 BTreeMap::from([
3106 (Variable::new("var1"), 1.into()),
3107 (Variable::new("var2"), -Fraction::from(2)),
3108 ]),
3109 ThresholdConstraint::new(
3110 ThresholdCompOp::Geq,
3111 BTreeMap::from([(Parameter::new("n"), 3)]),
3112 5,
3113 ),
3114 )
3115 .unwrap();
3116 let expected = "var1 + -2 * var2 >= 3 * n + 5";
3117 assert_eq!(guard.to_string(), expected);
3118 }
3119
3120 #[test]
3121 fn test_lower_and_upper_guard() {
3122 let lower_thr = ThresholdConstraint::new(
3123 ThresholdCompOp::Geq,
3124 BTreeMap::from([(Parameter::new("n"), 3)]),
3125 5,
3126 );
3127
3128 let upper_thr = ThresholdConstraint::new(
3129 ThresholdCompOp::Lt,
3130 BTreeMap::from([(Parameter::new("n"), 3)]),
3131 4,
3132 );
3133
3134 let guard = LIAVariableConstraint::ComparisonConstraint(
3135 ComparisonConstraint::try_new(
3136 BTreeMap::from([
3137 (Variable::new("var1"), 1.into()),
3138 (Variable::new("var2"), -Fraction::from(2)),
3139 ]),
3140 lower_thr.clone(),
3141 )
3142 .unwrap(),
3143 );
3144 assert!(guard.is_lower_guard());
3145 assert!(!guard.is_upper_guard());
3146
3147 let guard = LIAVariableConstraint::ComparisonConstraint(
3148 ComparisonConstraint::try_new(
3149 BTreeMap::from([
3150 (Variable::new("var1"), 1.into()),
3151 (Variable::new("var2"), -Fraction::from(2)),
3152 ]),
3153 upper_thr.clone(),
3154 )
3155 .unwrap(),
3156 );
3157 assert!(!guard.is_lower_guard());
3158 assert!(guard.is_upper_guard());
3159
3160 let guard = SingleAtomConstraint::new(Variable::new("var1"), lower_thr.clone());
3161 assert!(guard.is_lower_guard());
3162 assert!(!guard.is_upper_guard());
3163
3164 let guard = SingleAtomConstraint::new(Variable::new("var1"), upper_thr.clone());
3165 assert!(!guard.is_lower_guard());
3166 assert!(guard.is_upper_guard());
3167
3168 let guard = LIAVariableConstraint::SumVarConstraint(
3169 SumAtomConstraint::try_new(
3170 BTreeMap::from([
3171 (Variable::new("var1"), 1.into()),
3172 (Variable::new("var2"), Fraction::from(2)),
3173 ]),
3174 lower_thr.clone(),
3175 )
3176 .unwrap(),
3177 );
3178 assert!(guard.is_lower_guard());
3179 assert!(!guard.is_upper_guard());
3180
3181 let guard = LIAVariableConstraint::SumVarConstraint(
3182 SumAtomConstraint::try_new(
3183 BTreeMap::from([
3184 (Variable::new("var1"), 1.into()),
3185 (Variable::new("var2"), Fraction::from(2)),
3186 ]),
3187 upper_thr.clone(),
3188 )
3189 .unwrap(),
3190 );
3191 assert!(!guard.is_lower_guard());
3192 assert!(guard.is_upper_guard());
3193
3194 let guard = LIAVariableConstraint::True;
3195 assert!(!guard.is_lower_guard());
3196 assert!(!guard.is_upper_guard());
3197
3198 let guard = LIAVariableConstraint::False;
3199 assert!(!guard.is_lower_guard());
3200 assert!(!guard.is_upper_guard());
3201
3202 let guard = LIAVariableConstraint::BinaryGuard(
3203 Box::new(LIAVariableConstraint::True),
3204 BooleanConnective::And,
3205 Box::new(LIAVariableConstraint::False),
3206 );
3207 assert!(!guard.is_lower_guard());
3208 assert!(!guard.is_upper_guard());
3209
3210 let guard = LIAVariableConstraint::BinaryGuard(
3211 Box::new(LIAVariableConstraint::SingleVarConstraint(
3212 SingleAtomConstraint::new(Variable::new("var1"), lower_thr.clone()),
3213 )),
3214 BooleanConnective::Or,
3215 Box::new(LIAVariableConstraint::SingleVarConstraint(
3216 SingleAtomConstraint::new(Variable::new("var1"), upper_thr.clone()),
3217 )),
3218 );
3219 assert!(guard.is_lower_guard());
3220 assert!(guard.is_upper_guard());
3221 }
3222
3223 #[test]
3224 fn test_getters_single_var_thr() {
3225 let guard = SingleAtomConstraint::new(
3226 Variable::new("var1"),
3227 ThresholdConstraint::new(ThresholdCompOp::Geq, Vec::<(Parameter, Fraction)>::new(), 5),
3228 );
3229
3230 assert_eq!(guard.get_atom(), &Variable::new("var1"));
3231 assert_eq!(
3232 guard.get_threshold(),
3233 &Threshold::new(Vec::<(Parameter, Fraction)>::new(), 5)
3234 );
3235 }
3236
3237 #[test]
3238 fn test_getters_multi_var_thr() {
3239 let guard = SumAtomConstraint::try_new(
3240 BTreeMap::from([(Variable::new("var1"), 1), (Variable::new("var2"), 2)]),
3241 ThresholdConstraint::new(
3242 ThresholdCompOp::Geq,
3243 BTreeMap::from([(Parameter::new("n"), 3)]),
3244 5,
3245 ),
3246 )
3247 .unwrap();
3248
3249 assert_eq!(
3250 guard.get_atoms(),
3251 &WeightedSum::new(BTreeMap::from([
3252 (Variable::new("var1"), 1),
3253 (Variable::new("var2"), 2),
3254 ]))
3255 );
3256 assert_eq!(
3257 guard.get_threshold(),
3258 &Threshold::new(BTreeMap::from([(Parameter::new("n"), 3)]), 5,)
3259 );
3260 }
3261
3262 #[test]
3263 fn test_display_lia_transformation_error() {
3264 let error = LIATransformationError::GuardError(
3265 Box::new(RuleBuilder::new(0, Location::new("l1"), Location::new("l2")).build()),
3266 Box::new(BooleanExpression::False),
3267 Box::new(ConstraintRewriteError::NotLinearArithmetic),
3268 );
3269 let expected = "Error while transforming guard false of rule 0: l1 -> l2\n when ( true )\n do { }: Non linear integer arithmetic expression detected. Threshold automata only allow for linear arithmetic formula in their guards.";
3270
3271 assert_eq!(error.to_string(), expected);
3272
3273 let error = LIATransformationError::InitialConstraintError(
3274 Box::new(BooleanExpression::False),
3275 Box::new(ConstraintRewriteError::NotLinearArithmetic),
3276 );
3277 let expected = "Error while transforming initial constraint false into linear arithmetic: Non linear integer arithmetic expression detected. Threshold automata only allow for linear arithmetic formula in their guards.";
3278 assert_eq!(error.to_string(), expected);
3279 }
3280}