1use super::{FiniteStateTransitionSystemProver, ProofResult};
24use crate::{
25 formulas::{Cube, Literal, CNF},
26 models::FiniteStateTransitionSystem,
27 solvers::sat::{
28 stateful::{StatefulSatSolver, StatefulSatSolverHint},
29 SatResponse,
30 },
31};
32use priority_queue::PriorityQueue;
33use rand::rngs::ThreadRng;
34use rand::seq::SliceRandom;
35use rand::thread_rng;
36use std::{
37 cmp::{min, Reverse},
38 collections::HashSet,
39};
40use std::{collections::HashMap, time};
41
42#[derive(Clone, Copy, PartialEq, Eq, Hash)]
47enum Frame {
48 Null,
49 Inf,
50 Ok(usize),
51}
52
53#[derive(Clone, PartialEq, Eq, Hash)]
54struct TCube {
55 cube: Cube,
56 frame: Frame,
57}
58
59enum SolveRelativeParam {
60 DoNotExtractModel,
61 ExtractModel,
62 NoInd,
63}
64
65pub struct PDR<T> {
70 f: Vec<Vec<Cube>>,
72 fin_state: FiniteStateTransitionSystem,
73 rng: ThreadRng,
74
75 ri_and_not_p_solvers: Vec<T>, ri_solvers: Vec<T>, ri_and_t_solvers: Vec<T>, initial: CNF,
83 initial_and_not_p_cnf: CNF,
84 initial_and_t_cnf: CNF,
85 ri_and_not_p_cnf: CNF,
86 ri_and_t_cnf: CNF,
87
88 should_intersect_predecessor_with_transition_cone: bool,
90 should_intersect_cube_with_safety_cone: bool,
91
92 verbose: bool,
94 sat_call_stats: HashMap<String, (u32, f32)>,
95 start_time: time::Instant,
96}
97
98impl<T: StatefulSatSolver> PDR<T> {
103 fn next(&self, s: &TCube) -> TCube {
108 match s.frame {
109 Frame::Null => unreachable!(),
110 Frame::Inf => unreachable!(),
111 Frame::Ok(i) => TCube {
112 cube: s.cube.to_owned(),
113 frame: Frame::Ok(i + 1),
114 },
115 }
116 }
117
118 fn z_get_bad_cube(&mut self) -> Option<Cube> {
123 let depth = self.depth();
125
126 self.sat_call_stats.get_mut("z_get_bad_cube").unwrap().0 += 1;
127 let start_time = time::Instant::now();
128 let sat_response = self.ri_and_not_p_solvers[depth].solve(None, None);
129 self.sat_call_stats.get_mut("z_get_bad_cube").unwrap().1 +=
130 start_time.elapsed().as_secs_f32();
131
132 match sat_response {
133 SatResponse::Sat { assignment } => {
134 let mut bad_state = self.fin_state.extract_state_from_assignment(&assignment);
135
136 if self.should_intersect_cube_with_safety_cone {
137 let size_before = bad_state.len();
138 bad_state = self
139 .fin_state
140 .intersect_cube_with_cone_of_safety(&bad_state);
141 let size_after = bad_state.len();
142 debug_assert!(size_after <= size_before);
143 self.should_intersect_cube_with_safety_cone = size_before != size_after;
144 }
145
146 bad_state = self
148 .fin_state
149 .simplify_unsafe_cube_using_trinary_simulation(
150 &bad_state,
151 &self.fin_state.extract_input_from_assignment(&assignment),
152 &mut self.rng,
153 );
154 Option::Some(bad_state)
161 }
162 SatResponse::UnSat => Option::None,
163 }
164 }
165
166 fn z_is_blocked(&mut self, s: &TCube) -> bool {
167 match s.frame {
168 Frame::Null => unreachable!(),
169 Frame::Inf => unreachable!(),
170 Frame::Ok(frame) => {
171 self.sat_call_stats.get_mut("z_is_blocked").unwrap().0 += 1;
173 let start_time = time::Instant::now();
174 let sat_response = self.ri_solvers[frame].solve(None, None);
175 self.sat_call_stats.get_mut("z_is_blocked").unwrap().1 +=
176 start_time.elapsed().as_secs_f32();
177
178 match sat_response {
179 SatResponse::Sat { assignment: _ } => false,
180 SatResponse::UnSat => true,
181 }
182 }
183 }
184 }
185
186 fn z_is_initial(&self, c: &Cube) -> bool {
187 self.fin_state.is_cube_initial(c)
189 }
190
191 fn z_solve_relative(&mut self, s: &TCube, params: SolveRelativeParam) -> TCube {
192 match s.frame {
193 Frame::Null => unreachable!(),
194 Frame::Inf => unreachable!(),
195 Frame::Ok(i) => {
196 debug_assert!(0 < i && i < self.ri_and_t_solvers.len());
197 let extra_cube = self.fin_state.add_tags_to_cube(&s.cube, 1);
198 let extra_clause = !s.cube.to_owned();
199
200 match params {
202 SolveRelativeParam::DoNotExtractModel => {
203 self.sat_call_stats
204 .get_mut("z_solve_relative_DoNotExtractModel")
205 .unwrap()
206 .0 += 1
207 }
208 SolveRelativeParam::NoInd => {
209 self.sat_call_stats
210 .get_mut("z_solve_relative_NoInd")
211 .unwrap()
212 .0 += 1
213 }
214 SolveRelativeParam::ExtractModel => {
215 self.sat_call_stats
216 .get_mut("z_solve_relative_ExtractModel")
217 .unwrap()
218 .0 += 1
219 }
220 }
221
222 let start_time = time::Instant::now();
223 let sat_result = match params {
224 SolveRelativeParam::DoNotExtractModel | SolveRelativeParam::ExtractModel => {
225 self.ri_and_t_solvers[i - 1].solve(Some(&extra_cube), Some(&extra_clause))
227 }
228 SolveRelativeParam::NoInd => {
229 self.ri_and_t_solvers[i - 1].solve(Some(&extra_cube), None)
231 }
232 };
233
234 match params {
236 SolveRelativeParam::DoNotExtractModel => {
237 self.sat_call_stats
238 .get_mut("z_solve_relative_DoNotExtractModel")
239 .unwrap()
240 .1 += start_time.elapsed().as_secs_f32()
241 }
242 SolveRelativeParam::NoInd => {
243 self.sat_call_stats
244 .get_mut("z_solve_relative_NoInd")
245 .unwrap()
246 .1 += start_time.elapsed().as_secs_f32()
247 }
248 SolveRelativeParam::ExtractModel => {
249 self.sat_call_stats
250 .get_mut("z_solve_relative_ExtractModel")
251 .unwrap()
252 .1 += start_time.elapsed().as_secs_f32()
253 }
254 }
255
256 match sat_result {
257 SatResponse::Sat { assignment } => {
258 match params {
259 SolveRelativeParam::DoNotExtractModel | SolveRelativeParam::NoInd => {
260 TCube {
261 cube: Cube::new(&[]),
262 frame: Frame::Null,
263 }
264 }
265 SolveRelativeParam::ExtractModel => {
266 let mut predecessor =
267 self.fin_state.extract_state_from_assignment(&assignment);
268
269 if self.should_intersect_predecessor_with_transition_cone {
270 let size_before = predecessor.len();
271
272 predecessor = self
273 .fin_state
274 .intersect_cube_with_cone_of_transition(&predecessor);
275
276 let size_after = predecessor.len();
277 debug_assert!(size_after <= size_before);
278
279 self.should_intersect_predecessor_with_transition_cone =
280 size_before != size_after;
281 }
282
283 predecessor = self
285 .fin_state
286 .simplify_predecessor_cube_using_trinary_simulation(
287 &predecessor,
288 &s.cube,
289 &self.fin_state.extract_input_from_assignment(&assignment),
290 &mut self.rng,
291 );
292 TCube {
293 cube: predecessor,
294 frame: Frame::Null,
295 }
296 }
297 }
298 }
299 SatResponse::UnSat => {
300 debug_assert!(s.frame != Frame::Null);
301 s.to_owned()
302 }
303 }
304 }
305 }
306 }
307
308 fn z_block_cube_in_solver(&mut self, s: &TCube) {
309 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.ri_solvers.len());
310 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.ri_and_t_solvers.len());
311 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.f.len() - 1);
312 let mut cnf = CNF::new();
313 cnf.add_clause(&!s.cube.to_owned());
314 match s.frame {
315 Frame::Null => unreachable!(),
316 Frame::Inf => {
317 for i in 0..self.ri_solvers.len() {
318 self.ri_solvers[i].add_cnf(&cnf);
319 self.ri_and_not_p_solvers[i].add_cnf(&cnf);
320 self.ri_and_t_solvers[i].add_cnf(&cnf);
321 }
322 }
323 Frame::Ok(frame) => {
324 debug_assert!(frame < self.ri_solvers.len());
325 for i in 0..(frame + 1) {
326 self.ri_solvers[i].add_cnf(&cnf);
327 self.ri_and_not_p_solvers[i].add_cnf(&cnf);
328 self.ri_and_t_solvers[i].add_cnf(&cnf);
329 }
330 }
331 }
332 }
333
334 fn z_new_frame(&mut self) {
335 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.ri_solvers.len());
336 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.ri_and_t_solvers.len());
337 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.f.len() - 2);
338 if self.ri_and_not_p_solvers.is_empty() {
339 {
340 let mut a = T::new(StatefulSatSolverHint::None);
341 a.add_cnf(&self.initial_and_not_p_cnf);
342 self.ri_and_not_p_solvers.push(a);
343 }
344 {
345 let mut b = T::new(StatefulSatSolverHint::None);
346 b.add_cnf(&self.initial);
347 self.ri_solvers.push(b);
348 }
349 {
350 let mut c = T::new(StatefulSatSolverHint::None);
351 c.add_cnf(&self.initial_and_t_cnf);
352 self.ri_and_t_solvers.push(c);
353 }
354 } else {
355 {
356 let mut a = T::new(StatefulSatSolverHint::None);
357 a.add_cnf(&self.ri_and_not_p_cnf);
358 self.ri_and_not_p_solvers.push(a);
359 }
360 {
361 let b = T::new(StatefulSatSolverHint::None);
362 self.ri_solvers.push(b);
364 }
365 {
366 let mut c = T::new(StatefulSatSolverHint::None);
367 c.add_cnf(&self.ri_and_t_cnf);
368 self.ri_and_t_solvers.push(c);
369 }
370 }
371 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.ri_solvers.len());
372 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.ri_and_t_solvers.len());
373 debug_assert_eq!(self.ri_and_not_p_solvers.len(), self.f.len() - 1);
374 }
375
376 fn depth(&self) -> usize {
381 self.f.len() - 2
382 }
383
384 fn new_frame(&mut self) {
385 let n = self.f.len();
387 self.f.push(Vec::new());
388 self.f.swap(n - 1, n);
389 self.z_new_frame();
390 }
391
392 fn is_solve_relative_un_sat(&self, t: &TCube) -> bool {
393 t.frame != Frame::Null
394 }
395
396 fn add_blocked_cube(&mut self, s: &TCube) {
399 match s.frame {
400 Frame::Ok(s_frame_depth) => {
401 let k = min(s_frame_depth, self.depth() + 1);
402
403 for d in 1..(k + 1) {
405 let mut i = 0;
406 while i < self.f[d].len() {
407 if self.subsumes(&s.cube, &self.f[d][i]) {
408 self.f[d].swap_remove(i);
409 } else {
410 i += 1;
411 }
412 }
413 }
414
415 self.f[k].push(s.cube.to_owned());
417 self.z_block_cube_in_solver(s)
418 }
419 _ => {
420 unreachable!();
421 }
422 }
423 }
424
425 fn is_blocked(&mut self, s: &TCube) -> bool {
430 match s.frame {
431 Frame::Ok(s_frame) => {
432 for d in s_frame..self.f.len() {
434 for i in 0..self.f[d].len() {
435 if self.subsumes(&self.f[d][i], &s.cube) {
436 return true;
437 }
438 }
439 }
440
441 self.z_is_blocked(s)
442 }
443 _ => unreachable!(),
444 }
445 }
446
447 fn generalize(&mut self, s: &TCube) -> TCube {
448 let mut s_literals: Vec<Literal> = s.cube.iter().map(|l| l.to_owned()).collect();
449 s_literals.shuffle(&mut self.rng);
450 let mut j = 0;
451 while j < s_literals.len() {
452 let removed = s_literals.swap_remove(j);
454
455 let t = TCube {
456 cube: Cube::new(&s_literals),
457 frame: s.frame,
458 };
459 if !self.z_is_initial(&t.cube) {
460 let check_if_t_is_inductive_relative_to_frame =
461 self.z_solve_relative(&t, SolveRelativeParam::DoNotExtractModel);
462 if self.is_solve_relative_un_sat(&check_if_t_is_inductive_relative_to_frame) {
463 continue;
465 }
466 }
467
468 s_literals.push(removed);
470 let last_index = s_literals.len() - 1;
471 s_literals.swap(j, last_index);
472 j += 1;
474 }
475 TCube {
476 cube: Cube::new(&s_literals),
477 frame: s.frame,
478 }
479 }
480
481 fn propagate_blocked_cubes(&mut self) -> Option<usize> {
486 for k in 1..self.depth() {
487 let mut clause_index = 0;
488 while clause_index < self.f[k].len() {
489 let c = self.f[k][clause_index].to_owned();
490 let s = self.z_solve_relative(
491 &TCube {
492 cube: c,
493 frame: Frame::Ok(k + 1),
494 },
495 SolveRelativeParam::NoInd,
496 );
497 if s.frame != Frame::Null {
498 self.add_blocked_cube(&s);
499 } else {
500 clause_index += 1;
501 }
502 }
503 if self.f[k].is_empty() {
504 return Some(k); }
506 }
507 None
508 }
509
510 fn get_r_i(&self, i: usize) -> CNF {
515 assert!(self.f[0].is_empty());
517 let mut r_i = CNF::new();
518 if i == 0 {
519 r_i.append(&self.initial);
520 } else {
521 for i in i..self.f.len() {
522 let cubes = self.f[i].to_owned();
523 for cube in &cubes {
524 let clause = !cube.to_owned();
525 r_i.add_clause(&clause);
526 }
527 }
528 }
529 r_i
530 }
531
532 fn subsumes(&self, c1: &Cube, c2: &Cube) -> bool {
533 let c1_literals = c1.iter().collect::<HashSet<&Literal>>();
534 let c2_literals = c2.iter().collect::<HashSet<&Literal>>();
535 c1_literals.is_subset(&c2_literals)
536 }
537
538 fn print_progress_if_verbose(&self) {
539 if self.verbose {
540 let clauses = self
541 .f
542 .iter()
543 .map(|c| c.len())
544 .collect::<Vec<usize>>();
547 println!(
548 "PDR - is on depth = {}, clauses lengths = {:?}",
549 self.depth(),
550 clauses
551 );
552 println!(
553 "PDR - Time since start = {}",
554 self.start_time.elapsed().as_secs_f32()
555 );
556 println!(
557 "PDR - should_intersect_cube_with_safety_cone = {}",
558 self.should_intersect_cube_with_safety_cone
559 );
560 println!(
561 "PDR - should_intersect_predecessor_with_transition_cone = {}",
562 self.should_intersect_predecessor_with_transition_cone
563 );
564 println!("PDR - Sat call stats:");
565 for (key, value) in self.sat_call_stats.iter() {
566 println!("PDR - {}: {:?}", key, value);
567 }
568 }
569 }
570
571 fn rec_block_cube(&mut self, s0: TCube) -> bool {
576 let mut q = PriorityQueue::<TCube, Reverse<usize>>::new();
581 if let Frame::Ok(p) = s0.frame {
582 q.push(s0, Reverse(p));
583 } else {
584 panic!("Bad Cube to block, check get_bad_cube.");
585 }
586
587 while !q.is_empty() {
589 let s = q.pop().unwrap().0;
591 match s.frame {
592 Frame::Ok(s_frame) => {
593 if s_frame == 0 {
594 return false;
596 } else if !self.is_blocked(&s) {
597 debug_assert!(!self.z_is_initial(&s.cube));
598 let z = self.z_solve_relative(&s, SolveRelativeParam::ExtractModel);
599
600 if z.frame != Frame::Null {
601 let mut z = self.generalize(&z);
603 debug_assert!(z.frame != Frame::Inf);
604 match z.frame {
605 Frame::Ok(mut z_frame) => {
606 let mut another_iteration = true;
607 while (z_frame < (self.depth() - 1)) && another_iteration {
608 let potential_z = self.z_solve_relative(
609 &self.next(&z),
610 SolveRelativeParam::DoNotExtractModel,
611 );
612 another_iteration =
613 self.is_solve_relative_un_sat(&potential_z);
614 if another_iteration {
615 z = potential_z;
616 debug_assert!(z.frame != Frame::Inf);
617 debug_assert!(z.frame != Frame::Null);
618 match z.frame {
619 Frame::Ok(potential_z_frame) => {
620 z_frame = potential_z_frame
621 }
622 _ => unreachable!(),
623 }
624 }
625 }
626 debug_assert!(z.frame != Frame::Inf);
627 debug_assert!(z.frame != Frame::Null);
628 self.add_blocked_cube(&z);
629
630 debug_assert!(z.frame != Frame::Inf);
631 if (s_frame < self.depth()) && (z.frame != Frame::Inf) {
632 let next_s = self.next(&s);
633 match next_s.frame {
634 Frame::Ok(next_s_frame) => {
635 q.push(next_s, Reverse(next_s_frame));
636 }
637 _ => unreachable!(),
638 }
639 }
640 }
641 _ => unreachable!(),
642 }
643 } else {
644 match s.frame {
646 Frame::Ok(s_frame) => {
647 debug_assert!(s_frame > 0);
648 let z = TCube {
649 cube: z.cube,
650 frame: Frame::Ok(s_frame - 1),
651 };
652 q.push(z, Reverse(s_frame - 1));
653 q.push(s, Reverse(s_frame));
654 }
655 _ => unreachable!(),
656 }
657 }
658 }
659 }
660 _ => unreachable!(),
661 }
662 }
663 true
664 }
665
666 pub fn new(fin_state: &FiniteStateTransitionSystem, verbose: bool) -> Self {
671 let not_p0 = fin_state.get_unsafety_property();
673 let connection_from_state_to_safety0 = fin_state.get_state_to_safety_translation();
675 let transition = fin_state.get_transition_relation();
678 let initial = fin_state.get_initial_relation().to_cnf();
679
680 let mut initial_and_not_p_cnf = CNF::new();
681 initial_and_not_p_cnf.append(&initial);
682 initial_and_not_p_cnf.append(&connection_from_state_to_safety0);
683 initial_and_not_p_cnf.add_clause(¬_p0);
684
685 let mut initial_and_t_cnf = CNF::new();
686 initial_and_t_cnf.append(&initial);
687 initial_and_t_cnf.append(&transition);
688
689 let mut ri_and_not_p_cnf = CNF::new();
690 ri_and_not_p_cnf.append(&connection_from_state_to_safety0);
691 ri_and_not_p_cnf.add_clause(¬_p0);
692
693 let mut ri_and_t_cnf = CNF::new();
694 ri_and_t_cnf.append(&transition);
695
696 let sat_call_stats = HashMap::from([
697 ("z_get_bad_cube".to_string(), (0, 0_f32)),
698 ("z_is_blocked".to_string(), (0, 0_f32)),
699 ("z_solve_relative_DoNotExtractModel".to_string(), (0, 0_f32)),
700 ("z_solve_relative_NoInd".to_string(), (0, 0_f32)),
701 ("z_solve_relative_ExtractModel".to_string(), (0, 0_f32)),
702 ]);
703
704 Self {
705 f: Vec::new(),
706 fin_state: fin_state.to_owned(),
707 ri_and_not_p_solvers: Vec::new(),
708 ri_solvers: Vec::new(),
709 ri_and_t_solvers: Vec::new(),
710 rng: thread_rng(),
711 initial,
712 initial_and_not_p_cnf,
713 initial_and_t_cnf,
714 ri_and_not_p_cnf,
715 ri_and_t_cnf,
716 should_intersect_predecessor_with_transition_cone: true,
717 should_intersect_cube_with_safety_cone: true,
718 verbose,
719 start_time: time::Instant::now(),
720 sat_call_stats,
721 }
722 }
723
724 pub fn prove(&mut self) -> ProofResult {
725 self.start_time = time::Instant::now();
727
728 self.f.push(Vec::new()); self.new_frame(); loop {
732 let optional_c = self.z_get_bad_cube();
733 match optional_c {
734 Some(c) => {
735 if !self.rec_block_cube(TCube {
736 cube: c,
737 frame: Frame::Ok(self.depth()),
738 }) {
739 return ProofResult::CTX {
741 depth: self.depth().try_into().unwrap(),
742 };
743 }
744 }
745 None => {
746 self.print_progress_if_verbose();
747 self.new_frame();
748 let propagation_result = self.propagate_blocked_cubes();
749 if let Some(i) = propagation_result {
750 return ProofResult::Proof {
752 invariant: self.get_r_i(i),
753 };
754 }
755 }
756 }
757 }
758 }
759}
760
761impl<T: StatefulSatSolver> FiniteStateTransitionSystemProver for PDR<T> {
766 fn new(fin_state: &FiniteStateTransitionSystem) -> Self {
767 PDR::new(fin_state, true)
768 }
769
770 fn prove(&mut self) -> ProofResult {
771 self.prove()
772 }
773}