Agents Multi Agent Systems And Rl
Markov Decision Processes
Chapter 3's multi-armed bandit had no state — every choice was independent of the last. A Markov Decision Process (MDP) extends this with state, directly buildi
Jr Codex AI Notes
Level: Advanced Prerequisites: Chapter 3 Time to complete: ~30 minutes
Table of Contents
- Adding State to Reinforcement Learning
- Formal Definition of an MDP
- Policies
- The Value of a State
- The Bellman Equation
- Value Iteration
- A Worked Example: Grid World
- Summary & Next Steps
1. Adding State to Reinforcement Learning
Chapter 3's multi-armed bandit had no state — every choice was independent of the last. A Markov Decision Process (MDP) extends this with state, directly building on Module 4, Chapter 4's Markov chains — now with the agent's own actions influencing which state comes next, and rewards attached to transitions.
Markov Chain (Module 4, Ch.4) Markov Decision Process (this chapter)
───────────────────────────── ─────────────────────────────
States transition on their OWN, The AGENT chooses ACTIONS
no agent involved that INFLUENCE which state
comes next
P(next_state | current_state) P(next_state | current_state, ACTION)
No rewards Each transition has an
associated REWARD
───────────────────────────── ─────────────────────────────
2. Formal Definition of an MDP
The Five Components of an MDP
─────────────────────────────────────────
States (S): all possible situations the agent can be in
Actions (A): all actions the agent can take
Transition Model (P): P(s' | s, a) — probability of reaching
state s' after taking action a in state s
Reward Function (R): R(s, a, s') — the reward received for
this specific transition
Discount Factor (γ): how much FUTURE rewards are valued
relative to IMMEDIATE ones (0 to 1)
─────────────────────────────────────────
class MDP:
def __init__(self, states, actions, transition_model, reward_fn, discount=0.9):
self.states = states
self.actions = actions
self.transition_model = transition_model # (state, action) -> {next_state: probability}
self.reward_fn = reward_fn # (state, action, next_state) -> reward
self.discount = discount
def get_transitions(self, state, action):
return self.transition_model.get((state, action), {})This is a direct generalization of Module 2, Chapter 1's SearchProblem — states and actions are the same idea, but transitions are now probabilistic (an action might not deterministically lead to one outcome), and every transition carries a reward rather than just a cost.
3. Policies
A policy, written π(s), specifies which action the agent takes in each state — the object an MDP-solving algorithm is ultimately trying to find the best version of.
# A policy is simply a mapping from state to action
policy = {
"state_A": "move_right",
"state_B": "move_up",
"state_C": "move_left",
}
def follow_policy(policy, state):
return policy[state]Policy vs Plan (Module 3, Ch.5)
─────────────────────────────────────────
A PLAN (Module 3) is a fixed SEQUENCE of actions —
appropriate for deterministic environments where you can
predict exactly what happens next.
A POLICY is a mapping from EVERY state to an action —
necessary in STOCHASTIC environments (Module 1, Ch.4's
environment classification), since the agent might end up
in any of several states after an action and needs to know
what to do NO MATTER which one it lands in.
─────────────────────────────────────────
4. The Value of a State
The value of a state under a given policy is the expected total (discounted) reward the agent will accumulate starting from that state and following the policy thereafter — directly extending Module 4, Chapter 5's expected utility to a multi-step, sequential setting.
Why Discounting Future Rewards?
─────────────────────────────────────────
γ (gamma) close to 1: future rewards matter almost as
much as immediate ones — a
"patient" agent
γ (gamma) close to 0: only immediate rewards matter much
— a "short-sighted" agent
Discounting also ensures the total value stays FINITE even
for an infinitely long sequence of rewards, and reflects a
real-world intuition: a reward received SOONER is generally
worth more than the SAME reward received much later.
─────────────────────────────────────────
def calculate_return(rewards, discount):
"""Total DISCOUNTED return from a sequence of rewards."""
total = 0
for t, reward in enumerate(rewards):
total += (discount ** t) * reward
return total
rewards_received = [10, 5, 2, 1]
print(calculate_return(rewards_received, discount=0.9)) # 10 + 4.5 + 1.62 + 0.729 = 16.85
print(calculate_return(rewards_received, discount=0.5)) # 10 + 2.5 + 0.5 + 0.125 = 13.125 — future valued less5. The Bellman Equation
The Bellman equation expresses a state's value recursively — in terms of the immediate reward plus the (discounted) value of whatever state comes next — the mathematical heart of MDP-solving.
The Bellman Equation
─────────────────────────────────────────
V(s) = max_a Σ P(s'|s,a) [ R(s,a,s') + γ V(s') ]
│ │ │ │
│ │ │ └── discounted value of NEXT state
│ │ └── immediate reward for this transition
│ └── weighted by transition PROBABILITY
└── choose the ACTION that maximizes this expression
"The value of a state = the best possible IMMEDIATE reward,
plus the discounted value of wherever you end up next."
─────────────────────────────────────────
def bellman_update(mdp, state, values):
"""Compute the updated value of ONE state, using current value estimates for all states."""
best_value = float("-inf")
for action in mdp.actions:
transitions = mdp.get_transitions(state, action)
expected_value = sum(
prob * (mdp.reward_fn(state, action, next_state) + mdp.discount * values[next_state])
for next_state, prob in transitions.items()
)
best_value = max(best_value, expected_value)
return best_valueThis recursive structure is exactly the dynamic-programming pattern from Module 4, Chapter 4's Viterbi algorithm — a state's value is built from the values of states that follow it, rather than solved from scratch each time.
6. Value Iteration
Value iteration solves the Bellman equation by repeatedly applying it to every state until the values stop changing (converge) — a direct, practical algorithm for finding the optimal policy.
def value_iteration(mdp, iterations=100, tolerance=0.001):
values = {s: 0.0 for s in mdp.states} # start with all values at 0
for i in range(iterations):
new_values = {}
max_change = 0
for state in mdp.states:
new_values[state] = bellman_update(mdp, state, values)
max_change = max(max_change, abs(new_values[state] - values[state]))
values = new_values
if max_change < tolerance:
print(f"Converged after {i+1} iterations")
break
return values
def extract_policy(mdp, values):
"""Once values have converged, extract the BEST action for each state."""
policy = {}
for state in mdp.states:
best_action, best_value = None, float("-inf")
for action in mdp.actions:
transitions = mdp.get_transitions(state, action)
expected_value = sum(
prob * (mdp.reward_fn(state, action, next_state) + mdp.discount * values[next_state])
for next_state, prob in transitions.items()
)
if expected_value > best_value:
best_action, best_value = action, expected_value
policy[state] = best_action
return policyValue Iteration, Conceptually
─────────────────────────────────────────
Start: every state's value = 0 (no information yet)
Iterate: repeatedly apply the Bellman equation to
EVERY state, using the PREVIOUS iteration's
values for "what happens next"
Converge: values stabilize once they accurately
reflect the TRUE expected long-term reward
from each state
Extract policy: once values are accurate, the best ACTION
per state follows directly
─────────────────────────────────────────
7. A Worked Example: Grid World
The classic MDP teaching example — an agent navigating a small grid, with one good terminal state and one bad one.
# A simple 2x2 grid: states are (row, col). Terminal states: (0,1)=+10, (1,1)=-10
states = [(0,0), (0,1), (1,0), (1,1)]
actions = ["up", "down", "left", "right"]
def move(state, action):
row, col = state
moves = {"up": (-1,0), "down": (1,0), "left": (0,-1), "right": (0,1)}
dr, dc = moves[action]
new_row, new_col = max(0, min(1, row+dr)), max(0, min(1, col+dc))
return (new_row, new_col)
transition_model = {}
for state in states:
if state in [(0,1), (1,1)]: # terminal states — no outgoing transitions
continue
for action in actions:
next_state = move(state, action)
transition_model[(state, action)] = {next_state: 1.0} # deterministic movement
def reward_fn(state, action, next_state):
if next_state == (0,1):
return 10
elif next_state == (1,1):
return -10
return -1 # small cost per step, encouraging efficiency (same idea as Ch.3's reward design)
grid_mdp = MDP(states, actions, transition_model, reward_fn, discount=0.9)
optimal_values = value_iteration(grid_mdp)
optimal_policy = extract_policy(grid_mdp, optimal_values)
print("Optimal values:", optimal_values)
print("Optimal policy:", optimal_policy) # shows the best action from every non-terminal stateThis complete, small example ties every concept in this chapter together: states, actions, a transition model, a reward function, the Bellman equation, value iteration's convergence, and a final extracted policy — the exact recipe scales up (with more sophisticated algorithms) to far larger, real MDPs.
8. Summary & Next Steps
Key Takeaways
- A Markov Decision Process extends Module 4's Markov chains with agent-chosen actions and rewards attached to each transition — the formal framework underlying sequential reinforcement learning.
- A policy maps every state to an action; unlike a fixed plan (Module 3), a policy is necessary because stochastic environments can lead to any of several states after an action.
- The Bellman equation recursively defines a state's value as the best achievable immediate reward plus the discounted value of whatever state follows — the same dynamic-programming principle behind Viterbi (Module 4, Chapter 4).
- Value iteration solves the Bellman equation by repeatedly updating every state's value until convergence, then extracts the optimal policy directly from the converged values.
Concept Check
- Why is a policy necessary in a stochastic environment, rather than a fixed action sequence (plan)?
- What does the discount factor γ control, and what happens with γ close to 0 versus close to 1?
- How does the Bellman equation express a state's value recursively, and why does that enable value iteration to work?
Next Chapter
→ Chapter 5: Q-Learning Fundamentals
Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index