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)]
73pub struct MinimumDisjunctiveNormalForm {
74 num_variables: usize,
76 truth_table: Vec<bool>,
78 prime_implicants: Vec<PrimeImplicant>,
80 minterms: Vec<usize>,
82}
83
84impl MinimumDisjunctiveNormalForm {
85 pub fn new(num_variables: usize, truth_table: Vec<bool>) -> Self {
91 assert!(num_variables >= 1, "Need at least 1 variable");
92 assert_eq!(
93 truth_table.len(),
94 1 << num_variables,
95 "Truth table must have 2^n entries"
96 );
97
98 let minterms: Vec<usize> = truth_table
99 .iter()
100 .enumerate()
101 .filter_map(|(i, &v)| if v { Some(i) } else { None })
102 .collect();
103 assert!(
104 !minterms.is_empty(),
105 "Function must have at least one minterm"
106 );
107
108 let prime_implicants = compute_prime_implicants(num_variables, &minterms);
109
110 Self {
111 num_variables,
112 truth_table,
113 prime_implicants,
114 minterms,
115 }
116 }
117
118 pub fn num_variables(&self) -> usize {
120 self.num_variables
121 }
122
123 pub fn truth_table(&self) -> &[bool] {
125 &self.truth_table
126 }
127
128 pub fn prime_implicants(&self) -> &[PrimeImplicant] {
130 &self.prime_implicants
131 }
132
133 pub fn num_prime_implicants(&self) -> usize {
135 self.prime_implicants.len()
136 }
137
138 pub fn minterms(&self) -> &[usize] {
140 &self.minterms
141 }
142}
143
144impl Problem for MinimumDisjunctiveNormalForm {
145 const NAME: &'static str = "MinimumDisjunctiveNormalForm";
146 type Solution = Vec<bool>;
147 type Value = Min<i64>;
148
149 crate::problem_parameters![
150 ("num_variables", num_variables),
151 ("num_prime_implicants", num_prime_implicants),
152 ];
153
154 fn evaluate(
155 &self,
156 config: &Self::Solution,
157 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
158 Ok({
159 if config.len() != self.prime_implicants.len() {
160 return Err(crate::traits::EvaluationError::InvalidConfiguration(
161 "implicant-selection length does not match the instance".into(),
162 ));
163 }
164
165 let selected: Vec<usize> = config
167 .iter()
168 .enumerate()
169 .filter_map(|(i, &v)| if v { Some(i) } else { None })
170 .collect();
171
172 if selected.is_empty() {
173 return Ok(Min(None));
174 }
175
176 for &mt in &self.minterms {
178 let covered = selected
179 .iter()
180 .any(|&pi_idx| self.prime_implicants[pi_idx].covers(mt));
181 if !covered {
182 return Ok(Min(None));
183 }
184 }
185
186 Min(Some(i64::try_from(selected.len()).map_err(|_| {
187 crate::traits::EvaluationError::IntegerOverflow(
188 "converting DNF term count to i64".into(),
189 )
190 })?))
191 })
192 }
193
194 fn variant() -> Vec<(&'static str, &'static str)> {
195 crate::variant_params![]
196 }
197}
198
199impl crate::solvers::BruteForceProblem for MinimumDisjunctiveNormalForm {
200 fn dimensions(&self) -> Vec<usize> {
201 vec![2; self.prime_implicants.len()]
202 }
203}
204
205crate::declare_variants! {
206 default MinimumDisjunctiveNormalForm => "2^(3^num_variables)",
207}
208
209crate::register_brute_force! {
210 MinimumDisjunctiveNormalForm decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
211}
212
213fn compute_prime_implicants(num_vars: usize, minterms: &[usize]) -> Vec<PrimeImplicant> {
217 use std::collections::HashSet;
218
219 if minterms.is_empty() {
220 return vec![];
221 }
222
223 type Pattern = Vec<Option<bool>>;
224
225 let mut current: Vec<Pattern> = minterms
226 .iter()
227 .map(|&mt| {
228 (0..num_vars)
229 .map(|i| Some(((mt >> (num_vars - 1 - i)) & 1) == 1))
230 .collect()
231 })
232 .collect();
233
234 let mut all_prime: HashSet<Pattern> = HashSet::new();
235
236 loop {
237 let mut next_set: HashSet<Pattern> = HashSet::new();
238 let mut used = vec![false; current.len()];
239
240 for i in 0..current.len() {
241 for j in (i + 1)..current.len() {
242 if let Some(merged) = try_merge(¤t[i], ¤t[j]) {
243 next_set.insert(merged);
244 used[i] = true;
245 used[j] = true;
246 }
247 }
248 }
249
250 for (i, &was_used) in used.iter().enumerate() {
251 if !was_used {
252 all_prime.insert(current[i].clone());
253 }
254 }
255
256 if next_set.is_empty() {
257 break;
258 }
259 current = next_set.into_iter().collect();
260 }
261
262 let mut result: Vec<PrimeImplicant> = all_prime
263 .into_iter()
264 .map(|pattern| PrimeImplicant { pattern })
265 .collect();
266 result.sort_by(|a, b| a.pattern.cmp(&b.pattern));
268 result
269}
270
271fn try_merge(a: &[Option<bool>], b: &[Option<bool>]) -> Option<Vec<Option<bool>>> {
274 if a.len() != b.len() {
275 return None;
276 }
277
278 let mut diff_count = 0;
279 let mut diff_pos = 0;
280
281 for (i, (va, vb)) in a.iter().zip(b.iter()).enumerate() {
282 if va != vb {
283 diff_count += 1;
284 diff_pos = i;
285 if diff_count > 1 {
286 return None;
287 }
288 }
289 }
290
291 if diff_count == 1 {
292 let mut merged = a.to_vec();
293 merged[diff_pos] = None;
294 Some(merged)
295 } else {
296 None
297 }
298}
299
300#[cfg(feature = "example-db")]
301pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
302 vec![crate::example_db::specs::ModelExampleSpec {
303 id: "minimum_disjunctive_normal_form",
304 instance: Box::new(MinimumDisjunctiveNormalForm::new(
305 3,
306 vec![false, true, true, true, true, true, true, false],
307 )),
308 optimal_config: serde_json::json!(vec![true, false, false, true, true, false]),
312 optimal_value: serde_json::json!(3),
313 }]
314}
315
316#[cfg(test)]
317#[path = "../../unit_tests/models/misc/minimum_disjunctive_normal_form.rs"]
318mod tests;