1use crate::clause::Clause;
2use crate::cube::{Cube, CubeResult, CubeStats};
3#[allow(unused_imports)]
8use crate::prelude::*;
9use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
10use std::sync::Arc;
11use std::time::{Duration, Instant};
12
13#[derive(Debug, Clone)]
15pub struct CubeSolverConfig {
16 pub cube_timeout: Duration,
18 pub num_workers: usize,
20 pub early_termination: bool,
22 pub verbose: bool,
24}
25
26impl Default for CubeSolverConfig {
27 fn default() -> Self {
28 Self {
29 cube_timeout: Duration::from_secs(60),
30 num_workers: std::thread::available_parallelism()
31 .map(|n| n.get())
32 .unwrap_or(1),
33 early_termination: true,
34 verbose: false,
35 }
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct CubeSolveResult {
42 pub cube: Cube,
44 pub result: CubeResult,
46 pub time: Duration,
48 pub conflicts: u64,
50 pub decisions: u64,
52}
53
54pub struct ParallelCubeSolver {
56 config: CubeSolverConfig,
58 cubes_solved: Arc<AtomicUsize>,
60 sat_count: Arc<AtomicUsize>,
62 unsat_count: Arc<AtomicUsize>,
64 terminate: Arc<AtomicBool>,
66}
67
68impl ParallelCubeSolver {
69 pub fn new(config: CubeSolverConfig) -> Self {
71 Self {
72 config,
73 cubes_solved: Arc::new(AtomicUsize::new(0)),
74 sat_count: Arc::new(AtomicUsize::new(0)),
75 unsat_count: Arc::new(AtomicUsize::new(0)),
76 terminate: Arc::new(AtomicBool::new(false)),
77 }
78 }
79
80 pub fn solve(
84 &mut self,
85 cubes: Vec<Cube>,
86 clauses: &[Clause],
87 ) -> (CubeResult, Vec<CubeSolveResult>) {
88 let start_time = Instant::now();
89
90 self.cubes_solved.store(0, Ordering::Relaxed);
92 self.sat_count.store(0, Ordering::Relaxed);
93 self.unsat_count.store(0, Ordering::Relaxed);
94 self.terminate.store(false, Ordering::Relaxed);
95
96 if self.config.verbose {
97 println!(
98 "Solving {} cubes with {} workers...",
99 cubes.len(),
100 self.config.num_workers
101 );
102 }
103
104 let results: Vec<CubeSolveResult> = cubes
106 .iter()
107 .map(|cube| {
108 if self.terminate.load(Ordering::Relaxed) {
110 return CubeSolveResult {
111 cube: cube.clone(),
112 result: CubeResult::Unknown,
113 time: Duration::ZERO,
114 conflicts: 0,
115 decisions: 0,
116 };
117 }
118
119 let result = self.solve_cube(cube, clauses);
120
121 self.cubes_solved.fetch_add(1, Ordering::Relaxed);
123 match result.result {
124 CubeResult::Sat => {
125 self.sat_count.fetch_add(1, Ordering::Relaxed);
126 if self.config.early_termination {
127 self.terminate.store(true, Ordering::Relaxed);
128 }
129 }
130 CubeResult::Unsat => {
131 self.unsat_count.fetch_add(1, Ordering::Relaxed);
132 }
133 CubeResult::Unknown => {}
134 }
135
136 result
137 })
138 .collect();
139
140 let total_time = start_time.elapsed();
141
142 let overall_result = if self.sat_count.load(Ordering::Relaxed) > 0 {
144 CubeResult::Sat
145 } else if self.unsat_count.load(Ordering::Relaxed) == cubes.len() {
146 CubeResult::Unsat
147 } else {
148 CubeResult::Unknown
149 };
150
151 if self.config.verbose {
152 println!(
153 "Cube solving complete: {:?} in {:.2}s",
154 overall_result,
155 total_time.as_secs_f64()
156 );
157 println!(
158 " SAT: {}, UNSAT: {}, Unknown: {}",
159 self.sat_count.load(Ordering::Relaxed),
160 self.unsat_count.load(Ordering::Relaxed),
161 cubes.len() - self.cubes_solved.load(Ordering::Relaxed)
162 );
163 }
164
165 (overall_result, results)
166 }
167
168 fn solve_cube(&self, cube: &Cube, _clauses: &[Clause]) -> CubeSolveResult {
173 let start = Instant::now();
174
175 let result = if cube.is_consistent() {
180 CubeResult::Unknown
182 } else {
183 CubeResult::Unsat
184 };
185
186 CubeSolveResult {
187 cube: cube.clone(),
188 result,
189 time: start.elapsed(),
190 conflicts: 0,
191 decisions: cube.len() as u64,
192 }
193 }
194
195 pub fn stats(&self) -> CubeSolverStats {
197 CubeSolverStats {
198 cubes_solved: self.cubes_solved.load(Ordering::Relaxed),
199 sat_count: self.sat_count.load(Ordering::Relaxed),
200 unsat_count: self.unsat_count.load(Ordering::Relaxed),
201 num_workers: self.config.num_workers,
202 }
203 }
204}
205
206#[derive(Debug, Clone)]
208pub struct CubeSolverStats {
209 pub cubes_solved: usize,
211 pub sat_count: usize,
213 pub unsat_count: usize,
215 pub num_workers: usize,
217}
218
219impl CubeSolverStats {
220 pub fn display(&self) -> String {
222 format!(
223 "Cube Solver Statistics:\n\
224 - Cubes Solved: {}\n\
225 - SAT: {}\n\
226 - UNSAT: {}\n\
227 - Workers: {}",
228 self.cubes_solved, self.sat_count, self.unsat_count, self.num_workers
229 )
230 }
231}
232
233pub struct CubeAndConquer {
235 solver_config: CubeSolverConfig,
237}
238
239impl CubeAndConquer {
240 pub fn new(solver_config: CubeSolverConfig) -> Self {
242 Self { solver_config }
243 }
244
245 pub fn solve(
249 &mut self,
250 cubes: Vec<Cube>,
251 clauses: &[Clause],
252 ) -> (CubeResult, CubeStats, Vec<CubeSolveResult>) {
253 let cube_stats = CubeStats::from_cubes(&cubes);
255
256 let mut solver = ParallelCubeSolver::new(self.solver_config.clone());
258 let (result, solve_results) = solver.solve(cubes, clauses);
259
260 (result, cube_stats, solve_results)
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267 use crate::literal::{Lit, Var};
268
269 fn make_lit(var: usize, sign: bool) -> Lit {
270 let v = Var::new(var as u32);
271 if sign { Lit::pos(v) } else { Lit::neg(v) }
272 }
273
274 #[test]
275 fn test_cube_solver_config() {
276 let config = CubeSolverConfig::default();
277
278 assert!(config.cube_timeout.as_secs() > 0);
279 assert!(config.num_workers > 0);
280 assert!(config.early_termination);
281 }
282
283 #[test]
284 fn test_parallel_cube_solver_creation() {
285 let config = CubeSolverConfig::default();
286 let solver = ParallelCubeSolver::new(config);
287
288 assert_eq!(solver.cubes_solved.load(Ordering::Relaxed), 0);
289 assert_eq!(solver.sat_count.load(Ordering::Relaxed), 0);
290 assert_eq!(solver.unsat_count.load(Ordering::Relaxed), 0);
291 }
292
293 #[test]
294 fn test_solve_empty_cubes() {
295 let config = CubeSolverConfig {
296 verbose: false,
297 ..Default::default()
298 };
299 let mut solver = ParallelCubeSolver::new(config);
300
301 let cubes = vec![];
302 let clauses = vec![];
303
304 let (result, results) = solver.solve(cubes, &clauses);
305
306 assert_eq!(result, CubeResult::Unsat); assert_eq!(results.len(), 0);
308 }
309
310 #[test]
311 fn test_solve_single_cube() {
312 let config = CubeSolverConfig {
313 verbose: false,
314 early_termination: false,
315 ..Default::default()
316 };
317 let mut solver = ParallelCubeSolver::new(config);
318
319 let lit1 = make_lit(0, false);
320 let cube = Cube::new(vec![lit1]);
321 let cubes = vec![cube];
322 let clauses = vec![];
323
324 let (result, results) = solver.solve(cubes, &clauses);
325
326 assert_eq!(results.len(), 1);
327 assert!(matches!(result, CubeResult::Unknown | CubeResult::Unsat));
328 }
329
330 #[test]
331 fn test_solve_inconsistent_cube() {
332 let config = CubeSolverConfig {
333 verbose: false,
334 ..Default::default()
335 };
336 let mut solver = ParallelCubeSolver::new(config);
337
338 let lit1 = make_lit(0, false);
339 let lit2 = make_lit(0, true);
340 let cube = Cube::new(vec![lit1, lit2]);
341 let cubes = vec![cube];
342 let clauses = vec![];
343
344 let (result, results) = solver.solve(cubes, &clauses);
345
346 assert_eq!(results.len(), 1);
347 assert_eq!(results[0].result, CubeResult::Unsat);
348 assert_eq!(result, CubeResult::Unsat);
349 }
350
351 #[test]
352 fn test_solve_multiple_cubes() {
353 let config = CubeSolverConfig {
354 verbose: false,
355 early_termination: false,
356 num_workers: 2,
357 ..Default::default()
358 };
359 let mut solver = ParallelCubeSolver::new(config);
360
361 let cube1 = Cube::new(vec![make_lit(0, false)]);
362 let cube2 = Cube::new(vec![make_lit(1, false)]);
363 let cube3 = Cube::new(vec![make_lit(2, false)]);
364
365 let cubes = vec![cube1, cube2, cube3];
366 let clauses = vec![];
367
368 let (result, results) = solver.solve(cubes, &clauses);
369
370 assert_eq!(results.len(), 3);
371 assert!(matches!(result, CubeResult::Unknown | CubeResult::Unsat));
372 }
373
374 #[test]
375 fn test_cube_solver_stats() {
376 let config = CubeSolverConfig {
377 verbose: false,
378 ..Default::default()
379 };
380 let mut solver = ParallelCubeSolver::new(config);
381
382 let cube = Cube::new(vec![make_lit(0, false)]);
383 let cubes = vec![cube];
384 let clauses = vec![];
385
386 solver.solve(cubes, &clauses);
387
388 let stats = solver.stats();
389 assert_eq!(stats.cubes_solved, 1);
390 assert!(stats.num_workers > 0);
391 }
392
393 #[test]
394 fn test_cube_and_conquer() {
395 let config = CubeSolverConfig {
396 verbose: false,
397 ..Default::default()
398 };
399 let mut cac = CubeAndConquer::new(config);
400
401 let cube1 = Cube::new(vec![make_lit(0, false)]);
402 let cube2 = Cube::new(vec![make_lit(1, false)]);
403
404 let cubes = vec![cube1, cube2];
405 let clauses = vec![];
406
407 let (result, cube_stats, solve_results) = cac.solve(cubes, &clauses);
408
409 assert_eq!(cube_stats.total_cubes, 2);
410 assert_eq!(solve_results.len(), 2);
411 assert!(matches!(result, CubeResult::Unknown | CubeResult::Unsat));
412 }
413
414 #[test]
415 fn test_early_termination() {
416 let config = CubeSolverConfig {
417 verbose: false,
418 early_termination: true,
419 ..Default::default()
420 };
421 let solver = ParallelCubeSolver::new(config);
422
423 assert!(!solver.terminate.load(Ordering::Relaxed));
424 }
425
426 #[test]
427 fn test_solver_stats_display() {
428 let stats = CubeSolverStats {
429 cubes_solved: 10,
430 sat_count: 3,
431 unsat_count: 7,
432 num_workers: 4,
433 };
434
435 let display = stats.display();
436 assert!(display.contains("10"));
437 assert!(display.contains("3"));
438 assert!(display.contains("7"));
439 assert!(display.contains("4"));
440 }
441}