SMA* — Simplified Memory-Bounded A*
A* that fits in whatever memory you have — forgets the worst leaf, remembers the best forgotten path
The Simplest Explanation
A*'s Achilles heel is memory: Open grows as , and real machines run out. SMA* asks:
What if A* simply worked within a fixed memory budget?
When memory fills up, SMA* deletes the worst leaf — the one with the highest -value (least promising) — to make room. But it doesn't throw the information away blindly: it backs the deleted node's -value up into its parent, so the family remembers there was a promising-but-forgotten branch down there.
SMA* = A* + a memory cap. Full memory → drop the worst leaf → write its into its parent. If all alternatives are proven worse than the forgotten branch, SMA* regenerates it.
Why Not Just Cap A*'s Open List?
Naive fixes fail in instructive ways:
| Naive approach | Failure mode |
|---|---|
| Drop highest- nodes permanently | May discard the only solution path when the optimal route initially looks expensive |
| Switch to DFS when full | Loses optimality guarantees; dives down rabbit holes |
| Restart with a bigger bound (IDA*-style) | Re-expands everything from scratch each round |
SMA*'s trick is graceful forgetting: dropped branches leave a "tombstone value" on their ancestors, so nothing good is lost forever.
The Algorithm
Each node stores: , , , and whether it's fully expanded.
- Expand the best leaf (lowest ), as in A*
- If memory is full, drop the worst leaf (highest ) and remove it from its parent's successors
- Back up values: a fully expanded parent whose children are gone gets This makes the parent look like a leaf again — eligible for re-expansion
- When the parent is later re-expanded, regenerate its children; a child whose remembered matches the backup can inherit it instead of recomputing
Termination
- Success: goal dequeued (with admissible , cost is optimal if the goal was reached within memory)
- Failure: the root's = ∞ and memory is full → no solution exists within this space
- In-between: SMA* may return the best solution found so far — the most complete algorithm in this sense
Worked Example (Mini Trace)
Memory holds 3 nodes, tree below. Edge costs in gray, in parentheses.
| Step | Action | Memory | Notes |
|---|---|---|---|
| 1 | Expand S | {S} | children A(), B() enter Open |
| 2 | Expand A (lowest ) | full at 3 nodes | dropping worst leaf B (); back up onto S |
| 3 | Expand C () | must drop something | worst leaf now has ... but S's backup says a forgotten branch costs 13 > 7, so B stays forgotten |
| 4 | Continue toward goal via C | ≤ 3 nodes | optimal path S→A→C found without ever holding more than 3 nodes |
The key moment is step 2–3: SMA* preferred expanding nodes over regenerating the forgotten branch. Only if every live alternative exceeded 13 would it re-create B.
Properties
| Property | Value | Condition |
|---|---|---|
| Complete? | Yes — if memory ≥ depth of shallowest solution | finite , edge costs |
| Optimal? | Yes — if memory can hold the shallowest solution path | admissible |
| Optimal with tight memory? | Returns the best found so far — never gives up early like A* does (OOM crash) | — |
| Time | Can re-expand nodes many times when memory is very small | degrades gracefully to repeated work |
| Space | where = memory bound | by construction |
Both fight A*'s memory hunger. IDA* deepens iteratively on an f-cost threshold using only a DFS stack (no Closed list, no duplicates). SMA* keeps an explicit bounded Open list and uses its whole budget intelligently. IDA* is simpler and great when memory is tiny; SMA* uses available memory more effectively.
The Family Portrait
Every algorithm in this course is one idea away from A*:
| Algorithm | Change from A* | Cost paid |
|---|---|---|
| Dijkstra / Branch & Bound | explores blindly | |
| Greedy Best-First | loses optimality | |
| Weighted A* | , | bounded suboptimality |
| IDA* | iterative deepening on -threshold | repeated expansions, no duplicate detection |
| SMA* | hard memory ceiling | possible re-expansion of forgotten nodes |