Trajectory#
- class torchrl.data.Trajectory(data: TensorDictBase, *, batch_size, device=None, names=None)[source]#
A trajectory-first view over a contiguous slice of transitions.
A single-field tensorclass wrapping a
TensorDictBasethat holds the transitions of one trajectory, exposing its entries as attributes. Attribute lookup resolves keys against the root tensordict first and falls back to the("next", ...)sub-tensordict, sotraj.rewardandtraj.donereturn the conventional post-step entries whiletraj.observation,traj.stateortraj.actionreturn root entries.Being a tensorclass, slicing and indexing return
Trajectoryinstances, and trajectories of different lengths can be assembled into a single ragged batch withlazy_stack().- Variables:
data (TensorDictBase) – a tensordict with a single batch dimension of length
Tholding the trajectory’s transitions in order.
Examples
>>> trajectory = Trajectory(data) >>> trajectory.observation.shape torch.Size([200, 3]) >>> trajectory.reward.sum() tensor(-1234.5678) >>> trajectory.length 200 >>> trajectory[:10] Trajectory(length=10, keys=['action', 'next', 'observation']) >>> from tensordict import lazy_stack >>> stacked = lazy_stack([trajectory, other_trajectory])
- cat(dim: int = 0, *, out=None)#
Concatenates tensordicts along an existing batch dimension
dim.It is equivalent to
torch.cat(), and also works undertorch.compile(). The tensordicts must have the same keys, or aKeyErroris raised, and the same batch size except alongdim.- Parameters:
input (sequence of TensorDictBase) – the tensordicts to concatenate.
dim (int, optional) – the batch dimension to concatenate along. Defaults to
0.
- Keyword Arguments:
out (TensorDictBase, optional) – a tensordict to write the result into. Defaults to
None.
Examples
>>> import torch >>> from tensordict import TensorDict >>> a = TensorDict(x=torch.zeros(3), batch_size=[3]) >>> b = TensorDict(x=torch.ones(3), batch_size=[3]) >>> TensorDict.cat([a, b]).batch_size torch.Size([6])
- property device: torch.device#
The device of the tensorclass, or
Noneif its leaves may be on different devices.It is read-only: use
to()to move the tensorclass to another device.
- dumps(prefix: str | Path | None = None, copy_existing: bool = False, *, num_threads: int = 0, return_early: bool = False, share_non_tensor: bool = False, robust_key: bool | None = True, archive: bool | None = None, compression: str | int | None = None, atomic: bool = False) Self#
Saves the tensordict to disk, and returns a memory-mapped copy of it.
save(and its aliasdumps) callsmemmap()withexistsok=True.memmap()documents the arguments in full. The tensordict is not changed. Read the files back withload(). See Save and load tensordicts.An entry named
"shape","device"or"_type", at any depth, is not restored byload(), and saving it warns (seememmap()).- Parameters:
prefix (str or Path, optional) – the directory where the tensordict is written, one file per leaf. If
prefixends with".tdz"orarchive=True, a single archive file is written instead. Defaults toNone, which writes into the directory of the tensordict if it is already memory-mapped there, and otherwise into anonymous shared memory, which has no file name and lasts as long as the process.copy_existing (bool, optional) – if
False, a leaf that is already a memory-mapped tensor with a file at another path raises aRuntimeError. IfTrue, the file is copied to the new path. Defaults toFalse.
- Keyword Arguments:
num_threads (int, optional) – the number of threads that write the files. Defaults to
0, which writes them in the calling thread.return_early (bool, optional) – if
Trueandnum_threads > 1, returns aTensorDictFuturewhile the threads write the files. Defaults toFalse.share_non_tensor (bool, optional) – if
True, non-tensor data is shared between the processes of the node. Defaults toFalse.robust_key (bool, optional) – if
True, keys are percent-encoded in file names, so that every file stays insideprefix. Defaults toTrue.archive (bool, optional) – if
True, writes a single archive file. Defaults toNone, which writes an archive ifprefixends with".tdz".compression (str or int, optional) – the compression of the archive entries. Defaults to
None, which stores them uncompressed.atomic (bool, optional) – if
True, the directory is written next toprefixand then renamed to it, so that an error or a crash during the write leavesprefixas it was. Not supported on Windows for a directory. An archive is always written this way.memmap()gives the details. Defaults toFalse.
- Returns:
A new memory-mapped tensordict, as
memmap()returns.
Examples
>>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> td = TensorDict(a=torch.zeros(3), nested=TensorDict(b=torch.ones(3), batch_size=[3]), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as path: ... _ = td.save(path) ... loaded = TensorDict.load(path) ... print((loaded == td).all()) True
- extend(tensordict: list[TensorDictBase] | TensorDictBase) None#
Appends tensordicts to the lazy stack that the instance holds.
Deprecated since version 0.15: Build a new instance with
tensordict.lazy_stack()ortorch.cat()instead.
- classmethod fields() tuple[dataclasses.Field, ...]#
Returns the fields of the tensorclass.
Deprecated since version 0.15: Use
dataclasses.fields(cls)instead. It returns the same tuple ofdataclasses.Fieldobjects.
- from_any(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None)#
Converts a dict, tuple, dataclass, array or file to a tensordict, recursively.
from_anypicks the conversion from the type ofobj, and applies it to the values insideobjtoo. It accepts more types than theTensorDictconstructor, at the cost of some overhead and of opinionated choices about how each type maps to entries:a dict or
collections.UserDict:from_dict();a named tuple:
from_namedtuple();a tuple:
from_tuple(), with the keys"0","1", … This includes the results oftorch.return_types(max,sort,topk, …), which also emit aFutureWarning. Starting with TensorDict 0.17, the default will change to their field names ("values","indices", …), asfrom_namedtuple()gives;a dataclass instance:
from_dataclass()(the result is a tensordict, not a tensorclass). A dataclass type whose fields all have defaults gives a tensordict of the defaults too, with aFutureWarning. Starting with TensorDict 0.17, the default will change to returning the type unchanged, as for any other class;a NumPy structured array:
from_struct_array();a pandas DataFrame:
from_pandas();an open
h5py.File: aPersistentTensorDict(seefrom_h5()).
A tensor or a tensor collection is returned as it is. A list becomes a tensor if
torch.tensor()takes it, and aNonTensorStackotherwise. Any other object is returned unchanged.- Parameters:
obj – the object to convert.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size, except for a dict, wherebatch_sizewins, and for anh5py.File, whereauto_batch_sizewins. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size, except for a dict, wherebatch_sizewins and no batch size is computed, and for anh5py.File, whereauto_batch_sizewins andbatch_sizeis ignored. Defaults toNone.
- Returns:
A tensordict (a
PersistentTensorDictfor an HDF5 file), a tensor or aNonTensorStackfor a list, orobjitself for the other types.
Examples
>>> import dataclasses >>> import torch >>> from tensordict import TensorDict >>> @dataclasses.dataclass ... class Data: ... obs: torch.Tensor ... extra: tuple >>> data = Data(obs=torch.zeros(3, 4), extra=(torch.ones(3), {"x": torch.ones(3)})) >>> td = TensorDict.from_any(data, auto_batch_size=True) >>> td.batch_size torch.Size([3]) >>> sorted(td.keys(include_nested=True, leaves_only=True), key=str) [('extra', '0'), ('extra', '1', 'x'), 'obs'] >>> TensorDict.from_any([1, 2, 3]) tensor([1, 2, 3])
- from_consolidated()#
Reconstructs a tensordict from a file written by
consolidate(filename=...).The result is consolidated: its leaves are views into one copy-on-write memory map of the file, so in-place writes into them do not reach the file. It is not locked. The batch sizes and names are as they were when the file was written. Same as
from_consolidated().- Parameters:
filename (str or Path) – the file to read.
Examples
>>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> td = TensorDict(a=torch.zeros(3), b={"c": torch.ones(3)}, batch_size=[3]) >>> with tempfile.TemporaryDirectory() as path: ... _ = td.consolidate(filename=f"{path}/td.bin") ... loaded = TensorDict.from_consolidated(f"{path}/td.bin") ... print(loaded["b", "c"], loaded.is_consolidated()) tensor([1., 1., 1.]) True
- from_csv(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None, separator: str | None = None, dtype: torch.dtype | None = None, **kwargs) Self#
Reads a CSV file into a tensordict, with one entry per column.
Requires pandas or pyarrow: the file is read with
pandas.read_csv()when pandas is installed, and withpyarrow.csv.read_csvotherwise. The columns are converted as infrom_pandas().to_csv()writes such a file. Through pandas, a string column is one value for the whole batch (see the note infrom_pandas()); through pyarrow, it is aNonTensorStackwith one value per row.- Parameters:
path (str or Path) – the path of the CSV file.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, which gives[num_rows].separator (str, optional) – if given, splits the column names on it to build nested entries: with
separator=".", the column"obs.x"becomes the entry("obs", "x"). Defaults toNone, which keeps the column names as keys.dtype (torch.dtype, optional) – if given, casts the numeric and boolean columns to it. Defaults to
None, which keeps the dtype of each column.**kwargs – passed to the CSV reader.
- Returns:
A tensordict of the class this method is called on (
TensorDictforTensorDictBase).
Examples
>>> import os >>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> data = TensorDict(obs=torch.arange(3.0), reward=torch.ones(3), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = os.path.join(tmpdir, "data.csv") ... data.to_csv(path) ... td = TensorDict.from_csv(path) ... td32 = TensorDict.from_csv(path, dtype=torch.float32) >>> td.batch_size, td["obs"].dtype, td32["obs"].dtype (torch.Size([3]), torch.float64, torch.float32)
- from_dataclass(*, dest_cls: Type | None = None, auto_batch_size: bool = False, batch_dims: int | None = None, as_tensorclass: bool = False, device: torch.device | None = None, batch_size: torch.Size | None = None)#
Converts a dataclass instance to a tensordict, with one entry per field.
Each field value is converted with
from_any(), so a nested dataclass or dict becomes a nested tensordict and a number becomes a tensor. Withas_tensorclass=True, the conversion is that of the functionfrom_dataclass()instead, which returns a tensorclass.- Parameters:
dataclass – the dataclass instance to convert. With
as_tensorclass=True, it can also be a dataclass type. Without it, a dataclass type whose fields all have defaults gives a tensordict of the defaults, with aFutureWarning. Starting with TensorDict 0.17, the default will change to raising aTypeErrorfor it.- Keyword Arguments:
dest_cls (type, optional) – with
as_tensorclass=True, the tensorclass to convert to. Ignored otherwise. Defaults toNone, which creates a new tensorclass with the fields of the dataclass.auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.as_tensorclass (bool, optional) – if
True, returns a tensorclass instance (or a tensorclass type, for a dataclass type) throughfrom_dataclass(). Defaults toFalse.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, an empty batch size.
- Returns:
A
TensorDict, or a tensorclass instance or type ifas_tensorclass=True.- Raises:
TypeError – if
dataclassis not a dataclass andas_tensorclass=False, if it is a dataclass type with a field that cannot be read from the class (a field without a default, or a field of a tensorclass type) andas_tensorclass=False, or if bothauto_batch_size=Trueandbatch_sizeare given.
Examples
>>> import dataclasses >>> import torch >>> from tensordict import TensorDict >>> @dataclasses.dataclass ... class Data: ... obs: torch.Tensor ... reward: torch.Tensor >>> data = Data(obs=torch.zeros(3, 4), reward=torch.ones(3)) >>> td = TensorDict.from_dataclass(data, auto_batch_size=True) >>> type(td).__name__, td.batch_size ('TensorDict', torch.Size([3])) >>> tc = TensorDict.from_dataclass(data, as_tensorclass=True, batch_size=[3]) >>> type(tc).__name__, tc.batch_size ('Data_tc', torch.Size([3]))
- classmethod from_dict(input_dict, *, auto_batch_size: bool | None = None, batch_size=None, device=None, batch_dims=None)#
Builds an instance of the tensorclass from a dictionary of its fields.
The fields that
input_dictmisses are set toNone, also those that declare a default or a default factory. Starting with TensorDict 0.17, the default will change to filling these fields with their defaults, as the constructor does. Until then, a call that misses such a field warns with aFutureWarning.- Parameters:
input_dict (dict) – the values of the fields. Nested dictionaries become tensordicts, or tensorclasses for the fields annotated with one.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Defaults toNone, which behaves likeFalse, unlessbatch_dimsis passed: then it behaves likeTrue.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Defaults to
None, which gives an empty batch size unlessauto_batch_sizeis on.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it. Defaults to
None, which leaves each leaf where it is.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Exclusive withbatch_size. Defaults toNone.
- Returns:
A new instance of the class.
Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor ... y: torch.Tensor >>> foo = Foo.from_dict({"x": torch.zeros(3, 4), "y": torch.zeros(3)}, auto_batch_size=True) >>> foo.batch_size torch.Size([3])
- from_h5(*, mode: str = 'r', auto_batch_size: bool = False, batch_dims: int | None = None, batch_size: torch.Size | None = None, non_tensor_as_stack: bool = False)#
Opens an HDF5 file as a
PersistentTensorDict.The data stays in the file: entries are read when they are accessed. Requires
h5py. See Save and load tensordicts andPersistentTensorDict.- Parameters:
filename (str or path) – the path to the HDF5 file.
- Keyword Arguments:
mode (str, optional) – how the file is opened, as in
h5py.File:"r"(read only),"r+"(read and write), and so on. Defaults to"r".auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, which gives each nested tensordict the largest batch size that its entries share, and every nested tensordictbatch_sizewhen it is passed. A file written byto_h5()stores the batch sizes and the dim names. Starting with TensorDict 0.17, the default will change to restoring the stored ones, asfrom_zarrdoes: withbatch_size, the nested tensordicts get their stored batch sizes, and withauto_batch_size=True, the stored names are kept and the batch sizes are inferred from the stored ones, which an empty tensordict or a non-tensor entry keeps.from_h5warns when this changes its result.non_tensor_as_stack (bool, optional) – if
True, a non-tensor dataset that tensordict did not write, such as a string column with one string per row, reads as aNonTensorStackof its rows when its leading dimensions equal the batch size. Otherwise it reads as oneNonTensorDatathat holds the whole dataset. Defaults toFalse.
- Returns:
A
PersistentTensorDictbacked by the file.- Raises:
TypeError – if both
auto_batch_size=Trueandbatch_sizeare given.
Examples
>>> import os >>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> data = TensorDict(key1=torch.zeros(3), key2=torch.ones(3), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = os.path.join(tmpdir, "file.h5") ... _ = data.to_h5(path) ... td = TensorDict.from_h5(path) ... print(td) PersistentTensorDict(batch_size=[3], fields={key1: f32[], key2: f32[]})
- from_json(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None, separator: str | None = None, dtype: torch.dtype | None = None, lines: bool = False, **kwargs) Self#
Reads a JSON file of records into a tensordict, with one entry per field.
The file holds an array of records (
[{"a": 1, "b": 2.0}, ...]), or one record per line withlines=True(JSON Lines). Each field becomes an entry with one value per record. The file is read withpandas.read_json()when pandas is installed, and with thejsonmodule otherwise. Through pandas, a string field is one value for the whole batch (see the note infrom_pandas()); through thejsonmodule, it is aNonTensorStackwith one value per record. To convert a single JSON object, load it withjson.load()and pass it tofrom_dict().to_json()writes such a file.- Parameters:
path (str or Path) – the path of the JSON file.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, which gives[num_rows].separator (str, optional) – if given, splits the column names on it to build nested entries: with
separator=".", the column"obs.x"becomes the entry("obs", "x"). Defaults toNone, which keeps the column names as keys.dtype (torch.dtype, optional) – if given, casts the numeric and boolean columns to it. Defaults to
None, which keeps the dtype of each column.lines (bool, optional) – if
True, reads the file as JSON Lines, one record per line. Defaults toFalse.**kwargs – passed to
pandas.read_json(). Ignored without pandas.
- Returns:
A tensordict of the class this method is called on (
TensorDictforTensorDictBase).
Examples
>>> import os >>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> data = TensorDict(obs=torch.arange(3.0), reward=torch.ones(3), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = os.path.join(tmpdir, "data.json") ... data.to_json(path) ... td = TensorDict.from_json(path) ... path_lines = os.path.join(tmpdir, "data.jsonl") ... data.to_json(path_lines, lines=True) ... td_lines = TensorDict.from_json(path_lines, lines=True) >>> td.batch_size, td_lines.batch_size (torch.Size([3]), torch.Size([3]))
- from_list(*, auto_batch_size: bool | None = None, batch_size: torch.Size | None = None, device: torch.device | None = None, batch_dims: int | None = None, names: list[str] | None = None, lazy: bool | None = None) Self#
Builds a tensordict by stacking a list of dicts or tensor collections.
Each dict is converted with
from_dict()and the results are stacked along a new leading dimension. A tensordict or tensorclass in the list is stacked as it is: the keyword arguments do not apply to it. When the result does not get thebatch_sizeordeviceasked for, aFutureWarningis emitted: starting with TensorDict 0.17, the default will change to applying them to the result, which raises if the batch size does not fit, as for a list of dicts.- Parameters:
input (list) – the dicts or tensor collections to stack.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size of each element to the largest leading shape that all its entries share. Defaults toNone, which behaves likeFalseunlessbatch_dimsis given.batch_size (torch.Size or list of int, optional) – the batch size of the result, with the length of the list first. Each element gets
batch_size[1:]. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_dims (int, optional) – the maximum number of batch dimensions of the result, the list dimension included. Turns on
auto_batch_sizewhen that isNone. Exclusive withbatch_size. Defaults toNone.names (list of str, optional) – the names of the batch dimensions of the result, one per dimension, with the name of the list dimension first. Defaults to
None.lazy (bool, optional) – if
True, the result is a lazy stack; ifFalse, a dense stack. Defaults toNone, which builds a dense stack when the elements have the same shape and a lazy stack otherwise. See Stacking and variable-size data.
- Raises:
ValueError – if
batch_size[0]is not the length of the list.
Examples
>>> import torch >>> from tensordict import TensorDict >>> td = TensorDict.from_list( ... [{"a": torch.zeros(3)}, {"a": torch.zeros(3)}], ... batch_size=[2, 3], ... names=["x", "y"], ... ) >>> td.batch_size torch.Size([2, 3]) >>> td.names ['x', 'y']
- from_module(as_module: bool = False, lock: bool = False, use_state_dict: bool = False, filter_empty: bool = True)#
Returns a tensordict that holds the parameters and buffers of a module.
The entries are the parameters and buffers of the module themselves, not copies. Submodules become nested tensordicts.
Note
This method does not lock the result by default (
lock=False). The functiontensordict.from_module()locks it by default (lock=True).- Parameters:
module (torch.nn.Module) – the module to get the parameters from.
as_module (bool, optional) – if
True, aTensorDictParamsinstance is returned, which can be used to store parameters within atorch.nn.Module. Defaults toFalse.lock (bool, optional) – if
True, the resulting tensordict is locked. Defaults toFalse.use_state_dict (bool, optional) – if
True, the state-dict from the module is used and unflattened into a TensorDict with the tree structure of the model. Use it when the module has state-dict hooks. Defaults toFalse.filter_empty (bool, optional) – if
True, submodules without parameters or buffers (nn.ReLU, for example) get no entry. IfFalse, they get an empty tensordict. Defaults toTrue.
- Returns:
A
TensorDict, or aTensorDictParamsifas_module=True.
Examples
>>> from torch import nn >>> from tensordict import TensorDict >>> module = nn.Sequential(nn.Linear(3, 4), nn.ReLU()) >>> params = TensorDict.from_module(module) >>> params["0", "weight"] is module[0].weight True >>> list(params.keys()) ['0'] >>> params.is_locked False
- from_modules(*, as_module: bool = False, lock: bool = True, use_state_dict: bool = False, lazy_stack: bool = False, expand_identical: bool = False)#
Stacks the parameters and buffers of several modules in one tensordict.
The result has one batch dimension, with one element per module, and the structure that
from_module()gives. Use it to run an ensemble of modules that have the same structure withtorch.vmap()andto_module()(see the example).The default, dense result holds new parameters and buffers: the stacks of those of the modules, with the same
requires_grad. Pass these new parameters to the optimizer: gradients reach them, not the parameters of the modules. Withlazy_stack=True, the result is a lazy stack of the parameters of the modules, which receive the gradients.- Parameters:
*modules (nn.Module) – the modules to get the parameters from. With a dense stack, they must have the same parameters and buffers, with the same shapes.
- Keyword Arguments:
as_module (bool, optional) – if
True, returns aTensorDictParams, which can be stored as a submodule of atorch.nn.Module. Exclusive withlazy_stack. Defaults toFalse.lock (bool, optional) – if
True, the result is locked (see Locking and temporary changes). Defaults toTrue.use_state_dict (bool, optional) – if
True, reads thestate_dict()of each module, so that its state-dict hooks run. Defaults toFalse.lazy_stack (bool, optional) –
if
True, returns aLazyStackedTensorDictof the parameters of the modules; ifFalse, stacks them into new parameters. Exclusive withas_module, and not compatible with lazy modules. Defaults toFalse.Warning
A lazy stack stacks the parameters each time an entry is read with
get(), which costs memory (Ntimes the size of the parameters, more if a graph is built) and time, and the optimizer holdsNtimes more parameters. Uselazy_stack=Trueonly when you need to train the parameters of the modules themselves.expand_identical (bool, optional) – if
True, a parameter that is the same object in every module is expanded instead of stacked, so that the result shares its memory. Ignored whenlazy_stack=True. Defaults toFalse.
- Raises:
RuntimeError – if
lazy_stack=Trueandas_module=True, or iflazy_stack=Trueand a module has uninitialized (lazy) parameters.
Examples
>>> import torch >>> from torch import nn >>> from tensordict import TensorDict >>> _ = torch.manual_seed(0) >>> modules = [nn.Linear(3, 4) for _ in range(2)] >>> params = TensorDict.from_modules(*modules) >>> params.batch_size, params["weight"].shape (torch.Size([2]), torch.Size([2, 4, 3]))
Run the two modules on one input with
torch.vmap(), through a module on the"meta"device that holds no data. The gradients reach the stacked parameters, not those of the modules:>>> empty_module = nn.Linear(3, 4, device="meta") >>> def exec_module(params, x): ... with params.to_module(empty_module): ... return empty_module(x) >>> x = torch.randn(3) >>> y = torch.vmap(exec_module, (0, None))(params, x) >>> y.shape torch.Size([2, 4]) >>> y.sum().backward() >>> params["weight"].grad is not None, modules[0].weight.grad is None (True, True)
With
lazy_stack=True, the gradients reach the parameters of the modules:>>> params = TensorDict.from_modules(*modules, lazy_stack=True) >>> type(params).__name__ 'LazyStackedTensorDict' >>> y = torch.vmap(exec_module, (0, None))(params, x) >>> y.sum().backward() >>> modules[0].weight.grad is not None True
- from_namedtuple(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None)#
Converts a named tuple to a tensordict whose keys are its field names.
Each field value is converted with
from_any(), so a nested named tuple or dict becomes a nested tensordict and a number becomes a tensor. Thetorch.return_typesresults oftorch.max,torch.sortand the like are accepted too.to_namedtuple()converts back.- Parameters:
named_tuple (NamedTuple) – the named tuple to convert.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. It applies to the nested tensordicts too. Exclusive with
auto_batch_size. Defaults toNone, an empty batch size.
- Returns:
A
TensorDict.- Raises:
TypeError – if both
auto_batch_size=Trueandbatch_sizeare given.
Examples
>>> from tensordict import TensorDict >>> import torch >>> data = TensorDict({ ... "a_tensor": torch.zeros((3)), ... "nested": {"a_tensor": torch.zeros((3)), "a_string": "zero!"}}, [3]) >>> nt = data.to_namedtuple() >>> nt GenericDict(a_tensor=tensor([0., 0., 0.]), nested=GenericDict(a_tensor=tensor([0., 0., 0.]), a_string='zero!')) >>> td = TensorDict.from_namedtuple(nt, auto_batch_size=True) >>> td.batch_size, td["nested", "a_string"] (torch.Size([3]), 'zero!') >>> sorted(TensorDict.from_namedtuple(torch.tensor([2, 0, 1]).max(0)).keys()) ['indices', 'values']
- from_pandas(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None, separator: str | None = None, dtype: torch.dtype | None = None) Self#
Converts a pandas DataFrame to a tensordict, with one entry per column.
The batch size is
[num_rows]. Numeric and boolean columns become tensors, a string or object column becomes oneNonTensorDatathat holds the whole column, a categorical column becomes the tensor of its codes, and a datetime column becomes anint64tensor of nanoseconds.to_pandas()converts back.Note
A string or object column is one value for the whole batch, so
td[i]["name"]gives the values of every row, not the value of rowi. To get one value per row, set the column again as a list:td["name"] = df["name"].tolist()stores aNonTensorStack, whosetd[i]["name"]is the value of rowi. It builds one object per row, about 20 microseconds each, so it is much slower than reading the column for large frames.- Parameters:
dataframe (pd.DataFrame) – the DataFrame to convert.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, which gives[num_rows].separator (str, optional) – if given, splits the column names on it to build nested entries: with
separator=".", the column"obs.x"becomes the entry("obs", "x"). Defaults toNone, which keeps the column names as keys.dtype (torch.dtype, optional) – if given, casts the numeric and boolean columns to it. Defaults to
None, which keeps the dtype of each column.
- Returns:
A tensordict of the class this method is called on (
TensorDictforTensorDictBase).
Examples
>>> from tensordict import TensorDict >>> import pandas as pd >>> df = pd.DataFrame({"a": [1, 2, 3], "b": [4.0, 5.0, 6.0]}) >>> td = TensorDict.from_pandas(df) >>> print(td) TensorDict(batch_size=[3], fields={a: i64[], b: f64[]})
- from_parquet(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None, separator: str | None = None, dtype: torch.dtype | None = None, columns: list[str] | None = None, **kwargs) Self#
Reads a Parquet file into a tensordict, with one entry per column.
Requires pyarrow or pandas: the file is read with
pyarrow.parquet.read_tablewhen pyarrow is installed, and withpandas.read_parquet()otherwise. The columns are converted as infrom_pandas().to_parquet()writes such a file. Through pyarrow, a string column is aNonTensorStackwith one value per row; through pandas, it is one value for the whole batch (see the note infrom_pandas()).- Parameters:
path (str or Path) – the path of the Parquet file.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, which gives[num_rows].separator (str, optional) – if given, splits the column names on it to build nested entries: with
separator=".", the column"obs.x"becomes the entry("obs", "x"). Defaults toNone, which keeps the column names as keys.dtype (torch.dtype, optional) – if given, casts the numeric and boolean columns to it. Defaults to
None, which keeps the dtype of each column.columns (list of str, optional) – the columns to read. Defaults to
None, which reads all the columns.**kwargs – passed to the Parquet reader.
- Returns:
A tensordict of the class this method is called on (
TensorDictforTensorDictBase).
Examples
>>> import os >>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> data = TensorDict(obs=torch.arange(3.0), reward=torch.ones(3), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = os.path.join(tmpdir, "data.parquet") ... data.to_parquet(path) ... td = TensorDict.from_parquet(path) ... td_obs = TensorDict.from_parquet(path, columns=["obs"]) >>> sorted(td.keys()), sorted(td_obs.keys()) (['obs', 'reward'], ['obs'])
- from_pytree(*, batch_size: torch.Size | None = None, auto_batch_size: bool = False, batch_dims: int | None = None)#
Converts a pytree to a tensordict that
to_pytree()converts back.Each list, tuple, named tuple and dict of the pytree becomes a nested tensordict, and its type is stored in a
"_pytree_type"entry so thatto_pytree()can rebuild the pytree. The elements of a list or tuple get the keys"0","1", … The subclasses of these types, such ascollections.OrderedDict,collections.defaultdictand thetorch.return_typesresults oftorch.maxand the like, are accepted too. Adefaultdictis rebuilt without itsdefault_factory.A tensor or a tensor collection is returned as it is. Python ints and floats and NumPy arrays become tensors, so
to_pytree()returns them as tensors. A dict key that is not aNestedKey(such as an int or an object) is stored in aNonTensorDataentry next to its value. Other types raise aNotImplementedError.- Parameters:
pytree – the pytree to convert: a list, tuple, named tuple or dict, nested to any depth, whose leaves are tensors, tensor collections, Python numbers or NumPy arrays.
- Keyword Arguments:
batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Defaults to
None, which gives an empty batch size unlessauto_batch_size=True.auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.
Note
A module
state_dict()round-trips throughfrom_pytree, and itsmemmap()output loads withload_memmap(path, allow_pickle=False). The state dicts of an optimizer and of a learning-rate scheduler holdNone,strvalues and Python numbers, whichfrom_pytreerejects (Noneandstr) or returns as tensors (numbers): save them withtorch.save(sd, path)and load them withtorch.load(path, weights_only=True), which round-trips them exactly without unpickling arbitrary objects.Note
The list fields of a named tuple, and some of its dict and tuple fields, are converted with
from_any()and do not round-trip throughto_pytree():a list of numbers comes back as a tensor;
an empty tuple, an empty dict or named tuple, and a dict that holds a str or a list come back as a
GenericDictnamed tuple;a dict that holds a tuple, a tuple subclass (such as a
torch.return_typesresult), and a tuple that holds anything other than tensors, Python numbers, dicts and named tuples maketo_pytree()raise aValueError.
The other dict, named tuple and tuple fields come back with their own type.
Examples
>>> import torch >>> from tensordict import TensorDict >>> pytree = {"obs": torch.zeros(3, 4), "pair": (torch.zeros(3), torch.ones(3))} >>> td = TensorDict.from_pytree(pytree, auto_batch_size=True) >>> td.batch_size torch.Size([3]) >>> sorted(td.keys()), sorted(td["pair"].keys()) (['_pytree_type', 'obs', 'pair'], ['0', '1', '_pytree_type']) >>> type(td.to_pytree()["pair"]).__name__ 'tuple'
A pytree with lists, dicts, a named tuple and a dict key that is not a string round-trips:
>>> class Key: ... pass >>> key = Key() >>> pytree = ( ... [torch.arange(3), torch.zeros(2)], ... {"tensor": torch.ones(2), key: torch.arange(2), "list": [1, 2, 3]}, ... {"named_tuple": TensorDict(two=torch.ones(1)).to_namedtuple()}, ... ) >>> pytree_recon = TensorDict.from_pytree(pytree).to_pytree() >>> def check(v1, v2): ... assert (v1 == v2).all() >>> _ = torch.utils._pytree.tree_map(check, pytree, pytree_recon) >>> key in pytree_recon[1] True
- from_schema(*, batch_size: Sequence[int] | torch.Size | None = None, storage: str | None = None, device=None, **kwargs) TensorDictBase#
Allocates a zero-filled tensordict from a schema of shapes and dtypes.
Each entry of
schemamaps a key to an(element_shape, dtype)pair, and the entry gets the shape[*batch_size, *element_shape].storageselects where the zeros are stored, so that a buffer on disk or on a server can be created without building it in memory first.- Parameters:
schema (dict) – maps each key (a
str) to a pair(element_shape, dtype), whereelement_shape(a list of int or atorch.Size) is the shape of the entry after the batch dimensions.- Keyword Arguments:
batch_size (torch.Size or list of int, optional) – the batch size of the result, prepended to every element shape. Defaults to
None, an empty batch size.storage (str, optional) –
where the entries are stored:
None: aTensorDictof regular tensors."memmap": memory-mapped tensors, in the directory given byprefix=<dir>(seememmap_like())."h5": aPersistentTensorDictin the HDF5 file given byfilename=<path>."zarr": aPersistentTensorDictin the zarr store given byfilename=<path or store>(requireszarr>=3.1.1)."shared": tensors in shared memory (seeshare_memory_())."redis"or"dragonfly": aTensorDictStoreon a server, throughTensorDictStore.from_schema.
Defaults to
None.device (torch.device, str or int, optional) – the device of the result. The
"memmap"storage ignores it. Defaults toNone.**kwargs – passed to the backend:
prefixand the other arguments ofmemmap_like()for"memmap",filenamefor"h5"and"zarr",host,portand the like for"redis"and"dragonfly".
- Returns:
A new tensordict of the type that
storageselects, filled with zeros.- Raises:
ValueError – if
storageis not one of the values above.
Examples
>>> import torch >>> from tensordict import TensorDict >>> td = TensorDict.from_schema( ... {"obs": ([84, 84, 3], torch.uint8), ... "reward": ([], torch.float32)}, ... batch_size=[1000], ... ) >>> td["obs"].shape torch.Size([1000, 84, 84, 3])
>>> import tempfile >>> with tempfile.TemporaryDirectory() as d: ... td_mm = TensorDict.from_schema( ... {"obs": ([4], torch.float32)}, ... batch_size=[8], ... storage="memmap", ... prefix=d, ... ) ... print(td_mm.is_memmap()) True
- from_struct_array(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None) Self#
Converts a NumPy structured array to a tensordict, with one entry per field.
The batch size is the shape of the array, and each entry holds a field. The conversion is zero-copy: the tensors share memory with the array, so an in-place change to the array changes the tensordict, and the other way around. Two cases copy the data: a
deviceother than the CPU, and a field whose item size does not divide the size of a record, such as the"i8"field of a packed array of"i8"and"f4"fields (12 bytes per record), which torch cannot view. String fields become non-tensor data.to_struct_array()converts back.- Parameters:
struct_array (np.ndarray) – the structured array to convert.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share, which can add the dimensions of array fields. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, which uses the shape of the array.
- Returns:
A tensordict of the class this method is called on (
TensorDictforTensorDictBase).- Raises:
TypeError – if both
auto_batch_size=Trueandbatch_sizeare given.
Examples
>>> import numpy as np >>> from tensordict import TensorDict >>> x = np.array( ... [("Rex", 9, 81.0), ("Fido", 3, 27.0)], ... dtype=[("name", "U10"), ("age", "i4"), ("weight", "f4")], ... ) >>> td = TensorDict.from_struct_array(x) >>> td.batch_size, td["age"] (torch.Size([2]), tensor([9, 3], dtype=torch.int32)) >>> x["age"] += 1 # the tensordict shares memory with the array >>> td["age"] tensor([10, 4], dtype=torch.int32) >>> bool((td.to_struct_array() == x).all()) True
- classmethod from_tensordict(tensordict: TensorDictBase, non_tensordict: dict | None = None, safe: bool | None = None) Self#
Wraps a tensordict in a new instance of the tensorclass, without copying the leaves.
If
tensordictis aTensorDict, the entries of the fields annotated with a tensorclass (orOptionalof one) that are tensordicts become instances of that tensorclass, recursively. They are set in a shallow copy oftensordict, which keeps its own entries. An entry with keys that the annotated class does not declare stays a tensordict. Other backends are wrapped as they are: setting a tensorclass in them would write to their storage (e.g. an H5 file).- Parameters:
tensordict (TensorDictBase) – the tensordict that holds the tensor fields.
non_tensordict (dict, optional) – the values of the non-tensor fields. The fields that neither argument holds are set to
None. Defaults toNone.safe (bool, optional) – deprecated in 0.15, will be removed in TensorDict 0.17. If
False, skips the check thattensordictis aTensorDictBase. By default, the check runs, and another type raises aRuntimeError.
- Returns:
A new instance of the class, whose fields are the entries of
tensordict.
Examples
>>> import torch >>> from tensordict import TensorClass >>> class Pose(TensorClass): ... q: torch.Tensor >>> class Obs(TensorClass): ... a: Pose >>> obs = Obs(a=Pose(q=torch.zeros(5, 4), batch_size=[5]), batch_size=[5]) >>> type(Obs.from_tensordict(obs.to_tensordict()).a).__name__ 'Pose'
- from_tuple(*, auto_batch_size: bool = False, batch_dims: int | None = None, device: torch.device | None = None, batch_size: torch.Size | None = None)#
Converts a tuple to a tensordict with the keys
"0","1", …Each element is converted with
from_any(), so a nested tuple or dict becomes a nested tensordict and a number becomes a tensor.- Parameters:
obj (tuple) – the tuple to convert.
- Keyword Arguments:
auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.device (torch.device, str or int, optional) – the device of the result. Leaves on another device are moved to it.
Noneleaves each leaf where it is, and the result has no device. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. It applies to the nested tensordicts too. Exclusive with
auto_batch_size. Defaults toNone, an empty batch size.
- Returns:
A
TensorDict.- Raises:
TypeError – if both
auto_batch_size=Trueandbatch_sizeare given.
Examples
>>> import torch >>> from tensordict import TensorDict >>> td = TensorDict.from_tuple((torch.zeros(3, 4), 1), auto_batch_size=True) >>> td["0"].shape, td["1"], td.batch_size (torch.Size([3, 4]), tensor(1), torch.Size([])) >>> td = TensorDict.from_tuple((torch.zeros(3, 4), torch.ones(3)), auto_batch_size=True) >>> td.batch_size torch.Size([3])
- from_zarr(*, mode: str = 'r', auto_batch_size: bool = False, batch_dims: int | None = None, batch_size: torch.Size | None = None, non_tensor_as_stack: bool = False, allow_pickle: bool | None = None)#
Opens a zarr store as a
PersistentTensorDict.The data stays in the store: entries are read when they are accessed. Requires
zarr>=3.1.1. See Save and load tensordicts andPersistentTensorDict.- Parameters:
filename (str, path or zarr store) – the path to the zarr store (a directory), or a
zarr.abc.store.Storeinstance (e.g. azarr.storage.ZipStore).- Keyword Arguments:
mode (str, optional) – how the store is opened:
"r"(read only),"r+"(read and write), and so on, as inzarr.open_group(). Defaults to"r".auto_batch_size (bool, optional) – if
True, sets the batch size to the largest leading shape that all the entries share. Exclusive withbatch_size. Defaults toFalse.batch_dims (int, optional) – with
auto_batch_size, the maximum number of batch dimensions. Ignored withoutauto_batch_size, with aFutureWarningwhen it would change the result. Starting with TensorDict 0.17, the default will change to turning onauto_batch_sizewhenbatch_dimsis given, asfrom_dictdoes. Defaults toNone.batch_size (torch.Size or list of int, optional) – the batch size of the result: the leading dimensions that all its entries share. Exclusive with
auto_batch_size. Defaults toNone, which reads the batch size and the names of the batch dimensions thatto_zarr()stored, or, for a store written by another tool, gives each nested tensordict the largest batch size that its entries share.non_tensor_as_stack (bool, optional) – if
True, a non-tensor array that tensordict did not write, such as a string column with one string per row, reads as aNonTensorStackof its rows when its leading dimensions equal the batch size. Otherwise it reads as oneNonTensorDatathat holds the whole array. Defaults toFalse.allow_pickle (bool, optional) – whether to decode pickled non-tensor values.
Falserejects them before decoding.Truepermits them: use it only for data you trust.Nonepermits them with aFutureWarningwhen read. Starting with TensorDict 0.17, the default will change toallow_pickle=False. Tensor and JSON entries are unaffected. Defaults toNone.
- Returns:
A
PersistentTensorDictbacked by the store.- Raises:
TypeError – if both
auto_batch_size=Trueandbatch_sizeare given.
Examples
>>> import os >>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> data = TensorDict(key1=torch.zeros(3), key2=torch.ones(3), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as tmpdir: ... path = os.path.join(tmpdir, "store.zarr") ... _ = data.to_zarr(path) ... td = TensorDict.from_zarr(path) ... print(td) PersistentTensorDict(batch_size=[3], fields={key1: f32[], key2: f32[]})
- fromkeys(value: Any = 0)#
Creates a tensordict with the same value for every key, like
dict.fromkeys().The result is a
TensorDictwith an empty batch size. A nested key creates nested tensordicts. A Python number becomes a tensor for each key, and a tensor is stored as it is: every key then holds the same tensor object.- Parameters:
keys (list of NestedKey) – the keys of the result.
value (Any, optional) – the value of every key: a tensor, a number, or any value that the
TensorDictconstructor takes. Defaults to0.
Examples
>>> import torch >>> from tensordict import TensorDict >>> td = TensorDict.fromkeys(["a", ("b", "c")], torch.zeros(3)) >>> td["b", "c"] tensor([0., 0., 0.]) >>> td["a"] is td["b", "c"] True
- get(key: NestedKey, default=None) Tensor | None[source]#
Resolve
keyagainst the root tensordict, then("next", key).
- lazy_stack(dim: int = 0, *, out=None, **kwargs)#
Stacks tensordicts into a lazy stack.
A lazy stack keeps the tensordicts as they are and stacks their values only when you read them. The tensordicts can have different keys and different feature shapes. See Stacking and variable-size data.
- Parameters:
input (sequence of TensorDictBase) – the tensordicts to stack.
dim (int, optional) – the new batch dimension. Defaults to
0.
- Keyword Arguments:
out (TensorDictBase, optional) – a lazy stack to write the result into. Defaults to
None.**kwargs – passed to
LazyStackedTensorDict.lazy_stack, for examplestack_dim_nameorstrict_shape.
Examples
>>> import torch >>> from tensordict import TensorDict >>> a = TensorDict(x=torch.zeros(3), batch_size=[3]) >>> b = TensorDict(y=torch.ones(3), batch_size=[3]) >>> stack = TensorDict.lazy_stack([a, b]) >>> type(stack).__name__, stack.batch_size ('LazyStackedTensorDict', torch.Size([2, 3]))
- property length: int#
Number of transitions in the trajectory.
- load(*args, **kwargs) Self#
Loads a tensordict that
save()ormemmap()wrote to disk.loadcallsload_memmap(), which documents the other arguments, such asdevice,mode,subpathandallow_pickle. The leaves of the result read the files lazily. See Save and load tensordicts.- Parameters:
prefix (str or Path) – the directory, or the archive file, to read.
*args – passed to
load_memmap().**kwargs – passed to
load_memmap().
Examples
>>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> td = TensorDict(a=torch.arange(3), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as path: ... _ = td.save(path) ... loaded = TensorDict.load(path) ... print(loaded["a"], loaded.batch_size) MemoryMappedTensor([0, 1, 2]) torch.Size([3])
- load_(prefix: str | Path, *args, **kwargs)#
Loads a saved tensordict into the tensordict.
Calls
load_memmap_()with the same arguments.Deprecated since version 0.15: Use
load_memmap_()instead. It will be removed in TensorDict 0.17.- Parameters:
prefix (str or Path) – the directory, or the archive file, to read.
*args – passed to
load_memmap_().**kwargs – passed to
load_memmap_().
- load_memmap(device: torch.device | None = None, non_blocking: bool = False, *, out: TensorDictBase | None = None, robust_key: bool | None = True, subpath: NestedKey | None = None, mode: str | None = None, num_threads: int = 0, allow_pickle: bool | None = None) Self#
Loads a tensordict that
memmap()orsave()wrote, from a directory or an archive.The leaves of the result read the files lazily: from a directory, each leaf is a
MemoryMappedTensorof its file, and from an archive, each leaf is a view into one memory map of the archive. Only the pages of the leaves that you use are read from disk. The result has the class that was saved (a tensorclass is loaded as that tensorclass), and it is not locked.load()is this method. See Save and load tensordicts.- Parameters:
prefix (str or Path) – the directory to read, or an archive file written by
save(..., archive=True), by a".tdz"prefix, or bypack_memmap().device (torch.device, str or int, optional) – the device of the result. The leaves are copied to it, which reads the files, unless it is the CPU. With
"meta", no file is read: the leaves are meta tensors, which show the structure and size of the data. Defaults toNone, which keeps the leaves on the CPU, memory-mapped.non_blocking (bool, optional) – if
True, does not synchronize the device after the copies todevice. Defaults toFalse.
- Keyword Arguments:
out (TensorDictBase, optional) – a tensordict to load the data into. Its nested tensordicts are reused, but its leaves are replaced by the loaded tensors, so their storage is not reused. Its keys that are not in the saved data are removed. If
outhas a device, the data is loaded on it, and anotherdeviceraises aValueError. To write the data into the storage ofout, useout.update_(TensorDict.load_memmap(prefix, device=device)). Defaults toNone.robust_key (bool, optional) – if
True, reads the percent-encoded file names thatmemmap()writes by default, and falls back to the legacy names of keys that are safe file names. IfFalse, reads the legacy layout only.Noneis the same asTrue. Defaults toTrue.subpath (NestedKey or str, optional) – the nested tensordict to load, as a nested key (
("module", "0")) or as a"/"-separated path ("module/0"). Only that subtree is read, from a directory or an archive. A nested key reaches the entries of a saved tensorclass asobj.get(key)does. Defaults toNone, which loads the whole tensordict.mode (str, optional) – how the files are mapped. With
"r", the mapping is copy-on-write: in-place writes into the leaves stay in the memory of this process, and the files do not change. For a directory, a pickled copy (amultiprocessingqueue, a spawned worker) or atorch.save()of the result reads the files, without these writes: send or saveclone()to keep them. With"r+", in-place writes reach the files, and a file that the process cannot write raises aPermissionError. Defaults toNone, which maps an archive as with"r", and each file of a directory as with"r+"if the process can write it, and as with"r"otherwise. See the note below.num_threads (int, optional) – the number of threads that decompress the leaves of a compressed archive. It has no effect on an uncompressed archive or a directory. Defaults to
0, which decompresses them one after the other.allow_pickle (bool, optional) – whether non-tensor data saved with
picklemay be loaded. Pickle can run arbitrary code: passTrueonly for data you trust, andFalsefor other data, which raises aRuntimeErroron pickled data. Defaults toNone, which loads pickled data with aFutureWarning. Starting with TensorDict 0.16, the default will change toFalse. Data saved without pickle loads with any value.
- Returns:
The loaded tensordict, or
outif it is given.- Raises:
ValueError – if
prefixis a file that is not an archive, ifsubpathnames no saved tensordict, or ifmodeis notNone,"r"or"r+".
Note
Load with
mode="r"data that you only read: on some network file systems (for example Lustre), page faults on a shared writable mapping take write locks, so readers on different nodes block each other. On Linux, a copy-on-write mapping counts against the memory commit limit, so a file larger than RAM plus swap can fail to map with"r". With"r",memmap_()without a prefix does not write back toprefix.Note
An archive opened with
mode="r+"must have uncompressed, aligned payloads (as written by tensordict withoutcompression) and no nested tensor. In-place writes do not update the CRC-32 checksums of its entries:load_memmapdoes not check them, but callrefresh_archive_checksums()before you give the archive to a tool that does, such asunzip.Examples
>>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> td = TensorDict(a=torch.zeros(3), nested=TensorDict(b=torch.ones(3))) >>> with tempfile.TemporaryDirectory() as path: ... _ = td.memmap(path) ... loaded = TensorDict.load_memmap(path) ... print((loaded == td).all(), type(loaded["a"]).__name__) ... # load one nested tensordict ... print(TensorDict.load_memmap(path, subpath="nested")["b"]) ... # read the structure without reading the data ... print(TensorDict.load_memmap(path, device="meta")["a"].device) True MemoryMappedTensor MemoryMappedTensor([1., 1., 1.]) meta
With a
FakeTensorModeactive, the leaves are fake tensors and no file is read:>>> from torch._subclasses import FakeTensorMode >>> with tempfile.TemporaryDirectory() as path: ... _ = td.memmap(path) ... with FakeTensorMode(): ... print(type(TensorDict.load_memmap(path)["a"]).__name__) FakeTensor
- load_memmap_(prefix: str | Path, robust_key: bool | None = True, *, allow_pickle: bool | None = None, mode: str | None = None)#
Loads a memory-mapped tensorclass into this instance, and returns
self.Works as
load_memmap_(), and also loads the non-tensor fields.- Parameters:
prefix (str or Path) – the directory, or the
".tdz"archive file, to read.robust_key (bool, optional) – the key encoding used when the data was saved. See
memmap(). Defaults toTrue.
- Keyword Arguments:
allow_pickle (bool, optional) – whether pickled non-tensor fields may be loaded. See
load_memmap(). Defaults toNone, which loads pickled fields with aFutureWarning.mode (str, optional) – the mode of the memory maps. See
load_memmap(). Defaults toNone.
- load_state_dict(state_dict: dict[str, Any], strict=True, assign=False, from_flatten=None)#
Loads a state dict made by
state_dict()into the tensorclass, and returnsself.Works as
load_state_dict(), which describes the arguments, and also loads the non-tensor fields. It also reads the state dicts of older versions, which hold"_tensordict"and"_non_tensordict"entries.Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor ... name: str >>> sd = Foo(x=torch.zeros(3), name="a", batch_size=[3]).state_dict() >>> foo = Foo(x=torch.ones(3), name="b", batch_size=[3]) >>> _ = foo.load_state_dict(sd) >>> foo.x, foo.name (tensor([0., 0., 0.]), 'a')
- maybe_dense_stack(dim: int = 0, *, out=None, **kwargs)#
Stacks tensordicts densely if possible, or into a lazy stack otherwise.
The result is a
TensorDictif the tensordicts have the same keys and shapes, and aLazyStackedTensorDictotherwise. See Stacking and variable-size data.- Parameters:
input (sequence of TensorDictBase) – the tensordicts to stack.
dim (int, optional) – the new batch dimension. Defaults to
0.
- Keyword Arguments:
out (TensorDictBase, optional) – a tensordict to write the result into. Defaults to
None.**kwargs – passed to
LazyStackedTensorDict.maybe_dense_stack, for examplestrict.
Examples
>>> import torch >>> from tensordict import TensorDict >>> a = TensorDict(x=torch.zeros(3), batch_size=[3]) >>> b = TensorDict(x=torch.ones(3), batch_size=[3]) >>> c = TensorDict(y=torch.ones(3), batch_size=[3]) >>> type(TensorDict.maybe_dense_stack([a, b])).__name__ 'TensorDict' >>> type(TensorDict.maybe_dense_stack([a, c])).__name__ 'LazyStackedTensorDict'
- memmap(prefix: str | Path | None = None, copy_existing: bool = False, *, num_threads: int = 0, return_early: bool = False, share_non_tensor: bool = False, existsok: bool = True, robust_key: bool | None = True, archive: bool | None = None, compression: str | int | None = None, atomic: bool = False) Self#
Writes every leaf to a memory-mapped file and returns a new tensordict of them.
The tensordict is not changed: the result holds new
MemoryMappedTensorleaves. Each leaf is written to<prefix>/<key>.memmap, each nested tensordict to a subdirectory, and each level has ameta.jsonfile. Read the files back withload_memmap().save()anddumps()call this method, andmemmap_()writes in place. See Save and load tensordicts.The result is locked (see Locking and temporary changes): you can write into its leaves in place, but you cannot add, remove or replace an entry. Once it is unlocked,
is_memmap()returnsFalse.- Parameters:
prefix (str or Path, optional) – the directory to write to. If it ends with
".tdz", a single archive file is written instead (seearchive). Defaults toNone, which writes into the directory of the tensordict if it is already memory-mapped there, and otherwise into anonymous shared memory, which has no file name and lasts as long as the process.copy_existing (bool, optional) – if
False, a leaf that is already a memory-mapped tensor with a file at another path raises aRuntimeError, so that data on disk is not duplicated by accident. IfTrue, the file is copied to the new path. Defaults toFalse.
- Keyword Arguments:
num_threads (int, optional) – the number of threads that write the files.
0and1write them one after the other in the calling thread. Threads help with a few large leaves and slow down many small ones. Defaults to0.return_early (bool, optional) – if
Trueandnum_threads > 1, returns aTensorDictFutureat once, while the threads write the files:future.result()waits for them and returns the tensordict. Not supported for an archive. Defaults toFalse.share_non_tensor (bool, optional) – if
True, non-tensor data is shared between the processes of the node, so that an in-place write of a non-tensor entry in one process is seen by the others. Many non-tensor leaves (for example a large stack of non-tensor data) can use up the memory. Defaults toFalse.existsok (bool, optional) – if
False, a file that already exists at the path of a leaf (or at the archive path) raises aRuntimeError. IfTrue, it is overwritten. Defaults toTrue.robust_key (bool, optional) – if
True, keys are percent-encoded in file and directory names ("a/b"is written toa%2Fb.memmap), so that every file stays insideprefix. IfFalse, keys are used as file names unchanged, as in the legacy layout: do not use it with keys you do not trust.Noneis the same asTrue. Defaults toTrue.archive (bool, optional) – if
True,prefixis a file, and the tensordict is written to it as an archive: a zip file whose entries are the files of the directory layout, with uncompressed and aligned tensor payloads. Defaults toNone, which writes an archive ifprefixends with".tdz".compression (str or int, optional) – the compression of the archive entries:
"stored","deflate","bzip2","lzma", or azipfileconstant. A compressed leaf is decompressed in memory when it is loaded, instead of being memory-mapped. Only valid for an archive. Defaults toNone, which stores the entries uncompressed.atomic (bool, optional) – if
True, the files are written to a new directory next toprefix, which is then renamed toprefix, so that an error or a crash during the write leavesprefixas it was. The new directory is removed if the write fails (but not after a crash). An existing directory atprefixis replaced as a whole once the new one is complete, so files that the tensordict does not write are removed: it must be empty or hold a saved tensordict (ameta.jsonfile). Readers see the old or the new directory, except for the short time between two renames, during whichprefixis missing. The write needs about twice the disk space of the tensordict while it runs, and a leaf that is already memory-mapped to a file is copied, which needscopy_existing=True. The files are not synced to the disk, so this guards against a killed process, not against a power failure. Not supported withreturn_early=True, nor for a directory on Windows, where a directory that holds open files cannot be renamed. An archive is always written this way. Defaults toFalse.
- Returns:
A new memory-mapped tensordict, or a
TensorDictFutureof it ifreturn_early=Trueandnum_threads > 1.- Raises:
RuntimeError – if a leaf already has a file elsewhere and
copy_existing=False, if a file exists andexistsok=False, or withatomic=True, ifprefixis a non-empty directory andexistsok=False, or if it holds no saved tensordict.NotADirectoryError – if
atomic=Trueandprefixis a file.ValueError – if
compressionis given without an archive, or if an archive oratomic=Trueis requested withoutprefix.NotImplementedError – if
return_early=Truefor an archive or withatomic=True, oratomic=Truefor a directory on Windows.
Note
For an archive, the result is
load_memmap(prefix): its leaves are views into one copy-on-write mapping of the file, so in-place writes do not reach the file, and it is not locked. Archives are written in one pass, sonum_threadsdoes not speed them up. An existing archive is replaced by a new file: a symlink is followed, but other hard links keep the old content.pack_memmap()andunpack_memmap()convert between archives and directories.Note
meta.jsonuses the names"shape","device"and"_type"for the batch size, the device and the type of each level, soload_memmap()does not restore an entry with one of these names, at any depth. Saving such an entry warns. For a directory, the tensordict returned here still holds the entry, but an archive, which is read back, does not. Rename the entry before saving to keep it.Note
A view of a memory-mapped tensordict, such as
td[1:]or a transpose, written back to the directory that it maps can leave wrong data in that directory, without an error. Passatomic=Trueandcopy_existing=True, which write a new directory first (not on Windows), or write to another directory.Examples
>>> import os >>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> td = TensorDict( ... obs=torch.zeros(3, 4), ... next=TensorDict(reward=torch.ones(3, 1), batch_size=[3]), ... batch_size=[3], ... ) >>> with tempfile.TemporaryDirectory() as path: ... mm = td.memmap(path) ... print(type(mm["obs"]).__name__, mm.is_locked) ... print(sorted(os.listdir(path))) MemoryMappedTensor True ['meta.json', 'next', 'obs.memmap']
- memmap_(prefix: str | Path | None = None, copy_existing: bool = False, *, num_threads: int = 0, return_early: bool = False, share_non_tensor: bool = False, existsok: bool = True, robust_key: bool | None = True, atomic: bool = False) Self#
Writes every leaf to a memory-mapped file in place, and returns
self.Writes the same files as
memmap(), then replaces each leaf of the tensordict with itsMemoryMappedTensor, so thedata_ptr()of every leaf changes. The device of the tensordict becomes"cpu". See Save and load tensordicts.The tensordict is then locked (see Locking and temporary changes): you can write into its leaves in place, but you cannot add, remove or replace an entry. Once it is unlocked,
is_memmap()returnsFalse.An entry named
"shape","device"or"_type", at any depth, is not restored byload_memmap(), and writing it to a directory warns (seememmap()).A view of a memory-mapped tensordict, such as a slice or a transpose, written back to the directory that it maps can leave wrong data in that directory, without an error. Pass
atomic=Trueandcopy_existing=True, which write a new directory first (not on Windows), or write to another directory (seememmap()).- Parameters:
prefix (str or Path, optional) – the directory to write to. Defaults to
None, which writes into the directory of the tensordict if it is already memory-mapped there, and otherwise into anonymous shared memory, which has no file name and lasts as long as the process.copy_existing (bool, optional) – if
False, a leaf that is already a memory-mapped tensor with a file at another path raises aRuntimeError, so that data on disk is not duplicated by accident. IfTrue, the file is copied to the new path. Defaults toFalse.
- Keyword Arguments:
num_threads (int, optional) – the number of threads that write the files.
0and1write them one after the other in the calling thread. Threads help with a few large leaves and slow down many small ones. Defaults to0.return_early (bool, optional) – if
Trueandnum_threads > 1, returns aTensorDictFutureat once, while the threads write the files:future.result()waits for them and returns the tensordict. Defaults toFalse.share_non_tensor (bool, optional) – if
True, non-tensor data is shared between the processes of the node, so that an in-place write of a non-tensor entry in one process is seen by the others. Many non-tensor leaves (for example a large stack of non-tensor data) can use up the memory. Defaults toFalse.existsok (bool, optional) – if
False, a file that already exists at the path of a leaf raises aRuntimeError. IfTrue, it is overwritten. Defaults toTrue.robust_key (bool, optional) – if
True, keys are percent-encoded in file and directory names ("a/b"is written toa%2Fb.memmap), so that every file stays insideprefix. IfFalse, keys are used as file names unchanged, as in the legacy layout: do not use it with keys you do not trust.Noneis the same asTrue. Defaults toTrue.atomic (bool, optional) – if
True, the files are written to a new directory next toprefix, which is then renamed toprefix, so that an error or a crash during the write leavesprefixas it was. The new directory is removed if the write fails (but not after a crash). An existing directory atprefixis replaced as a whole once the new one is complete, so files that the tensordict does not write are removed: it must be empty or hold a saved tensordict (ameta.jsonfile). Readers see the old or the new directory, except for the short time between two renames, during whichprefixis missing. The write needs about twice the disk space of the tensordict while it runs, and a leaf that is already memory-mapped to a file is copied, which needscopy_existing=True. The files are not synced to the disk, so this guards against a killed process, not against a power failure. If the write fails, the leaves already converted map deleted files, so a retry needscopy_existing=True. Not supported withreturn_early=True, nor on Windows, where a directory that holds open files cannot be renamed. Defaults toFalse.
- Returns:
self, or aTensorDictFutureof it ifreturn_early=Trueandnum_threads > 1.- Raises:
RuntimeError – if a leaf already has a file elsewhere and
copy_existing=False, if a file exists andexistsok=False, if the tensordict is in shared memory (seeshare_memory_()), or if it is aPersistentTensorDict, aTensorDictStore, aLazyStackedTensorDictStoreor one of their elements. Usememmap()to make a memory-mapped copy of those. Withatomic=True, also ifprefixis a non-empty directory andexistsok=False, or if it holds no saved tensordict.NotADirectoryError – if
atomic=Trueandprefixis a file.ValueError – if
atomic=Trueand there is no directory to write to.NotImplementedError – if
return_early=Truewithatomic=True, oratomic=Trueon Windows.
Examples
>>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> td = TensorDict(obs=torch.zeros(3, 4), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as path: ... out = td.memmap_(path) ... print(out is td, td.is_memmap(), td.is_locked) ... print(type(td["obs"]).__name__) True True True MemoryMappedTensor
- memmap_like(prefix: str | Path | None = None, copy_existing: bool = False, *, existsok: bool = True, num_threads: int = 0, return_early: bool = False, share_non_tensor: bool = False, robust_key: bool | None = True, archive: bool | None = None) Self#
Creates a memory-mapped tensordict with the same keys, shapes and dtypes, without copying the values.
Use it to preallocate a large buffer on disk, then fill it with
update_()or by indexing:memmap()would write every value first. New files are filled with zeros. A file that already exists at the path of a leaf (withexistsok=True) is mapped as it is and keeps its content, which lets you reopen a buffer, or its start with a smaller leaf. If the file is smaller than the leaf, it is grown with zeros and aFutureWarningis emitted. Starting with TensorDict 0.17, the default will change to raising aRuntimeErrorin that case. A file written with another dtype or shape is not detected otherwise, and its bytes are read as the new dtype. An archive (".tdz") is always rebuilt with zeros. The tensordict is not changed. See Save and load tensordicts.The result is locked (see Locking and temporary changes): you can write into its leaves in place, but you cannot add, remove or replace an entry. Once it is unlocked,
is_memmap()returnsFalse.- Parameters:
prefix (str or Path, optional) – the directory to write to. If it ends with
".tdz", a single archive file is written instead (seearchive). Defaults toNone, which allocates the leaves in anonymous shared memory, which has no file name and lasts as long as the process.copy_existing (bool, optional) – accepted for symmetry with
memmap(). It has no effect, since no value is copied. Defaults toFalse.
- Keyword Arguments:
existsok (bool, optional) – if
False, a file that already exists at the path of a leaf (or at the archive path) raises aRuntimeError. IfTrue, it is reused. Defaults toTrue.num_threads (int, optional) – the number of threads that create the files.
0and1create them one after the other in the calling thread. Defaults to0.return_early (bool, optional) – if
Trueandnum_threads > 1, returns aTensorDictFutureat once, while the threads create the files:future.result()waits for them and returns the tensordict. Not supported for an archive. Defaults toFalse.share_non_tensor (bool, optional) – if
True, non-tensor data is shared between the processes of the node, so that an in-place write of a non-tensor entry in one process is seen by the others. Many non-tensor leaves (for example a large stack of non-tensor data) can use up the memory. Defaults toFalse.robust_key (bool, optional) – if
True, keys are percent-encoded in file and directory names ("a/b"is written toa%2Fb.memmap), so that every file stays insideprefix. IfFalse, keys are used as file names unchanged, as in the legacy layout: do not use it with keys you do not trust.Noneis the same asTrue. Defaults toTrue.archive (bool, optional) – if
True,prefixis a file, and a zero-filled archive is created there and loaded back withload_memmap(prefix, mode="r+"), so that in-place writes into the result reach the archive. Nested tensors are not supported. Defaults toNone, which creates an archive ifprefixends with".tdz".
- Returns:
A new memory-mapped tensordict, or a
TensorDictFutureof it ifreturn_early=Trueandnum_threads > 1.
Note
For an archive, the result is not locked, and in-place writes do not update the CRC-32 checksums of the zip entries. Call
refresh_archive_checksums()before you give the archive to a tool that checks them, such asunzip.Examples
>>> import torch >>> from tensordict import TensorDict >>> td = TensorDict({ ... "a": torch.zeros((3, 64, 64), dtype=torch.uint8), ... "b": torch.zeros(1, dtype=torch.int64), ... }, batch_size=[]).expand(1_000) # expand does not allocate new memory >>> import tempfile >>> with tempfile.TemporaryDirectory() as path: ... buffer = td.memmap_like(path) ... print(buffer.is_memmap(), buffer["a"].shape) True torch.Size([1000, 3, 64, 64])
- memmap_refresh_(*, allow_pickle: bool | None = None)#
Reloads the tensordict from its
saved_path, and returnsself.Use it when another process has changed the files: added or removed entries, or written non-tensor data. It calls
load_memmap_()onsaved_path, then memory-maps the tensordict there again.- Keyword Arguments:
allow_pickle (bool, optional) – whether non-tensor data saved with
picklemay be loaded. Seeload_memmap(). Defaults toNone.- Raises:
RuntimeError – if the tensordict is not memory-mapped in a directory, for example a tensordict returned by
load_memmap()(for whichis_memmap()returnsFalse) or bymemmap()without a prefix.
Examples
>>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> with tempfile.TemporaryDirectory() as path: ... td = TensorDict(a=torch.zeros(3)).memmap(path) ... _ = TensorDict(a=torch.ones(3), b=torch.ones(3)).memmap(path) ... _ = td.memmap_refresh_() ... print(sorted(td.keys()), td["a"]) ['a', 'b'] MemoryMappedTensor([1., 1., 1.])
- pack(dim: int = -1, *, layout: PackLayout | None = None) tuple[torch.Tensor, PackLayout]#
Packs all the leaves of a tensordict into one tensor of features.
Each leaf is flattened after the batch dimensions, and the leaves are concatenated in the order of
keys(True, True, sort=True), so the columns depend on the set of keys only. Leaves can have different ranks. The result has shape(*batch_size, F)fordim=-1, whereFis the sum of the numbers of features of the leaves, and it is contiguous for anydim.PackLayout.unpackinverts it.Packing along the last dim copies the data once, with one
torch.cat(), unless the leaves are channels-last:torch.cat()keeps their strides, and making the result contiguous takes a second copy. Along another dim the result can take a second copy too, since moving the feature dim makes a 4-d or 5-d result channels-last.All the leaves must share one dtype and one device. Non-tensor data cannot be packed: drop it with
filter_non_tensor_data()first. A lazy stack packs like its dense equivalent if all its members have the same keys and feature shapes.- Parameters:
dim (int, optional) – the position of the packed dimension in the result, in
[-(batch_dims + 1), batch_dims]as fortorch.stack(). Defaults to-1.- Keyword Arguments:
layout (PackLayout, optional) – a layout to pack into. The columns then follow its key order, and a missing, extra or reshaped leaf raises.
dimmust match the layout’s dim. Defaults toNone, which builds a new layout in sorted key order.- Returns:
A tuple of the packed tensor and its
PackLayout.
Examples
>>> import torch >>> from tensordict import TensorDict >>> td = TensorDict( ... p=torch.zeros(5), ... v=torch.ones(5, 3), ... nested={"stress": torch.ones(5, 3, 3)}, ... batch_size=[5], ... ) >>> flat, layout = td.pack() >>> flat.shape torch.Size([5, 13]) >>> layout.keys (('nested', 'stress'), 'p', 'v') >>> out = layout.unpack(flat.index_select(0, torch.tensor([0, 2]))) >>> out.batch_size torch.Size([2]) >>> flat2, _ = td.pack(layout=layout) >>> assert (flat2 == flat).all()
- rename_keys(mapping, *, strict: bool = True)#
Returns a tensorclass with several keys renamed at once.
Works as
rename_keys(), which describesmappingandstrict, with two differences: each destination must be a field of the class, or the call raises anAttributeError, and a field that the renaming empties is kept.Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor ... y: torch.Tensor >>> foo = Foo(x=torch.zeros(3), y=torch.ones(3), batch_size=[3]) >>> foo.rename_keys({"x": "y", "y": "x"}).x tensor([1., 1., 1.])
- save(prefix: str | Path | None = None, copy_existing: bool = False, *, num_threads: int = 0, return_early: bool = False, share_non_tensor: bool = False, robust_key: bool | None = True, archive: bool | None = None, compression: str | int | None = None, atomic: bool = False) Self#
Saves the tensordict to disk, and returns a memory-mapped copy of it.
save(and its aliasdumps) callsmemmap()withexistsok=True.memmap()documents the arguments in full. The tensordict is not changed. Read the files back withload(). See Save and load tensordicts.An entry named
"shape","device"or"_type", at any depth, is not restored byload(), and saving it warns (seememmap()).- Parameters:
prefix (str or Path, optional) – the directory where the tensordict is written, one file per leaf. If
prefixends with".tdz"orarchive=True, a single archive file is written instead. Defaults toNone, which writes into the directory of the tensordict if it is already memory-mapped there, and otherwise into anonymous shared memory, which has no file name and lasts as long as the process.copy_existing (bool, optional) – if
False, a leaf that is already a memory-mapped tensor with a file at another path raises aRuntimeError. IfTrue, the file is copied to the new path. Defaults toFalse.
- Keyword Arguments:
num_threads (int, optional) – the number of threads that write the files. Defaults to
0, which writes them in the calling thread.return_early (bool, optional) – if
Trueandnum_threads > 1, returns aTensorDictFuturewhile the threads write the files. Defaults toFalse.share_non_tensor (bool, optional) – if
True, non-tensor data is shared between the processes of the node. Defaults toFalse.robust_key (bool, optional) – if
True, keys are percent-encoded in file names, so that every file stays insideprefix. Defaults toTrue.archive (bool, optional) – if
True, writes a single archive file. Defaults toNone, which writes an archive ifprefixends with".tdz".compression (str or int, optional) – the compression of the archive entries. Defaults to
None, which stores them uncompressed.atomic (bool, optional) – if
True, the directory is written next toprefixand then renamed to it, so that an error or a crash during the write leavesprefixas it was. Not supported on Windows for a directory. An archive is always written this way.memmap()gives the details. Defaults toFalse.
- Returns:
A new memory-mapped tensordict, as
memmap()returns.
Examples
>>> import tempfile >>> import torch >>> from tensordict import TensorDict >>> td = TensorDict(a=torch.zeros(3), nested=TensorDict(b=torch.ones(3), batch_size=[3]), batch_size=[3]) >>> with tempfile.TemporaryDirectory() as path: ... _ = td.save(path) ... loaded = TensorDict.load(path) ... print((loaded == td).all()) True
- select(*keys, inplace: bool = False, strict: bool = True, as_tensordict: bool = False)#
Returns a tensorclass that holds only the fields of
keys, with the other fields set toNone.Works as
select(), which describeskeys: the leaves are not copied.- Keyword Arguments:
inplace (bool, optional) – if
True, sets the other fields of the tensorclass toNoneand returnsself. Defaults toFalse.strict (bool, optional) – if
True, a missing key raises aKeyError. IfFalse, missing keys are ignored. Defaults toTrue.as_tensordict (bool, optional) – if
True, returns the selected entries in aTensorDictinstead of a tensorclass. Defaults toFalse.
Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor ... y: torch.Tensor >>> foo = Foo(x=torch.zeros(3), y=torch.ones(3), batch_size=[3]) >>> print(foo.select("x").y) None >>> type(foo.select("x", as_tensordict=True)).__name__ 'TensorDict'
- set(key: NestedKey, value: Any, inplace: bool = False, non_blocking: bool = False)#
Sets a field, and returns the tensorclass.
tc.set("x", value)is the same astc.x = value: the value is cast according to the class flags (seeTensorClass). Otherwise, it works asset().- Parameters:
key (NestedKey) – the field to set. A tuple sets an entry of a nested field:
tc.set(("a", "b"), value)istc.a.set("b", value).value (Any) – the value of the field.
inplace (bool, optional) – if
Trueand the field holds a tensor, the value is copied into it, which keeps its storage. Defaults toFalse. See In-place operations.non_blocking (bool, optional) – if
True, copies between devices can run asynchronously with respect to the host. Defaults toFalse.
- Returns:
self.- Raises:
AttributeError – if
keyis not a field of the class.RuntimeError – if the tensorclass is locked, also with
inplace=True.ValueError – if
valueis the tensorclass itself.
Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor >>> foo = Foo(x=torch.zeros(3), batch_size=[3]) >>> foo.set("x", torch.ones(3)).x tensor([1., 1., 1.]) >>> foo.set("z", torch.ones(3)) Traceback (most recent call last): ... AttributeError: Cannot set the attribute 'z': Foo has no such field. Its fields are ['x'].
- stack(dim: int = 0, *, out=None)#
Stacks tensordicts into one dense tensordict along a new dimension
dim.It is equivalent to
torch.stack(), and also works undertorch.compile(). The tensordicts must have the same batch size and the same keys, or aRuntimeErroris raised. To stack tensordicts with different keys or shapes, uselazy_stack(). See Stacking and variable-size data.- Parameters:
input (sequence of TensorDictBase) – the tensordicts to stack.
dim (int, optional) – the new batch dimension. Defaults to
0.
- Keyword Arguments:
out (TensorDictBase, optional) – a tensordict to write the result into. Defaults to
None.
Examples
>>> import torch >>> from tensordict import TensorDict >>> a = TensorDict(x=torch.zeros(3), batch_size=[3]) >>> b = TensorDict(x=torch.ones(3), batch_size=[3]) >>> TensorDict.stack([a, b]).batch_size torch.Size([2, 3])
- state_dict(destination=None, prefix='', keep_vars=False, flatten=True) dict[str, Any]#
Returns the state dict of the tensorclass.
Works as
state_dict(), which describes the arguments. The tensor fields are the entries of the state dict. The non-tensor fields and the name of the class go to the_metadataattribute, under the"_non_tensor"and"_type"keys of their level.Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor ... name: str >>> foo = Foo(x=torch.zeros(3), name="a", batch_size=[3]) >>> sd = foo.state_dict() >>> list(sd.keys()) ['x'] >>> sd._metadata[""]["_non_tensor"] {'name': 'a'}
- to_tensordict(*, retain_none: bool | None = None) TensorDict#
Returns a
TensorDictwith a copy of the fields of the tensorclass.The tensors are copied, and memory-mapped or shared tensors become regular tensors. Nested tensorclasses become tensordicts, and non-tensor fields become
NonTensorDataentries.- Keyword Arguments:
retain_none (bool, optional) – if
True, fields set toNoneare written asNoneentries. Defaults toNone, which drops them.
Examples
>>> from typing import Any >>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor ... y: Any = None >>> foo = Foo(x=torch.zeros(3)) >>> sorted(foo.to_tensordict().keys()) ['x'] >>> sorted(foo.to_tensordict(retain_none=True).keys()) ['x', 'y']
- unbind(dim: int)#
Returns a tuple of tensorclasses, one per index along a batch dimension.
Works as
unbind(): the results share the storage of the tensorclass.- Parameters:
dim (int) – the batch dimension to remove.
Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor >>> foo = Foo(x=torch.arange(6).reshape(2, 3), batch_size=[2, 3]) >>> [item.x for item in foo.unbind(1)] [tensor([0, 3]), tensor([1, 4]), tensor([2, 5])]
- update_(input_dict_or_td: dict[str, CompatibleType] | T | None = None, clone: bool = False, inplace: bool = False, *, keys_to_update: Sequence[NestedKey] | None = None, non_blocking: bool = False, **kwargs)#
Copies the entries of a dictionary, a tensordict or a tensorclass into the existing entries.
Returns
self. Works asupdate_(), which describes the arguments: the existing tensors keep their storage. The fields can also be passed as keyword arguments,tc.update_(x=value).inplacehas no effect.A tensorclass or dict source ignores
clone,non_blockingandkeys_to_update: the entries thatkeys_to_updateleaves out are written too, and the call warns. Starting with TensorDict 0.17, the default will change to writing only the entries inkeys_to_update, as for a TensorDict source.Examples
>>> import torch >>> from tensordict import TensorClass >>> class Foo(TensorClass): ... x: torch.Tensor >>> foo = Foo(x=torch.zeros(3), batch_size=[3]) >>> x = foo.x >>> _ = foo.update_({"x": torch.ones(3)}) >>> x tensor([1., 1., 1.])
- update_at_(input_dict_or_td: dict[str, CompatibleType] | T, idx: IndexType, clone: bool = False, *, keys_to_update: Sequence[NestedKey] | None = None, non_blocking: bool = False)#
Writes the entries of a dictionary, a tensordict or a tensorclass in place at an index, and returns
self.Works as
update_at_(), which describes the arguments, with one difference. A tensorclass or dict source thatupdate()accepts, such as one with the batch size of the tensorclass, goes toupdate()instead: its entries replace those of the tensorclass whatever the index, and the call warns. Starting with TensorDict 0.17, the default will change to writing such a source in place at the index, asupdate_at_()does.