Skip to content
AI360Xpert
Beta

Equivalence of Forward and Backward Views

The forward view asks where future rewards will come from, while the backward view asks which past actions deserve credit for current rewards. Offline across an episode, these two fundamentally different perspectives produce the exact same mathematical updates.

The theoretical forward view and mechanistic backward view produce identical total updates across an episode through a telescoping sum.
The theoretical forward view and mechanistic backward view produce identical total updates across an episode through a telescoping sum.

Why Does This Exist?

In reinforcement learning theory, the forward view of TD(λ)\text{TD}(\lambda) is philosophically compelling: it defines an ideal learning target, the λ\lambda-return GtλG_t^\lambda, by blending all future nn-step returns into a geometric mixture. However, the forward view is non-causal and acausal online—an agent standing at time tt cannot compute GtλG_t^\lambda because it requires rewards and states that have not yet occurred.

To implement learning in real time, practitioners rely on the backward view: a mechanistic algorithm that looks backward into the past using an eligibility trace vector zt∈R∣S∣z_t \in \mathbb{R}^{|\mathcal{S}|}. Whenever a local one-step prediction error δt\delta_t occurs, it broadcasts that error backward to recently visited states.

This introduces a foundational theoretical question: Are these two formulations actually solving the same problem?

The mathematical equivalence between the forward view and backward view proves that the mechanistic, step-by-step backward algorithm is not merely an intuitive heuristic. In the offline setting (when updates are accumulated and applied at the end of an episode), the sum of all backward updates across the trajectory is mathematically identical to the sum of all theoretical forward-view updates.

Think of It Like This

Two Ways to Audit Corporate Books

Imagine two different accountants auditing an enterprise's machinery expenditure across a fiscal year:

  1. The Forward-Looking Auditor (Forward View): At the moment a machine is purchased in January, this auditor projects forward across the machine's entire lifetime. They examine future maintenance bills, expected salvage value, and discounted utility to write an idealized depreciation entry for January. However, this auditor cannot finalize their books until the end of the year, after all actual receipts have been collected.
  2. The Daily Expense Clerk (Backward View): Each afternoon, the clerk records the actual bills that arrived today. If an unexpected repair bill arrives in October, the clerk does not travel back in time to change the January books. Instead, they write an immediate variance voucher, look at their ledger of currently active machinery, and distribute the expense backward across all active equipment.

At year-end, both accountants compute their bottom line:

  • The forward auditor updated past purchases using total lifetime trajectories.
  • The backward clerk updated daily books using local surprises broadcast across running inventories.

The result: Both methods arrive at the exact same fiscal balance. Where the analogy stops is that human accounting often deals with discrete legal tax categories, whereas in TD(λ)\text{TD}(\lambda), the equivalence is established by an exact geometric decay factor (γλ)(\gamma \lambda) that telescopes across every time step.

How It Actually Works

The Telescoping Sum Decomposition of the λ-Return

Let an episode terminate at step TT. At each step kk, the standard one-step Temporal Difference error is:

δk=Rk+1+γV(Sk+1)−V(Sk)\delta_k = R_{k+1} + \gamma V(S_{k+1}) - V(S_k)

Now consider the sequence of nn-step returns starting from time tt:

  • 11-step return: Gt(1)=Rt+1+γV(St+1)G_t^{(1)} = R_{t+1} + \gamma V(S_{t+1})
  • 22-step return: Gt(2)=Rt+1+γRt+2+γ2V(St+2)G_t^{(2)} = R_{t+1} + \gamma R_{t+2} + \gamma^2 V(S_{t+2})
  • nn-step return: Gt(n)=∑i=1nγi−1Rt+i+γnV(St+n)G_t^{(n)} = \sum_{i=1}^n \gamma^{i-1} R_{t+i} + \gamma^n V(S_{t+n})

Notice that the difference between any nn-step return and the initial value estimate V(St)V(S_t) expands into a telescoping sum of one-step TD errors:

Gt(1)−V(St)=δtG_t^{(1)} - V(S_t) = \delta_t

Gt(2)−V(St)=[Rt+1+γV(St+1)−V(St)]+γ[Rt+2+γV(St+2)−V(St+1)]=δt+γδt+1G_t^{(2)} - V(S_t) = [R_{t+1} + \gamma V(S_{t+1}) - V(S_t)] + \gamma [R_{t+2} + \gamma V(S_{t+2}) - V(S_{t+1})] = \delta_t + \gamma \delta_{t+1}

In general, for any horizon n≥1n \ge 1:

Gt(n)−V(St)=∑k=tt+n−1γk−tδkG_t^{(n)} - V(S_t) = \sum_{k=t}^{t+n-1} \gamma^{k-t} \delta_k

The forward-view λ\lambda-return blends all nn-step returns geometrically with weights (1−λ)λn−1(1-\lambda)\lambda^{n-1}:

Gtλ=(1−λ)∑n=1T−t−1λn−1Gt(n)+λT−t−1GtG_t^\lambda = (1 - \lambda) \sum_{n=1}^{T - t - 1} \lambda^{n-1} G_t^{(n)} + \lambda^{T - t - 1} G_t

Subtracting V(St)V(S_t) from both sides and substituting the telescoping expansion, the geometric weights group each δk\delta_k into an exponential factor:

Gtλ−V(St)=∑k=tT−1(γλ)k−tδkG_t^\lambda - V(S_t) = \sum_{k=t}^{T-1} (\gamma \lambda)^{k-t} \delta_k

This identity reveals that the forward λ\lambda-error is equal to the discounted sum of all future one-step TD errors from time tt to the end of the episode.

Swapping Summations: From Lookahead to Eligibility Traces

In the offline forward view, the value function is held fixed throughout the episode, and the total update applied to state ss at episode termination is the sum of forward updates over all visits:

ΔVfwd(s)=∑t=0T−1α[Gtλ−V(St)]1St=s=∑t=0T−1α1St=s∑k=tT−1(γλ)k−tδk\Delta V_{\text{fwd}}(s) = \sum_{t=0}^{T-1} \alpha [G_t^\lambda - V(S_t)] \mathbf{1}_{S_t = s} = \sum_{t=0}^{T-1} \alpha \mathbf{1}_{S_t = s} \sum_{k=t}^{T-1} (\gamma \lambda)^{k-t} \delta_k

This expression features a double summation over indices 0≤t≤k≤T−10 \le t \le k \le T-1. Reversing the order of summation—making kk the outer loop over time steps and tt the inner loop over past visits:

ΔVfwd(s)=∑k=0T−1αδk∑t=0k(γλ)k−t1St=s\Delta V_{\text{fwd}}(s) = \sum_{k=0}^{T-1} \alpha \delta_k \sum_{t=0}^k (\gamma \lambda)^{k-t} \mathbf{1}_{S_t = s}

Inspect the inner summation closely:

zk(s)≜∑t=0k(γλ)k−t1St=sz_k(s) \triangleq \sum_{t=0}^k (\gamma \lambda)^{k-t} \mathbf{1}_{S_t = s}

This matches the recursive definition of the accumulating eligibility trace:

zk(s)=γλzk−1(s)+1Sk=s,z−1(s)=0z_k(s) = \gamma \lambda z_{k-1}(s) + \mathbf{1}_{S_k = s}, \quad z_{-1}(s) = 0

Substituting zk(s)z_k(s) directly back into the reversed summation gives:

ΔVfwd(s)=∑k=0T−1αδkzk(s)=ΔVbwd(s)\Delta V_{\text{fwd}}(s) = \sum_{k=0}^{T-1} \alpha \delta_k z_k(s) = \Delta V_{\text{bwd}}(s)

This proves the exact offline equivalence: the forward view's future lookahead and the backward view's memory traces generate the identical cumulative weight update.

Worked numerical example

Consider a 2-step episodic trajectory: S0→R1=3.0S1→R2=4.0ST(terminal, V(ST)=0.0)S_0 \xrightarrow{R_1 = 3.0} S_1 \xrightarrow{R_2 = 4.0} S_T \quad (\text{terminal, } V(S_T) = 0.0)

Hyperparameters:

  • Initial estimates: V(S0)=1.0V(S_0) = 1.0, V(S1)=2.0V(S_1) = 2.0.
  • Discount factor γ=0.9\gamma = 0.9, decay parameter λ=0.7\lambda = 0.7, learning rate α=0.1\alpha = 0.1.
  • Effective trace decay: γλ=0.9×0.7=0.63\gamma \lambda = 0.9 \times 0.7 = 0.63.

1. Forward View Calculation

At step t=0t = 0 for state S0S_0:

  • 1-step return: G0(1)=R1+γV(S1)=3.0+0.9(2.0)=4.800G_0^{(1)} = R_1 + \gamma V(S_1) = 3.0 + 0.9(2.0) = 4.800
  • 2-step full return: G0(2)=R1+γR2+γ2V(ST)=3.0+0.9(4.0)+0=6.600G_0^{(2)} = R_1 + \gamma R_2 + \gamma^2 V(S_T) = 3.0 + 0.9(4.0) + 0 = 6.600
  • λ\lambda-return: G0λ=(1−λ)G0(1)+λG0(2)=0.3(4.800)+0.7(6.600)=1.440+4.620=6.060G_0^\lambda = (1 - \lambda) G_0^{(1)} + \lambda G_0^{(2)} = 0.3(4.800) + 0.7(6.600) = 1.440 + 4.620 = 6.060
  • Forward update for S0S_0: ΔVfwd(S0)=α[G0λ−V(S0)]=0.1×[6.060−1.0]=0.1×5.060=0.506\Delta V_{\text{fwd}}(S_0) = \alpha [G_0^\lambda - V(S_0)] = 0.1 \times [6.060 - 1.0] = 0.1 \times 5.060 = \mathbf{0.506}

At step t=1t = 1 for state S1S_1:

  • Full return to terminal: G1(1)=R2+γV(ST)=4.0+0=4.000G_1^{(1)} = R_2 + \gamma V(S_T) = 4.0 + 0 = 4.000
  • λ\lambda-return: G1λ=4.000G_1^\lambda = 4.000
  • Forward update for S1S_1: ΔVfwd(S1)=α[G1λ−V(S1)]=0.1×[4.000−2.0]=0.1×2.000=0.200\Delta V_{\text{fwd}}(S_1) = \alpha [G_1^\lambda - V(S_1)] = 0.1 \times [4.000 - 2.0] = 0.1 \times 2.000 = \mathbf{0.200}

2. Backward View Calculation

One-step TD errors:

  • δ0=R1+γV(S1)−V(S0)=3.0+0.9(2.0)−1.0=3.800\delta_0 = R_1 + \gamma V(S_1) - V(S_0) = 3.0 + 0.9(2.0) - 1.0 = 3.800
  • δ1=R2+γV(ST)−V(S1)=4.0+0.0−2.0=2.000\delta_1 = R_2 + \gamma V(S_T) - V(S_1) = 4.0 + 0.0 - 2.0 = 2.000

Eligibility trace vector across steps (zk=[zk(S0),zk(S1)]z_k = [z_k(S_0), z_k(S_1)]):

  • Step 00: Visit S0  ⟹  z0(S0)=1.000,  z0(S1)=0.000S_0 \implies z_0(S_0) = 1.000, \; z_0(S_1) = 0.000
  • Step 11: Decay by 0.630.63, visit S1  ⟹  z1(S0)=0.63×1.0=0.630,  z1(S1)=0.63(0)+1.0=1.000S_1 \implies z_1(S_0) = 0.63 \times 1.0 = 0.630, \; z_1(S_1) = 0.63(0) + 1.0 = 1.000

Accumulated backward updates:

  • For state S0S_0: ΔVbwd(S0)=αδ0z0(S0)+αδ1z1(S0)=0.1(3.800)(1.0)+0.1(2.000)(0.630)=0.380+0.126=0.506\Delta V_{\text{bwd}}(S_0) = \alpha \delta_0 z_0(S_0) + \alpha \delta_1 z_1(S_0) = 0.1(3.800)(1.0) + 0.1(2.000)(0.630) = 0.380 + 0.126 = \mathbf{0.506}
  • For state S1S_1: ΔVbwd(S1)=αδ0z0(S1)+αδ1z1(S1)=0.1(3.800)(0.0)+0.1(2.000)(1.0)=0.000+0.200=0.200\Delta V_{\text{bwd}}(S_1) = \alpha \delta_0 z_0(S_1) + \alpha \delta_1 z_1(S_1) = 0.1(3.800)(0.0) + 0.1(2.000)(1.0) = 0.000 + 0.200 = \mathbf{0.200}

The offline forward updates and offline backward updates match identically to three decimal places.

Offline Equivalence vs. Online Drift and True Online TD(λ)

The mathematical derivation above assumes that value estimates VV remain frozen while accumulating updates until the episode ends. When practitioners run standard online TD(λ)\text{TD}(\lambda), value estimates are updated immediately after every transition:

Vt+1(s)←Vt(s)+αδtzt(s)V_{t+1}(s) \leftarrow V_t(s) + \alpha \delta_t z_t(s)

Because VV changes on the fly, subsequent prediction errors δt+1\delta_{t+1} are computed using Vt+1V_{t+1} rather than V0V_0. This causes standard online TD(λ)\text{TD}(\lambda) to drift away from the forward-view λ\lambda-return, especially under large learning rates α\alpha.

To resolve this discrepancy, van Seijen and Sutton (2014) introduced True Online TD(λ)\text{TD}(\lambda). By employing Dutch traces:

et=γλet−1+(1−αγλxt⊤et−1)xte_t = \gamma \lambda e_{t-1} + \left(1 - \alpha \gamma \lambda \mathbf{x}_t^\top e_{t-1}\right) \mathbf{x}_t

and maintaining an auxiliary tracking scalar, True Online TD(λ)\text{TD}(\lambda) achieves exact step-by-step equivalence to the forward view in an online, causal algorithm—strictly dominating standard TD(λ)\text{TD}(\lambda) across tabular and linear approximation benchmarks.

Code

import mathfrom typing import Dict, List, Tuple

def verify_forward_backward_equivalence(    trajectory: List[Tuple[str, float]],    initial_values: Dict[str, float],    gamma: float = 0.9,    lam: float = 0.7,    alpha: float = 0.1,) -> Tuple[Dict[str, float], Dict[str, float]]:    """Compute and verify offline forward and backward TD(lambda) updates.
    Args:        trajectory: Sequence of (state, reward) pairs ending in 'terminal'.        initial_values: Initial state-value mapping V_0(s).        gamma: Discount factor in [0, 1].        lam: Trace decay parameter lambda in [0, 1].        alpha: Learning rate step size.
    Returns:        Tuple of (forward_updates, backward_updates) dictionaries.    """    states = [s for s, _ in trajectory[:-1]]    t_max = len(states)    unique_states = sorted(list(set(states)))
    # ---------------------------------------------------------    # 1. Offline Forward View: Compute lambda-return G_t^lambda    # ---------------------------------------------------------    fwd_updates: Dict[str, float] = {s: 0.0 for s in unique_states}
    for t in range(t_max):        s_t = states[t]        # Collect all n-step returns from step t        n_step_returns: List[float] = []        for n in range(1, t_max - t + 1):            # Sum discounted rewards up to step t + n            return_val = 0.0            for i in range(n):                reward_step = trajectory[t + i + 1][1]                return_val += (gamma**i) * reward_step
            # Add bootstrapped terminal value of horizon            next_state = trajectory[t + n][0]            if next_state != "terminal":                return_val += (gamma**n) * initial_values[next_state]            n_step_returns.append(return_val)
        # Blend n-step returns with geometric weights (1 - lambda)*lambda^(n-1)        g_lambda = 0.0        for n_idx, g_n in enumerate(n_step_returns[:-1]):            weight = (1.0 - lam) * (lam**n_idx)            g_lambda += weight * g_n
        # Final term receives remaining geometric tail weight lambda^(H-1)        last_weight = lam ** (len(n_step_returns) - 1)        g_lambda += last_weight * n_step_returns[-1]
        # Accumulate offline forward update        fwd_updates[s_t] += alpha * (g_lambda - initial_values[s_t])
    # ---------------------------------------------------------    # 2. Offline Backward View: Accumulating Eligibility Traces    # ---------------------------------------------------------    bwd_updates: Dict[str, float] = {s: 0.0 for s in unique_states}    traces: Dict[str, float] = {s: 0.0 for s in unique_states}
    for t in range(t_max):        s_t = states[t]        next_s, r_t1 = trajectory[t + 1]
        # Decay traces and bump visited state        for s in traces:            traces[s] *= gamma * lam        traces[s_t] += 1.0
        # One-step TD error using initial values        next_v = 0.0 if next_s == "terminal" else initial_values[next_s]        delta_t = r_t1 + gamma * next_v - initial_values[s_t]
        # Accumulate backward update across all states        for s in bwd_updates:            bwd_updates[s] += alpha * delta_t * traces[s]
    return fwd_updates, bwd_updates

if __name__ == "__main__":    # Trajectory: S0 ->(R=3)-> S1 ->(R=4)-> terminal    episode: List[Tuple[str, float]] = [        ("S0", 0.0),        ("S1", 3.0),        ("terminal", 4.0),    ]    v_init: Dict[str, float] = {"S0": 1.0, "S1": 2.0}
    fwd, bwd = verify_forward_backward_equivalence(        episode, v_init, gamma=0.9, lam=0.7, alpha=0.1    )
    print("=== Verification of Forward-Backward Equivalence ===")    for state in sorted(fwd.keys()):        f_val = fwd[state]        b_val = bwd[state]        is_equal = math.isclose(f_val, b_val, rel_tol=1e-9)        print(            f"State {state} | Forward Delta V: {f_val:.6f} | "            f"Backward Delta V: {b_val:.6f} | Exact Match: {is_equal}"        )        assert is_equal, f"Mismatch found in state {state}!"
    print("\nAll offline updates match to machine precision.")
# Expected Output:# === Verification of Forward-Backward Equivalence ===# State S0 | Forward Delta V: 0.506000 | Backward Delta V: 0.506000 | Exact Match: True# State S1 | Forward Delta V: 0.200000 | Backward Delta V: 0.200000 | Exact Match: True## All offline updates match to machine precision.

Watch Out For

Assuming Standard Online TD(λ) Matches the Forward View Exactly

A widespread misconception among reinforcement learning practitioners is assuming that standard online TD(λ)\text{TD}(\lambda) (updating values immediately on each time step) exactly computes the forward-view λ\lambda-return online.

In standard online TD(λ)\text{TD}(\lambda), the value function changes during the trajectory. As a result, subsequent TD errors δt\delta_t are evaluated using newer parameters VtV_t rather than the baseline V0V_0. This online drift breaks the exact telescoping identity ∑ΔVfwd≠∑ΔVbwd\sum \Delta V_{\text{fwd}} \neq \sum \Delta V_{\text{bwd}}. In environments with large step sizes α\alpha, standard online TD(λ)\text{TD}(\lambda) can oscillate or perform noticeably worse than the forward-view ideal.

The Fix:

  • If exact correspondence to the forward view is required online, implement True Online TD(λ)\text{TD}(\lambda) with Dutch traces.
  • When benchmarking algorithms against theoretical bounds, ensure updates are performed offline (accumulated over the full episode before applying).
  • Tune α\alpha conservatively when using standard online TD(λ)\text{TD}(\lambda) to minimize online drift.

The Quick Version

  • Theoretical vs. mechanistic: The forward view defines an ideal learning target (GtλG_t^\lambda) by looking into the future, whereas the backward view implements a causal, online algorithm using decaying memory traces (ztz_t).
  • The telescoping proof: Any multi-step error Gt(n)−V(St)G_t^{(n)} - V(S_t) telescopes into a discounted sum of one-step TD errors ∑γkδk\sum \gamma^k \delta_k. Blending these into Gtλ−V(St)G_t^\lambda - V(S_t) yields ∑(γλ)kδk\sum (\gamma \lambda)^k \delta_k.
  • Summation swap equivalence: By reversing the order of double summation across the episode, the future lookahead factors naturally regroup into accumulating eligibility traces: ∑ΔVfwd(s)≡∑ΔVbwd(s)\sum \Delta V_{\text{fwd}}(s) \equiv \sum \Delta V_{\text{bwd}}(s).
  • Online drift and True Online TD: Exact equivalence holds strictly offline; standard online updating introduces drift, which True Online TD(λ)\text{TD}(\lambda) resolves using Dutch traces.