Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
State_Search_IDA_Star.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
49# ifndef STATE_SEARCH_IDA_STAR_H
50# define STATE_SEARCH_IDA_STAR_H
51
52#include <limits>
53#include <type_traits>
54#include <utility>
55
56#include <ah-errors.H>
57#include <state_search_common.H>
58#include <Backtracking.H>
59
60namespace Aleph {
61
73template <typename Domain>
76
85template <typename Distance>
93
102template <typename Solution, typename Distance>
113
114namespace ida_star_detail {
115
125template <typename Distance>
127{
128 if constexpr (std::is_floating_point_v<Distance>)
129 return std::numeric_limits<Distance>::infinity();
130 else
131 return std::numeric_limits<Distance>::max();
132}
133
135template <typename Distance>
141
148template <IDAStarDomain Domain, typename Solution, typename OnSolution>
151 typename Domain::State &state,
153 const typename Domain::Distance g,
154 const typename Domain::Distance threshold,
155 const size_t depth,
158{
159 using Distance = typename Domain::Distance;
160
161 ++result.stats.visited_states;
162 if (depth > result.stats.max_depth_reached)
163 result.stats.max_depth_reached = depth;
164
165 const Distance h = domain.heuristic(state);
166 const Distance f = g + h;
167
168 if (f > threshold)
169 return {false, f};
170
171 if (domain.is_goal(state))
172 {
173 ++result.stats.solutions_found;
174 Solution solution{state, path, depth};
175 if (result.best_solution.consider(solution))
176 result.total_cost = g;
177 const bool keep_searching = on_solution(solution);
180 return {true, threshold};
181 }
182
184 {
185 ++result.stats.terminal_states;
186 return {false, distance_unreachable<Distance>()};
187 }
188
189 if (search_engine_detail::should_prune_state(domain, state, depth))
190 {
191 ++result.stats.pruned_by_domain;
192 return {false, distance_unreachable<Distance>()};
193 }
194
195 if (depth >= result.limits.max_depth)
196 {
197 ++result.stats.pruned_by_depth;
198 return {false, distance_unreachable<Distance>()};
199 }
200
201 if (result.stats.expanded_states >= result.limits.max_expansions)
202 {
203 ++result.stats.limit_hits;
205 return {false, distance_unreachable<Distance>()};
206 }
207
208 ++result.stats.expanded_states;
209
210 using Move = typename Domain::Move;
212
213 (void) domain.for_each_successor(state,
214 [&](const Move &move) -> bool
215 {
218 return false;
219
221 const Distance step = domain.cost(state, move);
222 bool applied = false;
223 bool path_appended = false;
225 try
226 {
227 domain.apply(state, move);
228 applied = true;
229 path.append(move);
230 path_appended = true;
231 child = dfs(domain, state, path, g + step, threshold, depth + 1, result, on_solution);
232 }
233 catch (...)
234 {
235 if (path_appended)
236 (void) path.remove_last();
237 if (applied)
238 domain.undo(state, move);
239 throw;
240 }
241
242 (void) path.remove_last();
243 domain.undo(state, move);
244
245 if (child.found)
246 {
247 out.found = true;
248 return false;
249 }
250
253 return false;
254
255 if (child.next_bound < out.next_bound)
256 out.next_bound = child.next_bound;
257
258 return true;
259 });
260
261 return out;
262}
263
264} // end namespace ida_star_detail
265
284template <IDAStarDomain Domain>
286{
287public:
291 using State = typename Domain::State;
293 using Move = typename Domain::Move;
295 using Distance = typename Domain::Distance;
300
308 static constexpr bool supports_best_first = false;
309
316 const SearchLimits &limits = {})
318 {
319 // empty
320 }
321
324 {
325 return domain_;
326 }
327
330 {
331 return domain_;
332 }
333
336 {
337 return policy_;
338 }
339
342 {
343 return limits_;
344 }
345
347 void set_policy(const ExplorationPolicy &policy) noexcept
348 {
349 policy_ = policy;
350 }
351
353 void set_limits(const SearchLimits &limits) noexcept
354 {
355 limits_ = limits;
356 }
357
376
389 template <typename OnSolution>
392 {
394 << "IDA_Star_State_Search only supports depth-first strategy";
396 << "SearchLimits::max_solutions must be positive or Search_Unlimited";
397
398 Result result;
399 result.policy = policy_;
400 result.limits = limits_;
401
402 const auto start_time = SearchClock::now();
403
404 SearchPath<Move> path;
406
407 Distance threshold = domain_.heuristic(initial_state);
408
409 for (;;)
410 {
412 iteration.threshold = threshold;
413
414 const size_t stats_before = result.stats.visited_states;
415
416 auto [found, next_bound] = ida_star_detail::dfs(
417 domain_, initial_state, path, Distance{}, threshold, 0, result, on_solution);
418
419 iteration.visited_states = result.stats.visited_states - stats_before;
420 iteration.found_solution = found;
421 iteration.next_threshold = found ? threshold : next_bound;
422 result.iterations.append(iteration);
423
424 if (result.status == SearchStatus::StoppedOnSolution)
425 break;
426
427 if (found)
428 {
429 if (result.status != SearchStatus::LimitReached)
430 result.status = SearchStatus::StoppedOnSolution;
431 break;
432 }
433
434 if (result.status == SearchStatus::LimitReached)
435 break;
436
437 if (next_bound >= ida_star_detail::distance_unreachable<Distance>())
438 {
439 result.status = SearchStatus::Exhausted;
440 break;
441 }
442
443 threshold = next_bound;
444 }
445
446 if (result.status == SearchStatus::NotStarted)
448
449 result.stats.elapsed_ms = search_elapsed_ms(SearchClock::now() - start_time);
450
451 return result;
452 }
453
454private:
458};
459
461template <IDAStarDomain Domain>
463 typename Domain::State initial_state,
464 ExplorationPolicy policy = {},
465 SearchLimits limits = {})
466{
467 IDA_Star_State_Search<Domain> engine(std::move(domain), policy, limits);
468 return engine.search(std::move(initial_state));
469}
470
472template <IDAStarDomain Domain, typename OnSolution>
475 typename Domain::State initial_state,
477 ExplorationPolicy policy = {},
478 SearchLimits limits = {})
479{
480 IDA_Star_State_Search<Domain> engine(std::move(domain), policy, limits);
481 return engine.search(std::move(initial_state), on_solution);
482}
483
484} // end namespace Aleph
485
486# endif // STATE_SEARCH_IDA_STAR_H
Minimal depth-first backtracking engine for implicit state spaces.
Exception handling system with formatted messages for Aleph-w.
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
long double h
Definition btreepic.C:154
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
bool consider(const Solution &candidate)
Consider a candidate by copy.
IDA* engine for implicit state spaces with an admissible heuristic.
Domain & domain() noexcept
Mutable access to the bound domain adapter.
typename Domain::Move Move
Move type.
typename Domain::State State
Concrete search state type.
Domain Domain_Type
Type of the problem domain.
Result search(State initial_state, OnSolution &on_solution)
Run IDA* and invoke on_solution when the goal is reached.
const SearchLimits & limits() const noexcept
Current hard limits.
Result search(State initial_state)
Run IDA* from initial_state.
IDA_Star_State_Search(Domain domain, ExplorationPolicy policy={}, const SearchLimits &limits={})
Construct an engine bound to one domain adapter.
const ExplorationPolicy & policy() const noexcept
Current exploration policy.
const Domain & domain() const noexcept
Read-only access to the bound domain adapter.
static constexpr bool supports_best_first
Compile-time marker: IDA_Star_State_Search only supports Depth_First strategy.
void set_policy(const ExplorationPolicy &policy) noexcept
Replace the exploration policy for future runs.
void set_limits(const SearchLimits &limits) noexcept
Replace the hard limits for future runs.
Concept for domains that provide the cost of applying a move.
Minimal contract for DFS/backtracking domains.
Concept for domains that provide an admissible heuristic.
Minimal contract for IDA* domains.
Concept for callbacks invoked on each accepted solution.
DFSResult< typename Domain::Distance > dfs(Domain &domain, typename Domain::State &state, SearchPath< typename Domain::Move > &path, const typename Domain::Distance g, const typename Domain::Distance threshold, const size_t depth, IDAStarResult< Solution, typename Domain::Distance > &result, OnSolution &on_solution)
Core recursive DFS used by IDA* for a single threshold pass.
constexpr Distance distance_unreachable() noexcept
Sentinel value representing an unreachable or pruned f-cost.
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.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
auto ida_star_search(Domain domain, typename Domain::State initial_state, ExplorationPolicy policy={}, SearchLimits limits={})
Convenience wrapper for a one-shot IDA* search.
@ 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
@ Domain
Preserve the order emitted by for_each_successor().
STL namespace.
Common infrastructure for implicit state-space search.
Default solution visitor that always continues the search.
Exploration controls shared across engines.
Strategy strategy
Traversal strategy.
@ Depth_First
Recursive depth-first traversal (all engines).
Result of one threshold pass of IDA*.
bool found_solution
True if a goal was reached at or below threshold.
Distance next_threshold
Minimum f-value that exceeded threshold.
size_t visited_states
States visited during this pass.
Distance threshold
F-cost threshold used for this pass.
Aggregated result of a complete IDA* run.
Array< IDAStarIteration< Distance > > iterations
One entry per completed threshold pass.
Distance total_cost
Optimal path cost (valid when found_solution()).
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.
SearchStats stats
Statistics collected during the run.
ExplorationPolicy policy
Exploration policy used for the run.
SearchLimits limits
Limits used for the run.
SearchStatus status
Final execution state.
Incumbent_Type best_solution
Best incumbent retained by the engine.
Snapshot of a concrete solution encountered during the traversal.
size_t limit_hits
Number of hard-limit stops triggered.
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.
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.
Outcome of one recursive DFS step inside IDA*.
Distance accessor.
static mt19937 engine