problemreductions/models/misc/
minimum_disjunctive_normal_form.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
8use crate::traits::Problem;
9use crate::types::Min;
10use serde::{Deserialize, Serialize};
11
12inventory::submit! {
13 ProblemSchemaEntry {
14 name: "MinimumDisjunctiveNormalForm",
15 display_name: "Minimum Disjunctive Normal Form",
16 aliases: &["MinDNF"],
17 dimensions: &[],
18 category: crate::registry::ProblemCategory::Misc,
19 module_path: module_path!(),
20 description: "Find minimum-term DNF formula equivalent to a Boolean function",
21 fields: &[
22 FieldInfo { name: "num_variables", type_name: "usize", description: "Number of Boolean variables" },
23 FieldInfo { name: "truth_table", type_name: "Vec<bool>", description: "Truth table of length 2^n" },
24 ],
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub struct PrimeImplicant {
33 pub pattern: Vec<Option<bool>>,
35}
36
37impl PrimeImplicant {
38 pub fn covers(&self, minterm: usize) -> bool {
40 for (i, &p) in self.pattern.iter().enumerate() {
41 if let Some(val) = p {
42 let bit = ((minterm >> (self.pattern.len() - 1 - i)) & 1) == 1;
43 if bit != val {
44 return false;
45 }
46 }
47 }
48 true
49 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
73#[serde(try_from = "MinimumDisjunctiveNormalFormData")]
74pub struct MinimumDisjunctiveNormalForm {
75 num_variables: usize,
77 truth_table: Vec<bool>,
79 prime_implicants: Vec<PrimeImplicant>,
81 minterms: Vec<usize>,
83}
84
85#[derive(Deserialize)]
86struct MinimumDisjunctiveNormalFormData {
87 num_variables: usize,
88 truth_table: Vec<bool>,
89}
90
91impl TryFrom<MinimumDisjunctiveNormalFormData> for MinimumDisjunctiveNormalForm {
92 type Error = crate::registry::ConstructionError;
93 fn try_from(data: MinimumDisjunctiveNormalFormData) -> Result<Self, Self::Error> {
94 Self::try_new(data.num_variables, data.truth_table)
95 }
96}
97
98impl MinimumDisjunctiveNormalForm {
99 pub fn new(num_variables: usize, truth_table: Vec<bool>) -> Self {
105 Self::try_new(num_variables, truth_table).unwrap_or_else(|error| panic!("{error}"))
106 }
107
108 fn try_new(
109 num_variables: usize,
110 truth_table: Vec<bool>,
111 ) -> Result<Self, crate::registry::ConstructionError> {
112 if num_variables == 0 {
113 return Err("Need at least 1 variable".into());
114 }
115 if truth_table.len()
116 != 1usize
117 .checked_shl(
118 u32::try_from(num_variables).map_err(|_| "truth table size overflows usize")?,
119 )
120 .ok_or("truth table size overflows usize")?
121 {
122 return Err("Truth table must have 2^n entries".into());
123 }
124
125 let minterms: Vec<usize> = truth_table
126 .iter()
127 .enumerate()
128 .filter_map(|(i, &v)| if v { Some(i) } else { None })
129 .collect();
130 if minterms.is_empty() {
131 return Err("Function must have at least one minterm".into());
132 }
133
134 let prime_implicants = compute_prime_implicants(num_variables, &minterms);
135
136 Ok(Self {
137 num_variables,
138 truth_table,
139 prime_implicants,
140 minterms,
141 })
142 }
143
144 pub fn num_variables(&self) -> usize {
146 self.num_variables
147 }
148
149 pub fn truth_table(&self) -> &[bool] {
151 &self.truth_table
152 }
153
154 pub fn prime_implicants(&self) -> &[PrimeImplicant] {
156 &self.prime_implicants
157 }
158
159 pub fn num_prime_implicants(&self) -> usize {
161 self.prime_implicants.len()
162 }
163
164 pub fn minterms(&self) -> &[usize] {
166 &self.minterms
167 }
168}
169
170impl Problem for MinimumDisjunctiveNormalForm {
171 const NAME: &'static str = "MinimumDisjunctiveNormalForm";
172 type Solution = Vec<bool>;
173 type Value = Min<i64>;
174
175 crate::problem_parameters![
176 ("num_variables", num_variables),
177 ("num_prime_implicants", num_prime_implicants),
178 ];
179
180 fn evaluate(
181 &self,
182 config: &Self::Solution,
183 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
184 Ok({
185 if config.len() != self.prime_implicants.len() {
186 return Err(crate::traits::EvaluationError::InvalidConfiguration(
187 "implicant-selection length does not match the instance".into(),
188 ));
189 }
190
191 let selected: Vec<usize> = config
193 .iter()
194 .enumerate()
195 .filter_map(|(i, &v)| if v { Some(i) } else { None })
196 .collect();
197
198 if selected.is_empty() {
199 return Ok(Min(None));
200 }
201
202 for &mt in &self.minterms {
204 let covered = selected
205 .iter()
206 .any(|&pi_idx| self.prime_implicants[pi_idx].covers(mt));
207 if !covered {
208 return Ok(Min(None));
209 }
210 }
211
212 Min(Some(i64::try_from(selected.len()).map_err(|_| {
213 crate::traits::EvaluationError::IntegerOverflow(
214 "converting DNF term count to i64".into(),
215 )
216 })?))
217 })
218 }
219
220 fn variant() -> Vec<(&'static str, &'static str)> {
221 crate::variant_params![]
222 }
223}
224
225impl crate::solvers::BruteForceProblem for MinimumDisjunctiveNormalForm {
226 fn dimensions(&self) -> Vec<usize> {
227 vec![2; self.prime_implicants.len()]
228 }
229}
230
231crate::declare_variants! {
232 default MinimumDisjunctiveNormalForm => "2^(3^num_variables)",
233}
234
235crate::register_brute_force! {
236 MinimumDisjunctiveNormalForm decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
237}
238
239fn compute_prime_implicants(num_vars: usize, minterms: &[usize]) -> Vec<PrimeImplicant> {
243 use std::collections::HashSet;
244
245 if minterms.is_empty() {
246 return vec![];
247 }
248
249 type Pattern = Vec<Option<bool>>;
250
251 let mut current: Vec<Pattern> = minterms
252 .iter()
253 .map(|&mt| {
254 (0..num_vars)
255 .map(|i| Some(((mt >> (num_vars - 1 - i)) & 1) == 1))
256 .collect()
257 })
258 .collect();
259
260 let mut all_prime: HashSet<Pattern> = HashSet::new();
261
262 loop {
263 let mut next_set: HashSet<Pattern> = HashSet::new();
264 let mut used = vec![false; current.len()];
265
266 for i in 0..current.len() {
267 for j in (i + 1)..current.len() {
268 if let Some(merged) = try_merge(¤t[i], ¤t[j]) {
269 next_set.insert(merged);
270 used[i] = true;
271 used[j] = true;
272 }
273 }
274 }
275
276 for (i, &was_used) in used.iter().enumerate() {
277 if !was_used {
278 all_prime.insert(current[i].clone());
279 }
280 }
281
282 if next_set.is_empty() {
283 break;
284 }
285 current = next_set.into_iter().collect();
286 }
287
288 let mut result: Vec<PrimeImplicant> = all_prime
289 .into_iter()
290 .map(|pattern| PrimeImplicant { pattern })
291 .collect();
292 result.sort_by(|a, b| a.pattern.cmp(&b.pattern));
294 result
295}
296
297fn try_merge(a: &[Option<bool>], b: &[Option<bool>]) -> Option<Vec<Option<bool>>> {
300 if a.len() != b.len() {
301 return None;
302 }
303
304 let mut diff_count = 0;
305 let mut diff_pos = 0;
306
307 for (i, (va, vb)) in a.iter().zip(b.iter()).enumerate() {
308 if va != vb {
309 diff_count += 1;
310 diff_pos = i;
311 if diff_count > 1 {
312 return None;
313 }
314 }
315 }
316
317 if diff_count == 1 {
318 let mut merged = a.to_vec();
319 merged[diff_pos] = None;
320 Some(merged)
321 } else {
322 None
323 }
324}
325
326#[cfg(feature = "example-db")]
327pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
328 vec![crate::example_db::specs::ModelExampleSpec {
329 id: "minimum_disjunctive_normal_form",
330 instance: Box::new(MinimumDisjunctiveNormalForm::new(
331 3,
332 vec![false, true, true, true, true, true, true, false],
333 )),
334 optimal_config: serde_json::json!(vec![true, false, false, true, true, false]),
338 optimal_value: serde_json::json!(3),
339 }]
340}
341
342#[cfg(test)]
343#[path = "../../unit_tests/models/misc/minimum_disjunctive_normal_form.rs"]
344mod tests;