problemreductions/models/formula/
one_in_three_satisfiability.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use serde::{Deserialize, Serialize};
10
11use super::CNFClause;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "OneInThreeSatisfiability",
16 display_name: "One-in-Three Satisfiability",
17 aliases: &[],
18 dimensions: &[],
19 module_path: module_path!(),
20 description: "3-SAT variant where each clause has exactly one true literal",
21 fields: &[
22 FieldInfo { name: "num_vars", type_name: "usize", description: "Number of Boolean variables" },
23 FieldInfo { name: "clauses", type_name: "Vec<CNFClause>", description: "Clauses each with exactly 3 literals" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct OneInThreeSatisfiability {
59 num_vars: usize,
61 clauses: Vec<CNFClause>,
63}
64
65impl OneInThreeSatisfiability {
66 pub fn new(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
72 for (i, clause) in clauses.iter().enumerate() {
73 assert!(
74 clause.len() == 3,
75 "Clause {} has {} literals, expected 3",
76 i,
77 clause.len()
78 );
79 for &lit in &clause.literals {
80 let var = lit.unsigned_abs() as usize;
81 assert!(
82 var >= 1 && var <= num_vars,
83 "Clause {} contains literal {} referencing variable {} outside range [1, {}]",
84 i,
85 lit,
86 var,
87 num_vars
88 );
89 }
90 }
91 Self { num_vars, clauses }
92 }
93
94 pub fn num_vars(&self) -> usize {
96 self.num_vars
97 }
98
99 pub fn num_clauses(&self) -> usize {
101 self.clauses.len()
102 }
103
104 pub fn clauses(&self) -> &[CNFClause] {
106 &self.clauses
107 }
108
109 pub fn get_clause(&self, index: usize) -> Option<&CNFClause> {
111 self.clauses.get(index)
112 }
113
114 pub fn is_one_in_three_satisfying(&self, assignment: &[bool]) -> bool {
116 self.clauses.iter().all(|clause| {
117 let true_count = clause
118 .literals
119 .iter()
120 .filter(|&&lit| {
121 let var = lit.unsigned_abs() as usize - 1; let value = assignment.get(var).copied().unwrap_or(false);
123 if lit > 0 {
124 value
125 } else {
126 !value
127 }
128 })
129 .count();
130 true_count == 1
131 })
132 }
133}
134
135impl Problem for OneInThreeSatisfiability {
136 const NAME: &'static str = "OneInThreeSatisfiability";
137 type Value = crate::types::Or;
138
139 fn dims(&self) -> Vec<usize> {
140 vec![2; self.num_vars]
141 }
142
143 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
144 crate::types::Or({
145 let assignment = super::config_to_assignment(config);
146 self.is_one_in_three_satisfying(&assignment)
147 })
148 }
149
150 fn variant() -> Vec<(&'static str, &'static str)> {
151 crate::variant_params![]
152 }
153}
154
155crate::declare_variants! {
156 default OneInThreeSatisfiability => "1.307^num_variables",
157}
158
159#[cfg(feature = "example-db")]
160pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
161 vec![crate::example_db::specs::ModelExampleSpec {
162 id: "one_in_three_satisfiability",
163 instance: Box::new(OneInThreeSatisfiability::new(
164 4,
165 vec![
166 CNFClause::new(vec![1, 2, 3]),
167 CNFClause::new(vec![-1, 3, 4]),
168 CNFClause::new(vec![2, -3, -4]),
169 ],
170 )),
171 optimal_config: vec![1, 0, 0, 1],
172 optimal_value: serde_json::json!(true),
173 }]
174}
175
176#[cfg(test)]
177#[path = "../../unit_tests/models/formula/one_in_three_satisfiability.rs"]
178mod tests;