47# ifndef BRANCH_AND_BOUND_H
48# define BRANCH_AND_BOUND_H
68template <std::totally_ordered Value>
81 return bound > incumbent;
91template <std::totally_ordered Value>
104 return bound < incumbent;
114template <
typename Policy,
typename Value>
117 { policy.better(a, b) } -> std::convertible_to<bool>;
118 { policy.can_improve(a, b) } -> std::convertible_to<bool>;
119 { policy.more_promising(a, b) } -> std::convertible_to<bool>;
123template <SearchState State, SearchMove Move, std::totally_ordered Objective>
132template <
typename Solution,
typename ObjectivePolicy>
144template <
typename Solution,
typename ObjectivePolicy>
163 return best_.has_value();
173 return get().objective_value;
183 return best_.consider(solution);
188 return best_.consider(std::move(solution));
209template <
typename Solution,
typename ObjectivePolicy>
251template <
typename Domain>
253 { domain.is_complete(state) } -> std::convertible_to<bool>;
257template <
typename Domain>
259 typename Domain::Objective;
260 { domain.objective_value(state) } -> std::convertible_to<typename Domain::Objective>;
261 { domain.bound(state) } -> std::convertible_to<typename Domain::Objective>;
277template <
typename Domain>
280and requires(
Domain &domain,
typename Domain::State &state,
const typename Domain::Move &move) {
283 { domain.apply(state, move) } -> std::same_as<void>;
284 { domain.undo(state, move) } -> std::same_as<void>;
304 using State =
typename Domain::State;
306 using Move =
typename Domain::Move;
348 return lhs.depth <
rhs.depth;
417 template <
typename OnSolution>
422 <<
"SearchLimits::max_solutions must be positive or Search_Unlimited";
428 const auto start_time = SearchClock::now();
442 <<
"Branch_And_Bound received an unsupported exploration strategy";
453 template <
typename OnSolution>
457 auto handler = std::forward<OnSolution>(
on_solution);
475 template <
typename VisitedMap>
479 using State_Key =
typename Domain::State_Key;
481 "Visited map must satisfy VisitedBoundMap concept");
488 template <
typename VisitedMap,
typename OnSolution>
493 using State_Key =
typename Domain::State_Key;
495 "Visited map must satisfy VisitedBoundMap concept");
498 <<
"SearchLimits::max_solutions must be positive or Search_Unlimited";
500 <<
"Visited-map search only supports Depth_First strategy";
506 const auto start_time = SearchClock::now();
543 template <
typename Recurse>
560 stop = std::forward<Recurse>(recurse)();
583 <<
"Branch_And_Bound does not support MoveOrderingMode::Estimated_Score";
585 <<
"Branch_And_Bound does not use killer heuristics";
587 <<
"Branch_And_Bound does not use history heuristics";
611 [&](
const Move &move) ->
bool
615 ranked.ordinal = ordinal++;
662 template <
typename OnSolution>
672 solution.
state = state;
673 solution.
path = path;
674 solution.
depth = depth;
689 template <
typename OnSolution>
699 if (
domain_.is_complete(state))
737 [&]() {
return dfs(state, path, depth + 1, result,
on_solution); });
743 const auto &
ranked : ordered_moves)
748 (
void)
domain_.for_each_successor(state,
749 [&](
const Move &move) ->
bool
757 template <
typename OnSolution>
766 template <
typename OnSolution,
typename VisitedMap>
776 using State_Key =
typename Domain::State_Key;
778 "Visited map must satisfy VisitedBoundMap concept");
782 if (
domain_.is_complete(state))
810 const auto key =
domain_.state_key(state);
813 if (
auto *
pair = visited.search(key);
pair !=
nullptr)
821 pair->second = bound_value;
825 visited.insert(key, bound_value);
832 else if (
auto *
pair = visited.search(key);
pair !=
nullptr)
854 const auto &
ranked : ordered_moves)
859 (
void)
domain_.for_each_successor(state,
860 [&](
const Move &move) ->
bool
874 template <
typename OnSolution>
885 if (
domain_.is_complete(state))
917 template <
typename OnSolution>
946 [&](
const Move &move) ->
bool
970template <BranchAndBoundDomain
Domain,
988template <BranchAndBoundDomain
Domain,
992 and SearchSolutionVisitor<
Generic memoization / transposition-table support for state search.
Exception handling system with formatted messages for Aleph-w.
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
constexpr bool is_empty() const noexcept
Checks if the container is empty.
T & append(const T &data)
Append a copy of data
Tracks the best solution seen so far according to a comparator.
Reusable branch-and-bound engine over implicit state spaces.
typename Domain::Move Move
Move type.
static ExplorationPolicy default_policy() noexcept
Return the default branch-and-bound exploration policy.
void set_policy(const ExplorationPolicy &policy) noexcept
const ExplorationPolicy & policy() const noexcept
static void register_visit(const size_t depth, Result &result)
void validate_ordering_configuration() const
and SearchSolutionVisitor< OnSolution, Solution > Result search(State initial_state, VisitedMap &visited_map, OnSolution &on_solution)
Branch and bound with visited-bound map and solution callback.
void search_best_first(State initial_state, Result &result, OnSolution &on_solution)
and SearchStateKeyProvider< Domain > bool dfs_visited(State &state, SearchPath< Move > &path, const size_t depth, Result &result, OnSolution &on_solution, VisitedMap &visited)
bool expansion_limit_reached(Result &result) const
typename Domain::State State
Concrete search state type.
bool apply_move_and_recurse(const Move &move, State &state, SearchPath< Move > &path, Result &result, bool &stop, Recurse &&recurse)
Apply one move, recurse, then undo — shared by dfs and dfs_visited.
Branch_And_Bound(Domain domain, ExplorationPolicy policy=default_policy(), const SearchLimits &limits={}, ObjectivePolicy objective={})
Build a branch-and-bound engine bound to one optimization domain.
Result search(State initial_state, OnSolution &&on_solution)
bool dfs(State &state, SearchPath< Move > &path, const size_t depth, Result &result, OnSolution &on_solution)
bool process_best_first_candidate(State state, SearchPath< Move > path, const size_t depth, Result &result, Frontier &frontier, OnSolution &on_solution)
void set_limits(const SearchLimits &limits) noexcept
Result search(State initial_state)
Execute branch and bound and keep only the incumbent.
bool stop_after_solution(Result &result) const
ObjectivePolicy objective_
bool handle_complete_solution(const State &state, const SearchPath< Move > &path, const size_t depth, Result &result, OnSolution &on_solution)
Domain & domain() noexcept
Array< RankedMove< Move, Objective > > collect_ordered_moves(State &state, Result &result)
const Domain & domain() const noexcept
void search_depth_first(State &initial_state, Result &result, OnSolution &on_solution)
static constexpr bool supports_best_first
Compile-time marker: Branch_And_Bound supports both Depth_First and Best_First strategies.
Result search(State initial_state, OnSolution &on_solution)
Execute branch and bound with a callback/collector per solution.
Result search(State initial_state, VisitedMap &visited_map)
Branch and bound with a visited-bound map (DFS only).
Domain Domain_Type
Type of the problem domain.
ExplorationPolicy policy_
const SearchLimits & limits() const noexcept
bool ordering_active_for_depth_first() const noexcept
const ObjectivePolicy & objective_policy() const noexcept
typename Domain::Objective Objective
Type of the metric being optimized.
Dynamic heap of elements of type T ordered by a comparison functor.
Global incumbent manager for branch and bound.
bool consider(Solution &&solution)
BestSolution< Solution, Compare_Type > best_
typename Solution::Objective_Type Objective_Type
bool has_value() const noexcept
const ObjectivePolicy & objective() const noexcept
ObjectiveIncumbent(ObjectivePolicy objective)
ObjectivePolicy objective_
const Objective_Type & best_value() const
ObjectiveIncumbent()=default
bool can_improve(const Objective_Type &bound) const noexcept
ObjectivePolicy Objective_Policy
bool consider(const Solution &solution)
const Solution & get() const
Minimal contract for branch-and-bound domains.
Concept for objective policies used by branch and bound.
Concept for complete-solution predicates in optimization domains.
Concept for domains that can estimate a move's bound without modifying state (incremental bound for B...
Concept for domains that provide an objective and an optimistic bound.
Minimal requirement for search moves.
Concept for callbacks invoked on each accepted solution.
Generic concept for domains that can expose a stable state key.
Minimal requirement for mutable search states.
Concept for lazy successor generation.
Concept for a map tracking the best bound seen per visited state.
static Geom_Number objective(const Point &p)
bool should_prune_state(Domain &domain, const typename Domain::State &state, const size_t depth)
Dispatch helper for the optional should_prune hook.
bool is_terminal_state(const Domain &domain, const typename Domain::State &state)
Dispatch helper for the optional is_terminal hook.
bool stop_after_solution(Result &result, const ExplorationPolicy &policy, const SearchLimits &limits)
Decide whether to stop the search after a solution was accepted.
void register_visit(const size_t depth, Result &result)
Update visit counters and max-depth statistic.
bool expansion_limit_reached(Result &result, const SearchLimits &limits)
Check whether the expansion limit has been reached.
Main namespace for Aleph-w library functions.
and
Check uniqueness with explicit hash + equality functors.
void sort_ranked_moves(Array< RankedMove< Move, Priority > > &moves, BetterPriority better_priority, const bool prefer_killer, const bool prefer_history)
Sort one materialized move batch using priority and optional hooks.
SearchStatus
Final state of a search execution.
@ LimitReached
Search stopped because an external hard limit was hit.
@ Exhausted
Search space within the configured bounds was exhausted.
@ StoppedOnSolution
Search stopped because solution handling requested it.
@ NotStarted
Search object exists but no traversal has run yet.
void reserve_search_path(SearchPath< Move > &path, const SearchLimits &limits)
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
std::pair< First, Second > pair
Alias to std::pair kept for backwards compatibility.
auto branch_and_bound_search(Domain domain, typename Domain::State initial_state, ExplorationPolicy policy=Branch_And_Bound< Domain, ObjectivePolicy >::default_policy(), SearchLimits limits={}, ObjectivePolicy objective={})
Convenience wrapper for one-shot branch and bound.
OptimizationSense
Optimization direction supported by branch and bound.
@ Maximize
Larger objective values are better.
@ Minimize
Smaller objective values are better.
double search_elapsed_ms(const SearchClock::duration &duration) noexcept
@ Estimated_Bound
Rank successors by an optimistic child bound.
@ Domain
Preserve the order emitted by for_each_successor().
@ Estimated_Score
Rank successors by a cheap heuristic score estimate.
Common infrastructure for implicit state-space search.
Result of a branch-and-bound execution.
bool found_solution() const noexcept
BranchAndBoundResult(ObjectivePolicy objective)
bool limit_reached() const noexcept
bool exhausted() const noexcept
bool stopped_on_solution() const noexcept
SearchLimits limits
Hard limits used during the run.
ExplorationPolicy policy
Exploration policy used during the run.
BranchAndBoundStats stats
Collected branch-and-bound statistics.
ObjectivePolicy Objective_Policy
BranchAndBoundResult()=default
SearchStatus status
Final execution status.
Incumbent_Type incumbent
Global incumbent for the run.
Branch-and-bound specific statistics.
size_t incumbent_updates
Number of times the incumbent improved.
size_t pruned_by_bound
Nodes pruned because their bound cannot beat the incumbent.
ObjectivePolicy objective
bool operator()(const FrontierNode &lhs, const FrontierNode &rhs) const noexcept
Default solution visitor that always continues the search.
Exploration controls shared across engines.
bool stop_at_first_solution
Stop when the first goal is found.
Strategy strategy
Traversal strategy.
MoveOrderingMode move_ordering
Successor-ordering mode.
@ Best_First
Priority-guided expansion; only supported by Branch_And_Bound.
@ Depth_First
Recursive depth-first traversal (all engines).
bool use_history_heuristic
Enable experimental history heuristic where supported.
bool use_killer_moves
Enable experimental killer heuristic where supported.
Objective policy for maximization problems.
static bool can_improve(const Value &bound, const Value &incumbent) noexcept
static bool better(const Value &candidate, const Value &incumbent) noexcept
static bool more_promising(const Value &lhs, const Value &rhs) noexcept
static constexpr OptimizationSense sense
Objective policy for minimization problems.
static constexpr OptimizationSense sense
static bool more_promising(const Value &lhs, const Value &rhs) noexcept
static bool can_improve(const Value &bound, const Value &incumbent) noexcept
static bool better(const Value &candidate, const Value &incumbent) noexcept
size_t priority_estimates
Number of score/bound estimates computed for ordering.
size_t ordered_moves
Number of individual moves considered by ordering.
size_t ordered_batches
Number of successor batches materialized and ordered.
Snapshot of a complete optimization solution.
Objective objective_value
Objective value of the complete solution.
Compare two optimization solutions by objective quality.
bool operator()(const Solution &lhs, const Solution &rhs) const noexcept
ObjectivePolicy objective
One move plus the metadata used by ordering comparators.
Move move
The candidate move.
Hard bounds applied by the search engine.
size_t max_solutions
Maximum accepted solutions.
size_t max_depth
Maximum expansion depth.
Snapshot of a concrete solution encountered during the traversal.
SearchPath< Move > path
Move sequence leading to state.
State state
Snapshot of the terminal state.
size_t depth
Path depth of the solution.
Counters collected during a search run.
size_t pruned_by_domain
States discarded by domain-side pruning.
size_t terminal_states
Non-solution terminal states cut by the domain.
double elapsed_ms
Wall-clock time spent inside the search call.
MoveOrderingStats move_ordering
Successor-ordering activity for this run.
size_t pruned_by_depth
States not expanded due to max depth.
size_t generated_successors
Number of successor moves emitted.
size_t solutions_found
Number of goal states accepted.
size_t expanded_states
Number of non-terminal states expanded.
Dynamic binary heap with node-based storage.