rust_formal_verification/algorithms/proof/
pdr.rs

1// //! This algorithm is an exact implementation of what is described in "Efficient implementation of property directed reachability".
2// //!
3// //! N. Een, A. Mishchenko and R. Brayton,
4// //! "Efficient implementation of property directed reachability,"
5// //! 2011 Formal Methods in Computer-Aided Design (FMCAD), 2011, pp. 125-134.
6// //!
7// //! Abstract: Last spring, in March 2010, Aaron Bradley published the first truly new bit-level
8// //! symbolic model checking algorithm since Ken McMillan's interpolation based model checking
9// //! procedure introduced in 2003.
10// //! Our experience with the algorithm suggests that it is stronger than interpolation on industrial
11// //! problems, and that it is an important algorithm to study further.
12// //! In this paper, we present a simplified and faster implementation of Bradley's procedure, and
13// //! discuss our successful and unsuccessful attempts to improve it.
14// //! URL: https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6148886&isnumber=6148882
15// //!
16// //! The implementation of the original 2010 bit-level symbolic model checking algorithm is
17// //! available under ic3 stateless solver.
18
19// ************************************************************************************************
20// use
21// ************************************************************************************************
22
23use 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// ************************************************************************************************
43// Enum
44// ************************************************************************************************
45
46#[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
65// ************************************************************************************************
66// struct
67// ************************************************************************************************
68
69pub struct PDR<T> {
70    // for the algorithm
71    f: Vec<Vec<Cube>>,
72    fin_state: FiniteStateTransitionSystem,
73    rng: ThreadRng,
74
75    // stateful sat solvers for speedup
76    // Reminder: R0 == initial
77    ri_and_not_p_solvers: Vec<T>, // for each index i the solver holds Fi ^ !P
78    ri_solvers: Vec<T>,           // for each index i the solver holds Fi
79    ri_and_t_solvers: Vec<T>,     // for each index i the solver holds Fi ^ T
80
81    // caching for speedup
82    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    // checks that skip useless cube intersection
89    should_intersect_predecessor_with_transition_cone: bool,
90    should_intersect_cube_with_safety_cone: bool,
91
92    // for printing
93    verbose: bool,
94    sat_call_stats: HashMap<String, (u32, f32)>,
95    start_time: time::Instant,
96}
97
98// ************************************************************************************************
99// impl
100// ************************************************************************************************
101
102impl<T: StatefulSatSolver> PDR<T> {
103    // ********************************************************************************************
104    // helper functions - TCube
105    // ********************************************************************************************
106
107    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    // ********************************************************************************************
119    // helper functions - interface of sat queries
120    // ********************************************************************************************
121
122    fn z_get_bad_cube(&mut self) -> Option<Cube> {
123        // get cube that satisfies Ri && !P
124        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                // let size_before = bad_state.len();
147                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                // let size_after = bad_state.len();
155                // println!(
156                //     "PDR - Trinary simulation, size before = {}, size after = {}",
157                //     size_before, size_after
158                // );
159
160                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                // return true iff Ri ^ c == UnSat
172                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        // make sure all clauses in initial are in cube.
188        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                // update number of calls for sat solver stats
201                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                        // these two are the same sat call Ri-1 ^ T ^ !s.cube ^ s.cube'
226                        self.ri_and_t_solvers[i - 1].solve(Some(&extra_cube), Some(&extra_clause))
227                    }
228                    SolveRelativeParam::NoInd => {
229                        // this one has another sat call: Ri-1 ^ T ^ s.cube'
230                        self.ri_and_t_solvers[i - 1].solve(Some(&extra_cube), None)
231                    }
232                };
233
234                // update time of calls for sat solver stats
235                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                                // trinary simulation todo
284                                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                // b.add_cnf(&self.ri);
363                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    // ********************************************************************************************
377    // helper functions - helper functions in paper
378    // ********************************************************************************************
379
380    fn depth(&self) -> usize {
381        self.f.len() - 2
382    }
383
384    fn new_frame(&mut self) {
385        // add frame to f while moving f_inf forward.
386        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    // adds a cube to Fa nd the PdrSat object. It will also remove any subsumed cube in F.
397    // Subsumed cube in the Sat-Solver will be removed through periodic recycling.???????
398    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                // remove subsumed clauses
404                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                // store clause
416                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    // ********************************************************************************************
426    // helper functions - 2 helper functions shown in paper
427    // ********************************************************************************************
428
429    fn is_blocked(&mut self, s: &TCube) -> bool {
430        match s.frame {
431            Frame::Ok(s_frame) => {
432                // check syntactic submission (faster than SAT)
433                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            // remove current literal
453            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                    // remove successful, j should remain the same
464                    continue;
465                }
466            }
467
468            // undo remove
469            s_literals.push(removed);
470            let last_index = s_literals.len() - 1;
471            s_literals.swap(j, last_index);
472            // move on to next literal
473            j += 1;
474        }
475        TCube {
476            cube: Cube::new(&s_literals),
477            frame: s.frame,
478        }
479    }
480
481    // ********************************************************************************************
482    // helper functions - last helper functions shown in paper
483    // ********************************************************************************************
484
485    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); // invariant found
505            }
506        }
507        None
508    }
509
510    // ********************************************************************************************
511    // helper functions - my helper functions
512    // ********************************************************************************************
513
514    fn get_r_i(&self, i: usize) -> CNF {
515        // println!("get_r_i called with i = {}", i);
516        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                // .rev()
545                // .take(10)
546                .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    // ********************************************************************************************
572    // helper functions - main blocking function
573    // ********************************************************************************************
574
575    fn rec_block_cube(&mut self, s0: TCube) -> bool {
576        // create queue of proof obligations.
577        // Each proof obligation is a cube that reaches bad, and at what frame this cube was found.
578        // It's called proof obligation because you're obliged to prove that this cube cannot be
579        // reached by previous frames.
580        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 proof obligations remain.
588        while !q.is_empty() {
589            // take one out
590            let s = q.pop().unwrap().0;
591            match s.frame {
592                Frame::Ok(s_frame) => {
593                    if s_frame == 0 {
594                        // a bad reaching cube was found in F0 == initial
595                        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                            // cube 's' was blocked by image of predecessor.
602                            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                            // cube 's' was not blocked by image of predecessor.
645                            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    // ********************************************************************************************
667    // API functions
668    // ********************************************************************************************
669
670    pub fn new(fin_state: &FiniteStateTransitionSystem, verbose: bool) -> Self {
671        // let p0 = fin_state.get_safety_property();
672        let not_p0 = fin_state.get_unsafety_property();
673        // let not_p1 = fin_state.add_tags_to_clause(&not_p0, 1);
674        let connection_from_state_to_safety0 = fin_state.get_state_to_safety_translation();
675        // let connection_from_state_to_safety1 =
676        //     fin_state.add_tags_to_relation(&connection_from_state_to_safety0, 1);
677        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(&not_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(&not_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        // update start time.
726        self.start_time = time::Instant::now();
727
728        self.f.push(Vec::new()); // push F_inf
729        self.new_frame(); // create "F[0]"
730
731        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                        // failed to block 'c' => CTX found
740                        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                        // invariant found may store it here.
751                        return ProofResult::Proof {
752                            invariant: self.get_r_i(i),
753                        };
754                    }
755                }
756            }
757        }
758    }
759}
760
761// ************************************************************************************************
762// impl trait
763// ************************************************************************************************
764
765impl<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}