Classical Planning and Optimal Control › Dynamic Programming and Bellman Equations
World Models HandbookAll chapters Classical Planning and Optimal Control › Dynamic Programming and Bellman EquationsOne section · Sections in this chapter4 / 10 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Dynamic programming (DP) flips the question. Instead of "how do we get from here to the goal?", it asks "what is the value of being in each state, assuming we act optimally from now on?" In a tabular model, the resulting policy is a lookup for every represented state. DP handles stochastic transitions without changing its Bellman structure, whereas ordinary deterministic graph search must be generalized.
The central object is the optimal value function. Recall from Chapter 1 of this part that a policy assigns actions to states and that we score policies by expected discounted return. The optimal value of a state is the best return achievable from it:
where:
- : the optimal value of state , the best expected discounted return achievable starting from
- : a policy assigning an action distribution to each state, with selecting the best one; in a finite discounted MDP, a deterministic stationary optimum exists
- : the expectation over trajectories generated by following under the model's transition dynamics
- : the condition that the trajectory starts in state
- : the reward received at step , discounted by one factor of per step into the future
One subtlety worth pausing on: the maximum is over policies, which map states to action distributions; deterministic policies are the action-valued special case. The Bellman equation decomposes this global optimization into linked per-state action comparisons.
The Bellman optimality equation
What makes DP work is that the value function satisfies a self-referential equation. If we act optimally, our first action must be the one that maximizes immediate reward plus the discounted optimal value of wherever we land:
where:
- : a candidate action available in state , with selecting the best one
- : the immediate reward for taking action in state
- : a possible successor state after taking action in state
- : the model's transition probability, its prediction of where action from state lands
- : the discounted expected optimal value of the successor state
This is the Bellman optimality equation. Read it as a statement about consistency across time: the value of being somewhere equals the best one-step payoff plus the value of the future. In a Markov problem with an additive return, the principle of optimality says that an optimal continuation must itself be optimal from the successor state. That is what licenses the replacement of "the best policy from here on" with "the best action now, assuming optimality thereafter." The recursion turns a global policy problem into local action comparisons linked through the value function. Many methods in model-based RL solve this equation, or an approximation to it, but not every planner is organized around an explicit Bellman equation.
For a fixed policy , the maximization drops out and we get the Bellman expectation equation, a linear system in :
where:
- : the value of state when the agent follows the fixed policy from onward
- : the probability that policy assigns to choosing action in state
- , , and : the same reward, transition model, and discount factor as in the optimality equation above
The linearity of this equation is more useful than it may first appear. A finite-state version can be solved by linear algebra or converged to by iteration. With sampled transitions, Monte Carlo and temporal-difference methods instead estimate the same policy value or its Bellman fixed point without forming the full transition matrix. Learned critics extend that idea with function approximation.
Why iteration converges
For bounded rewards and , the Bellman optimality operator is a -contraction in the max-norm: applying it to any two bounded value functions leaves them at most times as far apart. Two value functions that differ by at most in every state therefore cannot differ by more than after one backup. The contraction mapping theorem gives a unique fixed point, . We can start from any bounded guess, say , and repeatedly apply the operator:
where is the value estimate after sweeps of the operator, with the starting guess , and is the result of one more application.
This is value iteration, and the contraction property guarantees geometric convergence to . With , the worst-case value error after a backup is at most 95 percent of the preceding error. To reduce an initial error bound by a factor , the usual worst-case count scales like . A large discount factor therefore expresses a long planning horizon and also gives a slower worst-case contraction. It does not, by itself, set a planner's noise floor.
Value iteration improves the value first and reads the policy out afterwards. Policy iteration alternates the two steps explicitly: (1) policy evaluation, solve the linear Bellman expectation equation for exactly; (2) policy improvement, set . Each improvement step is nondecreasing in value and is strictly better wherever it selects an action with strict positive advantage. With consistent tie handling, a finite discounted MDP reaches an optimal policy after finitely many distinct improvements. Modified variants truncate the evaluation step. Actor-critic methods use the related pattern of approximate evaluation plus policy improvement, though they are not simply exact policy iteration with fewer sweeps.
A worked Bellman backup
Let's make the iteration concrete on our grid world, where rewards are per step, the goal is terminal with value , and . Consider a free cell exactly one step from the goal. Its best action reaches the goal, so one sweep of the operator gives
A cell three steps away needs three sweeps before the goal's influence propagates back, and its converged value is the geometric sum
You can already see the mechanism: value flows backward from the goal through the Bellman operator, one step of lookahead per sweep. After sweeps, every state within steps of the goal has a correct value, and after convergence, every state does. This backward flow is the discrete cousin of the Riccati recursion we will meet in the LQR section. It is also related to backpropagation through time in trajectory optimization. A forward rollout computes states from controls; the reverse-mode gradient pass then propagates cost sensitivities backward through those dynamics. Bellman backups propagate value information backward across predecessor relationships. The objects differ, but both algorithms exploit temporal composition.
Now implement it. The environment transition is embedded in a small step function that bumps the agent back if it would leave the grid or hit a wall.
gamma = 0.95
step_reward = -1.0
actions = ((-1, 0), (1, 0), (0, -1), (0, 1)) # up, down, left, right
def step(s, a):
r, c = s[0] + a[0], s[1] + a[1]
inside = 0 <= r < grid.shape[0] and 0 <= c < grid.shape[1]
return (r, c) if inside and grid[r, c] == 0 else s
free_cells = [
(r, c)
for r in range(grid.shape[0])
for c in range(grid.shape[1])
if grid[r, c] == 0 and (r, c) != goal
]
V = np.zeros(grid.shape)
sweeps = 0
for _ in range(500):
delta = 0.0
V_new = V.copy()
for s in free_cells:
V_new[s] = step_reward + gamma * max(V[step(s, a)] for a in actions)
delta = max(delta, abs(V_new[s] - V[s]))
V = V_new
sweeps += 1
if delta < 1e-6:
break
policy = -np.ones(grid.shape, dtype=int)
for s in free_cells:
q_values = [step_reward + gamma * V[step(s, a)] for a in actions]
policy[s] = int(np.argmax(q_values))This is the entire algorithm: apply the Bellman operator until the change is small, then read the policy off as the argmax. We never specified a start state; the table covers the whole grid. The stopping statistic is the Bellman-update residual, not the value error itself. For a discounted contraction it supplies a bound, , so a tolerance of here implies a correspondingly small bounded value error.
s = start
walk = [s]
for _ in range(60):
if s == goal:
break
s = step(s, actions[policy[s]])
walk.append(s)Converged after 23 sweeps Value at start: -11.197 Greedy policy walks from start to goal in 16 steps
The greedy policy reproduces one of the same optimal routes that A* found. Value iteration did more work for this single query, sweeping every free cell repeatedly, but it now supplies an action for every represented free state. If the wheels can slip, the Bellman backup can average over a transition distribution without changing its basic structure. A deterministic shortest-path graph would need to be replaced by an appropriate stochastic planning formulation. With at most nonzero successors per state-action pair, a sweep costs ; a dense transition table gives .


The left panel shows value flowing toward the goal, brightest at the terminal state and darkest in the far corner. Notice how the contours bend around the walls: value is a distance-like field, but it is a distance through the navigable space, so the walls leave visible ridges in it. The right panel shows why value functions are so useful in practice: the policy is a vector field, a complete answer to "what should we do?" available at every state, computed once, queried forever. If the robot is picked up and dropped somewhere new, no replanning is needed; the field already covers that location. This "compute once, query forever" property is exactly what a learned world-model agent tries to approximate when it trains a policy network against rollouts from a latent dynamics model.
The curse of dimensionality
The catch is enumeration. If each state-action pair has at most possible successors, a Bellman sweep costs ; deterministic transitions give the special case, while a dense table costs . Our grid has 84 free states. A robot arm with 10 joints discretized at 10 values each has state combinations, and 50 binary variables give . This Cartesian growth is the curse of dimensionality.
At fixed resolution per coordinate, the table grows exponentially in the number of factored state dimensions, not in any direct measure of task difficulty. A ten-joint arm does not need qualitatively different behaviors; sensible behaviors may occupy a much smaller structured subset of that grid. Tabular representations cannot exploit such smoothness or structure, so the curse is partly representational. Function approximation attacks that problem, though it introduces new approximation and optimization errors.
Every escape route changes the problem. Discretization loses precision. Function approximation replaces the unconditional tabular contraction guarantee with guarantees that require additional assumptions about the architecture, objective, sampling, and update rule. Exploiting known structure, which is what LQR does next, restricts the problem class but buys exact and efficient solutions within it. Learned latent models make a different wager: that a compact representation preserves enough task structure for approximate planning to be useful.
The exponential character of the curse is easier to feel as a picture than as a sentence.
import numpy as np
## Tabular state counts for discretizing d dimensions at several resolutions.
## Purely combinatorial counting; no simulation or randomness involved.
dims = np.arange(1, 9)
resolutions = {"2 values/dim": 2, "5 values/dim": 5, "10 values/dim": 10}
state_counts = {name: res**dims for name, res in resolutions.items()}
A ten-dimensional system at ten values per dimension already sits at states, far beyond any table. This curve is the quantitative face of the curse, and it is the reason the next section's structural assumptions, and later chapters' learned compact representations, are not conveniences but necessities.