diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6d9a5c07e..1973cda5a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -8,33 +8,70 @@ on: env: CARGO_TERM_COLOR: always + RUST_CHANNEL: stable + # Lets us format with unstable rustfmt options + RUST_FMT_CHANNEL: nightly jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Build - run: cargo build --verbose + # build: + # runs-on: ubuntu-latest + # strategy: + # matrix: + # crate: [] + # steps: + # - uses: actions/checkout@v3 + # - name: Install Rust toolchain + # run: | + # rustup toolchain install --profile minimal --no-self-update ${{ env.RUST_CHANNEL }} + # rustup default ${{ env.RUST_CHANNEL }} + # - name: Cache dependencies + # uses: Swatinem/rust-cache@v2 + # - name: Build ${{ matrix.crate }} + # run: cargo install --root dist/ --path crates/${{ matrix.crate }} + # - name: Upload build artifact + # uses: actions/upload-artifact@v3 + # with: + # name: ${{ matrix.crate }}-${{ matrix.os }} + # path: dist/ test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Run tests - run: cargo test --workspace --verbose - check: + - name: Install Rust toolchain + run: | + rustup toolchain install --profile minimal --no-self-update ${{ env.RUST_CHANNEL }} + rustup default ${{ env.RUST_CHANNEL }} + - name: Cache dependencies + uses: Swatinem/rust-cache@v2 + - name: Run cargo test + run: cargo test + clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Check Formatting - run: cargo fmt --all -- --check + - name: Install Rust toolchain + run: | + rustup toolchain install --profile minimal --component clippy --no-self-update ${{ env.RUST_CHANNEL }} + rustup default ${{ env.RUST_CHANNEL }} + - name: Cache dependencies + uses: Swatinem/rust-cache@v2 - name: Run clippy run: cargo clippy -- -D warnings + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Rust toolchain + run: | + rustup toolchain install --profile minimal --component rustfmt --no-self-update ${{ env.RUST_FMT_CHANNEL }} + rustup default ${{ env.RUST_CHANNEL }} + - name: Run cargo format + run: cargo +nightly fmt --all --check semver: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Check semver - uses: obi1kenobi/cargo-semver-checks-action@v1 + uses: obi1kenobi/cargo-semver-checks-action@v2 with: crate-name: pindakaas diff --git a/crates/pindakaas/Cargo.toml b/crates/pindakaas/Cargo.toml index 0ec057298..2dfe5690a 100644 --- a/crates/pindakaas/Cargo.toml +++ b/crates/pindakaas/Cargo.toml @@ -15,14 +15,12 @@ cached = "0.46" ipasir = { version = "0.3", features = ["ffi"] } iset = "0.2" itertools = "0.11" -num = "0.4" rustc-hash = "1.1" # Optional encoding tracing capabilities tracing = { version = "0.1", optional = true } # Optional Solver Interfaces -minisat = { git = "https://github.com/luteberget/minisat-rs.git", optional = true } # waiting on release for minisat::Lit::var() splr = { version = "0.17", optional = true } [dev-dependencies] diff --git a/crates/pindakaas/src/cardinality.rs b/crates/pindakaas/src/cardinality.rs index aea4d1a1f..6a8ea527b 100644 --- a/crates/pindakaas/src/cardinality.rs +++ b/crates/pindakaas/src/cardinality.rs @@ -1,29 +1,29 @@ use crate::{ linear::{LimitComp, LinMarker, Linear, PosCoeff}, - CardinalityOne, Checker, ClauseDatabase, Coefficient, Encoder, Literal, + CardinalityOne, CheckError, Checker, ClauseDatabase, Encoder, Lit, Valuation, }; mod sorting_network; pub use sorting_network::SortingNetworkEncoder; #[derive(Clone, Debug)] -pub struct Cardinality { - pub lits: Vec, - pub cmp: LimitComp, - pub k: PosCoeff, +pub struct Cardinality { + pub(crate) lits: Vec, + pub(crate) cmp: LimitComp, + pub(crate) k: PosCoeff, } -impl From> for Cardinality { - fn from(card1: CardinalityOne) -> Self { +impl From for Cardinality { + fn from(card1: CardinalityOne) -> Self { Self { lits: card1.lits, cmp: card1.cmp, - k: C::one().into(), + k: PosCoeff::new(1), } } } -impl Cardinality { +impl Cardinality { #[cfg(feature = "trace")] #[allow(dead_code)] // FIXME: Remove when used pub(crate) fn trace_print(&self) -> String { @@ -39,20 +39,18 @@ impl Cardinality { } } -impl Checker for Cardinality { - type Lit = Lit; - - fn check(&self, solution: &[Self::Lit]) -> Result<(), crate::CheckError> { - Linear::from(self.clone()).check(solution) +impl Checker for Cardinality { + fn check(&self, value: F) -> Result<(), CheckError> { + Linear::from(self.clone()).check(value) } } // Automatically implement AtMostOne encoding when you can encode Cardinality constraints -impl> + CardMarker> - Encoder> for Enc +impl + CardMarker> Encoder + for Enc { - fn encode(&mut self, db: &mut DB, con: &CardinalityOne) -> crate::Result { - self.encode(db, &Cardinality::::from(con.clone())) + fn encode(&mut self, db: &mut DB, con: &CardinalityOne) -> crate::Result { + self.encode(db, &Cardinality::from(con.clone())) } } // local marker trait, to ensure the previous definition only applies within this crate @@ -71,18 +69,18 @@ pub(crate) mod tests { $encoder, 3, &Cardinality { - lits: vec![-1, -2, 3], + lits: lits![-1, -2, 3], cmp: LimitComp::LessEq, - k: 2.into() + k: PosCoeff::new(2) } => vec![ - vec![-1, -2, -3], - vec![-1, 2, -3], - vec![-1, 2, 3], - vec![1, -2, -3], - vec![1, -2, 3], - vec![1, 2, -3], - vec![1, 2, 3], + lits![-1, -2, -3], + lits![-1, 2, -3], + lits![-1, 2, 3], + lits![1, -2, -3], + lits![1, -2, 3], + lits![1, 2, -3], + lits![1, 2, 3], ] ); } @@ -93,15 +91,15 @@ pub(crate) mod tests { $encoder, 3, &Cardinality { - lits: vec![1, 2, 3], + lits: lits![1, 2, 3], cmp: LimitComp::Equal, - k: 1.into() + k: PosCoeff::new(1) } => vec![ - vec![ 1,-2,-3], - vec![-1, 2,-3], - vec![-1,-2, 3], + lits![ 1,-2,-3], + lits![-1, 2,-3], + lits![-1,-2, 3], ] ); } @@ -113,15 +111,15 @@ pub(crate) mod tests { $encoder, 3, &Cardinality { - lits: vec![1, 2, 3], + lits: lits![1, 2, 3], cmp: LimitComp::Equal, - k: 2.into() + k: PosCoeff::new(2) } => vec![ - vec![1, 2, -3], - vec![1, -2, 3], - vec![-1, 2, 3], + lits![1, 2, -3], + lits![1, -2, 3], + lits![-1, 2, 3], ] ); } @@ -132,17 +130,17 @@ pub(crate) mod tests { $encoder, 4, &Cardinality { - lits: vec![1, 2, 3, 4], + lits: lits![1, 2, 3, 4], cmp: LimitComp::Equal, - k: 2.into() + k: PosCoeff::new(2) } => vec![ - vec![1, 2, -3, -4], - vec![1, -2, 3, -4], - vec![-1, 2, 3, -4], - vec![1, -2, -3, 4], - vec![-1, 2, -3, 4], - vec![-1, -2, 3, 4], + lits![1, 2, -3, -4], + lits![1, -2, 3, -4], + lits![-1, 2, 3, -4], + lits![1, -2, -3, 4], + lits![-1, 2, -3, 4], + lits![-1, -2, 3, 4], ] ); } @@ -155,28 +153,25 @@ pub(crate) mod tests { $encoder, 5, &Cardinality { - lits: vec![ 1, 2, 3, 4 ,5 ], + lits: lits![1, 2, 3, 4 ,5], cmp: LimitComp::Equal, - k: 3.into() + k: PosCoeff::new(3) } => vec![ - vec![1, 2, 3, -4, -5], - vec![1, 2, -3, 4, -5], - vec![1, -2, 3, 4, -5], - vec![-1, 2, 3, 4, -5], - vec![1, 2, -3, -4, 5], - vec![1, -2, 3, -4, 5], - vec![-1, 2, 3, -4, 5], - vec![1, -2, -3, 4, 5], - vec![-1, 2, -3, 4, 5], - vec![-1, -2, 3, 4, 5], + lits![1, 2, 3, -4, -5], + lits![1, 2, -3, 4, -5], + lits![1, -2, 3, 4, -5], + lits![-1, 2, 3, 4, -5], + lits![1, 2, -3, -4, 5], + lits![1, -2, 3, -4, 5], + lits![-1, 2, 3, -4, 5], + lits![1, -2, -3, 4, 5], + lits![-1, 2, -3, 4, 5], + lits![-1, -2, 3, 4, 5], ] ); } - - - }; } diff --git a/crates/pindakaas/src/cardinality/sorting_network.rs b/crates/pindakaas/src/cardinality/sorting_network.rs index 44df15339..078e0b25d 100644 --- a/crates/pindakaas/src/cardinality/sorting_network.rs +++ b/crates/pindakaas/src/cardinality/sorting_network.rs @@ -1,7 +1,7 @@ use crate::{ int::IntVarEnc, sorted::{Sorted, SortedEncoder}, - Cardinality, ClauseDatabase, Coefficient, Encoder, Result, + Cardinality, ClauseDatabase, Encoder, Result, }; /// Encoder for the linear constraints that ∑ litsᵢ ≷ k using a sorting network @@ -28,16 +28,14 @@ impl SortingNetworkEncoder { } } -impl Encoder> - for SortingNetworkEncoder -{ - fn encode(&mut self, db: &mut DB, card: &Cardinality) -> Result { +impl Encoder for SortingNetworkEncoder { + fn encode(&mut self, db: &mut DB, card: &Cardinality) -> Result { self.sorted_encoder.encode( db, &Sorted::new( card.lits.as_slice(), card.cmp.clone(), - &IntVarEnc::Const(*card.k), + &IntVarEnc::Const(card.k.into()), ), ) } @@ -48,7 +46,7 @@ mod tests { use super::*; use crate::{ helpers::tests::assert_sol, - linear::LimitComp, + linear::{LimitComp, PosCoeff}, sorted::{SortedEncoder, SortedStrategy}, Cardinality, Encoder, }; @@ -59,9 +57,9 @@ mod tests { $encoder, $n, &Cardinality { - lits: (1..=$n).collect(), + lits: (1..=$n).map(|l| l.into()).collect(), cmp: $cmp, - k: $k.into() + k: PosCoeff::new($k) } ); }; diff --git a/crates/pindakaas/src/cardinality_one.rs b/crates/pindakaas/src/cardinality_one.rs index afe416053..438375f66 100644 --- a/crates/pindakaas/src/cardinality_one.rs +++ b/crates/pindakaas/src/cardinality_one.rs @@ -1,7 +1,7 @@ use crate::{ linear::{LimitComp, Linear}, trace::emit_clause, - Checker, ClauseDatabase, Literal, Result, + CheckError, Checker, ClauseDatabase, Lit, Result, Valuation, }; mod bitwise; @@ -13,12 +13,12 @@ pub use ladder::LadderEncoder; pub use pairwise::PairwiseEncoder; #[derive(Debug, Clone)] -pub struct CardinalityOne { +pub struct CardinalityOne { pub lits: Vec, pub cmp: LimitComp, } -impl CardinalityOne { +impl CardinalityOne { #[cfg(feature = "trace")] pub(crate) fn trace_print(&self) -> String { use crate::trace::trace_print_lit; @@ -33,27 +33,25 @@ impl CardinalityOne { } } -impl Checker for CardinalityOne { - type Lit = Lit; - - fn check(&self, solution: &[Self::Lit]) -> Result<(), crate::CheckError> { - Linear::::from(self.clone()).check(solution) +impl Checker for CardinalityOne { + fn check(&self, value: F) -> Result<(), CheckError> { + Linear::from(self.clone()).check(value) } } pub(crate) fn at_least_one_clause( db: &mut DB, - card1: &CardinalityOne, + card1: &CardinalityOne, ) -> Result { debug_assert_eq!(card1.cmp, LimitComp::Equal); - emit_clause!(db, &card1.lits) + emit_clause!(db, card1.lits.iter().copied()) } #[cfg(test)] pub(crate) mod tests { macro_rules! card1_test_suite { ($encoder:expr) => { - const LARGE_N: i32 = 50; + const LARGE_N: i32 = 50; // ------ At Most One testing ------ #[test] fn test_amo_pair() { @@ -61,10 +59,10 @@ pub(crate) mod tests { $encoder, 2, &CardinalityOne { - lits: vec![1, 2], + lits: lits![1, 2], cmp: LimitComp::LessEq } - => vec![vec![-1, -2], vec![1, -2], vec![-1, 2]] + => vec![lits![-1, -2], lits![1, -2], lits![-1, 2]] ); } #[test] @@ -73,10 +71,10 @@ pub(crate) mod tests { $encoder, 2, &CardinalityOne { - lits: vec![1, -2], + lits: lits![1, -2], cmp: LimitComp::LessEq } - => vec![vec![-1, -2], vec![-1, 2], vec![1, 2]] + => vec![lits![-1, -2], lits![-1, 2], lits![1, 2]] ); } #[test] @@ -85,10 +83,10 @@ pub(crate) mod tests { $encoder, 2, &CardinalityOne { - lits: vec![-1, -2], + lits: lits![-1, -2], cmp: LimitComp::LessEq } - => vec![vec![-1, 2], vec![1, -2], vec![1, 2]] + => vec![lits![-1, 2], lits![1, -2], lits![1, 2]] ); } #[test] @@ -97,10 +95,10 @@ pub(crate) mod tests { $encoder, 3, &CardinalityOne { - lits: vec![1, 2, 3], + lits: lits![1, 2, 3], cmp: LimitComp::LessEq } - => vec![vec![-1, -2, -3], vec![1, -2, -3], vec![-1, 2, -3], vec![-1, -2, 3]] + => vec![lits![-1, -2, -3], lits![1, -2, -3], lits![-1, 2, -3], lits![-1, -2, 3]] ); } #[test] @@ -109,7 +107,7 @@ pub(crate) mod tests { $encoder, LARGE_N, &CardinalityOne { - lits: (1..=LARGE_N).collect::>(), + lits: (1..=LARGE_N).map(|l| l.into()).collect::>(), cmp: LimitComp::LessEq } ); @@ -120,7 +118,7 @@ pub(crate) mod tests { $encoder, LARGE_N, &CardinalityOne { - lits: (-LARGE_N..=-1).collect::>(), + lits: (-LARGE_N..=-1).map(|l| l.into()).collect::>(), cmp: LimitComp::LessEq } ); @@ -131,7 +129,7 @@ pub(crate) mod tests { $encoder, LARGE_N, &CardinalityOne { - lits: (1..=LARGE_N).map(|i| if i % 2 != 0 { -i } else { i }).collect::>(), + lits: (1..=LARGE_N).map(|i| (if i % 2 != 0 { -i } else { i }).into()).collect::>(), cmp: LimitComp::LessEq } ); @@ -143,10 +141,10 @@ pub(crate) mod tests { $encoder, 2, &CardinalityOne { - lits: vec![1, 2], + lits: lits![1, 2], cmp: LimitComp::Equal } - => vec![vec![1, -2], vec![-1, 2]] + => vec![lits![1, -2], lits![-1, 2]] ); } #[test] @@ -155,10 +153,10 @@ pub(crate) mod tests { $encoder, 2, &CardinalityOne { - lits: vec![1, -2], + lits: lits![1, -2], cmp: LimitComp::Equal } - => vec![vec![-1, -2], vec![1, 2]] + => vec![lits![-1, -2], lits![1, 2]] ); } #[test] @@ -167,10 +165,10 @@ pub(crate) mod tests { $encoder, 2, &CardinalityOne { - lits: vec![-1, -2], + lits: lits![-1, -2], cmp: LimitComp::Equal } - => vec![vec![-1, 2], vec![1, -2]] + => vec![lits![-1, 2], lits![1, -2]] ); } #[test] @@ -179,10 +177,10 @@ pub(crate) mod tests { $encoder, 3, &CardinalityOne { - lits: vec![1, 2, 3], + lits: lits![1, 2, 3], cmp: LimitComp::Equal } - => vec![vec![1, -2, -3], vec![-1, 2, -3], vec![-1, -2, 3]] + => vec![lits![1, -2, -3], lits![-1, 2, -3], lits![-1, -2, 3]] ); } #[test] @@ -191,7 +189,7 @@ pub(crate) mod tests { $encoder, LARGE_N, &CardinalityOne { - lits: (1..=LARGE_N).collect::>(), + lits: (1..=LARGE_N).map(|l| l.into()).collect(), cmp: LimitComp::Equal } ); @@ -202,7 +200,7 @@ pub(crate) mod tests { $encoder, LARGE_N, &CardinalityOne { - lits: (-LARGE_N..=-1).collect::>(), + lits: (-LARGE_N..=-1).map(|l| l.into()).collect::>(), cmp: LimitComp::Equal } ); @@ -213,7 +211,7 @@ pub(crate) mod tests { $encoder, LARGE_N, &CardinalityOne { - lits: (1..=LARGE_N).map(|i| if i % 2 != 0 { -i } else { i }).collect::>(), + lits: (1..=LARGE_N).map(|i| (if i % 2 != 0 { -i } else { i }).into()).collect::>(), cmp: LimitComp::Equal } ); diff --git a/crates/pindakaas/src/cardinality_one/bitwise.rs b/crates/pindakaas/src/cardinality_one/bitwise.rs index 1981f100e..2a920b073 100644 --- a/crates/pindakaas/src/cardinality_one/bitwise.rs +++ b/crates/pindakaas/src/cardinality_one/bitwise.rs @@ -1,6 +1,8 @@ +use itertools::Itertools; + use super::at_least_one_clause; use crate::{ - linear::LimitComp, trace::emit_clause, CardinalityOne, ClauseDatabase, Encoder, Literal, Result, + linear::LimitComp, trace::emit_clause, CardinalityOne, ClauseDatabase, Encoder, Result, }; /// An encoder for [`CardinalityOne`] constraints that uses a logarithm @@ -9,12 +11,12 @@ use crate::{ #[derive(Default)] pub struct BitwiseEncoder {} -impl Encoder> for BitwiseEncoder { +impl Encoder for BitwiseEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "bitwise_encoder", skip_all, fields(constraint = card1.trace_print())) )] - fn encode(&mut self, db: &mut DB, card1: &CardinalityOne) -> Result { + fn encode(&mut self, db: &mut DB, card1: &CardinalityOne) -> Result { let size = card1.lits.len(); let bits = (usize::BITS - (size - 1).leading_zeros()) as usize; @@ -24,15 +26,15 @@ impl Encoder> for BitwiseEncoder } // Create a log encoded selection variable - let signals = (0..bits).map(|_| db.new_var()).collect::>(); + let signals = (0..bits).map(|_| db.new_var()).collect_vec(); // Enforce that literal can only be true when selected for (i, lit) in card1.lits.iter().enumerate() { for (j, sig) in signals.iter().enumerate() { if i & (1 << j) != 0 { - emit_clause!(db, &[lit.negate(), sig.clone()])?; + emit_clause!(db, [!lit, *sig])?; } else { - emit_clause!(db, &[lit.negate(), sig.negate()])?; + emit_clause!(db, [!lit, !sig])?; } } } @@ -49,8 +51,9 @@ mod tests { use super::*; use crate::{ cardinality_one::tests::card1_test_suite, - helpers::tests::{assert_enc_sol, assert_sol}, + helpers::tests::{assert_enc_sol, assert_sol, lits}, linear::LimitComp, + Lit, }; card1_test_suite!(BitwiseEncoder::default()); @@ -60,15 +63,15 @@ mod tests { assert_enc_sol!( BitwiseEncoder::default(), 2, - &CardinalityOne { lits: vec![1, 2], cmp: LimitComp::Equal } + &CardinalityOne { lits: lits![1, 2], cmp: LimitComp::Equal } => vec![ - vec![1, 2], - vec![-1, -3], - vec![-2, 3], + lits![1, 2], + lits![-1, -3], + lits![-2, 3], ], vec![ - vec![1, -2], - vec![-1, 2], + lits![1, -2], + lits![-1, 2], ] ); } diff --git a/crates/pindakaas/src/cardinality_one/ladder.rs b/crates/pindakaas/src/cardinality_one/ladder.rs index 99a125eeb..69dd778c2 100644 --- a/crates/pindakaas/src/cardinality_one/ladder.rs +++ b/crates/pindakaas/src/cardinality_one/ladder.rs @@ -1,34 +1,34 @@ use crate::{ - linear::LimitComp, trace::emit_clause, CardinalityOne, ClauseDatabase, Encoder, Literal, Result, + linear::LimitComp, trace::emit_clause, CardinalityOne, ClauseDatabase, Encoder, Result, }; /// An encoder for an At Most One constraints that TODO #[derive(Default)] pub struct LadderEncoder {} -impl Encoder> for LadderEncoder { +impl Encoder for LadderEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "ladder_encoder", skip_all, fields(constraint = card1.trace_print())) )] - fn encode(&mut self, db: &mut DB, card1: &CardinalityOne) -> Result { + fn encode(&mut self, db: &mut DB, card1: &CardinalityOne) -> Result { // TODO could be slightly optimised to not introduce fixed lits let mut a = db.new_var(); // y_v-1 if card1.cmp == LimitComp::Equal { - emit_clause!(db, &[a.clone()])?; + emit_clause!(db, [a])?; } for x in card1.lits.iter() { let b = db.new_var(); // y_v - emit_clause!(db, &[b.negate(), a.clone()])?; // y_v -> y_v-1 + emit_clause!(db, [!b, a])?; // y_v -> y_v-1 // "Channelling" clauses for x_v <-> (y_v-1 /\ ¬y_v) - emit_clause!(db, &[x.negate(), a.clone()])?; // x_v -> y_v-1 - emit_clause!(db, &[x.negate(), b.negate()])?; // x_v -> ¬y_v - emit_clause!(db, &[a.negate(), b.clone(), x.clone()])?; // (y_v-1 /\ ¬y_v) -> x=v + emit_clause!(db, [!x, a])?; // x_v -> y_v-1 + emit_clause!(db, [!x, !b])?; // x_v -> ¬y_v + emit_clause!(db, [!a, b, *x])?; // (y_v-1 /\ ¬y_v) -> x=v a = b; } if card1.cmp == LimitComp::Equal { - emit_clause!(db, &[a.negate()])?; + emit_clause!(db, [!a])?; } Ok(()) } @@ -42,8 +42,9 @@ mod tests { use super::*; use crate::{ cardinality_one::tests::card1_test_suite, - helpers::tests::{assert_enc_sol, assert_sol}, + helpers::tests::{assert_enc_sol, assert_sol, lits}, linear::LimitComp, + Lit, }; card1_test_suite!(LadderEncoder::default()); @@ -53,22 +54,22 @@ mod tests { assert_enc_sol!( LadderEncoder::default(), 2, - &CardinalityOne { lits: vec![1, 2], cmp: LimitComp::Equal } + &CardinalityOne { lits: lits![1, 2], cmp: LimitComp::Equal } => vec![ - vec![-1, 3], - vec![1, -3, 4], - vec![-1, -4], - vec![-2, -5], - vec![-2, 4], - vec![3], - vec![-4, 3], - vec![-4, 5, 2], - vec![-5, 4], - vec![-5], + lits![-1, 3], + lits![1, -3, 4], + lits![-1, -4], + lits![-2, -5], + lits![-2, 4], + lits![3], + lits![-4, 3], + lits![-4, 5, 2], + lits![-5, 4], + lits![-5], ], vec![ - vec![1, -2], - vec![-1, 2], + lits![1, -2], + lits![-1, 2], ] ); } diff --git a/crates/pindakaas/src/cardinality_one/pairwise.rs b/crates/pindakaas/src/cardinality_one/pairwise.rs index a6561b102..729e0509e 100644 --- a/crates/pindakaas/src/cardinality_one/pairwise.rs +++ b/crates/pindakaas/src/cardinality_one/pairwise.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use super::at_least_one_clause; use crate::{ - linear::LimitComp, trace::emit_clause, CardinalityOne, ClauseDatabase, Encoder, Literal, Result, + linear::LimitComp, trace::emit_clause, CardinalityOne, ClauseDatabase, Encoder, Result, }; /// An encoder for an At Most One constraints that for every pair of literals @@ -10,19 +10,19 @@ use crate::{ #[derive(Default)] pub struct PairwiseEncoder {} -impl Encoder> for PairwiseEncoder { +impl Encoder for PairwiseEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "pairwise_encoder", skip_all, fields(constraint = card1.trace_print())) )] - fn encode(&mut self, db: &mut DB, card1: &CardinalityOne) -> Result { + fn encode(&mut self, db: &mut DB, card1: &CardinalityOne) -> Result { // Add clause to ensure "at least one" literal holds if card1.cmp == LimitComp::Equal { at_least_one_clause(db, card1)? } // For every pair of literals (i, j) add "¬i ∨ ¬j" for (a, b) in card1.lits.iter().tuple_combinations() { - emit_clause!(db, &[a.negate(), b.negate()])? + emit_clause!(db, [!a, !b])? } Ok(()) } @@ -36,8 +36,9 @@ mod tests { use super::*; use crate::{ cardinality_one::tests::card1_test_suite, - helpers::tests::{assert_enc_sol, assert_sol}, + helpers::tests::{assert_enc_sol, assert_sol, lits}, linear::LimitComp, + Lit, }; card1_test_suite!(PairwiseEncoder::default()); @@ -48,24 +49,24 @@ mod tests { assert_enc_sol!( PairwiseEncoder::default(), 2, - &CardinalityOne { lits: vec![1, 2], cmp: LimitComp::LessEq } - => vec![vec![-1, -2]], - vec![vec![-1, -2], vec![1, -2], vec![-1, 2]] + &CardinalityOne { lits: lits![1, 2], cmp: LimitComp::LessEq } + => vec![lits![-1, -2]], + vec![lits![-1, -2], lits![1, -2], lits![-1, 2]] ); // AMO on a negated literals assert_enc_sol!( PairwiseEncoder::default(), 2, - &CardinalityOne { lits: vec![-1, 2], cmp: LimitComp::LessEq } - => vec![vec![1, -2]], - vec![vec![1, -2], vec![-1, -2], vec![1, 2]] + &CardinalityOne { lits: lits![-1, 2], cmp: LimitComp::LessEq } + => vec![lits![1, -2]], + vec![lits![1, -2], lits![-1, -2], lits![1, 2]] ); // AMO on three literals assert_enc_sol!( PairwiseEncoder::default(), 3, - &CardinalityOne { lits: vec![1, 2, 3], cmp: LimitComp::LessEq } - => vec![vec![-1, -2], vec![-1, -3], vec![-2, -3]] + &CardinalityOne { lits: lits![1, 2, 3], cmp: LimitComp::LessEq } + => vec![lits![-1, -2], lits![-1, -3], lits![-2, -3]] ); } } diff --git a/crates/pindakaas/src/helpers.rs b/crates/pindakaas/src/helpers.rs index fc068b342..8edb58105 100644 --- a/crates/pindakaas/src/helpers.rs +++ b/crates/pindakaas/src/helpers.rs @@ -1,33 +1,35 @@ +use std::collections::HashSet; + +use itertools::Itertools; + use crate::{ - int::IntVar, linear::PosCoeff, trace::emit_clause, CheckError, Checker, ClauseDatabase, - Coefficient, Encoder, LinExp, Literal, Result, Unsatisfiable, + int::IntVar, linear::PosCoeff, trace::emit_clause, CheckError, Checker, ClauseDatabase, Coeff, + Encoder, LinExp, Lit, Result, Unsatisfiable, Valuation, }; -use itertools::Itertools; -use std::collections::HashSet; /// Given coefficients are powers of two multiplied by some value (1*c, 2*c, 4*c, 8*c, ..) -pub(crate) fn is_powers_of_two(coefs: &[C]) -> bool { - let mult = coefs[0]; - coefs - .iter() - .enumerate() - .all(|(i, c)| c == &(num::pow(C::from(2).unwrap(), i) * mult)) +pub(crate) fn is_powers_of_two>(coefs: I) -> bool { + let mut it = coefs.into_iter().enumerate(); + if let Some((_, mult)) = it.next() { + const TWO: Coeff = 2; + it.all(|(i, c)| c == (TWO.pow(i as u32) * mult)) + } else { + false + } } -pub(crate) fn unsigned_binary_range_ub(bits: usize) -> C { - (0..bits).fold(C::zero(), |a, i| a + (num::pow(C::from(2).unwrap(), i))) +pub(crate) fn unsigned_binary_range_ub(bits: u32) -> Coeff { + const TWO: Coeff = 2; + (0u32..bits).fold(0, |sum, i| sum + TWO.pow(i)) } /// Convert `k` to unsigned binary in `bits` -pub(crate) fn as_binary(k: PosCoeff, bits: Option) -> Vec { - let bits = bits.unwrap_or_else(|| IntVar::required_bits(C::zero(), *k) as usize); +pub(crate) fn as_binary(k: PosCoeff, bits: Option) -> Vec { + let bits = bits.unwrap_or_else(|| IntVar::required_bits(0, *k)); assert!( *k <= unsigned_binary_range_ub(bits), - "{} cannot be represented in {bits} bits", - *k, + "{k} cannot be represented in {bits} bits" ); - (0..bits) - .map(|b| *k & (C::one() << b) != C::zero()) - .collect::>() + (0..bits).map(|b| *k & (1 << b) != 0).collect() } const FILTER_TRIVIAL_CLAUSES: bool = false; @@ -36,7 +38,7 @@ const FILTER_TRIVIAL_CLAUSES: bool = false; /// If any disjunction is empty, this satisfies the whole formula. If any element contains the empty conjunction, that element is falsified in the final clause. pub(crate) fn add_clauses_for( db: &mut DB, - expression: Vec>>, + expression: Vec>>, ) -> Result { // TODO doctor out type of expression (clauses containing conjunctions?) @@ -47,25 +49,25 @@ pub(crate) fn add_clauses_for( { let cls = cls.concat(); // filter out [] (empty conjunctions?) of the clause if FILTER_TRIVIAL_CLAUSES { - let mut lits = HashSet::::with_capacity(cls.len()); + let mut lits = HashSet::::with_capacity(cls.len()); if cls.iter().any(|lit| { - if lits.contains(&lit.negate()) { + if lits.contains(&(!lit)) { true } else { - lits.insert(lit.clone()); + lits.insert(*lit); false } }) { continue; } } - emit_clause!(db, &cls)? + emit_clause!(db, cls)? } Ok(()) } /// Negates CNF (flipping between empty clause and formula) -pub(crate) fn negate_cnf(clauses: Vec>) -> Vec> { +pub(crate) fn negate_cnf(clauses: Vec>) -> Vec> { if clauses.is_empty() { vec![vec![]] } else if clauses.contains(&vec![]) { @@ -74,7 +76,7 @@ pub(crate) fn negate_cnf(clauses: Vec>) -> Vec> assert!(clauses.len() == 1); clauses .into_iter() - .map(|clause| clause.into_iter().map(|lit| lit.negate()).collect()) + .map(|clause| clause.into_iter().map(|lit| !lit).collect()) .collect() } } @@ -85,77 +87,70 @@ pub(crate) fn negate_cnf(clauses: Vec>) -> Vec> #[derive(Default)] pub struct XorEncoder {} -impl<'a, DB: ClauseDatabase> Encoder> for XorEncoder { +impl<'a, DB: ClauseDatabase> Encoder> for XorEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "xor_encoder", skip_all, fields( constraint = itertools::join(xor.lits.iter().map(crate::trace::trace_print_lit), " ⊻ ") )) )] - fn encode(&mut self, db: &mut DB, xor: &XorConstraint) -> Result { - match xor.lits { - [a] => emit_clause!(db, &[a.clone()]), + fn encode(&mut self, db: &mut DB, xor: &XorConstraint) -> Result { + match *xor.lits { + [a] => emit_clause!(db, [a]), [a, b] => { - emit_clause!(db, &[a.clone(), b.clone()])?; - emit_clause!(db, &[a.negate(), b.negate()]) + emit_clause!(db, [a, b])?; + emit_clause!(db, [!a, !b]) } [a, b, c] => { - emit_clause!(db, &[a.clone(), b.clone(), c.clone()])?; - emit_clause!(db, &[a.clone(), b.negate(), c.negate()])?; - emit_clause!(db, &[a.negate(), b.clone(), c.negate()])?; - emit_clause!(db, &[a.negate(), b.negate(), c.clone()]) + emit_clause!(db, [a, b, c])?; + emit_clause!(db, [a, !b, !c])?; + emit_clause!(db, [!a, b, !c])?; + emit_clause!(db, [!a, !b, c]) } _ => panic!("Unexpected usage of XOR with zero or more than three arguments"), } } } -pub struct XorConstraint<'a, Lit: Literal> { +pub struct XorConstraint<'a> { pub(crate) lits: &'a [Lit], } -impl<'a, Lit: Literal> XorConstraint<'a, Lit> { +impl<'a> XorConstraint<'a> { pub fn new(lits: &'a [Lit]) -> Self { Self { lits } } } -impl<'a, Lit: Literal> Checker for XorConstraint<'a, Lit> { - type Lit = Lit; - fn check(&self, solution: &[Self::Lit]) -> Result<(), CheckError> { - let count = LinExp::from_terms( - self.lits - .iter() - .map(|l| (l.clone(), 1)) - .collect::>() - .as_slice(), - ) - .assign(solution)?; +impl<'a> Checker for XorConstraint<'a> { + fn check(&self, value: F) -> Result<(), CheckError> { + let count = LinExp::from_terms(self.lits.iter().map(|&l| (l, 1)).collect_vec().as_slice()) + .value(value)?; if count % 2 == 1 { Ok(()) } else { - Err(CheckError::Unsatisfiable(Unsatisfiable)) + Err(Unsatisfiable.into()) } } } #[cfg(test)] pub mod tests { - type Lit = i32; // TODO replace all i32s for Lit + use std::{ + collections::{HashMap, HashSet}, + num::NonZeroI32, + thread::panicking, + }; use splr::{ types::{CNFDescription, Instantiate}, Certificate, Config, SatSolverIF, SolveIF, Solver, SolverError, }; - use std::{ - collections::{HashMap, HashSet}, - thread::panicking, - }; #[cfg(feature = "trace")] use traced_test::test; use super::*; - use crate::{linear::LimitComp, CardinalityOne, Encoder, LadderEncoder, Unsatisfiable}; + use crate::{linear::LimitComp, CardinalityOne, Encoder, LadderEncoder, Unsatisfiable, Var}; macro_rules! assert_enc { ($enc:expr, $max:expr, $arg:expr => $clauses:expr) => { @@ -268,6 +263,18 @@ pub mod tests { } pub(crate) use assert_trivial_unsat; + macro_rules! lits { + () => ( + std::vec::Vec::new() + ); + ($($x:expr),+ $(,)?) => ( + <[Lit]>::into_vec( + std::boxed::Box::new([$($crate::Lit::from($x)),+]) + ) + ); + } + pub(crate) use lits; + #[test] fn test_assert_macros() { #[derive(Default)] @@ -277,38 +284,38 @@ pub mod tests { feature = "trace", tracing::instrument(name = "negate_encoder", skip_all) )] - fn encode<'a, DB: ClauseDatabase>(&mut self, db: &mut DB, lit: &DB::Lit) -> Result { - emit_clause!(db, &[lit.negate()]) + fn encode<'a, DB: ClauseDatabase>(&mut self, db: &mut DB, lit: Lit) -> Result { + emit_clause!(db, [!lit]) } } // Test resulting encoding - assert_enc!(Negate::default(), 1, &1 => vec![vec![-1]]); + assert_enc!(Negate::default(), 1, 1.into() => vec![lits![-1]]); // Test possible solutions (using specification) - assert_sol!(Negate::default(), 1, &1 => vec![vec![-1]]); + assert_sol!(Negate::default(), 1, 1.into() => vec![lits![-1]]); // Test encoding and possible solutions - assert_enc_sol!(Negate::default(), 1, &1 => vec![vec![-1]], vec![vec![-1]]); + assert_enc_sol!(Negate::default(), 1, 1.into() => vec![lits![-1]], vec![lits![-1]]); // Test resulting encoding for given TestDB instance let mut tdb = TestDB::new(2); - tdb.add_clause(&[2]).unwrap(); - assert_enc!(tdb => Negate::default(), &1 => vec![vec![-1]]); // only clauses of encoder are checked against + tdb.add_clause(lits![2]).unwrap(); + assert_enc!(tdb => Negate::default(), 1.into() => vec![lits![-1]]); // only clauses of encoder are checked against let mut tdb = TestDB::new(2); - tdb.add_clause(&[2]).unwrap(); - assert_sol!(tdb => Negate::default(), &1 => vec![vec![-1,2]]); + tdb.add_clause(lits![2]).unwrap(); + assert_sol!(tdb => Negate::default(), 1.into() => vec![lits![-1,2]]); let mut tdb = TestDB::new(2); - tdb.add_clause(&[2]).unwrap(); - assert_enc_sol!(tdb => Negate::default(), &1 => vec![vec![-1]], vec![vec![-1,2]]); + tdb.add_clause(lits![2]).unwrap(); + assert_enc_sol!(tdb => Negate::default(), 1.into() => vec![lits![-1]], vec![lits![-1,2]]); } #[test] fn test_assert_macros_with_check() { let mut tdb = TestDB::new(3); - tdb.add_clause(&[1]).unwrap(); + tdb.add_clause(lits![1]).unwrap(); assert_sol!(tdb => LadderEncoder::default(), &CardinalityOne { - lits: vec![2, 3], + lits: lits![2, 3], cmp: LimitComp::LessEq, }); } @@ -318,9 +325,9 @@ pub mod tests { assert_enc_sol!( XorEncoder::default(), 2, - &XorConstraint::new(&[1,2]) => - vec![vec![1, 2], vec![-1, -2]], - vec![vec![-1, 2], vec![1, -2]] + &XorConstraint::new(&lits![1,2]) => + vec![lits![1, 2], lits![-1, -2]], + vec![lits![-1, 2], lits![1, -2]] ); } @@ -330,14 +337,27 @@ pub mod tests { tdb = tdb.expect_vars(2); tdb = tdb.expect_cls(3); tdb = tdb.expect_lits(5); - tdb.add_clause(&[1, 2]).unwrap(); + tdb.add_clause(lits![1, 2]).unwrap(); tdb.new_var(); - tdb.add_clause(&[-3, -4]).unwrap(); + tdb.add_clause(lits![-3, -4]).unwrap(); tdb.new_var(); - tdb.add_clause(&[5]).unwrap(); + tdb.add_clause(lits![5]).unwrap(); tdb.check_complete(); } + pub(crate) fn make_valuation + Copy>(g: &[L]) -> impl Valuation + '_ { + |l: Lit| { + let abs: Lit = l.var().into(); + let v = Into::::into(abs) as usize; + if v <= g.len() { + debug_assert_eq!(g[v - 1].into().var(), l.var()); + Some(g[v - 1].into() == l) + } else { + None + } + } + } + const OUTPUT_SPLR: bool = false; /// The maximum number of variable to generate expected solutions for const GENERATE_EXPECTED_SOLUTIONS: i32 = 0; @@ -347,10 +367,10 @@ pub mod tests { /// Number of variables available when solver is created pub(crate) num_var: i32, /// Clauses expected by the test case - clauses: Option)>>, + clauses: Option)>>, /// Solutions expected by the test case - solutions: Option>>, - check: Option bool>, + solutions: Option>>, + check: Option bool>, unchecked: bool, expected_vars: Option, expected_cls: Option, @@ -385,9 +405,9 @@ pub mod tests { } } - pub fn expect_clauses(mut self, mut clauses: Vec>) -> TestDB { + pub fn expect_clauses(mut self, mut clauses: Vec>) -> TestDB { for cl in &mut clauses { - cl.sort_by_key(|a| a.abs()); + cl.sort(); } clauses.sort(); self.clauses = Some(clauses.into_iter().map(|cl| (false, cl)).collect()); @@ -410,9 +430,9 @@ pub mod tests { self } - pub fn expect_solutions(mut self, mut solutions: Vec>) -> TestDB { + pub fn expect_solutions(mut self, mut solutions: Vec>) -> TestDB { for sol in &mut solutions { - sol.sort_by_key(|a| a.abs()); + sol.sort(); } solutions.sort(); if let Some(self_solutions) = &self.solutions { @@ -424,7 +444,11 @@ pub mod tests { } #[allow(dead_code)] - pub fn generate_solutions(&self, check: impl Fn(&[i32]) -> bool, n: i32) -> Vec> { + pub fn generate_solutions( + &self, + check: impl Fn(&dyn Valuation) -> bool, + n: i32, + ) -> Vec> { if n > 32 { unimplemented!( "Cannot generate solutions using binary shifts with more than 32 variables." @@ -434,34 +458,29 @@ pub mod tests { (0..((2_i32).pow(n as u32))) .map(|i| { (0..n) - .map(|j| if ((i >> j) & 1) == 1 { j + 1 } else { -(j + 1) }) - .collect::>() + .map(|j| if ((i >> j) & 1) == 1 { j + 1 } else { -(j + 1) }.into()) + .collect_vec() }) - .filter(|g| check(&g[..])) + .filter(|g| check(&make_valuation(g))) .collect() } - pub fn _print_solutions(sols: &Vec>) -> String { + pub fn _print_solutions(sols: &Vec>) -> String { format!( - "vec![ -{} -]", + "vec![\n{}\n]", sols.iter() .map(|sol| format!( "\tvec![{}]", (*sol) .iter() - .map(|lit| lit.to_string()) - .collect::>() + .map(|&lit| Into::::into(lit).to_string()) .join(", ") )) - .collect::>() .join(",\n") ) - .to_string() } - pub fn with_check(mut self, checker: fn(&[i32]) -> bool) -> TestDB { + pub fn with_check(mut self, checker: fn(&dyn Valuation) -> bool) -> TestDB { if self.solutions.is_none() && self.num_var <= GENERATE_EXPECTED_SOLUTIONS { let solutions = self.generate_solutions(checker, self.num_var); self.expect_solutions(solutions) @@ -475,12 +494,10 @@ pub mod tests { pub fn check_complete(&mut self) { self.unchecked = false; if let Some(clauses) = &self.clauses { - let missing: Vec> = clauses + let missing: Vec> = clauses .iter() - .filter(|exp| !exp.0) - .map(|exp| exp.1.clone()) + .filter_map(|(found, cl)| if *found { None } else { Some(cl.clone()) }) .collect(); - // assert!(false, "{:?} {:?}", clauses, missing); assert!( missing.is_empty(), "clauses are missing from the encoding: {:?}", @@ -499,21 +516,21 @@ pub mod tests { eprintln!("let result: Vec> = slv.iter().collect();"); } const ONLY_OUTPUT: bool = true; - let mut from_slv: Vec> = Vec::new(); + let mut from_slv: Vec> = Vec::new(); while let Ok(Certificate::SAT(model)) = self.slv.solve() { - let solution = if ONLY_OUTPUT { + let solution: Vec = if ONLY_OUTPUT { model - .clone() - .into_iter() + .iter() .filter(|l| l.abs() <= self.num_var) + .map(|&l| l.into()) .collect() } else { - model + model.iter().map(|&l| l.into()).collect() }; from_slv.push(solution.clone()); - let nogood: Vec = solution.iter().map(|l| -l).collect(); + let nogood: Vec = solution.iter().map(|l| (!l).into()).collect(); match SatSolverIF::add_clause(&mut self.slv, nogood) { Err(SolverError::Inconsistent | SolverError::EmptyClause) => { break; @@ -525,11 +542,15 @@ pub mod tests { } } for sol in &mut from_slv { - sol.sort_by_key(|a| a.abs()); + sol.sort(); } if let Some(check) = &self.check { for sol in &mut from_slv { - assert!(check(sol), "solution {:?} failed check", sol) + assert!( + check(&make_valuation(sol)), + "solution {:?} failed check", + sol + ) } } if let Some(solutions) = &self.solutions { @@ -543,9 +564,9 @@ pub mod tests { .iter() .map(|sol| { sol.iter() - .filter(|l| l.abs() <= self.num_var) + .filter(|l| Into::::into(l.var()) <= self.num_var) .cloned() - .collect::>() + .collect_vec() }) .collect() }; @@ -553,7 +574,7 @@ pub mod tests { let misses = solutions .iter() .filter(|s| !from_slv_output.contains(s)) - .collect::>(); + .collect_vec(); if !misses.is_empty() { println!("Missing solutions ({})", misses.len()); @@ -566,7 +587,7 @@ pub mod tests { .iter() .zip(from_slv_output) .filter_map(|(sol, out)| (!solutions.contains(&out)).then_some(sol)) - .collect::>(); + .collect_vec(); if !extras.is_empty() { println!("Extra solutions ({})", extras.len()); @@ -575,16 +596,15 @@ pub mod tests { } } - let vars = HashSet::::from_iter( - solutions - .iter() - .flat_map(|sol| sol.iter().map(|lit| lit.abs())), - ); + let vars: HashSet = solutions + .iter() + .flat_map(|sol| sol.iter().map(|lit| lit.var())) + .collect(); - let mut from_slv: Vec> = HashSet::>::from_iter( + let mut from_slv: Vec> = HashSet::>::from_iter( from_slv .into_iter() - .map(|xs| xs.into_iter().filter(|x| vars.contains(&x.abs())).collect()), + .map(|xs| xs.into_iter().filter(|x| vars.contains(&x.var())).collect()), ) .into_iter() .collect(); @@ -616,17 +636,14 @@ pub mod tests { } impl ClauseDatabase for TestDB { - type Lit = i32; + fn add_clause>(&mut self, cl: I) -> Result { + let cl = cl.into_iter().sorted().collect_vec(); - fn add_clause(&mut self, cl: &[Self::Lit]) -> Result { - let mut cl = Vec::from(cl); - - cl.sort_by_key(|a| a.abs()); if let Some(clauses) = &mut self.clauses { let mut found = false; - for exp in clauses { - if cl == exp.1 { - exp.0 = true; + for (f, x) in clauses { + if &cl == x { + *f = true; found = true; break; } @@ -636,7 +653,7 @@ pub mod tests { if self.expecting_no_unit_clauses { assert!( - cl.len() > 1 || cl[0] <= self.num_var, + cl.len() > 1 || Into::::into(cl[0]) <= self.num_var, "Unexpected unit clause on aux var {:?}", cl ); @@ -644,40 +661,36 @@ pub mod tests { if let Some(equivalences) = &mut self.expecting_no_equivalences { let mut cl = cl.clone(); - cl.sort_by_key(|l| l.abs()); + cl.sort(); if match cl[..] { [a, b] => { + let (a, b): (Lit, Lit) = (a.var().into(), b.var().into()); if !a.is_negated() && !b.is_negated() { - let (a, b) = (a.var(), b.var()); // a \/ b = ~a -> b - equivalences.insert(a.negate(), b); + equivalences.insert(!a, b); // do we have b -> ~a = ~b \/ ~a = a -> ~b? - equivalences.get(&a) == Some(&b.negate()) + equivalences.get(&a) == Some(&!b) } else if a.is_negated() && !b.is_negated() { - let (a, b) = (a.var(), b.var()); // ~a \/ b = a -> b equivalences.insert(a, b); // do we have b -> a = ~b \/ a = ~a -> ~b? - equivalences.get(&a.negate()) == Some(&b.negate()) + equivalences.get(&!a) == Some(&!b) } else if !a.is_negated() && b.is_negated() { - let (a, b) = (a.var(), b.var()); // a \/ ~b = ~a -> ~b - equivalences.insert(a.negate(), b.negate()); + equivalences.insert(!a, !b); // do we have ~b -> ~a = b \/ ~a = a -> b? equivalences.get(&a) == Some(&b) } else if a.is_negated() && b.is_negated() { - let (a, b) = (a.var(), b.var()); // ~a \/ ~b = a -> ~b - equivalences.insert(a.negate(), b.negate()); + equivalences.insert(!a, !b); // do we have ~b -> a = b \/ a = ~a -> b? - equivalences.get(&a.negate()) == Some(&b) + equivalences.get(&!a) == Some(&b) } else { unreachable!("{:?}", cl); } } _ => false, } { - //panic!("Unexpected equivalence by adding {cl:?}"); println!("Unexpected equivalence by adding {cl:?}"); } } @@ -695,10 +708,11 @@ pub mod tests { let list: Vec = cl .iter() .map(|l| { - if l.abs() <= self.num_var { + let v: i32 = l.var().into(); + if v <= self.num_var { l.to_string() } else { - format!("{}x{}", if *l < 0 { "-" } else { "" }, l.abs()) + format!("{}x{}", if l.is_negated() { "-" } else { "" }, v) } }) .collect(); @@ -719,8 +733,11 @@ pub mod tests { match match cl.len() { 0 => return Err(Unsatisfiable), - 1 => self.slv.add_assignment(cl[0]), - _ => SatSolverIF::add_clause(&mut self.slv, cl), + 1 => self.slv.add_assignment(cl[0].into()), + _ => SatSolverIF::add_clause( + &mut self.slv, + cl.iter().map(|&l| l.into()).collect_vec(), + ), } { Ok(_) => Ok(()), Err(err) => match err { @@ -733,7 +750,7 @@ pub mod tests { } } - fn new_var(&mut self) -> Self::Lit { + fn new_var(&mut self) -> Lit { let res = self.slv.add_var() as i32; if let Some(num) = &mut self.expected_vars { @@ -744,7 +761,7 @@ pub mod tests { if OUTPUT_SPLR { eprintln!("let x{} = slv.add_var() as i32;", res); } - res + Lit(NonZeroI32::new(res).unwrap()) } } } diff --git a/crates/pindakaas/src/int.rs b/crates/pindakaas/src/int.rs index 6a93419cc..7cbfb541a 100644 --- a/crates/pindakaas/src/int.rs +++ b/crates/pindakaas/src/int.rs @@ -2,62 +2,69 @@ mod constrain; mod enc; mod model; -use itertools::Itertools; use std::collections::BTreeSet; pub(crate) use constrain::{TernLeConstraint, TernLeEncoder}; pub(crate) use enc::{IntVarBin, IntVarEnc, IntVarOrd, LitOrConst}; +use itertools::Itertools; pub(crate) use model::{Consistency, IntVar, Lin, Model}; -use crate::{linear::Constraint, CheckError, Coefficient, LinExp, Literal, Unsatisfiable}; - use self::enc::GROUND_BINARY_AT_LB; +use crate::{linear::Constraint, CheckError, Coeff, LinExp, Result, Unsatisfiable, Valuation}; -impl LinExp { - pub(crate) fn assign(&self, solution: &[Lit]) -> Result> { - let evaluate = |assignments: &Vec<(Lit, C, Lit)>| { - assignments +impl LinExp { + pub(crate) fn value(&self, value: F) -> Result { + let mut total = self.add; + for (constraint, terms) in self.iter() { + // Calculate sum for constraint + let sum = terms .iter() - .fold(C::zero(), |acc, (lit, coef, assignment)| { - acc + if lit == assignment { *coef } else { C::zero() } - }) - }; - self.iter().try_fold(self.add, |acc, (constraint,terms) | { - let assignments: Vec<(Lit,C,Lit)> = terms.into_iter().map(|(lit,coef)| { - let assignment = solution.iter() - .find(|x| x.var() == lit.var()) - .unwrap_or_else(|| panic!("Could not find lit {lit:?} in solution {solution:?}; perhaps this variable did not occur in any clause")); - (lit.clone(), *coef,assignment.clone()) - }).collect(); - - let is_consistent = match constraint { - Some(Constraint::AtMostOne) => assignments.iter().filter(|(lit,_,a)| lit == a).count() <= 1, - Some(Constraint::ImplicationChain) => assignments.iter().map(|(lit,_,a)| lit == a).tuple_windows().all(|(a, b)| a.cmp(&b).is_ge()), + .filter(|(lit, _)| value(*lit).expect("missing assignment to literal")) + .map(|(_, i)| i) + .sum(); + match constraint { + Some(Constraint::AtMostOne) => { + if sum != 0 + && terms + .iter() + .filter(|&(l, _)| value(*l).unwrap_or(true)) + .count() > 1 + { + return Err(Unsatisfiable.into()); + } + } + Some(Constraint::ImplicationChain) => { + if terms + .iter() + .map(|(l, _)| *l) + .tuple_windows() + .any(|(a, b)| !value(a).unwrap_or(false) & value(b).unwrap_or(true)) + { + return Err(Unsatisfiable.into()); + } + } Some(Constraint::Domain { lb, ub }) => { // divide by first coeff to get int assignment - let a = evaluate(&assignments).div(assignments[0].1); if GROUND_BINARY_AT_LB { - a <= ub - lb - } else { - lb <= a && a <= ub + if sum > ub - lb { + return Err(Unsatisfiable.into()); + } + } else if lb > sum || sum > ub { + return Err(Unsatisfiable.into()); } - }, - None => true + } + None => {} }; - - if is_consistent { - Ok(acc+evaluate(&assignments) * self.mult) - } else { - Err(CheckError::Unsatisfiable(Unsatisfiable)) - } - }) + total += sum; + } + Ok(total * self.mult) } } -pub(crate) fn display_dom(dom: &BTreeSet) -> String { +pub(crate) fn display_dom(dom: &BTreeSet) -> String { const ELIPSIZE: usize = 8; let (lb, ub) = (*dom.first().unwrap(), *dom.last().unwrap()); - if dom.len() > ELIPSIZE && C::from(dom.len()).unwrap() == ub - lb + C::one() { + if dom.len() > ELIPSIZE && dom.len() == (ub - lb + 1) as usize { format!("{}..{}", dom.first().unwrap(), dom.last().unwrap()) } else if dom.len() > ELIPSIZE { format!( diff --git a/crates/pindakaas/src/int/constrain.rs b/crates/pindakaas/src/int/constrain.rs index 04d1a1ab1..f082e3413 100644 --- a/crates/pindakaas/src/int/constrain.rs +++ b/crates/pindakaas/src/int/constrain.rs @@ -1,33 +1,27 @@ +use std::fmt; + use itertools::Itertools; -use crate::helpers::{add_clauses_for, negate_cnf}; +use super::{enc::GROUND_BINARY_AT_LB, IntVarBin, IntVarEnc, LitOrConst}; use crate::{ - helpers::as_binary, - linear::{lex_geq_const, lex_leq_const, log_enc_add, log_enc_add_, LimitComp, LinExp}, + helpers::{add_clauses_for, as_binary, negate_cnf}, + linear::{ + lex_geq_const, lex_leq_const, log_enc_add, log_enc_add_, LimitComp, LinExp, PosCoeff, + }, trace::emit_clause, - Coefficient, Literal, + CheckError, Checker, ClauseDatabase, Coeff, Encoder, Unsatisfiable, Valuation, }; -use crate::{CheckError, Checker, ClauseDatabase, Encoder, Unsatisfiable}; -use std::fmt; - -use super::enc::GROUND_BINARY_AT_LB; -use super::{IntVarBin, IntVarEnc, LitOrConst}; #[derive(Debug)] -pub(crate) struct TernLeConstraint<'a, Lit: Literal, C: Coefficient> { - pub(crate) x: &'a IntVarEnc, - pub(crate) y: &'a IntVarEnc, +pub(crate) struct TernLeConstraint<'a> { + pub(crate) x: &'a IntVarEnc, + pub(crate) y: &'a IntVarEnc, pub(crate) cmp: LimitComp, - pub(crate) z: &'a IntVarEnc, + pub(crate) z: &'a IntVarEnc, } -impl<'a, Lit: Literal, C: Coefficient> TernLeConstraint<'a, Lit, C> { - pub fn new( - x: &'a IntVarEnc, - y: &'a IntVarEnc, - cmp: LimitComp, - z: &'a IntVarEnc, - ) -> Self { +impl<'a> TernLeConstraint<'a> { + pub fn new(x: &'a IntVarEnc, y: &'a IntVarEnc, cmp: LimitComp, z: &'a IntVarEnc) -> Self { Self { x, y, cmp, z } } @@ -47,7 +41,7 @@ impl<'a, Lit: Literal, C: Coefficient> TernLeConstraint<'a, Lit, C> { Ok(false) } - fn check(x: C, y: C, cmp: &LimitComp, z: C) -> bool { + fn check(x: Coeff, y: Coeff, cmp: &LimitComp, z: Coeff) -> bool { match cmp { LimitComp::LessEq => x + y <= z, LimitComp::Equal => x + y == z, @@ -55,12 +49,11 @@ impl<'a, Lit: Literal, C: Coefficient> TernLeConstraint<'a, Lit, C> { } } -impl<'a, Lit: Literal, C: Coefficient> Checker for TernLeConstraint<'a, Lit, C> { - type Lit = Lit; - fn check(&self, solution: &[Self::Lit]) -> Result<(), CheckError> { - let x = LinExp::from(self.x).assign(solution)?; - let y = LinExp::from(self.y).assign(solution)?; - let z = LinExp::from(self.z).assign(solution)?; +impl<'a> Checker for TernLeConstraint<'a> { + fn check(&self, value: F) -> Result<(), CheckError> { + let x = LinExp::from(self.x).value(&value)?; + let y = LinExp::from(self.y).value(&value)?; + let z = LinExp::from(self.z).value(&value)?; if Self::check(x, y, &self.cmp, z) { Ok(()) } else { @@ -71,73 +64,33 @@ impl<'a, Lit: Literal, C: Coefficient> Checker for TernLeConstraint<'a, Lit, C> } } -impl fmt::Display for TernLeConstraint<'_, Lit, C> { +impl fmt::Display for TernLeConstraint<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} + {} {} {}", self.x, self.y, self.cmp, self.z) } } -#[allow(dead_code)] // TODO -pub(crate) struct TernLeConstraintContainer { - pub(crate) x: IntVarEnc, - pub(crate) y: IntVarEnc, - pub(crate) cmp: LimitComp, - pub(crate) z: IntVarEnc, -} - -impl<'a, Lit: Literal, C: Coefficient> TernLeConstraintContainer { - #[allow(dead_code)] - pub(crate) fn get(&'a self) -> TernLeConstraint<'a, Lit, C> { - TernLeConstraint { - x: &self.x, - y: &self.y, - cmp: self.cmp.clone(), - z: &self.z, - } - } -} - #[derive(Debug, Default)] pub(crate) struct TernLeEncoder {} const ENCODE_REDUNDANT_X_O_Y_O_Z_B: bool = true; -impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder> - for TernLeEncoder -{ +impl<'a, DB: ClauseDatabase> Encoder> for TernLeEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "tern_le_encoder", skip_all, fields(constraint = format!("{} + {} {} {}", tern.x, tern.y, tern.cmp, tern.z))) )] - fn encode(&mut self, db: &mut DB, tern: &TernLeConstraint) -> crate::Result { + fn encode(&mut self, db: &mut DB, tern: &TernLeConstraint) -> crate::Result { #[cfg(debug_assertions)] { const PRINT_TESTCASES: bool = false; if PRINT_TESTCASES { println!(" // {tern}"); - let x = tern - .x - .dom() - .iter(..) - .map(|iv| iv.end - C::one()) - .collect::>(); - let y = tern - .y - .dom() - .iter(..) - .map(|iv| iv.end - C::one()) - .collect::>(); - let z = tern - .z - .dom() - .iter(..) - .map(|iv| iv.end - C::one()) - .collect::>(); + let x = tern.x.dom().iter(..).map(|iv| iv.end - 1).collect_vec(); + let y = tern.y.dom().iter(..).map(|iv| iv.end - 1).collect_vec(); + let z = tern.z.dom().iter(..).map(|iv| iv.end - 1).collect_vec(); println!( - "mod _test_{}_{}_{} {{ - test_int_lin!($encoder, &[{}], &[{}], $cmp, &[{}]); - }} - ", + "mod _test_{}_{}_{} {{\n\ttest_int_lin!($encoder, &[{}], &[{}], $cmp, &[{}]);\n}}\n", x.iter().join(""), y.iter().join(""), z.iter().join(""), @@ -152,7 +105,7 @@ impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder { - if tern.check(&[]).is_ok() { + if tern.check(|_| None).is_ok() { Ok(()) } else { Err(Unsatisfiable) @@ -164,25 +117,19 @@ impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder lex_geq_const( db, - z_bin - .xs - .iter() - .map(|x| Some(x.clone())) - .collect::>() - .as_slice(), - if GROUND_BINARY_AT_LB { + z_bin.xs.iter().map(|x| Some(*x)).collect_vec().as_slice(), + PosCoeff::new(if GROUND_BINARY_AT_LB { lhs - z_bin.lb() } else { lhs - } - .into(), + }), z_bin.lits(), ), LimitComp::Equal => self.encode( db, &TernLeConstraint { x: z, - y: &IntVarEnc::Const(C::zero()), + y: &IntVarEnc::Const(0), cmp: cmp.clone(), z: &IntVarEnc::Const(lhs), }, @@ -197,30 +144,22 @@ impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder lex_leq_const( db, - x_bin - .xs - .iter() - .map(|x| Some(x.clone())) - .collect::>() - .as_slice(), + x_bin.xs.iter().map(|x| Some(*x)).collect_vec().as_slice(), rhs, x_bin.lits(), ), - LimitComp::Equal => as_binary(rhs, Some(x_bin.lits())) + LimitComp::Equal => as_binary(rhs, Some(x_bin.lits() as u32)) .into_iter() - .zip(x_bin.xs.iter()) - .try_for_each(|(b, x)| { - emit_clause!(db, &[if b { x.clone() } else { x.negate() }]) - }), + .zip(x_bin.xs.iter().copied()) + .try_for_each(|(b, x)| emit_clause!(db, [if b { x } else { !x }])), } } (IntVarEnc::Bin(x_bin), IntVarEnc::Const(y_const), IntVarEnc::Bin(z_bin)) @@ -234,23 +173,13 @@ impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder>(), - &as_binary((*y_const).into(), Some(x_bin.lits())) + &x_bin.xs.iter().cloned().map(LitOrConst::from).collect_vec(), + &as_binary(PosCoeff::new(*y_const), Some(x_bin.lits() as u32)) .into_iter() .map(LitOrConst::Const) - .collect::>(), + .collect_vec(), cmp, - &z_bin - .xs - .iter() - .cloned() - .map(LitOrConst::from) - .collect::>(), + &z_bin.xs.iter().cloned().map(LitOrConst::from).collect_vec(), ) } (IntVarEnc::Bin(x_bin), IntVarEnc::Bin(y_bin), IntVarEnc::Bin(z_bin)) => { @@ -262,12 +191,7 @@ impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder Encoder Encoder Encoder { - let z = z.add( - db, - self, - &IntVarEnc::Const(c.neg()), - Some(z.lb()), - Some(z.ub()), - )?; + let z = z.add(db, self, &IntVarEnc::Const(-c), Some(z.lb()), Some(z.ub()))?; // x + c <= z == z-c >= x == /\ (z'<=a -> x<=a) for (c_a, z_leq_c_a) in z.leqs() { // TODO alt; just propagate by adding lex constraint let c_a = if z_leq_c_a.is_empty() { - c_a.start..(x.ub() + C::one()) + c_a.start..(x.ub() + 1) } else { c_a }; @@ -365,7 +283,7 @@ impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder Encoder Encoder { - use super::*; #[cfg(feature = "trace")] use traced_test::test; + use super::*; + #[test] fn o_o_o() { test_int_lin_encs!( @@ -555,13 +474,7 @@ pub mod tests { z: &z, }; - let sols = - db.generate_solutions( - |sol| { - tern.check(sol).is_ok() - }, - db.num_var, - ); + let sols = db.generate_solutions(|sol| tern.check(sol).is_ok(), db.num_var); x.consistent(&mut db).unwrap(); y.consistent(&mut db).unwrap(); @@ -626,12 +539,12 @@ pub mod tests { int_lin_test_suite!(TernLeEncoder::default(), LimitComp::Equal); } - fn get_ord_x( + fn get_ord_x( db: &mut DB, - dom: IntervalSet, + dom: IntervalSet, consistent: bool, lbl: String, - ) -> IntVarEnc { + ) -> IntVarEnc { let x = IntVarOrd::from_syms(db, dom, lbl); if consistent { x.consistent(db).unwrap(); @@ -639,13 +552,13 @@ pub mod tests { IntVarEnc::Ord(x) } - fn get_bin_x( + fn get_bin_x( db: &mut DB, - lb: C, - ub: C, + lb: Coeff, + ub: Coeff, consistent: bool, lbl: String, - ) -> IntVarEnc { + ) -> IntVarEnc { let x = IntVarBin::from_bounds(db, lb, ub, lbl); if consistent { x.consistent(db).unwrap(); @@ -655,10 +568,10 @@ pub mod tests { #[test] fn constant_test() { - let c: IntVarEnc = IntVarEnc::Const(42); + let c: IntVarEnc = IntVarEnc::Const(42); assert_eq!(c.lb(), 42); assert_eq!(c.ub(), 42); - assert_eq!(c.geq(6..7), Vec::>::new()); + assert_eq!(c.geq(6..7), Vec::>::new()); assert_eq!(c.geq(45..46), vec![vec![]]); } @@ -666,8 +579,8 @@ pub mod tests { // #[test] fn _bin_1_test() { let mut db = TestDB::new(0); - let x = get_bin_x::<_, i32>(&mut db, 2, 12, true, "x".to_string()); - let x_lin: LinExp = LinExp::from(&x); + let x = get_bin_x(&mut db, 2, 12, true, "x".to_string()); + let x_lin = LinExp::from(&x); assert_eq!(x.lits(), 4); assert_eq!(x.lb(), 2); @@ -678,38 +591,44 @@ pub mod tests { assert_eq!(IntVar::required_bits(3, 10), 3); // 8 vals => 3 bits // geq looks zeroes of at (start+1..) end-2 - lb - assert_eq!(x.geq(3..4), vec![vec![1, 2, 3, 4]]); // 4-2 - 2 = 4 == 0000 (right-to-left!) - assert_eq!(x.geq(7..8), vec![vec![1, 2, 4]]); // 8-2 - 2 = 4 == 0100 - assert_eq!(x.geq(7..9), vec![vec![1, 2, 4], vec![2, 4]]); // and 9-2 -2 = 5 = 0101 - assert_eq!(x.geq(5..6), vec![vec![1, 3, 4]]); // 6-2 - 2 = 2 == 0010 - assert_eq!(x.geq(6..7), vec![vec![3, 4]]); // 7-2 - 2 = 3 == 0011 + assert_eq!(x.geq(3..4), vec![lits![1, 2, 3, 4]]); // 4-2 - 2 = 4 == 0000 (right-to-left!) + assert_eq!(x.geq(7..8), vec![lits![1, 2, 4]]); // 8-2 - 2 = 4 == 0100 + assert_eq!(x.geq(7..9), vec![lits![1, 2, 4], lits![2, 4]]); // and 9-2 -2 = 5 = 0101 + assert_eq!(x.geq(5..6), vec![lits![1, 3, 4]]); // 6-2 - 2 = 2 == 0010 + assert_eq!(x.geq(6..7), vec![lits![3, 4]]); // 7-2 - 2 = 3 == 0011 // leq looks at ones at start+1 - lb? - assert_eq!(x.leq(6..7), vec![vec![-1, -3]]); // 6+1 - 2 = 5 == 0101 - assert_eq!(x.leq(6..8), vec![vec![-1, -3], vec![-2, -3]]); // and 7+1 - 2 = 6 == 0110 + assert_eq!(x.leq(6..7), vec![lits![-1, -3]]); // 6+1 - 2 = 5 == 0101 + assert_eq!(x.leq(6..8), vec![lits![-1, -3], lits![-2, -3]]); // and 7+1 - 2 = 6 == 0110 assert_eq!( x.leq(6..9), - vec![vec![-1, -3], vec![-2, -3], vec![-1, -2, -3]] + vec![lits![-1, -3], lits![-2, -3], lits![-1, -2, -3]] ); // and 8+1 -2 = 7 == 0111 - assert_eq!(x.leq(5..8), vec![vec![-3], vec![-1, -3], vec![-2, -3]]); // and 5+1 -2 = 4 == 0100 + assert_eq!(x.leq(5..8), vec![lits![-3], lits![-1, -3], lits![-2, -3]]); // and 5+1 -2 = 4 == 0100 - assert_eq!(x.geq(1..5), vec![vec![1, 2, 3, 4], vec![2, 3, 4]]); // trues and 0000 and 0001 - assert_eq!(x.geq(15..20), vec![vec![1, 2], vec![2], vec![1], vec![]]); // 16-2 - 2 = 12 = 1100, 1101, 1110, false - - assert_eq!(x.leq(-2..3), vec![vec![], vec![-1]]); // - assert_eq!(x.leq(15..20), vec![vec![-2, -3, -4], vec![-1, -2, -3, -4]]); // 15+1 -2 = 14 = 1110, 1111, true + assert_eq!(x.geq(1..5), vec![lits![1, 2, 3, 4], lits![2, 3, 4]]); // trues and 0000 and 0001 + assert_eq!( + x.geq(15..20), + vec![lits![1, 2], lits![2], lits![1], lits![]] + ); // 16-2 - 2 = 12 = 1100, 1101, 1110, false - assert_eq!(x_lin.assign(&[-1, -2, -3, -4]), Ok(2)); - assert_eq!(x_lin.assign(&[1, -2, -3, -4]), Ok(2 + 1)); - assert_eq!(x_lin.assign(&[1, 2, -3, -4]), Ok(2 + 3)); - assert_eq!(x_lin.assign(&[-1, 2, -3, 4]), Ok(2 + 10)); + assert_eq!(x.leq(-2..3), vec![lits![], lits![-1]]); // assert_eq!( - x_lin.assign(&[1, 2, -3, 4]), - Err(CheckError::Unsatisfiable(Unsatisfiable)) // 2 + 11 = 13 + x.leq(15..20), + vec![lits![-2, -3, -4], lits![-1, -2, -3, -4]] + ); // 15+1 -2 = 14 = 1110, 1111, true + + assert_eq!(x_lin.value(&make_valuation(&[-1, -2, -3, -4])), Ok(2)); + assert_eq!(x_lin.value(&make_valuation(&[1, -2, -3, -4])), Ok(2 + 1)); + assert_eq!(x_lin.value(&make_valuation(&[1, 2, -3, -4])), Ok(2 + 3)); + assert_eq!(x_lin.value(&make_valuation(&[-1, 2, -3, 4])), Ok(2 + 10)); + assert_eq!( + x_lin.value(&make_valuation(&[1, 2, -3, 4])), + Err(Unsatisfiable.into()) // 2 + 11 = 13 ); assert_eq!( - x_lin.assign(&[1, 2, 3, 4]), - Err(CheckError::Unsatisfiable(Unsatisfiable)) + x_lin.value(&make_valuation(&[1, 2, 3, 4])), + Err(Unsatisfiable.into()) ); let tern = TernLeConstraint { @@ -739,20 +658,20 @@ pub mod tests { }; assert_sol!(db => TernLeEncoder::default(), &tern => vec![ - vec![-1, -2, -3, -4], // 0 - vec![1, -2, -3, -4], // 1 - vec![-1, 2, -3, -4], // 2 - vec![1, 2, -3, -4], // 3 - vec![-1, -2, 3, -4], // 4 - vec![1, -2, 3, -4], // 5 - vec![-1, 2, 3, -4],// 6 + lits![-1, -2, -3, -4], // 0 + lits![1, -2, -3, -4], // 1 + lits![-1, 2, -3, -4], // 2 + lits![1, 2, -3, -4], // 3 + lits![-1, -2, 3, -4], // 4 + lits![1, -2, 3, -4], // 5 + lits![-1, 2, 3, -4],// 6 ]); } #[test] fn ord_geq_test() { let mut db = TestDB::new(0); - let x = get_ord_x::<_, i32>( + let x = get_ord_x( &mut db, interval_set!(3..5, 5..7, 7..11), true, @@ -762,16 +681,16 @@ pub mod tests { assert_eq!(x.lits(), 3); assert_eq!(x.lb(), 2); assert_eq!(x.ub(), 10); - assert_eq!(x.geq(6..7), vec![vec![2]]); - assert_eq!(x.geq(4..7), vec![vec![2]]); + assert_eq!(x.geq(6..7), vec![lits![2]]); + assert_eq!(x.geq(4..7), vec![lits![2]]); - let x_lin: LinExp = LinExp::from(&x); - assert!(x_lin.assign(&[1, -2, 3]).is_err()); - assert!(x_lin.assign(&[-1, 2, -3]).is_err()); - assert_eq!(x_lin.assign(&[-1, -2, -3]), Ok(2)); - assert_eq!(x_lin.assign(&[1, -2, -3]), Ok(4)); - assert_eq!(x_lin.assign(&[1, 2, -3]), Ok(6)); - assert_eq!(x_lin.assign(&[1, 2, 3]), Ok(10)); + let x_lin = LinExp::from(&x); + assert!(x_lin.value(&make_valuation(&[1, -2, 3])).is_err()); + assert!(x_lin.value(&make_valuation(&[-1, 2, -3])).is_err()); + assert_eq!(x_lin.value(&make_valuation(&[-1, -2, -3])), Ok(2)); + assert_eq!(x_lin.value(&make_valuation(&[1, -2, -3])), Ok(4)); + assert_eq!(x_lin.value(&make_valuation(&[1, 2, -3])), Ok(6)); + assert_eq!(x_lin.value(&make_valuation(&[1, 2, 3])), Ok(10)); let tern = TernLeConstraint { x: &x, @@ -784,9 +703,9 @@ pub mod tests { assert_sol!(db => TernLeEncoder::default(), &tern => vec![ - vec![-1, -2, -3], - vec![1, -2, -3], - vec![1, 2, -3], + lits![-1, -2, -3], + lits![1, -2, -3], + lits![1, 2, -3], ]); } @@ -833,20 +752,20 @@ pub mod tests { assert_sol!(db => TernLeEncoder::default(), &tern => vec![ - vec![-1, -2, -3, -4, 5, -6], - vec![-1, -2, -3, -4, 5, 6], - vec![-1, -2, 3, -4, 5, -6], - vec![-1, -2, 3, -4, 5, 6], - vec![-1, -2, 3, 4, 5, 6], - vec![1, -2, -3, -4, 5, -6], - vec![1, -2, -3, -4, 5, 6], - vec![1, -2, 3, -4, 5, -6], - vec![1, -2, 3, -4, 5, 6], - vec![1, -2, 3, 4, 5, 6], - vec![1, 2, -3, -4, 5, 6], - vec![1, 2, 3, -4, 5, 6], - vec![1, 2, 3, 4, 5, 6], - ]); + lits![-1, -2, -3, -4, 5, -6], + lits![-1, -2, -3, -4, 5, 6], + lits![-1, -2, 3, -4, 5, -6], + lits![-1, -2, 3, -4, 5, 6], + lits![-1, -2, 3, 4, 5, 6], + lits![1, -2, -3, -4, 5, -6], + lits![1, -2, -3, -4, 5, 6], + lits![1, -2, 3, -4, 5, -6], + lits![1, -2, 3, -4, 5, 6], + lits![1, -2, 3, 4, 5, 6], + lits![1, 2, -3, -4, 5, 6], + lits![1, 2, 3, -4, 5, 6], + lits![1, 2, 3, 4, 5, 6], + ]); } #[test] @@ -918,24 +837,24 @@ pub mod tests { assert_sol!(db => TernLeEncoder::default(), &tern => vec![ - vec![-1, -2, -3, -4, -5], - vec![-1, -2, 3, -4, -5], - vec![-1, -2, -3, 4, -5], - vec![1, -2, -3, 4, -5], - vec![-1, -2, 3, 4, -5], - vec![1, -2, 3, 4, -5], - vec![-1, 2, 3, 4, -5], - vec![-1, -2, -3, -4, 5], - vec![1, -2, -3, -4, 5], - vec![-1, 2, -3, -4, 5], - vec![-1, -2, 3, -4, 5], - vec![1, -2, 3, -4, 5], - vec![-1, 2, 3, -4, 5], - vec![1, 2, 3, -4, 5], - vec![-1, -2, -3, 4, 5], - vec![1, -2, -3, 4, 5], - vec![-1, 2, -3, 4, 5], - vec![1, 2, -3, 4, 5], + lits![-1, -2, -3, -4, -5], + lits![-1, -2, 3, -4, -5], + lits![-1, -2, -3, 4, -5], + lits![1, -2, -3, 4, -5], + lits![-1, -2, 3, 4, -5], + lits![1, -2, 3, 4, -5], + lits![-1, 2, 3, 4, -5], + lits![-1, -2, -3, -4, 5], + lits![1, -2, -3, -4, 5], + lits![-1, 2, -3, -4, 5], + lits![-1, -2, 3, -4, 5], + lits![1, -2, 3, -4, 5], + lits![-1, 2, 3, -4, 5], + lits![1, 2, 3, -4, 5], + lits![-1, -2, -3, 4, 5], + lits![1, -2, -3, 4, 5], + lits![-1, 2, -3, 4, 5], + lits![1, 2, -3, 4, 5], ]); } @@ -944,7 +863,7 @@ pub mod tests { let mut db = TestDB::new(0); let n = 4; let lb = 0; - let ub = (2i32.pow(n)) - 1; + let ub = ((2i32.pow(n)) - 1) as Coeff; let (x, y, z) = ( get_bin_x(&mut db, lb, ub, true, "x".to_string()), @@ -964,9 +883,7 @@ pub mod tests { let sols = db.generate_solutions(|sol| tern.check(sol).is_ok(), db.num_var); - assert_sol!(db => TernLeEncoder::default(), &tern => - sols - ); + assert_sol!(db => TernLeEncoder::default(), &tern => sols); } #[test] @@ -974,7 +891,7 @@ pub mod tests { let mut db = TestDB::new(0); let n = 5; let lb = 0; - let ub = (2i32.pow(n)) - 1; + let ub = ((2i32.pow(n)) - 1) as Coeff; let (x, y, z) = ( get_bin_x(&mut db, lb, ub, true, "x".to_string()), @@ -1004,9 +921,27 @@ pub mod tests { let mut db = TestDB::new(0); let n = 2; let (x, y, z) = ( - get_bin_x(&mut db, 0, (2i32.pow(n)) - 1, true, "x".to_string()), - get_bin_x(&mut db, 0, (2i32.pow(n)) - 1, true, "y".to_string()), - get_bin_x(&mut db, 0, (2i32.pow(n + 1)) - 2, true, "z".to_string()), + get_bin_x( + &mut db, + 0, + ((2i32.pow(n)) - 1) as Coeff, + true, + "x".to_string(), + ), + get_bin_x( + &mut db, + 0, + ((2i32.pow(n)) - 1) as Coeff, + true, + "y".to_string(), + ), + get_bin_x( + &mut db, + 0, + ((2i32.pow(n + 1)) - 2) as Coeff, + true, + "z".to_string(), + ), ); let tern = TernLeConstraint { @@ -1068,18 +1003,18 @@ pub mod tests { assert_sol!(db => TernLeEncoder::default(), &tern => vec![ - vec![-1, -2, -3, -4, -5, -6, -7], - vec![1, -2, -3, -4, 5, -6, -7], - vec![-1, -2, 3, -4, 5, -6, -7], - vec![-1, 2, -3, -4, -5, 6, -7], - vec![1, -2, 3, -4, -5, 6, -7], - vec![-1, -2, -3, 4, -5, 6, -7], - vec![-1, 2, 3, -4, 5, 6, -7], - vec![1, -2, -3, 4, 5, 6, -7], - vec![-1, -2, 3, 4, 5, 6, -7], - vec![-1, 2, -3, 4, -5, -6, 7], - vec![1, -2, 3, 4, -5, -6, 7], - vec![-1, 2, 3, 4, 5, -6, 7], + lits![-1, -2, -3, -4, -5, -6, -7], + lits![1, -2, -3, -4, 5, -6, -7], + lits![-1, -2, 3, -4, 5, -6, -7], + lits![-1, 2, -3, -4, -5, 6, -7], + lits![1, -2, 3, -4, -5, 6, -7], + lits![-1, -2, -3, 4, -5, 6, -7], + lits![-1, 2, 3, -4, 5, 6, -7], + lits![1, -2, -3, 4, 5, 6, -7], + lits![-1, -2, 3, 4, 5, 6, -7], + lits![-1, 2, -3, 4, -5, -6, 7], + lits![1, -2, 3, 4, -5, -6, 7], + lits![-1, 2, 3, 4, 5, -6, 7], ] ); } @@ -1128,37 +1063,37 @@ pub mod tests { assert_sol!(db => TernLeEncoder::default(), &tern => vec![ - vec![-1, -2, -3, -4, -5, -6, -7], - vec![-1, -2, -3, -4, 5, -6, -7], - vec![1, -2, -3, -4, 5, -6, -7], - vec![-1, -2, -3, -4, -5, 6, -7], - vec![1, -2, -3, -4, -5, 6, -7], - vec![-1, 2, -3, -4, -5, 6, -7], - vec![-1, -2, -3, -4, 5, 6, -7], - vec![1, -2, -3, -4, 5, 6, -7], - vec![-1, 2, -3, -4, 5, 6, -7], - vec![1, 2, -3, -4, 5, 6, -7], - vec![-1, -2, -3, -4, -5, -6, 7], - vec![1, -2, -3, -4, -5, -6, 7], - vec![-1, 2, -3, -4, -5, -6, 7], - vec![1, 2, -3, -4, -5, -6, 7], - vec![-1, -2, 3, -4, -5, -6, 7], - vec![-1, -2, -3, -4, 5, -6, 7], - vec![1, -2, -3, -4, 5, -6, 7], - vec![-1, 2, -3, -4, 5, -6, 7], - vec![1, 2, -3, -4, 5, -6, 7], - vec![-1, -2, 3, -4, 5, -6, 7], - vec![1, -2, 3, -4, 5, -6, 7], - vec![-1, -2, -3, 4, 5, -6, 7], - vec![-1, -2, -3, -4, -5, 6, 7], - vec![1, -2, -3, -4, -5, 6, 7], - vec![-1, 2, -3, -4, -5, 6, 7], - vec![1, 2, -3, -4, -5, 6, 7], - vec![-1, -2, 3, -4, -5, 6, 7], - vec![1, -2, 3, -4, -5, 6, 7], - vec![-1, 2, 3, -4, -5, 6, 7], - vec![-1, -2, -3, 4, -5, 6, 7], - vec![1, -2, -3, 4, -5, 6, 7], + lits![-1, -2, -3, -4, -5, -6, -7], + lits![-1, -2, -3, -4, 5, -6, -7], + lits![1, -2, -3, -4, 5, -6, -7], + lits![-1, -2, -3, -4, -5, 6, -7], + lits![1, -2, -3, -4, -5, 6, -7], + lits![-1, 2, -3, -4, -5, 6, -7], + lits![-1, -2, -3, -4, 5, 6, -7], + lits![1, -2, -3, -4, 5, 6, -7], + lits![-1, 2, -3, -4, 5, 6, -7], + lits![1, 2, -3, -4, 5, 6, -7], + lits![-1, -2, -3, -4, -5, -6, 7], + lits![1, -2, -3, -4, -5, -6, 7], + lits![-1, 2, -3, -4, -5, -6, 7], + lits![1, 2, -3, -4, -5, -6, 7], + lits![-1, -2, 3, -4, -5, -6, 7], + lits![-1, -2, -3, -4, 5, -6, 7], + lits![1, -2, -3, -4, 5, -6, 7], + lits![-1, 2, -3, -4, 5, -6, 7], + lits![1, 2, -3, -4, 5, -6, 7], + lits![-1, -2, 3, -4, 5, -6, 7], + lits![1, -2, 3, -4, 5, -6, 7], + lits![-1, -2, -3, 4, 5, -6, 7], + lits![-1, -2, -3, -4, -5, 6, 7], + lits![1, -2, -3, -4, -5, 6, 7], + lits![-1, 2, -3, -4, -5, 6, 7], + lits![1, 2, -3, -4, -5, 6, 7], + lits![-1, -2, 3, -4, -5, 6, 7], + lits![1, -2, 3, -4, -5, 6, 7], + lits![-1, 2, 3, -4, -5, 6, 7], + lits![-1, -2, -3, 4, -5, 6, 7], + lits![1, -2, -3, 4, -5, 6, 7], ] ); } @@ -1169,12 +1104,12 @@ pub mod tests { Bin, } - fn from_dom( + fn from_dom( db: &mut DB, - dom: &[C], + dom: &[Coeff], enc: &IntVarEncoding, lbl: String, - ) -> IntVarEnc { + ) -> IntVarEnc { if dom.len() == 1 { IntVarEnc::Const(dom[0]) } else { diff --git a/crates/pindakaas/src/int/enc.rs b/crates/pindakaas/src/int/enc.rs index a2b295889..d49efff24 100644 --- a/crates/pindakaas/src/int/enc.rs +++ b/crates/pindakaas/src/int/enc.rs @@ -1,132 +1,119 @@ +use std::{ + collections::HashSet, + fmt, + fmt::Display, + hash::BuildHasherDefault, + ops::{Not, Range}, +}; + use iset::{interval_map, interval_set, IntervalMap, IntervalSet}; +use itertools::Itertools; use rustc_hash::FxHashMap; use super::display_dom; -use itertools::Itertools; -use std::fmt::Display; - -use crate::int::{IntVar, TernLeConstraint, TernLeEncoder}; use crate::{ helpers::{as_binary, is_powers_of_two, unsigned_binary_range_ub}, - linear::{LimitComp, LinExp, Part}, + int::{IntVar, TernLeConstraint, TernLeEncoder}, + linear::{LimitComp, LinExp, Part, PosCoeff}, trace::{emit_clause, new_var}, - CheckError, Checker, ClauseDatabase, Coefficient, Encoder, Literal, PosCoeff, Result, - Unsatisfiable, -}; -use std::{ - collections::HashSet, - fmt, - hash::BuildHasherDefault, - ops::{Neg, Range}, + CheckError, Checker, ClauseDatabase, Coeff, Encoder, Lit, Result, Unsatisfiable, Valuation, }; #[derive(Clone, Debug, PartialEq)] -pub(crate) enum LitOrConst { +pub(crate) enum LitOrConst { Lit(Lit), Const(bool), } -impl From for LitOrConst { +impl From for LitOrConst { fn from(item: Lit) -> Self { LitOrConst::Lit(item) } } -impl From for LitOrConst { +impl From for LitOrConst { fn from(item: bool) -> Self { LitOrConst::Const(item) } } -impl Display for LitOrConst { +impl Display for LitOrConst { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - LitOrConst::Const(b) => write!(f, "{}", *b as i32), - LitOrConst::Lit(l) => write!(f, "b{l:?}"), + LitOrConst::Const(b) => write!(f, "{b}"), + LitOrConst::Lit(l) => write!(f, "{l}"), } } } -impl Neg for LitOrConst { - type Output = Self; - - fn neg(self) -> Self { +impl Not for LitOrConst { + type Output = LitOrConst; + fn not(self) -> Self::Output { match self { - Self::Lit(lit) => Self::Lit(lit.negate()), - Self::Const(b) => Self::Const(!b), + LitOrConst::Lit(l) => (!l).into(), + LitOrConst::Const(b) => (!b).into(), } } } -impl fmt::Display for IntVarOrd { +impl fmt::Display for IntVarOrd { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}:O ∈ {}", self.lbl, - display_dom(&self.dom().iter(..).map(|d| d.end - C::one()).collect()) + display_dom(&self.dom().iter(..).map(|d| d.end - 1).collect()) ) } } pub(crate) const GROUND_BINARY_AT_LB: bool = false; -impl fmt::Display for IntVarBin { +impl fmt::Display for IntVarBin { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}:B ∈ {} [{}]", self.lbl, - display_dom(&self.dom().iter(..).map(|d| d.end - C::one()).collect()), + display_dom(&self.dom().iter(..).map(|d| d.end - 1).collect()), self.lits() ) } } #[derive(Debug, Clone)] -pub(crate) struct IntVarOrd { - pub(crate) xs: IntervalMap, +pub(crate) struct IntVarOrd { + pub(crate) xs: IntervalMap, pub(crate) lbl: String, } -impl IntVarOrd { - pub fn from_bounds>( - db: &mut DB, - lb: C, - ub: C, - lbl: String, - ) -> Self { - Self::from_dom( - db, - num::iter::range_inclusive(lb, ub) - .collect::>() - .as_slice(), - lbl, - ) +impl IntVarOrd { + pub fn from_bounds(db: &mut DB, lb: Coeff, ub: Coeff, lbl: String) -> Self { + Self::from_dom(db, (lb..=ub).collect_vec().as_slice(), lbl) } - pub fn from_dom>(db: &mut DB, dom: &[C], lbl: String) -> Self { + pub fn from_dom(db: &mut DB, dom: &[Coeff], lbl: String) -> Self { Self::from_syms( db, dom.iter() .tuple_windows() - .map(|(&a, &b)| (a + C::one())..(b + C::one())) + .map(|(a, b)| (a + 1)..(b + 1)) .collect(), lbl, ) } - pub fn from_syms>( + pub fn from_syms( db: &mut DB, - syms: IntervalSet, + syms: IntervalSet, lbl: String, ) -> Self { Self::from_views(db, syms.into_iter(..).map(|c| (c, None)).collect(), lbl) } - pub fn from_views>( + pub fn from_views( db: &mut DB, - views: IntervalMap>, + views: IntervalMap>, lbl: String, ) -> Self { assert!(!views.is_empty()); @@ -142,37 +129,36 @@ impl IntVarOrd { .into_iter(..) .map(|(v, lit)| { #[cfg(feature = "trace")] - let lbl = format!("{lbl}>={}..{}", v.start, v.end - C::one()); + let lbl = format!("{lbl}>={}..{}", v.start, v.end - 1); (v, lit.unwrap_or_else(|| new_var!(db, lbl))) }) .collect::>(); Self { xs, lbl } } - pub fn consistency(&self) -> ImplicationChainConstraint { + pub fn consistency(&self) -> ImplicationChainConstraint { ImplicationChainConstraint { - lits: self.xs.values(..).cloned().collect::>(), + lits: self.xs.values(..).cloned().collect_vec(), } } #[allow(dead_code)] - pub fn consistent>(&self, db: &mut DB) -> Result { + pub fn consistent(&self, db: &mut DB) -> Result { ImplicationChainEncoder::default()._encode(db, &self.consistency()) } - pub fn div(&self, c: &C) -> IntVarEnc { - assert!(*c == C::one() + C::one(), "Can only divide IntVarOrd by 2"); - let xs = self + pub fn div(&self, c: Coeff) -> IntVarEnc { + assert!(c == 2, "Can only divide IntVarOrd by 2"); + let xs: IntervalMap<_, _> = self .xs - .clone() - .into_iter(..) - .filter(|(c, _)| (c.end - C::one()).is_even()) - .map(|(c, l)| (((c.end - C::one()) / (C::one() + C::one())), l)) - .map(|(c, l)| (c..(c + C::one()), l)) - .collect::>(); + .iter(..) + .filter(|(c, _)| (c.end - 1) % 2 == 0) + .map(|(c, l)| (((c.end - 1) / (1 + 1)), *l)) + .map(|(c, l)| (c..(c + 1), l)) + .collect(); if xs.is_empty() { - IntVarEnc::Const(self.lb() / *c) + IntVarEnc::Const(self.lb() / c) } else { IntVarOrd { xs, @@ -182,90 +168,87 @@ impl IntVarOrd { } } - pub fn dom(&self) -> IntervalSet { - std::iter::once(self.lb()..(self.lb() + C::one())) + pub fn dom(&self) -> IntervalSet { + std::iter::once(self.lb()..(self.lb() + 1)) .chain(self.xs.intervals(..)) .collect() } - pub fn leqs(&self) -> Vec<(Range, Vec>)> { + pub fn leqs(&self) -> Vec<(Range, Vec>)> { self.xs .iter(..) - .map(|(v, x)| { - ( - (v.start - C::one())..(v.end - C::one()), - vec![vec![x.negate()]], - ) - }) - .chain(std::iter::once((self.ub()..self.ub() + C::one(), vec![]))) + .map(|(v, x)| ((v.start - 1)..(v.end - 1), vec![vec![!x]])) + .chain(std::iter::once((self.ub()..self.ub() + 1, vec![]))) .collect() } - pub fn geqs(&self) -> Vec<(Range, Vec>)> { - std::iter::once((self.lb()..(self.lb() + C::one()), vec![])) - .chain(self.xs.iter(..).map(|(v, x)| (v, vec![vec![x.clone()]]))) + pub fn geqs(&self) -> Vec<(Range, Vec>)> { + std::iter::once((self.lb()..(self.lb() + 1), vec![])) + .chain(self.xs.iter(..).map(|(v, x)| (v, vec![vec![*x]]))) .collect() } - pub fn lb(&self) -> C { - self.xs.range().unwrap().start - C::one() + pub fn lb(&self) -> Coeff { + self.xs.range().unwrap().start - 1 } - pub fn ub(&self) -> C { - self.xs.range().unwrap().end - C::one() + pub fn ub(&self) -> Coeff { + self.xs.range().unwrap().end - 1 } - pub fn leq(&self, v: Range) -> Vec> { - let v = v.start + C::one(); // [x<=v] = [x < v+1] + pub fn leq(&self, v: Range) -> Vec> { + let v = v.start + 1; // [x<=v] = [x < v+1] if v <= self.lb() { vec![vec![]] } else if v > self.ub() { vec![] } else { - match self.xs.overlap(v).collect::>()[..] { - [(_, x)] => vec![vec![x.negate()]], + match self.xs.overlap(v).collect_vec()[..] { + [(_, x)] => vec![vec![!x]], _ => panic!("No or multiples literals at {v:?} for var {self:?}"), } } } - pub fn geq(&self, v: Range) -> Vec> { - let v = v.end - C::one(); + pub fn geq(&self, v: Range) -> Vec> { + let v = v.end - 1; if v <= self.lb() { vec![] } else if v > self.ub() { vec![vec![]] } else { - match self.xs.overlap(v).collect::>()[..] { - [(_, x)] => vec![vec![x.clone()]], + match self.xs.overlap(v).collect_vec()[..] { + [(_, x)] => vec![vec![*x]], _ => panic!("No or multiples literals at {v:?} for var {self:?}"), } } } - pub fn as_lin_exp(&self) -> LinExp { - let mut acc = self.lb(); - LinExp::new() + pub fn lits(&self) -> usize { + self.xs.len() + } +} + +impl From<&IntVarOrd> for LinExp { + fn from(value: &IntVarOrd) -> Self { + let mut acc = value.lb(); + LinExp::default() .add_chain( - &self + &value .xs .iter(..) .map(|(iv, lit)| { - let v = iv.end - C::one() - acc; + let v = iv.end - 1 - acc; acc += v; - (lit.clone(), v) + (*lit, v) }) - .collect::>(), + .collect_vec(), ) - .add_constant(self.lb()) - } - - pub fn lits(&self) -> usize { - self.xs.len() + .add_constant(value.lb()) } } -pub(crate) struct ImplicationChainConstraint { +pub(crate) struct ImplicationChainConstraint { lits: Vec, } @@ -276,46 +259,37 @@ impl ImplicationChainEncoder { pub fn _encode( &mut self, db: &mut DB, - ic: &ImplicationChainConstraint, + ic: &ImplicationChainConstraint, ) -> Result { - for (a, b) in ic.lits.iter().tuple_windows() { - emit_clause!(db, &[b.negate(), a.clone()])? + for (a, b) in ic.lits.iter().copied().tuple_windows() { + emit_clause!(db, [!b, a])? } Ok(()) } } -impl Checker for ImplicationChainConstraint { - type Lit = Lit; - fn check(&self, solution: &[Self::Lit]) -> Result<(), CheckError> { - self.lits.iter().tuple_windows().try_for_each(|(a, b)| { - let a = Self::assign(a, solution); - let b = Self::assign(b, solution); - if b.is_negated() || !a.is_negated() { - Ok(()) - } else { - Err(CheckError::Unsatisfiable(Unsatisfiable)) +impl Checker for ImplicationChainConstraint { + fn check(&self, value: F) -> Result<(), CheckError> { + for (a, b) in self.lits.iter().copied().tuple_windows() { + if value(a).unwrap_or(true) & !value(b).unwrap_or(false) { + return Err(Unsatisfiable.into()); } - }) + } + Ok(()) } } #[derive(Debug, Clone)] -pub(crate) struct IntVarBin { +pub(crate) struct IntVarBin { pub(crate) xs: Vec, - lb: C, - ub: C, + lb: Coeff, + ub: Coeff, lbl: String, } -impl IntVarBin { +impl IntVarBin { // TODO change to with_label or something - pub fn from_bounds>( - db: &mut DB, - lb: C, - ub: C, - lbl: String, - ) -> Self { + pub fn from_bounds(db: &mut DB, lb: Coeff, ub: Coeff, lbl: String) -> Self { Self { xs: (0..IntVar::required_bits(lb, ub)) .map(|_i| new_var!(db, format!("{}^{}", lbl, _i))) @@ -327,18 +301,12 @@ impl IntVarBin { } pub fn from_terms( - terms: Vec<(Lit, PosCoeff)>, - lb: PosCoeff, - ub: PosCoeff, + terms: Vec<(Lit, PosCoeff)>, + lb: PosCoeff, + ub: PosCoeff, lbl: String, ) -> Self { - debug_assert!(is_powers_of_two( - terms - .iter() - .map(|(_, c)| **c) - .collect::>() - .as_slice() - )); + debug_assert!(is_powers_of_two(terms.iter().map(|(_, c)| **c))); Self { xs: terms.into_iter().map(|(l, _)| l).collect(), lb: *lb, // TODO support non-zero @@ -347,14 +315,14 @@ impl IntVarBin { } } - pub fn consistent>(&self, db: &mut DB) -> Result { + pub fn consistent(&self, db: &mut DB) -> Result { let mut encoder = TernLeEncoder::default(); if !GROUND_BINARY_AT_LB { encoder.encode( db, &TernLeConstraint { x: &IntVarEnc::Const(self.lb), - y: &IntVarEnc::Const(C::zero()), + y: &IntVarEnc::Const(0), cmp: LimitComp::LessEq, z: &IntVarEnc::Bin(self.clone()), }, @@ -364,40 +332,38 @@ impl IntVarBin { db, &TernLeConstraint { x: &IntVarEnc::Bin(self.clone()), - y: &IntVarEnc::Const(C::zero()), + y: &IntVarEnc::Const(0), cmp: LimitComp::LessEq, z: &IntVarEnc::Const(self.ub), }, ) } - fn div(&self, _: &C) -> IntVarEnc { + fn div(&self, _: Coeff) -> IntVarEnc { todo!() } - fn dom(&self) -> IntervalSet { - num::iter::range_inclusive(self.lb, self.ub) - .map(|i| i..(i + C::one())) - .collect() + fn dom(&self) -> IntervalSet { + (self.lb..=self.ub).map(|i| i..(i + 1)).collect() } - pub(crate) fn lb(&self) -> C { + pub(crate) fn lb(&self) -> Coeff { self.lb } - pub(crate) fn ub(&self) -> C { + pub(crate) fn ub(&self) -> Coeff { self.ub } - pub(crate) fn geq(&self, v: Range) -> Vec> { + pub(crate) fn geq(&self, v: Range) -> Vec> { self.ineq(v, true) } - pub(crate) fn leq(&self, v: Range) -> Vec> { + pub(crate) fn leq(&self, v: Range) -> Vec> { self.ineq(v, false) } - fn ineq(&self, v: Range, geq: bool) -> Vec> { + fn ineq(&self, v: Range, geq: bool) -> Vec> { // TODO could *maybe* be domain lb/ub let v = if GROUND_BINARY_AT_LB { (v.start - self.lb())..(v.end - self.lb()) @@ -406,65 +372,43 @@ impl IntVarBin { }; // The range 0..(2^n)-1 covered by the (unsigned) binary representation - let range_lb = C::zero(); - let range_ub = unsigned_binary_range_ub::(self.lits()); - - num::iter::range( - std::cmp::max(range_lb - C::one(), v.start), - std::cmp::min(v.end, range_ub + C::one() + C::one()), - ) - .filter_map(|v| { - let v = if geq { v - C::one() } else { v + C::one() }; - if v < range_lb { - (!geq).then_some(vec![]) - } else if v > range_ub { - geq.then_some(vec![]) - } else { - Some( - as_binary(v.into(), Some(self.lits())) - .into_iter() - .zip(self.xs.iter()) - // if >=, find 0s, if <=, find 1s - .filter_map(|(b, x)| (b != geq).then_some(x)) - .map(|x| if geq { x.clone() } else { x.negate() }) - .collect(), - ) - } - }) - .collect() - } - - fn as_lin_exp(&self) -> LinExp { - let mut k = C::one(); - let two = C::one() + C::one(); - let terms = self - .xs - .iter() - .map(|x| { - let term = (x.clone(), k); - k *= two; - term + let range_lb = 0; + let range_ub = unsigned_binary_range_ub(self.lits() as u32); + + let range = std::cmp::max(range_lb - 1, v.start)..std::cmp::min(v.end, range_ub + 1 + 1); + range + .filter_map(|v| { + let v = if geq { v - 1 } else { v + 1 }; + if v < range_lb { + (!geq).then_some(vec![]) + } else if v > range_ub { + geq.then_some(vec![]) + } else { + Some( + as_binary(PosCoeff::new(v), Some(self.lits() as u32)) + .into_iter() + .zip(self.xs.iter()) + // if >=, find 0s, if <=, find 1s + .filter_map(|(b, x)| (b != geq).then_some(x)) + .map(|x| if geq { *x } else { !x }) + .collect(), + ) + } }) - .collect::>(); - let lin_exp = LinExp::new().add_bounded_log_encoding(terms.as_slice(), self.lb, self.ub); - if GROUND_BINARY_AT_LB { - lin_exp.add_constant(self.lb) - } else { - lin_exp - } + .collect() } pub(crate) fn lits(&self) -> usize { self.xs.len() } - pub(crate) fn add>( + pub(crate) fn add( &self, db: &mut DB, encoder: &mut TernLeEncoder, - y: C, + y: Coeff, ) -> Result { - if y.is_zero() { + if y == 0 { Ok(self.clone()) } else if GROUND_BINARY_AT_LB { Ok(IntVarBin { @@ -495,49 +439,72 @@ impl IntVarBin { } } +impl From<&IntVarBin> for LinExp { + fn from(value: &IntVarBin) -> Self { + let mut k = 1; + let terms = value + .xs + .iter() + .map(|x| { + let term = (*x, k); + k *= 2; + term + }) + .collect_vec(); + let lin_exp = + LinExp::default().add_bounded_log_encoding(terms.as_slice(), value.lb, value.ub); + if GROUND_BINARY_AT_LB { + lin_exp.add_constant(value.lb) + } else { + lin_exp + } + } +} + #[derive(Debug, Clone)] -pub(crate) enum IntVarEnc { - Ord(IntVarOrd), - Bin(IntVarBin), - Const(C), +pub(crate) enum IntVarEnc { + Ord(IntVarOrd), + Bin(IntVarBin), + Const(Coeff), } const COUPLE_DOM_PART_TO_ORD: bool = false; -impl IntVarEnc { +impl IntVarEnc { /// Constructs (one or more) IntVar `ys` for linear expression `xs` so that ∑ xs ≦ ∑ ys - pub fn from_part>( + pub fn from_part( db: &mut DB, - xs: &Part>, - ub: PosCoeff, + xs: &Part, + ub: PosCoeff, lbl: String, ) -> Vec { match xs { Part::Amo(terms) => { - let terms: Vec<(PosCoeff, Lit)> = terms + let terms: Vec<(Coeff, Lit)> = terms .iter() - .map(|(lit, coef)| (coef.clone(), lit.clone())) + .copied() + .map(|(lit, coef)| (*coef, lit)) .collect(); // for a set of terms with the same coefficients, replace by a single term with fresh variable o (implied by each literal) - let mut h: FxHashMap> = + let mut h: FxHashMap> = FxHashMap::with_capacity_and_hasher(terms.len(), BuildHasherDefault::default()); for (coef, lit) in terms { - debug_assert!(coef <= ub); - h.entry(*coef).or_insert_with(Vec::new).push(lit); + debug_assert!(coef <= *ub); + h.entry(coef).or_default().push(lit); } - let dom = std::iter::once((C::zero(), vec![])) + let dom = std::iter::once((0, vec![])) .chain(h) .sorted_by(|(a, _), (b, _)| a.cmp(b)) .tuple_windows() .map(|((prev, _), (coef, lits))| { - let interval = (prev + C::one())..(coef + C::one()); + let interval = (prev + 1)..(coef + 1); if lits.len() == 1 { - (interval, Some(lits[0].clone())) + (interval, Some(lits[0])) } else { let o = new_var!(db, format!("y_{:?}>={:?}", lits, coef)); for lit in lits { - emit_clause!(db, &[lit.negate(), o.clone()]).unwrap(); + emit_clause!(db, [!lit, o]).unwrap(); } (interval, Some(o)) } @@ -547,51 +514,50 @@ impl IntVarEnc { } // Leaves built from Ic/Dom groups are guaranteed to have unique values Part::Ic(terms) => { - let mut acc = C::zero(); // running sum - let dom = std::iter::once(&(terms[0].0.clone(), C::zero().into())) + let mut acc = 0; // running sum + let dom = std::iter::once(&(terms[0].0, PosCoeff::new(0))) .chain(terms.iter()) - .map(|(lit, coef)| { - acc += **coef; + .map(|&(lit, coef)| { + acc += *coef; debug_assert!(acc <= *ub); - (acc, lit.clone()) + (acc, lit) }) .tuple_windows() .map(|((prev, _), (coef, lit))| { - ((prev + C::one())..(coef + C::one()), Some(lit)) + ((prev + 1)..(coef + 1), Some(lit)) }) .collect::>(); vec![IntVarEnc::Ord(IntVarOrd::from_views(db, dom, lbl))] } Part::Dom(terms, l, u) => { - // TODO account for bounds (or even better, create IntVarBin) - if COUPLE_DOM_PART_TO_ORD { - // TODO old method (which at least respected bounds) - let x_bin = - IntVarBin::from_terms(terms.to_vec(), l.clone(), u.clone(), String::from("x")); - let x_ord = IntVarEnc::Ord(IntVarOrd::from_bounds(db, x_bin.lb(), x_bin.ub(), String::from("x"))); - - TernLeEncoder::default() - .encode( - db, - &TernLeConstraint::new( - &x_ord, - &IntVarEnc::Const(C::zero()), + // TODO account for bounds (or even better, create IntVarBin) + // TODO old method (which at least respected bounds) + if COUPLE_DOM_PART_TO_ORD { + let x_bin = IntVarBin::from_terms(terms.iter().copied().map(|(l, i)| (l, i)).collect(), *l, *u, String::from("x")); + let x_ord = IntVarEnc::Ord(IntVarOrd::from_bounds(db, x_bin.lb(), x_bin.ub(), String::from("x"))); + + TernLeEncoder::default() + .encode( + db, + &TernLeConstraint::new( + &x_ord, + &IntVarEnc::Const(0), LimitComp::LessEq, &x_bin.into(), ), - ) - .unwrap(); - vec![x_ord] - } else { - terms.iter().enumerate().map(|(i,(lit, coef))| {IntVarEnc::Ord(IntVarOrd::from_views( - db, - interval_map! { C::one()..(**coef+C::one()) => Some(lit.clone()) }, - format!("{lbl}^{i}") - ))}).collect() - } + ).unwrap(); + vec![x_ord] + } else { + terms.iter().enumerate().map(|(i,(lit, coef))| {IntVarEnc::Ord(IntVarOrd::from_views( + db, + interval_map! { 1..(**coef+1) => Some(*lit) }, + format!("{lbl}^{i}") + ))} + ).collect() + } } - // TODO Not so easy to transfer a binary encoded int var + // TODO Not so easy to transfer a binary encoded int var // Part::Dom(terms, l, u) => { // let coef = (terms[0].1); // let false_ if (coef > 1).then(|| let false_ = Some(new_var!(db)); emit_clause!(&[-false_]); false_ }); @@ -607,11 +573,11 @@ impl IntVarEnc { } #[allow(dead_code)] - pub(crate) fn from_dom>( + pub(crate) fn from_dom( db: &mut DB, - dom: &[C], + dom: &[Coeff], lbl: String, - ) -> Result> { + ) -> Result { match dom { [] => Err(Unsatisfiable), [d] => Ok(IntVarEnc::Const(*d)), @@ -619,16 +585,16 @@ impl IntVarEnc { } } - pub(crate) fn add>( + pub(crate) fn add( &self, db: &mut DB, encoder: &mut TernLeEncoder, - y: &IntVarEnc, - lb: Option, - ub: Option, + y: &IntVarEnc, + lb: Option, + ub: Option, // cmp: &LimitComp, // enc: &'a mut dyn Encoder>, - ) -> Result> { + ) -> Result { let comp_lb = self.lb() + y.lb(); let lb = std::cmp::max(lb.unwrap_or(comp_lb), comp_lb); @@ -638,12 +604,12 @@ impl IntVarEnc { match (self, y) { (IntVarEnc::Const(a), IntVarEnc::Const(b)) => Ok(IntVarEnc::Const(*a + *b)), // TODO only used in sorters which enforce the constraints later! - (IntVarEnc::Const(c), x) | (x, IntVarEnc::Const(c)) if c.is_zero() => Ok(x.clone()), + (IntVarEnc::Const(c), x) | (x, IntVarEnc::Const(c)) if (*c == 0) => Ok(x.clone()), (IntVarEnc::Ord(x), IntVarEnc::Ord(y)) => Ok(IntVarEnc::Ord(IntVarOrd::from_syms( db, ord_plus_ord_le_ord_sparse_dom( - x.dom().iter(..).map(|d| d.end - C::one()).collect(), - y.dom().iter(..).map(|d| d.end - C::one()).collect(), + x.dom().iter(..).map(|d| d.end - 1).collect(), + y.dom().iter(..).map(|d| d.end - 1).collect(), lb, ub, ), @@ -651,9 +617,8 @@ impl IntVarEnc { ))), (IntVarEnc::Ord(x), IntVarEnc::Const(y)) | (IntVarEnc::Const(y), IntVarEnc::Ord(x)) => { let xs = - x.xs.clone() - .into_iter(..) - .map(|(c, l)| ((c.start + *y)..(c.end + *y), l)) + x.xs.iter(..) + .map(|(c, l)| ((c.start + *y)..(c.end + *y), *l)) .collect(); Ok(IntVarOrd { xs, @@ -692,7 +657,7 @@ impl IntVarEnc { } } - pub(crate) fn leqs(&self) -> Vec<(Range, Vec>)> { + pub(crate) fn leqs(&self) -> Vec<(Range, Vec>)> { match self { IntVarEnc::Ord(o) => o.leqs(), x => x @@ -703,7 +668,7 @@ impl IntVarEnc { } } - pub(crate) fn geqs(&self) -> Vec<(Range, Vec>)> { + pub(crate) fn geqs(&self) -> Vec<(Range, Vec>)> { match self { IntVarEnc::Ord(o) => o.geqs(), x => x @@ -715,12 +680,12 @@ impl IntVarEnc { } /// Returns cnf constraining `x<=v`, which is empty if true and contains empty if false - pub(crate) fn leq(&self, v: Range) -> Vec> { + pub(crate) fn leq(&self, v: Range) -> Vec> { match self { IntVarEnc::Ord(o) => o.leq(v), IntVarEnc::Bin(b) => b.leq(v), IntVarEnc::Const(c) => { - let v = v.start + C::one(); // [x<=v] = [x < v+1] + let v = v.start + 1; // [x<=v] = [x < v+1] if v <= *c { vec![vec![]] } else { @@ -731,12 +696,12 @@ impl IntVarEnc { } /// Returns a clause constraining `x>=v`, which is None if true and empty if false - pub(crate) fn geq(&self, v: Range) -> Vec> { + pub(crate) fn geq(&self, v: Range) -> Vec> { match self { IntVarEnc::Ord(o) => o.geq(v), IntVarEnc::Bin(b) => b.geq(v), IntVarEnc::Const(c) => { - let v = v.end - C::one(); + let v = v.end - 1; if v <= *c { vec![] } else { @@ -746,24 +711,24 @@ impl IntVarEnc { } } - pub(crate) fn div(&self, c: &C) -> IntVarEnc { + pub(crate) fn div(&self, c: Coeff) -> IntVarEnc { match self { IntVarEnc::Ord(o) => o.div(c), IntVarEnc::Bin(b) => b.div(c), - IntVarEnc::Const(m) => IntVarEnc::Const(*m / *c), + &IntVarEnc::Const(m) => IntVarEnc::Const(m / c), } } /// Returns a partitioned domain - pub(crate) fn dom(&self) -> IntervalSet { + pub(crate) fn dom(&self) -> IntervalSet { match self { IntVarEnc::Ord(o) => o.dom(), IntVarEnc::Bin(b) => b.dom(), - IntVarEnc::Const(c) => interval_set!(*c..(*c + C::one())), + &IntVarEnc::Const(c) => interval_set!(c..(c + 1)), } } - pub(crate) fn consistent>(&self, db: &mut DB) -> Result { + pub(crate) fn consistent(&self, db: &mut DB) -> Result { match self { IntVarEnc::Ord(o) => o.consistent(db), IntVarEnc::Bin(b) => b.consistent(db), @@ -771,32 +736,24 @@ impl IntVarEnc { } } - #[allow(dead_code)] - pub(crate) fn lb(&self) -> C { + pub(crate) fn lb(&self) -> Coeff { match self { IntVarEnc::Ord(o) => o.lb(), IntVarEnc::Bin(b) => b.lb(), IntVarEnc::Const(c) => *c, - // _ => self.dom().range().unwrap().start - C::one(), + // _ => self.dom().range().unwrap().start - 1, } } - pub(crate) fn ub(&self) -> C { + pub(crate) fn ub(&self) -> Coeff { match self { IntVarEnc::Ord(o) => o.ub(), IntVarEnc::Bin(b) => b.ub(), IntVarEnc::Const(c) => *c, - // _ => self.dom().range().unwrap().end - C::one(), + // _ => self.dom().range().unwrap().end - 1, } } - pub(crate) fn as_lin_exp(&self) -> LinExp { - match self { - IntVarEnc::Ord(o) => o.as_lin_exp(), - IntVarEnc::Bin(b) => b.as_lin_exp(), - IntVarEnc::Const(c) => LinExp::new().add_constant(*c), - } - } /// Return number of lits in encoding #[allow(dead_code)] pub(crate) fn lits(&self) -> usize { @@ -808,19 +765,29 @@ impl IntVarEnc { } } -impl From> for IntVarEnc { - fn from(b: IntVarBin) -> Self { +impl From<&IntVarEnc> for LinExp { + fn from(value: &IntVarEnc) -> Self { + match value { + IntVarEnc::Ord(o) => o.into(), + IntVarEnc::Bin(b) => b.into(), + &IntVarEnc::Const(c) => c.into(), + } + } +} + +impl From for IntVarEnc { + fn from(b: IntVarBin) -> Self { Self::Bin(b) } } -impl From> for IntVarEnc { - fn from(o: IntVarOrd) -> Self { +impl From for IntVarEnc { + fn from(o: IntVarOrd) -> Self { Self::Ord(o) } } -impl fmt::Display for IntVarEnc { +impl fmt::Display for IntVarEnc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { IntVarEnc::Ord(o) => o.fmt(f), @@ -829,14 +796,14 @@ impl fmt::Display for IntVarEnc { } } } -pub(crate) fn ord_plus_ord_le_ord_sparse_dom( - a: Vec, - b: Vec, - l: C, - u: C, -) -> IntervalSet { +pub(crate) fn ord_plus_ord_le_ord_sparse_dom( + a: Vec, + b: Vec, + l: Coeff, + u: Coeff, +) -> IntervalSet { // TODO optimize by dedup (if already sorted?) - HashSet::::from_iter(a.iter().flat_map(|a| { + HashSet::::from_iter(a.iter().flat_map(|a| { b.iter().filter_map(move |b| { // TODO refactor: use then_some when stabilized if *a + *b >= l && *a + *b <= u { @@ -849,6 +816,6 @@ pub(crate) fn ord_plus_ord_le_ord_sparse_dom( .into_iter() .sorted() .tuple_windows() - .map(|(a, b)| (a + C::one())..(b + C::one())) + .map(|(a, b)| (a + 1)..(b + 1)) .collect::>() } diff --git a/crates/pindakaas/src/int/model.rs b/crates/pindakaas/src/int/model.rs index b6fcd9515..2d5164b53 100644 --- a/crates/pindakaas/src/int/model.rs +++ b/crates/pindakaas/src/int/model.rs @@ -1,5 +1,3 @@ -use crate::Encoder; -use itertools::Itertools; use std::{ cell::RefCell, collections::{BTreeSet, HashMap}, @@ -8,18 +6,18 @@ use std::{ }; use iset::IntervalMap; +use itertools::Itertools; +use super::{display_dom, enc::GROUND_BINARY_AT_LB, IntVarBin, IntVarEnc, IntVarOrd}; use crate::{ int::{TernLeConstraint, TernLeEncoder}, - ClauseDatabase, Coefficient, LimitComp, Literal, + ClauseDatabase, Coeff, Encoder, LimitComp, Lit, }; -use super::{display_dom, enc::GROUND_BINARY_AT_LB, IntVarBin, IntVarEnc, IntVarOrd}; - -#[derive(Debug)] -pub(crate) struct Model { - vars: HashMap>, - pub(crate) cons: Vec>, +#[derive(Debug, Default)] +pub(crate) struct Model { + vars: HashMap, + pub(crate) cons: Vec, var_ids: usize, } @@ -33,7 +31,7 @@ pub enum Consistency { Domain, } -impl Display for Model { +impl Display for Model { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for con in &self.cons { writeln!(f, "{}", con)?; @@ -42,11 +40,11 @@ impl Display for Model { } } -impl Display for Lin { +impl Display for Lin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let disp_x = |x: &(C, Rc>>)| -> String { + let disp_x = |x: &(Coeff, Rc>)| -> String { let (coef, x) = x; - assert!(coef.abs() == C::one()); + assert!(coef.abs() == 1); let x = x.borrow(); format!("{}", x) @@ -62,28 +60,20 @@ impl Display for Lin { } } -impl fmt::Display for IntVar { +impl fmt::Display for IntVar { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "x{} ∈ {}", self.id, display_dom(&self.dom)) } } -impl Model { - pub fn new() -> Self { - Self { - vars: HashMap::new(), - cons: vec![], - var_ids: 0, - } - } - - pub fn add_int_var_enc(&mut self, x: IntVarEnc) -> IntVar { - let var = self.new_var(x.dom().iter(..).map(|d| d.end - C::one()).collect(), false); +impl Model { + pub fn add_int_var_enc(&mut self, x: IntVarEnc) -> IntVar { + let var = self.new_var(x.dom().iter(..).map(|d| d.end - 1).collect(), false); self.vars.insert(var.id, x); var } - pub fn new_var(&mut self, dom: BTreeSet, add_consistency: bool) -> IntVar { + pub fn new_var(&mut self, dom: BTreeSet, add_consistency: bool) -> IntVar { self.var_ids += 1; IntVar { id: self.var_ids, @@ -93,22 +83,20 @@ impl Model { } } - pub fn new_constant(&mut self, c: C) -> IntVar { + pub fn new_constant(&mut self, c: Coeff) -> IntVar { self.new_var(BTreeSet::from([c]), false) } - pub fn encode>( + pub fn encode( &mut self, db: &mut DB, - cutoff: Option, + cutoff: Option, ) -> crate::Result { let mut all_views = HashMap::new(); for con in &self.cons { let Lin { xs, cmp } = con; assert!( - con.xs.len() == 3 - && con.xs.iter().map(|(c, _)| c).collect::>() - == [&C::one(), &C::one(), &-C::one()] + con.xs.len() == 3 && con.xs.iter().map(|(c, _)| c).collect_vec() == [&1, &1, &-1] ); for (_, x) in xs { @@ -148,43 +136,37 @@ impl Model { .any(|(_, x)| changed.contains(&x.borrow().id)) .then_some(i) }) - .collect::>(); + .collect_vec(); queue.append(&mut cons); } } } #[derive(Debug)] -pub struct Lin { - pub(crate) xs: Vec<(C, Rc>>)>, +pub struct Lin { + pub(crate) xs: Vec<(Coeff, Rc>)>, pub(crate) cmp: LimitComp, } -impl Lin { +impl Lin { pub fn tern( - x: Rc>>, - y: Rc>>, + x: Rc>, + y: Rc>, cmp: LimitComp, - z: Rc>>, + z: Rc>, ) -> Self { Lin { - xs: vec![(C::one(), x), (C::one(), y), (-C::one(), z)], + xs: vec![(1, x), (1, y), (-1, z)], cmp, } } - pub fn lb(&self) -> C { - self.xs - .iter() - .map(|(c, x)| x.borrow().lb(c)) - .fold(C::zero(), |a, b| a + b) + pub fn lb(&self) -> Coeff { + self.xs.iter().map(|(c, x)| x.borrow().lb(c)).sum::() } - pub fn ub(&self) -> C { - self.xs - .iter() - .map(|(c, x)| x.borrow().ub(c)) - .fold(C::zero(), |a, b| a + b) + pub fn ub(&self) -> Coeff { + self.xs.iter().map(|(c, x)| x.borrow().ub(c)).sum::() } pub(crate) fn propagate(&mut self, consistency: &Consistency) -> Vec { @@ -268,20 +250,17 @@ impl Lin { .xs .iter() .enumerate() - .filter_map(|(j, (c_j, x_j))| { - (i != j).then(|| { - x_j.borrow() - .dom - .iter() - .map(|d_j_k| *c_j * *d_j_k) - .collect::>() - }) + .filter(|&(j, (_c_j, _x_j))| (i != j)) + .map(|(_j, (c_j, x_j))| { + x_j.borrow() + .dom + .iter() + .map(|d_j_k| *c_j * *d_j_k) + .collect_vec() }) .multi_cartesian_product() - .any(|rs| { - *c_i * *d_i + rs.into_iter().fold(C::zero(), |a, b| a + b) - == C::zero() - }) { + .any(|rs| *c_i * *d_i + rs.into_iter().sum::() == 0) + { true } else { fixpoint = false; @@ -303,20 +282,20 @@ impl Lin { // TODO perhaps id can be used by replacing vars HashMap to just vec #[derive(Debug, Clone, PartialEq, Eq)] -pub struct IntVar { +pub struct IntVar { pub(crate) id: usize, - pub(crate) dom: BTreeSet, + pub(crate) dom: BTreeSet, add_consistency: bool, - pub(crate) views: HashMap, + pub(crate) views: HashMap, } -impl IntVar { +impl IntVar { fn encode( &self, db: &mut DB, - views: &mut HashMap<(usize, C), DB::Lit>, + views: &mut HashMap<(usize, Coeff), Lit>, prefer_order: bool, - ) -> IntVarEnc { + ) -> IntVarEnc { if self.size() == 1 { IntVarEnc::Const(*self.dom.first().unwrap()) } else { @@ -327,8 +306,8 @@ impl IntVar { .sorted() .cloned() .tuple_windows() - .map(|(a, b)| (a + C::one())..(b + C::one())) - .map(|v| (v.clone(), views.get(&(self.id, v.end - C::one())).cloned())) + .map(|(a, b)| (a + 1)..(b + 1)) + .map(|v| (v.clone(), views.get(&(self.id, v.end - 1)).cloned())) .collect::>(); IntVarEnc::Ord(IntVarOrd::from_views(db, dom, "x".to_string())) } else { @@ -348,30 +327,30 @@ impl IntVar { for view in self .views .iter() - .map(|(c, (id, val))| ((*id, *val), x.geq(*c..(*c + C::one())))) + .map(|(c, (id, val))| ((*id, *val), x.geq(*c..(*c + 1)))) { // TODO refactor if !view.1.is_empty() { - views.insert(view.0, view.1[0][0].clone()); + views.insert(view.0, view.1[0][0]); } } x } } - fn ge(&mut self, bound: &C) { + fn ge(&mut self, bound: &Coeff) { self.dom = self.dom.split_off(bound); } - fn le(&mut self, bound: &C) { - self.dom.split_off(&(*bound + C::one())); + fn le(&mut self, bound: &Coeff) { + self.dom.split_off(&(*bound + 1)); } pub(crate) fn size(&self) -> usize { self.dom.len() } - pub(crate) fn lb(&self, c: &C) -> C { + pub(crate) fn lb(&self, c: &Coeff) -> Coeff { *c * *(if c.is_negative() { self.dom.last() } else { @@ -380,7 +359,7 @@ impl IntVar { .unwrap() } - pub(crate) fn ub(&self, c: &C) -> C { + pub(crate) fn ub(&self, c: &Coeff) -> Coeff { *c * *(if c.is_negative() { self.dom.first() } else { @@ -389,19 +368,20 @@ impl IntVar { .unwrap() } - pub fn required_bits(lb: C, ub: C) -> u32 { + pub fn required_bits(lb: Coeff, ub: Coeff) -> u32 { + const ZERO: Coeff = 0; if GROUND_BINARY_AT_LB { - C::zero().leading_zeros() - ((ub - lb).leading_zeros()) + ZERO.leading_zeros() - ((ub - lb).leading_zeros()) } else { - C::zero().leading_zeros() - (ub.leading_zeros()) + ZERO.leading_zeros() - (ub.leading_zeros()) } } - fn prefer_order(&self, cutoff: Option) -> bool { + fn prefer_order(&self, cutoff: Option) -> bool { match cutoff { None => true, - Some(cutoff) if cutoff == C::zero() => false, - Some(cutoff) => C::from(self.dom.len()).unwrap() < cutoff, + Some(0) => false, + Some(cutoff) => (self.dom.len() as Coeff) < cutoff, } } } diff --git a/crates/pindakaas/src/lib.rs b/crates/pindakaas/src/lib.rs index 5b38f5969..0742d665f 100755 --- a/crates/pindakaas/src/lib.rs +++ b/crates/pindakaas/src/lib.rs @@ -9,25 +9,19 @@ use std::{ clone::Clone, - cmp::Eq, - cmp::Ordering, + cmp::{Eq, Ordering}, error::Error, fmt::{self, Display}, fs::File, hash::Hash, io::{self, BufRead, BufReader, Write}, - ops::Neg, + num::NonZeroI32, + ops::Not, path::Path, - str::FromStr, }; use itertools::{Itertools, Position}; -use num::{ - traits::{NumAssignOps, NumOps}, - Integer, One, PrimInt, Signed, Zero, -}; -// mod bench; mod cardinality; mod cardinality_one; pub(crate) mod helpers; @@ -41,51 +35,123 @@ pub use cardinality::{Cardinality, SortingNetworkEncoder}; pub use cardinality_one::{CardinalityOne, LadderEncoder, PairwiseEncoder}; pub use linear::{ AdderEncoder, BddEncoder, Comparator, LimitComp, LinExp, LinVariant, Linear, LinearAggregator, - LinearConstraint, LinearEncoder, PosCoeff, SwcEncoder, TotalizerEncoder, + LinearConstraint, LinearEncoder, SwcEncoder, TotalizerEncoder, }; +use solvers::VarFactory; pub use sorted::{SortedEncoder, SortedStrategy}; -/// Literal is the super-trait for types that can be used to represent boolean -/// literals in this library. -/// -/// Literals need to implement the following trait to be used as literals: -/// -/// - [`std::clone::Clone`] to allow creating a new copy of the literal to -/// create clauses. -/// -/// - [`std::cmp::Eq`] and [`std::hash::Hash`] to allow PB constraints to be -/// simplified -pub trait Literal: fmt::Debug + Clone + Eq + Hash { - /// Returns the negation of the literal in its current form - fn negate(&self) -> Self; - /// Returns `true` when the literal a negated boolean variable. - fn is_negated(&self) -> bool; - /// Returns a non-negated version of the literal - fn var(&self) -> Self { - if self.is_negated() { - self.negate() - } else { - self.clone() +use crate::trace::subscript_number; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Var(NonZeroI32); + +impl Var { + fn next_var(&self) -> Option { + let v = self.0.get(); + if let Some(v) = v.checked_add(1) { + return Some(Var(v.try_into().unwrap())); } + None + } +} + +impl Not for Var { + type Output = Lit; + fn not(self) -> Self::Output { + !Lit::from(self) + } +} +impl Not for &Var { + type Output = Lit; + fn not(self) -> Self::Output { + !*self + } +} + +impl Display for Var { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "x{}", subscript_number(self.0.get() as usize).format("")) + } +} + +impl From for NonZeroI32 { + fn from(val: Var) -> Self { + val.0 } } -impl + LitMarker> Literal for T { - fn is_negated(&self) -> bool { - self.is_negative() +impl From for i32 { + fn from(val: Var) -> Self { + val.0.get() } - fn negate(&self) -> Self { - -(self.clone()) +} + +/// Literal is type that can be use to represent Boolean decision variables and +/// their negations +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Lit(NonZeroI32); + +impl Lit { + pub fn var(&self) -> Var { + Var(self.0.abs()) + } + pub fn is_negated(&self) -> bool { + self.0.is_negative() + } +} + +impl Not for Lit { + type Output = Lit; + fn not(self) -> Self::Output { + Lit(-self.0) } - fn var(&self) -> Self { - self.abs() +} +impl Not for &Lit { + type Output = Lit; + fn not(self) -> Self::Output { + !(*self) + } +} + +impl PartialOrd for Lit { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for Lit { + fn cmp(&self, other: &Self) -> Ordering { + match self.var().cmp(&other.var()) { + std::cmp::Ordering::Equal => (self.is_negated()).cmp(&other.is_negated()), + r => r, + } + } +} + +impl From for Lit { + fn from(value: Var) -> Self { + Lit(value.0) + } +} +impl From for NonZeroI32 { + fn from(val: Lit) -> Self { + val.0 + } +} +impl From for i32 { + fn from(val: Lit) -> Self { + val.0.get() + } +} + +impl Display for Lit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}", + if self.is_negated() { "¬" } else { "" }, + self.var() + ) } } -pub(crate) trait LitMarker {} -impl LitMarker for i8 {} -impl LitMarker for i16 {} -impl LitMarker for i32 {} -impl LitMarker for i64 {} -impl LitMarker for i128 {} /// Unsatisfiable is an error type returned when the problem being encoded is /// found to be inconsistent. @@ -102,6 +168,14 @@ impl fmt::Display for Unsatisfiable { /// an empty value, or the [`Unsatisfiable`] error type. pub type Result = std::result::Result; +/// A function that gives the valuation/truth-value for a given literal in the +/// current solution/model. +/// +/// Note that the function can return None if the model/solution is independent +/// of the given literal. +pub trait Valuation: Fn(Lit) -> Option {} +impl Option> Valuation for F {} + /// Encoder is the central trait implemented for all the encoding algorithms pub trait Encoder { fn encode(&mut self, db: &mut DB, con: &Constraint) -> Result; @@ -111,31 +185,22 @@ pub trait Encoder { /// [`Checker::check`] methods checks whether an assignment (often referred to /// as a model) satisfies the constraint. pub trait Checker { - type Lit: Literal; - /// Check whether the constraint represented by the object is violated. /// /// - The method returns [`Result::Ok`] when the assignment satisfies /// the constraint, /// - it returns [`Unsatisfiable`] when the assignment violates the - /// constraint, - /// - and it return [`Incomplete`] when not all the literals used in the - /// constraint have been assigned. - fn check(&self, solution: &[Self::Lit]) -> Result<(), CheckError>; - - /// Returns assignment of `lit` in `solution` - fn assign<'a>(lit: &'a Self::Lit, solution: &'a [Self::Lit]) -> &'a Self::Lit { - solution.iter().find(|x| x.var() == lit.var()).unwrap_or_else(|| panic!("Could not find lit {lit:?} in solution {solution:?}; perhaps this variable did not occur in any clause")) - } + /// constraint + fn check(&self, value: F) -> Result<(), CheckError>; } /// Incomplete is a error type returned by a [`Checker`] type when the #[derive(Clone, Debug, PartialEq, Eq, PartialOrd)] -pub struct Incomplete { +pub struct Incomplete { missing: Box<[Lit]>, } -impl Error for Incomplete {} -impl fmt::Display for Incomplete { +impl Error for Incomplete {} +impl fmt::Display for Incomplete { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.missing.len() { 0 => write!(f, "Unknown literal is unasssigned"), @@ -158,54 +223,41 @@ impl fmt::Display for Incomplete { /// Enumerated type of #[derive(Clone, Debug, PartialEq, Eq, PartialOrd)] -pub enum CheckError { +pub enum CheckError { Unsatisfiable(Unsatisfiable), - Incomplete(Incomplete), Fail(String), } -impl Error for CheckError {} -impl fmt::Display for CheckError { +impl Error for CheckError {} +impl fmt::Display for CheckError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { CheckError::Fail(err) => err.fmt(f), CheckError::Unsatisfiable(err) => err.fmt(f), - CheckError::Incomplete(err) => err.fmt(f), } } } - -/// Coefficient in PB constraints are represented by types that implement the -/// `Coefficient` constraint. -pub trait Coefficient: - Signed + Integer + PrimInt + NumAssignOps + NumOps + Hash + Default + fmt::Debug + fmt::Display -{ -} -impl< - T: Signed - + PrimInt - + Integer - + NumAssignOps - + NumOps - + Hash - + Default - + fmt::Debug - + fmt::Display, - > Coefficient for T -{ +impl From for CheckError { + fn from(value: Unsatisfiable) -> Self { + Self::Unsatisfiable(value) + } } +/// Coeff is a type alias used for the number type used to represent the +/// coefficients in pseudo-Boolean constraints and expression. +pub type Coeff = i64; + /// IntEncoding is a enumerated type use to represent Boolean encodings of /// integer variables within this library -pub enum IntEncoding<'a, Lit: Literal, C: Coefficient> { +pub enum IntEncoding<'a> { /// The Direct variant represents a integer variable encoded using domain /// or direct encoding of an integer variable. Each given Boolean literal /// represents whether the integer takes the associated value (i.e., X = /// (first+i) ↔ vals\[i\]). - Direct { first: C, vals: &'a [Lit] }, + Direct { first: Coeff, vals: &'a [Lit] }, /// The Order variant represents a integer variable using an order /// encoding. Each given Boolean literal represents whether the integer /// is bigger than the associated value(i.e., X > (first+i) ↔ vals\[i\]). - Order { first: C, vals: &'a [Lit] }, + Order { first: Coeff, vals: &'a [Lit] }, /// The Log variant represents a integer variable using a two's complement /// encoding. The sum of the Boolean literals multiplied by their /// associated power of two represents value of the integer (i.e., X = ∑ @@ -220,39 +272,35 @@ pub enum IntEncoding<'a, Lit: Literal, C: Coefficient> { /// To satisfy the trait, the type must implement a [`Self::add_clause`] method /// and a [`Self::new_var`] method. pub trait ClauseDatabase { - /// Type used to represent a Boolean literal in the constraint input and - /// generated clauses. - type Lit: Literal; /// Method to be used to receive a new Boolean variable that can be used in /// the encoding of a constraint. - fn new_var(&mut self) -> Self::Lit; + fn new_var(&mut self) -> Lit; /// Add a clause to the `ClauseDatabase`. The sink is allowed to return /// [`Unsatisfiable`] when the collection of clauses has been *proven* to /// be unsatisfiable. This is used as a signal to the encoder that any /// subsequent encoding effort can be abandoned. - fn add_clause(&mut self, cl: &[Self::Lit]) -> Result; + fn add_clause>(&mut self, cl: I) -> Result; } // TODO: Add usage and think about interface pub struct ConditionalDatabase<'a, DB: ClauseDatabase> { db: &'a mut DB, - conditions: &'a [DB::Lit], + conditions: &'a [Lit], } impl<'a, DB: ClauseDatabase> ConditionalDatabase<'a, DB> { - pub fn new(db: &'a mut DB, conditions: &'a [DB::Lit]) -> Self { + pub fn new(db: &'a mut DB, conditions: &'a [Lit]) -> Self { Self { db, conditions } } } impl<'a, DB: ClauseDatabase> ClauseDatabase for ConditionalDatabase<'a, DB> { - type Lit = DB::Lit; - fn new_var(&mut self) -> Self::Lit { + fn new_var(&mut self) -> Lit { self.db.new_var() } - fn add_clause(&mut self, cl: &[Self::Lit]) -> Result { - self.db.add_clause(&Vec::from_iter( - self.conditions.iter().chain(cl.iter()).cloned(), - )) + + fn add_clause>(&mut self, cl: I) -> Result { + let chain = self.conditions.iter().copied().chain(cl); + self.db.add_clause(chain) } } @@ -260,10 +308,10 @@ impl<'a, DB: ClauseDatabase> ClauseDatabase for ConditionalDatabase<'a, DB> { /// /// It can be used to create formulas manually, to store the results from /// encoders, read formulas from a file, and write them to a file -#[derive(Clone, Debug)] -pub struct Cnf { - /// The last variable created by [`new_var`] - last_var: Lit, +#[derive(Clone, Debug, Default)] +pub struct Cnf { + /// The variable factory used by [`new_var`] + nvar: VarFactory, /// The literals from *all* clauses lits: Vec, /// The size *for each* clause @@ -273,17 +321,17 @@ pub struct Cnf { /// A representation for a weighted CNF formula /// /// Same as CNF, but every clause has an optional weight. Otherwise, it is a hard clause. -#[derive(Clone, Debug)] -pub struct Wcnf { +#[derive(Clone, Debug, Default)] +pub struct Wcnf { /// The CNF formula - cnf: Cnf, + cnf: Cnf, /// The weight for every clause - weights: Vec>, + weights: Vec>, // TODO this can be optimised, for example by having all weighted clauses at the start/end } // TODO not sure how to handle converting num_var for vars() -impl Cnf { +impl Cnf { /// Store CNF formula at given path in DIMACS format /// /// File will optionally be prefaced by a given comment @@ -297,8 +345,8 @@ impl Cnf { write!(file, "{self}") } - pub fn variables(&self) -> Lit { - self.last_var.clone() + pub fn variables(&self) -> usize { + self.nvar.emited_vars() } pub fn clauses(&self) -> usize { @@ -310,15 +358,15 @@ impl Cnf { } } -impl From> for Wcnf { - fn from(cnf: Cnf) -> Self { +impl From for Wcnf { + fn from(cnf: Cnf) -> Self { let weights = std::iter::repeat(None).take(cnf.clauses()).collect(); Wcnf { cnf, weights } } } -impl From> for Cnf { +impl From for Cnf { // TODO implement iter for Cnf - fn from(wcnf: Wcnf) -> Self { + fn from(wcnf: Wcnf) -> Self { let mut start = 0; let lits_size = wcnf .cnf @@ -334,7 +382,7 @@ impl From> for .skip(start) .take(*size) .cloned() - .collect::>(), + .collect_vec(), size, ); start += size; @@ -344,28 +392,25 @@ impl From> for None } }) - .collect::>(); + .collect_vec(); let lits = lits_size .iter() .flat_map(|lit_size| lit_size.0.clone()) .collect(); - let size = lits_size - .iter() - .map(|lit_size| *lit_size.1) - .collect::>(); + let size = lits_size.iter().map(|lit_size| *lit_size.1).collect_vec(); Self { + nvar: wcnf.cnf.nvar, lits, - last_var: wcnf.cnf.last_var, size, } } } -impl Display for Wcnf { +impl Display for Wcnf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let num_var = &self.cnf.last_var; + let num_var = &self.cnf.nvar.emited_vars(); let num_clauses = self.cnf.size.len(); - let top = self.weights.iter().flatten().fold(C::one(), |a, b| a + *b); + let top = self.weights.iter().flatten().fold(1, |a, b| a + *b); writeln!(f, "p wcnf {num_var} {num_clauses} {top}")?; let mut start = 0; for (size, weight) in self.cnf.size.iter().zip(self.weights.iter()) { @@ -382,7 +427,7 @@ impl Display for Wcnf Wcnf { +impl Wcnf { /// Store WCNF formula at given path in WDIMACS format /// /// File will optionally be prefaced by a given comment @@ -396,7 +441,7 @@ impl Wcnf { write!(file, "{self}") } - pub fn variables(&self) -> Lit { + pub fn variables(&self) -> usize { self.cnf.variables() } @@ -409,9 +454,9 @@ impl Wcnf { } } -impl Display for Cnf { +impl Display for Cnf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let num_var = &self.last_var; + let num_var = &self.variables(); let num_clauses = self.size.len(); writeln!(f, "p cnf {num_var} {num_clauses}")?; let mut start = 0; @@ -427,23 +472,20 @@ impl Display for Cnf { } } -enum Dimacs { - Cnf(Cnf), - Wcnf(Wcnf), +enum Dimacs { + Cnf(Cnf), + Wcnf(Wcnf), } -fn parse_dimacs_file( - path: &Path, - expect_wcnf: bool, -) -> Result, io::Error> { +fn parse_dimacs_file(path: &Path, expect_wcnf: bool) -> Result { let file = File::open(path)?; let mut had_header = false; - let mut wcnf = Wcnf::::default(); + let mut wcnf = Wcnf::default(); let mut cl: Vec = Vec::new(); - let mut top: Option = None; - let weight: Option = None; + let mut top: Option = None; + let weight: Option = None; for line in BufReader::new(file).lines() { match line { @@ -451,7 +493,7 @@ fn parse_dimacs_file { for seg in line.split(' ') { if expect_wcnf { - if let Ok(weight) = seg.parse::() { + if let Ok(weight) = seg.parse::() { wcnf.weights.push(match weight.cmp(&top.unwrap()) { Ordering::Less => Some(weight), Ordering::Equal => None, @@ -464,13 +506,12 @@ fn parse_dimacs_file() { - if lit == Lit::zero() { - wcnf.add_weighted_clause(&cl, weight) + if let Ok(lit) = seg.parse::() { + if lit == 0 { + wcnf.add_weighted_clause(cl.drain(..), weight) .expect("CNF::add_clause does not return Unsatisfiable"); - cl.clear(); } else { - cl.push(lit); + cl.push(Lit(NonZeroI32::new(lit).unwrap())); } } } @@ -491,12 +532,14 @@ fn parse_dimacs_file().map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidInput, + "unable to parse number of variables", + ) + })?)), + }; // parse number of clauses let num_clauses: usize = vec[3].parse().map_err(|_| { io::Error::new( @@ -524,95 +567,76 @@ fn parse_dimacs_file::from(wcnf))) + Ok(Dimacs::Cnf(Cnf::from(wcnf))) } } -impl Cnf { +impl Cnf { /// Read a CNF formula from a file formatted in the DIMACS CNF format pub fn from_file(path: &Path) -> Result { - match parse_dimacs_file::(path, false)? { + match parse_dimacs_file(path, false)? { Dimacs::Cnf(cnf) => Ok(cnf), _ => unreachable!(), } } } -impl Wcnf { +impl Wcnf { /// Read a WCNF formula from a file formatted in the (W)DIMACS WCNF format pub fn from_file(path: &Path) -> Result { - match parse_dimacs_file::(path, true)? { + match parse_dimacs_file(path, true)? { Dimacs::Wcnf(wcnf) => Ok(wcnf), _ => unreachable!(), } } } -impl Default for Wcnf { - fn default() -> Self { - Self { - cnf: Cnf::::default(), - weights: Vec::new(), - } +impl ClauseDatabase for Cnf { + fn new_var(&mut self) -> Lit { + self.nvar.next().expect("exhausted variable pool").into() } -} -impl Default for Cnf { - fn default() -> Self { - Self { - last_var: Lit::zero(), - lits: Vec::new(), - size: Vec::new(), + fn add_clause>(&mut self, cl: I) -> Result { + let size = self.lits.len(); + self.lits.extend(cl); + let len = self.lits.len() - size; + if len > 0 { + self.size.push(len); } - } -} -impl ClauseDatabase for Cnf { - type Lit = Lit; - fn new_var(&mut self) -> Self::Lit { - self.last_var = self.last_var.clone() + Lit::one(); - self.last_var.clone() - } - - fn add_clause(&mut self, cl: &[Self::Lit]) -> Result { - self.lits.reserve(cl.len()); - self.lits.extend_from_slice(cl); - self.size.push(cl.len()); Ok(()) } } -impl Wcnf { - pub fn add_weighted_clause(&mut self, cl: &[Lit], weight: Option) -> Result { +impl Wcnf { + pub fn add_weighted_clause>( + &mut self, + cl: I, + weight: Option, + ) -> Result { + let clauses = self.cnf.clauses(); self.cnf.add_clause(cl)?; - self.weights.push(weight); + if self.cnf.clauses() > clauses { + self.weights.push(weight); + } Ok(()) } - pub fn iter(&self) -> impl Iterator)> { + pub fn iter(&self) -> impl Iterator)> { self.cnf.iter().zip(self.weights.iter()) } } -impl ClauseDatabase for Wcnf { - type Lit = Lit; - fn new_var(&mut self) -> Self::Lit { +impl ClauseDatabase for Wcnf { + fn new_var(&mut self) -> Lit { self.cnf.new_var() } - - fn add_clause(&mut self, cl: &[Self::Lit]) -> Result { - self.cnf.add_clause(cl) + fn add_clause>(&mut self, cl: I) -> Result { + self.add_weighted_clause(cl, None) } } -impl Cnf { - /// Construct a new `Cnf` with no clauses. New variables are offset by `last_var`. - pub fn new(last_var: Lit) -> Self { - Self { - last_var, - ..Self::default() - } - } - pub fn iter(&self) -> CnfIterator { +impl Cnf { + pub fn iter(&self) -> CnfIterator<'_> { CnfIterator { lits: &self.lits, size: self.size.iter(), @@ -620,12 +644,12 @@ impl Cnf { } } } -pub struct CnfIterator<'a, Lit: Literal + Zero + One> { +pub struct CnfIterator<'a> { lits: &'a Vec, size: std::slice::Iter<'a, usize>, index: usize, } -impl<'a, Lit: Literal + Zero + One> Iterator for CnfIterator<'a, Lit> { +impl<'a> Iterator for CnfIterator<'a> { type Item = &'a [Lit]; fn next(&mut self) -> Option { @@ -648,45 +672,13 @@ impl<'a, Lit: Literal + Zero + One> Iterator for CnfIterator<'a, Lit> { #[cfg(test)] mod tests { - #[cfg(feature = "trace")] - use traced_test::test; - - use super::*; - - #[test] - fn test_int_literals() { - assert!(is_lit(1i8)); - assert!(is_lit(1i16)); - assert!(is_lit(1i32)); - assert!(is_lit(1i64)); - assert!(is_lit(1i128)); - } - fn is_lit(_: T) -> bool { - true - } - - #[test] - fn test_coefficients() { - assert!(is_coeff(1i8)); - assert!(is_coeff(1i16)); - assert!(is_coeff(1i32)); - assert!(is_coeff(1i64)); - assert!(is_coeff(1i128)); - } - fn is_coeff(_: T) -> bool { - true - } - - #[test] - fn test_positive_coefficients() { - assert!(is_poscoeff(1i8)); - assert!(is_poscoeff(1i16)); - assert!(is_poscoeff(1i32)); - assert!(is_poscoeff(1i64)); - assert!(is_poscoeff(1i128)); - } - fn is_poscoeff(c: T) -> bool { - let _ = PosCoeff::from(c); - true + use std::num::NonZeroI32; + + use crate::Lit; + + impl From for Lit { + fn from(value: i32) -> Self { + Lit(NonZeroI32::new(value).expect("cannot create literal with value zero")) + } } } diff --git a/crates/pindakaas/src/linear.rs b/crates/pindakaas/src/linear.rs index 208fded08..3d5069c19 100644 --- a/crates/pindakaas/src/linear.rs +++ b/crates/pindakaas/src/linear.rs @@ -1,12 +1,12 @@ use std::{ collections::VecDeque, - fmt, + fmt::{self, Display}, ops::{Add, AddAssign, Deref, DerefMut, Mul, MulAssign}, }; use crate::{ - int::IntVarEnc, Cardinality, CardinalityOne, CheckError, Checker, ClauseDatabase, Coefficient, - Encoder, IntEncoding, Literal, PairwiseEncoder, Result, Unsatisfiable, + Cardinality, CardinalityOne, CheckError, Checker, ClauseDatabase, Coeff, Encoder, IntEncoding, + Lit, PairwiseEncoder, Result, Unsatisfiable, Valuation, }; mod adder; @@ -19,52 +19,63 @@ pub use adder::AdderEncoder; pub(crate) use adder::{lex_geq_const, lex_leq_const, log_enc_add, log_enc_add_}; pub use aggregator::LinearAggregator; pub use bdd::BddEncoder; +use itertools::Itertools; pub use swc::SwcEncoder; pub use totalizer::TotalizerEncoder; -/// PosCoeff is a container used when coefficients that are guaranteed -/// by the programmer to be 0 or greater. -/// -/// # Warning -/// The [`From`] implementation of this type will panic if the -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] -pub struct PosCoeff(C); -impl From for PosCoeff { - fn from(c: C) -> Self { - assert!( - !c.is_negative(), - "could not create PosCoeff, value was found to be negative" - ); +/// PosCoeff is a type for coefficients that are guaranteed by the programmer to +/// be 0 or greater. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct PosCoeff(Coeff); + +impl PosCoeff { + pub fn new(c: Coeff) -> Self { + if c < 0 { + panic!("cannot create a PosCoeff with a negative value") + } Self(c) } } -impl Deref for PosCoeff { - type Target = C; + +impl From for Coeff { + fn from(val: PosCoeff) -> Self { + val.0 + } +} +impl Deref for PosCoeff { + type Target = Coeff; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut for PosCoeff { +impl DerefMut for PosCoeff { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } + +impl Display for PosCoeff { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + #[derive(Debug)] -pub enum LinVariant { - Linear(Linear), - Cardinality(Cardinality), - CardinalityOne(CardinalityOne), +pub enum LinVariant { + Linear(Linear), + Cardinality(Cardinality), + CardinalityOne(CardinalityOne), Trivial, } #[derive(Debug, Clone)] -pub struct Linear { - pub(crate) terms: Vec>>, +pub struct Linear { + pub(crate) terms: Vec, pub cmp: LimitComp, - pub(crate) k: PosCoeff, + pub(crate) k: PosCoeff, } -impl Linear { +impl Linear { #[cfg(feature = "trace")] pub(crate) fn trace_print(&self) -> String { use crate::trace::trace_print_lit; @@ -84,8 +95,8 @@ impl Linear { format!("{x} {op} {:?}", *self.k) } - pub fn set_k(&mut self, k: C) { - self.k = k.into(); + pub fn set_k(&mut self, k: Coeff) { + self.k = PosCoeff::new(k); } pub fn len(&self) -> usize { @@ -97,39 +108,37 @@ impl Linear { } } -impl From> for Linear { - fn from(card: Cardinality) -> Self { +impl From for Linear { + fn from(card: Cardinality) -> Self { Self { terms: card .lits .into_iter() - .map(|l| Part::Amo(vec![(l, C::one().into())])) + .map(|l| Part::Amo(vec![(l, PosCoeff::new(1))])) .collect(), cmp: card.cmp, k: card.k, } } } -impl From> for Linear { - fn from(amo: CardinalityOne) -> Self { +impl From for Linear { + fn from(amo: CardinalityOne) -> Self { Self::from(Cardinality::from(amo)) } } // Automatically implement Cardinality encoding when you can encode Linear constraints -impl> + LinMarker> - Encoder> for Enc -{ - fn encode(&mut self, db: &mut DB, con: &Cardinality) -> crate::Result { - self.encode(db, &Linear::::from(con.clone())) +impl + LinMarker> Encoder for Enc { + fn encode(&mut self, db: &mut DB, con: &Cardinality) -> crate::Result { + self.encode(db, &Linear::from(con.clone())) } } // local marker trait, to ensure the previous definition only applies within this crate pub(crate) trait LinMarker {} impl LinMarker for AdderEncoder {} -impl LinMarker for TotalizerEncoder {} -impl LinMarker for SwcEncoder {} -impl LinMarker for BddEncoder {} +impl LinMarker for TotalizerEncoder {} +impl LinMarker for SwcEncoder {} +impl LinMarker for BddEncoder {} // TODO how can we support both Part(itions) of "terms" ( for pb // constraints) and just lits () for AMK/AMO's? @@ -137,10 +146,10 @@ impl LinMarker for BddEncoder {} // TODO add EO, and probably something for Unconstrained // TODO this can probably follow the same structure as LinExp #[derive(Debug, Clone)] -pub(crate) enum Part { - Amo(Vec<(Lit, C)>), - Ic(Vec<(Lit, C)>), - Dom(Vec<(Lit, C)>, C, C), +pub(crate) enum Part { + Amo(Vec<(Lit, PosCoeff)>), + Ic(Vec<(Lit, PosCoeff)>), + Dom(Vec<(Lit, PosCoeff)>, PosCoeff, PosCoeff), } #[derive(Clone, Debug, Eq, PartialEq)] @@ -159,27 +168,39 @@ impl fmt::Display for LimitComp { } #[derive(Clone, Debug)] -pub struct LinExp { +pub struct LinExp { /// All terms of the pseudo-Boolean linear expression - terms: VecDeque<(Lit, C)>, + terms: VecDeque<(Lit, Coeff)>, /// Number of unconstrained terms (located at the front of `terms`) num_free: usize, /// Constraints placed on different terms, and the number of terms involved in the constraint - constraints: Vec<(Constraint, usize)>, + constraints: Vec<(Constraint, usize)>, /// Additive constant - pub add: C, + pub add: Coeff, /// Multiplicative contant - pub mult: C, + pub mult: Coeff, +} + +impl Default for LinExp { + fn default() -> Self { + Self { + terms: Default::default(), + num_free: 0, + constraints: Default::default(), + add: 0, + mult: 1, + } + } } #[derive(Debug, Clone)] -pub struct LinearConstraint { +pub struct LinearConstraint { /// Expression being constrained - pub exp: LinExp, + pub exp: LinExp, /// Comparator when exp is on the left hand side and k is on the right hand side pub cmp: Comparator, /// Coefficient providing the upper bound or lower bound to exp, or both - pub k: C, + pub k: Coeff, } impl From for Comparator { @@ -191,14 +212,14 @@ impl From for Comparator { } } -impl From> for LinearConstraint { - fn from(lin: Linear) -> Self { +impl From for LinearConstraint { + fn from(lin: Linear) -> Self { LinearConstraint { exp: LinExp::from_terms( lin.terms .iter() - .flat_map(|part| part.iter().map(|(l, c)| (l.clone(), *c.clone()))) - .collect::>() + .flat_map(|part| part.into_iter().map(|&(l, c)| (l, *c))) + .collect_vec() .as_slice(), ), cmp: lin.cmp.into(), @@ -207,8 +228,8 @@ impl From> for LinearConstraint LinearConstraint { - pub fn new(exp: LinExp, cmp: Comparator, k: C) -> Self { +impl LinearConstraint { + pub fn new(exp: LinExp, cmp: Comparator, k: Coeff) -> Self { Self { exp, cmp, k } } @@ -236,16 +257,9 @@ impl LinearConstraint { } } -impl From<&IntVarEnc> for LinExp { - fn from(x: &IntVarEnc) -> Self { - x.as_lin_exp() - } -} - -impl Checker for LinearConstraint { - type Lit = Lit; - fn check(&self, solution: &[Self::Lit]) -> Result<(), CheckError> { - let lhs = self.exp.assign(solution)?; +impl Checker for LinearConstraint { + fn check(&self, value: F) -> Result<(), CheckError> { + let lhs = self.exp.value(value)?; if match self.cmp { Comparator::LessEq => lhs <= self.k, Comparator::Equal => lhs == self.k, @@ -266,14 +280,22 @@ pub enum Comparator { } #[derive(Debug, Clone)] -pub(crate) enum Constraint { +pub(crate) enum Constraint { AtMostOne, ImplicationChain, - Domain { lb: C, ub: C }, + Domain { lb: Coeff, ub: Coeff }, } -impl Part { - fn iter(&self) -> std::slice::Iter<(Lit, C)> { +impl Part { + pub fn iter(&self) -> impl Iterator { + self.into_iter() + } +} +impl<'a> IntoIterator for &'a Part { + type Item = &'a (Lit, PosCoeff); + type IntoIter = std::slice::Iter<'a, (Lit, PosCoeff)>; + + fn into_iter(self) -> Self::IntoIter { match self { Part::Amo(terms) => terms.iter(), Part::Ic(terms) => terms.iter(), @@ -282,15 +304,13 @@ impl Part { } } -impl LinExp { - pub fn new() -> Self { - Self { - ..Default::default() - } - } - - pub fn from_slices(weights: &[C], lits: &[Lit]) -> Self { - assert!(weights.len() == lits.len(), ""); +impl LinExp { + pub fn from_slices(weights: &[Coeff], lits: &[Lit]) -> Self { + assert_eq!( + weights.len(), + lits.len(), + "the number of weights and literals must be equal" + ); Self { terms: lits.iter().cloned().zip(weights.iter().cloned()).collect(), num_free: lits.len(), @@ -298,7 +318,7 @@ impl LinExp { } } - pub fn from_terms(terms: &[(Lit, C)]) -> Self { + pub fn from_terms(terms: &[(Lit, Coeff)]) -> Self { Self { terms: terms.iter().cloned().collect(), num_free: terms.len(), @@ -308,9 +328,9 @@ impl LinExp { /// Add multiple terms to the linear expression of which at most one /// can be chosen - pub fn add_choice(mut self, choice: &[(Lit, C)]) -> Self { + pub fn add_choice(mut self, choice: &[(Lit, Coeff)]) -> Self { if let [term] = choice { - self += term.clone(); + self += *term; } else { self.terms.extend(choice.iter().cloned()); self.constraints.push((Constraint::AtMostOne, choice.len())) @@ -320,9 +340,9 @@ impl LinExp { /// Add multiple terms to the linear expression where the literal /// in each term is implied by the literal in the consecutive term - pub fn add_chain(mut self, chain: &[(Lit, C)]) -> Self { + pub fn add_chain(mut self, chain: &[(Lit, Coeff)]) -> Self { if let [term] = chain { - self += term.clone(); + self += *term; } else { self.terms.extend(chain.iter().cloned()); self.constraints @@ -331,26 +351,31 @@ impl LinExp { self } - pub fn add_constant(mut self, k: C) -> Self { + pub fn add_constant(mut self, k: Coeff) -> Self { self.add += k; self } pub fn add_lit(mut self, lit: Lit) -> Self { - self += (lit, C::one()); + self += (lit, 1); self } // TODO I'm not really happy with this interface yet... // Probably makes more sense to use something like int encodings - pub fn add_bounded_log_encoding(mut self, terms: &[(Lit, C)], lb: C, ub: C) -> Self { + pub fn add_bounded_log_encoding( + mut self, + terms: &[(Lit, Coeff)], + lb: Coeff, + ub: Coeff, + ) -> Self { self.constraints .push((Constraint::Domain { lb, ub }, terms.len())); self.terms.extend(terms.iter().cloned()); self } - pub(crate) fn iter(&self) -> impl Iterator>, Vec<&(Lit, C)>)> { + pub(crate) fn iter(&self) -> impl Iterator, Vec<&(Lit, Coeff)>)> { let mut it = self.terms.iter(); std::iter::once(( None, @@ -367,50 +392,37 @@ impl LinExp { })) } - pub fn terms(&self) -> std::collections::vec_deque::Iter<'_, (Lit, C)> { - self.terms.iter() - } -} - -impl Default for LinExp { - fn default() -> Self { - Self { - terms: VecDeque::new(), - num_free: 0, - constraints: Vec::new(), - add: C::zero(), - mult: C::one(), - } + pub fn terms(&self) -> impl Iterator + '_ { + self.terms.iter().copied() } } -impl From for LinExp { +impl From for LinExp { fn from(lit: Lit) -> Self { Self { - terms: VecDeque::from([(lit, C::one())]), + terms: VecDeque::from([(lit, 1)]), num_free: 1, ..Default::default() } } } -impl From<(Lit, C)> for LinExp { - fn from(term: (Lit, C)) -> Self { +impl From for LinExp { + fn from(value: Coeff) -> Self { Self { - terms: VecDeque::from([term]), - num_free: 1, + add: value, ..Default::default() } } } -impl<'a, Lit: Literal, C: Coefficient> From> for LinExp { - fn from(var: IntEncoding<'a, Lit, C>) -> Self { +impl<'a> From> for LinExp { + fn from(var: IntEncoding<'a>) -> Self { match var { IntEncoding::Direct { first, vals } => { let mut terms = VecDeque::with_capacity(vals.len()); let mut k = first; for lit in vals { - terms.push_back((lit.clone(), k)); - k += C::one(); + terms.push_back((*lit, k)); + k += 1; } Self { terms, @@ -419,21 +431,21 @@ impl<'a, Lit: Literal, C: Coefficient> From> for LinExp< } } IntEncoding::Order { first, vals } => Self { - terms: vals.iter().map(|lit| (lit.clone(), C::one())).collect(), + terms: vals.iter().map(|lit| (*lit, 1)).collect(), constraints: vec![(Constraint::ImplicationChain, vals.len())], add: first, ..Default::default() }, IntEncoding::Log { signed, bits } => { let mut terms = VecDeque::with_capacity(bits.len()); - let two = C::one() + C::one(); - let mut k = C::one(); + let two = 1 + 1; + let mut k = 1; for lit in bits { - terms.push_back((lit.clone(), k)); + terms.push_back((*lit, k)); k *= two; } if signed { - terms.back_mut().unwrap().1 *= -C::one(); + terms.back_mut().unwrap().1 *= -1; } Self { terms, @@ -445,48 +457,48 @@ impl<'a, Lit: Literal, C: Coefficient> From> for LinExp< } } -impl AddAssign<(Lit, C)> for LinExp { - fn add_assign(&mut self, rhs: (Lit, C)) { +impl AddAssign<(Lit, Coeff)> for LinExp { + fn add_assign(&mut self, rhs: (Lit, Coeff)) { self.terms.push_front(rhs); self.num_free += 1 } } -impl Add<(Lit, C)> for LinExp { - type Output = LinExp; - fn add(mut self, rhs: (Lit, C)) -> Self::Output { +impl Add<(Lit, Coeff)> for LinExp { + type Output = LinExp; + fn add(mut self, rhs: (Lit, Coeff)) -> Self::Output { self += rhs; self } } -impl<'a, Lit: Literal, C: Coefficient> AddAssign> for LinExp { - fn add_assign(&mut self, rhs: IntEncoding<'a, Lit, C>) { +impl<'a> AddAssign> for LinExp { + fn add_assign(&mut self, rhs: IntEncoding<'a>) { match rhs { IntEncoding::Direct { first, vals } => { let mut k = first; for lit in vals { - self.terms.push_back((lit.clone(), k)); - k += C::one(); + self.terms.push_back((*lit, k)); + k += 1; } self.constraints.push((Constraint::AtMostOne, vals.len())) } IntEncoding::Order { first, vals } => { for lit in vals { - self.terms.push_back((lit.clone(), C::one())) + self.terms.push_back((*lit, 1)) } self.add += first; self.constraints .push((Constraint::ImplicationChain, vals.len())) } IntEncoding::Log { signed, bits } => { - let two = C::one() + C::one(); - let mut k = C::one(); + let two = 1 + 1; + let mut k = 1; for lit in bits { - self.terms.push_front((lit.clone(), k)); + self.terms.push_front((*lit, k)); k *= two; } // TODO! if signed { - self.terms.front_mut().unwrap().1 *= -C::one(); + self.terms.front_mut().unwrap().1 *= -1; } self.num_free += bits.len(); } @@ -494,23 +506,23 @@ impl<'a, Lit: Literal, C: Coefficient> AddAssign> for Li } } -impl<'a, Lit: Literal, C: Coefficient> Add> for LinExp { - type Output = LinExp; - fn add(mut self, rhs: IntEncoding<'a, Lit, C>) -> Self::Output { +impl<'a> Add> for LinExp { + type Output = LinExp; + fn add(mut self, rhs: IntEncoding<'a>) -> Self::Output { self += rhs; self } } -impl AddAssign> for LinExp { - fn add_assign(&mut self, rhs: LinExp) { +impl AddAssign for LinExp { + fn add_assign(&mut self, rhs: LinExp) { // Multiply the current expression - if self.mult != C::one() { + if self.mult != 1 { self.add *= self.mult; for term in &mut self.terms { term.1 *= self.mult; } } - self.mult = C::one(); + self.mult = 1; // Add other LinExp self.add += rhs.add * rhs.mult; let mut rh_terms = rhs.terms; @@ -527,50 +539,45 @@ impl AddAssign> for LinExp self.constraints.extend(rhs.constraints); } } -impl Add> for LinExp { - type Output = LinExp; - fn add(mut self, rhs: LinExp) -> Self::Output { +impl Add for LinExp { + type Output = LinExp; + fn add(mut self, rhs: LinExp) -> Self::Output { self += rhs; self } } -impl MulAssign for LinExp { - fn mul_assign(&mut self, rhs: C) { +impl MulAssign for LinExp { + fn mul_assign(&mut self, rhs: Coeff) { self.mult *= rhs; } } -impl Mul for LinExp { - type Output = LinExp; - fn mul(mut self, rhs: C) -> Self::Output { +impl Mul for LinExp { + type Output = LinExp; + fn mul(mut self, rhs: Coeff) -> Self::Output { self *= rhs; self } } -impl Checker for Linear { - type Lit = Lit; - - fn check(&self, solution: &[Self::Lit]) -> Result<(), crate::CheckError> { - let lhs = &self - .terms - .iter() - .flat_map(|part| part.iter().map(|(lit, coef)| (lit.clone(), **coef))) - .fold(C::zero(), |acc, (lit, coef)| { - let a = solution.iter().find(|x| x.var() == lit.var()); - acc + if lit == *a.unwrap() { - C::one() - } else { - C::zero() - } * coef - }); +impl Checker for Linear { + fn check(&self, value: F) -> Result<(), CheckError> { + let mut sum = 0; + for (lit, coef) in self.terms.iter().flat_map(|p| p.iter().copied()) { + match value(lit) { + Some(true) => sum += *coef, + None if self.cmp == LimitComp::LessEq => sum += *coef, + Some(false) => {} + None => return Err(Unsatisfiable.into()), + } + } if match self.cmp { - LimitComp::LessEq => *lhs <= *self.k, - LimitComp::Equal => *lhs == *self.k, + LimitComp::LessEq => sum <= *self.k, + LimitComp::Equal => sum == *self.k, } { Ok(()) } else { - Err(CheckError::Unsatisfiable(Unsatisfiable)) + Err(Unsatisfiable.into()) } } } @@ -595,14 +602,14 @@ impl LinearEncoder { } } -impl>> - Encoder> for LinearEncoder +impl> Encoder + for LinearEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "linear_encoder", skip_all, fields(constraint = lin.trace_print())) )] - fn encode(&mut self, db: &mut DB, lin: &LinearConstraint) -> Result { + fn encode(&mut self, db: &mut DB, lin: &LinearConstraint) -> Result { let variant = self.agg.aggregate(db, lin)?; self.enc.encode(db, &variant) } @@ -642,13 +649,12 @@ impl StaticLinEncoder { impl< DB: ClauseDatabase, - C: Coefficient, - LinEnc: Encoder>, - CardEnc: Encoder>, - AmoEnc: Encoder>, - > Encoder> for StaticLinEncoder + LinEnc: Encoder, + CardEnc: Encoder, + AmoEnc: Encoder, + > Encoder for StaticLinEncoder { - fn encode(&mut self, db: &mut DB, lin: &LinVariant) -> Result { + fn encode(&mut self, db: &mut DB, lin: &LinVariant) -> Result { match &lin { LinVariant::Linear(lin) => self.lin_enc.encode(db, lin), LinVariant::Cardinality(card) => self.card_enc.encode(db, card), @@ -660,13 +666,13 @@ impl< #[cfg(test)] mod tests { - use super::Part; - use crate::PosCoeff; + use super::{Part, PosCoeff}; + use crate::{Coeff, Lit}; - pub(crate) fn construct_terms(terms: &[(i32, i32)]) -> Vec>> { + pub(crate) fn construct_terms + Clone>(terms: &[(L, Coeff)]) -> Vec { terms .iter() - .map(|(lit, coef)| Part::Amo(vec![(*lit, PosCoeff::from(*coef))])) + .map(|(lit, coef)| Part::Amo(vec![(lit.clone().into(), PosCoeff::new(*coef))])) .collect() } @@ -680,14 +686,14 @@ mod tests { &Linear { terms: construct_terms(&[(1, 2), (2, 3), (3, 5),]), cmp: LimitComp::LessEq, - k: 6.into() + k: PosCoeff::new(6) } => vec![ - vec![-1, -2, -3], // 0 - vec![ 1, -2, -3], // 2 - vec![-1, 2, -3], // 3 - vec![ 1, 2, -3], // 5 - vec![-1, -2, 3], // 5 + lits![-1, -2, -3], // 0 + lits![ 1, -2, -3], // 2 + lits![-1, 2, -3], // 3 + lits![ 1, 2, -3], // 5 + lits![-1, -2, 3], // 5 ] ); } @@ -707,7 +713,7 @@ mod tests { (-6, 6) ]), cmp: LimitComp::LessEq, - k: 19.into() + k: PosCoeff::new(19) } ); } @@ -720,15 +726,15 @@ mod tests { &Linear { terms: construct_terms(&[(1, 1), (2, 2), (3, 4),]), cmp: LimitComp::LessEq, - k: 5.into() + k: PosCoeff::new(5) } => vec![ - vec![-1, -2, -3], - vec![ 1, -2, -3], - vec![-1, 2, -3], - vec![ 1, 2, -3], - vec![-1, -2, 3], - vec![ 1, -2, 3], + lits![-1, -2, -3], + lits![ 1, -2, -3], + lits![-1, 2, -3], + lits![ 1, 2, -3], + lits![-1, -2, 3], + lits![ 1, -2, 3], ] ); } @@ -741,15 +747,15 @@ mod tests { &Linear { terms: construct_terms(&[(1, 4), (2, 6), (3, 7),]), cmp: LimitComp::LessEq, - k: 10.into() + k: PosCoeff::new(10) } - => vec![ - vec![-1, -2, -3], - vec![1, -2, -3], - vec![-1, 2, -3], - vec![1, 2, -3], - vec![-1, -2, 3], - ] + => vec![ + lits![-1, -2, -3], + lits![1, -2, -3], + lits![-1, 2, -3], + lits![1, 2, -3], + lits![-1, -2, 3], + ] ); } @@ -764,10 +770,10 @@ mod tests { &Linear { terms: construct_terms(&[(1, 1), (2, 2), (3, 4)]), cmp: LimitComp::Equal, - k: 5.into() + k: PosCoeff::new(5) } => vec![ - vec![ 1, -2, 3], + lits![ 1, -2, 3], ] ); } @@ -780,11 +786,11 @@ mod tests { &Linear { terms: construct_terms(&[(1, 1), (2, 2), (3, 3),]), cmp: LimitComp::Equal, - k: 3.into() + k: PosCoeff::new(3) } => vec![ - vec![-1, -2, 3], - vec![ 1, 2, -3], + lits![-1, -2, 3], + lits![ 1, 2, -3], ] ); } @@ -797,11 +803,11 @@ mod tests { &Linear { terms: construct_terms(&[(1, 2), (2, 3), (3, 5), (4,7)]), cmp: LimitComp::Equal, - k: 10.into() + k: PosCoeff::new(10) } => vec![ - vec![-1, 2,-3, 4], - vec![ 1, 2, 3,-4], + lits![-1, 2,-3, 4], + lits![ 1, 2, 3,-4], ] ); } @@ -814,12 +820,12 @@ mod tests { &Linear { terms: construct_terms(&[(1, 2), (2, 1), (3, 2), (4,2)]), cmp: LimitComp::Equal, - k: 4.into() + k: PosCoeff::new(4) } => vec![ - vec![ 1,-2,-3, 4], - vec![-1,-2, 3, 4], - vec![ 1,-2, 3,-4], + lits![ 1,-2,-3, 4], + lits![-1,-2, 3, 4], + lits![ 1,-2, 3,-4], ] ); } diff --git a/crates/pindakaas/src/linear/adder.rs b/crates/pindakaas/src/linear/adder.rs index ff781bc0b..ffc69f1a6 100644 --- a/crates/pindakaas/src/linear/adder.rs +++ b/crates/pindakaas/src/linear/adder.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use rustc_hash::FxHashMap; use crate::{ @@ -5,38 +6,41 @@ use crate::{ int::LitOrConst, linear::{LimitComp, PosCoeff}, trace::{emit_clause, new_var}, - ClauseDatabase, Coefficient, Encoder, Linear, Literal, Result, Unsatisfiable, + ClauseDatabase, Coeff, Encoder, Linear, Lit, Result, Unsatisfiable, }; /// Encoder for the linear constraints that ∑ coeffᵢ·litsᵢ ≷ k using a binary adders circuits #[derive(Default)] pub struct AdderEncoder {} -impl Encoder> for AdderEncoder { +impl Encoder for AdderEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "adder_encoder", skip_all, fields(constraint = lin.trace_print())) )] - fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { + fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { let pair = &lin .terms .iter() - .flat_map(|part| part.iter().map(|(lit, coef)| (lit.clone(), **coef))) - .collect::>(); + .flat_map(|part| part.iter().map(|&(lit, coef)| (lit, coef))) + .collect::>(); debug_assert!(lin.cmp == LimitComp::LessEq || lin.cmp == LimitComp::Equal); // The number of relevant bits in k - let bits = (C::zero().leading_zeros() - lin.k.leading_zeros()) as usize; + const ZERO: Coeff = 0; + let bits = ZERO.leading_zeros() - lin.k.leading_zeros(); + let mut k = as_binary(lin.k, Some(bits)); + let first_zero = lin.k.trailing_ones() as usize; - let mut k = as_binary(lin.k.clone(), Some(bits)); + let bits = bits as usize; debug_assert!(k[bits - 1]); // Create structure with which coefficients use which bits let mut bucket = vec![Vec::new(); bits]; for (i, bucket) in bucket.iter_mut().enumerate().take(bits) { for (lit, coef) in pair { - if *coef & (C::one() << i) != C::zero() { - bucket.push(lit.clone()); + if **coef & (1 << i) != 0 { + bucket.push(*lit); } } } @@ -55,7 +59,7 @@ impl Encoder> for Add 1 => { let x = bucket[b].pop().unwrap(); if lin.cmp == LimitComp::Equal { - emit_clause!(db, &[if k[b] { x.clone() } else { x.negate() }])? + emit_clause!(db, [if k[b] { x } else { !x }])? } else { sum[b] = Some(x); } @@ -88,7 +92,7 @@ impl Encoder> for Add ) } ); - sum_circuit(db, lits.as_slice(), LitOrConst::Lit(sum.clone()))?; + sum_circuit(db, lits.as_slice(), LitOrConst::Lit(sum))?; bucket[b].push(sum); } @@ -117,7 +121,7 @@ impl Encoder> for Add ) } ); - carry_circuit(db, lits.as_slice(), LitOrConst::Lit(carry.clone()))?; + carry_circuit(db, lits.as_slice(), LitOrConst::Lit(carry))?; bucket[b + 1].push(carry); } } @@ -135,7 +139,7 @@ impl Encoder> for Add // Enforce less-than constraint if lin.cmp == LimitComp::LessEq { - lex_leq_const(db, sum.as_slice(), lin.k.clone(), bits)?; + lex_leq_const(db, sum.as_slice(), lin.k, bits)?; } Ok(()) } @@ -146,13 +150,13 @@ impl Encoder> for Add feature = "trace", tracing::instrument(name = "lex_lesseq_const", skip_all) )] -pub(crate) fn lex_leq_const( +pub(crate) fn lex_leq_const( db: &mut DB, - x: &[Option], - k: PosCoeff, + x: &[Option], + k: PosCoeff, bits: usize, ) -> Result { - let k = as_binary(k, Some(bits)); + let k = as_binary(k, Some(bits as u32)); // For every zero bit in k: // - either the `x` bit is also zero, or // - a higher `x` bit is zero that was one in k. @@ -160,10 +164,9 @@ pub(crate) fn lex_leq_const( if !k[i] && x[i].is_some() { emit_clause!( db, - &(i..bits) - .filter_map(|j| if j == i || k[j] { x[j].clone() } else { None }) - .map(|lit| lit.negate()) - .collect::>() + (i..bits) + .filter_map(|j| if j == i || k[j] { x[j] } else { None }) + .map(|lit| !lit) )?; } } @@ -172,20 +175,18 @@ pub(crate) fn lex_leq_const( /// Uses lexicographic constraint to constrain x:B >= k #[cfg_attr(feature = "trace", tracing::instrument(name = "lex_geq", skip_all))] -pub(crate) fn lex_geq_const( +pub(crate) fn lex_geq_const( db: &mut DB, - x: &[Option], - k: PosCoeff, + x: &[Option], + k: PosCoeff, bits: usize, ) -> Result { - let k = as_binary(k, Some(bits)); + let k = as_binary(k, Some(bits as u32)); for i in 0..bits { if k[i] && x[i].is_some() { emit_clause!( db, - &(i..bits) - .filter_map(|j| if j == i || !k[j] { x[j].clone() } else { None }) - .collect::>() + (i..bits).filter_map(|j| if j == i || !k[j] { x[j] } else { None }) )?; } } @@ -197,34 +198,32 @@ pub(crate) fn lex_geq_const( /// TODO: Should this use the IntEncoding::Log input?? pub(crate) fn log_enc_add( db: &mut DB, - x: &[DB::Lit], - y: &[DB::Lit], + x: &[Lit], + y: &[Lit], cmp: &LimitComp, - z: &[DB::Lit], + z: &[Lit], ) -> Result { log_enc_add_( db, - &x.iter().cloned().map(LitOrConst::from).collect::>(), - &y.iter().cloned().map(LitOrConst::from).collect::>(), + &x.iter().copied().map(LitOrConst::from).collect_vec(), + &y.iter().copied().map(LitOrConst::from).collect_vec(), cmp, - &z.iter().cloned().map(LitOrConst::from).collect::>(), + &z.iter().copied().map(LitOrConst::from).collect_vec(), ) } #[cfg_attr(feature = "trace", tracing::instrument(name = "log_enc_add", skip_all, fields(constraint = format!("{x:?} + {y:?} {cmp} {z:?}"))))] pub(crate) fn log_enc_add_( db: &mut DB, - x: &[LitOrConst], - y: &[LitOrConst], + x: &[LitOrConst], + y: &[LitOrConst], cmp: &LimitComp, - z: &[LitOrConst], + z: &[LitOrConst], ) -> Result { let n = itertools::max([x.len(), y.len(), z.len()]).unwrap(); - let bit = |x: &[LitOrConst], i: usize| -> LitOrConst { - x.get(i) - .map(LitOrConst::::clone) - .unwrap_or(LitOrConst::Const(false)) + let bit = |x: &[LitOrConst], i: usize| -> LitOrConst { + x.get(i).unwrap_or(&LitOrConst::Const(false)).clone() }; match cmp { @@ -233,26 +232,26 @@ pub(crate) fn log_enc_add_( .chain((1..n).map(|_i| { LitOrConst::Lit(new_var!(db, crate::trace::subscripted_name("c", _i))) })) - .collect::>(); + .collect_vec(); for i in 0..n { // sum circuit - clause(db, &[bit(x, i), bit(y, i), bit(c, i), -bit(z, i)])?; - clause(db, &[bit(x, i), -bit(y, i), -bit(c, i), -bit(z, i)])?; - clause(db, &[-bit(x, i), bit(y, i), -bit(c, i), -bit(z, i)])?; - clause(db, &[-bit(x, i), -bit(y, i), bit(c, i), -bit(z, i)])?; + emit_filtered_clause(db, [bit(x, i), bit(y, i), bit(c, i), !bit(z, i)])?; + emit_filtered_clause(db, [bit(x, i), !bit(y, i), !bit(c, i), !bit(z, i)])?; + emit_filtered_clause(db, [!bit(x, i), bit(y, i), !bit(c, i), !bit(z, i)])?; + emit_filtered_clause(db, [!bit(x, i), !bit(y, i), bit(c, i), !bit(z, i)])?; - clause(db, &[-bit(x, i), -bit(y, i), -bit(c, i), bit(z, i)])?; - clause(db, &[-bit(x, i), bit(y, i), bit(c, i), bit(z, i)])?; - clause(db, &[bit(x, i), -bit(y, i), bit(c, i), bit(z, i)])?; - clause(db, &[bit(x, i), bit(y, i), -bit(c, i), bit(z, i)])?; + emit_filtered_clause(db, [!bit(x, i), !bit(y, i), !bit(c, i), bit(z, i)])?; + emit_filtered_clause(db, [!bit(x, i), bit(y, i), bit(c, i), bit(z, i)])?; + emit_filtered_clause(db, [bit(x, i), !bit(y, i), bit(c, i), bit(z, i)])?; + emit_filtered_clause(db, [bit(x, i), bit(y, i), !bit(c, i), bit(z, i)])?; // carry circuit - clause(db, &[bit(x, i), bit(y, i), -bit(c, i + 1)])?; - clause(db, &[bit(x, i), bit(c, i), -bit(c, i + 1)])?; - clause(db, &[bit(y, i), bit(c, i), -bit(c, i + 1)])?; - clause(db, &[-bit(x, i), -bit(y, i), bit(c, i + 1)])?; - clause(db, &[-bit(x, i), -bit(c, i), bit(c, i + 1)])?; - clause(db, &[-bit(y, i), -bit(c, i), bit(c, i + 1)])?; + emit_filtered_clause(db, [bit(x, i), bit(y, i), !bit(c, i + 1)])?; + emit_filtered_clause(db, [bit(x, i), bit(c, i), !bit(c, i + 1)])?; + emit_filtered_clause(db, [bit(y, i), bit(c, i), !bit(c, i + 1)])?; + emit_filtered_clause(db, [!bit(x, i), !bit(y, i), bit(c, i + 1)])?; + emit_filtered_clause(db, [!bit(x, i), !bit(c, i), bit(c, i + 1)])?; + emit_filtered_clause(db, [!bit(y, i), !bit(c, i), bit(c, i + 1)])?; } Ok(()) } @@ -260,43 +259,46 @@ pub(crate) fn log_enc_add_( let c = &(0..n) .map(|_i| LitOrConst::Lit(new_var!(db, crate::trace::subscripted_name("c", _i)))) .chain(std::iter::once(LitOrConst::Const(true))) - .collect::>(); + .collect_vec(); // higher i -> more significant for i in 0..n { // c = all more significant bits are equal AND current one is // if up to i is equal, all preceding must be equal - clause(db, &[-bit(c, i), bit(c, i + 1)])?; + emit_filtered_clause(db, [!bit(c, i), bit(c, i + 1)])?; // if up to i is equal, x<->z - clause(db, &[-bit(c, i), -bit(x, i), bit(z, i)])?; - clause(db, &[-bit(c, i), -bit(z, i), bit(x, i)])?; + emit_filtered_clause(db, [!bit(c, i), !bit(x, i), bit(z, i)])?; + emit_filtered_clause(db, [!bit(c, i), !bit(z, i), bit(x, i)])?; // if not up to i is equal, either preceding bit was not equal, or x!=z - clause(db, &[bit(c, i), -bit(c, i + 1), bit(x, i), bit(z, i)])?; - clause(db, &[bit(c, i), -bit(c, i + 1), -bit(x, i), -bit(z, i)])?; + emit_filtered_clause(db, [bit(c, i), !bit(c, i + 1), bit(x, i), bit(z, i)])?; + emit_filtered_clause(db, [bit(c, i), !bit(c, i + 1), !bit(x, i), !bit(z, i)])?; // if preceding bits are equal, then x<=z - clause(db, &[-bit(c, i + 1), -bit(x, i), bit(z, i)])?; + emit_filtered_clause(db, [!bit(c, i + 1), !bit(x, i), bit(z, i)])?; } - clause(db, &[-bit(x, n - 1), bit(z, n - 1)])?; + emit_filtered_clause(db, [!bit(x, n - 1), bit(z, n - 1)])?; Ok(()) } } } -fn clause(db: &mut DB, lits: &[LitOrConst]) -> Result { +fn emit_filtered_clause>( + db: &mut DB, + lits: I, +) -> Result { if let Ok(clause) = lits - .iter() + .into_iter() .filter_map(|lit| match lit { - LitOrConst::Lit(lit) => Some(Ok(lit.clone())), + LitOrConst::Lit(lit) => Some(Ok(lit)), LitOrConst::Const(true) => Some(Err(())), // clause satisfied LitOrConst::Const(false) => None, // literal falsified }) .collect::, ()>>() { - emit_clause!(db, &clause) + emit_clause!(db, clause) } else { Ok(()) } @@ -309,43 +311,39 @@ fn clause(db: &mut DB, lits: &[LitOrConst]) -> Resu /// /// `output` can be either a literal, or a constant Boolean value. #[cfg_attr(feature = "trace", tracing::instrument(name = "sum_circuit", skip_all, fields(constraint = trace_print_sum(input, &output))))] -fn sum_circuit( - db: &mut DB, - input: &[DB::Lit], - output: LitOrConst, -) -> Result { +fn sum_circuit(db: &mut DB, input: &[Lit], output: LitOrConst) -> Result { match output { - LitOrConst::Lit(sum) => match input { + LitOrConst::Lit(sum) => match *input { [a, b] => { - emit_clause!(db, &[a.negate(), b.negate(), sum.negate()])?; - emit_clause!(db, &[a.negate(), b.clone(), sum.clone()])?; - emit_clause!(db, &[a.clone(), b.negate(), sum.clone()])?; - emit_clause!(db, &[a.clone(), b.clone(), sum.negate()]) + emit_clause!(db, [!a, !b, !sum])?; + emit_clause!(db, [!a, b, sum])?; + emit_clause!(db, [a, !b, sum])?; + emit_clause!(db, [a, b, !sum]) } [a, b, c] => { - emit_clause!(db, &[a.clone(), b.clone(), c.clone(), sum.negate()])?; - emit_clause!(db, &[a.clone(), b.negate(), c.negate(), sum.negate()])?; - emit_clause!(db, &[a.negate(), b.clone(), c.negate(), sum.negate()])?; - emit_clause!(db, &[a.negate(), b.negate(), c.clone(), sum.negate()])?; - - emit_clause!(db, &[a.negate(), b.negate(), c.negate(), sum.clone()])?; - emit_clause!(db, &[a.negate(), b.clone(), c.clone(), sum.clone()])?; - emit_clause!(db, &[a.clone(), b.negate(), c.clone(), sum.clone()])?; - emit_clause!(db, &[a.clone(), b.clone(), c.negate(), sum]) + emit_clause!(db, [a, b, c, !sum])?; + emit_clause!(db, [a, !b, !c, !sum])?; + emit_clause!(db, [!a, b, !c, !sum])?; + emit_clause!(db, [!a, !b, c, !sum])?; + + emit_clause!(db, [!a, !b, !c, sum])?; + emit_clause!(db, [!a, b, c, sum])?; + emit_clause!(db, [a, !b, c, sum])?; + emit_clause!(db, [a, b, !c, sum]) } _ => unreachable!(), }, LitOrConst::Const(true) => XorEncoder::default().encode(db, &XorConstraint::new(input)), - LitOrConst::Const(false) => match input { + LitOrConst::Const(false) => match *input { [a, b] => { - emit_clause!(db, &[a.clone(), b.negate()])?; - emit_clause!(db, &[a.negate(), b.clone()]) + emit_clause!(db, [a, !b])?; + emit_clause!(db, [!a, b]) } [a, b, c] => { - emit_clause!(db, &[a.negate(), b.negate(), c.negate()])?; - emit_clause!(db, &[a.negate(), b.clone(), c.clone()])?; - emit_clause!(db, &[a.clone(), b.negate(), c.clone()])?; - emit_clause!(db, &[a.clone(), b.clone(), c.negate()]) + emit_clause!(db, [!a, !b, !c])?; + emit_clause!(db, [!a, b, c])?; + emit_clause!(db, [a, !b, c])?; + emit_clause!(db, [a, b, !c]) } _ => unreachable!(), }, @@ -353,7 +351,7 @@ fn sum_circuit( } #[cfg(feature = "trace")] -fn trace_print_sum(input: &[Lit], output: &LitOrConst) -> String { +fn trace_print_sum(input: &[Lit], output: &LitOrConst) -> String { use crate::trace::trace_print_lit; let inner = itertools::join(input.iter().map(trace_print_lit), " ⊻ "); match output { @@ -370,44 +368,40 @@ fn trace_print_sum(input: &[Lit], output: &LitOrConst) -> Str /// /// `output` can be either a literal, or a constant Boolean value. #[cfg_attr(feature = "trace", tracing::instrument(name = "carry_circuit", skip_all, fields(constraint = trace_print_carry(input, &output))))] -fn carry_circuit( - db: &mut DB, - input: &[DB::Lit], - output: LitOrConst, -) -> Result { +fn carry_circuit(db: &mut DB, input: &[Lit], output: LitOrConst) -> Result { match output { - LitOrConst::Lit(carry) => match input { + LitOrConst::Lit(carry) => match *input { [a, b] => { - emit_clause!(db, &[a.negate(), b.negate(), carry.clone()])?; - emit_clause!(db, &[a.clone(), carry.negate()])?; - emit_clause!(db, &[b.clone(), carry.negate()]) + emit_clause!(db, [!a, !b, carry])?; + emit_clause!(db, [a, !carry])?; + emit_clause!(db, [b, !carry]) } [a, b, c] => { - emit_clause!(db, &[a.clone(), b.clone(), carry.negate()])?; - emit_clause!(db, &[a.clone(), c.clone(), carry.negate()])?; - emit_clause!(db, &[b.clone(), c.clone(), carry.negate()])?; + emit_clause!(db, [a, b, !carry])?; + emit_clause!(db, [a, c, !carry])?; + emit_clause!(db, [b, c, !carry])?; - emit_clause!(db, &[a.negate(), b.negate(), carry.clone()])?; - emit_clause!(db, &[a.negate(), c.negate(), carry.clone()])?; - emit_clause!(db, &[b.negate(), c.negate(), carry]) + emit_clause!(db, [!a, !b, carry])?; + emit_clause!(db, [!a, !c, carry])?; + emit_clause!(db, [!b, !c, carry]) } _ => unreachable!(), }, - LitOrConst::Const(k) => match input { + LitOrConst::Const(k) => match *input { [a, b] => { if k { // TODO: Can we avoid this? - emit_clause!(db, &[a.clone()])?; - emit_clause!(db, &[b.clone()]) + emit_clause!(db, [a])?; + emit_clause!(db, [b]) } else { - emit_clause!(db, &[a.negate(), b.negate()]) + emit_clause!(db, [!a, !b]) } } [a, b, c] => { - let neg = |x: &DB::Lit| if k { x.clone() } else { x.negate() }; - emit_clause!(db, &[neg(a), neg(b)])?; - emit_clause!(db, &[neg(a), neg(c)])?; - emit_clause!(db, &[neg(b), neg(c)]) + let neg = |x: Lit| if k { x } else { !x }; + emit_clause!(db, [neg(a), neg(b)])?; + emit_clause!(db, [neg(a), neg(c)])?; + emit_clause!(db, [neg(b), neg(c)]) } _ => unreachable!(), }, @@ -415,7 +409,7 @@ fn carry_circuit( } #[cfg(feature = "trace")] -fn trace_print_carry(input: &[Lit], output: &LitOrConst) -> String { +fn trace_print_carry(input: &[Lit], output: &LitOrConst) -> String { use crate::trace::trace_print_lit; let inner = itertools::join(input.iter().map(trace_print_lit), " + "); match output { @@ -434,7 +428,7 @@ mod tests { use crate::{ cardinality::tests::card_test_suite, cardinality_one::tests::card1_test_suite, - helpers::tests::{assert_enc_sol, assert_sol, TestDB}, + helpers::tests::{assert_enc_sol, assert_sol, lits, TestDB}, linear::{ tests::{construct_terms, linear_test_suite}, LimitComp, StaticLinEncoder, @@ -448,18 +442,20 @@ mod tests { assert_enc_sol!( LinearEncoder::::default(), 4, - &LinearConstraint::::new(LinExp::from((1,1)) + (2,1) + (3,1) + (4,2), - Comparator::LessEq, - 1) + &LinearConstraint::new( + LinExp::from_slices(&[1,1,1,2], &lits![1,2,3,4]), + Comparator::LessEq, + 1 + ) => vec![ - vec![-4], vec![-3, -1], vec![-2, -1], vec![-3, -2] + lits![-4], lits![-3, -1], lits![-2, -1], lits![-3, -2] ], vec![ - vec![-1, -2, -3, -4], - vec![-1, -2, 3, -4], - vec![-1, 2, -3, -4], - vec![1, -2, -3, -4], + lits![-1, -2, -3, -4], + lits![-1, -2, 3, -4], + lits![-1, 2, -3, -4], + lits![1, -2, -3, -4], ] ); } @@ -468,17 +464,17 @@ mod tests { fn test_encoders() { // +7*x1 +10*x2 +4*x3 +4*x4 <= 9 let mut db = TestDB::new(4).expect_solutions(vec![ - vec![-1, -2, -3, -4], - vec![1, -2, -3, -4], - vec![-1, -2, 3, -4], - vec![-1, -2, -3, 4], + lits![-1, -2, -3, -4], + lits![1, -2, -3, -4], + lits![-1, -2, 3, -4], + lits![-1, -2, -3, 4], ]); // TODO encode this if encoder does not support constraint assert!(PairwiseEncoder::default() .encode( &mut db, &CardinalityOne { - lits: vec![1, 2], + lits: lits![1, 2], cmp: LimitComp::LessEq } ) @@ -487,7 +483,7 @@ mod tests { .encode( &mut db, &CardinalityOne { - lits: vec![3, 4], + lits: lits![3, 4], cmp: LimitComp::LessEq } ) @@ -495,10 +491,10 @@ mod tests { assert!(LinearEncoder::>::default() .encode( &mut db, - &LinearConstraint::::new( + &LinearConstraint::new( LinExp::default() - .add_choice(&[(1, 7), (2, 10)]) - .add_choice(&[(3, 4), (4, 4)]), + .add_choice(&[(1.into(), 7), (2.into(), 10)]) + .add_choice(&[(3.into(), 4), (4.into(), 4)]), Comparator::LessEq, 9, ), diff --git a/crates/pindakaas/src/linear/aggregator.rs b/crates/pindakaas/src/linear/aggregator.rs index d2d4461a1..bf96610b7 100644 --- a/crates/pindakaas/src/linear/aggregator.rs +++ b/crates/pindakaas/src/linear/aggregator.rs @@ -1,16 +1,17 @@ -use rustc_hash::FxHashMap; use std::hash::BuildHasherDefault; +use itertools::Itertools; +use rustc_hash::FxHashMap; + use crate::{ helpers::is_powers_of_two, int::IntVarOrd, linear::{Constraint, LimitComp, Part, PosCoeff}, sorted::{Sorted, SortedEncoder}, trace::emit_clause, - Cardinality, CardinalityOne, ClauseDatabase, Coefficient, Comparator, Encoder, LinExp, - LinVariant, Linear, LinearConstraint, Literal, Result, Unsatisfiable, + Cardinality, CardinalityOne, ClauseDatabase, Coeff, Comparator, Encoder, LinExp, LinVariant, + Linear, LinearConstraint, Lit, Result, Unsatisfiable, }; -use itertools::Itertools; #[derive(Default, Clone)] pub struct LinearAggregator { @@ -30,11 +31,11 @@ impl LinearAggregator { feature = "trace", tracing::instrument(name = "aggregator", skip_all, fields(constraint = lin.trace_print())) )] - pub fn aggregate( + pub fn aggregate( &mut self, db: &mut DB, - lin: &LinearConstraint, - ) -> Result> { + lin: &LinearConstraint, + ) -> Result { let mut k = lin.k; // Aggregate multiple occurrences of the same // variable. @@ -42,13 +43,12 @@ impl LinearAggregator { FxHashMap::with_capacity_and_hasher(lin.exp.terms.len(), BuildHasherDefault::default()); for term in &lin.exp.terms { let var = term.0.var(); - let entry = agg.entry(var).or_insert_with(C::zero); + let entry = agg.entry(var).or_insert(0); let mut coef = term.1 * lin.exp.mult; if term.0.is_negated() { k -= coef; coef = -coef; } - *entry += coef; } @@ -58,54 +58,49 @@ impl LinearAggregator { k = -k; } - let mut partition = Vec::with_capacity(lin.exp.constraints.len()); + let mut partition: Vec<(Constraint, Vec<(Lit, Coeff)>)> = + Vec::with_capacity(lin.exp.constraints.len()); // Adjust side constraints when literals are combined (and currently transform to partition structure) let mut iter = lin.exp.terms.iter().skip(lin.exp.num_free); for con in &lin.exp.constraints { let mut terms = Vec::with_capacity(con.1); for _ in 0..con.1 { let term = iter.next().unwrap(); - let entry = agg.remove_entry(&term.0.var()); - if let Some(term) = entry { - terms.push(term) + if let Some((var, i)) = agg.remove_entry(&term.0.var()) { + terms.push((var.into(), i)) } } if !terms.is_empty() { match con.0 { - Constraint::AtMostOne => partition.push(Part::Amo(terms)), - Constraint::ImplicationChain => partition.push(Part::Ic(terms)), Constraint::Domain { lb, ub } => { // Domain constraint can only be enforced when PB is coef*(x1 + 2x2 + 4x3 + ...), where l <= x1 + 2*x2 + 4*x3 + ... <= u - if terms.len() == con.1 - && is_powers_of_two( - terms.iter().map(|(_, c)| *c).collect::>().as_slice(), - ) { + if terms.len() == con.1 && is_powers_of_two(terms.iter().map(|(_, c)| *c)) { // Adjust the bounds to account for coef let (lb, ub) = if lin.cmp == Comparator::GreaterEq { // 0..range can be encoded by the bits multiplied by coef - let range = - -terms.iter().fold(C::zero(), |acc, (_, coef)| acc + *coef); + let range = -terms.iter().fold(0, |acc, (_, coef)| acc + *coef); // this range is inverted if we have flipped the comparator (range - ub, range - lb) } else { // in both cases, l and u now represent the true constraint (terms[0].1 * lb, terms[0].1 * ub) }; - partition.push(Part::Dom(terms, lb, ub)) + partition.push((Constraint::Domain { lb, ub }, terms)) } else { for term in terms { - partition.push(Part::Amo(vec![term])); + partition.push((Constraint::AtMostOne, vec![term])); } } } + _ => partition.push((con.0.clone(), terms)), } } } // Add remaining (unconstrained) terms debug_assert!(agg.len() <= lin.exp.num_free); - for term in agg.drain() { - partition.push(Part::Amo(vec![term])); + for (var, coeff) in agg.drain() { + partition.push((Constraint::AtMostOne, vec![(var.into(), coeff)])); } k -= lin.exp.add; @@ -114,28 +109,28 @@ impl LinearAggregator { Comparator::Equal => LimitComp::Equal, }; - let convert_term_if_negative = |term: (DB::Lit, C), k: &mut C| -> (DB::Lit, PosCoeff) { + let convert_term_if_negative = |term: (Lit, Coeff), k: &mut Coeff| -> (Lit, PosCoeff) { let (mut lit, mut coef) = term; if coef.is_negative() { coef = -coef; - lit = lit.negate(); + lit = !lit; *k += coef; }; - (lit, coef.into()) + (lit, PosCoeff::new(coef)) }; - let partition: Vec>> = partition + let partition: Vec = partition .into_iter() - .filter(|part| part.iter().next().is_some()) // filter out empty groups - .flat_map(|part| { + .filter(|(_, t)| !t.is_empty()) // filter out empty groups + .flat_map(|part| -> Vec { // convert terms with negative coefficients match part { - Part::Amo(mut terms) => { + (Constraint::AtMostOne, mut terms) => { if terms.len() == 1 { return vec![Part::Amo( terms .into_iter() - .filter(|(_, coef)| coef != &C::zero()) + .filter(|&(_, coef)| coef != 0) .map(|(lit, coef)| { convert_term_if_negative((lit, coef), &mut k) }) @@ -157,20 +152,19 @@ impl LinearAggregator { // add aux var y and constrain y <-> ( ~x1 /\ ~x2 /\ .. ) let y = db.new_var(); - // ~x1 /\ ~x2 /\ .. -> y == x1 \/ x2 \/ .. \/ y + // ~x1 /\ ~x2 /\ .. -> y == x1 \/ x2 \/ .. \/ y emit_clause!( db, - &terms + terms .iter() - .map(|(lit, _)| lit.clone()) - .chain(std::iter::once(y.clone())) - .collect::>() + .map(|(lit, _)| *lit) + .chain(std::iter::once(y)) ) .unwrap(); - // y -> ( ~x1 /\ ~x2 /\ .. ) == ~y \/ ~x1, ~y \/ ~x2, .. - for lit in terms.iter().map(|tup| tup.0.clone()) { - emit_clause!(db, &[y.negate(), lit.negate()]).unwrap(); + // y -> ( ~x1 /\ ~x2 /\ .. ) == ~y \/ ~x1, ~y \/ ~x2, .. + for lit in terms.iter().map(|tup| tup.0) { + emit_clause!(db, [!y, !lit]).unwrap(); } // this term will cancel out later when we add q*min_lit to the LHS @@ -178,10 +172,10 @@ impl LinearAggregator { // since y + x1 + x2 + ... = 1 (exactly-one), we have q*y + q*x1 + q*x2 + ... = q // after adding term 0*y, we can add q*y + q*x1 + q*x2 + ... on the LHS, and q on the RHS - terms.push((y, C::zero())); // note: it's fine to add y into the same AMO group + terms.push((y, 0)); // note: it's fine to add y into the same AMO group terms = terms .iter() - .map(|(lit, coef)| (lit.clone(), *coef + q)) + .map(|(lit, coef)| (*lit, *coef + q)) .collect(); k += q; } @@ -190,19 +184,20 @@ impl LinearAggregator { vec![Part::Amo( terms .into_iter() - .map(|(lit, coef)| (lit, coef.into())) + .map(|(lit, coef)| (lit, PosCoeff::new(coef))) .collect(), )] } - Part::Ic(terms) => { + + (Constraint::ImplicationChain, terms) => { // normalize by splitting up the chain into two chains by coef polarity, inverting the coefs of the neg - let (pos_chain, neg_chain): (_, Vec<(DB::Lit, C)>) = + let (pos_chain, neg_chain): (_, Vec<_>) = terms.into_iter().partition(|(_, coef)| coef.is_positive()); vec![ Part::Ic( pos_chain .into_iter() - .map(|(lit, coef)| (lit, coef.into())) + .map(|(lit, coef)| (lit, PosCoeff::new(coef))) .collect(), ), Part::Ic( @@ -216,29 +211,29 @@ impl LinearAggregator { ), ] } - Part::Dom(terms, l, u) => { + (Constraint::Domain { lb: l, ub: u }, terms) => { assert!( terms.iter().all(|(_,coef)| coef.is_positive()) || terms.iter().all(|(_,coef)| coef.is_negative()), - "Normalizing mixed positive/negative coefficients not yet supported for Dom constraint on {:?}", terms + "Normalizing mixed positive/negative coefficients not yet supported for Dom constraint on {:?}", terms ); vec![Part::Dom( terms .into_iter() .map(|(lit, coef)| convert_term_if_negative((lit, coef), &mut k)) .collect(), - l.into(), - u.into(), + PosCoeff::new(l), + PosCoeff::new(u), )] } } }) .map(|part| { // This step has to come *after* Amo normalization - let filter_zero_coefficients = |terms: Vec<(DB::Lit, PosCoeff)>| -> Vec<(DB::Lit, PosCoeff)> { + let filter_zero_coefficients = |terms: Vec<(Lit, PosCoeff)>| -> Vec<(Lit, PosCoeff)> { terms .into_iter() - .filter(|(_, coef)| **coef != C::zero()) + .filter(|&(_, coef)| *coef != 0) .collect() }; @@ -252,20 +247,19 @@ impl LinearAggregator { .collect(); // trivial case: constraint is unsatisfiable - if k < C::zero() { + if k < 0 { return Err(Unsatisfiable); } // trivial case: no literals can be activated - if k == C::zero() { + if k == 0 { for part in partition { for (lit, _) in part.iter() { - emit_clause!(db, &[lit.negate()])? + emit_clause!(db, [!lit])? } } return Ok(LinVariant::Trivial); } - - let mut k = k.into(); + let mut k = PosCoeff::new(k); // Remove terms with coefs higher than k let partition = partition @@ -276,7 +270,7 @@ impl LinearAggregator { .into_iter() .filter(|(lit, coef)| { if coef > &k { - emit_clause!(db, &[lit.negate()]).unwrap(); + emit_clause!(db, [!lit]).unwrap(); false } else { true @@ -286,14 +280,14 @@ impl LinearAggregator { ), Part::Ic(terms) => { // for IC, we can compare the running sum to k - let mut acc = C::zero(); + let mut acc = 0; Part::Ic( terms .into_iter() - .filter(|(lit, coef)| { - acc += **coef; + .filter(|&(lit, coef)| { + acc += *coef; if acc > *k { - emit_clause!(db, &[lit.negate()]).unwrap(); + emit_clause!(db, [!lit]).unwrap(); false } else { true @@ -308,41 +302,37 @@ impl LinearAggregator { .into_iter() .filter(|(lit, coef)| { if coef > &k { - emit_clause!(db, &[lit.negate()]).unwrap(); + emit_clause!(db, [!lit]).unwrap(); false } else { true } }) - .collect::>(); + .collect_vec(); // the one or more of the most significant bits have been removed, the upper bound could have dropped to a power of 2 (but not beyond) - let u = std::cmp::min( - u, - terms - .iter() - .map(|(_, coef)| coef) - .fold(C::zero(), |a, b| a + **b) - .into(), - ); + let u = PosCoeff::new(std::cmp::min( + *u, + terms.iter().map(|&(_, coef)| *coef).sum(), + )); Part::Dom(terms, l, u) } }) .filter(|part| part.iter().next().is_some()) // filter out empty groups - .collect::>>>(); + .collect_vec(); // Check whether some literals can violate / satisfy the constraint - let lhs_ub: PosCoeff = partition - .iter() - .fold(C::zero(), |acc, part| match part { - Part::Amo(terms) => { - acc + terms.iter().map(|tup| *tup.1).max().unwrap_or_else(C::zero) - } - Part::Ic(terms) | Part::Dom(terms, _, _) => { - acc + terms.iter().fold(C::zero(), |acc, (_, coef)| acc + **coef) - // TODO max(k, acc + ..) - } - }) - .into(); + let lhs_ub = PosCoeff::new( + partition + .iter() + .map(|part| match part { + Part::Amo(terms) => terms.iter().map(|&(_, i)| *i).max().unwrap_or(0), + Part::Ic(terms) | Part::Dom(terms, _, _) => { + terms.iter().map(|&(_, coef)| *coef).sum() + // TODO max(k, acc + ..) + } + }) + .sum(), + ); match cmp { LimitComp::LessEq => { @@ -354,11 +344,11 @@ impl LinearAggregator { if partition.iter().flat_map(|part| part.iter()).count() == 2 { emit_clause!( db, - &partition + partition .iter() .flat_map(|part| part.iter()) - .map(|(lit, _)| lit.negate()) - .collect::>() + .map(|(lit, _)| !lit) + .collect_vec() )?; return Ok(LinVariant::Trivial); } @@ -373,17 +363,12 @@ impl LinearAggregator { Part::Amo(terms) => { emit_clause!( db, - &[terms - .iter() - .max_by(|(_, a), (_, b)| a.cmp(b)) - .unwrap() - .0 - .clone()] + [terms.iter().max_by(|(_, a), (_, b)| a.cmp(b)).unwrap().0] )?; } Part::Ic(terms) | Part::Dom(terms, _, _) => { for (lit, _) in terms { - emit_clause!(db, &[lit.clone()])?; + emit_clause!(db, [lit])?; } } }; @@ -406,27 +391,27 @@ impl LinearAggregator { // special case: all coefficients are equal (and can be made one) let val = partition .iter() - .flat_map(|part| part.iter().map(|(_, coef)| coef)) + .flat_map(|part| part.iter().map(|&(_, coef)| coef)) .next() .unwrap(); if partition .iter() .flat_map(|part| part.iter()) - .all(|(_, coef)| *coef == *val) + .all(|&(_, coef)| coef == val) { // trivial case: k cannot be made from the coefficients - if cmp == LimitComp::Equal && *k % **val != C::zero() { + if cmp == LimitComp::Equal && *k % *val != 0 { return Err(Unsatisfiable); } - *k /= **val; + k = PosCoeff::new(*k / *val); let partition = partition .iter() .flat_map(|part| part.iter()) - .map(|(lit, _)| lit.clone()) - .collect::>(); - if *k == C::one() { + .map(|&(lit, _)| lit) + .collect_vec(); + if *k == 1 { // Cardinality One constraint return Ok(LinVariant::CardinalityOne(CardinalityOne { lits: partition, @@ -436,9 +421,9 @@ impl LinearAggregator { // At most n-1 out of n is equivalent to at least *not* one // Ex. at most 2 out of 3 true = at least 1 out of 3 false - if (0..partition.len()).fold(C::zero(), |acc, _| acc + C::one()) == *k + C::one() { - let neg = partition.iter().map(|l| l.negate()).collect::>(); - emit_clause!(db, &neg)?; + if partition.len() == (*k + 1) as usize { + let neg = partition.iter().map(|l| !l); + emit_clause!(db, neg.clone())?; if cmp == LimitComp::LessEq { return Ok(LinVariant::Trivial); @@ -448,7 +433,7 @@ impl LinearAggregator { // == - ~x1 - ~x2 - .. <= n-1-n ( == .. <= -1) // == ~x1 + ~x2 + .. <= 1 return Ok(LinVariant::CardinalityOne(CardinalityOne { - lits: neg, + lits: neg.collect_vec(), cmp: LimitComp::LessEq, })); } @@ -470,17 +455,17 @@ impl LinearAggregator { for (coef, lits) in free_lits .into_iter() .map(|part| match part { - Part::Amo(x) | Part::Ic(x) | Part::Dom(x, _, _) if x.len() == 1 => x[0].clone(), + Part::Amo(x) | Part::Ic(x) | Part::Dom(x, _, _) if x.len() == 1 => x[0], _ => unreachable!(), }) - .map(|(lit, coef)| (*coef, lit)) + .map(|(lit, coef)| (coef, lit)) .into_group_map() .into_iter() { if self.sort_same_coefficients >= 2 && lits.len() >= self.sort_same_coefficients { - let c = *k / coef; + let c = *k / *coef; - let y = IntVarOrd::from_bounds(db, C::zero(), c, String::from("s")).into(); + let y = IntVarOrd::from_bounds(db, 0, c, String::from("s")).into(); self.sorted_encoder .encode(db, &Sorted::new(&lits, cmp.clone(), &y)) .unwrap(); @@ -490,12 +475,12 @@ impl LinearAggregator { lin_exp .terms .into_iter() - .map(|(lit, _)| (lit, coef.into())) + .map(|(lit, _)| (lit, coef)) .collect(), )); } else { for x in lits { - partition.push(Part::Amo(vec![(x, coef.into())])); + partition.push(Part::Amo(vec![(x, coef)])); } } } @@ -522,8 +507,8 @@ mod tests { use super::*; use crate::{ - helpers::tests::{assert_trivial_unsat, TestDB}, - linear::tests::construct_terms, + helpers::tests::{assert_trivial_unsat, lits, TestDB}, + linear::{tests::construct_terms, PosCoeff}, LinExp, }; @@ -535,7 +520,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (1, 2) + (2, 1) + (3, 2), + LinExp::from_slices(&[1, 2, 1, 2], &lits![1, 1, 2, 3]), Comparator::LessEq, 3 ) @@ -543,7 +528,7 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: construct_terms(&[(1, 3), (2, 1), (3, 2)]), cmp: LimitComp::LessEq, - k: 3.into() + k: PosCoeff::new(3) })) ); @@ -557,7 +542,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (-1, 2) + (2, 1) + (3, 2), + LinExp::from_slices(&[1, 2, 1, 2], &lits![1, -1, 2, 3]), Comparator::LessEq, 3 ) @@ -565,7 +550,7 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: construct_terms(&[(-1, 1), (2, 1), (3, 2)]), cmp: LimitComp::LessEq, - k: 2.into() + k: PosCoeff::new(2) })) ); @@ -574,7 +559,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (1, -2) + (2, 1) + (3, 2), + LinExp::from_slices(&[1, -2, 1, 2], &lits![1, 1, 2, 3]), Comparator::LessEq, 2, ) @@ -582,7 +567,7 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: construct_terms(&[(-1, 1), (2, 1), (3, 2)]), cmp: LimitComp::LessEq, - k: 3.into() + k: PosCoeff::new(3) })) ); db.check_complete() @@ -597,13 +582,13 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 1) + (3, 1), + LinExp::from_slices(&[1, 1, 1], &lits![1, 2, 3]), Comparator::LessEq, 1 ) ), Ok(LinVariant::CardinalityOne(CardinalityOne { - lits: vec![1, 2, 3], + lits: lits![1, 2, 3], cmp: LimitComp::LessEq })) ); @@ -611,13 +596,13 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 2)) + (2, 2) + (3, 2), + LinExp::from_slices(&[2, 2, 2], &lits![1, 2, 3]), Comparator::LessEq, 2 ) ), Ok(LinVariant::CardinalityOne(CardinalityOne { - lits: vec![1, 2, 3], + lits: lits![1, 2, 3], cmp: LimitComp::LessEq })) ); @@ -627,30 +612,30 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 1) + (3, 1) + (4, 1), + LinExp::from_slices(&[1, 1, 1, 1], &lits![1, 2, 3, 4]), Comparator::LessEq, 2 ) ), Ok(LinVariant::Cardinality(Cardinality { - lits: vec![1, 2, 3, 4], + lits: lits![1, 2, 3, 4], cmp: LimitComp::LessEq, - k: 2.into(), + k: PosCoeff::new(2), })) ); assert_eq!( LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 3)) + (2, 3) + (3, 3) + (4, 3), + LinExp::from_slices(&[3, 3, 3, 3], &lits![1, 2, 3, 4]), Comparator::LessEq, 7 ) ), Ok(LinVariant::Cardinality(Cardinality { - lits: vec![1, 2, 3, 4], + lits: lits![1, 2, 3, 4], cmp: LimitComp::LessEq, - k: 2.into(), + k: PosCoeff::new(2), })) ); @@ -659,30 +644,30 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 1) + (3, 1) + (4, 1), + LinExp::from_slices(&[1, 1, 1, 1], &lits![1, 2, 3, 4]), Comparator::Equal, 2 ) ), Ok(LinVariant::Cardinality(Cardinality { - lits: vec![1, 2, 3, 4], + lits: lits![1, 2, 3, 4], cmp: LimitComp::Equal, - k: 2.into(), + k: PosCoeff::new(2), })) ); assert_eq!( LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 3)) + (2, 3) + (3, 3) + (4, 3), + LinExp::from_slices(&[3, 3, 3, 3], &lits![1, 2, 3, 4]), Comparator::Equal, 6 ) ), Ok(LinVariant::Cardinality(Cardinality { - lits: vec![1, 2, 3, 4], + lits: lits![1, 2, 3, 4], cmp: LimitComp::Equal, - k: 2.into(), + k: PosCoeff::new(2), })) ); @@ -691,7 +676,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 2) + (3, 2), + LinExp::from_slices(&[1, 2, 2], &lits![1, 2, 3]), Comparator::LessEq, 2 ) @@ -699,7 +684,7 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: construct_terms(&[(1, 1), (2, 2), (3, 2)]), cmp: LimitComp::LessEq, - k: 2.into(), + k: PosCoeff::new(2), })) ); @@ -708,7 +693,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 2) + (3, 2), + LinExp::from_slices(&[1, 2, 2], &lits![1, 2, 3]), Comparator::Equal, 2 ) @@ -716,7 +701,7 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: construct_terms(&[(1, 1), (2, 2), (3, 2)]), cmp: LimitComp::Equal, - k: 2.into(), + k: PosCoeff::new(2), })) ); @@ -725,7 +710,8 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((3, -1)).add_choice(&[(1, -1), (2, -1)]), + LinExp::from_terms(&[(3.into(), -1)]) + .add_choice(&[(1.into(), -1), (2.into(), -1)]), Comparator::LessEq, -2, ) @@ -743,18 +729,22 @@ mod tests { .aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 3)) + (2, 3) + (4, 5) + (3, 3), + LinExp::from_slices(&[3, 3, 5, 3], &lits![1, 2, 4, 3]), Comparator::LessEq, 10 ) ), Ok(LinVariant::Linear(Linear { terms: vec![ - Part::Ic(vec![(5, 3.into()), (6, 3.into()), (7, 3.into())]), - Part::Amo(vec![(4, 5.into())]), + Part::Ic(vec![ + (5.into(), PosCoeff::new(3)), + (6.into(), PosCoeff::new(3)), + (7.into(), PosCoeff::new(3)) + ]), + Part::Amo(vec![(4.into(), PosCoeff::new(5))]), ], cmp: LimitComp::LessEq, - k: 10.into(), + k: PosCoeff::new(10), })) ); db.check_complete() @@ -769,18 +759,21 @@ mod tests { .aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 5)) + (2, 5) + (3, 5) + (4, 5) + (5, 4), + LinExp::from_slices(&[5, 5, 5, 5, 4], &lits![1, 2, 3, 4, 5]), Comparator::LessEq, 12 // only need 2 to sort ) ), Ok(LinVariant::Linear(Linear { terms: vec![ - Part::Amo(vec![(5, 4.into())]), - Part::Ic(vec![(6, 5.into()), (7, 5.into())]), + Part::Amo(vec![(5.into(), PosCoeff::new(4))]), + Part::Ic(vec![ + (6.into(), PosCoeff::new(5)), + (7.into(), PosCoeff::new(5)) + ]), ], cmp: LimitComp::LessEq, - k: 12.into(), + k: PosCoeff::new(12), })) ); db.check_complete() @@ -794,13 +787,13 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 1) + (3, 1), + LinExp::from_slices(&[1, 1, 1], &lits![1, 2, 3]), Comparator::Equal, 1 ) ), Ok(LinVariant::CardinalityOne(CardinalityOne { - lits: vec![1, 2, 3], + lits: lits![1, 2, 3], cmp: LimitComp::Equal })) ); @@ -816,7 +809,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 2)) + (2, 3) + (3, -2), + LinExp::from_slices(&[2, 3, -2], &lits![1, 2, 3]), Comparator::LessEq, 2 ) @@ -824,7 +817,7 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: construct_terms(&[(1, 2), (2, 3), (-3, 2)]), cmp: LimitComp::LessEq, - k: 4.into(), + k: PosCoeff::new(4), })) ); @@ -833,13 +826,13 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, -1)) + (2, -1) + (3, -1), + LinExp::from_slices(&[-1, -1, -1], &lits![1, 2, 3]), Comparator::LessEq, -2, ) ), Ok(LinVariant::CardinalityOne(CardinalityOne { - lits: vec![-1, -2, -3], + lits: lits![-1, -2, -3], cmp: LimitComp::LessEq })) ); @@ -847,7 +840,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, -1)) + (2, -2) + (3, -3), + LinExp::from_slices(&[-1, -2, -3], &lits![1, 2, 3]), Comparator::LessEq, -2, ) @@ -855,7 +848,7 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: construct_terms(&[(-1, 1), (-2, 2), (-3, 3)]), cmp: LimitComp::LessEq, - k: 4.into(), + k: PosCoeff::new(4), })) ); @@ -866,19 +859,27 @@ mod tests { &mut db, &LinearConstraint::new( LinExp::default() - .add_choice(&[(1, -1), (2, -3), (3, -4)]) - .add_choice(&[(4, -2), (5, -3), (6, -5)]), + .add_choice(&[(1.into(), -1), (2.into(), -3), (3.into(), -4)]) + .add_choice(&[(4.into(), -2), (5.into(), -3), (6.into(), -5)]), Comparator::LessEq, -4, ) ), Ok(LinVariant::Linear(Linear { terms: vec![ - Part::Amo(vec![(1, 3.into()), (2, 1.into()), (7, 4.into())]), - Part::Amo(vec![(4, 3.into()), (5, 2.into()), (8, 5.into())]), + Part::Amo(vec![ + (1.into(), PosCoeff::new(3)), + (2.into(), PosCoeff::new(1)), + (7.into(), PosCoeff::new(4)) + ]), + Part::Amo(vec![ + (4.into(), PosCoeff::new(3)), + (5.into(), PosCoeff::new(2)), + (8.into(), PosCoeff::new(5)) + ]), ], cmp: LimitComp::LessEq, - k: 5.into(), + k: PosCoeff::new(5), })) ); @@ -889,12 +890,12 @@ mod tests { &mut db, &LinearConstraint::new( LinExp::default().add_chain(&[ - (1, 1), - (2, -3), - (3, -2), - (4, 2), - (5, 5), - (6, -3) + (1.into(), 1), + (2.into(), -3), + (3.into(), -2), + (4.into(), 2), + (5.into(), 5), + (6.into(), -3) ]), Comparator::LessEq, 3 @@ -902,11 +903,19 @@ mod tests { ), Ok(LinVariant::Linear(Linear { terms: vec![ - Part::Ic(vec![(1, 1.into()), (4, 2.into()), (5, 5.into())]), - Part::Ic(vec![(-6, 3.into()), (-3, 2.into()), (-2, 3.into())]), + Part::Ic(vec![ + (1.into(), PosCoeff::new(1)), + (4.into(), PosCoeff::new(2)), + (5.into(), PosCoeff::new(5)) + ]), + Part::Ic(vec![ + ((-6).into(), PosCoeff::new(3)), + ((-3).into(), PosCoeff::new(2)), + ((-2).into(), PosCoeff::new(3)) + ]), ], cmp: LimitComp::LessEq, - k: 11.into(), + k: PosCoeff::new(11), })) ); @@ -917,8 +926,8 @@ mod tests { &mut db, &LinearConstraint::new( LinExp::default() - .add_choice(&[(1, 1), (2, 2), (3, 3), (4, 4)]) - .add_choice(&[(5, 1), (6, 3)]), + .add_choice(&[(1.into(), 1), (2.into(), 2), (3.into(), 3), (4.into(), 4)]) + .add_choice(&[(5.into(), 1), (6.into(), 3)]), Comparator::GreaterEq, 3, ) @@ -926,15 +935,18 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: vec![ Part::Amo(vec![ - (1, 3.into()), - (2, 2.into()), - (3, 1.into()), - (7, 4.into()) + (1.into(), PosCoeff::new(3)), + (2.into(), PosCoeff::new(2)), + (3.into(), PosCoeff::new(1)), + (7.into(), PosCoeff::new(4)) + ]), + Part::Amo(vec![ + (5.into(), PosCoeff::new(2)), + (8.into(), PosCoeff::new(3)) ]), - Part::Amo(vec![(5, 2.into()), (8, 3.into())]), ], cmp: LimitComp::LessEq, - k: 4.into(), // -3 + 4 + 3 + k: PosCoeff::new(4), // -3 + 4 + 3 })) ); @@ -945,8 +957,8 @@ mod tests { &mut db, &LinearConstraint::new( LinExp::default() - .add_chain(&[(1, 1), (2, 1), (3, 1), (4, 1)]) - .add_chain(&[(5, 1), (6, 2)]), + .add_chain(&[(1.into(), 1), (2.into(), 1), (3.into(), 1), (4.into(), 1)]) + .add_chain(&[(5.into(), 1), (6.into(), 2)]), Comparator::GreaterEq, 3, ) @@ -954,15 +966,18 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: vec![ Part::Ic(vec![ - (-4, 1.into()), - (-3, 1.into()), - (-2, 1.into()), - (-1, 1.into()), + ((-4).into(), PosCoeff::new(1)), + ((-3).into(), PosCoeff::new(1)), + ((-2).into(), PosCoeff::new(1)), + ((-1).into(), PosCoeff::new(1)), + ]), + Part::Ic(vec![ + ((-6).into(), PosCoeff::new(2)), + ((-5).into(), PosCoeff::new(1)) ]), - Part::Ic(vec![(-6, 2.into()), (-5, 1.into())]), ], cmp: LimitComp::LessEq, - k: 4.into(), + k: PosCoeff::new(4), })) ); @@ -972,19 +987,27 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::default().add_bounded_log_encoding(&[(1, 1), (2, 2), (3, 4)], 0, 3), + LinExp::default().add_bounded_log_encoding( + &[(1.into(), 1), (2.into(), 2), (3.into(), 4)], + 0, + 3 + ), Comparator::LessEq, 5, ) ), Ok(LinVariant::Linear(Linear { terms: vec![Part::Dom( - vec![(1, 1.into()), (2, 2.into()), (3, 4.into())], - 0.into(), - 7.into() + vec![ + (1.into(), PosCoeff::new(1)), + (2.into(), PosCoeff::new(2)), + (3.into(), PosCoeff::new(4)) + ], + PosCoeff::new(0), + PosCoeff::new(7) ),], cmp: LimitComp::LessEq, - k: 5.into(), + k: PosCoeff::new(5), })) ); @@ -995,8 +1018,12 @@ mod tests { &mut db, &LinearConstraint::new( LinExp::default() - .add_bounded_log_encoding(&[(1, 1), (2, 2), (3, 4)], 0, 5) - .add_bounded_log_encoding(&[(4, 3), (5, 6)], 0, 2), + .add_bounded_log_encoding( + &[(1.into(), 1), (2.into(), 2), (3.into(), 4)], + 0, + 5 + ) + .add_bounded_log_encoding(&[(4.into(), 3), (5.into(), 6)], 0, 2), Comparator::GreaterEq, 3, ) @@ -1004,14 +1031,25 @@ mod tests { Ok(LinVariant::Linear(Linear { terms: vec![ Part::Dom( - vec![(-1, 1.into()), (-2, 2.into()), (-3, 4.into())], - 2.into(), - 7.into() + vec![ + ((-1).into(), PosCoeff::new(1)), + ((-2).into(), PosCoeff::new(2)), + ((-3).into(), PosCoeff::new(4)) + ], + PosCoeff::new(2), + PosCoeff::new(7), + ), + Part::Dom( + vec![ + ((-4).into(), PosCoeff::new(3)), + ((-5).into(), PosCoeff::new(6)) + ], + PosCoeff::new(7), + PosCoeff::new(9), ), - Part::Dom(vec![(-4, 3.into()), (-5, 6.into())], 7.into(), 9.into()), ], cmp: LimitComp::LessEq, - k: 13.into(), + k: PosCoeff::new(13), })) ); } @@ -1023,7 +1061,7 @@ mod tests { LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (-2, 2) + (3, 1) + (4, 1) + (-5, 4) + (6, 1) + (-7, 1), + LinExp::from_slices(&[1, 2, 1, 1, 4, 1, 1], &lits![1, -2, 3, 4, -5, 6, -7]), Comparator::GreaterEq, 7 ) @@ -1039,7 +1077,7 @@ mod tests { (-3, 1) ]), cmp: LimitComp::LessEq, - k: 4.into(), + k: PosCoeff::new(4), })) ); db.check_complete(); @@ -1047,13 +1085,13 @@ mod tests { #[test] fn test_at_least_one_negated() { - let mut db = TestDB::new(4).expect_clauses(vec![vec![-1, -2, -3, -4]]); + let mut db = TestDB::new(4).expect_clauses(vec![lits![-1, -2, -3, -4]]); // An exactly one constraint adds an at most one constraint + a clause for all literals assert_eq!( LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 1) + (3, 1) + (4, 1), + LinExp::from_slices(&[1, 1, 1, 1], &lits![1, 2, 3, 4]), Comparator::LessEq, 3 ) @@ -1067,14 +1105,14 @@ mod tests { LinearAggregator::default().aggregate( &mut TestDB::new(3), &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 1) + (3, 1), + LinExp::from_slices(&[1, 1, 1], &lits![1, 2, 3]), Comparator::Equal, 2 ) ), // actually leaves over a CardinalityOne constraint Ok(LinVariant::CardinalityOne(CardinalityOne { - lits: vec![-1, -2, -3], + lits: lits![-1, -2, -3], cmp: LimitComp::LessEq, })) ); @@ -1087,12 +1125,16 @@ mod tests { // Constant cannot be reached assert_trivial_unsat!(LinearAggregator::default().aggregate( &mut db, - &LinearConstraint::new(LinExp::from((1, 1)) + (2, 2) + (3, 2), Comparator::Equal, 6) + &LinearConstraint::new( + LinExp::from_slices(&[1, 2, 2], &lits![1, 2, 3]), + Comparator::Equal, + 6 + ) )); assert_trivial_unsat!(LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 2) + (3, 2), + LinExp::from_slices(&[1, 2, 2], &lits![1, 2, 3]), Comparator::GreaterEq, 6, ) @@ -1100,7 +1142,7 @@ mod tests { assert_trivial_unsat!(LinearAggregator::default().aggregate( &mut db, &LinearConstraint::new( - LinExp::from((1, 1)) + (2, 2) + (3, 2), + LinExp::from_slices(&[1, 2, 2], &lits![1, 2, 3]), Comparator::LessEq, -1 ) @@ -1109,13 +1151,17 @@ mod tests { // Scaled counting constraint with off-scaled Constant assert_trivial_unsat!(LinearAggregator::default().aggregate( &mut db, - &LinearConstraint::new(LinExp::from((1, 4)) + (2, 4) + (3, 4), Comparator::Equal, 6) + &LinearConstraint::new( + LinExp::from_slices(&[4, 4, 4], &lits![1, 2, 3]), + Comparator::Equal, + 6 + ) )); } - impl PartialEq for Part { + impl PartialEq for Part { fn eq(&self, other: &Self) -> bool { - let term_eq = |a: &Vec<(i32, u32)>, b: &Vec<(i32, u32)>| { + let term_eq = |a: &Vec<(_, _)>, b: &Vec<(_, _)>| { itertools::equal(a.iter().sorted(), b.iter().sorted()) }; match self { @@ -1144,9 +1190,9 @@ mod tests { } } - impl PartialOrd for Part { + impl PartialOrd for Part { fn partial_cmp(&self, other: &Self) -> Option { - let termcmp = |a: &Vec<(i32, u32)>, b: &Vec<(i32, u32)>| { + let termcmp = |a: &Vec<(Lit, PosCoeff)>, b: &Vec<(Lit, PosCoeff)>| { let cmp = a.len().cmp(&b.len()); if cmp != std::cmp::Ordering::Equal { cmp @@ -1190,18 +1236,14 @@ mod tests { } } - impl PartialEq for LinVariant { + impl PartialEq for LinVariant { fn eq(&self, other: &Self) -> bool { let liteq = - |a: &Vec, b: &Vec| itertools::equal(a.iter().sorted(), b.iter().sorted()); - let parteq = |a: &Vec>>, b: &Vec>>| { + |a: &Vec, b: &Vec| itertools::equal(a.iter().sorted(), b.iter().sorted()); + let parteq = |a: &Vec, b: &Vec| { itertools::equal( - a.iter() - .map(|p| p.iter().sorted().collect::)>>()) - .sorted(), - b.iter() - .map(|p| p.iter().sorted().collect::)>>()) - .sorted(), + a.iter().map(|p| p.iter().sorted().collect_vec()).sorted(), + b.iter().map(|p| p.iter().sorted().collect_vec()).sorted(), ) }; match self { diff --git a/crates/pindakaas/src/linear/bdd.rs b/crates/pindakaas/src/linear/bdd.rs index 6fe05a097..ef0e9e895 100755 --- a/crates/pindakaas/src/linear/bdd.rs +++ b/crates/pindakaas/src/linear/bdd.rs @@ -1,59 +1,54 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, ops::Range, rc::Rc}; use iset::IntervalMap; use itertools::Itertools; -use crate::Literal; -#[allow(unused_imports)] +use super::PosCoeff; use crate::{ - int::{IntVarEnc, IntVarOrd, Lin, Model}, + int::{IntVarEnc, Lin, Model}, linear::LimitComp, - ClauseDatabase, Coefficient, Encoder, Linear, PosCoeff, Result, + ClauseDatabase, Coeff, Encoder, Linear, Result, }; /// Encode the constraint that ∑ coeffᵢ·litsᵢ ≦ k using a Binary Decision Diagram (BDD) #[derive(Default, Clone)] -pub struct BddEncoder { +pub struct BddEncoder { add_consistency: bool, - cutoff: Option, + cutoff: Option, } -impl BddEncoder { +impl BddEncoder { pub fn add_consistency(&mut self, b: bool) -> &mut Self { self.add_consistency = b; self } - pub fn add_cutoff(&mut self, c: Option) -> &mut Self { + pub fn add_cutoff(&mut self, c: Option) -> &mut Self { self.cutoff = c; self } } -impl Encoder> for BddEncoder -where - DB: ClauseDatabase, - C: Coefficient, -{ +impl Encoder for BddEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "bdd_encoder", skip_all, fields(constraint = lin.trace_print())) )] - fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { + fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { let xs = lin .terms .iter() .enumerate() - .flat_map(|(i, part)| IntVarEnc::from_part(db, part, lin.k.clone(), format!("x_{i}"))) - .sorted_by(|a: &IntVarEnc<_, C>, b: &IntVarEnc<_, C>| b.ub().cmp(&a.ub())) // sort by *decreasing* ub - .collect::>(); + .flat_map(|(i, part)| IntVarEnc::from_part(db, part, lin.k, format!("x_{i}"))) + .sorted_by(|a: &IntVarEnc, b: &IntVarEnc| b.ub().cmp(&a.ub())) // sort by *decreasing* ub + .collect_vec(); - let mut model = Model::new(); + let mut model = Model::default(); - let ys = construct_bdd(&xs, &lin.cmp, lin.k.clone()); + let ys = construct_bdd(&xs, &lin.cmp, lin.k); let xs = xs .into_iter() .map(|x| Rc::new(RefCell::new(model.add_int_var_enc(x)))) - .collect::>(); + .collect_vec(); // TODO cannot avoid? #[allow(clippy::needless_collect)] @@ -66,9 +61,9 @@ where .into_iter(..) .filter_map(|(iv, node)| match node { BddNode::Gap => None, - BddNode::Val => Some(iv.end - C::one()), + BddNode::Val => Some(iv.end - 1), BddNode::View(view) => { - let val = iv.end - C::one(); + let val = iv.end - 1; views.insert(val, view); Some(val) } @@ -83,7 +78,7 @@ where y })) }) - .collect::>(); + .collect_vec(); let mut ys = ys.into_iter(); let first = ys.next().unwrap(); @@ -100,21 +95,21 @@ where } } -fn construct_bdd( - xs: &Vec>, +fn construct_bdd( + xs: &Vec, cmp: &LimitComp, - k: PosCoeff, -) -> Vec>> { + k: PosCoeff, +) -> Vec> { let k = *k; let bounds = xs .iter() - .scan((C::zero(), C::zero()), |state, x| { + .scan((0, 0), |state, x| { *state = (state.0 + x.lb(), state.1 + x.ub()); Some(*state) }) - .chain(std::iter::once((C::zero(), k))) - .collect::>(); + .chain(std::iter::once((0, k))) + .collect_vec(); // TODO ? also hard to avoid? #[allow(clippy::needless_collect)] @@ -125,9 +120,9 @@ fn construct_bdd( *state = (state.0 - x.ub(), state.1 - x.lb()); Some(*state) }) - .collect::>(); + .collect_vec(); - let inf = xs.iter().fold(C::zero(), |a, x| a + x.ub()) + C::one(); + let inf = xs.iter().fold(0, |a, x| a + x.ub()) + 1; let mut ws = margins .into_iter() @@ -137,13 +132,13 @@ fn construct_bdd( .map(|((lb_margin, ub_margin), (lb, ub))| { match cmp { LimitComp::LessEq => vec![ - (lb_margin > lb).then_some((C::zero()..(lb_margin + C::one()), BddNode::Val)), - (ub_margin <= ub).then_some(((ub_margin + C::one())..inf, BddNode::Gap)), + (lb_margin > lb).then_some((0..(lb_margin + 1), BddNode::Val)), + (ub_margin <= ub).then_some(((ub_margin + 1)..inf, BddNode::Gap)), ], LimitComp::Equal => vec![ - (lb_margin > lb).then_some((C::zero()..lb_margin, BddNode::Gap)), - (lb_margin == ub_margin).then_some((k..(k + C::one()), BddNode::Val)), - (ub_margin <= ub).then_some(((ub_margin + C::one())..inf, BddNode::Gap)), + (lb_margin > lb).then_some((0..lb_margin, BddNode::Gap)), + (lb_margin == ub_margin).then_some((k..(k + 1), BddNode::Val)), + (ub_margin <= ub).then_some(((ub_margin + 1)..inf, BddNode::Gap)), ], } .into_iter() @@ -152,38 +147,38 @@ fn construct_bdd( }) .collect(); - bdd(0, xs, C::zero(), &mut ws); + bdd(0, xs, 0, &mut ws); ws } #[derive(Debug, Clone, PartialEq)] -enum BddNode { +enum BddNode { Val, Gap, - View(C), + View(Coeff), } -fn bdd( +fn bdd( i: usize, - xs: &Vec>, - sum: C, - ws: &mut Vec>>, -) -> (std::ops::Range, BddNode) { - match &ws[i].overlap(sum).collect::>()[..] { + xs: &Vec, + sum: Coeff, + ws: &mut Vec>, +) -> (Range, BddNode) { + match &ws[i].overlap(sum).collect_vec()[..] { [] => { let views = xs[i] .dom() .iter(..) - .map(|v| v.end - C::one()) + .map(|v| v.end - 1) .map(|v| (v, bdd(i + 1, xs, sum + v, ws))) - .collect::>(); + .collect_vec(); // TODO could we check whether a domain value of x always leads to gaps? let is_gap = views.iter().all(|(_, (_, v))| v == &BddNode::Gap); // TODO without checking actual Val identity, could we miss when the next layer has two // adjacent nodes that are both views on the same node at the layer below? let view = (views.iter().map(|(_, (iv, _))| iv).all_equal()) - .then(|| views.first().unwrap().1 .0.end - C::one()); + .then(|| views.first().unwrap().1 .0.end - 1); let interval = views .into_iter() @@ -219,12 +214,13 @@ mod tests { use super::*; use crate::{ // cardinality_one::tests::card1_test_suite, CardinalityOne, - helpers::tests::assert_sol, + helpers::tests::{assert_sol, lits}, linear::{ tests::{construct_terms, linear_test_suite}, LimitComp, }, Encoder, + Lit, }; linear_test_suite!(BddEncoder::default()); // FIXME: BDD does not support LimitComp::Equal diff --git a/crates/pindakaas/src/linear/swc.rs b/crates/pindakaas/src/linear/swc.rs index 51e03944d..92f491486 100644 --- a/crates/pindakaas/src/linear/swc.rs +++ b/crates/pindakaas/src/linear/swc.rs @@ -1,18 +1,21 @@ -use crate::int::{Consistency, Lin, Model}; -use crate::{int::IntVarEnc, ClauseDatabase, Coefficient, Encoder, Linear, Result}; +use std::{cell::RefCell, rc::Rc}; + use itertools::Itertools; -use std::cell::RefCell; -use std::rc::Rc; + +use crate::{ + int::{Consistency, IntVarEnc, Lin, Model}, + ClauseDatabase, Coeff, Encoder, Linear, Result, +}; /// Encode the constraint that ∑ coeffᵢ·litsᵢ ≦ k using a Sorted Weight Counter (SWC) #[derive(Clone, Default)] -pub struct SwcEncoder { +pub struct SwcEncoder { add_consistency: bool, add_propagation: Consistency, - cutoff: Option, + cutoff: Option, } -impl SwcEncoder { +impl SwcEncoder { pub fn add_consistency(&mut self, b: bool) -> &mut Self { self.add_consistency = b; self @@ -21,48 +24,43 @@ impl SwcEncoder { self.add_propagation = c; self } - pub fn add_cutoff(&mut self, c: Option) -> &mut Self { + pub fn add_cutoff(&mut self, c: Option) -> &mut Self { self.cutoff = c; self } } -impl Encoder> for SwcEncoder { +impl Encoder for SwcEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "swc_encoder", skip_all, fields(constraint = lin.trace_print())) )] - fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { - // self.cutoff = -C::one(); + fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { + // self.cutoff = -1; // self.add_consistency = true; - let mut model = Model::new(); + let mut model = Model::default(); let xs = lin .terms .iter() .enumerate() - .flat_map(|(i, part)| IntVarEnc::from_part(db, part, lin.k.clone(), format!("x_{i}"))) + .flat_map(|(i, part)| IntVarEnc::from_part(db, part, lin.k, format!("x_{i}"))) .map(|x| Rc::new(RefCell::new(model.add_int_var_enc(x)))) - .collect::>(); + .collect_vec(); let n = xs.len(); // TODO not possible to fix since both closures use db? #[allow(clippy::needless_collect)] // TODO no idea how to avoid collect - let ys = std::iter::once(model.new_constant(C::zero())) + let ys = std::iter::once(model.new_constant(0)) .chain( (1..n) - .map(|_| { - model.new_var( - num::iter::range_inclusive(-*lin.k, C::zero()).collect(), - self.add_consistency, - ) - }) + .map(|_| model.new_var((-(*lin.k)..=0).collect(), self.add_consistency)) .take(n), ) - .collect::>() + .collect_vec() .into_iter() .chain(std::iter::once(model.new_constant(-*lin.k))) .map(|y| Rc::new(RefCell::new(y))) - .collect::>(); + .collect_vec(); ys.into_iter() .tuple_windows() @@ -85,12 +83,12 @@ mod tests { use super::*; use crate::{ - helpers::tests::assert_sol, + helpers::tests::{assert_sol, lits}, linear::{ tests::{construct_terms, linear_test_suite}, - LimitComp, + LimitComp, PosCoeff, }, - Encoder, + Encoder, Lit, }; linear_test_suite!(SwcEncoder::default()); diff --git a/crates/pindakaas/src/linear/totalizer.rs b/crates/pindakaas/src/linear/totalizer.rs index 72eb7f70f..c26cdfab3 100755 --- a/crates/pindakaas/src/linear/totalizer.rs +++ b/crates/pindakaas/src/linear/totalizer.rs @@ -1,29 +1,25 @@ -use crate::int::Consistency; -pub(crate) use crate::int::IntVar; -use crate::int::Lin; -use crate::int::Model; -use crate::Literal; -use std::cell::RefCell; -use std::collections::BTreeSet; -use std::rc::Rc; +use std::{cell::RefCell, collections::BTreeSet, rc::Rc}; use itertools::Itertools; +pub(crate) use crate::int::IntVar; use crate::{ - int::IntVarEnc, linear::LimitComp, ClauseDatabase, Coefficient, Encoder, Linear, Result, + int::{Consistency, IntVarEnc, Lin, Model}, + linear::LimitComp, + ClauseDatabase, Coeff, Encoder, Linear, Result, }; const EQUALIZE_INTERMEDIATES: bool = false; /// Encode the constraint that ∑ coeffᵢ·litsᵢ ≦ k using a Generalized Totalizer (GT) #[derive(Clone, Default)] -pub struct TotalizerEncoder { +pub struct TotalizerEncoder { add_consistency: bool, add_propagation: Consistency, - cutoff: Option, + cutoff: Option, } -impl TotalizerEncoder { +impl TotalizerEncoder { pub fn add_consistency(&mut self, b: bool) -> &mut Self { self.add_consistency = b; self @@ -32,25 +28,25 @@ impl TotalizerEncoder { self.add_propagation = c; self } - pub fn add_cutoff(&mut self, c: Option) -> &mut Self { + pub fn add_cutoff(&mut self, c: Option) -> &mut Self { self.cutoff = c; self } } -impl Encoder> for TotalizerEncoder { +impl Encoder for TotalizerEncoder { #[cfg_attr( feature = "trace", tracing::instrument(name = "totalizer_encoder", skip_all, fields(constraint = lin.trace_print())) )] - fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { + fn encode(&mut self, db: &mut DB, lin: &Linear) -> Result { let xs = lin .terms .iter() .enumerate() - .flat_map(|(i, part)| IntVarEnc::from_part(db, part, lin.k.clone(), format!("x_{i}"))) + .flat_map(|(i, part)| IntVarEnc::from_part(db, part, lin.k, format!("x_{i}"))) .sorted_by_key(|x| x.ub()) - .collect::>(); + .collect_vec(); // The totalizer encoding constructs a binary tree starting from a layer of leaves let mut model = self.build_totalizer(xs, &lin.cmp, *lin.k); @@ -59,21 +55,16 @@ impl Encoder> for Tot } } -impl TotalizerEncoder { - fn build_totalizer( - &self, - xs: Vec>, - cmp: &LimitComp, - k: C, - ) -> Model { - let mut model = Model::new(); +impl TotalizerEncoder { + fn build_totalizer(&self, xs: Vec, cmp: &LimitComp, k: Coeff) -> Model { + let mut model = Model::default(); let mut layer = xs .into_iter() .map(|x| Rc::new(RefCell::new(model.add_int_var_enc(x)))) - .collect::>(); + .collect_vec(); while layer.len() > 1 { - let mut next_layer = Vec::>>>::new(); + let mut next_layer = Vec::>>::new(); for children in layer.chunks(2) { match children { [x] => { @@ -127,43 +118,44 @@ mod tests { use super::*; use crate::{ // cardinality_one::tests::card1_test_suite, CardinalityOne, - helpers::tests::{assert_sol, TestDB}, + helpers::tests::{assert_sol, lits, TestDB}, linear::{ tests::{construct_terms, linear_test_suite}, - LimitComp, + LimitComp, PosCoeff, }, Comparator, Encoder, LinExp, LinearAggregator, LinearConstraint, + Lit, SortedEncoder, }; #[test] fn test_sort_same_coefficients_2() { - use crate::linear::{LinearEncoder, StaticLinEncoder}; - use crate::{Checker, Encoder}; + use crate::{ + linear::{LinearEncoder, StaticLinEncoder}, + Checker, Encoder, + }; let mut db = TestDB::new(5); let mut agg = LinearAggregator::default(); agg.sort_same_coefficients(SortedEncoder::default(), 3); - let mut encoder = LinearEncoder::>>::default(); + let mut encoder = LinearEncoder::>::default(); encoder.add_linear_aggregater(agg); let con = LinearConstraint::new( - LinExp::new() + (1, 3) + (2, 3) + (3, 1) + (4, 1) + (5, 3), + LinExp::from_slices(&[3, 3, 1, 1, 3], &lits![1, 2, 3, 4, 5]), Comparator::GreaterEq, 2, ); assert!(encoder.encode(&mut db, &con).is_ok()); - db.with_check(|sol| { - { - let con = LinearConstraint::new( - LinExp::new() + (1, 3) + (2, 3) + (3, 1) + (4, 1) + (5, 3), - Comparator::GreaterEq, - 2, - ); - con.check(sol) - } + db.with_check(|value| { + LinearConstraint::new( + LinExp::from_slices(&[3, 3, 1, 1, 3], &lits![1, 2, 3, 4, 5]), + Comparator::GreaterEq, + 2, + ) + .check(value) .is_ok() }) .check_complete() diff --git a/crates/pindakaas/src/solvers.rs b/crates/pindakaas/src/solvers.rs index fbf731cab..59340d44f 100644 --- a/crates/pindakaas/src/solvers.rs +++ b/crates/pindakaas/src/solvers.rs @@ -1,58 +1,66 @@ -use crate::Cnf; -use itertools::Itertools; -use num::{One, Zero}; -use std::{ffi::c_int, ops::AddAssign}; +use std::num::NonZeroI32; + +use crate::{ClauseDatabase, Cnf, Lit, Result, Var}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct VarFactory { + pub(crate) next_var: Option, +} + +impl VarFactory { + const MAX_VARS: usize = NonZeroI32::MAX.get() as usize; + + pub fn emited_vars(&self) -> usize { + if let Some(x) = self.next_var { + x.0.get() as usize - 1 + } else { + Self::MAX_VARS + } + } +} + +impl Default for VarFactory { + fn default() -> Self { + Self { + next_var: Some(Var(NonZeroI32::new(1).unwrap())), + } + } +} + +impl Iterator for VarFactory { + type Item = Var; + + fn next(&mut self) -> Option { + let var = self.next_var; + if let Some(var) = var { + self.next_var = var.next_var(); + } + var + } +} -use crate::{ClauseDatabase, Literal}; pub struct IpasirSolver { slv: ipasir::ffi::Solver, - last_var: Option, + nvar: VarFactory, } impl ClauseDatabase for IpasirSolver { - type Lit = c_int; // FIXME: Should be ipasir::Lit, but that does not implement Hash - fn new_var(&mut self) -> Self::Lit { - match self.last_var { - None => { - self.last_var = Some(ipasir::Lit::try_from(1).unwrap().var()); - 1 - } - Some(x) => { - let new_var = x.to_raw() + 1; - self.last_var = Some(ipasir::Lit::try_from(new_var).unwrap().var()); - new_var - } - } + fn new_var(&mut self) -> Lit { + self.nvar.next().expect("variable pool exhausted").into() } - fn add_clause(&mut self, cl: &[Self::Lit]) -> crate::Result { + fn add_clause>(&mut self, cl: I) -> Result { use ipasir::IpasirSolver as SolverProtocol; - self.slv.add_clause( - cl.iter() - .map(|lit| ipasir::Lit::try_from(*lit).unwrap()) - .collect_vec(), - ); + self.slv.add_clause(cl); Ok(()) } } -impl> From> for IpasirSolver { - fn from(cnf: Cnf) -> Self { +impl From<&Cnf> for IpasirSolver { + fn from(cnf: &Cnf) -> Self { use ipasir::IpasirSolver as SolverProtocol; - let mut slv = IpasirSolver { - slv: ipasir::ffi::Solver::init(), - last_var: Some( - ipasir::Lit::try_from(cnf.last_var.clone().into()) - .unwrap() - .var(), - ), - }; + let mut slv = Self::init(); + slv.nvar = cnf.nvar.clone(); for cl in cnf.iter() { - slv.slv.add_clause( - cl.iter() - .map(|lit| { - let lit: c_int = lit.clone().into(); - ipasir::Lit::try_from(lit).unwrap() - }) - .collect_vec(), - ) + // Ignore result from add_clause (known to be Ok in this case) + let _ = ClauseDatabase::add_clause(&mut slv, cl.iter().copied()); } slv } @@ -64,7 +72,7 @@ impl ipasir::IpasirSolver for IpasirSolver { fn init() -> Self { IpasirSolver { slv: ipasir::ffi::Solver::init(), - last_var: None, + nvar: VarFactory::default(), } } fn add_clause, L: Into>(&mut self, lits: I) { @@ -90,44 +98,9 @@ impl ipasir::IpasirSolver for IpasirSolver { } } -#[cfg(feature = "minisat")] -pub use minisat::Solver as MiniSatSolver; -#[cfg(feature = "minisat")] -impl Literal for minisat::Lit { - fn negate(&self) -> Self { - !(*self) - } - fn is_negated(&self) -> bool { - minisat::Lit::var(*self).1 - } -} -#[cfg(feature = "minisat")] -impl ClauseDatabase for MiniSatSolver { - type Lit = minisat::Lit; - fn new_var(&mut self) -> Self::Lit { - self.new_lit() - } - fn add_clause(&mut self, cl: &[Self::Lit]) -> crate::Result { - self.add_clause(cl.iter().cloned()); - Ok(()) - } -} -#[cfg(feature = "minisat")] -impl> From> for MiniSatSolver { - fn from(cnf: Cnf) -> Self { - let mut slv = minisat::Solver::new(); - let mut map = rustc_hash::FxHashMap::default(); - for cl in cnf.iter() { - let cl = cl - .iter() - .map(|lit| { - let ival: c_int = lit.clone().into(); - *map.entry(ival).or_insert_with(|| slv.new_var()) - }) - .collect_vec(); - slv.add_clause(cl) - } - slv +impl From for ipasir::Lit { + fn from(_val: Lit) -> Self { + todo!() } } @@ -135,19 +108,19 @@ impl> From> for MiniSatSolver { pub use splr::Solver as SplrSolver; #[cfg(feature = "splr")] impl ClauseDatabase for SplrSolver { - type Lit = i32; - fn new_var(&mut self) -> Self::Lit { + fn new_var(&mut self) -> Lit { use splr::SatSolverIF; - self.add_var() - .try_into() - .expect("unable to convert splr variable into literal") + let var = self.add_var(); + let var: i32 = var.try_into().expect("exhausted variable pool"); + Lit(NonZeroI32::new(var).expect("variables cannot use the value zero")) } - fn add_clause(&mut self, cl: &[Self::Lit]) -> crate::Result { + fn add_clause>(&mut self, cl: I) -> crate::Result { use splr::{SatSolverIF, SolverError::*}; + let cl: Vec<_> = cl.into_iter().map(Into::::into).collect(); match SatSolverIF::add_clause(self, cl) { Ok(_) => Ok(()), Err(e) => match e { - OutOfRange => panic!("clause referenced a non-existing variable"), + InvalidLiteral => panic!("clause referenced a non-existing variable"), RootLevelConflict(_) | EmptyClause | Inconsistent => Err(crate::Unsatisfiable), TimeOut => unreachable!(), SolverBug | UndescribedError | IOError | OutOfMemory => { @@ -158,8 +131,8 @@ impl ClauseDatabase for SplrSolver { } } #[cfg(feature = "splr")] -impl> From> for SplrSolver { - fn from(cnf: Cnf) -> Self { +impl From for SplrSolver { + fn from(cnf: Cnf) -> Self { use splr::{ types::{CNFDescription, Instantiate}, Config, @@ -167,17 +140,13 @@ impl> From> for SplrSolver { let mut slv = SplrSolver::instantiate( &Config::default(), &CNFDescription { - num_of_variables: cnf.last_var.clone().into() as usize, + num_of_variables: cnf.nvar.emited_vars(), ..CNFDescription::default() }, ); for cl in cnf.iter() { - if slv - .add_clause(&cl.iter().map(|lit| lit.clone().into()).collect_vec()) - .is_err() - { - // Ignore early detected unsatisfiability - }; + // Ignore early detected unsatisfiability + let _ = slv.add_clause(cl.iter().copied()); } slv } @@ -193,9 +162,10 @@ mod tests { #[cfg(feature = "splr")] #[test] fn test_splr() { + use splr::{Certificate, SolveIF}; + use super::*; use crate::{linear::LimitComp, CardinalityOne, Encoder, PairwiseEncoder}; - use splr::{Certificate, SolveIF}; let mut slv = splr::Solver::default(); let a = slv.new_var(); @@ -210,6 +180,6 @@ mod tests { ) .unwrap(); let res = slv.solve().unwrap(); - assert!(res == Certificate::SAT(vec![-a, b]) || res == Certificate::SAT(vec![-a, b])) + assert!(res == Certificate::SAT(vec![!a, b]) || res == Certificate::SAT(vec![-a, b])) } } diff --git a/crates/pindakaas/src/sorted.rs b/crates/pindakaas/src/sorted.rs index dde73e219..ba4416ecb 100644 --- a/crates/pindakaas/src/sorted.rs +++ b/crates/pindakaas/src/sorted.rs @@ -1,16 +1,15 @@ +use cached::proc_macro::cached; +use iset::interval_map; +use itertools::Itertools; + use crate::{ helpers::{add_clauses_for, negate_cnf}, int::{IntVarEnc, IntVarOrd, TernLeConstraint, TernLeEncoder}, linear::LimitComp, trace::emit_clause, - CheckError, Checker, ClauseDatabase, Coefficient, Encoder, LinExp, Literal, Result, - Unsatisfiable, + CheckError, Checker, ClauseDatabase, Coeff, Encoder, LinExp, Lit, Result, Unsatisfiable, + Valuation, }; -use cached::proc_macro::cached; - -use iset::interval_map; -use itertools::Itertools; -use num::Integer; #[derive(Clone)] pub struct SortedEncoder { @@ -58,35 +57,28 @@ impl SortedEncoder { } } -pub struct Sorted<'a, Lit: Literal, C: Coefficient> { +pub struct Sorted<'a> { pub(crate) xs: &'a [Lit], pub(crate) cmp: LimitComp, - pub(crate) y: &'a IntVarEnc, + pub(crate) y: &'a IntVarEnc, } -impl<'a, Lit: Literal, C: Coefficient> Sorted<'a, Lit, C> { - pub(crate) fn new(xs: &'a [Lit], cmp: LimitComp, y: &'a IntVarEnc) -> Self { +impl<'a> Sorted<'a> { + pub(crate) fn new(xs: &'a [Lit], cmp: LimitComp, y: &'a IntVarEnc) -> Self { Self { xs, cmp, y } } } -impl<'a, Lit: Literal, C: Coefficient> Checker for Sorted<'a, Lit, C> { - type Lit = Lit; - fn check(&self, solution: &[Self::Lit]) -> Result<(), CheckError> { - let lhs = LinExp::from_terms( - self.xs - .iter() - .map(|x| (x.clone(), C::one())) - .collect::>() - .as_slice(), - ) - .assign(solution)?; - - let rhs = LinExp::<_, _>::from(self.y).assign(solution)?; +impl<'a> Checker for Sorted<'a> { + fn check(&self, value: F) -> Result<(), CheckError> { + let lhs = LinExp::from_terms(self.xs.iter().map(|x| (*x, 1)).collect_vec().as_slice()) + .value(&value)?; + let rhs = LinExp::from(self.y).value(&value)?; - if (self.cmp == LimitComp::LessEq && lhs <= rhs) - || (self.cmp == LimitComp::Equal && lhs == rhs) - { + if match self.cmp { + LimitComp::LessEq => lhs <= rhs, + LimitComp::Equal => lhs == rhs, + } { Ok(()) } else { Err(CheckError::Unsatisfiable(Unsatisfiable)) @@ -94,22 +86,18 @@ impl<'a, Lit: Literal, C: Coefficient> Checker for Sorted<'a, Lit, C> { } } -impl Encoder> for SortedEncoder { - fn encode(&mut self, db: &mut DB, sorted: &Sorted) -> Result { +impl Encoder> for SortedEncoder { + fn encode(&mut self, db: &mut DB, sorted: &Sorted) -> Result { let xs = sorted .xs .iter() - .map(|x| Some(x.clone())) + .map(|x| Some(*x)) .enumerate() .map(|(i, x)| { - IntVarOrd::from_views( - db, - interval_map! { C::one()..(C::one()+C::one()) => x }, - format!("x_{}", i + 1), - ) - .into() + IntVarOrd::from_views(db, interval_map! { 1..2 => x }, format!("x_{}", i + 1)) + .into() }) - .collect::>(); + .collect_vec(); if self.add_consistency { sorted.y.consistent(db).unwrap(); @@ -119,10 +107,8 @@ impl Encoder> for } } -impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder> - for SortedEncoder -{ - fn encode(&mut self, db: &mut DB, tern: &TernLeConstraint) -> Result { +impl Encoder> for SortedEncoder { + fn encode(&mut self, db: &mut DB, tern: &TernLeConstraint) -> Result { let TernLeConstraint { x, y, cmp, z } = tern; if tern.is_fixed()? { Ok(()) @@ -138,17 +124,17 @@ impl<'a, DB: ClauseDatabase, C: Coefficient> Encoder( + fn next_int_var( &mut self, db: &mut DB, - ub: C, + ub: Coeff, lbl: String, - ) -> IntVarEnc { + ) -> IntVarEnc { // TODO We always have the view x>=1 <-> y>=1, which is now realized using equiv - if ub == C::zero() { - IntVarEnc::Const(C::zero()) + if ub == 0 { + IntVarEnc::Const(0) } else { - let y = IntVarOrd::from_bounds(db, C::zero(), ub, lbl); + let y = IntVarOrd::from_bounds(db, 0, ub, lbl); if self.add_consistency { y.consistent(db).unwrap(); } @@ -157,38 +143,38 @@ impl SortedEncoder { } /// The sorted/merged base case of x1{0,1}+x2{0,1}<=y{0,1,2} - fn smerge( + fn smerge( &mut self, db: &mut DB, - x1: &IntVarEnc, - x2: &IntVarEnc, + x1: &IntVarEnc, + x2: &IntVarEnc, cmp: &LimitComp, - y: &IntVarEnc, + y: &IntVarEnc, ) -> Result { // we let x2 take the place of z_ceil, so we need to add 1 to both sides let x2 = x2.add( db, &mut TernLeEncoder::default(), - &IntVarEnc::Const(C::one()), + &IntVarEnc::Const(1), None, None, )?; let y = y.add( db, &mut TernLeEncoder::default(), - &IntVarEnc::Const(C::one()), + &IntVarEnc::Const(1), None, None, )?; - self.comp(db, x1, &x2, cmp, &y, C::one()) + self.comp(db, x1, &x2, cmp, &y, 1) } - fn sorted( + fn sorted( &mut self, db: &mut DB, - xs: &[IntVarEnc], + xs: &[IntVarEnc], cmp: &LimitComp, - y: &IntVarEnc, + y: &IntVarEnc, _lvl: usize, ) -> Result { let (n, m) = (xs.len(), y.ub()); @@ -205,19 +191,19 @@ impl SortedEncoder { // _lvl = _lvl // ); - debug_assert!(xs.iter().all(|x| x.ub() == C::one())); + debug_assert!(xs.iter().all(|x| x.ub() == 1)); if direct { - return num::range_inclusive(C::one(), m + C::one()).try_for_each(|k| { + return (1..=m + 1).try_for_each(|k| { xs.iter() - .map(|x| x.geq(C::one()..(C::one() + C::one()))[0][0].clone()) - .combinations(k.to_usize().unwrap()) + .map(|x| x.geq(1..2)[0][0]) + .combinations(k as usize) .try_for_each(|lits| { - let cl = lits - .into_iter() - .map(|lit| lit.negate()) - .chain(y.geq(k..(k + C::one()))[0].iter().cloned()) - .collect::>(); - emit_clause!(db, cl.as_slice()) + emit_clause!( + db, + lits.into_iter() + .map(|lit| !lit) + .chain(y.geq(k..(k + 1))[0].iter().cloned()) + ) }) }); } @@ -227,12 +213,12 @@ impl SortedEncoder { db, &TernLeConstraint { x, - y: &IntVarEnc::Const(C::zero()), + y: &IntVarEnc::Const(0), cmp: cmp.clone(), z: y, }, ), - [x1, x2] if m <= C::one() + C::one() => self.smerge(db, x1, x2, cmp, y), + [x1, x2] if m <= 2 => self.smerge(db, x1, x2, cmp, y), xs => { let n = match self.sort_n { SortN::One => 1, @@ -242,7 +228,7 @@ impl SortedEncoder { db, &xs[..n], cmp, - std::cmp::min((0..n).fold(C::zero(), |a, _| a + C::one()), y.ub()), + std::cmp::min((0..n).fold(0, |a, _| a + 1), y.ub()), String::from("y1"), _lvl, ); @@ -250,7 +236,7 @@ impl SortedEncoder { db, &xs[n..], cmp, - std::cmp::min((n..xs.len()).fold(C::zero(), |a, _| a + C::one()), y.ub()), + std::cmp::min((n..xs.len()).fold(0, |a, _| a + 1), y.ub()), String::from("y2"), _lvl, ); @@ -268,15 +254,15 @@ impl SortedEncoder { } } - fn sort( + fn sort( &mut self, db: &mut DB, - xs: &[IntVarEnc], + xs: &[IntVarEnc], cmp: &LimitComp, - ub: C, + ub: Coeff, lbl: String, _lvl: usize, - ) -> Option> { + ) -> Option { match xs { [] => None, [x] => Some(x.clone()), @@ -288,22 +274,17 @@ impl SortedEncoder { } } - fn merged( + fn merged( &mut self, db: &mut DB, - x1: &IntVarEnc, - x2: &IntVarEnc, + x1: &IntVarEnc, + x2: &IntVarEnc, cmp: &LimitComp, - y: &IntVarEnc, + y: &IntVarEnc, _lvl: usize, ) -> Result { let (a, b, c) = (x1.ub(), x2.ub(), y.ub()); - let (strat, _cost) = merged_cost( - a.to_u128().unwrap(), - b.to_u128().unwrap(), - c.to_u128().unwrap(), - self.strategy.clone(), - ); + let (strat, _cost) = merged_cost(a as u128, b as u128, c as u128, self.strategy.clone()); // TODO: Add tracing // eprintln!( @@ -330,33 +311,32 @@ impl SortedEncoder { ) } SortedStrategy::Recursive => { - if a.is_zero() && b.is_zero() { + if a == 0 && b == 0 { Ok(()) - } else if a.is_one() && b.is_one() && c <= C::one() + C::one() { + } else if a == 1 && b == 1 && c <= 2 { self.smerge(db, x1, x2, cmp, y) } else { - let two = C::one() + C::one(); - let x1_floor = x1.div(&two); + let x1_floor = x1.div(2); let x1_ceil = x1 .add( db, &mut TernLeEncoder::default(), - &IntVarEnc::Const(C::one()), + &IntVarEnc::Const(1), None, None, )? - .div(&two); + .div(2); - let x2_floor = x2.div(&two); + let x2_floor = x2.div(2); let x2_ceil = x2 .add( db, &mut TernLeEncoder::default(), - &IntVarEnc::Const(C::one()), + &IntVarEnc::Const(1), None, None, )? - .div(&two); + .div(2); let z_floor = x1_floor.add(db, &mut TernLeEncoder::default(), &x2_floor, None, None)?; @@ -372,7 +352,7 @@ impl SortedEncoder { &TernLeConstraint::new(&x1_ceil, &x2_ceil, cmp.clone(), &z_ceil), )?; - for c in num::iter::range_inclusive(C::zero(), c) { + for c in 0..=c { self.comp(db, &z_floor, &z_ceil, cmp, y, c)?; } @@ -383,20 +363,20 @@ impl SortedEncoder { } } - fn comp( + fn comp( &mut self, db: &mut DB, - x: &IntVarEnc, - y: &IntVarEnc, + x: &IntVarEnc, + y: &IntVarEnc, cmp: &LimitComp, - z: &IntVarEnc, - c: C, + z: &IntVarEnc, + c: Coeff, ) -> Result { let cmp = self.overwrite_recursive_cmp.as_ref().unwrap_or(cmp); - let to_iv = |c: C| c..(c + C::one()); - let empty_clause: Vec> = vec![Vec::new()]; + let to_iv = |c: Coeff| c..(c + 1); + let empty_clause: Vec> = vec![Vec::new()]; let c1 = c; - let c2 = c + C::one(); + let c2 = c + 1; let x = x.geq(to_iv(c1)); // c let y = y.geq(to_iv(c2)); // c+1 let z1 = z.geq(to_iv(c1 + c1)); // 2c @@ -460,7 +440,7 @@ fn merged_rec_cost(a: u128, b: u128, c: u128, strat: SortedStrategy) -> (u128, u merged_cost(a / 2, b / 2, c / 2, strat), ( c - 1, - if c.is_odd() { + if c % 2 == 1 { (3 * c - 3) / 2 } else { ((3 * c - 2) / 2) + 2 @@ -514,7 +494,7 @@ mod tests { use traced_test::test; use super::*; - use crate::helpers::tests::{assert_sol, TestDB}; + use crate::helpers::tests::{assert_sol, lits, TestDB}; fn get_sorted_encoder(strategy: SortedStrategy) -> SortedEncoder { SortedEncoder { @@ -528,18 +508,18 @@ mod tests { #[test] fn test_2_merged_eq() { let mut db = TestDB::new(0); - let x: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 1, String::from("x")).into(); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 1, String::from("y")).into(); - let z: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("z")).into(); + let x: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 1, String::from("x")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 1, String::from("y")).into(); + let z: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("z")).into(); db.num_var = (x.lits() + y.lits() + z.lits()) as i32; let con = TernLeConstraint::new(&x, &y, LimitComp::Equal, &z); let mut enc = get_sorted_encoder(SortedStrategy::Recursive); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4], - vec![1, -2, 3, -4], - vec![-1, 2, 3, -4], - vec![1, 2, 3, 4], + lits![-1, -2, -3, -4], + lits![1, -2, 3, -4], + lits![-1, 2, 3, -4], + lits![1, 2, 3, 4], ]; assert_sol!(db => enc, &con => sols); } @@ -547,27 +527,28 @@ mod tests { #[test] fn test_2_sorted_eq() { let mut db = TestDB::new(4); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2], LimitComp::Equal, &y); + let lits = lits![1, 2]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4, -5, -6], - vec![-1, -2, 3, -4, -5, -6], - vec![-1, -2, -3, 4, -5, -6], - vec![-1, -2, 3, 4, -5, -6], - vec![1, -2, -3, -4, 5, -6], - vec![-1, 2, -3, -4, 5, -6], - vec![1, -2, 3, -4, 5, -6], - vec![-1, 2, 3, -4, 5, -6], - vec![1, -2, -3, 4, 5, -6], - vec![-1, 2, -3, 4, 5, -6], - vec![1, -2, 3, 4, 5, -6], - vec![-1, 2, 3, 4, 5, -6], - vec![1, 2, -3, -4, 5, 6], - vec![1, 2, 3, -4, 5, 6], - vec![1, 2, -3, 4, 5, 6], - vec![1, 2, 3, 4, 5, 6], + lits![-1, -2, -3, -4, -5, -6], + lits![-1, -2, 3, -4, -5, -6], + lits![-1, -2, -3, 4, -5, -6], + lits![-1, -2, 3, 4, -5, -6], + lits![1, -2, -3, -4, 5, -6], + lits![-1, 2, -3, -4, 5, -6], + lits![1, -2, 3, -4, 5, -6], + lits![-1, 2, 3, -4, 5, -6], + lits![1, -2, -3, 4, 5, -6], + lits![-1, 2, -3, 4, 5, -6], + lits![1, -2, 3, 4, 5, -6], + lits![-1, 2, 3, 4, 5, -6], + lits![1, 2, -3, -4, 5, 6], + lits![1, 2, 3, -4, 5, 6], + lits![1, 2, -3, 4, 5, 6], + lits![1, 2, 3, 4, 5, 6], ]; let mut enc = get_sorted_encoder(SortedStrategy::Recursive); assert_sol!(db => enc, con => sols); @@ -576,75 +557,76 @@ mod tests { #[test] fn test_3_sorted_eq() { let mut db = TestDB::new(6); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 3, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 3, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3], LimitComp::Equal, &y); + let lits = lits![1, 2, 3]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4, -5, -6, -7, -8, -9], - vec![-1, -2, -3, 4, -5, -6, -7, -8, -9], - vec![-1, -2, -3, -4, 5, -6, -7, -8, -9], - vec![-1, -2, -3, 4, 5, -6, -7, -8, -9], - vec![-1, -2, -3, -4, -5, 6, -7, -8, -9], - vec![-1, -2, -3, 4, -5, 6, -7, -8, -9], - vec![-1, -2, -3, -4, 5, 6, -7, -8, -9], - vec![-1, -2, -3, 4, 5, 6, -7, -8, -9], - vec![1, -2, -3, -4, -5, -6, 7, -8, -9], - vec![-1, 2, -3, -4, -5, -6, 7, -8, -9], - vec![-1, -2, 3, -4, -5, -6, 7, -8, -9], - vec![1, -2, -3, 4, -5, -6, 7, -8, -9], - vec![-1, 2, -3, 4, -5, -6, 7, -8, -9], - vec![-1, -2, 3, 4, -5, -6, 7, -8, -9], - vec![1, -2, -3, -4, 5, -6, 7, -8, -9], - vec![-1, 2, -3, -4, 5, -6, 7, -8, -9], - vec![-1, -2, 3, -4, 5, -6, 7, -8, -9], - vec![1, -2, -3, 4, 5, -6, 7, -8, -9], - vec![-1, 2, -3, 4, 5, -6, 7, -8, -9], - vec![-1, -2, 3, 4, 5, -6, 7, -8, -9], - vec![1, -2, -3, -4, -5, 6, 7, -8, -9], - vec![-1, 2, -3, -4, -5, 6, 7, -8, -9], - vec![-1, -2, 3, -4, -5, 6, 7, -8, -9], - vec![1, -2, -3, 4, -5, 6, 7, -8, -9], - vec![-1, 2, -3, 4, -5, 6, 7, -8, -9], - vec![-1, -2, 3, 4, -5, 6, 7, -8, -9], - vec![1, -2, -3, -4, 5, 6, 7, -8, -9], - vec![-1, 2, -3, -4, 5, 6, 7, -8, -9], - vec![-1, -2, 3, -4, 5, 6, 7, -8, -9], - vec![1, -2, -3, 4, 5, 6, 7, -8, -9], - vec![-1, 2, -3, 4, 5, 6, 7, -8, -9], - vec![-1, -2, 3, 4, 5, 6, 7, -8, -9], - vec![1, 2, -3, -4, -5, -6, 7, 8, -9], - vec![1, -2, 3, -4, -5, -6, 7, 8, -9], - vec![-1, 2, 3, -4, -5, -6, 7, 8, -9], - vec![1, 2, -3, 4, -5, -6, 7, 8, -9], - vec![1, -2, 3, 4, -5, -6, 7, 8, -9], - vec![-1, 2, 3, 4, -5, -6, 7, 8, -9], - vec![1, 2, -3, -4, 5, -6, 7, 8, -9], - vec![1, -2, 3, -4, 5, -6, 7, 8, -9], - vec![-1, 2, 3, -4, 5, -6, 7, 8, -9], - vec![1, 2, -3, 4, 5, -6, 7, 8, -9], - vec![1, -2, 3, 4, 5, -6, 7, 8, -9], - vec![-1, 2, 3, 4, 5, -6, 7, 8, -9], - vec![1, 2, -3, -4, -5, 6, 7, 8, -9], - vec![1, -2, 3, -4, -5, 6, 7, 8, -9], - vec![-1, 2, 3, -4, -5, 6, 7, 8, -9], - vec![1, 2, -3, 4, -5, 6, 7, 8, -9], - vec![1, -2, 3, 4, -5, 6, 7, 8, -9], - vec![-1, 2, 3, 4, -5, 6, 7, 8, -9], - vec![1, 2, -3, -4, 5, 6, 7, 8, -9], - vec![1, -2, 3, -4, 5, 6, 7, 8, -9], - vec![-1, 2, 3, -4, 5, 6, 7, 8, -9], - vec![1, 2, -3, 4, 5, 6, 7, 8, -9], - vec![1, -2, 3, 4, 5, 6, 7, 8, -9], - vec![-1, 2, 3, 4, 5, 6, 7, 8, -9], - vec![1, 2, 3, -4, -5, -6, 7, 8, 9], - vec![1, 2, 3, 4, -5, -6, 7, 8, 9], - vec![1, 2, 3, -4, 5, -6, 7, 8, 9], - vec![1, 2, 3, 4, 5, -6, 7, 8, 9], - vec![1, 2, 3, -4, -5, 6, 7, 8, 9], - vec![1, 2, 3, 4, -5, 6, 7, 8, 9], - vec![1, 2, 3, -4, 5, 6, 7, 8, 9], - vec![1, 2, 3, 4, 5, 6, 7, 8, 9], + lits![-1, -2, -3, -4, -5, -6, -7, -8, -9], + lits![-1, -2, -3, 4, -5, -6, -7, -8, -9], + lits![-1, -2, -3, -4, 5, -6, -7, -8, -9], + lits![-1, -2, -3, 4, 5, -6, -7, -8, -9], + lits![-1, -2, -3, -4, -5, 6, -7, -8, -9], + lits![-1, -2, -3, 4, -5, 6, -7, -8, -9], + lits![-1, -2, -3, -4, 5, 6, -7, -8, -9], + lits![-1, -2, -3, 4, 5, 6, -7, -8, -9], + lits![1, -2, -3, -4, -5, -6, 7, -8, -9], + lits![-1, 2, -3, -4, -5, -6, 7, -8, -9], + lits![-1, -2, 3, -4, -5, -6, 7, -8, -9], + lits![1, -2, -3, 4, -5, -6, 7, -8, -9], + lits![-1, 2, -3, 4, -5, -6, 7, -8, -9], + lits![-1, -2, 3, 4, -5, -6, 7, -8, -9], + lits![1, -2, -3, -4, 5, -6, 7, -8, -9], + lits![-1, 2, -3, -4, 5, -6, 7, -8, -9], + lits![-1, -2, 3, -4, 5, -6, 7, -8, -9], + lits![1, -2, -3, 4, 5, -6, 7, -8, -9], + lits![-1, 2, -3, 4, 5, -6, 7, -8, -9], + lits![-1, -2, 3, 4, 5, -6, 7, -8, -9], + lits![1, -2, -3, -4, -5, 6, 7, -8, -9], + lits![-1, 2, -3, -4, -5, 6, 7, -8, -9], + lits![-1, -2, 3, -4, -5, 6, 7, -8, -9], + lits![1, -2, -3, 4, -5, 6, 7, -8, -9], + lits![-1, 2, -3, 4, -5, 6, 7, -8, -9], + lits![-1, -2, 3, 4, -5, 6, 7, -8, -9], + lits![1, -2, -3, -4, 5, 6, 7, -8, -9], + lits![-1, 2, -3, -4, 5, 6, 7, -8, -9], + lits![-1, -2, 3, -4, 5, 6, 7, -8, -9], + lits![1, -2, -3, 4, 5, 6, 7, -8, -9], + lits![-1, 2, -3, 4, 5, 6, 7, -8, -9], + lits![-1, -2, 3, 4, 5, 6, 7, -8, -9], + lits![1, 2, -3, -4, -5, -6, 7, 8, -9], + lits![1, -2, 3, -4, -5, -6, 7, 8, -9], + lits![-1, 2, 3, -4, -5, -6, 7, 8, -9], + lits![1, 2, -3, 4, -5, -6, 7, 8, -9], + lits![1, -2, 3, 4, -5, -6, 7, 8, -9], + lits![-1, 2, 3, 4, -5, -6, 7, 8, -9], + lits![1, 2, -3, -4, 5, -6, 7, 8, -9], + lits![1, -2, 3, -4, 5, -6, 7, 8, -9], + lits![-1, 2, 3, -4, 5, -6, 7, 8, -9], + lits![1, 2, -3, 4, 5, -6, 7, 8, -9], + lits![1, -2, 3, 4, 5, -6, 7, 8, -9], + lits![-1, 2, 3, 4, 5, -6, 7, 8, -9], + lits![1, 2, -3, -4, -5, 6, 7, 8, -9], + lits![1, -2, 3, -4, -5, 6, 7, 8, -9], + lits![-1, 2, 3, -4, -5, 6, 7, 8, -9], + lits![1, 2, -3, 4, -5, 6, 7, 8, -9], + lits![1, -2, 3, 4, -5, 6, 7, 8, -9], + lits![-1, 2, 3, 4, -5, 6, 7, 8, -9], + lits![1, 2, -3, -4, 5, 6, 7, 8, -9], + lits![1, -2, 3, -4, 5, 6, 7, 8, -9], + lits![-1, 2, 3, -4, 5, 6, 7, 8, -9], + lits![1, 2, -3, 4, 5, 6, 7, 8, -9], + lits![1, -2, 3, 4, 5, 6, 7, 8, -9], + lits![-1, 2, 3, 4, 5, 6, 7, 8, -9], + lits![1, 2, 3, -4, -5, -6, 7, 8, 9], + lits![1, 2, 3, 4, -5, -6, 7, 8, 9], + lits![1, 2, 3, -4, 5, -6, 7, 8, 9], + lits![1, 2, 3, 4, 5, -6, 7, 8, 9], + lits![1, 2, 3, -4, -5, 6, 7, 8, 9], + lits![1, 2, 3, 4, -5, 6, 7, 8, 9], + lits![1, 2, 3, -4, 5, 6, 7, 8, 9], + lits![1, 2, 3, 4, 5, 6, 7, 8, 9], ]; let mut enc = get_sorted_encoder(SortedStrategy::Recursive); // println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -654,40 +636,41 @@ mod tests { #[test] fn test_3_2_sorted_eq() { let mut db = TestDB::new(5); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3], LimitComp::Equal, &y); + let lits = lits![1, 2, 3]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4, -5, -6, -7], - vec![-1, -2, -3, 4, -5, -6, -7], - vec![-1, -2, -3, -4, 5, -6, -7], - vec![-1, -2, -3, 4, 5, -6, -7], - vec![1, -2, -3, -4, -5, 6, -7], - vec![-1, 2, -3, -4, -5, 6, -7], - vec![-1, -2, 3, -4, -5, 6, -7], - vec![1, -2, -3, 4, -5, 6, -7], - vec![-1, 2, -3, 4, -5, 6, -7], - vec![-1, -2, 3, 4, -5, 6, -7], - vec![1, -2, -3, -4, 5, 6, -7], - vec![-1, 2, -3, -4, 5, 6, -7], - vec![-1, -2, 3, -4, 5, 6, -7], - vec![1, -2, -3, 4, 5, 6, -7], - vec![-1, 2, -3, 4, 5, 6, -7], - vec![-1, -2, 3, 4, 5, 6, -7], - vec![1, 2, -3, -4, -5, 6, 7], - vec![1, -2, 3, -4, -5, 6, 7], - vec![-1, 2, 3, -4, -5, 6, 7], - vec![1, 2, -3, 4, -5, 6, 7], - vec![1, -2, 3, 4, -5, 6, 7], - vec![-1, 2, 3, 4, -5, 6, 7], - vec![1, 2, -3, -4, 5, 6, 7], - vec![1, -2, 3, -4, 5, 6, 7], - vec![-1, 2, 3, -4, 5, 6, 7], - vec![1, 2, -3, 4, 5, 6, 7], - vec![1, -2, 3, 4, 5, 6, 7], - vec![-1, 2, 3, 4, 5, 6, 7], + lits![-1, -2, -3, -4, -5, -6, -7], + lits![-1, -2, -3, 4, -5, -6, -7], + lits![-1, -2, -3, -4, 5, -6, -7], + lits![-1, -2, -3, 4, 5, -6, -7], + lits![1, -2, -3, -4, -5, 6, -7], + lits![-1, 2, -3, -4, -5, 6, -7], + lits![-1, -2, 3, -4, -5, 6, -7], + lits![1, -2, -3, 4, -5, 6, -7], + lits![-1, 2, -3, 4, -5, 6, -7], + lits![-1, -2, 3, 4, -5, 6, -7], + lits![1, -2, -3, -4, 5, 6, -7], + lits![-1, 2, -3, -4, 5, 6, -7], + lits![-1, -2, 3, -4, 5, 6, -7], + lits![1, -2, -3, 4, 5, 6, -7], + lits![-1, 2, -3, 4, 5, 6, -7], + lits![-1, -2, 3, 4, 5, 6, -7], + lits![1, 2, -3, -4, -5, 6, 7], + lits![1, -2, 3, -4, -5, 6, 7], + lits![-1, 2, 3, -4, -5, 6, 7], + lits![1, 2, -3, 4, -5, 6, 7], + lits![1, -2, 3, 4, -5, 6, 7], + lits![-1, 2, 3, 4, -5, 6, 7], + lits![1, 2, -3, -4, 5, 6, 7], + lits![1, -2, 3, -4, 5, 6, 7], + lits![-1, 2, 3, -4, 5, 6, 7], + lits![1, 2, -3, 4, 5, 6, 7], + lits![1, -2, 3, 4, 5, 6, 7], + lits![-1, 2, 3, 4, 5, 6, 7], ]; let mut enc = get_sorted_encoder(SortedStrategy::Recursive); // println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -697,267 +680,268 @@ mod tests { #[test] fn test_4_sorted_eq() { let mut db = TestDB::new(8); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 4, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 4, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3, 4], LimitComp::Equal, &y); + let lits = lits![1, 2, 3, 4]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, -6, -7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, -5, 6, -7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, 6, -7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, -5, -6, 7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, -6, 7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, -5, 6, 7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, 6, 7, -8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, -5, -6, -7, 8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, -6, -7, 8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, -5, 6, -7, 8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, 6, -7, 8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, -5, -6, 7, 8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, -6, 7, 8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, -5, 6, 7, 8, -9, -10, -11, -12], - vec![-1, -2, -3, -4, 5, 6, 7, 8, -9, -10, -11, -12], - vec![1, -2, -3, -4, -5, -6, -7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, -6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, -6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, -6, -7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, -6, -7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, -6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, -6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, -6, -7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, -5, 6, -7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, 6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, 6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, 6, -7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, 6, -7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, 6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, 6, -7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, 6, -7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, -5, -6, 7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, -6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, -6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, -6, 7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, -6, 7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, -6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, -6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, -6, 7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, -5, 6, 7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, 6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, 6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, 6, 7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, 6, 7, -8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, 6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, 6, 7, -8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, 6, 7, -8, 9, -10, -11, -12], - vec![1, -2, -3, -4, -5, -6, -7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, -6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, -6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, -6, -7, 8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, -6, -7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, -6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, -6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, -6, -7, 8, 9, -10, -11, -12], - vec![1, -2, -3, -4, -5, 6, -7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, 6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, 6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, 6, -7, 8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, 6, -7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, 6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, 6, -7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, 6, -7, 8, 9, -10, -11, -12], - vec![1, -2, -3, -4, -5, -6, 7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, -6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, -6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, -6, 7, 8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, -6, 7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, -6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, -6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, -6, 7, 8, 9, -10, -11, -12], - vec![1, -2, -3, -4, -5, 6, 7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, -5, 6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, -5, 6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, -5, 6, 7, 8, 9, -10, -11, -12], - vec![1, -2, -3, -4, 5, 6, 7, 8, 9, -10, -11, -12], - vec![-1, 2, -3, -4, 5, 6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, 3, -4, 5, 6, 7, 8, 9, -10, -11, -12], - vec![-1, -2, -3, 4, 5, 6, 7, 8, 9, -10, -11, -12], - vec![1, 2, -3, -4, -5, -6, -7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, -6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, -6, -7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, -6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, -6, -7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, -6, -7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, -6, -7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, -6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, -6, -7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, -6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, -6, -7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, -6, -7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, -5, 6, -7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, 6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, 6, -7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, 6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, 6, -7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, 6, -7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, 6, -7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, 6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, 6, -7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, 6, -7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, 6, -7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, 6, -7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, -5, -6, 7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, -6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, -6, 7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, -6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, -6, 7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, -6, 7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, -6, 7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, -6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, -6, 7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, -6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, -6, 7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, -6, 7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, -5, 6, 7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, 6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, 6, 7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, 6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, 6, 7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, 6, 7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, 6, 7, -8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, 6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, 6, 7, -8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, 6, 7, -8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, 6, 7, -8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, 6, 7, -8, 9, 10, -11, -12], - vec![1, 2, -3, -4, -5, -6, -7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, -6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, -6, -7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, -6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, -6, -7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, -6, -7, 8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, -6, -7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, -6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, -6, -7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, -6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, -6, -7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, -6, -7, 8, 9, 10, -11, -12], - vec![1, 2, -3, -4, -5, 6, -7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, 6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, 6, -7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, 6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, 6, -7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, 6, -7, 8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, 6, -7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, 6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, 6, -7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, 6, -7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, 6, -7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, 6, -7, 8, 9, 10, -11, -12], - vec![1, 2, -3, -4, -5, -6, 7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, -6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, -6, 7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, -6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, -6, 7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, -6, 7, 8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, -6, 7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, -6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, -6, 7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, -6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, -6, 7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, -6, 7, 8, 9, 10, -11, -12], - vec![1, 2, -3, -4, -5, 6, 7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, -5, 6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, -5, 6, 7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, -5, 6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, -5, 6, 7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, -5, 6, 7, 8, 9, 10, -11, -12], - vec![1, 2, -3, -4, 5, 6, 7, 8, 9, 10, -11, -12], - vec![1, -2, 3, -4, 5, 6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, 3, -4, 5, 6, 7, 8, 9, 10, -11, -12], - vec![1, -2, -3, 4, 5, 6, 7, 8, 9, 10, -11, -12], - vec![-1, 2, -3, 4, 5, 6, 7, 8, 9, 10, -11, -12], - vec![-1, -2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12], - vec![1, 2, 3, -4, -5, -6, -7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, -6, -7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, -6, -7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, -6, -7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, -6, -7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, -6, -7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, -6, -7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, -6, -7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, -5, 6, -7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, 6, -7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, 6, -7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, 6, -7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, 6, -7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, 6, -7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, 6, -7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, 6, -7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, -5, -6, 7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, -6, 7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, -6, 7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, -6, 7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, -6, 7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, -6, 7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, -6, 7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, -6, 7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, -5, 6, 7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, 6, 7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, 6, 7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, 6, 7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, 6, 7, -8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, 6, 7, -8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, 6, 7, -8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, 6, 7, -8, 9, 10, 11, -12], - vec![1, 2, 3, -4, -5, -6, -7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, -6, -7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, -6, -7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, -6, -7, 8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, -6, -7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, -6, -7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, -6, -7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, -6, -7, 8, 9, 10, 11, -12], - vec![1, 2, 3, -4, -5, 6, -7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, 6, -7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, 6, -7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, 6, -7, 8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, 6, -7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, 6, -7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, 6, -7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, 6, -7, 8, 9, 10, 11, -12], - vec![1, 2, 3, -4, -5, -6, 7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, -6, 7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, -6, 7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, -6, 7, 8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, -6, 7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, -6, 7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, -6, 7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, -6, 7, 8, 9, 10, 11, -12], - vec![1, 2, 3, -4, -5, 6, 7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, -5, 6, 7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, -5, 6, 7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, -5, 6, 7, 8, 9, 10, 11, -12], - vec![1, 2, 3, -4, 5, 6, 7, 8, 9, 10, 11, -12], - vec![1, 2, -3, 4, 5, 6, 7, 8, 9, 10, 11, -12], - vec![1, -2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -12], - vec![-1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -12], - vec![1, 2, 3, 4, -5, -6, -7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, -6, -7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, -5, 6, -7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, 6, -7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, -5, -6, 7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, -6, 7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, -5, 6, 7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, 6, 7, -8, 9, 10, 11, 12], - vec![1, 2, 3, 4, -5, -6, -7, 8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, -6, -7, 8, 9, 10, 11, 12], - vec![1, 2, 3, 4, -5, 6, -7, 8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, 6, -7, 8, 9, 10, 11, 12], - vec![1, 2, 3, 4, -5, -6, 7, 8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, -6, 7, 8, 9, 10, 11, 12], - vec![1, 2, 3, 4, -5, 6, 7, 8, 9, 10, 11, 12], - vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + lits![-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, -6, -7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, -5, 6, -7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, 6, -7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, -5, -6, 7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, -6, 7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, -5, 6, 7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, 6, 7, -8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, -5, -6, -7, 8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, -6, -7, 8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, -5, 6, -7, 8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, 6, -7, 8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, -5, -6, 7, 8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, -6, 7, 8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, -5, 6, 7, 8, -9, -10, -11, -12], + lits![-1, -2, -3, -4, 5, 6, 7, 8, -9, -10, -11, -12], + lits![1, -2, -3, -4, -5, -6, -7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, -6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, -6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, -6, -7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, -6, -7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, -6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, -6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, -6, -7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, -5, 6, -7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, 6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, 6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, 6, -7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, 6, -7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, 6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, 6, -7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, 6, -7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, -5, -6, 7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, -6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, -6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, -6, 7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, -6, 7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, -6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, -6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, -6, 7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, -5, 6, 7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, 6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, 6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, 6, 7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, 6, 7, -8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, 6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, 6, 7, -8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, 6, 7, -8, 9, -10, -11, -12], + lits![1, -2, -3, -4, -5, -6, -7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, -6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, -6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, -6, -7, 8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, -6, -7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, -6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, -6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, -6, -7, 8, 9, -10, -11, -12], + lits![1, -2, -3, -4, -5, 6, -7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, 6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, 6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, 6, -7, 8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, 6, -7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, 6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, 6, -7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, 6, -7, 8, 9, -10, -11, -12], + lits![1, -2, -3, -4, -5, -6, 7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, -6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, -6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, -6, 7, 8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, -6, 7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, -6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, -6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, -6, 7, 8, 9, -10, -11, -12], + lits![1, -2, -3, -4, -5, 6, 7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, -5, 6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, -5, 6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, -5, 6, 7, 8, 9, -10, -11, -12], + lits![1, -2, -3, -4, 5, 6, 7, 8, 9, -10, -11, -12], + lits![-1, 2, -3, -4, 5, 6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, 3, -4, 5, 6, 7, 8, 9, -10, -11, -12], + lits![-1, -2, -3, 4, 5, 6, 7, 8, 9, -10, -11, -12], + lits![1, 2, -3, -4, -5, -6, -7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, -6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, -6, -7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, -6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, -6, -7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, -6, -7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, -6, -7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, -6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, -6, -7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, -6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, -6, -7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, -6, -7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, -5, 6, -7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, 6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, 6, -7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, 6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, 6, -7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, 6, -7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, 6, -7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, 6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, 6, -7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, 6, -7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, 6, -7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, 6, -7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, -5, -6, 7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, -6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, -6, 7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, -6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, -6, 7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, -6, 7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, -6, 7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, -6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, -6, 7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, -6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, -6, 7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, -6, 7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, -5, 6, 7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, 6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, 6, 7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, 6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, 6, 7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, 6, 7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, 6, 7, -8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, 6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, 6, 7, -8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, 6, 7, -8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, 6, 7, -8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, 6, 7, -8, 9, 10, -11, -12], + lits![1, 2, -3, -4, -5, -6, -7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, -6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, -6, -7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, -6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, -6, -7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, -6, -7, 8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, -6, -7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, -6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, -6, -7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, -6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, -6, -7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, -6, -7, 8, 9, 10, -11, -12], + lits![1, 2, -3, -4, -5, 6, -7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, 6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, 6, -7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, 6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, 6, -7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, 6, -7, 8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, 6, -7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, 6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, 6, -7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, 6, -7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, 6, -7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, 6, -7, 8, 9, 10, -11, -12], + lits![1, 2, -3, -4, -5, -6, 7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, -6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, -6, 7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, -6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, -6, 7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, -6, 7, 8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, -6, 7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, -6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, -6, 7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, -6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, -6, 7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, -6, 7, 8, 9, 10, -11, -12], + lits![1, 2, -3, -4, -5, 6, 7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, -5, 6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, -5, 6, 7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, -5, 6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, -5, 6, 7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, -5, 6, 7, 8, 9, 10, -11, -12], + lits![1, 2, -3, -4, 5, 6, 7, 8, 9, 10, -11, -12], + lits![1, -2, 3, -4, 5, 6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, 3, -4, 5, 6, 7, 8, 9, 10, -11, -12], + lits![1, -2, -3, 4, 5, 6, 7, 8, 9, 10, -11, -12], + lits![-1, 2, -3, 4, 5, 6, 7, 8, 9, 10, -11, -12], + lits![-1, -2, 3, 4, 5, 6, 7, 8, 9, 10, -11, -12], + lits![1, 2, 3, -4, -5, -6, -7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, -6, -7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, -6, -7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, -6, -7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, -6, -7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, -6, -7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, -6, -7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, -6, -7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, -5, 6, -7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, 6, -7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, 6, -7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, 6, -7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, 6, -7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, 6, -7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, 6, -7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, 6, -7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, -5, -6, 7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, -6, 7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, -6, 7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, -6, 7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, -6, 7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, -6, 7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, -6, 7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, -6, 7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, -5, 6, 7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, 6, 7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, 6, 7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, 6, 7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, 6, 7, -8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, 6, 7, -8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, 6, 7, -8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, 6, 7, -8, 9, 10, 11, -12], + lits![1, 2, 3, -4, -5, -6, -7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, -6, -7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, -6, -7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, -6, -7, 8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, -6, -7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, -6, -7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, -6, -7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, -6, -7, 8, 9, 10, 11, -12], + lits![1, 2, 3, -4, -5, 6, -7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, 6, -7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, 6, -7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, 6, -7, 8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, 6, -7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, 6, -7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, 6, -7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, 6, -7, 8, 9, 10, 11, -12], + lits![1, 2, 3, -4, -5, -6, 7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, -6, 7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, -6, 7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, -6, 7, 8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, -6, 7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, -6, 7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, -6, 7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, -6, 7, 8, 9, 10, 11, -12], + lits![1, 2, 3, -4, -5, 6, 7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, -5, 6, 7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, -5, 6, 7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, -5, 6, 7, 8, 9, 10, 11, -12], + lits![1, 2, 3, -4, 5, 6, 7, 8, 9, 10, 11, -12], + lits![1, 2, -3, 4, 5, 6, 7, 8, 9, 10, 11, -12], + lits![1, -2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -12], + lits![-1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -12], + lits![1, 2, 3, 4, -5, -6, -7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, -6, -7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, -5, 6, -7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, 6, -7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, -5, -6, 7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, -6, 7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, -5, 6, 7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, 6, 7, -8, 9, 10, 11, 12], + lits![1, 2, 3, 4, -5, -6, -7, 8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, -6, -7, 8, 9, 10, 11, 12], + lits![1, 2, 3, 4, -5, 6, -7, 8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, 6, -7, 8, 9, 10, 11, 12], + lits![1, 2, 3, 4, -5, -6, 7, 8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, -6, 7, 8, 9, 10, 11, 12], + lits![1, 2, 3, 4, -5, 6, 7, 8, 9, 10, 11, 12], + lits![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ]; let mut enc = get_sorted_encoder(SortedStrategy::Recursive); // println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -967,56 +951,57 @@ mod tests { #[test] fn test_4_2_sorted_eq() { let mut db = TestDB::new(6); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 2, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3, 4], LimitComp::Equal, &y); + let lits = lits![1, 2, 3, 4]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4, -5, -6, -7, -8], - vec![-1, -2, -3, -4, 5, -6, -7, -8], - vec![-1, -2, -3, -4, -5, 6, -7, -8], - vec![-1, -2, -3, -4, 5, 6, -7, -8], - vec![1, -2, -3, -4, -5, -6, 7, -8], - vec![-1, 2, -3, -4, -5, -6, 7, -8], - vec![-1, -2, 3, -4, -5, -6, 7, -8], - vec![-1, -2, -3, 4, -5, -6, 7, -8], - vec![1, -2, -3, -4, 5, -6, 7, -8], - vec![-1, 2, -3, -4, 5, -6, 7, -8], - vec![-1, -2, 3, -4, 5, -6, 7, -8], - vec![-1, -2, -3, 4, 5, -6, 7, -8], - vec![1, -2, -3, -4, -5, 6, 7, -8], - vec![-1, 2, -3, -4, -5, 6, 7, -8], - vec![-1, -2, 3, -4, -5, 6, 7, -8], - vec![-1, -2, -3, 4, -5, 6, 7, -8], - vec![1, -2, -3, -4, 5, 6, 7, -8], - vec![-1, 2, -3, -4, 5, 6, 7, -8], - vec![-1, -2, 3, -4, 5, 6, 7, -8], - vec![-1, -2, -3, 4, 5, 6, 7, -8], - vec![1, 2, -3, -4, -5, -6, 7, 8], - vec![1, -2, 3, -4, -5, -6, 7, 8], - vec![-1, 2, 3, -4, -5, -6, 7, 8], - vec![1, -2, -3, 4, -5, -6, 7, 8], - vec![-1, 2, -3, 4, -5, -6, 7, 8], - vec![-1, -2, 3, 4, -5, -6, 7, 8], - vec![1, 2, -3, -4, 5, -6, 7, 8], - vec![1, -2, 3, -4, 5, -6, 7, 8], - vec![-1, 2, 3, -4, 5, -6, 7, 8], - vec![1, -2, -3, 4, 5, -6, 7, 8], - vec![-1, 2, -3, 4, 5, -6, 7, 8], - vec![-1, -2, 3, 4, 5, -6, 7, 8], - vec![1, 2, -3, -4, -5, 6, 7, 8], - vec![1, -2, 3, -4, -5, 6, 7, 8], - vec![-1, 2, 3, -4, -5, 6, 7, 8], - vec![1, -2, -3, 4, -5, 6, 7, 8], - vec![-1, 2, -3, 4, -5, 6, 7, 8], - vec![-1, -2, 3, 4, -5, 6, 7, 8], - vec![1, 2, -3, -4, 5, 6, 7, 8], - vec![1, -2, 3, -4, 5, 6, 7, 8], - vec![-1, 2, 3, -4, 5, 6, 7, 8], - vec![1, -2, -3, 4, 5, 6, 7, 8], - vec![-1, 2, -3, 4, 5, 6, 7, 8], - vec![-1, -2, 3, 4, 5, 6, 7, 8], + lits![-1, -2, -3, -4, -5, -6, -7, -8], + lits![-1, -2, -3, -4, 5, -6, -7, -8], + lits![-1, -2, -3, -4, -5, 6, -7, -8], + lits![-1, -2, -3, -4, 5, 6, -7, -8], + lits![1, -2, -3, -4, -5, -6, 7, -8], + lits![-1, 2, -3, -4, -5, -6, 7, -8], + lits![-1, -2, 3, -4, -5, -6, 7, -8], + lits![-1, -2, -3, 4, -5, -6, 7, -8], + lits![1, -2, -3, -4, 5, -6, 7, -8], + lits![-1, 2, -3, -4, 5, -6, 7, -8], + lits![-1, -2, 3, -4, 5, -6, 7, -8], + lits![-1, -2, -3, 4, 5, -6, 7, -8], + lits![1, -2, -3, -4, -5, 6, 7, -8], + lits![-1, 2, -3, -4, -5, 6, 7, -8], + lits![-1, -2, 3, -4, -5, 6, 7, -8], + lits![-1, -2, -3, 4, -5, 6, 7, -8], + lits![1, -2, -3, -4, 5, 6, 7, -8], + lits![-1, 2, -3, -4, 5, 6, 7, -8], + lits![-1, -2, 3, -4, 5, 6, 7, -8], + lits![-1, -2, -3, 4, 5, 6, 7, -8], + lits![1, 2, -3, -4, -5, -6, 7, 8], + lits![1, -2, 3, -4, -5, -6, 7, 8], + lits![-1, 2, 3, -4, -5, -6, 7, 8], + lits![1, -2, -3, 4, -5, -6, 7, 8], + lits![-1, 2, -3, 4, -5, -6, 7, 8], + lits![-1, -2, 3, 4, -5, -6, 7, 8], + lits![1, 2, -3, -4, 5, -6, 7, 8], + lits![1, -2, 3, -4, 5, -6, 7, 8], + lits![-1, 2, 3, -4, 5, -6, 7, 8], + lits![1, -2, -3, 4, 5, -6, 7, 8], + lits![-1, 2, -3, 4, 5, -6, 7, 8], + lits![-1, -2, 3, 4, 5, -6, 7, 8], + lits![1, 2, -3, -4, -5, 6, 7, 8], + lits![1, -2, 3, -4, -5, 6, 7, 8], + lits![-1, 2, 3, -4, -5, 6, 7, 8], + lits![1, -2, -3, 4, -5, 6, 7, 8], + lits![-1, 2, -3, 4, -5, 6, 7, 8], + lits![-1, -2, 3, 4, -5, 6, 7, 8], + lits![1, 2, -3, -4, 5, 6, 7, 8], + lits![1, -2, 3, -4, 5, 6, 7, 8], + lits![-1, 2, 3, -4, 5, 6, 7, 8], + lits![1, -2, -3, 4, 5, 6, 7, 8], + lits![-1, 2, -3, 4, 5, 6, 7, 8], + lits![-1, -2, 3, 4, 5, 6, 7, 8], ]; let mut enc = get_sorted_encoder(SortedStrategy::Recursive); // println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -1026,132 +1011,133 @@ mod tests { #[test] fn test_4_3_sorted_eq() { let mut db = TestDB::new(7); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 3, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 3, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3, 4], LimitComp::Equal, &y); + let lits = lits![1, 2, 3, 4]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4, -5, -6, -7, -8, -9, -10], - vec![-1, -2, -3, -4, 5, -6, -7, -8, -9, -10], - vec![-1, -2, -3, -4, -5, 6, -7, -8, -9, -10], - vec![-1, -2, -3, -4, 5, 6, -7, -8, -9, -10], - vec![-1, -2, -3, -4, -5, -6, 7, -8, -9, -10], - vec![-1, -2, -3, -4, 5, -6, 7, -8, -9, -10], - vec![-1, -2, -3, -4, -5, 6, 7, -8, -9, -10], - vec![-1, -2, -3, -4, 5, 6, 7, -8, -9, -10], - vec![1, -2, -3, -4, -5, -6, -7, 8, -9, -10], - vec![-1, 2, -3, -4, -5, -6, -7, 8, -9, -10], - vec![-1, -2, 3, -4, -5, -6, -7, 8, -9, -10], - vec![-1, -2, -3, 4, -5, -6, -7, 8, -9, -10], - vec![1, -2, -3, -4, 5, -6, -7, 8, -9, -10], - vec![-1, 2, -3, -4, 5, -6, -7, 8, -9, -10], - vec![-1, -2, 3, -4, 5, -6, -7, 8, -9, -10], - vec![-1, -2, -3, 4, 5, -6, -7, 8, -9, -10], - vec![1, -2, -3, -4, -5, 6, -7, 8, -9, -10], - vec![-1, 2, -3, -4, -5, 6, -7, 8, -9, -10], - vec![-1, -2, 3, -4, -5, 6, -7, 8, -9, -10], - vec![-1, -2, -3, 4, -5, 6, -7, 8, -9, -10], - vec![1, -2, -3, -4, 5, 6, -7, 8, -9, -10], - vec![-1, 2, -3, -4, 5, 6, -7, 8, -9, -10], - vec![-1, -2, 3, -4, 5, 6, -7, 8, -9, -10], - vec![-1, -2, -3, 4, 5, 6, -7, 8, -9, -10], - vec![1, -2, -3, -4, -5, -6, 7, 8, -9, -10], - vec![-1, 2, -3, -4, -5, -6, 7, 8, -9, -10], - vec![-1, -2, 3, -4, -5, -6, 7, 8, -9, -10], - vec![-1, -2, -3, 4, -5, -6, 7, 8, -9, -10], - vec![1, -2, -3, -4, 5, -6, 7, 8, -9, -10], - vec![-1, 2, -3, -4, 5, -6, 7, 8, -9, -10], - vec![-1, -2, 3, -4, 5, -6, 7, 8, -9, -10], - vec![-1, -2, -3, 4, 5, -6, 7, 8, -9, -10], - vec![1, -2, -3, -4, -5, 6, 7, 8, -9, -10], - vec![-1, 2, -3, -4, -5, 6, 7, 8, -9, -10], - vec![-1, -2, 3, -4, -5, 6, 7, 8, -9, -10], - vec![-1, -2, -3, 4, -5, 6, 7, 8, -9, -10], - vec![1, -2, -3, -4, 5, 6, 7, 8, -9, -10], - vec![-1, 2, -3, -4, 5, 6, 7, 8, -9, -10], - vec![-1, -2, 3, -4, 5, 6, 7, 8, -9, -10], - vec![-1, -2, -3, 4, 5, 6, 7, 8, -9, -10], - vec![1, 2, -3, -4, -5, -6, -7, 8, 9, -10], - vec![1, -2, 3, -4, -5, -6, -7, 8, 9, -10], - vec![-1, 2, 3, -4, -5, -6, -7, 8, 9, -10], - vec![1, -2, -3, 4, -5, -6, -7, 8, 9, -10], - vec![-1, 2, -3, 4, -5, -6, -7, 8, 9, -10], - vec![-1, -2, 3, 4, -5, -6, -7, 8, 9, -10], - vec![1, 2, -3, -4, 5, -6, -7, 8, 9, -10], - vec![1, -2, 3, -4, 5, -6, -7, 8, 9, -10], - vec![-1, 2, 3, -4, 5, -6, -7, 8, 9, -10], - vec![1, -2, -3, 4, 5, -6, -7, 8, 9, -10], - vec![-1, 2, -3, 4, 5, -6, -7, 8, 9, -10], - vec![-1, -2, 3, 4, 5, -6, -7, 8, 9, -10], - vec![1, 2, -3, -4, -5, 6, -7, 8, 9, -10], - vec![1, -2, 3, -4, -5, 6, -7, 8, 9, -10], - vec![-1, 2, 3, -4, -5, 6, -7, 8, 9, -10], - vec![1, -2, -3, 4, -5, 6, -7, 8, 9, -10], - vec![-1, 2, -3, 4, -5, 6, -7, 8, 9, -10], - vec![-1, -2, 3, 4, -5, 6, -7, 8, 9, -10], - vec![1, 2, -3, -4, 5, 6, -7, 8, 9, -10], - vec![1, -2, 3, -4, 5, 6, -7, 8, 9, -10], - vec![-1, 2, 3, -4, 5, 6, -7, 8, 9, -10], - vec![1, -2, -3, 4, 5, 6, -7, 8, 9, -10], - vec![-1, 2, -3, 4, 5, 6, -7, 8, 9, -10], - vec![-1, -2, 3, 4, 5, 6, -7, 8, 9, -10], - vec![1, 2, -3, -4, -5, -6, 7, 8, 9, -10], - vec![1, -2, 3, -4, -5, -6, 7, 8, 9, -10], - vec![-1, 2, 3, -4, -5, -6, 7, 8, 9, -10], - vec![1, -2, -3, 4, -5, -6, 7, 8, 9, -10], - vec![-1, 2, -3, 4, -5, -6, 7, 8, 9, -10], - vec![-1, -2, 3, 4, -5, -6, 7, 8, 9, -10], - vec![1, 2, -3, -4, 5, -6, 7, 8, 9, -10], - vec![1, -2, 3, -4, 5, -6, 7, 8, 9, -10], - vec![-1, 2, 3, -4, 5, -6, 7, 8, 9, -10], - vec![1, -2, -3, 4, 5, -6, 7, 8, 9, -10], - vec![-1, 2, -3, 4, 5, -6, 7, 8, 9, -10], - vec![-1, -2, 3, 4, 5, -6, 7, 8, 9, -10], - vec![1, 2, -3, -4, -5, 6, 7, 8, 9, -10], - vec![1, -2, 3, -4, -5, 6, 7, 8, 9, -10], - vec![-1, 2, 3, -4, -5, 6, 7, 8, 9, -10], - vec![1, -2, -3, 4, -5, 6, 7, 8, 9, -10], - vec![-1, 2, -3, 4, -5, 6, 7, 8, 9, -10], - vec![-1, -2, 3, 4, -5, 6, 7, 8, 9, -10], - vec![1, 2, -3, -4, 5, 6, 7, 8, 9, -10], - vec![1, -2, 3, -4, 5, 6, 7, 8, 9, -10], - vec![-1, 2, 3, -4, 5, 6, 7, 8, 9, -10], - vec![1, -2, -3, 4, 5, 6, 7, 8, 9, -10], - vec![-1, 2, -3, 4, 5, 6, 7, 8, 9, -10], - vec![-1, -2, 3, 4, 5, 6, 7, 8, 9, -10], - vec![1, 2, 3, -4, -5, -6, -7, 8, 9, 10], - vec![1, 2, -3, 4, -5, -6, -7, 8, 9, 10], - vec![1, -2, 3, 4, -5, -6, -7, 8, 9, 10], - vec![-1, 2, 3, 4, -5, -6, -7, 8, 9, 10], - vec![1, 2, 3, -4, 5, -6, -7, 8, 9, 10], - vec![1, 2, -3, 4, 5, -6, -7, 8, 9, 10], - vec![1, -2, 3, 4, 5, -6, -7, 8, 9, 10], - vec![-1, 2, 3, 4, 5, -6, -7, 8, 9, 10], - vec![1, 2, 3, -4, -5, 6, -7, 8, 9, 10], - vec![1, 2, -3, 4, -5, 6, -7, 8, 9, 10], - vec![1, -2, 3, 4, -5, 6, -7, 8, 9, 10], - vec![-1, 2, 3, 4, -5, 6, -7, 8, 9, 10], - vec![1, 2, 3, -4, 5, 6, -7, 8, 9, 10], - vec![1, 2, -3, 4, 5, 6, -7, 8, 9, 10], - vec![1, -2, 3, 4, 5, 6, -7, 8, 9, 10], - vec![-1, 2, 3, 4, 5, 6, -7, 8, 9, 10], - vec![1, 2, 3, -4, -5, -6, 7, 8, 9, 10], - vec![1, 2, -3, 4, -5, -6, 7, 8, 9, 10], - vec![1, -2, 3, 4, -5, -6, 7, 8, 9, 10], - vec![-1, 2, 3, 4, -5, -6, 7, 8, 9, 10], - vec![1, 2, 3, -4, 5, -6, 7, 8, 9, 10], - vec![1, 2, -3, 4, 5, -6, 7, 8, 9, 10], - vec![1, -2, 3, 4, 5, -6, 7, 8, 9, 10], - vec![-1, 2, 3, 4, 5, -6, 7, 8, 9, 10], - vec![1, 2, 3, -4, -5, 6, 7, 8, 9, 10], - vec![1, 2, -3, 4, -5, 6, 7, 8, 9, 10], - vec![1, -2, 3, 4, -5, 6, 7, 8, 9, 10], - vec![-1, 2, 3, 4, -5, 6, 7, 8, 9, 10], - vec![1, 2, 3, -4, 5, 6, 7, 8, 9, 10], - vec![1, 2, -3, 4, 5, 6, 7, 8, 9, 10], - vec![1, -2, 3, 4, 5, 6, 7, 8, 9, 10], - vec![-1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + lits![-1, -2, -3, -4, -5, -6, -7, -8, -9, -10], + lits![-1, -2, -3, -4, 5, -6, -7, -8, -9, -10], + lits![-1, -2, -3, -4, -5, 6, -7, -8, -9, -10], + lits![-1, -2, -3, -4, 5, 6, -7, -8, -9, -10], + lits![-1, -2, -3, -4, -5, -6, 7, -8, -9, -10], + lits![-1, -2, -3, -4, 5, -6, 7, -8, -9, -10], + lits![-1, -2, -3, -4, -5, 6, 7, -8, -9, -10], + lits![-1, -2, -3, -4, 5, 6, 7, -8, -9, -10], + lits![1, -2, -3, -4, -5, -6, -7, 8, -9, -10], + lits![-1, 2, -3, -4, -5, -6, -7, 8, -9, -10], + lits![-1, -2, 3, -4, -5, -6, -7, 8, -9, -10], + lits![-1, -2, -3, 4, -5, -6, -7, 8, -9, -10], + lits![1, -2, -3, -4, 5, -6, -7, 8, -9, -10], + lits![-1, 2, -3, -4, 5, -6, -7, 8, -9, -10], + lits![-1, -2, 3, -4, 5, -6, -7, 8, -9, -10], + lits![-1, -2, -3, 4, 5, -6, -7, 8, -9, -10], + lits![1, -2, -3, -4, -5, 6, -7, 8, -9, -10], + lits![-1, 2, -3, -4, -5, 6, -7, 8, -9, -10], + lits![-1, -2, 3, -4, -5, 6, -7, 8, -9, -10], + lits![-1, -2, -3, 4, -5, 6, -7, 8, -9, -10], + lits![1, -2, -3, -4, 5, 6, -7, 8, -9, -10], + lits![-1, 2, -3, -4, 5, 6, -7, 8, -9, -10], + lits![-1, -2, 3, -4, 5, 6, -7, 8, -9, -10], + lits![-1, -2, -3, 4, 5, 6, -7, 8, -9, -10], + lits![1, -2, -3, -4, -5, -6, 7, 8, -9, -10], + lits![-1, 2, -3, -4, -5, -6, 7, 8, -9, -10], + lits![-1, -2, 3, -4, -5, -6, 7, 8, -9, -10], + lits![-1, -2, -3, 4, -5, -6, 7, 8, -9, -10], + lits![1, -2, -3, -4, 5, -6, 7, 8, -9, -10], + lits![-1, 2, -3, -4, 5, -6, 7, 8, -9, -10], + lits![-1, -2, 3, -4, 5, -6, 7, 8, -9, -10], + lits![-1, -2, -3, 4, 5, -6, 7, 8, -9, -10], + lits![1, -2, -3, -4, -5, 6, 7, 8, -9, -10], + lits![-1, 2, -3, -4, -5, 6, 7, 8, -9, -10], + lits![-1, -2, 3, -4, -5, 6, 7, 8, -9, -10], + lits![-1, -2, -3, 4, -5, 6, 7, 8, -9, -10], + lits![1, -2, -3, -4, 5, 6, 7, 8, -9, -10], + lits![-1, 2, -3, -4, 5, 6, 7, 8, -9, -10], + lits![-1, -2, 3, -4, 5, 6, 7, 8, -9, -10], + lits![-1, -2, -3, 4, 5, 6, 7, 8, -9, -10], + lits![1, 2, -3, -4, -5, -6, -7, 8, 9, -10], + lits![1, -2, 3, -4, -5, -6, -7, 8, 9, -10], + lits![-1, 2, 3, -4, -5, -6, -7, 8, 9, -10], + lits![1, -2, -3, 4, -5, -6, -7, 8, 9, -10], + lits![-1, 2, -3, 4, -5, -6, -7, 8, 9, -10], + lits![-1, -2, 3, 4, -5, -6, -7, 8, 9, -10], + lits![1, 2, -3, -4, 5, -6, -7, 8, 9, -10], + lits![1, -2, 3, -4, 5, -6, -7, 8, 9, -10], + lits![-1, 2, 3, -4, 5, -6, -7, 8, 9, -10], + lits![1, -2, -3, 4, 5, -6, -7, 8, 9, -10], + lits![-1, 2, -3, 4, 5, -6, -7, 8, 9, -10], + lits![-1, -2, 3, 4, 5, -6, -7, 8, 9, -10], + lits![1, 2, -3, -4, -5, 6, -7, 8, 9, -10], + lits![1, -2, 3, -4, -5, 6, -7, 8, 9, -10], + lits![-1, 2, 3, -4, -5, 6, -7, 8, 9, -10], + lits![1, -2, -3, 4, -5, 6, -7, 8, 9, -10], + lits![-1, 2, -3, 4, -5, 6, -7, 8, 9, -10], + lits![-1, -2, 3, 4, -5, 6, -7, 8, 9, -10], + lits![1, 2, -3, -4, 5, 6, -7, 8, 9, -10], + lits![1, -2, 3, -4, 5, 6, -7, 8, 9, -10], + lits![-1, 2, 3, -4, 5, 6, -7, 8, 9, -10], + lits![1, -2, -3, 4, 5, 6, -7, 8, 9, -10], + lits![-1, 2, -3, 4, 5, 6, -7, 8, 9, -10], + lits![-1, -2, 3, 4, 5, 6, -7, 8, 9, -10], + lits![1, 2, -3, -4, -5, -6, 7, 8, 9, -10], + lits![1, -2, 3, -4, -5, -6, 7, 8, 9, -10], + lits![-1, 2, 3, -4, -5, -6, 7, 8, 9, -10], + lits![1, -2, -3, 4, -5, -6, 7, 8, 9, -10], + lits![-1, 2, -3, 4, -5, -6, 7, 8, 9, -10], + lits![-1, -2, 3, 4, -5, -6, 7, 8, 9, -10], + lits![1, 2, -3, -4, 5, -6, 7, 8, 9, -10], + lits![1, -2, 3, -4, 5, -6, 7, 8, 9, -10], + lits![-1, 2, 3, -4, 5, -6, 7, 8, 9, -10], + lits![1, -2, -3, 4, 5, -6, 7, 8, 9, -10], + lits![-1, 2, -3, 4, 5, -6, 7, 8, 9, -10], + lits![-1, -2, 3, 4, 5, -6, 7, 8, 9, -10], + lits![1, 2, -3, -4, -5, 6, 7, 8, 9, -10], + lits![1, -2, 3, -4, -5, 6, 7, 8, 9, -10], + lits![-1, 2, 3, -4, -5, 6, 7, 8, 9, -10], + lits![1, -2, -3, 4, -5, 6, 7, 8, 9, -10], + lits![-1, 2, -3, 4, -5, 6, 7, 8, 9, -10], + lits![-1, -2, 3, 4, -5, 6, 7, 8, 9, -10], + lits![1, 2, -3, -4, 5, 6, 7, 8, 9, -10], + lits![1, -2, 3, -4, 5, 6, 7, 8, 9, -10], + lits![-1, 2, 3, -4, 5, 6, 7, 8, 9, -10], + lits![1, -2, -3, 4, 5, 6, 7, 8, 9, -10], + lits![-1, 2, -3, 4, 5, 6, 7, 8, 9, -10], + lits![-1, -2, 3, 4, 5, 6, 7, 8, 9, -10], + lits![1, 2, 3, -4, -5, -6, -7, 8, 9, 10], + lits![1, 2, -3, 4, -5, -6, -7, 8, 9, 10], + lits![1, -2, 3, 4, -5, -6, -7, 8, 9, 10], + lits![-1, 2, 3, 4, -5, -6, -7, 8, 9, 10], + lits![1, 2, 3, -4, 5, -6, -7, 8, 9, 10], + lits![1, 2, -3, 4, 5, -6, -7, 8, 9, 10], + lits![1, -2, 3, 4, 5, -6, -7, 8, 9, 10], + lits![-1, 2, 3, 4, 5, -6, -7, 8, 9, 10], + lits![1, 2, 3, -4, -5, 6, -7, 8, 9, 10], + lits![1, 2, -3, 4, -5, 6, -7, 8, 9, 10], + lits![1, -2, 3, 4, -5, 6, -7, 8, 9, 10], + lits![-1, 2, 3, 4, -5, 6, -7, 8, 9, 10], + lits![1, 2, 3, -4, 5, 6, -7, 8, 9, 10], + lits![1, 2, -3, 4, 5, 6, -7, 8, 9, 10], + lits![1, -2, 3, 4, 5, 6, -7, 8, 9, 10], + lits![-1, 2, 3, 4, 5, 6, -7, 8, 9, 10], + lits![1, 2, 3, -4, -5, -6, 7, 8, 9, 10], + lits![1, 2, -3, 4, -5, -6, 7, 8, 9, 10], + lits![1, -2, 3, 4, -5, -6, 7, 8, 9, 10], + lits![-1, 2, 3, 4, -5, -6, 7, 8, 9, 10], + lits![1, 2, 3, -4, 5, -6, 7, 8, 9, 10], + lits![1, 2, -3, 4, 5, -6, 7, 8, 9, 10], + lits![1, -2, 3, 4, 5, -6, 7, 8, 9, 10], + lits![-1, 2, 3, 4, 5, -6, 7, 8, 9, 10], + lits![1, 2, 3, -4, -5, 6, 7, 8, 9, 10], + lits![1, 2, -3, 4, -5, 6, 7, 8, 9, 10], + lits![1, -2, 3, 4, -5, 6, 7, 8, 9, 10], + lits![-1, 2, 3, 4, -5, 6, 7, 8, 9, 10], + lits![1, 2, 3, -4, 5, 6, 7, 8, 9, 10], + lits![1, 2, -3, 4, 5, 6, 7, 8, 9, 10], + lits![1, -2, 3, 4, 5, 6, 7, 8, 9, 10], + lits![-1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ]; let mut enc = get_sorted_encoder(SortedStrategy::Recursive); // println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -1172,220 +1158,221 @@ mod tests { #[test] fn test_5_3_sorted_eq() { let mut db = TestDB::new(8); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 3, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 3, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3, 4, 5], LimitComp::Equal, &y); + let lits = lits![1, 2, 3, 4, 5]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11], - vec![-1, -2, -3, -4, -5, 6, -7, -8, -9, -10, -11], - vec![-1, -2, -3, -4, -5, -6, 7, -8, -9, -10, -11], - vec![-1, -2, -3, -4, -5, 6, 7, -8, -9, -10, -11], - vec![-1, -2, -3, -4, -5, -6, -7, 8, -9, -10, -11], - vec![-1, -2, -3, -4, -5, 6, -7, 8, -9, -10, -11], - vec![-1, -2, -3, -4, -5, -6, 7, 8, -9, -10, -11], - vec![-1, -2, -3, -4, -5, 6, 7, 8, -9, -10, -11], - vec![1, -2, -3, -4, -5, -6, -7, -8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, -6, -7, -8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, -6, -7, -8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, -6, -7, -8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, -6, -7, -8, 9, -10, -11], - vec![1, -2, -3, -4, -5, 6, -7, -8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, 6, -7, -8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, 6, -7, -8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, 6, -7, -8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, 6, -7, -8, 9, -10, -11], - vec![1, -2, -3, -4, -5, -6, 7, -8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, -6, 7, -8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, -6, 7, -8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, -6, 7, -8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, -6, 7, -8, 9, -10, -11], - vec![1, -2, -3, -4, -5, 6, 7, -8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, 6, 7, -8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, 6, 7, -8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, 6, 7, -8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, 6, 7, -8, 9, -10, -11], - vec![1, -2, -3, -4, -5, -6, -7, 8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, -6, -7, 8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, -6, -7, 8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, -6, -7, 8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, -6, -7, 8, 9, -10, -11], - vec![1, -2, -3, -4, -5, 6, -7, 8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, 6, -7, 8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, 6, -7, 8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, 6, -7, 8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, 6, -7, 8, 9, -10, -11], - vec![1, -2, -3, -4, -5, -6, 7, 8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, -6, 7, 8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, -6, 7, 8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, -6, 7, 8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, -6, 7, 8, 9, -10, -11], - vec![1, -2, -3, -4, -5, 6, 7, 8, 9, -10, -11], - vec![-1, 2, -3, -4, -5, 6, 7, 8, 9, -10, -11], - vec![-1, -2, 3, -4, -5, 6, 7, 8, 9, -10, -11], - vec![-1, -2, -3, 4, -5, 6, 7, 8, 9, -10, -11], - vec![-1, -2, -3, -4, 5, 6, 7, 8, 9, -10, -11], - vec![1, 2, -3, -4, -5, -6, -7, -8, 9, 10, -11], - vec![1, -2, 3, -4, -5, -6, -7, -8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, -6, -7, -8, 9, 10, -11], - vec![1, -2, -3, 4, -5, -6, -7, -8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, -6, -7, -8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, -6, -7, -8, 9, 10, -11], - vec![1, -2, -3, -4, 5, -6, -7, -8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, -6, -7, -8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, -6, -7, -8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, -6, -7, -8, 9, 10, -11], - vec![1, 2, -3, -4, -5, 6, -7, -8, 9, 10, -11], - vec![1, -2, 3, -4, -5, 6, -7, -8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, 6, -7, -8, 9, 10, -11], - vec![1, -2, -3, 4, -5, 6, -7, -8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, 6, -7, -8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, 6, -7, -8, 9, 10, -11], - vec![1, -2, -3, -4, 5, 6, -7, -8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, 6, -7, -8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, 6, -7, -8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, 6, -7, -8, 9, 10, -11], - vec![1, 2, -3, -4, -5, -6, 7, -8, 9, 10, -11], - vec![1, -2, 3, -4, -5, -6, 7, -8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, -6, 7, -8, 9, 10, -11], - vec![1, -2, -3, 4, -5, -6, 7, -8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, -6, 7, -8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, -6, 7, -8, 9, 10, -11], - vec![1, -2, -3, -4, 5, -6, 7, -8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, -6, 7, -8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, -6, 7, -8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, -6, 7, -8, 9, 10, -11], - vec![1, 2, -3, -4, -5, 6, 7, -8, 9, 10, -11], - vec![1, -2, 3, -4, -5, 6, 7, -8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, 6, 7, -8, 9, 10, -11], - vec![1, -2, -3, 4, -5, 6, 7, -8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, 6, 7, -8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, 6, 7, -8, 9, 10, -11], - vec![1, -2, -3, -4, 5, 6, 7, -8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, 6, 7, -8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, 6, 7, -8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, 6, 7, -8, 9, 10, -11], - vec![1, 2, -3, -4, -5, -6, -7, 8, 9, 10, -11], - vec![1, -2, 3, -4, -5, -6, -7, 8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, -6, -7, 8, 9, 10, -11], - vec![1, -2, -3, 4, -5, -6, -7, 8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, -6, -7, 8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, -6, -7, 8, 9, 10, -11], - vec![1, -2, -3, -4, 5, -6, -7, 8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, -6, -7, 8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, -6, -7, 8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, -6, -7, 8, 9, 10, -11], - vec![1, 2, -3, -4, -5, 6, -7, 8, 9, 10, -11], - vec![1, -2, 3, -4, -5, 6, -7, 8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, 6, -7, 8, 9, 10, -11], - vec![1, -2, -3, 4, -5, 6, -7, 8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, 6, -7, 8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, 6, -7, 8, 9, 10, -11], - vec![1, -2, -3, -4, 5, 6, -7, 8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, 6, -7, 8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, 6, -7, 8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, 6, -7, 8, 9, 10, -11], - vec![1, 2, -3, -4, -5, -6, 7, 8, 9, 10, -11], - vec![1, -2, 3, -4, -5, -6, 7, 8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, -6, 7, 8, 9, 10, -11], - vec![1, -2, -3, 4, -5, -6, 7, 8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, -6, 7, 8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, -6, 7, 8, 9, 10, -11], - vec![1, -2, -3, -4, 5, -6, 7, 8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, -6, 7, 8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, -6, 7, 8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, -6, 7, 8, 9, 10, -11], - vec![1, 2, -3, -4, -5, 6, 7, 8, 9, 10, -11], - vec![1, -2, 3, -4, -5, 6, 7, 8, 9, 10, -11], - vec![-1, 2, 3, -4, -5, 6, 7, 8, 9, 10, -11], - vec![1, -2, -3, 4, -5, 6, 7, 8, 9, 10, -11], - vec![-1, 2, -3, 4, -5, 6, 7, 8, 9, 10, -11], - vec![-1, -2, 3, 4, -5, 6, 7, 8, 9, 10, -11], - vec![1, -2, -3, -4, 5, 6, 7, 8, 9, 10, -11], - vec![-1, 2, -3, -4, 5, 6, 7, 8, 9, 10, -11], - vec![-1, -2, 3, -4, 5, 6, 7, 8, 9, 10, -11], - vec![-1, -2, -3, 4, 5, 6, 7, 8, 9, 10, -11], - vec![1, 2, 3, -4, -5, -6, -7, -8, 9, 10, 11], - vec![1, 2, -3, 4, -5, -6, -7, -8, 9, 10, 11], - vec![1, -2, 3, 4, -5, -6, -7, -8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, -6, -7, -8, 9, 10, 11], - vec![1, 2, -3, -4, 5, -6, -7, -8, 9, 10, 11], - vec![1, -2, 3, -4, 5, -6, -7, -8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, -6, -7, -8, 9, 10, 11], - vec![1, -2, -3, 4, 5, -6, -7, -8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, -6, -7, -8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, -6, -7, -8, 9, 10, 11], - vec![1, 2, 3, -4, -5, 6, -7, -8, 9, 10, 11], - vec![1, 2, -3, 4, -5, 6, -7, -8, 9, 10, 11], - vec![1, -2, 3, 4, -5, 6, -7, -8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, 6, -7, -8, 9, 10, 11], - vec![1, 2, -3, -4, 5, 6, -7, -8, 9, 10, 11], - vec![1, -2, 3, -4, 5, 6, -7, -8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, 6, -7, -8, 9, 10, 11], - vec![1, -2, -3, 4, 5, 6, -7, -8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, 6, -7, -8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, 6, -7, -8, 9, 10, 11], - vec![1, 2, 3, -4, -5, -6, 7, -8, 9, 10, 11], - vec![1, 2, -3, 4, -5, -6, 7, -8, 9, 10, 11], - vec![1, -2, 3, 4, -5, -6, 7, -8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, -6, 7, -8, 9, 10, 11], - vec![1, 2, -3, -4, 5, -6, 7, -8, 9, 10, 11], - vec![1, -2, 3, -4, 5, -6, 7, -8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, -6, 7, -8, 9, 10, 11], - vec![1, -2, -3, 4, 5, -6, 7, -8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, -6, 7, -8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, -6, 7, -8, 9, 10, 11], - vec![1, 2, 3, -4, -5, 6, 7, -8, 9, 10, 11], - vec![1, 2, -3, 4, -5, 6, 7, -8, 9, 10, 11], - vec![1, -2, 3, 4, -5, 6, 7, -8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, 6, 7, -8, 9, 10, 11], - vec![1, 2, -3, -4, 5, 6, 7, -8, 9, 10, 11], - vec![1, -2, 3, -4, 5, 6, 7, -8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, 6, 7, -8, 9, 10, 11], - vec![1, -2, -3, 4, 5, 6, 7, -8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, 6, 7, -8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, 6, 7, -8, 9, 10, 11], - vec![1, 2, 3, -4, -5, -6, -7, 8, 9, 10, 11], - vec![1, 2, -3, 4, -5, -6, -7, 8, 9, 10, 11], - vec![1, -2, 3, 4, -5, -6, -7, 8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, -6, -7, 8, 9, 10, 11], - vec![1, 2, -3, -4, 5, -6, -7, 8, 9, 10, 11], - vec![1, -2, 3, -4, 5, -6, -7, 8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, -6, -7, 8, 9, 10, 11], - vec![1, -2, -3, 4, 5, -6, -7, 8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, -6, -7, 8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, -6, -7, 8, 9, 10, 11], - vec![1, 2, 3, -4, -5, 6, -7, 8, 9, 10, 11], - vec![1, 2, -3, 4, -5, 6, -7, 8, 9, 10, 11], - vec![1, -2, 3, 4, -5, 6, -7, 8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, 6, -7, 8, 9, 10, 11], - vec![1, 2, -3, -4, 5, 6, -7, 8, 9, 10, 11], - vec![1, -2, 3, -4, 5, 6, -7, 8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, 6, -7, 8, 9, 10, 11], - vec![1, -2, -3, 4, 5, 6, -7, 8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, 6, -7, 8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, 6, -7, 8, 9, 10, 11], - vec![1, 2, 3, -4, -5, -6, 7, 8, 9, 10, 11], - vec![1, 2, -3, 4, -5, -6, 7, 8, 9, 10, 11], - vec![1, -2, 3, 4, -5, -6, 7, 8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, -6, 7, 8, 9, 10, 11], - vec![1, 2, -3, -4, 5, -6, 7, 8, 9, 10, 11], - vec![1, -2, 3, -4, 5, -6, 7, 8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, -6, 7, 8, 9, 10, 11], - vec![1, -2, -3, 4, 5, -6, 7, 8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, -6, 7, 8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, -6, 7, 8, 9, 10, 11], - vec![1, 2, 3, -4, -5, 6, 7, 8, 9, 10, 11], - vec![1, 2, -3, 4, -5, 6, 7, 8, 9, 10, 11], - vec![1, -2, 3, 4, -5, 6, 7, 8, 9, 10, 11], - vec![-1, 2, 3, 4, -5, 6, 7, 8, 9, 10, 11], - vec![1, 2, -3, -4, 5, 6, 7, 8, 9, 10, 11], - vec![1, -2, 3, -4, 5, 6, 7, 8, 9, 10, 11], - vec![-1, 2, 3, -4, 5, 6, 7, 8, 9, 10, 11], - vec![1, -2, -3, 4, 5, 6, 7, 8, 9, 10, 11], - vec![-1, 2, -3, 4, 5, 6, 7, 8, 9, 10, 11], - vec![-1, -2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + lits![-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11], + lits![-1, -2, -3, -4, -5, 6, -7, -8, -9, -10, -11], + lits![-1, -2, -3, -4, -5, -6, 7, -8, -9, -10, -11], + lits![-1, -2, -3, -4, -5, 6, 7, -8, -9, -10, -11], + lits![-1, -2, -3, -4, -5, -6, -7, 8, -9, -10, -11], + lits![-1, -2, -3, -4, -5, 6, -7, 8, -9, -10, -11], + lits![-1, -2, -3, -4, -5, -6, 7, 8, -9, -10, -11], + lits![-1, -2, -3, -4, -5, 6, 7, 8, -9, -10, -11], + lits![1, -2, -3, -4, -5, -6, -7, -8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, -6, -7, -8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, -6, -7, -8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, -6, -7, -8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, -6, -7, -8, 9, -10, -11], + lits![1, -2, -3, -4, -5, 6, -7, -8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, 6, -7, -8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, 6, -7, -8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, 6, -7, -8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, 6, -7, -8, 9, -10, -11], + lits![1, -2, -3, -4, -5, -6, 7, -8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, -6, 7, -8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, -6, 7, -8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, -6, 7, -8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, -6, 7, -8, 9, -10, -11], + lits![1, -2, -3, -4, -5, 6, 7, -8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, 6, 7, -8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, 6, 7, -8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, 6, 7, -8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, 6, 7, -8, 9, -10, -11], + lits![1, -2, -3, -4, -5, -6, -7, 8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, -6, -7, 8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, -6, -7, 8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, -6, -7, 8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, -6, -7, 8, 9, -10, -11], + lits![1, -2, -3, -4, -5, 6, -7, 8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, 6, -7, 8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, 6, -7, 8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, 6, -7, 8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, 6, -7, 8, 9, -10, -11], + lits![1, -2, -3, -4, -5, -6, 7, 8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, -6, 7, 8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, -6, 7, 8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, -6, 7, 8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, -6, 7, 8, 9, -10, -11], + lits![1, -2, -3, -4, -5, 6, 7, 8, 9, -10, -11], + lits![-1, 2, -3, -4, -5, 6, 7, 8, 9, -10, -11], + lits![-1, -2, 3, -4, -5, 6, 7, 8, 9, -10, -11], + lits![-1, -2, -3, 4, -5, 6, 7, 8, 9, -10, -11], + lits![-1, -2, -3, -4, 5, 6, 7, 8, 9, -10, -11], + lits![1, 2, -3, -4, -5, -6, -7, -8, 9, 10, -11], + lits![1, -2, 3, -4, -5, -6, -7, -8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, -6, -7, -8, 9, 10, -11], + lits![1, -2, -3, 4, -5, -6, -7, -8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, -6, -7, -8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, -6, -7, -8, 9, 10, -11], + lits![1, -2, -3, -4, 5, -6, -7, -8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, -6, -7, -8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, -6, -7, -8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, -6, -7, -8, 9, 10, -11], + lits![1, 2, -3, -4, -5, 6, -7, -8, 9, 10, -11], + lits![1, -2, 3, -4, -5, 6, -7, -8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, 6, -7, -8, 9, 10, -11], + lits![1, -2, -3, 4, -5, 6, -7, -8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, 6, -7, -8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, 6, -7, -8, 9, 10, -11], + lits![1, -2, -3, -4, 5, 6, -7, -8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, 6, -7, -8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, 6, -7, -8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, 6, -7, -8, 9, 10, -11], + lits![1, 2, -3, -4, -5, -6, 7, -8, 9, 10, -11], + lits![1, -2, 3, -4, -5, -6, 7, -8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, -6, 7, -8, 9, 10, -11], + lits![1, -2, -3, 4, -5, -6, 7, -8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, -6, 7, -8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, -6, 7, -8, 9, 10, -11], + lits![1, -2, -3, -4, 5, -6, 7, -8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, -6, 7, -8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, -6, 7, -8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, -6, 7, -8, 9, 10, -11], + lits![1, 2, -3, -4, -5, 6, 7, -8, 9, 10, -11], + lits![1, -2, 3, -4, -5, 6, 7, -8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, 6, 7, -8, 9, 10, -11], + lits![1, -2, -3, 4, -5, 6, 7, -8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, 6, 7, -8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, 6, 7, -8, 9, 10, -11], + lits![1, -2, -3, -4, 5, 6, 7, -8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, 6, 7, -8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, 6, 7, -8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, 6, 7, -8, 9, 10, -11], + lits![1, 2, -3, -4, -5, -6, -7, 8, 9, 10, -11], + lits![1, -2, 3, -4, -5, -6, -7, 8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, -6, -7, 8, 9, 10, -11], + lits![1, -2, -3, 4, -5, -6, -7, 8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, -6, -7, 8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, -6, -7, 8, 9, 10, -11], + lits![1, -2, -3, -4, 5, -6, -7, 8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, -6, -7, 8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, -6, -7, 8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, -6, -7, 8, 9, 10, -11], + lits![1, 2, -3, -4, -5, 6, -7, 8, 9, 10, -11], + lits![1, -2, 3, -4, -5, 6, -7, 8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, 6, -7, 8, 9, 10, -11], + lits![1, -2, -3, 4, -5, 6, -7, 8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, 6, -7, 8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, 6, -7, 8, 9, 10, -11], + lits![1, -2, -3, -4, 5, 6, -7, 8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, 6, -7, 8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, 6, -7, 8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, 6, -7, 8, 9, 10, -11], + lits![1, 2, -3, -4, -5, -6, 7, 8, 9, 10, -11], + lits![1, -2, 3, -4, -5, -6, 7, 8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, -6, 7, 8, 9, 10, -11], + lits![1, -2, -3, 4, -5, -6, 7, 8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, -6, 7, 8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, -6, 7, 8, 9, 10, -11], + lits![1, -2, -3, -4, 5, -6, 7, 8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, -6, 7, 8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, -6, 7, 8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, -6, 7, 8, 9, 10, -11], + lits![1, 2, -3, -4, -5, 6, 7, 8, 9, 10, -11], + lits![1, -2, 3, -4, -5, 6, 7, 8, 9, 10, -11], + lits![-1, 2, 3, -4, -5, 6, 7, 8, 9, 10, -11], + lits![1, -2, -3, 4, -5, 6, 7, 8, 9, 10, -11], + lits![-1, 2, -3, 4, -5, 6, 7, 8, 9, 10, -11], + lits![-1, -2, 3, 4, -5, 6, 7, 8, 9, 10, -11], + lits![1, -2, -3, -4, 5, 6, 7, 8, 9, 10, -11], + lits![-1, 2, -3, -4, 5, 6, 7, 8, 9, 10, -11], + lits![-1, -2, 3, -4, 5, 6, 7, 8, 9, 10, -11], + lits![-1, -2, -3, 4, 5, 6, 7, 8, 9, 10, -11], + lits![1, 2, 3, -4, -5, -6, -7, -8, 9, 10, 11], + lits![1, 2, -3, 4, -5, -6, -7, -8, 9, 10, 11], + lits![1, -2, 3, 4, -5, -6, -7, -8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, -6, -7, -8, 9, 10, 11], + lits![1, 2, -3, -4, 5, -6, -7, -8, 9, 10, 11], + lits![1, -2, 3, -4, 5, -6, -7, -8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, -6, -7, -8, 9, 10, 11], + lits![1, -2, -3, 4, 5, -6, -7, -8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, -6, -7, -8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, -6, -7, -8, 9, 10, 11], + lits![1, 2, 3, -4, -5, 6, -7, -8, 9, 10, 11], + lits![1, 2, -3, 4, -5, 6, -7, -8, 9, 10, 11], + lits![1, -2, 3, 4, -5, 6, -7, -8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, 6, -7, -8, 9, 10, 11], + lits![1, 2, -3, -4, 5, 6, -7, -8, 9, 10, 11], + lits![1, -2, 3, -4, 5, 6, -7, -8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, 6, -7, -8, 9, 10, 11], + lits![1, -2, -3, 4, 5, 6, -7, -8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, 6, -7, -8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, 6, -7, -8, 9, 10, 11], + lits![1, 2, 3, -4, -5, -6, 7, -8, 9, 10, 11], + lits![1, 2, -3, 4, -5, -6, 7, -8, 9, 10, 11], + lits![1, -2, 3, 4, -5, -6, 7, -8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, -6, 7, -8, 9, 10, 11], + lits![1, 2, -3, -4, 5, -6, 7, -8, 9, 10, 11], + lits![1, -2, 3, -4, 5, -6, 7, -8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, -6, 7, -8, 9, 10, 11], + lits![1, -2, -3, 4, 5, -6, 7, -8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, -6, 7, -8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, -6, 7, -8, 9, 10, 11], + lits![1, 2, 3, -4, -5, 6, 7, -8, 9, 10, 11], + lits![1, 2, -3, 4, -5, 6, 7, -8, 9, 10, 11], + lits![1, -2, 3, 4, -5, 6, 7, -8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, 6, 7, -8, 9, 10, 11], + lits![1, 2, -3, -4, 5, 6, 7, -8, 9, 10, 11], + lits![1, -2, 3, -4, 5, 6, 7, -8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, 6, 7, -8, 9, 10, 11], + lits![1, -2, -3, 4, 5, 6, 7, -8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, 6, 7, -8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, 6, 7, -8, 9, 10, 11], + lits![1, 2, 3, -4, -5, -6, -7, 8, 9, 10, 11], + lits![1, 2, -3, 4, -5, -6, -7, 8, 9, 10, 11], + lits![1, -2, 3, 4, -5, -6, -7, 8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, -6, -7, 8, 9, 10, 11], + lits![1, 2, -3, -4, 5, -6, -7, 8, 9, 10, 11], + lits![1, -2, 3, -4, 5, -6, -7, 8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, -6, -7, 8, 9, 10, 11], + lits![1, -2, -3, 4, 5, -6, -7, 8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, -6, -7, 8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, -6, -7, 8, 9, 10, 11], + lits![1, 2, 3, -4, -5, 6, -7, 8, 9, 10, 11], + lits![1, 2, -3, 4, -5, 6, -7, 8, 9, 10, 11], + lits![1, -2, 3, 4, -5, 6, -7, 8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, 6, -7, 8, 9, 10, 11], + lits![1, 2, -3, -4, 5, 6, -7, 8, 9, 10, 11], + lits![1, -2, 3, -4, 5, 6, -7, 8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, 6, -7, 8, 9, 10, 11], + lits![1, -2, -3, 4, 5, 6, -7, 8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, 6, -7, 8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, 6, -7, 8, 9, 10, 11], + lits![1, 2, 3, -4, -5, -6, 7, 8, 9, 10, 11], + lits![1, 2, -3, 4, -5, -6, 7, 8, 9, 10, 11], + lits![1, -2, 3, 4, -5, -6, 7, 8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, -6, 7, 8, 9, 10, 11], + lits![1, 2, -3, -4, 5, -6, 7, 8, 9, 10, 11], + lits![1, -2, 3, -4, 5, -6, 7, 8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, -6, 7, 8, 9, 10, 11], + lits![1, -2, -3, 4, 5, -6, 7, 8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, -6, 7, 8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, -6, 7, 8, 9, 10, 11], + lits![1, 2, 3, -4, -5, 6, 7, 8, 9, 10, 11], + lits![1, 2, -3, 4, -5, 6, 7, 8, 9, 10, 11], + lits![1, -2, 3, 4, -5, 6, 7, 8, 9, 10, 11], + lits![-1, 2, 3, 4, -5, 6, 7, 8, 9, 10, 11], + lits![1, 2, -3, -4, 5, 6, 7, 8, 9, 10, 11], + lits![1, -2, 3, -4, 5, 6, 7, 8, 9, 10, 11], + lits![-1, 2, 3, -4, 5, 6, 7, 8, 9, 10, 11], + lits![1, -2, -3, 4, 5, 6, 7, 8, 9, 10, 11], + lits![-1, 2, -3, 4, 5, 6, 7, 8, 9, 10, 11], + lits![-1, -2, 3, 4, 5, 6, 7, 8, 9, 10, 11], ]; let mut enc = get_sorted_encoder(SortedStrategy::Recursive); // println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -1395,9 +1382,10 @@ mod tests { // #[test] fn _test_5_6_sorted_eq() { let mut db = TestDB::new(11); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 5, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 5, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3, 4, 5, 6], LimitComp::Equal, &y); + let lits = lits![1, 2, 3, 4, 5, 6]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let mut enc = get_sorted_encoder(SortedStrategy::Recursive); println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -1407,9 +1395,10 @@ mod tests { // #[test] fn _test_6_7_sorted_eq() { let mut db = TestDB::new(13); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 6, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 6, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[1, 2, 3, 4, 5, 6, 7], LimitComp::Equal, &y); + let lits = lits![1, 2, 3, 4, 5, 6, 7]; + let con = &Sorted::new(&lits, LimitComp::Equal, &y); let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let mut enc = get_sorted_encoder(SortedStrategy::Recursive); println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); @@ -1419,26 +1408,27 @@ mod tests { #[test] fn test_5_1_sorted_eq_negated() { let mut db = TestDB::new(6); - let y: IntVarEnc<_, _> = IntVarOrd::from_bounds(&mut db, 0, 1, String::from("y")).into(); + let y: IntVarEnc = IntVarOrd::from_bounds(&mut db, 0, 1, String::from("y")).into(); db.num_var += y.lits() as i32; - let con = &Sorted::new(&[-1, -2, -3, -4, -5], LimitComp::LessEq, &y); + let lits = lits![-1, -2, -3, -4, -5]; + let con = &Sorted::new(&lits, LimitComp::LessEq, &y); // let sols = db.generate_solutions(|sol| con.check(sol).is_ok(), db.num_var); let sols = vec![ - vec![1, 2, 3, 4, 5, -6, -7], - vec![1, 2, 3, 4, 5, 6, -7], - vec![1, 2, 3, 4, -5, -6, 7], - vec![1, 2, 3, -4, 5, -6, 7], - vec![1, 2, -3, 4, 5, -6, 7], - vec![1, -2, 3, 4, 5, -6, 7], - vec![-1, 2, 3, 4, 5, -6, 7], - vec![1, 2, 3, 4, 5, -6, 7], - vec![1, 2, 3, 4, -5, 6, 7], - vec![1, 2, 3, -4, 5, 6, 7], - vec![1, 2, -3, 4, 5, 6, 7], - vec![1, -2, 3, 4, 5, 6, 7], - vec![-1, 2, 3, 4, 5, 6, 7], - vec![1, 2, 3, 4, 5, 6, 7], + lits![1, 2, 3, 4, 5, -6, -7], + lits![1, 2, 3, 4, 5, 6, -7], + lits![1, 2, 3, 4, -5, -6, 7], + lits![1, 2, 3, -4, 5, -6, 7], + lits![1, 2, -3, 4, 5, -6, 7], + lits![1, -2, 3, 4, 5, -6, 7], + lits![-1, 2, 3, 4, 5, -6, 7], + lits![1, 2, 3, 4, 5, -6, 7], + lits![1, 2, 3, 4, -5, 6, 7], + lits![1, 2, 3, -4, 5, 6, 7], + lits![1, 2, -3, 4, 5, 6, 7], + lits![1, -2, 3, 4, 5, 6, 7], + lits![-1, 2, 3, 4, 5, 6, 7], + lits![1, 2, 3, 4, 5, 6, 7], ]; let mut enc = get_sorted_encoder(SortedStrategy::Direct); // println!("\nlet sols = {};", TestDB::_print_solutions(&sols)); diff --git a/crates/pindakaas/src/trace.rs b/crates/pindakaas/src/trace.rs index 88bada353..79699a844 100644 --- a/crates/pindakaas/src/trace.rs +++ b/crates/pindakaas/src/trace.rs @@ -1,3 +1,5 @@ +use itertools::Itertools; + /// Helper marco to create a new variable within an Encoder #[cfg(feature = "trace")] macro_rules! new_var { @@ -27,9 +29,9 @@ pub(crate) use new_var; #[cfg(feature = "trace")] macro_rules! emit_clause { ($db:expr, $cl:expr) => {{ - let slice = $cl; - let res = $db.add_clause(slice); - tracing::info!(clause = ?slice, fail = matches!(res, Err($crate::Unsatisfiable)), "emit clause"); + let slice = $cl.into_iter().collect::>(); + let res = $db.add_clause(slice.iter().copied()); + tracing::info!(clause = ?&slice, fail = matches!(res, Err($crate::Unsatisfiable)), "emit clause"); res }}; } @@ -335,15 +337,15 @@ mod subscriber { impl Visit for EventVisitor { fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { - let value = Some(format!("{value:?}")); + let value = format!("{value:?}"); match field.name() { - "message" => match value.unwrap().as_str() { + "message" => match value.as_str() { "new variable" => self.kind = Some(EventKind::NewVar), "emit clause" => self.kind = Some(EventKind::Clause), _ => {} }, - "var" => self.var = value, - "clause" => self.clause = value, + "var" => self.var = Some(value), + "clause" => self.clause = Some(value), _ => {} } } @@ -400,12 +402,14 @@ mod subscriber { pub use subscriber::{FlushGuard, Tracer}; #[cfg(feature = "trace")] +use crate::Lit; + pub(crate) fn subscript_number(num: usize) -> impl Iterator { num.to_string() .chars() .map(|d| d.to_digit(10).unwrap()) .map(|d| char::from_u32(0x2080 + d).unwrap()) - .collect::>() + .collect_vec() .into_iter() } @@ -419,6 +423,6 @@ pub(crate) fn subscripted_name(name: &str, sub: usize) -> String { } #[cfg(feature = "trace")] -pub(crate) fn trace_print_lit(l: &Lit) -> String { +pub(crate) fn trace_print_lit(l: &Lit) -> String { format!("{}{{{:?}}}", if l.is_negated() { "¬" } else { "" }, l.var()) } diff --git a/crates/pyndakaas/src/lib.rs b/crates/pyndakaas/src/lib.rs index e5f340a40..7c26e7055 100644 --- a/crates/pyndakaas/src/lib.rs +++ b/crates/pyndakaas/src/lib.rs @@ -1,28 +1,18 @@ -use std::{ - fmt::Display, - num::ParseIntError, - ops::{Add, DerefMut, Mul}, - path::PathBuf, - str::FromStr, -}; +use std::{fmt::Display, ops::DerefMut, path::PathBuf}; -use base::{Encoder, LinExp, LinearConstraint, LinearEncoder, Literal}; -use num::{One, Zero}; +use base::{ClauseDatabase, Encoder, LinExp, LinearConstraint, LinearEncoder}; use pindakaas as base; use pyo3::{exceptions::PyArithmeticError, prelude::*}; #[pyclass] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Lit(i32); -impl Literal for Lit { - fn negate(&self) -> Self { - Lit(-self.0) - } - fn is_negated(&self) -> bool { +pub struct Lit(base::Lit); +impl Lit { + pub fn is_negated(&self) -> bool { self.0.is_negated() } - fn var(&self) -> Self { - Lit(self.0.abs()) + pub fn var(&self) -> Self { + Self(self.0.var().into()) // TODO } } impl Display for Lit { @@ -30,37 +20,6 @@ impl Display for Lit { self.0.fmt(f) } } -impl FromStr for Lit { - type Err = ParseIntError; - fn from_str(s: &str) -> Result { - Ok(Lit(i32::from_str(s)?)) - } -} -impl Zero for Lit { - fn zero() -> Self { - Lit(0) - } - fn is_zero(&self) -> bool { - self.0 == 0 - } -} -impl One for Lit { - fn one() -> Self { - Lit(1) - } -} -impl Add for Lit { - type Output = Lit; - fn add(self, rhs: Self) -> Self::Output { - Lit(self.0 + rhs.0) - } -} -impl Mul for Lit { - type Output = Lit; - fn mul(self, rhs: Self) -> Self::Output { - Lit(self.0 * rhs.0) - } -} type Clause = Vec; #[pymodule] @@ -75,7 +34,7 @@ fn module(_py: Python, m: &PyModule) -> PyResult<()> { fn adder_encode(mut db: PyRefMut<'_, Cnf>) -> Result<(), PyErr> { let pref = db.deref_mut(); let db = &mut pref.0; - let x = LinExp::from_slices(&[1, 2, 3], &[Lit(1), Lit(2), Lit(3)]); + let x = LinExp::from_slices(&[1, 2, 3], &[db.new_var(), db.new_var(), db.new_var()]); let con = LinearConstraint::new(x, base::Comparator::Equal, 2); let mut enc: LinearEncoder = LinearEncoder::default(); enc.encode(db, &con) @@ -83,7 +42,7 @@ fn adder_encode(mut db: PyRefMut<'_, Cnf>) -> Result<(), PyErr> { } #[pyclass(name = "CNF")] -struct Cnf(base::Cnf); +struct Cnf(base::Cnf); #[pymethods] impl Cnf { @@ -102,9 +61,10 @@ impl Cnf { fn __iter__(&self) -> ClauseIter { // FIXME: It would be great if this could be made lazily instead of copying everything when creating the iterator - ClauseIter { - inner: Vec::from_iter(self.0.iter().map(Vec::from)).into_iter(), - } + // ClauseIter { + // inner: Vec::from_iter(self.0.iter().map(Vec::from)).into_iter(), + // } + todo!() } } diff --git a/rustfmt.toml b/rustfmt.toml index 218e20321..9df34cdd6 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,3 @@ hard_tabs = true +imports_granularity = "Crate" +group_imports = "StdExternalCrate"