problemreductions/models/formula/
ksat.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
9use crate::traits::Problem;
10use crate::variant::{KValue, K2, K3, KN};
11use serde::{de::Error as _, Deserialize, Deserializer, Serialize};
12
13use super::{sat::validate_cnf_literals, CNFClause};
14
15pub(crate) fn first_n_odd_primes(count: usize) -> Vec<u64> {
16 let mut primes = Vec::with_capacity(count);
17 let mut candidate = 3u64;
18
19 while primes.len() < count {
20 if is_prime(candidate) {
21 primes.push(candidate);
22 }
23 candidate += 2;
24 }
25
26 primes
27}
28
29fn is_prime(candidate: u64) -> bool {
30 if candidate < 2 {
31 return false;
32 }
33 if candidate == 2 {
34 return true;
35 }
36 if candidate.is_multiple_of(2) {
37 return false;
38 }
39
40 let mut divisor = 3u64;
41 while divisor * divisor <= candidate {
42 if candidate.is_multiple_of(divisor) {
43 return false;
44 }
45 divisor += 2;
46 }
47
48 true
49}
50
51inventory::submit! {
52 ProblemSchemaEntry {
53 name: "KSatisfiability",
54 display_name: "K-Satisfiability",
55 aliases: &["KSAT"],
56 dimensions: &[VariantDimension::new("k", "KN", &["KN", "K2", "K3"])],
57 category: crate::registry::ProblemCategory::Formula,
58 module_path: module_path!(),
59 description: "SAT with exactly k literals per clause",
60 fields: &[
61 FieldInfo { name: "num_vars", type_name: "usize", description: "Number of Boolean variables" },
62 FieldInfo { name: "clauses", type_name: "Vec<CNFClause>", description: "Clauses each with exactly K literals" },
63 ],
64 }
65}
66
67#[derive(Debug, Clone, Serialize)]
98pub struct KSatisfiability<K: KValue> {
99 num_vars: usize,
101 clauses: Vec<CNFClause>,
103 #[serde(skip)]
104 _phantom: std::marker::PhantomData<K>,
105}
106
107#[derive(Deserialize)]
108struct KSatisfiabilityDef {
109 num_vars: usize,
110 clauses: Vec<CNFClause>,
111}
112
113impl<'de, K: KValue> Deserialize<'de> for KSatisfiability<K> {
114 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115 where
116 D: Deserializer<'de>,
117 {
118 let value = KSatisfiabilityDef::deserialize(deserializer)?;
119 Self::try_new(value.num_vars, value.clauses).map_err(D::Error::custom)
120 }
121}
122
123impl<K: KValue> KSatisfiability<K> {
124 pub fn new(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
131 Self::try_new(num_vars, clauses).unwrap_or_else(|message| panic!("{message}"))
132 }
133
134 pub fn try_new(
136 num_vars: usize,
137 clauses: Vec<CNFClause>,
138 ) -> Result<Self, crate::registry::ConstructionError> {
139 validate_cnf_literals(num_vars, &clauses)?;
140 if let Some(k) = K::K {
141 for (i, clause) in clauses.iter().enumerate() {
142 if clause.len() != k {
143 return Err(
144 format!("Clause {i} has {} literals, expected {k}", clause.len()).into(),
145 );
146 }
147 }
148 }
149 Ok(Self {
150 num_vars,
151 clauses,
152 _phantom: std::marker::PhantomData,
153 })
154 }
155
156 pub fn new_allow_less(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
166 Self::try_new_allow_less(num_vars, clauses).unwrap_or_else(|message| panic!("{message}"))
167 }
168
169 pub fn try_new_allow_less(
171 num_vars: usize,
172 clauses: Vec<CNFClause>,
173 ) -> Result<Self, crate::registry::ConstructionError> {
174 validate_cnf_literals(num_vars, &clauses)?;
175 if let Some(k) = K::K {
176 for (i, clause) in clauses.iter().enumerate() {
177 if clause.len() > k {
178 return Err(format!(
179 "Clause {i} has {} literals, expected at most {k}",
180 clause.len()
181 )
182 .into());
183 }
184 }
185 }
186 Ok(Self {
187 num_vars,
188 clauses,
189 _phantom: std::marker::PhantomData,
190 })
191 }
192
193 pub fn num_vars(&self) -> usize {
195 self.num_vars
196 }
197
198 pub fn num_clauses(&self) -> usize {
200 self.clauses.len()
201 }
202
203 pub fn clauses(&self) -> &[CNFClause] {
205 &self.clauses
206 }
207
208 pub fn get_clause(&self, index: usize) -> Option<&CNFClause> {
210 self.clauses.get(index)
211 }
212
213 pub fn num_literals(&self) -> usize {
215 self.clauses().iter().map(|c| c.len()).sum()
216 }
217
218 pub fn count_satisfied(
220 &self,
221 assignment: &[bool],
222 ) -> Result<i64, crate::traits::EvaluationError> {
223 let count = self
224 .clauses
225 .iter()
226 .filter(|c| c.is_satisfied(assignment))
227 .count();
228 i64::try_from(count).map_err(|_| {
229 crate::traits::EvaluationError::IntegerOverflow(
230 "converting satisfied-clause count to i64".into(),
231 )
232 })
233 }
234
235 pub fn is_satisfying(&self, assignment: &[bool]) -> bool {
237 self.clauses.iter().all(|c| c.is_satisfied(assignment))
238 }
239}
240
241impl<K: KValue> Problem for KSatisfiability<K> {
242 const NAME: &'static str = "KSatisfiability";
243 type Solution = Vec<bool>;
244 type Value = crate::types::Or;
245
246 crate::problem_parameters![
247 ("num_clauses", num_clauses),
248 ("num_literals", num_literals),
249 ("num_vars", num_vars),
250 ];
251
252 fn evaluate(
253 &self,
254 config: &Self::Solution,
255 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
256 if config.len() != self.num_vars {
257 return Err(crate::traits::EvaluationError::InvalidConfiguration(
258 "assignment length does not match the formula variables".into(),
259 ));
260 }
261 Ok(crate::types::Or(self.is_satisfying(config)))
262 }
263
264 fn variant() -> Vec<(&'static str, &'static str)> {
265 crate::variant_params![K]
266 }
267}
268
269impl<K: KValue> crate::solvers::BruteForceProblem for KSatisfiability<K> {
270 fn dimensions(&self) -> Vec<usize> {
271 vec![2; self.num_vars]
272 }
273}
274
275crate::declare_variants! {
276 default KSatisfiability<KN> => "2^num_vars",
277 KSatisfiability<K2> => "num_vars + num_clauses" aliases ["2SAT"],
278 KSatisfiability<K3> => "1.307^num_vars" aliases ["3SAT"],
279}
280
281crate::register_brute_force! {
282 KSatisfiability<KN> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
283 KSatisfiability<K2> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
284 KSatisfiability<K3> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
285}
286
287#[cfg(feature = "example-db")]
288pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
289 use super::CNFClause;
290 vec![crate::example_db::specs::ModelExampleSpec {
291 id: "ksatisfiability_k3",
292 instance: Box::new(KSatisfiability::<K3>::new(
293 3,
294 vec![
295 CNFClause::new(vec![1, 2, 3]),
296 CNFClause::new(vec![-1, -2, 3]),
297 CNFClause::new(vec![1, -2, -3]),
298 ],
299 )),
300 optimal_config: serde_json::json!(vec![false, false, true]),
301 optimal_value: serde_json::json!(true),
302 }]
303}
304
305#[cfg(test)]
306#[path = "../../unit_tests/models/formula/ksat.rs"]
307mod tests;