- This notebook mirrors the
L12_01_gridworld_4x3notebook cell-by-cell - Uses a
gymnasium.Envsubclass for the same 4x3 grid world - The environment exposes
P[s][a](FrozenLake convention) for planning, andstep()for model-free learning - The pedagogical arc is identical:
- Build the env -> solve it with full knowledge -> learn without a model
- The only difference from the from-scratch version is the API:
- States are integer IDs (0-10) instead of
(col, row)tuples - Actions are integer IDs (0-3) instead of strings
gymnasiumcallsreset()andstep()instead of direct model access
- States are integer IDs (0-10) instead of
Imports¶
%load_ext autoreload
%autoreload 2
import logging
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")
plt.rcParams["figure.figsize"] = (14, 5)The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
import helpers.hintrospection as hintros
import helpers.hnotebook as hnotebook
import L12_02_gridworld_4x3_gymnasium_utils as utils
hnotebook.config_notebook()
_LOG = logging.getLogger(__name__)
utils.init_loggers(_LOG)WARNING: Logger already initialized: skipping
Part 1: Building the Grid World Environment (Gymnasium)¶
Cell 1.1: The 4x3 grid and its states¶
Goal:
- Same layout as the from-scratch version: 4x3 grid with START, +1, -1, WALL
- This time, states are
gym.spaces.Discrete(11)and actions aregym.spaces.Discrete(4) - The grid world is fully observable (the agent sees its state ID) but stochastic (actions do not always succeed)
Implementation: utils.cell1_1_show_grid()
# Draw the grid and print the gymnasium observation / action spaces.
utils.cell1_1_show_grid()
observation_space: Discrete(11)
action_space: Discrete(4)
n_states=11
state id -> cell mapping:
0 -> (1, 1)
1 -> (2, 1)
2 -> (3, 1)
3 -> (4, 1)
4 -> (1, 2)
5 -> (3, 2)
6 -> (4, 2)
7 -> (1, 3)
8 -> (2, 3)
9 -> (3, 3)
10 -> (4, 3)
terminal ids: [6, 10]
- The environment has 11 reachable states (12 cells minus 1 wall)
- Two states are terminal: reaching either ends the episode
- State IDs 0-10 map to the same
(col, row)cells
Cell 1.2: Stochastic action model¶
Goal:
- See the stochastic slip model through the gymnasium
env.P[s][a]interface - Understand how the intended action and perpendicular slips share probability mass
Implementation: utils.cell1_2_stochastic_action()
hintros.print_obj_info(utils.cell1_2_stochastic_action)# Show how an intended action spreads probability mass through env.P.
utils.cell1_2_stochastic_action()Usage
Inputs
action: which action to inspectp_intended: probability the intended action succeeds
Panels
Grid: highlighted state, with arrow thickness encoding outcome probabilityComments: current parameter values and computed outcome probabilities
Guided usage
- Raise
p_intendedtoward1.0- Observe the intended-direction arrow thickens while the 2 perpendicular arrows thin out: the intended action and perpendicular slips share probability mass
Cell 1.3: Transition model from env.P¶
Goal:
- See how
env.P[s][a]exposes the transition model as(prob, s', reward, terminated)tuples - Understand that planning algorithms read this dict while Q-learning
uses
step()
Implementation: utils.cell1_3_transition_table()
hintros.print_obj_info(utils.cell1_3_transition_table)# Display the explicit transition row from env.P for a chosen (state, action).
utils.cell1_3_transition_table()Usage
Inputs
state: which state to inspectaction: which action to inspect
Panels
Transition heatmap: probability distribution over next states for the selected (state, action)Transition table: each outcome with probability, reward, and terminal flag
Guided usage
- Select a state next to a terminal, with the action pointing into it
- Observe the outcome row’s
terminatedflag is set for the terminal next state, and its reward matches the terminal’s value
- Observe the outcome row’s
Cell 1.4: Rewards and episode returns¶
Goal:
- Define the reward structure and connect per-step rewards to discounted return
- See a sample trajectory rolled out via
env.step()
Implementation: utils.cell1_4_rewards_and_returns()
hintros.print_obj_info(utils.cell1_4_rewards_and_returns)# Show per-cell rewards and the discounted return of a sample trajectory.
utils.cell1_4_rewards_and_returns()Usage
Inputs
r_step: living reward per stepgamma: discount factorseed: trajectory seed
Panels
Rewards and trajectory: per-cell living rewards, with a sample path from STARTComments: the discounted-return computation and step-by-step rewards
Guided usage
- Resample with a different
seed- Observe
env.step()produces a different path each time, and the Comments panel’s discounted return changes with it
- Observe
Part 2: Solving the MDP with Value Iteration¶
Cell 2.1: The Bellman equation for one state¶
Goal:
- Build intuition for the Bellman update on a single state using integer state and action IDs
- See how the Bellman update reads
env.P[s][a]to compute Q-values
Implementation: utils.cell2_1_bellman_one_state()
hintros.print_obj_info(utils.cell2_1_bellman_one_state)# Show the value of each action at one state under converged utilities.
utils.cell2_1_bellman_one_state()Usage
Inputs
state: which state to inspect the action values forgamma: discount factor
Panels
- the Q-value of each action at the selected state
Guided usage
- Compare the Q-values across actions at any state
- Observe the utility of a state is the value of its best action: the max is what makes the system nonlinear, requiring iteration
Cell 2.2: Value iteration converging over sweeps¶
Goal:
- Watch value iteration converge on the gymnasium
env.Pmodel - See utility information propagate backward from the terminals
Implementation: utils.cell2_2_value_iteration()
hintros.print_obj_info(utils.cell2_2_value_iteration)# Step through value iteration sweeps and watch utilities converge.
utils.cell2_2_value_iteration()Usage
Inputs
iteration: value iteration sweep to displaygamma: discount factorr_step: living reward
Panels
- the grid’s utilities at the selected sweep
Guided usage
- Step
iterationforward from 0- Observe value propagates backward from the terminals, one ring per sweep, and the change per sweep shrinks geometrically
Cell 2.3: Extracting the optimal policy¶
Goal:
- Turn converged utilities into an actionable policy
- Take the greedy action in every cell
Implementation: utils.cell2_3_extract_policy()
hintros.print_obj_info(utils.cell2_3_extract_policy)# Show the greedy policy extracted from converged utilities.
utils.cell2_3_extract_policy()Usage
Inputs
r_step: living reward
Panels
- the grid with an arrow per cell pointing toward the greedy action
Guided usage
- Make
r_stepa large negative number- Observe expensive steps push the agent toward the short risky path
- Make
r_stepclose to 0- Observe cheap steps let the agent take the long safe route instead
Part 3: Solving the MDP with Policy Iteration¶
Cell 3.1: Policy evaluation for a fixed policy¶
Goal:
- Compute the utility of a fixed policy by solving
- Read transition probabilities from
env.P[s][a]
Implementation: utils.cell3_1_policy_evaluation()
hintros.print_obj_info(utils.cell3_1_policy_evaluation)# Evaluate a fixed policy by solving the linear Bellman system.
utils.cell3_1_policy_evaluation()Usage
Inputs
policy: which fixed policy to evaluategamma: discount factor
Panels
- the utility of every state under the selected fixed policy
Guided usage
- Select a deliberately bad
policy- Observe it yields low utilities, especially near the
-1terminal: evaluation only answers “how good is this policy”
- Observe it yields low utilities, especially near the
Cell 3.2: Policy improvement and iteration to optimality¶
Goal:
- Alternate evaluation and improvement until the policy stops changing
- Watch convergence from a deliberately poor policy to the optimal one
Implementation: utils.cell3_2_policy_iteration()
hintros.print_obj_info(utils.cell3_2_policy_iteration)# Step through policy iteration rounds and watch arrows flip.
utils.cell3_2_policy_iteration()Usage
Inputs
iteration: evaluate/improve round to display
Panels
- the policy arrows at the selected round
Guided usage
- Step
iterationforward from 0- Observe policy iteration typically converges in fewer rounds than value iteration sweeps; each round is more expensive (it solves a linear system), but the total wall-clock can still be lower
Cell 3.3: Value iteration vs policy iteration¶
Goal:
- Contrast the two exact methods and their convergence behavior
- Understand the tradeoff between many cheap sweeps and few expensive rounds
Implementation: utils.cell3_3_compare_solvers()
hintros.print_obj_info(utils.cell3_3_compare_solvers)# Compare convergence of the two exact methods.
utils.cell3_3_compare_solvers()Usage
Inputs
gamma: discount factor
Panels
- convergence curves for both methods
Guided usage
- Raise
gammatoward 1- Observe value iteration needs many more sweeps, while policy iteration is relatively unaffected by gamma
Part 4: Learning Without a Model (Q-Learning)¶
Cell 4.1: Why reinforcement learning is harder than planning¶
Goal:
- Contrast planning (reading
env.P[s][a]) with learning (callingenv.step()) - Understand why the same optimal policy takes a harder route in RL
- The agent calls
env.step()and never readsenv.P[s][a] - It must learn the value of actions purely from the experience tuples it gets back from each step
Implementation: utils.cell4_1_planning_vs_learning()
# Contrast planning (reads env.P) with learning (calls env.step()).
utils.cell4_1_planning_vs_learning()
- The transition model is hidden behind the gymnasium API
- The agent discovers the world by interacting with it
Cell 4.2: The Q-learning update rule¶
Goal:
- Introduce the single update that powers Q-learning
- Show how one
env.step()call produces the TD update tuple
Implementation: utils.cell4_2_q_update_rule()
hintros.print_obj_info(utils.cell4_2_q_update_rule)# Show how a single env.step() tuple nudges a Q-value.
utils.cell4_2_q_update_rule()Usage
Inputs
alpha: learning rategamma: discount factor
Panels
- the TD update applied to one
env.step()tuple
- the TD update applied to one
Guided usage
- Set
alphanear 0 vs near 1- Observe the TD error measures surprise, the gap between old expectation and observed outcome, and no transition probabilities are needed to compute it
Cell 4.3: Exploration vs exploitation with epsilon-greedy¶
Goal:
- Show why the agent must sometimes act randomly
- Compare state coverage at low and high exploration rates
Implementation: utils.cell4_3_exploration()
hintros.print_obj_info(utils.cell4_3_exploration)# Compare state coverage under low vs high epsilon using q_learning().
utils.cell4_3_exploration()Usage
Inputs
epsilon: exploration probabilitylog(n_episodes): training episodesseed: random seed
Panels
- a visit heatmap of which states the agent has explored
Guided usage
- Compare
epsilonlow vs high- Observe the visit heatmap reveals exactly which states the agent has explored: a balance between exploration and exploitation is essential
Cell 4.4: Watching Q-learning learn the optimal policy¶
Goal:
- Run full Q-learning and watch the learned policy emerge
- Compare it to the policy value iteration found with full knowledge
Implementation: utils.cell4_4_q_learning_converges()
hintros.print_obj_info(utils.cell4_4_q_learning_converges)# Train Q-learning via env.step() and compare to the planning optimum.
utils.cell4_4_q_learning_converges()Usage
Inputs
log(n_episodes): training episodesalpha: learning rateepsilon: exploration probabilityseed: random seed
Panels
- the learned policy arrows, and the learning curve of returns over episodes
Guided usage
- Raise
log(n_episodes)to its max- Observe the learned arrows converge to the value iteration arrows as episodes grow, and returns rise and flatten as the Q-table stabilises: the same optimal behaviour emerges from pure experience, no model required
Summary: The Mental Model¶
- A
gymnasium.Envsubclass is an MDP: itsP[s][a]encodes the transition model and itsstep()lets the agent interact without reading the model - When the model is known (planning),
value_iteration()andpolicy_iteration()readenv.Pand compute the optimal policy exactly - When the model is unknown (learning),
q_learning()callsenv.step()and discovers the optimal policy from experience tuples - All three methods converge to the same optimal policy on the same environment: the difference is whether you plan with the model or learn without it
- The gymnasium
GridWorldEnvproduces exactly the same optimal utilities and policies as the from-scratchGridWorldclass