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 module_path: module_path!(),
19 description: "Find minimum-term DNF formula equivalent to a Boolean function",
20 fields: &[
21 FieldInfo { name: "num_variables", type_name: "usize", description: "Number of Boolean variables" },
22 FieldInfo { name: "truth_table", type_name: "Vec<bool>", description: "Truth table of length 2^n" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
31pub struct PrimeImplicant {
32 pub pattern: Vec<Option<bool>>,
34}
35
36impl PrimeImplicant {
37 pub fn covers(&self, minterm: usize) -> bool {
39 for (i, &p) in self.pattern.iter().enumerate() {
40 if let Some(val) = p {
41 let bit = ((minterm >> (self.pattern.len() - 1 - i)) & 1) == 1;
42 if bit != val {
43 return false;
44 }
45 }
46 }
47 true
48 }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct MinimumDisjunctiveNormalForm {
73 num_variables: usize,
75 truth_table: Vec<bool>,
77 prime_implicants: Vec<PrimeImplicant>,
79 minterms: Vec<usize>,
81}
82
83impl MinimumDisjunctiveNormalForm {
84 pub fn new(num_variables: usize, truth_table: Vec<bool>) -> Self {
90 assert!(num_variables >= 1, "Need at least 1 variable");
91 assert_eq!(
92 truth_table.len(),
93 1 << num_variables,
94 "Truth table must have 2^n entries"
95 );
96
97 let minterms: Vec<usize> = truth_table
98 .iter()
99 .enumerate()
100 .filter_map(|(i, &v)| if v { Some(i) } else { None })
101 .collect();
102 assert!(
103 !minterms.is_empty(),
104 "Function must have at least one minterm"
105 );
106
107 let prime_implicants = compute_prime_implicants(num_variables, &minterms);
108
109 Self {
110 num_variables,
111 truth_table,
112 prime_implicants,
113 minterms,
114 }
115 }
116
117 pub fn num_variables(&self) -> usize {
119 self.num_variables
120 }
121
122 pub fn truth_table(&self) -> &[bool] {
124 &self.truth_table
125 }
126
127 pub fn prime_implicants(&self) -> &[PrimeImplicant] {
129 &self.prime_implicants
130 }
131
132 pub fn num_prime_implicants(&self) -> usize {
134 self.prime_implicants.len()
135 }
136
137 pub fn minterms(&self) -> &[usize] {
139 &self.minterms
140 }
141}
142
143impl Problem for MinimumDisjunctiveNormalForm {
144 const NAME: &'static str = "MinimumDisjunctiveNormalForm";
145 type Value = Min<usize>;
146
147 fn dims(&self) -> Vec<usize> {
148 vec![2; self.prime_implicants.len()]
149 }
150
151 fn evaluate(&self, config: &[usize]) -> Min<usize> {
152 if config.len() != self.prime_implicants.len() {
153 return Min(None);
154 }
155
156 let selected: Vec<usize> = config
158 .iter()
159 .enumerate()
160 .filter_map(|(i, &v)| if v == 1 { Some(i) } else { None })
161 .collect();
162
163 if selected.is_empty() {
164 return Min(None);
165 }
166
167 for &mt in &self.minterms {
169 let covered = selected
170 .iter()
171 .any(|&pi_idx| self.prime_implicants[pi_idx].covers(mt));
172 if !covered {
173 return Min(None);
174 }
175 }
176
177 Min(Some(selected.len()))
178 }
179
180 fn variant() -> Vec<(&'static str, &'static str)> {
181 crate::variant_params![]
182 }
183}
184
185crate::declare_variants! {
186 default MinimumDisjunctiveNormalForm => "2^(3^num_variables)",
187}
188
189fn compute_prime_implicants(num_vars: usize, minterms: &[usize]) -> Vec<PrimeImplicant> {
193 use std::collections::HashSet;
194
195 if minterms.is_empty() {
196 return vec![];
197 }
198
199 type Pattern = Vec<Option<bool>>;
200
201 let mut current: Vec<Pattern> = minterms
202 .iter()
203 .map(|&mt| {
204 (0..num_vars)
205 .map(|i| Some(((mt >> (num_vars - 1 - i)) & 1) == 1))
206 .collect()
207 })
208 .collect();
209
210 let mut all_prime: HashSet<Pattern> = HashSet::new();
211
212 loop {
213 let mut next_set: HashSet<Pattern> = HashSet::new();
214 let mut used = vec![false; current.len()];
215
216 for i in 0..current.len() {
217 for j in (i + 1)..current.len() {
218 if let Some(merged) = try_merge(¤t[i], ¤t[j]) {
219 next_set.insert(merged);
220 used[i] = true;
221 used[j] = true;
222 }
223 }
224 }
225
226 for (i, &was_used) in used.iter().enumerate() {
227 if !was_used {
228 all_prime.insert(current[i].clone());
229 }
230 }
231
232 if next_set.is_empty() {
233 break;
234 }
235 current = next_set.into_iter().collect();
236 }
237
238 let mut result: Vec<PrimeImplicant> = all_prime
239 .into_iter()
240 .map(|pattern| PrimeImplicant { pattern })
241 .collect();
242 result.sort_by(|a, b| a.pattern.cmp(&b.pattern));
244 result
245}
246
247fn try_merge(a: &[Option<bool>], b: &[Option<bool>]) -> Option<Vec<Option<bool>>> {
250 if a.len() != b.len() {
251 return None;
252 }
253
254 let mut diff_count = 0;
255 let mut diff_pos = 0;
256
257 for (i, (va, vb)) in a.iter().zip(b.iter()).enumerate() {
258 if va != vb {
259 diff_count += 1;
260 diff_pos = i;
261 if diff_count > 1 {
262 return None;
263 }
264 }
265 }
266
267 if diff_count == 1 {
268 let mut merged = a.to_vec();
269 merged[diff_pos] = None;
270 Some(merged)
271 } else {
272 None
273 }
274}
275
276#[cfg(feature = "example-db")]
277pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
278 vec![crate::example_db::specs::ModelExampleSpec {
279 id: "minimum_disjunctive_normal_form",
280 instance: Box::new(MinimumDisjunctiveNormalForm::new(
281 3,
282 vec![false, true, true, true, true, true, true, false],
283 )),
284 optimal_config: vec![1, 0, 0, 1, 1, 0],
288 optimal_value: serde_json::json!(3),
289 }]
290}
291
292#[cfg(test)]
293#[path = "../../unit_tests/models/misc/minimum_disjunctive_normal_form.rs"]
294mod tests;