problemreductions/models/formula/
ksat.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
9use crate::traits::{Problem, SatisfactionProblem};
10use crate::variant::{KValue, K2, K3, KN};
11use serde::{Deserialize, Serialize};
12
13use super::CNFClause;
14
15inventory::submit! {
16 ProblemSchemaEntry {
17 name: "KSatisfiability",
18 display_name: "K-Satisfiability",
19 aliases: &["KSAT"],
20 dimensions: &[VariantDimension::new("k", "KN", &["KN", "K2", "K3"])],
21 module_path: module_path!(),
22 description: "SAT with exactly k literals per clause",
23 fields: &[
24 FieldInfo { name: "num_vars", type_name: "usize", description: "Number of Boolean variables" },
25 FieldInfo { name: "clauses", type_name: "Vec<CNFClause>", description: "Clauses each with exactly K literals" },
26 ],
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(bound(deserialize = ""))]
62pub struct KSatisfiability<K: KValue> {
63 num_vars: usize,
65 clauses: Vec<CNFClause>,
67 #[serde(skip)]
68 _phantom: std::marker::PhantomData<K>,
69}
70
71impl<K: KValue> KSatisfiability<K> {
72 pub fn new(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
79 if let Some(k) = K::K {
80 for (i, clause) in clauses.iter().enumerate() {
81 assert!(
82 clause.len() == k,
83 "Clause {} has {} literals, expected {}",
84 i,
85 clause.len(),
86 k
87 );
88 }
89 }
90 Self {
91 num_vars,
92 clauses,
93 _phantom: std::marker::PhantomData,
94 }
95 }
96
97 pub fn new_allow_less(num_vars: usize, clauses: Vec<CNFClause>) -> Self {
107 if let Some(k) = K::K {
108 for (i, clause) in clauses.iter().enumerate() {
109 assert!(
110 clause.len() <= k,
111 "Clause {} has {} literals, expected at most {}",
112 i,
113 clause.len(),
114 k
115 );
116 }
117 }
118 Self {
119 num_vars,
120 clauses,
121 _phantom: std::marker::PhantomData,
122 }
123 }
124
125 pub fn num_vars(&self) -> usize {
127 self.num_vars
128 }
129
130 pub fn num_clauses(&self) -> usize {
132 self.clauses.len()
133 }
134
135 pub fn clauses(&self) -> &[CNFClause] {
137 &self.clauses
138 }
139
140 pub fn get_clause(&self, index: usize) -> Option<&CNFClause> {
142 self.clauses.get(index)
143 }
144
145 pub fn num_literals(&self) -> usize {
147 self.clauses().iter().map(|c| c.len()).sum()
148 }
149
150 pub fn count_satisfied(&self, assignment: &[bool]) -> usize {
152 self.clauses
153 .iter()
154 .filter(|c| c.is_satisfied(assignment))
155 .count()
156 }
157
158 pub fn is_satisfying(&self, assignment: &[bool]) -> bool {
160 self.clauses.iter().all(|c| c.is_satisfied(assignment))
161 }
162
163 fn config_to_assignment(config: &[usize]) -> Vec<bool> {
165 config.iter().map(|&v| v == 1).collect()
166 }
167}
168
169impl<K: KValue> Problem for KSatisfiability<K> {
170 const NAME: &'static str = "KSatisfiability";
171 type Metric = bool;
172
173 fn dims(&self) -> Vec<usize> {
174 vec![2; self.num_vars]
175 }
176
177 fn evaluate(&self, config: &[usize]) -> bool {
178 let assignment = Self::config_to_assignment(config);
179 self.is_satisfying(&assignment)
180 }
181
182 fn variant() -> Vec<(&'static str, &'static str)> {
183 crate::variant_params![K]
184 }
185}
186
187impl<K: KValue> SatisfactionProblem for KSatisfiability<K> {}
188
189crate::declare_variants! {
190 default sat KSatisfiability<KN> => "2^num_variables",
191 sat KSatisfiability<K2> => "num_variables + num_clauses",
192 sat KSatisfiability<K3> => "1.307^num_variables",
193}
194
195#[cfg(feature = "example-db")]
196pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
197 use super::CNFClause;
198 vec![crate::example_db::specs::ModelExampleSpec {
199 id: "ksatisfiability_k3",
200 instance: Box::new(KSatisfiability::<K3>::new(
201 3,
202 vec![
203 CNFClause::new(vec![1, 2, 3]),
204 CNFClause::new(vec![-1, -2, 3]),
205 CNFClause::new(vec![1, -2, -3]),
206 ],
207 )),
208 optimal_config: vec![0, 0, 1],
209 optimal_value: serde_json::json!(true),
210 }]
211}
212
213#[cfg(test)]
214#[path = "../../unit_tests/models/formula/ksat.rs"]
215mod tests;