@@ -58,6 +58,9 @@ concept heuristic_search_heuristic =
58
58
{ H.operator ()(S, ctx...) } -> std::convertible_to<double >;
59
59
};
60
60
61
+ template <typename Heurisitc>
62
+ concept is_admissible = (Heurisitc::is_admissible);
63
+
61
64
62
65
/* ===== Search Algorithm =============================================================================================*/
63
66
@@ -85,6 +88,7 @@ concept heuristic_search =
85
88
template <
86
89
heuristic_search_state State,
87
90
typename Expand,
91
+ typename Heurisitc,
88
92
bool HasRegularQueue,
89
93
bool HasBeamQueue,
90
94
typename Config,
@@ -252,9 +256,12 @@ struct StateManager
252
256
static_assert (ToBeamQueue or HasRegularQueue, " not ToBeamQueue implies HasRegularQueue" );
253
257
254
258
if constexpr (enable_cost_based_pruning) {
255
- /* ----- Fast path: if the current state is already more costly than the cheapest complete path found so far,
256
- * we can safely ignore that state. -----*/
257
- if (state.g () >= least_path_cost) [[unlikely]] { // initially unlikely, as we first have to find *a* path
259
+ /* Early pruning: if the current state is already more costly than the cheapest complete path found so far,
260
+ * we can safely ignore that state. Additionally, if the heuristic is admissible, we know that the
261
+ * remaining cost from the current state to the goal is *at least* `h`. Therefore, any complete path from
262
+ * start to goal that contains this state has cost at least `g + h`. */
263
+ const auto min_path_cost = M_CONSTEXPR_COND (is_admissible<Heurisitc>, state.g () + h, state.g ());
264
+ if (min_path_cost >= least_path_cost) [[unlikely]] {
258
265
inc_pruned_by_cost ();
259
266
return ;
260
267
} else if (expand_type::is_goal (state, context...)) [[unlikely]] {
@@ -330,9 +337,12 @@ struct StateManager
330
337
push<false >(std::move (state), h, context...);
331
338
} else {
332
339
if constexpr (enable_cost_based_pruning) {
333
- /* ----- Fast path: if the current state is already more costly than the cheapest complete path found
334
- * so far, we can safely ignore that state. -----*/
335
- if (state.g () >= least_path_cost) [[unlikely]] {
340
+ /* Early pruning: if the current state is already more costly than the cheapest complete path found
341
+ * so far, we can safely ignore that state. Additionally, if the heuristic is admissible, we know
342
+ * that the remaining cost from the current state to the goal is *at least* `h`. Therefore, any
343
+ * complete path from start to goal that contains this state has cost at least `g + h`. */
344
+ const auto min_path_cost = M_CONSTEXPR_COND (is_admissible<Heurisitc>, state.g () + h, state.g ());
345
+ if (min_path_cost >= least_path_cost) [[unlikely]] {
336
346
inc_pruned_by_cost ();
337
347
return ;
338
348
} else if (expand_type::is_goal (state, context...)) [[unlikely]] {
@@ -450,12 +460,13 @@ struct StateManager
450
460
template <
451
461
heuristic_search_state State,
452
462
typename Expand,
463
+ typename Heurisitc,
453
464
bool HasRegularQueue,
454
465
bool HasBeamQueue,
455
466
typename Config,
456
467
typename ... Context
457
468
>
458
- bool StateManager<State, Expand, HasRegularQueue, HasBeamQueue, Config, Context...>::comparator::
469
+ bool StateManager<State, Expand, Heurisitc, HasRegularQueue, HasBeamQueue, Config, Context...>::comparator::
459
470
operator ()(StateManager::pointer_type p_left, StateManager::pointer_type p_right) const
460
471
{
461
472
auto left = static_cast <typename map_type::value_type*>(p_left);
@@ -547,6 +558,7 @@ struct genericAStar
547
558
548
559
StateManager</* State= */ State,
549
560
/* Expand= */ Expand,
561
+ /* Heurisitc= */ Heuristic,
550
562
/* HasRegularQueue= */ not (use_beam_search and is_monotone),
551
563
/* HasBeamQueue= */ use_beam_search,
552
564
/* Config= */ Config,
0 commit comments