logicaffeine_verify/
compositional.rs1use crate::ir::VerifyExpr;
8use crate::equivalence::Trace;
9use crate::kinduction;
10use std::collections::HashMap;
11
12#[derive(Debug, Clone)]
14pub struct ComponentSpec {
15 pub name: String,
16 pub assumes: Vec<VerifyExpr>,
17 pub guarantees: Vec<VerifyExpr>,
18 pub init: VerifyExpr,
19 pub transition: VerifyExpr,
20}
21
22#[derive(Debug)]
24pub enum CompositionalResult {
25 AllVerified,
27 ComponentFailed { name: String, trace: Trace },
29 CircularDependency { components: Vec<String> },
31 Unknown,
33}
34
35pub fn verify_compositional(components: &[ComponentSpec]) -> CompositionalResult {
42 if components.is_empty() {
43 return CompositionalResult::AllVerified;
44 }
45
46
47 for comp in components {
49 for guarantee in &comp.guarantees {
50 let assumptions = if comp.assumes.is_empty() {
52 VerifyExpr::bool(true)
53 } else {
54 comp.assumes.iter().cloned().reduce(|a, b| VerifyExpr::and(a, b)).unwrap()
55 };
56
57 let strengthened_init = VerifyExpr::and(comp.init.clone(), assumptions.clone());
59 let strengthened_transition = VerifyExpr::and(comp.transition.clone(), assumptions.clone());
60
61 let result = kinduction::k_induction(
62 &strengthened_init,
63 &strengthened_transition,
64 guarantee,
65 &[],
66 10,
67 );
68
69 match result {
70 kinduction::KInductionResult::Counterexample { trace, .. } => {
71 return CompositionalResult::ComponentFailed {
72 name: comp.name.clone(),
73 trace,
74 };
75 }
76 kinduction::KInductionResult::Proven { .. } => {
77 }
79 _ => {
80 let ic3_result = crate::ic3::ic3(
82 &strengthened_init,
83 &strengthened_transition,
84 guarantee,
85 10,
86 );
87 match ic3_result {
88 crate::ic3::Ic3Result::Safe { .. } => {}
89 crate::ic3::Ic3Result::Unsafe { trace } => {
90 return CompositionalResult::ComponentFailed {
91 name: comp.name.clone(),
92 trace,
93 };
94 }
95 _ => return CompositionalResult::Unknown,
96 }
97 }
98 }
99 }
100 }
101
102 let all_guarantees: Vec<VerifyExpr> = components.iter()
106 .flat_map(|c| c.guarantees.clone())
107 .collect();
108
109 if !all_guarantees.is_empty() {
110 let guarantees_conj = all_guarantees.iter().cloned()
111 .reduce(|a, b| VerifyExpr::and(a, b)).unwrap();
112
113 for comp in components {
114 for assumption in &comp.assumes {
115 let check = VerifyExpr::implies(guarantees_conj.clone(), assumption.clone());
117 let solver = crate::solver::new_solver();
118 let not_check = VerifyExpr::not(check);
119 let encoded = encode_bool(¬_check);
120 solver.assert(&encoded);
121 match solver.check() {
122 z3::SatResult::Sat => {
123 return CompositionalResult::CircularDependency {
126 components: vec![comp.name.clone()],
127 };
128 }
129 z3::SatResult::Unsat => {} z3::SatResult::Unknown => return CompositionalResult::Unknown,
131 }
132 }
133 }
134 }
135
136 CompositionalResult::AllVerified
137}
138
139fn encode_bool(expr: &VerifyExpr) -> z3::ast::Bool {
140 let mut bool_vars = HashMap::new();
141 let mut int_vars = HashMap::new();
142 let mut all_vars = std::collections::HashSet::new();
143 crate::equivalence::collect_vars_pub(expr, &mut all_vars);
144 for name in &all_vars {
145 bool_vars.insert(name.clone(), z3::ast::Bool::new_const(name.as_str()));
146 }
147 crate::equivalence::collect_int_vars_pub(expr, &mut int_vars);
148 kinduction::encode_expr_bool(expr, &bool_vars, &int_vars)
149}