Source code for protomotions.agents.ppo.utils.motion_index

# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Training-only, stable row allocation for per-motion Gaussian exploration."""

from __future__ import annotations

from dataclasses import asdict, dataclass
import hashlib
import json
from pathlib import Path

import torch
import torch.distributed as dist

from protomotions.components.motion_lib import discover_motion_shards
from protomotions.agents.utils.distributed import raise_if_any_rank_failed


[docs] def names_digest(names): return hashlib.sha256(json.dumps(list(names)).encode()).hexdigest()
[docs] @dataclass(frozen=True) class MotionShard: path: str offset: int count: int names_digest: str
[docs] @dataclass class MotionIndex: shards: list[MotionShard] @property def num_motions(self): return sum(shard.count for shard in self.shards)
[docs] def state_dict(self): return [asdict(shard) for shard in self.shards]
[docs] def offset_for(self, motion_lib): path = str(Path(motion_lib.motion_file).resolve()) shard = next(shard for shard in self.shards if shard.path == path) if ( len(motion_lib.motion_files) != shard.count or names_digest(motion_lib.motion_files) != shard.names_digest ): raise ValueError( f"Motion ordering changed in {path}; rebuild exploration state" ) return shard.offset
[docs] @classmethod def from_sources(cls, sources): """Read each packaged shard's metadata once; mmap avoids loading poses.""" shards = [] offset = 0 seen = set() for pattern, indices, loaded_path, loaded_names in sources: if "slurmrank" in pattern: paths = discover_motion_shards(pattern, indices).values() else: paths = [loaded_path] for path in paths: path = str(Path(path).resolve()) if path in seen: continue seen.add(path) if path == str(Path(loaded_path).resolve()): names = loaded_names else: names = torch.load( path, map_location="cpu", weights_only=False, mmap=True )["motion_files"] shards.append( MotionShard(path, offset, len(names), names_digest(names)) ) offset += len(names) if not offset: raise ValueError("Per-motion std requires a nonempty motion library") return cls(shards)
[docs] @classmethod def build(cls, motion_lib): source = ( motion_lib.config.motion_file, motion_lib.config.motion_file_shard_indices, motion_lib.motion_file, motion_lib.motion_files, ) sources = [source] if dist.is_initialized(): sources = [None] * dist.get_world_size() dist.all_gather_object(sources, source) # All ranks construct the same ordered union, including different task # groups. Only rank zero reads metadata for shards not currently loaded. result = [None] error = None try: if not dist.is_initialized() or dist.get_rank() == 0: result[0] = cls.from_sources(sources) except Exception as caught: error = caught # A missing/broken shard must fail all ranks, not strand them in broadcast. raise_if_any_rank_failed(error, "Motion exploration index", motion_lib.device) if dist.is_initialized(): dist.broadcast_object_list(result) return result[0]