xwm.families¶
The three model families and a registry. Each family is a different answer to what trains the latent space; they share encoders, dynamics and planners. See Models for the narrative version.
Model families.
Three ways to learn an action-conditioned world model, all sharing the same
encoders (xwm.encoders), latent dynamics (xwm.dynamics) and
planners (xwm.planning). What separates them is what signal trains the
latent space:
| family | learning signal | planner |
|---|---|---|
jepa |
its own future embeddings | CEM / MPPI |
tdmpc2 |
reward + TD value | MPPI |
muzero |
search-improved targets | MCTS |
JEPA needs no reward, so it can pretrain on passive video -- abundant, unlabelled robot footage. TD-MPC2 and MuZero need reward and therefore interaction, but they learn a value function, so their planner can look beyond its horizon. They are complementary: a JEPA encoder is a reasonable initialisation for either.
Functions:
| Name | Description |
|---|---|
available |
Registered model names. |
create |
Build a registered model by name. |
families |
The family each name belongs to. |
Attributes:
| Name | Type | Description |
|---|---|---|
REGISTRY |
dict[str, Callable[..., WorldModel]]
|
|
REGISTRY
¶
REGISTRY: dict[str, Callable[..., WorldModel]] = {'jepa/image': _jepa_image, 'jepa/image-lejepa': _jepa_image_lejepa, 'jepa/video': _jepa_video, 'jepa/video-lejepa': _jepa_video_lejepa, 'jepa/action': _jepa_action, 'tdmpc2': _tdmpc2, 'muzero': _muzero}
available
¶
xwm.families.jepa¶
The JEPA family: predict masked latents, don't collapse.
Self-supervised world models that learn a representation by predicting their own embeddings at positions they were not shown. No reward, no reconstruction.
JEPA-- the model;ijepa,vjepa,lejepaare recipes over it.ActionWorldModel-- the V-JEPA 2-AC stage: freeze a JEPA encoder and learn action-conditioned dynamics in its latent space. This is the family's bridge to robotics.
Modules:
| Name | Description |
|---|---|
action |
Action-conditioned world models (the V-JEPA 2-AC recipe). |
model |
The JEPA world model: predict masked latents, and don't collapse. |
predictor |
The JEPA predictor: predict embeddings at positions it has not seen. |
recipes |
Recipes: the published JEPA configurations, assembled from the parts. |
Classes:
| Name | Description |
|---|---|
ActionWorldModel |
A frozen (or fine-tuned) observation encoder plus learned latent dynamics. |
JEPA |
Joint-embedding predictive architecture over a token grid. |
JEPAPredictor |
Predict embeddings at positions it has not seen, given context tokens. |
Functions:
| Name | Description |
|---|---|
action_world_model |
Assemble an |
ijepa |
I-JEPA: multi-block latent prediction with an EMA teacher. |
lejepa |
LeJEPA for images: the same prediction problem, SIGReg instead of a teacher. |
video_lejepa |
LeJEPA for video: tube-masked prediction with SIGReg instead of a teacher. |
vjepa |
V-JEPA: tube-masked latent prediction with an EMA teacher. |
ActionWorldModel
¶
ActionWorldModel(encoder: VisionEncoder, dynamics: ActionConditionedPredictor, *, freeze_encoder: bool = True, teacher_forcing_weight: float = 1.0, rollout_weight: float = 1.0, loss_kind: LossKind = 'l1', normalize_target: bool = False, collapse: str = 'none', reg_weight: float = 1.0, n_proj: int = 256, video_key: str = 'video', action_key: str = 'action')
Bases: WorldModel
A frozen (or fine-tuned) observation encoder plus learned latent dynamics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
VisionEncoder
|
maps one observation to |
required |
dynamics
|
ActionConditionedPredictor
|
the one-step action-conditioned model. |
required |
freeze_encoder
|
bool
|
exclude the encoder from |
True
|
teacher_forcing_weight, rollout_weight
|
mixture of the two losses. |
required | |
loss_kind
|
LossKind
|
how latents are compared (V-JEPA 2-AC uses L1). |
'l1'
|
collapse
|
str
|
|
'none'
|
reg_weight, n_proj
|
SIGReg settings, used only when |
required |
Methods:
| Name | Description |
|---|---|
trainable |
Everything inexact, minus the encoder when it is frozen. |
dynamics_fn |
A plain |
encode |
Encode one observation to |
embed |
Mean-pooled |
encode_sequence |
Encode |
imagine |
Roll the dynamics forward from |
loss |
Args: |
Source code in xwm/families/jepa/action.py
trainable
¶
Everything inexact, minus the encoder when it is frozen.
Source code in xwm/families/jepa/action.py
dynamics_fn
¶
A plain (z, a) -> z' closure in eval mode, ready for a planner.
encode
¶
embed
¶
Mean-pooled (D,) representation of one observation, in eval mode.
Mirrors xwm.JEPA.embed, so a probe or a nearest-neighbour lookup
works the same way whichever model family produced the encoder.
Source code in xwm/families/jepa/action.py
encode_sequence
¶
Encode (T, ...) observations independently to (T, N, D).
Source code in xwm/families/jepa/action.py
imagine
¶
Roll the dynamics forward from z0: (H, A) -> (H, N, D).
Wrap with equinox.filter_jit rather than jax.jit -- the
bound method carries the model's parameters, which plain jit would
try to treat as static.
Source code in xwm/families/jepa/action.py
loss
¶
loss(batch: Batch, *, key: PRNGKey, target: ActionWorldModel | None = None) -> tuple[Array, Metrics]
batch: {"video": (B, T, ...), "action": (B, T - 1, A)}.
action[b, t] is the action taken between frames t and
t + 1.
Source code in xwm/families/jepa/action.py
JEPA
¶
JEPA(encoder: VisionEncoder, predictor: JEPAPredictor, mask_sampler: Module, *, input_key: str = 'image', collapse: Collapse = 'ema', loss_kind: LossKind = 'smooth_l1', normalize_target: bool = False, reg_weight: float = 1.0, n_proj: int = 256, statistic: Statistic = 'epps_pulley', n_nodes: int = 32, sigma: float = 1.0)
Bases: WorldModel
Joint-embedding predictive architecture over a token grid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
VisionEncoder
|
context encoder; also the teacher unless |
required |
predictor
|
JEPAPredictor
|
maps context tokens to target-position embeddings. |
required |
mask_sampler
|
Module
|
callable |
required |
input_key
|
str
|
which field of the batch holds the input tensor. |
'image'
|
collapse
|
Collapse
|
anti-collapse strategy; see the module docstring. |
'ema'
|
loss_kind
|
LossKind
|
how predicted and target embeddings are compared. |
'smooth_l1'
|
normalize_target
|
bool
|
LayerNorm targets before comparing (V-JEPA does). |
False
|
reg_weight
|
float
|
coefficient on the |
1.0
|
n_proj, statistic, n_nodes, sigma
|
SIGReg settings. |
required |
Methods:
| Name | Description |
|---|---|
encode |
Encode one sample to |
embed |
Mean-pooled |
prepare_batch |
Attach a freshly sampled mask. Runs on the host, outside |
predict |
Predict every target block from one sample's context tokens. |
Source code in xwm/families/jepa/model.py
encode
¶
Encode one sample to (N, D) tokens. This is the transferable part.
embed
¶
prepare_batch
¶
Attach a freshly sampled mask. Runs on the host, outside jit.
predict
¶
predict(context: Array, masks: MaskBatch, *, key: PRNGKey | None = None) -> Array
Predict every target block from one sample's context tokens.
Returns (M, K_tgt, D). Each block gets its own mask token (cycling
if there are more blocks than tokens), so the predictor can distinguish
the concurrent prediction problems it is being asked to solve.
Source code in xwm/families/jepa/model.py
JEPAPredictor
¶
JEPAPredictor(grid: tuple[int, ...], embed_dim: int, *, pred_dim: int, depth: int, num_heads: int, key: PRNGKey | None = None, n_mask_tokens: int = 1, pos: PosKind = 'sincos', mlp_ratio: float = 4.0, dropout: float = 0.0, drop_path: float = 0.0, layer_scale: float | None = None, remat: bool = False)
Bases: Module
Predict embeddings at positions it has not seen, given context tokens.
This is the module that makes a JEPA predictive rather than merely contrastive. Context tokens are projected into a narrower predictor width, concatenated with one learned mask token per target position -- each carrying the target's positional embedding, and nothing else about its content -- and the stack attends over the whole sequence. Reading out the mask-token positions gives the prediction.
Keeping the predictor narrower than the encoder (pred_dim < embed_dim)
is deliberate: a predictor strong enough to model detail removes the
pressure on the encoder to make its representation predictable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
grid
|
tuple[int, ...]
|
token grid the positions index into. |
required |
embed_dim
|
int
|
encoder width (both the input and the output width). |
required |
pred_dim
|
int
|
internal predictor width. |
required |
n_mask_tokens
|
int
|
distinct learned mask tokens. Using one per target block lets the predictor tell concurrent prediction problems apart. |
1
|
Source code in xwm/families/jepa/predictor.py
action_world_model
¶
action_world_model(*, key: PRNGKey | None = None, action_dim: int, encoder: VisionEncoder | None = None, size: str = 'small', img_size: int | tuple[int, int] = 224, patch_size: int = 16, in_channels: int = 3, action_scale: float = 1.0, conditioning: Conditioning = 'both', freeze_encoder: bool = True, dynamics_kwargs: dict | None = None, **model_kwargs) -> ActionWorldModel
Assemble an ActionWorldModel.
Pass encoder to build dynamics on top of an existing (typically
pretrained and frozen) encoder -- the V-JEPA 2-AC setup. Omit it and a fresh
ImageEncoder of the given size is created, which is what
you want for a from-scratch experiment.
Source code in xwm/families/jepa/action.py
ijepa
¶
ijepa(*, key: PRNGKey | None = None, size: str = 'small', img_size: int | tuple[int, int] = 224, patch_size: int = 16, in_channels: int = 3, n_targets: int = 4, target_scale: float = 0.15, context_scale: float | None = None, loss_kind: str = 'smooth_l1', encoder_kwargs: dict | None = None, predictor_kwargs: dict | None = None) -> JEPA
I-JEPA: multi-block latent prediction with an EMA teacher.
Train it with xwm.training.Trainer, which maintains the teacher.
Source code in xwm/families/jepa/recipes.py
lejepa
¶
lejepa(*, key: PRNGKey | None = None, size: str = 'small', img_size: int | tuple[int, int] = 224, patch_size: int = 16, in_channels: int = 3, n_targets: int = 4, target_scale: float = 0.15, context_scale: float | None = None, reg_weight: float = 1.0, n_proj: int = 256, loss_kind: str = 'smooth_l1', encoder_kwargs: dict | None = None, predictor_kwargs: dict | None = None) -> JEPA
LeJEPA for images: the same prediction problem, SIGReg instead of a teacher.
reg_weight is the only knob that governs the collapse/expressivity
trade-off, and there is no teacher for the trainer to maintain.
Source code in xwm/families/jepa/recipes.py
video_lejepa
¶
video_lejepa(*, key: PRNGKey | None = None, size: str = 'small', img_size: int | tuple[int, int] = 224, patch_size: int = 16, num_frames: int = 16, tubelet_size: int = 2, in_channels: int = 3, n_targets: int = 8, spatial_scale: float = 0.15, temporal_extent: int | None = None, context_scale: float | None = None, reg_weight: float = 1.0, n_proj: int = 256, loss_kind: str = 'l1', encoder_kwargs: dict | None = None, predictor_kwargs: dict | None = None) -> JEPA
LeJEPA for video: tube-masked prediction with SIGReg instead of a teacher.
Source code in xwm/families/jepa/recipes.py
vjepa
¶
vjepa(*, key: PRNGKey | None = None, size: str = 'small', img_size: int | tuple[int, int] = 224, patch_size: int = 16, num_frames: int = 16, tubelet_size: int = 2, in_channels: int = 3, n_targets: int = 8, spatial_scale: float = 0.15, temporal_extent: int | None = None, context_scale: float | None = None, loss_kind: str = 'l1', normalize_target: bool = True, encoder_kwargs: dict | None = None, predictor_kwargs: dict | None = None) -> JEPA
V-JEPA: tube-masked latent prediction with an EMA teacher.
Defaults follow the paper: L1 loss on LayerNorm-ed targets, eight short-range tubes spanning the whole clip.
Source code in xwm/families/jepa/recipes.py
xwm.families.tdmpc2¶
TD-MPC2: latent dynamics trained by reward and temporal-difference value.
Modules:
| Name | Description |
|---|---|
model |
TD-MPC2: a latent world model trained by reward and TD value. |
Classes:
| Name | Description |
|---|---|
TDMPC2 |
A TD-MPC2 agent. |
Functions:
| Name | Description |
|---|---|
planner |
An MPPI planner wired to |
tdmpc2 |
Assemble a TD-MPC2 agent. |
TDMPC2
¶
TDMPC2(encoder: Encoder, dynamics: MLPDynamics, reward: ScalarHead, critic: QEnsemble, policy: GaussianPolicy, *, horizon: int = 3, discount: float = 0.99, consistency_coef: float = 20.0, reward_coef: float = 0.1, value_coef: float = 0.1, rho: float = 0.5, entropy_coef: float = 0.0001, obs_key: str = 'observation', action_key: str = 'action', reward_key: str = 'reward')
Bases: WorldModel
A TD-MPC2 agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
Encoder
|
observation encoder. Token output is mean-pooled to a flat latent, since the dynamics and heads here are MLPs. |
required |
dynamics
|
MLPDynamics
|
latent dynamics; SimNorm-normalised by default. |
required |
reward
|
ScalarHead
|
reward head, action-conditioned. |
required |
critic
|
QEnsemble
|
Q-ensemble. |
required |
policy
|
GaussianPolicy
|
policy prior. |
required |
horizon
|
int
|
unroll length for the consistency/reward/value losses. |
3
|
discount
|
float
|
RL discount. |
0.99
|
consistency_coef, reward_coef, value_coef
|
loss weights. |
required | |
rho
|
float
|
per-step decay on the unroll losses. Later steps are less reliable because they start from a predicted latent, so they count less. |
0.5
|
entropy_coef
|
float
|
SAC-style entropy bonus for the policy. |
0.0001
|
Methods:
| Name | Description |
|---|---|
encode |
Observation -> flat latent |
dynamics_fn |
A plain |
act |
Policy-prior action, no planning. See |
td_target |
|
loss |
Args: |
Source code in xwm/families/tdmpc2/model.py
encode
¶
Observation -> flat latent (D,).
Encoders emit (N, D) tokens; the dynamics and heads here are MLPs
over a single vector, so tokens are mean-pooled.
Source code in xwm/families/tdmpc2/model.py
dynamics_fn
¶
act
¶
Policy-prior action, no planning. See xwm.planning to plan.
td_target
¶
td_target(target: TDMPC2, z_next: Array, reward: Array, key: PRNGKey) -> Array
r + gamma * Q_target(z', pi(z')), fully detached.
The next action comes from the current policy but the Q from the EMA target critic: bootstrapping off the online critic is what makes value learning diverge.
Source code in xwm/families/tdmpc2/model.py
loss
¶
loss(batch: Batch, *, key: PRNGKey, target: TDMPC2 | None = None) -> tuple[Array, Metrics]
batch: {"observation": (B, H + 1, ...), "action": (B, H, A),
"reward": (B, H)} -- a contiguous slice of a trajectory.
Source code in xwm/families/tdmpc2/model.py
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 258 259 260 261 262 263 264 | |
planner
¶
planner(model: TDMPC2, *, horizon: int | None = None, n_samples: int = 512, n_iters: int = 6, temperature: float = 0.5, noise_std: float = 0.5)
An MPPI planner wired to model's own reward and value heads.
Returns (planner, cost_fn); call planner.plan(key, model.dynamics_fn(),
z, cost_fn). The planner scores candidates by discounted predicted reward
plus a terminal value bootstrap, evaluated at the pre-transition latent
because that is how the reward head was trained.
Source code in xwm/families/tdmpc2/model.py
tdmpc2
¶
tdmpc2(*, action_dim: int, encoder: Encoder | None = None, observation: str = 'state', state_dim: int | None = None, img_size: int = 64, patch_size: int = 8, latent_dim: int = 512, hidden_dim: int = 512, horizon: int = 3, n_bins: int = 101, key: PRNGKey | None = None, **model_kwargs) -> TDMPC2
Assemble a TD-MPC2 agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
observation
|
str
|
|
'state'
|
encoder
|
Encoder | None
|
supply your own -- e.g. a frozen JEPA encoder -- and the rest is built around it. |
None
|
latent_dim
|
int
|
width of the pooled latent the dynamics and heads act on. |
512
|
Source code in xwm/families/tdmpc2/model.py
xwm.families.muzero¶
MuZero: a latent model trained to agree with its own tree search.
Modules:
| Name | Description |
|---|---|
model |
MuZero: a latent model trained to make search consistent. |
targets |
Building MuZero's training targets from self-play. |
Classes:
| Name | Description |
|---|---|
MuZero |
A MuZero agent over a discrete action space. |
PolicyHead |
|
Functions:
| Name | Description |
|---|---|
muzero |
Assemble a MuZero agent over a discrete action space. |
n_step_value_targets |
|
MuZero
¶
MuZero(encoder: Encoder, dynamics: MLPDynamics, reward: ScalarHead, value: ScalarHead, policy: PolicyHead, *, n_actions: int, horizon: int = 5, reward_coef: float = 1.0, value_coef: float = 0.25, policy_coef: float = 1.0, obs_key: str = 'observation', action_key: str = 'action', reward_key: str = 'reward', value_key: str = 'value_target', policy_key: str = 'policy_target')
Bases: WorldModel
A MuZero agent over a discrete action space.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
Encoder
|
the representation network. |
required |
dynamics
|
MLPDynamics
|
the recurrent latent dynamics. |
required |
reward
|
ScalarHead
|
reward head, conditioned on the one-hot action. |
required |
value
|
ScalarHead
|
value head. |
required |
policy
|
PolicyHead
|
policy logits head. |
required |
n_actions
|
int
|
size of the action set. |
required |
horizon
|
int
|
unroll length during training. |
5
|
reward_coef, value_coef, policy_coef
|
loss weights. |
required |
Methods:
| Name | Description |
|---|---|
represent |
Observation -> latent |
recurrent |
|
predict |
|
search_fns |
Eval-mode |
loss |
Args: |
Source code in xwm/families/muzero/model.py
represent
¶
recurrent
¶
(z, action_index) -> (z', reward). The signature MCTS wants.
Source code in xwm/families/muzero/model.py
predict
¶
search_fns
¶
loss
¶
loss(batch: Batch, *, key: PRNGKey, target: MuZero | None = None) -> tuple[Array, Metrics]
batch: exactly what xwm.training.ReplayBuffer.sample returns --
{"observation": (B, H + 1, ...), "action": (B, H),
"reward": (B, H), "value_target": (B, H + 1),
"policy_target": (B, H + 1, n_actions)}.
Only ``observation[:, 0]`` is read. Everything after it comes
from the model's own unroll, and that is the whole point: MuZero
never re-encodes an observation mid-unroll, so the latent is free
to be whatever makes the predictions work. The later
observations are accepted (and ignored) so the same batch feeds
both this family and TD-MPC2.
Source code in xwm/families/muzero/model.py
PolicyHead
¶
PolicyHead(latent_dim: int, n_actions: int, *, key: PRNGKey | None = None, hidden_dim: int = 256, depth: int = 2)
Bases: WorldModel
s -> logits over a discrete action set.
Source code in xwm/families/muzero/model.py
muzero
¶
muzero(*, n_actions: int, encoder: Encoder | None = None, observation: str = 'state', state_dim: int | None = None, img_size: int = 64, patch_size: int = 8, latent_dim: int = 256, hidden_dim: int = 256, horizon: int = 5, n_bins: int = 101, key: PRNGKey | None = None, **model_kwargs) -> MuZero
Assemble a MuZero agent over a discrete action space.
Source code in xwm/families/muzero/model.py
n_step_value_targets
¶
n_step_value_targets(rewards: ndarray, root_values: ndarray, *, discount: float = 0.997, n_steps: int = 5) -> ndarray
(T + 1,) value targets for an episode of T transitions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rewards
|
ndarray
|
|
required |
root_values
|
ndarray
|
|
required |
discount
|
float
|
RL discount. |
0.997
|
n_steps
|
int
|
bootstrap horizon. Longer reduces bias and adds variance; beyond the episode end the sum simply truncates. |
5
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
ndarray
|
bootstrap dropped when |