From b3ae02c128f0b211cce6159b7e9b2e06a345ba53 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Fri, 20 Mar 2026 11:09:32 +1100 Subject: [PATCH 01/14] Create CLAUDE.md file --- CLAUDE.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..61898f246 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,82 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +Pumpkin is a constraint programming (CP) solver written in pure Rust, based on the lazy clause generation paradigm. It supports proof logging via DRAT (SAT) and DRCP (CP) certificates. + +## Build & Development Commands + +```bash +# Build +cargo build +cargo build --release + +# Test (standard CI command) +cargo test --release --no-fail-fast --features pumpkin-solver/check-propagations + +# Run a single test +cargo test --release --features pumpkin-solver/check-propagations + +# Format (requires nightly) +cargo +nightly fmt +cargo +nightly fmt --check # check only + +# Lint (requires nightly) +cargo +nightly clippy --all-targets -- -Dwarnings + +# Docs +cargo doc --no-deps + +# License check +cargo deny check + +# WASM tests (for pumpkin-crates/core) +wasm-pack test --release --node + +# Python tests +cd pumpkin-solver-py && pytest +``` + +Scripts in `scripts/` (`fmt.sh`, `clippy.sh`, `documentation.sh`, `deny.sh`) mirror the CI commands and are also run by the pre-commit hook (`.githooks/pre-commit`). + +## Workspace Architecture + +The project is a Cargo workspace (edition 2024, resolver 2). Crates are grouped by role: + +### Core Engine (`pumpkin-crates/`) +- **`core`** — The main solver engine. Contains the CDCL loop, propagation engine, nogood learning, branching heuristics, and proof logging infrastructure. This is the heart of the solver. +- **`checking`** — Shared types used by both `core` and `pumpkin-checker` (avoids circular deps). +- **`propagators`** — Implementations of CP propagators (arithmetic, cumulative, disjunctive, element, etc.). +- **`conflict-resolvers`** — Pluggable conflict analysis strategies for nogood derivation. +- **`constraints`** — High-level constraint API built on top of `core`. + +### Interfaces +- **`pumpkin-solver`** — CLI binary. Accepts CNF, WCNF (MaxSAT), and FlatZinc input formats. +- **`pumpkin-solver-py`** — Python bindings via PyO3. + +### Proof Infrastructure +- **`drcp-format`** — Reading/writing the DRCP proof certificate format. +- **`pumpkin-checker`** — Standalone proof verification tool. +- **`pumpkin-proof-processor`** — Proof transformation/preprocessing utility. +- **`drcp-debugger`** — Debugging tool for DRCP proofs. + +### Parsing & Utilities +- **`fzn-rs`** / **`fzn-rs-derive`** — FlatZinc parser and derive macros. +- **`pumpkin-macros`** — Procedural macros used across the workspace. +- **`minizinc/`** — MiniZinc integration (solver plugin). + +## Key Design Concepts + +- **Lazy Clause Generation (LCG)**: The solver operates on a hybrid SAT/CP model. CP propagators generate explanations (clauses/nogoods) on demand during conflict analysis. +- **Proof Logging**: Every inference can be certified. The `core` crate threads proof-logging through propagators via a `Proof` type. The `check-propagations` feature enables runtime validation of propagator explanations during tests. +- **Propagator Interface**: Custom propagators implement the `Propagator` trait in `pumpkin-crates/core`. They must provide both `propagate` and `explain` methods for proof soundness. +- **Feature Flags**: `pumpkin-solver/check-propagations` enables expensive correctness assertions — always enable this when running tests. + +## Code Style + +- **Rust edition 2024**, stable toolchain (see `rust-toolchain.toml`), but formatting/linting requires nightly. +- Line length: 120 chars for comments (`rustfmt.toml`). +- Imports: grouped (std / external / crate), single-line style enforced. +- Workspace-level lints are configured in the root `Cargo.toml` — check there before suppressing warnings locally. From 492da7e1ea03d4aa6addcd6b26d5f216c668e49b Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Fri, 20 Mar 2026 13:41:17 +1100 Subject: [PATCH 02/14] refactor(pumpkin-propagators): Move away from `TestSolver` in propagator tests The `TestSolver` is deprecated because it has unclear semantics on how it works. The `State` abstraction is much better designed, so more appropriate for test cases for propagators. Not all tests are converted, because I wanted to avoid changing the State API in this PR. Some tests use features in `TestSolver` that are not present in `State` for various reasons. This is entirely produced by clause code, and nice to evaluate how well it performs on these refactoring tasks. The task is not difficult, but tedious for us to complete. --- .../propagators/arithmetic/absolute_value.rs | 147 ++-- .../arithmetic/binary/binary_equals.rs | 69 +- .../arithmetic/binary/binary_not_equals.rs | 82 ++- .../arithmetic/integer_division.rs | 18 +- .../arithmetic/integer_multiplication.rs | 304 +++++---- .../arithmetic/linear_less_or_equal.rs | 122 ++-- .../arithmetic/linear_not_equal.rs | 130 ++-- .../src/propagators/arithmetic/maximum.rs | 138 ++-- .../time_table_over_interval_incremental.rs | 635 +++++++++--------- .../synchronisation.rs | 11 +- .../time_table_per_point_incremental.rs | 602 +++++++++-------- .../time_table/time_table_over_interval.rs | 549 ++++++++------- .../time_table/time_table_per_point.rs | 551 ++++++++------- .../disjunctive/disjunctive_propagator.rs | 64 +- .../disjunctive/theta_lambda_tree.rs | 19 +- .../propagators/src/propagators/element.rs | 235 ++++--- 16 files changed, 1978 insertions(+), 1698 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs index 95b6e334a..3d0d6efa7 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs @@ -205,124 +205,123 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; + use pumpkin_core::state::State; use super::*; #[test] fn absolute_bounds_are_propagated_at_initialise() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let signed = solver.new_variable(-3, 4); - let absolute = solver.new_variable(-2, 10); - let constraint_tag = solver.new_constraint_tag(); + let signed = state.new_interval_variable(-3, 4, None); + let absolute = state.new_interval_variable(-2, 10, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(AbsoluteValueArgs { - signed, - absolute, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(AbsoluteValueArgs { + signed, + absolute, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(absolute, 0, 4); + assert_eq!(state.lower_bound(absolute), 0); + assert_eq!(state.upper_bound(absolute), 4); } #[test] fn signed_bounds_are_propagated_at_initialise() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let signed = solver.new_variable(-5, 5); - let absolute = solver.new_variable(0, 3); - let constraint_tag = solver.new_constraint_tag(); + let signed = state.new_interval_variable(-5, 5, None); + let absolute = state.new_interval_variable(0, 3, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(AbsoluteValueArgs { - signed, - absolute, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(AbsoluteValueArgs { + signed, + absolute, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(signed, -3, 3); + assert_eq!(state.lower_bound(signed), -3); + assert_eq!(state.upper_bound(signed), 3); } #[test] fn absolute_lower_bound_can_be_strictly_positive() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let signed = solver.new_variable(3, 6); - let absolute = solver.new_variable(0, 10); - let constraint_tag = solver.new_constraint_tag(); + let signed = state.new_interval_variable(3, 6, None); + let absolute = state.new_interval_variable(0, 10, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(AbsoluteValueArgs { - signed, - absolute, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(AbsoluteValueArgs { + signed, + absolute, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(absolute, 3, 6); + assert_eq!(state.lower_bound(absolute), 3); + assert_eq!(state.upper_bound(absolute), 6); } #[test] fn strictly_negative_signed_value_can_propagate_lower_bound_on_absolute() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let signed = solver.new_variable(-5, -3); - let absolute = solver.new_variable(1, 5); - let constraint_tag = solver.new_constraint_tag(); + let signed = state.new_interval_variable(-5, -3, None); + let absolute = state.new_interval_variable(1, 5, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(AbsoluteValueArgs { - signed, - absolute, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(AbsoluteValueArgs { + signed, + absolute, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(absolute, 3, 5); + assert_eq!(state.lower_bound(absolute), 3); + assert_eq!(state.upper_bound(absolute), 5); } #[test] fn lower_bound_on_absolute_can_propagate_negative_upper_bound_on_signed() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let signed = solver.new_variable(-5, 0); - let absolute = solver.new_variable(1, 5); - let constraint_tag = solver.new_constraint_tag(); + let signed = state.new_interval_variable(-5, 0, None); + let absolute = state.new_interval_variable(1, 5, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(AbsoluteValueArgs { - signed, - absolute, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(AbsoluteValueArgs { + signed, + absolute, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(signed, -5, -1); + assert_eq!(state.lower_bound(signed), -5); + assert_eq!(state.upper_bound(signed), -1); } #[test] fn lower_bound_on_absolute_can_propagate_positive_lower_bound_on_signed() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let signed = solver.new_variable(1, 5); - let absolute = solver.new_variable(3, 5); - let constraint_tag = solver.new_constraint_tag(); + let signed = state.new_interval_variable(1, 5, None); + let absolute = state.new_interval_variable(3, 5, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(AbsoluteValueArgs { - signed, - absolute, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(AbsoluteValueArgs { + signed, + absolute, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(signed, 3, 5); + assert_eq!(state.lower_bound(signed), 3); + assert_eq!(state.upper_bound(signed), 5); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs index 7241fd3ae..f9909e58a 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs @@ -439,59 +439,67 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; - use pumpkin_core::propagation::EnqueueDecision; + use pumpkin_core::state::State; use crate::propagators::arithmetic::BinaryEqualsPropagatorArgs; #[test] fn test_propagation_of_bounds() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(0, 5); - let b = solver.new_variable(3, 7); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let a = state.new_interval_variable(0, 5, None); + let b = state.new_interval_variable(3, 7, None); + let constraint_tag = state.new_constraint_tag(); - let result = solver.new_propagator(BinaryEqualsPropagatorArgs { + let _ = state.add_propagator(BinaryEqualsPropagatorArgs { a, b, constraint_tag, }); + let result = state.propagate_to_fixed_point(); assert!(result.is_ok()); - solver.assert_bounds(a, 3, 5); - solver.assert_bounds(b, 3, 5); + assert_eq!(state.lower_bound(a), 3); + assert_eq!(state.upper_bound(a), 5); + assert_eq!(state.lower_bound(b), 3); + assert_eq!(state.upper_bound(b), 5); } #[test] fn test_propagation_of_holes() { - let mut solver = TestSolver::default(); - let a = solver.new_sparse_variable(vec![2, 4, 6, 9]); - let b = solver.new_sparse_variable(vec![3, 4, 7, 9]); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let a = state.new_sparse_variable(vec![2, 4, 6, 9], None); + let b = state.new_sparse_variable(vec![3, 4, 7, 9], None); + let constraint_tag = state.new_constraint_tag(); - let result = solver.new_propagator(BinaryEqualsPropagatorArgs { + let _ = state.add_propagator(BinaryEqualsPropagatorArgs { a, b, constraint_tag, }); + let result = state.propagate_to_fixed_point(); assert!(result.is_ok()); - solver.assert_bounds(a, 4, 9); - solver.assert_bounds(b, 4, 9); + assert_eq!(state.lower_bound(a), 4); + assert_eq!(state.upper_bound(a), 9); + assert_eq!(state.lower_bound(b), 4); + assert_eq!(state.upper_bound(b), 9); for i in 5..=8 { - assert!(!solver.contains(a, i)); - assert!(!solver.contains(b, i)); + assert!(!state.contains(a, i)); + assert!(!state.contains(b, i)); } } + #[allow(deprecated, reason = "Uses TestSolver for EnqueueDecision assertions")] #[test] fn test_propagation_of_holes_incremental() { + use pumpkin_core::TestSolver; + use pumpkin_core::propagation::EnqueueDecision; + let mut solver = TestSolver::default(); let a = solver.new_variable(2, 9); let b = solver.new_variable(3, 9); @@ -527,17 +535,18 @@ mod tests { #[test] fn test_conflict() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(0, 5); - let b = solver.new_variable(6, 9); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let a = state.new_interval_variable(0, 5, None); + let b = state.new_interval_variable(6, 9, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(BinaryEqualsPropagatorArgs { - a, - b, - constraint_tag, - }) - .expect_err("Expected result to be err"); + let _ = state.add_propagator(BinaryEqualsPropagatorArgs { + a, + b, + constraint_tag, + }); + let result = state.propagate_to_fixed_point(); + + assert!(result.is_err()); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index efe6d8768..966b33789 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -206,50 +206,55 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; - use pumpkin_core::propagation::EnqueueDecision; + use pumpkin_core::state::State; use crate::propagators::arithmetic::BinaryNotEqualsPropagatorArgs; #[test] fn detects_conflict() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(0, 0); - let b = solver.new_variable(0, 0); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let a = state.new_interval_variable(0, 0, None); + let b = state.new_interval_variable(0, 0, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(BinaryNotEqualsPropagatorArgs { - a, - b, - constraint_tag, - }) - .expect_err("Expected conflict to be detected"); + let _ = state.add_propagator(BinaryNotEqualsPropagatorArgs { + a, + b, + constraint_tag, + }); + let result = state.propagate_to_fixed_point(); + + assert!(result.is_err(), "Expected conflict to be detected"); } #[test] fn propagate_when_one_is_fixed() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(0, 0); - let b = solver.new_variable(0, 1); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let a = state.new_interval_variable(0, 0, None); + let b = state.new_interval_variable(0, 1, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(BinaryNotEqualsPropagatorArgs { - a, - b, - constraint_tag, - }) + let _ = state.add_propagator(BinaryNotEqualsPropagatorArgs { + a, + b, + constraint_tag, + }); + state + .propagate_to_fixed_point() .expect("Expected no conflict to be detected"); - solver.assert_bounds(b, 1, 1); + assert_eq!(state.lower_bound(b), 1); + assert_eq!(state.upper_bound(b), 1); } + #[allow(deprecated, reason = "Uses TestSolver for EnqueueDecision assertions")] #[test] fn incremental_propagation() { + use pumpkin_core::TestSolver; + use pumpkin_core::propagation::EnqueueDecision; + let mut solver = TestSolver::default(); let a = solver.new_variable(0, 0); let b = solver.new_variable(0, 10); @@ -277,20 +282,23 @@ mod tests { #[test] fn non_overlapping_is_ok() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(0, 5); - let b = solver.new_variable(6, 10); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let a = state.new_interval_variable(0, 5, None); + let b = state.new_interval_variable(6, 10, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(BinaryNotEqualsPropagatorArgs { - a, - b, - constraint_tag, - }) + let _ = state.add_propagator(BinaryNotEqualsPropagatorArgs { + a, + b, + constraint_tag, + }); + state + .propagate_to_fixed_point() .expect("Expected no conflict to be detected"); - solver.assert_bounds(a, 0, 5); - solver.assert_bounds(b, 6, 10); + assert_eq!(state.lower_bound(a), 0); + assert_eq!(state.upper_bound(a), 5); + assert_eq!(state.lower_bound(b), 6); + assert_eq!(state.upper_bound(b), 10); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs index 526f1511c..e6e7ae2a6 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs @@ -457,28 +457,28 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; + use pumpkin_core::state::State; use super::*; #[test] fn detects_conflicts() { - let mut solver = TestSolver::default(); - let numerator = solver.new_variable(1, 1); - let denominator = solver.new_variable(2, 2); - let rhs = solver.new_variable(2, 2); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let numerator = state.new_interval_variable(1, 1, None); + let denominator = state.new_interval_variable(2, 2, None); + let rhs = state.new_interval_variable(2, 2, None); + let constraint_tag = state.new_constraint_tag(); - let propagator = solver.new_propagator(DivisionArgs { + let _ = state.add_propagator(DivisionArgs { numerator, denominator, rhs, constraint_tag, }); + let result = state.propagate_to_fixed_point(); - assert!(propagator.is_err()); + assert!(result.is_err()); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index 79dd4de68..937ec601f 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -417,44 +417,56 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; + use pumpkin_core::predicate; + use pumpkin_core::predicates::Predicate; + use pumpkin_core::predicates::PropositionalConjunction; + use pumpkin_core::propagation::CurrentNogood; + use pumpkin_core::state::State; use super::*; #[test] fn bounds_of_a_and_b_propagate_bounds_c() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(1, 3); - let b = solver.new_variable(0, 4); - let c = solver.new_variable(-10, 20); - - let constraint_tag = solver.new_constraint_tag(); - - let propagator = solver - .new_propagator(IntegerMultiplicationArgs { - a, - b, - c, - constraint_tag, - }) - .expect("no empty domains"); - - solver.propagate(propagator).expect("no empty domains"); - - assert_eq!(1, solver.lower_bound(a)); - assert_eq!(3, solver.upper_bound(a)); - assert_eq!(0, solver.lower_bound(b)); - assert_eq!(4, solver.upper_bound(b)); - assert_eq!(0, solver.lower_bound(c)); - assert_eq!(12, solver.upper_bound(c)); - - let reason_lb = solver.get_reason_int(predicate![c >= 0]); + let mut state = State::default(); + let a = state.new_interval_variable(1, 3, None); + let b = state.new_interval_variable(0, 4, None); + let c = state.new_interval_variable(-10, 20, None); + + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(IntegerMultiplicationArgs { + a, + b, + c, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); + + assert_eq!(1, state.lower_bound(a)); + assert_eq!(3, state.upper_bound(a)); + assert_eq!(0, state.lower_bound(b)); + assert_eq!(4, state.upper_bound(b)); + assert_eq!(0, state.lower_bound(c)); + assert_eq!(12, state.upper_bound(c)); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![c >= 0], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason_lb: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([a >= 0] & [b >= 0]), reason_lb); - let reason_ub = solver.get_reason_int(predicate![c <= 12]); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![c <= 12], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason_ub: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( conjunction!([a >= 0] & [a <= 3] & [b >= 0] & [b <= 4]), reason_ub @@ -463,141 +475,155 @@ mod tests { #[test] fn bounds_of_a_and_c_propagate_bounds_b() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(2, 3); - let b = solver.new_variable(0, 12); - let c = solver.new_variable(2, 12); - - let constraint_tag = solver.new_constraint_tag(); - - let propagator = solver - .new_propagator(IntegerMultiplicationArgs { - a, - b, - c, - constraint_tag, - }) - .expect("no empty domains"); - - solver.propagate(propagator).expect("no empty domains"); - - assert_eq!(2, solver.lower_bound(a)); - assert_eq!(3, solver.upper_bound(a)); - assert_eq!(1, solver.lower_bound(b)); - assert_eq!(6, solver.upper_bound(b)); - assert_eq!(2, solver.lower_bound(c)); - assert_eq!(12, solver.upper_bound(c)); - - let reason_lb = solver.get_reason_int(predicate![b >= 1]); + let mut state = State::default(); + let a = state.new_interval_variable(2, 3, None); + let b = state.new_interval_variable(0, 12, None); + let c = state.new_interval_variable(2, 12, None); + + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(IntegerMultiplicationArgs { + a, + b, + c, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); + + assert_eq!(2, state.lower_bound(a)); + assert_eq!(3, state.upper_bound(a)); + assert_eq!(1, state.lower_bound(b)); + assert_eq!(6, state.upper_bound(b)); + assert_eq!(2, state.lower_bound(c)); + assert_eq!(12, state.upper_bound(c)); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![b >= 1], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason_lb: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([a >= 1] & [c >= 1]), reason_lb); - let reason_ub = solver.get_reason_int(predicate![b <= 6]); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![b <= 6], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason_ub: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([a >= 2] & [c >= 0] & [c <= 12]), reason_ub); } #[test] fn bounds_of_b_and_c_propagate_bounds_a() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(0, 10); - let b = solver.new_variable(3, 6); - let c = solver.new_variable(2, 12); - - let constraint_tag = solver.new_constraint_tag(); - - let propagator = solver - .new_propagator(IntegerMultiplicationArgs { - a, - b, - c, - constraint_tag, - }) - .expect("no empty domains"); - - solver.propagate(propagator).expect("no empty domains"); - - assert_eq!(1, solver.lower_bound(a)); - assert_eq!(4, solver.upper_bound(a)); - assert_eq!(3, solver.lower_bound(b)); - assert_eq!(6, solver.upper_bound(b)); - assert_eq!(3, solver.lower_bound(c)); - assert_eq!(12, solver.upper_bound(c)); - - let reason_lb = solver.get_reason_int(predicate![a >= 1]); + let mut state = State::default(); + let a = state.new_interval_variable(0, 10, None); + let b = state.new_interval_variable(3, 6, None); + let c = state.new_interval_variable(2, 12, None); + + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(IntegerMultiplicationArgs { + a, + b, + c, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); + + assert_eq!(1, state.lower_bound(a)); + assert_eq!(4, state.upper_bound(a)); + assert_eq!(3, state.lower_bound(b)); + assert_eq!(6, state.upper_bound(b)); + assert_eq!(3, state.lower_bound(c)); + assert_eq!(12, state.upper_bound(c)); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![a >= 1], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason_lb: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([b >= 1] & [c >= 1]), reason_lb); - let reason_ub = solver.get_reason_int(predicate![a <= 4]); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![a <= 4], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason_ub: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([b >= 3] & [c >= 0] & [c <= 12]), reason_ub); } #[test] fn b_unbounded_does_not_panic() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(12, 12); - let b = solver.new_variable(i32::MIN, i32::MAX); - let c = solver.new_variable(144, 144); - - let constraint_tag = solver.new_constraint_tag(); - let _ = solver - .new_propagator(IntegerMultiplicationArgs { - a, - b, - c, - constraint_tag, - }) - .expect("No empty domains"); + let mut state = State::default(); + let a = state.new_interval_variable(12, 12, None); + let b = state.new_interval_variable(i32::MIN, i32::MAX, None); + let c = state.new_interval_variable(144, 144, None); + + let constraint_tag = state.new_constraint_tag(); + let _ = state.add_propagator(IntegerMultiplicationArgs { + a, + b, + c, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("No empty domains"); } #[test] fn a_unbounded_does_not_panic() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(i32::MIN, i32::MAX); - let b = solver.new_variable(12, 12); - let c = solver.new_variable(144, 144); - - let constraint_tag = solver.new_constraint_tag(); - let _ = solver - .new_propagator(IntegerMultiplicationArgs { - a, - b, - c, - constraint_tag, - }) - .expect("No empty domains"); + let mut state = State::default(); + let a = state.new_interval_variable(i32::MIN, i32::MAX, None); + let b = state.new_interval_variable(12, 12, None); + let c = state.new_interval_variable(144, 144, None); + + let constraint_tag = state.new_constraint_tag(); + let _ = state.add_propagator(IntegerMultiplicationArgs { + a, + b, + c, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("No empty domains"); } #[test] fn c_unbounded_does_not_panic() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(12, 12); - let b = solver.new_variable(12, 12); - let c = solver.new_variable(i32::MIN, i32::MAX); - - let constraint_tag = solver.new_constraint_tag(); - let _ = solver - .new_propagator(IntegerMultiplicationArgs { - a, - b, - c, - constraint_tag, - }) - .expect("No empty domains"); + let mut state = State::default(); + let a = state.new_interval_variable(12, 12, None); + let b = state.new_interval_variable(12, 12, None); + let c = state.new_interval_variable(i32::MIN, i32::MAX, None); + + let constraint_tag = state.new_constraint_tag(); + let _ = state.add_propagator(IntegerMultiplicationArgs { + a, + b, + c, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("No empty domains"); } #[test] fn all_unbounded_does_not_panic() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(i32::MIN, i32::MAX); - let b = solver.new_variable(i32::MIN, i32::MAX); - let c = solver.new_variable(i32::MIN, i32::MAX); - - let constraint_tag = solver.new_constraint_tag(); - let _ = solver - .new_propagator(IntegerMultiplicationArgs { - a, - b, - c, - constraint_tag, - }) - .expect("No empty domains"); + let mut state = State::default(); + let a = state.new_interval_variable(i32::MIN, i32::MAX, None); + let b = state.new_interval_variable(i32::MIN, i32::MAX, None); + let c = state.new_interval_variable(i32::MIN, i32::MAX, None); + + let constraint_tag = state.new_constraint_tag(); + let _ = state.add_propagator(IntegerMultiplicationArgs { + a, + b, + c, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("No empty domains"); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index e1e616574..ef3c9200f 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -324,89 +324,95 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; use pumpkin_core::conjunction; + use pumpkin_core::predicate; + use pumpkin_core::predicates::Predicate; + use pumpkin_core::predicates::PropositionalConjunction; + use pumpkin_core::propagation::CurrentNogood; + use pumpkin_core::state::State; use super::*; #[test] fn test_bounds_are_propagated() { - let mut solver = TestSolver::default(); - let x = solver.new_variable(1, 5); - let y = solver.new_variable(0, 10); + let mut state = State::default(); + let x = state.new_interval_variable(1, 5, None); + let y = state.new_interval_variable(0, 10, None); - let constraint_tag = solver.new_constraint_tag(); + let constraint_tag = state.new_constraint_tag(); - let propagator = solver - .new_propagator(LinearLessOrEqualPropagatorArgs { - x: [x, y].into(), - c: 7, - constraint_tag, - }) - .expect("no empty domains"); - - solver.propagate(propagator).expect("non-empty domain"); + let _ = state.add_propagator(LinearLessOrEqualPropagatorArgs { + x: [x, y].into(), + c: 7, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(x, 1, 5); - solver.assert_bounds(y, 0, 6); + assert_eq!(state.lower_bound(x), 1); + assert_eq!(state.upper_bound(x), 5); + assert_eq!(state.lower_bound(y), 0); + assert_eq!(state.upper_bound(y), 6); } #[test] fn test_explanations() { - let mut solver = TestSolver::default(); - let x = solver.new_variable(1, 5); - let y = solver.new_variable(0, 10); - let constraint_tag = solver.new_constraint_tag(); - - let propagator = solver - .new_propagator(LinearLessOrEqualPropagatorArgs { - x: [x, y].into(), - c: 7, - constraint_tag, - }) - .expect("no empty domains"); - - solver.propagate(propagator).expect("non-empty domain"); - - let reason = solver.get_reason_int(predicate![y <= 6]); + let mut state = State::default(); + let x = state.new_interval_variable(1, 5, None); + let y = state.new_interval_variable(0, 10, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(LinearLessOrEqualPropagatorArgs { + x: [x, y].into(), + c: 7, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![y <= 6], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([x >= 1]), reason); } #[test] fn overflow_leads_to_conflict() { - let mut solver = TestSolver::default(); - - let x = solver.new_variable(i32::MAX, i32::MAX); - let y = solver.new_variable(1, 1); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator(LinearLessOrEqualPropagatorArgs { - x: [x, y].into(), - c: i32::MAX, - constraint_tag, - }) - .expect_err("Expected overflow to be detected"); + let mut state = State::default(); + + let x = state.new_interval_variable(i32::MAX, i32::MAX, None); + let y = state.new_interval_variable(1, 1, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(LinearLessOrEqualPropagatorArgs { + x: [x, y].into(), + c: i32::MAX, + constraint_tag, + }); + let result = state.propagate_to_fixed_point(); + assert!(result.is_err(), "Expected overflow to be detected"); } #[test] fn underflow_leads_to_no_propagation() { - let mut solver = TestSolver::default(); - - let x = solver.new_variable(i32::MIN, i32::MIN); - let y = solver.new_variable(-1, -1); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator(LinearLessOrEqualPropagatorArgs { - x: [x, y].into(), - c: i32::MIN, - constraint_tag, - }) + let mut state = State::default(); + + let x = state.new_interval_variable(i32::MIN, i32::MIN, None); + let y = state.new_interval_variable(-1, -1, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(LinearLessOrEqualPropagatorArgs { + x: [x, y].into(), + c: i32::MIN, + constraint_tag, + }); + state + .propagate_to_fixed_point() .expect("Expected no error to be detected"); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index 31f8a96e3..afbf77b86 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -402,54 +402,55 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; use pumpkin_core::conjunction; + use pumpkin_core::predicate; + use pumpkin_core::predicates::Predicate; + use pumpkin_core::predicates::PropositionalConjunction; + use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::Conflict; + use pumpkin_core::state::State; use pumpkin_core::variables::TransformableVariable; use super::*; #[test] fn test_value_is_removed() { - let mut solver = TestSolver::default(); - let x = solver.new_variable(2, 2); - let y = solver.new_variable(1, 5); + let mut state = State::default(); + let x = state.new_interval_variable(2, 2, None); + let y = state.new_interval_variable(1, 5, None); - let constraint_tag = solver.new_constraint_tag(); + let constraint_tag = state.new_constraint_tag(); - let propagator = solver - .new_propagator(LinearNotEqualPropagatorArgs { - terms: [x.scaled(1), y.scaled(-1)].into(), - rhs: 0, - constraint_tag, - }) - .expect("non-empty domain"); - - solver.propagate(propagator).expect("non-empty domain"); - - solver.assert_bounds(x, 2, 2); - solver.assert_bounds(y, 1, 5); - assert!(!solver.contains(y, 2)); + let _ = state.add_propagator(LinearNotEqualPropagatorArgs { + terms: [x.scaled(1), y.scaled(-1)].into(), + rhs: 0, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("non-empty domain"); + + assert_eq!(state.lower_bound(x), 2); + assert_eq!(state.upper_bound(x), 2); + assert_eq!(state.lower_bound(y), 1); + assert_eq!(state.upper_bound(y), 5); + assert!(!state.contains(y, 2)); } #[test] fn test_empty_domain_is_detected() { - let mut solver = TestSolver::default(); - let x = solver.new_variable(2, 2); - let y = solver.new_variable(2, 2); + let mut state = State::default(); + let x = state.new_interval_variable(2, 2, None); + let y = state.new_interval_variable(2, 2, None); - let constraint_tag = solver.new_constraint_tag(); + let constraint_tag = state.new_constraint_tag(); - let err = solver - .new_propagator(LinearNotEqualPropagatorArgs { - terms: [x.scaled(1), y.scaled(-1)].into(), - rhs: 0, - constraint_tag, - }) - .expect_err("empty domain"); + let _ = state.add_propagator(LinearNotEqualPropagatorArgs { + terms: [x.scaled(1), y.scaled(-1)].into(), + rhs: 0, + constraint_tag, + }); + let err = state.propagate_to_fixed_point().expect_err("empty domain"); let expected = conjunction!([x == 2] & [y == 2]); @@ -461,53 +462,52 @@ mod tests { #[test] fn explanation_for_propagation() { - let mut solver = TestSolver::default(); - let x = solver.new_variable(2, 2).scaled(1); - let y = solver.new_variable(1, 5).scaled(-1); - - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let x = state.new_interval_variable(2, 2, None).scaled(1); + let y = state.new_interval_variable(1, 5, None).scaled(-1); - let propagator = solver - .new_propagator(LinearNotEqualPropagatorArgs { - terms: [x, y].into(), - rhs: 0, - constraint_tag, - }) - .expect("non-empty domain"); - - solver.propagate(propagator).expect("non-empty domain"); + let constraint_tag = state.new_constraint_tag(); - let reason = solver.get_reason_int(predicate![y != -2]); + let _ = state.add_propagator(LinearNotEqualPropagatorArgs { + terms: [x, y].into(), + rhs: 0, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("non-empty domain"); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![y != -2], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([x == 2]), reason); } #[test] fn satisfied_constraint_does_not_trigger_conflict() { - let mut solver = TestSolver::default(); - let x = solver.new_variable(0, 3); - let y = solver.new_variable(0, 3); + let mut state = State::default(); + let x = state.new_interval_variable(0, 3, None); + let y = state.new_interval_variable(0, 3, None); - let constraint_tag = solver.new_constraint_tag(); + let constraint_tag = state.new_constraint_tag(); - let propagator = solver - .new_propagator(LinearNotEqualPropagatorArgs { - terms: [x.scaled(1), y.scaled(-1)].into(), - rhs: 0, - constraint_tag, - }) - .expect("non-empty domain"); - - solver.remove(x, 0).expect("non-empty domain"); - solver.remove(x, 2).expect("non-empty domain"); - solver.remove(x, 3).expect("non-empty domain"); + let _ = state.add_propagator(LinearNotEqualPropagatorArgs { + terms: [x.scaled(1), y.scaled(-1)].into(), + rhs: 0, + constraint_tag, + }); - solver.remove(y, 0).expect("non-empty domain"); - solver.remove(y, 1).expect("non-empty domain"); - solver.remove(y, 2).expect("non-empty domain"); + let _ = state.post(predicate![x != 0]).unwrap(); + let _ = state.post(predicate![x != 2]).unwrap(); + let _ = state.post(predicate![x != 3]).unwrap(); - solver.notify_propagator(propagator); + let _ = state.post(predicate![y != 0]).unwrap(); + let _ = state.post(predicate![y != 1]).unwrap(); + let _ = state.post(predicate![y != 2]).unwrap(); - solver.propagate(propagator).expect("non-empty domain"); + state.propagate_to_fixed_point().expect("non-empty domain"); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index d1f927697..c87e83edc 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -224,109 +224,131 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; + use pumpkin_core::predicate; + use pumpkin_core::predicates::Predicate; + use pumpkin_core::predicates::PropositionalConjunction; + use pumpkin_core::propagation::CurrentNogood; + use pumpkin_core::state::State; use super::*; #[test] fn upper_bound_of_rhs_matches_maximum_upper_bound_of_array_at_initialise() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let a = solver.new_variable(1, 3); - let b = solver.new_variable(1, 4); - let c = solver.new_variable(1, 5); + let a = state.new_interval_variable(1, 3, None); + let b = state.new_interval_variable(1, 4, None); + let c = state.new_interval_variable(1, 5, None); - let rhs = solver.new_variable(1, 10); - let constraint_tag = solver.new_constraint_tag(); + let rhs = state.new_interval_variable(1, 10, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(MaximumArgs { - array: [a, b, c].into(), - rhs, - constraint_tag, - }) - .expect("no empty domain"); + let _ = state.add_propagator(MaximumArgs { + array: [a, b, c].into(), + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domain"); - solver.assert_bounds(rhs, 1, 5); + assert_eq!(state.lower_bound(rhs), 1); + assert_eq!(state.upper_bound(rhs), 5); - let reason = solver.get_reason_int(predicate![rhs <= 5]); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![rhs <= 5], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([a <= 5] & [b <= 5] & [c <= 5]), reason); } #[test] fn lower_bound_of_rhs_is_maximum_of_lower_bounds_in_array() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let a = solver.new_variable(3, 10); - let b = solver.new_variable(4, 10); - let c = solver.new_variable(5, 10); + let a = state.new_interval_variable(3, 10, None); + let b = state.new_interval_variable(4, 10, None); + let c = state.new_interval_variable(5, 10, None); - let rhs = solver.new_variable(1, 10); - let constraint_tag = solver.new_constraint_tag(); + let rhs = state.new_interval_variable(1, 10, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(MaximumArgs { - array: [a, b, c].into(), - rhs, - constraint_tag, - }) - .expect("no empty domain"); + let _ = state.add_propagator(MaximumArgs { + array: [a, b, c].into(), + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domain"); - solver.assert_bounds(rhs, 5, 10); + assert_eq!(state.lower_bound(rhs), 5); + assert_eq!(state.upper_bound(rhs), 10); - let reason = solver.get_reason_int(predicate![rhs >= 5]); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![rhs >= 5], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([c >= 5]), reason); } #[test] fn upper_bound_of_all_array_elements_at_most_rhs_max_at_initialise() { - let mut solver = TestSolver::default(); + let mut state = State::default(); let array = (1..=5) - .map(|idx| solver.new_variable(1, 4 + idx)) + .map(|idx| state.new_interval_variable(1, 4 + idx, None)) .collect::>(); - let rhs = solver.new_variable(1, 3); - let constraint_tag = solver.new_constraint_tag(); + let rhs = state.new_interval_variable(1, 3, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(MaximumArgs { - array: array.clone(), - rhs, - constraint_tag, - }) - .expect("no empty domain"); + let _ = state.add_propagator(MaximumArgs { + array: array.clone(), + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domain"); for var in array.iter() { - solver.assert_bounds(*var, 1, 3); - let reason = solver.get_reason_int(predicate![var <= 3]); + assert_eq!(state.lower_bound(*var), 1); + assert_eq!(state.upper_bound(*var), 3); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![var <= 3], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([rhs <= 3]), reason); } } #[test] fn single_variable_propagate() { - let mut solver = TestSolver::default(); + let mut state = State::default(); let array = (1..=5) - .map(|idx| solver.new_variable(1, 1 + 10 * idx)) + .map(|idx| state.new_interval_variable(1, 1 + 10 * idx, None)) .collect::>(); - let rhs = solver.new_variable(45, 60); - let constraint_tag = solver.new_constraint_tag(); + let rhs = state.new_interval_variable(45, 60, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(MaximumArgs { - array: array.clone(), - rhs, - constraint_tag, - }) - .expect("no empty domain"); + let _ = state.add_propagator(MaximumArgs { + array: array.clone(), + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domain"); - solver.assert_bounds(*array.last().unwrap(), 45, 51); - solver.assert_bounds(rhs, 45, 51); + assert_eq!(state.lower_bound(*array.last().unwrap()), 45); + assert_eq!(state.upper_bound(*array.last().unwrap()), 51); + assert_eq!(state.lower_bound(rhs), 45); + assert_eq!(state.upper_bound(rhs), 51); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index 07e5b8cb2..8f35a5ba1 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -649,15 +649,21 @@ fn find_overlapping_profile( }) } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { + #[allow( + deprecated, + reason = "TestSolver is deprecated but still used in these tests" + )] use pumpkin_core::TestSolver; use pumpkin_core::conjunction; use pumpkin_core::predicate; use pumpkin_core::predicates::Predicate; + use pumpkin_core::predicates::PropositionalConjunction; + use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::state::Conflict; + use pumpkin_core::state::State; use pumpkin_core::variables::DomainId; use crate::cumulative::ArgTask; @@ -667,72 +673,71 @@ mod tests { #[test] fn propagator_propagates_from_profile() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTableOverIntervalIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); } #[test] fn propagator_detects_conflict() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 1); - let constraint_tag = solver.new_constraint_tag(); - - let result = solver.new_propagator(TimeTableOverIntervalIncrementalPropagator::< - DomainId, - false, - >::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 4, - resource_usage: 1, + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 1, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 4, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() - }, - constraint_tag, - )); + constraint_tag, + ), + ); + let result = state.propagate_to_fixed_point(); assert!(matches!(result, Err(Conflict::Propagator(_)))); assert!(match result { @@ -757,98 +762,100 @@ mod tests { #[test] fn propagator_propagates_nothing() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(0, 6); - let s2 = solver.new_variable(0, 6); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTableOverIntervalIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 6); - assert_eq!(solver.lower_bound(s1), 0); - assert_eq!(solver.upper_bound(s1), 6); + let mut state = State::default(); + let s1 = state.new_interval_variable(0, 6, None); + let s2 = state.new_interval_variable(0, 6, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 6); + assert_eq!(state.lower_bound(s1), 0); + assert_eq!(state.upper_bound(s1), 6); } #[test] fn propagator_propagates_example_4_3_schutt() { - let mut solver = TestSolver::default(); - let f = solver.new_variable(0, 14); - let e = solver.new_variable(2, 4); - let d = solver.new_variable(0, 2); - let c = solver.new_variable(8, 9); - let b = solver.new_variable(2, 3); - let a = solver.new_variable(0, 1); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTableOverIntervalIncrementalPropagator::::new( - &[ - ArgTask { - start_time: a, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: b, - processing_time: 6, - resource_usage: 2, - }, - ArgTask { - start_time: c, - processing_time: 2, - resource_usage: 4, - }, - ArgTask { - start_time: d, - processing_time: 2, - resource_usage: 2, - }, - ArgTask { - start_time: e, - processing_time: 5, - resource_usage: 2, - }, - ArgTask { - start_time: f, - processing_time: 6, - resource_usage: 2, - }, - ] - .into_iter() - .collect::>(), - 5, - CumulativePropagatorOptions::default(), - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(f), 10); + let mut state = State::default(); + let f = state.new_interval_variable(0, 14, None); + let e = state.new_interval_variable(2, 4, None); + let d = state.new_interval_variable(0, 2, None); + let c = state.new_interval_variable(8, 9, None); + let b = state.new_interval_variable(2, 3, None); + let a = state.new_interval_variable(0, 1, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: a, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: b, + processing_time: 6, + resource_usage: 2, + }, + ArgTask { + start_time: c, + processing_time: 2, + resource_usage: 4, + }, + ArgTask { + start_time: d, + processing_time: 2, + resource_usage: 2, + }, + ArgTask { + start_time: e, + processing_time: 5, + resource_usage: 2, + }, + ArgTask { + start_time: f, + processing_time: 6, + resource_usage: 2, + }, + ] + .into_iter() + .collect::>(), + 5, + CumulativePropagatorOptions::default(), + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(f), 10); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_after_assignment() { let mut solver = TestSolver::default(); let s1 = solver.new_variable(0, 6); @@ -898,47 +905,56 @@ mod tests { #[test] fn propagator_propagates_end_time() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(6, 6); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTableOverIntervalIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let mut state = State::default(); + let s1 = state.new_interval_variable(6, 6, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 1); - assert_eq!(solver.upper_bound(s2), 3); - assert_eq!(solver.lower_bound(s1), 6); - assert_eq!(solver.upper_bound(s1), 6); - - let reason = solver.get_reason_int(predicate!(s2 <= 3)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 1); + assert_eq!(state.upper_bound(s2), 3); + assert_eq!(state.lower_bound(s1), 6); + assert_eq!(state.upper_bound(s1), 6); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 <= 3), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_after_update() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -1016,6 +1032,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_multiple_profiles() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -1098,138 +1118,153 @@ mod tests { #[test] fn propagator_propagates_from_profile_reason() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTableOverIntervalIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); - - let reason = solver.get_reason_int(predicate!(s2 >= 5)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 >= 5), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); } #[test] fn propagator_propagates_generic_bounds() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(3, 3); - let s2 = solver.new_variable(5, 5); - let s3 = solver.new_variable(1, 15); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTableOverIntervalIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s3, - processing_time: 4, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let mut state = State::default(); + let s1 = state.new_interval_variable(3, 3, None); + let s2 = state.new_interval_variable(5, 5, None); + let s3 = state.new_interval_variable(1, 15, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 2, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s3), 7); - assert_eq!(solver.upper_bound(s3), 15); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 5); - assert_eq!(solver.lower_bound(s1), 3); - assert_eq!(solver.upper_bound(s1), 3); - - let reason = solver.get_reason_int(predicate!(s3 >= 7)); + ArgTask { + start_time: s2, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: s3, + processing_time: 4, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s3), 7); + assert_eq!(state.upper_bound(s3), 15); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 5); + assert_eq!(state.lower_bound(s1), 3); + assert_eq!(state.upper_bound(s1), 3); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s3 >= 7), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 5]), reason); } #[test] fn propagator_propagates_with_holes() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(4, 4); - let s2 = solver.new_variable(0, 8); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTableOverIntervalIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - allow_holes_in_domain: true, - ..Default::default() + let mut state = State::default(); + let s1 = state.new_interval_variable(4, 4, None); + let s2 = state.new_interval_variable(0, 8, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTableOverIntervalIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 4); - assert_eq!(solver.upper_bound(s1), 4); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + allow_holes_in_domain: true, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 4); + assert_eq!(state.upper_bound(s1), 4); for removed in 2..8 { - assert!(!solver.contains(s2, removed)); - let reason = solver.get_reason_int(predicate!(s2 != removed)); + assert!(!state.contains(s2, removed)); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 != removed), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/synchronisation.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/synchronisation.rs index c5e9e40c9..fca2a5be2 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/synchronisation.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/synchronisation.rs @@ -160,13 +160,12 @@ fn sort_profile_based_on_id(profile: &mut Resour profile.profile_tasks.sort_by_key(|task| task.id.unpack()); } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { use std::rc::Rc; - use pumpkin_core::TestSolver; use pumpkin_core::propagation::LocalId; + use pumpkin_core::state::State; use super::find_synchronised_conflict; use crate::cumulative::CumulativeParameters; @@ -177,11 +176,11 @@ mod tests { #[test] fn test_correct_conflict_returned() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let x0 = solver.new_variable(0, 10); - let x1 = solver.new_variable(0, 10); - let x2 = solver.new_variable(0, 10); + let x0 = state.new_interval_variable(0, 10, None); + let x1 = state.new_interval_variable(0, 10, None); + let x2 = state.new_interval_variable(0, 10, None); let tasks = vec![ Task { diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs index 3ab30639d..6c7dc3ed7 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs @@ -646,15 +646,21 @@ mod debug { } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { + #[allow( + deprecated, + reason = "TestSolver is deprecated but still used in these tests" + )] use pumpkin_core::TestSolver; use pumpkin_core::conjunction; use pumpkin_core::predicate; use pumpkin_core::predicates::Predicate; + use pumpkin_core::predicates::PropositionalConjunction; + use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::state::Conflict; + use pumpkin_core::state::State; use pumpkin_core::variables::DomainId; use crate::cumulative::ArgTask; @@ -665,48 +671,47 @@ mod tests { #[test] fn propagator_propagates_from_profile() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator( - TimeTablePerPointIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); + let _ = state.add_propagator( + TimeTablePerPointIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); } #[test] fn propagator_detects_conflict() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 1); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 1, None); + let constraint_tag = state.new_constraint_tag(); - let result = solver.new_propagator( + let _ = state.add_propagator( TimeTablePerPointIncrementalPropagator::::new( &[ ArgTask { @@ -730,6 +735,7 @@ mod tests { constraint_tag, ), ); + let result = state.propagate_to_fixed_point(); assert!(match result { Err(Conflict::Propagator(x)) => { let expected = [ @@ -751,98 +757,100 @@ mod tests { #[test] fn propagator_propagates_nothing() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(0, 6); - let s2 = solver.new_variable(0, 6); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(0, 6, None); + let s2 = state.new_interval_variable(0, 6, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator( - TimeTablePerPointIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 6); - assert_eq!(solver.lower_bound(s1), 0); - assert_eq!(solver.upper_bound(s1), 6); + let _ = state.add_propagator( + TimeTablePerPointIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 6); + assert_eq!(state.lower_bound(s1), 0); + assert_eq!(state.upper_bound(s1), 6); } #[test] fn propagator_propagates_example_4_3_schutt() { - let mut solver = TestSolver::default(); - let f = solver.new_variable(0, 14); - let e = solver.new_variable(2, 4); - let d = solver.new_variable(0, 2); - let c = solver.new_variable(8, 9); - let b = solver.new_variable(2, 3); - let a = solver.new_variable(0, 1); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator( - TimeTablePerPointIncrementalPropagator::::new( - &[ - ArgTask { - start_time: a, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: b, - processing_time: 6, - resource_usage: 2, - }, - ArgTask { - start_time: c, - processing_time: 2, - resource_usage: 4, - }, - ArgTask { - start_time: d, - processing_time: 2, - resource_usage: 2, - }, - ArgTask { - start_time: e, - processing_time: 5, - resource_usage: 2, - }, - ArgTask { - start_time: f, - processing_time: 6, - resource_usage: 2, - }, - ] - .into_iter() - .collect::>(), - 5, - CumulativePropagatorOptions::default(), - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(f), 10); + let mut state = State::default(); + let f = state.new_interval_variable(0, 14, None); + let e = state.new_interval_variable(2, 4, None); + let d = state.new_interval_variable(0, 2, None); + let c = state.new_interval_variable(8, 9, None); + let b = state.new_interval_variable(2, 3, None); + let a = state.new_interval_variable(0, 1, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator( + TimeTablePerPointIncrementalPropagator::::new( + &[ + ArgTask { + start_time: a, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: b, + processing_time: 6, + resource_usage: 2, + }, + ArgTask { + start_time: c, + processing_time: 2, + resource_usage: 4, + }, + ArgTask { + start_time: d, + processing_time: 2, + resource_usage: 2, + }, + ArgTask { + start_time: e, + processing_time: 5, + resource_usage: 2, + }, + ArgTask { + start_time: f, + processing_time: 6, + resource_usage: 2, + }, + ] + .into_iter() + .collect::>(), + 5, + CumulativePropagatorOptions::default(), + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(f), 10); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_after_assignment() { let mut solver = TestSolver::default(); let s1 = solver.new_variable(0, 6); @@ -892,49 +900,56 @@ mod tests { #[test] fn propagator_propagates_end_time() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(6, 6); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(6, 6, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); - let propagator = solver - .new_propagator( - TimeTablePerPointIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator( + TimeTablePerPointIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - let result = solver.propagate_until_fixed_point(propagator); - assert!(result.is_ok()); - assert_eq!(solver.lower_bound(s2), 1); - assert_eq!(solver.upper_bound(s2), 3); - assert_eq!(solver.lower_bound(s1), 6); - assert_eq!(solver.upper_bound(s1), 6); - - let reason = solver.get_reason_int(predicate!(s2 <= 3)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 1); + assert_eq!(state.upper_bound(s2), 3); + assert_eq!(state.lower_bound(s1), 6); + assert_eq!(state.upper_bound(s1), 6); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 <= 3), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 <= 5] & [s1 >= 6] & [s1 <= 6]), reason); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_after_update() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -1012,6 +1027,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_multiple_profiles() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -1094,43 +1113,48 @@ mod tests { #[test] fn propagator_propagates_from_profile_reason() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator( - TimeTablePerPointIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator( + TimeTablePerPointIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); - - let reason = solver.get_reason_int(predicate!(s2 >= 5)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 >= 5), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( conjunction!([s2 >= 4] & [s1 >= 1] & [s1 <= 1]), /* Note that this not * the most general @@ -1146,51 +1170,56 @@ mod tests { #[test] fn propagator_propagates_generic_bounds() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(3, 3); - let s2 = solver.new_variable(5, 5); - let s3 = solver.new_variable(1, 15); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(3, 3, None); + let s2 = state.new_interval_variable(5, 5, None); + let s3 = state.new_interval_variable(1, 15, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator( - TimeTablePerPointIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s3, - processing_time: 4, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator( + TimeTablePerPointIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 2, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s3), 7); - assert_eq!(solver.upper_bound(s3), 15); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 5); - assert_eq!(solver.lower_bound(s1), 3); - assert_eq!(solver.upper_bound(s1), 3); - - let reason = solver.get_reason_int(predicate!(s3 >= 7)); + ArgTask { + start_time: s2, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: s3, + processing_time: 4, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s3), 7); + assert_eq!(state.upper_bound(s3), 15); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 5); + assert_eq!(state.lower_bound(s1), 3); + assert_eq!(state.upper_bound(s1), 3); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s3 >= 7), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 6]), /* Note that s3 would * have been able to @@ -1203,51 +1232,60 @@ mod tests { #[test] fn propagator_propagates_with_holes() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(4, 4); - let s2 = solver.new_variable(0, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(4, 4, None); + let s2 = state.new_interval_variable(0, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator( - TimeTablePerPointIncrementalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - allow_holes_in_domain: true, - ..Default::default() + let _ = state.add_propagator( + TimeTablePerPointIncrementalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - ), - ) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 4); - assert_eq!(solver.upper_bound(s1), 4); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + allow_holes_in_domain: true, + ..Default::default() + }, + constraint_tag, + ), + ); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 4); + assert_eq!(state.upper_bound(s1), 4); for removed in 2..8 { - assert!(!solver.contains(s2, removed)); - let reason = solver.get_reason_int(predicate!(s2 != removed)); + assert!(!state.contains(s2, removed)); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 != removed), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn synchronisation_leads_to_same_conflict_explanation() { let mut solver_scratch = TestSolver::default(); let s1_scratch = solver_scratch.new_variable(5, 5); @@ -1346,6 +1384,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn synchronisation_leads_to_same_conflict_after_propagating() { let mut solver_scratch = TestSolver::default(); let s1_scratch = solver_scratch.new_variable(5, 5); @@ -1443,6 +1485,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn no_synchronisation_leads_to_different_conflict_explanation() { let mut solver_scratch = TestSolver::default(); let s1_scratch = solver_scratch.new_variable(5, 5); @@ -1538,6 +1584,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn synchronisation_leads_to_same_explanation() { let mut solver_scratch = TestSolver::default(); let s1_scratch = solver_scratch.new_variable(1, 6); @@ -1632,6 +1682,10 @@ mod tests { ); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn no_synchronisation_leads_to_different_explanation() { let mut solver_scratch = TestSolver::default(); let s1_scratch = solver_scratch.new_variable(1, 6); @@ -1729,6 +1783,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn synchronisation_leads_to_same_conflict() { let mut solver_scratch = TestSolver::default(); let s0_scratch = solver_scratch.new_variable(1, 11); diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index 3d3640783..c819aea79 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -490,15 +490,22 @@ pub(crate) fn propagate_from_scratch_time_table_interval>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); } #[test] fn propagator_detects_conflict() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 1); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 1, None); + let constraint_tag = state.new_constraint_tag(); - let result = solver.new_propagator(TimeTableOverIntervalPropagator::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( &[ ArgTask { start_time: s1, @@ -568,6 +574,7 @@ mod tests { }, constraint_tag, )); + let result = state.propagate_to_fixed_point(); assert!(match result { Err(e) => { @@ -595,94 +602,96 @@ mod tests { #[test] fn propagator_propagates_nothing() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(0, 6); - let s2 = solver.new_variable(0, 6); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(0, 6, None); + let s2 = state.new_interval_variable(0, 6, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 6); - assert_eq!(solver.lower_bound(s1), 0); - assert_eq!(solver.upper_bound(s1), 6); + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 6); + assert_eq!(state.lower_bound(s1), 0); + assert_eq!(state.upper_bound(s1), 6); } #[test] fn propagator_propagates_example_4_3_schutt() { - let mut solver = TestSolver::default(); - let f = solver.new_variable(0, 14); - let e = solver.new_variable(2, 4); - let d = solver.new_variable(0, 2); - let c = solver.new_variable(8, 9); - let b = solver.new_variable(2, 3); - let a = solver.new_variable(0, 1); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: a, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: b, - processing_time: 6, - resource_usage: 2, - }, - ArgTask { - start_time: c, - processing_time: 2, - resource_usage: 4, - }, - ArgTask { - start_time: d, - processing_time: 2, - resource_usage: 2, - }, - ArgTask { - start_time: e, - processing_time: 5, - resource_usage: 2, - }, - ArgTask { - start_time: f, - processing_time: 6, - resource_usage: 2, - }, - ] - .into_iter() - .collect::>(), - 5, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(f), 10); + let mut state = State::default(); + let f = state.new_interval_variable(0, 14, None); + let e = state.new_interval_variable(2, 4, None); + let d = state.new_interval_variable(0, 2, None); + let c = state.new_interval_variable(8, 9, None); + let b = state.new_interval_variable(2, 3, None); + let a = state.new_interval_variable(0, 1, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: a, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: b, + processing_time: 6, + resource_usage: 2, + }, + ArgTask { + start_time: c, + processing_time: 2, + resource_usage: 4, + }, + ArgTask { + start_time: d, + processing_time: 2, + resource_usage: 2, + }, + ArgTask { + start_time: e, + processing_time: 5, + resource_usage: 2, + }, + ArgTask { + start_time: f, + processing_time: 6, + resource_usage: 2, + }, + ] + .into_iter() + .collect::>(), + 5, + CumulativePropagatorOptions::default(), + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(f), 10); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_after_assignment() { let mut solver = TestSolver::default(); let s1 = solver.new_variable(0, 6); @@ -730,45 +739,54 @@ mod tests { #[test] fn propagator_propagates_end_time() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(6, 6); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(6, 6, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 1); - assert_eq!(solver.upper_bound(s2), 3); - assert_eq!(solver.lower_bound(s1), 6); - assert_eq!(solver.upper_bound(s1), 6); - - let reason = solver.get_reason_int(predicate!(s2 <= 3)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 1); + assert_eq!(state.upper_bound(s2), 3); + assert_eq!(state.lower_bound(s1), 6); + assert_eq!(state.upper_bound(s1), 6); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 <= 3), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_after_update() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -844,6 +862,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_multiple_profiles() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -925,132 +947,147 @@ mod tests { #[test] fn propagator_propagates_from_profile_reason() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); - - let reason = solver.get_reason_int(predicate!(s2 >= 5)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 >= 5), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); } #[test] fn propagator_propagates_generic_bounds() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(3, 3); - let s2 = solver.new_variable(5, 5); - let s3 = solver.new_variable(1, 15); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(3, 3, None); + let s2 = state.new_interval_variable(5, 5, None); + let s3 = state.new_interval_variable(1, 15, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s3, - processing_time: 4, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 2, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s3), 7); - assert_eq!(solver.upper_bound(s3), 15); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 5); - assert_eq!(solver.lower_bound(s1), 3); - assert_eq!(solver.upper_bound(s1), 3); - - let reason = solver.get_reason_int(predicate!(s3 >= 7)); + ArgTask { + start_time: s2, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: s3, + processing_time: 4, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s3), 7); + assert_eq!(state.upper_bound(s3), 15); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 5); + assert_eq!(state.lower_bound(s1), 3); + assert_eq!(state.upper_bound(s1), 3); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s3 >= 7), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 5]), reason); } #[test] fn propagator_propagates_with_holes() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(4, 4); - let s2 = solver.new_variable(0, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(4, 4, None); + let s2 = state.new_interval_variable(0, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - allow_holes_in_domain: true, - ..Default::default() + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 4); - assert_eq!(solver.upper_bound(s1), 4); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + allow_holes_in_domain: true, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 4); + assert_eq!(state.upper_bound(s1), 4); for removed in 2..8 { - assert!(!solver.contains(s2, removed)); - let reason = solver.get_reason_int(predicate!(s2 != removed)); + assert!(!state.contains(s2, removed)); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 != removed), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index 2b26a43b2..6aca03bce 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -296,15 +296,22 @@ pub(crate) fn propagate_from_scratch_time_table_point>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); } #[test] fn propagator_detects_conflict() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 1); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 1, None); + let constraint_tag = state.new_constraint_tag(); - let result = solver.new_propagator(TimeTablePerPointPropagator::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( &[ ArgTask { start_time: s1, @@ -374,6 +380,7 @@ mod tests { }, constraint_tag, )); + let result = state.propagate_to_fixed_point(); assert!(match result { Err(e) => match e { Conflict::EmptyDomain(_) => false, @@ -399,94 +406,96 @@ mod tests { #[test] fn propagator_propagates_nothing() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(0, 6); - let s2 = solver.new_variable(0, 6); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(0, 6, None); + let s2 = state.new_interval_variable(0, 6, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTablePerPointPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 6); - assert_eq!(solver.lower_bound(s1), 0); - assert_eq!(solver.upper_bound(s1), 6); + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions::default(), + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 6); + assert_eq!(state.lower_bound(s1), 0); + assert_eq!(state.upper_bound(s1), 6); } #[test] fn propagator_propagates_example_4_3_schutt() { - let mut solver = TestSolver::default(); - let f = solver.new_variable(0, 14); - let e = solver.new_variable(2, 4); - let d = solver.new_variable(0, 2); - let c = solver.new_variable(8, 9); - let b = solver.new_variable(2, 3); - let a = solver.new_variable(0, 1); - let constraint_tag = solver.new_constraint_tag(); - - let _ = solver - .new_propagator(TimeTablePerPointPropagator::new( - &[ - ArgTask { - start_time: a, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: b, - processing_time: 6, - resource_usage: 2, - }, - ArgTask { - start_time: c, - processing_time: 2, - resource_usage: 4, - }, - ArgTask { - start_time: d, - processing_time: 2, - resource_usage: 2, - }, - ArgTask { - start_time: e, - processing_time: 5, - resource_usage: 2, - }, - ArgTask { - start_time: f, - processing_time: 6, - resource_usage: 2, - }, - ] - .into_iter() - .collect::>(), - 5, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(f), 10); + let mut state = State::default(); + let f = state.new_interval_variable(0, 14, None); + let e = state.new_interval_variable(2, 4, None); + let d = state.new_interval_variable(0, 2, None); + let c = state.new_interval_variable(8, 9, None); + let b = state.new_interval_variable(2, 3, None); + let a = state.new_interval_variable(0, 1, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + &[ + ArgTask { + start_time: a, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: b, + processing_time: 6, + resource_usage: 2, + }, + ArgTask { + start_time: c, + processing_time: 2, + resource_usage: 4, + }, + ArgTask { + start_time: d, + processing_time: 2, + resource_usage: 2, + }, + ArgTask { + start_time: e, + processing_time: 5, + resource_usage: 2, + }, + ArgTask { + start_time: f, + processing_time: 6, + resource_usage: 2, + }, + ] + .into_iter() + .collect::>(), + 5, + CumulativePropagatorOptions::default(), + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(f), 10); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_after_assignment() { let mut solver = TestSolver::default(); let s1 = solver.new_variable(0, 6); @@ -534,47 +543,54 @@ mod tests { #[test] fn propagator_propagates_end_time() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(6, 6); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(6, 6, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); - let propagator = solver - .new_propagator(TimeTablePerPointPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - let result = solver.propagate_until_fixed_point(propagator); - assert!(result.is_ok()); - assert_eq!(solver.lower_bound(s2), 1); - assert_eq!(solver.upper_bound(s2), 3); - assert_eq!(solver.lower_bound(s1), 6); - assert_eq!(solver.upper_bound(s1), 6); - - let reason = solver.get_reason_int(predicate!(s2 <= 3)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 1); + assert_eq!(state.upper_bound(s2), 3); + assert_eq!(state.lower_bound(s1), 6); + assert_eq!(state.upper_bound(s1), 6); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 <= 3), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s2 <= 5] & [s1 >= 6] & [s1 <= 6]), reason); } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_after_update() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -650,6 +666,10 @@ mod tests { } #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] fn propagator_propagates_example_4_3_schutt_multiple_profiles() { let mut solver = TestSolver::default(); let f = solver.new_variable(0, 14); @@ -730,41 +750,46 @@ mod tests { #[test] fn propagator_propagates_from_profile_reason() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(1, 1); - let s2 = solver.new_variable(1, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTablePerPointPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 1); - assert_eq!(solver.upper_bound(s1), 1); - - let reason = solver.get_reason_int(predicate!(s2 >= 5)); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 1); + assert_eq!(state.upper_bound(s1), 1); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 >= 5), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( conjunction!([s2 >= 4] & [s1 >= 1] & [s1 <= 1]), /* Note that this not * the most general @@ -780,49 +805,54 @@ mod tests { #[test] fn propagator_propagates_generic_bounds() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(3, 3); - let s2 = solver.new_variable(5, 5); - let s3 = solver.new_variable(1, 15); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(3, 3, None); + let s2 = state.new_interval_variable(5, 5, None); + let s3 = state.new_interval_variable(1, 15, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTablePerPointPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: s3, - processing_time: 4, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 2, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s3), 7); - assert_eq!(solver.upper_bound(s3), 15); - assert_eq!(solver.lower_bound(s2), 5); - assert_eq!(solver.upper_bound(s2), 5); - assert_eq!(solver.lower_bound(s1), 3); - assert_eq!(solver.upper_bound(s1), 3); - - let reason = solver.get_reason_int(predicate!(s3 >= 7)); + ArgTask { + start_time: s2, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: s3, + processing_time: 4, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s3), 7); + assert_eq!(state.upper_bound(s3), 15); + assert_eq!(state.lower_bound(s2), 5); + assert_eq!(state.upper_bound(s2), 5); + assert_eq!(state.lower_bound(s1), 3); + assert_eq!(state.upper_bound(s1), 3); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s3 >= 7), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 6]), /* Note that s3 would * have been able to @@ -835,44 +865,49 @@ mod tests { #[test] fn propagator_propagates_with_holes() { - let mut solver = TestSolver::default(); - let s1 = solver.new_variable(4, 4); - let s2 = solver.new_variable(0, 8); - let constraint_tag = solver.new_constraint_tag(); + let mut state = State::default(); + let s1 = state.new_interval_variable(4, 4, None); + let s2 = state.new_interval_variable(0, 8, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(TimeTablePerPointPropagator::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - allow_holes_in_domain: true, - ..Default::default() + let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, }, - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 0); - assert_eq!(solver.upper_bound(s2), 8); - assert_eq!(solver.lower_bound(s1), 4); - assert_eq!(solver.upper_bound(s1), 4); + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + allow_holes_in_domain: true, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(s2), 0); + assert_eq!(state.upper_bound(s2), 8); + assert_eq!(state.lower_bound(s1), 4); + assert_eq!(state.upper_bound(s1), 4); for removed in 2..8 { - assert!(!solver.contains(s2, removed)); - let reason = solver.get_reason_int(predicate!(s2 != removed)); + assert!(!state.contains(s2, removed)); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 != removed), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs index 8799b938c..57c8f6fd7 100644 --- a/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs +++ b/pumpkin-crates/propagators/src/propagators/disjunctive/disjunctive_propagator.rs @@ -417,46 +417,44 @@ fn create_propagation_explanation<'a, Var: IntegerVariable>( explanation.into() } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; + use pumpkin_core::state::State; use crate::disjunctive::ArgDisjunctiveTask; use crate::disjunctive::DisjunctiveConstructor; #[test] fn propagator_propagates_lower_bound() { - let mut solver = TestSolver::default(); - let c = solver.new_variable(4, 26); - let d = solver.new_variable(13, 13); - let e = solver.new_variable(5, 10); - let f = solver.new_variable(5, 10); - - let constraint_tag = solver.new_constraint_tag(); - let _ = solver - .new_propagator(DisjunctiveConstructor::new( - [ - ArgDisjunctiveTask { - start_time: c, - processing_time: 4, - }, - ArgDisjunctiveTask { - start_time: d, - processing_time: 5, - }, - ArgDisjunctiveTask { - start_time: e, - processing_time: 3, - }, - ArgDisjunctiveTask { - start_time: f, - processing_time: 3, - }, - ], - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(c), 18); + let mut state = State::default(); + let c = state.new_interval_variable(4, 26, None); + let d = state.new_interval_variable(13, 13, None); + let e = state.new_interval_variable(5, 10, None); + let f = state.new_interval_variable(5, 10, None); + + let constraint_tag = state.new_constraint_tag(); + let _ = state.add_propagator(DisjunctiveConstructor::new( + [ + ArgDisjunctiveTask { + start_time: c, + processing_time: 4, + }, + ArgDisjunctiveTask { + start_time: d, + processing_time: 5, + }, + ArgDisjunctiveTask { + start_time: e, + processing_time: 3, + }, + ArgDisjunctiveTask { + start_time: f, + processing_time: 3, + }, + ], + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + assert_eq!(state.lower_bound(c), 18); } } diff --git a/pumpkin-crates/propagators/src/propagators/disjunctive/theta_lambda_tree.rs b/pumpkin-crates/propagators/src/propagators/disjunctive/theta_lambda_tree.rs index e8c8e2834..2519a0f8c 100644 --- a/pumpkin-crates/propagators/src/propagators/disjunctive/theta_lambda_tree.rs +++ b/pumpkin-crates/propagators/src/propagators/disjunctive/theta_lambda_tree.rs @@ -374,11 +374,10 @@ impl ThetaLambdaTree { } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { - use pumpkin_core::TestSolver; use pumpkin_core::propagation::LocalId; + use pumpkin_core::state::State; use crate::disjunctive::theta_lambda_tree::DisjunctiveTask; use crate::propagators::disjunctive::theta_lambda_tree::Node; @@ -386,11 +385,11 @@ mod tests { #[test] fn tree_built_correctly() { - let mut solver = TestSolver::default(); - let a = solver.new_variable(0, 0); - let b = solver.new_variable(25, 25); - let c = solver.new_variable(30, 30); - let d = solver.new_variable(32, 32); + let mut state = State::default(); + let a = state.new_interval_variable(0, 0, None); + let b = state.new_interval_variable(25, 25, None); + let c = state.new_interval_variable(30, 30, None); + let d = state.new_interval_variable(32, 32, None); let tasks = [ DisjunctiveTask { start_time: a, @@ -416,12 +415,12 @@ mod tests { let mut tree = ThetaLambdaTree::new(&tasks); - tree.update(solver.state.get_domains()); + tree.update(state.get_domains()); for task in tasks.iter() { - tree.add_to_theta(task, solver.state.get_domains()); + tree.add_to_theta(task, state.get_domains()); } tree.remove_from_theta(&tasks[2]); - tree.add_to_lambda(&tasks[2], solver.state.get_domains()); + tree.add_to_lambda(&tasks[2], state.get_domains()); assert_eq!( tree.nodes[6], diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index c401eb847..4488b400b 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -399,154 +399,203 @@ where } } -#[allow(deprecated, reason = "Will be refactored")] #[cfg(test)] mod tests { use pumpkin_checking::TestAtomic; use pumpkin_checking::VariableState; - use pumpkin_core::TestSolver; + use pumpkin_core::predicate; + use pumpkin_core::predicates::Predicate; + use pumpkin_core::predicates::PropositionalConjunction; + use pumpkin_core::propagation::CurrentNogood; + use pumpkin_core::state::State; use super::*; #[test] fn elements_from_array_with_disjoint_domains_to_rhs_are_filtered_from_index() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let x_0 = solver.new_variable(4, 6); - let x_1 = solver.new_variable(2, 3); - let x_2 = solver.new_variable(7, 9); - let x_3 = solver.new_variable(14, 15); + let x_0 = state.new_interval_variable(4, 6, None); + let x_1 = state.new_interval_variable(2, 3, None); + let x_2 = state.new_interval_variable(7, 9, None); + let x_3 = state.new_interval_variable(14, 15, None); - let index = solver.new_variable(0, 3); - let rhs = solver.new_variable(6, 9); - let constraint_tag = solver.new_constraint_tag(); + let index = state.new_interval_variable(0, 3, None); + let rhs = state.new_interval_variable(6, 9, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(ElementArgs { - array: vec![x_0, x_1, x_2, x_3].into(), - index, - rhs, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(ElementArgs { + array: vec![x_0, x_1, x_2, x_3].into(), + index, + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(index, 0, 2); + assert_eq!(state.lower_bound(index), 0); + assert_eq!(state.upper_bound(index), 2); - assert_eq!( - solver.get_reason_int(predicate![index != 3]), - conjunction!([x_3 >= 10] & [rhs <= 9]) + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![index != 3], + &mut reason_buffer, + CurrentNogood::empty(), ); - - assert_eq!( - solver.get_reason_int(predicate![index != 1]), - conjunction!([x_1 <= 5] & [rhs >= 6]) + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + assert_eq!(conjunction!([x_3 >= 10] & [rhs <= 9]), reason); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![index != 1], + &mut reason_buffer, + CurrentNogood::empty(), ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + assert_eq!(conjunction!([x_1 <= 5] & [rhs >= 6]), reason); } #[test] fn bounds_of_rhs_are_min_and_max_of_lower_and_upper_in_array() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let x_0 = solver.new_variable(3, 10); - let x_1 = solver.new_variable(2, 3); - let x_2 = solver.new_variable(7, 9); - let x_3 = solver.new_variable(14, 15); + let x_0 = state.new_interval_variable(3, 10, None); + let x_1 = state.new_interval_variable(2, 3, None); + let x_2 = state.new_interval_variable(7, 9, None); + let x_3 = state.new_interval_variable(14, 15, None); - let index = solver.new_variable(0, 3); - let rhs = solver.new_variable(0, 20); - let constraint_tag = solver.new_constraint_tag(); + let index = state.new_interval_variable(0, 3, None); + let rhs = state.new_interval_variable(0, 20, None); + let constraint_tag = state.new_constraint_tag(); - let _ = solver - .new_propagator(ElementArgs { - array: vec![x_0, x_1, x_2, x_3].into(), - index, - rhs, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(ElementArgs { + array: vec![x_0, x_1, x_2, x_3].into(), + index, + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(rhs, 2, 15); + assert_eq!(state.lower_bound(rhs), 2); + assert_eq!(state.upper_bound(rhs), 15); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![rhs >= 2], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( - solver.get_reason_int(predicate![rhs >= 2]), - conjunction!([x_0 >= 2] & [x_1 >= 2] & [x_2 >= 2] & [x_3 >= 2]) + conjunction!([x_0 >= 2] & [x_1 >= 2] & [x_2 >= 2] & [x_3 >= 2]), + reason ); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![rhs <= 15], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( - solver.get_reason_int(predicate![rhs <= 15]), - conjunction!([x_0 <= 15] & [x_1 <= 15] & [x_2 <= 15] & [x_3 <= 15]) + conjunction!([x_0 <= 15] & [x_1 <= 15] & [x_2 <= 15] & [x_3 <= 15]), + reason ); } #[test] fn fixed_index_propagates_bounds_on_element() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let x_0 = solver.new_variable(3, 10); - let x_1 = solver.new_variable(0, 15); - let x_2 = solver.new_variable(7, 9); - let x_3 = solver.new_variable(14, 15); - let constraint_tag = solver.new_constraint_tag(); + let x_0 = state.new_interval_variable(3, 10, None); + let x_1 = state.new_interval_variable(0, 15, None); + let x_2 = state.new_interval_variable(7, 9, None); + let x_3 = state.new_interval_variable(14, 15, None); + let constraint_tag = state.new_constraint_tag(); - let index = solver.new_variable(1, 1); - let rhs = solver.new_variable(6, 9); + let index = state.new_interval_variable(1, 1, None); + let rhs = state.new_interval_variable(6, 9, None); - let _ = solver - .new_propagator(ElementArgs { - array: vec![x_0, x_1, x_2, x_3].into(), - index, - rhs, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(ElementArgs { + array: vec![x_0, x_1, x_2, x_3].into(), + index, + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(x_1, 6, 9); + assert_eq!(state.lower_bound(x_1), 6); + assert_eq!(state.upper_bound(x_1), 9); - assert_eq!( - solver.get_reason_int(predicate![x_1 >= 6]), - conjunction!([index == 1] & [rhs >= 6]) + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![x_1 >= 6], + &mut reason_buffer, + CurrentNogood::empty(), ); - - assert_eq!( - solver.get_reason_int(predicate![x_1 <= 9]), - conjunction!([index == 1] & [rhs <= 9]) + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + assert_eq!(conjunction!([index == 1] & [rhs >= 6]), reason); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![x_1 <= 9], + &mut reason_buffer, + CurrentNogood::empty(), ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + assert_eq!(conjunction!([index == 1] & [rhs <= 9]), reason); } #[test] fn index_hole_propagates_bounds_on_rhs() { - let mut solver = TestSolver::default(); + let mut state = State::default(); - let x_0 = solver.new_variable(3, 10); - let x_1 = solver.new_variable(0, 15); - let x_2 = solver.new_variable(7, 9); - let x_3 = solver.new_variable(14, 15); - let constraint_tag = solver.new_constraint_tag(); + let x_0 = state.new_interval_variable(3, 10, None); + let x_1 = state.new_interval_variable(0, 15, None); + let x_2 = state.new_interval_variable(7, 9, None); + let x_3 = state.new_interval_variable(14, 15, None); + let constraint_tag = state.new_constraint_tag(); - let index = solver.new_variable(0, 3); - solver.remove(index, 1).expect("Value can be removed"); + let index = state.new_interval_variable(0, 3, None); + let _ = state + .post(predicate![index != 1]) + .expect("Value can be removed"); - let rhs = solver.new_variable(-10, 30); + let rhs = state.new_interval_variable(-10, 30, None); - let _ = solver - .new_propagator(ElementArgs { - array: vec![x_0, x_1, x_2, x_3].into(), - index, - rhs, - constraint_tag, - }) - .expect("no empty domains"); + let _ = state.add_propagator(ElementArgs { + array: vec![x_0, x_1, x_2, x_3].into(), + index, + rhs, + constraint_tag, + }); + state.propagate_to_fixed_point().expect("no empty domains"); - solver.assert_bounds(rhs, 3, 15); + assert_eq!(state.lower_bound(rhs), 3); + assert_eq!(state.upper_bound(rhs), 15); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![rhs >= 3], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( - solver.get_reason_int(predicate![rhs >= 3]), - conjunction!([x_0 >= 3] & [x_2 >= 3] & [x_3 >= 3] & [index != 1]) + conjunction!([x_0 >= 3] & [x_2 >= 3] & [x_3 >= 3] & [index != 1]), + reason ); + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate![rhs <= 15], + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); assert_eq!( - solver.get_reason_int(predicate![rhs <= 15]), - conjunction!([x_0 <= 15] & [x_2 <= 15] & [x_3 <= 15] & [index != 1]) + conjunction!([x_0 <= 15] & [x_2 <= 15] & [x_3 <= 15] & [index != 1]), + reason ); } From 1fad8afcc5f76f051fb951c307e4e3383b67d245 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Sun, 29 Mar 2026 14:25:18 +1100 Subject: [PATCH 03/14] Be consistent in the way conflicts are expected in tests --- .../propagators/arithmetic/binary/binary_equals.rs | 12 +++--------- .../arithmetic/binary/binary_not_equals.rs | 4 +--- .../propagators/arithmetic/linear_less_or_equal.rs | 3 +-- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs index f9909e58a..05ec3383a 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs @@ -457,9 +457,7 @@ mod tests { b, constraint_tag, }); - let result = state.propagate_to_fixed_point(); - - assert!(result.is_ok()); + state.propagate_to_fixed_point().expect("no conflict"); assert_eq!(state.lower_bound(a), 3); assert_eq!(state.upper_bound(a), 5); @@ -479,9 +477,7 @@ mod tests { b, constraint_tag, }); - let result = state.propagate_to_fixed_point(); - - assert!(result.is_ok()); + state.propagate_to_fixed_point().expect("no conflict"); assert_eq!(state.lower_bound(a), 4); assert_eq!(state.upper_bound(a), 9); @@ -545,8 +541,6 @@ mod tests { b, constraint_tag, }); - let result = state.propagate_to_fixed_point(); - - assert!(result.is_err()); + let _ = state.propagate_to_fixed_point().expect_err("expected conflict"); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index 966b33789..db33405fa 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -224,9 +224,7 @@ mod tests { b, constraint_tag, }); - let result = state.propagate_to_fixed_point(); - - assert!(result.is_err(), "Expected conflict to be detected"); + let _ = state.propagate_to_fixed_point().expect_err("Expected conflict to be detected"); } #[test] diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index ef3c9200f..c7689eb06 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -394,8 +394,7 @@ mod tests { c: i32::MAX, constraint_tag, }); - let result = state.propagate_to_fixed_point(); - assert!(result.is_err(), "Expected overflow to be detected"); + let _ = state.propagate_to_fixed_point().expect_err("Expected overflow to be detected"); } #[test] From 6762154a8743ca3b2c0e7b9c57c7656c22c51930 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Sun, 29 Mar 2026 15:32:43 +1100 Subject: [PATCH 04/14] Update arithmetic propagators to use StateExt::assert_bounds --- pumpkin-crates/propagators/src/lib.rs | 21 ++++++++++++++ .../propagators/arithmetic/absolute_value.rs | 20 +++++-------- .../arithmetic/binary/binary_equals.rs | 18 +++++------- .../arithmetic/binary/binary_not_equals.rs | 15 +++++----- .../arithmetic/integer_division.rs | 3 +- .../arithmetic/integer_multiplication.rs | 29 +++++++------------ .../arithmetic/linear_less_or_equal.rs | 12 ++++---- .../arithmetic/linear_not_equal.rs | 8 ++--- .../src/propagators/arithmetic/maximum.rs | 18 +++++------- 9 files changed, 75 insertions(+), 69 deletions(-) diff --git a/pumpkin-crates/propagators/src/lib.rs b/pumpkin-crates/propagators/src/lib.rs index b78a60169..d6ab80ddb 100644 --- a/pumpkin-crates/propagators/src/lib.rs +++ b/pumpkin-crates/propagators/src/lib.rs @@ -4,3 +4,24 @@ //! [`pumpkin_core::propagation`]. mod propagators; pub use propagators::*; +#[cfg(test)] +use pumpkin_core::{state::State, variables::DomainId}; + +#[cfg(test)] +pub(crate) trait StateExt { + fn assert_bounds(&self, domain_id: DomainId, lower_bound: i32, upper_bound: i32); +} + +#[cfg(test)] +impl StateExt for State { + fn assert_bounds(&self, domain_id: DomainId, lower_bound: i32, upper_bound: i32) { + let actual_lb = self.lower_bound(domain_id); + let actual_ub = self.upper_bound(domain_id); + + assert_eq!( + (lower_bound, upper_bound), + (actual_lb, actual_ub), + "The expected bounds [{lower_bound}..{upper_bound}] did not match the actual bounds [{actual_lb}..{actual_ub}]" + ); + } +} diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs index 3d0d6efa7..98fc3ca2a 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs @@ -209,6 +209,8 @@ where mod tests { use pumpkin_core::state::State; + use crate::StateExt; + use super::*; #[test] @@ -226,8 +228,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(absolute), 0); - assert_eq!(state.upper_bound(absolute), 4); + state.assert_bounds(absolute, 0, 4); } #[test] @@ -245,8 +246,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(signed), -3); - assert_eq!(state.upper_bound(signed), 3); + state.assert_bounds(signed, -3, 3); } #[test] @@ -264,8 +264,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(absolute), 3); - assert_eq!(state.upper_bound(absolute), 6); + state.assert_bounds(absolute, 3, 6); } #[test] @@ -283,8 +282,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(absolute), 3); - assert_eq!(state.upper_bound(absolute), 5); + state.assert_bounds(absolute, 3, 5); } #[test] @@ -302,8 +300,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(signed), -5); - assert_eq!(state.upper_bound(signed), -1); + state.assert_bounds(signed, -5, -1); } #[test] @@ -321,7 +318,6 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(signed), 3); - assert_eq!(state.upper_bound(signed), 5); + state.assert_bounds(signed, 3, 5); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs index 05ec3383a..4a3fd9b1a 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs @@ -443,7 +443,7 @@ where mod tests { use pumpkin_core::state::State; - use crate::propagators::arithmetic::BinaryEqualsPropagatorArgs; + use crate::{StateExt, propagators::arithmetic::BinaryEqualsPropagatorArgs}; #[test] fn test_propagation_of_bounds() { @@ -459,10 +459,8 @@ mod tests { }); state.propagate_to_fixed_point().expect("no conflict"); - assert_eq!(state.lower_bound(a), 3); - assert_eq!(state.upper_bound(a), 5); - assert_eq!(state.lower_bound(b), 3); - assert_eq!(state.upper_bound(b), 5); + state.assert_bounds(a, 3, 5); + state.assert_bounds(b, 3, 5); } #[test] @@ -479,10 +477,8 @@ mod tests { }); state.propagate_to_fixed_point().expect("no conflict"); - assert_eq!(state.lower_bound(a), 4); - assert_eq!(state.upper_bound(a), 9); - assert_eq!(state.lower_bound(b), 4); - assert_eq!(state.upper_bound(b), 9); + state.assert_bounds(a, 4, 9); + state.assert_bounds(b, 4, 9); for i in 5..=8 { assert!(!state.contains(a, i)); @@ -541,6 +537,8 @@ mod tests { b, constraint_tag, }); - let _ = state.propagate_to_fixed_point().expect_err("expected conflict"); + let _ = state + .propagate_to_fixed_point() + .expect_err("expected conflict"); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index db33405fa..928da4edd 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -210,7 +210,7 @@ where mod tests { use pumpkin_core::state::State; - use crate::propagators::arithmetic::BinaryNotEqualsPropagatorArgs; + use crate::{StateExt, propagators::arithmetic::BinaryNotEqualsPropagatorArgs}; #[test] fn detects_conflict() { @@ -224,7 +224,9 @@ mod tests { b, constraint_tag, }); - let _ = state.propagate_to_fixed_point().expect_err("Expected conflict to be detected"); + let _ = state + .propagate_to_fixed_point() + .expect_err("Expected conflict to be detected"); } #[test] @@ -243,8 +245,7 @@ mod tests { .propagate_to_fixed_point() .expect("Expected no conflict to be detected"); - assert_eq!(state.lower_bound(b), 1); - assert_eq!(state.upper_bound(b), 1); + state.assert_bounds(b, 1, 1); } #[allow(deprecated, reason = "Uses TestSolver for EnqueueDecision assertions")] @@ -294,9 +295,7 @@ mod tests { .propagate_to_fixed_point() .expect("Expected no conflict to be detected"); - assert_eq!(state.lower_bound(a), 0); - assert_eq!(state.upper_bound(a), 5); - assert_eq!(state.lower_bound(b), 6); - assert_eq!(state.upper_bound(b), 10); + state.assert_bounds(a, 0, 5); + state.assert_bounds(b, 6, 10); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs index e6e7ae2a6..e0ba0e6a6 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_division.rs @@ -477,8 +477,7 @@ mod tests { rhs, constraint_tag, }); - let result = state.propagate_to_fixed_point(); - assert!(result.is_err()); + let _ = state.propagate_to_fixed_point().unwrap_err(); } } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index 937ec601f..79bfd43b9 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -425,6 +425,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; + use crate::StateExt; + use super::*; #[test] @@ -444,12 +446,9 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(1, state.lower_bound(a)); - assert_eq!(3, state.upper_bound(a)); - assert_eq!(0, state.lower_bound(b)); - assert_eq!(4, state.upper_bound(b)); - assert_eq!(0, state.lower_bound(c)); - assert_eq!(12, state.upper_bound(c)); + state.assert_bounds(a, 1, 3); + state.assert_bounds(b, 0, 4); + state.assert_bounds(c, 0, 12); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -490,12 +489,9 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(2, state.lower_bound(a)); - assert_eq!(3, state.upper_bound(a)); - assert_eq!(1, state.lower_bound(b)); - assert_eq!(6, state.upper_bound(b)); - assert_eq!(2, state.lower_bound(c)); - assert_eq!(12, state.upper_bound(c)); + state.assert_bounds(a, 2, 3); + state.assert_bounds(b, 1, 6); + state.assert_bounds(c, 2, 12); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -533,12 +529,9 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(1, state.lower_bound(a)); - assert_eq!(4, state.upper_bound(a)); - assert_eq!(3, state.lower_bound(b)); - assert_eq!(6, state.upper_bound(b)); - assert_eq!(3, state.lower_bound(c)); - assert_eq!(12, state.upper_bound(c)); + state.assert_bounds(a, 1, 4); + state.assert_bounds(b, 3, 6); + state.assert_bounds(c, 3, 12); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index c7689eb06..cf4a89260 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -333,6 +333,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; + use crate::StateExt; + use super::*; #[test] @@ -350,10 +352,8 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(x), 1); - assert_eq!(state.upper_bound(x), 5); - assert_eq!(state.lower_bound(y), 0); - assert_eq!(state.upper_bound(y), 6); + state.assert_bounds(x, 1, 5); + state.assert_bounds(y, 0, 6); } #[test] @@ -394,7 +394,9 @@ mod tests { c: i32::MAX, constraint_tag, }); - let _ = state.propagate_to_fixed_point().expect_err("Expected overflow to be detected"); + let _ = state + .propagate_to_fixed_point() + .expect_err("Expected overflow to be detected"); } #[test] diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index afbf77b86..0efcdc7eb 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -413,6 +413,8 @@ mod tests { use pumpkin_core::state::State; use pumpkin_core::variables::TransformableVariable; + use crate::StateExt; + use super::*; #[test] @@ -430,10 +432,8 @@ mod tests { }); state.propagate_to_fixed_point().expect("non-empty domain"); - assert_eq!(state.lower_bound(x), 2); - assert_eq!(state.upper_bound(x), 2); - assert_eq!(state.lower_bound(y), 1); - assert_eq!(state.upper_bound(y), 5); + state.assert_bounds(x, 2, 2); + state.assert_bounds(y, 1, 5); assert!(!state.contains(y, 2)); } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index c87e83edc..790ae0f6d 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -232,6 +232,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; + use crate::StateExt; + use super::*; #[test] @@ -252,8 +254,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domain"); - assert_eq!(state.lower_bound(rhs), 1); - assert_eq!(state.upper_bound(rhs), 5); + state.assert_bounds(rhs, 1, 5); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -283,8 +284,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domain"); - assert_eq!(state.lower_bound(rhs), 5); - assert_eq!(state.upper_bound(rhs), 10); + state.assert_bounds(rhs, 5, 10); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -315,8 +315,8 @@ mod tests { state.propagate_to_fixed_point().expect("no empty domain"); for var in array.iter() { - assert_eq!(state.lower_bound(*var), 1); - assert_eq!(state.upper_bound(*var), 3); + state.assert_bounds(*var, 1, 3); + let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( predicate![var <= 3], @@ -346,9 +346,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domain"); - assert_eq!(state.lower_bound(*array.last().unwrap()), 45); - assert_eq!(state.upper_bound(*array.last().unwrap()), 51); - assert_eq!(state.lower_bound(rhs), 45); - assert_eq!(state.upper_bound(rhs), 51); + state.assert_bounds(*array.last().unwrap(), 45, 51); + state.assert_bounds(rhs, 45, 51); } } From 85d5fa39a0f9c16b9f07b532b8628f84e4958d81 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Sun, 29 Mar 2026 15:39:15 +1100 Subject: [PATCH 05/14] Move cumulative and element to use `StateExt::assert_bounds` --- .../time_table/time_table_over_interval.rs | 243 ++++++++++++++++-- .../time_table/time_table_per_point.rs | 40 +-- .../propagators/src/propagators/element.rs | 14 +- 3 files changed, 238 insertions(+), 59 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index c819aea79..241024062 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -507,6 +507,7 @@ mod tests { use pumpkin_core::state::State; use pumpkin_core::variables::DomainId; + use crate::StateExt; use crate::cumulative::ArgTask; use crate::cumulative::options::CumulativePropagatorOptions; use crate::cumulative::time_table::CumulativeExplanationType; @@ -539,10 +540,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); } #[test] @@ -627,10 +626,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 6); - assert_eq!(state.lower_bound(s1), 0); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 0, 6); + state.assert_bounds(s2, 0, 6); } #[test] @@ -767,10 +764,214 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 1); - assert_eq!(state.upper_bound(s2), 3); - assert_eq!(state.lower_bound(s1), 6); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 6, 6); + state.assert_bounds(s2, 1, 3); + + let mut reason_buffer: Vec = vec![]; + let _ = state.get_propagation_reason( + predicate!(s2 <= 3), + &mut reason_buffer, + CurrentNogood::empty(), + ); + let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); + } + + #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] + fn propagator_propagates_example_4_3_schutt_after_update() { + let mut solver = TestSolver::default(); + let f = solver.new_variable(0, 14); + let e = solver.new_variable(0, 4); + let d = solver.new_variable(0, 2); + let c = solver.new_variable(8, 9); + let b = solver.new_variable(2, 3); + let a = solver.new_variable(0, 1); + let constraint_tag = solver.new_constraint_tag(); + + let propagator = solver + .new_propagator(TimeTableOverIntervalPropagator::new( + &[ + ArgTask { + start_time: a, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: b, + processing_time: 6, + resource_usage: 2, + }, + ArgTask { + start_time: c, + processing_time: 2, + resource_usage: 4, + }, + ArgTask { + start_time: d, + processing_time: 2, + resource_usage: 2, + }, + ArgTask { + start_time: e, + processing_time: 4, + resource_usage: 2, + }, + ArgTask { + start_time: f, + processing_time: 6, + resource_usage: 2, + }, + ] + .into_iter() + .collect::>(), + 5, + CumulativePropagatorOptions::default(), + constraint_tag, + )) + .expect("No conflict"); + assert_eq!(solver.lower_bound(a), 0); + assert_eq!(solver.upper_bound(a), 1); + assert_eq!(solver.lower_bound(b), 2); + assert_eq!(solver.upper_bound(b), 3); + assert_eq!(solver.lower_bound(c), 8); + assert_eq!(solver.upper_bound(c), 9); + assert_eq!(solver.lower_bound(d), 0); + assert_eq!(solver.upper_bound(d), 2); + assert_eq!(solver.lower_bound(e), 0); + assert_eq!(solver.upper_bound(e), 4); + assert_eq!(solver.lower_bound(f), 0); + assert_eq!(solver.upper_bound(f), 14); + + let notification_status = solver.increase_lower_bound_and_notify(propagator, 3, e, 3); + assert!(match notification_status { + EnqueueDecision::Enqueue => true, + EnqueueDecision::Skip => false, + }); + let result = solver.propagate(propagator); + assert!(result.is_ok()); + assert_eq!(solver.lower_bound(f), 10); + } + + #[test] + #[allow( + deprecated, + reason = "Uses TestSolver for incremental notification assertions" + )] + fn propagator_propagates_example_4_3_schutt_multiple_profiles() { + let mut solver = TestSolver::default(); + let f = solver.new_variable(0, 14); + let e = solver.new_variable(0, 4); + let d = solver.new_variable(0, 2); + let c = solver.new_variable(8, 9); + let b2 = solver.new_variable(5, 5); + let b1 = solver.new_variable(3, 3); + let a = solver.new_variable(0, 1); + + let constraint_tag = solver.new_constraint_tag(); + + let propagator = solver + .new_propagator(TimeTableOverIntervalPropagator::new( + &[ + ArgTask { + start_time: a, + processing_time: 2, + resource_usage: 1, + }, + ArgTask { + start_time: b1, + processing_time: 2, + resource_usage: 2, + }, + ArgTask { + start_time: b2, + processing_time: 3, + resource_usage: 2, + }, + ArgTask { + start_time: c, + processing_time: 2, + resource_usage: 4, + }, + ArgTask { + start_time: d, + processing_time: 2, + resource_usage: 2, + }, + ArgTask { + start_time: e, + processing_time: 4, + resource_usage: 2, + }, + ArgTask { + start_time: f, + processing_time: 6, + resource_usage: 2, + }, + ] + .into_iter() + .collect::>(), + 5, + CumulativePropagatorOptions::default(), + constraint_tag, + )) + .expect("No conflict"); + assert_eq!(solver.lower_bound(a), 0); + assert_eq!(solver.upper_bound(a), 1); + assert_eq!(solver.lower_bound(c), 8); + assert_eq!(solver.upper_bound(c), 9); + assert_eq!(solver.lower_bound(d), 0); + assert_eq!(solver.upper_bound(d), 2); + assert_eq!(solver.lower_bound(e), 0); + assert_eq!(solver.upper_bound(e), 4); + assert_eq!(solver.lower_bound(f), 0); + assert_eq!(solver.upper_bound(f), 14); + + let notification_status = solver.increase_lower_bound_and_notify(propagator, 4, e, 3); + assert!(match notification_status { + EnqueueDecision::Enqueue => true, + EnqueueDecision::Skip => false, + }); + let result = solver.propagate(propagator); + assert!(result.is_ok()); + assert_eq!(solver.lower_bound(f), 10); + } + + #[test] + fn propagator_propagates_from_profile_reason() { + let mut state = State::default(); + let s1 = state.new_interval_variable(1, 1, None); + let s2 = state.new_interval_variable(1, 8, None); + let constraint_tag = state.new_constraint_tag(); + + let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + &[ + ArgTask { + start_time: s1, + processing_time: 4, + resource_usage: 1, + }, + ArgTask { + start_time: s2, + processing_time: 3, + resource_usage: 1, + }, + ] + .into_iter() + .collect::>(), + 1, + CumulativePropagatorOptions { + explanation_type: CumulativeExplanationType::Naive, + ..Default::default() + }, + constraint_tag, + )); + state.propagate_to_fixed_point().expect("No conflict"); + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -976,9 +1177,6 @@ mod tests { )); state.propagate_to_fixed_point().expect("No conflict"); assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1026,12 +1224,9 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s3), 7); - assert_eq!(state.upper_bound(s3), 15); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 5); - assert_eq!(state.lower_bound(s1), 3); - assert_eq!(state.upper_bound(s1), 3); + state.assert_bounds(s1, 3, 3); + state.assert_bounds(s2, 5, 5); + state.assert_bounds(s2, 7, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1074,10 +1269,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 4); - assert_eq!(state.upper_bound(s1), 4); + state.assert_bounds(s1, 4, 4); + state.assert_bounds(s2, 0, 8); for removed in 2..8 { assert!(!state.contains(s2, removed)); diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index 6aca03bce..105cd78c2 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -313,6 +313,7 @@ mod tests { use pumpkin_core::state::State; use pumpkin_core::variables::DomainId; + use crate::StateExt; use crate::cumulative::ArgTask; use crate::cumulative::options::CumulativePropagatorOptions; use crate::cumulative::time_table::CumulativeExplanationType; @@ -345,10 +346,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); } #[test] @@ -431,10 +430,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 6); - assert_eq!(state.lower_bound(s1), 0); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 0, 6); + state.assert_bounds(s2, 0, 6); } #[test] @@ -571,10 +568,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 1); - assert_eq!(state.upper_bound(s2), 3); - assert_eq!(state.lower_bound(s1), 6); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 1, 3); + state.assert_bounds(s2, 6, 6); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -778,10 +773,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -839,12 +832,9 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s3), 7); - assert_eq!(state.upper_bound(s3), 15); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 5); - assert_eq!(state.lower_bound(s1), 3); - assert_eq!(state.upper_bound(s1), 3); + state.assert_bounds(s1, 3, 3); + state.assert_bounds(s2, 5, 5); + state.assert_bounds(s3, 7, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -894,10 +884,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 4); - assert_eq!(state.upper_bound(s1), 4); + state.assert_bounds(s1, 4, 4); + state.assert_bounds(s2, 0, 8); for removed in 2..8 { assert!(!state.contains(s2, removed)); diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index 4488b400b..95cf31f2d 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -409,6 +409,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; + use crate::StateExt; + use super::*; #[test] @@ -432,8 +434,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(index), 0); - assert_eq!(state.upper_bound(index), 2); + state.assert_bounds(index, 0, 2); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -475,8 +476,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(rhs), 2); - assert_eq!(state.upper_bound(rhs), 15); + state.assert_bounds(rhs, 2, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -524,8 +524,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(x_1), 6); - assert_eq!(state.upper_bound(x_1), 9); + state.assert_bounds(x_1, 6, 9); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -571,8 +570,7 @@ mod tests { }); state.propagate_to_fixed_point().expect("no empty domains"); - assert_eq!(state.lower_bound(rhs), 3); - assert_eq!(state.upper_bound(rhs), 15); + state.assert_bounds(rhs, 3, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( From 5cf02dcb0dc724bfc27b7d132f5bfb988441b3ac Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Sun, 29 Mar 2026 15:39:45 +1100 Subject: [PATCH 06/14] Fix formatting --- pumpkin-crates/propagators/src/lib.rs | 4 +++- .../propagators/src/propagators/arithmetic/absolute_value.rs | 3 +-- .../src/propagators/arithmetic/binary/binary_equals.rs | 3 ++- .../src/propagators/arithmetic/binary/binary_not_equals.rs | 3 ++- .../src/propagators/arithmetic/integer_multiplication.rs | 3 +-- .../src/propagators/arithmetic/linear_less_or_equal.rs | 3 +-- .../src/propagators/arithmetic/linear_not_equal.rs | 3 +-- .../propagators/src/propagators/arithmetic/maximum.rs | 3 +-- pumpkin-crates/propagators/src/propagators/element.rs | 3 +-- 9 files changed, 13 insertions(+), 15 deletions(-) diff --git a/pumpkin-crates/propagators/src/lib.rs b/pumpkin-crates/propagators/src/lib.rs index d6ab80ddb..7e84bd7aa 100644 --- a/pumpkin-crates/propagators/src/lib.rs +++ b/pumpkin-crates/propagators/src/lib.rs @@ -5,7 +5,9 @@ mod propagators; pub use propagators::*; #[cfg(test)] -use pumpkin_core::{state::State, variables::DomainId}; +use pumpkin_core::state::State; +#[cfg(test)] +use pumpkin_core::variables::DomainId; #[cfg(test)] pub(crate) trait StateExt { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs index 98fc3ca2a..753384dc1 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/absolute_value.rs @@ -209,9 +209,8 @@ where mod tests { use pumpkin_core::state::State; - use crate::StateExt; - use super::*; + use crate::StateExt; #[test] fn absolute_bounds_are_propagated_at_initialise() { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs index 4a3fd9b1a..40f429ab8 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_equals.rs @@ -443,7 +443,8 @@ where mod tests { use pumpkin_core::state::State; - use crate::{StateExt, propagators::arithmetic::BinaryEqualsPropagatorArgs}; + use crate::StateExt; + use crate::propagators::arithmetic::BinaryEqualsPropagatorArgs; #[test] fn test_propagation_of_bounds() { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs index 928da4edd..f000a7eeb 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/binary/binary_not_equals.rs @@ -210,7 +210,8 @@ where mod tests { use pumpkin_core::state::State; - use crate::{StateExt, propagators::arithmetic::BinaryNotEqualsPropagatorArgs}; + use crate::StateExt; + use crate::propagators::arithmetic::BinaryNotEqualsPropagatorArgs; #[test] fn detects_conflict() { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index 79bfd43b9..eb6fcb536 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -425,9 +425,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; - use crate::StateExt; - use super::*; + use crate::StateExt; #[test] fn bounds_of_a_and_b_propagate_bounds_c() { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index cf4a89260..b1356d241 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -333,9 +333,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; - use crate::StateExt; - use super::*; + use crate::StateExt; #[test] fn test_bounds_are_propagated() { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index 0efcdc7eb..2876ff375 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -413,9 +413,8 @@ mod tests { use pumpkin_core::state::State; use pumpkin_core::variables::TransformableVariable; - use crate::StateExt; - use super::*; + use crate::StateExt; #[test] fn test_value_is_removed() { diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index 790ae0f6d..0ed5374e7 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -232,9 +232,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; - use crate::StateExt; - use super::*; + use crate::StateExt; #[test] fn upper_bound_of_rhs_matches_maximum_upper_bound_of_array_at_initialise() { diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index 95cf31f2d..dfc4b092f 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -409,9 +409,8 @@ mod tests { use pumpkin_core::propagation::CurrentNogood; use pumpkin_core::state::State; - use crate::StateExt; - use super::*; + use crate::StateExt; #[test] fn elements_from_array_with_disjoint_domains_to_rhs_are_filtered_from_index() { From 17bc9141bc7d63e459cbc15ed9bff15b8d27389e Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Sun, 29 Mar 2026 15:50:50 +1100 Subject: [PATCH 07/14] More cleanup of cumulative test cases --- .../time_table_over_interval_incremental.rs | 85 +++++++++---------- .../time_table_per_point_incremental.rs | 77 +++++++---------- .../time_table/time_table_over_interval.rs | 44 +++++----- .../time_table/time_table_per_point.rs | 38 ++++----- 4 files changed, 107 insertions(+), 137 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index 8f35a5ba1..c157255fb 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -666,6 +666,7 @@ mod tests { use pumpkin_core::state::State; use pumpkin_core::variables::DomainId; + use crate::StateExt; use crate::cumulative::ArgTask; use crate::cumulative::options::CumulativePropagatorOptions; use crate::cumulative::time_table::CumulativeExplanationType; @@ -699,11 +700,11 @@ mod tests { constraint_tag, ), ); + state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); + + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); } #[test] @@ -737,27 +738,27 @@ mod tests { constraint_tag, ), ); - let result = state.propagate_to_fixed_point(); - - assert!(matches!(result, Err(Conflict::Propagator(_)))); - assert!(match result { - Err(Conflict::Propagator(conflict)) => { - let expected = [ - predicate!(s1 <= 1), - predicate!(s1 >= 1), - predicate!(s2 >= 1), - predicate!(s2 <= 1), - ]; - expected.iter().all(|y| { - conflict - .conjunction - .iter() - .collect::>() - .contains(&y) - }) && conflict.conjunction.iter().all(|y| expected.contains(y)) - } - _ => false, - }); + + let Conflict::Propagator(conflict) = state.propagate_to_fixed_point().unwrap_err() else { + panic!("an explicit conflict should be detected"); + }; + + let expected = [ + predicate!(s1 <= 1), + predicate!(s1 >= 1), + predicate!(s2 >= 1), + predicate!(s2 <= 1), + ]; + + assert!(expected.iter().all(|y| { + conflict + .conjunction + .iter() + .collect::>() + .contains(&y) + })); + + assert!(conflict.conjunction.iter().all(|y| expected.contains(y))); } #[test] @@ -789,10 +790,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 6); - assert_eq!(state.lower_bound(s1), 0); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 0, 6); + state.assert_bounds(s2, 0, 6); } #[test] @@ -935,10 +934,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 1); - assert_eq!(state.upper_bound(s2), 3); - assert_eq!(state.lower_bound(s1), 6); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 6, 6); + state.assert_bounds(s2, 1, 3); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1148,10 +1145,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1201,12 +1196,9 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s3), 7); - assert_eq!(state.upper_bound(s3), 15); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 5); - assert_eq!(state.lower_bound(s1), 3); - assert_eq!(state.upper_bound(s1), 3); + state.assert_bounds(s1, 3, 3); + state.assert_bounds(s2, 5, 5); + state.assert_bounds(s2, 7, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1251,10 +1243,9 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 4); - assert_eq!(state.upper_bound(s1), 4); + + state.assert_bounds(s1, 4, 4); + state.assert_bounds(s2, 0, 8); for removed in 2..8 { assert!(!state.contains(s2, removed)); diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs index 6c7dc3ed7..415412c4e 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs @@ -663,6 +663,7 @@ mod tests { use pumpkin_core::state::State; use pumpkin_core::variables::DomainId; + use crate::StateExt; use crate::cumulative::ArgTask; use crate::cumulative::options::CumulativePropagatorOptions; use crate::cumulative::time_table::CumulativeExplanationType; @@ -698,10 +699,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); } #[test] @@ -735,24 +734,25 @@ mod tests { constraint_tag, ), ); - let result = state.propagate_to_fixed_point(); - assert!(match result { - Err(Conflict::Propagator(x)) => { - let expected = [ - predicate!(s1 <= 1), - predicate!(s1 >= 1), - predicate!(s2 <= 1), - predicate!(s2 >= 1), - ]; - expected.iter().all(|y| { - x.conjunction - .iter() - .collect::>() - .contains(&y) - }) && x.conjunction.iter().all(|y| expected.contains(y)) - } - _ => false, - }); + let Conflict::Propagator(x) = state.propagate_to_fixed_point().unwrap_err() else { + panic!("an explicit conflict should have been detected"); + }; + + let expected = [ + predicate!(s1 <= 1), + predicate!(s1 >= 1), + predicate!(s2 <= 1), + predicate!(s2 >= 1), + ]; + + assert!(expected.iter().all(|y| { + x.conjunction + .iter() + .collect::>() + .contains(&y) + })); + + assert!(x.conjunction.iter().all(|y| expected.contains(y))); } #[test] @@ -784,10 +784,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 6); - assert_eq!(state.lower_bound(s1), 0); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 0, 6); + state.assert_bounds(s2, 0, 6); } #[test] @@ -930,10 +928,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 1); - assert_eq!(state.upper_bound(s2), 3); - assert_eq!(state.lower_bound(s1), 6); - assert_eq!(state.upper_bound(s1), 6); + state.assert_bounds(s1, 6, 6); + state.assert_bounds(s2, 1, 3); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1143,10 +1139,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 1); - assert_eq!(state.upper_bound(s1), 1); + state.assert_bounds(s1, 1, 1); + state.assert_bounds(s2, 5, 8); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1206,12 +1200,9 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s3), 7); - assert_eq!(state.upper_bound(s3), 15); - assert_eq!(state.lower_bound(s2), 5); - assert_eq!(state.upper_bound(s2), 5); - assert_eq!(state.lower_bound(s1), 3); - assert_eq!(state.upper_bound(s1), 3); + state.assert_bounds(s1, 3, 3); + state.assert_bounds(s2, 5, 5); + state.assert_bounds(s3, 7, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( @@ -1263,10 +1254,8 @@ mod tests { ), ); state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 0); - assert_eq!(state.upper_bound(s2), 8); - assert_eq!(state.lower_bound(s1), 4); - assert_eq!(state.upper_bound(s1), 4); + state.assert_bounds(s1, 4, 4); + state.assert_bounds(s2, 0, 8); for removed in 2..8 { assert!(!state.contains(s2, removed)); diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index 241024062..7c19fb988 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -573,30 +573,26 @@ mod tests { }, constraint_tag, )); - let result = state.propagate_to_fixed_point(); - - assert!(match result { - Err(e) => { - match e { - Conflict::EmptyDomain(_) => false, - Conflict::Propagator(x) => { - let expected = [ - predicate!(s1 <= 1), - predicate!(s1 >= 1), - predicate!(s2 >= 1), - predicate!(s2 <= 1), - ]; - expected.iter().all(|y| { - x.conjunction - .iter() - .collect::>() - .contains(&y) - }) && x.conjunction.iter().all(|y| expected.contains(y)) - } - } - } - _ => false, - }); + + let Conflict::Propagator(x) = state.propagate_to_fixed_point().unwrap_err() else { + panic!("an explicit conflict should have been detected"); + }; + + let expected = [ + predicate!(s1 <= 1), + predicate!(s1 >= 1), + predicate!(s2 >= 1), + predicate!(s2 <= 1), + ]; + + assert!(expected.iter().all(|y| { + x.conjunction + .iter() + .collect::>() + .contains(&y) + })); + + assert!(x.conjunction.iter().all(|y| expected.contains(y))); } #[test] diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index 105cd78c2..ad3d3fc5a 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -379,28 +379,22 @@ mod tests { }, constraint_tag, )); - let result = state.propagate_to_fixed_point(); - assert!(match result { - Err(e) => match e { - Conflict::EmptyDomain(_) => false, - Conflict::Propagator(x) => { - let expected = [ - predicate!(s1 <= 1), - predicate!(s1 >= 1), - predicate!(s2 <= 1), - predicate!(s2 >= 1), - ]; - expected.iter().all(|y| { - x.conjunction - .iter() - .collect::>() - .contains(&y) - }) && x.conjunction.iter().all(|y| expected.contains(y)) - } - }, - - Ok(_) => false, - }); + let Conflict::Propagator(x) = state.propagate_to_fixed_point().unwrap_err() else { + panic!("an explicit conflict should have been detected"); + }; + let expected = [ + predicate!(s1 <= 1), + predicate!(s1 >= 1), + predicate!(s2 <= 1), + predicate!(s2 >= 1), + ]; + assert!(expected.iter().all(|y| { + x.conjunction + .iter() + .collect::>() + .contains(&y) + })); + assert!(x.conjunction.iter().all(|y| expected.contains(y))); } #[test] From 8d73261bcd2b81fe44e22bf6315355a2c5b1baaf Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Sun, 29 Mar 2026 16:10:39 +1100 Subject: [PATCH 08/14] And even more cumulative cleanup --- .../time_table/time_table_over_interval.rs | 250 ++---------------- 1 file changed, 15 insertions(+), 235 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index 7c19fb988..e9c4f6818 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -712,10 +712,8 @@ mod tests { constraint_tag, )) .expect("No conflict"); - assert_eq!(solver.lower_bound(s2), 6); - assert_eq!(solver.upper_bound(s2), 10); - assert_eq!(solver.lower_bound(s1), 0); - assert_eq!(solver.upper_bound(s1), 6); + solver.assert_bounds(s1, 0, 6); + solver.assert_bounds(s2, 6, 10); let notification_status = solver.increase_lower_bound_and_notify(propagator, 0, s1, 5); assert!(match notification_status { EnqueueDecision::Enqueue => true, @@ -724,10 +722,8 @@ mod tests { let result = solver.propagate(propagator); assert!(result.is_ok()); - assert_eq!(solver.lower_bound(s2), 7); - assert_eq!(solver.upper_bound(s2), 10); - assert_eq!(solver.lower_bound(s1), 5); - assert_eq!(solver.upper_bound(s1), 6); + solver.assert_bounds(s1, 5, 6); + solver.assert_bounds(s2, 7, 10); } #[test] @@ -773,85 +769,6 @@ mod tests { assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); } - #[test] - #[allow( - deprecated, - reason = "Uses TestSolver for incremental notification assertions" - )] - fn propagator_propagates_example_4_3_schutt_after_update() { - let mut solver = TestSolver::default(); - let f = solver.new_variable(0, 14); - let e = solver.new_variable(0, 4); - let d = solver.new_variable(0, 2); - let c = solver.new_variable(8, 9); - let b = solver.new_variable(2, 3); - let a = solver.new_variable(0, 1); - let constraint_tag = solver.new_constraint_tag(); - - let propagator = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: a, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: b, - processing_time: 6, - resource_usage: 2, - }, - ArgTask { - start_time: c, - processing_time: 2, - resource_usage: 4, - }, - ArgTask { - start_time: d, - processing_time: 2, - resource_usage: 2, - }, - ArgTask { - start_time: e, - processing_time: 4, - resource_usage: 2, - }, - ArgTask { - start_time: f, - processing_time: 6, - resource_usage: 2, - }, - ] - .into_iter() - .collect::>(), - 5, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(a), 0); - assert_eq!(solver.upper_bound(a), 1); - assert_eq!(solver.lower_bound(b), 2); - assert_eq!(solver.upper_bound(b), 3); - assert_eq!(solver.lower_bound(c), 8); - assert_eq!(solver.upper_bound(c), 9); - assert_eq!(solver.lower_bound(d), 0); - assert_eq!(solver.upper_bound(d), 2); - assert_eq!(solver.lower_bound(e), 0); - assert_eq!(solver.upper_bound(e), 4); - assert_eq!(solver.lower_bound(f), 0); - assert_eq!(solver.upper_bound(f), 14); - - let notification_status = solver.increase_lower_bound_and_notify(propagator, 3, e, 3); - assert!(match notification_status { - EnqueueDecision::Enqueue => true, - EnqueueDecision::Skip => false, - }); - let result = solver.propagate(propagator); - assert!(result.is_ok()); - assert_eq!(solver.lower_bound(f), 10); - } - #[test] #[allow( deprecated, @@ -915,16 +832,11 @@ mod tests { constraint_tag, )) .expect("No conflict"); - assert_eq!(solver.lower_bound(a), 0); - assert_eq!(solver.upper_bound(a), 1); - assert_eq!(solver.lower_bound(c), 8); - assert_eq!(solver.upper_bound(c), 9); - assert_eq!(solver.lower_bound(d), 0); - assert_eq!(solver.upper_bound(d), 2); - assert_eq!(solver.lower_bound(e), 0); - assert_eq!(solver.upper_bound(e), 4); - assert_eq!(solver.lower_bound(f), 0); - assert_eq!(solver.upper_bound(f), 14); + solver.assert_bounds(a, 0, 1); + solver.assert_bounds(c, 8, 9); + solver.assert_bounds(d, 0, 2); + solver.assert_bounds(e, 0, 4); + solver.assert_bounds(f, 0, 14); let notification_status = solver.increase_lower_bound_and_notify(propagator, 4, e, 3); assert!(match notification_status { @@ -1035,18 +947,12 @@ mod tests { constraint_tag, )) .expect("No conflict"); - assert_eq!(solver.lower_bound(a), 0); - assert_eq!(solver.upper_bound(a), 1); - assert_eq!(solver.lower_bound(b), 2); - assert_eq!(solver.upper_bound(b), 3); - assert_eq!(solver.lower_bound(c), 8); - assert_eq!(solver.upper_bound(c), 9); - assert_eq!(solver.lower_bound(d), 0); - assert_eq!(solver.upper_bound(d), 2); - assert_eq!(solver.lower_bound(e), 0); - assert_eq!(solver.upper_bound(e), 4); - assert_eq!(solver.lower_bound(f), 0); - assert_eq!(solver.upper_bound(f), 14); + solver.assert_bounds(a, 0, 1); + solver.assert_bounds(b, 2, 3); + solver.assert_bounds(c, 8, 9); + solver.assert_bounds(d, 0, 2); + solver.assert_bounds(e, 0, 4); + solver.assert_bounds(f, 0, 14); let notification_status = solver.increase_lower_bound_and_notify(propagator, 3, e, 3); assert!(match notification_status { @@ -1058,132 +964,6 @@ mod tests { assert_eq!(solver.lower_bound(f), 10); } - #[test] - #[allow( - deprecated, - reason = "Uses TestSolver for incremental notification assertions" - )] - fn propagator_propagates_example_4_3_schutt_multiple_profiles() { - let mut solver = TestSolver::default(); - let f = solver.new_variable(0, 14); - let e = solver.new_variable(0, 4); - let d = solver.new_variable(0, 2); - let c = solver.new_variable(8, 9); - let b2 = solver.new_variable(5, 5); - let b1 = solver.new_variable(3, 3); - let a = solver.new_variable(0, 1); - - let constraint_tag = solver.new_constraint_tag(); - - let propagator = solver - .new_propagator(TimeTableOverIntervalPropagator::new( - &[ - ArgTask { - start_time: a, - processing_time: 2, - resource_usage: 1, - }, - ArgTask { - start_time: b1, - processing_time: 2, - resource_usage: 2, - }, - ArgTask { - start_time: b2, - processing_time: 3, - resource_usage: 2, - }, - ArgTask { - start_time: c, - processing_time: 2, - resource_usage: 4, - }, - ArgTask { - start_time: d, - processing_time: 2, - resource_usage: 2, - }, - ArgTask { - start_time: e, - processing_time: 4, - resource_usage: 2, - }, - ArgTask { - start_time: f, - processing_time: 6, - resource_usage: 2, - }, - ] - .into_iter() - .collect::>(), - 5, - CumulativePropagatorOptions::default(), - constraint_tag, - )) - .expect("No conflict"); - assert_eq!(solver.lower_bound(a), 0); - assert_eq!(solver.upper_bound(a), 1); - assert_eq!(solver.lower_bound(c), 8); - assert_eq!(solver.upper_bound(c), 9); - assert_eq!(solver.lower_bound(d), 0); - assert_eq!(solver.upper_bound(d), 2); - assert_eq!(solver.lower_bound(e), 0); - assert_eq!(solver.upper_bound(e), 4); - assert_eq!(solver.lower_bound(f), 0); - assert_eq!(solver.upper_bound(f), 14); - - let notification_status = solver.increase_lower_bound_and_notify(propagator, 4, e, 3); - assert!(match notification_status { - EnqueueDecision::Enqueue => true, - EnqueueDecision::Skip => false, - }); - let result = solver.propagate(propagator); - assert!(result.is_ok()); - assert_eq!(solver.lower_bound(f), 10); - } - - #[test] - fn propagator_propagates_from_profile_reason() { - let mut state = State::default(); - let s1 = state.new_interval_variable(1, 1, None); - let s2 = state.new_interval_variable(1, 8, None); - let constraint_tag = state.new_constraint_tag(); - - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( - &[ - ArgTask { - start_time: s1, - processing_time: 4, - resource_usage: 1, - }, - ArgTask { - start_time: s2, - processing_time: 3, - resource_usage: 1, - }, - ] - .into_iter() - .collect::>(), - 1, - CumulativePropagatorOptions { - explanation_type: CumulativeExplanationType::Naive, - ..Default::default() - }, - constraint_tag, - )); - state.propagate_to_fixed_point().expect("No conflict"); - assert_eq!(state.lower_bound(s2), 5); - - let mut reason_buffer: Vec = vec![]; - let _ = state.get_propagation_reason( - predicate!(s2 >= 5), - &mut reason_buffer, - CurrentNogood::empty(), - ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); - assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); - } - #[test] fn propagator_propagates_generic_bounds() { let mut state = State::default(); From ecdd9e1d92c7fa1a39e7d340042f57dc4afb0fa1 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Tue, 31 Mar 2026 10:54:18 +1100 Subject: [PATCH 09/14] Fix cumulative test cases --- .../time_table_over_interval_incremental.rs | 2 +- .../cumulative/time_table/time_table_over_interval.rs | 6 +++--- .../cumulative/time_table/time_table_per_point.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index c157255fb..5143f984e 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -1198,7 +1198,7 @@ mod tests { state.propagate_to_fixed_point().expect("No conflict"); state.assert_bounds(s1, 3, 3); state.assert_bounds(s2, 5, 5); - state.assert_bounds(s2, 7, 15); + state.assert_bounds(s3, 7, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index e9c4f6818..b96da29d5 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -883,12 +883,12 @@ mod tests { let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( - predicate!(s2 <= 3), + predicate!(s2 >= 5), &mut reason_buffer, CurrentNogood::empty(), ); let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); - assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); + assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); } #[test] @@ -1002,7 +1002,7 @@ mod tests { state.propagate_to_fixed_point().expect("No conflict"); state.assert_bounds(s1, 3, 3); state.assert_bounds(s2, 5, 5); - state.assert_bounds(s2, 7, 15); + state.assert_bounds(s3, 7, 15); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index ad3d3fc5a..d6b034c55 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -562,8 +562,8 @@ mod tests { constraint_tag, )); state.propagate_to_fixed_point().expect("No conflict"); - state.assert_bounds(s1, 1, 3); - state.assert_bounds(s2, 6, 6); + state.assert_bounds(s1, 6, 6); + state.assert_bounds(s2, 1, 3); let mut reason_buffer: Vec = vec![]; let _ = state.get_propagation_reason( From 0b1d9278050de883920c103d2b0018cfa6dfb57c Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 1 Apr 2026 11:08:34 +1100 Subject: [PATCH 10/14] Fix conjunction creation --- .../arithmetic/integer_multiplication.rs | 12 ++++++------ .../arithmetic/linear_less_or_equal.rs | 2 +- .../propagators/arithmetic/linear_not_equal.rs | 2 +- .../src/propagators/arithmetic/maximum.rs | 6 +++--- .../time_table_over_interval_incremental.rs | 8 ++++---- .../time_table_per_point_incremental.rs | 8 ++++---- .../time_table/time_table_over_interval.rs | 8 ++++---- .../time_table/time_table_per_point.rs | 8 ++++---- .../propagators/src/propagators/element.rs | 16 ++++++++-------- 9 files changed, 35 insertions(+), 35 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index eb6fcb536..135c102e9 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -455,7 +455,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([a >= 0] & [b >= 0]), reason_lb); let mut reason_buffer: Vec = vec![]; @@ -464,7 +464,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_ub: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([a >= 0] & [a <= 3] & [b >= 0] & [b <= 4]), reason_ub @@ -498,7 +498,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([a >= 1] & [c >= 1]), reason_lb); let mut reason_buffer: Vec = vec![]; @@ -507,7 +507,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_ub: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([a >= 2] & [c >= 0] & [c <= 12]), reason_ub); } @@ -538,7 +538,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([b >= 1] & [c >= 1]), reason_lb); let mut reason_buffer: Vec = vec![]; @@ -547,7 +547,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_ub: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([b >= 3] & [c >= 0] & [c <= 12]), reason_ub); } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index b1356d241..5d1ff53d9 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -375,7 +375,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x >= 1]), reason); } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index 2876ff375..c79c5c089 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -480,7 +480,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x == 2]), reason); } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index 0ed5374e7..f8317f829 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -261,7 +261,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([a <= 5] & [b <= 5] & [c <= 5]), reason); } @@ -291,7 +291,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([c >= 5]), reason); } @@ -322,7 +322,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([rhs <= 3]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index 5143f984e..2896c79a6 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -943,7 +943,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -1154,7 +1154,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); } @@ -1206,7 +1206,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 5]), reason); } @@ -1255,7 +1255,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs index 415412c4e..8a63ea030 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs @@ -937,7 +937,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -1148,7 +1148,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 >= 4] & [s1 >= 1] & [s1 <= 1]), /* Note that this not * the most general @@ -1210,7 +1210,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 6]), /* Note that s3 would * have been able to @@ -1265,7 +1265,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index b96da29d5..a94266deb 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -765,7 +765,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -887,7 +887,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); } @@ -1010,7 +1010,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 5]), reason); } @@ -1056,7 +1056,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index d6b034c55..81e8e1747 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -571,7 +571,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -776,7 +776,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 >= 4] & [s1 >= 1] & [s1 <= 1]), /* Note that this not * the most general @@ -836,7 +836,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 6]), /* Note that s3 would * have been able to @@ -889,7 +889,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index dfc4b092f..07b99b13a 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -441,7 +441,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x_3 >= 10] & [rhs <= 9]), reason); let mut reason_buffer: Vec = vec![]; @@ -450,7 +450,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x_1 <= 5] & [rhs >= 6]), reason); } @@ -483,7 +483,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 >= 2] & [x_1 >= 2] & [x_2 >= 2] & [x_3 >= 2]), reason @@ -495,7 +495,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 <= 15] & [x_1 <= 15] & [x_2 <= 15] & [x_3 <= 15]), reason @@ -531,7 +531,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([index == 1] & [rhs >= 6]), reason); let mut reason_buffer: Vec = vec![]; @@ -540,7 +540,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([index == 1] & [rhs <= 9]), reason); } @@ -577,7 +577,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 >= 3] & [x_2 >= 3] & [x_3 >= 3] & [index != 1]), reason @@ -589,7 +589,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason: PropositionalConjunction = reason_buffer.into_iter().collect(); + let reason_lb: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 <= 15] & [x_2 <= 15] & [x_3 <= 15] & [index != 1]), reason From 611ed5fcf3c7abca033a5186c28777860a201cb5 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 1 Apr 2026 11:11:35 +1100 Subject: [PATCH 11/14] Document StateExt --- pumpkin-crates/propagators/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pumpkin-crates/propagators/src/lib.rs b/pumpkin-crates/propagators/src/lib.rs index 7e84bd7aa..6210ef73b 100644 --- a/pumpkin-crates/propagators/src/lib.rs +++ b/pumpkin-crates/propagators/src/lib.rs @@ -9,8 +9,11 @@ use pumpkin_core::state::State; #[cfg(test)] use pumpkin_core::variables::DomainId; +/// Utilities that simplify test code using the [`State`]. #[cfg(test)] pub(crate) trait StateExt { + /// Assert that the bounds of the given `domain_id` match the provided `lower_bound` and + /// `upper_bound`. fn assert_bounds(&self, domain_id: DomainId, lower_bound: i32, upper_bound: i32); } From 74598bc95440a34780ff290a263ef320494d6adb Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 1 Apr 2026 11:12:57 +1100 Subject: [PATCH 12/14] Remove unnecessary qualification --- .../time_table/time_table_over_interval.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index a94266deb..41ab73c07 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -520,7 +520,7 @@ mod tests { let s2 = state.new_interval_variable(1, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: s1, @@ -551,7 +551,7 @@ mod tests { let s2 = state.new_interval_variable(1, 1, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: s1, @@ -602,7 +602,7 @@ mod tests { let s2 = state.new_interval_variable(0, 6, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: s1, @@ -637,7 +637,7 @@ mod tests { let a = state.new_interval_variable(0, 1, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: a, @@ -733,7 +733,7 @@ mod tests { let s2 = state.new_interval_variable(1, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: s1, @@ -855,7 +855,7 @@ mod tests { let s2 = state.new_interval_variable(1, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: s1, @@ -972,7 +972,7 @@ mod tests { let s3 = state.new_interval_variable(1, 15, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: s1, @@ -1021,7 +1021,7 @@ mod tests { let s2 = state.new_interval_variable(0, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTableOverIntervalPropagator::::new( + let _ = state.add_propagator(TimeTableOverIntervalPropagator::new( &[ ArgTask { start_time: s1, From 9af7f88ecbc9e2edf5a301608a86652f63485461 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 1 Apr 2026 11:15:38 +1100 Subject: [PATCH 13/14] Fix renaming errors --- .../arithmetic/integer_multiplication.rs | 6 +++--- .../arithmetic/linear_less_or_equal.rs | 2 +- .../propagators/arithmetic/linear_not_equal.rs | 2 +- .../src/propagators/arithmetic/maximum.rs | 6 +++--- .../time_table_over_interval_incremental.rs | 8 ++++---- .../time_table_per_point_incremental.rs | 8 ++++---- .../time_table/time_table_over_interval.rs | 9 ++++----- .../time_table/time_table_per_point.rs | 8 ++++---- .../propagators/src/propagators/element.rs | 16 ++++++++-------- 9 files changed, 32 insertions(+), 33 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs index 135c102e9..7af7ea417 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/integer_multiplication.rs @@ -464,7 +464,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason_ub: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([a >= 0] & [a <= 3] & [b >= 0] & [b <= 4]), reason_ub @@ -507,7 +507,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason_ub: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([a >= 2] & [c >= 0] & [c <= 12]), reason_ub); } @@ -547,7 +547,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason_ub: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([b >= 3] & [c >= 0] & [c <= 12]), reason_ub); } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs index 5d1ff53d9..7ccb902c1 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_less_or_equal.rs @@ -375,7 +375,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x >= 1]), reason); } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs index c79c5c089..c91a57c3a 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/linear_not_equal.rs @@ -480,7 +480,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x == 2]), reason); } diff --git a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs index f8317f829..f59252ba2 100644 --- a/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs +++ b/pumpkin-crates/propagators/src/propagators/arithmetic/maximum.rs @@ -261,7 +261,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([a <= 5] & [b <= 5] & [c <= 5]), reason); } @@ -291,7 +291,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([c >= 5]), reason); } @@ -322,7 +322,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([rhs <= 3]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs index 2896c79a6..ffe3e0b91 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/over_interval_incremental_propagator/time_table_over_interval_incremental.rs @@ -943,7 +943,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -1154,7 +1154,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); } @@ -1206,7 +1206,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 5]), reason); } @@ -1255,7 +1255,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs index 8a63ea030..955e8d531 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/per_point_incremental_propagator/time_table_per_point_incremental.rs @@ -937,7 +937,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -1148,7 +1148,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 >= 4] & [s1 >= 1] & [s1 <= 1]), /* Note that this not * the most general @@ -1210,7 +1210,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 6]), /* Note that s3 would * have been able to @@ -1265,7 +1265,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs index 41ab73c07..9dbf8f62c 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_over_interval.rs @@ -505,7 +505,6 @@ mod tests { use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::state::Conflict; use pumpkin_core::state::State; - use pumpkin_core::variables::DomainId; use crate::StateExt; use crate::cumulative::ArgTask; @@ -765,7 +764,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 8] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -887,7 +886,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 >= 1] & [s1 >= 1] & [s1 <= 1]), reason); } @@ -1010,7 +1009,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 5]), reason); } @@ -1056,7 +1055,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index 81e8e1747..cbb294cf8 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -571,7 +571,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s2 <= 5] & [s1 >= 6] & [s1 <= 6]), reason); } @@ -776,7 +776,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 >= 4] & [s1 >= 1] & [s1 <= 1]), /* Note that this not * the most general @@ -836,7 +836,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([s2 <= 5] & [s2 >= 5] & [s3 >= 6]), /* Note that s3 would * have been able to @@ -889,7 +889,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([s1 <= 4] & [s1 >= 4]), reason); } } diff --git a/pumpkin-crates/propagators/src/propagators/element.rs b/pumpkin-crates/propagators/src/propagators/element.rs index 07b99b13a..3f9b011d4 100644 --- a/pumpkin-crates/propagators/src/propagators/element.rs +++ b/pumpkin-crates/propagators/src/propagators/element.rs @@ -441,7 +441,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x_3 >= 10] & [rhs <= 9]), reason); let mut reason_buffer: Vec = vec![]; @@ -450,7 +450,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([x_1 <= 5] & [rhs >= 6]), reason); } @@ -483,7 +483,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 >= 2] & [x_1 >= 2] & [x_2 >= 2] & [x_3 >= 2]), reason @@ -495,7 +495,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 <= 15] & [x_1 <= 15] & [x_2 <= 15] & [x_3 <= 15]), reason @@ -531,7 +531,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([index == 1] & [rhs >= 6]), reason); let mut reason_buffer: Vec = vec![]; @@ -540,7 +540,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!(conjunction!([index == 1] & [rhs <= 9]), reason); } @@ -577,7 +577,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 >= 3] & [x_2 >= 3] & [x_3 >= 3] & [index != 1]), reason @@ -589,7 +589,7 @@ mod tests { &mut reason_buffer, CurrentNogood::empty(), ); - let reason_lb: PropositionalConjunction = reason_buffer.into(); + let reason: PropositionalConjunction = reason_buffer.into(); assert_eq!( conjunction!([x_0 <= 15] & [x_2 <= 15] & [x_3 <= 15] & [index != 1]), reason From be1381429d2f8bbfd44dda1f8010c1e082e8a8e9 Mon Sep 17 00:00:00 2001 From: Maarten Flippo Date: Wed, 1 Apr 2026 11:16:55 +1100 Subject: [PATCH 14/14] Remove more unnecessary qualification --- .../time_table/time_table_per_point.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs index cbb294cf8..28ee01df4 100644 --- a/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs +++ b/pumpkin-crates/propagators/src/propagators/cumulative/time_table/time_table_per_point.rs @@ -311,7 +311,6 @@ mod tests { use pumpkin_core::propagation::EnqueueDecision; use pumpkin_core::state::Conflict; use pumpkin_core::state::State; - use pumpkin_core::variables::DomainId; use crate::StateExt; use crate::cumulative::ArgTask; @@ -326,7 +325,7 @@ mod tests { let s2 = state.new_interval_variable(1, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: s1, @@ -357,7 +356,7 @@ mod tests { let s2 = state.new_interval_variable(1, 1, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: s1, @@ -404,7 +403,7 @@ mod tests { let s2 = state.new_interval_variable(0, 6, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: s1, @@ -439,7 +438,7 @@ mod tests { let a = state.new_interval_variable(0, 1, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: a, @@ -539,7 +538,7 @@ mod tests { let s2 = state.new_interval_variable(1, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: s1, @@ -744,7 +743,7 @@ mod tests { let s2 = state.new_interval_variable(1, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: s1, @@ -798,7 +797,7 @@ mod tests { let s3 = state.new_interval_variable(1, 15, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: s1, @@ -854,7 +853,7 @@ mod tests { let s2 = state.new_interval_variable(0, 8, None); let constraint_tag = state.new_constraint_tag(); - let _ = state.add_propagator(TimeTablePerPointPropagator::::new( + let _ = state.add_propagator(TimeTablePerPointPropagator::new( &[ ArgTask { start_time: s1,