1use std::collections::HashMap;
7
8use taco_interval_ta::{IntervalThresholdAutomaton, interval::Interval};
9
10use taco_threshold_automaton::{
11 ThresholdAutomaton,
12 expressions::{Location, Variable},
13};
14
15use crate::acs_threshold_automaton::{ACSInterval, ACSLocation, CSVariable};
16
17#[derive(Debug, Clone, PartialEq)]
20pub struct IndexCtx {
21 loc_to_index: HashMap<Location, ACSLocation>,
23 index_to_loc: HashMap<ACSLocation, Location>,
25 var_to_index: HashMap<Variable, CSVariable>,
27 index_to_var: HashMap<CSVariable, Variable>,
29 interval_to_index: Vec<HashMap<Interval, ACSInterval>>,
31 index_to_interval: Vec<HashMap<ACSInterval, Interval>>,
33}
34
35impl IndexCtx {
36 pub fn new(ta: &IntervalThresholdAutomaton) -> Self {
38 let loc_to_index: HashMap<_, _> = ta
40 .locations()
41 .enumerate()
42 .map(|(i, l)| (l.clone(), ACSLocation(i)))
43 .collect();
44 let index_to_loc = loc_to_index
45 .iter()
46 .map(|(l, csl)| (*csl, l.clone()))
47 .collect();
48
49 let var_to_index: HashMap<_, _> = ta
50 .variables()
51 .enumerate()
52 .map(|(i, v)| (v.clone(), CSVariable(i)))
53 .collect();
54 let index_to_var = var_to_index
55 .iter()
56 .map(|(v, csv)| (*csv, v.clone()))
57 .collect();
58
59 let mut interval_to_index = vec![HashMap::new(); var_to_index.len()];
60 let mut index_to_interval = vec![HashMap::new(); var_to_index.len()];
61
62 for (var, cs_var) in var_to_index.iter() {
63 interval_to_index[cs_var.0] = ta
64 .get_intervals(var)
65 .iter()
66 .enumerate()
67 .map(|(i, interval)| (interval.clone(), ACSInterval(i)))
68 .collect();
69
70 index_to_interval[cs_var.0] = interval_to_index[cs_var.0]
71 .iter()
72 .map(|(int, csint)| (*csint, int.clone()))
73 .collect();
74 }
75
76 Self {
77 loc_to_index,
78 index_to_loc,
79 var_to_index,
80 index_to_var,
81 interval_to_index,
82 index_to_interval,
83 }
84 }
85
86 pub fn to_cs_loc(&self, loc: &Location) -> ACSLocation {
88 self.loc_to_index[loc]
89 }
90
91 pub fn get_loc(&self, loc: &ACSLocation) -> &Location {
93 &self.index_to_loc[loc]
94 }
95
96 pub fn locations(&self) -> impl Iterator<Item = (&Location, &ACSLocation)> {
98 self.loc_to_index.iter()
99 }
100
101 pub fn to_cs_var(&self, var: &Variable) -> CSVariable {
103 self.var_to_index[var]
104 }
105
106 pub fn get_var(&self, var: &CSVariable) -> &Variable {
108 &self.index_to_var[var]
109 }
110
111 pub fn variables(&self) -> impl Iterator<Item = (&Variable, &CSVariable)> {
113 self.var_to_index.iter()
114 }
115
116 pub fn to_cs_interval(&self, var: &CSVariable, i: &Interval) -> ACSInterval {
119 self.interval_to_index[var.0][i]
120 }
121
122 pub fn get_interval(&self, var: &CSVariable, i: &ACSInterval) -> &Interval {
125 &self.index_to_interval[var.0][i]
126 }
127
128 pub fn interval_exists(&self, var: &CSVariable, interval: ACSInterval) -> bool {
131 interval.0 < self.interval_to_index[var.0].len()
132 }
133
134 pub fn intervals(&self, var: &CSVariable) -> impl Iterator<Item = (&Interval, &ACSInterval)> {
136 self.interval_to_index[var.0].iter()
137 }
138}
139
140#[cfg(test)]
141mod mock_objects {
142 use std::collections::HashMap;
143
144 use taco_interval_ta::interval::Interval;
145 use taco_threshold_automaton::expressions::{Location, Variable};
146
147 use crate::acs_threshold_automaton::{
148 ACSInterval, ACSLocation, CSVariable, index_ctx::IndexCtx,
149 };
150
151 impl IndexCtx {
152 pub fn new_mock(
154 loc_to_index: HashMap<Location, ACSLocation>,
155 var_to_index: HashMap<Variable, CSVariable>,
156 interval_to_index: Vec<HashMap<Interval, ACSInterval>>,
157 ) -> Self {
158 Self {
159 loc_to_index: loc_to_index.clone(),
160 index_to_loc: loc_to_index.into_iter().map(|(k, v)| (v, k)).collect(),
161 var_to_index: var_to_index.clone(),
162 index_to_var: var_to_index.into_iter().map(|(k, v)| (v, k)).collect(),
163 interval_to_index: interval_to_index.clone(),
164 index_to_interval: interval_to_index
165 .into_iter()
166 .map(|inner| inner.into_iter().map(|(k, v)| (v, k)).collect())
167 .collect(),
168 }
169 }
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use std::collections::HashMap;
176
177 use taco_interval_ta::{
178 builder::IntervalTABuilder,
179 interval::{Interval, IntervalBoundary},
180 };
181
182 use taco_smt_encoder::SMTSolverBuilder;
183 use taco_threshold_automaton::{
184 expressions::{
185 BooleanExpression, ComparisonOp, IntegerExpression, Location, Parameter, Variable,
186 },
187 general_threshold_automaton::{
188 Action,
189 builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
190 },
191 lia_threshold_automaton::LIAThresholdAutomaton,
192 };
193
194 use crate::acs_threshold_automaton::{
195 ACSInterval, ACSLocation, CSVariable, index_ctx::IndexCtx,
196 };
197
198 #[test]
199 fn test_new_index_ctx() {
200 let var = Variable::new("x");
201
202 let rc = BooleanExpression::ComparisonExpression(
203 Box::new(IntegerExpression::Param(Parameter::new("n"))),
204 ComparisonOp::Gt,
205 Box::new(IntegerExpression::Const(3)),
206 );
207
208 let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
209 .with_variables([var.clone()])
210 .unwrap()
211 .with_locations([Location::new("l1")])
212 .unwrap()
213 .with_parameter(Parameter::new("n"))
214 .unwrap()
215 .initialize()
216 .with_resilience_condition(rc.clone())
217 .unwrap()
218 .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
219 Box::new(IntegerExpression::Atom(Location::new("l1"))),
220 ComparisonOp::Eq,
221 Box::new(IntegerExpression::Const(0)),
222 ))
223 .unwrap()
224 .with_rule(
225 RuleBuilder::new(0, Location::new("l1"), Location::new("l1"))
226 .with_guard(BooleanExpression::ComparisonExpression(
227 Box::new(IntegerExpression::Atom(var.clone())),
228 ComparisonOp::Gt,
229 Box::new(IntegerExpression::Const(2)),
230 ))
231 .with_action(
232 Action::new(
233 var.clone(),
234 IntegerExpression::Const(1) + IntegerExpression::Atom(var.clone()),
235 )
236 .unwrap(),
237 )
238 .build(),
239 )
240 .unwrap()
241 .build();
242
243 let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
244
245 let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
246 .build()
247 .unwrap();
248 let interval_threshold_automaton = interval_tas.next().unwrap();
249 assert!(interval_tas.next().is_none());
250
251 let got_idx_ctx = IndexCtx::new(&interval_threshold_automaton);
252
253 let expected_idx_ctx = IndexCtx {
254 loc_to_index: HashMap::from([(Location::new("l1"), ACSLocation(0))]),
255 index_to_loc: HashMap::from([(ACSLocation(0), Location::new("l1"))]),
256 var_to_index: HashMap::from([(Variable::new("x"), CSVariable(0))]),
257 index_to_var: HashMap::from([(CSVariable(0), Variable::new("x"))]),
258 interval_to_index: vec![HashMap::from([
259 (Interval::new_constant(0, 1), ACSInterval(0)),
260 (Interval::new_constant(1, 3), ACSInterval(1)),
261 (
262 Interval::new(
263 IntervalBoundary::from_const(3),
264 false,
265 IntervalBoundary::new_infty(),
266 true,
267 ),
268 ACSInterval(2),
269 ),
270 ])],
271 index_to_interval: vec![HashMap::from([
272 (ACSInterval(0), Interval::new_constant(0, 1)),
273 (ACSInterval(1), Interval::new_constant(1, 3)),
274 (
275 ACSInterval(2),
276 Interval::new(
277 IntervalBoundary::from_const(3),
278 false,
279 IntervalBoundary::new_infty(),
280 true,
281 ),
282 ),
283 ])],
284 };
285
286 assert_eq!(got_idx_ctx, expected_idx_ctx);
287 }
288
289 #[test]
290 fn test_to_cs_loc() {
291 let ctx = IndexCtx {
292 loc_to_index: HashMap::from([
293 (Location::new("l1"), ACSLocation(0)),
294 (Location::new("l2"), ACSLocation(1)),
295 ]),
296 index_to_loc: HashMap::from([
297 (ACSLocation(0), Location::new("l1")),
298 (ACSLocation(1), Location::new("l2")),
299 ]),
300 var_to_index: HashMap::from([]),
301 index_to_var: HashMap::from([]),
302 interval_to_index: vec![],
303 index_to_interval: vec![],
304 };
305
306 assert_eq!(ctx.to_cs_loc(&Location::new("l1")), ACSLocation(0));
307 assert_eq!(ctx.to_cs_loc(&Location::new("l2")), ACSLocation(1));
308 }
309
310 #[test]
311 fn test_from_cs_loc() {
312 let ctx = IndexCtx {
313 loc_to_index: HashMap::from([
314 (Location::new("l1"), ACSLocation(0)),
315 (Location::new("l2"), ACSLocation(1)),
316 ]),
317 index_to_loc: HashMap::from([
318 (ACSLocation(0), Location::new("l1")),
319 (ACSLocation(1), Location::new("l2")),
320 ]),
321 var_to_index: HashMap::from([]),
322 index_to_var: HashMap::from([]),
323 interval_to_index: vec![],
324 index_to_interval: vec![],
325 };
326
327 assert_eq!(ctx.get_loc(&ACSLocation(0)), &Location::new("l1"));
328 assert_eq!(ctx.get_loc(&ACSLocation(1)), &Location::new("l2"));
329 }
330
331 #[test]
332 fn test_locations() {
333 let ctx = IndexCtx {
334 loc_to_index: HashMap::from([
335 (Location::new("l1"), ACSLocation(0)),
336 (Location::new("l2"), ACSLocation(1)),
337 ]),
338 index_to_loc: HashMap::from([
339 (ACSLocation(0), Location::new("l1")),
340 (ACSLocation(1), Location::new("l2")),
341 ]),
342 var_to_index: HashMap::from([]),
343 index_to_var: HashMap::from([]),
344 interval_to_index: vec![],
345 index_to_interval: vec![],
346 };
347
348 assert_eq!(
349 ctx.locations().collect::<HashMap<_, _>>(),
350 HashMap::from([
351 (&Location::new("l1"), &ACSLocation(0)),
352 (&Location::new("l2"), &ACSLocation(1)),
353 ])
354 );
355 }
356
357 #[test]
358 fn test_to_cs_var() {
359 let ctx = IndexCtx {
360 loc_to_index: HashMap::from([]),
361 index_to_loc: HashMap::from([]),
362 var_to_index: HashMap::from([
363 (Variable::new("x"), CSVariable(0)),
364 (Variable::new("y"), CSVariable(1)),
365 ]),
366 index_to_var: HashMap::from([
367 (CSVariable(0), Variable::new("x")),
368 (CSVariable(1), Variable::new("y")),
369 ]),
370 interval_to_index: vec![],
371 index_to_interval: vec![],
372 };
373
374 assert_eq!(ctx.to_cs_var(&Variable::new("x")), CSVariable(0));
375 assert_eq!(ctx.to_cs_var(&Variable::new("y")), CSVariable(1));
376 }
377
378 #[test]
379 fn test_from_cs_var() {
380 let ctx = IndexCtx {
381 loc_to_index: HashMap::from([]),
382 index_to_loc: HashMap::from([]),
383 var_to_index: HashMap::from([
384 (Variable::new("x"), CSVariable(0)),
385 (Variable::new("y"), CSVariable(1)),
386 ]),
387 index_to_var: HashMap::from([
388 (CSVariable(0), Variable::new("x")),
389 (CSVariable(1), Variable::new("y")),
390 ]),
391 interval_to_index: vec![],
392 index_to_interval: vec![],
393 };
394
395 assert_eq!(ctx.get_var(&CSVariable(0)), &Variable::new("x"));
396 assert_eq!(ctx.get_var(&CSVariable(1)), &Variable::new("y"));
397 }
398
399 #[test]
400 fn test_variables() {
401 let ctx = IndexCtx {
402 loc_to_index: HashMap::from([]),
403 index_to_loc: HashMap::from([]),
404 var_to_index: HashMap::from([
405 (Variable::new("x"), CSVariable(0)),
406 (Variable::new("y"), CSVariable(1)),
407 ]),
408 index_to_var: HashMap::from([
409 (CSVariable(0), Variable::new("x")),
410 (CSVariable(1), Variable::new("y")),
411 ]),
412 interval_to_index: vec![],
413 index_to_interval: vec![],
414 };
415
416 assert_eq!(
417 ctx.variables().collect::<HashMap<_, _>>(),
418 HashMap::from([
419 (&Variable::new("x"), &CSVariable(0)),
420 (&Variable::new("y"), &CSVariable(1)),
421 ])
422 );
423 }
424
425 #[test]
426 fn test_to_cs_interval() {
427 let ctx = IndexCtx {
428 loc_to_index: HashMap::from([]),
429 index_to_loc: HashMap::from([]),
430 var_to_index: HashMap::from([]),
431 index_to_var: HashMap::from([]),
432 interval_to_index: vec![
433 HashMap::from([
434 (Interval::new_constant(0, 1), ACSInterval(0)),
435 (Interval::new_constant(1, 2), ACSInterval(1)),
436 (
437 Interval::new(
438 IntervalBoundary::from_const(2),
439 false,
440 IntervalBoundary::new_infty(),
441 true,
442 ),
443 ACSInterval(2),
444 ),
445 ]),
446 HashMap::from([
447 (Interval::new_constant(0, 1), ACSInterval(0)),
448 (
449 Interval::new(
450 IntervalBoundary::from_const(1),
451 false,
452 IntervalBoundary::new_infty(),
453 true,
454 ),
455 ACSInterval(1),
456 ),
457 ]),
458 ],
459 index_to_interval: vec![
460 HashMap::from([
461 (ACSInterval(0), Interval::new_constant(0, 1)),
462 (ACSInterval(1), Interval::new_constant(1, 2)),
463 (
464 ACSInterval(2),
465 Interval::new(
466 IntervalBoundary::from_const(2),
467 false,
468 IntervalBoundary::new_infty(),
469 true,
470 ),
471 ),
472 ]),
473 HashMap::from([
474 (ACSInterval(0), Interval::new_constant(0, 1)),
475 (
476 ACSInterval(1),
477 Interval::new(
478 IntervalBoundary::from_const(1),
479 false,
480 IntervalBoundary::new_infty(),
481 true,
482 ),
483 ),
484 ]),
485 ],
486 };
487
488 assert_eq!(
489 ctx.to_cs_interval(&CSVariable(0), &Interval::new_constant(0, 1)),
490 ACSInterval(0)
491 );
492 assert_eq!(
493 ctx.to_cs_interval(&CSVariable(0), &Interval::new_constant(1, 2)),
494 ACSInterval(1)
495 );
496 assert_eq!(
497 ctx.to_cs_interval(
498 &CSVariable(0),
499 &Interval::new(
500 IntervalBoundary::from_const(2),
501 false,
502 IntervalBoundary::new_infty(),
503 true,
504 ),
505 ),
506 ACSInterval(2)
507 );
508 assert_eq!(
509 ctx.to_cs_interval(&CSVariable(1), &Interval::new_constant(0, 1)),
510 ACSInterval(0)
511 );
512 assert_eq!(
513 ctx.to_cs_interval(
514 &CSVariable(1),
515 &Interval::new(
516 IntervalBoundary::from_const(1),
517 false,
518 IntervalBoundary::new_infty(),
519 true,
520 ),
521 ),
522 ACSInterval(1)
523 );
524 }
525
526 #[test]
527 fn test_from_cs_interval() {
528 let ctx = IndexCtx {
529 loc_to_index: HashMap::from([]),
530 index_to_loc: HashMap::from([]),
531 var_to_index: HashMap::from([]),
532 index_to_var: HashMap::from([]),
533 interval_to_index: vec![
534 HashMap::from([
535 (Interval::new_constant(0, 1), ACSInterval(0)),
536 (Interval::new_constant(1, 2), ACSInterval(1)),
537 (
538 Interval::new(
539 IntervalBoundary::from_const(2),
540 false,
541 IntervalBoundary::new_infty(),
542 true,
543 ),
544 ACSInterval(2),
545 ),
546 ]),
547 HashMap::from([
548 (Interval::new_constant(0, 1), ACSInterval(0)),
549 (
550 Interval::new(
551 IntervalBoundary::from_const(1),
552 false,
553 IntervalBoundary::new_infty(),
554 true,
555 ),
556 ACSInterval(1),
557 ),
558 ]),
559 ],
560 index_to_interval: vec![
561 HashMap::from([
562 (ACSInterval(0), Interval::new_constant(0, 1)),
563 (ACSInterval(1), Interval::new_constant(1, 2)),
564 (
565 ACSInterval(2),
566 Interval::new(
567 IntervalBoundary::from_const(2),
568 false,
569 IntervalBoundary::new_infty(),
570 true,
571 ),
572 ),
573 ]),
574 HashMap::from([
575 (ACSInterval(0), Interval::new_constant(0, 1)),
576 (
577 ACSInterval(1),
578 Interval::new(
579 IntervalBoundary::from_const(1),
580 false,
581 IntervalBoundary::new_infty(),
582 true,
583 ),
584 ),
585 ]),
586 ],
587 };
588
589 assert_eq!(
590 ctx.get_interval(&CSVariable(0), &ACSInterval(0)),
591 &Interval::new_constant(0, 1)
592 );
593 assert_eq!(
594 ctx.get_interval(&CSVariable(0), &ACSInterval(1)),
595 &Interval::new_constant(1, 2)
596 );
597 assert_eq!(
598 ctx.get_interval(&CSVariable(0), &ACSInterval(2)),
599 &Interval::new(
600 IntervalBoundary::from_const(2),
601 false,
602 IntervalBoundary::new_infty(),
603 true,
604 ),
605 );
606 assert_eq!(
607 ctx.get_interval(&CSVariable(1), &ACSInterval(0)),
608 &Interval::new_constant(0, 1)
609 );
610 assert_eq!(
611 ctx.get_interval(&CSVariable(1), &ACSInterval(1)),
612 &Interval::new(
613 IntervalBoundary::from_const(1),
614 false,
615 IntervalBoundary::new_infty(),
616 true,
617 ),
618 );
619 }
620
621 #[test]
622 fn test_exists_interval() {
623 let ctx = IndexCtx {
624 loc_to_index: HashMap::from([]),
625 index_to_loc: HashMap::from([]),
626 var_to_index: HashMap::from([]),
627 index_to_var: HashMap::from([]),
628 interval_to_index: vec![
629 HashMap::from([
630 (Interval::new_constant(0, 1), ACSInterval(0)),
631 (Interval::new_constant(1, 2), ACSInterval(1)),
632 (
633 Interval::new(
634 IntervalBoundary::from_const(2),
635 false,
636 IntervalBoundary::new_infty(),
637 true,
638 ),
639 ACSInterval(2),
640 ),
641 ]),
642 HashMap::from([
643 (Interval::new_constant(0, 1), ACSInterval(0)),
644 (
645 Interval::new(
646 IntervalBoundary::from_const(1),
647 false,
648 IntervalBoundary::new_infty(),
649 true,
650 ),
651 ACSInterval(1),
652 ),
653 ]),
654 ],
655 index_to_interval: vec![
656 HashMap::from([
657 (ACSInterval(0), Interval::new_constant(0, 1)),
658 (ACSInterval(1), Interval::new_constant(1, 2)),
659 (
660 ACSInterval(2),
661 Interval::new(
662 IntervalBoundary::from_const(2),
663 false,
664 IntervalBoundary::new_infty(),
665 true,
666 ),
667 ),
668 ]),
669 HashMap::from([
670 (ACSInterval(0), Interval::new_constant(0, 1)),
671 (
672 ACSInterval(1),
673 Interval::new(
674 IntervalBoundary::from_const(1),
675 false,
676 IntervalBoundary::new_infty(),
677 true,
678 ),
679 ),
680 ]),
681 ],
682 };
683
684 assert!(ctx.interval_exists(&CSVariable(0), ACSInterval(0)));
685 assert!(ctx.interval_exists(&CSVariable(1), ACSInterval(0)));
686
687 assert!(!ctx.interval_exists(&CSVariable(0), ACSInterval(5)));
688 assert!(!ctx.interval_exists(&CSVariable(0), ACSInterval(3)));
689 }
690
691 #[test]
692 fn test_intervals() {
693 let ctx = IndexCtx {
694 loc_to_index: HashMap::from([]),
695 index_to_loc: HashMap::from([]),
696 var_to_index: HashMap::from([]),
697 index_to_var: HashMap::from([]),
698 interval_to_index: vec![
699 HashMap::from([
700 (Interval::new_constant(0, 1), ACSInterval(0)),
701 (Interval::new_constant(1, 2), ACSInterval(1)),
702 (
703 Interval::new(
704 IntervalBoundary::from_const(2),
705 false,
706 IntervalBoundary::new_infty(),
707 true,
708 ),
709 ACSInterval(2),
710 ),
711 ]),
712 HashMap::from([
713 (Interval::new_constant(0, 1), ACSInterval(0)),
714 (
715 Interval::new(
716 IntervalBoundary::from_const(1),
717 false,
718 IntervalBoundary::new_infty(),
719 true,
720 ),
721 ACSInterval(1),
722 ),
723 ]),
724 ],
725 index_to_interval: vec![
726 HashMap::from([
727 (ACSInterval(0), Interval::new_constant(0, 1)),
728 (ACSInterval(1), Interval::new_constant(1, 2)),
729 (
730 ACSInterval(2),
731 Interval::new(
732 IntervalBoundary::from_const(2),
733 false,
734 IntervalBoundary::new_infty(),
735 true,
736 ),
737 ),
738 ]),
739 HashMap::from([
740 (ACSInterval(0), Interval::new_constant(0, 1)),
741 (
742 ACSInterval(1),
743 Interval::new(
744 IntervalBoundary::from_const(1),
745 false,
746 IntervalBoundary::new_infty(),
747 true,
748 ),
749 ),
750 ]),
751 ],
752 };
753
754 assert_eq!(
755 ctx.intervals(&CSVariable(0))
756 .map(|(a, b)| (a.clone(), *b))
757 .collect::<HashMap<_, _>>(),
758 HashMap::from([
759 (Interval::new_constant(0, 1), ACSInterval(0)),
760 (Interval::new_constant(1, 2), ACSInterval(1)),
761 (
762 Interval::new(
763 IntervalBoundary::from_const(2),
764 false,
765 IntervalBoundary::new_infty(),
766 true,
767 ),
768 ACSInterval(2),
769 ),
770 ])
771 );
772 assert_eq!(
773 ctx.intervals(&CSVariable(1))
774 .map(|(a, b)| (a.clone(), *b))
775 .collect::<HashMap<_, _>>(),
776 HashMap::from([
777 (Interval::new_constant(0, 1), ACSInterval(0)),
778 (
779 Interval::new(
780 IntervalBoundary::from_const(1),
781 false,
782 IntervalBoundary::new_infty(),
783 true,
784 ),
785 ACSInterval(1),
786 ),
787 ])
788 );
789 }
790}