PIXELBANKv9.1.0
Menu

Bellman Optimality Backup on Action Values

Problem Statement

Apply one sweep of the Bellman optimality operator to a table of action values.

Background

The Bellman expectation equation averages over whatever the policy does. The Bellman optimality equation replaces that average with a maximum, which is what makes it non-linear and what makes it describe the best achievable behaviour rather than some particular behaviour:

q∗(s,a)=r(s,a)+γ∑s′p(s′∣s,a) max⁡a′q∗(s′,a′)q_*(s,a) = r(s,a) + \gamma \sum_{s'} p(s' \mid s, a)\, \max_{a'} q_*(s', a')

Read the structure carefully. The action a in state s is given — you are committed to it, so no max there. The max lives at the successor state, encoding "after this action I will act optimally from then on". Turning that equation into an assignment gives the optimality operator, and repeatedly applying it is value iteration.

This operator is a γ\gamma-contraction in the max norm, so for gamma < 1 it has a unique fixed point and iterating from any starting table converges to q∗q_*. And because the max is attained by some action in every state, a deterministic optimal policy always exists — you never need to randomise to be optimal in a finite MDP.

Your Task

Implement:

def q_optimality_backup(P, R, q, gamma):
    ...
  • P[s][a][s2] — transition probabilities.
  • R[s][a] — expected immediate reward.
  • q[s][a] — the current action-value table.
  • gamma — discount factor.

Return the new table as a list of n_states lists of n_actions floats. Use the old q for every entry of the sweep (a synchronous update); do not read back values you wrote during this same sweep.

Input / Output Format

Nested lists of floats in, a nested list of floats out, rounded to 4 decimals by the grader.

Sample

P = [[[0.0, 1.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]]]
R = [[0.0, 1.0], [2.0, 0.0]]
q = [[1.0, 3.0], [4.0, -1.0]]
print([[round(x, 4) for x in row] for row in q_optimality_backup(P, R, q, 0.5)])

Output:

[[2.0, 2.5], [3.5, 2.0]]

The successor-state maxima are max(q[0]) = 3.0 and max(q[1]) = 4.0. Then q'(0,0) = 0.0 + 0.5*4.0 = 2.0, q'(0,1) = 1.0 + 0.5*3.0 = 2.5, and so on.

Example:

Input:
q_optimality_backup([[[0.0, 1.0], [1.0, 0.0]], [[1.0, 0.0], [0.0, 1.0]]], [[0.0, 1.0], [2.0, 0.0]], [[1.0, 3.0], [4.0, -1.0]], 0.5)
Output:
[[2.0, 2.5], [3.5, 2.0]]
Reasoning:

First take the max over actions at each successor state: max(q[0])=3.0, max(q[1])=4.0. Action 0 in state 0 lands in state 1 with certainty, so q'(0,0)=0.0+0.54.0=2.0. Action 1 in state 0 lands in state 0, so q'(0,1)=1.0+0.53.0=2.5.

Constraints:

  • 1 <= n_states <= 100, 1 <= n_actions <= 20
  • Each P[s][a] sums to 1.
  • 0.0 <= gamma <= 1.0
  • The update must be synchronous: compute every new entry from the old q table.
  • Do not round inside the function.
🔒

Editor locked

The code editor is locked for Pro problems. It is only available for free problems. Please upgrade to gain access to the code editor for all problems.

solution.py

Test Results

0/0
Run code to see test results.
Bellman Optimality Backup on Action Values - Medium | PixelBank