espresso_logic/expression/
bdd.rs1use super::manager::{BddNode, NodeId, VarId, FALSE_NODE, TRUE_NODE};
7use super::BoolExpr;
8use std::collections::{BTreeMap, BTreeSet, HashMap};
9use std::sync::Arc;
10
11impl BoolExpr {
12 pub fn collect_variables(&self) -> BTreeSet<Arc<str>> {
17 let mut var_ids = std::collections::HashSet::new();
18 self.collect_var_ids(self.root, &mut var_ids);
19
20 let mgr = self.manager.read().unwrap();
22 var_ids
23 .into_iter()
24 .filter_map(|id| mgr.var_name(id).cloned())
25 .collect()
26 }
27
28 fn collect_var_ids(&self, node: NodeId, vars: &mut std::collections::HashSet<VarId>) {
30 let node_info = {
33 let inner = self.manager.read().unwrap();
34 match inner.get_node(node) {
35 Some(BddNode::Terminal(_)) => None,
36 Some(BddNode::Decision { var, low, high }) => Some((*var, *low, *high)),
37 None => {
38 panic!("Invalid node ID {} encountered during variable collection - this indicates a bug in the BDD implementation", node);
39 }
40 }
41 }; if let Some((var, low, high)) = node_info {
44 if vars.insert(var) {
45 self.collect_var_ids(low, vars);
46 self.collect_var_ids(high, vars);
47 }
48 }
49 }
50
51 pub fn to_cubes(&self) -> Vec<BTreeMap<Arc<str>, bool>> {
60 let dnf = self.get_or_create_dnf();
61 dnf.cubes().to_vec()
62 }
63
64 pub(super) fn extract_cubes_from_bdd(&self) -> Vec<BTreeMap<Arc<str>, bool>> {
69 let mut results = Vec::new();
70 let mut current_path = BTreeMap::new();
71 self.extract_cubes(self.root, &mut current_path, &mut results);
72 results
73 }
74
75 fn extract_cubes(
77 &self,
78 node: NodeId,
79 current_path: &mut BTreeMap<Arc<str>, bool>,
80 results: &mut Vec<BTreeMap<Arc<str>, bool>>,
81 ) {
82 let node_info = {
85 let inner = self.manager.read().unwrap();
86 match inner.get_node(node) {
87 Some(BddNode::Terminal(true)) => Some((true, None)),
88 Some(BddNode::Terminal(false)) => Some((false, None)),
89 Some(BddNode::Decision { var, low, high }) => {
90 let var_name = inner.var_name(*var)
91 .expect("Invalid variable ID encountered during cube extraction - this indicates a bug in the BDD implementation");
92 Some((false, Some((Arc::clone(var_name), *low, *high))))
93 }
94 None => {
95 panic!("Invalid node ID {} encountered during cube extraction - this indicates a bug in the BDD implementation", node);
96 }
97 }
98 }; match node_info {
101 Some((true, None)) => {
102 results.push(current_path.clone());
104 }
105 Some((false, None)) => {
106 }
108 Some((false, Some((var_name, low, high)))) => {
109 current_path.insert(Arc::clone(&var_name), false);
111 self.extract_cubes(low, current_path, results);
112 current_path.remove(&var_name);
113
114 current_path.insert(Arc::clone(&var_name), true);
116 self.extract_cubes(high, current_path, results);
117 current_path.remove(&var_name);
118 }
119 _ => unreachable!(),
120 }
121 }
122
123 pub fn is_terminal(&self) -> bool {
125 self.root == TRUE_NODE || self.root == FALSE_NODE
126 }
127
128 pub fn is_true(&self) -> bool {
130 self.root == TRUE_NODE
131 }
132
133 pub fn is_false(&self) -> bool {
135 self.root == FALSE_NODE
136 }
137
138 pub fn node_count(&self) -> usize {
140 self.count_reachable_nodes(self.root, &mut HashMap::new())
141 }
142
143 fn count_reachable_nodes(&self, node: NodeId, visited: &mut HashMap<NodeId, ()>) -> usize {
145 if visited.contains_key(&node) {
146 return 0;
147 }
148 visited.insert(node, ());
149
150 let (is_terminal, low, high) = {
153 let inner = self.manager.read().unwrap();
154 match inner.get_node(node) {
155 Some(BddNode::Terminal(_)) => (true, 0, 0),
156 Some(BddNode::Decision { low, high, .. }) => (false, *low, *high),
157 None => {
158 panic!("Invalid node ID {} encountered during node counting - this indicates a bug in the BDD implementation", node);
159 }
160 }
161 }; if is_terminal {
164 1
165 } else {
166 1 + self.count_reachable_nodes(low, visited) + self.count_reachable_nodes(high, visited)
167 }
168 }
169
170 pub fn var_count(&self) -> usize {
172 let mut vars = std::collections::HashSet::new();
173 self.collect_var_ids(self.root, &mut vars);
174 vars.len()
175 }
176}