1use super::{FiniteStateTransitionSystemProver, ProofResult};
25use crate::{
26 formulas::{literal::VariableType, Clause, Cube, Literal, CNF},
27 models::FiniteStateTransitionSystem,
28 solvers::sat::{
29 stateful::{StatefulSatSolver, StatefulSatSolverHint},
30 SatResponse,
31 },
32};
33use priority_queue::PriorityQueue;
34use rand::rngs::ThreadRng;
35use rand::seq::SliceRandom;
36use rand::thread_rng;
37use std::cmp::{max, Reverse};
38use std::time;
39
40enum StrengthenResult {
45 Success,
46 Failure { _depth: VariableType },
47}
48
49enum InductivelyGeneralizeResult {
50 Success { n: usize },
51 Failure,
52}
53
54enum PushGeneralizeResult {
55 Success,
56 Failure,
57}
58
59enum SolverVariant {
60 Initial,
61 FiAndT(usize),
62 FiAndTAndNotPTag(usize),
63 Custom(CNF),
64}
65
66pub struct IC3Stateful<T> {
71 clauses: Vec<CNF>,
73 fin_state: FiniteStateTransitionSystem,
74 rng: ThreadRng,
75
76 fi_and_t_solvers: Vec<T>, initial_solver: T, fi_and_t_and_not_p_tag_solvers: Vec<T>, initial: CNF,
84 transition: CNF,
85 p0: CNF,
86 not_p0: CNF,
87 not_p1: CNF,
88
89 verbose: bool,
91 number_of_sat_calls: u32,
92 time_in_sat_calls: time::Duration,
93 start_time: time::Instant,
94}
95
96impl<T: StatefulSatSolver> IC3Stateful<T> {
101 fn add_clause_to_clauses(&mut self, index: usize, clause: &Clause) {
106 debug_assert_eq!(self.clauses.len(), self.fi_and_t_solvers.len());
107 debug_assert_eq!(
108 self.clauses.len(),
109 self.fi_and_t_and_not_p_tag_solvers.len()
110 );
111
112 self.clauses[index].add_clause(clause);
113 self.fi_and_t_solvers[index].add_cnf(&clause.to_cnf());
114 self.fi_and_t_and_not_p_tag_solvers[index].add_cnf(&clause.to_cnf());
115 }
116
117 fn get_clause_from_clauses(&self, index: usize) -> CNF {
118 self.clauses[index].to_owned()
119 }
120
121 fn push_extra_frame_to_clauses(&mut self) {
122 debug_assert_eq!(self.clauses.len(), self.fi_and_t_solvers.len());
123 debug_assert_eq!(
124 self.clauses.len(),
125 self.fi_and_t_and_not_p_tag_solvers.len()
126 );
127
128 self.clauses.push(CNF::new());
129
130 let mut fi_and_t = T::new(StatefulSatSolverHint::None);
132 fi_and_t.add_cnf(&self.transition);
133
134 let mut fi_and_t_and_not_p_tag = T::new(StatefulSatSolverHint::None);
135 fi_and_t_and_not_p_tag.add_cnf(&self.transition);
136 fi_and_t_and_not_p_tag.add_cnf(&self.not_p1);
137
138 if self.fi_and_t_solvers.is_empty() {
139 fi_and_t.add_cnf(&self.initial);
140 fi_and_t_and_not_p_tag.add_cnf(&self.initial);
141 } else {
142 fi_and_t.add_cnf(&self.p0);
143 fi_and_t_and_not_p_tag.add_cnf(&self.p0);
144 }
145
146 self.fi_and_t_solvers.push(fi_and_t);
147 self.fi_and_t_and_not_p_tag_solvers
148 .push(fi_and_t_and_not_p_tag);
149 }
150
151 fn sat_call(
156 &mut self,
157 solver_index: SolverVariant,
158 cube_assumptions: Option<&Cube>,
159 clause_assumptions: Option<&Clause>,
160 ) -> SatResponse {
161 self.number_of_sat_calls += 1;
162 let start_time = time::Instant::now();
163
164 let result = match solver_index {
166 SolverVariant::Initial => self
167 .initial_solver
168 .solve(cube_assumptions, clause_assumptions),
169 SolverVariant::FiAndT(j) => {
170 self.fi_and_t_solvers[j].solve(cube_assumptions, clause_assumptions)
171 }
172 SolverVariant::FiAndTAndNotPTag(j) => {
173 self.fi_and_t_and_not_p_tag_solvers[j].solve(cube_assumptions, clause_assumptions)
174 }
175 SolverVariant::Custom(cnf) => {
176 let mut current_solver = T::new(StatefulSatSolverHint::None);
177 current_solver.add_cnf(&cnf);
178 current_solver.solve(cube_assumptions, clause_assumptions)
179 }
180 };
181
182 self.time_in_sat_calls += start_time.elapsed();
183 result
184 }
185
186 fn is_bad_reached_in_0_steps(&mut self) -> SatResponse {
187 let mut cnf = CNF::new();
189 cnf.append(&self.initial);
190 cnf.append(&self.not_p0);
191 self.sat_call(SolverVariant::Custom(cnf), None, None)
192 }
193
194 fn is_bad_reached_in_1_steps(&mut self) -> SatResponse {
195 let mut cnf = CNF::new();
197 cnf.append(&self.initial);
198 cnf.append(&self.transition);
199 cnf.append(&self.not_p1);
200 self.sat_call(SolverVariant::Custom(cnf), None, None)
201 }
202
203 fn is_cube_reachable_in_1_step_from_fi(&mut self, i: usize, cube: &Cube) -> SatResponse {
204 let cube_tag = self.fin_state.add_tags_to_cube(cube, 1);
206 self.sat_call(SolverVariant::FiAndT(i), Some(&cube_tag), None)
207 }
208
209 fn is_bad_reachable_in_1_step_from_fi(&mut self, i: usize) -> SatResponse {
210 self.sat_call(SolverVariant::FiAndTAndNotPTag(i), None, None)
212 }
213
214 fn is_fi_and_t_and_not_s_and_s_tag_sat(&mut self, i: usize, s: &Cube) -> bool {
215 let s_tag = self.fin_state.add_tags_to_cube(s, 1);
217 let not_s = !(s.to_owned());
218
219 match self.sat_call(SolverVariant::FiAndT(i), Some(&s_tag), Some(¬_s)) {
220 SatResponse::UnSat => false,
221 SatResponse::Sat { assignment: _ } => true,
222 }
223 }
224
225 fn is_fi_and_t_and_clause_and_not_clause_tag_sat(&mut self, i: usize, d: &Clause) -> bool {
226 let not_d_tag = self.fin_state.add_tags_to_cube(&(!(d.to_owned())), 1);
228
229 match self.sat_call(SolverVariant::FiAndT(i), Some(¬_d_tag), Some(d)) {
230 SatResponse::UnSat => false,
231 SatResponse::Sat { assignment: _ } => true,
232 }
233 }
234
235 fn does_cube_intersect_with_initial(&mut self, cube: &Cube) -> bool {
236 match self.sat_call(SolverVariant::Initial, Some(cube), None) {
237 SatResponse::UnSat => false,
238 SatResponse::Sat { assignment: _ } => true,
239 }
240 }
241
242 fn get_fk(&self, k: usize) -> CNF {
247 let mut clauses_fk = self.get_clause_from_clauses(k);
248 if k == 0 {
249 clauses_fk.append(&self.initial);
250 } else {
251 clauses_fk.append(&self.p0);
252 }
253 clauses_fk
254 }
255
256 fn propagate_clauses(&mut self, k: usize) {
257 for i in 1..(k + 1) {
258 let clauses_fi = self.get_clause_from_clauses(i);
259 for c in clauses_fi.iter() {
260 let check = self.is_cube_reachable_in_1_step_from_fi(i, &(!(c.to_owned())));
261 match check {
262 SatResponse::UnSat => {
263 self.add_clause_to_clauses(i + 1, c);
265 }
266 SatResponse::Sat { assignment: _ } => {
267 }
269 }
270 }
271 }
272 }
273
274 fn is_clause_inductive_relative_to_fi(&mut self, d: &Clause, i: usize) -> bool {
275 if self.fin_state.is_cube_initial(&(!(d.to_owned()))) {
277 debug_assert!(self.does_cube_intersect_with_initial(&(!(d.to_owned()))));
278 return false;
279 } else {
280 debug_assert!(!self.does_cube_intersect_with_initial(&(!(d.to_owned()))));
281 }
282
283 !self.is_fi_and_t_and_clause_and_not_clause_tag_sat(i, d)
284 }
285
286 fn get_subclause_of_not_s_that_is_inductive_relative_to_fi(
287 &mut self,
288 s: &Cube,
289 i: usize,
290 ) -> Clause {
291 let c = !(s.to_owned());
292 let mut c_literals: Vec<Literal> = c.iter().map(|l| l.to_owned()).collect();
293 c_literals.shuffle(&mut self.rng);
294 let mut j = 0;
295 while j < c_literals.len() {
296 let removed = c_literals.swap_remove(j);
297 let d = Clause::new(&c_literals);
298 if self.is_clause_inductive_relative_to_fi(&d, i) {
299 } else {
301 c_literals.push(removed);
303 let last_index = c_literals.len() - 1;
304 c_literals.swap(j, last_index);
305 j += 1;
307 }
308 }
309 Clause::new(&c_literals)
310 }
311
312 fn generate_clause(&mut self, s: &Cube, i: usize, _k: usize) {
313 let c = self.get_subclause_of_not_s_that_is_inductive_relative_to_fi(s, i);
314 for j in 1..(i + 2) {
315 self.add_clause_to_clauses(j, &c);
316 }
317 }
318
319 fn inductively_generalize(
320 &mut self,
321 s: &Cube,
322 min: isize,
323 k: usize,
324 ) -> InductivelyGeneralizeResult {
325 if min < 0 && self.is_fi_and_t_and_not_s_and_s_tag_sat(0, s) {
326 return InductivelyGeneralizeResult::Failure;
327 }
328
329 for i in max(1, min + 1).try_into().unwrap()..(k + 1) {
330 if self.is_fi_and_t_and_not_s_and_s_tag_sat(i, s) {
331 self.generate_clause(s, i - 1, k);
332 return InductivelyGeneralizeResult::Success { n: i - 1 };
333 }
334 }
335 self.generate_clause(s, k, k);
336 InductivelyGeneralizeResult::Success { n: k }
337 }
338
339 fn push_generalization(
340 &mut self,
341 states: &PriorityQueue<Cube, Reverse<usize>>,
342 k: usize,
343 ) -> PushGeneralizeResult {
344 let mut states = states.to_owned();
345 loop {
346 let (s, reversed_n) = states.pop().unwrap();
347 let n = reversed_n.0;
348 if n > k {
349 return PushGeneralizeResult::Success;
350 }
351 match self.is_cube_reachable_in_1_step_from_fi(n, &s) {
352 SatResponse::Sat { assignment } => {
353 let p = self.fin_state.extract_state_from_assignment(&assignment);
355 match self.inductively_generalize(
357 &p,
358 <usize as TryInto<isize>>::try_into(n).unwrap() - 2,
359 k,
360 ) {
361 InductivelyGeneralizeResult::Failure => {
362 return PushGeneralizeResult::Failure;
363 }
364 InductivelyGeneralizeResult::Success { n: m } => {
365 states.push(s, reversed_n);
366 states.push(p, Reverse(m + 1));
367 }
368 }
369 }
370 SatResponse::UnSat => {
371 match self.inductively_generalize(&s, n.try_into().unwrap(), k) {
373 InductivelyGeneralizeResult::Failure => {
374 return PushGeneralizeResult::Failure;
375 }
376 InductivelyGeneralizeResult::Success { n: m } => {
377 states.push(s.to_owned(), Reverse(m + 1));
378 }
379 }
380 }
381 }
382 }
383 }
384
385 fn print_progress_if_verbose(&self, k: usize) {
386 if self.verbose {
387 let clauses = self
388 .clauses
389 .iter()
390 .map(|c| c.len())
391 .rev()
392 .take(10)
393 .collect::<Vec<usize>>();
394 println!("IC3 is on k = {}, clauses lengths = {:?}", k, clauses);
395 println!("Number of SAT calls = {}", self.number_of_sat_calls);
396 println!(
397 "Time since start = {}",
398 self.start_time.elapsed().as_secs_f32()
399 );
400 println!(
401 "Time in SAT calls = {}",
402 self.time_in_sat_calls.as_secs_f32()
403 );
404 }
405 }
406
407 fn strengthen(&mut self, k: usize) -> StrengthenResult {
408 loop {
409 match self.is_bad_reachable_in_1_step_from_fi(k) {
410 SatResponse::UnSat => {
411 break;
412 }
413 SatResponse::Sat { assignment } => {
414 let s = self.fin_state.extract_state_from_assignment(&assignment);
415 match self.inductively_generalize(
417 &s,
418 <usize as TryInto<isize>>::try_into(k).unwrap() - 2,
419 k,
420 ) {
421 InductivelyGeneralizeResult::Failure => {
422 return StrengthenResult::Failure {
423 _depth: k.try_into().unwrap(),
424 };
425 }
426 InductivelyGeneralizeResult::Success { n } => {
427 let mut queue = PriorityQueue::<Cube, Reverse<usize>>::new();
428 queue.push(s, Reverse(n + 1));
429 match self.push_generalization(&queue, k) {
430 PushGeneralizeResult::Failure => {
431 return StrengthenResult::Failure {
432 _depth: k.try_into().unwrap(),
433 };
434 }
435 PushGeneralizeResult::Success => {}
436 };
437 }
438 };
439 }
440 }
441 }
442
443 StrengthenResult::Success
444 }
445
446 pub fn new(fin_state: &FiniteStateTransitionSystem, verbose: bool) -> Self {
451 let mut p0 = fin_state.get_state_to_safety_translation();
452 p0.append(&fin_state.get_safety_property().to_cnf());
453
454 let mut not_p0 = fin_state.get_state_to_safety_translation();
455 not_p0.append(&fin_state.get_unsafety_property().to_cnf());
456
457 let mut initial_solver = T::new(StatefulSatSolverHint::None);
458 initial_solver.add_cnf(&fin_state.get_initial_relation().to_cnf());
459
460 Self {
461 clauses: Vec::new(),
462 fin_state: fin_state.to_owned(),
463 fi_and_t_solvers: Vec::new(),
464 initial_solver,
465 fi_and_t_and_not_p_tag_solvers: Vec::new(),
466 rng: thread_rng(),
467 initial: fin_state.get_initial_relation().to_cnf(),
468 transition: fin_state.get_transition_relation(),
469 p0,
470 not_p0: not_p0.to_owned(),
471 not_p1: fin_state.add_tags_to_relation(¬_p0, 1),
472 verbose,
473 number_of_sat_calls: 0,
474 time_in_sat_calls: time::Duration::from_secs(0),
475 start_time: time::Instant::now(),
476 }
477 }
478
479 pub fn prove(&mut self) -> ProofResult {
480 self.start_time = time::Instant::now();
482
483 let init_and_not_p = self.is_bad_reached_in_0_steps();
484 match init_and_not_p {
485 SatResponse::Sat { assignment: _ } => return ProofResult::CTX { depth: 0 },
486 SatResponse::UnSat => (),
487 }
488 let init_and_tr_and_not_p_tag = self.is_bad_reached_in_1_steps();
491 match init_and_tr_and_not_p_tag {
492 SatResponse::Sat { assignment: _ } => return ProofResult::CTX { depth: 1 },
493 SatResponse::UnSat => (),
494 }
495
496 self.push_extra_frame_to_clauses();
497 self.push_extra_frame_to_clauses();
498 for k in 1.. {
499 self.push_extra_frame_to_clauses();
500 self.print_progress_if_verbose(k);
502 debug_assert_eq!(self.clauses.len(), (k + 2));
503 debug_assert_eq!(self.get_clause_from_clauses(0).len(), 0);
504 match self.strengthen(k) {
505 StrengthenResult::Success => {}
506 StrengthenResult::Failure { _depth: _ } => {
507 return ProofResult::CTX {
508 depth: k.try_into().unwrap(),
509 };
510 }
511 };
512 self.propagate_clauses(k);
513 for i in 1..(k + 1) {
514 debug_assert!(self
516 .get_clause_from_clauses(i + 1)
517 .iter()
518 .all(|c| self.get_clause_from_clauses(i).contains(c)));
519 if self.get_clause_from_clauses(i).len()
520 == self.get_clause_from_clauses(i + 1).len()
521 {
522 self.print_progress_if_verbose(k);
524 return ProofResult::Proof {
525 invariant: self.get_fk(i),
526 };
527 }
528 }
529 }
530 unreachable!();
531 }
532}
533
534impl<T: StatefulSatSolver> FiniteStateTransitionSystemProver for IC3Stateful<T> {
539 fn new(fin_state: &FiniteStateTransitionSystem) -> Self {
540 IC3Stateful::new(fin_state, true)
541 }
542
543 fn prove(&mut self) -> ProofResult {
544 self.prove()
545 }
546}