diff options
| author | 2025-04-20 19:58:39 +0300 | |
|---|---|---|
| committer | 2025-04-20 19:58:39 +0300 | |
| commit | 7c5de6bcd852284289ecf59f0eb509c5fb599e50 (patch) | |
| tree | 1ebe9fd8a93b920a874315b41dcd6128ff475259 | |
| parent | feat: initial commit (diff) | |
| download | logic-rust-7c5de6bcd852284289ecf59f0eb509c5fb599e50.tar.gz logic-rust-7c5de6bcd852284289ecf59f0eb509c5fb599e50.tar.bz2 logic-rust-7c5de6bcd852284289ecf59f0eb509c5fb599e50.tar.lz logic-rust-7c5de6bcd852284289ecf59f0eb509c5fb599e50.tar.xz logic-rust-7c5de6bcd852284289ecf59f0eb509c5fb599e50.tar.zst logic-rust-7c5de6bcd852284289ecf59f0eb509c5fb599e50.zip | |
feat: basic minimization algorithm
Diffstat (limited to '')
| -rw-r--r-- | Cargo.lock | 18 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/main.rs | 272 | 
3 files changed, 290 insertions, 1 deletions
| @@ -3,5 +3,23 @@  version = 4  [[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]]  name = "logic-rust"  version = "0.1.0" +dependencies = [ + "itertools", +] @@ -4,3 +4,4 @@ version = "0.1.0"  edition = "2024"  [dependencies] +itertools = "0.14.0" diff --git a/src/main.rs b/src/main.rs index e7a11a9..18d6ca2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,273 @@ +use std::collections::{HashMap, HashSet}; + +use itertools::Itertools; + +// NOTE: PartialOrd and Ord have no sense, but it is needed to sort them somehow +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Cube { +    t: usize, +    f: usize, +} + +impl Cube { +    fn cost(&self) -> usize { +        self.t.count_ones() as usize + self.f.count_ones() as usize +    } + +    fn covers(&self, minterm: usize) -> bool { +        let mask = self.t | self.f; +        minterm & mask == self.t && !minterm & mask == self.f +    } + +    fn combine(&self, other: &Self) -> Option<Self> { +        let dt = self.t ^ other.t; +        let df = self.f ^ other.f; + +        if dt == df && dt.count_ones() == 1 { +            Some(Self { +                t: self.t & other.t, +                f: self.f & other.f, +            }) +        } else { +            None +        } +    } +} + +fn cube_to_string(n: usize, cube: &Cube) -> String { +    let mut s = String::new(); + +    let Cube { mut t, mut f } = *cube; +    for _ in 0..n { +        match (t & 1, f & 1) { +            (1, 0) => s.push('1'), +            (0, 1) => s.push('0'), +            (0, 0) => s.push('x'), +            _ => unreachable!(), +        } + +        t >>= 1; +        f >>= 1; +    } + +    s.chars().rev().collect() +} + +fn cube_to_var_string(vars: &[&str], cube: &Cube) -> String { +    let mut used_vars: Vec<String> = Vec::with_capacity(vars.len()); + +    let Cube { mut t, mut f } = *cube; +    for i in (0..vars.len()).rev() { +        match (t & 1, f & 1) { +            (1, 0) => used_vars.push(vars[i].into()), +            (0, 1) => used_vars.push(format!("{}'", vars[i])), +            (0, 0) => (), +            _ => unreachable!(), +        } + +        t >>= 1; +        f >>= 1; +    } + +    used_vars.into_iter().rev().join(" * ") +} + +fn minimize_prime_implicants(n: usize, minterms: &[usize], maxterms: &[usize]) -> Vec<Cube> { +    let minterms_set: HashSet<_> = minterms.iter().copied().collect(); +    let maxterms_set: HashSet<_> = maxterms.iter().copied().collect(); + +    let mut anyterms_set = HashSet::new(); +    for i in 0..2usize.pow(n as u32) { +        if !minterms_set.contains(&i) && !maxterms_set.contains(&i) { +            anyterms_set.insert(i); +        } +    } + +    let mask = (1 << n) - 1; +    let initial_cubes: Vec<_> = minterms_set +        .union(&anyterms_set) +        .sorted() +        .map(|&i| Cube { t: i, f: !i & mask }) +        .collect(); + +    let mut covered = vec![vec![false; initial_cubes.len()]]; +    let mut cubes = vec![initial_cubes]; + +    for iteration in 0..n { +        let current_covered = &mut covered[iteration]; +        let current_cubes = &cubes[iteration]; + +        let mut new_cubes = HashSet::new(); +        for i in 0..current_cubes.len() { +            for j in i + 1..current_cubes.len() { +                let a = ¤t_cubes[i]; +                let b = ¤t_cubes[j]; + +                if let Some(combined_cube) = a.combine(b) { +                    current_covered[i] = true; +                    current_covered[j] = true; +                    new_cubes.insert(combined_cube); +                } +            } +        } + +        covered.push(vec![false; new_cubes.len()]); +        cubes.push(new_cubes.into_iter().collect()); +    } + +    let mut final_cubes = vec![]; +    for (iteration, iteration_cubes) in cubes.into_iter().enumerate() { +        for (i, cube) in iteration_cubes.into_iter().enumerate() { +            if !covered[iteration][i] { +                final_cubes.push(cube); +            } +        } +    } + +    final_cubes.sort(); +    final_cubes +} + +fn print_prime_implicants_table(n: usize, minterms: &[usize], prime_implicants: &[Cube]) { +    println!("Prime implicants:"); +    for (i, prime_implicant) in prime_implicants.iter().enumerate() { +        let cube_str = cube_to_string(n, prime_implicant); +        println!("{i}: {cube_str}"); +    } + +    println!(); +    println!("Prime Implicants Table (PIT):"); +    print!(" "); +    println!( +        "{}", +        (0..minterms.len()) +            .map(|i| format!(" {} ", minterms[i])) +            .join("") +    ); +    for (i, prime_implicant) in prime_implicants.iter().enumerate() { +        print!("{i}"); +        for &minterm in minterms { +            if prime_implicant.covers(minterm) { +                print!(" x "); +            } else { +                print!("   "); +            } +        } +        println!(); +    } +} + +fn solve_prime_implicants_table(minterms: &[usize], prime_implicants: &[Cube]) -> Vec<Cube> { +    let mut table: HashSet<(usize, usize)> = (0..prime_implicants.len()) +        .cartesian_product(0..minterms.len()) +        .filter(|&(i, j)| prime_implicants[i].covers(minterms[j])) +        .collect(); + +    let mut selected_implicants = vec![false; prime_implicants.len()]; +    loop { +        // Select essential minterms +        let mut minterms_freq = vec![0; minterms.len()]; +        table.iter().for_each(|&(_, j)| minterms_freq[j] += 1); +        table +            .iter() +            .for_each(|&(i, j)| selected_implicants[i] |= minterms_freq[j] == 1); + +        // Check if minterms are fully covered +        let mut covered_minterms = vec![false; minterms.len()]; +        table +            .iter() +            .filter(|&&(i, _)| selected_implicants[i]) +            .for_each(|&(_, j)| covered_minterms[j] = true); +        if table.is_empty() || covered_minterms.iter().all(|&v| v) { +            break; +        } + +        // Removing essential implicants +        let new_table: HashSet<_> = table +            .iter() +            .filter(|&&(i, j)| !selected_implicants[i] && !covered_minterms[j]) +            .cloned() +            .collect(); +        table = new_table; + +        if table.is_empty() { +            // All implicants are used +            break; +        } + +        // Finding minterm coverage by implicants +        let mut implicants = HashSet::new(); +        let mut covered_by_implicants: HashMap<usize, HashSet<_>> = HashMap::new(); +        table.iter().for_each(|&(i, j)| { +            implicants.insert(i); +            covered_by_implicants.entry(i).or_default().insert(j); +        }); + +        // Removing implicants by cost when essentials are not found +        // NOTE: when checking combinations, implicants must be sorted, to give constant result +        // (If not, it will variate due to order in HashSet) +        let mut removed = false; +        let mut implicants_to_remove = vec![false; prime_implicants.len()]; +        for (a, b) in implicants.iter().sorted().tuple_combinations() { +            let a_set = &covered_by_implicants[a]; +            let b_set = &covered_by_implicants[b]; +            let eq = a_set == b_set; + +            let a_cost = prime_implicants[*a].cost(); +            let b_cost = prime_implicants[*b].cost(); + +            if eq && a_cost >= b_cost { +                implicants_to_remove[*a] = true; +                removed = true; +            } else if eq { +                implicants_to_remove[*b] = true; +                removed = true; +            } +        } + +        if removed { +            let new_table: HashSet<_> = table +                .iter() +                .filter(|&&(i, _)| !implicants_to_remove[i]) +                .cloned() +                .collect(); +            table = new_table; +        } else { +            // We can't remove implicants by cost, have to choose by ourselves. +            // NOTE: this leads to non-minimal solution +            // NOTE: this SHOULD NOT happen, as we are filtering equal implicant costs too +            todo!() +        } +    } + +    prime_implicants +        .iter() +        .zip(selected_implicants) +        .filter(|&(_, select)| select) +        .map(|(cube, _)| cube) +        .copied() +        .collect() +} + +fn minimize(n: usize, minterms: &[usize], maxterms: &[usize]) -> Vec<Cube> { +    let prime_implicants = minimize_prime_implicants(n, minterms, maxterms); +    print_prime_implicants_table(n, minterms, &prime_implicants); +    solve_prime_implicants_table(minterms, &prime_implicants) +} +  fn main() { -    println!("Hello, world!"); +    let vars = ["X4", "X3", "X2", "X1"]; +    let minterms = [0, 1, 2, 3, 4]; // Термы со значением 1 +    let maxterms = [5, 6, 7, 8, 9]; // Термы со значением 0 + +    let min_cubes = minimize(4, &minterms, &maxterms); + +    println!("Итоговые термы: "); +    for cube in min_cubes { +        println!( +            "{} -> {}", +            cube_to_string(4, &cube), +            cube_to_var_string(&vars, &cube) +        ); +    }  } | 
