1use crate::{
2 core::{EdgeIndex, Node, NodeIndex},
3 DecisionDNNF, Literal,
4};
5use rug::Integer;
6use std::sync::OnceLock;
7
8static INTEGER_ZERO: Integer = Integer::ZERO;
9
10pub struct ModelCounter<'a> {
26 ddnnf: &'a DecisionDNNF,
27 assumptions: Option<Vec<Option<bool>>>,
28 n_models: OnceLock<Vec<Option<Integer>>>,
29 partial_models: bool,
30}
31
32impl<'a> ModelCounter<'a> {
33 #[must_use]
37 pub fn new(ddnnf: &'a DecisionDNNF, partial_models: bool) -> Self {
38 Self {
39 ddnnf,
40 assumptions: None,
41 n_models: OnceLock::new(),
42 partial_models,
43 }
44 }
45
46 pub fn set_assumptions(&mut self, assumptions: &[Literal]) {
55 let mut assumps = vec![None; self.ddnnf.n_vars()];
56 for a in assumptions {
57 assert!(a.var_index() < self.ddnnf.n_vars(), "undefined variable");
58 assert!(
59 assumps[a.var_index()].replace(a.polarity()).is_none(),
60 "multiple definition of the same variable in assumptions"
61 );
62 }
63 self.assumptions = Some(assumps);
64 self.n_models.take();
65 }
66
67 fn get_or_compute_n_models(&self) -> &[Option<Integer>] {
68 self.n_models.get_or_init(|| {
69 let (n_root_free_vars, or_children_free_vars_len) = self.free_vars_params();
70 let mut n_models = vec![None; self.ddnnf.nodes().as_slice().len()];
71 compute_models_from(
72 self.ddnnf,
73 n_root_free_vars,
74 &or_children_free_vars_len,
75 NodeIndex::from(0),
76 &mut n_models,
77 self.assumptions.as_deref(),
78 );
79 n_models
80 })
81 }
82
83 fn free_vars_params(&self) -> (usize, Vec<Vec<usize>>) {
84 if self.partial_models {
85 (
86 0,
87 self.ddnnf
88 .nodes()
89 .as_slice()
90 .iter()
91 .map(|n| {
92 if let Node::Or(children) = n {
93 vec![0; children.len()]
94 } else {
95 vec![]
96 }
97 })
98 .collect::<Vec<_>>(),
99 )
100 } else {
101 let free_variables = self.ddnnf.free_vars();
102 if let Some(assumps) = &self.assumptions {
103 let n_free_vars = |lits: &[Literal]| {
104 lits.iter()
105 .filter(|l| assumps[l.var_index()].is_none())
106 .count()
107 };
108 (
109 n_free_vars(free_variables.root_free_vars()),
110 (0..self.ddnnf.n_nodes())
111 .map(|i| {
112 free_variables
113 .or_free_vars()
114 .iter_child_free_vars(i)
115 .map(n_free_vars)
116 .collect::<Vec<_>>()
117 })
118 .collect::<Vec<_>>(),
119 )
120 } else {
121 (
122 free_variables.root_free_vars().len(),
123 (0..self.ddnnf.n_nodes())
124 .map(|i| {
125 free_variables
126 .or_free_vars()
127 .iter_child_free_vars_lengths(i)
128 .collect::<Vec<_>>()
129 })
130 .collect::<Vec<_>>(),
131 )
132 }
133 }
134 }
135
136 #[must_use]
138 #[allow(clippy::missing_panics_doc)]
139 pub fn global_count(&self) -> &Integer {
140 self.get_or_compute_n_models()[0].as_ref().unwrap()
141 }
142
143 #[must_use]
145 pub fn ddnnf(&self) -> &DecisionDNNF {
146 self.ddnnf
147 }
148
149 #[must_use]
155 pub fn count_from(&self, index: NodeIndex) -> &Integer {
156 self.get_or_compute_n_models()[usize::from(index)]
157 .as_ref()
158 .unwrap()
159 }
160
161 #[must_use]
163 pub fn partial_models(&self) -> bool {
164 self.partial_models
165 }
166}
167
168fn compute_models_from<'a>(
169 ddnnf: &'a DecisionDNNF,
170 n_root_free_vars: usize,
171 or_children_free_vars_len: &[Vec<usize>],
172 index: NodeIndex,
173 n_models: &mut [Option<Integer>],
174 assumptions: Option<&[Option<bool>]>,
175) {
176 if n_models[usize::from(index)].is_some() {
177 return;
178 }
179 let edge_indices = match &ddnnf.nodes()[index] {
180 Node::And(edge_indices) | Node::Or(edge_indices) => edge_indices.clone(),
181 Node::True | Node::False => vec![],
182 };
183 let in_contradiction_with_assumptions = |e: EdgeIndex| {
184 if let Some(assumps) = assumptions {
185 ddnnf.edges()[e]
186 .propagated()
187 .iter()
188 .any(|l| assumps[l.var_index()].is_some_and(|p| p != l.polarity()))
189 } else {
190 false
191 }
192 };
193 for e in edge_indices {
194 if in_contradiction_with_assumptions(e) {
195 continue;
196 }
197 let target = ddnnf.edges()[e].target();
198 compute_models_from(
199 ddnnf,
200 n_root_free_vars,
201 or_children_free_vars_len,
202 target,
203 n_models,
204 assumptions,
205 );
206 }
207 let iter_for_children = |c: &'a Vec<EdgeIndex>| {
208 c.iter().map(|e| {
209 if in_contradiction_with_assumptions(*e) {
210 return &INTEGER_ZERO;
211 }
212 n_models[usize::from(ddnnf.edges()[*e].target())]
213 .as_ref()
214 .unwrap()
215 })
216 };
217 let mut n = match &ddnnf.nodes()[index] {
218 Node::And(edge_indices) => iter_for_children(edge_indices).product(),
219 Node::Or(edge_indices) => iter_for_children(edge_indices)
220 .zip(or_children_free_vars_len[usize::from(index)].iter())
221 .map(|(n, w)| n.clone() << w)
222 .sum(),
223 Node::True => Integer::ONE.clone(),
224 Node::False => Integer::ZERO,
225 };
226 if index == NodeIndex::from(0) {
227 n <<= n_root_free_vars;
228 }
229 n_models[usize::from(index)] = Some(n);
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use crate::D4Reader;
236
237 fn assert_counts(
238 instance: &str,
239 n_vars: Option<usize>,
240 expected_model_count: usize,
241 expected_path_count: usize,
242 ) {
243 assert_counts_under_assumptions(
244 instance,
245 n_vars,
246 expected_model_count,
247 expected_path_count,
248 None,
249 );
250 }
251
252 fn assert_counts_under_assumptions(
253 instance: &str,
254 n_vars: Option<usize>,
255 expected_model_count: usize,
256 expected_path_count: usize,
257 assumptions: Option<Vec<isize>>,
258 ) {
259 let mut ddnnf = D4Reader::default().read(instance.as_bytes()).unwrap();
260 if let Some(n) = n_vars {
261 ddnnf.update_n_vars(n);
262 }
263 let mut model_counter = ModelCounter::new(&ddnnf, false);
264 if let Some(assumps) = assumptions.clone() {
265 model_counter.set_assumptions(
266 &assumps
267 .iter()
268 .map(|i| Literal::from(*i))
269 .collect::<Vec<_>>(),
270 );
271 }
272 assert_eq!(
273 expected_model_count,
274 model_counter.global_count().to_usize_wrapping()
275 );
276 let mut path_counter = ModelCounter::new(&ddnnf, true);
277 if let Some(assumps) = assumptions {
278 path_counter.set_assumptions(
279 &assumps
280 .iter()
281 .map(|i| Literal::from(*i))
282 .collect::<Vec<_>>(),
283 );
284 }
285 assert_eq!(
286 expected_path_count,
287 path_counter.global_count().to_usize_wrapping()
288 );
289 }
290
291 #[test]
292 fn test_ok() {
293 assert_counts(
294 "a 1 0\no 2 0\no 3 0\nt 4 0\n1 2 0\n1 3 0\n2 4 -1 0\n2 4 1 0\n3 4 -2 0\n3 4 2 0\n",
295 None,
296 4,
297 4,
298 );
299 }
300
301 #[test]
302 fn test_true_no_vars() {
303 assert_counts("t 1 0\n", None, 1, 1);
304 }
305
306 #[test]
307 fn test_true_one_var() {
308 assert_counts("t 1 0\n", Some(1), 2, 1);
309 }
310
311 #[test]
312 fn test_true_two_vars() {
313 assert_counts("t 1 0\n", Some(2), 4, 1);
314 }
315
316 #[test]
317 fn test_false() {
318 assert_counts("f 1 0\n", None, 0, 0);
319 }
320
321 #[test]
322 fn test_clause() {
323 assert_counts(
324 r"
325 o 1 0
326 o 2 0
327 t 3 0
328 2 3 -1 -2 0
329 2 3 1 0
330 1 2 0",
331 None,
332 3,
333 2,
334 );
335 }
336
337 #[test]
338 fn test_implied_lit() {
339 assert_counts(
340 r"
341 o 1 0
342 o 2 0
343 t 3 0
344 f 4 0
345 2 3 -1 0
346 2 4 1 0
347 1 2 0",
348 Some(2),
349 2,
350 1,
351 );
352 }
353
354 #[test]
355 fn test_assumptions() {
356 let instance = r"
357 o 1 0
358 a 2 0
359 a 3 0
360 t 4 0
361 1 2 0
362 1 3 0
363 2 4 1 2 0
364 3 4 -1 3 0
365 ";
366 let check = |assumptions, models, paths| {
367 assert_counts_under_assumptions(instance, None, models, paths, assumptions);
368 };
369 check(None, 4, 2);
370 check(Some(vec![-1]), 2, 1);
371 check(Some(vec![1]), 2, 1);
372 check(Some(vec![-2]), 1, 1);
373 check(Some(vec![2]), 3, 2);
374 check(Some(vec![-1, -2]), 1, 1);
375 check(Some(vec![-1, 2]), 1, 1);
376 check(Some(vec![1, -2]), 0, 0);
377 check(Some(vec![1, 2]), 2, 1);
378 check(Some(vec![-1, -2, -3]), 0, 0);
379 check(Some(vec![-1, -2, 3]), 1, 1);
380 check(Some(vec![-1, 2, -3]), 0, 0);
381 check(Some(vec![-1, 2, 3]), 1, 1);
382 check(Some(vec![1, -2, -3]), 0, 0);
383 check(Some(vec![1, -2, 3]), 0, 0);
384 check(Some(vec![1, 2, -3]), 1, 1);
385 check(Some(vec![1, 2, 3]), 1, 1);
386 }
387
388 #[test]
389 fn test_count_under_assumptions_top() {
390 let instance = "t 1 0\n";
391 let mut ddnnf = D4Reader::default().read(instance.as_bytes()).unwrap();
392 ddnnf.update_n_vars(1);
393 assert_counts_under_assumptions("t 1 0\n", Some(1), 2, 1, Some(vec![]));
394 assert_counts_under_assumptions("t 1 0\n", Some(1), 1, 1, Some(vec![-1]));
395 assert_counts_under_assumptions("t 1 0\n", Some(1), 1, 1, Some(vec![1]));
396 }
397}