problemreductions/models/formula/
ksat.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
9use crate::traits::Problem;
10use crate::variant::{KValue, K2, K3, KN};
11use serde::{Deserialize, Serialize};
12
13use super::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 module_path: module_path!(),
58 description: "SAT with exactly k literals per clause",
59 fields: &[
60 FieldInfo { name: "num_vars", type_name: "usize", description: "Number of Boolean variables" },
61 FieldInfo { name: "clauses", type_name: "Vec<CNFClause>", description: "Clauses each with exactly K literals" },
62 ],
63 }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(bound(deserialize = ""))]
98pub struct KSatisfiability<K: KValue> {
99 num_vars: usize,
101 clauses: Vec<CNFClause>,
103 #[serde(skip)]
104 _phantom: std::marker::PhantomData<K>,
105}
106
107impl<K: KValue> KSatisfiability<K> {
108 pub fn new(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
115 if let Some(k) = K::K {
116 for (i, clause) in clauses.iter().enumerate() {
117 assert!(
118 clause.len() == k,
119 "Clause {} has {} literals, expected {}",
120 i,
121 clause.len(),
122 k
123 );
124 }
125 }
126 Self {
127 num_vars,
128 clauses,
129 _phantom: std::marker::PhantomData,
130 }
131 }
132
133 pub fn new_allow_less(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
143 if let Some(k) = K::K {
144 for (i, clause) in clauses.iter().enumerate() {
145 assert!(
146 clause.len() <= k,
147 "Clause {} has {} literals, expected at most {}",
148 i,
149 clause.len(),
150 k
151 );
152 }
153 }
154 Self {
155 num_vars,
156 clauses,
157 _phantom: std::marker::PhantomData,
158 }
159 }
160
161 pub fn num_vars(&self) -> usize {
163 self.num_vars
164 }
165
166 pub fn num_clauses(&self) -> usize {
168 self.clauses.len()
169 }
170
171 pub fn clauses(&self) -> &[CNFClause] {
173 &self.clauses
174 }
175
176 pub fn get_clause(&self, index: usize) -> Option<&CNFClause> {
178 self.clauses.get(index)
179 }
180
181 pub fn num_literals(&self) -> usize {
183 self.clauses().iter().map(|c| c.len()).sum()
184 }
185
186 pub fn register_sufficiency_padding(&self) -> usize {
188 (2 * self.num_vars).saturating_sub(self.num_clauses())
189 }
190
191 pub fn simultaneous_incongruences_num_incongruences(&self) -> usize {
192 first_n_odd_primes(self.num_vars)
193 .into_iter()
194 .map(|prime| usize::try_from(prime - 2).expect("prime fits in usize"))
195 .sum::<usize>()
196 + self.num_clauses()
197 }
198
199 pub fn simultaneous_incongruences_bound(&self) -> usize {
200 first_n_odd_primes(self.num_vars)
201 .into_iter()
202 .try_fold(1usize, |product, prime| {
203 product.checked_mul(usize::try_from(prime).expect("prime fits in usize"))
204 })
205 .expect("simultaneous incongruences bound must fit in usize")
206 }
207
208 pub fn count_satisfied(&self, assignment: &[bool]) -> usize {
210 self.clauses
211 .iter()
212 .filter(|c| c.is_satisfied(assignment))
213 .count()
214 }
215
216 pub fn is_satisfying(&self, assignment: &[bool]) -> bool {
218 self.clauses.iter().all(|c| c.is_satisfied(assignment))
219 }
220}
221
222impl<K: KValue> Problem for KSatisfiability<K> {
223 const NAME: &'static str = "KSatisfiability";
224 type Value = crate::types::Or;
225
226 fn dims(&self) -> Vec<usize> {
227 vec![2; self.num_vars]
228 }
229
230 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
231 crate::types::Or({
232 let assignment = super::config_to_assignment(config);
233 self.is_satisfying(&assignment)
234 })
235 }
236
237 fn variant() -> Vec<(&'static str, &'static str)> {
238 crate::variant_params![K]
239 }
240}
241
242crate::declare_variants! {
243 default KSatisfiability<KN> => "2^num_variables",
244 KSatisfiability<K2> => "num_variables + num_clauses" aliases ["2SAT"],
245 KSatisfiability<K3> => "1.307^num_variables" aliases ["3SAT"],
246}
247
248#[cfg(feature = "example-db")]
249pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
250 use super::CNFClause;
251 vec![crate::example_db::specs::ModelExampleSpec {
252 id: "ksatisfiability_k3",
253 instance: Box::new(KSatisfiability::<K3>::new(
254 3,
255 vec![
256 CNFClause::new(vec![1, 2, 3]),
257 CNFClause::new(vec![-1, -2, 3]),
258 CNFClause::new(vec![1, -2, -3]),
259 ],
260 )),
261 optimal_config: vec![0, 0, 1],
262 optimal_value: serde_json::json!(true),
263 }]
264}
265
266#[cfg(test)]
267#[path = "../../unit_tests/models/formula/ksat.rs"]
268mod tests;