49# ifndef STATE_SEARCH_COMMON_H
50# define STATE_SEARCH_COMMON_H
99template <SearchMove Move>
106 return std::chrono::duration<double, std::milli>(
duration).count();
116template <
typename Key,
128template <
typename Key,
142template <
typename Set,
typename Key>
144 { set.contains(key) } -> std::convertible_to<bool>;
162template <
typename Map,
typename Key,
typename Objective>
164 map.insert(key,
obj);
166 { map.search(key) ==
nullptr } -> std::convertible_to<bool>;
167 map.search(key)->second =
obj;
190template <
typename Domain>
193 const typename Domain::State &state,
194 const typename Domain::Move &move) {
195 { domain.bound_after(state, move) }
196 -> std::convertible_to<typename Domain::Objective>;
215template <
typename Domain>
218 const typename Domain::State &state,
219 const typename Domain::Move &move) {
220 { domain.evaluate_after(state, move) }
221 -> std::convertible_to<typename Domain::Score>;
242template <
typename Engine>
276template <SearchMove Move>
325template <SearchState State, SearchMove Move>
340template <
typename Solution>
343 bool operator()(
const Solution &,
const Solution &)
const noexcept
357 template <
typename Solution>
369template <
typename Visitor,
typename Solution>
371 {
visitor(solution) } -> std::convertible_to<bool>;
382template <
typename Solution>
400 <<
"SearchSolutionCollector: max_solutions must be positive";
456template <
typename Solution,
typename Compare = KeepFirstSolution<Solution>>
495 <<
"BestSolution::get: no incumbent solution available";
505 <<
"BestSolution::get: no incumbent solution available";
549template <
typename Solution,
typename Compare = KeepFirstSolution<Solution>>
560template <
typename Domain>
562 typename Domain::State;
563 typename Domain::Move;
567 d.for_each_successor(state,
568 [](
const typename Domain::Move &) ->
bool
572 } -> std::convertible_to<bool>;
590template <
typename Domain>
592 { d.is_goal(state) } -> std::convertible_to<bool>;
596template <
typename Domain>
598 { d.is_terminal(state) } -> std::convertible_to<bool>;
607template <
typename Domain>
609 { d.should_prune(state, depth) } -> std::convertible_to<bool>;
613template <
typename Domain>
615 typename Domain::Move_Key;
616 { d.move_key(move) } -> std::convertible_to<typename Domain::Move_Key>;
623template <
typename Prov
ider>
625 typename Provider::State_Key;
626 {
provider.state_key(state) } -> std::convertible_to<typename Provider::State_Key>;
635template <
typename Domain>
637 typename Domain::Distance;
638 { d.heuristic(state) } -> std::convertible_to<typename Domain::Distance>;
646template <
typename Domain>
648 =
requires(
Domain &d,
const typename Domain::State &state,
const typename Domain::Move &move) {
649 typename Domain::Distance;
650 { d.cost(state, move) } -> std::convertible_to<typename Domain::Distance>;
664template <
typename Domain>
667and requires(
Domain &d,
typename Domain::State &state,
const typename Domain::Move &move) {
668 { d.apply(state, move) } -> std::same_as<void>;
669 { d.undo(state, move) } -> std::same_as<void>;
677template <
typename Solution,
typename Compare = KeepFirstSolution<Solution>>
737namespace search_engine_detail {
749template <
typename Domain>
751 const typename Domain::State &state)
754 return domain.is_terminal(state);
770template <
typename Domain>
772 const typename Domain::State &state,
776 return domain.should_prune(state, depth);
789template <
typename Result>
792 ++result.stats.visited_states;
793 if (depth > result.stats.max_depth_reached)
794 result.stats.max_depth_reached = depth;
809template <
typename Result>
814 ++result.stats.limit_hits;
842template <
typename Result>
850 ++result.stats.limit_hits;
C++20 concepts for constraining comparison functors.
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
void reserve(size_t cap)
Reserves cap cells into the array.
Tracks the best solution seen so far according to a comparator.
bool has_value() const noexcept
Return true if an incumbent exists.
Solution & get()
Mutable access to the current incumbent.
std::optional< Solution > current_
Compare Compare_Type
Type of the comparison policy.
bool consider(Solution &&candidate)
Consider a candidate by move.
const Compare & compare() const noexcept
Access the ordering functor used by this incumbent.
void clear() noexcept
Remove the stored incumbent, if any.
BestSolution(Compare compare)
Build a tracker with a custom comparator.
bool consider(const Solution &candidate)
Consider a candidate by copy.
BestSolution()=default
Build an empty incumbent tracker.
const Solution & get() const
Read the current incumbent.
Solution Solution_Type
Type of stored solutions.
T & append(const T &item)
void empty() noexcept
empty the list
Open addressing hash table with linear probing collision resolution.
Collector that stores accepted solutions in an Aleph list.
bool operator()(const Solution &solution)
Append one solution and report whether enumeration should continue.
Container_Type & solutions() noexcept
Mutable access to the collected solutions.
size_t size() const noexcept
Number of collected solutions.
const Container_Type & solutions() const noexcept
Access the collected solutions.
bool is_empty() const noexcept
Return true if no solution has been collected.
SearchSolutionCollector(const size_t max_solutions)
Build a collector that stops after max_solutions.
void clear() noexcept
Remove all collected solutions.
Solution Solution_Type
Type of collected solutions.
SearchSolutionCollector()=default
Build a collector with no limit.
Container_Type solutions_
Concept for domains that provide the cost of applying a move.
Minimal contract for DFS/backtracking domains.
A callable that takes two const T& and returns bool.
Optional concept for domain-side pruning hooks.
Concept for domains that can recognize goal states.
Concept for domains that provide an admissible heuristic.
Concept for domains that can estimate a move's bound without modifying state (incremental bound for B...
Concept for adversarial-search domains that can estimate a child score without modifying state (incre...
Optional concept for domains that can key moves for history heuristics.
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 that checks whether a search engine supports the Best_First exploration strategy at compile t...
Optional concept for explicit non-solution terminal states.
Concept for a map tracking the best bound seen per visited state.
Concept for a set suitable for tracking globally visited states.
Singly linked list implementations with head-tail access.
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.
std::chrono::steady_clock SearchClock
Monotonic clock used for search timings.
constexpr size_t Search_Unlimited
Sentinel used by SearchLimits to mean "no bound".
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.
double search_elapsed_ms(const SearchClock::duration &duration) noexcept
MoveOrderingMode
Built-in move-ordering modes supported by the framework.
@ Domain
Preserve the order emitted by for_each_successor().
Shared support for configurable successor ordering heuristics.
Default solution visitor that always continues the search.
bool operator()(const Solution &) const noexcept
Accepts any solution and returns true.
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.
Strategy
Traversal strategy supported by the engine.
@ 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.
Comparison policy that keeps the first solution seen.
bool operator()(const Solution &, const Solution &) const noexcept
Open addressing hash map using linear probing.
Statistics collected by engines that reorder successor batches.
Hard bounds applied by the search engine.
size_t max_expansions
Maximum expanded states.
size_t max_solutions
Maximum accepted solutions.
size_t max_depth
Maximum expansion depth.
Aggregates the outcome of one search execution.
bool limit_reached() const noexcept
Return true if a hard search limit stopped the traversal.
SearchStats stats
Statistics collected during the run.
bool found_solution() const noexcept
Return true if at least one solution was retained.
bool stopped_on_solution() const noexcept
Return true if search stopped because solution handling requested it.
ExplorationPolicy policy
Exploration policy used for the run.
Solution Solution_Type
Type of solutions in this result.
SearchResult()=default
Build an empty result.
Compare Compare_Type
Type of the comparison policy.
bool exhausted() const noexcept
Return true if the search exhausted the configured region.
SearchLimits limits
Limits used for the run.
SearchStatus status
Final execution state.
Incumbent_Type best_solution
Best incumbent retained by the engine.
SearchResult(Compare compare)
Build a result with a specific solution comparator.
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 limit_hits
Number of hard-limit stops triggered.
size_t pruned_by_visited
States skipped because already seen (visited-set duplicate suppression).
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 max_depth_reached
Deepest path depth visited.
size_t solutions_found
Number of goal states accepted.
size_t expanded_states
Number of non-terminal states expanded.
size_t visited_states
Number of states entered by the engine.
Dynamic array container with automatic resizing.
Unified hash table interface.