Press Ctrl+D to draw

Drawing Tools

Log in for saved annotations

1px

2.3  Heuristic Search and A*

Uninformed search algorithms like breadth-first search (BFS) and uniform-cost search (UCS) explore the state space blindly, treating all nodes equally until a goal is found. For many problems, this is prohibitively slow: a warehouse robot navigating a 100×100 grid might expand thousands of nodes before reaching the goal. Informed search uses domain-specific knowledge in the form of a heuristic function to guide exploration toward promising states. The most celebrated informed algorithm is A*, which combines the path cost of UCS with a heuristic estimate to the goal. This section covers heuristics, their properties, and A*, then explores variants for practical applications where perfect solutions are less important than fast answers.

2.3.1 What is a Heuristic?

Definition 2.15  Heuristic Function

A heuristic function \(h(n)\) estimates the cost of the cheapest path from node \(n\) (with state \(s = \text{state}(n)\)) to a goal state. The estimate is used to prioritize which nodes to expand during search.

In the warehouse example, if the robot needs to reach a goal location \((g_x, g_y)\) from its current position \((x, y)\), a natural heuristic is the Manhattan distance:

\[h(n) = |x - g_x| + |y - g_y|\]

This estimate is cheaper than the true path cost because it ignores obstacles and robot dynamics—Manhattan distance assumes a straight-line-like path on a grid. Yet it provides useful guidance: there is often a cheaper path to the goal from states that are closer by Manhattan distance.

2.3.1.1 Admissibility and Consistency

Not all heuristics are equally useful. Two key properties determine whether a heuristic provides correctness guarantees.

Definition 2.16  Admissible Heuristic

A heuristic \(h(n)\) is admissible if it never overestimates the true cost to the goal. Formally: \(h(n) \leq c^*(n)\), where \(c^*(n)\) is the actual minimum cost from node \(n\) to a goal.

Intuition: An admissible heuristic is optimistic. It says "the goal is at least this far away," and the true minimum distance is never less.

Example: Manhattan distance on a grid with obstacles is admissible because obstacles only increase the path cost. Without obstacles, Manhattan distance is the minimum; with obstacles, it may underestimate.

Definition 2.17  Consistent Heuristic

A heuristic \(h(n)\) is consistent if for every node \(n\) and its successor \(n'\) reached by action \(a\) with step cost \(c(n, a, n')\):

\[h(n) \leq c(n, a, n') + h(n')\]

Intuition: This is admissibility applied to each step: the cost to get to \(n'\) must be more than or equal to the decrease in heuristic value. We see then why consistency is also called monotonicity.

Relationship: Every consistent heuristic is admissible. The converse is not always true: an admissible heuristic may not be consistent. However, consistency is the stronger property and is what enables efficient graph search in A*. In practice, most useful heuristics are consistent.

Example: Manhattan distance is consistent on a grid because for any step the heuristic can decrease by at most the step cost.

2.3.2 Heuristics for Warehouse Routing

Three common heuristics for pathfinding in warehouse domains:

2.3.2.1 Manhattan Distance

Definition 2.18  Manhattan Distance

The Manhattan distance (or "taxicab distance") between two points \((x_1, y_1)\) and \((x_2, y_2)\) is:

\[d_M = |x_1 - x_2| + |y_1 - y_2|\]

It represents the minimum number of moves on an axis-aligned grid without obstacles.

Consider a grid world where the robot can move only in the four cardinal directions. Is the heuristic:
Admissible? Yes because obstacles only increase cost
Consistent? Yes because any step can reduce distance by at most the step cost

The Manhattan distance is fast, simple, and effective when obstacles are sparse. However, since it ignores obstacles, it may lead a search astray in cluttered environments.

2.3.2.2 Euclidean Distance

Definition 2.19  Euclidean Distance

The Euclidean distance is the straight-line distance:

\[d_E = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}\]

In grid worlds where diagonal movement is allowed, Euclidean distance can provide a tighter estimate than Manhattan distance. Is the heuristic: Admissible? Yes (straight line is always shortest)
Consistent? On grids with diagonal moves (cost ~1.414), Euclidean may violate consistency with uniform costs. Generally consistent in continuous spaces where diagonal costs match \(\sqrt{2}\).

The Euclidean distance is more accurate than Manhattan in environments with diagonal movement; it dominates Manhattan distance (\(d_E < d_M\)), leading to fewer nodes expanded.

2.3.2.3 Congestion-Aware Heuristic

In multi-robot or dynamic warehouse environments, congestion—areas where many robots compete—can inflate path costs beyond simple distance metrics. A congestion-aware heuristic augments distance with occupancy data:

\[h(n) = d_M(\text{state}(n), \text{goal}) + \lambda \cdot \text{occupancy}(\text{state}(n))\]

where \(\text{occupancy}(s)\) reflects the density of robots or obstacles in the region near \(s\), and \(\lambda > 0\) is a weighting parameter.

Is the heuristic admissible? Requires careful tuning; naive congestion penalties can overestimate

For multi-robot coordination in dynamic environments, congestion-aware heuristics can significantly improve pathfinding efficiency by steering robots away from crowded areas.

2.3.3 A* Search

Now we formalize A*, which combines path cost and heuristic estimates.

Definition 2.20  A* Search

A* search expands nodes in order of the evaluation function:

\[f(n) = g(n) + h(n)\]

where \(g(n)\) is the cost from the start to node \(n\), and \(h(n)\) is the heuristic estimate from node \(n\) to a goal. A* uses a priority queue ordered by \(f(n)\).

Relationship to other algorithms:

  • If \(h(n) = 0\) for all nodes, A* becomes uniform-cost search (UCS)
  • If \(h(n)\) is the true cost to the goal, A* expands only nodes on the optimal path
  • A* balances exploration (via \(g(n)\), actual cost spent) and exploitation (via \(h(n)\), estimated benefit)

2.3.3.1 Pseudocode

Here is a standard A* implementation using graph search (which maintains an explored set):

def a_star(problem, h):
    node = Node(problem.initial_state, g=0, h=h(problem.initial_state))
    frontier = priority queue (ordered by f = g + h)
    frontier.add(node, priority=node.h)
    explored = set()
    
    while frontier not empty:
        node = frontier.pop()  # expand node with lowest f(n) first
        if problem.goal_test(node.state):
            return node
        explored.add(node.state)
        
        for action, child_state, step_cost in problem.successors(node.state):
            if child_state not in explored:
                g_child = node.g + step_cost
                h_child = h(child_state)
                child = Node(child_state, parent=node, action=action, 
                            g=g_child, h=h_child)
                # If child already in frontier with higher cost, update it
                frontier.add_or_update(child, priority=g_child + h_child)
    
    return None

A key variant is tree search (without the explored set), which allows revisiting states but guarantees optimality with admissible heuristics, though sometimes less efficiently.

2.3.3.2 Optimality of A*

For finite state spaces and positive action costs, A* is complete: if a solution exists, it will find one.

The optimality of A* depends on the heuristic used.

Theorem: A* with admissible heuristic

If the heuristic \(h(n)\) is admissible, A* will find an optimal path (lowest cost solution).

Since \(h(n)\) never overestimates, A* will not overlook a potentially optimal path. It expands nodes in order of increasing \(f(n)\), ensuring that once a goal is reached, no cheaper path exists.

If the heuristic is also consistent, A* avoids re-expanding nodes, making it more efficient.

2.3.4 Variants and Practical Tweaks

In practice, A* can be slow if the problem is large or the heuristic is weak. Several variants trade off solution quality for speed.

2.3.4.1 Weighted A*

Definition 2.21  Weighted A*

Weighted A* modifies the evaluation function:

\[f(n) = g(n) + w \cdot h(n)\]

where \(w > 1\) is a weight. Larger \(w\) emphasizes the heuristic, expanding fewer nodes but risking suboptimal solutions.

Behavior:

  • \(w = 1\): Standard A*, optimal (with admissible \(h\))
  • \(w > 1\): Faster but suboptimal (solution cost bounded by \(w \cdot c^*\), where \(c^*\) is optimal cost)
  • \(w \to \infty\): Greedy best-first search (expand lowest \(h(n)\) first)—very fast but often the solution is suboptimal

Warehouse example: A robot with a 10-hour battery might use \(w = 2\) to find a "good enough" path quickly, ensuring energy efficiency without insisting on the absolute optimal route.

Anytime search starts with a high weight (greedy, fast, suboptimal) and gradually decreases it over time, improving solution quality as computation allows. If interrupted, it returns the best solution found so far.

def anytime_a_star(problem, h, time_limit):
    best_solution = None
    w = w_initial  # start with high weight (e.g., 5)
    start_time = current_time()
    
    while current_time() - start_time < time_limit:
        solution = weighted_a_star(problem, h, w)
        if solution and (best_solution is None or 
                         cost(solution) < cost(best_solution)):
            best_solution = solution
        w = w - 0.1  # gradually decrease weight
    
    return best_solution

The advantages of the anytime approach are that it greedily provides a usable solution quickly and refines it as time permits. Use cases include real-time applications like robots navigating dynamic environments and live path replanning.

2.3.5 Evaluating Metrics

When comparing search algorithms, measure:

  1. Nodes expanded: How many nodes were added to the search tree?
    • Lower is better (efficiency indicator)
    • Plot as a bar chart comparing BFS, UCS, A* with different heuristics
  2. Path cost: What is the cost of the solution found?
    • Should be optimal for admissible heuristics
    • Plot cost vs. problem difficulty (grid size, obstacle density)
  3. Runtime: Wall-clock time to find a solution
    • Varies by implementation and hardware
    • Plot runtime vs. problem size, showing scaling behavior
  4. Heuristic quality: How well does \(h\) estimate actual costs?
    • Compute the ratio \(h(n) / c^*(n)\) for sample nodes. Higher (closer to 1, but \(\leq 1\)) is better (closer to true cost).
    • "Effective branching factor" \(b^*\): if a solution of depth \(d\) requires expanding \(N\) nodes, \(b^*\) satisfies \(N = 1 + b^* + (b^*)^2 + \ldots + (b^*)^d\). Lower \(b^*\) indicates a better heuristic.

2.3.6 Designing Domain-Specific Heuristics

For new domains, heuristics must be engineered carefully. This section gives practical guidance.

2.3.6.1 Relaxed Problems

A relaxed problem removes constraints from the original, making the goal easier to reach. The optimal solution cost of the relaxed problem is an admissible heuristic for the original.

Example: Warehouse routing

  • Original: navigate grid with obstacles, avoid other robots
  • Relaxation 1: navigate grid ignoring obstacles, with Manhattan distance as heuristic
  • Relaxation 2: navigate grid ignoring obstacles, with Euclidean distance as heuristic

Relaxation 2's heuristic is always \(\leq\) Relaxation 1's, so in a world without diagonal movement, the Manhattan distance is always better (i.e., closer to true cost). Globally, we call this the dominance of the larger heuristic. Larger is always better for admissible heuristics (which never overestimate) because it provides tighter estimates, leading to fewer node expansions in A*.

2.3.6.2 Composite Heuristics

If multiple relaxations exist, combine them using dominance:

\[h(n) = \max(h_1(n), h_2(n), \ldots, h_k(n))\]

Dominance means \(h_i\) will expand fewer nodes in A* search. Taking the max of multiple admissible heuristics creates a new admissible heuristic that dominates each individual heuristic.

Example: For warehouse routing with multiple objectives (reach goal, avoid obstacles, minimize energy), compute heuristics for each and take the max to get a stronger estimate. The downside is increased computation per node, but the reduced search space often compensates.

2.3.6.3 Heuristic Learning

For repeated problem instances (e.g., many warehouse routing queries), you can learn better heuristics:

  1. Solve a few problems offline with standard A* (with weak heuristic)
  2. Store the \(h(n)\) values in a database for encountered nodes (by state)
  3. Use the database values in future searches (pattern database, or "PDB")

This approach bridges uninformed and learned search, useful when domain-specific knowledge is expensive to encode but many similar problems arise.

2.3.7 Summary and Connections

Informed search uses heuristics—domain-specific estimates of progress toward the goal—to guide exploration. A* combines actual cost \(g\) and estimated cost \(h\) to balance exploration and exploitation. With admissible heuristics, A* guarantees optimality; with weighted variants, it trades off solution quality for speed. Designing good heuristics requires understanding the problem structure: relaxations, composition, and learning can all help.

All the search algorithms we've studied so far—BFS, UCS, A*—maintain a search tree or frontier and explore paths systematically. But for many optimization problems, we don't need the path, just the solution itself. The next section introduces local search, which operates on a single current state and iteratively improves it, trading completeness for memory efficiency and scalability.