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