rust_formal_verification/models/finite_state_transition_system/
features.rs

1// ************************************************************************************************
2// use
3// ************************************************************************************************
4
5use crate::algorithms::formula_logic::{does_a_imply_b, is_a_and_b_satisfiable};
6use crate::formulas::literal::VariableType;
7use crate::formulas::{Clause, Cube, Literal, CNF};
8use crate::solvers::sat::{Assignment, StatelessSatSolver};
9
10use super::FiniteStateTransitionSystem;
11
12// ************************************************************************************************
13// impl
14// ************************************************************************************************
15
16impl FiniteStateTransitionSystem {
17    // ********************************************************************************************
18    // helper functions
19    // ********************************************************************************************
20
21    fn bump_all_clause_variables_by_some_number(
22        original_clause: &Clause,
23        number_to_bump: VariableType,
24    ) -> Clause {
25        let mut literals = Vec::new();
26        for literal in original_clause.iter() {
27            assert_ne!(literal.get_number(), 0);
28            let mut new_number = literal.get_number();
29            new_number += number_to_bump;
30            let is_negated = literal.is_negated();
31            let new_lit = Literal::new(new_number).negate_if_true(is_negated);
32            literals.push(new_lit);
33        }
34        Clause::new(&literals)
35    }
36
37    fn bump_all_cnf_variables_by_some_number(
38        original_cnf: &CNF,
39        number_to_bump: VariableType,
40    ) -> CNF {
41        if number_to_bump == 0 {
42            // this makes the function faster for the simple case
43            original_cnf.to_owned()
44        } else {
45            let mut cnf_to_add_to = CNF::new();
46            for clause in original_cnf.iter() {
47                let new_clause =
48                    Self::bump_all_clause_variables_by_some_number(clause, number_to_bump);
49                cnf_to_add_to.add_clause(&new_clause);
50            }
51            cnf_to_add_to
52        }
53    }
54
55    // ********************************************************************************************
56    // aig api functions
57    // ********************************************************************************************
58
59    pub fn add_tags_to_relation(&self, relation: &CNF, number_of_tags: VariableType) -> CNF {
60        Self::bump_all_cnf_variables_by_some_number(
61            relation,
62            self.max_literal_number * number_of_tags,
63        )
64    }
65
66    pub fn add_tags_to_cube(&self, cube: &Cube, number_of_tags: VariableType) -> Cube {
67        if number_of_tags == 0 {
68            // this makes the function faster for the simple case
69            cube.to_owned()
70        } else {
71            let bumped_as_clause = Self::bump_all_clause_variables_by_some_number(
72                &(!(cube.to_owned())),
73                self.max_literal_number * number_of_tags,
74            );
75            !bumped_as_clause
76        }
77    }
78
79    pub fn add_tags_to_clause(&self, clause: &Clause, number_of_tags: VariableType) -> Clause {
80        if number_of_tags == 0 {
81            // this makes the function faster for the simple case
82            clause.to_owned()
83        } else {
84            Self::bump_all_clause_variables_by_some_number(
85                clause,
86                self.max_literal_number * number_of_tags,
87            )
88        }
89    }
90
91    pub fn is_cube_initial(&self, cube: &Cube) -> bool {
92        // check that cube contains no contradiction with initial.
93        for literal in cube.iter() {
94            if self.initial_literals.contains(&!literal.to_owned()) {
95                return false;
96            }
97        }
98        true
99    }
100
101    pub fn extract_state_from_assignment(&self, assignment: &Assignment) -> Cube {
102        let mut literals = Vec::new();
103
104        for state_lit_num in &self.state_literals {
105            literals.push(
106                Literal::new(state_lit_num.to_owned())
107                    .negate_if_true(!assignment.get_value(state_lit_num).unwrap()),
108            )
109        }
110
111        Cube::new(&literals)
112    }
113
114    pub fn extract_input_from_assignment(&self, assignment: &Assignment) -> Cube {
115        let mut literals = Vec::new();
116
117        for input_lit_num in &self.input_literals {
118            literals.push(
119                Literal::new(input_lit_num.to_owned())
120                    .negate_if_true(!assignment.get_value(input_lit_num).unwrap()),
121            )
122        }
123
124        Cube::new(&literals)
125    }
126
127    pub fn intersect_cube_with_cone_of_safety(&self, c: &Cube) -> Cube {
128        let filtered_c: Vec<Literal> = c
129            .iter()
130            .filter(|l| self.cone_of_safety_only_latches.contains(&l.get_number()))
131            .map(|l| l.to_owned())
132            .collect();
133        Cube::new(&filtered_c)
134    }
135
136    pub fn intersect_cube_with_cone_of_transition(&self, c: &Cube) -> Cube {
137        let filtered_c: Vec<Literal> = c
138            .iter()
139            .filter(|l| {
140                self.cone_of_transition_only_latches
141                    .contains(&l.get_number())
142            })
143            .map(|l| l.to_owned())
144            .collect();
145        Cube::new(&filtered_c)
146    }
147
148    // pub fn get_clone_of_cube(&self, c: &Cube) -> HashSet<VariableType> {
149    //     let mut set = HashSet::new();
150    //     set.reserve(self.state_literals.len());
151    //     for l in c.iter() {
152    //         let current_clone = self.cones_of_state_literals.get(&l.get_number()).unwrap();
153    //         for v in current_clone {
154    //             set.insert(v.to_owned());
155    //         }
156    //     }
157    //     set
158    // }
159
160    // pub fn intersect_cube_with_clone_of_other_cube(&self, c: &Cube, other: &Cube) -> Cube {
161    //     let cone_of_other = self.get_clone_of_cube(other);
162
163    //     let filtered_c: Vec<Literal> = c
164    //         .iter()
165    //         .filter(|l| cone_of_other.contains(&l.get_number()))
166    //         .map(|l| l.to_owned())
167    //         .collect();
168    //     Cube::new(&filtered_c)
169    // }
170
171    pub fn check_invariant<T: StatelessSatSolver>(&self, inv_candidate: &CNF) {
172        // println!("inv_candidate = {}", inv_candidate);
173        // check INIT -> inv_candidate
174        let mut init = self.get_initial_relation().to_cnf();
175        init.append(&self.get_state_to_safety_translation());
176        // println!("init = {}", init);
177
178        assert!(
179            does_a_imply_b::<T>(&init, inv_candidate),
180            "Invariant does not cover all of init."
181        );
182
183        // check inv_candidate && Tr -> inv_candidate'
184        let mut a = self.get_transition_relation();
185        a.append(inv_candidate);
186        a.append(&self.get_state_to_safety_translation());
187        a.append(&self.add_tags_to_relation(&self.get_state_to_safety_translation(), 1));
188        let b = self.add_tags_to_relation(inv_candidate, 1);
189        assert!(
190            does_a_imply_b::<T>(&a, &b),
191            "Invariant doesn't cover all of the reachable states."
192        );
193
194        // check inv_candidate ^ !p is un-sat
195        let mut bad = self.get_unsafety_property().to_cnf();
196        bad.append(&self.get_state_to_safety_translation());
197        assert!(
198            !is_a_and_b_satisfiable::<T>(inv_candidate, &bad),
199            "Invariant isn't always safe.",
200        );
201    }
202}