DoneTransform#
- class torchrl.envs.transforms.DoneTransform(in_keys: Sequence[NestedKey] | NestedKey | None = None, out_keys: Sequence[NestedKey] | NestedKey | None = None, *, reward_key: NestedKey | None = None, done_keys: Sequence[NestedKey] | NestedKey | None = None)[source]#
Expands done flags to match the reward shape.
Multi-agent environments often expose a shared (environment-level) done while rewards are per-agent. Value estimators such as GAE expect these entries to share a trailing shape. This transform expands each done key to the reward shape and writes the result under the reward group (for example
("agents", "done")when the reward key is("agents", "reward")). Remapped dones are observations and are not added toenv.done_keys.The transform can be appended to a
TransformedEnv, a collector (aspostproc), or a replay buffer. When used as a collector or replay-buffer transform,forward()expands entries under the"next"sub-tensordict if that key is present.- Parameters:
in_keys (NestedKey or sequence of NestedKey, optional) – done keys to expand. Defaults to
("done", "terminated"). A single NestedKey is accepted. Mutually exclusive withdone_keys.out_keys (NestedKey or sequence of NestedKey, optional) – destination keys, one per
in_keysentry. Defaults to the last component of each input key placed under the reward group (e.g.("agents", "done")ifreward_keyis("agents", "reward")and the input key ends with"done").
- Keyword Arguments:
reward_key (NestedKey, optional) – key of the reward used as the expansion target. Defaults to
"reward". The defaultout_keysare derived from this key’s group unlessout_keysis provided.done_keys (NestedKey or sequence of NestedKey, optional) – alias of
in_keyskept for compatibility with the historical multi-agent helper. Mutually exclusive within_keys.
See also
DoneTransformConfig.Examples
Expand shared done flags onto the per-agent reward shape:
>>> import torch >>> from tensordict import TensorDict >>> from torchrl.envs.transforms import DoneTransform >>> n_envs, n_agents = 2, 3 >>> td = TensorDict( ... { ... "done": torch.tensor([[False], [True]]), ... "terminated": torch.tensor([[False], [True]]), ... "agents": {"reward": torch.zeros(n_envs, n_agents, 1)}, ... }, ... [n_envs], ... ) >>> transform = DoneTransform( ... in_keys=["done", "terminated"], ... reward_key=("agents", "reward"), ... ) >>> td = transform(td) >>> td["agents", "done"].shape torch.Size([2, 3, 1]) >>> bool((td["agents", "done"] == td["done"].unsqueeze(-1)).all()) True
As a collector post-processing transform the same keys are expanded under
"next":>>> collected = TensorDict( ... { ... "next": TensorDict( ... { ... "done": torch.tensor([[False], [True]]), ... "terminated": torch.tensor([[False], [True]]), ... "agents": {"reward": torch.zeros(n_envs, n_agents, 1)}, ... }, ... [n_envs], ... ) ... }, ... [n_envs], ... ) >>> collected = DoneTransform( ... reward_key=("agents", "reward"), ... done_keys=["done", "terminated"], ... )(collected) >>> collected["next", "agents", "done"].shape torch.Size([2, 3, 1])
- forward(tensordict: TensorDictBase) TensorDictBase[source]#
Reads the input tensordict, and for the selected keys, applies the transform.
By default, this method:
calls directly
_apply_transform().does not call
_step()or_call().
This method is not called within env.step at any point. However, is is called within
sample().Note
forwardalso works with regular keyword arguments usingdispatchto cast the args names to the keys.Examples
>>> class TransformThatMeasuresBytes(Transform): ... '''Measures the number of bytes in the tensordict, and writes it under `"bytes"`.''' ... def __init__(self): ... super().__init__(in_keys=[], out_keys=["bytes"]) ... ... def forward(self, tensordict: TensorDictBase) -> TensorDictBase: ... bytes_in_td = tensordict.bytes() ... tensordict["bytes"] = bytes ... return tensordict >>> t = TransformThatMeasuresBytes() >>> env = env.append_transform(t) # works within envs >>> t(TensorDict(a=0)) # Works offline too.
- transform_output_spec(output_spec: Composite) Composite[source]#
Transforms the output spec such that the resulting spec matches transform mapping.
This method should generally be left untouched. Changes should be implemented using
transform_observation_spec(),transform_reward_spec()andtransform_full_done_spec(). :param output_spec: spec before the transform :type output_spec: TensorSpec- Returns:
expected spec after the transform