xwm.planning¶
CEM, MPPI, gradient planning, MPC and PUCT-MCTS, plus latent cost functions. Everything here is jittable: candidates are vmaped and refinement is a lax.fori_loop. See Planning.
Planning: using a learned world model to choose actions.
This is what a predictive world model is for. The encoder turns observations into latents, the action-conditioned predictor imagines what actions would do, and a planner searches for the action sequence whose imagined outcome is best -- all without ever rendering a pixel.
Which planner depends on the action space and on whether you have a value function:
CEM,MPPI-- continuous actions, sample whole sequences. What TD-MPC2 and the JEPA action-conditioned models use.GradientPlanner-- continuous, differentiates through the rollout. Sample-efficient but happy to exploit model error.MCTS-- discrete actions, grows a tree and spends its budget where the model is least certain. What MuZero uses.
Modules:
| Name | Description |
|---|---|
cost |
Cost functions defined in latent space. |
gradient |
Gradient-based planning. |
mcts |
Monte-Carlo tree search over a learned model (MuZero's planner). |
mpc |
Receding-horizon control (MPC) on top of a planner. |
sampling |
Sampling-based planners: CEM and MPPI. |
Classes:
| Name | Description |
|---|---|
GradientPlanner |
Optimise an action sequence by gradient descent on the rollout cost. |
MCTS |
PUCT tree search over a learned latent model. |
SearchResult |
What a search returns. |
ControlStep |
One closed-loop control step. |
Planner |
Anything with a |
CEM |
Cross-entropy method: iteratively refit a Gaussian to the elite samples. |
MPPI |
Model-predictive path integral: softmax-weighted average of all samples. |
Plan |
The result of a planning call. |
Functions:
| Name | Description |
|---|---|
goal_cost |
Drive the latent state toward |
latent_distance |
Scalar distance between two latent states of identical shape. |
return_cost |
Negated discounted return -- the objective a value-based agent plans on. |
reward_cost |
Turn a learned latent reward (higher is better) into a cost to minimise. |
sum_costs |
Weighted sum of several cost terms. |
control_step |
Plan from the current latent state and return the first action. |
run_mpc |
Run a closed loop against a real (or simulated) environment. |
shift_plan |
Advance a plan by one step, repeating the last action at the tail. |
GradientPlanner
¶
GradientPlanner(horizon: int, action_dim: int, *, n_steps: int = 100, learning_rate: float = 0.05, low: float | Array = -1.0, high: float | Array = 1.0, cost_on: str = 'next')
Bases: Module
Optimise an action sequence by gradient descent on the rollout cost.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
horizon
|
int
|
planning horizon. |
required |
action_dim
|
int
|
action dimensionality. |
required |
n_steps
|
int
|
optimisation steps. |
100
|
learning_rate
|
float
|
Adam step size on the actions. |
0.05
|
low, high
|
bounds, enforced by projection after each step. |
required |
Source code in xwm/planning/gradient.py
MCTS
¶
MCTS(n_actions: int, *, n_simulations: int = 50, discount: float = 0.997, c_puct: float = 1.25, dirichlet_alpha: float = 0.3, root_noise_fraction: float = 0.25, max_depth: int = 50)
Bases: Module
PUCT tree search over a learned latent model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_actions
|
int
|
size of the discrete action space. |
required |
n_simulations
|
int
|
search budget. |
50
|
discount
|
float
|
RL discount used when backing values up the tree. |
0.997
|
c_puct
|
float
|
exploration constant. |
1.25
|
dirichlet_alpha, root_noise_fraction
|
root exploration noise. |
required | |
max_depth
|
int
|
hard cap on tree depth, which also bounds the arrays. |
50
|
Methods:
| Name | Description |
|---|---|
search |
Run the search from |
Source code in xwm/planning/mcts.py
search
¶
search(key: PRNGKey, root_latent: Array, recurrent: Callable[[Array, Array], tuple[Array, Array]], predict: Callable[[Array], tuple[Array, Array]], *, add_noise: bool = True) -> SearchResult
Run the search from root_latent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
recurrent
|
Callable[[Array, Array], tuple[Array, Array]]
|
|
required |
predict
|
Callable[[Array], tuple[Array, Array]]
|
|
required |
add_noise
|
bool
|
Dirichlet noise at the root. On for self-play data collection, off for evaluation. |
True
|
Returns:
| Type | Description |
|---|---|
SearchResult
|
A |
Source code in xwm/planning/mcts.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | |
SearchResult
¶
Bases: NamedTuple
What a search returns.
Attributes:
| Name | Type | Description |
|---|---|---|
policy |
Array
|
|
value |
Array
|
root value, the visit-weighted mean of the children's returns. |
action |
Array
|
the most-visited action. |
visits |
Array
|
raw visit counts, for diagnostics. |
ControlStep
¶
Bases: NamedTuple
One closed-loop control step.
Attributes:
| Name | Type | Description |
|---|---|---|
action |
Array
|
the action to execute now. |
warm_start |
Array
|
shifted plan to seed the next call. |
plan |
Plan
|
the full plan, for logging or diagnostics. |
Planner
¶
Bases: Protocol
Anything with a plan method: CEM, MPPI, GradientPlanner.
CEM
¶
CEM(horizon: int, action_dim: int, *, n_samples: int = 512, n_elites: int = 64, n_iters: int = 6, low: float | Array = -1.0, high: float | Array = 1.0, init_std: float = 0.5, min_std: float = 0.05, momentum: float = 0.1, cost_on: str = 'next')
Bases: Module
Cross-entropy method: iteratively refit a Gaussian to the elite samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
horizon
|
int
|
planning horizon in steps. |
required |
action_dim
|
int
|
action dimensionality. |
required |
n_samples
|
int
|
candidates per iteration. |
512
|
n_elites
|
int
|
how many best candidates define the next proposal. |
64
|
n_iters
|
int
|
refinement iterations. |
6
|
low, high
|
action bounds; candidates are clipped into them. |
required | |
init_std
|
float
|
initial per-dimension spread. |
0.5
|
min_std
|
float
|
floor on the spread, so the search cannot collapse to a point and stop exploring. |
0.05
|
momentum
|
float
|
smoothing of the proposal across iterations, in |
0.1
|
Methods:
| Name | Description |
|---|---|
plan |
Search for a low-cost action sequence from latent state |
Source code in xwm/planning/sampling.py
plan
¶
plan(key: PRNGKey, dynamics: LatentDynamics, z0: Array, cost_fn: CostFn, *, init_mean: Array | None = None) -> Plan
Search for a low-cost action sequence from latent state z0.
Source code in xwm/planning/sampling.py
MPPI
¶
MPPI(horizon: int, action_dim: int, *, n_samples: int = 512, n_iters: int = 4, low: float | Array = -1.0, high: float | Array = 1.0, temperature: float = 1.0, noise_std: float = 0.5, cost_on: str = 'next')
Bases: Module
Model-predictive path integral: softmax-weighted average of all samples.
Unlike CEM's hard elite cut, every candidate contributes in proportion to
exp(-cost / temperature). The soft weighting makes the update smoother
across control steps, which matters when the planner is in a feedback loop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
temperature
|
float
|
lower values approach CEM's greedy behaviour; higher values average more broadly. Costs are shifted by their minimum before exponentiating, so the scale is relative and numerically safe. |
1.0
|
noise_std
|
float
|
proposal spread, held fixed rather than refit. |
0.5
|
Source code in xwm/planning/sampling.py
Plan
¶
Bases: NamedTuple
The result of a planning call.
Attributes:
| Name | Type | Description |
|---|---|---|
actions |
Array
|
|
cost |
Array
|
its predicted cost under the world model. |
mean |
Array
|
|
std |
Array
|
|
goal_cost
¶
goal_cost(z_goal: Array, *, kind: Distance = 'l2', horizon: int | None = None, terminal_only: bool = False, action_penalty: float = 0.0, discount: float = 1.0) -> CostFn
Drive the latent state toward z_goal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
z_goal
|
Array
|
target latent, same shape as the rollout states. |
required |
kind
|
Distance
|
distance to use. |
'l2'
|
horizon
|
int | None
|
needed only when |
None
|
terminal_only
|
bool
|
score just the final state. Charging every step instead (the default) rewards reaching the goal early and staying there, which is usually what you want and is much better conditioned. |
False
|
action_penalty
|
float
|
weight on |
0.0
|
discount
|
float
|
per-step multiplier; |
1.0
|
Source code in xwm/planning/cost.py
latent_distance
¶
Scalar distance between two latent states of identical shape.
Source code in xwm/planning/cost.py
return_cost
¶
return_cost(reward_fn: Callable[[Array, Array], Array], value_fn: Callable[[Array], Array] | None = None, *, horizon: int, discount: float = 0.99) -> CostFn
Negated discounted return -- the objective a value-based agent plans on.
-(sum_t gamma^t r(z_t, a_t) + gamma^H V(z_H)).
The terminal value is what makes this different from a goal cost, and it is the whole reason TD-MPC2 can plan with a horizon of three: the value head summarises everything beyond the horizon, so the planner does not have to simulate it. Without that term a short-horizon planner is myopic by construction -- it cannot prefer a move whose payoff arrives on step four.
Use with cost_on="current" (the default for planners built by
xwm.families.tdmpc2.planner): a reward head is trained as
r(z_t, a_t), so it must be evaluated at the latent the action was taken
from.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reward_fn
|
Callable[[Array, Array], Array]
|
|
required |
value_fn
|
Callable[[Array], Array] | None
|
|
None
|
horizon
|
int
|
planning horizon, needed to know which step is terminal. |
required |
discount
|
float
|
RL discount. |
0.99
|
Source code in xwm/planning/cost.py
reward_cost
¶
reward_cost(reward_fn: Callable[[Array], Array], *, action_penalty: float = 0.0, discount: float = 1.0) -> CostFn
Turn a learned latent reward (higher is better) into a cost to minimise.
Source code in xwm/planning/cost.py
sum_costs
¶
Weighted sum of several cost terms.
Source code in xwm/planning/cost.py
control_step
¶
control_step(key: PRNGKey, planner: Planner, dynamics: LatentDynamics, z: Array, cost_fn: CostFn, *, warm_start: Array | None = None) -> ControlStep
Plan from the current latent state and return the first action.
Source code in xwm/planning/mpc.py
run_mpc
¶
run_mpc(key: PRNGKey, planner: Planner, dynamics: LatentDynamics, cost_fn: CostFn, *, encode: Callable[[Array], Array], step_env: Callable[[Array, Array], Array], observation: Array, n_steps: int) -> tuple[list[Array], list[Array], list[Array]]
Run a closed loop against a real (or simulated) environment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encode
|
Callable[[Array], Array]
|
observation -> latent state, i.e. the world model's encoder. |
required |
step_env
|
Callable[[Array, Array], Array]
|
|
required |
observation
|
Array
|
the starting observation. |
required |
n_steps
|
int
|
control steps to execute. |
required |
Returns:
| Type | Description |
|---|---|
list[Array]
|
|
list[Array]
|
Python loop because the environment step is outside JAX; each planning |
list[Array]
|
call is still a single jitted device call. |