Source code for embodichain_models.dynamics.rwm.predictor

# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------

"""Graph-preserving robot world model prediction state."""

from __future__ import annotations

from collections.abc import Mapping
from typing import Any

import torch

from .model import RobotWorldModel

__all__ = ["RWMPredictor"]


[docs] class RWMPredictor: """Retain a batched prediction state without native simulation buffers. Args: model: Caller-owned world model. initial_state: Initial canonical FP32 state batch. Calls allocate new states, preserving outstanding results and unselected environment graphs. A caller performing policy optimization should use ``frozen_world_model`` around the rollout and dependent backward work. """
[docs] def __init__(self, model: RobotWorldModel, initial_state: torch.Tensor) -> None: self.model = model self._state = initial_state.clone() if self._state.ndim != 2 or self._state.shape[-1] != model.state_dim: raise ValueError("Predictor initial state must be a canonical state batch")
@property def state(self) -> torch.Tensor: """Current graph-connected state; callers must not mutate its storage.""" return self._state
[docs] def step( self, action: torch.Tensor, context: torch.Tensor | None = None ) -> torch.Tensor: """Advance one transition and retain the returned graph-connected state. Args: action: One aligned control per environment. context: Optional physical context. Returns: Newly allocated canonical states. """ self._state = self.model(self._state, action, context) return self._state
[docs] def reset( self, initial_state: torch.Tensor, env_ids: torch.Tensor | None = None ) -> torch.Tensor: """Replace all or selected states without overwriting old graph tensors. Args: initial_state: Full replacement batch, or one state per selected id. env_ids: Optional unique one-dimensional environment indices. Returns: Current state after replacement. Unselected rows retain their graph. """ if ( initial_state.dtype != self._state.dtype or initial_state.device != self._state.device ): raise ValueError("Predictor reset state dtype and device must match") if env_ids is None: if initial_state.shape != self._state.shape: raise ValueError("Full predictor reset must preserve batch layout") self._state = initial_state.clone() else: if ( env_ids.ndim != 1 or env_ids.dtype != torch.int64 or initial_state.shape != (len(env_ids), self.model.state_dim) ): raise ValueError( "Selective predictor reset requires aligned int64 indices" ) if env_ids.unique().numel() != env_ids.numel(): raise ValueError("Selective predictor reset indices must be unique") self._state = self._state.index_copy(0, env_ids, initial_state) return self._state
[docs] def detach_state(self) -> torch.Tensor: """Cut the current prediction graph while preserving every state value. Returns: The detached state. No reset, resampling or statistics update occurs. """ self._state = self._state.detach() return self._state
[docs] def state_dict(self) -> dict[str, Any]: """Return an owned graph-free prediction-state snapshot. Returns: Canonical state and model identity; model weights remain owned by the learning checkpoint. """ return { "format": "rwm-predictor-v1", "contract": self.model.get_extra_state(), "state": self._state.detach().clone(), }
[docs] def load_state_dict(self, state: Mapping[str, Any]) -> None: """Restore prediction state under the same physical/action contract. Args: state: Snapshot from :meth:`state_dict`. """ value = state["state"] if ( state.get("format") != "rwm-predictor-v1" or state.get("contract") != self.model.get_extra_state() or value.shape != self._state.shape or value.dtype != self._state.dtype ): raise ValueError("Predictor snapshot contract or state layout differs") self._state = value.detach().to(self._state.device).clone()