Markov Decision Processes
An MDP formalizes sequential decision-making: an agent in a state takes an action, receives a reward, and transitions to a new state, with the goal of maximizing cumulative discounted reward over time.
Why Does This Exist?
To train an agent to make decisions over time, you need a mathematical model: what states exist, what actions are available, what happens when you take an action, and what reward you receive. The Markov Decision Process provides exactly this framework — the foundation on which every reinforcement learning algorithm builds.
MDPs formalize a deceptively simple scenario: an agent repeatedly chooses actions, receives rewards, and observes state transitions. The catch is that consequences can be delayed — a chess move that looks brilliant now might cost you the game ten moves later. The MDP gives you the mathematics to reason about these delayed consequences.
Think of It Like This
Navigating a city with traffic lights
Your state is your current position. Actions are turning choices (left, right, straight). Each action takes you to a new position — possibly with a traffic delay (stochastic transition). The reward is your travel time (negative: shorter is better). Your goal is to find the route — the policy — that minimizes total travel time, accounting for all future intersections you'll encounter.
How It Actually Works
The five-tuple definition
An MDP is defined by :
| Symbol | Name | Meaning |
|---|---|---|
| State space | All possible situations the agent can be in | |
| Action space | All possible actions the agent can take | |
| Transition function | Probability of reaching from after | |
| Reward function | Immediate reward for transition | |
| Discount factor | How much future rewards are discounted |
The Markov property
The future depends only on the current state, not on the history of how you arrived there:
This assumption makes the math tractable — the agent doesn't need to remember the full history of actions taken.
Policy and value functions
A policy maps states to a distribution over actions (or deterministically to one action). The value function is the expected discounted return when starting from state and following :
The action-value function extends this to evaluate specific actions:
Bellman equations
The Bellman equations express value functions recursively:
The Bellman optimality equations characterize the optimal value function :
Solving small MDPs: value iteration
For small discrete MDPs, value iteration repeatedly applies the Bellman optimality operator until convergence:
V(s) ← max_a Σ_s' T(s'|s,a)[R(s,a,s') + γV(s')] for all sThis converges to in a finite number of iterations for finite MDPs with .
Code
import numpy as np
# ── Gridworld MDP: 4×4 grid, goal at state 15, holes at 5 and 13 ─────────────# States: 0-15 (row-major). Actions: 0=up, 1=right, 2=down, 3=left# Transitions: deterministic (simplification; real FrozenLake is stochastic)
NUM_STATES = 16NUM_ACTIONS = 4GAMMA = 0.95
def get_transitions(s, a): """Returns list of (next_state, reward, done) for deterministic gridworld.""" row, col = divmod(s, 4) if s in [5, 13, 15]: # holes and goal are terminal return [(s, 0.0, True)] dr, dc = [(-1,0),(0,1),(1,0),(0,-1)][a] nr, nc = row + dr, col + dc # Bounce off walls if 0 <= nr < 4 and 0 <= nc < 4: ns = nr * 4 + nc else: ns = s # stay in place if ns == 15: reward = 10.0 elif ns in [5, 13]: reward = -10.0 else: reward = -1.0 # step cost done = ns in [5, 13, 15] return [(ns, reward, done)]
# Value iterationV = np.zeros(NUM_STATES)for iteration in range(500): V_new = V.copy() for s in range(NUM_STATES): if s in [5, 13, 15]: # terminal states V_new[s] = 0.0 continue q_values = [] for a in range(NUM_ACTIONS): q = sum(prob * (r + GAMMA * V[ns] * (1 - int(done))) for ns, r, done in get_transitions(s, a) for prob in [1.0]) q_values.append(q) V_new[s] = max(q_values) if np.max(np.abs(V_new - V)) < 1e-6: print(f"Converged after {iteration+1} iterations") break V = V_new
print("Optimal value function:")print(V.reshape(4, 4).round(1))
# Derive greedy policypolicy = []for s in range(NUM_STATES): if s in [5, 13, 15]: policy.append(-1) continue q_values = [sum(r + GAMMA * V[ns] * (1 - int(done)) for ns, r, done in get_transitions(s, a)) for a in range(NUM_ACTIONS)] policy.append(int(np.argmax(q_values)))
action_names = ['↑', '→', '↓', '←']print("\nOptimal policy:")for row in range(4): print(" ".join(action_names[policy[row*4+col]] if policy[row*4+col] >= 0 else 'X' for col in range(4)))Watch Out For
The curse of dimensionality
Value iteration and policy iteration enumerate all states. With 20 binary features, the state space has states — manageable. With 100 continuous sensors, it's infinite and completely intractable. Real-world RL uses function approximation (neural networks) to generalize across states rather than tabulating them all. The MDP framework still applies; only the solution method changes.
Reward hacking from misspecified rewards
An RL agent optimizes the reward you give it, not the reward you meant to give it. A cleaning robot rewarded for maximizing covered area may learn to move in circles. A game-playing agent rewarded for score may exploit bugs. Reward design is the central alignment challenge: specify what you actually want, not a convenient proxy.
The Quick Version
- An MDP = : states, actions, transition function, reward function, discount.
- The Markov property: future depends only on the current state, not history. Makes the math tractable.
- Policy maps states to actions; the value function measures expected discounted return.
- Bellman equations express value recursively; value iteration solves them for small discrete MDPs.
- Large/continuous state spaces require neural function approximation — this is where DQN and actor-critic methods come in.