Skip to content
AI360Xpert
Beta

Monte Carlo Control

Monte Carlo control learns optimal policies without knowing environment dynamics by rolling out complete episodes, averaging total returns to estimate action values, and greedily refining actions toward high-reward trajectories.

Monte Carlo control optimizes episodic policies through generalized policy iteration, sampling full trajectories to evaluate state-action values and greedifying the policy without transition dynamics.
Monte Carlo control optimizes episodic policies through generalized policy iteration, sampling full trajectories to evaluate state-action values and greedifying the policy without transition dynamics.

Why Does This Exist?

In classical planning frameworks like Dynamic Programming, finding an optimal policy requires full access to the environment's transition dynamics P(s′∣s,a)P(s' \mid s, a) and reward distributions R(s,a)R(s, a). However, in practical reinforcement learning problems—such as robotic manipulation, video games, or financial order routing—these governing probabilities are unknown or too intricate to compute analytically.

When dynamics are unknown, evaluating only state values V(s)V(s) is not enough to make decisions. Under state values alone, selecting the greedy action requires computing:

π′(s)=arg⁡max⁡a∈A∑s′∈SP(s′∣s,a)[R(s,a,s′)+γV(s′)]\pi'(s) = \arg\max_{a \in \mathcal{A}} \sum_{s' \in \mathcal{S}} P(s' \mid s, a) \left[ R(s, a, s') + \gamma V(s') \right]

This one-step lookahead still depends explicitly on the transition distribution P(s′∣s,a)P(s' \mid s, a). Without a transition model, the agent cannot evaluate which action leads to which successor states.

Monte Carlo control resolves this by shifting evaluation from state values V(s)V(s) to state-action values Q(s,a)Q(s, a). By estimating the expected return of taking action aa in state ss directly from sample episodes, the agent can greedify its policy without any model:

π′(s)=arg⁡max⁡a∈AQ(s,a)\pi'(s) = \arg\max_{a \in \mathcal{A}} Q(s, a)

Unlike Temporal Difference Learning, which bootstraps value estimates from immediate subsequent states (St+1S_{t+1}), Monte Carlo control samples complete episodes to calculate true discounted returns GtG_t. This eliminates bootstrapping bias completely and prevents divergence under function approximation in on-policy settings, providing a grounded framework for model-free policy optimization.

Think of It Like This

Golf player adjusting swing mechanics after full 18-hole rounds

Imagine a golfer trying out a new grip and swing tempo on an 18-hole course.

The golfer does not halt mid-swing on the 4th fairway to change their mechanics based on a speculative guess of how holes 5 through 18 might turn out—an approach analogous to one-step bootstrapping. Instead, they commit to their chosen swing technique (π\pi) for the entirety of the 18-hole round until sinking the final putt on the 18th green (the terminal state).

At the clubhouse, the golfer examines their scorecard:

  1. They tally the actual cumulative strokes taken across the entire round (GtG_t).
  2. They evaluate which club choices and shot selections from specific lies yielded lower aggregate scores (Q(s,a)Q(s, a)).
  3. They adjust their strategy for the next round (πk+1\pi_{k+1}), favoring the clubs and shots that produced the lowest total scores.

By repeating this round-by-round assessment, the golfer refines their game based entirely on real, completed outcomes rather than mid-round guesses.

Where the analogy stops: A golf course has mostly deterministic terrain, so single rounds provide reliable feedback. In stochastic reinforcement learning environments, wind gusts and unpredictable bounces alter outcomes. Monte Carlo control must average returns over multiple independent trajectories to wash out environmental noise, and must deliberately test varied clubs across rounds to avoid settling into suboptimal habits.

How It Actually Works

Generalized Policy Iteration with Monte Carlo Evaluation

Monte Carlo control applies the framework of Generalized Policy Iteration (GPI) to model-free settings. It alternates between two competing processes:

  1. Policy Evaluation: Estimating action values Q(s,a)Q(s, a) from empirical trajectory rollouts generated under the current policy π\pi.
  2. Policy Improvement: Updating the policy greedily with respect to the updated action values Q(s,a)Q(s, a).

π0→EvaluationQπ0→Improvementπ1→EvaluationQπ1→Improvement⋯→π∗,Q∗\pi_0 \xrightarrow{\text{Evaluation}} Q^{\pi_0} \xrightarrow{\text{Improvement}} \pi_1 \xrightarrow{\text{Evaluation}} Q^{\pi_1} \xrightarrow{\text{Improvement}} \dots \to \pi^*, Q^*

Mathematical Formulation

Consider a finite Markov Decision Process defined by the tuple (S,A,P,R,γ)(\mathcal{S}, \mathcal{A}, P, R, \gamma). The agent executes policy π\pi to generate a complete episode terminating at time step TT:

τ=(S0,A0,R1,S1,A1,R2,…,ST−1,AT−1,RT,ST)\tau = (S_0, A_0, R_1, S_1, A_1, R_2, \dots, S_{T-1}, A_{T-1}, R_T, S_T)

For each time step t∈[0,T−1]t \in [0, T-1], the total discounted return GtG_t is defined as:

Gt≐∑k=0T−t−1γkRt+k+1=Rt+1+γGt+1G_t \doteq \sum_{k=0}^{T - t - 1} \gamma^k R_{t+k+1} = R_{t+1} + \gamma G_{t+1}

To estimate Qπ(s,a)=Eπ[Gt∣St=s,At=a]Q^\pi(s, a) = \mathbb{E}_\pi [G_t \mid S_t = s, A_t = a], Monte Carlo evaluation tracks empirical returns:

  • First-Visit MC: Updates Q(s,a)Q(s, a) using only the first occurrence of state-action pair (s,a)(s, a) within episode τ\tau.
  • Every-Visit MC: Updates Q(s,a)Q(s, a) for every occurrence of (s,a)(s, a) within episode τ\tau.

Using an incremental sample average with visit count N(s,a)N(s, a):

N(St,At)←N(St,At)+1N(S_t, A_t) \leftarrow N(S_t, A_t) + 1

Q(St,At)←Q(St,At)+1N(St,At)[Gt−Q(St,At)]Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \frac{1}{N(S_t, A_t)} \left[ G_t - Q(S_t, A_t) \right]

For non-stationary policies or tracking tracking shifting values across iterations, practitioners often use a constant step size α∈(0,1]\alpha \in (0, 1]:

Q(St,At)←Q(St,At)+α[Gt−Q(St,At)]Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha \left[ G_t - Q(S_t, A_t) \right]

Policy Greedification and Monotonic Improvement

Once action values are updated, policy improvement makes the policy greedy with respect to QQ:

π′(s)←arg⁡max⁡a∈AQ(s,a)\pi'(s) \leftarrow \arg\max_{a \in \mathcal{A}} Q(s, a)

By the Policy Improvement Theorem, choosing the greedy action guarantees that the new policy π′\pi' achieves expected returns greater than or equal to the previous policy π\pi:

qπ(s,π′(s))=max⁡a∈Aqπ(s,a)≥qπ(s,π(s))=vπ(s)∀s∈Sq_\pi(s, \pi'(s)) = \max_{a \in \mathcal{A}} q_\pi(s, a) \ge q_\pi(s, \pi(s)) = v_\pi(s) \quad \forall s \in \mathcal{S}

Because the state space is finite, repeated alternation between evaluation and improvement drives the policy toward the unique optimal policy π∗\pi^*.

The Exploration Requirement

If policy π\pi becomes strictly deterministic, unchosen actions in state ss are never executed. Consequently, their action values Q(s,a)Q(s, a) receive no empirical returns and remain unrefined, risking premature convergence to suboptimal policies.

To ensure convergence, every state-action pair must be sampled infinitely often:

  • Monte Carlo with Exploring Starts: Assumes every episode starts with a randomly selected state-action pair (S0,A0)(S_0, A_0), guaranteeing that all pairs are visited.
  • ϵ\epsilon-Greedy Policies: Selects the greedy action with probability 1−ϵ+ϵ∣A∣1 - \epsilon + \frac{\epsilon}{|\mathcal{A}|}, and explores random actions with probability ϵ∣A∣\frac{\epsilon}{|\mathcal{A}|}, maintaining continual exploration throughout learning.

Worked numerical example

Consider an agent in decision state s0s_0 with discount factor γ=0.9\gamma = 0.9.

  • Available actions at s0s_0: asafea_{\text{safe}} and ariskya_{\text{risky}}.
  • Taking asafea_{\text{safe}} transitions to intermediate state s1s_1 with immediate reward R1=+1.0R_1 = +1.0. From s1s_1, the agent takes aproceeda_{\text{proceed}} to reach terminal state sTs_T with reward R2=+4.0R_2 = +4.0.
  • Taking ariskya_{\text{risky}} transitions directly to terminal state sTs_T with reward R1=+8.0R_1 = +8.0.

Initialize all tabular values to zero:

  • Q(s0,asafe)=0.0Q(s_0, a_{\text{safe}}) = 0.0, N(s0,asafe)=0N(s_0, a_{\text{safe}}) = 0
  • Q(s0,arisky)=0.0Q(s_0, a_{\text{risky}}) = 0.0, N(s0,arisky)=0N(s_0, a_{\text{risky}}) = 0
  • Q(s1,aproceed)=0.0Q(s_1, a_{\text{proceed}}) = 0.0, N(s1,aproceed)=0N(s_1, a_{\text{proceed}}) = 0
  • Initial policy: π0(s0)=asafe\pi_0(s_0) = a_{\text{safe}}, π0(s1)=aproceed\pi_0(s_1) = a_{\text{proceed}}.

Iteration 1: Rollout under π0\pi_0

  1. Trajectory Generation: τ1=(S0=s0,A0=asafe,R1=1.0,S1=s1,A1=aproceed,R2=4.0,S2=sT)\tau_1 = (S_0 = s_0, A_0 = a_{\text{safe}}, R_1 = 1.0, S_1 = s_1, A_1 = a_{\text{proceed}}, R_2 = 4.0, S_2 = s_T)

  2. Backward Return Accumulation:

    • At t=1t = 1 ((s1,aproceed)(s_1, a_{\text{proceed}})): G1=R2=4.0G_1 = R_2 = 4.0
    • At t=0t = 0 ((s0,asafe)(s_0, a_{\text{safe}})): G0=R1+γG1=1.0+(0.9×4.0)=1.0+3.6=4.6G_0 = R_1 + \gamma G_1 = 1.0 + (0.9 \times 4.0) = 1.0 + 3.6 = 4.6
  3. Q-Value Evaluation Update:

    • For (s1,aproceed)(s_1, a_{\text{proceed}}): N(s1,aproceed)=1N(s_1, a_{\text{proceed}}) = 1 Q(s1,aproceed)=0.0+11(4.0−0.0)=4.00Q(s_1, a_{\text{proceed}}) = 0.0 + \frac{1}{1}(4.0 - 0.0) = 4.00
    • For (s0,asafe)(s_0, a_{\text{safe}}): N(s0,asafe)=1N(s_0, a_{\text{safe}}) = 1 Q(s0,asafe)=0.0+11(4.6−0.0)=4.60Q(s_0, a_{\text{safe}}) = 0.0 + \frac{1}{1}(4.6 - 0.0) = 4.60
  4. Policy Greedification: π1(s0)=arg⁡max⁡{Q(s0,asafe)=4.60,Q(s0,arisky)=0.00}=asafe\pi_1(s_0) = \arg\max \left\{ Q(s_0, a_{\text{safe}}) = 4.60, Q(s_0, a_{\text{risky}}) = 0.00 \right\} = a_{\text{safe}}

The policy retains asafea_{\text{safe}} as the preferred action.

Iteration 2: Exploring Action ariskya_{\text{risky}}

An exploratory episode tests action ariskya_{\text{risky}} from state s0s_0:

  1. Trajectory Generation: τ2=(S0=s0,A0=arisky,R1=8.0,S1=sT)\tau_2 = (S_0 = s_0, A_0 = a_{\text{risky}}, R_1 = 8.0, S_1 = s_T)

  2. Backward Return Accumulation:

    • At t=0t = 0 ((s0,arisky)(s_0, a_{\text{risky}})): G0=R1=8.0G_0 = R_1 = 8.0
  3. Q-Value Evaluation Update:

    • For (s0,arisky)(s_0, a_{\text{risky}}): N(s0,arisky)=1N(s_0, a_{\text{risky}}) = 1 Q(s0,arisky)=0.0+11(8.0−0.0)=8.00Q(s_0, a_{\text{risky}}) = 0.0 + \frac{1}{1}(8.0 - 0.0) = 8.00
  4. Policy Greedification: Compare action values at state s0s_0: Q(s0,asafe)=4.60vsQ(s0,arisky)=8.00Q(s_0, a_{\text{safe}}) = 4.60 \quad \text{vs} \quad Q(s_0, a_{\text{risky}}) = 8.00 π2(s0)=arg⁡max⁡{4.60,8.00}=arisky\pi_2(s_0) = \arg\max \left\{ 4.60, 8.00 \right\} = a_{\text{risky}}

The policy switches from asafea_{\text{safe}} to ariskya_{\text{risky}}. Through sample-based returns alone, Generalized Policy Iteration identified the higher-value action without computing transition probability expectations.

Code

from collections import defaultdictimport randomfrom typing import Dict, List, Optional, Set, Tuple

class StepTransition:    """Represents a single step in an episodic trajectory."""
    def __init__(self, state: str, action: str, reward: float) -> None:        self.state: str = state        self.action: str = action        self.reward: float = reward
    def __repr__(self) -> str:        return f"({self.state}, {self.action}, R={self.reward})"

class EpisodicEnvironment:    """Benchmark discrete MDP with absorbing terminal state."""
    def __init__(self) -> None:        self.state: str = "s0"
    def reset(self, start_state: Optional[str] = None) -> str:        self.state = start_state if start_state is not None else "s0"        return self.state
    def step(self, action: str) -> Tuple[str, float, bool]:        """Execute an action and return (next_state, reward, is_terminal)."""        if self.state == "s0":            if action == "safe":                self.state = "s1"                return "s1", 1.0, False            elif action == "risky":                self.state = "terminal"                return "terminal", 8.0, True        elif self.state == "s1":            if action == "proceed":                self.state = "terminal"                return "terminal", 4.0, True        raise ValueError(f"Invalid transition from state {self.state} with action {action}")

class MonteCarloControl:    """First-visit Monte Carlo Control algorithm with tabular Q-values."""
    def __init__(        self,        actions_by_state: Dict[str, List[str]],        gamma: float = 0.9,        epsilon: float = 0.1,    ) -> None:        self.actions_by_state: Dict[str, List[str]] = actions_by_state        self.gamma: float = gamma        self.epsilon: float = epsilon
        # Tabular estimates and visit counts        self.q_table: Dict[Tuple[str, str], float] = defaultdict(float)        self.returns_sum: Dict[Tuple[str, str], float] = defaultdict(float)        self.returns_count: Dict[Tuple[str, str], int] = defaultdict(int)
        # Deterministic target policy mapping state -> best action        self.policy: Dict[str, str] = {            s: acts[0] for s, acts in actions_by_state.items()        }
    def select_action(self, state: str) -> str:        """Select action via epsilon-greedy exploration."""        if random.random() < self.epsilon:            return random.choice(self.actions_by_state[state])        return self.policy[state]
    def update_from_episode(self, trajectory: List[StepTransition]) -> None:        """Evaluate action values and greedify policy from completed trajectory."""        g: float = 0.0
        # Backward recursion from terminal time step        for t in reversed(range(len(trajectory))):            step = trajectory[t]            g = self.gamma * g + step.reward
            # First-visit check: verify (state, action) did not occur prior to t            prior_pairs = {(trajectory[i].state, trajectory[i].action) for i in range(t)}            pair = (step.state, step.action)
            if pair not in prior_pairs:                self.returns_sum[pair] += g                self.returns_count[pair] += 1                self.q_table[pair] = self.returns_sum[pair] / self.returns_count[pair]
                # Policy improvement: greedify with respect to Q(s, a)                best_action = max(                    self.actions_by_state[step.state],                    key=lambda a: self.q_table[(step.state, a)],                )                self.policy[step.state] = best_action

if __name__ == "__main__":    env = EpisodicEnvironment()    actions = {"s0": ["safe", "risky"], "s1": ["proceed"]}    mc = MonteCarloControl(actions_by_state=actions, gamma=0.9, epsilon=0.0)
    # Episode 1: Policy follows s0 -> safe -> s1 -> proceed -> terminal    ep1 = [StepTransition("s0", "safe", 1.0), StepTransition("s1", "proceed", 4.0)]    mc.update_from_episode(ep1)
    print("--- Iteration 1 (Episode 1) ---")    print("Trajectory:", ep1)    print("Q-values:  ", {k: round(v, 2) for k, v in mc.q_table.items()})    print("Policy:    ", mc.policy)
    # Episode 2: Exploring start tests s0 -> risky -> terminal    ep2 = [StepTransition("s0", "risky", 8.0)]    mc.update_from_episode(ep2)
    print("\n--- Iteration 2 (Episode 2) ---")    print("Trajectory:", ep2)    print("Q-values:  ", {k: round(v, 2) for k, v in mc.q_table.items()})    print("Policy:    ", mc.policy)
    # Expected Output:    # --- Iteration 1 (Episode 1) ---    # Trajectory: [(s0, safe, R=1.0), (s1, proceed, R=4.0)]    # Q-values:   {('s1', 'proceed'): 4.0, ('s0', 'safe'): 4.6}    # Policy:     {'s0': 'safe', 's1': 'proceed'}    #    # --- Iteration 2 (Episode 2) ---    # Trajectory: [(s0, risky, R=8.0)]    # Q-values:   {('s1', 'proceed'): 4.0, ('s0', 'safe'): 4.6, ('s0', 'risky'): 8.0}    # Policy:     {'s0': 'risky', 's1': 'proceed'}

Watch Out For

Long or infinite episodes blocking value updates in continuing tasks

Monte Carlo control requires trajectories to reach an absorbing terminal state before executing a single update. Because discounted returns are computed backwards from the terminal step (Gt=Rt+1+γGt+1G_t = R_{t+1} + \gamma G_{t+1}), non-terminating continuing tasks or environments with cyclic loops prevent backward passes entirely.

If an episode runs for thousands of steps without terminating, the agent buffers transitions in memory without updating its policy. In strictly continuing tasks (T→∞T \to \infty), the algorithm halts learning completely and exhausts available RAM.

The Fix: Impose an explicit horizon truncation (Tmax⁡T_{\max}) with environment resets, or switch to one-step bootstrapping methods like SARSA or Q-learning. Bootstrapping algorithms update values after every transition (St,At,Rt+1,St+1)(S_t, A_t, R_{t+1}, S_{t+1}), enabling continuous learning without episodic termination.

Premature policy convergence from insufficient action exploration

A deterministic greedy improvement step (π(s)=arg⁡max⁡aQ(s,a)\pi(s) = \arg\max_a Q(s, a)) introduces severe confirmation bias. If an agent tests a mediocre action that happens to return a modest positive reward +1+1, the policy greedily locks onto that action. Alternative actions that might yield +100+100 remain unvisited, leaving their Q(s,a)Q(s, a) values uninitialized.

Because the policy never selects unvisited actions again, the agent permanently misses optimal paths, stabilizing on suboptimal local optima.

The Fix: Deploy persistent exploration mechanisms. In simulation environments where initial states are configurable, use Exploring Starts to guarantee all (s,a)(s, a) combinations begin episodes. In standard environments, enforce an ϵ\epsilon-greedy policy with a decaying schedule (ϵt=max⁡(ϵmin⁡,ϵ0⋅dt)\epsilon_t = \max(\epsilon_{\min}, \epsilon_0 \cdot d^t)), ensuring continuous exploration early in training while converging toward greedy exploitation.

The Quick Version

  • Model-free control via Q-values: Monte Carlo control optimizes policies without transition probabilities P(s′∣s,a)P(s' \mid s, a) by learning action values Q(s,a)Q(s, a), enabling direct greedy action selection (arg⁡max⁡aQ(s,a)\arg\max_a Q(s, a)).
  • Episodic Generalized Policy Iteration: It alternates complete trajectory sampling to estimate empirical returns with immediate greedy policy improvement, monotonically increasing policy performance.
  • Zero bootstrapping bias: Evaluating true cumulative returns GtG_t eliminates bootstrapping error and prevents divergence, but requires waiting for complete episode termination.
  • Mandatory exploration: Because deterministic policies lock onto early positive returns, algorithms must employ Exploring Starts or ϵ\epsilon-soft exploration to ensure all state-action pairs are sampled.