<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>LaRoche.ai</title>
<link>https://laroche.ai/writing.html</link>
<atom:link href="https://laroche.ai/writing.xml" rel="self" type="application/rss+xml"/>
<description>Portfolio and research notes — autonomous systems, applied mathematics, and software.</description>
<generator>quarto-1.8.26</generator>
<lastBuildDate>Thu, 15 Jan 2026 22:00:00 GMT</lastBuildDate>
<item>
  <title>Implementing the Dynamic Programming Algorithm with Value Iteration in a Toy MDP</title>
  <dc:creator>Henry Rochester</dc:creator>
  <link>https://laroche.ai/posts/2026-01-16-toy-mdp-value-iteration/</link>
  <description><![CDATA[ 




<section id="introduction" class="level1">
<h1>Introduction</h1>
<blockquote class="blockquote">
<p>Over the past few months I’ve been getting up to speed on the theory of multi-agent reinforcement learning, starting from the fundamentals of single-agent RL.</p>
</blockquote>
<p>Here is the link to the <a href="https://www.marl-book.com" target="_blank">MARL book</a> for anyone reading that wants to read through the theory of what I am about to implement (This is specically from sections 2.4 and 2.5).</p>
<p>Fundamentally, I believe theory becomes useful once you can implement it, test it, and see its consequences in a concrete example/model. In this post I implement my first planning algorithm from the MARL book: <strong>Dynamic Programming</strong>, using <strong>Value Iteration</strong> on a small toy Markov Decision Process (MDP).</p>
<p>Even though Dynamic Programming does not learn from interaction (it assumes the MDP is fully known), it is a foundational building block, as it shows how an optimal policy emerges from applying the Bellman optimality operator recursively.</p>
<p>To keep the mechanics visible, I built a toy environment inspired by <em>Indiana Jones</em>: a single-lane, five-state corridor with a <strong>treasure</strong> at one end and a <strong>trap</strong> at the other. The agent can move left or right, but actions are stochastic, in the sense that sometimes it can slip and move in the opposite direction. The goal here is for the agent to reach the treasure, without falling into a trap. In order to do so, it needs to learn an optimal policy to reach the treasure.</p>
<p>I use <strong>Value Iteration</strong> rather than Policy Iteration because it is simpler to implement and, in many settings, scales more cleanly as the number of states grows.</p>
<p>Since Dynamic Programming requires a complete specification of the MDP, I define the corridor by its state space, action space, transition model, rewards, and discount factor below:</p>
<hr>
</section>
<section id="defining-the-toy-markov-decision-process-mdp" class="level1">
<h1>Defining the Toy Markov Decision Process (MDP)</h1>
<p>This environment is deliberately small. I wanted to do several calculations by hand to sanity-check my code and to develop an intuition of how value iteration is being computed under the hood.</p>
<section id="state-space" class="level2">
<h2 class="anchored" data-anchor-id="state-space">State Space</h2>
<p>We define a finite set of states:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AS%20=%20%5C%7B0,%201,%202,%203,%204%5C%7D%0A"></p>
<ul>
<li>State <strong>0</strong> is a terminal trap (absorbing, negative reward)</li>
<li>State <strong>4</strong> is a terminal treasure (absorbing, positive reward)</li>
<li>States <strong>1, 2, 3</strong> are non-terminal corridor states</li>
</ul>
</section>
<section id="action-space" class="level2">
<h2 class="anchored" data-anchor-id="action-space">Action Space</h2>
<p>At every non-terminal state, the agent may choose:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AA%20=%20%5C%7B%5Ctext%7Bleft%7D,%20%5Ctext%7Bright%7D%5C%7D%0A"></p>
<p>These actions attempt (we use ‘attempt’ here because of the stochasticity within our MDP) to move the agent along the corridor.</p>
</section>
<section id="transition-model" class="level2">
<h2 class="anchored" data-anchor-id="transition-model">Transition Model</h2>
<p>This world is stochastic, meaning that the agents intended actions do not always happen. In this case, the corridor is slippery, and as a result he may slip and move in the other direction.</p>
<p>For any non-terminal state:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctau(s'%20%7C%20s,%20a)%20=%0A%5Cbegin%7Bcases%7D%0A0.8%20&amp;%20%5Ctext%7Bif%20%7D%20s'%20%5Ctext%7B%20is%20the%20intended%20direction%7D%20%5C%5C%0A0.2%20&amp;%20%5Ctext%7Bif%20%7D%20s'%20%5Ctext%7B%20is%20the%20opposite%20direction%7D%0A%5Cend%7Bcases%7D%0A"></p>
<p>This introduces uncertainty and forces the agent to reason in expectation (i.e.&nbsp;most of the time it will go where intended, but sometimes, it may slip and move in the other direction).</p>
</section>
<section id="reward-function" class="level2">
<h2 class="anchored" data-anchor-id="reward-function">Reward Function</h2>
<p>The agent shall be rewarded with ‘+1’ when it reaches the treasure. If it becomes immobilised during its expedition, then it receives a reward of ‘-1’. Furthermore, there is a ‘living cost’, which is a very small negative value. The reason for the reward being negative is so the agent understands there is a cost for wandering, and thus is motivated to choose shorter paths.</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AR(s,%20a,%20s')%20=%0A%5Cbegin%7Bcases%7D%0A+1%20&amp;%20%5Ctext%7Bif%20%7D%20s'%20=%204%20%5C%5C%0A-1%20&amp;%20%5Ctext%7Bif%20%7D%20s'%20=%200%20%5C%5C%0A-0.04%20&amp;%20%5Ctext%7Botherwise%7D%0A%5Cend%7Bcases%7D%0A"></p>
</section>
<section id="discount-factor" class="level2">
<h2 class="anchored" data-anchor-id="discount-factor">Discount Factor</h2>
<p>Future rewards are discounted by:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cgamma%20=%200.95%0A"></p>
<hr>
</section>
</section>
<section id="visualization-of-environment" class="level1">
<h1>Visualization of Environment</h1>
<div class="figure">
<p><img src="https://laroche.ai/posts/2026-01-16-toy-mdp-value-iteration/images/corridor-diagram2.jpeg" class="img-fluid"> The 5-step corridor environment. The agent moves left or right with stochastic transitions and terminal states at both ends.</p>
</div>
<hr>
</section>
<section id="defining-the-problem" class="level1">
<h1>Defining the Problem</h1>
<p>In this RL problem, at a high-level, we need the agent to reach the treasure, without landing on the trap and becoming immobilised. Technically, this translates to an objective of obtaining the optimal policy which I define as the set actions that the agent should take in every state, such that it maximises the sum of its expected future discounted returns. The natural question then becomes: ‘how do we find this optimal policy’?</p>
<p>Rather than solving for the policy directly, we first solve for a proxy, called the Optimal state-value function. which assigns to every state, the expected return for the agent acting optimally. Essentially, this function assigns, to every state, the expected return the agent would receive if it were to act optimally from that state onward. Intuitively, this tells us how “good” each state is under the best possible behaviour.</p>
<p>Once we have this function, extracting the optimal policy becomes straightforward. For each state, we simply iterate through all the actions and choose the action that maximises the corresponding action-value function - which corresponds to the optimal state-value function, as the optimal state-value function is defined as the maximum of the action-value function…In other words, the action that leads to the highest expected future discounted return from that state. Thus, the optimal policy is the collection of these maximising actions, one for each state in the state space. Intuitively, I like to think about it like an instruction manual which shows the best action you can take in a specific scenario.</p>
<p>The algorithm I use to compute this optimal state-value function is Value Iteration, which repeatedly applies the Bellman optimality operator until the values stop changing. I will go into some detail as to why this works Mathematically. For now, let’s state the algorithm, as per the MARL book, and implement this in Python. In doing so, we can obtain a strong intuition as to how the theory works in practice, which leads to a stronger understanding of the concept from first principles.</p>
<p><strong>Algorithm: Value Iteration for MDPs</strong></p>
<ol type="1">
<li><p><strong>Initialize:</strong><br>
<img src="https://latex.codecogs.com/png.latex?%0AV(s)%20=%200%20%5Cquad%20%5Ctext%7Bfor%20all%20%7D%20s%20%5Cin%20S%0A"></p></li>
<li><p><strong>Repeat until <img src="https://latex.codecogs.com/png.latex?V"> converged:</strong><br>
We apply the Bellman optimality operator as follows:</p></li>
</ol>
<p>For all <img src="https://latex.codecogs.com/png.latex?s%20%5Cin%20S">, <img src="https://latex.codecogs.com/png.latex?%0AV(s)%20%5Cleftarrow%20%5Cmax_%7Ba%20%5Cin%20A%7D%20%5Csum_%7Bs'%20%5Cin%20S%7D%20%5Ctau(s'%20%5Cmid%20s,%20a)%5CBig(R(s,%20a,%20s')%20+%20%5Cgamma%20V(s')%5CBig)%0A"></p>
<ol start="3" type="1">
<li><strong>Return optimal policy</strong> <img src="https://latex.codecogs.com/png.latex?%5Cpi%5E%7B*%7D"> with, for all <img src="https://latex.codecogs.com/png.latex?s%20%5Cin%20S">, <img src="https://latex.codecogs.com/png.latex?%0A%5Cpi%5E*(s)%20%5Cleftarrow%20%5Carg%5Cmax_%7Ba%20%5Cin%20A%7D%20%5Csum_%7Bs'%20%5Cin%20S%7D%20%5Ctau(s'%20%5Cmid%20s,%20a)%5CBig(R(s,%20a,%20s')%20+%20%5Cgamma%20V(s')%5CBig)%0A"></li>
</ol>
<hr>
<section id="full-python-implementation-value-iteration" class="level2">
<h2 class="anchored" data-anchor-id="full-python-implementation-value-iteration">Full Python Implementation (Value Iteration)</h2>
<p>Now, I shall implement the algorithm in Python. For anyone curious and wanting to implement the code yourself, I recommend going through the code section by section to understand what is being done, and then writing the code line by line, and lastly (which I think is the most important part), implement the code base yourself from scratch.</p>
<p>It builds the transition tensor <img src="https://latex.codecogs.com/png.latex?%5Ctau(s'%20%5Cmid%20s,a)"> (which is the probability distribution for the transition process), the reward tensor <img src="https://latex.codecogs.com/png.latex?R(s,a,s')"> (which specifies the immediate reward received when the agent takes action <img src="https://latex.codecogs.com/png.latex?a"> in state <img src="https://latex.codecogs.com/png.latex?s">, and transitions to the next state <img src="https://latex.codecogs.com/png.latex?s'">); performs value iteration (as per the algorithm above), and then extracts the greedy optimal policy, with respect to <img src="https://latex.codecogs.com/png.latex?V%5E%7B%5Cpi%7D"> for all <img src="https://latex.codecogs.com/png.latex?s%20%5Cin%20S">.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># MDP Setup</span></span>
<span id="cb1-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-6"></span>
<span id="cb1-7">S <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># we have 5 states</span></span>
<span id="cb1-8">A <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2 actions: left and right</span></span>
<span id="cb1-9"></span>
<span id="cb1-10">LEFT, RIGHT <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb1-11">terminal_states <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>}</span>
<span id="cb1-12"></span>
<span id="cb1-13">gamma <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.95</span></span>
<span id="cb1-14">p_intended <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span></span>
<span id="cb1-15">p_slip <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span></span>
<span id="cb1-16">living_cost <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.04</span></span>
<span id="cb1-17"></span>
<span id="cb1-18"></span>
<span id="cb1-19"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> is_terminal(s: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bool</span>:</span>
<span id="cb1-20">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Check whether we are in a terminal state."""</span></span>
<span id="cb1-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> terminal_states</span>
<span id="cb1-22"></span>
<span id="cb1-23"></span>
<span id="cb1-24"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> next_state(s: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, action: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>:</span>
<span id="cb1-25">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb1-26"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Terminal state is absorbing.</span></span>
<span id="cb1-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Otherwise next state is s-1 (LEFT) or s+1 (RIGHT).</span></span>
<span id="cb1-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb1-29">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is_terminal(s):</span>
<span id="cb1-30">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> s</span>
<span id="cb1-31">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> s <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> action <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> LEFT <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> s <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb1-32"></span>
<span id="cb1-33"></span>
<span id="cb1-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-35"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Build out Transition Distribution and Reward Table</span></span>
<span id="cb1-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-37"></span>
<span id="cb1-38">Tau <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((S, A, S))</span>
<span id="cb1-39">R <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((S, A, S))</span>
<span id="cb1-40"></span>
<span id="cb1-41"></span>
<span id="cb1-42"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> create_TauR():</span>
<span id="cb1-43">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(S):</span>
<span id="cb1-44">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(A):</span>
<span id="cb1-45"></span>
<span id="cb1-46">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is_terminal(s):</span>
<span id="cb1-47">                Tau[s, a, s] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span></span>
<span id="cb1-48">                R[s, a, s] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span></span>
<span id="cb1-49">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">continue</span></span>
<span id="cb1-50"></span>
<span id="cb1-51">            INTENDED <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> LEFT <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> LEFT <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> RIGHT</span>
<span id="cb1-52">            SLIP <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> RIGHT <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> INTENDED <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> LEFT <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> LEFT</span>
<span id="cb1-53"></span>
<span id="cb1-54">            sp_intended <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> next_state(s, INTENDED)</span>
<span id="cb1-55">            sp_slip <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> next_state(s, SLIP)</span>
<span id="cb1-56"></span>
<span id="cb1-57">            Tau[s, a, sp_intended] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> p_intended</span>
<span id="cb1-58">            Tau[s, a, sp_slip] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> p_slip</span>
<span id="cb1-59"></span>
<span id="cb1-60">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> sp <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [sp_intended, sp_slip]:</span>
<span id="cb1-61">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> sp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>:</span>
<span id="cb1-62">                    R[s, a, sp] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span></span>
<span id="cb1-63">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elif</span> sp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb1-64">                    R[s, a, sp] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span></span>
<span id="cb1-65">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-66">                    R[s, a, sp] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> living_cost</span>
<span id="cb1-67"></span>
<span id="cb1-68"></span>
<span id="cb1-69"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-70"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># One-Step Bellman Backup: Q(s,a)</span></span>
<span id="cb1-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-72"></span>
<span id="cb1-73"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> compute_Q(V: np.ndarray) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray:</span>
<span id="cb1-74">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb1-75"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    Q[s,a] = sum_{s'} Tau(s'|s,a) * ( R(s,a,s') + gamma * V(s') )</span></span>
<span id="cb1-76"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb1-77">    Q <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((S, A))</span>
<span id="cb1-78">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(S):</span>
<span id="cb1-79">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> a <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(A):</span>
<span id="cb1-80">            Q[s, a] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(Tau[s, a, :] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (R[s, a, :] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> gamma <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> V))</span>
<span id="cb1-81">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> Q</span>
<span id="cb1-82"></span>
<span id="cb1-83"></span>
<span id="cb1-84"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-85"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Value Iteration</span></span>
<span id="cb1-86"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-87"></span>
<span id="cb1-88"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> value_iteration(tol: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-8</span>, max_iters: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10_000</span>):</span>
<span id="cb1-89">    V <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros(S)</span>
<span id="cb1-90"></span>
<span id="cb1-91">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(max_iters):</span>
<span id="cb1-92">        Q <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> compute_Q(V)</span>
<span id="cb1-93">        V_new <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> V.copy()</span>
<span id="cb1-94"></span>
<span id="cb1-95">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(S):</span>
<span id="cb1-96">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is_terminal(s):</span>
<span id="cb1-97">                V_new[s] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span></span>
<span id="cb1-98">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-99">                V_new[s] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(Q[s])</span>
<span id="cb1-100"></span>
<span id="cb1-101">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(np.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">abs</span>(V_new <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> V)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> tol:</span>
<span id="cb1-102">            V <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> V_new</span>
<span id="cb1-103">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb1-104"></span>
<span id="cb1-105">        V <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> V_new</span>
<span id="cb1-106"></span>
<span id="cb1-107">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Extract greedy policy</span></span>
<span id="cb1-108">    Q_final <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> compute_Q(V)</span>
<span id="cb1-109">    pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros(S, dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>)</span>
<span id="cb1-110">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(S):</span>
<span id="cb1-111">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is_terminal(s):</span>
<span id="cb1-112">            pi[s] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb1-113">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-114">            pi[s] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.argmax(Q_final[s])</span>
<span id="cb1-115"></span>
<span id="cb1-116">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> V, pi</span>
<span id="cb1-117"></span>
<span id="cb1-118"></span>
<span id="cb1-119"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-120"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Display the Policy with the Values</span></span>
<span id="cb1-121"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># --------</span></span>
<span id="cb1-122"></span>
<span id="cb1-123"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> action_name(a: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>:</span>
<span id="cb1-124">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"L"</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> LEFT <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"R"</span></span>
<span id="cb1-125"></span>
<span id="cb1-126"></span>
<span id="cb1-127">create_TauR()</span>
<span id="cb1-128">V, pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> value_iteration()</span>
<span id="cb1-129"></span>
<span id="cb1-130"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"VALUE ITERATION SOLUTION:"</span>)</span>
<span id="cb1-131"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"----------"</span>)</span>
<span id="cb1-132"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(S):</span>
<span id="cb1-133">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is_terminal(s):</span>
<span id="cb1-134">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"s=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: V=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>V[s]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">: .6f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> terminal"</span>)</span>
<span id="cb1-135">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-136">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"s=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: V=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>V[s]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">: .6f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> pi = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>action_name(pi[s])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb1-137"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"----------"</span>)</span>
<span id="cb1-138"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The Policy is:"</span>)</span>
<span id="cb1-139"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(pi)</span>
<span id="cb1-140"></span>
<span id="cb1-141">OUTPUT:</span>
<span id="cb1-142"></span>
<span id="cb1-143">VALUE ITERATION SOLUTION:</span>
<span id="cb1-144"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">----------</span></span>
<span id="cb1-145">s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>: V<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.000000</span> terminal</span>
<span id="cb1-146">s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>: V<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.321372</span>  pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> R</span>
<span id="cb1-147">s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>: V<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.728121</span>  pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> R</span>
<span id="cb1-148">s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>: V<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.930343</span>  pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> R</span>
<span id="cb1-149">s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>: V<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.000000</span> terminal</span>
<span id="cb1-150"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">----------</span></span>
<span id="cb1-151">The Policy <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span>:</span>
<span id="cb1-152">[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span></code></pre></div></div>
<hr>
</section>
<section id="explanation-of-the-output" class="level2">
<h2 class="anchored" data-anchor-id="explanation-of-the-output">Explanation of the Output</h2>
<p>Now, as a recap, our goal at a high level was simple: guide the agent to the treasure without stepping on the trap and becoming immobilised. Recall that, by definition, we set all terminal states to have value <img src="https://latex.codecogs.com/png.latex?V%5E%7B%5Cpi%7D%20=%200"> - similar to a Dirichlet-style boundary condition.</p>
<p>So, from this, our task was to recover an optimal policy, which is just a mapping from states to actions that maximises the agent’s expected, discounted future rewards. Also, an important side note: this policy need not be unique. There can be multiple paths that lead to the same optimal outcome - as we find to be true in life.</p>
<p>Looking at the output, we first see that the terminal states <img src="https://latex.codecogs.com/png.latex?s=0">, and <img src="https://latex.codecogs.com/png.latex?s=4">, the trap and the treasure, respectively, both have values equal to zero, exactly as defined. The lowest non-terminal value appears at <img src="https://latex.codecogs.com/png.latex?s=1">. Intuitively, this makes sense since this is the most dangerous place to be for the agent. From here, the agent is close to the trap, and the 20% slip probability introduces a high risk of becoming immobilised. As a result, the optimal action in this state is to move RIGHT, away from danger.</p>
<p>At <img src="https://latex.codecogs.com/png.latex?s=2">, our Dr Jones is further from the trap and closer to the treasure. The risk has dropped, and so the value of the state increases. Taking another step RIGHT to <img src="https://latex.codecogs.com/png.latex?s=3"> yields the highest value among the non-terminal states: the agent is now as far from the trap as possible and one step away from the goal. From here, a final move to the right delivers the agent into the desired terminal state - the treasure!!</p>
<p>In other words, the optimal policy is rather simple: from any non-terminal state, go right until you reach the treasure.</p>
<p>Now what makes this result quite satisfying is that it didn’t come from the designer hard-coding the agents of the action (which would be glaringly obvious when looking at this problem); or trial-and-error’ing’ the solution in a more complicated scenario…instead the behaviour of the agent resulted from the mathematics - effectively, from repeatedly applying the Bellman optimality operator to our value function. I want to delve into the mathematics a bit more now:</p>
<p>At each state, we are asking the following question: “if I was standing in this state, and I could choose the best possible action, what is the maximum expected return that I could obtain from here onward?” This question is encoded into the Bellman optimality equation, which is defined as: <img src="https://latex.codecogs.com/png.latex?%0AV%5E*(s)%20=%20%5Cmax_%7Ba%20%5Cin%20%5Cmathcal%7BA%7D%7D%20%5Csum_%7Bs'%20%5Cin%20%5Cmathcal%7BS%7D%7D%20%5Ctau(s'%20%5Cmid%20s,%20a)%5C,%5Cbig%5B%20R(s,%20a,%20s')%20+%20%5Cgamma%20V%5E*(s')%20%5Cbig%5D%0A"></p>
<p>In the code, I broke this up into two separate computations for simplicity:</p>
<p>First, I computed the action-value function, defined as: <img src="https://latex.codecogs.com/png.latex?%0AQ(s,a)%20=%20%5Csum_%7Bs'%20%5Cin%20%5Cmathcal%7BS%7D%7D%20%5Ctau(s'%20%5Cmid%20s,%20a)%5C,%5Cbig%5B%20R(s,%20a,%20s')%20+%20%5Cgamma%20V(s')%20%5Cbig%5D%0A"></p>
<p>This can be interpreted as the agent saying: ‘for each action, I’ll average over all possible next states and obtain a value weighted by the immediate reward, and the discounted value of future rewards.’</p>
<p>Then, for each state, I applied the Bellman Optimality operator, which yields:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AV_%7B%5Ctext%7Bnew%7D%7D(s)%20=%20%5Cmax_%7Ba%20%5Cin%20%5Cmathcal%7BA%7D%7D%20Q(s,a)%0A"></p>
<p>i.e.&nbsp;we only keep the best possible action that promises the highest expected return.</p>
<p>Each iteration shrinks the gap between our current estimate, <img src="https://latex.codecogs.com/png.latex?V"> and the true optimal value function <img src="https://latex.codecogs.com/png.latex?V%5E%7B*%7D">. The reason for this is that the Bellman Operator is a contraction mapping, for <img src="https://latex.codecogs.com/png.latex?%5Cgamma%20%5Cin%20%5B0,1)"> (which just means that values are being pulled closer and closer together). From this, we can apply the Banach fixed-point theorem (which is why one has to show that the operator one uses, is actually a contraction mapping), to show repeated application of the Bellman operator converges to a unique fixed point, which is <img src="https://latex.codecogs.com/png.latex?V%5E%7B*%7D">.</p>
<p>Now once we check that the values were within a very small tolerance of each other, we can just extract the policy out from there. We simply fix a state, take the most current <img src="https://latex.codecogs.com/png.latex?V%5E%7B*%7D">, and then figure out the action that achieved the maximum value such that:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cpi%5E*(s)%20=%20%5Carg%5Cmax_%7Ba%20%5Cin%20%5Cmathcal%7BA%7D%7D%20Q(s,a)%0A"></p>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>This was the first toy example I implemented which allowed me to gain a better understanding of the theory in practice.</p>
<p>Moreover, it is important to realise that the dynamic programming algorithm is not feasible for large MDPs, given the effective computational complexity of order <img src="https://latex.codecogs.com/png.latex?O(%7CS%7C%5E2%7CA%7C)"> (as during the Bellman backup step, for each state and action, we need to sum over all possible next states).</p>
<p>I look forward to continuing my learning and pushing this framework beyond a single corridor, into settings where the state space is much larger, the dynamics are unknwon and the structure has to be learned rather than assumed.</p>
<p>Temporal Difference Learning - c’est parti!</p>
</section>
<section id="references" class="level2">
<h2 class="anchored" data-anchor-id="references">References</h2>
<ul>
<li>Albrecht, S. V., Christianos, F., &amp; Schäfer, L. <em>Multi-Agent Reinforcement Learning: Foundations and Modern Approaches</em>. MIT Press, 2024.</li>
<li>Sutton, R. S., &amp; Barto, A. G. <em>Reinforcement Learning: An Introduction</em></li>
</ul>


</section>
</section>

 ]]></description>
  <category>Reinforcement Learning</category>
  <category>Dynamic Programming</category>
  <category>MDPs</category>
  <guid>https://laroche.ai/posts/2026-01-16-toy-mdp-value-iteration/</guid>
  <pubDate>Thu, 15 Jan 2026 22:00:00 GMT</pubDate>
</item>
</channel>
</rss>
