1use std::fmt::Debug;
4
5use taco_bdd::{BDD, BDDManager, BddManager};
6use taco_interval_ta::IntervalActionEffect;
7use taco_interval_ta::{IntervalConstraint, IntervalTARule, IntervalThresholdAutomaton};
8use taco_threshold_automaton::ActionDefinition;
9use taco_threshold_automaton::RuleDefinition;
10use taco_threshold_automaton::expressions::Variable;
11
12use taco_threshold_automaton::ThresholdAutomaton;
13
14use crate::zcs::bdd_var_manager::BddVarManager;
15
16use super::{Bdd, SymbolicTransition, ZCS, ZCSStates};
17
18#[derive(Debug)]
26pub struct ZCSBuilder<'a> {
27 cs: ZCS<'a>,
29}
30impl<'a> ZCSBuilder<'a> {
31 pub fn new(mgr: &BDDManager, aut: &'a IntervalThresholdAutomaton) -> Self {
33 ZCSBuilder {
34 cs: ZCS {
35 bdd_mgr: BddVarManager::new(mgr.clone(), aut),
36 ata: aut,
37 initial_states: ZCSStates {
38 set_of_states: mgr.get_bdd_false(),
39 },
40 transitions: Vec::new(),
41 transition_relation: mgr.get_bdd_false(),
42 },
43 }
44 }
45
46 pub fn initialize(&mut self) {
50 self.cs.initial_states = self.build_initial_states();
51 self.cs.transitions = self.build_symbolic_transitions();
52 self.cs.transition_relation = self.build_symbolic_transition_relation();
53 }
54
55 fn build_initial_states(&self) -> ZCSStates {
57 let initial_states_bdd = self
60 .cs
61 .ata
62 .locations()
63 .filter(|loc| !self.cs.ata.can_be_initial_location(loc))
64 .map(|loc| {
65 !self
66 .cs
67 .bdd_mgr
68 .get_location_var(loc)
69 .unwrap_or_else(|_| panic!("No bdd variable for location {loc} created"))
70 })
71 .fold(self.cs.bdd_mgr.get_bdd_true(), |acc, bdd| acc & bdd);
72
73 let initial_states_bdd =
75 self.cs
76 .ata
77 .variables()
78 .fold(initial_states_bdd, |acc, var| {
79 let initial_intervals = self.cs.ata.get_initial_interval(var);
80 acc.and(
81 &initial_intervals.into_iter().map(|initial_interval|{
82 self
83 .cs
84 .bdd_mgr
85 .get_shared_interval_bdd(var, initial_interval)
86 .unwrap_or_else(|_| panic!("No shared interval bdd for shared variable {var} and interval {initial_interval} created"))
87 }).fold(self.cs.bdd_mgr.get_bdd_false(), |acc, bdd| {
88 acc.or(bdd)
89 }
90 ))
91 });
92
93 ZCSStates::new(initial_states_bdd)
94 }
95
96 fn build_symbolic_transitions(&self) -> Vec<SymbolicTransition> {
98 self.cs
99 .ata
100 .rules()
101 .map(|rule| self.build_symbolic_transition(rule))
102 .collect()
103 }
104
105 fn build_symbolic_transition(&self, rule: &IntervalTARule) -> SymbolicTransition {
113 let mut symbolic_transition = self.build_location_constraints_for_rule(rule);
115
116 symbolic_transition &= self
118 .cs
119 .bdd_mgr
120 .get_rule_bdd(rule)
121 .unwrap_or_else(|_| panic!("No bdd variable for rule {rule} created"))
122 .clone();
123
124 symbolic_transition &= self.construct_bdd_guard(rule.get_guard());
126
127 symbolic_transition &= self.build_effect_constraints_for_rule(rule);
129
130 SymbolicTransition {
132 abstract_rule: rule.clone(),
133 transition_bdd: symbolic_transition,
134 }
135 }
136
137 fn build_location_constraints_for_rule(&self, rule: &IntervalTARule) -> BDD {
142 let loc_constraint = self
144 .cs
145 .bdd_mgr
146 .get_location_var(rule.source())
147 .unwrap_or_else(|_| panic!("No bdd variable for location {} created", rule.source()))
148 .and(
149 self.cs
150 .bdd_mgr
151 .get_primed_location_var(rule.target())
152 .unwrap_or_else(|_| {
153 panic!(
154 "No primed bdd variable for location {} created",
155 rule.target()
156 )
157 }),
158 );
159
160 self.cs
162 .ata
163 .locations()
164 .filter(|loc| *loc != rule.source() && *loc != rule.target())
165 .fold(loc_constraint, |acc, loc| {
166 acc & self
167 .cs
168 .bdd_mgr
169 .get_location_var(loc)
170 .unwrap_or_else(|_| panic!("No bdd variable for location {loc} created"))
171 .equiv(
172 self.cs
173 .bdd_mgr
174 .get_primed_location_var(loc)
175 .unwrap_or_else(|_| {
176 panic!("No primed bdd variable for location {loc} created")
177 }),
178 )
179 })
180 }
181
182 fn build_effect_constraints_for_rule(&self, rule: &IntervalTARule) -> BDD {
188 let effect_constraints = rule
189 .actions()
190 .fold(self.cs.bdd_mgr.get_bdd_true(), |acc, act| {
191 let var = act.variable();
192 let effect = act.effect();
193
194 if matches!(effect, IntervalActionEffect::Reset) {
196 let primed_initial_interval_bdd =
197 self.build_reset_constraints_for_shared_var(var);
198
199 acc.and(primed_initial_interval_bdd)
200 } else {
201 let update_constraints =
203 self.build_update_constraints_for_shared_var(var, effect);
204 acc.and(&update_constraints)
205 }
206 });
207
208 self.cs
210 .ata
211 .variables()
212 .filter(|var| !rule.updates_variable(var))
213 .fold(effect_constraints, |acc, var| {
214 acc & self
215 .cs
216 .bdd_mgr
217 .get_unchanged_interval_bdd(var)
218 .unwrap_or_else(|_| {
219 panic!(
220 "No unchanged interval bdd for shared variable {var} could be created"
221 )
222 })
223 })
224 }
225
226 fn build_reset_constraints_for_shared_var(&self, shared: &Variable) -> &BDD {
229 let initial_interval = &self.cs.ata.get_zero_interval(shared);
230
231 self
232 .cs
233 .bdd_mgr
234 .get_primed_shared_interval_bdd(shared, initial_interval)
235 .unwrap_or_else(|_| panic!("No primed interval bdd for shared variable {shared} and interval {initial_interval} created"))
236 }
237
238 fn build_update_constraints_for_shared_var(
241 &self,
242 shared: &Variable,
243 effect: &IntervalActionEffect,
244 ) -> BDD {
245 self.cs.ata.get_intervals(shared).iter().fold(
246 self.cs.bdd_mgr.get_bdd_true(),
247 |acc, interval| {
248 let unprimed_interval_bdd = self
249 .cs
250 .bdd_mgr
251 .get_shared_interval_bdd(shared, interval)
252 .unwrap_or_else(|_| panic!("No interval bdd for shared variable {shared} and interval {interval} created"));
253
254 let unchanged_primed_interval_bdd = self
255 .cs
256 .bdd_mgr
257 .get_primed_shared_interval_bdd(shared, interval)
258 .unwrap_or_else(|_| panic!("No primed interval bdd for shared variable {shared} and interval {interval} created"));
259
260 let succ_primed_interval = match effect {
261 IntervalActionEffect::Inc(_) => self.cs.ata.get_next_interval(shared, interval),
262 IntervalActionEffect::Dec(_) => {
263 self.cs.ata.get_previous_interval(shared, interval)
264 }
265 IntervalActionEffect::Reset => unreachable!(),
266 };
267
268 let mut possible_intervals = unchanged_primed_interval_bdd.clone();
270 if let Some(next) = succ_primed_interval {
271 let next_bdd = self
273 .cs
274 .bdd_mgr
275 .get_primed_shared_interval_bdd(shared, next)
276 .unwrap_or_else(|_| panic!("No primed interval bdd for shared variable {shared} and interval {next} created"));
277 if interval.check_add_always_out_of_interval(1) {
280 possible_intervals = next_bdd.clone();
281 } else {
282 possible_intervals |= next_bdd.clone();
283 }
284 }
285 acc.and(&unprimed_interval_bdd.implies(&possible_intervals))
286 },
287 )
288 }
289
290 fn build_symbolic_transition_relation(&self) -> BDD {
293 self.cs.transitions.iter().fold(
294 self.cs.bdd_mgr.get_bdd_false(),
295 |acc, symbolic_transition| acc.or(&symbolic_transition.transition_bdd),
296 )
297 }
298
299 fn construct_bdd_guard(&self, guard: &IntervalConstraint) -> BDD {
302 match guard {
303 IntervalConstraint::True => self.cs.bdd_mgr.get_bdd_true(),
304 IntervalConstraint::Conj(lhs, rhs) => {
305 let lhs_bdd = self.construct_bdd_guard(lhs);
306 let rhs_bdd = self.construct_bdd_guard(rhs);
307 lhs_bdd.and(&rhs_bdd)
308 }
309 IntervalConstraint::Disj(lhs, rhs) => {
310 let lhs_bdd = self.construct_bdd_guard(lhs);
311 let rhs_bdd = self.construct_bdd_guard(rhs);
312 lhs_bdd.or(&rhs_bdd)
313 }
314 IntervalConstraint::SingleVarIntervalConstr(variable, hash_set) => {
315 let mut possible_intervals = self.cs.bdd_mgr.get_bdd_false();
316 for interval in hash_set {
317 possible_intervals = possible_intervals.or(self
318 .cs
319 .bdd_mgr
320 .get_shared_interval_bdd(variable, interval)
321 .unwrap_or_else(|_| panic!("No interval bdd for shared variable {variable} and interval {interval} created")));
322 }
323 possible_intervals
324 }
325 IntervalConstraint::MultiVarIntervalConstr(_, _) => {
326 unimplemented!("No support for multivar variables yet")
327 }
328 IntervalConstraint::False => self.cs.bdd_mgr.get_bdd_false(),
329 }
330 }
331
332 pub fn build(mut self) -> ZCS<'a> {
334 self.initialize();
335 self.cs
336 }
337}
338
339#[cfg(test)]
340mod tests {
341
342 use super::*;
343 use taco_interval_ta::builder::IntervalTABuilder;
344
345 use taco_interval_ta::interval::Interval;
346 use taco_interval_ta::interval::IntervalBoundary;
347
348 use taco_smt_encoder::SMTSolverBuilder;
349 use taco_threshold_automaton::expressions::Location;
350 use taco_threshold_automaton::expressions::Variable;
351 use taco_threshold_automaton::expressions::fraction::Fraction;
352 use taco_threshold_automaton::lia_threshold_automaton::integer_thresholds::Threshold;
353 use taco_threshold_automaton::lia_threshold_automaton::integer_thresholds::WeightedSum;
354 use taco_threshold_automaton::{
355 BooleanVarConstraint, LocationConstraint, ParameterConstraint,
356 expressions::{ComparisonOp, IntegerExpression, Parameter},
357 general_threshold_automaton::{Action, builder::RuleBuilder},
358 };
359
360 fn test_automaton() -> IntervalThresholdAutomaton {
387 let ta_builder =
388 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
389 .with_locations(vec![
390 Location::new("v0"),
391 Location::new("v1"),
392 Location::new("wait"),
393 Location::new("d0"),
394 Location::new("d1"),
395 ])
396 .unwrap()
397 .with_variables(vec![Variable::new("x0"), Variable::new("x1")])
398 .unwrap()
399 .with_parameters(vec![
400 Parameter::new("n"),
401 Parameter::new("t"),
402 Parameter::new("f"),
403 ])
404 .unwrap()
405 .initialize()
406 .with_initial_location_constraints(vec![
407 LocationConstraint::ComparisonExpression(
408 Box::new(
409 IntegerExpression::Atom(Location::new("v0"))
410 + IntegerExpression::Atom(Location::new("v1")),
411 ),
412 ComparisonOp::Eq,
413 Box::new(
414 IntegerExpression::Param(Parameter::new("n"))
415 - IntegerExpression::Param(Parameter::new("t")),
416 ),
417 ),
418 LocationConstraint::ComparisonExpression(
419 Box::new(IntegerExpression::Atom(Location::new("wait"))),
420 ComparisonOp::Eq,
421 Box::new(IntegerExpression::Const(0)),
422 ),
423 LocationConstraint::ComparisonExpression(
424 Box::new(IntegerExpression::Atom(Location::new("d0"))),
425 ComparisonOp::Eq,
426 Box::new(IntegerExpression::Const(0)),
427 ),
428 LocationConstraint::ComparisonExpression(
429 Box::new(IntegerExpression::Atom(Location::new("d1"))),
430 ComparisonOp::Eq,
431 Box::new(IntegerExpression::Const(0)),
432 ),
433 ])
434 .unwrap()
435 .with_resilience_conditions(vec![
436 ParameterConstraint::ComparisonExpression(
437 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
438 ComparisonOp::Gt,
439 Box::new(
440 IntegerExpression::Const(3)
441 * IntegerExpression::Atom(Parameter::new("t")),
442 ),
443 ),
444 ParameterConstraint::ComparisonExpression(
445 Box::new(IntegerExpression::Atom(Parameter::new("t"))),
446 ComparisonOp::Geq,
447 Box::new(IntegerExpression::Atom(Parameter::new("f"))),
448 ),
449 ParameterConstraint::ComparisonExpression(
450 Box::new(IntegerExpression::Atom(Parameter::new("n")) - IntegerExpression::Atom(Parameter::new("t"))),
451 ComparisonOp::Gt,
452 Box::new(IntegerExpression::Const(1)),
453 ),
454
455 ])
456 .unwrap()
457 .with_rules(vec![
458 RuleBuilder::new(0, Location::new("v0"), Location::new("wait"))
459 .with_action(
460 Action::new(
461 Variable::new("x0"),
462 IntegerExpression::Atom(Variable::new("x0"))
463 + IntegerExpression::Const(1),
464 )
465 .unwrap(),
466 )
467 .build(),
468 RuleBuilder::new(1, Location::new("v1"), Location::new("wait"))
469 .with_action(
470 Action::new(
471 Variable::new("x1"),
472 IntegerExpression::Atom(Variable::new("x1"))
473 + IntegerExpression::Const(1),
474 )
475 .unwrap(),
476 )
477 .build(),
478 RuleBuilder::new(2, Location::new("wait"), Location::new("d0"))
479 .with_guard(BooleanVarConstraint::ComparisonExpression(
480 Box::new(IntegerExpression::Atom(Variable::new("x0"))),
481 ComparisonOp::Geq,
482 Box::new(
483 IntegerExpression::Param(Parameter::new("n"))
484 - IntegerExpression::Param(Parameter::new("t")),
485 ),
486 ))
487 .build(),
488 RuleBuilder::new(3, Location::new("wait"), Location::new("d1"))
489 .with_guard(BooleanVarConstraint::ComparisonExpression(
490 Box::new(IntegerExpression::Atom(Variable::new("x1"))),
491 ComparisonOp::Geq,
492 Box::new(
493 IntegerExpression::Param(Parameter::new("n"))
494 - IntegerExpression::Param(Parameter::new("t")),
495 ),
496 ))
497 .build(),
498 ])
499 .unwrap();
500 let ta = ta_builder.build();
501 let lia =
502 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
503 ta.clone(),
504 )
505 .unwrap();
506
507 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
508 .build()
509 .unwrap();
510 let interval_threshold_automaton = interval_tas.next().unwrap();
511
512 let nxt = interval_tas.next();
513 assert!(nxt.is_none(), "Got additional ta {}", nxt.unwrap());
514
515 interval_threshold_automaton
516 }
517
518 fn test_dec_reset_automaton() -> IntervalThresholdAutomaton {
524 let ta_builder =
525 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
526 .with_locations(vec![Location::new("l0"), Location::new("l1")])
527 .unwrap()
528 .with_variable(Variable::new("x"))
529 .unwrap()
530 .with_parameters(vec![Parameter::new("n"), Parameter::new("t")])
531 .unwrap()
532 .initialize().with_resilience_condition(ParameterConstraint::ComparisonExpression(
533 Box::new(IntegerExpression::Atom(Parameter::new("n")) - IntegerExpression::Atom(Parameter::new("t"))),
534 ComparisonOp::Gt,
535 Box::new(IntegerExpression::Const(1)),
536 )).unwrap().with_initial_variable_constraint(BooleanVarConstraint::ComparisonExpression(
537 Box::new(IntegerExpression::Atom(Variable::new("x"))),
538 ComparisonOp::Geq,
539 Box::new(
540 IntegerExpression::Param(Parameter::new("n"))
541 - IntegerExpression::Param(Parameter::new("t")),
542 ),
543 )).unwrap()
544 .with_rules(vec![
545 RuleBuilder::new(0, Location::new("l0"), Location::new("l1"))
546 .with_action(
547 Action::new(
548 Variable::new("x"),
549 IntegerExpression::Atom(Variable::new("x"))
550 - IntegerExpression::Const(1),
551 )
552 .unwrap(),
553 )
554 .build(),
555 RuleBuilder::new(1, Location::new("l0"), Location::new("l1"))
556 .with_action(Action::new_reset(Variable::new("x")))
557 .build(),
558 ])
559 .unwrap();
560 let ta = ta_builder.build();
561 let lia =
562 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
563 ta.clone(),
564 )
565 .unwrap();
566
567 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
568 .build()
569 .unwrap();
570 let interval_threshold_automaton = interval_tas.next().unwrap();
571 let nxt = interval_tas.next();
572 assert!(nxt.is_none(), "Got additional ta {}", nxt.unwrap());
573
574 interval_threshold_automaton
575 }
576
577 #[test]
578 fn test_location_variables() {
579 let mgr = taco_bdd::BDDManager::default();
580 let ata = test_automaton();
581 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
582 let var_mgr = builder.cs.bdd_mgr;
583 assert!(var_mgr.get_location_var(&Location::new("v0")).is_ok());
584 assert!(
585 var_mgr
586 .get_primed_location_var(&Location::new("v0"))
587 .is_ok()
588 );
589 assert!(var_mgr.get_location_var(&Location::new("unknown")).is_err());
590 assert!(
591 !var_mgr
592 .get_location_var(&Location::new("v0"))
593 .unwrap()
594 .eq(var_mgr
595 .get_primed_location_var(&Location::new("v0"))
596 .unwrap())
597 );
598 }
599
600 #[test]
601 fn test_create_shared_variables() {
602 let mgr = taco_bdd::BDDManager::default();
603 let ata = test_automaton();
604 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
605
606 let interval_0 = Interval::zero();
608 let interval_1 = Interval::new(
610 IntervalBoundary::new_bound(WeightedSum::new_empty(), Fraction::new(1, 1, false)),
611 false,
612 IntervalBoundary::Bound(Threshold::new(
613 [
614 (Parameter::new("n"), 1.into()),
615 (Parameter::new("t"), Fraction::new(1, 1, true)),
616 ],
617 0,
618 )),
619 true,
620 );
621 let interval_2 = Interval::new(
623 IntervalBoundary::Bound(Threshold::new(
624 [
625 (Parameter::new("n"), 1.into()),
626 (Parameter::new("t"), Fraction::new(1, 1, true)),
627 ],
628 0,
629 )),
630 false,
631 IntervalBoundary::new_infty(),
632 true,
633 );
634
635 let interval_0_x0_bdd = builder
636 .cs
637 .bdd_mgr
638 .get_shared_interval_bdd(&Variable::new("x0"), &interval_0);
639 let interval_1_x0_bdd = builder
640 .cs
641 .bdd_mgr
642 .get_shared_interval_bdd(&Variable::new("x0"), &interval_1);
643 let interval_2_x0_bdd = builder
644 .cs
645 .bdd_mgr
646 .get_shared_interval_bdd(&Variable::new("x0"), &interval_2);
647
648 assert!(interval_0_x0_bdd.is_ok());
649 assert!(interval_1_x0_bdd.is_ok());
650 assert!(interval_2_x0_bdd.is_ok());
651
652 let x0_0 = (interval_0_x0_bdd
654 .clone()
655 .unwrap()
656 .or(interval_2_x0_bdd.clone().unwrap()))
657 .not();
658 let x0_1 = (interval_0_x0_bdd
660 .clone()
661 .unwrap()
662 .or(interval_1_x0_bdd.clone().unwrap()))
663 .not();
664
665 assert!(
666 interval_0_x0_bdd
667 .unwrap()
668 .eq(&(x0_0.not().and(&x0_1.not())))
669 );
670 assert!(
671 interval_1_x0_bdd
672 .clone()
673 .unwrap()
674 .eq(&(x0_0.and(&x0_1.not())))
675 );
676 assert!(interval_2_x0_bdd.unwrap().eq(&(x0_0.not().and(&x0_1))));
677
678 let interval_1_x0_primed_bdd = builder
679 .cs
680 .bdd_mgr
681 .get_primed_shared_interval_bdd(&Variable::new("x0"), &interval_1);
682 assert!(interval_1_x0_primed_bdd.is_ok());
683 assert!(
684 !interval_1_x0_primed_bdd
685 .unwrap()
686 .eq(interval_1_x0_bdd.unwrap())
687 );
688
689 assert!(
690 builder
691 .cs
692 .bdd_mgr
693 .get_shared_interval_bdd(&Variable::new("x1"), &interval_2)
694 .is_ok()
695 );
696 assert!(
697 builder
698 .cs
699 .bdd_mgr
700 .get_primed_shared_interval_bdd(&Variable::new("x1"), &interval_0)
701 .is_ok()
702 );
703 }
704
705 #[test]
706 fn test_build_initial_states() {
707 let mgr = taco_bdd::BDDManager::default();
708 let ata = test_automaton();
709 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
710 let initial_states_bdd = builder.build_initial_states().set_of_states;
711
712 let wait = builder.cs.bdd_mgr.get_location_var(&Location::new("wait"));
714 let d_0 = builder.cs.bdd_mgr.get_location_var(&Location::new("d0"));
715 let d_1 = builder.cs.bdd_mgr.get_location_var(&Location::new("d1"));
716 assert!(wait.is_ok());
717 assert!(d_0.is_ok());
718 assert!(d_1.is_ok());
719 let mut correct_initial_states_bdd = wait
720 .unwrap()
721 .not()
722 .and(&(d_0.unwrap().not().and(&d_1.unwrap().not())));
723
724 let interval_0 = Interval::zero();
727 let interval_0_x0_bdd = builder
728 .cs
729 .bdd_mgr
730 .get_shared_interval_bdd(&Variable::new("x0"), &interval_0);
731 let interval_0_x1_bdd = builder
732 .cs
733 .bdd_mgr
734 .get_shared_interval_bdd(&Variable::new("x1"), &interval_0);
735 assert!(interval_0_x0_bdd.is_ok());
736 assert!(interval_0_x1_bdd.is_ok());
737 let interval_0_x0_bdd = interval_0_x0_bdd.unwrap();
738 let interval_0_x1_bdd = interval_0_x1_bdd.unwrap();
739
740 let interval_1_nt = Interval::new(
742 IntervalBoundary::from_const(1),
743 false,
744 IntervalBoundary::new_bound(
745 WeightedSum::new([
746 (Parameter::new("n"), Fraction::from(1)),
747 (Parameter::new("t"), -Fraction::from(1)),
748 ]),
749 0,
750 ),
751 true,
752 );
753 let interval_1_nt_x0 = builder
754 .cs
755 .bdd_mgr
756 .get_shared_interval_bdd(&Variable::new("x0"), &interval_1_nt);
757 let interval_1_nt_x1 = builder
758 .cs
759 .bdd_mgr
760 .get_shared_interval_bdd(&Variable::new("x1"), &interval_1_nt);
761 assert!(interval_1_nt_x0.is_ok());
762 assert!(interval_1_nt_x1.is_ok());
763 let interval_1_nt_x0 = interval_1_nt_x0.unwrap();
764 let interval_1_nt_x1 = interval_1_nt_x1.unwrap();
765
766 let interval_nt_inf = Interval::new(
768 IntervalBoundary::new_bound(
769 WeightedSum::new([
770 (Parameter::new("n"), Fraction::from(1)),
771 (Parameter::new("t"), -Fraction::from(1)),
772 ]),
773 0,
774 ),
775 false,
776 IntervalBoundary::new_infty(),
777 true,
778 );
779 let interval_nt_inf_x0 = builder
780 .cs
781 .bdd_mgr
782 .get_shared_interval_bdd(&Variable::new("x0"), &interval_nt_inf);
783 let interval_nt_inf_x1 = builder
784 .cs
785 .bdd_mgr
786 .get_shared_interval_bdd(&Variable::new("x1"), &interval_nt_inf);
787 assert!(interval_nt_inf_x0.is_ok());
788 assert!(interval_nt_inf_x1.is_ok());
789 let interval_nt_inf_x0 = interval_nt_inf_x0.unwrap();
790 let interval_nt_inf_x1 = interval_nt_inf_x1.unwrap();
791
792 let init_intervals_x0 = interval_0_x0_bdd
793 .or(interval_1_nt_x0)
794 .or(interval_nt_inf_x0);
795 let init_intervals_x1 = interval_0_x1_bdd
796 .or(interval_1_nt_x1)
797 .or(interval_nt_inf_x1);
798
799 correct_initial_states_bdd =
800 correct_initial_states_bdd.and(&init_intervals_x0.and(&init_intervals_x1));
801
802 assert!(initial_states_bdd.eq(&correct_initial_states_bdd));
803 }
804
805 #[test]
806 fn test_build_symbolic_transitions() {
807 let mgr = taco_bdd::BDDManager::default();
808 let ata = test_automaton();
809 let builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
810
811 let transitions = builder.build_symbolic_transitions();
812
813 assert_eq!(transitions.len(), 4);
814
815 let var_mgr = builder.cs.bdd_mgr;
816
817 let mut correct_r0 = var_mgr
822 .get_location_var(&Location::new("v0"))
823 .unwrap()
824 .clone();
825 correct_r0 = correct_r0.and(
826 var_mgr
827 .get_primed_location_var(&Location::new("wait"))
828 .unwrap(),
829 );
830 correct_r0 = correct_r0.and(
832 &(var_mgr
833 .get_location_var(&Location::new("v1"))
834 .unwrap()
835 .equiv(
836 var_mgr
837 .get_primed_location_var(&Location::new("v1"))
838 .unwrap(),
839 )),
840 );
841 correct_r0 = correct_r0.and(
842 &(var_mgr
843 .get_location_var(&Location::new("d0"))
844 .unwrap()
845 .equiv(
846 var_mgr
847 .get_primed_location_var(&Location::new("d0"))
848 .unwrap(),
849 )),
850 );
851 correct_r0 = correct_r0.and(
852 &(var_mgr
853 .get_location_var(&Location::new("d1"))
854 .unwrap()
855 .equiv(
856 var_mgr
857 .get_primed_location_var(&Location::new("d1"))
858 .unwrap(),
859 )),
860 );
861
862 let interval_0 = Interval::zero();
865 let interval_1 = Interval::new(
867 IntervalBoundary::new_bound(WeightedSum::new_empty(), Fraction::new(1, 1, false)),
868 false,
869 IntervalBoundary::Bound(Threshold::new(
870 [
871 (Parameter::new("n"), 1.into()),
872 (Parameter::new("t"), Fraction::new(1, 1, true)),
873 ],
874 0,
875 )),
876 true,
877 );
878 let interval_2 = Interval::new(
880 IntervalBoundary::Bound(Threshold::new(
881 [
882 (Parameter::new("n"), 1.into()),
883 (Parameter::new("t"), Fraction::new(1, 1, true)),
884 ],
885 0,
886 )),
887 false,
888 IntervalBoundary::new_infty(),
889 true,
890 );
891
892 let interval_0_x0_bdd = var_mgr
893 .get_shared_interval_bdd(&Variable::new("x0"), &interval_0)
894 .unwrap();
895 let interval_1_x0_bdd = var_mgr
896 .get_shared_interval_bdd(&Variable::new("x0"), &interval_1)
897 .unwrap();
898 let interval_2_x0_bdd = var_mgr
899 .get_shared_interval_bdd(&Variable::new("x0"), &interval_2)
900 .unwrap();
901 let interval_1_x0_prime_bdd = var_mgr
902 .get_primed_shared_interval_bdd(&Variable::new("x0"), &interval_1)
903 .unwrap();
904 let interval_2_x0_prime_bdd = var_mgr
905 .get_primed_shared_interval_bdd(&Variable::new("x0"), &interval_2)
906 .unwrap();
907
908 correct_r0 = correct_r0.and(&(interval_0_x0_bdd.implies(interval_1_x0_prime_bdd)));
910 correct_r0 = correct_r0.and(
912 &(interval_1_x0_bdd.implies(&(interval_1_x0_prime_bdd.or(interval_2_x0_prime_bdd)))),
913 );
914 correct_r0 = correct_r0.and(&(interval_2_x0_bdd.implies(interval_2_x0_prime_bdd)));
916
917 correct_r0 = correct_r0.and(
919 &var_mgr
920 .get_unchanged_interval_bdd(&Variable::new("x1"))
921 .unwrap(),
922 );
923
924 for t in &transitions {
926 if t.abstract_rule().id() == 0 {
927 correct_r0 = correct_r0.and(var_mgr.get_rule_bdd(t.abstract_rule()).unwrap());
928 }
929 }
930
931 let mut correct_r3 = var_mgr
936 .get_location_var(&Location::new("wait"))
937 .unwrap()
938 .clone();
939 correct_r3 = correct_r3.and(
940 var_mgr
941 .get_primed_location_var(&Location::new("d1"))
942 .unwrap(),
943 );
944 correct_r3 = correct_r3.and(
946 &(var_mgr
947 .get_location_var(&Location::new("v0"))
948 .unwrap()
949 .equiv(
950 var_mgr
951 .get_primed_location_var(&Location::new("v0"))
952 .unwrap(),
953 )),
954 );
955 correct_r3 = correct_r3.and(
956 &(var_mgr
957 .get_location_var(&Location::new("v1"))
958 .unwrap()
959 .equiv(
960 var_mgr
961 .get_primed_location_var(&Location::new("v1"))
962 .unwrap(),
963 )),
964 );
965 correct_r3 = correct_r3.and(
966 &(var_mgr
967 .get_location_var(&Location::new("d0"))
968 .unwrap()
969 .equiv(
970 var_mgr
971 .get_primed_location_var(&Location::new("d0"))
972 .unwrap(),
973 )),
974 );
975
976 correct_r3 = correct_r3.and(
978 &var_mgr
979 .get_unchanged_interval_bdd(&Variable::new("x0"))
980 .unwrap(),
981 );
982
983 correct_r3 = correct_r3.and(
985 &var_mgr
986 .get_unchanged_interval_bdd(&Variable::new("x1"))
987 .unwrap(),
988 );
989
990 for t in &transitions {
992 if t.abstract_rule().id() == 3 {
993 correct_r3 = correct_r3.and(var_mgr.get_rule_bdd(t.abstract_rule()).unwrap());
994 }
995 }
996
997 correct_r3 = correct_r3.and(
999 var_mgr
1000 .get_shared_interval_bdd(&Variable::new("x1"), &interval_2)
1001 .unwrap(),
1002 );
1003
1004 for t in transitions {
1005 assert!(t.abstract_rule().id() < 4);
1006 if t.abstract_rule().id() == 0 {
1007 assert!(correct_r0.eq(&t.transition_bdd));
1008 }
1009 if t.abstract_rule().id() == 3 {
1010 assert!(correct_r3.eq(&t.transition_bdd));
1011 }
1012 }
1013 }
1014
1015 #[test]
1016 fn test_build_symbolic_transition_relation() {
1017 let mgr = taco_bdd::BDDManager::default();
1018 let ata = test_automaton();
1019 let mut builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
1020 builder.initialize();
1021
1022 assert!(
1023 builder
1024 .cs
1025 .transition_relation
1026 .and(&builder.cs.transitions[1].transition_bdd)
1027 .satisfiable()
1028 );
1029 assert!(
1030 builder
1031 .cs
1032 .transition_relation
1033 .and(
1034 builder
1035 .cs
1036 .bdd_mgr
1037 .get_rule_bdd(builder.cs.transitions[1].abstract_rule())
1038 .unwrap()
1039 )
1040 .satisfiable()
1041 );
1042 assert!(
1043 builder
1044 .cs
1045 .transition_relation
1046 .and(&builder.cs.transitions[2].transition_bdd)
1047 .satisfiable()
1048 );
1049 assert!(
1050 builder
1051 .cs
1052 .transition_relation
1053 .and(
1054 builder
1055 .cs
1056 .bdd_mgr
1057 .get_rule_bdd(builder.cs.transitions[2].abstract_rule())
1058 .unwrap()
1059 )
1060 .satisfiable()
1061 );
1062 }
1063
1064 #[test]
1065 fn test_decrement_transition() {
1066 let mgr = taco_bdd::BDDManager::default();
1067 let ata = test_dec_reset_automaton();
1068
1069 let mut builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
1070 builder.initialize();
1071
1072 assert_eq!(builder.cs.transitions.len(), 2);
1073
1074 let var_mgr = builder.cs.bdd_mgr;
1075
1076 let mut correct_r0 = var_mgr
1081 .get_location_var(&Location::new("l0"))
1082 .unwrap()
1083 .clone();
1084 correct_r0 = correct_r0.and(
1085 var_mgr
1086 .get_primed_location_var(&Location::new("l1"))
1087 .unwrap(),
1088 );
1089
1090 let interval_0 = Interval::zero();
1093 let interval_1 = Interval::new(
1095 IntervalBoundary::new_bound(WeightedSum::new_empty(), Fraction::new(1, 1, false)),
1096 false,
1097 IntervalBoundary::Bound(Threshold::new(
1098 [
1099 (Parameter::new("n"), 1.into()),
1100 (Parameter::new("t"), Fraction::new(1, 1, true)),
1101 ],
1102 0,
1103 )),
1104 true,
1105 );
1106 let interval_2 = Interval::new(
1108 IntervalBoundary::Bound(Threshold::new(
1109 [
1110 (Parameter::new("n"), 1.into()),
1111 (Parameter::new("t"), Fraction::new(1, 1, true)),
1112 ],
1113 0,
1114 )),
1115 false,
1116 IntervalBoundary::new_infty(),
1117 true,
1118 );
1119
1120 let interval_0_bdd = var_mgr
1121 .get_shared_interval_bdd(&Variable::new("x"), &interval_0)
1122 .unwrap();
1123 let interval_1_bdd = var_mgr
1124 .get_shared_interval_bdd(&Variable::new("x"), &interval_1)
1125 .unwrap();
1126 let interval_2_bdd = var_mgr
1127 .get_shared_interval_bdd(&Variable::new("x"), &interval_2)
1128 .unwrap();
1129 let interval_0_prime_bdd = var_mgr
1130 .get_primed_shared_interval_bdd(&Variable::new("x"), &interval_0)
1131 .unwrap();
1132 let interval_1_prime_bdd = var_mgr
1133 .get_primed_shared_interval_bdd(&Variable::new("x"), &interval_1)
1134 .unwrap();
1135 let interval_2_prime_bdd = var_mgr
1136 .get_primed_shared_interval_bdd(&Variable::new("x"), &interval_2)
1137 .unwrap();
1138
1139 correct_r0 = correct_r0.and(&(interval_0_bdd.implies(interval_0_prime_bdd)));
1141 correct_r0 = correct_r0
1143 .and(&(interval_1_bdd.implies(&(interval_1_prime_bdd.or(interval_0_prime_bdd)))));
1144 correct_r0 = correct_r0
1146 .and(&(interval_2_bdd.implies(&(interval_2_prime_bdd.or(interval_1_prime_bdd)))));
1147
1148 for t in &builder.cs.transitions {
1150 if t.abstract_rule.id() == 0 {
1151 correct_r0 = correct_r0.and(var_mgr.get_rule_bdd(t.abstract_rule()).unwrap());
1152 assert!(correct_r0.eq(&t.transition_bdd));
1153 }
1154 }
1155 }
1156
1157 #[test]
1158 fn test_reset_transition() {
1159 let mgr = taco_bdd::BDDManager::default();
1160 let ata = test_dec_reset_automaton();
1161
1162 let mut builder: ZCSBuilder = ZCSBuilder::new(&mgr, &ata);
1163 builder.initialize();
1164
1165 assert_eq!(builder.cs.transitions.len(), 2);
1166
1167 let var_mgr = builder.cs.bdd_mgr;
1168
1169 let mut correct_r1 = var_mgr
1174 .get_location_var(&Location::new("l0"))
1175 .unwrap()
1176 .clone();
1177 correct_r1 = correct_r1.and(
1178 var_mgr
1179 .get_primed_location_var(&Location::new("l1"))
1180 .unwrap(),
1181 );
1182
1183 let interval_0 = Interval::zero();
1186
1187 let interval_0_prime_bdd = var_mgr
1188 .get_primed_shared_interval_bdd(&Variable::new("x"), &interval_0)
1189 .unwrap();
1190
1191 correct_r1 = correct_r1.and(interval_0_prime_bdd);
1193
1194 for t in &builder.cs.transitions {
1196 if t.abstract_rule().id() == 1 {
1197 correct_r1 = correct_r1.and(var_mgr.get_rule_bdd(t.abstract_rule()).unwrap());
1198 assert!(correct_r1.eq(&t.transition_bdd));
1199 }
1200 }
1201 }
1202}