1use crate::graph_types::SimpleGraph;
7use crate::traits::Problem;
8use crate::types::{EnergyMode, ProblemSize, SolutionSize};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub enum BooleanOp {
15 Var(String),
17 Const(bool),
19 Not(Box<BooleanExpr>),
21 And(Vec<BooleanExpr>),
23 Or(Vec<BooleanExpr>),
25 Xor(Vec<BooleanExpr>),
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub struct BooleanExpr {
32 pub op: BooleanOp,
33}
34
35impl BooleanExpr {
36 pub fn var(name: &str) -> Self {
38 BooleanExpr {
39 op: BooleanOp::Var(name.to_string()),
40 }
41 }
42
43 pub fn constant(value: bool) -> Self {
45 BooleanExpr {
46 op: BooleanOp::Const(value),
47 }
48 }
49
50 #[allow(clippy::should_implement_trait)]
52 pub fn not(expr: BooleanExpr) -> Self {
53 BooleanExpr {
54 op: BooleanOp::Not(Box::new(expr)),
55 }
56 }
57
58 pub fn and(args: Vec<BooleanExpr>) -> Self {
60 BooleanExpr {
61 op: BooleanOp::And(args),
62 }
63 }
64
65 pub fn or(args: Vec<BooleanExpr>) -> Self {
67 BooleanExpr {
68 op: BooleanOp::Or(args),
69 }
70 }
71
72 pub fn xor(args: Vec<BooleanExpr>) -> Self {
74 BooleanExpr {
75 op: BooleanOp::Xor(args),
76 }
77 }
78
79 pub fn variables(&self) -> Vec<String> {
81 let mut vars = Vec::new();
82 self.extract_variables(&mut vars);
83 vars.sort();
84 vars.dedup();
85 vars
86 }
87
88 fn extract_variables(&self, vars: &mut Vec<String>) {
89 match &self.op {
90 BooleanOp::Var(name) => vars.push(name.clone()),
91 BooleanOp::Const(_) => {}
92 BooleanOp::Not(inner) => inner.extract_variables(vars),
93 BooleanOp::And(args) | BooleanOp::Or(args) | BooleanOp::Xor(args) => {
94 for arg in args {
95 arg.extract_variables(vars);
96 }
97 }
98 }
99 }
100
101 pub fn evaluate(&self, assignments: &HashMap<String, bool>) -> bool {
103 match &self.op {
104 BooleanOp::Var(name) => *assignments.get(name).unwrap_or(&false),
105 BooleanOp::Const(value) => *value,
106 BooleanOp::Not(inner) => !inner.evaluate(assignments),
107 BooleanOp::And(args) => args.iter().all(|a| a.evaluate(assignments)),
108 BooleanOp::Or(args) => args.iter().any(|a| a.evaluate(assignments)),
109 BooleanOp::Xor(args) => args
110 .iter()
111 .fold(false, |acc, a| acc ^ a.evaluate(assignments)),
112 }
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
118pub struct Assignment {
119 pub outputs: Vec<String>,
121 pub expr: BooleanExpr,
123}
124
125impl Assignment {
126 pub fn new(outputs: Vec<String>, expr: BooleanExpr) -> Self {
128 Self { outputs, expr }
129 }
130
131 pub fn variables(&self) -> Vec<String> {
133 let mut vars = self.outputs.clone();
134 vars.extend(self.expr.variables());
135 vars.sort();
136 vars.dedup();
137 vars
138 }
139
140 pub fn is_satisfied(&self, assignments: &HashMap<String, bool>) -> bool {
142 let result = self.expr.evaluate(assignments);
143 self.outputs
144 .iter()
145 .all(|o| assignments.get(o).copied().unwrap_or(false) == result)
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
151pub struct Circuit {
152 pub assignments: Vec<Assignment>,
154}
155
156impl Circuit {
157 pub fn new(assignments: Vec<Assignment>) -> Self {
159 Self { assignments }
160 }
161
162 pub fn variables(&self) -> Vec<String> {
164 let mut vars = Vec::new();
165 for assign in &self.assignments {
166 vars.extend(assign.variables());
167 }
168 vars.sort();
169 vars.dedup();
170 vars
171 }
172
173 pub fn num_assignments(&self) -> usize {
175 self.assignments.len()
176 }
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct CircuitSAT<W = i32> {
207 circuit: Circuit,
209 variables: Vec<String>,
211 weights: Vec<W>,
213}
214
215impl<W: Clone + Default + From<i32>> CircuitSAT<W> {
216 pub fn new(circuit: Circuit) -> Self {
218 let variables = circuit.variables();
219 let weights = vec![W::from(1); circuit.num_assignments()];
220 Self {
221 circuit,
222 variables,
223 weights,
224 }
225 }
226}
227
228impl<W> CircuitSAT<W> {
229 pub fn with_weights(circuit: Circuit, weights: Vec<W>) -> Self {
231 assert_eq!(weights.len(), circuit.num_assignments());
232 let variables = circuit.variables();
233 Self {
234 circuit,
235 variables,
236 weights,
237 }
238 }
239
240 pub fn circuit(&self) -> &Circuit {
242 &self.circuit
243 }
244
245 pub fn variable_names(&self) -> &[String] {
247 &self.variables
248 }
249
250 fn config_to_assignments(&self, config: &[usize]) -> HashMap<String, bool> {
252 self.variables
253 .iter()
254 .enumerate()
255 .map(|(i, name)| (name.clone(), config.get(i).copied().unwrap_or(0) == 1))
256 .collect()
257 }
258
259 fn count_satisfied(&self, config: &[usize]) -> usize {
261 let assignments = self.config_to_assignments(config);
262 self.circuit
263 .assignments
264 .iter()
265 .filter(|a| a.is_satisfied(&assignments))
266 .count()
267 }
268}
269
270impl<W> Problem for CircuitSAT<W>
271where
272 W: Clone + Default + PartialOrd + num_traits::Num + num_traits::Zero + std::ops::AddAssign + 'static,
273{
274 const NAME: &'static str = "CircuitSAT";
275 type GraphType = SimpleGraph;
276 type Weight = W;
277 type Size = W;
278
279 fn num_variables(&self) -> usize {
280 self.variables.len()
281 }
282
283 fn num_flavors(&self) -> usize {
284 2 }
286
287 fn problem_size(&self) -> ProblemSize {
288 ProblemSize::new(vec![
289 ("num_variables", self.variables.len()),
290 ("num_assignments", self.circuit.num_assignments()),
291 ])
292 }
293
294 fn energy_mode(&self) -> EnergyMode {
295 EnergyMode::LargerSizeIsBetter }
297
298 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
299 let assignments = self.config_to_assignments(config);
300 let mut total = W::zero();
301
302 for (i, assign) in self.circuit.assignments.iter().enumerate() {
303 if assign.is_satisfied(&assignments) {
304 total += self.weights[i].clone();
305 }
306 }
307
308 let is_valid = self.count_satisfied(config) == self.circuit.num_assignments();
310 SolutionSize::new(total, is_valid)
311 }
312}
313
314pub fn is_circuit_satisfying(circuit: &Circuit, assignments: &HashMap<String, bool>) -> bool {
316 circuit
317 .assignments
318 .iter()
319 .all(|a| a.is_satisfied(assignments))
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use crate::solvers::{BruteForce, Solver};
326
327 #[test]
328 fn test_boolean_expr_var() {
329 let expr = BooleanExpr::var("x");
330 let mut assignments = HashMap::new();
331 assignments.insert("x".to_string(), true);
332 assert!(expr.evaluate(&assignments));
333
334 assignments.insert("x".to_string(), false);
335 assert!(!expr.evaluate(&assignments));
336 }
337
338 #[test]
339 fn test_boolean_expr_const() {
340 let t = BooleanExpr::constant(true);
341 let f = BooleanExpr::constant(false);
342 let assignments = HashMap::new();
343 assert!(t.evaluate(&assignments));
344 assert!(!f.evaluate(&assignments));
345 }
346
347 #[test]
348 fn test_boolean_expr_not() {
349 let expr = BooleanExpr::not(BooleanExpr::var("x"));
350 let mut assignments = HashMap::new();
351 assignments.insert("x".to_string(), true);
352 assert!(!expr.evaluate(&assignments));
353
354 assignments.insert("x".to_string(), false);
355 assert!(expr.evaluate(&assignments));
356 }
357
358 #[test]
359 fn test_boolean_expr_and() {
360 let expr = BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]);
361 let mut assignments = HashMap::new();
362
363 assignments.insert("x".to_string(), true);
364 assignments.insert("y".to_string(), true);
365 assert!(expr.evaluate(&assignments));
366
367 assignments.insert("y".to_string(), false);
368 assert!(!expr.evaluate(&assignments));
369 }
370
371 #[test]
372 fn test_boolean_expr_or() {
373 let expr = BooleanExpr::or(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]);
374 let mut assignments = HashMap::new();
375
376 assignments.insert("x".to_string(), false);
377 assignments.insert("y".to_string(), false);
378 assert!(!expr.evaluate(&assignments));
379
380 assignments.insert("y".to_string(), true);
381 assert!(expr.evaluate(&assignments));
382 }
383
384 #[test]
385 fn test_boolean_expr_xor() {
386 let expr = BooleanExpr::xor(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]);
387 let mut assignments = HashMap::new();
388
389 assignments.insert("x".to_string(), true);
390 assignments.insert("y".to_string(), true);
391 assert!(!expr.evaluate(&assignments)); assignments.insert("y".to_string(), false);
394 assert!(expr.evaluate(&assignments)); }
396
397 #[test]
398 fn test_boolean_expr_variables() {
399 let expr = BooleanExpr::and(vec![
400 BooleanExpr::var("x"),
401 BooleanExpr::or(vec![BooleanExpr::var("y"), BooleanExpr::var("z")]),
402 ]);
403 let vars = expr.variables();
404 assert_eq!(vars, vec!["x", "y", "z"]);
405 }
406
407 #[test]
408 fn test_assignment_satisfied() {
409 let assign = Assignment::new(
410 vec!["c".to_string()],
411 BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]),
412 );
413
414 let mut assignments = HashMap::new();
415 assignments.insert("x".to_string(), true);
416 assignments.insert("y".to_string(), true);
417 assignments.insert("c".to_string(), true);
418 assert!(assign.is_satisfied(&assignments));
419
420 assignments.insert("c".to_string(), false);
421 assert!(!assign.is_satisfied(&assignments));
422 }
423
424 #[test]
425 fn test_circuit_variables() {
426 let circuit = Circuit::new(vec![
427 Assignment::new(
428 vec!["c".to_string()],
429 BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]),
430 ),
431 Assignment::new(
432 vec!["d".to_string()],
433 BooleanExpr::or(vec![BooleanExpr::var("c"), BooleanExpr::var("z")]),
434 ),
435 ]);
436 let vars = circuit.variables();
437 assert_eq!(vars, vec!["c", "d", "x", "y", "z"]);
438 }
439
440 #[test]
441 fn test_circuit_sat_creation() {
442 let circuit = Circuit::new(vec![Assignment::new(
443 vec!["c".to_string()],
444 BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]),
445 )]);
446 let problem = CircuitSAT::<i32>::new(circuit);
447 assert_eq!(problem.num_variables(), 3); assert_eq!(problem.num_flavors(), 2);
449 }
450
451 #[test]
452 fn test_circuit_sat_solution_size() {
453 let circuit = Circuit::new(vec![Assignment::new(
455 vec!["c".to_string()],
456 BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]),
457 )]);
458 let problem = CircuitSAT::<i32>::new(circuit);
459
460 let sol = problem.solution_size(&[1, 1, 1]);
463 assert!(sol.is_valid);
464 assert_eq!(sol.size, 1);
465
466 let sol = problem.solution_size(&[0, 0, 0]);
468 assert!(sol.is_valid);
469 assert_eq!(sol.size, 1);
470
471 let sol = problem.solution_size(&[1, 0, 0]);
473 assert!(!sol.is_valid);
474 assert_eq!(sol.size, 0);
475 }
476
477 #[test]
478 fn test_circuit_sat_brute_force() {
479 let circuit = Circuit::new(vec![Assignment::new(
481 vec!["c".to_string()],
482 BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]),
483 )]);
484 let problem = CircuitSAT::<i32>::new(circuit);
485 let solver = BruteForce::new();
486
487 let solutions = solver.find_best(&problem);
488 assert_eq!(solutions.len(), 4);
491 for sol in &solutions {
492 assert!(problem.solution_size(sol).is_valid);
493 }
494 }
495
496 #[test]
497 fn test_circuit_sat_complex() {
498 let circuit = Circuit::new(vec![
501 Assignment::new(
502 vec!["c".to_string()],
503 BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]),
504 ),
505 Assignment::new(
506 vec!["d".to_string()],
507 BooleanExpr::or(vec![BooleanExpr::var("c"), BooleanExpr::var("z")]),
508 ),
509 ]);
510 let problem = CircuitSAT::<i32>::new(circuit);
511 let solver = BruteForce::new();
512
513 let solutions = solver.find_best(&problem);
514 for sol in &solutions {
516 let sol_size = problem.solution_size(sol);
517 assert!(sol_size.is_valid);
518 assert_eq!(sol_size.size, 2);
519 }
520 }
521
522 #[test]
523 fn test_is_circuit_satisfying() {
524 let circuit = Circuit::new(vec![Assignment::new(
525 vec!["c".to_string()],
526 BooleanExpr::and(vec![BooleanExpr::var("x"), BooleanExpr::var("y")]),
527 )]);
528
529 let mut assignments = HashMap::new();
530 assignments.insert("x".to_string(), true);
531 assignments.insert("y".to_string(), true);
532 assignments.insert("c".to_string(), true);
533 assert!(is_circuit_satisfying(&circuit, &assignments));
534
535 assignments.insert("c".to_string(), false);
536 assert!(!is_circuit_satisfying(&circuit, &assignments));
537 }
538
539 #[test]
540 fn test_problem_size() {
541 let circuit = Circuit::new(vec![
542 Assignment::new(vec!["c".to_string()], BooleanExpr::var("x")),
543 Assignment::new(vec!["d".to_string()], BooleanExpr::var("y")),
544 ]);
545 let problem = CircuitSAT::<i32>::new(circuit);
546 let size = problem.problem_size();
547 assert_eq!(size.get("num_variables"), Some(4));
548 assert_eq!(size.get("num_assignments"), Some(2));
549 }
550
551 #[test]
552 fn test_energy_mode() {
553 let circuit = Circuit::new(vec![]);
554 let problem = CircuitSAT::<i32>::new(circuit);
555 assert!(problem.energy_mode().is_maximization());
556 }
557
558 #[test]
559 fn test_empty_circuit() {
560 let circuit = Circuit::new(vec![]);
561 let problem = CircuitSAT::<i32>::new(circuit);
562 let sol = problem.solution_size(&[]);
563 assert!(sol.is_valid);
564 assert_eq!(sol.size, 0);
565 }
566
567 #[test]
568 fn test_weighted_circuit_sat() {
569 let circuit = Circuit::new(vec![
570 Assignment::new(vec!["c".to_string()], BooleanExpr::var("x")),
571 Assignment::new(vec!["d".to_string()], BooleanExpr::var("y")),
572 ]);
573 let problem = CircuitSAT::with_weights(circuit, vec![10, 1]);
574
575 let sol = problem.solution_size(&[1, 0, 1, 0]);
579 assert_eq!(sol.size, 11); assert!(sol.is_valid);
581
582 let sol = problem.solution_size(&[1, 0, 0, 0]);
585 assert_eq!(sol.size, 1); assert!(!sol.is_valid);
587
588 let sol = problem.solution_size(&[0, 1, 0, 0]);
591 assert_eq!(sol.size, 10); assert!(!sol.is_valid);
593 }
594}