Given a policy already folded into the MDP, compute vπ exactly by solving a linear system — no iteration.
Fix a policy and the MDP collapses into a Markov reward process: a transition matrix Pπ where Ps,s′π=∑aπ(a∣s)p(s′∣s,a), and a reward vector Rsπ=∑aπ(a∣s)r(s,a). The Bellman expectation equation is then one vector equation:
vπ=Rπ+γPπvπ
This is linear in vπ, which people often miss because the recursive statement looks like something you have to unroll. Rearranged:
(I−γPπ)vπ=Rπ⟹vπ=(I−γPπ)−1Rπ
For gamma < 1 the matrix I−γPπ is always invertible, so the solution exists and is unique. The catch is cost: a direct solve is O(n3) 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.
Implement:
def solve_v_pi(P_pi, R_pi, gamma):
...
Return a list of n floats.
Nested lists of floats in, a list of floats out. The grader rounds to 4 decimals.
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....
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)
[3.4545, 2.0, 0.0]
Solving (I - 0.9P)v = R. State 2 absorbs with zero reward so v(2)=0; v(1)=2+0.90=2; v(0)=1+0.9(0.5v(0)+0.52) => 0.55*v(0)=1.9 => v(0)=3.4545.
1 <= n <= 200P_pi sums to 1.0.0 <= gamma < 1.0, so I - gamma*P_pi is invertible.numpy.linalg.solve is available); do not iterate to convergence.