Equivalence of Forward and Backward Views
Looking ahead to future rewards and looking back with a memory trace sound different, but offline they add up to the same total. The proof swaps two sums.
Why Does This Exist?
The forward view defines the ideal target: mix all future -step returns into one lambda return . You cannot compute it online because it needs rewards you have not seen yet. The backward view is the runnable method: keep eligibility traces and broadcast each one-step error backward as it arrives.
Are they solving the same problem or just rhyming? The equivalence says the totals match exactly when you freeze values during the episode and add updates at the end. That license matters: it lets you implement the cheap backward pass while claiming the forward target.
Think of It Like This
Splitting a dinner bill two ways
Four friends share dishes over two hours. One person totals the whole receipt at the end and divides it by what each person ordered. Another writes down each new dish as it arrives and splits that dish on the spot across whoever is still at the table.
At midnight both methods charge each person the same total. One waited for the full picture, the other kept a running tab. The running tab only matches because nothing on the receipt changed mid-meal.
Where it stops: restaurant bills do not discount later dishes. TD weights later errors by , so timing changes the split.
How It Actually Works
Write the one-step error with reward , discount , and values :
An -step return minus the current value telescopes into errors:
Mixing all horizons with weights groups each error under one exponential:
In words: the forward error equals discounted future one-step errors. Summing that over every visit and swapping the two sums regroups the weights into accumulating traces , so the forward total equals . That sum is exactly the backward total. Same number, two routes.
Worked example
Walk to to termination with and . Start and , with , , decay , and .
Forward: and . The lambda return is . Updates are for and for .
Backward: errors are and . Traces visit then , giving and . Totals are for and for . Identical to three decimals.
Code
Compute both totals from frozen start values and compare. They agree to machine precision.
def both_views() -> tuple: gamma, lam, alpha = 0.9, 0.7, 0.1 # shared decay 0.63 gl = 0.3 * 4.8 + 0.7 * 6.6 # lambda return from 4.8 and 6.6 fwd0 = alpha * (gl - 1.0) # forward S0 bwd0 = alpha * 3.8 * 1.0 + alpha * 2.0 * 0.63 # backward S0 return round(fwd0, 3), round(bwd0, 3), round(alpha * 2.0, 3)
print(both_views())# -> (0.506, 0.506, 0.2)The triple is forward , backward , and the shared update. The first two match.
Watch Out For
Expecting online TD to match the forward total
The proof freezes values while accumulating. Standard online TD updates values mid-episode, so later errors use newer values and the telescoping breaks. With a large step size the drift is visible within a few episodes.
If you need the forward number while learning online, use true online TD with Dutch traces, or keep alpha small so drift stays minor. Do not benchmark online updates against offline totals.
Swapping in replacing traces and keeping the proof
The sum swap produces accumulating traces, which add on revisits. Replacing traces reset to 1.0 and behave better in loops, but they change the weights and the exact identity no longer holds.
Use accumulating traces when you claim exact offline equivalence. Use replacing traces as a practical upgrade and describe it as an approximation, not the same theorem.
The Quick Version
- The forward view defines the lambda return from future rewards, which is ideal but not causal.
- The backward view broadcasts each one-step error through eligibility traces, which runs online.
- Multi-step errors telescope into sums of one-step errors weighted by gamma times lambda.
- Swapping the double sum turns future weights into past traces, so offline totals match.
- Online updates drift unless you use true online TD, and replacing traces break the exact match.