1use std::{
14 collections::{BTreeSet, HashMap, HashSet},
15 fmt::{Debug, Display},
16};
17
18use taco_display_utils::{
19 display_iterator_stable_order, indent_all, join_iterator, join_iterator_and_add_back,
20};
21
22use crate::{
23 ActionDefinition, BooleanVarConstraint, LocationConstraint, ModifiableThresholdAutomaton,
24 ParameterConstraint, RuleDefinition, ThresholdAutomaton, VariableConstraint,
25 expressions::{
26 BooleanExpression, IntegerExpression, IsDeclared, Location, Parameter, Variable,
27 },
28};
29use std::hash::Hash;
30
31pub mod builder;
32
33#[derive(Debug, Eq, Clone)]
37pub struct GeneralThresholdAutomaton {
38 name: String,
40 parameters: HashSet<Parameter>,
42 variables: HashSet<Variable>,
44 locations: HashSet<Location>,
46 outgoing_rules: HashMap<Location, Vec<Rule>>,
48 initial_location_constraint: Vec<LocationConstraint>,
50 initial_variable_constraint: Vec<BooleanVarConstraint>,
52 resilience_condition: Vec<ParameterConstraint>,
54}
55
56impl GeneralThresholdAutomaton {
57 pub fn to_bymc(&self) -> String {
59 self.to_string()
60 }
61
62 pub fn initial_variable_conditions(&self) -> impl Iterator<Item = &BooleanVarConstraint> {
64 self.initial_variable_constraint.iter()
65 }
66
67 pub fn initial_location_conditions(&self) -> impl Iterator<Item = &LocationConstraint> {
69 self.initial_location_constraint.iter()
70 }
71
72 pub(crate) fn get_rule_map(&self) -> HashMap<Location, Vec<Rule>> {
74 self.outgoing_rules.clone()
75 }
76
77 pub fn get_ta_body_in_bymc_format(&self) -> String {
81 let variables = format!("shared {};", display_iterator_stable_order(&self.variables));
82 let parameters = format!(
83 "parameters {};",
84 display_iterator_stable_order(self.parameters())
85 );
86
87 let rc = format!(
88 "assumptions ({}) {{\n{}}}",
89 self.resilience_condition.len(),
90 indent_all(join_iterator_and_add_back(
91 self.resilience_conditions(),
92 ";\n"
93 ))
94 );
95
96 let mut location_str = String::new();
97 let mut locations = self.locations().collect::<Vec<_>>();
98 locations.sort();
99
100 for (i, loc) in locations.into_iter().enumerate() {
101 location_str += &format!("{loc}:[{i}];\n");
102 }
103 let locations = format!(
104 "locations ({}) {{\n{}}}",
105 self.locations.len(),
106 indent_all(location_str)
107 );
108
109 let initial_cond = format!(
110 "inits ({}) {{\n{}}}",
111 self.initial_location_constraint.len() + self.initial_variable_constraint.len(),
112 indent_all(
113 join_iterator_and_add_back(self.initial_location_constraints(), ";\n")
114 + &join_iterator_and_add_back(self.initial_variable_constraints(), ";\n")
115 )
116 );
117
118 let mut sorted_rules_by_id = self.rules().collect::<Vec<_>>();
119 sorted_rules_by_id.sort_by_key(|r| &r.id);
120 let rules = format!(
121 "rules ({}) {{\n{}}}",
122 self.rules().count(),
123 indent_all(join_iterator_and_add_back(
124 sorted_rules_by_id.into_iter(),
125 ";\n"
126 ))
127 );
128
129 format!("{variables}\n\n{parameters}\n\n{rc}\n\n{locations}\n\n{initial_cond}\n\n{rules}")
130 }
131}
132
133impl ModifiableThresholdAutomaton for GeneralThresholdAutomaton {
134 fn set_name(&mut self, new_name: String) {
135 self.name = new_name;
136 }
137
138 fn add_rule(&mut self, rule: Self::Rule) {
139 self.outgoing_rules
140 .entry(rule.source().clone())
141 .or_default()
142 .push(rule.clone());
143 }
144
145 fn retain_rules<F>(&mut self, predicate: F)
146 where
147 F: Fn(&Self::Rule) -> bool,
148 {
149 self.outgoing_rules
150 .iter_mut()
151 .for_each(|(_, rs)| rs.retain(&predicate));
152 self.outgoing_rules.retain(|_, rs| !rs.is_empty());
153 }
154
155 fn transform_rules<F>(&mut self, mut f: F)
156 where
157 F: FnMut(&mut Self::Rule),
158 {
159 self.outgoing_rules.iter_mut().for_each(|(_, rs)| {
160 rs.iter_mut().for_each(&mut f);
161 });
162 }
163
164 fn remove_location_and_replace_with(
165 &mut self,
166 location: &Location,
167 subst: IntegerExpression<Location>,
168 ) {
169 self.locations.remove(location);
170
171 self.initial_location_constraint.iter_mut().for_each(|ic| {
172 ic.substitute_atom_with(location, &subst);
173 });
174
175 self.retain_rules(|r| r.source() != location && r.target() != location);
176 }
177
178 fn remove_variable_and_replace_with(
179 &mut self,
180 variable: &Variable,
181 subst: IntegerExpression<Variable>,
182 ) {
183 self.variables.remove(variable);
184
185 self.initial_variable_constraint.iter_mut().for_each(|ic| {
186 ic.substitute_atom_with(variable, &subst);
187 });
188
189 self.transform_rules(|r| {
190 r.guard.substitute_atom_with(variable, &subst);
191 r.actions.retain(|act| act.variable() != variable);
192 });
193 }
194
195 fn add_resilience_conditions<C: IntoIterator<Item = BooleanExpression<Parameter>>>(
196 &mut self,
197 conditions: C,
198 ) {
199 self.resilience_condition.extend(conditions);
200 }
201
202 fn add_initial_location_constraints<C: IntoIterator<Item = LocationConstraint>>(
203 &mut self,
204 constraints: C,
205 ) {
206 self.initial_location_constraint.extend(constraints);
207 }
208
209 fn add_initial_variable_constraints<C: IntoIterator<Item = BooleanVarConstraint>>(
210 &mut self,
211 constraints: C,
212 ) {
213 self.initial_variable_constraint.extend(constraints);
214 }
215
216 fn replace_initial_location_constraints<C: IntoIterator<Item = LocationConstraint>>(
217 &mut self,
218 constraints: C,
219 ) {
220 self.initial_location_constraint = constraints.into_iter().collect();
221 }
222
223 fn replace_initial_variable_constraints<C: IntoIterator<Item = BooleanVarConstraint>>(
224 &mut self,
225 constraints: C,
226 ) {
227 self.initial_variable_constraint = constraints.into_iter().collect();
228 }
229}
230
231impl ThresholdAutomaton for GeneralThresholdAutomaton {
232 type Rule = Rule;
233 type InitialVariableConstraintType = BooleanVarConstraint;
234
235 fn name(&self) -> &str {
236 self.name.as_str()
237 }
238
239 fn parameters(&self) -> impl Iterator<Item = &Parameter> {
240 self.parameters.iter()
241 }
242
243 fn resilience_conditions(&self) -> impl Iterator<Item = &BooleanExpression<Parameter>> {
244 self.resilience_condition.iter()
245 }
246
247 fn variables(&self) -> impl Iterator<Item = &Variable> {
248 self.variables.iter()
249 }
250
251 fn locations(&self) -> impl Iterator<Item = &Location> {
252 self.locations.iter()
253 }
254
255 fn can_be_initial_location(&self, loc: &Location) -> bool {
315 !self
316 .initial_location_conditions()
317 .any(|loc_constr| loc_constr.try_check_expr_constraints_to_zero(loc))
318 }
319
320 fn rules(&self) -> impl Iterator<Item = &Self::Rule> {
321 self.outgoing_rules.values().flatten()
322 }
323
324 fn incoming_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
325 let o = self
326 .outgoing_rules
327 .values()
328 .flatten()
329 .filter(|r| r.target == *location)
330 .collect::<HashSet<_>>();
331 o.into_iter()
332 }
333
334 fn outgoing_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
335 self.outgoing_rules.get(location).into_iter().flatten()
336 }
337
338 fn initial_location_constraints(&self) -> impl Iterator<Item = &LocationConstraint> {
339 self.initial_location_constraint.iter()
340 }
341
342 fn initial_variable_constraints(&self) -> impl Iterator<Item = &BooleanVarConstraint> {
343 self.initial_variable_constraint.iter()
344 }
345
346 fn has_decrements_or_resets(&self) -> bool {
347 self.rules().any(|r| r.has_decrements() || r.has_resets())
348 }
349
350 fn has_decrements(&self) -> bool {
351 self.rules().any(|r| r.has_decrements())
352 }
353
354 fn has_resets(&self) -> bool {
355 self.rules().any(|r| r.has_resets())
356 }
357}
358
359impl IsDeclared<Variable> for GeneralThresholdAutomaton {
360 fn is_declared(&self, var: &Variable) -> bool {
361 self.variables.contains(var)
362 }
363}
364
365impl IsDeclared<Location> for GeneralThresholdAutomaton {
366 fn is_declared(&self, loc: &Location) -> bool {
367 self.locations.contains(loc)
368 }
369}
370
371impl IsDeclared<Parameter> for GeneralThresholdAutomaton {
372 fn is_declared(&self, param: &Parameter) -> bool {
373 self.parameters.contains(param)
374 }
375}
376
377impl Display for GeneralThresholdAutomaton {
380 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381 let ta_body = self.get_ta_body_in_bymc_format();
382
383 write!(
384 f,
385 "thresholdAutomaton {} {{\n{}\n}}\n",
386 self.name(),
387 indent_all(ta_body)
388 )
389 }
390}
391
392impl PartialEq for GeneralThresholdAutomaton {
393 fn eq(&self, other: &Self) -> bool {
394 let self_out_rules = self
395 .outgoing_rules
396 .iter()
397 .map(|(l, rs)| {
398 let r_set = rs.iter().collect::<HashSet<_>>();
399 (l, r_set)
400 })
401 .collect::<HashMap<_, HashSet<_>>>();
402 let other_out_rules = other
403 .outgoing_rules
404 .iter()
405 .map(|(l, rs)| {
406 let r_set = rs.iter().collect::<HashSet<_>>();
407 (l, r_set)
408 })
409 .collect::<HashMap<_, HashSet<_>>>();
410
411 let self_init_loc = self
412 .initial_location_constraint
413 .iter()
414 .collect::<HashSet<_>>();
415 let other_init_loc = other
416 .initial_location_constraint
417 .iter()
418 .collect::<HashSet<_>>();
419
420 let self_init_var = self
421 .initial_variable_constraint
422 .iter()
423 .collect::<HashSet<_>>();
424 let other_init_var = other
425 .initial_variable_constraint
426 .iter()
427 .collect::<HashSet<_>>();
428
429 let self_rc = self.resilience_condition.iter().collect::<HashSet<_>>();
430 let other_rc = other.resilience_condition.iter().collect::<HashSet<_>>();
431
432 self.name == other.name
433 && self.parameters == other.parameters
434 && self.variables == other.variables
435 && self.locations == other.locations
436 && self_out_rules == other_out_rules
437 && self_init_loc == other_init_loc
438 && self_init_var == other_init_var
439 && self_rc == other_rc
440 }
441}
442
443#[derive(Debug, Eq, Clone, PartialOrd, Ord)]
447pub struct Rule {
448 id: u32,
450 source: Location,
452 target: Location,
454 guard: BooleanVarConstraint,
456 actions: Vec<Action>,
458}
459
460impl Rule {
461 pub fn transform_guard<F: FnMut(&mut BooleanVarConstraint)>(&mut self, mut t: F) {
466 t(&mut self.guard);
467 }
468
469 pub fn retain_actions<F: Fn(&Action) -> bool>(&mut self, f: F) {
475 self.actions.retain(f);
476 }
477
478 pub fn update_target(&mut self, loc: Location) {
482 self.target = loc;
483 }
484}
485
486impl RuleDefinition for Rule {
487 type Action = Action;
488 type Guard = BooleanVarConstraint;
489
490 fn id(&self) -> u32 {
491 self.id
492 }
493
494 fn source(&self) -> &Location {
495 &self.source
496 }
497
498 fn target(&self) -> &Location {
499 &self.target
500 }
501
502 fn guard(&self) -> &BooleanVarConstraint {
503 &self.guard
504 }
505
506 fn actions(&self) -> impl Iterator<Item = &Self::Action> {
507 self.actions.iter()
508 }
509}
510
511impl Display for Rule {
512 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513 let guard_string = self.guard.to_string();
514 let update_string = join_iterator(self.actions.iter(), "; ");
515
516 let rule_body = indent_all(format!("when ( {guard_string} )\ndo {{ {update_string} }}"));
517
518 write!(
519 f,
520 "{}: {} -> {}\n{}",
521 self.id, self.source, self.target, rule_body
522 )
523 }
524}
525
526impl PartialEq for Rule {
527 fn eq(&self, other: &Self) -> bool {
528 let acts_self = self.actions.iter().collect::<HashSet<_>>();
529 let acts_other = other.actions.iter().collect::<HashSet<_>>();
530
531 self.id == other.id
532 && self.source == other.source
533 && self.target == other.target
534 && self.guard == other.guard
535 && acts_self == acts_other
536 }
537}
538
539impl Hash for Rule {
540 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
541 self.id.hash(state);
542 self.source.hash(state);
543 self.target.hash(state);
544 self.guard.hash(state);
545
546 let acts = self.actions.iter().collect::<BTreeSet<_>>();
547 acts.hash(state);
548 }
549}
550
551#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
556pub struct Action {
557 variable_to_update: Variable,
559 update_expr: UpdateExpression,
561}
562
563#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
568pub enum UpdateExpression {
569 Inc(u32),
571 Dec(u32),
573 Reset,
575 Unchanged,
577}
578
579impl VariableConstraint for BooleanVarConstraint {
580 fn as_boolean_expr(&self) -> BooleanExpression<Variable> {
581 self.clone()
582 }
583}
584
585impl Action {
586 pub fn update(&self) -> &UpdateExpression {
588 &self.update_expr
589 }
590}
591
592impl ActionDefinition for Action {
593 fn variable(&self) -> &Variable {
594 &self.variable_to_update
595 }
596
597 fn is_unchanged(&self) -> bool {
598 match self.update_expr {
599 UpdateExpression::Unchanged => true,
600 UpdateExpression::Inc(_) | UpdateExpression::Dec(_) | UpdateExpression::Reset => false,
601 }
602 }
603
604 fn is_reset(&self) -> bool {
605 match self.update_expr {
606 UpdateExpression::Reset => true,
607 UpdateExpression::Inc(_) | UpdateExpression::Dec(_) | UpdateExpression::Unchanged => {
608 false
609 }
610 }
611 }
612
613 fn is_increment(&self) -> bool {
614 match &self.update_expr {
615 UpdateExpression::Inc(_) => true,
616 UpdateExpression::Reset | UpdateExpression::Dec(_) | UpdateExpression::Unchanged => {
617 false
618 }
619 }
620 }
621
622 fn is_decrement(&self) -> bool {
623 match &self.update_expr {
624 UpdateExpression::Inc(_) | UpdateExpression::Reset | UpdateExpression::Unchanged => {
625 false
626 }
627 UpdateExpression::Dec(_) => true,
628 }
629 }
630}
631
632impl Display for Action {
633 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
634 match self.update_expr {
635 UpdateExpression::Inc(_) | UpdateExpression::Dec(_) => {
636 write!(
637 f,
638 "{}' == {} {}",
639 self.variable_to_update, self.variable_to_update, self.update_expr
640 )
641 }
642 UpdateExpression::Reset => {
643 write!(f, "{}' == {}", self.variable_to_update, self.update_expr)
644 }
645 UpdateExpression::Unchanged => write!(
646 f,
647 "{}' == {}",
648 self.variable_to_update, self.variable_to_update
649 ),
650 }
651 }
652}
653
654impl Display for UpdateExpression {
655 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
656 match self {
657 UpdateExpression::Reset => write!(f, "0"),
658 UpdateExpression::Inc(x) => write!(f, "+ {x}"),
659 UpdateExpression::Dec(x) => write!(f, "- {x}"),
660 UpdateExpression::Unchanged => write!(f, ""),
661 }
662 }
663}
664
665#[cfg(test)]
666mod test {
667
668 use crate::expressions::{ComparisonOp, IntegerExpression};
669 use builder::{GeneralThresholdAutomatonBuilder, RuleBuilder};
670
671 use super::*;
672 use crate::expressions::{BooleanExpression, IntegerOp, Location, Parameter, Variable};
673 use std::{collections::HashMap, vec};
674
675 #[test]
676 fn ta_has_decrements_or_resets() {
677 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
678 .with_locations(vec![
679 Location::new("loc1"),
680 Location::new("loc2"),
681 Location::new("loc3"),
682 ])
683 .unwrap()
684 .with_variable(Variable::new("var1"))
685 .unwrap()
686 .initialize()
687 .with_rules(vec![
688 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
689 .with_guard(BooleanExpression::True)
690 .build(),
691 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
692 .with_guard(BooleanExpression::True)
693 .with_actions(vec![Action::new_reset(Variable::new("var1"))])
694 .build(),
695 ])
696 .unwrap()
697 .build();
698
699 assert!(ta.has_decrements_or_resets());
700 assert!(!ta.has_decrements());
701 assert!(ta.has_resets());
702
703 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
704 .with_locations(vec![
705 Location::new("loc1"),
706 Location::new("loc2"),
707 Location::new("loc3"),
708 ])
709 .unwrap()
710 .with_variable(Variable::new("var1"))
711 .unwrap()
712 .initialize()
713 .with_rules(vec![
714 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
715 .with_guard(BooleanExpression::True)
716 .build(),
717 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
718 .with_guard(BooleanExpression::True)
719 .with_actions(vec![
720 Action::new(
721 Variable::new("var1"),
722 IntegerExpression::Atom(Variable::new("var1"))
723 - IntegerExpression::Const(1),
724 )
725 .unwrap(),
726 ])
727 .build(),
728 ])
729 .unwrap()
730 .build();
731 assert!(ta.has_decrements_or_resets());
732
733 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
734 .with_locations(vec![
735 Location::new("loc1"),
736 Location::new("loc2"),
737 Location::new("loc3"),
738 ])
739 .unwrap()
740 .with_variable(Variable::new("var1"))
741 .unwrap()
742 .initialize()
743 .with_rules(vec![
744 RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
745 .with_guard(BooleanExpression::True)
746 .build(),
747 RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
748 .with_guard(BooleanExpression::True)
749 .with_actions(vec![
750 Action::new(
751 Variable::new("var1"),
752 IntegerExpression::Const(1)
753 + IntegerExpression::Atom(Variable::new("var1")),
754 )
755 .unwrap(),
756 ])
757 .build(),
758 ])
759 .unwrap()
760 .build();
761 assert!(!ta.has_decrements_or_resets());
762 assert!(!ta.has_decrements());
763 assert!(!ta.has_resets());
764 }
765
766 #[test]
767 fn test_ta_getters() {
768 let ta = GeneralThresholdAutomaton {
769 name: "test_ta1".to_string(),
770 parameters: HashSet::from([
771 Parameter::new("n"),
772 Parameter::new("t"),
773 Parameter::new("f"),
774 ]),
775 variables: HashSet::from([
776 Variable::new("var1"),
777 Variable::new("var2"),
778 Variable::new("var3"),
779 ]),
780 locations: HashSet::from([
781 Location::new("loc1"),
782 Location::new("loc2"),
783 Location::new("loc3"),
784 ]),
785 initial_variable_constraint: vec![BooleanVarConstraint::ComparisonExpression(
786 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
787 ComparisonOp::Eq,
788 Box::new(IntegerExpression::Const(1)),
789 )],
790 outgoing_rules: HashMap::from([
791 (
792 Location::new("loc1"),
793 vec![Rule {
794 id: 0,
795 source: Location::new("loc1"),
796 target: Location::new("loc2"),
797 guard: BooleanExpression::True,
798 actions: vec![],
799 }],
800 ),
801 (
802 Location::new("loc2"),
803 vec![Rule {
804 id: 1,
805 source: Location::new("loc2"),
806 target: Location::new("loc3"),
807 guard: 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 actions: vec![
817 Action {
818 variable_to_update: Variable::new("var3"),
819 update_expr: UpdateExpression::Reset,
820 },
821 Action {
822 variable_to_update: Variable::new("var1"),
823 update_expr: UpdateExpression::Inc(1),
824 },
825 ],
826 }],
827 ),
828 ]),
829
830 initial_location_constraint: vec![
831 LocationConstraint::ComparisonExpression(
832 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
833 ComparisonOp::Eq,
834 Box::new(
835 IntegerExpression::Param(Parameter::new("n"))
836 - IntegerExpression::Param(Parameter::new("f")),
837 ),
838 ) | LocationConstraint::ComparisonExpression(
839 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
840 ComparisonOp::Eq,
841 Box::new(IntegerExpression::Const(0)),
842 ),
843 ],
844 resilience_condition: vec![ParameterConstraint::ComparisonExpression(
845 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
846 ComparisonOp::Gt,
847 Box::new(IntegerExpression::BinaryExpr(
848 Box::new(IntegerExpression::Const(3)),
849 IntegerOp::Mul,
850 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
851 )),
852 )],
853 };
854
855 assert_eq!(ta.name(), "test_ta1");
856
857 assert!(
858 [
859 Location::new("loc1"),
860 Location::new("loc2"),
861 Location::new("loc3")
862 ]
863 .iter()
864 .all(|loc| ta.locations().any(|ta_l| ta_l == loc))
865 );
866
867 assert!(
868 [
869 Variable::new("var1"),
870 Variable::new("var2"),
871 Variable::new("var3")
872 ]
873 .iter()
874 .all(|var| ta.variables().any(|ta_v| ta_v == var))
875 );
876
877 assert!(
878 [
879 Parameter::new("n"),
880 Parameter::new("t"),
881 Parameter::new("f")
882 ]
883 .iter()
884 .all(|param| ta.parameters().any(|ta_p| ta_p == param))
885 );
886
887 assert!(
888 [BooleanVarConstraint::ComparisonExpression(
889 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
890 ComparisonOp::Eq,
891 Box::new(IntegerExpression::Const(1)),
892 )]
893 .iter()
894 .all(|var_constr| ta
895 .initial_variable_conditions()
896 .any(|ta_vc| ta_vc == var_constr))
897 );
898
899 assert!(
900 [LocationConstraint::ComparisonExpression(
901 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
902 ComparisonOp::Eq,
903 Box::new(
904 IntegerExpression::Param(Parameter::new("n"))
905 - IntegerExpression::Param(Parameter::new("f")),
906 ),
907 ) | LocationConstraint::ComparisonExpression(
908 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
909 ComparisonOp::Eq,
910 Box::new(IntegerExpression::Const(0)),
911 )]
912 .iter()
913 .all(|loc_constr| ta
914 .initial_location_conditions()
915 .any(|ta_lc| ta_lc == loc_constr))
916 );
917
918 assert!(
919 [ParameterConstraint::ComparisonExpression(
920 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
921 ComparisonOp::Gt,
922 Box::new(IntegerExpression::BinaryExpr(
923 Box::new(IntegerExpression::Const(3)),
924 IntegerOp::Mul,
925 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
926 )),
927 )]
928 .iter()
929 .all(|param_constr| ta
930 .resilience_conditions()
931 .any(|ta_rc| ta_rc == param_constr))
932 );
933
934 assert!(
935 [
936 Rule {
937 id: 0,
938 source: Location::new("loc1"),
939 target: Location::new("loc2"),
940 guard: BooleanExpression::True,
941 actions: vec![],
942 },
943 Rule {
944 id: 1,
945 source: Location::new("loc2"),
946 target: Location::new("loc3"),
947 guard: BooleanVarConstraint::ComparisonExpression(
948 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
949 ComparisonOp::Eq,
950 Box::new(IntegerExpression::Const(1)),
951 ) & BooleanVarConstraint::ComparisonExpression(
952 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
953 ComparisonOp::Eq,
954 Box::new(IntegerExpression::Param(Parameter::new("n"))),
955 ),
956 actions: vec![
957 Action {
958 variable_to_update: Variable::new("var3"),
959 update_expr: UpdateExpression::Reset,
960 },
961 Action {
962 variable_to_update: Variable::new("var1"),
963 update_expr: UpdateExpression::Inc(1),
964 },
965 ],
966 }
967 ]
968 .iter()
969 .all(|rule| ta.rules().any(|ta_r| ta_r == rule))
970 );
971
972 assert_eq!(ta.outgoing_rules(&Location::new("loc1")).count(), 1);
973 assert!(ta.outgoing_rules(&Location::new("loc1")).any(|r| *r
974 == Rule {
975 id: 0,
976 source: Location::new("loc1"),
977 target: Location::new("loc2"),
978 guard: BooleanExpression::True,
979 actions: vec![],
980 }));
981
982 assert_eq!(ta.resilience_conditions().count(), 1);
983 assert!(ta.resilience_conditions().any(|r| *r
984 == ParameterConstraint::ComparisonExpression(
985 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
986 ComparisonOp::Gt,
987 Box::new(IntegerExpression::BinaryExpr(
988 Box::new(IntegerExpression::Const(3)),
989 IntegerOp::Mul,
990 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
991 )),
992 )));
993
994 assert_eq!(ta.incoming_rules(&Location::new("loc2")).count(), 1);
995 assert!(ta.incoming_rules(&Location::new("loc2")).any(|r| *r
996 == Rule {
997 id: 0,
998 source: Location::new("loc1"),
999 target: Location::new("loc2"),
1000 guard: BooleanExpression::True,
1001 actions: vec![],
1002 }));
1003
1004 assert!(ta.is_declared(&Variable::new("var1")));
1005 assert!(ta.is_declared(&Variable::new("var2")));
1006 assert!(ta.is_declared(&Variable::new("var3")));
1007 }
1008
1009 #[test]
1010 fn test_ta_remove_rule() {
1011 let mut ta = GeneralThresholdAutomaton {
1012 name: "test_ta1".to_string(),
1013 parameters: HashSet::from([
1014 Parameter::new("n"),
1015 Parameter::new("t"),
1016 Parameter::new("f"),
1017 ]),
1018 variables: HashSet::from([
1019 Variable::new("var1"),
1020 Variable::new("var2"),
1021 Variable::new("var3"),
1022 ]),
1023 locations: HashSet::from([
1024 Location::new("loc1"),
1025 Location::new("loc2"),
1026 Location::new("loc3"),
1027 ]),
1028 initial_variable_constraint: vec![BooleanVarConstraint::ComparisonExpression(
1029 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1030 ComparisonOp::Eq,
1031 Box::new(IntegerExpression::Const(1)),
1032 )],
1033 outgoing_rules: HashMap::from([
1034 (
1035 Location::new("loc1"),
1036 vec![Rule {
1037 id: 0,
1038 source: Location::new("loc1"),
1039 target: Location::new("loc2"),
1040 guard: BooleanExpression::True,
1041 actions: vec![],
1042 }],
1043 ),
1044 (
1045 Location::new("loc2"),
1046 vec![Rule {
1047 id: 1,
1048 source: Location::new("loc2"),
1049 target: Location::new("loc3"),
1050 guard: BooleanVarConstraint::ComparisonExpression(
1051 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1052 ComparisonOp::Eq,
1053 Box::new(IntegerExpression::Const(1)),
1054 ) & BooleanVarConstraint::ComparisonExpression(
1055 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1056 ComparisonOp::Eq,
1057 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1058 ),
1059 actions: vec![
1060 Action {
1061 variable_to_update: Variable::new("var3"),
1062 update_expr: UpdateExpression::Reset,
1063 },
1064 Action {
1065 variable_to_update: Variable::new("var1"),
1066 update_expr: UpdateExpression::Inc(1),
1067 },
1068 ],
1069 }],
1070 ),
1071 (
1072 Location::new("loc3"),
1073 vec![Rule {
1074 id: 3,
1075 source: Location::new("loc3"),
1076 target: Location::new("loc2"),
1077 guard: BooleanExpression::True,
1078 actions: vec![],
1079 }],
1080 ),
1081 ]),
1082
1083 initial_location_constraint: vec![
1084 LocationConstraint::ComparisonExpression(
1085 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1086 ComparisonOp::Eq,
1087 Box::new(
1088 IntegerExpression::Param(Parameter::new("n"))
1089 - IntegerExpression::Param(Parameter::new("f")),
1090 ),
1091 ) | LocationConstraint::ComparisonExpression(
1092 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1093 ComparisonOp::Eq,
1094 Box::new(IntegerExpression::Const(0)),
1095 ),
1096 ],
1097 resilience_condition: vec![ParameterConstraint::ComparisonExpression(
1098 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1099 ComparisonOp::Gt,
1100 Box::new(IntegerExpression::BinaryExpr(
1101 Box::new(IntegerExpression::Const(3)),
1102 IntegerOp::Mul,
1103 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
1104 )),
1105 )],
1106 };
1107
1108 let rule = Rule {
1109 id: 0,
1110 source: Location::new("loc1"),
1111 target: Location::new("loc2"),
1112 guard: BooleanExpression::True,
1113 actions: vec![],
1114 };
1115
1116 ta.retain_rules(|r| *r != rule);
1117
1118 let expected_ta = GeneralThresholdAutomaton {
1119 name: "test_ta1".to_string(),
1120 parameters: HashSet::from([
1121 Parameter::new("n"),
1122 Parameter::new("t"),
1123 Parameter::new("f"),
1124 ]),
1125 variables: HashSet::from([
1126 Variable::new("var1"),
1127 Variable::new("var2"),
1128 Variable::new("var3"),
1129 ]),
1130 locations: HashSet::from([
1131 Location::new("loc1"),
1132 Location::new("loc2"),
1133 Location::new("loc3"),
1134 ]),
1135 initial_variable_constraint: vec![BooleanVarConstraint::ComparisonExpression(
1136 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1137 ComparisonOp::Eq,
1138 Box::new(IntegerExpression::Const(1)),
1139 )],
1140 outgoing_rules: HashMap::from([
1141 (
1142 Location::new("loc2"),
1143 vec![Rule {
1144 id: 1,
1145 source: Location::new("loc2"),
1146 target: Location::new("loc3"),
1147 guard: BooleanVarConstraint::ComparisonExpression(
1148 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1149 ComparisonOp::Eq,
1150 Box::new(IntegerExpression::Const(1)),
1151 ) & BooleanVarConstraint::ComparisonExpression(
1152 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1153 ComparisonOp::Eq,
1154 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1155 ),
1156 actions: vec![
1157 Action {
1158 variable_to_update: Variable::new("var3"),
1159 update_expr: UpdateExpression::Reset,
1160 },
1161 Action {
1162 variable_to_update: Variable::new("var1"),
1163 update_expr: UpdateExpression::Inc(1),
1164 },
1165 ],
1166 }],
1167 ),
1168 (
1169 Location::new("loc3"),
1170 vec![Rule {
1171 id: 3,
1172 source: Location::new("loc3"),
1173 target: Location::new("loc2"),
1174 guard: BooleanExpression::True,
1175 actions: vec![],
1176 }],
1177 ),
1178 ]),
1179
1180 initial_location_constraint: vec![
1181 LocationConstraint::ComparisonExpression(
1182 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1183 ComparisonOp::Eq,
1184 Box::new(
1185 IntegerExpression::Param(Parameter::new("n"))
1186 - IntegerExpression::Param(Parameter::new("f")),
1187 ),
1188 ) | LocationConstraint::ComparisonExpression(
1189 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1190 ComparisonOp::Eq,
1191 Box::new(IntegerExpression::Const(0)),
1192 ),
1193 ],
1194 resilience_condition: vec![ParameterConstraint::ComparisonExpression(
1195 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1196 ComparisonOp::Gt,
1197 Box::new(IntegerExpression::BinaryExpr(
1198 Box::new(IntegerExpression::Const(3)),
1199 IntegerOp::Mul,
1200 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
1201 )),
1202 )],
1203 };
1204
1205 assert_eq!(ta, expected_ta);
1206 }
1207
1208 #[test]
1209 fn test_ta_remove_location() {
1210 let mut ta = GeneralThresholdAutomaton {
1211 name: "test_ta1".to_string(),
1212 parameters: HashSet::from([
1213 Parameter::new("n"),
1214 Parameter::new("t"),
1215 Parameter::new("f"),
1216 ]),
1217 variables: HashSet::from([
1218 Variable::new("var1"),
1219 Variable::new("var2"),
1220 Variable::new("var3"),
1221 ]),
1222 locations: HashSet::from([
1223 Location::new("loc1"),
1224 Location::new("loc2"),
1225 Location::new("loc3"),
1226 ]),
1227 initial_variable_constraint: vec![BooleanVarConstraint::ComparisonExpression(
1228 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1229 ComparisonOp::Eq,
1230 Box::new(IntegerExpression::Const(1)),
1231 )],
1232 outgoing_rules: HashMap::from([
1233 (
1234 Location::new("loc1"),
1235 vec![Rule {
1236 id: 0,
1237 source: Location::new("loc1"),
1238 target: Location::new("loc2"),
1239 guard: BooleanExpression::True,
1240 actions: vec![],
1241 }],
1242 ),
1243 (
1244 Location::new("loc2"),
1245 vec![Rule {
1246 id: 1,
1247 source: Location::new("loc2"),
1248 target: Location::new("loc3"),
1249 guard: BooleanVarConstraint::ComparisonExpression(
1250 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1251 ComparisonOp::Eq,
1252 Box::new(IntegerExpression::Const(1)),
1253 ) & BooleanVarConstraint::ComparisonExpression(
1254 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1255 ComparisonOp::Eq,
1256 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1257 ),
1258 actions: vec![
1259 Action {
1260 variable_to_update: Variable::new("var3"),
1261 update_expr: UpdateExpression::Reset,
1262 },
1263 Action {
1264 variable_to_update: Variable::new("var1"),
1265 update_expr: UpdateExpression::Inc(1),
1266 },
1267 ],
1268 }],
1269 ),
1270 (
1271 Location::new("loc3"),
1272 vec![Rule {
1273 id: 3,
1274 source: Location::new("loc3"),
1275 target: Location::new("loc2"),
1276 guard: BooleanExpression::True,
1277 actions: vec![],
1278 }],
1279 ),
1280 ]),
1281
1282 initial_location_constraint: vec![
1283 LocationConstraint::ComparisonExpression(
1284 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1285 ComparisonOp::Eq,
1286 Box::new(
1287 IntegerExpression::Param(Parameter::new("n"))
1288 - IntegerExpression::Param(Parameter::new("f")),
1289 ),
1290 ) | LocationConstraint::ComparisonExpression(
1291 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1292 ComparisonOp::Eq,
1293 Box::new(IntegerExpression::Const(0)),
1294 ),
1295 ],
1296 resilience_condition: vec![ParameterConstraint::ComparisonExpression(
1297 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1298 ComparisonOp::Gt,
1299 Box::new(IntegerExpression::BinaryExpr(
1300 Box::new(IntegerExpression::Const(3)),
1301 IntegerOp::Mul,
1302 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
1303 )),
1304 )],
1305 };
1306
1307 ta.remove_location_and_replace_with(&Location::new("loc3"), IntegerExpression::Const(0));
1308
1309 let expected_ta = GeneralThresholdAutomaton {
1310 name: "test_ta1".to_string(),
1311 parameters: HashSet::from([
1312 Parameter::new("n"),
1313 Parameter::new("t"),
1314 Parameter::new("f"),
1315 ]),
1316 variables: HashSet::from([
1317 Variable::new("var1"),
1318 Variable::new("var2"),
1319 Variable::new("var3"),
1320 ]),
1321 locations: HashSet::from([Location::new("loc1"), Location::new("loc2")]),
1322 initial_variable_constraint: vec![BooleanVarConstraint::ComparisonExpression(
1323 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1324 ComparisonOp::Eq,
1325 Box::new(IntegerExpression::Const(1)),
1326 )],
1327 outgoing_rules: HashMap::from([(
1328 Location::new("loc1"),
1329 vec![Rule {
1330 id: 0,
1331 source: Location::new("loc1"),
1332 target: Location::new("loc2"),
1333 guard: BooleanExpression::True,
1334 actions: vec![],
1335 }],
1336 )]),
1337
1338 initial_location_constraint: vec![
1339 LocationConstraint::ComparisonExpression(
1340 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1341 ComparisonOp::Eq,
1342 Box::new(
1343 IntegerExpression::Param(Parameter::new("n"))
1344 - IntegerExpression::Param(Parameter::new("f")),
1345 ),
1346 ) | LocationConstraint::ComparisonExpression(
1347 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1348 ComparisonOp::Eq,
1349 Box::new(IntegerExpression::Const(0)),
1350 ),
1351 ],
1352 resilience_condition: vec![ParameterConstraint::ComparisonExpression(
1353 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1354 ComparisonOp::Gt,
1355 Box::new(IntegerExpression::BinaryExpr(
1356 Box::new(IntegerExpression::Const(3)),
1357 IntegerOp::Mul,
1358 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
1359 )),
1360 )],
1361 };
1362
1363 assert_eq!(ta, expected_ta,);
1364 }
1365
1366 #[test]
1367 fn test_can_be_initial_location() {
1368 let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1369 .with_locations(vec![
1370 Location::new("loc1"),
1371 Location::new("loc2"),
1372 Location::new("loc3"),
1373 Location::new("loc4"),
1374 Location::new("loc5"),
1375 Location::new("loc6"),
1376 ])
1377 .unwrap()
1378 .initialize()
1379 .with_initial_location_constraints(vec![
1380 LocationConstraint::ComparisonExpression(
1381 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1382 ComparisonOp::Eq,
1383 Box::new(IntegerExpression::Const(0)),
1384 ),
1385 LocationConstraint::ComparisonExpression(
1386 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1387 ComparisonOp::Eq,
1388 Box::new(IntegerExpression::Const(1) + -IntegerExpression::Const(1)),
1389 ),
1390 LocationConstraint::ComparisonExpression(
1391 Box::new(IntegerExpression::Atom(Location::new("loc4"))),
1392 ComparisonOp::Eq,
1393 Box::new(IntegerExpression::Const(1) * IntegerExpression::Const(0)),
1394 ) | LocationConstraint::ComparisonExpression(
1395 Box::new(IntegerExpression::Atom(Location::new("loc4"))),
1396 ComparisonOp::Eq,
1397 Box::new(IntegerExpression::Const(0)),
1398 ),
1399 LocationConstraint::ComparisonExpression(
1400 Box::new(IntegerExpression::Atom(Location::new("loc5"))),
1401 ComparisonOp::Eq,
1402 Box::new(IntegerExpression::Const(0)),
1403 ) & LocationConstraint::ComparisonExpression(
1404 Box::new(IntegerExpression::Atom(Location::new("loc5"))),
1405 ComparisonOp::Eq,
1406 Box::new(IntegerExpression::Const(1)),
1407 ),
1408 LocationConstraint::True,
1409 LocationConstraint::False,
1410 !LocationConstraint::ComparisonExpression(
1411 Box::new(IntegerExpression::Atom(Location::new("loc6"))),
1412 ComparisonOp::Eq,
1413 Box::new(IntegerExpression::Const(0)),
1414 ),
1415 LocationConstraint::ComparisonExpression(
1416 Box::new(IntegerExpression::Const(0)),
1417 ComparisonOp::Eq,
1418 Box::new(IntegerExpression::Const(0)),
1419 ),
1420 ])
1421 .unwrap()
1422 .build();
1423
1424 assert!(!ta.can_be_initial_location(&Location::new("loc1")));
1425 assert!(!ta.can_be_initial_location(&Location::new("loc2")));
1426 assert!(ta.can_be_initial_location(&Location::new("loc3")));
1427 assert!(!ta.can_be_initial_location(&Location::new("loc4")));
1428 assert!(!ta.can_be_initial_location(&Location::new("loc5")));
1429 assert!(ta.can_be_initial_location(&Location::new("loc6")));
1430 }
1431
1432 #[test]
1433 fn test_display_ta() {
1434 let ta = GeneralThresholdAutomaton {
1435 name: "test_ta1".to_string(),
1436 parameters: HashSet::from([
1437 Parameter::new("n"),
1438 Parameter::new("t"),
1439 Parameter::new("f"),
1440 ]),
1441 variables: HashSet::from([
1442 Variable::new("var1"),
1443 Variable::new("var2"),
1444 Variable::new("var3"),
1445 ]),
1446 locations: HashSet::from([
1447 Location::new("loc1"),
1448 Location::new("loc2"),
1449 Location::new("loc3"),
1450 ]),
1451 initial_variable_constraint: vec![BooleanVarConstraint::ComparisonExpression(
1452 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1453 ComparisonOp::Eq,
1454 Box::new(IntegerExpression::Const(1)),
1455 )],
1456 outgoing_rules: HashMap::from([
1457 (
1458 Location::new("loc1"),
1459 vec![Rule {
1460 id: 0,
1461 source: Location::new("loc1"),
1462 target: Location::new("loc2"),
1463 guard: BooleanExpression::True,
1464 actions: vec![
1465 Action::new(
1466 Variable::new("var1"),
1467 IntegerExpression::Atom(Variable::new("var1")),
1468 )
1469 .unwrap(),
1470 ],
1471 }],
1472 ),
1473 (
1474 Location::new("loc2"),
1475 vec![Rule {
1476 id: 1,
1477 source: Location::new("loc2"),
1478 target: Location::new("loc3"),
1479 guard: BooleanVarConstraint::ComparisonExpression(
1480 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1481 ComparisonOp::Eq,
1482 Box::new(IntegerExpression::Const(1)),
1483 ) & BooleanVarConstraint::ComparisonExpression(
1484 Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1485 ComparisonOp::Eq,
1486 Box::new(IntegerExpression::Param(Parameter::new("n"))),
1487 ),
1488 actions: vec![
1489 Action {
1490 variable_to_update: Variable::new("var3"),
1491 update_expr: UpdateExpression::Reset,
1492 },
1493 Action {
1494 variable_to_update: Variable::new("var1"),
1495 update_expr: UpdateExpression::Inc(1),
1496 },
1497 ],
1498 }],
1499 ),
1500 ]),
1501
1502 initial_location_constraint: vec![
1503 LocationConstraint::ComparisonExpression(
1504 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1505 ComparisonOp::Eq,
1506 Box::new(
1507 IntegerExpression::Param(Parameter::new("n"))
1508 - IntegerExpression::Param(Parameter::new("f")),
1509 ),
1510 ) | LocationConstraint::ComparisonExpression(
1511 Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1512 ComparisonOp::Eq,
1513 Box::new(IntegerExpression::Const(0)),
1514 ),
1515 ],
1516
1517 resilience_condition: vec![ParameterConstraint::ComparisonExpression(
1518 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1519 ComparisonOp::Gt,
1520 Box::new(IntegerExpression::BinaryExpr(
1521 Box::new(IntegerExpression::Const(3)),
1522 IntegerOp::Mul,
1523 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
1524 )),
1525 )],
1526 };
1527 let expected_ta_string = "thresholdAutomaton test_ta1 {
1528 shared var1, var2, var3;
1529
1530 parameters f, n, t;
1531
1532 assumptions (1) {
1533 n > (3 * f);
1534 }
1535
1536 locations (3) {
1537 loc1:[0];
1538 loc2:[1];
1539 loc3:[2];
1540 }
1541
1542 inits (2) {
1543 (loc1 == (n - f) || loc2 == 0);
1544 var1 == 1;
1545 }
1546
1547 rules (2) {
1548 0: loc1 -> loc2
1549 when ( true )
1550 do { var1' == var1 };
1551 1: loc2 -> loc3
1552 when ( (var1 == 1 && var2 == n) )
1553 do { var3' == 0; var1' == var1 + 1 };
1554 }
1555}
1556";
1557
1558 assert_eq!(ta.to_bymc(), expected_ta_string);
1559 assert_eq!(ta.to_string(), expected_ta_string);
1560 }
1561
1562 #[test]
1563 fn test_rule_getters() {
1564 let rule = Rule {
1565 id: 0,
1566 source: Location::new("loc1"),
1567 target: Location::new("loc2"),
1568 guard: BooleanExpression::True,
1569 actions: vec![],
1570 };
1571
1572 assert_eq!(rule.id(), 0);
1573 assert_eq!(rule.source(), &Location::new("loc1"));
1574 assert_eq!(rule.target(), &Location::new("loc2"));
1575 assert_eq!(rule.guard(), &BooleanExpression::True);
1576 assert_eq!(rule.actions().cloned().collect::<Vec<Action>>(), vec![]);
1577
1578 assert_eq!(rule.guard().as_boolean_expr(), BooleanExpression::True);
1579 }
1580
1581 #[test]
1582 fn test_rule_has_decrements_or_resets() {
1583 let rule = Rule {
1584 id: 0,
1585 source: Location::new("loc1"),
1586 target: Location::new("loc2"),
1587 guard: BooleanExpression::True,
1588 actions: vec![],
1589 };
1590 assert!(!rule.has_resets());
1591 assert!(!rule.has_decrements());
1592
1593 let rule = Rule {
1594 id: 0,
1595 source: Location::new("loc1"),
1596 target: Location::new("loc2"),
1597 guard: BooleanExpression::True,
1598 actions: vec![Action::new_reset(Variable::new("var1"))],
1599 };
1600 assert!(rule.has_resets());
1601 assert!(!rule.has_decrements());
1602
1603 let rule = Rule {
1604 id: 0,
1605 source: Location::new("loc1"),
1606 target: Location::new("loc2"),
1607 guard: BooleanExpression::True,
1608 actions: vec![
1609 Action::new(
1610 Variable::new("var1"),
1611 IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1),
1612 )
1613 .unwrap(),
1614 ],
1615 };
1616 assert!(!rule.has_resets());
1617 assert!(rule.has_decrements());
1618
1619 let rule = Rule {
1620 id: 0,
1621 source: Location::new("loc1"),
1622 target: Location::new("loc2"),
1623 guard: BooleanExpression::True,
1624 actions: vec![
1625 Action::new(
1626 Variable::new("var1"),
1627 IntegerExpression::Const(1) + IntegerExpression::Atom(Variable::new("var1")),
1628 )
1629 .unwrap(),
1630 Action::new(
1631 Variable::new("var2"),
1632 IntegerExpression::Const(1) + IntegerExpression::Atom(Variable::new("var2")),
1633 )
1634 .unwrap(),
1635 ],
1636 };
1637 assert!(!rule.has_resets());
1638 assert!(!rule.has_decrements());
1639
1640 let rule = Rule {
1641 id: 0,
1642 source: Location::new("loc1"),
1643 target: Location::new("loc2"),
1644 guard: BooleanExpression::True,
1645 actions: vec![
1646 Action::new_reset(Variable::new("var1")),
1647 Action::new(
1648 Variable::new("var1"),
1649 IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1),
1650 )
1651 .unwrap(),
1652 ],
1653 };
1654 assert!(rule.has_resets());
1655 assert!(rule.has_decrements());
1656 }
1657
1658 #[test]
1659 fn test_action_getters() {
1660 let act = Action {
1661 variable_to_update: Variable::new("var1"),
1662 update_expr: UpdateExpression::Inc(1),
1663 };
1664
1665 assert_eq!(act.variable(), &Variable::new("var1"));
1666 assert_eq!(act.update(), &UpdateExpression::Inc(1));
1667 }
1668
1669 #[test]
1670 fn test_action_is_reset() {
1671 let act = Action {
1672 variable_to_update: Variable::new("var1"),
1673 update_expr: UpdateExpression::Reset,
1674 };
1675 assert!(act.is_reset());
1676
1677 let act = Action {
1678 variable_to_update: Variable::new("var1"),
1679 update_expr: UpdateExpression::Inc(1),
1680 };
1681 assert!(!act.is_reset());
1682
1683 let act = Action {
1684 variable_to_update: Variable::new("var1"),
1685 update_expr: UpdateExpression::Dec(1),
1686 };
1687 assert!(!act.is_reset());
1688
1689 let act = Action {
1690 variable_to_update: Variable::new("var1"),
1691 update_expr: UpdateExpression::Unchanged,
1692 };
1693 assert!(!act.is_reset());
1694
1695 let act = Action::new_reset(Variable::new("var1"));
1696 assert!(act.is_reset());
1697
1698 let act = Action::new(
1699 Variable::new("var1"),
1700 IntegerExpression::Const(1) + IntegerExpression::Atom(Variable::new("var1")),
1701 )
1702 .unwrap();
1703 assert!(!act.is_reset());
1704
1705 let act = Action::new(
1706 Variable::new("var1"),
1707 IntegerExpression::Const(5) + -IntegerExpression::Const(5),
1708 )
1709 .unwrap();
1710 assert!(act.is_reset());
1711 }
1712
1713 #[test]
1714 fn test_act_is_decrement() {
1715 let act = Action {
1716 variable_to_update: Variable::new("var1"),
1717 update_expr: UpdateExpression::Dec(1),
1718 };
1719 assert!(act.is_decrement());
1720
1721 let act = Action {
1722 variable_to_update: Variable::new("var1"),
1723 update_expr: UpdateExpression::Inc(1),
1724 };
1725 assert!(!act.is_decrement());
1726
1727 let act = Action {
1728 variable_to_update: Variable::new("var1"),
1729 update_expr: UpdateExpression::Reset,
1730 };
1731 assert!(!act.is_decrement());
1732
1733 let act = Action {
1734 variable_to_update: Variable::new("var1"),
1735 update_expr: UpdateExpression::Unchanged,
1736 };
1737 assert!(!act.is_decrement());
1738
1739 let act = Action::new(
1740 Variable::new("var1"),
1741 IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1),
1742 )
1743 .unwrap();
1744 assert!(act.is_decrement());
1745
1746 let act = Action::new(
1747 Variable::new("var1"),
1748 IntegerExpression::Const(1) + IntegerExpression::Atom(Variable::new("var1")),
1749 )
1750 .unwrap();
1751 assert!(!act.is_decrement());
1752
1753 let act = Action::new(
1754 Variable::new("var1"),
1755 -IntegerExpression::Const(5) + IntegerExpression::Atom(Variable::new("var1")),
1756 )
1757 .unwrap();
1758 assert!(act.is_decrement());
1759 }
1760
1761 #[test]
1762 fn test_act_is_unchanged() {
1763 let act = Action {
1764 variable_to_update: Variable::new("var1"),
1765 update_expr: UpdateExpression::Unchanged,
1766 };
1767 assert!(act.is_unchanged());
1768
1769 let act = Action {
1770 variable_to_update: Variable::new("var1"),
1771 update_expr: UpdateExpression::Inc(1),
1772 };
1773 assert!(!act.is_unchanged());
1774
1775 let act = Action {
1776 variable_to_update: Variable::new("var1"),
1777 update_expr: UpdateExpression::Dec(1),
1778 };
1779 assert!(!act.is_unchanged());
1780
1781 let act = Action {
1782 variable_to_update: Variable::new("var1"),
1783 update_expr: UpdateExpression::Reset,
1784 };
1785 assert!(!act.is_unchanged());
1786
1787 let act = Action::new(
1788 Variable::new("var1"),
1789 IntegerExpression::Atom(Variable::new("var1")),
1790 )
1791 .unwrap();
1792 assert!(act.is_unchanged());
1793
1794 let act = Action::new(
1795 Variable::new("var1"),
1796 IntegerExpression::Const(1) + IntegerExpression::Atom(Variable::new("var1")),
1797 )
1798 .unwrap();
1799 assert!(!act.is_unchanged());
1800
1801 let act = Action::new(
1802 Variable::new("var1"),
1803 -IntegerExpression::Const(5) + IntegerExpression::Atom(Variable::new("var1")),
1804 )
1805 .unwrap();
1806 assert!(!act.is_unchanged());
1807 }
1808
1809 #[test]
1810 fn test_act_is_increment() {
1811 let act = Action {
1812 variable_to_update: Variable::new("var1"),
1813 update_expr: UpdateExpression::Inc(1),
1814 };
1815 assert!(act.is_increment());
1816
1817 let act = Action {
1818 variable_to_update: Variable::new("var1"),
1819 update_expr: UpdateExpression::Dec(1),
1820 };
1821 assert!(!act.is_increment());
1822
1823 let act = Action {
1824 variable_to_update: Variable::new("var1"),
1825 update_expr: UpdateExpression::Reset,
1826 };
1827 assert!(!act.is_increment());
1828
1829 let act = Action {
1830 variable_to_update: Variable::new("var1"),
1831 update_expr: UpdateExpression::Unchanged,
1832 };
1833 assert!(!act.is_increment());
1834
1835 let act = Action::new(
1836 Variable::new("var1"),
1837 IntegerExpression::Const(1) + IntegerExpression::Atom(Variable::new("var1")),
1838 )
1839 .unwrap();
1840 assert!(act.is_increment());
1841
1842 let act = Action::new(
1843 Variable::new("var1"),
1844 IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1),
1845 )
1846 .unwrap();
1847 assert!(!act.is_increment());
1848
1849 let act = Action::new(
1850 Variable::new("var1"),
1851 IntegerExpression::Atom(Variable::new("var1")) + IntegerExpression::Const(5),
1852 )
1853 .unwrap();
1854 assert!(act.is_increment());
1855 }
1856
1857 #[test]
1858 fn test_update_expr_display() {
1859 let inc_expr = UpdateExpression::Inc(1);
1860 let dec_expr = UpdateExpression::Dec(1);
1861 let reset_expr = UpdateExpression::Reset;
1862 let unchanged_expr = UpdateExpression::Unchanged;
1863
1864 assert_eq!(inc_expr.to_string(), "+ 1");
1865 assert_eq!(dec_expr.to_string(), "- 1");
1866 assert_eq!(reset_expr.to_string(), "0");
1867 assert_eq!(unchanged_expr.to_string(), "");
1868 }
1869
1870 #[test]
1871 fn test_set_name() {
1872 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1873 .initialize()
1874 .build();
1875 ta.set_name("new_ta".to_string());
1876
1877 assert_eq!(ta.name(), "new_ta")
1878 }
1879
1880 #[test]
1881 fn test_add_rule() {
1882 let r = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
1883 .with_guard(BooleanExpression::True)
1884 .build();
1885
1886 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1887 .with_locations(vec![Location::new("loc1"), Location::new("loc2")])
1888 .unwrap()
1889 .initialize()
1890 .with_initial_location_constraints(vec![])
1891 .unwrap()
1892 .build();
1893
1894 ta.add_rule(r);
1895
1896 assert!(ta.outgoing_rules.contains_key(&Location::new("loc1")));
1897 assert!(
1898 ta.outgoing_rules[&Location::new("loc1")]
1899 .iter()
1900 .any(|rule| rule.id == 0)
1901 );
1902 }
1903
1904 #[test]
1905 fn test_add_resilience_condition() {
1906 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1907 .with_locations(vec![Location::new("loc1"), Location::new("loc2")])
1908 .unwrap()
1909 .with_parameters(vec![
1910 Parameter::new("n"),
1911 Parameter::new("t"),
1912 Parameter::new("f"),
1913 ])
1914 .unwrap()
1915 .initialize()
1916 .with_initial_location_constraints(vec![])
1917 .unwrap()
1918 .build();
1919
1920 let condition = ParameterConstraint::ComparisonExpression(
1921 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1922 ComparisonOp::Gt,
1923 Box::new(IntegerExpression::Const(0)),
1924 );
1925
1926 ta.add_resilience_conditions([condition.clone()]);
1927
1928 assert_eq!(ta.resilience_condition.len(), 1);
1929 assert_eq!(ta.resilience_condition[0], condition);
1930 }
1931
1932 #[test]
1933 fn test_add_initial_location_constraint() {
1934 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1935 .with_locations(vec![Location::new("loc1"), Location::new("loc2")])
1936 .unwrap()
1937 .initialize()
1938 .with_initial_location_constraints(vec![])
1939 .unwrap()
1940 .build();
1941
1942 let constraint = LocationConstraint::ComparisonExpression(
1943 Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1944 ComparisonOp::Eq,
1945 Box::new(IntegerExpression::Const(0)),
1946 );
1947
1948 ta.add_initial_location_constraints([constraint.clone()]);
1949
1950 assert_eq!(ta.initial_location_constraint.len(), 1);
1951 assert_eq!(ta.initial_location_constraint[0], constraint);
1952 }
1953
1954 #[test]
1955 fn test_add_initial_variable_constraint() {
1956 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1957 .with_locations(vec![Location::new("loc1"), Location::new("loc2")])
1958 .unwrap()
1959 .with_variables(vec![
1960 Variable::new("var1"),
1961 Variable::new("var2"),
1962 Variable::new("var3"),
1963 ])
1964 .unwrap()
1965 .initialize()
1966 .with_initial_variable_constraints(vec![])
1967 .unwrap()
1968 .build();
1969
1970 let constraint = BooleanVarConstraint::ComparisonExpression(
1971 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1972 ComparisonOp::Eq,
1973 Box::new(IntegerExpression::Const(0)),
1974 );
1975
1976 ta.add_initial_variable_constraints([constraint.clone()]);
1977
1978 assert_eq!(ta.initial_variable_constraint.len(), 1);
1979 assert_eq!(ta.initial_variable_constraint[0], constraint);
1980 }
1981
1982 #[test]
1983 fn test_retain_rule() {
1984 let r0 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
1985 .with_guard(BooleanExpression::True)
1986 .build();
1987
1988 let r1 = Rule {
1989 id: 1,
1990 source: Location::new("loc2"),
1991 target: Location::new("loc1"),
1992 guard: BooleanExpression::True,
1993 actions: vec![],
1994 };
1995
1996 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1997 .with_locations(vec![Location::new("loc1"), Location::new("loc2")])
1998 .unwrap()
1999 .initialize()
2000 .with_rules([r0.clone(), r1.clone()])
2001 .unwrap()
2002 .build();
2003
2004 ta.retain_rules(|rule| *rule != r0);
2005
2006 assert_eq!(ta.rules().count(), 1);
2007 assert!(!ta.rules().any(|r| *r == r0));
2008 assert!(ta.rules().any(|r| *r == r1));
2009 }
2010
2011 #[test]
2012 fn test_transform_rules() {
2013 let r0 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
2014 .with_guard(BooleanExpression::True)
2015 .build();
2016
2017 let r1 = Rule {
2018 id: 1,
2019 source: Location::new("loc2"),
2020 target: Location::new("loc1"),
2021 guard: BooleanExpression::True,
2022 actions: vec![],
2023 };
2024
2025 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
2026 .with_locations(vec![
2027 Location::new("loc1"),
2028 Location::new("loc2"),
2029 Location::new("loc3"),
2030 Location::new("loc4"),
2031 ])
2032 .unwrap()
2033 .initialize()
2034 .with_rules([r0.clone(), r1.clone()])
2035 .unwrap()
2036 .build();
2037
2038 ta.transform_rules(|rule| {
2039 if rule.id == 0 {
2040 rule.source = Location::new("loc3");
2041 rule.target = Location::new("loc4");
2042 rule.id = 2;
2043 }
2044 });
2045
2046 let r2 = Rule {
2047 id: 2,
2048 source: Location::new("loc3"),
2049 target: Location::new("loc4"),
2050 guard: BooleanExpression::True,
2051 actions: vec![],
2052 };
2053
2054 assert_eq!(ta.rules().count(), 2);
2055 assert!(ta.rules().any(|r| *r == r1));
2056 assert!(ta.rules().any(|r| *r == r2));
2057 assert!(!ta.rules().any(|r| *r == r0));
2058 }
2059
2060 #[test]
2061 fn test_remove_location() {
2062 let r0 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
2063 .with_guard(BooleanExpression::True)
2064 .build();
2065
2066 let r1 = Rule {
2067 id: 1,
2068 source: Location::new("loc3"),
2069 target: Location::new("loc4"),
2070 guard: BooleanExpression::True,
2071 actions: vec![],
2072 };
2073
2074 let r2 = Rule {
2075 id: 2,
2076 source: Location::new("loc4"),
2077 target: Location::new("loc3"),
2078 guard: BooleanExpression::True,
2079 actions: vec![],
2080 };
2081
2082 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
2083 .with_locations(vec![
2084 Location::new("loc1"),
2085 Location::new("loc2"),
2086 Location::new("loc3"),
2087 Location::new("loc4"),
2088 ])
2089 .unwrap()
2090 .initialize()
2091 .with_rules([r0.clone(), r1.clone(), r2.clone()])
2092 .unwrap()
2093 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2094 Box::new(IntegerExpression::Atom(Location::new("loc3"))),
2095 ComparisonOp::Eq,
2096 Box::new(IntegerExpression::Const(0)),
2097 ))
2098 .unwrap()
2099 .build();
2100
2101 ta.remove_location_and_replace_with(&Location::new("loc3"), IntegerExpression::Const(0));
2102
2103 assert_eq!(ta.locations().count(), 3);
2104 assert!(!ta.locations().any(|l| *l == Location::new("loc3")));
2105
2106 assert_eq!(ta.rules().count(), 1);
2107 assert!(ta.rules().any(|r| *r == r0));
2108 assert!(!ta.rules().any(|r| *r == r1));
2109 assert!(!ta.rules().any(|r| *r == r2));
2110
2111 let expected_cond = BooleanExpression::ComparisonExpression(
2112 Box::new(IntegerExpression::Const(0)),
2113 ComparisonOp::Eq,
2114 Box::new(IntegerExpression::Const(0)),
2115 );
2116 assert_eq!(ta.initial_location_conditions().count(), 1);
2117 assert!(
2118 ta.initial_location_conditions()
2119 .any(|c| *c == expected_cond)
2120 );
2121 }
2122
2123 #[test]
2124 fn test_remove_variable() {
2125 let r0 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
2126 .with_guard(BooleanExpression::ComparisonExpression(
2127 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2128 ComparisonOp::Eq,
2129 Box::new(IntegerExpression::Const(0)),
2130 ))
2131 .build();
2132
2133 let r1 = Rule {
2134 id: 1,
2135 source: Location::new("loc3"),
2136 target: Location::new("loc4"),
2137 guard: BooleanExpression::True,
2138 actions: vec![
2139 Action::new(
2140 Variable::new("var1"),
2141 IntegerExpression::Atom(Variable::new("var1")) + IntegerExpression::Const(1),
2142 )
2143 .unwrap(),
2144 Action::new(
2145 Variable::new("var2"),
2146 IntegerExpression::Atom(Variable::new("var2")) + IntegerExpression::Const(1),
2147 )
2148 .unwrap(),
2149 ],
2150 };
2151
2152 let mut ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
2153 .with_locations(vec![
2154 Location::new("loc1"),
2155 Location::new("loc2"),
2156 Location::new("loc3"),
2157 Location::new("loc4"),
2158 ])
2159 .unwrap()
2160 .with_variables(vec![
2161 Variable::new("var1"),
2162 Variable::new("var2"),
2163 Variable::new("var3"),
2164 ])
2165 .unwrap()
2166 .initialize()
2167 .with_rules([r0.clone(), r1.clone()])
2168 .unwrap()
2169 .with_initial_variable_constraints([BooleanVarConstraint::ComparisonExpression(
2170 Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2171 ComparisonOp::Eq,
2172 Box::new(IntegerExpression::Const(0)),
2173 )])
2174 .unwrap()
2175 .build();
2176
2177 ta.remove_variable_and_replace_with(&Variable::new("var1"), IntegerExpression::Const(0));
2178
2179 assert_eq!(ta.variables().count(), 2);
2180 assert!(!ta.variables().any(|v| *v == Variable::new("var1")));
2181
2182 let expected_r0 = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2"))
2183 .with_guard(BooleanExpression::ComparisonExpression(
2184 Box::new(IntegerExpression::Const(0)),
2185 ComparisonOp::Eq,
2186 Box::new(IntegerExpression::Const(0)),
2187 ))
2188 .build();
2189 let expected_r1 = Rule {
2190 id: 1,
2191 source: Location::new("loc3"),
2192 target: Location::new("loc4"),
2193 guard: BooleanExpression::True,
2194 actions: vec![
2195 Action::new(
2196 Variable::new("var2"),
2197 IntegerExpression::Atom(Variable::new("var2")) + IntegerExpression::Const(1),
2198 )
2199 .unwrap(),
2200 ],
2201 };
2202
2203 assert_eq!(ta.rules().count(), 2);
2204 assert!(ta.rules().any(|r| *r == expected_r0));
2205 assert!(ta.rules().any(|r| *r == expected_r1));
2206
2207 assert_eq!(ta.initial_variable_constraints().count(), 1);
2208 assert!(ta.initial_variable_constraints().any(|c| *c
2209 == BooleanVarConstraint::ComparisonExpression(
2210 Box::new(IntegerExpression::Const(0)),
2211 ComparisonOp::Eq,
2212 Box::new(IntegerExpression::Const(0)),
2213 )));
2214 }
2215}