rust_formal_verification/algorithms/proof/
ic3_stateless_solver.rs1use crate::{
23 formulas::{literal::VariableType, Clause, Cube, Literal, CNF},
24 models::FiniteStateTransitionSystem,
25 solvers::sat::{SatResponse, StatelessSatSolver},
26};
27use priority_queue::PriorityQueue;
28use rand::rngs::ThreadRng;
29use rand::seq::SliceRandom;
30use rand::thread_rng;
31use std::cmp::{max, Reverse};
32use std::time;
33
34use super::{FiniteStateTransitionSystemProver, ProofResult};
35
36enum StrengthenResult {
41 Success,
42 Failure { _depth: VariableType },
43}
44
45enum InductivelyGeneralizeResult {
46 Success { n: usize },
47 Failure,
48}
49
50enum PushGeneralizeResult {
51 Success,
52 Failure,
53}
54
55pub struct IC3Stateless<T> {
60 clauses: Vec<CNF>,
62 fin_state: FiniteStateTransitionSystem,
63 solver: T,
64 rng: ThreadRng,
65
66 initial: CNF,
68 transition: CNF,
69 p0: CNF,
70 not_p0: CNF,
71 not_p1: CNF,
72
73 verbose: bool,
75 number_of_sat_calls: u32,
76 time_in_sat_calls: time::Duration,
77 start_time: time::Instant,
78}
79
80impl<T: StatelessSatSolver> IC3Stateless<T> {
85 fn sat_call(&mut self, cnf_to_solve: &CNF) -> SatResponse {
90 self.number_of_sat_calls += 1;
91 let start_time = time::Instant::now();
92 let result = self.solver.solve_cnf(cnf_to_solve);
93 self.time_in_sat_calls += start_time.elapsed();
94 result
95 }
96
97 fn get_fk(&self, k: usize) -> CNF {
102 let mut clauses_fk = self.clauses[k].to_owned();
103 if k == 0 {
104 clauses_fk.append(&self.initial);
105 } else {
106 clauses_fk.append(&self.p0);
107 }
108 clauses_fk
109 }
110
111 fn is_bad_reached_in_0_steps(&mut self) -> SatResponse {
112 let mut cnf = CNF::new();
113 cnf.append(&self.initial);
114 cnf.append(&self.not_p0);
115 self.sat_call(&cnf)
117 }
118
119 fn is_bad_reached_in_1_steps(&mut self) -> SatResponse {
120 let mut cnf = CNF::new();
121 cnf.append(&self.initial);
122 cnf.append(&self.transition);
123 cnf.append(&self.not_p1);
124 self.sat_call(&cnf)
126 }
127
128 fn propagate_clauses(&mut self, k: usize) {
129 for i in 1..(k + 1) {
130 let clauses_fi = self.clauses[i].to_owned();
131 for c in clauses_fi.iter() {
132 let mut cnf = CNF::new();
133 cnf.append(&self.get_fk(i));
134 cnf.append(&self.transition);
135 cnf.append(
136 &self
137 .fin_state
138 .add_tags_to_relation(&(!(c.to_owned())).to_cnf(), 1),
139 );
140 match self.sat_call(&cnf) {
141 SatResponse::UnSat => {
142 self.clauses[i + 1].add_clause(c);
144 }
145 SatResponse::Sat { assignment: _ } => {
146 }
148 }
149 }
150 }
151 }
152
153 fn is_bad_reached_in_1_step_from_cnf(&mut self, cnf: &CNF) -> SatResponse {
154 let mut new_cnf = CNF::new();
155 new_cnf.append(cnf);
156 new_cnf.append(&self.transition);
157 new_cnf.append(&self.not_p1);
158 self.sat_call(&new_cnf)
159 }
160
161 fn is_fi_and_t_and_not_s_and_s_tag_sat(&mut self, i: usize, s: &Cube) -> bool {
163 let mut new_cnf = CNF::new();
164 new_cnf.append(&self.get_fk(i));
165 new_cnf.append(&self.transition);
166 new_cnf.add_clause(&!(s.to_owned()));
167 new_cnf.append(&self.fin_state.add_tags_to_relation(&s.to_cnf(), 1));
168 match self.sat_call(&new_cnf) {
169 SatResponse::UnSat => false,
170 SatResponse::Sat { assignment: _ } => true,
171 }
172 }
173
174 fn is_clause_inductive_relative_to_fi(&mut self, d: &Clause, i: usize) -> bool {
175 let mut first_cnf = self.initial.to_owned();
178 first_cnf.append(&(!d.to_owned()).to_cnf());
179 match self.sat_call(&first_cnf) {
180 SatResponse::UnSat => {}
181 SatResponse::Sat { assignment: _ } => {
182 return false;
183 }
184 }
185
186 let mut second_cnf = self.get_fk(i);
187 second_cnf.add_clause(d);
188 second_cnf.append(&self.transition);
189 second_cnf.append(
190 &self
191 .fin_state
192 .add_tags_to_relation(&(!d.to_owned()).to_cnf(), 1),
193 );
194 match self.sat_call(&second_cnf) {
195 SatResponse::UnSat => true,
196 SatResponse::Sat { assignment: _ } => false,
197 }
198 }
199
200 fn get_subclause_of_not_s_that_is_inductive_relative_to_fi(
201 &mut self,
202 s: &Cube,
203 i: usize,
204 ) -> Clause {
205 let c = !(s.to_owned());
206 let mut c_literals: Vec<Literal> = c.iter().map(|l| l.to_owned()).collect();
207 c_literals.shuffle(&mut self.rng);
208 let mut j = 0;
209 while j < c_literals.len() {
210 let removed = c_literals.swap_remove(j);
211 let d = Clause::new(&c_literals);
212 if self.is_clause_inductive_relative_to_fi(&d, i) {
213 } else {
215 c_literals.push(removed);
217 let last_index = c_literals.len() - 1;
218 c_literals.swap(j, last_index);
219 j += 1;
221 }
222 }
223 Clause::new(&c_literals)
224 }
225
226 fn generate_clause(&mut self, s: &Cube, i: usize, _k: usize) {
227 let c = self.get_subclause_of_not_s_that_is_inductive_relative_to_fi(s, i);
228 for j in 1..(i + 2) {
229 self.clauses[j].add_clause(&c);
230 }
231 }
232
233 fn inductively_generalize(
234 &mut self,
235 s: &Cube,
236 min: isize,
237 k: usize,
238 ) -> InductivelyGeneralizeResult {
239 if min < 0 && self.is_fi_and_t_and_not_s_and_s_tag_sat(0, s) {
240 return InductivelyGeneralizeResult::Failure;
241 }
242
243 for i in max(1, min + 1).try_into().unwrap()..(k + 1) {
244 if self.is_fi_and_t_and_not_s_and_s_tag_sat(i, s) {
245 self.generate_clause(s, i - 1, k);
246 return InductivelyGeneralizeResult::Success { n: i - 1 };
247 }
248 }
249 self.generate_clause(s, k, k);
250 InductivelyGeneralizeResult::Success { n: k }
251 }
252
253 fn solve_fi_and_t_and_s_tag(&mut self, i: usize, s: &Cube) -> SatResponse {
255 let mut new_cnf = CNF::new();
256 new_cnf.append(&self.get_fk(i));
257 new_cnf.append(&self.transition);
258 new_cnf.append(&self.fin_state.add_tags_to_relation(&s.to_cnf(), 1));
259 self.sat_call(&new_cnf)
260 }
261
262 fn push_generalization(
263 &mut self,
264 states: &PriorityQueue<Cube, Reverse<usize>>,
265 k: usize,
266 ) -> PushGeneralizeResult {
267 let mut states = states.to_owned();
268 loop {
269 let (s, reversed_n) = states.pop().unwrap();
270 let n = reversed_n.0;
271 if n > k {
272 return PushGeneralizeResult::Success;
273 }
274 match self.solve_fi_and_t_and_s_tag(n, &s) {
275 SatResponse::Sat { assignment } => {
276 let p = self.fin_state.extract_state_from_assignment(&assignment);
278 match self.inductively_generalize(
280 &p,
281 <usize as TryInto<isize>>::try_into(n).unwrap() - 2,
282 k,
283 ) {
284 InductivelyGeneralizeResult::Failure => {
285 return PushGeneralizeResult::Failure;
286 }
287 InductivelyGeneralizeResult::Success { n: m } => {
288 states.push(s, reversed_n);
289 states.push(p, Reverse(m + 1));
290 }
291 }
292 }
293 SatResponse::UnSat => {
294 match self.inductively_generalize(&s, n.try_into().unwrap(), k) {
296 InductivelyGeneralizeResult::Failure => {
297 return PushGeneralizeResult::Failure;
298 }
299 InductivelyGeneralizeResult::Success { n: m } => {
300 states.push(s.to_owned(), Reverse(m + 1));
301 }
302 }
303 }
304 }
305 }
306 }
307
308 fn print_progress_if_verbose(&self, k: usize) {
309 if self.verbose {
310 let clauses = self
311 .clauses
312 .iter()
313 .map(|c| c.len())
314 .rev()
315 .take(10)
316 .collect::<Vec<usize>>();
317 println!("IC3 is on k = {}, clauses lengths = {:?}", k, clauses);
318 println!("Number of SAT calls = {}", self.number_of_sat_calls);
319 println!(
320 "Time since start = {}",
321 self.start_time.elapsed().as_secs_f32()
322 );
323 println!(
324 "Time in SAT calls = {}",
325 self.time_in_sat_calls.as_secs_f32()
326 );
327 }
328 }
329
330 fn strengthen(&mut self, k: usize) -> StrengthenResult {
331 loop {
332 match self.is_bad_reached_in_1_step_from_cnf(&self.get_fk(k)) {
333 SatResponse::UnSat => {
334 break;
335 }
336 SatResponse::Sat { assignment } => {
337 let s = self.fin_state.extract_state_from_assignment(&assignment);
338 match self.inductively_generalize(
340 &s,
341 <usize as TryInto<isize>>::try_into(k).unwrap() - 2,
342 k,
343 ) {
344 InductivelyGeneralizeResult::Failure => {
345 return StrengthenResult::Failure {
346 _depth: k.try_into().unwrap(),
347 };
348 }
349 InductivelyGeneralizeResult::Success { n } => {
350 let mut queue = PriorityQueue::<Cube, Reverse<usize>>::new();
351 queue.push(s, Reverse(n + 1));
352 match self.push_generalization(&queue, k) {
353 PushGeneralizeResult::Failure => {
354 return StrengthenResult::Failure {
355 _depth: k.try_into().unwrap(),
356 };
357 }
358 PushGeneralizeResult::Success => {}
359 };
360 }
361 };
362 }
363 }
364 }
365
366 StrengthenResult::Success
367 }
368
369 pub fn new(fin_state: &FiniteStateTransitionSystem, verbose: bool) -> Self {
374 let mut p0 = fin_state.get_state_to_safety_translation();
375 p0.append(&fin_state.get_safety_property().to_cnf());
376
377 let mut not_p0 = fin_state.get_state_to_safety_translation();
378 not_p0.append(&fin_state.get_unsafety_property().to_cnf());
379
380 Self {
381 clauses: Vec::new(),
382 fin_state: fin_state.to_owned(),
383 solver: T::default(),
384 rng: thread_rng(),
385 initial: fin_state.get_initial_relation().to_cnf(),
386 transition: fin_state.get_transition_relation(),
387 p0,
388 not_p0: not_p0.to_owned(),
389 not_p1: fin_state.add_tags_to_relation(¬_p0, 1),
390 verbose,
391 number_of_sat_calls: 0,
392 time_in_sat_calls: time::Duration::from_secs(0),
393 start_time: time::Instant::now(),
394 }
395 }
396
397 pub fn prove(&mut self) -> ProofResult {
398 self.start_time = time::Instant::now();
400
401 let init_and_not_p = self.is_bad_reached_in_0_steps();
402 match init_and_not_p {
403 SatResponse::Sat { assignment: _ } => return ProofResult::CTX { depth: 0 },
404 SatResponse::UnSat => (),
405 }
406 let init_and_tr_and_not_p_tag = self.is_bad_reached_in_1_steps();
409 match init_and_tr_and_not_p_tag {
410 SatResponse::Sat { assignment: _ } => return ProofResult::CTX { depth: 1 },
411 SatResponse::UnSat => (),
412 }
413
414 self.clauses.push(CNF::new());
415 self.clauses.push(CNF::new());
416 for k in 1.. {
417 self.clauses.push(CNF::new());
418 self.print_progress_if_verbose(k);
420 debug_assert_eq!(self.clauses.len(), (k + 2));
421 match self.strengthen(k) {
422 StrengthenResult::Success => {}
423 StrengthenResult::Failure { _depth: _ } => {
424 return ProofResult::CTX {
425 depth: k.try_into().unwrap(),
426 };
427 }
428 };
429 self.propagate_clauses(k);
430 for i in 1..(k + 1) {
431 debug_assert!(self.clauses[i + 1]
433 .iter()
434 .all(|c| self.clauses[i].contains(c)));
435 if self.clauses[i].len() == self.clauses[i + 1].len() {
436 self.print_progress_if_verbose(k);
438 return ProofResult::Proof {
439 invariant: self.get_fk(i),
440 };
441 }
442 }
443 }
444 unreachable!();
445 }
446}
447
448impl<T: StatelessSatSolver> FiniteStateTransitionSystemProver for IC3Stateless<T> {
453 fn new(fin_state: &FiniteStateTransitionSystem) -> Self {
454 IC3Stateless::new(fin_state, true)
455 }
456
457 fn prove(&mut self) -> ProofResult {
458 self.prove()
459 }
460}