A*+BFHS: A Hybrid Heuristic Search Algorithm
Abstract
We present a new algorithm A*+BFHS for solving problems where A* and IDA* fail due to memory limitations and/or the existence of many short cycles.
A*+BFHS is based on A* and breadth-ﬁrst heuristic search (BFHS).
A*+BFHS combines advantages from both algorithms, namely A*’s node ordering, BFHS’s memory savings, and both algorithms’ duplicate detection.
On easy problems, A*+BFHS behaves the same as A*.
On hard problems, it is slower than A* but saves a large amount of memory.
Compared to BFIDA*, A*+BFHS reduces the search time and/or memory requirement by several times on a variety of planning domains.
Introduction and Overview
A* (Hart, Nilsson, and Raphael 1968) is a classic heuristic search algorithm that is used by many state-of-the-art optimal track planners (Katz et al.
; Franco et al. 2017, 2018; Martinez et al. 2018).
One advantage of A* is duplicate detection.
A* uses a Closed list and an Open list to prune duplicate nodes.
A state is a unique conﬁguration of the problem while a node is a data structure that represents a state reached by a particular path.
Duplicate nodes represent the same state arrived at via different paths.
The second advantage of A* is node ordering.
A* always picks an Open node whose f -value is minimum among all
Open nodes to expand next, which guarantees an optimal solution returned by A* when using an admissible heuristic.
When using a consistent heuristic, A* expands all nodes whose f -value is less than the optimal solution cost ( C ∗ ).
However, tie-breaking among nodes of equal f -value signiﬁcantly affects the set of expanded nodes whose f -value equals C ∗ .
It is common practice to choose an Open node whose h -value is minimum among all Open nodes with the same f -value, as this strategy usually leads to fewer nodes expanded.
A survey of tie-breaking strategies in A* can be found in (Asai and Fukunaga 2016).
A*’s main drawback is its exponential space requirement as it stores in memory all nodes generated during the search.
For example, A* can ﬁll up 8 GB of memory in a few minutes on common heuristic search and planning domains.
To solve hard problems where A* fails due to memory limitations, researchers have proposed various algorithms, usually by forgoing A*’s duplicate detection or node ordering.
For example, Iterative-Deepening-A* (IDA*, Korf 1985) only has a linear memory requirement, at the price of no duplicate detection and a depth-ﬁrst order within each search bound.
However, IDA* may generate too many duplicate nodes on domains containing lots of short cycles, such as Towers of
Hanoi and many planning domains, limiting its application.
This paper introduces a new algorithm for solving hard problems with many short cycles, where IDA* is not effective.
First, we review previously developed algorithms.
Second, we present our algorithm A*+BFHS, which is based on A* and Breadth-First Heuristic Search (Zhou and Hansen 2004).
Third, we present experimental results on 32 hard instances from 18 International Planning Competition (IPC) domains.
On those problems, A*+BFHS is slower than A* but requires signiﬁcantly less memory.
Compared to BFIDA*, which is an algorithm that requires less memory than A*, A*+BFHS reduces the search time and/or memory requirement by several times, and sometimes by an order of magnitude, on a variety of domains.
Previous Work
IDA* with a transposition table (IDA*+TT, Sen and Bagchi 1989; Reinefeld and Marsland 1994) uses a transposition table to detect duplicate nodes.
However, IDA*+TT is outperformed by other algorithms on both heuristic search (Bu and
Korf 2019) and planning domains (Zhou and Hansen 2004).
A*+IDA* (Bu and Korf 2019) combines A* and IDA*, and is the state-of-the-art algorithm on the 24-Puzzle.
It ﬁrst runs A* until memory is almost full, then runs IDA* below each frontier node without duplicate detection.
By sorting the frontier nodes with the same f -value in increasing order of h -values, A*+IDA* can signiﬁcantly reduce the number of nodes generated in its last iteration.
Compared to IDA*, we reported a reduction by a factor of 400 in the total number of nodes generated in the last iteration on all 50 24Puzzle test cases in (Korf and Felner 2002).
Similar to IDA*, A*+IDA* does not work well on domains with many short cycles, however, as in many planning domains.
Frontier search (Korf et al. 2005) is a family of heuristic search algorithms that work well on domains with many short cycles.
Rather than storing all nodes generated, it stores only nodes that are at or near the search frontier, including all Open nodes and only one or two layers of Closed nodes.
As a result, when a goal node is expanded, only the optimal cost is known.
To reconstruct the solution path, fron- tier search keeps a middle layer of Closed nodes in memory.
For example, we can save the Closed nodes at depth h ( start ) / 2 as the middle layer.
Each node generated below this middle layer has a pointer to its ancestor in the middle layer.
After discovering the optimal cost, a node in the middle layer that is on an optimal path is identiﬁed.
Then the same algorithm can be applied recursively to compute the solution path from the start node to the middle node, and from the middle node to the goal node.
In general, however, frontier search cannot prune all duplicates in directed graphs (Korf et al. 2005;
Zhou and Hansen 2004).
Divide-and-Conquer Frontier-A* (DCFA*, Korf and
Zhang 2000) is a best-ﬁrst frontier search based on A*.
To reconstruct the solution path, DCFA* keeps a middle layer of Closed nodes that are roughly halfway along the solution path.
DCFA* detects duplicates and maintains A*’s node ordering, but its memory savings compared to A* is limited on domains where the Open list is larger than the Closed list.
Breadth-First Heuristic Search (BFHS, Zhou and Hansen 2004) is a frontier search algorithm for unit-cost domains.
BFHS also detects duplicates but uses a breadth-ﬁrst node ordering instead of A*’s best-ﬁrst ordering.
At ﬁrst, assume the optimal cost C ∗ is known in advance.
BFHS runs a breadth-ﬁrst search (BFS) from the start node and prunes every generated node whose f -value exceeds C ∗ .
To save memory, BFHS only keeps a few layers of nodes in memory.
On undirected graphs, if we store the operators used to generate each node, and do not regenerate the parents of a node via the inverses of those operators, frontier search only needs to store two layers of nodes, the currently expanding layer and their child nodes (Korf et al. 2005).
On directed graphs, one previous layer besides the above-mentioned two layers is usually stored to detect duplicates (Zhou and Hansen 2004).
To reconstruct the solution path, Zhou and Hansen (2004) recommend saving the layer at the 3/4 point of the solution length as the middle layer instead of the layer at the halfway point, which usually requires more memory.
As shown in (Zhou and Hansen 2004), on a domain where the
Open list of A* is larger than the Closed list, BFHS usually ends up storing fewer nodes than DCFA*.
In general, C ∗ is not known in advance.
Breadth-First Iterative-Deepening-A* (BFIDA*, Zhou and Hansen 2004) overcomes this issue by running multiple iterations of BFHS, each with a different f -bound, starting with the heuristic value of the start node.
Similar to IDA*, the last iteration of BFIDA* is often signiﬁcantly larger than previous iterations, so most search time is spent on the last iteration on many domains.
Compared to A*, BFHS and BFIDA* save signiﬁcant memory but generate more nodes.
The main drawback of
BFHS and BFIDA* is that their node ordering is almost the worst among different node ordering schemes.
BFHS and
BFIDA*’s breadth-ﬁrst ordering means they have to expand all nodes stored at one depth before expanding any nodes in the next depth.
As a result, they have to expand almost all nodes whose f -value equals C ∗ , excepting only some nodes at the same depth as the goal node, while A* may only expand a small fraction of such nodes due to its node ordering.
Forward Perimeter Search (FPS, Sch¨utt, D¨obbelin, and {+6 S 
+}
{+A 7 D 8 H 
+}
I 8 E 8 B 7 C 8 F 7 G 8 J 8 K
Reinefeld 2013) builds a perimeter around the start node via
, then runs
[Equation]
Algorithm Description
We propose a hybrid algorithm we call A*+BFHS to solve hard problems with many short cycles.
A*+BFHS ﬁrst runs
A* until a storage threshold is reached, then runs a series of BFHS iterations on sets of frontier nodes, which are the
Open nodes at the end of the A* phase.
The BFHS phase can be viewed as a doubly nested loop.
Each iteration of the outer loop, which we deﬁne as an iteration of the BFHS phase, corresponds to a different cost bound for BFHS.
The ﬁrst cost bound is set to the smallest f -value among all frontier nodes.
In each iteration of the
BFHS phase, we ﬁrst partition the frontier nodes whose fvalue equals the cost bound into different sets according to their depths.
Then the inner loop makes one call to BFHS on each set of frontier nodes, in decreasing order of their depths.
This is done by initializing the BFS queue of each call to BFHS with all the nodes in the set.
This inner loop continues until a solution is found or all calls to BFHS with the current bound fail to ﬁnd a solution.
After each call to
BFHS on a set of frontier nodes, we increase the f -value of all nodes in the set to the minimum f -value of the nodes generated but not expanded in the previous call to BFHS.
Figure 1 presents an example of the Open and Closed nodes at the end of the A* phase.
Node S is the start node.
All edge costs are 1 and the number in each node is its fvalue.
Closed nodes are gray.
The Open nodes B, E, F, H, I, J, K are the frontier nodes for the BFHS phase.
A*+BFHS ﬁrst makes a call to BFHS with a cost bound of 8 on all frontier nodes at depth 3, namely nodes H, I, J, K.
If no solution is found, A*+BFHS updates the f -values of all these nodes to the minimum f -value of the nodes generated but not expanded in that call to BFHS.
A*+BFHS then makes a second call to BFHS with bound 8, starting with all frontier nodes at depth 2, namely nodes E and F.
If no solution is found, A*+BFHS updates the f -values of these nodes, then makes a third call to BFHS with bound 8, starting with the frontier node B at depth 1.
Suppose that no solution is found with bound 8, the updated f -values for nodes E, F, H, I, J, K are 9, and the updated f -value for node B is 10.
A*+BFHS then starts a new iteration of BFHS with a cost bound of 9, making two calls to BFHS on nodes at depth 3 and 2 respectively.
If the solution is found in the ﬁrst call to BFHS with bound 9, BFHS will not be called again on nodes E and F.
A*+BFHS is complete and admissible when using an admissible heuristic. A*+BFHS potentially makes calls to BFHS on all frontier nodes.
When an optimal solution exists, one node on this optimal path will serve as one of the start nodes for one of the calls to BFHS.
Such a node is guaranteed to exist by A*’s completeness and admissibility. Then when the cost bound for the calls to BFHS equals C ∗ , the optimal solution will be found, guaranteed by BFHS’s completeness and admissibility. A state can be regenerated in separate calls to BFHS in the same iteration.
To reduce such duplicates, we can decrease the number of calls to BFHS in each iteration by making each call to BFHS on a combined set of frontier nodes at adjacent depths.
For the example in Figure 1, we can make one call to BFHS on the frontier nodes at depths 2 andtogether instead of two separate calls to BFHS, by putting the frontier nodes at depth 3 after the frontier nodes at depth 2 in the initial BFS queue.
In practice, we can specify a maximum number of calls to BFHS per iteration.
Then in each iteration, we divide the number of depths of the frontier nodes by the number of calls to BFHS to get the number of depths for each call to
BFHS.
For example, if the depths of the frontier nodes range from 7 to 12 and we are limited to three calls to BFHS per iteration, each call to BFHS will start with frontier nodes at two depths.
We used this strategy in our experiments.
For each node generated in the BFHS phase, we check if it was generated in the A* phase.
If so, we immediately prune the node if its current g -value in the BFHS phase is greater than or equal to its stored g -value in the A* phase.
The primary purpose of the A* phase is to build a frontier set, so that A*+BFHS can terminate early in its last iteration.
In the A* phase we have to reserve some memory for the
BFHS phase.
In our experiments, we ﬁrst generated pattern databases or the merge-and-shrink heuristic, then allocated 1/10 of the remaining memory of 8 GB for the A* phase.
Comparisons to BFIDA* and FPS
A*+BFHS’s BFHS phase also uses the iterative deepening concept of BFIDA*, but there are two key differences.
First, in each iteration, BFIDA* always makes one call to BFHS on the start node, while we call BFHS multiple times, each on a different set of frontier nodes.
Second, in each iteration, we order the frontier nodes based on their depth, and run BFHS on the deepest frontier nodes ﬁrst.
These differences lead to one drawback and two advantages.
The drawback is that A*+BFHS may generate more nodes than BFIDA*, as the same state can be regenerated in separate calls to BFHS in the same iteration.
The ﬁrst advantage is that A*+BFHS may terminate early in its last iteration.
If A*+BFHS generates a goal node in the last iteration below a relatively deep frontier node, no frontier nodes above that depth will be expanded.
Therefore,
A*+BFHS may generate only a small number of nodes in its last iteration.
In contrast, BFIDA* has to expand almost all nodes whose f -value is less than or equal to C ∗ in its last iteration.
As a result, A*+BFHS can be faster than BFIDA*.
The second advantage is that A*+BFHS’s memory usage, which is the maximum number of nodes stored during the entire search, may be smaller than that of BFIDA* for two reasons.
First, the partition of frontier nodes and separate calls to BFHS within the same iteration can reduce the maximum number of nodes stored in the BFHS phase.
Second, BFIDA* stores the most nodes in its last iteration while
A*+BFHS may store only a small number of nodes in the last iteration due to early termination.
Thus, A*+BFHS may store the most nodes in the penultimate iteration instead.
FPS looks similar to A*+BFHS, but there are several fundamental differences.
First, FPS builds the perimeter using a breadth-ﬁrst approach while A*+BFHS builds the frontier via a best-ﬁrst approach.
FPS can also dynamically extend the perimeter but this approach does not always speed up the search (Sch¨utt, D¨obbelin, and Reinefeld 2013).
Second, in each iteration of FPS’s BFIDA* phase, FPS makes one call to BFHS on each perimeter node.
In contrast, in A*+BFHS each call to BFHS is on a set of frontier nodes.
Third, FPS sorts the perimeter nodes at the same f -value using a maxtree-ﬁrst or longest-path-ﬁrst policy, while A*+BFHS sorts the frontier nodes at the same f -value in decreasing order of their depth.
Fourth, FPS needs two separate searches for solution reconstruction while A*+BFHS only needs one.
Solution Reconstruction
Each node generated in A*+BFHS’s BFHS phase has a pointer to its ancestral frontier node.
When a goal node is generated, the solution path from the start node to the ancestral frontier node is stored in the A* phase and only one more search is needed to reconstruct the solution path from the ancestral frontier node to the goal node.
This subproblem is much easier than the original problem and we can use the same heuristic function as for the original problem.
Therefore, we just use A* to solve this subproblem.
In addition, since we know the optimal cost of this subproblem, we can prune any node whose f -value exceeds this cost.
In BFIDA*, we have to solve two subproblems to recover the solution path from the start node to the middle node and from the middle node to the goal node. Zhou and
Hansen (2004) called BFHS recursively to solve these two subproblems.
However, pattern database heuristics (PDB, Culberson and Schaeffer 1998) only store heuristic values to the goal state, and not between arbitrary pairs of states, which complicates ﬁnding a path to a middle node.
Similar to A*+BFHS, we use A* to solve the second subproblem. For the ﬁrst subproblem, we use A* to compute the path from the start node to the middle node using the same heuristic function as for the original problem, which measures the distance to the goal node, not the middle node.
To save memory, we prune any node whose g -value is greater than or equal to the depth of the middle node, and any node whose f -value exceeds the optimal cost of the original problem.
Since a deeper middle layer leads to more nodes stored in this approach, we saved the layer at the 1/4 point of the solution length as the middle layer instead of the 3/4 point.
In this way, we do not need to build a new heuristic function for the middle node.
In our experiments, the search time for solution reconstruction in BFIDA* is usually less than 1% of the total search time.
Experimental Results and Analysis
We implemented BFIDA* and A*+BFHS in the planner Fast
Downward 20.06 (Helmert 2006), using the existing code for node expansion and heuristic value lookups.
A*+BFHS’s
A* phase reused the existing A* code.
A* stores all nodes in one hash map.
We used the same hash map implementation with the following difference.
In each call to BFHS in both
BFIDA* and A*+BFHS, we saved three layers of nodes for duplicate detection and we created one hash map for each layer of nodes.
We did this because storing all nodes in one hash map in BFHS involves a lot of overhead, and is more complicated.
Sch¨utt, D¨obbelin, and Reinefeld (2013) did not test FPS on planning domains and we do not know the optimal perimeter radius and sorting strategy for each domain, so we did not implement FPS in Fast Downward.
We solved about 550 problem instances from 32 unitcost domains.
We present the results of A*, BFIDA*, and A*+BFHS on the 32 hardest instances.
All remaining instances were easily solved by A*.
We tested two A*+BFHS versions.
A*+BFHS ( ∞ ) starts each call to BFHS on frontier nodes at one depth.
A*+BFHS (4) makes each call to
BFHS on frontier nodes at multiple depths with at most four calls to BFHS in each iteration.
All tests were run on a 3. GHz Intel Xeon X5680 CPU with 236 GB of RAM.
We used the landmark-cut heuristic (LM-cut, Helmert and Domshlak 2009) for the satellite domain, the merge-and-shrink heuristic (M&S) with the recommended conﬁguration (Sievers, Wehrle, and Helmert 2014, 2016; Sievers 2018) for the tpp and hiking14 domains, and the iPDB heuristic with the default conﬁguration (Haslum et al.
; Sievers, Ortlieb, and Helmert 2012) for all other domains.
We present the results in Tables 1, 2, and 3.
Tables 1 andcontain the 26 hardest instances solved by A*.
Table 3 contains the remaining 6 instances where A* terminated early without ﬁnding a solution due to the limitation of the hash map size in Fast Downward 20.06.
The instances in Tables 1 and 2 are sorted by the A* running times and the instances in Table 3 are sorted by the BFIDA* running times.
All three tables have the same columns.
The ﬁrst column gives the domain name, the instance ID, the optimal solution cost C ∗ , and the heuristic function used.
The second column lists the different algorithms.
We ran each algorithm until it found an optimal cost and returned the optimal path.
The third column gives the maximum number of nodes stored by each algorithm.
For A*, this is the number of nodes stored at the end of the search.
For BFIDA*, this is the largest sum of the number of nodes stored in all three layers of the search, plus the nodes stored in the 1/4 layer for solution reconstruction.
For A*+BFHS, this is the largest number of nodes stored in the BFHS phase plus the number of nodes stored in the A* phase.
An underline means the speciﬁc algorithm needed more than 8 GB of memory to solve the problem.
The fourth column is the total number of nodes generated, including the nodes generated during solution reconstruction.
The ﬁfth column is the number of nodes generated in all        0 .0 .0 .0 .x = A* peak stored # / A*+BFHS peak stored # A * ti m e / A * + B
F H S ti m e A*+BFHS (4) A*+BFHS ( ∞ ) but the last iteration. For A*, this is the number of nodes generated before expanding an Open node whose f -value is C ∗ .
For A*+BFHS, this number includes the nodes generated in its A* phase.
The sixth column is the number of nodes generated in the last iteration.
For A*, this is the number of nodes generated while expanding the Open nodes whose f -value equals C ∗ .
The last column is the running time in seconds, including the time for solution reconstruction but excluding the time spent on precomputing the heuristic function, which is the same for all algorithms.
For each instance, the smallest maximum number of stored nodes and shortest running time are indicated in boldface.
For the A* data in Table 3, we report the numbers of nodes and running times just before A* terminated, with a > symbol to indicate such numbers. We further compare the time and memory between A* and A*+BFHS in Figure 2, and between BFIDA* and A*+BFHS in Figure 3, where the x -axis is A*/BFIDA*’s peak stored nodes over A*+BFHS’s and the y -axis is A*/BFIDA*’s running time over A*+BFHS’s.
Figure 2 contains the 26 instances solved by A* and Figure 3 contains all 32 instances.
The red circles and green triangles correspond to A*+BFHS (4) and A*+BFHS ( ∞ ) respectively.
The data points above the y = 1 line or to the right of the x =line represent instances where A*+BFHS outperformed the comparison algorithm in terms of time or memory. A*+BFHS vs. A* A* was the fastest on all problem instances that it solved, but also used the most memory. Among the 32 hardest problem instances we present, A* required more than 8 GB of memory on 22 instances and could not ﬁnd a solution on 6 of those after running out of the hash map used by Fast
Downward 20.06.
On some of these instances, A* used
GB to 40 GB of memory before it terminated.
This means
A* cannot solve these 22 instances under the current IPC memory requirement, which is 8 GB.
A*+BFHS required several times, sometimes an order of magnitude, less mem- y
=x =
BFIDA* peak stored # / A*+BFHS peak stored #
B F
I
DA * ti m e / A
* + B
F H S ti ory than A*.
As a result, A*+BFHS only used more than
GB of memory on one instance.
An interesting comparison is the space and time trade-off.
For example, on parking14,
A*+BFHS increased the running time by less than 100% while saving more than an order of magnitude in memory.
A*+BFHS vs. BFIDA*
In summary, on easy problems that A*+BFHS can solve in its A* phase, A*+BFHS behaves the same as A*, and is always faster than BFIDA*.
We solved around 500 such problems, which are not included here due to space limitations.
On the 32 hardest problems we present, A*+BFHS is faster than BFIDA* on 27 instances and at least twice as fast onof those.
Furthermore, A*+BFHS requires less memory than
BFIDA* on 25 of the 32 instances and saves more than half the memory on 14 of those.
In addition, these time and memory reductions exist on both the relatively easy and hard ones of the 32 instances presented, demonstrating that A*+BFHS is in general better than BFIDA* on very hard problems as well as easy problems.
In the following paragraphs, we compare A*+BFHS with BFIDA* in four aspects: duplicate detection, node ordering, memory, and running times.
The relative numbers of nodes generated in the previous iterations reﬂect the power of duplicate detection.
Compared to BFIDA*, A*+BFHS (4) generated a similar number of nodes in the previous iterations on most instances.
Hiking 2-3-6 is the only instance where A*+BFHS (4) generated at least twice as many nodes in the previous iterations as
BFIDA*.
However, A*+BFHS ( ∞ ) generated 2 to 7 times as many nodes in the previous iterations as BFIDA* on 11 instances.
This contrast shows that, compared to BFIDA*, signiﬁcantly more duplicate nodes can be generated by making each call to BFHS on frontier nodes at only one depth.
However, most of those duplicate nodes can be avoided by making each call to BFHS on frontier nodes at multiple depths.
A*+BFHS can generate fewer duplicate nodes than
BFIDA* due to fewer BFHS iterations and making each call to BFHS on a set of frontier nodes.
A*+BFHS reduced the number of nodes in previous iterations by around 50% on freecell 06 and snake18 17, and a factor of 4 on snake18 08.
To our surprise, we found that on snake18 08, the number of nodes generated in the penultimate iteration of BFIDA* was twice as many as the sum of the nodes generated in
A*+BFHS’s A* phase and the penultimate iteration of the
BFHS phase.
This means a lot of duplicate nodes were generated in BFIDA*.
Snake18 generates a directed graph, in which case frontier search cannot detect all duplicate nodes (Korf et al. 2005;
Zhou and Hansen 2004).
Compared to BFIDA*, A*+BFHS reduced the number of nodes in the last iteration signiﬁcantly, and usually by several orders of magnitude, on 28 of the 32 instances.
This large reduction proves that when ordering the frontier nodes by deepest-ﬁrst, A*+BFHS can terminate early in its last iteration.
On the three blocks instances and depot 11,
A*+BFHS did not terminate early in its last iteration because the ancestral frontier node of the goal had a relatively low g -value.
In fact, A* generated the most nodes in its last iteration on the three blocks instances, which shows that node ordering is also difﬁcult for A* on those instances. In contrast, A* generated very few nodes in its last iteration on depot 11, suggesting that A*+BFHS may terminate early in its last iteration given more memory for its A* phase. A*+BFHS’s A* phase usually stored from 10 to 20 million nodes, with the exception of the snake18 domain where 40 to 50 million nodes were stored. Comparing the maximum number of stored nodes, A*+BFHS ( ∞ ) required less memory than BFIDA* on 25 instances and less than half the memory on 14 of those.
For A*+BFHS (4), these two numbers are 23 and 11 respectively. In contrast, termes18is the only instance where the maximum number of stored nodes of A*+BFHS was at least twice that of BFIDA*. Comparing the two versions of A*+BFHS, A*+BFHS (4) was usually faster, sometimes signiﬁcantly, due to the reduction in duplicate nodes. Compared to BFIDA*, A*+BFHS (4) was slightly slower on four instances and 80% slower on one instance.
On the other 27 instances, A*+BFHS was faster than BFIDA*, and at least twice as fast on 16 of those. The large speedups usually were on the instances where BFIDA* generated the most nodes in its last iteration. The best result was on the logistics00 domain, where an order of magnitude speedup was achieved.
This is because BFIDA* performed very poorly on this domain due to its breadth-ﬁrst node ordering.
Comparing A*+BFHS ( ∞ ) with BFIDA*,
A*+BFHS ( ∞ ) was slower on 11 instances and at least twice as slow on three of those, but also at least twice as fast oninstances. The main reason for the slower cases is the presence of many duplicate nodes generated in certain domains. Calling BFHS on Nodes at Multiple Depths Comparing the two A*+BFHS versions, each has its pros and cons.
A*+BFHS (4) always generated fewer duplicate nodes.
Comparing the number of nodes generated in the previous iterations, A*+BFHS ( ∞ ) generated at least twice as many nodes on 7 instances. A*+BFHS ( ∞ ) generated significantly fewer nodes in the last iteration than A*+BFHS (4) on 22 instances. However, the number of nodes generated in
[Table]
35,120, 403,465, 302,581, 100,875,  logistics
A* 57,689, 107,083, 106,929, 154,  14BFIDA* 15,441, 3,137,204, 106,929, 3,020,315, 10,381 C ∗ =
A*+BFHS ( ∞ ) 19,472, 354,438, 354,058, 368, 1,160 iPDB
A*+BFHS (4) 20,169, 227,903, 110,674, 117,217,  driverlog
A* 144,065, 420,609, 420,609,
BFIDA* 35,034, 1,718,350, 678,644, 1,030,180, 1,676 C ∗ =
A*+BFHS ( ∞ ) 24,712, 1,020,438, 1,020,410, 27, 944 iPDB
A*+BFHS (4) 30,270, 643,723, 641,790, 1,933,  freecell
A* 107,183, 531,379, 531,378,
BFIDA* 77,196, 4,152,881, 2,897,339, 1,143,762, 6,416 C ∗ =
A*+BFHS ( ∞ ) 54,171, 3,095,608, 2,370,094, 725,267, 4,775 iPDB
A*+BFHS (4) 58,058, 2,430,947, 1,896,369, 534,331, 3, depot
A* 172,447, 764,608, 764,607,
BFIDA* 27,192, 3,037,154, 1,260,718, 1,755,157, 3,544 C ∗ =
A*+BFHS ( ∞ ) 37,977, 6,268,318, 3,092,746, 3,175,552, 7,314 iPDB
A*+BFHS (4) 46,923, 3,319,995, 1,262,429, 2,057,547, 4, tpp
A* 187,011, 610,996, 610,995, 1,
BFIDA* 93,759, 4,290,825, 754,905, 3,525,135, 7,214 C ∗ =
A*+BFHS ( ∞ ) 30,856, 5,504,314, 5,504,268, 46, 9,550 M&S
A*+BFHS (4) 33,368, 1,419,143, 1,285,410, 133,732, 2, mystery
A* 139,924, 652,569, 650,036, 2,533,
C ∗ =
BFIDA* 135,963, 6,213,135, 727,753, 5,430,082, 7, iPDB
A*+BFHS ( ∞ /4) 20,302, 730,971, 676,473, 54,497,  tidybot
A* 69,953, 171,363, 170,286, 1,076,
BFIDA* 42,080, 776,084, 486,518, 281,131, 3,684 C ∗ =
A*+BFHS ( ∞ ) 33,969, 661,386, 467,282, 194,103, 3,223 iPDB
A*+BFHS (4) 37,090, 547,745, 397,125, 150,620, 2, logistics
A* 82,161, 167,974, 163,970, 4,004,  15BFIDA* 13,638, 2,847,571, 163,970, 2,660,698, 19,062 C ∗ =
A*+BFHS ( ∞ ) 18,827, 730,154, 722,390, 7,763, 4,897 iPDB
A*+BFHS (4) 18,827, 251,960, 198,537, 53,422, 1, pipesworldA* 123,553, 284,884, 284,880, 4,  notankage
BFIDA* 86,818, 1,227,115, 634,454, 576,633, 4,140 C ∗ =
A*+BFHS ( ∞ ) 42,192, 619,095, 619,013, 81, 2,072 iPDB
A*+BFHS (4) 44,706, 574,957, 570,451, 4,505, 1, parking
A* 351,976, 828,472, 828,472,   16 9BFIDA* 183,832, 4,846,132, 1,023,897, 3,821,980, 6,236 C ∗ =
A*+BFHS ( ∞ ) 30,675, 1,191,570, 1,191,514, 55, 1,468 iPDB A*+BFHS (4) 51,147, 1,013,776, 1,011,227, 2,549, 1, visitall A* 407,182, 795,670, 795,669,  1, 08-half BFIDA* 172,474, 3,159,596, 1,332,828, 1,824,866, 4,220 C ∗ =
A*+BFHS ( ∞ ) 34,406, 1,639,641, 1,639,585, 55, 2,233 iPDB
A*+BFHS (4) 64,671, 1,346,690, 1,312,333, 34,356, 1,
Instance
Algorithm stored
Total nodes
Prev iterations
Last iteration
Time (s) tidybot
A* 115,965, 246,756, 246,756,  1,
BFIDA* 86,095, 1,090,011, 652,777, 431,816, 5,512 C ∗ =
A*+BFHS ( ∞ ) 41,342, 583,309, 570,082, 13,225, 2,923 iPDB
A*+BFHS (4) 57,026, 598,365, 519,723, 78,641, 3, snake
A* 94,699, 129,288, 129,273, 14, 1,
BFIDA* 44,231, 1,852,488, 1,517,078, 325,204, 14,877 C ∗ =
A*+BFHS ( ∞ ) 44,081, 391,010, 390,681, 328, 3,445 iPDB
A*+BFHS (4) 51,166, 356,988, 348,015, 8,973, 3, hiking
A* 287,192, 3,299,939, 3,299,937, 1, 1, 2-2BFIDA* 42,570, 11,376,337, 5,757,334, 5,582,502, 10,847 C ∗ =
A*+BFHS ( ∞ ) 44,454, 16,233,911, 12,346,881, 3,886,689, 14,897 M&S
A*+BFHS (4) 53,148, 9,850,751, 6,310,295, 3,540,114, 9, pipesworldA* 292,998, 907,283, 907,283,  1, tankage BFIDA* 158,262, 5,354,342, 3,680,871, 1,661,344, 10,609 C ∗ = A*+BFHS ( ∞ ) 84,077, 5,768,933, 5,763,927, 5,002, 11,622 iPDB A*+BFHS (4) 103,288, 3,300,541, 3,220,772, 79,765, 6, blocks
A* 555,864, 1,185,065, 205,172, 979,893, 1, 13BFIDA* 99,782, 1,742,819, 463,603, 1,224,383, 2,142 C ∗ =
A*+BFHS ( ∞ ) 54,601, 2,261,321, 425,991, 1,827,341, 2,817 iPDB
A*+BFHS (4) 79,572, 1,817,197, 401,559, 1,407,648, 2, parking
A* 606,117, 1,430,911, 1,430,746, 165, 1, 16 9BFIDA* 291,822, 8,077,642, 1,796,305, 6,280,923, 10,
C ∗ =
A*+BFHS ( ∞ ) 48,304, 2,519,414, 2,328,368, 191,043, 3,124 iPDB
A*+BFHS (4) 63,455, 2,151,415, 1,992,188, 159,224, 2, tidybot
A* 175,574, 372,772, 372,771,  1,
[Table] [Table]
∗ [Table]
A*+BFHS ( ∞ ) 40,540, 1,045,166, 1,028,635, 16,529, 5,
[Table]
A*+BFHS (4) 65,784, 1,204,942, 931,501, 273,439, 6, [Table]
[Table]
[Table]
A*+BFHS ( ∞ ) 81,918, 3,498,922, 774,231, 2,710,189, 4,
[Table]
A*+BFHS (4) 126,629, 2,615,897, 698,028, 1,903,367, 3, hiking
A* 368,433, 6,711,042, 6,710,971, 71, 2, 2-3BFIDA* 124,686, 38,476,138, 29,175,130, 8,123,329, 42,379 C ∗ =
A*+BFHS ( ∞ ) 146,623, 107,138,328, 106,429,883, 682,558, 120,494 M&S
A*+BFHS (4) 148,357, 68,496,320, 65,779,382, 2,691,051, 76, pipesworldA* 442,232, 1,028,882, 1,028,880, 1, 2, notankage
BFIDA* 301,349, 4,454,789, 2,384,958, 2,032,377, 15,
C ∗ =
A*+BFHS ( ∞ ) 133,708, 3,325,668, 3,267,529, 58,132, 11, iPDB
A*+BFHS (4) 148,029, 2,988,248, 2,728,140, 260,097, 10, snake
A* 265,033, 367,639, 365,927, 1,712, 3,
BFIDA* 60,041, 2,162,411, 1,464,995, 639,565, 20,418 C ∗ =
A*+BFHS ( ∞ ) 56,839, 877,
[Table]
the last iteration of A*+BFHS is usually only a small portion of the total nodes generated, so the large difference in the last iteration is not very important.
A*+BFHS (4) stored a larger maximum number of nodes than A*+BFHS ( ∞ ) on almost all instances.
However, the difference was usually small and never more than a factor of two.
For the running time, the difference was usually less than 50%.
Compared to
A*+BFHS ( ∞ ), A*+BFHS (4) was faster by a factor of 3 on logistics00 15-1, 2.5 on rovers 09 and 11, 4.6 on termes 05, 3.9 on tpp 11, and never more than 30% slower.
In general, making each call to BFHS on frontier nodes at multiple depths increases both the memory usage and the number of nodes generated in the last iteration, but reduces the number of duplicate nodes and hence is often faster.
Con-
[Table] [Table] [Table]
[Table]
[Table] [Table] [Table] [Table]
[Table]
[Table]
[Table]
parking
A* (unfinished) > 770,
[Table]
sidering the memory and time trade-off, given a new problem, we recommend making each call to BFHS on frontier nodes at multiple depths.
So far, we have only tested limiting
BFHS to four calls in each iteration.
Determining the optimal number of calls to BFHS is a subject for future work.
Heuristic Functions and Running Times
For each node generated, A* ﬁrst does duplicate checking then looks up its heuristic value if needed.
Thus for each state, A* only computes its heuristic value once, no matter how many times this state is generated.
However, the situation is different in BFHS.
Even in a single call to BFHS, a state’s heuristic value may be calculated multiple times.
For example, if a state’s f -value is greater than the cost bound of
BFHS, then this state is never stored in this call to BFHS and its heuristic value has to be computed every time it is generated.
In addition, A* has only one hash map but our BFHS implementation has one hash map for each layer of nodes.
Consequently, for each node generated, A* does only one hash map lookup while BFHS may have multiple lookups.
Due to the above differences, the number of nodes generated per second of BFIDA* and A*+BFHS was smaller than that of A*.
For the iPDB and M&S heuristics, this difference was usually less than a factor of two.
For the LM-cut heuristic, A* was faster by a factor of four in terms of nodes generated per second on the satellite domain.
This is because computing a node’s LM-cut heuristic is much more expensive than iPDB and M&S heuristics.
This contrast shows that the choice of heuristic function also plays an important role in comparing the running times of different algorithms.
Future Work
Future work includes the following.
First, test A*+BFHS on more unit-cost domains.
Second, investigate what is the best memory threshold for the A* phase.
Third, determine the optimal number of calls to BFHS in each iteration.
Fourth, ﬁnd other ways to partition the frontier nodes besides the current depth-based approach.
If a set of frontier nodes is too large, we may split it into multiple smaller sets and make one call to BFHS on each such smaller set.
This approach may reduce the maximum number of stored nodes but may generate more duplicate nodes.
In addition, when we make each call to BFHS on frontier nodes at multiple depths, we may consider the number of frontier nodes at each depth so each call to BFHS is on a different number of depths instead of a ﬁxed number.
Fifth, ﬁnd out how to apply A*+BFHS to domains with non-unit operator costs.
For such domains,
BFHS’s
BFS can be replaced by uniform-cost search or Dijkstra’s algorithm (Dijkstra 1959).
In this case, we can store nodes with multiple costs in each layer (Zhou and Hansen 2006).
Sixth, use external memory such as magnetic disk or ﬂash memory in A*+BFHS to solve very hard problems.
For example, instead of allocating 1/10 of RAM for the A* phase, we can ﬁrst run A* until RAM is almost full, then store both Open and Closed nodes in external memory and remove them from RAM.
Then in the BFHS phase, we load back the set of frontier nodes for each call to BFHS from external memory.
This A*+BFHS version would never perform worse than A*, since it is identical to A* until memory is exhausted, at which point the BHFS phase would begin.
Conclusions
We introduce a hybrid heuristic search algorithm A*+BFHS for solving hard problems that cannot be solved by A* due to memory limitations, or IDA* due to the existence of many short cycles.
A*+BFHS ﬁrst runs A* until a user-speciﬁed storage threshold is reached, then runs multiple iterations of
BFHS on the frontier nodes, which are the Open nodes at the end of the A* phase.
Each iteration has a unique cost bound and contains multiple calls to BFHS.
Each call to
BFHS within the same iteration has the same cost bound but a different set of frontier nodes to start with.
Within an iteration, frontier nodes are sorted deepest-ﬁrst so that A*+BFHS can terminate early in its last iteration.
On the around 500 easy problems solved, A*+BFHS behaves the same as A*, and is always faster than BFIDA*.
On the 32 hard instances presented, A*+BFHS is slower than
A* but uses signiﬁcantly less memory.
A*+BFHS is faster than BFIDA* on 27 of those 32 instances and at least twice as fast on 16 of those.
Furthermore, A*+BFHS requires less memory than BFIDA* on 25 of those 32 instances and saves more than half the memory on 14 of those.
Another contribution of this paper is a comprehensive testing of BFIDA* on many planning domains, which is lacking in the literature.