diff --git a/CLAUDE.md b/CLAUDE.md index 61898f246..59d3df6d1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,11 +46,12 @@ Scripts in `scripts/` (`fmt.sh`, `clippy.sh`, `documentation.sh`, `deny.sh`) mir 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. +- **`core`** — The main solver engine. Contains the CDCL loop, propagation engine, nogood learning, 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`. +- **`branching`** — Branching heuristics (variable/value selection, tie-breaking) and `Brancher` implementations built on top of `core`. ### Interfaces - **`pumpkin-solver`** — CLI binary. Accepts CNF, WCNF (MaxSAT), and FlatZinc input formats. diff --git a/Cargo.lock b/Cargo.lock index d272a37ba..9fc659555 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -943,6 +943,15 @@ dependencies = [ "cc", ] +[[package]] +name = "pumpkin-branching" +version = "0.5.0" +dependencies = [ + "enum-map", + "log", + "pumpkin-core", +] + [[package]] name = "pumpkin-checker" version = "0.5.0" @@ -1069,6 +1078,7 @@ dependencies = [ "env_logger", "flatzinc", "log", + "pumpkin-branching", "pumpkin-checker", "pumpkin-conflict-resolvers", "pumpkin-constraints", @@ -1086,6 +1096,7 @@ dependencies = [ name = "pumpkin-solver-py" version = "0.5.0" dependencies = [ + "pumpkin-branching", "pumpkin-conflict-resolvers", "pumpkin-constraints", "pumpkin-solver", diff --git a/README.md b/README.md index 564b564d1..1c81e05f7 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Pumpkin consists of several different components: - [pumpkin-propagators](https://github.com/ConSol-Lab/Pumpkin/tree/main/pumpkin-crates/propagators); contains (most of) the propagators used by Pumpkin. - [pumpkin-constraints](https://github.com/ConSol-Lab/Pumpkin/tree/main/pumpkin-crates/constraints); contains convenient ways to add one or more propagators modelling certain constraints to the solver. - [pumpkin-conflict-resolvers](https://github.com/ConSol-Lab/Pumpkin/tree/main/pumpkin-crates/conflict-resolvers); contains the conflict resolvers (e.g., 1UIP or All-Decision conflict resolvers) used by Pumpkin. + - [pumpkin-branching](https://github.com/ConSol-Lab/Pumpkin/tree/main/pumpkin-crates/branching); contains the branching heuristics (e.g., variable/value selection strategies) used by Pumpkin. - [pumpkin-checking](https://github.com/ConSol-Lab/Pumpkin/tree/main/pumpkin-crates/checking); contains the types used for checking the soundness of propagators in Pumpkin. - The CLI contained in [pumpkin-solver](https://github.com/ConSol-Lab/Pumpkin/tree/main/pumpkin-solver). - The Python bindings contained in [pumpkin-solver-py](https://github.com/ConSol-Lab/Pumpkin/tree/main/pumpkin-solver-py). diff --git a/pumpkin-crates/branching/Cargo.toml b/pumpkin-crates/branching/Cargo.toml new file mode 100644 index 000000000..a118b6e21 --- /dev/null +++ b/pumpkin-crates/branching/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "pumpkin-branching" +version.workspace = true +repository.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +description = "The branching heuristics of the Pumpkin constraint programming solver." + +[lints] +workspace = true + +[dependencies] +enum-map = "3.1.0" +log = "0.4.30" +pumpkin-core = { version = "0.5.0", path = "../core" } diff --git a/pumpkin-crates/core/src/branching/branchers/alternating/alternating_brancher.rs b/pumpkin-crates/branching/src/branching/alternating/alternating_brancher.rs similarity index 83% rename from pumpkin-crates/core/src/branching/branchers/alternating/alternating_brancher.rs rename to pumpkin-crates/branching/src/branching/alternating/alternating_brancher.rs index 36d5d5af2..e641323f1 100644 --- a/pumpkin-crates/core/src/branching/branchers/alternating/alternating_brancher.rs +++ b/pumpkin-crates/branching/src/branching/alternating/alternating_brancher.rs @@ -1,17 +1,20 @@ //! A [`Brancher`] which alternates between the [`DefaultBrancher`] and another [`Brancher`] based //! on the strategy specified in [`AlternatingStrategy`]. +use pumpkin_core::Solver; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::results::SolutionReference; +#[cfg(doc)] +use pumpkin_core::state::State; +use pumpkin_core::statistics::StatisticLogger; +use pumpkin_core::variables::DomainId; + use super::BrancherToUse; use crate::DefaultBrancher; -use crate::Solver; -use crate::basic_types::SolutionReference; -use crate::branching::Brancher; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::branchers::alternating::strategies::AlternatingStrategy; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::statistics::StatisticLogger; +use crate::branching::alternating::strategies::AlternatingStrategy; /// A [`Brancher`] which switches between its provided brancher and [`DefaultBrancher`] based on the /// provided [`AlternatingStrategy`]. @@ -35,9 +38,18 @@ impl AlternatingBrancher { pub fn new(solver: &Solver, other_brancher: OtherBrancher, strategy: Strategy) -> Self { + Self::new_from_domains(solver.get_domains(), other_brancher, strategy) + } + + /// Creates a new instance considering all of the provided `domains`. + pub(crate) fn new_from_domains( + domains: impl IntoIterator, + other_brancher: OtherBrancher, + strategy: Strategy, + ) -> Self { Self { other_brancher, - default_brancher: solver.default_brancher(), + default_brancher: DefaultBrancher::new_from_domains(domains), strategy, } } diff --git a/pumpkin-crates/core/src/branching/branchers/alternating/mod.rs b/pumpkin-crates/branching/src/branching/alternating/mod.rs similarity index 100% rename from pumpkin-crates/core/src/branching/branchers/alternating/mod.rs rename to pumpkin-crates/branching/src/branching/alternating/mod.rs diff --git a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/every_x_restarts.rs b/pumpkin-crates/branching/src/branching/alternating/strategies/every_x_restarts.rs similarity index 58% rename from pumpkin-crates/core/src/branching/branchers/alternating/strategies/every_x_restarts.rs rename to pumpkin-crates/branching/src/branching/alternating/strategies/every_x_restarts.rs index 9aa1e0473..c760a5913 100644 --- a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/every_x_restarts.rs +++ b/pumpkin-crates/branching/src/branching/alternating/strategies/every_x_restarts.rs @@ -1,10 +1,11 @@ -use crate::branching::Brancher; -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; + #[cfg(doc)] -use crate::branching::branchers::alternating::AlternatingBrancher; -use crate::branching::branchers::alternating::BrancherToUse; -use crate::branching::branchers::alternating::strategies::AlternatingStrategy; +use crate::branching::alternating::AlternatingBrancher; +use crate::branching::alternating::BrancherToUse; +use crate::branching::alternating::strategies::AlternatingStrategy; /// Specifies that the [`AlternatingBrancher`] should switch between /// [`BrancherToUse::Default`] and the provided brancher every `x`th considered restart. @@ -82,91 +83,76 @@ impl AlternatingStrategy for EveryXRestarts { #[cfg(test)] mod tests { - use crate::Solver; - use crate::basic_types::tests::TestRandom; - use crate::branching::Brancher; - use crate::branching::SelectionContext; - use crate::branching::branchers::alternating::alternating_brancher::AlternatingBrancher; - use crate::branching::branchers::alternating::every_x_restarts::EveryXRestarts; - use crate::engine::Assignments; + use pumpkin_core::branching::Brancher; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::state::State; + + use crate::DefaultBrancher; + use crate::branching::alternating::alternating_brancher::AlternatingBrancher; + use crate::branching::alternating::every_x_restarts::EveryXRestarts; + use crate::testing::TestRandom; #[test] fn test_every_restart() { - let assignments = Assignments::default(); - let solver = Solver::default(); - let mut brancher = - AlternatingBrancher::new(&solver, solver.default_brancher(), EveryXRestarts::new(1)); + let state = State::default(); + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + let mut brancher = AlternatingBrancher::new_from_domains( + state.get_domain_ids(), + DefaultBrancher::new_from_domains(state.get_domain_ids()), + EveryXRestarts::new(1), + ); assert!(!brancher.is_using_default_brancher()); brancher.on_restart(); // next_decision is called to ensure that the brancher has actually switched - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(brancher.is_using_default_brancher()); brancher.on_restart(); - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(!brancher.is_using_default_brancher()); brancher.on_restart(); - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(brancher.is_using_default_brancher()); } #[test] fn test_every_other_restart() { - let assignments = Assignments::default(); - let solver = Solver::default(); - let mut brancher = - AlternatingBrancher::new(&solver, solver.default_brancher(), EveryXRestarts::new(2)); + let state = State::default(); + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + let mut brancher = AlternatingBrancher::new_from_domains( + state.get_domain_ids(), + DefaultBrancher::new_from_domains(state.get_domain_ids()), + EveryXRestarts::new(2), + ); assert!(!brancher.is_using_default_brancher()); brancher.on_restart(); // next_decision is called to ensure that the brancher has actually switched - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(!brancher.is_using_default_brancher()); brancher.on_restart(); - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(brancher.is_using_default_brancher()); brancher.on_restart(); - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(brancher.is_using_default_brancher()); brancher.on_restart(); - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(!brancher.is_using_default_brancher()); brancher.on_restart(); - let _ = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = brancher.next_decision(&mut context); assert!(!brancher.is_using_default_brancher()); } diff --git a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/every_x_solutions.rs b/pumpkin-crates/branching/src/branching/alternating/strategies/every_x_solutions.rs similarity index 58% rename from pumpkin-crates/core/src/branching/branchers/alternating/strategies/every_x_solutions.rs rename to pumpkin-crates/branching/src/branching/alternating/strategies/every_x_solutions.rs index ecdbd72de..aa05a1fba 100644 --- a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/every_x_solutions.rs +++ b/pumpkin-crates/branching/src/branching/alternating/strategies/every_x_solutions.rs @@ -1,10 +1,11 @@ -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::results::SolutionReference; + #[cfg(doc)] -use crate::branching::branchers::alternating::AlternatingBrancher; -use crate::branching::branchers::alternating::BrancherToUse; -use crate::branching::branchers::alternating::strategies::AlternatingStrategy; -use crate::results::SolutionReference; +use crate::branching::alternating::AlternatingBrancher; +use crate::branching::alternating::BrancherToUse; +use crate::branching::alternating::strategies::AlternatingStrategy; /// Specifies that the [`AlternatingBrancher`] should switch between /// [`BrancherToUse::Default`] and the provided brancher every `x`th solution. @@ -52,30 +53,33 @@ impl AlternatingStrategy for EveryXSolutions { #[cfg(test)] mod tests { - use crate::Solver; - use crate::branching::Brancher; - use crate::branching::branchers::alternating::alternating_brancher::AlternatingBrancher; - use crate::branching::branchers::alternating::strategies::every_x_solutions::EveryXSolutions; - use crate::engine::Assignments; - use crate::results::SolutionReference; + use pumpkin_core::Solver; + use pumpkin_core::branching::Brancher; + use pumpkin_core::results::Solution; + + use crate::DefaultBrancher; + use crate::branching::alternating::alternating_brancher::AlternatingBrancher; + use crate::branching::alternating::strategies::every_x_solutions::EveryXSolutions; #[test] fn test_every_other_solution() { let solver = Solver::default(); - let mut brancher = - AlternatingBrancher::new(&solver, solver.default_brancher(), EveryXSolutions::new(2)); + let mut brancher = AlternatingBrancher::new( + &solver, + DefaultBrancher::default_over_all_variables(&solver), + EveryXSolutions::new(2), + ); - let assignments = Assignments::default(); - let empty_solution_reference = SolutionReference::new(&assignments); + let empty_solution = Solution::default(); assert!(!brancher.is_using_default_brancher()); - brancher.on_solution(empty_solution_reference); + brancher.on_solution(empty_solution.as_reference()); assert!(!brancher.is_using_default_brancher()); - brancher.on_solution(empty_solution_reference); + brancher.on_solution(empty_solution.as_reference()); assert!(brancher.is_using_default_brancher()); - brancher.on_solution(empty_solution_reference); + brancher.on_solution(empty_solution.as_reference()); assert!(brancher.is_using_default_brancher()); - brancher.on_solution(empty_solution_reference); + brancher.on_solution(empty_solution.as_reference()); assert!(!brancher.is_using_default_brancher()); } } diff --git a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/mod.rs b/pumpkin-crates/branching/src/branching/alternating/strategies/mod.rs similarity index 89% rename from pumpkin-crates/core/src/branching/branchers/alternating/strategies/mod.rs rename to pumpkin-crates/branching/src/branching/alternating/strategies/mod.rs index f91638a50..7a08db723 100644 --- a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/mod.rs +++ b/pumpkin-crates/branching/src/branching/alternating/strategies/mod.rs @@ -1,11 +1,12 @@ use std::fmt::Debug; -use crate::branching::Brancher; -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::results::SolutionReference; + #[cfg(doc)] -use crate::branching::branchers::alternating::AlternatingBrancher; -use crate::results::SolutionReference; +use crate::branching::alternating::AlternatingBrancher; /// Defines methods for selecting which of two branching strategies to use; the default or the /// other brancher. diff --git a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/other_only.rs b/pumpkin-crates/branching/src/branching/alternating/strategies/other_only.rs similarity index 71% rename from pumpkin-crates/core/src/branching/branchers/alternating/strategies/other_only.rs rename to pumpkin-crates/branching/src/branching/alternating/strategies/other_only.rs index 0eab16d31..00d44b1c6 100644 --- a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/other_only.rs +++ b/pumpkin-crates/branching/src/branching/alternating/strategies/other_only.rs @@ -1,10 +1,11 @@ -use crate::branching::Brancher; -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; + #[cfg(doc)] -use crate::branching::branchers::alternating::AlternatingBrancher; -use crate::branching::branchers::alternating::AlternatingStrategy; -use crate::branching::branchers::alternating::BrancherToUse; +use crate::branching::alternating::AlternatingBrancher; +use crate::branching::alternating::AlternatingStrategy; +use crate::branching::alternating::BrancherToUse; /// Specifies that the [`AlternatingBrancher`] should always use the other strategy. #[derive(Default, Debug, Clone, Copy)] diff --git a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/until_solution.rs b/pumpkin-crates/branching/src/branching/alternating/strategies/until_solution.rs similarity index 56% rename from pumpkin-crates/core/src/branching/branchers/alternating/strategies/until_solution.rs rename to pumpkin-crates/branching/src/branching/alternating/strategies/until_solution.rs index 8abcbf904..495fb8321 100644 --- a/pumpkin-crates/core/src/branching/branchers/alternating/strategies/until_solution.rs +++ b/pumpkin-crates/branching/src/branching/alternating/strategies/until_solution.rs @@ -1,11 +1,12 @@ -use crate::branching::Brancher; -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::results::SolutionReference; + #[cfg(doc)] -use crate::branching::branchers::alternating::AlternatingBrancher; -use crate::branching::branchers::alternating::AlternatingStrategy; -use crate::branching::branchers::alternating::BrancherToUse; -use crate::results::SolutionReference; +use crate::branching::alternating::AlternatingBrancher; +use crate::branching::alternating::AlternatingStrategy; +use crate::branching::alternating::BrancherToUse; /// Specifies that the [`AlternatingBrancher`] should use the provided [`AlternatingStrategy`] until /// a solution is found, after which it will use [`BrancherToUse::Default`]. @@ -84,146 +85,112 @@ impl AlternatingStrategy for UntilSolution DefaultBrancher { + /// + /// The `domains` are the variables which are considered by the backup + /// [`RandomSelector`]; typically this is [`Solver::get_domains`]. + pub fn default_over_all_variables(solver: &Solver) -> DefaultBrancher { + Self::new_from_domains(solver.get_domains()) + } + + pub(crate) fn new_from_domains(domains: impl IntoIterator) -> DefaultBrancher { AutonomousSearch { predicate_id_info: DeletablePredicateIdGenerator::default(), heap: KeyValueHeap::default(), @@ -130,7 +138,7 @@ impl DefaultBrancher { best_known_solution: None, should_synchronise: false, backup_brancher: IndependentVariableValueBrancher::new( - RandomSelector::new(assignments.get_domains()), + RandomSelector::new(domains), RandomSplitter, ), statistics: Default::default(), @@ -354,22 +362,21 @@ impl Brancher for AutonomousSearch { #[cfg(test)] mod tests { + use pumpkin_core::branching::Brancher; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::AutonomousSearch; - use crate::basic_types::tests::TestRandom; - use crate::branching::Brancher; - use crate::branching::SelectionContext; - use crate::engine::Assignments; - use crate::engine::notifications::NotificationEngine; - use crate::predicate; - use crate::results::SolutionReference; + use crate::testing::TestRandom; #[test] fn brancher_picks_bumped_values() { - let mut assignments = Assignments::default(); - let x = assignments.grow(0, 10); - let y = assignments.grow(-10, 0); + let mut state = State::default(); + let x = state.new_interval_variable(0, 10, None); + let y = state.new_interval_variable(-10, 0, None); - let mut brancher = AutonomousSearch::default_over_all_variables(&assignments); + let mut brancher = AutonomousSearch::new_from_domains([x, y]); brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5)); brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5)); brancher.on_appearance_in_conflict_predicate(predicate!(y >= -5)); @@ -379,101 +386,91 @@ mod tests { #[test] fn dormant_values() { - let mut notification_engine = NotificationEngine::default(); - let mut assignments = Assignments::default(); - let x = assignments.grow(0, 10); - notification_engine.grow(); + let mut state = State::default(); + let x = state.new_interval_variable(0, 10, None); - let mut brancher = AutonomousSearch::default_over_all_variables(&assignments); + let mut brancher = AutonomousSearch::new_from_domains([x]); + let mut test_rng = TestRandom::default(); let predicate = predicate!(x >= 5); brancher.on_appearance_in_conflict_predicate(predicate); - let decision = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let decision = brancher.next_decision(&mut SelectionContext::new(&state, &mut test_rng)); assert_eq!(decision, Some(predicate)); - assignments.new_checkpoint(); + state.new_checkpoint(); // Decision Level 1 - let _ = assignments.post_predicate(predicate!(x >= 5), None, &mut notification_engine); + let _ = state + .post(predicate!(x >= 5)) + .expect("Expected posting the predicate to not result in an empty domain"); - assignments.new_checkpoint(); + state.new_checkpoint(); // Decision Level 2 - let _ = assignments.post_predicate(predicate!(x >= 7), None, &mut notification_engine); + let _ = state + .post(predicate!(x >= 7)) + .expect("Expected posting the predicate to not result in an empty domain"); - assignments.new_checkpoint(); + state.new_checkpoint(); // Decision Level 3 - let _ = assignments.post_predicate(predicate!(x >= 10), None, &mut notification_engine); + let _ = state + .post(predicate!(x >= 10)) + .expect("Expected posting the predicate to not result in an empty domain"); - assignments.new_checkpoint(); + state.new_checkpoint(); // We end at decision level 4 - let decision = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let decision = brancher.next_decision(&mut SelectionContext::new(&state, &mut test_rng)); assert!(decision.is_none()); assert!(brancher.dormant_predicates.contains(&predicate)); - let _ = assignments.synchronise(3, &mut notification_engine); + let _ = state.restore_to(3); - let decision = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let decision = brancher.next_decision(&mut SelectionContext::new(&state, &mut test_rng)); assert!(decision.is_none()); assert!(brancher.dormant_predicates.contains(&predicate)); - let _ = assignments.synchronise(0, &mut notification_engine); - brancher.synchronise(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let _ = state.restore_to(0); + brancher.synchronise(&mut SelectionContext::new(&state, &mut test_rng)); - let decision = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let decision = brancher.next_decision(&mut SelectionContext::new(&state, &mut test_rng)); assert_eq!(decision, Some(predicate)); assert!(!brancher.dormant_predicates.contains(&predicate)); } #[test] fn uses_fallback() { - let mut assignments = Assignments::default(); - let x = assignments.grow(0, 10); + let mut state = State::default(); + let x = state.new_interval_variable(0, 10, None); - let mut brancher = AutonomousSearch::default_over_all_variables(&assignments); + let mut brancher = AutonomousSearch::new_from_domains([x]); - let result = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom { - integers: vec![2], - usizes: vec![0], - bools: vec![false], - weighted_choice: |_| unreachable!(), - }, - )); + let mut test_rng = TestRandom { + integers: vec![2], + usizes: vec![0], + bools: vec![false], + weighted_choice: |_| unreachable!(), + }; + let result = brancher.next_decision(&mut SelectionContext::new(&state, &mut test_rng)); assert_eq!(result, Some(predicate!(x <= 2))); } #[test] fn uses_stored_solution() { - let mut notification_engine = NotificationEngine::default(); - let mut assignments = Assignments::default(); - let x = assignments.grow(0, 10); - notification_engine.grow(); + let mut state = State::default(); + let x = state.new_interval_variable(0, 10, None); - assignments.new_checkpoint(); - let _ = assignments.post_predicate(predicate!(x == 7), None, &mut notification_engine); + state.new_checkpoint(); + let _ = state + .post(predicate!(x == 7)) + .expect("Expected posting the predicate to not result in an empty domain"); - let mut brancher = AutonomousSearch::default_over_all_variables(&assignments); + let mut brancher = AutonomousSearch::new_from_domains([x]); - brancher.on_solution(SolutionReference::new(&assignments)); + let mut test_rng = TestRandom::default(); + let solution = SelectionContext::new(&state, &mut test_rng).solution(); + brancher.on_solution(solution.as_reference()); - let _ = assignments.synchronise(0, &mut notification_engine); + let _ = state.restore_to(0); assert_eq!( predicate!(x >= 5), @@ -494,10 +491,7 @@ mod tests { brancher.on_appearance_in_conflict_predicate(predicate!(x >= 5)); - let result = brancher.next_decision(&mut SelectionContext::new( - &assignments, - &mut TestRandom::default(), - )); + let result = brancher.next_decision(&mut SelectionContext::new(&state, &mut test_rng)); assert_eq!(result, Some(predicate!(x >= 5))); } } diff --git a/pumpkin-crates/core/src/branching/branchers/dynamic_brancher.rs b/pumpkin-crates/branching/src/branching/dynamic_brancher.rs similarity index 94% rename from pumpkin-crates/core/src/branching/branchers/dynamic_brancher.rs rename to pumpkin-crates/branching/src/branching/dynamic_brancher.rs index d977a93ae..8ccb80595 100644 --- a/pumpkin-crates/core/src/branching/branchers/dynamic_brancher.rs +++ b/pumpkin-crates/branching/src/branching/dynamic_brancher.rs @@ -7,15 +7,14 @@ use std::cmp::min; use std::fmt::Debug; use enum_map::EnumMap; - -use crate::basic_types::SolutionReference; -use crate::branching::Brancher; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::containers::HashSet; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::statistics::StatisticLogger; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::containers::HashSet; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::results::SolutionReference; +use pumpkin_core::statistics::StatisticLogger; +use pumpkin_core::variables::DomainId; /// An implementation of a [`Brancher`] which takes a [`Vec`] of `Box` and /// sequentially applies [`Brancher::next_decision`] until all of them return [`None`]. @@ -171,8 +170,9 @@ impl Brancher for DynamicBrancher { #[cfg(test)] mod tests { + use pumpkin_core::branching::Brancher; + use super::DynamicBrancher; - use crate::branching::Brancher; #[test] fn is_restart_pointless_with_no_branchers_is_true() { diff --git a/pumpkin-crates/core/src/branching/branchers/independent_variable_value_brancher.rs b/pumpkin-crates/branching/src/branching/independent_variable_value_brancher.rs similarity index 90% rename from pumpkin-crates/core/src/branching/branchers/independent_variable_value_brancher.rs rename to pumpkin-crates/branching/src/branching/independent_variable_value_brancher.rs index 9dec63303..2a631d294 100644 --- a/pumpkin-crates/core/src/branching/branchers/independent_variable_value_brancher.rs +++ b/pumpkin-crates/branching/src/branching/independent_variable_value_brancher.rs @@ -4,14 +4,15 @@ use std::fmt::Debug; use std::marker::PhantomData; -use crate::basic_types::SolutionReference; -use crate::branching::Brancher; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::results::SolutionReference; +use pumpkin_core::variables::DomainId; + +use crate::value_selection::ValueSelector; +use crate::variable_selection::VariableSelector; /// An implementation of a [`Brancher`] which simply uses a single /// [`VariableSelector`] and a single [`ValueSelector`] independently of one another. diff --git a/pumpkin-crates/core/src/branching/branchers/mod.rs b/pumpkin-crates/branching/src/branching/mod.rs similarity index 100% rename from pumpkin-crates/core/src/branching/branchers/mod.rs rename to pumpkin-crates/branching/src/branching/mod.rs diff --git a/pumpkin-crates/core/src/branching/branchers/warm_start.rs b/pumpkin-crates/branching/src/branching/warm_start.rs similarity index 81% rename from pumpkin-crates/core/src/branching/branchers/warm_start.rs rename to pumpkin-crates/branching/src/branching/warm_start.rs index ca3a81dd8..ef2e580c2 100644 --- a/pumpkin-crates/core/src/branching/branchers/warm_start.rs +++ b/pumpkin-crates/branching/src/branching/warm_start.rs @@ -1,11 +1,11 @@ -use crate::branching::Brancher; -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; -use crate::predicate; -use crate::predicates::Predicate; -use crate::pumpkin_assert_eq_simple; -use crate::results::SolutionReference; -use crate::variables::IntegerVariable; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::results::SolutionReference; +use pumpkin_core::variables::IntegerVariable; /// A [`Brancher`] which represents warm starting. /// @@ -48,7 +48,7 @@ impl Brancher for WarmStart { self.index += 1; - if context.assignments.evaluate_predicate(predicate).is_none() { + if !context.is_predicate_assigned(predicate) { return Some(predicate); } } diff --git a/pumpkin-crates/branching/src/default_brancher.rs b/pumpkin-crates/branching/src/default_brancher.rs new file mode 100644 index 000000000..6ea232720 --- /dev/null +++ b/pumpkin-crates/branching/src/default_brancher.rs @@ -0,0 +1,23 @@ +use pumpkin_core::variables::DomainId; + +use crate::branching::autonomous_search::AutonomousSearch; +use crate::branching::independent_variable_value_brancher::IndependentVariableValueBrancher; +use crate::value_selection::RandomSplitter; +use crate::variable_selection::RandomSelector; + +/// A brancher which makes use of VSIDS \[1\] and solution-based phase saving (both adapted for CP). +/// +/// If VSIDS does not contain any (unfixed) predicates then it will default to the +/// [`IndependentVariableValueBrancher`]. +/// +/// Create an instance using [`DefaultBrancher::default_over_all_variables`]. +/// +/// # Bibliography +/// \[1\] M. W. Moskewicz, C. F. Madigan, Y. Zhao, L. Zhang, and S. Malik, ‘Chaff: Engineering an +/// efficient SAT solver’, in Proceedings of the 38th annual Design Automation Conference, 2001. +/// +/// \[2\] E. Demirović, G. Chu, and P. J. Stuckey, ‘Solution-based phase saving for CP: A +/// value-selection heuristic to simulate local search behavior in complete solvers’, in the +/// proceedings of the Principles and Practice of Constraint Programming (CP 2018). +pub type DefaultBrancher = + AutonomousSearch>; diff --git a/pumpkin-crates/branching/src/lib.rs b/pumpkin-crates/branching/src/lib.rs new file mode 100644 index 000000000..f60ffd57d --- /dev/null +++ b/pumpkin-crates/branching/src/lib.rs @@ -0,0 +1,34 @@ +//! Contains the branching heuristics of the Pumpkin constraint programming solver. +//! +//! In general, it provides implementations of 3 traits (defined in `pumpkin-core`): +//! - [`Brancher`] implementations (see [`branching`]) which define how a branching procedure (which +//! selects an unfixed variable and splits the domain in some way) should operate; an example +//! implementation is the [`IndependentVariableValueBrancher`]. +//! - [`VariableSelector`] implementations (see [`variable_selection`]) which define how a variable +//! is selected; an example implementation is [`AntiFirstFail`]. +//! - [`ValueSelector`] implementations (see [`value_selection`]) which define how a value is +//! selected for the variable chosen by a [`VariableSelector`]. +//! +//! It also provides [`tie_breaking`] strategies which can be used by [`VariableSelector`]s. + +pub mod branching; +pub mod tie_breaking; +pub mod value_selection; +pub mod variable_selection; + +mod default_brancher; +#[cfg(test)] +pub(crate) mod testing; + +pub use default_brancher::DefaultBrancher; +#[cfg(doc)] +use pumpkin_core::branching::Brancher; + +#[cfg(doc)] +use crate::branching::independent_variable_value_brancher::IndependentVariableValueBrancher; +#[cfg(doc)] +use crate::value_selection::ValueSelector; +#[cfg(doc)] +use crate::variable_selection::AntiFirstFail; +#[cfg(doc)] +use crate::variable_selection::VariableSelector; diff --git a/pumpkin-crates/branching/src/testing.rs b/pumpkin-crates/branching/src/testing.rs new file mode 100644 index 000000000..f946c811b --- /dev/null +++ b/pumpkin-crates/branching/src/testing.rs @@ -0,0 +1,74 @@ +//! Testing utilities for the [`Random`] trait, used across this crate's test modules. + +use std::cmp::Ordering; +use std::fmt::Debug; +use std::ops::Range; + +use pumpkin_core::Random; +use pumpkin_core::asserts::pumpkin_assert_simple; + +/// A test "random" generator which takes as input a list of elements of [`usize`] and [`bool`] +/// and returns them in order. If more values are attempted to be generated than are provided +/// then this will result in panicking. +#[derive(Debug)] +pub(crate) struct TestRandom { + pub usizes: Vec, + pub integers: Vec, + pub bools: Vec, + pub weighted_choice: fn(&[f64]) -> Option, +} + +impl Default for TestRandom { + fn default() -> Self { + TestRandom { + weighted_choice: |_| unimplemented!(), + usizes: vec![], + integers: vec![], + bools: vec![], + } + } +} + +impl Random for TestRandom { + fn generate_bool(&mut self, probability: f64) -> bool { + let selected = self.bools.remove(0); + pumpkin_assert_simple!( + if matches!(probability.partial_cmp(&1.0), Some(Ordering::Equal)) { + selected + } else if matches!(probability.partial_cmp(&0.0), Some(Ordering::Equal)) { + !selected + } else { + true + }, + "The probability is {probability} but the selected value is {selected}, this should not be possible, please ensure that your test cases are correctly defined" + ); + selected + } + + fn generate_i32_in_range(&mut self, lb: i32, ub: i32) -> i32 { + let selected = self.integers.remove(0); + let range = lb..ub; + pumpkin_assert_simple!( + range.contains(&selected), + "The selected element by `TestRandom` ({selected}) is not in the provided range ({range:?}) and thus should not be returned, please ensure that your test cases are correctly defined" + ); + selected + } + + fn generate_usize_in_range(&mut self, range: Range) -> usize { + let selected = self.usizes.remove(0); + pumpkin_assert_simple!( + range.contains(&selected), + "The selected element by `TestRandom` ({selected}) is not in the provided range ({range:?}) and thus should not be returned, please ensure that your test cases are correctly defined" + ); + selected + } + + fn generate_f64(&mut self) -> f64 { + unimplemented!() + } + + fn get_weighted_choice(&mut self, weights: &[f64]) -> Option { + (self.weighted_choice)(weights) + } +} diff --git a/pumpkin-crates/core/src/branching/tie_breaking/in_order_tie_breaker.rs b/pumpkin-crates/branching/src/tie_breaking/in_order_tie_breaker.rs similarity index 96% rename from pumpkin-crates/core/src/branching/tie_breaking/in_order_tie_breaker.rs rename to pumpkin-crates/branching/src/tie_breaking/in_order_tie_breaker.rs index b5af7d963..56fcf035d 100644 --- a/pumpkin-crates/core/src/branching/tie_breaking/in_order_tie_breaker.rs +++ b/pumpkin-crates/branching/src/tie_breaking/in_order_tie_breaker.rs @@ -75,10 +75,11 @@ impl TieBreaker for InOrderTieBreaker< #[cfg(test)] mod tests { + use pumpkin_core::variables::DomainId; + use super::InOrderTieBreaker; - use crate::branching::tie_breaking::Direction; - use crate::branching::tie_breaking::TieBreaker; - use crate::engine::variables::DomainId; + use crate::tie_breaking::Direction; + use crate::tie_breaking::TieBreaker; #[test] fn test_selection_first_value() { diff --git a/pumpkin-crates/core/src/branching/tie_breaking/mod.rs b/pumpkin-crates/branching/src/tie_breaking/mod.rs similarity index 88% rename from pumpkin-crates/core/src/branching/tie_breaking/mod.rs rename to pumpkin-crates/branching/src/tie_breaking/mod.rs index 7c41ce3b6..171d803be 100644 --- a/pumpkin-crates/core/src/branching/tie_breaking/mod.rs +++ b/pumpkin-crates/branching/src/tie_breaking/mod.rs @@ -22,10 +22,10 @@ //! select the first variable with the lowest-value that it has found. //! //! ```rust -//! # use pumpkin_core::branching::tie_breaking::InOrderTieBreaker; +//! # use pumpkin_branching::tie_breaking::InOrderTieBreaker; //! # use pumpkin_core::variables::DomainId; -//! # use pumpkin_core::branching::tie_breaking::Direction; -//! # use pumpkin_core::branching::tie_breaking::TieBreaker; +//! # use pumpkin_branching::tie_breaking::Direction; +//! # use pumpkin_branching::tie_breaking::TieBreaker; //! let mut breaker = InOrderTieBreaker::new(Direction::Minimum); //! //! // We consider 3 variables, where only variables with ID 1 and ID 2 should be considered. @@ -49,6 +49,6 @@ pub use random_tie_breaker::*; pub use tie_breaker::*; #[cfg(doc)] -use crate::branching::variable_selection::Smallest; +use crate::variable_selection::Smallest; #[cfg(doc)] -use crate::branching::variable_selection::VariableSelector; +use crate::variable_selection::VariableSelector; diff --git a/pumpkin-crates/core/src/branching/tie_breaking/random_tie_breaker.rs b/pumpkin-crates/branching/src/tie_breaking/random_tie_breaker.rs similarity index 97% rename from pumpkin-crates/core/src/branching/tie_breaking/random_tie_breaker.rs rename to pumpkin-crates/branching/src/tie_breaking/random_tie_breaker.rs index 6d2a780a2..9b596133e 100644 --- a/pumpkin-crates/core/src/branching/tie_breaking/random_tie_breaker.rs +++ b/pumpkin-crates/branching/src/tie_breaking/random_tie_breaker.rs @@ -1,6 +1,7 @@ +use pumpkin_core::Random; + use super::Direction; use super::TieBreaker; -use crate::basic_types::Random; /// A tie breaker which selects the variable with the "best" value (according to the [`Direction`]), /// if there is a tie then it will select any of the variables part of this tie with equal @@ -121,9 +122,9 @@ impl TieBreaker for RandomTieBreaker for InDomainInterval { #[cfg(test)] mod tests { + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::InDomainInterval; - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use crate::testing::TestRandom; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 10)]); - let mut test_rng = TestRandom::default(); - let domain_ids = assignments.get_domains().collect::>(); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); let mut selector = InDomainInterval; for to_remove in [2, 3, 7, 8] { - let _ = assignments.post_predicate( - predicate!(domain_ids[0] != to_remove), - None, - &mut notification_engine, - ); + let _ = state + .post(predicate!(domain_id != to_remove)) + .expect("Expected posting the predicate to not result in an empty domain"); } - let mut context = SelectionContext::new(&assignments, &mut test_rng); - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] <= 1)) + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id <= 1)) } #[test] fn test_no_holes_in_domain_bisects_domain() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainInterval; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] <= 5),) + assert_eq!(selected_predicate, predicate!(domain_id <= 5),) } #[test] fn test_domain_of_size_two() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(1, 2)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 2, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainInterval; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] <= 1)) + assert_eq!(selected_predicate, predicate!(domain_id <= 1)) } } diff --git a/pumpkin-crates/branching/src/value_selection/in_domain_max.rs b/pumpkin-crates/branching/src/value_selection/in_domain_max.rs new file mode 100644 index 000000000..16df1a293 --- /dev/null +++ b/pumpkin-crates/branching/src/value_selection/in_domain_max.rs @@ -0,0 +1,50 @@ +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::IntegerVariable; + +use crate::value_selection::ValueSelector; + +/// [`ValueSelector`] which chooses to assign the provided variable to its upper-bound. +#[derive(Debug, Copy, Clone)] +pub struct InDomainMax; + +impl ValueSelector for InDomainMax { + fn select_value( + &mut self, + context: &mut SelectionContext, + decision_variable: Var, + ) -> Predicate { + predicate!(decision_variable >= context.upper_bound(decision_variable)) + } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } +} + +#[cfg(test)] +mod tests { + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::InDomainMax; + use crate::value_selection::ValueSelector; + + #[test] + fn test_returns_correct_literal() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + + let mut selector = InDomainMax; + + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id >= 10)) + } +} diff --git a/pumpkin-crates/branching/src/value_selection/in_domain_median.rs b/pumpkin-crates/branching/src/value_selection/in_domain_median.rs new file mode 100644 index 000000000..f7765cab3 --- /dev/null +++ b/pumpkin-crates/branching/src/value_selection/in_domain_median.rs @@ -0,0 +1,87 @@ +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::IntegerVariable; + +use crate::value_selection::ValueSelector; + +/// A [`ValueSelector`] which selects the median value in the domain (or if this value is already +/// assigned then the closest variable to it in terms of index). +#[derive(Debug, Copy, Clone)] +pub struct InDomainMedian; + +impl ValueSelector for InDomainMedian { + fn select_value( + &mut self, + context: &mut SelectionContext, + decision_variable: Var, + ) -> Predicate { + let values_in_domain = (context.lower_bound(decision_variable) + ..=context.upper_bound(decision_variable)) + .filter(|bound| context.contains(decision_variable, *bound)) + .collect::>(); + predicate!(decision_variable == values_in_domain[values_in_domain.len() / 2]) + } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } +} + +#[cfg(test)] +mod tests { + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::InDomainMedian; + use crate::value_selection::ValueSelector; + + #[test] + fn test_returns_correct_literal() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + + let mut selector = InDomainMedian; + + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 5)) + } + + #[test] + fn test_returns_correct_literal_no_median() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 10, None); + let mut selector = InDomainMedian; + + let _ = state + .post(predicate!(domain_id != 9)) + .expect("Expected posting the predicate to not result in an empty domain"); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 5)) + } + + #[test] + fn test_returns_correct_literal_removed_median() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 10, None); + let mut selector = InDomainMedian; + + let _ = state + .post(predicate!(domain_id != 5)) + .expect("Expected posting the predicate to not result in an empty domain"); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 6)) + } +} diff --git a/pumpkin-crates/core/src/branching/value_selection/in_domain_middle.rs b/pumpkin-crates/branching/src/value_selection/in_domain_middle.rs similarity index 54% rename from pumpkin-crates/core/src/branching/value_selection/in_domain_middle.rs rename to pumpkin-crates/branching/src/value_selection/in_domain_middle.rs index eca1151a0..39befd17b 100644 --- a/pumpkin-crates/core/src/branching/value_selection/in_domain_middle.rs +++ b/pumpkin-crates/branching/src/value_selection/in_domain_middle.rs @@ -1,12 +1,13 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; +use pumpkin_core::asserts::pumpkin_assert_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::IntegerVariable; + #[cfg(doc)] -use crate::branching::value_selection::InDomainMedian; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::IntegerVariable; -use crate::predicate; -use crate::pumpkin_assert_simple; +use crate::value_selection::InDomainMedian; +use crate::value_selection::ValueSelector; /// A [`ValueSelector`] which selects the middle value in the domain (or if this value is already /// assigned then the closest variable to it). @@ -54,94 +55,97 @@ impl ValueSelector for InDomainMiddle { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::InDomainMiddle; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::InDomainMiddle; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainMiddle; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 5)) + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 5)) } #[test] fn test_returns_correct_literal_no_middle() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(1, 10)]); - let mut test_rng = TestRandom::default(); - let domain_ids = assignments.get_domains().collect::>(); + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 10, None); let mut selector = InDomainMiddle; - let _ = assignments.post_predicate( - predicate!(domain_ids[0] != 5), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(domain_id != 5)) + .expect("Expected posting the predicate to not result in an empty domain"); - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 4)) + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 4)) } #[test] fn test_returns_correct_literal_size_two_domain() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(1, 2)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 2, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainMiddle; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 1)) + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 1)) } #[test] fn test_returns_correct_literal_size_three_domain() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(1, 3)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 3, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainMiddle; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 2)) + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 2)) } #[test] fn test_returns_correct_literal_negative_lower_bound() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(-5, 5)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(-5, 5, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainMiddle; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 0)) + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == 0)) } #[test] fn test_returns_correct_literal_negative_upper_bound() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(-10, -5)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(-10, -5, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainMiddle; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == -8)) + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id == -8)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/in_domain_min.rs b/pumpkin-crates/branching/src/value_selection/in_domain_min.rs similarity index 50% rename from pumpkin-crates/core/src/branching/value_selection/in_domain_min.rs rename to pumpkin-crates/branching/src/value_selection/in_domain_min.rs index c9012d8f4..9135ae3a2 100644 --- a/pumpkin-crates/core/src/branching/value_selection/in_domain_min.rs +++ b/pumpkin-crates/branching/src/value_selection/in_domain_min.rs @@ -1,9 +1,10 @@ +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::IntegerVariable; + use super::ValueSelector; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::IntegerVariable; -use crate::predicate; /// [`ValueSelector`] which chooses to assign the provided variable to its lowest-bound. #[derive(Debug, Copy, Clone)] @@ -25,22 +26,25 @@ impl ValueSelector for InDomainMin { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::InDomainMin; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::InDomainMin; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainMin; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] <= 0)) + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id <= 0)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/in_domain_random.rs b/pumpkin-crates/branching/src/value_selection/in_domain_random.rs similarity index 67% rename from pumpkin-crates/core/src/branching/value_selection/in_domain_random.rs rename to pumpkin-crates/branching/src/value_selection/in_domain_random.rs index f6c3a2d92..ab60dfafd 100644 --- a/pumpkin-crates/core/src/branching/value_selection/in_domain_random.rs +++ b/pumpkin-crates/branching/src/value_selection/in_domain_random.rs @@ -1,10 +1,11 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::predicate; -use crate::variables::Literal; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; +use pumpkin_core::variables::Literal; + +use crate::value_selection::ValueSelector; /// A [`ValueSelector`] which assigns to a random value in the domain. #[derive(Debug, Clone, Copy)] @@ -59,26 +60,29 @@ impl ValueSelector for InDomainRandom { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::InDomainRandom; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::InDomainRandom; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_random = TestRandom { usizes: vec![3], ..Default::default() }; - let mut context = SelectionContext::new(&assignments, &mut test_random); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_random); let mut selector = InDomainRandom; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 3)) + assert_eq!(selected_predicate, predicate!(domain_id == 3)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/in_domain_split.rs b/pumpkin-crates/branching/src/value_selection/in_domain_split.rs similarity index 63% rename from pumpkin-crates/core/src/branching/value_selection/in_domain_split.rs rename to pumpkin-crates/branching/src/value_selection/in_domain_split.rs index 22ec66d58..1760f8e77 100644 --- a/pumpkin-crates/core/src/branching/value_selection/in_domain_split.rs +++ b/pumpkin-crates/branching/src/value_selection/in_domain_split.rs @@ -1,10 +1,11 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::IntegerVariable; -use crate::predicate; -use crate::pumpkin_assert_advanced; +use pumpkin_core::asserts::pumpkin_assert_advanced; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::IntegerVariable; + +use crate::value_selection::ValueSelector; /// A [`ValueSelector`] which splits the domain in half (based on the lower-bound and upper-bound, /// disregarding holes) and removes the upper-half from the domain. @@ -50,37 +51,41 @@ impl InDomainSplit { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::InDomainSplit; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::InDomainSplit; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainSplit; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] <= 5)) + assert_eq!(selected_predicate, predicate!(domain_id <= 5)) } #[test] fn test_domain_of_size_two() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(1, 2)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 2, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = InDomainSplit; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] <= 1)) + assert_eq!(selected_predicate, predicate!(domain_id <= 1)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/in_domain_split_random.rs b/pumpkin-crates/branching/src/value_selection/in_domain_split_random.rs similarity index 61% rename from pumpkin-crates/core/src/branching/value_selection/in_domain_split_random.rs rename to pumpkin-crates/branching/src/value_selection/in_domain_split_random.rs index 5275c7935..c56193ddd 100644 --- a/pumpkin-crates/core/src/branching/value_selection/in_domain_split_random.rs +++ b/pumpkin-crates/branching/src/value_selection/in_domain_split_random.rs @@ -1,9 +1,10 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::predicate; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + +use crate::value_selection::ValueSelector; /// A [`ValueSelector`] which bisects the domain in the middle (between the lower-bound and /// lower-bound, disregarding holes), randomly selecting whether to exclude the lower-half or the @@ -39,27 +40,30 @@ impl ValueSelector for InDomainSplitRandom { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::InDomainSplitRandom; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::InDomainSplitRandom; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_random = TestRandom { usizes: vec![5], bools: vec![true], ..Default::default() }; - let mut context = SelectionContext::new(&assignments, &mut test_random); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_random); let mut selector = InDomainSplitRandom; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] >= 5)) + assert_eq!(selected_predicate, predicate!(domain_id >= 5)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/mod.rs b/pumpkin-crates/branching/src/value_selection/mod.rs similarity index 100% rename from pumpkin-crates/core/src/branching/value_selection/mod.rs rename to pumpkin-crates/branching/src/value_selection/mod.rs diff --git a/pumpkin-crates/branching/src/value_selection/out_domain_max.rs b/pumpkin-crates/branching/src/value_selection/out_domain_max.rs new file mode 100644 index 000000000..a79c82065 --- /dev/null +++ b/pumpkin-crates/branching/src/value_selection/out_domain_max.rs @@ -0,0 +1,50 @@ +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + +use crate::value_selection::ValueSelector; + +/// A [`ValueSelector`] which excludes the largest value from the domain. +#[derive(Debug, Copy, Clone)] +pub struct OutDomainMax; + +impl ValueSelector for OutDomainMax { + fn select_value( + &mut self, + context: &mut SelectionContext, + decision_variable: DomainId, + ) -> Predicate { + predicate!(decision_variable <= context.upper_bound(decision_variable) - 1) + } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } +} + +#[cfg(test)] +mod tests { + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::OutDomainMax; + use crate::value_selection::ValueSelector; + + #[test] + fn test_returns_correct_literal() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + + let mut selector = OutDomainMax; + + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id <= 9)) + } +} diff --git a/pumpkin-crates/branching/src/value_selection/out_domain_median.rs b/pumpkin-crates/branching/src/value_selection/out_domain_median.rs new file mode 100644 index 000000000..adcd8985f --- /dev/null +++ b/pumpkin-crates/branching/src/value_selection/out_domain_median.rs @@ -0,0 +1,70 @@ +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + +use crate::value_selection::ValueSelector; + +/// A [`ValueSelector`] which excludes the median value from the domain. +#[derive(Debug, Copy, Clone)] +pub struct OutDomainMedian; + +impl ValueSelector for OutDomainMedian { + fn select_value( + &mut self, + context: &mut SelectionContext, + decision_variable: DomainId, + ) -> Predicate { + let values_in_domain = (context.lower_bound(decision_variable) + ..=context.upper_bound(decision_variable)) + .filter(|bound| context.contains(decision_variable, *bound)) + .collect::>(); + predicate!(decision_variable != values_in_domain[values_in_domain.len() / 2]) + } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } +} + +#[cfg(test)] +mod tests { + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::OutDomainMedian; + use crate::value_selection::ValueSelector; + + #[test] + fn test_returns_correct_literal() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + + let mut selector = OutDomainMedian; + + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id != 5)) + } + + #[test] + fn test_returns_correct_literal_no_median() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 10, None); + let mut selector = OutDomainMedian; + + let _ = state + .post(predicate!(domain_id != 9)) + .expect("Expected posting the predicate to not result in an empty domain"); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id != 5)) + } +} diff --git a/pumpkin-crates/branching/src/value_selection/out_domain_min.rs b/pumpkin-crates/branching/src/value_selection/out_domain_min.rs new file mode 100644 index 000000000..c5ce030e2 --- /dev/null +++ b/pumpkin-crates/branching/src/value_selection/out_domain_min.rs @@ -0,0 +1,50 @@ +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + +use crate::value_selection::ValueSelector; + +/// A [`ValueSelector`] which excludes the smallest value from the domain. +#[derive(Debug, Copy, Clone)] +pub struct OutDomainMin; + +impl ValueSelector for OutDomainMin { + fn select_value( + &mut self, + context: &mut SelectionContext, + decision_variable: DomainId, + ) -> Predicate { + predicate!(decision_variable >= context.lower_bound(decision_variable) + 1) + } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } +} + +#[cfg(test)] +mod tests { + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::OutDomainMin; + use crate::value_selection::ValueSelector; + + #[test] + fn test_returns_correct_literal() { + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + + let mut test_rng = TestRandom::default(); + let mut context = SelectionContext::new(&state, &mut test_rng); + + let mut selector = OutDomainMin; + + let selected_predicate = selector.select_value(&mut context, domain_id); + assert_eq!(selected_predicate, predicate!(domain_id >= 1)) + } +} diff --git a/pumpkin-crates/core/src/branching/value_selection/out_domain_random.rs b/pumpkin-crates/branching/src/value_selection/out_domain_random.rs similarity index 60% rename from pumpkin-crates/core/src/branching/value_selection/out_domain_random.rs rename to pumpkin-crates/branching/src/value_selection/out_domain_random.rs index 238e7bc9d..6191035c0 100644 --- a/pumpkin-crates/core/src/branching/value_selection/out_domain_random.rs +++ b/pumpkin-crates/branching/src/value_selection/out_domain_random.rs @@ -1,9 +1,10 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::predicate; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + +use crate::value_selection::ValueSelector; /// A [`ValueSelector`] which excludes a random value from the domain. #[derive(Debug, Clone, Copy)] @@ -36,26 +37,29 @@ impl ValueSelector for OutDomainRandom { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::OutDomainRandom; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::OutDomainRandom; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_random = TestRandom { usizes: vec![3], ..Default::default() }; - let mut context = SelectionContext::new(&assignments, &mut test_random); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_random); let mut selector = OutDomainRandom; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] != 3)) + assert_eq!(selected_predicate, predicate!(domain_id != 3)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/random_splitter.rs b/pumpkin-crates/branching/src/value_selection/random_splitter.rs similarity index 71% rename from pumpkin-crates/core/src/branching/value_selection/random_splitter.rs rename to pumpkin-crates/branching/src/value_selection/random_splitter.rs index c5b43b74c..82068c650 100644 --- a/pumpkin-crates/core/src/branching/value_selection/random_splitter.rs +++ b/pumpkin-crates/branching/src/value_selection/random_splitter.rs @@ -1,9 +1,10 @@ -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::predicate; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + +use crate::value_selection::ValueSelector; /// A [`ValueSelector`] which splits the domain in a random manner (between the lower-bound and /// lower-bound, disregarding holes), randomly selecting whether to exclude the lower-half or the @@ -54,27 +55,30 @@ impl ValueSelector for RandomSplitter { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::RandomSplitter; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::value_selection::RandomSplitter; + use crate::value_selection::ValueSelector; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_random = TestRandom { integers: vec![2], bools: vec![true], ..Default::default() }; - let mut context = SelectionContext::new(&assignments, &mut test_random); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_random); let mut selector = RandomSplitter; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] >= 2)) + assert_eq!(selected_predicate, predicate!(domain_id >= 2)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/reverse_in_domain_split.rs b/pumpkin-crates/branching/src/value_selection/reverse_in_domain_split.rs similarity index 64% rename from pumpkin-crates/core/src/branching/value_selection/reverse_in_domain_split.rs rename to pumpkin-crates/branching/src/value_selection/reverse_in_domain_split.rs index 0220e9a1b..11d1b54a8 100644 --- a/pumpkin-crates/core/src/branching/value_selection/reverse_in_domain_split.rs +++ b/pumpkin-crates/branching/src/value_selection/reverse_in_domain_split.rs @@ -1,10 +1,11 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::IntegerVariable; -use crate::predicate; -use crate::pumpkin_assert_advanced; +use pumpkin_core::asserts::pumpkin_assert_advanced; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicate; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::IntegerVariable; + +use crate::value_selection::ValueSelector; /// A [`ValueSelector`] which splits the domain in half (based on the lower-bound and upper-bound, /// disregarding holes) and removes the lower-half from the domain. @@ -41,34 +42,38 @@ impl ValueSelector for ReverseInDomainSplit { #[cfg(test)] mod tests { + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; + use crate::testing::TestRandom; #[test] fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(0, 10, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = ReverseInDomainSplit; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] >= 5)) + assert_eq!(selected_predicate, predicate!(domain_id >= 5)) } #[test] fn test_domain_of_size_two() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(1, 2)]); + let mut state = State::default(); + let domain_id = state.new_interval_variable(1, 2, None); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut selector = ReverseInDomainSplit; - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); + let selected_predicate = selector.select_value(&mut context, domain_id); - assert_eq!(selected_predicate, predicate!(domain_ids[0] >= 2)) + assert_eq!(selected_predicate, predicate!(domain_id >= 2)) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/value_selector.rs b/pumpkin-crates/branching/src/value_selection/value_selector.rs similarity index 84% rename from pumpkin-crates/core/src/branching/value_selection/value_selector.rs rename to pumpkin-crates/branching/src/value_selection/value_selector.rs index ee2292da6..027be1fbc 100644 --- a/pumpkin-crates/core/src/branching/value_selection/value_selector.rs +++ b/pumpkin-crates/branching/src/value_selection/value_selector.rs @@ -1,20 +1,19 @@ use std::fmt::Debug; -use crate::basic_types::SolutionReference; #[cfg(doc)] -use crate::branching::Brancher; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -#[cfg(doc)] -use crate::branching::branchers::dynamic_brancher::DynamicBrancher; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::results::SolutionReference; +use pumpkin_core::variables::DomainId; + #[cfg(doc)] -use crate::branching::value_selection::InDomainMin; +use crate::branching::dynamic_brancher::DynamicBrancher; #[cfg(doc)] -use crate::branching::value_selection::InDomainRandom; +use crate::value_selection::InDomainMin; #[cfg(doc)] -use crate::engine::ConstraintSatisfactionSolver; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; +use crate::value_selection::InDomainRandom; /// A trait containing the interface for [`ValueSelector`]s, /// specifying the appropriate hooks into the solver and the methods required for selecting a value diff --git a/pumpkin-crates/core/src/branching/variable_selection/anti_first_fail.rs b/pumpkin-crates/branching/src/variable_selection/anti_first_fail.rs similarity index 69% rename from pumpkin-crates/core/src/branching/variable_selection/anti_first_fail.rs rename to pumpkin-crates/branching/src/variable_selection/anti_first_fail.rs index 596a50983..766a34210 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/anti_first_fail.rs +++ b/pumpkin-crates/branching/src/variable_selection/anti_first_fail.rs @@ -1,13 +1,13 @@ use log::warn; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::tie_breaking::Direction; -use crate::branching::tie_breaking::InOrderTieBreaker; -use crate::branching::tie_breaking::TieBreaker; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::variables::DomainId; -use crate::pumpkin_assert_eq_simple; +use crate::tie_breaking::Direction; +use crate::tie_breaking::InOrderTieBreaker; +use crate::tie_breaking::TieBreaker; +use crate::variable_selection::VariableSelector; /// A [`VariableSelector`] which selects the variable with the largest domain (based on the /// lower-bound and upper-bound, disregarding holes). @@ -81,36 +81,38 @@ impl> VariableSelector #[cfg(test)] mod tests { + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::AntiFirstFail; - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::variable_selection::VariableSelector; - use crate::predicate; + use crate::testing::TestRandom; + use crate::variable_selection::VariableSelector; #[test] fn test_correctly_selected() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 10), (5, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (5, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom::default(); - let integer_variables = assignments.get_domains().collect::>(); let mut strategy = AntiFirstFail::new(&integer_variables); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); } - let _ = assignments.post_predicate( - predicate!(integer_variables[1] >= 15), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(integer_variables[1] >= 15)) + .expect("Expected posting the predicate to not result in an empty domain"); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]); @@ -118,10 +120,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = AntiFirstFail::new(&integer_variables); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/dynamic_variable_selector.rs b/pumpkin-crates/branching/src/variable_selection/dynamic_variable_selector.rs similarity index 84% rename from pumpkin-crates/core/src/branching/variable_selection/dynamic_variable_selector.rs rename to pumpkin-crates/branching/src/variable_selection/dynamic_variable_selector.rs index bbd46205e..3f0c727cd 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/dynamic_variable_selector.rs +++ b/pumpkin-crates/branching/src/variable_selection/dynamic_variable_selector.rs @@ -1,12 +1,13 @@ use std::fmt::Debug; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + use super::VariableSelector; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; #[cfg(doc)] -use crate::branching::branchers::dynamic_brancher::DynamicBrancher; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; +use crate::branching::dynamic_brancher::DynamicBrancher; /// Similar to [`DynamicBrancher`], this is a pass-along structure which should be used when a /// [`Sized`] object is required. diff --git a/pumpkin-crates/core/src/branching/variable_selection/first_fail.rs b/pumpkin-crates/branching/src/variable_selection/first_fail.rs similarity index 70% rename from pumpkin-crates/core/src/branching/variable_selection/first_fail.rs rename to pumpkin-crates/branching/src/variable_selection/first_fail.rs index d1d38cb95..0e89dc86c 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/first_fail.rs +++ b/pumpkin-crates/branching/src/variable_selection/first_fail.rs @@ -1,13 +1,13 @@ use log::warn; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::tie_breaking::Direction; -use crate::branching::tie_breaking::InOrderTieBreaker; -use crate::branching::tie_breaking::TieBreaker; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::variables::DomainId; -use crate::pumpkin_assert_eq_simple; +use crate::tie_breaking::Direction; +use crate::tie_breaking::InOrderTieBreaker; +use crate::tie_breaking::TieBreaker; +use crate::variable_selection::VariableSelector; /// A [`VariableSelector`] which selects the variable with the smallest domain (based on the /// lower-bound and upper-bound, disregarding holes). @@ -82,34 +82,36 @@ where #[cfg(test)] mod tests { + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; - use crate::predicate; + use crate::testing::TestRandom; #[test] fn test_correctly_selected() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 10), (5, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (5, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom::default(); - let integer_variables = assignments.get_domains().collect::>(); let mut strategy = FirstFail::new(&integer_variables); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]); } - let _ = assignments.post_predicate( - predicate!(integer_variables[1] >= 15), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(integer_variables[1] >= 15)) + .expect("Expected posting the predicate to not result in an empty domain"); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); @@ -117,10 +119,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = FirstFail::new(&integer_variables); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/input_order.rs b/pumpkin-crates/branching/src/variable_selection/input_order.rs similarity index 63% rename from pumpkin-crates/core/src/branching/variable_selection/input_order.rs rename to pumpkin-crates/branching/src/variable_selection/input_order.rs index a8ccadf0c..a331719b8 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/input_order.rs +++ b/pumpkin-crates/branching/src/variable_selection/input_order.rs @@ -1,10 +1,10 @@ use log::warn; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; +use pumpkin_core::variables::Literal; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::variables::DomainId; -use crate::variables::Literal; +use crate::variable_selection::VariableSelector; /// A [`VariableSelector`] which selects the first variable which is not fixed given the order in /// the provided list. @@ -56,34 +56,36 @@ impl VariableSelector for InputOrder { #[cfg(test)] mod tests { + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; - use crate::predicate; + use crate::testing::TestRandom; #[test] fn test_correctly_selected() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 10), (5, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (5, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom::default(); - let integer_variables = assignments.get_domains().collect::>(); let mut strategy = InputOrder::new(&integer_variables); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]); } - let _ = assignments.post_predicate( - predicate!(integer_variables[0] == 0), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(integer_variables[0] == 0)) + .expect("Expected posting the predicate to not result in an empty domain"); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); @@ -91,10 +93,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = InputOrder::new(&integer_variables); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/largest.rs b/pumpkin-crates/branching/src/variable_selection/largest.rs similarity index 70% rename from pumpkin-crates/core/src/branching/variable_selection/largest.rs rename to pumpkin-crates/branching/src/variable_selection/largest.rs index fe1b301ec..899639912 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/largest.rs +++ b/pumpkin-crates/branching/src/variable_selection/largest.rs @@ -1,13 +1,13 @@ use log::warn; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::tie_breaking::Direction; -use crate::branching::tie_breaking::InOrderTieBreaker; -use crate::branching::tie_breaking::TieBreaker; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::variables::DomainId; -use crate::pumpkin_assert_eq_simple; +use crate::tie_breaking::Direction; +use crate::tie_breaking::InOrderTieBreaker; +use crate::tie_breaking::TieBreaker; +use crate::variable_selection::VariableSelector; /// A [`VariableSelector`] which selects the variable with the largest value in its domain. /// @@ -85,34 +85,36 @@ where #[cfg(test)] mod tests { + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; - use crate::predicate; + use crate::testing::TestRandom; #[test] fn test_correctly_selected() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 10), (5, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (5, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom::default(); - let integer_variables = assignments.get_domains().collect::>(); let mut strategy = Largest::new(&integer_variables); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); } - let _ = assignments.post_predicate( - predicate!(integer_variables[1] <= 9), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(integer_variables[1] <= 9)) + .expect("Expected posting the predicate to not result in an empty domain"); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]); @@ -120,10 +122,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = Largest::new(&integer_variables); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/max_regret.rs b/pumpkin-crates/branching/src/variable_selection/max_regret.rs similarity index 69% rename from pumpkin-crates/core/src/branching/variable_selection/max_regret.rs rename to pumpkin-crates/branching/src/variable_selection/max_regret.rs index 5609b7183..156a36dfd 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/max_regret.rs +++ b/pumpkin-crates/branching/src/variable_selection/max_regret.rs @@ -1,14 +1,14 @@ use log::warn; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::asserts::pumpkin_assert_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::tie_breaking::Direction; -use crate::branching::tie_breaking::InOrderTieBreaker; -use crate::branching::tie_breaking::TieBreaker; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::variables::DomainId; -use crate::pumpkin_assert_eq_simple; -use crate::pumpkin_assert_simple; +use crate::tie_breaking::Direction; +use crate::tie_breaking::InOrderTieBreaker; +use crate::tie_breaking::TieBreaker; +use crate::variable_selection::VariableSelector; /// A [`VariableSelector`] which selects the variable with the largest difference between the two /// smallest values in its domain. @@ -99,45 +99,43 @@ where #[cfg(test)] mod tests { + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; - use crate::predicate; + use crate::testing::TestRandom; #[test] fn test_correctly_selected() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 10), (5, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (5, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom::default(); - let integer_variables = assignments.get_domains().collect::>(); let mut strategy = MaxRegret::new(&integer_variables); - let _ = assignments.post_predicate( - predicate!(integer_variables[1] != 6), - None, - &mut notification_engine, - ); + let _ = state + .post(predicate!(integer_variables[1] != 6)) + .expect("Expected posting the predicate to not result in an empty domain"); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); } - let _ = assignments.post_predicate( - predicate!(integer_variables[0] != 1), - None, - &mut notification_engine, - ); - let _ = assignments.post_predicate( - predicate!(integer_variables[0] != 2), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(integer_variables[0] != 1)) + .expect("Expected posting the predicate to not result in an empty domain"); + let _ = state + .post(predicate!(integer_variables[0] != 2)) + .expect("Expected posting the predicate to not result in an empty domain"); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]) @@ -145,10 +143,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = MaxRegret::new(&integer_variables); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/mod.rs b/pumpkin-crates/branching/src/variable_selection/mod.rs similarity index 100% rename from pumpkin-crates/core/src/branching/variable_selection/mod.rs rename to pumpkin-crates/branching/src/variable_selection/mod.rs diff --git a/pumpkin-crates/core/src/branching/variable_selection/most_constrained.rs b/pumpkin-crates/branching/src/variable_selection/most_constrained.rs similarity index 70% rename from pumpkin-crates/core/src/branching/variable_selection/most_constrained.rs rename to pumpkin-crates/branching/src/variable_selection/most_constrained.rs index 723a3de15..4bc8eb12c 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/most_constrained.rs +++ b/pumpkin-crates/branching/src/variable_selection/most_constrained.rs @@ -1,17 +1,17 @@ use std::cmp::Ordering; use log::warn; - -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::tie_breaking::Direction; -use crate::branching::tie_breaking::InOrderTieBreaker; -use crate::branching::tie_breaking::TieBreaker; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; + +use crate::tie_breaking::Direction; +use crate::tie_breaking::InOrderTieBreaker; +use crate::tie_breaking::TieBreaker; #[cfg(doc)] -use crate::branching::variable_selection::FirstFail; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::variables::DomainId; -use crate::pumpkin_assert_eq_simple; +use crate::variable_selection::FirstFail; +use crate::variable_selection::VariableSelector; /// A [`VariableSelector`] which selects the variable with the smallest domain (similar to /// [`FirstFail`]). @@ -99,32 +99,35 @@ where #[cfg(test)] mod tests { + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; - use crate::predicate; + use crate::testing::TestRandom; #[test] fn test_correctly_selected() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 10), (15, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (15, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom::default(); - let integer_variables = assignments.get_domains().collect::>(); let mut strategy = MostConstrained::new(&integer_variables, &[2, 1]); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); } - let _ = assignments.post_predicate( - predicate!(integer_variables[0] <= 2), - None, - &mut notification_engine, - ); - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(integer_variables[0] <= 2)) + .expect("Expected posting the predicate to not result in an empty domain"); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]); @@ -132,10 +135,16 @@ mod tests { #[test] fn test_correctly_selected_tie() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10), (10, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (10, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = MostConstrained::new(&integer_variables, &[2, 1]); let selected = strategy.select_variable(&mut context); @@ -145,10 +154,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = MostConstrained::new(&integer_variables, &[1, 2]); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/occurrence.rs b/pumpkin-crates/branching/src/variable_selection/occurrence.rs similarity index 69% rename from pumpkin-crates/core/src/branching/variable_selection/occurrence.rs rename to pumpkin-crates/branching/src/variable_selection/occurrence.rs index a72ad5ec6..4bf99b362 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/occurrence.rs +++ b/pumpkin-crates/branching/src/variable_selection/occurrence.rs @@ -1,13 +1,13 @@ use log::warn; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::tie_breaking::Direction; -use crate::branching::tie_breaking::InOrderTieBreaker; -use crate::branching::tie_breaking::TieBreaker; -use crate::branching::variable_selection::VariableSelector; -use crate::engine::variables::DomainId; -use crate::pumpkin_assert_eq_simple; +use crate::tie_breaking::Direction; +use crate::tie_breaking::InOrderTieBreaker; +use crate::tie_breaking::TieBreaker; +use crate::variable_selection::VariableSelector; /// A [`VariableSelector`] which selects the variable with the largest number of attached /// constraints (where the provided `num_occurrences` stores the number of @@ -65,15 +65,23 @@ where #[cfg(test)] mod tests { + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; + use crate::testing::TestRandom; #[test] fn test_correctly_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10), (10, 20)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (10, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = Occurrence::new(&integer_variables, &[2, 1]); let selected = strategy.select_variable(&mut context); @@ -83,10 +91,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = Occurrence::new(&integer_variables, &[1, 2]); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/proportional_domain_size.rs b/pumpkin-crates/branching/src/variable_selection/proportional_domain_size.rs similarity index 93% rename from pumpkin-crates/core/src/branching/variable_selection/proportional_domain_size.rs rename to pumpkin-crates/branching/src/variable_selection/proportional_domain_size.rs index e39d825b0..c02b5da09 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/proportional_domain_size.rs +++ b/pumpkin-crates/branching/src/variable_selection/proportional_domain_size.rs @@ -1,8 +1,9 @@ +use pumpkin_core::asserts::pumpkin_assert_extreme; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; + use super::VariableSelector; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::pumpkin_assert_extreme; -use crate::variables::DomainId; #[derive(Debug)] pub struct ProportionalDomainSize { diff --git a/pumpkin-crates/core/src/branching/variable_selection/random.rs b/pumpkin-crates/branching/src/variable_selection/random.rs similarity index 57% rename from pumpkin-crates/core/src/branching/variable_selection/random.rs rename to pumpkin-crates/branching/src/variable_selection/random.rs index dbb0441f7..6c618a354 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/random.rs +++ b/pumpkin-crates/branching/src/variable_selection/random.rs @@ -1,8 +1,9 @@ +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::containers::SparseSet; +use pumpkin_core::variables::DomainId; + use super::VariableSelector; -use crate::branching::BrancherEvent; -use crate::branching::SelectionContext; -use crate::containers::SparseSet; -use crate::variables::DomainId; /// A [`VariableSelector`] which selects a random unfixed variable. #[derive(Debug)] @@ -67,24 +68,30 @@ impl VariableSelector for RandomSelector { #[cfg(test)] mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::variable_selection::RandomSelector; - use crate::branching::variable_selection::VariableSelector; - use crate::predicate; + use pumpkin_core::branching::SelectionContext; + use pumpkin_core::predicate; + use pumpkin_core::state::State; + + use crate::testing::TestRandom; + use crate::variable_selection::RandomSelector; + use crate::variable_selection::VariableSelector; #[test] fn test_selects_randomly() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10), (5, 20), (1, 3)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (5, 20), (1, 3)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom { usizes: vec![1], ..Default::default() }; - let integer_variables = assignments.get_domains().collect::>(); - let mut strategy = RandomSelector::new(assignments.get_domains()); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let mut strategy = RandomSelector::new(integer_variables.iter().copied()); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); @@ -92,16 +99,20 @@ mod tests { #[test] fn test_selects_randomly_not_unfixed() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10), (5, 5), (1, 3)]); + let mut state = State::default(); + let integer_variables = [(0, 10), (5, 5), (1, 3)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom { usizes: vec![1, 0], ..Default::default() }; - let integer_variables = assignments.get_domains().collect::>(); - let mut strategy = RandomSelector::new(assignments.get_domains()); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let mut strategy = RandomSelector::new(integer_variables.iter().copied()); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]); @@ -109,55 +120,60 @@ mod tests { #[test] fn test_select_nothing_if_all_fixed() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 0), (5, 5), (1, 1)]); + let mut state = State::default(); + let integer_variables = [(0, 0), (5, 5), (1, 1)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom { usizes: vec![1, 0, 0], ..Default::default() }; - let mut strategy = RandomSelector::new(assignments.get_domains()); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let mut strategy = RandomSelector::new(integer_variables); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_none()); } #[test] fn test_select_unfixed_variable_after_fixing() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(0, 0), (5, 7), (1, 1)]); + let mut state = State::default(); + let integer_variables = [(0, 0), (5, 7), (1, 1)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom { usizes: vec![2, 0, 0, 0, 0], ..Default::default() }; - let integer_variables = assignments.get_domains().collect::>(); - let mut strategy = RandomSelector::new(assignments.get_domains()); + let mut strategy = RandomSelector::new(integer_variables.iter().copied()); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); } - assignments.new_checkpoint(); - let _ = assignments.post_predicate( - predicate!(integer_variables[1] >= 7), - None, - &mut notification_engine, - ); + state.new_checkpoint(); + let _ = state + .post(predicate!(integer_variables[1] >= 7)) + .expect("Expected posting the predicate to not result in an empty domain"); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_none()); } - let _ = assignments.synchronise(0, &mut notification_engine); + let _ = state.restore_to(0); strategy.on_unassign_integer(integer_variables[1], 7); - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); diff --git a/pumpkin-crates/core/src/branching/variable_selection/smallest.rs b/pumpkin-crates/branching/src/variable_selection/smallest.rs similarity index 70% rename from pumpkin-crates/core/src/branching/variable_selection/smallest.rs rename to pumpkin-crates/branching/src/variable_selection/smallest.rs index 3e4c96e2e..4c4e10631 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/smallest.rs +++ b/pumpkin-crates/branching/src/variable_selection/smallest.rs @@ -1,13 +1,13 @@ use log::warn; +use pumpkin_core::asserts::pumpkin_assert_eq_simple; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::variables::DomainId; use super::VariableSelector; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::tie_breaking::Direction; -use crate::branching::tie_breaking::InOrderTieBreaker; -use crate::branching::tie_breaking::TieBreaker; -use crate::engine::variables::DomainId; -use crate::pumpkin_assert_eq_simple; +use crate::tie_breaking::Direction; +use crate::tie_breaking::InOrderTieBreaker; +use crate::tie_breaking::TieBreaker; /// A [`VariableSelector`] which selects the variable with the smallest value in its domain. /// @@ -81,31 +81,34 @@ where #[cfg(test)] mod tests { + use pumpkin_core::predicate; + use pumpkin_core::state::State; + use super::*; - use crate::basic_types::tests::TestRandom; - use crate::predicate; + use crate::testing::TestRandom; #[test] fn test_correctly_selected() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(11, 15), (10, 20)]); - let integer_variables = assignments.get_domains().collect::>(); - let mut strategy = Smallest::new(&integer_variables); + let mut state = State::default(); + let integer_variables = [(11, 15), (10, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); let mut test_rng = TestRandom::default(); + let mut strategy = Smallest::new(&integer_variables); { - let mut context = SelectionContext::new(&assignments, &mut test_rng); - + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[1]); } - let _ = assignments.post_predicate( - predicate!(integer_variables[1] >= 15), - None, - &mut notification_engine, - ); - let mut context = SelectionContext::new(&assignments, &mut test_rng); + let _ = state + .post(predicate!(integer_variables[1] >= 15)) + .expect("Expected posting the predicate to not result in an empty domain"); + let mut context = SelectionContext::new(&state, &mut test_rng); let selected = strategy.select_variable(&mut context); assert!(selected.is_some()); assert_eq!(selected.unwrap(), integer_variables[0]); @@ -113,10 +116,16 @@ mod tests { #[test] fn fixed_variables_are_not_selected() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(10, 10), (20, 20)]); + let mut state = State::default(); + let integer_variables = [(10, 10), (20, 20)] + .into_iter() + .map(|(lower_bound, upper_bound)| { + state.new_interval_variable(lower_bound, upper_bound, None) + }) + .collect::>(); + let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let integer_variables = context.get_domains().collect::>(); + let mut context = SelectionContext::new(&state, &mut test_rng); let mut strategy = Smallest::new(&integer_variables); let selected = strategy.select_variable(&mut context); diff --git a/pumpkin-crates/core/src/branching/variable_selection/variable_selector.rs b/pumpkin-crates/branching/src/variable_selection/variable_selector.rs similarity index 88% rename from pumpkin-crates/core/src/branching/variable_selection/variable_selector.rs rename to pumpkin-crates/branching/src/variable_selection/variable_selector.rs index 34fd85410..4499c15bf 100644 --- a/pumpkin-crates/core/src/branching/variable_selection/variable_selector.rs +++ b/pumpkin-crates/branching/src/variable_selection/variable_selector.rs @@ -1,17 +1,16 @@ use std::fmt::Debug; #[cfg(doc)] -use crate::branching::Brancher; -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -#[cfg(doc)] -use crate::branching::branchers::dynamic_brancher::DynamicBrancher; +use pumpkin_core::branching::Brancher; +use pumpkin_core::branching::BrancherEvent; +use pumpkin_core::branching::SelectionContext; +use pumpkin_core::predicates::Predicate; +use pumpkin_core::variables::DomainId; + #[cfg(doc)] -use crate::branching::variable_selection::Smallest; +use crate::branching::dynamic_brancher::DynamicBrancher; #[cfg(doc)] -use crate::engine::ConstraintSatisfactionSolver; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; +use crate::variable_selection::Smallest; /// A trait containing the interface for [`VariableSelector`]s, /// specifying the appropriate hooks into the solver and the methods required for selecting diff --git a/pumpkin-crates/core/src/api/mod.rs b/pumpkin-crates/core/src/api/mod.rs index 8d66f9873..e761103ab 100644 --- a/pumpkin-crates/core/src/api/mod.rs +++ b/pumpkin-crates/core/src/api/mod.rs @@ -51,6 +51,7 @@ pub mod variables { use crate::Solver; pub use crate::engine::Reason; pub use crate::engine::variables::AffineView; + pub use crate::engine::variables::DomainGeneratorIterator; pub use crate::engine::variables::DomainId; pub use crate::engine::variables::IntegerVariable; pub use crate::engine::variables::Literal; @@ -104,6 +105,7 @@ pub mod predicates { //! //! In general, these [`Predicate`]s are used to represent propagations, explanations or //! decisions. + pub use crate::basic_types::DeletablePredicateIdGenerator; pub use crate::basic_types::PredicateIdGenerator; pub use crate::basic_types::PropositionalConjunction; pub use crate::engine::Lbd; diff --git a/pumpkin-crates/core/src/api/solver.rs b/pumpkin-crates/core/src/api/solver.rs index 9e9401516..f7dd7f41a 100644 --- a/pumpkin-crates/core/src/api/solver.rs +++ b/pumpkin-crates/core/src/api/solver.rs @@ -7,14 +7,6 @@ use crate::basic_types::CSPSolverExecutionFlag; use crate::branching::Brancher; use crate::branching::BrancherEvent; use crate::branching::SelectionContext; -use crate::branching::branchers::autonomous_search::AutonomousSearch; -use crate::branching::branchers::independent_variable_value_brancher::IndependentVariableValueBrancher; -use crate::branching::value_selection::RandomSplitter; -#[cfg(doc)] -use crate::branching::value_selection::ValueSelector; -use crate::branching::variable_selection::RandomSelector; -#[cfg(doc)] -use crate::branching::variable_selection::VariableSelector; use crate::conflict_resolving::ConflictAnalysisContext; use crate::conflict_resolving::ConflictResolver; use crate::constraints::ConstraintPoster; @@ -22,6 +14,7 @@ use crate::containers::HashSet; use crate::engine::ConstraintSatisfactionSolver; use crate::engine::predicates::predicate::Predicate; use crate::engine::termination::TerminationCondition; +use crate::engine::variables::DomainGeneratorIterator; use crate::engine::variables::DomainId; use crate::engine::variables::IntegerVariable; use crate::engine::variables::Literal; @@ -150,7 +143,10 @@ impl Solver { log_statistic_postfix(); } - pub fn get_solution_reference(&self) -> SolutionReference<'_> { + /// Internal accessor used by [`Satisfiable::solution`](crate::results::Satisfiable::solution); + /// a [`SolutionReference`] should only be obtained through the results of + /// [`Solver::satisfy`]/[`Solver::satisfy_under_assumptions`]/[`Solver::optimise`]. + pub(crate) fn get_solution_reference(&self) -> SolutionReference<'_> { self.satisfaction_solver.get_solution_reference() } @@ -598,11 +594,13 @@ impl Solver { } } -/// Default brancher implementation impl Solver { - /// Creates an instance of the [`DefaultBrancher`]. - pub fn default_brancher(&self) -> DefaultBrancher { - DefaultBrancher::default_over_all_variables(self.satisfaction_solver.assignments()) + /// Returns an iterator over the [`DomainId`]s which are currently defined on the [`Solver`]. + /// + /// This can be used to implement a [`Brancher`] which considers (a subset of) the variables of + /// the [`Solver`]. + pub fn get_domains(&self) -> DomainGeneratorIterator { + self.satisfaction_solver.get_domain_ids() } } @@ -645,18 +643,3 @@ impl Solver { } } } - -/// A brancher which makes use of VSIDS \[1\] and solution-based phase saving (both adapted for CP). -/// -/// If VSIDS does not contain any (unfixed) predicates then it will default to the -/// [`IndependentVariableValueBrancher`]. -/// -/// # Bibliography -/// \[1\] M. W. Moskewicz, C. F. Madigan, Y. Zhao, L. Zhang, and S. Malik, ‘Chaff: Engineering an -/// efficient SAT solver’, in Proceedings of the 38th annual Design Automation Conference, 2001. -/// -/// \[2\] E. Demirović, G. Chu, and P. J. Stuckey, ‘Solution-based phase saving for CP: A -/// value-selection heuristic to simulate local search behavior in complete solvers’, in the -/// proceedings of the Principles and Practice of Constraint Programming (CP 2018). -pub type DefaultBrancher = - AutonomousSearch>; diff --git a/pumpkin-crates/core/src/basic_types/mod.rs b/pumpkin-crates/core/src/basic_types/mod.rs index 45f437534..364d4d12c 100644 --- a/pumpkin-crates/core/src/basic_types/mod.rs +++ b/pumpkin-crates/core/src/basic_types/mod.rs @@ -13,7 +13,7 @@ mod trail; pub use constraint_operation_error::ConstraintOperationError; pub use csp_solver_execution_flag::CSPSolverExecutionFlag; pub use function::Function; -pub(crate) use predicate_id_generators::DeletablePredicateIdGenerator; +pub use predicate_id_generators::DeletablePredicateIdGenerator; pub use predicate_id_generators::PredicateId; pub use predicate_id_generators::PredicateIdGenerator; pub use propositional_conjunction::PropositionalConjunction; diff --git a/pumpkin-crates/core/src/basic_types/predicate_id_generators/deletable_predicate_id_generator.rs b/pumpkin-crates/core/src/basic_types/predicate_id_generators/deletable_predicate_id_generator.rs index 84170622a..a34ce611a 100644 --- a/pumpkin-crates/core/src/basic_types/predicate_id_generators/deletable_predicate_id_generator.rs +++ b/pumpkin-crates/core/src/basic_types/predicate_id_generators/deletable_predicate_id_generator.rs @@ -10,7 +10,7 @@ use crate::pumpkin_assert_moderate; /// This can be useful when numerous [`PredicateId`]s are defined, not all of which are required to /// be defined at the moment. #[derive(Debug, Default, Clone)] -pub(crate) struct DeletablePredicateIdGenerator { +pub struct DeletablePredicateIdGenerator { /// The value of the next id, provided there are no delete_ids that can be reused. next_id: u32, /// When an id is deleted, it gets stored here, so that the id can be reused in the future. @@ -22,7 +22,7 @@ pub(crate) struct DeletablePredicateIdGenerator { } impl DeletablePredicateIdGenerator { - pub(crate) fn has_id_for_predicate(&self, predicate: Predicate) -> bool { + pub fn has_id_for_predicate(&self, predicate: Predicate) -> bool { self.predicate_to_id.contains_key(&predicate) } @@ -39,7 +39,7 @@ impl DeletablePredicateIdGenerator { /// Returns an id for the predicate. If the predicate already has an id, its id is returned. /// Otherwise, a new id is create and returned. - pub(crate) fn get_id(&mut self, predicate: Predicate) -> PredicateId { + pub fn get_id(&mut self, predicate: Predicate) -> PredicateId { if let Some(id) = self.predicate_to_id.get(&predicate) { *id } else { @@ -51,11 +51,11 @@ impl DeletablePredicateIdGenerator { } } - pub(crate) fn get_predicate(&self, id: PredicateId) -> Option { + pub fn get_predicate(&self, id: PredicateId) -> Option { self.id_to_predicate.get(&id).copied() } - pub(crate) fn delete_id(&mut self, id: PredicateId) { + pub fn delete_id(&mut self, id: PredicateId) { pumpkin_assert_moderate!(!self.deleted_ids.contains(&id)); // Add the deleted id for future reuse. self.deleted_ids.push(id); diff --git a/pumpkin-crates/core/src/basic_types/predicate_id_generators/mod.rs b/pumpkin-crates/core/src/basic_types/predicate_id_generators/mod.rs index 5b49949e9..7fa33278e 100644 --- a/pumpkin-crates/core/src/basic_types/predicate_id_generators/mod.rs +++ b/pumpkin-crates/core/src/basic_types/predicate_id_generators/mod.rs @@ -1,6 +1,6 @@ mod deletable_predicate_id_generator; mod predicate_id_generator; -pub(crate) use deletable_predicate_id_generator::DeletablePredicateIdGenerator; +pub use deletable_predicate_id_generator::DeletablePredicateIdGenerator; pub use predicate_id_generator::PredicateId; pub use predicate_id_generator::PredicateIdGenerator; diff --git a/pumpkin-crates/core/src/basic_types/random.rs b/pumpkin-crates/core/src/basic_types/random.rs index 1adaa758e..97206877b 100644 --- a/pumpkin-crates/core/src/basic_types/random.rs +++ b/pumpkin-crates/core/src/basic_types/random.rs @@ -136,79 +136,3 @@ where Some(i) } } - -#[cfg(test)] -pub(crate) mod tests { - use std::cmp::Ordering; - use std::fmt::Debug; - use std::ops::Range; - - use super::Random; - use crate::pumpkin_assert_simple; - - /// A test "random" generator which takes as input a list of elements of [`usize`] and [`bool`] - /// and returns them in order. If more values are attempted to be generated than are provided - /// then this will result in panicking. - #[derive(Debug)] - pub(crate) struct TestRandom { - pub(crate) usizes: Vec, - pub(crate) integers: Vec, - pub(crate) bools: Vec, - pub(crate) weighted_choice: fn(&[f64]) -> Option, - } - - impl Default for TestRandom { - fn default() -> Self { - TestRandom { - weighted_choice: |_| unimplemented!(), - usizes: vec![], - integers: vec![], - bools: vec![], - } - } - } - - impl Random for TestRandom { - fn generate_bool(&mut self, probability: f64) -> bool { - let selected = self.bools.remove(0); - pumpkin_assert_simple!( - if matches!(probability.partial_cmp(&1.0), Some(Ordering::Equal)) { - selected - } else if matches!(probability.partial_cmp(&0.0), Some(Ordering::Equal)) { - !selected - } else { - true - }, - "The probability is {probability} but the selected value is {selected}, this should not be possible, please ensure that your test cases are correctly defined" - ); - selected - } - - fn generate_i32_in_range(&mut self, lb: i32, ub: i32) -> i32 { - let selected = self.integers.remove(0); - let range = lb..ub; - pumpkin_assert_simple!( - range.contains(&selected), - "The selected element by `TestRandom` ({selected}) is not in the provided range ({range:?}) and thus should not be returned, please ensure that your test cases are correctly defined" - ); - selected - } - - fn generate_usize_in_range(&mut self, range: Range) -> usize { - let selected = self.usizes.remove(0); - pumpkin_assert_simple!( - range.contains(&selected), - "The selected element by `TestRandom` ({selected}) is not in the provided range ({range:?}) and thus should not be returned, please ensure that your test cases are correctly defined" - ); - selected - } - - fn generate_f64(&mut self) -> f64 { - unimplemented!() - } - - fn get_weighted_choice(&mut self, weights: &[f64]) -> Option { - (self.weighted_choice)(weights) - } - } -} diff --git a/pumpkin-crates/core/src/branching/brancher.rs b/pumpkin-crates/core/src/branching/brancher.rs index 92e2c0243..9f8d9f949 100644 --- a/pumpkin-crates/core/src/branching/brancher.rs +++ b/pumpkin-crates/core/src/branching/brancher.rs @@ -11,12 +11,6 @@ use crate::basic_types::SolutionReference; use crate::branching; use crate::branching::SelectionContext; #[cfg(doc)] -use crate::branching::branchers::dynamic_brancher::DynamicBrancher; -#[cfg(doc)] -use crate::branching::value_selection::ValueSelector; -#[cfg(doc)] -use crate::branching::variable_selection::VariableSelector; -#[cfg(doc)] use crate::create_statistics_struct; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainId; @@ -24,8 +18,8 @@ use crate::engine::variables::DomainId; use crate::results::solution_iterator::SolutionIterator; use crate::statistics::StatisticLogger; -/// A trait for definining a branching strategy (oftentimes utilising a [`VariableSelector`] and a -/// [`ValueSelector`]). +/// A trait for definining a branching strategy (oftentimes utilising a variable selector and a +/// value selector, see the `pumpkin-branching` crate for concrete implementations). /// /// In general, implementations of this trait define how the search of the solver proceeds (i.e. it /// controls how the solver determines which part of the search space to explore). It is required @@ -118,13 +112,13 @@ pub trait Brancher: Debug { /// Indicates which [`BrancherEvent`] are relevant for this particular [`Brancher`]. /// /// This can be used by [`Brancher::subscribe_to_events`] to determine upon which - /// events which [`VariableSelector`] should be called. + /// events which component of the [`Brancher`] should be called. fn subscribe_to_events(&self) -> Vec; } /// The events which can occur for a [`Brancher`]. Used for returning which events are relevant in -/// [`Brancher::subscribe_to_events`], [`VariableSelector::subscribe_to_events`], -/// and [`ValueSelector::subscribe_to_events`]. +/// [`Brancher::subscribe_to_events`] (and, in the `pumpkin-branching` crate, the analogous methods +/// on variable and value selectors). #[derive(Debug, Clone, Copy, Enum, Hash, PartialEq, Eq)] pub enum BrancherEvent { /// Event for when a conflict is detected diff --git a/pumpkin-crates/core/src/branching/mod.rs b/pumpkin-crates/core/src/branching/mod.rs index f29d684d8..2dc5a1b86 100644 --- a/pumpkin-crates/core/src/branching/mod.rs +++ b/pumpkin-crates/core/src/branching/mod.rs @@ -1,37 +1,23 @@ -//! Contains structures and traits to define the decision making procedure of the [`Solver`]. +//! Contains the core structures used to define the decision making procedure of the [`Solver`]. //! -//! In general, it provides 3 traits: -//! - The [`Brancher`] which defines how a branching procedure (which selects an unfixed variable and splits the domain in some way, see [Section 4.3.1 of \[1\]](http://www.cse.unsw.com.au/~tw/brwhkr08.pdf) -//! for more information) should operate; the main method of this trait is the [`Brancher::next_decision`] method. An example implementation of this trait is the [`IndependentVariableValueBrancher`]. -//! - The [`VariableSelector`] which defines the method required of a variable selector (including -//! the hooks into the solver); the main method of this trait is the -//! [`VariableSelector::select_variable`] method. An example implementation of this trait is the -//! [`AntiFirstFail`] strategy. -//! - The [`ValueSelector`] which defines the method required of a value selector (including the -//! hooks into the solver); the main method of this trait is the [`ValueSelector::select_value`] -//! method. +//! The [`Brancher`] trait defines how a branching procedure (which selects an unfixed variable and +//! splits the domain in some way, see [Section 4.3.1 of \[1\]](http://www.cse.unsw.com.au/~tw/brwhkr08.pdf) +//! for more information) should operate; the main method of this trait is the +//! [`Brancher::next_decision`] method. The [`SelectionContext`] is passed to a [`Brancher`] and +//! allows it to inspect the domains of the variables and to make use of a random number generator. +//! +//! Implementations of [`Brancher`] (e.g. based on variable/value selection heuristics) are +//! provided by the `pumpkin-branching` crate. //! //! A [`Brancher`] is expected to be passed to [`Solver::satisfy`], and [`Solver::optimise`]: //! //! \[1\] F. Rossi, P. Van Beek, and T. Walsh, Handbook of constraint programming. Elsevier, 2006. mod brancher; -pub mod branchers; mod selection_context; -pub mod tie_breaking; -pub mod value_selection; -pub mod variable_selection; pub use brancher::*; pub use selection_context::SelectionContext; #[cfg(doc)] use crate::Solver; -#[cfg(doc)] -use crate::branching::branchers::independent_variable_value_brancher::IndependentVariableValueBrancher; -#[cfg(doc)] -use crate::branching::value_selection::ValueSelector; -#[cfg(doc)] -use crate::branching::variable_selection::AntiFirstFail; -#[cfg(doc)] -use crate::branching::variable_selection::VariableSelector; diff --git a/pumpkin-crates/core/src/branching/selection_context.rs b/pumpkin-crates/core/src/branching/selection_context.rs index 26c4fd86a..460f30ba9 100644 --- a/pumpkin-crates/core/src/branching/selection_context.rs +++ b/pumpkin-crates/core/src/branching/selection_context.rs @@ -1,11 +1,10 @@ use std::fmt::Debug; use crate::basic_types::Random; +use crate::basic_types::Solution; #[cfg(doc)] use crate::branching::Brancher; -use crate::engine::Assignments; -#[cfg(test)] -use crate::engine::notifications::NotificationEngine; +use crate::engine::State; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainGeneratorIterator; #[cfg(doc)] @@ -19,22 +18,23 @@ use crate::propagation::Domains; /// generator. #[derive(Debug)] pub struct SelectionContext<'a> { - pub(crate) assignments: &'a Assignments, + state: &'a State, random_generator: &'a mut dyn Random, } impl<'a> SelectionContext<'a> { - pub fn new(assignments: &'a Assignments, rng: &'a mut dyn Random) -> Self { + pub fn new(state: &'a State, rng: &'a mut dyn Random) -> Self { SelectionContext { - assignments, + state, random_generator: rng, } } pub fn are_all_variables_assigned(&self) -> bool { - self.assignments + self.state + .assignments .get_domains() - .all(|domain_id| self.assignments.is_domain_assigned(&domain_id)) + .all(|domain_id| self.state.assignments.is_domain_assigned(&domain_id)) } /// Returns a random generator which can be used to generate random values (see [`Random`] for @@ -47,22 +47,22 @@ impl<'a> SelectionContext<'a> { /// [`IntegerVariable`]. Note that this is different from the number of values which are in the /// domain of `var` since this calculation does not take into account holes in the domain. pub fn get_size_of_domain(&self, var: Var) -> i32 { - var.upper_bound(self.assignments) - var.lower_bound(self.assignments) + self.state.upper_bound(var.clone()) - self.state.lower_bound(var) } /// Returns the lower bound of the provided [`IntegerVariable`] pub fn lower_bound(&self, var: Var) -> i32 { - var.lower_bound(self.assignments) + self.state.lower_bound(var) } /// Returns the upper bound of the provided [`IntegerVariable`] pub fn upper_bound(&self, var: Var) -> i32 { - var.upper_bound(self.assignments) + self.state.upper_bound(var) } /// Determines whether the provided value is in the domain of the provided [`IntegerVariable`] pub fn contains(&self, var: Var, value: i32) -> bool { - var.contains(self.assignments, value) + self.state.contains(var, value) } /// Determines whether the provided [`IntegerVariable`] has a unit domain (i.e. a domain of size @@ -72,27 +72,16 @@ impl<'a> SelectionContext<'a> { } pub fn is_predicate_assigned(&self, predicate: Predicate) -> bool { - self.assignments.evaluate_predicate(predicate).is_some() + self.state.truth_value(predicate).is_some() } /// Returns all currently defined [`DomainId`]s. pub fn get_domains(&self) -> DomainGeneratorIterator { - self.assignments.get_domains() + self.state.assignments.get_domains() } - #[cfg(test)] - /// Create an ['Assignments'] with the variables having the input bounds. - pub(crate) fn create_for_testing( - domains: Vec<(i32, i32)>, - ) -> (Assignments, NotificationEngine) { - let mut assignments = Assignments::default(); - let mut notification_engine = NotificationEngine::default(); - - for (lower_bound, upper_bound) in domains { - _ = assignments.grow(lower_bound, upper_bound); - notification_engine.grow(); - } - - (assignments, notification_engine) + /// Returns a [`Solution`] containing the values which are currently assigned. + pub fn solution(&self) -> Solution { + Solution::from(self.state.assignments.clone()) } } diff --git a/pumpkin-crates/core/src/branching/value_selection/in_domain_max.rs b/pumpkin-crates/core/src/branching/value_selection/in_domain_max.rs deleted file mode 100644 index aba7b44a5..000000000 --- a/pumpkin-crates/core/src/branching/value_selection/in_domain_max.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::IntegerVariable; -use crate::predicate; - -/// [`ValueSelector`] which chooses to assign the provided variable to its upper-bound. -#[derive(Debug, Copy, Clone)] -pub struct InDomainMax; - -impl ValueSelector for InDomainMax { - fn select_value( - &mut self, - context: &mut SelectionContext, - decision_variable: Var, - ) -> Predicate { - predicate!(decision_variable >= context.upper_bound(decision_variable)) - } - - fn subscribe_to_events(&self) -> Vec { - vec![] - } -} - -#[cfg(test)] -mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::InDomainMax; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; - - #[test] - fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); - let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); - - let mut selector = InDomainMax; - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] >= 10)) - } -} diff --git a/pumpkin-crates/core/src/branching/value_selection/in_domain_median.rs b/pumpkin-crates/core/src/branching/value_selection/in_domain_median.rs deleted file mode 100644 index df44e750e..000000000 --- a/pumpkin-crates/core/src/branching/value_selection/in_domain_median.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::IntegerVariable; -use crate::predicate; - -/// A [`ValueSelector`] which selects the median value in the domain (or if this value is already -/// assigned then the closest variable to it in terms of index). -#[derive(Debug, Copy, Clone)] -pub struct InDomainMedian; - -impl ValueSelector for InDomainMedian { - fn select_value( - &mut self, - context: &mut SelectionContext, - decision_variable: Var, - ) -> Predicate { - let values_in_domain = (context.lower_bound(decision_variable) - ..=context.upper_bound(decision_variable)) - .filter(|bound| context.contains(decision_variable, *bound)) - .collect::>(); - predicate!(decision_variable == values_in_domain[values_in_domain.len() / 2]) - } - - fn subscribe_to_events(&self) -> Vec { - vec![] - } -} - -#[cfg(test)] -mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::InDomainMedian; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; - - #[test] - fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); - let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); - - let mut selector = InDomainMedian; - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 5)) - } - - #[test] - fn test_returns_correct_literal_no_median() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(1, 10)]); - let mut test_rng = TestRandom::default(); - - let domain_ids = assignments.get_domains().collect::>(); - let mut selector = InDomainMedian; - - let _ = assignments.post_predicate( - predicate!(domain_ids[0] != 9), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 5)) - } - - #[test] - fn test_returns_correct_literal_removed_median() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(1, 10)]); - let mut test_rng = TestRandom::default(); - - let domain_ids = assignments.get_domains().collect::>(); - let mut selector = InDomainMedian; - - let _ = assignments.post_predicate( - predicate!(domain_ids[0] != 5), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] == 6)) - } -} diff --git a/pumpkin-crates/core/src/branching/value_selection/out_domain_max.rs b/pumpkin-crates/core/src/branching/value_selection/out_domain_max.rs deleted file mode 100644 index dc2c8b761..000000000 --- a/pumpkin-crates/core/src/branching/value_selection/out_domain_max.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::predicate; - -/// A [`ValueSelector`] which excludes the largest value from the domain. -#[derive(Debug, Copy, Clone)] -pub struct OutDomainMax; - -impl ValueSelector for OutDomainMax { - fn select_value( - &mut self, - context: &mut SelectionContext, - decision_variable: DomainId, - ) -> Predicate { - predicate!(decision_variable <= context.upper_bound(decision_variable) - 1) - } - - fn subscribe_to_events(&self) -> Vec { - vec![] - } -} - -#[cfg(test)] -mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::OutDomainMax; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; - - #[test] - fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); - let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); - - let mut selector = OutDomainMax; - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] <= 9)) - } -} diff --git a/pumpkin-crates/core/src/branching/value_selection/out_domain_median.rs b/pumpkin-crates/core/src/branching/value_selection/out_domain_median.rs deleted file mode 100644 index 60b0fda50..000000000 --- a/pumpkin-crates/core/src/branching/value_selection/out_domain_median.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::predicate; - -/// A [`ValueSelector`] which excludes the median value from the domain. -#[derive(Debug, Copy, Clone)] -pub struct OutDomainMedian; - -impl ValueSelector for OutDomainMedian { - fn select_value( - &mut self, - context: &mut SelectionContext, - decision_variable: DomainId, - ) -> Predicate { - let values_in_domain = (context.lower_bound(decision_variable) - ..=context.upper_bound(decision_variable)) - .filter(|bound| context.contains(decision_variable, *bound)) - .collect::>(); - predicate!(decision_variable != values_in_domain[values_in_domain.len() / 2]) - } - - fn subscribe_to_events(&self) -> Vec { - vec![] - } -} - -#[cfg(test)] -mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::OutDomainMedian; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; - - #[test] - fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); - let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); - - let mut selector = OutDomainMedian; - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] != 5)) - } - - #[test] - fn test_returns_correct_literal_no_median() { - let (mut assignments, mut notification_engine) = - SelectionContext::create_for_testing(vec![(1, 10)]); - let mut test_rng = TestRandom::default(); - let domain_ids = assignments.get_domains().collect::>(); - - let mut selector = OutDomainMedian; - - let _ = assignments.post_predicate( - predicate!(domain_ids[0] != 9), - None, - &mut notification_engine, - ); - - let mut context = SelectionContext::new(&assignments, &mut test_rng); - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] != 5)) - } -} diff --git a/pumpkin-crates/core/src/branching/value_selection/out_domain_min.rs b/pumpkin-crates/core/src/branching/value_selection/out_domain_min.rs deleted file mode 100644 index 8cfb7a178..000000000 --- a/pumpkin-crates/core/src/branching/value_selection/out_domain_min.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::branching::SelectionContext; -use crate::branching::brancher::BrancherEvent; -use crate::branching::value_selection::ValueSelector; -use crate::engine::predicates::predicate::Predicate; -use crate::engine::variables::DomainId; -use crate::predicate; - -/// A [`ValueSelector`] which excludes the smallest value from the domain. -#[derive(Debug, Copy, Clone)] -pub struct OutDomainMin; - -impl ValueSelector for OutDomainMin { - fn select_value( - &mut self, - context: &mut SelectionContext, - decision_variable: DomainId, - ) -> Predicate { - predicate!(decision_variable >= context.lower_bound(decision_variable) + 1) - } - - fn subscribe_to_events(&self) -> Vec { - vec![] - } -} - -#[cfg(test)] -mod tests { - use crate::basic_types::tests::TestRandom; - use crate::branching::SelectionContext; - use crate::branching::value_selection::OutDomainMin; - use crate::branching::value_selection::ValueSelector; - use crate::predicate; - - #[test] - fn test_returns_correct_literal() { - let (assignments, _) = SelectionContext::create_for_testing(vec![(0, 10)]); - let mut test_rng = TestRandom::default(); - let mut context = SelectionContext::new(&assignments, &mut test_rng); - let domain_ids = context.get_domains().collect::>(); - - let mut selector = OutDomainMin; - - let selected_predicate = selector.select_value(&mut context, domain_ids[0]); - assert_eq!(selected_predicate, predicate!(domain_ids[0] >= 1)) - } -} diff --git a/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs b/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs index bd1ebfa42..baeb98a8e 100644 --- a/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs +++ b/pumpkin-crates/core/src/conflict_resolving/conflict_analysis_context.rs @@ -4,8 +4,6 @@ use crate::Random; use crate::basic_types::StoredConflictInfo; use crate::branching::Brancher; #[cfg(doc)] -use crate::branching::branchers::autonomous_search::AutonomousSearch; -#[cfg(doc)] use crate::conflict_resolving::ConflictResolver; use crate::conflict_resolving::LearnedNogood; use crate::containers::HashMap; @@ -271,7 +269,7 @@ impl ConflictAnalysisContext<'_> { /// Informs the used [`Brancher`] that the provided `predicate` appeared during conflict /// analysis. /// - /// This is used by [`Brancher`]s such as [`AutonomousSearch`] to guide the search. + /// This is used by [`Brancher`]s (e.g. VSIDS-based branchers) to guide the search. pub fn predicate_appeared_in_conflict(&mut self, predicate: Predicate) { self.brancher.on_appearance_in_conflict_predicate(predicate); } diff --git a/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs b/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs index f315d8624..1002d55e7 100644 --- a/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs +++ b/pumpkin-crates/core/src/engine/constraint_satisfaction_solver.rs @@ -32,7 +32,6 @@ use crate::conflict_resolving::ConflictResolver; use crate::containers::HashMap; use crate::containers::HashSet; use crate::declare_inference_label; -use crate::engine::Assignments; use crate::engine::RestartOptions; use crate::engine::RestartStrategy; use crate::engine::State; @@ -58,6 +57,7 @@ use crate::pumpkin_assert_simple; use crate::state::CurrentNogood; use crate::statistics::StatisticLogger; use crate::statistics::statistic_logging::should_log_statistics; +use crate::variables::DomainGeneratorIterator; use crate::variables::DomainId; /// A solver which attempts to find a solution to a Constraint Satisfaction Problem (CSP) using @@ -192,12 +192,13 @@ impl Default for SatisfactionSolverOptions { } impl ConstraintSatisfactionSolver { - pub(crate) fn assignments(&self) -> &Assignments { - &self.state.assignments + /// Returns an iterator over the [`DomainId`]s which are currently defined. + pub(crate) fn get_domain_ids(&self) -> DomainGeneratorIterator { + self.state.get_domain_ids() } /// This is a temporary accessor to help refactoring. - pub fn get_solution_reference(&self) -> SolutionReference<'_> { + pub(crate) fn get_solution_reference(&self) -> SolutionReference<'_> { self.state.get_solution_reference() } @@ -624,10 +625,8 @@ impl ConstraintSatisfactionSolver { } // Otherwise proceed with standard branching. - let context = &mut SelectionContext::new( - &self.state.assignments, - &mut self.internal_parameters.random_generator, - ); + let context = + &mut SelectionContext::new(&self.state, &mut self.internal_parameters.random_generator); // If there is a next decision, make the decision. let Some(decision_predicate) = brancher.next_decision(context) else { @@ -755,7 +754,7 @@ impl ConstraintSatisfactionSolver { brancher.on_unassign_integer(domain_id, previous_value) }); - brancher.synchronise(&mut SelectionContext::new(&state.assignments, rng)); + brancher.synchronise(&mut SelectionContext::new(state, rng)); } /// Main propagation loop. @@ -1148,8 +1147,10 @@ mod tests { } use super::ConstraintSatisfactionSolver; use super::CoreExtractionResult; - use crate::DefaultBrancher; use crate::basic_types::CSPSolverExecutionFlag; + use crate::branching::Brancher; + use crate::branching::BrancherEvent; + use crate::branching::SelectionContext; use crate::conflict_resolving::ConflictAnalysisContext; use crate::conflict_resolving::ConflictResolver; use crate::predicate; @@ -1158,6 +1159,24 @@ mod tests { use crate::pumpkin_assert_simple; use crate::termination::Indefinite; + /// A minimal [`Brancher`] which selects the first unfixed variable and assigns it its + /// lower-bound; used to drive search in tests which do not care about the branching heuristic. + #[derive(Debug)] + struct SimpleBrancher; + + impl Brancher for SimpleBrancher { + fn next_decision(&mut self, context: &mut SelectionContext) -> Option { + context + .get_domains() + .find(|&variable| !context.is_integer_fixed(variable)) + .map(|variable| predicate!(variable == context.lower_bound(variable))) + } + + fn subscribe_to_events(&self) -> Vec { + vec![] + } + } + fn is_same_core(core1: &[Predicate], core2: &[Predicate]) -> bool { core1.len() == core2.len() && core2.iter().all(|lit| core1.contains(lit)) } @@ -1181,7 +1200,7 @@ mod tests { expected_flag: CSPSolverExecutionFlag, expected_result: CoreExtractionResult, ) { - let mut brancher = DefaultBrancher::default_over_all_variables(&solver.state.assignments); + let mut brancher = SimpleBrancher; let mut resolver = NoLearningResolver; let flag = solver.solve_under_assumptions( diff --git a/pumpkin-crates/core/src/engine/state.rs b/pumpkin-crates/core/src/engine/state.rs index e6a5c00e2..041b53699 100644 --- a/pumpkin-crates/core/src/engine/state.rs +++ b/pumpkin-crates/core/src/engine/state.rs @@ -48,6 +48,7 @@ use crate::state::EmptyDomainConflict; use crate::state::PropagatorHandle; use crate::statistics::StatisticLogger; use crate::statistics::log_statistic; +use crate::variables::DomainGeneratorIterator; use crate::variables::DomainId; use crate::variables::IntegerVariable; use crate::variables::Literal; @@ -277,6 +278,11 @@ impl State { pub fn get_checkpoint(&self) -> usize { self.assignments.get_checkpoint() } + + /// Returns an iterator over the [`DomainId`]s which are currently defined. + pub fn get_domain_ids(&self) -> DomainGeneratorIterator { + self.assignments.get_domains() + } } /// Operations for retrieving information about trail diff --git a/pumpkin-crates/core/src/engine/variables/mod.rs b/pumpkin-crates/core/src/engine/variables/mod.rs index 42837e402..ce9e1b650 100644 --- a/pumpkin-crates/core/src/engine/variables/mod.rs +++ b/pumpkin-crates/core/src/engine/variables/mod.rs @@ -11,7 +11,7 @@ mod literal; mod transformable_variable; pub use affine_view::AffineView; -pub(crate) use domain_generator_iterator::DomainGeneratorIterator; +pub use domain_generator_iterator::DomainGeneratorIterator; pub use domain_id::DomainId; pub use integer_variable::IntegerVariable; pub use literal::Literal; diff --git a/pumpkin-crates/core/src/lib.rs b/pumpkin-crates/core/src/lib.rs index 19d6fcefb..1a4396e6a 100644 --- a/pumpkin-crates/core/src/lib.rs +++ b/pumpkin-crates/core/src/lib.rs @@ -35,7 +35,6 @@ mod api; pub use api::*; -pub use crate::api::solver::DefaultBrancher; pub use crate::api::solver::Solver; pub use crate::basic_types::ConstraintOperationError; pub use crate::basic_types::Duration; diff --git a/pumpkin-solver-py/Cargo.toml b/pumpkin-solver-py/Cargo.toml index f28f9b3ae..b06cb6360 100644 --- a/pumpkin-solver-py/Cargo.toml +++ b/pumpkin-solver-py/Cargo.toml @@ -20,6 +20,7 @@ pyo3 = { version = "0.29.0", features= ["extension-module"] } pumpkin-solver = { version = "0.5.0", path = "../pumpkin-solver" } pumpkin-constraints = { version = "0.5.0", path = "../pumpkin-crates/constraints", features=["clap"] } pumpkin-conflict-resolvers = { version = "0.5.0", path = "../pumpkin-crates/conflict-resolvers/"} +pumpkin-branching = { version = "0.5.0", path = "../pumpkin-crates/branching/"} [build-dependencies] pyo3-build-config = "0.29.0" diff --git a/pumpkin-solver-py/src/brancher.rs b/pumpkin-solver-py/src/brancher.rs index 88d2e0437..67f295a96 100644 --- a/pumpkin-solver-py/src/brancher.rs +++ b/pumpkin-solver-py/src/brancher.rs @@ -1,8 +1,8 @@ -use pumpkin_solver::core::DefaultBrancher; +use pumpkin_solver::branching::DefaultBrancher; +use pumpkin_solver::branching::branching::warm_start::WarmStart; use pumpkin_solver::core::branching::Brancher; use pumpkin_solver::core::branching::BrancherEvent; use pumpkin_solver::core::branching::SelectionContext; -use pumpkin_solver::core::branching::branchers::warm_start::WarmStart; use pumpkin_solver::core::containers::HashMap; use pumpkin_solver::core::predicates::Predicate; use pumpkin_solver::core::results::SolutionReference; diff --git a/pumpkin-solver-py/src/model.rs b/pumpkin-solver-py/src/model.rs index 58e3984db..047fe51ff 100644 --- a/pumpkin-solver-py/src/model.rs +++ b/pumpkin-solver-py/src/model.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use std::time::Duration; use std::time::Instant; +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_solver::Solver; use pumpkin_solver::core::containers::HashMap; @@ -88,7 +89,7 @@ impl Model { }; let solver = Solver::with_options(options); - let brancher = PythonBrancher::new(solver.default_brancher()); + let brancher = PythonBrancher::new(DefaultBrancher::default_over_all_variables(&solver)); Ok(Model { solver, brancher }) } diff --git a/pumpkin-solver/Cargo.toml b/pumpkin-solver/Cargo.toml index c3cabaa48..2eebe4f3a 100644 --- a/pumpkin-solver/Cargo.toml +++ b/pumpkin-solver/Cargo.toml @@ -17,6 +17,7 @@ pumpkin-core = { version = "0.5.0", path = "../pumpkin-crates/core/", features = pumpkin-constraints = { version = "0.5.0", path = "../pumpkin-crates/constraints/"} pumpkin-propagators = { version = "0.5.0", path = "../pumpkin-crates/propagators/", features=["clap"]} pumpkin-conflict-resolvers = { version = "0.5.0", path = "../pumpkin-crates/conflict-resolvers/"} +pumpkin-branching = { version = "0.5.0", path = "../pumpkin-crates/branching/"} signal-hook = "0.4.4" thiserror = "2.0.12" diff --git a/pumpkin-solver/examples/bibd.rs b/pumpkin-solver/examples/bibd.rs index 7b26c4d1e..6805c6a1b 100644 --- a/pumpkin-solver/examples/bibd.rs +++ b/pumpkin-solver/examples/bibd.rs @@ -13,6 +13,7 @@ //! //! Hence, the problem is defined in terms of v, k, and l. +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_solver::Solver; use pumpkin_solver::core::results::ProblemSolution; @@ -145,7 +146,7 @@ fn main() { } } - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); match solver.satisfy(&mut brancher, &mut Indefinite, &mut resolver) { diff --git a/pumpkin-solver/examples/disjunctive_scheduling.rs b/pumpkin-solver/examples/disjunctive_scheduling.rs index bd431d5d5..3810679e5 100644 --- a/pumpkin-solver/examples/disjunctive_scheduling.rs +++ b/pumpkin-solver/examples/disjunctive_scheduling.rs @@ -9,6 +9,7 @@ //! To ensure that one of these occurs, we create two Boolean variables, l_xy and l_yx, to signify //! the two possibilities, and then post the constraint (l_xy \/ l_yx). +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_core::constraints::NegatableConstraint; use pumpkin_solver::Solver; @@ -87,7 +88,7 @@ fn main() { } } - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); if matches!( solver.satisfy(&mut brancher, &mut Indefinite, &mut resolver), diff --git a/pumpkin-solver/examples/nqueens.rs b/pumpkin-solver/examples/nqueens.rs index d6f91873d..409dc7996 100644 --- a/pumpkin-solver/examples/nqueens.rs +++ b/pumpkin-solver/examples/nqueens.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use clap::Parser; +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_solver::Solver; use pumpkin_solver::core::options::SolverOptions; @@ -87,7 +88,7 @@ fn main() { .add_constraint(pumpkin_constraints::all_different(diag2, c3_tag)) .post(); - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); match solver.satisfy(&mut brancher, &mut Indefinite, &mut resolver) { diff --git a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/ast.rs b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/ast.rs index b653e221b..5bff2cbaf 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/ast.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/ast.rs @@ -1,25 +1,25 @@ use log::warn; -use pumpkin_solver::core::branching::value_selection::DynamicValueSelector; -use pumpkin_solver::core::branching::value_selection::InDomainInterval; -use pumpkin_solver::core::branching::value_selection::InDomainMax; -use pumpkin_solver::core::branching::value_selection::InDomainMedian; -use pumpkin_solver::core::branching::value_selection::InDomainMiddle; -use pumpkin_solver::core::branching::value_selection::InDomainMin; -use pumpkin_solver::core::branching::value_selection::InDomainRandom; -use pumpkin_solver::core::branching::value_selection::InDomainSplit; -use pumpkin_solver::core::branching::value_selection::InDomainSplitRandom; -use pumpkin_solver::core::branching::value_selection::OutDomainMax; -use pumpkin_solver::core::branching::value_selection::OutDomainMedian; -use pumpkin_solver::core::branching::value_selection::OutDomainMin; -use pumpkin_solver::core::branching::value_selection::OutDomainRandom; -use pumpkin_solver::core::branching::value_selection::ReverseInDomainSplit; -use pumpkin_solver::core::branching::variable_selection::AntiFirstFail; -use pumpkin_solver::core::branching::variable_selection::DynamicVariableSelector; -use pumpkin_solver::core::branching::variable_selection::FirstFail; -use pumpkin_solver::core::branching::variable_selection::InputOrder; -use pumpkin_solver::core::branching::variable_selection::Largest; -use pumpkin_solver::core::branching::variable_selection::MaxRegret; -use pumpkin_solver::core::branching::variable_selection::Smallest; +use pumpkin_branching::value_selection::DynamicValueSelector; +use pumpkin_branching::value_selection::InDomainInterval; +use pumpkin_branching::value_selection::InDomainMax; +use pumpkin_branching::value_selection::InDomainMedian; +use pumpkin_branching::value_selection::InDomainMiddle; +use pumpkin_branching::value_selection::InDomainMin; +use pumpkin_branching::value_selection::InDomainRandom; +use pumpkin_branching::value_selection::InDomainSplit; +use pumpkin_branching::value_selection::InDomainSplitRandom; +use pumpkin_branching::value_selection::OutDomainMax; +use pumpkin_branching::value_selection::OutDomainMedian; +use pumpkin_branching::value_selection::OutDomainMin; +use pumpkin_branching::value_selection::OutDomainRandom; +use pumpkin_branching::value_selection::ReverseInDomainSplit; +use pumpkin_branching::variable_selection::AntiFirstFail; +use pumpkin_branching::variable_selection::DynamicVariableSelector; +use pumpkin_branching::variable_selection::FirstFail; +use pumpkin_branching::variable_selection::InputOrder; +use pumpkin_branching::variable_selection::Largest; +use pumpkin_branching::variable_selection::MaxRegret; +use pumpkin_branching::variable_selection::Smallest; use pumpkin_solver::core::pumpkin_assert_eq_simple; use pumpkin_solver::core::pumpkin_assert_simple; use pumpkin_solver::core::variables::DomainId; diff --git a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/compiler/create_search_strategy.rs b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/compiler/create_search_strategy.rs index 52d1fc609..a5f65be40 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/compiler/create_search_strategy.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/compiler/create_search_strategy.rs @@ -1,13 +1,14 @@ use std::rc::Rc; use flatzinc::AnnExpr; +use pumpkin_branching::DefaultBrancher; +use pumpkin_branching::branching::dynamic_brancher::DynamicBrancher; +use pumpkin_branching::branching::independent_variable_value_brancher::IndependentVariableValueBrancher; +use pumpkin_branching::branching::warm_start::WarmStart; +use pumpkin_branching::value_selection::InDomainMax; +use pumpkin_branching::value_selection::InDomainMin; +use pumpkin_branching::variable_selection::InputOrder; use pumpkin_solver::core::branching::Brancher; -use pumpkin_solver::core::branching::branchers::dynamic_brancher::DynamicBrancher; -use pumpkin_solver::core::branching::branchers::independent_variable_value_brancher::IndependentVariableValueBrancher; -use pumpkin_solver::core::branching::branchers::warm_start::WarmStart; -use pumpkin_solver::core::branching::value_selection::InDomainMax; -use pumpkin_solver::core::branching::value_selection::InDomainMin; -use pumpkin_solver::core::branching::variable_selection::InputOrder; use pumpkin_solver::core::variables::DomainId; use pumpkin_solver::core::variables::Literal; @@ -210,7 +211,9 @@ fn create_from_search_strategy( )), None => {} } - brancher.add_brancher(Box::new(context.solver.default_brancher())); + brancher.add_brancher(Box::new(DefaultBrancher::default_over_all_variables( + context.solver, + ))); } Ok(brancher) diff --git a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/instance.rs b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/instance.rs index a3e6f3e17..602e86d60 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/instance.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/instance.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use std::fmt::Write; use std::rc::Rc; -use pumpkin_solver::core::branching::branchers::dynamic_brancher::DynamicBrancher; +use pumpkin_branching::branching::dynamic_brancher::DynamicBrancher; use pumpkin_solver::core::optimisation::OptimisationDirection; use pumpkin_solver::core::variables::DomainId; use pumpkin_solver::core::variables::Literal; diff --git a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs index eae152942..95e37ee9b 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs @@ -11,15 +11,15 @@ use std::path::Path; use std::time::Duration; use std::time::Instant; -use pumpkin_core::branching::branchers::alternating::AlternatingBrancher; -use pumpkin_core::branching::branchers::alternating::every_x_restarts::EveryXRestarts; -use pumpkin_core::branching::branchers::alternating::until_solution::UntilSolution; +use pumpkin_branching::branching::alternating::AlternatingBrancher; +use pumpkin_branching::branching::alternating::every_x_restarts::EveryXRestarts; +use pumpkin_branching::branching::alternating::until_solution::UntilSolution; +use pumpkin_branching::branching::dynamic_brancher::DynamicBrancher; use pumpkin_core::conflict_resolving::ConflictResolver; use pumpkin_core::statistics::log_statistic; use pumpkin_propagators::cumulative::options::CumulativeOptions; use pumpkin_solver::Solver; use pumpkin_solver::core::branching::Brancher; -use pumpkin_solver::core::branching::branchers::dynamic_brancher::DynamicBrancher; #[cfg(doc)] use pumpkin_solver::core::constraints::cumulative; use pumpkin_solver::core::optimisation::OptimisationDirection; diff --git a/pumpkin-solver/src/bin/pumpkin-solver/main.rs b/pumpkin-solver/src/bin/pumpkin-solver/main.rs index 3d1e4ee0f..8721742ec 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/main.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/main.rs @@ -24,6 +24,7 @@ use maxsat::PseudoBooleanEncoding; use parsers::dimacs::SolverArgs; use parsers::dimacs::SolverDimacsSink; use parsers::dimacs::parse_cnf; +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::AnalysisMode; use pumpkin_conflict_resolvers::resolvers::NoLearningResolver; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; @@ -741,7 +742,7 @@ fn cnf_problem( let mut termination = TimeBudget::starting_now(time_limit.unwrap_or(Duration::from_secs(u64::MAX))); - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); match solver.satisfy(&mut brancher, &mut termination, &mut resolver) { diff --git a/pumpkin-solver/src/bin/pumpkin-solver/maxsat/mod.rs b/pumpkin-solver/src/bin/pumpkin-solver/maxsat/mod.rs index 12739a5ae..96d88128b 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/maxsat/mod.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/maxsat/mod.rs @@ -8,6 +8,7 @@ pub(crate) use encoders::PseudoBooleanEncoding; use optimisation::linear_search::LinearSearch; use optimisation::optimisation_result::MaxSatOptimisationResult; use optimisation::optimisation_solver::OptimisationSolver; +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_solver::core::options::SolverOptions; use pumpkin_solver::core::termination::TimeBudget; @@ -32,7 +33,7 @@ pub(crate) fn wcnf_problem( .. } = parse_wcnf::(instance_file, SolverArgs::new(solver_options))?; - let brancher = solver.default_brancher(); + let brancher = DefaultBrancher::default_over_all_variables(&solver); let mut termination = time_limit.map(TimeBudget::starting_now); let resolver = ResolutionResolver::default(); diff --git a/pumpkin-solver/src/lib.rs b/pumpkin-solver/src/lib.rs index cfc776f4b..4f91e69d0 100644 --- a/pumpkin-solver/src/lib.rs +++ b/pumpkin-solver/src/lib.rs @@ -67,12 +67,13 @@ //! ```rust //! # use pumpkin_solver::Solver; //! # use pumpkin_solver::core::termination::Indefinite; +//! # use pumpkin_branching::DefaultBrancher; //! # use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; //! # let mut solver = Solver::default(); //! // We create a termination condition which allows the solver to run indefinitely //! let mut termination = Indefinite; //! // And we create a search strategy (in this case, simply the default) -//! let mut brancher = solver.default_brancher(); +//! let mut brancher = DefaultBrancher::default_over_all_variables(&solver); //! // Finally, we create a default conflict resolver //! let mut resolver = ResolutionResolver::default(); //! ``` @@ -85,6 +86,7 @@ //! # use pumpkin_solver::core::results::ProblemSolution; //! # use pumpkin_solver::core::constraints; //! # use pumpkin_solver::core::constraints::Constraint; +//! # use pumpkin_branching::DefaultBrancher; //! # use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; //! # use std::cmp::max; //! # let mut solver = Solver::default(); @@ -94,7 +96,7 @@ //! # let c1 = solver.new_constraint_tag(); //! # solver.add_constraint(pumpkin_constraints::equals(vec![x, y, z], 17, c1)).post(); //! # let mut termination = Indefinite; -//! # let mut brancher = solver.default_brancher(); +//! # let mut brancher = DefaultBrancher::default_over_all_variables(&solver); //! # let mut resolver = ResolutionResolver::default(); //! // Then we find a solution to the problem //! let result = solver.satisfy(&mut brancher, &mut termination, &mut resolver); @@ -146,7 +148,7 @@ //! # use std::cmp::max; //! # use crate::pumpkin_solver::core::optimisation::OptimisationProcedure; //! # use pumpkin_solver::core::results::SolutionReference; -//! # use pumpkin_solver::core::DefaultBrancher; +//! # use pumpkin_branching::DefaultBrancher; //! # use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; //! # let mut solver = Solver::default(); //! # let x = solver.new_bounded_integer(5, 10); @@ -157,7 +159,7 @@ //! # solver.add_constraint(pumpkin_constraints::equals(vec![x, y, z], 17, c1)).post(); //! # solver.add_constraint(pumpkin_constraints::maximum(vec![x, y, z], objective, c1)).post(); //! # let mut termination = Indefinite; -//! # let mut brancher = solver.default_brancher(); +//! # let mut brancher = DefaultBrancher::default_over_all_variables(&solver); //! # let mut resolver = ResolutionResolver::default(); //! //! let callback = |_: &Solver, _: SolutionReference, _: &DefaultBrancher, _: &ResolutionResolver| -> ControlFlow<()> { @@ -205,6 +207,7 @@ //! # use pumpkin_solver::core::results::solution_iterator::IteratedSolution; //! # use pumpkin_solver::core::constraints; //! # use pumpkin_solver::core::constraints::Constraint; +//! # use pumpkin_branching::DefaultBrancher; //! # use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; //! // We create the solver with default options //! let mut solver = Solver::default(); @@ -221,7 +224,7 @@ //! // We create a termination condition which allows the solver to run indefinitely //! let mut termination = Indefinite; //! // And we create a search strategy (in this case, simply the default) -//! let mut brancher = solver.default_brancher(); +//! let mut brancher = DefaultBrancher::default_over_all_variables(&solver); //! // Finally, we create a default conflict resolver //! let mut resolver = ResolutionResolver::default(); //! @@ -280,6 +283,7 @@ //! # use pumpkin_solver::core::predicate; //! # use pumpkin_solver::core::constraints; //! # use pumpkin_solver::core::constraints::Constraint; +//! # use pumpkin_branching::DefaultBrancher; //! # use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; //! // We create the solver with default options //! let mut solver = Solver::default(); @@ -298,7 +302,7 @@ //! // We create a termination condition which allows the solver to run indefinitely //! let mut termination = Indefinite; //! // And we create a search strategy (in this case, simply the default) -//! let mut brancher = solver.default_brancher(); +//! let mut brancher = DefaultBrancher::default_over_all_variables(&solver); //! // Finally, we create a default conflict resolver //! let mut resolver = ResolutionResolver::default(); //! @@ -347,6 +351,18 @@ pub mod propagators { pub use pumpkin_propagators::*; } +pub mod branching { + //! Contains the implementations of [`Brancher`]s, [`VariableSelector`]s and + //! [`ValueSelector`]s. + #[cfg(doc)] + use pumpkin_branching::value_selection::ValueSelector; + #[cfg(doc)] + use pumpkin_branching::variable_selection::VariableSelector; + pub use pumpkin_branching::*; + #[cfg(doc)] + use pumpkin_core::branching::Brancher; +} + pub mod core { //! The core interfaces and structures used by the pumpkin solver. pub use pumpkin_core::*; diff --git a/pumpkin-solver/tests/core_extraction.rs b/pumpkin-solver/tests/core_extraction.rs index 316401c2d..66a763f42 100644 --- a/pumpkin-solver/tests/core_extraction.rs +++ b/pumpkin-solver/tests/core_extraction.rs @@ -1,5 +1,6 @@ #![cfg(test)] // workaround for https://github.com/rust-lang/rust-clippy/issues/11024 +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_core::Solver; use pumpkin_core::predicate; @@ -30,7 +31,7 @@ fn basic_core_extraction() { // We create a termination condition which allows the solver to run indefinitely let mut termination = Indefinite; // And we create a search strategy (in this case, simply the default) - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); diff --git a/pumpkin-solver/tests/iteration_test.rs b/pumpkin-solver/tests/iteration_test.rs index 88912ebe4..d1649bf75 100644 --- a/pumpkin-solver/tests/iteration_test.rs +++ b/pumpkin-solver/tests/iteration_test.rs @@ -1,5 +1,6 @@ #![cfg(test)] // workaround for https://github.com/rust-lang/rust-clippy/issues/11024 +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_solver::Solver; use pumpkin_solver::core::results::ProblemSolution; @@ -29,7 +30,7 @@ fn iterator_finds_all_solutions() { // We create a termination condition which allows the solver to run indefinitely let mut termination = Indefinite; // And we create a search strategy (in this case, simply the default) - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); // Then we solve to satisfaction diff --git a/pumpkin-solver/tests/solver_test.rs b/pumpkin-solver/tests/solver_test.rs index 36cd74b45..af18940bf 100644 --- a/pumpkin-solver/tests/solver_test.rs +++ b/pumpkin-solver/tests/solver_test.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; +use pumpkin_branching::DefaultBrancher; use pumpkin_conflict_resolvers::resolvers::ResolutionResolver; use pumpkin_solver::Solver; use pumpkin_solver::core::options::SolverOptions; @@ -34,7 +35,7 @@ fn proof_with_reified_literals() { )) .post(); - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); let result = solver.satisfy(&mut brancher, &mut Indefinite, &mut resolver); @@ -69,7 +70,7 @@ fn proof_with_equality_unit_nogood_step() { )) .post(); - let mut brancher = solver.default_brancher(); + let mut brancher = DefaultBrancher::default_over_all_variables(&solver); let mut resolver = ResolutionResolver::default(); let result = solver.satisfy(&mut brancher, &mut Indefinite, &mut resolver);