1use crate::literal::Lit;
2#[allow(unused_imports)]
13use crate::prelude::*;
14
15#[derive(Debug, Clone)]
20pub struct Cube {
21 pub literals: Vec<Lit>,
23 pub difficulty: f64,
25 pub depth: usize,
27}
28
29impl Cube {
30 pub fn new(literals: Vec<Lit>) -> Self {
32 Self {
33 depth: literals.len(),
34 literals,
35 difficulty: 0.0,
36 }
37 }
38
39 pub fn with_difficulty(literals: Vec<Lit>, difficulty: f64) -> Self {
41 Self {
42 depth: literals.len(),
43 literals,
44 difficulty,
45 }
46 }
47
48 pub fn len(&self) -> usize {
50 self.literals.len()
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.literals.is_empty()
56 }
57
58 pub fn extend(&self, lit: Lit) -> Self {
60 let mut new_lits = self.literals.clone();
61 new_lits.push(lit);
62 Self {
63 literals: new_lits,
64 difficulty: 0.0,
65 depth: self.depth + 1,
66 }
67 }
68
69 pub fn is_consistent(&self) -> bool {
71 let mut seen = HashSet::new();
72 for &lit in &self.literals {
73 if seen.contains(&lit.negate()) {
74 return false;
75 }
76 seen.insert(lit);
77 }
78 true
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum CubeSplittingStrategy {
85 Activity,
87 Lookahead,
89 MostConstrained,
91 Balanced,
93}
94
95#[derive(Debug, Clone)]
97pub struct CubeConfig {
98 pub max_depth: usize,
100 pub target_cubes: usize,
102 pub min_cube_size: usize,
104 pub max_cube_size: usize,
106 pub strategy: CubeSplittingStrategy,
108 pub adaptive_depth: bool,
110}
111
112impl Default for CubeConfig {
113 fn default() -> Self {
114 Self {
115 max_depth: 10,
116 target_cubes: 100,
117 min_cube_size: 3,
118 max_cube_size: 15,
119 strategy: CubeSplittingStrategy::Lookahead,
120 adaptive_depth: true,
121 }
122 }
123}
124
125pub struct CubeGenerator {
127 config: CubeConfig,
129 cubes: Vec<Cube>,
131 num_vars: usize,
133}
134
135impl CubeGenerator {
136 pub fn new(num_vars: usize, config: CubeConfig) -> Self {
138 Self {
139 config,
140 cubes: Vec::new(),
141 num_vars,
142 }
143 }
144
145 pub fn generate(&mut self, variable_scores: &[f64]) -> Vec<Cube> {
150 self.cubes.clear();
151
152 let initial = Cube::new(Vec::new());
154 self.split_recursive(initial, variable_scores);
155
156 if self.config.adaptive_depth && self.cubes.len() < self.config.target_cubes / 2 {
158 self.config.max_depth = (self.config.max_depth * 3) / 2;
159 self.cubes.clear();
160 let initial = Cube::new(Vec::new());
161 self.split_recursive(initial, variable_scores);
162 }
163
164 core::mem::take(&mut self.cubes)
165 }
166
167 fn split_recursive(&mut self, cube: Cube, variable_scores: &[f64]) {
169 if cube.depth >= self.config.max_depth || self.cubes.len() >= self.config.target_cubes {
171 if cube.len() >= self.config.min_cube_size && cube.is_consistent() {
172 self.cubes.push(cube);
173 }
174 return;
175 }
176
177 if let Some(var) = self.select_splitting_variable(&cube, variable_scores) {
179 use crate::literal::Var;
180 let v = Var::new(var as u32);
181
182 let pos_cube = cube.extend(Lit::pos(v));
184 let neg_cube = cube.extend(Lit::neg(v));
185
186 self.split_recursive(pos_cube, variable_scores);
188 self.split_recursive(neg_cube, variable_scores);
189 } else {
190 if cube.len() >= self.config.min_cube_size && cube.is_consistent() {
192 self.cubes.push(cube);
193 }
194 }
195 }
196
197 fn select_splitting_variable(&self, cube: &Cube, variable_scores: &[f64]) -> Option<usize> {
199 let mut assigned = HashSet::new();
201 for lit in &cube.literals {
202 assigned.insert(lit.var().index());
203 }
204
205 let mut best_var = None;
207 let mut best_score = f64::NEG_INFINITY;
208
209 for var in 0..self.num_vars {
210 if assigned.contains(&var) {
211 continue;
212 }
213
214 let score = if var < variable_scores.len() {
215 variable_scores[var]
216 } else {
217 0.0
218 };
219
220 if score > best_score {
221 best_score = score;
222 best_var = Some(var);
223 }
224 }
225
226 best_var
227 }
228
229 #[allow(dead_code)]
231 fn estimate_difficulty(&self, cube: &Cube) -> f64 {
232 let size_factor = 1.0 / (cube.len() as f64 + 1.0);
234 let depth_factor = cube.depth as f64;
236
237 size_factor * depth_factor
238 }
239
240 pub fn config(&self) -> &CubeConfig {
242 &self.config
243 }
244
245 pub fn num_cubes(&self) -> usize {
247 self.cubes.len()
248 }
249}
250
251#[derive(Debug, Clone, Default)]
253pub struct CubeStats {
254 pub total_cubes: usize,
256 pub avg_cube_size: f64,
258 pub min_cube_size: usize,
260 pub max_cube_size: usize,
262 pub max_depth: usize,
264 pub avg_difficulty: f64,
266}
267
268impl CubeStats {
269 pub fn from_cubes(cubes: &[Cube]) -> Self {
271 if cubes.is_empty() {
272 return Self::default();
273 }
274
275 let total = cubes.len();
276 let sizes: Vec<usize> = cubes.iter().map(|c| c.len()).collect();
277 let avg_size = sizes.iter().sum::<usize>() as f64 / total as f64;
278 let min_size = sizes.iter().copied().min().unwrap_or(0);
279 let max_size = sizes.iter().copied().max().unwrap_or(0);
280 let max_depth = cubes.iter().map(|c| c.depth).max().unwrap_or(0);
281 let avg_diff = cubes.iter().map(|c| c.difficulty).sum::<f64>() / total as f64;
282
283 Self {
284 total_cubes: total,
285 avg_cube_size: avg_size,
286 min_cube_size: min_size,
287 max_cube_size: max_size,
288 max_depth,
289 avg_difficulty: avg_diff,
290 }
291 }
292
293 pub fn display(&self) -> String {
295 format!(
296 "Cube Generation Statistics:\n\
297 - Total Cubes: {}\n\
298 - Avg Size: {:.2}\n\
299 - Size Range: [{}, {}]\n\
300 - Max Depth: {}\n\
301 - Avg Difficulty: {:.4}",
302 self.total_cubes,
303 self.avg_cube_size,
304 self.min_cube_size,
305 self.max_cube_size,
306 self.max_depth,
307 self.avg_difficulty
308 )
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq)]
314pub enum CubeResult {
315 Unsat,
317 Sat,
319 Unknown,
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326 use crate::literal::Var;
327
328 #[test]
329 fn test_cube_creation() {
330 let lit1 = Lit::pos(Var::new(0));
331 let lit2 = Lit::neg(Var::new(1));
332 let cube = Cube::new(vec![lit1, lit2]);
333
334 assert_eq!(cube.len(), 2);
335 assert!(!cube.is_empty());
336 assert_eq!(cube.depth, 2);
337 }
338
339 #[test]
340 fn test_cube_extend() {
341 let lit1 = Lit::pos(Var::new(0));
342 let cube1 = Cube::new(vec![lit1]);
343
344 let lit2 = Lit::neg(Var::new(1));
345 let cube2 = cube1.extend(lit2);
346
347 assert_eq!(cube1.len(), 1);
348 assert_eq!(cube2.len(), 2);
349 assert_eq!(cube2.depth, 2);
350 }
351
352 #[test]
353 fn test_cube_consistency() {
354 let lit1 = Lit::pos(Var::new(0));
355 let lit2 = Lit::neg(Var::new(1));
356 let cube = Cube::new(vec![lit1, lit2]);
357
358 assert!(cube.is_consistent());
359 }
360
361 #[test]
362 fn test_cube_inconsistency() {
363 let lit1 = Lit::pos(Var::new(0));
364 let lit2 = Lit::neg(Var::new(0));
365 let cube = Cube::new(vec![lit1, lit2]);
366
367 assert!(!cube.is_consistent());
368 }
369
370 #[test]
371 fn test_empty_cube() {
372 let cube = Cube::new(Vec::new());
373
374 assert_eq!(cube.len(), 0);
375 assert!(cube.is_empty());
376 assert!(cube.is_consistent());
377 }
378
379 #[test]
380 fn test_cube_config_default() {
381 let config = CubeConfig::default();
382
383 assert_eq!(config.max_depth, 10);
384 assert_eq!(config.target_cubes, 100);
385 assert!(config.adaptive_depth);
386 }
387
388 #[test]
389 fn test_cube_generator_creation() {
390 let config = CubeConfig::default();
391 let generator = CubeGenerator::new(10, config);
392
393 assert_eq!(generator.num_vars, 10);
394 assert_eq!(generator.num_cubes(), 0);
395 }
396
397 #[test]
398 fn test_cube_generation() {
399 let config = CubeConfig {
400 max_depth: 3,
401 target_cubes: 10,
402 min_cube_size: 1,
403 max_cube_size: 5,
404 strategy: CubeSplittingStrategy::Activity,
405 adaptive_depth: false,
406 };
407 let mut generator = CubeGenerator::new(5, config);
408 let scores = vec![1.0, 0.9, 0.8, 0.7, 0.6];
409
410 let cubes = generator.generate(&scores);
411
412 assert!(!cubes.is_empty());
413 assert!(cubes.len() <= 10);
414
415 for cube in &cubes {
417 assert!(cube.is_consistent());
418 }
419 }
420
421 #[test]
422 fn test_cube_stats() {
423 let lit1 = Lit::pos(Var::new(0));
424 let lit2 = Lit::neg(Var::new(1));
425 let lit3 = Lit::pos(Var::new(2));
426
427 let cubes = vec![
428 Cube::new(vec![lit1]),
429 Cube::new(vec![lit1, lit2]),
430 Cube::new(vec![lit1, lit2, lit3]),
431 ];
432
433 let stats = CubeStats::from_cubes(&cubes);
434
435 assert_eq!(stats.total_cubes, 3);
436 assert_eq!(stats.min_cube_size, 1);
437 assert_eq!(stats.max_cube_size, 3);
438 assert_eq!(stats.avg_cube_size, 2.0);
439 }
440
441 #[test]
442 fn test_empty_cube_stats() {
443 let stats = CubeStats::from_cubes(&[]);
444
445 assert_eq!(stats.total_cubes, 0);
446 assert_eq!(stats.avg_cube_size, 0.0);
447 }
448
449 #[test]
450 fn test_cube_splitting_strategies() {
451 let strategies = vec![
452 CubeSplittingStrategy::Activity,
453 CubeSplittingStrategy::Lookahead,
454 CubeSplittingStrategy::MostConstrained,
455 CubeSplittingStrategy::Balanced,
456 ];
457
458 for strategy in strategies {
459 let config = CubeConfig {
460 strategy,
461 max_depth: 2,
462 target_cubes: 4,
463 min_cube_size: 1,
464 max_cube_size: 5,
465 adaptive_depth: false,
466 };
467
468 let mut generator = CubeGenerator::new(3, config);
469 let scores = vec![1.0, 0.5, 0.2];
470 let cubes = generator.generate(&scores);
471
472 assert!(!cubes.is_empty());
473 }
474 }
475
476 #[test]
477 fn test_adaptive_depth() {
478 let config = CubeConfig {
479 max_depth: 2,
480 target_cubes: 100,
481 min_cube_size: 1,
482 max_cube_size: 10,
483 strategy: CubeSplittingStrategy::Activity,
484 adaptive_depth: true,
485 };
486
487 let mut generator = CubeGenerator::new(3, config);
488 let scores = vec![1.0, 0.5, 0.2];
489 let _cubes = generator.generate(&scores);
490
491 assert!(generator.config().max_depth >= 2);
493 }
494}