ZibinDong's picture
Publish audited ActionCodec2 release files
1bfc3f0 verified
Raw History Blame Contribute Delete
4.49 kB
"""Variable-width trajectory contract shared by statistics and fitting."""
from __future__ import annotations
from collections.abc import Sequence
from dataclasses import dataclass
import numpy as np
from ._validation import active_dimensions
from .configuration import ActionCodec2Config
__all__ = ["ActiveTrajectory", "TrajectoryLike", "prepare_trajectory"]
@dataclass(frozen=True)
class ActiveTrajectory:
"""One complete trajectory over an explicit subset of codebook dimensions."""
values: np.ndarray
active_dims: tuple[int, ...]
previous_first_order: np.ndarray | None = None
def __init__(
self,
values: np.ndarray,
active_dims: Sequence[int],
*,
previous_first_order: np.ndarray | None = None,
) -> None:
object.__setattr__(self, "values", np.asarray(values))
object.__setattr__(self, "active_dims", tuple(active_dims))
previous = (
None
if previous_first_order is None
else np.asarray(previous_first_order, dtype=np.float32)
)
if previous is not None and (
previous.shape != (len(self.active_dims),) or not np.all(np.isfinite(previous))
):
raise ValueError("previous_first_order must be finite with shape [D_active]")
object.__setattr__(self, "previous_first_order", previous)
@classmethod
def from_padded(
cls,
values: np.ndarray,
*,
active_dims: Sequence[int],
previous_first_order: np.ndarray | None = None,
) -> ActiveTrajectory:
"""Drop engineering-only dense padding before the semantic boundary."""
array = np.asarray(values)
dims = tuple(int(value) for value in active_dims)
if array.ndim != 2 or not dims or min(dims) < 0 or max(dims) >= array.shape[1]:
raise ValueError("padded values and active_dims do not define a valid trajectory")
previous = None if previous_first_order is None else np.take(previous_first_order, dims)
return cls(np.take(array, dims, axis=1), dims, previous_first_order=previous)
def trajectory_boundary(
trajectory: TrajectoryLike, config: ActionCodec2Config, active_dims: tuple[int, ...]
) -> np.ndarray | None:
"""Expand explicit compact state to ``[D]`` for fitting and validation."""
previous = getattr(trajectory, "previous_first_order", None)
active = set(active_dims)
required = any(
c.primitive_order == 2 and active.intersection(c.dims)
for c in config.physical.components.values()
)
if previous is None and required:
raise ValueError("second-order trajectories require explicit previous_first_order")
if previous is None and not any(
c.primitive_order == 2 for c in config.physical.components.values()
):
return None
dense = np.zeros(config.physical.input_dim, dtype=np.float32)
if previous is not None:
dense[list(active_dims)] = previous
return dense
TrajectoryLike = np.ndarray | ActiveTrajectory
def prepare_trajectory(
trajectory: TrajectoryLike,
*,
dimension: int,
index: int,
dtype: np.dtype | type = np.float32,
) -> tuple[np.ndarray, tuple[int, ...]]:
"""Validate a trajectory and return compact values plus global dimension ids."""
if isinstance(trajectory, ActiveTrajectory):
raw = trajectory.values
active_dims = trajectory.active_dims
else:
raw = trajectory
array = np.asarray(raw)
active_dims = tuple(range(array.shape[1])) if array.ndim == 2 else ()
values = np.asarray(raw, dtype=dtype)
if values.ndim != 2 or len(values) < 1 or values.shape[1] < 1:
raise ValueError(
f"trajectory {index} must have non-empty shape [T, D_active], got {values.shape}"
)
if len(active_dims) != values.shape[1]:
raise ValueError(
f"trajectory {index} active_dims has {len(active_dims)} entries for "
f"width {values.shape[1]}"
)
if any(isinstance(item, bool) or not isinstance(item, int) for item in active_dims):
raise ValueError(f"trajectory {index} active_dims must contain integers")
active_dims = active_dimensions(
active_dims, dimension, subject=f"trajectory {index} active_dims"
)
if not np.all(np.isfinite(values)):
raise ValueError(f"trajectory {index} contains a non-finite value")
return np.ascontiguousarray(values), active_dims