Value Iteration on a Small MDP
Problem Statement
Run value iteration to convergence on a finite MDP and return both the optimal value function and the optimal deterministic policy.
Background
Policy iteration alternates a full evaluation with a greedy improvement. The evaluation is the expensive half, and value iteration collapses it away: it turns out you can truncate policy evaluation after a single sweep, provided that sweep uses the max instead of the policy average.
vk+1(s)←maxa[r(s,a)+γ∑s′p(s′∣s,a)vk(s′)]
That is one update, doing evaluation and improvement at the same time. It is the Bellman optimality operator, a γ-contraction in the max norm, so it converges to the unique fixed point v∗ from any start.
The policy is not maintained during the loop. It is extracted once at the end, greedily with respect to the converged values. This is the sharpest illustration of generalized policy iteration: evaluation and improvement do not have to complete, or even be separate steps, for the pair to converge — they only have to keep pushing toward consistency and greediness.
A practical note: the values converge much faster than the greedy policy changes, so the extracted policy is usually optimal long before delta gets small. Stopping early therefore costs you accurate values, rarely a good policy.
Your Task
Implement:
def value_iteration(P, R, gamma, theta=1e-10, max_sweeps=100000):
...
- Initialise v to all zeros.
- Each sweep is synchronous: compute the entire new table from the old one, then delta = max(abs(new - old)); stop when delta < theta.
- After convergence, extract the greedy policy, breaking ties toward the lowest action index.
Return a tuple (v, policy) — a list of floats and a list of ints.
Input / Output Format
Nested lists of floats in. A (list_of_floats, list_of_ints) tuple out; the grader rounds the values to 4 decimals.
Sample
P = [[[0.0, 1.0], [1.0, 0.0]], [[0.0, 1.0], [0.0, 1.0]]]
R = [[1.0, 0.0], [0.0, 0.0]]
v, policy = value_iteration(P, R, 0.9)
print([round(x, 4) for x in v], policy)
Output:
[1.0, 0.0] [0, 0]
State 1 is absorbing with zero reward, so v(1) = 0. In state 0, action 0 earns 1 and moves to state 1 while action 1 earns 0 and stays put, so v(0) = 1 and the optimal action is 0.
Example:
value_iteration([[[0.0, 1.0], [1.0, 0.0]], [[0.0, 1.0], [0.0, 1.0]]], [[1.0, 0.0], [0.0, 0.0]], 0.9)
[1.0, 0.0] [0, 0]
State 1 self-loops with zero reward so v(1)=0. In state 0 action 0 pays 1 then leaves, action 1 pays 0 and stays, so the optimality backup fixes v(0)=1 and the greedy action in state 0 is 0. In state 1 both actions are identical, so the tie-break picks action 0.
Constraints:
1 <= n_states <= 200,1 <= n_actions <= 20- Each
P[s][a]sums to 1. 0.0 <= gamma < 1.0for the general case;gamma = 0.0must give the greedy-on-immediate-reward answer.- Sweeps must be synchronous.
- Ties in the final greedy extraction go to the lowest action index.
- Do not round inside the function; return
(v, policy)in that order.
1. Background Knowledge
Value Iteration is a fundamental algorithm in Reinforcement Learning used to solve Markov Decision Processes (MDP). It is a specific instance of Generalized Policy Iteration (GPI), which combines policy evaluation and policy improvement into a single, unified update step. Unlike Policy Iteration, which performs a full convergence of the value function for a fixed policy before improving it, Value Iteration truncates the evaluation phase to a single sweep. This makes it computationally cheaper per iteration, though it may require more iterations to converge.
The core theoretical guarantee of Value Iteration relies on the Bellman Optimality Operator. This operator is a contraction mapping with respect to the max-norm (infinity norm) when the discount factor γ is strictly less than 1. Specifically, applying the operator reduces the distance between the current value estimate and the true optimal value function v∗ by a factor of γ. This property ensures that, regardless of the initial value function, the algorithm will converge to the unique fixed point v∗ as the number of iterations approaches infinity.
In an MDP, the environment is defined by states, actions, transition probabilities, and rewards. The value function v(s) represents the expected cumulative discounted reward starting from state s and following the optimal policy thereafter. The optimal policy π∗(s) is the deterministic action that maximizes the expected immediate reward plus the discounted value of the next state. Value Iteration computes v∗ first and then extracts π∗ by choosing the action that achieves the maximum in the Bellman optimality equation for each state.
2. Algorithm Approach
The approach is to iteratively update the value function using the Bellman Optimality Equation. We start with an arbitrary value function (typically all zeros) and repeatedly apply the update rule:
vk+1(s)←maxa[r(s,a)+γ∑s′p(s′∣s,a)vk(s′)]
This update is synchronous, meaning we compute the new values for all states based on the old values from the previous iteration before updating any state. We continue this process until the change in values between iterations, measured by the max-norm difference δ=maxs∣vk+1(s)−vk(s)∣, falls below a small threshold θ. Once convergence is reached, we derive the optimal policy by selecting the action that maximizes the right-hand side of the Bellman equation for each state, breaking ties by choosing the action with the lowest index.
3. Step-by-Step Strategy
- Initialize: Create a value array v of size equal to the number of states, initialized to zeros.
- Iterate: Loop up to max_sweeps or until convergence: a. Create a copy of the current value array v_new to store updated values. b. For each state s: i. For each action a:
- Calculate the expected return: Q(s,a)=R[s][a]+γ∑s′P[s][a][s′]⋅v[s′].
- Note: P[s][a] is a list of probabilities for transitioning to each next state s′. ii. Set v_new[s] to the maximum Q(s,a) across all actions. c. Compute the maximum absolute difference δ=maxs∣v_new[s]−v[s]∣. d. Update v = v_new. e. If δ<θ, break the loop.
- Extract Policy: Initialize an empty policy list. For each state s: a. Recompute Q(s,a) for all actions using the converged v. b. Find the action a that maximizes Q(s,a). If there are ties, choose the one with the smallest index. c. Append this action to the policy list.
- Return: Return the tuple (v, policy).
4. Common Pitfalls
- Asynchronous Updates: Do not update v[s] in-place while computing other states in the same sweep. This violates the synchronous update requirement and can lead to incorrect convergence or oscillation. Always use a separate v_new array.
- Tie-Breaking: When multiple actions yield the same maximum Q-value, the problem specifies choosing the lowest action index. Failing to handle this correctly will result in a wrong policy, even if the values are correct.
- Transition Probabilities: Ensure you correctly sum over all possible next states s′ weighted by their transition probabilities p(s′∣s,a). A common error is forgetting to multiply by the probability or summing incorrectly.
- Convergence Check: The stopping condition is based on the max-norm of the difference between consecutive value functions, not the sum of squared errors or any other metric. Ensure δ is computed correctly as maxs∣vnew[s]−v[s]∣.
- Discount Factor: Remember that γ must be applied to the expected future value, not the immediate reward. The formula is r+γ⋅expected_future_value.
5. Time & Space Complexity
- Time Complexity: Each iteration involves computing the Q-value for every state-action pair. For S states and A actions, and assuming transitions to S next states, one iteration takes O(S⋅A⋅S)=O(S2A). The number of iterations required for convergence depends on γ and θ, but is generally bounded by O(log(1/θ)) due to the contraction property. Thus, the total time complexity is O(S2Alog(1/θ)).
- Space Complexity: We need to store the value function v and v_new, both of size S. The policy is also of size S. The input MDP structures P and R take O(S2A) space. Therefore, the auxiliary space complexity is O(S), while the total space complexity including input is O(S2A).