From 11d355a9066c8c9931a560c9fafa7e68e4c0f565 Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Mon, 20 Jan 2025 16:50:33 +0100 Subject: [PATCH 1/4] feat: add logging for branchers --- .../outputs/solution_callback_arguments.rs | 21 +++++++++++- pumpkin-solver/src/api/solver.rs | 1 + .../src/bin/pumpkin-solver/flatzinc/mod.rs | 4 +++ pumpkin-solver/src/branching/brancher.rs | 8 +++++ .../branchers/alternating_brancher.rs | 7 ++++ .../branching/branchers/autonomous_search.rs | 33 ++++++++++++++++++- .../branching/branchers/dynamic_brancher.rs | 10 ++++++ .../src/statistics/statistic_logger.rs | 2 +- 8 files changed, 83 insertions(+), 3 deletions(-) diff --git a/pumpkin-solver/src/api/outputs/solution_callback_arguments.rs b/pumpkin-solver/src/api/outputs/solution_callback_arguments.rs index 805e9790..d6158fc0 100644 --- a/pumpkin-solver/src/api/outputs/solution_callback_arguments.rs +++ b/pumpkin-solver/src/api/outputs/solution_callback_arguments.rs @@ -1,4 +1,8 @@ +use std::fmt::Debug; + +use crate::branching::Brancher; use crate::results::Solution; +use crate::statistics::StatisticLogger; use crate::Solver; /// The input which is passed to the solution callback (which can be set using @@ -6,7 +10,6 @@ use crate::Solver; /// /// Provides direct access to the solution via [`SolutionCallbackArguments::solution`] and allows /// logging the statistics of the [`Solver`] using [`SolutionCallbackArguments::log_statistics`]. -#[derive(Debug)] pub struct SolutionCallbackArguments<'a, 'b> { /// The solver which found the solution solver: &'a Solver, @@ -14,6 +17,19 @@ pub struct SolutionCallbackArguments<'a, 'b> { pub solution: &'b Solution, /// The (optional) objective value provided to the [`Solver`]. objective_value: Option, + /// The brancher that was used to find the solution + brancher: &'a mut dyn Brancher, +} + +impl Debug for SolutionCallbackArguments<'_, '_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SolutionCallbackArguments") + .field("solver", self.solver) + .field("solution", self.solution) + .field("objective_value", &self.objective_value) + .field("brancher", &"brancher") + .finish() + } } impl<'a, 'b> SolutionCallbackArguments<'a, 'b> { @@ -21,11 +37,13 @@ impl<'a, 'b> SolutionCallbackArguments<'a, 'b> { solver: &'a Solver, solution: &'b Solution, objective_value: Option, + brancher: &'a mut dyn Brancher, ) -> Self { Self { solver, solution, objective_value, + brancher, } } @@ -34,6 +52,7 @@ impl<'a, 'b> SolutionCallbackArguments<'a, 'b> { /// If the solution was found using [`Solver::minimise`] or [`Solver::maximise`] then the /// objective value of the current solution is included in the statistics. pub fn log_statistics(&self) { + self.brancher.log_statistics(StatisticLogger::default()); if let Some(objective_value) = self.objective_value { self.solver.log_statistics_with_objective(objective_value) } else { diff --git a/pumpkin-solver/src/api/solver.rs b/pumpkin-solver/src/api/solver.rs index 76cd0499..84773750 100644 --- a/pumpkin-solver/src/api/solver.rs +++ b/pumpkin-solver/src/api/solver.rs @@ -592,6 +592,7 @@ impl Solver { self, solution, objective_value, + brancher, )); } diff --git a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs index 64305a6f..cce58391 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs @@ -12,6 +12,7 @@ use std::time::Duration; use pumpkin_solver::branching::branchers::alternating_brancher::AlternatingBrancher; use pumpkin_solver::branching::branchers::alternating_brancher::AlternatingStrategy; use pumpkin_solver::branching::branchers::dynamic_brancher::DynamicBrancher; +use pumpkin_solver::branching::Brancher; #[cfg(doc)] use pumpkin_solver::constraints::cumulative; use pumpkin_solver::options::CumulativeOptions; @@ -20,6 +21,7 @@ use pumpkin_solver::results::OptimisationResult; use pumpkin_solver::results::ProblemSolution; use pumpkin_solver::results::SatisfactionResult; use pumpkin_solver::results::Solution; +use pumpkin_solver::statistics::StatisticLogger; use pumpkin_solver::termination::Combinator; use pumpkin_solver::termination::OsSignal; use pumpkin_solver::termination::TimeBudget; @@ -95,6 +97,7 @@ pub(crate) fn solve( let optimal_objective_value = optimal_solution.get_integer_value(*objective_function.get_domain()); if !options.all_solutions { + brancher.log_statistics(StatisticLogger::default()); solver.log_statistics(); print_solution_from_solver(&optimal_solution, &instance.outputs) } @@ -150,6 +153,7 @@ pub(crate) fn solve( None }; + brancher.log_statistics(StatisticLogger::default()); if let Some(value) = value { solver.log_statistics_with_objective(value as i64) } else { diff --git a/pumpkin-solver/src/branching/brancher.rs b/pumpkin-solver/src/branching/brancher.rs index ecdeb006..3ab2e19f 100644 --- a/pumpkin-solver/src/branching/brancher.rs +++ b/pumpkin-solver/src/branching/brancher.rs @@ -8,11 +8,14 @@ use crate::branching::value_selection::ValueSelector; #[cfg(doc)] use crate::branching::variable_selection::VariableSelector; use crate::branching::SelectionContext; +#[cfg(doc)] +use crate::create_statistics_struct; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainId; use crate::engine::Assignments; #[cfg(doc)] use crate::results::solution_iterator::SolutionIterator; +use crate::statistics::StatisticLogger; #[cfg(doc)] use crate::Solver; @@ -28,6 +31,11 @@ use crate::Solver; /// If the [`Brancher`] (or any component thereof) is implemented incorrectly then the /// behaviour of the solver is undefined. pub trait Brancher { + /// Logs statistics of the brancher using the provided [`StatisticLogger`]. + /// + /// It is recommended to create a struct through the [`create_statistics_struct!`] macro! + fn log_statistics(&self, _statistic_logger: StatisticLogger) {} + /// Returns the next decision concerning a single variable and value; it returns the /// [`Predicate`] corresponding to this decision (or [`None`] if all variables under /// consideration are assigned). diff --git a/pumpkin-solver/src/branching/branchers/alternating_brancher.rs b/pumpkin-solver/src/branching/branchers/alternating_brancher.rs index e42a45d8..da51e3f5 100644 --- a/pumpkin-solver/src/branching/branchers/alternating_brancher.rs +++ b/pumpkin-solver/src/branching/branchers/alternating_brancher.rs @@ -7,6 +7,7 @@ use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainId; use crate::engine::Assignments; +use crate::statistics::StatisticLogger; use crate::DefaultBrancher; use crate::Solver; @@ -109,6 +110,12 @@ impl Brancher for AlternatingBrancher { } } + fn log_statistics(&self, statistic_logger: StatisticLogger) { + self.default_brancher + .log_statistics(statistic_logger.clone()); + self.other_brancher.log_statistics(statistic_logger); + } + fn on_appearance_in_conflict_predicate(&mut self, predicate: Predicate) { self.default_brancher .on_appearance_in_conflict_predicate(predicate); diff --git a/pumpkin-solver/src/branching/branchers/autonomous_search.rs b/pumpkin-solver/src/branching/branchers/autonomous_search.rs index 0522129a..623eec5d 100644 --- a/pumpkin-solver/src/branching/branchers/autonomous_search.rs +++ b/pumpkin-solver/src/branching/branchers/autonomous_search.rs @@ -1,4 +1,6 @@ use super::independent_variable_value_brancher::IndependentVariableValueBrancher; +use crate::basic_types::moving_averages::CumulativeMovingAverage; +use crate::basic_types::moving_averages::MovingAverage; use crate::basic_types::PredicateId; use crate::basic_types::PredicateIdGenerator; use crate::basic_types::SolutionReference; @@ -8,9 +10,12 @@ use crate::branching::Brancher; use crate::branching::SelectionContext; use crate::containers::KeyValueHeap; use crate::containers::StorageKey; +use crate::create_statistics_struct; use crate::engine::predicates::predicate::Predicate; use crate::engine::Assignments; use crate::results::Solution; +use crate::statistics::Statistic; +use crate::statistics::StatisticLogger; use crate::DefaultBrancher; /// A [`Brancher`] that combines [VSIDS \[1\]](https://dl.acm.org/doi/pdf/10.1145/378239.379017) /// and [Solution-based phase saving \[2\]](https://people.eng.unimelb.edu.au/pstuckey/papers/lns-restarts.pdf). @@ -54,7 +59,6 @@ use crate::DefaultBrancher; /// value-selection heuristic to simulate local search behavior in complete solvers’, in the /// proceedings of the Principles and Practice of Constraint Programming (CP 2018). #[derive(Debug)] - pub struct AutonomousSearch { /// Predicates are mapped to ids. This is used internally in the heap. predicate_id_info: PredicateIdGenerator, @@ -81,8 +85,19 @@ pub struct AutonomousSearch { /// If the heap does not contain any more unfixed predicates then this backup_brancher will be /// used instead. backup_brancher: BackupBrancher, + + statistics: AutonomousSearchStatistics, } +create_statistics_struct!(AutonomousSearchStatistics { + num_backup_called: usize, + num_predicates_removed: usize, + num_calls: usize, + num_predicates_added: usize, + average_size_of_heap: CumulativeMovingAverage, + num_assigned_predicates_encountered: usize, +}); + const DEFAULT_VSIDS_INCREMENT: f64 = 1.0; const DEFAULT_VSIDS_MAX_THRESHOLD: f64 = 1e100; const DEFAULT_VSIDS_DECAY_FACTOR: f64 = 0.95; @@ -109,6 +124,7 @@ impl DefaultBrancher { Smallest::new(&variables), InDomainMin, ), + statistics: Default::default(), } } } @@ -129,6 +145,7 @@ impl AutonomousSearch { decay_factor: DEFAULT_VSIDS_DECAY_FACTOR, best_known_solution: None, backup_brancher, + statistics: Default::default(), } } @@ -147,6 +164,8 @@ impl AutonomousSearch { /// Bumps the activity of a predicate by [`Vsids::increment`]. /// Used when a predicate is encountered during a conflict. fn bump_activity(&mut self, predicate: Predicate) { + self.statistics.num_predicates_added += + (!self.predicate_id_info.has_id_for_predicate(predicate)) as usize; let id = self.predicate_id_info.get_id(predicate); self.resize_heap(id); @@ -168,6 +187,7 @@ impl AutonomousSearch { if *self.heap.get_value(predicate_id) <= self.minimum_activity_threshold() && predicate_id != id { + self.statistics.num_predicates_removed += 1; self.heap.delete_key(predicate_id); self.predicate_id_info.delete_id(predicate_id); } @@ -198,6 +218,7 @@ impl AutonomousSearch { .get_predicate(*candidate) .expect("We expected present predicates to be registered."); if context.is_predicate_assigned(predicate) { + self.statistics.num_assigned_predicates_encountered += 1; let _ = self.heap.pop_max(); // We know that this predicate is now dormant @@ -243,17 +264,27 @@ impl AutonomousSearch { impl Brancher for AutonomousSearch { fn next_decision(&mut self, context: &mut SelectionContext) -> Option { + self.statistics.num_calls += 1; + self.statistics + .average_size_of_heap + .add_term(self.heap.num_nonremoved_elements()); let result = self .next_candidate_predicate(context) .map(|predicate| self.determine_polarity(predicate)); if result.is_none() && !context.are_all_variables_assigned() { // There are variables for which we do not have a predicate, rely on the backup + self.statistics.num_backup_called += 1; self.backup_brancher.next_decision(context) } else { result } } + fn log_statistics(&self, statistic_logger: StatisticLogger) { + let statistic_logger = statistic_logger.attach_to_prefix("AutonomousSearch"); + self.statistics.log(statistic_logger); + } + fn on_backtrack(&mut self) { self.backup_brancher.on_backtrack() } diff --git a/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs b/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs index 22d18e7a..2c6f9145 100644 --- a/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs +++ b/pumpkin-solver/src/branching/branchers/dynamic_brancher.rs @@ -12,6 +12,7 @@ use crate::branching::SelectionContext; use crate::engine::predicates::predicate::Predicate; use crate::engine::variables::DomainId; use crate::engine::Assignments; +use crate::statistics::StatisticLogger; /// An implementation of a [`Brancher`] which takes a [`Vec`] of `Box` and /// sequentially applies [`Brancher::next_decision`] until all of them return [`None`]. @@ -66,6 +67,15 @@ impl Brancher for DynamicBrancher { } } + fn log_statistics(&self, statistic_logger: StatisticLogger) { + self.branchers + .iter() + .enumerate() + .for_each(move |(index, brancher)| { + brancher.log_statistics(statistic_logger.attach_to_prefix(index)) + }) + } + fn on_conflict(&mut self) { // A conflict has occurred, we do not know which brancher now can select a variable, reset // to the first one diff --git a/pumpkin-solver/src/statistics/statistic_logger.rs b/pumpkin-solver/src/statistics/statistic_logger.rs index b772d4a8..c651dd4b 100644 --- a/pumpkin-solver/src/statistics/statistic_logger.rs +++ b/pumpkin-solver/src/statistics/statistic_logger.rs @@ -8,7 +8,7 @@ use crate::engine::propagation::Propagator; /// Responsible for logging the statistics with the provided prefix; currently used when logging /// the statistics of propagators. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct StatisticLogger { /// The prefix which will be attached to the statistic name name_prefix: String, From a07d609f8b1cf28e9dd810444b159febc52622d7 Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Thu, 27 Feb 2025 10:07:54 +0100 Subject: [PATCH 2/4] refactor: make brancher generic in solution iterator --- pumpkin-solver/src/api/outputs/solution_iterator.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pumpkin-solver/src/api/outputs/solution_iterator.rs b/pumpkin-solver/src/api/outputs/solution_iterator.rs index d089ddb1..08f7e7e4 100644 --- a/pumpkin-solver/src/api/outputs/solution_iterator.rs +++ b/pumpkin-solver/src/api/outputs/solution_iterator.rs @@ -42,7 +42,7 @@ impl<'solver, 'brancher, 'termination, B: Brancher, T: TerminationCondition> /// Find a new solution by blocking the previous solution from being found. Also calls the /// [`Brancher::on_solution`] method from the [`Brancher`] used to run the initial solve. - pub fn next_solution(&mut self) -> IteratedSolution { + pub fn next_solution(&mut self) -> IteratedSolution { if let Some(blocking_clause) = self.next_blocking_clause.take() { self.solver .get_satisfaction_solver_mut() @@ -85,9 +85,9 @@ fn get_blocking_clause(solution: &Solution) -> Vec { clippy::large_enum_variant, reason = "these will not be stored in bulk, so this is not an issue" )] -pub enum IteratedSolution<'a> { +pub enum IteratedSolution<'a, B: Brancher> { /// A new solution was identified. - Solution(Solution, &'a Solver, &'a dyn Brancher), + Solution(Solution, &'a Solver, &'a B), /// No more solutions exist. Finished, @@ -99,7 +99,7 @@ pub enum IteratedSolution<'a> { Unsatisfiable, } -impl Debug for IteratedSolution<'_> { +impl Debug for IteratedSolution<'_, B> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { IteratedSolution::Solution(solution, _, _) => write!(f, "Solution({solution:?})"), From 59674c56201192cd0bb53f58ee8872985c256e56 Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Thu, 27 Feb 2025 10:34:31 +0100 Subject: [PATCH 3/4] refactor: change brancher from dyn to generic --- pumpkin-py/src/model.rs | 8 +++-- pumpkin-solver/src/api/solver.rs | 7 +++-- .../src/bin/pumpkin-solver/flatzinc/mod.rs | 21 +++++++------- .../src/optimisation/linear_sat_unsat.rs | 21 ++++---------- .../src/optimisation/linear_unsat_sat.rs | 29 ++++--------------- pumpkin-solver/src/optimisation/mod.rs | 21 ++++---------- 6 files changed, 38 insertions(+), 69 deletions(-) diff --git a/pumpkin-py/src/model.rs b/pumpkin-py/src/model.rs index 0d965d28..d96fe4d6 100644 --- a/pumpkin-py/src/model.rs +++ b/pumpkin-py/src/model.rs @@ -9,10 +9,12 @@ use pumpkin_solver::options::SolverOptions; use pumpkin_solver::predicate; use pumpkin_solver::proof::Format; use pumpkin_solver::proof::ProofLog; +use pumpkin_solver::results::SolutionReference; use pumpkin_solver::termination::Indefinite; use pumpkin_solver::variables::DomainId; use pumpkin_solver::variables::Literal; use pumpkin_solver::ConstraintOperationError; +use pumpkin_solver::DefaultBrancher; use pumpkin_solver::Solver; use pyo3::prelude::*; @@ -261,16 +263,18 @@ impl Model { let objective = objective.to_affine_view(&variable_map); + let callback: fn(&Solver, SolutionReference, &DefaultBrancher) = |_, _, _| {}; + let result = match optimiser { Optimiser::LinearSatUnsat => solver.optimise( &mut brancher, &mut Indefinite, - LinearSatUnsat::new(direction, objective, |_, _, _| {}), + LinearSatUnsat::new(direction, objective, callback), ), Optimiser::LinearUnsatSat => solver.optimise( &mut brancher, &mut Indefinite, - LinearUnsatSat::new(direction, objective, |_, _, _| {}), + LinearUnsatSat::new(direction, objective, callback), ), }; diff --git a/pumpkin-solver/src/api/solver.rs b/pumpkin-solver/src/api/solver.rs index cc86f526..690fce54 100644 --- a/pumpkin-solver/src/api/solver.rs +++ b/pumpkin-solver/src/api/solver.rs @@ -408,12 +408,13 @@ impl Solver { /// it exists. pub fn optimise< Var: IntegerVariable, - Callback: Fn(&Solver, SolutionReference, &dyn Brancher), + B: Brancher, + Callback: Fn(&Solver, SolutionReference, &B), >( &mut self, - brancher: &mut impl Brancher, + brancher: &mut B, termination: &mut impl TerminationCondition, - mut optimisation_procedure: impl OptimisationProcedure, + mut optimisation_procedure: impl OptimisationProcedure, ) -> OptimisationResult { optimisation_procedure.optimise(brancher, termination, self) } diff --git a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs index 201d7012..ccf13478 100644 --- a/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs +++ b/pumpkin-solver/src/bin/pumpkin-solver/flatzinc/mod.rs @@ -113,16 +113,17 @@ pub(crate) fn solve( } }; - let callback = |solver: &Solver, solution: SolutionReference<'_>, brancher: &(dyn Brancher)| { - solution_callback( - brancher, - Some(objective), - options.all_solutions, - &outputs, - solver, - solution, - ); - }; + let callback = + |solver: &Solver, solution: SolutionReference<'_>, brancher: &DynamicBrancher| { + solution_callback( + brancher, + Some(objective), + options.all_solutions, + &outputs, + solver, + solution, + ); + }; let result = match options.optimisation_strategy { OptimisationStrategy::LinearSatUnsat => solver.optimise( diff --git a/pumpkin-solver/src/optimisation/linear_sat_unsat.rs b/pumpkin-solver/src/optimisation/linear_sat_unsat.rs index e5da2eb8..34521605 100644 --- a/pumpkin-solver/src/optimisation/linear_sat_unsat.rs +++ b/pumpkin-solver/src/optimisation/linear_sat_unsat.rs @@ -20,12 +20,7 @@ pub struct LinearSatUnsat { solution_callback: Callback, } -impl LinearSatUnsat -where - // The trait bound here is not common; see - // linear_unsat_sat for more info. - Callback: Fn(&Solver, SolutionReference, &dyn Brancher), -{ +impl LinearSatUnsat { /// Create a new instance of [`LinearSatUnsat`]. pub fn new( direction: OptimisationDirection, @@ -80,14 +75,15 @@ impl LinearSatUnsat { } } -impl OptimisationProcedure for LinearSatUnsat +impl OptimisationProcedure for LinearSatUnsat where Var: IntegerVariable, - Callback: Fn(&Solver, SolutionReference, &dyn Brancher), + B: Brancher, + Callback: Fn(&Solver, SolutionReference, &B), { fn optimise( &mut self, - brancher: &mut impl Brancher, + brancher: &mut B, termination: &mut impl TerminationCondition, solver: &mut Solver, ) -> OptimisationResult { @@ -189,12 +185,7 @@ where } } - fn on_solution_callback( - &self, - solver: &Solver, - solution: SolutionReference, - brancher: &impl Brancher, - ) { + fn on_solution_callback(&self, solver: &Solver, solution: SolutionReference, brancher: &B) { (self.solution_callback)(solver, solution, brancher) } } diff --git a/pumpkin-solver/src/optimisation/linear_unsat_sat.rs b/pumpkin-solver/src/optimisation/linear_unsat_sat.rs index ff3c3f33..15c5be3d 100644 --- a/pumpkin-solver/src/optimisation/linear_unsat_sat.rs +++ b/pumpkin-solver/src/optimisation/linear_unsat_sat.rs @@ -20,21 +20,7 @@ pub struct LinearUnsatSat { solution_callback: Callback, } -impl LinearUnsatSat -where - // The trait bound here is contrary to common - // practice; typically the bounds are only enforced - // where they are required (in this case, in the - // implementation of OptimisationProcedure). - // - // However, if we don't have the trait bound here, - // the compiler may implement `FnOnce` for the - // empty closure, which causes problems. So, we - // have the hint here. - // - // Similar is also the case in linear SAT-UNSAT. - Callback: Fn(&Solver, SolutionReference, &dyn Brancher), -{ +impl LinearUnsatSat { /// Create a new instance of [`LinearUnsatSat`]. pub fn new( direction: OptimisationDirection, @@ -49,12 +35,12 @@ where } } -impl - OptimisationProcedure for LinearUnsatSat +impl + OptimisationProcedure for LinearUnsatSat { fn optimise( &mut self, - brancher: &mut impl Brancher, + brancher: &mut B, termination: &mut impl TerminationCondition, solver: &mut Solver, ) -> OptimisationResult { @@ -152,12 +138,7 @@ impl { fn optimise( &mut self, - brancher: &mut impl Brancher, + brancher: &mut B, termination: &mut impl TerminationCondition, solver: &mut Solver, ) -> OptimisationResult; - fn on_solution_callback( - &self, - solver: &Solver, - solution: SolutionReference, - brancher: &impl Brancher, - ); + fn on_solution_callback(&self, solver: &Solver, solution: SolutionReference, brancher: &B); /// Processes a solution when it is found, it consists of the following procedure: /// - Assigning `best_objective_value` the value assigned to `objective_variable` (multiplied by @@ -46,7 +42,7 @@ pub trait OptimisationProcedure< objective_variable: &impl IntegerVariable, best_objective_value: &mut i64, best_solution: &mut Solution, - brancher: &mut impl Brancher, + brancher: &mut B, solver: &Solver, ) { *best_objective_value = (objective_multiplier @@ -59,12 +55,7 @@ pub trait OptimisationProcedure< self.internal_process_solution(best_solution, brancher, solver) } - fn internal_process_solution( - &self, - solution: &Solution, - brancher: &mut impl Brancher, - solver: &Solver, - ) { + fn internal_process_solution(&self, solution: &Solution, brancher: &mut B, solver: &Solver) { brancher.on_solution(solution.as_reference()); self.on_solution_callback(solver, solution.as_reference(), brancher) From 6578d677f408940a83a29ea89ad3bb69048fe1d9 Mon Sep 17 00:00:00 2001 From: ImkoMarijnissen Date: Thu, 27 Feb 2025 12:02:03 +0100 Subject: [PATCH 4/4] fix: updating doc test --- pumpkin-solver/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pumpkin-solver/src/lib.rs b/pumpkin-solver/src/lib.rs index 74410ca1..d51096f7 100644 --- a/pumpkin-solver/src/lib.rs +++ b/pumpkin-solver/src/lib.rs @@ -135,6 +135,8 @@ //! # use pumpkin_solver::optimisation::linear_sat_unsat::LinearSatUnsat; //! # use std::cmp::max; //! # use crate::pumpkin_solver::optimisation::OptimisationProcedure; +//! # use pumpkin_solver::results::SolutionReference; +//! # use pumpkin_solver::DefaultBrancher; //! # let mut solver = Solver::default(); //! # let x = solver.new_bounded_integer(5, 10); //! # let y = solver.new_bounded_integer(-3, 15); @@ -145,10 +147,11 @@ //! # let mut termination = Indefinite; //! # let mut brancher = solver.default_brancher(); //! // Then we solve to optimality +//! let callback: fn(&Solver, SolutionReference, &DefaultBrancher) = |_, _, _| {}; //! let result = solver.optimise( //! &mut brancher, //! &mut termination, -//! LinearSatUnsat::new(OptimisationDirection::Minimise, objective, |_, _, _| {}), +//! LinearSatUnsat::new(OptimisationDirection::Minimise, objective, callback), //! ); //! //! if let OptimisationResult::Optimal(optimal_solution) = result {