1use crate::ir::{VerifyExpr, VerifyOp};
17use crate::equivalence::Trace;
18use crate::kinduction;
19use std::collections::{HashMap, HashSet};
20use z3::{ast::Bool, SatResult};
21
22#[derive(Debug, Clone)]
23pub struct AbstractModel {
24 pub predicates: Vec<VerifyExpr>,
25 pub abstract_init: VerifyExpr,
26 pub abstract_transition: VerifyExpr,
27}
28
29#[derive(Debug)]
30pub enum AbstractionResult {
31 Safe,
32 Unsafe { concrete_trace: Trace },
33 SpuriousRefined { new_predicates: Vec<VerifyExpr> },
34 Unknown,
35}
36
37pub fn abstract_model(
43 init: &VerifyExpr,
44 predicates: &[VerifyExpr],
45) -> AbstractModel {
46 let abstract_init = if predicates.is_empty() {
47 init.clone()
48 } else {
49 let mut abs = init.clone();
50 for pred in predicates {
51 abs = VerifyExpr::and(abs, pred.clone());
52 }
53 abs
54 };
55
56 AbstractModel {
57 predicates: predicates.to_vec(),
58 abstract_init,
59 abstract_transition: VerifyExpr::bool(true),
60 }
61}
62
63pub fn abstract_model_full(
77 init: &VerifyExpr,
78 transition: &VerifyExpr,
79 predicates: &[VerifyExpr],
80) -> AbstractModel {
81 if predicates.is_empty() {
82 return AbstractModel {
83 predicates: vec![],
84 abstract_init: init.clone(),
85 abstract_transition: transition.clone(),
86 };
87 }
88
89 let mut abstract_init = init.clone();
91 for pred in predicates {
92 abstract_init = VerifyExpr::and(abstract_init, pred.clone());
93 }
94
95 let mut abstract_trans_parts: Vec<VerifyExpr> = Vec::new();
98 abstract_trans_parts.push(transition.clone());
99
100
101 for pred in predicates {
102 let pred_next = replace_t_with_t1(pred);
103
104 let check = VerifyExpr::and(
107 pred.clone(),
108 VerifyExpr::and(
109 transition.clone(),
110 VerifyExpr::not(pred_next.clone()),
111 ),
112 );
113
114 let check_inst = kinduction::instantiate_transition(
116 &kinduction::instantiate_at(&check, 0),
117 0,
118 );
119
120 let is_inductive = {
121 let solver = crate::solver::new_solver();
122 let encoded = encode_bool(&check_inst);
123 solver.assert(&encoded);
124 matches!(solver.check(), SatResult::Unsat)
125 };
126
127 if is_inductive {
128 let implication = VerifyExpr::implies(
130 VerifyExpr::and(pred.clone(), transition.clone()),
131 pred_next,
132 );
133 abstract_trans_parts.push(implication);
134 } else {
135 let soft = VerifyExpr::implies(
144 pred.clone(),
145 VerifyExpr::or(pred_next.clone(), VerifyExpr::not(pred_next)),
146 );
147 abstract_trans_parts.push(soft);
148 }
149 }
150
151 let mut abstract_transition = abstract_trans_parts[0].clone();
152 for part in &abstract_trans_parts[1..] {
153 abstract_transition = VerifyExpr::and(abstract_transition, part.clone());
154 }
155
156 AbstractModel {
157 predicates: predicates.to_vec(),
158 abstract_init,
159 abstract_transition,
160 }
161}
162
163fn replace_t_with_t1(expr: &VerifyExpr) -> VerifyExpr {
165 match expr {
166 VerifyExpr::Var(name) => {
167 if name.ends_with("@t") {
168 let base = &name[..name.len() - 2];
169 VerifyExpr::Var(format!("{}@t1", base))
170 } else {
171 VerifyExpr::Var(name.clone())
172 }
173 }
174 VerifyExpr::Binary { op, left, right } => VerifyExpr::binary(
175 *op,
176 replace_t_with_t1(left),
177 replace_t_with_t1(right),
178 ),
179 VerifyExpr::Not(inner) => VerifyExpr::not(replace_t_with_t1(inner)),
180 VerifyExpr::Bool(b) => VerifyExpr::Bool(*b),
181 VerifyExpr::Int(n) => VerifyExpr::Int(*n),
182 VerifyExpr::Iff(l, r) => VerifyExpr::iff(replace_t_with_t1(l), replace_t_with_t1(r)),
183 VerifyExpr::ForAll { vars, body } => VerifyExpr::forall(
184 vars.clone(),
185 replace_t_with_t1(body),
186 ),
187 VerifyExpr::Exists { vars, body } => VerifyExpr::exists(
188 vars.clone(),
189 replace_t_with_t1(body),
190 ),
191 VerifyExpr::ApplyInt { name, args } => VerifyExpr::apply_int(
192 name.clone(),
193 args.iter().map(|a| replace_t_with_t1(a)).collect(),
194 ),
195 VerifyExpr::Apply { name, args } => VerifyExpr::apply(
196 name.clone(),
197 args.iter().map(|a| replace_t_with_t1(a)).collect(),
198 ),
199 VerifyExpr::BitVecConst { width, value } => VerifyExpr::bv_const(*width, *value),
200 VerifyExpr::BitVecBinary { op, left, right } => VerifyExpr::bv_binary(
201 *op,
202 replace_t_with_t1(left),
203 replace_t_with_t1(right),
204 ),
205 VerifyExpr::BitVecExtract { high, low, operand } => VerifyExpr::BitVecExtract {
206 high: *high,
207 low: *low,
208 operand: Box::new(replace_t_with_t1(operand)),
209 },
210 VerifyExpr::BitVecConcat(l, r) => VerifyExpr::BitVecConcat(
211 Box::new(replace_t_with_t1(l)),
212 Box::new(replace_t_with_t1(r)),
213 ),
214 VerifyExpr::Select { array, index } => VerifyExpr::Select {
215 array: Box::new(replace_t_with_t1(array)),
216 index: Box::new(replace_t_with_t1(index)),
217 },
218 VerifyExpr::Store { array, index, value } => VerifyExpr::Store {
219 array: Box::new(replace_t_with_t1(array)),
220 index: Box::new(replace_t_with_t1(index)),
221 value: Box::new(replace_t_with_t1(value)),
222 },
223 VerifyExpr::AtState { state, expr } => VerifyExpr::AtState {
224 state: Box::new(replace_t_with_t1(state)),
225 expr: Box::new(replace_t_with_t1(expr)),
226 },
227 VerifyExpr::Transition { from, to } => VerifyExpr::Transition {
228 from: Box::new(replace_t_with_t1(from)),
229 to: Box::new(replace_t_with_t1(to)),
230 },
231 }
232}
233
234pub fn cegar_verify(
243 init: &VerifyExpr,
244 transition: &VerifyExpr,
245 property: &VerifyExpr,
246 initial_predicates: &[VerifyExpr],
247 max_refinements: u32,
248) -> AbstractionResult {
249 let mut predicates = initial_predicates.to_vec();
250
251 for _iteration in 0..max_refinements {
252 let abs_model = abstract_model_full(init, transition, &predicates);
254
255 let abs_result = kinduction::k_induction(
257 &abs_model.abstract_init,
258 &abs_model.abstract_transition,
259 property,
260 &[],
261 10,
262 );
263
264 match abs_result {
265 kinduction::KInductionResult::Proven { .. } => {
266 return AbstractionResult::Safe;
271 }
272 kinduction::KInductionResult::Counterexample { trace: _, k } => {
273 let concrete_result = kinduction::k_induction(
276 init, transition, property, &[], k.max(10),
277 );
278
279 match concrete_result {
280 kinduction::KInductionResult::Counterexample { trace: ctrace, .. } => {
281 return AbstractionResult::Unsafe { concrete_trace: ctrace };
282 }
283 kinduction::KInductionResult::Proven { .. } => {
284 let new_preds = extract_refinement_predicates(
286 init, transition, property, &predicates, k,
287 );
288 if new_preds.is_empty() {
289 return AbstractionResult::SpuriousRefined {
290 new_predicates: predicates,
291 };
292 }
293 predicates.extend(new_preds);
294 }
295 _ => {
296 let new_preds = extract_refinement_predicates(
298 init, transition, property, &predicates, k,
299 );
300 if new_preds.is_empty() {
301 return AbstractionResult::SpuriousRefined {
302 new_predicates: predicates,
303 };
304 }
305 predicates.extend(new_preds);
306 }
307 }
308 }
309 kinduction::KInductionResult::InductionFailed { k, .. } => {
310 let concrete_result = kinduction::k_induction(
313 init, transition, property, &[], k.max(10),
314 );
315 match concrete_result {
316 kinduction::KInductionResult::Proven { .. } => {
317 return AbstractionResult::Safe;
318 }
319 kinduction::KInductionResult::Counterexample { trace, .. } => {
320 return AbstractionResult::Unsafe { concrete_trace: trace };
321 }
322 _ => {
323 let new_preds = extract_refinement_predicates(
324 init, transition, property, &predicates, k,
325 );
326 if new_preds.is_empty() {
327 return AbstractionResult::Unknown;
328 }
329 predicates.extend(new_preds);
330 }
331 }
332 }
333 kinduction::KInductionResult::Unknown => {
334 return AbstractionResult::Unknown;
335 }
336 }
337 }
338
339 let final_result = kinduction::k_induction(init, transition, property, &[], 10);
341 match final_result {
342 kinduction::KInductionResult::Proven { .. } => AbstractionResult::Safe,
343 kinduction::KInductionResult::Counterexample { trace, .. } => {
344 AbstractionResult::Unsafe { concrete_trace: trace }
345 }
346 _ => AbstractionResult::Unknown,
347 }
348}
349
350fn extract_refinement_predicates(
363 init: &VerifyExpr,
364 transition: &VerifyExpr,
365 property: &VerifyExpr,
366 existing_predicates: &[VerifyExpr],
367 _depth: u32,
368) -> Vec<VerifyExpr> {
369 let mut candidates = Vec::new();
370
371 collect_comparison_predicates(transition, &mut candidates);
373 collect_comparison_predicates(init, &mut candidates);
374 collect_comparison_predicates(property, &mut candidates);
375
376 for pred in existing_predicates {
378 if let VerifyExpr::Binary { op, left, right } = pred {
379 match op {
380 VerifyOp::Gte | VerifyOp::Gt | VerifyOp::Lte | VerifyOp::Lt => {
381 if let VerifyExpr::Int(n) = right.as_ref() {
382 candidates.push(VerifyExpr::binary(*op, *left.clone(), VerifyExpr::int(n + 1)));
383 candidates.push(VerifyExpr::binary(*op, *left.clone(), VerifyExpr::int(n - 1)));
384 }
385 }
386 _ => {}
387 }
388 }
389 }
390
391 let existing_dbg: HashSet<String> = existing_predicates.iter()
393 .map(|p| format!("{:?}", p))
394 .collect();
395
396 let mut seen = HashSet::new();
397 let mut result = Vec::new();
398 for candidate in candidates {
399 let dbg = format!("{:?}", candidate);
400 if !existing_dbg.contains(&dbg) && seen.insert(dbg) {
401 result.push(candidate);
402 }
403 }
404
405 result
406}
407
408fn collect_comparison_predicates(expr: &VerifyExpr, out: &mut Vec<VerifyExpr>) {
410 match expr {
411 VerifyExpr::Binary { op, left, right } => {
412 match op {
413 VerifyOp::Gte | VerifyOp::Gt | VerifyOp::Lte | VerifyOp::Lt
414 | VerifyOp::Eq | VerifyOp::Neq => {
415 out.push(expr.clone());
416 }
417 _ => {}
418 }
419 collect_comparison_predicates(left, out);
420 collect_comparison_predicates(right, out);
421 }
422 VerifyExpr::Not(inner) => collect_comparison_predicates(inner, out),
423 VerifyExpr::Iff(l, r) => {
424 collect_comparison_predicates(l, out);
425 collect_comparison_predicates(r, out);
426 }
427 VerifyExpr::ForAll { body, .. } | VerifyExpr::Exists { body, .. } => {
428 collect_comparison_predicates(body, out);
429 }
430 _ => {}
431 }
432}
433
434fn encode_bool(expr: &VerifyExpr) -> Bool {
436 let mut bool_vars = HashMap::new();
437 let mut int_vars = HashMap::new();
438 let mut all_vars = HashSet::new();
439 crate::equivalence::collect_vars_pub(expr, &mut all_vars);
440 for name in &all_vars {
441 bool_vars.insert(name.clone(), Bool::new_const(name.as_str()));
442 }
443 crate::equivalence::collect_int_vars_pub(expr, &mut int_vars);
444 kinduction::encode_expr_bool(expr, &bool_vars, &int_vars)
445}