Skip to content

Commit fb79d78

Browse files
committed
Improved doc; conversion between Lut7 and u128; cleanup rustsat use; bump version number
1 parent 5e33802 commit fb79d78

File tree

5 files changed

+111
-70
lines changed

5 files changed

+111
-70
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[package]
22
name = "volute"
3-
version = "1.1.5"
3+
version = "1.1.6"
44
edition = "2021"
55

66
authors = ["Gabriel Gouvine <[email protected]>"]
77
description = "Boolean functions implementation, represented as lookup tables (LUT) or sum-of-products (SOP)"
88
license = "MIT OR Apache-2.0"
9-
keywords = ["boolean", "logic", "LUT", "lookup-table", "truth-table"]
9+
keywords = ["boolean-logic", "lookup-table", "truth-table"]
1010
repository = "https://github.com/Coloquinte/volute"
1111
homepage = "https://github.com/Coloquinte/volute"
1212
categories = ["mathematics", "algorithms"]

README.md

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<!-- cargo-rdme start -->
66

7-
Logic function manipulation using truth tables (LUTs) that represent the
7+
Logic function manipulation using truth tables (or lookup tables, `Luts`) that represent the
88
value of the function for the 2<sup>n</sup> possible inputs.
99

1010
The crate implements optimized truth table datastructures, either arbitrary-size truth tables
@@ -19,32 +19,35 @@ API and documentation try to follow the same terminology as the C++ library
1919

2020
# Examples
2121

22-
Create a constant-one Lut with five variables.
23-
Check its hexadecimal value.
22+
Create a constant-one Lut with five variables and a constant-zero Lut with 4 variables.
2423
```rust
25-
let lut = Lut::one(5);
26-
assert_eq!(lut.to_string(), "Lut5(ffffffff)");
24+
let lut5 = Lut::one(5);
25+
let lut4 = Lut::zero(4);
2726
```
2827

29-
Create a Lut4 (four variables) which is the logical and of the 1st and 3rd.
30-
Check its hexadecimal value.
28+
Create a Lut2 representing the first variable. Swap its inputs. Check the result.
29+
```rust
30+
let lut = Lut2::nth_var(0);
31+
assert_eq!(lut.swap(0, 1), Lut2::nth_var(1));
32+
```
33+
34+
Perform the logical and between two Lut4. Check its hexadecimal value.
3135
```rust
3236
let lut = Lut4::nth_var(0) & Lut4::nth_var(2);
3337
assert_eq!(lut.to_string(), "Lut4(a0a0)");
3438
```
3539

36-
Create a Lut6 (six variables) from its hexadecimal value
37-
Display its hexadecimal value.
40+
Create a Lut6 (6 variables) from its hexadecimal value. Display it.
3841
```rust
3942
let lut = Lut6::from_hex_string("0123456789abcdef").unwrap();
4043
print!("{lut}");
4144
```
4245

43-
Create a random Lut6 (six variables).
44-
Display its hexadecimal value.
46+
Small Luts (3 to 7 variables) can be converted to the integer type of the same size.
4547
```rust
46-
let lut = Lut6::random();
47-
print!("{lut}");
48+
let lut5: u32 = Lut5::random().into();
49+
let lut6: u64 = Lut6::random().into();
50+
let lut7: u128 = Lut7::random().into();
4851
```
4952

5053
Create the parity function on three variables, and check that in can be decomposed as a Xor.
@@ -55,32 +58,37 @@ assert_eq!(lut.top_decomposition(0), DecompositionType::Xor);
5558
assert_eq!(format!("{lut:b}"), "Lut3(10010110)");
5659
```
5760

58-
Create a Lut2 and swap its inputs.
59-
Check the result.
60-
```rust
61-
let lut = Lut2::nth_var(0);
62-
assert_eq!(lut.swap(0, 1), Lut2::nth_var(1));
63-
```
64-
6561
## Sum of products and Exclusive sum of products
6662

6763
Volute provides Sum-of-Products (SOP) and Exclusive Sum-of-Products (ESOP)
68-
representations, including exact decomposition methods using a MILP solver
69-
or a SAT solver.
64+
representations.
65+
66+
Create Sum of products and perform operations on them.
67+
```rust
68+
let var4 = Sop::nth_var(10, 4);
69+
let var2 = Sop::nth_var(10, 2);
70+
let var_and = var4 & var2;
71+
```
72+
73+
Exact decomposition methods can be used with the features `optim-mip` (using a MILP solver)
74+
or `optim-sat` (using a SAT solver).
7075

7176
```rust
72-
let lut = Lut::parity(6);
73-
let sop = sop::optim::optimize_esop_mip(&[lut], 1, 2);
77+
let lut = Lut::threshold(6, 5);
78+
let esop = sop::optim::optimize_esop_mip(&[lut], 1, 2);
7479
```
7580

7681
## Canonical representation
7782

78-
For boolean optimization, Luts can be represented by a canonical
79-
representation, that is identical up to variable complementation (N),
80-
input permutation (P), or both (NPN).
83+
For boolean optimization, Luts have several canonical forms that allow to only store
84+
optimizations for a small subset of Luts.
85+
Methods are available to find the smallest Lut that is identical up to variable
86+
complementation (N), input permutation (P), or both (NPN).
8187

82-
```rust
83-
let lut = Lut6::parity();
88+
```rust
89+
let lut = Lut4::threshold(3);
90+
let (canonical, flips) = lut.n_canonization();
91+
let (canonical, perm) = lut.p_canonization();
8492
let (canonical, perm, flips) = lut.npn_canonization();
8593
```
8694

src/lib.rs

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![warn(missing_docs)]
22
#![cfg_attr(docsrs, feature(doc_cfg))]
33

4-
//! Logic function manipulation using truth tables (LUTs) that represent the
4+
//! Logic function manipulation using truth tables (or lookup tables, `Luts`) that represent the
55
//! value of the function for the 2<sup>n</sup> possible inputs.
66
//!
77
//! The crate implements optimized truth table datastructures, either arbitrary-size truth tables
@@ -16,39 +16,46 @@
1616
//!
1717
//! # Examples
1818
//!
19-
//! Create a constant-one Lut with five variables.
20-
//! Check its hexadecimal value.
19+
//! Create a constant-one Lut with five variables and a constant-zero Lut with 4 variables.
2120
//! ```
2221
//! # use volute::Lut;
23-
//! let lut = Lut::one(5);
24-
//! assert_eq!(lut.to_string(), "Lut5(ffffffff)");
22+
//! let lut5 = Lut::one(5);
23+
//! let lut4 = Lut::zero(4);
2524
//! ```
2625
//!
27-
//! Create a Lut4 (four variables) which is the logical and of the 1st and 3rd.
28-
//! Check its hexadecimal value.
26+
//! Create a Lut2 representing the first variable. Swap its inputs. Check the result.
27+
//! ```
28+
//! # use volute::Lut2;
29+
//! let lut = Lut2::nth_var(0);
30+
//! assert_eq!(lut.swap(0, 1), Lut2::nth_var(1));
31+
//! ```
32+
//!
33+
//! Perform the logical and between two Lut4. Check its hexadecimal value.
2934
//! ```
3035
//! # use volute::Lut4;
3136
//! let lut = Lut4::nth_var(0) & Lut4::nth_var(2);
3237
//! assert_eq!(lut.to_string(), "Lut4(a0a0)");
3338
//! ```
3439
//!
35-
//! Create a Lut6 (six variables) from its hexadecimal value
36-
//! Display its hexadecimal value.
40+
//! Create a Lut6 (6 variables) from its hexadecimal value. Display it.
3741
//! ```
3842
//! # use volute::Lut6;
3943
//! # use std::fmt::Display;
4044
//! let lut = Lut6::from_hex_string("0123456789abcdef").unwrap();
4145
//! print!("{lut}");
4246
//! ```
4347
//!
44-
//! Create a random Lut6 (six variables).
45-
//! Display its hexadecimal value.
48+
//! Small Luts (3 to 7 variables) can be converted to the integer type of the same size.
4649
//! ```
50+
//! # use volute::Lut5;
4751
//! # use volute::Lut6;
52+
//! # use volute::Lut7;
4853
//! # #[cfg(feature = "rand")]
49-
//! let lut = Lut6::random();
54+
//! let lut5: u32 = Lut5::random().into();
5055
//! # #[cfg(feature = "rand")]
51-
//! print!("{lut}");
56+
//! let lut6: u64 = Lut6::random().into();
57+
//! # #[cfg(feature = "rand")]
58+
//! let lut7: u128 = Lut7::random().into();
5259
//! ```
5360
//!
5461
//! Create the parity function on three variables, and check that in can be decomposed as a Xor.
@@ -61,37 +68,43 @@
6168
//! assert_eq!(format!("{lut:b}"), "Lut3(10010110)");
6269
//! ```
6370
//!
64-
//! Create a Lut2 and swap its inputs.
65-
//! Check the result.
66-
//! ```
67-
//! # use volute::Lut2;
68-
//! let lut = Lut2::nth_var(0);
69-
//! assert_eq!(lut.swap(0, 1), Lut2::nth_var(1));
70-
//! ```
71-
//!
7271
//! ## Sum of products and Exclusive sum of products
7372
//!
7473
//! Volute provides Sum-of-Products (SOP) and Exclusive Sum-of-Products (ESOP)
75-
//! representations, including exact decomposition methods using a MILP solver
76-
//! or a SAT solver.
74+
//! representations.
75+
//!
76+
//! Create Sum of products and perform operations on them.
77+
//! ```
78+
//! # use volute::sop::Cube;
79+
//! # use volute::sop::Sop;
80+
//! let var4 = Sop::nth_var(10, 4);
81+
//! let var2 = Sop::nth_var(10, 2);
82+
//! let var_and = var4 & var2;
83+
//! ```
84+
//!
85+
//! Exact decomposition methods can be used with the features `optim-mip` (using a MILP solver)
86+
//! or `optim-sat` (using a SAT solver).
7787
//!
7888
//! ```
7989
//! # use volute::Lut;
8090
//! # use volute::sop;
81-
//! let lut = Lut::parity(6);
91+
//! let lut = Lut::threshold(6, 5);
8292
//! # #[cfg(feature = "optim-mip")]
83-
//! let sop = sop::optim::optimize_esop_mip(&[lut], 1, 2);
93+
//! let esop = sop::optim::optimize_esop_mip(&[lut], 1, 2);
8494
//! ```
8595
//!
8696
//! ## Canonical representation
8797
//!
88-
//! For boolean optimization, Luts can be represented by a canonical
89-
//! representation, that is identical up to variable complementation (N),
90-
//! input permutation (P), or both (NPN).
98+
//! For boolean optimization, Luts have several canonical forms that allow to only store
99+
//! optimizations for a small subset of Luts.
100+
//! Methods are available to find the smallest Lut that is identical up to variable
101+
//! complementation (N), input permutation (P), or both (NPN).
91102
//!
92-
//! ```
93-
//! # use volute::Lut6;
94-
//! let lut = Lut6::parity();
103+
//! ```
104+
//! # use volute::Lut4;
105+
//! let lut = Lut4::threshold(3);
106+
//! let (canonical, flips) = lut.n_canonization();
107+
//! let (canonical, perm) = lut.p_canonization();
95108
//! let (canonical, perm, flips) = lut.npn_canonization();
96109
//! ```
97110
//!

src/sop/optim/sat.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
use std::cmp::max;
44
use std::iter::zip;
55

6-
use rustsat::instances::ManageVars;
76
use rustsat::instances::SatInstance;
87
use rustsat::solvers::Solve;
98
use rustsat::solvers::SolverResult;
@@ -81,7 +80,7 @@ impl<'a> SopModeler<'a> {
8180
fn build_variables(&mut self, n: usize) -> Vec<Lit> {
8281
let mut ret = Vec::new();
8382
for _ in 0..n {
84-
ret.push(self.instance.var_manager().new_var().pos_lit());
83+
ret.push(self.instance.new_var().pos_lit());
8584
}
8685
ret
8786
}
@@ -91,7 +90,7 @@ impl<'a> SopModeler<'a> {
9190
for _ in 0..n {
9291
let mut fn_vars = Vec::new();
9392
for _ in self.functions {
94-
fn_vars.push(self.instance.var_manager().new_var().pos_lit());
93+
fn_vars.push(self.instance.new_var().pos_lit());
9594
}
9695
ret.push(fn_vars);
9796
}
@@ -217,7 +216,7 @@ impl<'a> SopModeler<'a> {
217216
));
218217
}
219218
let mut solver = rustsat_kissat::Kissat::default();
220-
solver.add_cnf(inst.clone().as_cnf().0).unwrap();
219+
solver.add_cnf(inst.into_cnf().0).unwrap();
221220
if solver.solve().unwrap() == SolverResult::Sat {
222221
let sol = solver.solution(self.max_var()).unwrap();
223222
Some(sol)
@@ -368,7 +367,7 @@ impl<'a> EsopModeler<'a> {
368367
fn build_variables(&mut self, n: usize) -> Vec<Lit> {
369368
let mut ret = Vec::new();
370369
for _ in 0..n {
371-
ret.push(self.instance.var_manager().new_var().pos_lit());
370+
ret.push(self.instance.new_var().pos_lit());
372371
}
373372
ret
374373
}
@@ -378,7 +377,7 @@ impl<'a> EsopModeler<'a> {
378377
for _ in 0..n {
379378
let mut fn_vars = Vec::new();
380379
for _ in self.functions {
381-
fn_vars.push(self.instance.var_manager().new_var().pos_lit());
380+
fn_vars.push(self.instance.new_var().pos_lit());
382381
}
383382
ret.push(fn_vars);
384383
}
@@ -424,7 +423,7 @@ impl<'a> EsopModeler<'a> {
424423
let mut xor_val = vars[0];
425424
for i in 1..vars.len() {
426425
let v = vars[i];
427-
let next_val = self.instance.var_manager().new_var().pos_lit();
426+
let next_val = self.instance.new_var().pos_lit();
428427
self.instance.add_ternary(!xor_val, !next_val, !v);
429428
self.instance.add_ternary(!xor_val, next_val, v);
430429
self.instance.add_ternary(xor_val, !next_val, v);
@@ -508,7 +507,7 @@ impl<'a> EsopModeler<'a> {
508507
));
509508
}
510509
let mut solver = rustsat_kissat::Kissat::default();
511-
solver.add_cnf(inst.clone().as_cnf().0).unwrap();
510+
solver.add_cnf(inst.into_cnf().0).unwrap();
512511
if solver.solve().unwrap() == SolverResult::Sat {
513512
let sol = solver.solution(self.max_var()).unwrap();
514513
Some(sol)

src/static_lut.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,12 @@ impl From<u64> for Lut6 {
660660
}
661661
}
662662

663+
impl From<u128> for Lut7 {
664+
fn from(table: u128) -> Lut7 {
665+
Lut7::from_blocks(&[(table & 0xffff_ffff_ffff_ffff) as u64, (table >> 64) as u64])
666+
}
667+
}
668+
663669
impl From<Lut3> for u8 {
664670
fn from(lut: Lut3) -> u8 {
665671
(lut.table[0] & !VAR_MASK[3]) as u8
@@ -684,6 +690,12 @@ impl From<Lut6> for u64 {
684690
}
685691
}
686692

693+
impl From<Lut7> for u128 {
694+
fn from(lut: Lut7) -> u128 {
695+
lut.table[0] as u128 + ((lut.table[1] as u128) << 64)
696+
}
697+
}
698+
687699
#[cfg(test)]
688700
mod tests {
689701
use crate::{Lut0, Lut1, Lut2, Lut3, Lut4, Lut5, Lut6, Lut7};
@@ -791,18 +803,27 @@ mod tests {
791803
for _ in 0..10 {
792804
let lut = Lut3::random();
793805
assert_eq!(lut, Lut3::from(u8::from(lut)));
806+
assert_eq!(lut.to_hex_string(), format!("{:02x}", u8::from(lut)));
794807
}
795808
for _ in 0..10 {
796809
let lut = Lut4::random();
797810
assert_eq!(lut, Lut4::from(u16::from(lut)));
811+
assert_eq!(lut.to_hex_string(), format!("{:04x}", u16::from(lut)));
798812
}
799813
for _ in 0..10 {
800814
let lut = Lut5::random();
801815
assert_eq!(lut, Lut5::from(u32::from(lut)));
816+
assert_eq!(lut.to_hex_string(), format!("{:08x}", u32::from(lut)));
802817
}
803818
for _ in 0..10 {
804819
let lut = Lut6::random();
805820
assert_eq!(lut, Lut6::from(u64::from(lut)));
821+
assert_eq!(lut.to_hex_string(), format!("{:016x}", u64::from(lut)));
822+
}
823+
for _ in 0..10 {
824+
let lut = Lut7::random();
825+
assert_eq!(lut, Lut7::from(u128::from(lut)));
826+
assert_eq!(lut.to_hex_string(), format!("{:032x}", u128::from(lut)));
806827
}
807828
}
808829

0 commit comments

Comments
 (0)