v9.1.1
Menu

Bellman Optimality Backup on Action Values

mediumPROMarkov Decision Processes and Bellman EquationsReinforcement LearningRL 1: Foundations

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.

Learn this in

More from RL 1: Foundations

PRO

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

Sign in to run your code against the test cases.