Skip to content
AI360Xpert
Beta

On-Policy MC Control

On-policy Monte Carlo control learns optimal behavior directly from sampled episodes without unrealistic exploring starts by using an epsilon-soft policy that ensures every action continues to be visited.

On-Policy First-Visit Monte Carlo Control alternates episodic return evaluation and epsilon-greedy policy improvement to converge toward the optimal epsilon-soft policy without exploring starts.
On-Policy First-Visit Monte Carlo Control alternates episodic return evaluation and epsilon-greedy policy improvement to converge toward the optimal epsilon-soft policy without exploring starts.

Why Does This Exist?

In model-free reinforcement learning, an agent does not have access to transition probabilities p(s′,r∣s,a)p(s', r \mid s, a). To make decisions without knowing the environment's underlying physics, the agent cannot simply learn state values v(s)v(s); it must learn action values q(s,a)q(s, a). With q(s,a)q(s, a) in hand, acting greedily is straightforward: choose arg⁡max⁡aq(s,a)\arg\max_a q(s, a) without looking one step ahead into unknown future states.

However, estimating action values from sampled experience creates a fundamental exploration dilemma. If an agent behaves according to a deterministic policy π(s)\pi(s), it will always choose the same action from a given state. Alternative actions from that state will never be sampled, their returns will never be observed, and the agent can never discover whether an untested branch holds higher reward.

The theoretical solution used in early Monte Carlo control is the Assumption of Exploring Starts: every episode begins at a state-action pair (s0,a0)(s_0, a_0) sampled with non-zero probability across all possible pairs in S×A\mathcal{S} \times \mathcal{A}. While mathematically convenient, exploring starts is practically impossible in real systems. An autonomous car cannot be initialized in mid-collision on step zero; a chemical plant cannot be booted directly into an explosive pressure state; a game of chess must always begin at the standard opening board.

Without exploring starts, reinforcement learning divides into two paradigms:

  1. Off-policy learning: The agent uses an exploratory behavior policy bb to generate transitions, while using importance sampling ratios to evaluate and improve a distinct target policy π\pi.
  2. On-policy learning: The agent evaluates and improves the very same policy that it uses to generate decisions in the environment.

On-Policy First-Visit Monte Carlo Control solves the exploration bottleneck without exploring starts by constraining the policy to remain ε\varepsilon-soft. By assigning a minimum probability of selection to every available action at every state, the agent guarantees that all state-action pairs will be sampled infinitely often as training proceeds, enabling true model-free Generalized Policy Iteration from arbitrary fixed starting states.

Think of It Like This

The touring comedian testing fresh jokes

Imagine a professional stand-up comedian embarking on a 100-night nationwide theater tour.

Every evening, the comedian faces a live paying crowd. The audience expects a hilarious show, which tempts the comedian to perform only their battle-tested, guaranteed laugh lines (pure exploitation). However, if the comedian only tells old jokes, their routine stagnates, and they will never develop brilliant new closers for their upcoming special.

Crucially, the comedian cannot rely on "exploring starts." They cannot teleport into the middle of a bizarre joke before walking onto stage; every single performance begins at the stage wings with the standard microphone check.

Instead, the comedian adopts an on-policy ε\varepsilon-soft strategy. For 1−ε1 - \varepsilon (say, 85%85\%) of their set time, they deliver their highest-rated material—the greedy punchlines based on laughs accumulated over earlier tour dates. But for the remaining ε\varepsilon (15%15\%) of the set, they deliberately experiment with untested jokes drawn uniformly from their notebook.

Because this method is on-policy, the comedian experiences the real-world consequences of their experiments. If a new joke bombs, the room goes silent, and that joke's empirical value plummets immediately. If a fresh punchline produces an eruption of laughter, its estimated value rises. Over successive tour stops, the highest-value joke displaces the old closer, and the set's greedy core shifts.

Where the analogy stops: A human comedian eventually retires the 15%15\% experimentation slot for their televised special to deliver a 100%100\% polished, deterministic performance. In standard on-policy MC control with a fixed ε\varepsilon, the agent never stops testing: it remains permanently ε\varepsilon-soft, forever reserving an ε\varepsilon probability of taking suboptimal exploratory actions.

How It Actually Works

Eliminating Exploring Starts via ε\varepsilon-Soft Policies

Let S\mathcal{S} denote the set of states, and A(s)\mathcal{A}(s) denote the finite set of actions available in state ss. A policy π(a∣s)\pi(a \mid s) defines the probability of selecting action aa given state ss.

An ε\varepsilon-soft policy is any stochastic policy where the probability of choosing any available action is strictly bounded below:

π(a∣s)≥ε∣A(s)∣∀s∈S,  a∈A(s)\pi(a \mid s) \ge \frac{\varepsilon}{|\mathcal{A}(s)|} \quad \forall s \in \mathcal{S}, \; a \in \mathcal{A}(s)

where ε∈(0,1]\varepsilon \in (0, 1]. Because every action maintains non-zero probability at every visit, every state-action pair (s,a)(s, a) reachable under the environment dynamics is guaranteed to be visited infinitely often as the number of episodes approaches infinity.

Among all valid ε\varepsilon-soft policies, the policy that places maximum probability on the best-performing action is the ε\varepsilon-greedy policy:

π(a∣s)={1−ε+ε∣A(s)∣if a=a∗=arg⁡max⁡a′Q(s,a′)ε∣A(s)∣if a≠a∗\pi(a \mid s) = \begin{cases} 1 - \varepsilon + \dfrac{\varepsilon}{|\mathcal{A}(s)|} & \text{if } a = a^* = \arg\max_{a'} Q(s, a') \\[8pt] \dfrac{\varepsilon}{|\mathcal{A}(s)|} & \text{if } a \neq a^* \end{cases}

where ties for the maximal action value are broken arbitrarily.

The non-greedy actions (of which there are ∣A(s)∣−1|\mathcal{A}(s)| - 1) each receive an exploration share of ε∣A(s)∣\frac{\varepsilon}{|\mathcal{A}(s)|}. Their combined mass is:

(∣A(s)∣−1)ε∣A(s)∣=ε−ε∣A(s)∣(|\mathcal{A}(s)| - 1) \frac{\varepsilon}{|\mathcal{A}(s)|} = \varepsilon - \frac{\varepsilon}{|\mathcal{A}(s)|}

Summing the greedy and non-greedy action probabilities confirms that π(⋅∣s)\pi(\cdot \mid s) is a valid probability distribution:

(1−ε+ε∣A(s)∣)+(ε−ε∣A(s)∣)=1\left(1 - \varepsilon + \frac{\varepsilon}{|\mathcal{A}(s)|}\right) + \left(\varepsilon - \frac{\varepsilon}{|\mathcal{A}(s)|}\right) = 1

Policy Improvement Theorem for ε\varepsilon-Soft Policies

In Generalized Policy Iteration (GPI), we alternate between policy evaluation (updating Q≈qπQ \approx q_\pi) and policy improvement (updating π≈greedy(Q)\pi \approx \text{greedy}(Q)). For on-policy MC control to work, shifting from an ε\varepsilon-greedy policy π\pi to a new ε\varepsilon-greedy policy π′\pi' with respect to qπq_\pi must guarantee that the expected return does not decrease.

Let qπ(s,a)q_\pi(s, a) be the true action-value function under policy π\pi. The expected value of state ss when taking actions according to the new ε\varepsilon-greedy policy π′\pi' is:

qπ(s,π′(s))=∑a∈A(s)π′(a∣s)qπ(s,a)q_\pi(s, \pi'(s)) = \sum_{a \in \mathcal{A}(s)} \pi'(a \mid s) q_\pi(s, a)

Substituting the definition of the ε\varepsilon-greedy policy π′\pi':

qπ(s,π′(s))=ε∣A(s)∣∑a∈A(s)qπ(s,a)+(1−ε)max⁡a∈A(s)qπ(s,a)q_\pi(s, \pi'(s)) = \frac{\varepsilon}{|\mathcal{A}(s)|} \sum_{a \in \mathcal{A}(s)} q_\pi(s, a) + (1 - \varepsilon) \max_{a \in \mathcal{A}(s)} q_\pi(s, a)

Now consider the expected value of state ss under the original policy π\pi:

vπ(s)=∑a∈A(s)π(a∣s)qπ(s,a)=ε∣A(s)∣∑a∈A(s)qπ(s,a)+(1−ε)∑a∈A(s)π(a∣s)−ε∣A(s)∣1−εqπ(s,a)v_\pi(s) = \sum_{a \in \mathcal{A}(s)} \pi(a \mid s) q_\pi(s, a) = \frac{\varepsilon}{|\mathcal{A}(s)|} \sum_{a \in \mathcal{A}(s)} q_\pi(s, a) + (1 - \varepsilon) \sum_{a \in \mathcal{A}(s)} \frac{\pi(a \mid s) - \frac{\varepsilon}{|\mathcal{A}(s)|}}{1 - \varepsilon} q_\pi(s, a)

Notice that the weighting coefficients w(a)=π(a∣s)−ε∣A(s)∣1−εw(a) = \frac{\pi(a \mid s) - \frac{\varepsilon}{|\mathcal{A}(s)|}}{1 - \varepsilon} are non-negative (since π(a∣s)≥ε∣A(s)∣\pi(a \mid s) \ge \frac{\varepsilon}{|\mathcal{A}(s)|}) and sum to 11:

∑a∈A(s)w(a)=∑aπ(a∣s)−ε1−ε=1−ε1−ε=1\sum_{a \in \mathcal{A}(s)} w(a) = \frac{\sum_{a} \pi(a \mid s) - \varepsilon}{1 - \varepsilon} = \frac{1 - \varepsilon}{1 - \varepsilon} = 1

Because any convex combination (weighted average) of numbers cannot exceed their maximum:

∑a∈A(s)w(a)qπ(s,a)≤max⁡a∈A(s)qπ(s,a)\sum_{a \in \mathcal{A}(s)} w(a) q_\pi(s, a) \le \max_{a \in \mathcal{A}(s)} q_\pi(s, a)

Multiplying by (1−ε)(1 - \varepsilon) and adding the uniform baseline ε∣A(s)∣∑aqπ(s,a)\frac{\varepsilon}{|\mathcal{A}(s)|} \sum_a q_\pi(s, a) to both sides gives:

qπ(s,π′(s))≥vπ(s)∀s∈Sq_\pi(s, \pi'(s)) \ge v_\pi(s) \quad \forall s \in \mathcal{S}

By the Policy Improvement Theorem, if qπ(s,π′(s))≥vπ(s)q_\pi(s, \pi'(s)) \ge v_\pi(s) for all states ss, then:

vπ′(s)≥vπ(s)∀s∈Sv_{\pi'}(s) \ge v_\pi(s) \quad \forall s \in \mathcal{S}

Equality holds if and only if π\pi is already optimal among all ε\varepsilon-soft policies. Thus, every ε\varepsilon-greedy policy improvement step strictly increases or maintains performance within the space of ε\varepsilon-soft policies.

Complete Algorithm: On-Policy First-Visit MC Control

The full algorithm proceeds episode by episode without needing a transition model or exploring starts:

  1. Initialization:

    • Set Q(s,a)∈RQ(s, a) \in \mathbb{R} arbitrarily (typically 0.00.0) and visit counts N(s,a)←0N(s, a) \leftarrow 0 for all s∈S,a∈A(s)s \in \mathcal{S}, a \in \mathcal{A}(s).
    • Initialize π(a∣s)\pi(a \mid s) as an arbitrary ε\varepsilon-soft policy (e.g., uniform probability 1∣A(s)∣\frac{1}{|\mathcal{A}(s)|}).
    • Fix discount factor γ∈[0,1]\gamma \in [0, 1] and exploration parameter ε∈(0,1]\varepsilon \in (0, 1].
  2. Episode Generation:

    • Sample a complete trajectory using behavior policy π\pi: S0,A0,R1,S1,A1,R2,…,ST−1,AT−1,RT,STS_0, A_0, R_1, S_1, A_1, R_2, \dots, S_{T-1}, A_{T-1}, R_T, S_T
  3. Backward Return Accumulation & First-Visit Update:

    • Initialize cumulative return G←0G \leftarrow 0.
    • For each step t=T−1,T−2,…,0t = T-1, T-2, \dots, 0: G←γG+Rt+1G \leftarrow \gamma G + R_{t+1}
    • First-Visit Gate: Check if the pair (St,At)(S_t, A_t) appeared earlier in the episode (i.e., at any index k<tk < t).
    • If (St,At)(S_t, A_t) is visited for the first time:
      • Increment count: N(St,At)←N(St,At)+1N(S_t, A_t) \leftarrow N(S_t, A_t) + 1
      • Update action value via incremental sample average: Q(St,At)←Q(St,At)+1N(St,At)[G−Q(St,At)]Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \frac{1}{N(S_t, A_t)} \left[ G - Q(S_t, A_t) \right]
      • Determine best action: A∗←arg⁡max⁡aQ(St,a)A^* \leftarrow \arg\max_a Q(S_t, a)
      • Update policy π(⋅∣St)\pi(\cdot \mid S_t) for all a∈A(St)a \in \mathcal{A}(S_t): π(a∣St)←{1−ε+ε∣A(St)∣if a=A∗ε∣A(St)∣if a≠A∗\pi(a \mid S_t) \leftarrow \begin{cases} 1 - \varepsilon + \dfrac{\varepsilon}{|\mathcal{A}(S_t)|} & \text{if } a = A^* \\[6pt] \dfrac{\varepsilon}{|\mathcal{A}(S_t)|} & \text{if } a \neq A^* \end{cases}

Worked numerical example

Consider a decision state ss with 3 available actions: A(s)={a1,a2,a3}\mathcal{A}(s) = \{a_1, a_2, a_3\} (∣A∣=3|\mathcal{A}| = 3), discount factor γ=0.9\gamma = 0.9, and exploration parameter ε=0.3\varepsilon = 0.3.

Initial Values and Baseline Policy

Prior to the current episode, the agent has recorded:

  • Q(s,a1)=2.0Q(s, a_1) = 2.0 with N(s,a1)=4N(s, a_1) = 4
  • Q(s,a2)=1.0Q(s, a_2) = 1.0 with N(s,a2)=2N(s, a_2) = 2
  • Q(s,a3)=0.5Q(s, a_3) = 0.5 with N(s,a3)=1N(s, a_3) = 1

The current greedy action is a∗=a1a^* = a_1. Under ε=0.3\varepsilon = 0.3, the exploratory allocation per action is:

ε∣A∣=0.33=0.10\frac{\varepsilon}{|\mathcal{A}|} = \frac{0.3}{3} = 0.10

The baseline ε\varepsilon-greedy policy assigns:

π(a1∣s)=1−0.3+0.10=0.80\pi(a_1 \mid s) = 1 - 0.3 + 0.10 = 0.80 π(a2∣s)=0.10,π(a3∣s)=0.10\pi(a_2 \mid s) = 0.10, \quad \pi(a_3 \mid s) = 0.10

Episode Generation

At time step t=0t=0, the agent is in state ss. A random roll falls into the exploratory interval, selecting action a2a_2 (drawn with probability 0.100.10).

The episode continues through subsequent states, generating transition rewards:

  • Step 00: (s,a2)→(s, a_2) \to reward R1=+1.0R_1 = +1.0, next state s1s_1
  • Step 11: (s1,a1)→(s_1, a_1) \to reward R2=+4.0R_2 = +4.0, next state s2s_2
  • Step 22: (s2,a3)→(s_2, a_3) \to reward R3=+10.0R_3 = +10.0, terminal state sTs_T

Backward Return Computation

Iterating backward from terminal state sTs_T with γ=0.9\gamma = 0.9:

  • Step 22: G2=R3=10.0G_2 = R_3 = 10.0
  • Step 11: G1=R2+γG2=4.0+0.9(10.0)=4.0+9.0=13.0G_1 = R_2 + \gamma G_2 = 4.0 + 0.9(10.0) = 4.0 + 9.0 = 13.0
  • Step 00: G0=R1+γG1=1.0+0.9(13.0)=1.0+11.7=12.7G_0 = R_1 + \gamma G_1 = 1.0 + 0.9(13.0) = 1.0 + 11.7 = 12.7

First-Visit Update for (s,a2)(s, a_2)

We inspect the trajectory: pair (s,a2)(s, a_2) appeared only at t=0t=0. The first-visit condition is satisfied.

  1. Increment visit count: N(s,a2)←2+1=3N(s, a_2) \leftarrow 2 + 1 = 3
  2. Update action-value estimate Q(s,a2)Q(s, a_2): Qnew(s,a2)=Qold(s,a2)+1N(s,a2)[G0−Qold(s,a2)]Q_{\text{new}}(s, a_2) = Q_{\text{old}}(s, a_2) + \frac{1}{N(s, a_2)} \left[ G_0 - Q_{\text{old}}(s, a_2) \right] Qnew(s,a2)=1.0+13(12.7−1.0)=1.0+11.73=1.0+3.9=4.9Q_{\text{new}}(s, a_2) = 1.0 + \frac{1}{3} \left( 12.7 - 1.0 \right) = 1.0 + \frac{11.7}{3} = 1.0 + 3.9 = 4.9

Policy Improvement Step

Compare the updated action values at state ss:

  • Q(s,a1)=2.0Q(s, a_1) = 2.0
  • Q(s,a2)=4.9Q(s, a_2) = 4.9
  • Q(s,a3)=0.5Q(s, a_3) = 0.5

The greedy action flips from a1a_1 to a2a_2 because 4.9>2.04.9 > 2.0. The policy π(⋅∣s)\pi(\cdot \mid s) updates immediately:

πnew(a2∣s)=1−0.3+0.33=0.80\pi_{\text{new}}(a_2 \mid s) = 1 - 0.3 + \frac{0.3}{3} = 0.80 πnew(a1∣s)=0.33=0.10,πnew(a3∣s)=0.33=0.10\pi_{\text{new}}(a_1 \mid s) = \frac{0.3}{3} = 0.10, \quad \pi_{\text{new}}(a_3 \mid s) = \frac{0.3}{3} = 0.10

Through exploratory sampling on-policy, the agent discovered that a2a_2 leads to high long-term returns, updated Q(s,a2)Q(s, a_2), and shifted 80%80\% of its future action selection mass onto a2a_2 without requiring exploring starts.

Code

The following self-contained Python implementation trains an agent on a gridworld navigation task using on-policy first-visit Monte Carlo control. The agent begins at (0,0)(0, 0) and must reach the goal (2,2)(2, 2) while accumulating −1-1 reward per step:

import randomfrom typing import Dict, List, Tuple

class GridWorld:    """A 3x3 gridworld navigation task where reaching (2, 2) is terminal."""
    def __init__(self, width: int = 3, height: int = 3) -> None:        self.width = width        self.height = height        self.start = (0, 0)        self.goal = (width - 1, height - 1)        self.state = self.start        self.num_actions = 4        # Actions: 0: Up, 1: Down, 2: Left, 3: Right        self.moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    def reset(self) -> Tuple[int, int]:        """Reset environment to standard start state."""        self.state = self.start        return self.state
    def step(self, action: int) -> Tuple[Tuple[int, int], float, bool]:        """Apply action, returning next_state, reward, and done flag."""        dr, dc = self.moves[action]        nr = max(0, min(self.height - 1, self.state[0] + dr))        nc = max(0, min(self.width - 1, self.state[1] + dc))        self.state = (nr, nc)        reward = -1.0        done = (self.state == self.goal)        return self.state, reward, done

class OnPolicyFirstVisitMCControl:    """On-policy first-visit Monte Carlo control using epsilon-soft policies."""
    def __init__(        self,        env: GridWorld,        epsilon: float = 0.2,        gamma: float = 0.9,    ) -> None:        self.env = env        self.epsilon = epsilon        self.gamma = gamma        self.num_actions = env.num_actions
        # Initialize Q-values and visit counts for all state-action pairs        self.Q: Dict[Tuple[int, int], List[float]] = {            (r, c): [0.0] * self.num_actions            for r in range(env.height)            for c in range(env.width)        }        self.N: Dict[Tuple[int, int], List[int]] = {            (r, c): [0] * self.num_actions            for r in range(env.height)            for c in range(env.width)        }
    def select_action(self, state: Tuple[int, int]) -> int:        """Select action according to current epsilon-greedy policy."""        if random.random() < self.epsilon:            # Explore: uniform random selection across all actions            return random.randint(0, self.num_actions - 1)        # Exploit: pick greedy action with random tie-breaking        q_vals = self.Q[state]        max_q = max(q_vals)        best_actions = [a for a, q in enumerate(q_vals) if q == max_q]        return random.choice(best_actions)
    def generate_episode(        self, max_steps: int = 100    ) -> List[Tuple[Tuple[int, int], int, float]]:        """Roll out an episode under the epsilon-soft policy."""        episode: List[Tuple[Tuple[int, int], int, float]] = []        state = self.env.reset()        for _ in range(max_steps):            if state == self.env.goal:                break            action = self.select_action(state)            next_state, reward, done = self.env.step(action)            episode.append((state, action, reward))            state = next_state            if done:                break        return episode
    def train(self, num_episodes: int = 3000) -> None:        """Run on-policy first-visit Monte Carlo control across episodes."""        for _ in range(num_episodes):            episode = self.generate_episode()            if not episode:                continue
            # Identify the first occurrence step index for each (state, action) pair            first_occurrence: Dict[Tuple[Tuple[int, int], int], int] = {}            for t, (s, a, _) in enumerate(episode):                if (s, a) not in first_occurrence:                    first_occurrence[(s, a)] = t
            # Backward pass to accumulate discounted return G            G = 0.0            for t in reversed(range(len(episode))):                s, a, r = episode[t]                G = self.gamma * G + r
                # First-visit update condition                if first_occurrence[(s, a)] == t:                    self.N[s][a] += 1                    # Incremental sample average update                    self.Q[s][a] += (G - self.Q[s][a]) / self.N[s][a]
    def get_greedy_policy(self) -> Dict[Tuple[int, int], str]:        """Extract dominant greedy direction for each state."""        action_chars = {0: "^", 1: "v", 2: "<", 3: ">"}        policy: Dict[Tuple[int, int], str] = {}        for s, q_vals in self.Q.items():            if s == self.env.goal:                policy[s] = "G"            else:                best_a = max(range(self.num_actions), key=lambda a: q_vals[a])                policy[s] = action_chars[best_a]        return policy

# Execution and validationif __name__ == "__main__":    random.seed(42)    env = GridWorld(width=3, height=3)    agent = OnPolicyFirstVisitMCControl(env, epsilon=0.2, gamma=0.9)    agent.train(num_episodes=3000)
    policy = agent.get_greedy_policy()    print("Learned Greedy Policy:")    for r in range(3):        row = [f" {policy[(r, c)]} " for c in range(3)]        print("".join(row))
    print("\nState-Action Values Q(s, a) at Start State (0, 0):")    action_labels = ["Up   ", "Down ", "Left ", "Right"]    for a, name in enumerate(action_labels):        print(f"  {name}: {agent.Q[(0, 0)][a]:.2f} (Visits: {agent.N[(0, 0)][a]})")

Expected Output

Learned Greedy Policy: v  v  v  >  v  v  >  >  G 
State-Action Values Q(s, a) at Start State (0, 0):  Up   : -4.55 (Visits: 166)  Down : -3.90 (Visits: 2829)  Left : -4.56 (Visits: 188)  Right: -3.98 (Visits: 189)

Watch Out For

The Suboptimality Gap of Fixed Epsilon

A frequent misconception is assuming that on-policy Monte Carlo control converges to the globally optimal deterministic policy π∗\pi^*. It does not.

Because the agent is on-policy, the policy evaluated by Q(s,a)Q(s, a) is the exploratory policy π\pi itself. Under a fixed ε>0\varepsilon > 0, the agent must commit to taking a random action ε\varepsilon fraction of the time for all eternity. Consequently, the algorithm converges to π∗ε\pi_*^\varepsilon—the optimal policy among all ε\varepsilon-soft policies—not the unconstrained optimum π∗\pi^*. In environments with high-penalty boundaries (such as cliff walking), an ε\varepsilon-soft agent will deliberately choose longer, detour paths away from the cliff because it anticipates its own inevitable random exploratory mistakes.

The Fix: If a deterministic optimal policy is required, gradually anneal ε\varepsilon toward zero across training episodes using a decay schedule (such as εk=ε01+c⋅k\varepsilon_k = \frac{\varepsilon_0}{1 + c \cdot k}), satisfying stochastic approximation conditions. Alternatively, switch to an off-policy method (such as Q-learning or Off-Policy MC with Weighted Importance Sampling), where the behavior policy remains exploratory while the target policy is strictly greedy.

Duplicate Visits and Backward Return Leaks

In episodic tasks with cycles or revisits, a single state-action pair (s,a)(s, a) can occur multiple times within the same episode rollout.

If a developer mistakenly updates Q(St,At)Q(S_t, A_t) on every backward step without checking for earlier occurrences, the algorithm degenerates into every-visit Monte Carlo. While every-visit MC is asymptotically consistent, treating dependent returns from the same episode as independent samples introduces severe bias in finite-sample estimates and destabilizes early policy updates. Conversely, accumulating returns forward without tracking first-visit indices leads to incorrect discount powers.

The Fix: Pre-compute first-visit timestamps using a dictionary or set during the forward trajectory rollout, and only trigger the incremental QQ-value update when the backward iterator index tt matches the recorded first-visit timestamp.

The Quick Version

  • On-Policy Monte Carlo Control eliminates the unrealistic requirement of exploring starts by forcing the behavior policy to remain ε\varepsilon-soft (π(a∣s)≥ε∣A(s)∣>0\pi(a \mid s) \ge \frac{\varepsilon}{|\mathcal{A}(s)|} > 0).
  • The algorithm is strictly on-policy: it evaluates and improves the exact same ε\varepsilon-greedy distribution that generates trajectories in the environment.
  • The Policy Improvement Theorem for ε\varepsilon-Soft Policies guarantees that choosing the greedy action with probability 1−ε+ε∣A∣1 - \varepsilon + \frac{\varepsilon}{|\mathcal{A}|} monotonically improves expected return: qπ(s,π′(s))≥vπ(s)q_\pi(s, \pi'(s)) \ge v_\pi(s).
  • Returns are accumulated backwards along complete episodic rollouts, and action values Q(s,a)Q(s, a) are updated incrementally on the first visit of each state-action pair.
  • The learned policy converges to π∗ε\pi_*^\varepsilon (the best ε\varepsilon-soft policy); annealing ε→0\varepsilon \to 0 over training bridges the gap to the globally optimal deterministic policy π∗\pi^*.