TensorDictBase.apply#
- TensorDictBase.apply(fn: Callable, *others: T, batch_size: Sequence[int] | None = None, device: device | None = NO_DEFAULT, names: Sequence[str] | None = NO_DEFAULT, inplace: bool = False, default: Any = NO_DEFAULT, filter_empty: bool | None = None, propagate_lock: bool = False, call_on_nested: bool = False, out: TensorDictBase | None = None, **constructor_kwargs) Self | None#
Applies a callable to all values stored in the tensordict and sets them in a new tensordict.
The callable signature must be
Callable[Tuple[Tensor, ...], Tensor | TensorDictBase | None].- Parameters:
fn (Callable) – function to be applied to the tensors in the tensordict.
*others (TensorDictBase instances, optional) – if provided, these tensordict instances should have a structure matching the one of self. The
fnargument should receive as many unnamed inputs as the number of tensordicts, including self. If other tensordicts have missing entries, a default value can be passed through thedefaultkeyword argument.
- Keyword Arguments:
batch_size (sequence of int, optional) – if provided, the resulting TensorDict will have the desired batch_size. The
batch_sizeargument should match the batch_size after the transformation. This is a keyword only argument. With abatch_size, the call is treated as a shape operation:fnis not called onUnbatchedTensorentries, which keep their data and take the new batch size.device (device, optional) – the resulting device, if any.
names (list of str, optional) – the new dimension names, in case the batch_size is modified.
inplace (bool, optional) – if
True, the results are written into the existing leaves ofself(asset_()does), andselfis returned. Defaults toFalse. See In-place operations.default (Any, optional) – default value for missing entries in the other tensordicts. If not provided, missing entries will raise a KeyError.
filter_empty (bool, optional) – if
True, empty tensordicts will be filtered out. This also comes with a lower computational cost as empty data structures won’t be created and destroyed. Non-tensor data is considered as a leaf and thereby will be kept in the tensordict even if left untouched by the function. IfFalse, empty tensordicts are kept. Defaults toNone, which behaves likeTrue(tensordicts left empty byfnare filtered out, andNoneis returned iffnreturns no value at all) except that tensordicts that were already empty are kept.propagate_lock (bool, optional) – if
True, a locked tensordict will produce another locked tensordict. Defaults toFalse.call_on_nested (bool, optional) –
if
True, the function will be called on first-level tensors and containers (TensorDict or tensorclass). In this scenario,funcis responsible of propagating its calls to nested levels. This allows a fine-grained behaviour when propagating the calls to nested tensordicts. IfFalse, the function will only be called on leaves, andapplywill take care of dispatching the function to all leaves.>>> from tensordict import TensorDict, is_tensor_collection >>> td = TensorDict({"a": {"b": [0.0, 1.0]}, "c": [1.0, 2.0]}) >>> def mean_tensor_only(val): ... if is_tensor_collection(val): ... raise RuntimeError("Unexpected!") ... return val.mean() >>> td_mean = td.apply(mean_tensor_only) >>> def mean_any(val): ... if is_tensor_collection(val): ... # Recurse ... return val.apply(mean_any, call_on_nested=True) ... return val.mean() >>> td_mean = td.apply(mean_any, call_on_nested=True)
out (TensorDictBase, optional) –
a tensordict where to write the results. This can be used to avoid creating a new tensordict:
>>> td = TensorDict({"a": 0}) >>> _ = td.apply(lambda x: x+1, out=td) >>> assert (td==1).all()
Warning
If the operation executed on the tensordict requires multiple keys to be accessed for a single computation, providing an
outargument equal toselfcan cause the operation to provide silently wrong results. For instance:>>> td = TensorDict({"a": 1, "b": 1}) >>> td.apply(lambda x: x+td["a"])["b"] # Right! tensor(2) >>> td.apply(lambda x: x+td["a"], out=td)["b"] # Wrong! tensor(3)
**constructor_kwargs – additional keyword arguments to be passed to the TensorDict constructor.
- Returns:
a new tensordict with the transformed tensors, or
selfifinplace=True.NoneiffnreturnsNonefor every leaf andfilter_emptyis notFalse.The result has the same type as
selfforTensorDict,LazyStackedTensorDictand tensorclasses. Other types, for examplePersistentTensorDictor a memory-mapped tensordict, give aTensorDictthat lives in memory.A locked tensordict gives an unlocked result unless
propagate_lock=True.
Examples
>>> import torch >>> from tensordict import TensorDict >>> td = TensorDict({ ... "a": -torch.ones(3), ... "b": {"c": torch.ones(3)}}, ... batch_size=[3]) >>> td_1 = td.apply(lambda x: x+1) >>> assert (td_1["a"] == 0).all() >>> assert (td_1["b", "c"] == 2).all() >>> td_2 = td.apply(lambda x, y: x+y, td) >>> assert (td_2["a"] == -2).all() >>> assert (td_2["b", "c"] == 2).all()
Note
If
Noneis returned by the function, the entry is ignored. This can be used to filter the data in the tensordict:>>> td = TensorDict({"1": 1, "2": 2, "b": {"2": 2, "1": 1}}, []) >>> def filter(tensor): ... if tensor == 1: ... return tensor >>> td.apply(filter) TensorDict( fields={ 1: Tensor(shape=torch.Size([]), device=cpu, dtype=torch.int64, is_shared=False), b: TensorDict( fields={ 1: Tensor(shape=torch.Size([]), device=cpu, dtype=torch.int64, is_shared=False)}, batch_size=torch.Size([]), device=None, is_shared=False)}, batch_size=torch.Size([]), device=None, is_shared=False)
Every tensordict class and every tensorclass has this method. For the other methods, see Methods by task.