PIXELBANKv9.1.0
Menu

Solve the Bellman Expectation Equation Exactly

Problem Statement

Given a policy already folded into the MDP, compute vπv_\pi exactly by solving a linear system — no iteration.

Background

Fix a policy and the MDP collapses into a Markov reward process: a transition matrix PπP^\pi where Ps,s′π=∑aπ(a∣s) p(s′∣s,a)P^\pi_{s,s'} = \sum_a \pi(a\mid s)\, p(s'\mid s,a), and a reward vector Rsπ=∑aπ(a∣s) r(s,a)R^\pi_s = \sum_a \pi(a \mid s)\, r(s,a). The Bellman expectation equation is then one vector equation:

vπ=Rπ+γPπvπv_\pi = R^\pi + \gamma P^\pi v_\pi

This is linear in vπv_\pi, which people often miss because the recursive statement looks like something you have to unroll. Rearranged:

(I−γPπ) vπ=Rπ⟹vπ=(I−γPπ)−1Rπ(I - \gamma P^\pi)\, v_\pi = R^\pi \quad\Longrightarrow\quad v_\pi = (I - \gamma P^\pi)^{-1} R^\pi

For gamma < 1 the matrix I−γPπI - \gamma P^\pi is always invertible, so the solution exists and is unique. The catch is cost: a direct solve is O(n3)O(n^3) in the number of states, which is why iterative policy evaluation exists at all. Knowing the closed form still matters — it is the ground truth you check your iterative code against.

Your Task

Implement:

def solve_v_pi(P_pi, R_pi, gamma):
    ...
  • P_pi — an n x n nested list, P_pi[s][s2] is the probability of moving from s to s2 under the policy.
  • R_pi — a list of n floats, the expected immediate reward in each state under the policy.
  • gamma — discount factor, 0.0 <= gamma < 1.0.

Return a list of n floats.

Input / Output Format

Nested lists of floats in, a list of floats out. The grader rounds to 4 decimals.

Sample

P = [[0.5, 0.5, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]]
R = [1.0, 2.0, 0.0]
print([round(x, 4) for x in solve_v_pi(P, R, 0.9)])

Output:

[3.4545, 2.0, 0.0]

State 2 is absorbing with zero reward so v(2) = 0. Then v(1) = 2 + 0.9*0 = 2. Finally v(0) = 1 + 0.9(0.5v(0) + 0.52)** gives **0.55v(0) = 1.9**, i.e. v(0) = 3.4545....

Example:

Input:
solve_v_pi([[0.5, 0.5, 0.0], [0.0, 0.0, 1.0], [0.0, 0.0, 1.0]], [1.0, 2.0, 0.0], 0.9)
Output:
[3.4545, 2.0, 0.0]
Reasoning:
  • Identify the linear system: The Bellman equation vπ=Rπ+γPπvπv_\pi = R^\pi + \gamma P^\pi v_\pi is rearranged to (I−γPπ)vπ=Rπ(I - \gamma P^\pi)v_\pi = R^\pi. We construct the coefficient matrix A=I−0.9PπA = I - 0.9 P^\pi and the reward vector Rπ=[1.0,2.0,0.0]TR^\pi = [1.0, 2.0, 0.0]^T.

  • Compute matrix AA: Subtract 0.90.9 times the transition matrix from the identity matrix:

    A=[100010001]−0.9[0.50.50.00.00.01.00.00.01.0]=[0.55−0.450.00.01.0−0.90.00.00.1]A = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} - 0.9 \begin{bmatrix} 0.5 & 0.5 & 0.0 \\ 0.0 & 0.0 & 1.0 \\ 0.0 & 0.0 & 1.0 \end{bmatrix} = \begin{bmatrix} 0.55 & -0.45 & 0.0 \\ 0.0 & 1.0 & -0.9 \\ 0.0 & 0.0 & 0.1 \end{bmatrix}
  • Solve for v2v_2: The third row of the system corresponds to the absorbing state 2. The equation is 0.1v2=0.00.1 v_2 = 0.0, which yields v2=0.0v_2 = 0.0.

  • Solve for v1v_1: Substitute v2=0.0v_2 = 0.0 into the second row equation 1.0v1−0.9v2=2.01.0 v_1 - 0.9 v_2 = 2.0. This simplifies to v1=2.0v_1 = 2.0.

  • Solve for v0v_0: Substitute v1=2.0v_1 = 2.0 into the first row equation 0.55v0−0.45v1=1.00.55 v_0 - 0.45 v_1 = 1.0. This gives 0.55v0−0.9=1.0  ⟹  0.55v0=1.90.55 v_0 - 0.9 = 1.0 \implies 0.55 v_0 = 1.9, so v0=1.9/0.55≈3.454545...v_0 = 1.9 / 0.55 \approx 3.454545...

  • Final Output: Rounding the values to 4 decimal places results in the list [3.4545, 2.0, 0.0].

Constraints:

  • 1 <= n <= 200
  • Each row of P_pi sums to 1.
  • 0.0 <= gamma < 1.0, so I - gamma*P_pi is invertible.
  • Solve the system directly (numpy.linalg.solve is available); do not iterate to convergence.
  • Do not round inside the function.
solution.py

Test Results

0/0
Run code to see test results.
Solve the Bellman Expectation Equation Exactly - Medium | PixelBank