1use std::collections::HashMap;
5use std::fmt::Display;
6use taco_interval_ta::IntervalThresholdAutomaton;
7use taco_interval_ta::interval::Interval;
8use taco_model_checker::internal_spec::ErrorTarget;
9use taco_threshold_automaton::RuleDefinition;
10use taco_threshold_automaton::expressions::Variable;
11use taco_threshold_automaton::general_threshold_automaton::Rule;
12use zcs::{SymbolicTransition, ZCSStates};
13
14use crate::ZCSModelCheckerContext;
15use crate::smt_encoder_steady::SMTEncoderSteady;
16use crate::smt_encoder_steady::SpuriousResult;
17use crate::zcs;
18use crate::zcs::SymbolicVariableAssignment;
19use crate::zcs_error_graph::ZCSErrorGraph;
20use taco_threshold_automaton::ActionDefinition;
21use taco_threshold_automaton::general_threshold_automaton::UpdateExpression;
22use taco_threshold_automaton::general_threshold_automaton::UpdateExpression::{Dec, Inc};
23
24#[derive(Debug, Clone)]
30pub struct SteadyErrorPath<'a> {
31 steady_paths: Vec<SteadyPath>,
32 step_transitions: Vec<SymbolicTransition>,
33 error_graph: &'a ZCSErrorGraph<'a>,
35}
36impl<'a> SteadyErrorPath<'a> {
37 pub fn new(steady_path: SteadyPath, error_graph: &'a ZCSErrorGraph) -> Self {
39 SteadyErrorPath {
40 steady_paths: vec![steady_path],
41 step_transitions: Vec::new(),
42 error_graph,
43 }
44 }
45 pub fn get_last_states(&self) -> ZCSStates {
47 self.steady_paths
48 .last()
49 .expect("the steady error path is empty")
50 .reachable_states
51 .clone()
52 }
53 pub fn get_last_assignment(&self) -> SymbolicVariableAssignment {
55 self.steady_paths
56 .last()
57 .expect("the steady error path is empty")
58 .variable_assignment
59 .clone()
60 }
61 pub fn add_successor(&mut self, step_transition: &SymbolicTransition, steady_path: SteadyPath) {
63 self.step_transitions.push(step_transition.clone());
64 self.steady_paths.push(steady_path);
65 }
66
67 pub fn is_non_spurious(
71 &self,
72 reached_error_state: bool,
73 smc_ctx: &ZCSModelCheckerContext,
74 ) -> SpuriousResult {
75 let mut smt_encoder = SMTEncoderSteady::new(
76 smc_ctx.smt_bdd_context().smt_solver_builder().clone(),
77 smc_ctx.ta(),
78 self,
79 );
80
81 let ErrorTarget::Reach(spec) = smc_ctx.spec() else {
82 todo!("Remove once implemented");
83 };
84
85 if reached_error_state {
86 smt_encoder.steady_is_non_spurious(Some(spec))
87 } else {
88 smt_encoder.steady_is_non_spurious(None)
89 }
90 }
91
92 pub fn length_configurations(&self) -> u32 {
94 self.steady_paths.len().try_into().unwrap()
95 }
96 pub fn concrete_rules_ordered(
98 &self,
99 ta: &'a IntervalThresholdAutomaton,
100 ) -> impl Iterator<Item = &'a Rule> {
101 let mut ordered_rule_vec = Vec::new();
102 for i in 0..self.steady_paths.len() {
103 let path = &self.steady_paths[i];
104 for tr in &path.transitions {
105 ordered_rule_vec.push(ta.get_derived_rule(tr.abstract_rule()));
106 }
107 if i < self.steady_paths.len() - 1 {
109 let step_rule = &self.step_transitions[i];
110 ordered_rule_vec.push(ta.get_derived_rule(step_rule.abstract_rule()));
111 }
112 }
113 ordered_rule_vec.into_iter()
114 }
115
116 pub fn steady_paths(&self) -> impl Iterator<Item = &SteadyPath> {
118 self.steady_paths.iter()
119 }
120
121 pub fn last_is_monotonic(&self, ta: &IntervalThresholdAutomaton) -> bool {
124 let last_steady_path = self
125 .steady_paths
126 .last()
127 .expect("the steady error path is empty");
128 for shared in self.error_graph.variables() {
129 let mut inc_rules = false;
130 let mut dec_rules = false;
131 for rule in last_steady_path.transitions() {
132 let rule = ta.get_derived_rule(rule.abstract_rule());
133 let action_map = rule
134 .actions()
135 .map(|action| (action.variable(), action.update().clone()))
136 .collect::<HashMap<_, _>>();
137 let update = action_map
138 .get(shared)
139 .unwrap_or(&UpdateExpression::Unchanged);
140 match update {
141 Inc(_) => {
142 inc_rules = true;
143 if dec_rules {
144 return false;
145 }
146 }
147 Dec(_) => {
148 dec_rules = true;
149 if inc_rules {
150 return false;
151 }
152 }
153 UpdateExpression::Reset | UpdateExpression::Unchanged => {}
154 }
155 }
156 }
157 true
158 }
159}
160
161impl Display for SteadyErrorPath<'_> {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 writeln!(
164 f,
165 "Symbolic Path with {} steady paths:",
166 self.steady_paths.len(),
167 )?;
168
169 fn possible_locations(
170 f: &mut std::fmt::Formatter<'_>,
171 error_graph: &ZCSErrorGraph,
172 sym_states: &ZCSStates,
173 ) -> Result<(), Box<dyn std::error::Error>> {
174 let mut uncovered_locations = Vec::new();
175 let mut covered_locations = Vec::new();
176 let mut both_locations = Vec::new();
177 for loc in error_graph.locations() {
178 let loc_sym_states = error_graph.get_sym_state_for_loc(loc);
179 let covered = !sym_states.intersection(&loc_sym_states).is_empty();
180 let uncovered = !sym_states
181 .intersection(&loc_sym_states.complement())
182 .is_empty();
183 if covered && uncovered {
184 both_locations.push(loc);
185 } else if covered {
186 covered_locations.push(loc.clone());
187 } else if uncovered {
188 uncovered_locations.push(loc.clone());
189 }
190 }
191 writeln!(f, "covered locations: {:?}", covered_locations)?;
192 writeln!(f, "uncovered locations: {:?}", uncovered_locations)?;
193 writeln!(
194 f,
195 "locations which can be covered or uncovered: {:?}",
196 both_locations
197 )?;
198 Ok(())
199 }
200
201 let mut rule_index = 0;
202 for steady_path in &self.steady_paths {
203 writeln!(f, "Steady Path")?;
204
205 let _ = possible_locations(f, self.error_graph, &steady_path.reachable_states);
206 let rules = steady_path.transitions.clone();
207 writeln!(
208 f,
209 "Possible rules: {:?}",
210 rules
211 .iter()
212 .map(|r| "r_".to_owned() + &r.abstract_rule().id().to_string())
213 .collect::<Vec<String>>()
214 )?;
215 writeln!(
216 f,
217 "Variable Assignment:\n {}",
218 steady_path.variable_assignment.assignment()
219 )?;
220
221 if rule_index < self.step_transitions.len() {
222 writeln!(f, " |",)?;
223 writeln!(
224 f,
225 " | reach new fragment by applying rule {}",
226 "r_".to_owned()
227 + &self.step_transitions[rule_index]
228 .abstract_rule()
229 .id()
230 .to_string()
231 )?;
232 writeln!(f, " V")?;
233 rule_index += 1;
234 }
235 }
236
237 Ok(())
238 }
239}
240
241#[derive(Debug, Clone)]
247pub struct SteadyPath {
248 transitions: Vec<SymbolicTransition>,
249 reachable_states: ZCSStates,
250 variable_assignment: SymbolicVariableAssignment,
251}
252impl SteadyPath {
253 pub fn new(
255 transitions: Vec<&SymbolicTransition>,
256 reachable_states: ZCSStates,
257 variable_assignment: SymbolicVariableAssignment,
258 ) -> Self {
259 let mut transitions_without_duplicates = Vec::new();
260 for tr in transitions {
261 if !transitions_without_duplicates.contains(tr) {
262 transitions_without_duplicates.push(tr.clone());
263 }
264 }
265 SteadyPath {
266 transitions: transitions_without_duplicates,
267 reachable_states,
268 variable_assignment,
269 }
270 }
271 pub fn length_transitions(&self) -> u32 {
273 self.transitions.len().try_into().unwrap()
274 }
275 pub fn transitions(&self) -> impl Iterator<Item = &SymbolicTransition> {
277 self.transitions.iter()
278 }
279 pub fn variable_assignment(&self) -> &VariableAssignment {
281 self.variable_assignment.assignment()
282 }
283 pub fn _reachable_states(&self) -> &ZCSStates {
285 &self.reachable_states
286 }
287}
288
289#[derive(Debug, Clone)]
293pub struct VariableAssignment {
294 assignment: HashMap<Variable, Interval>,
295}
296impl VariableAssignment {
297 pub fn new() -> Self {
299 VariableAssignment {
300 assignment: HashMap::new(),
301 }
302 }
303
304 pub fn new_for_testing(assignment: HashMap<Variable, Interval>) -> Self {
306 VariableAssignment { assignment }
307 }
308
309 pub fn add_shared_var_interval(
312 &mut self,
313 shared: Variable,
314 interval: Interval,
315 ) -> Result<(), VariableAssignmentError> {
316 if self.assignment.contains_key(&shared) {
317 return Err(VariableAssignmentError::VariableAlreadyAssigned(shared));
318 }
319 self.assignment.insert(shared, interval);
320 Ok(())
321 }
322 pub fn assignments(&self) -> impl Iterator<Item = (&Variable, &Interval)> {
324 self.assignment.iter()
325 }
326}
327impl Default for VariableAssignment {
328 fn default() -> Self {
329 Self::new()
330 }
331}
332impl Display for VariableAssignment {
333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334 for (shared, interval) in self.assignment.iter() {
335 writeln!(f, " {shared}: {interval}")?;
336 }
337 Ok(())
338 }
339}
340
341#[derive(Debug, Clone)]
344pub enum VariableAssignmentError {
345 VariableAlreadyAssigned(Variable),
347}
348
349impl std::error::Error for VariableAssignmentError {}
350
351impl Display for VariableAssignmentError {
352 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353 match self {
354 VariableAssignmentError::VariableAlreadyAssigned(shared) => {
355 write!(
356 f,
357 "There already exists an entry for the shared variable {shared}"
358 )
359 }
360 }
361 }
362}
363
364#[cfg(test)]
365mod tests {
366
367 use super::VariableAssignment;
368 use crate::paths::VariableAssignmentError;
369 use crate::zcs::ZCS;
370 use crate::zcs::ZCSStates;
371 use std::vec;
372 use taco_interval_ta::IntervalThresholdAutomaton;
373 use taco_interval_ta::builder::IntervalTABuilder;
374 use taco_interval_ta::interval::Interval;
375 use taco_interval_ta::interval::IntervalBoundary;
376 use taco_smt_encoder::SMTSolverBuilder;
377 use taco_threshold_automaton::BooleanVarConstraint;
378 use taco_threshold_automaton::LocationConstraint;
379 use taco_threshold_automaton::ParameterConstraint;
380 use taco_threshold_automaton::expressions::ComparisonOp;
381 use taco_threshold_automaton::expressions::IntegerExpression;
382 use taco_threshold_automaton::expressions::{Location, Parameter, Variable};
383 use taco_threshold_automaton::general_threshold_automaton::Action;
384 use taco_threshold_automaton::general_threshold_automaton::builder::RuleBuilder;
385
386 fn _get_ata() -> IntervalThresholdAutomaton {
428 let ta_builder =
429 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
430 .with_locations(vec![
431 Location::new("l0"),
432 Location::new("l1"),
433 Location::new("l2"),
434 ])
435 .unwrap()
436 .with_variable(Variable::new("x"))
437 .unwrap()
438 .with_parameters(vec![
439 Parameter::new("n"),
440 Parameter::new("t"),
441 Parameter::new("f"),
442 ])
443 .unwrap()
444 .initialize()
445 .with_resilience_conditions([ParameterConstraint::ComparisonExpression(
446 Box::new(IntegerExpression::Atom(Parameter::new("n")) - IntegerExpression::Atom(Parameter::new("t"))),
447 ComparisonOp::Gt,
448 Box::new(IntegerExpression::Const(1)),
449 )])
450 .unwrap()
451 .with_initial_location_constraints(vec![
452 LocationConstraint::ComparisonExpression(
453 Box::new(IntegerExpression::Atom(Location::new("l0"))),
454 ComparisonOp::Eq,
455 Box::new(
456 IntegerExpression::Param(Parameter::new("n"))
457 - IntegerExpression::Param(Parameter::new("t")),
458 ),
459 ),
460 LocationConstraint::ComparisonExpression(
461 Box::new(IntegerExpression::Atom(Location::new("l1"))),
462 ComparisonOp::Eq,
463 Box::new(IntegerExpression::Const(0)),
464 ),
465 LocationConstraint::ComparisonExpression(
466 Box::new(IntegerExpression::Atom(Location::new("l2"))),
467 ComparisonOp::Eq,
468 Box::new(IntegerExpression::Const(0)),
469 ),
470 ])
471 .unwrap()
472 .with_initial_variable_constraints(vec![
473 BooleanVarConstraint::ComparisonExpression(
474 Box::new(IntegerExpression::Atom(Variable::new("x"))),
475 ComparisonOp::Eq,
476 Box::new(IntegerExpression::Const(0)),
477 ),
478 ])
479 .unwrap()
480 .with_rules(vec![
481 RuleBuilder::new(0, Location::new("l0"), Location::new("l1"))
482 .with_action(
483 Action::new(
484 Variable::new("x"),
485 IntegerExpression::Atom(Variable::new("x"))
486 + IntegerExpression::Const(1),
487 )
488 .unwrap(),
489 )
490 .build(),
491 RuleBuilder::new(1, Location::new("l0"), Location::new("l2"))
492 .with_guard(BooleanVarConstraint::ComparisonExpression(
493 Box::new(IntegerExpression::Atom(Variable::new("x"))),
494 ComparisonOp::Geq,
495 Box::new(
496 IntegerExpression::Param(Parameter::new("n"))
497 - IntegerExpression::Param(Parameter::new("t")),
498 ),
499 ))
500 .build(),
501 RuleBuilder::new(2, Location::new("l1"), Location::new("l2"))
502 .with_guard(BooleanVarConstraint::ComparisonExpression(
503 Box::new(IntegerExpression::Atom(Variable::new("x"))),
504 ComparisonOp::Geq,
505 Box::new(
506 IntegerExpression::Param(Parameter::new("n"))
507 - IntegerExpression::Param(Parameter::new("t")),
508 ),
509 ))
510 .build(),
511 ])
512 .unwrap();
513 let ta = ta_builder.build();
514 let lia =
515 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
516 ta.clone(),
517 )
518 .unwrap();
519
520 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
521 .build()
522 .unwrap();
523 let ata = interval_tas.next().unwrap();
524
525 let nxt = interval_tas.next();
526 assert!(nxt.is_none(), "Got additional ta {}", nxt.unwrap());
527
528 ata
529 }
530
531 fn _get_error_states(cs: &ZCS) -> ZCSStates {
533 let l0 = cs.get_sym_state_for_loc(&Location::new("l0"));
534 let l1 = cs.get_sym_state_for_loc(&Location::new("l1"));
535 let l2 = cs.get_sym_state_for_loc(&Location::new("l2"));
536 l0.complement()
537 .intersection(&l1.complement())
538 .intersection(&l2)
539 }
540
541 #[test]
542 fn test_variable_assignment() {
543 let mut var_assignment = VariableAssignment::new();
544
545 let test_interval = Interval::new(
546 IntervalBoundary::new_infty(),
547 true,
548 IntervalBoundary::new_infty(),
549 true,
550 );
551
552 let mut res =
553 var_assignment.add_shared_var_interval(Variable::new("x"), test_interval.clone());
554
555 assert!(res.is_ok());
556
557 assert!(var_assignment.assignment.contains_key(&Variable::new("x")));
558 assert_eq!(var_assignment.assignment.len(), 1);
559
560 let cloned_assign = var_assignment.clone();
561 let mut assign_it = cloned_assign.assignments();
562
563 let mut next = assign_it.next();
564
565 assert!(next.is_some());
566 assert_eq!(*next.unwrap().0, Variable::new("x"));
567
568 next = assign_it.next();
569
570 assert!(next.is_none());
571
572 res = var_assignment.add_shared_var_interval(Variable::new("x"), test_interval);
573
574 assert!(res.is_err());
575
576 assert!(matches!(
577 res.clone().unwrap_err(),
578 VariableAssignmentError::VariableAlreadyAssigned(_)
579 ));
580 }
581
582 fn _get_ata_for_spurious_checking() -> IntervalThresholdAutomaton {
624 let ta_builder =
625 taco_threshold_automaton::general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder::new("test")
626 .with_locations(vec![
627 Location::new("l0"),
628 Location::new("l1"),
629 Location::new("l2"),
630 ])
631 .unwrap()
632 .with_variables(vec![Variable::new("x"), Variable::new("y")])
633 .unwrap()
634 .with_parameters(vec![
635 Parameter::new("n"),
636 Parameter::new("t"),
637 Parameter::new("f"),
638 ])
639 .unwrap()
640 .initialize()
641 .with_initial_location_constraints(vec![
642 LocationConstraint::ComparisonExpression(
643 Box::new(IntegerExpression::Atom(Location::new("l0"))),
644 ComparisonOp::Eq,
645 Box::new(
646 IntegerExpression::Param(Parameter::new("n"))
647 - IntegerExpression::Param(Parameter::new("t")),
648 ),
649 ),
650 LocationConstraint::ComparisonExpression(
651 Box::new(IntegerExpression::Atom(Location::new("l1"))),
652 ComparisonOp::Eq,
653 Box::new(IntegerExpression::Const(0)),
654 ),
655 LocationConstraint::ComparisonExpression(
656 Box::new(IntegerExpression::Atom(Location::new("l2"))),
657 ComparisonOp::Eq,
658 Box::new(IntegerExpression::Const(0)),
659 ),
660 ])
661 .unwrap()
662 .with_resilience_conditions([ParameterConstraint::ComparisonExpression(
663 Box::new(IntegerExpression::Atom(Parameter::new("n")) - IntegerExpression::Atom(Parameter::new("t"))),
664 ComparisonOp::Gt,
665 Box::new(IntegerExpression::Const(1)),
666 )])
667 .unwrap()
668 .with_initial_variable_constraints(vec![BooleanVarConstraint::ComparisonExpression(
669 Box::new(IntegerExpression::Atom(Variable::new("x"))),
670 ComparisonOp::Eq,
671 Box::new(IntegerExpression::Const(0)),
672 )])
673 .unwrap()
674 .with_rules(vec![
675 RuleBuilder::new(0, Location::new("l0"), Location::new("l1"))
676 .with_action(
677 Action::new(
678 Variable::new("x"),
679 IntegerExpression::Atom(Variable::new("x"))
680 + IntegerExpression::Const(1),
681 )
682 .unwrap(),
683 )
684 .build(),
685 RuleBuilder::new(1, Location::new("l0"), Location::new("l2"))
686 .with_guard(BooleanVarConstraint::ComparisonExpression(
687 Box::new(IntegerExpression::Atom(Variable::new("x"))),
688 ComparisonOp::Geq,
689 Box::new(
690 IntegerExpression::Param(Parameter::new("n"))
691 - IntegerExpression::Param(Parameter::new("t")),
692 ),
693 ))
694 .build(),
695 RuleBuilder::new(2, Location::new("l1"), Location::new("l2"))
696 .with_guard(BooleanVarConstraint::ComparisonExpression(
697 Box::new(IntegerExpression::Atom(Variable::new("x"))),
698 ComparisonOp::Geq,
699 Box::new(
700 IntegerExpression::Param(Parameter::new("n"))
701 - IntegerExpression::Param(Parameter::new("t")),
702 ),
703 ))
704 .build(),
705 ])
706 .unwrap();
707 let ta = ta_builder.build();
708 let lia =
709 taco_threshold_automaton::lia_threshold_automaton::LIAThresholdAutomaton::try_from(
710 ta.clone(),
711 )
712 .unwrap();
713
714 let mut interval_tas = IntervalTABuilder::new(lia, SMTSolverBuilder::default(), vec![])
715 .build()
716 .unwrap();
717 let ata = interval_tas.next().unwrap();
718
719 let nxt = interval_tas.next();
720 assert!(nxt.is_none(), "Got additional ta {}", nxt.unwrap());
721
722 ata
723 }
724
725 fn _get_error_states_for_spurious_checking(cs: &ZCS) -> ZCSStates {
727 cs.get_sym_state_for_loc(&Location::new("l2"))
728 }
729}