Update the metadata from the output of `MetaTensor.__torch_function__`. The output of `torch.Tensor.__torch_function__` could be a single object or a sequence of them. Hence, in `MetaTensor.__torch_function__` we convert them to a list of not already, and then we lo
(rets: Sequence, func, args, kwargs)
| 173 | |
| 174 | @staticmethod |
| 175 | def update_meta(rets: Sequence, func, args, kwargs) -> Sequence: |
| 176 | """ |
| 177 | Update the metadata from the output of `MetaTensor.__torch_function__`. |
| 178 | |
| 179 | The output of `torch.Tensor.__torch_function__` could be a single object or a |
| 180 | sequence of them. Hence, in `MetaTensor.__torch_function__` we convert them to a |
| 181 | list of not already, and then we loop across each element, processing metadata |
| 182 | as necessary. For each element, if not of type `MetaTensor`, then nothing to do. |
| 183 | |
| 184 | Args: |
| 185 | rets: the output from `torch.Tensor.__torch_function__`, which has been |
| 186 | converted to a list in `MetaTensor.__torch_function__` if it wasn't |
| 187 | already a `Sequence`. |
| 188 | func: the torch function that was applied. Examples might be `torch.squeeze` |
| 189 | or `torch.Tensor.__add__`. We need this since the metadata need to be |
| 190 | treated differently if a batch of data is considered. For example, |
| 191 | slicing (`torch.Tensor.__getitem__`) the ith element of the 0th |
| 192 | dimension of a batch of data should return a ith tensor with the ith |
| 193 | metadata. |
| 194 | args: positional arguments that were passed to `func`. |
| 195 | kwargs: keyword arguments that were passed to `func`. |
| 196 | |
| 197 | Returns: |
| 198 | A sequence with the same number of elements as `rets`. For each element, if |
| 199 | the input type was not `MetaTensor`, then no modifications will have been |
| 200 | made. If global parameters have been set to false (e.g., |
| 201 | `not get_track_meta()`), then any `MetaTensor` will be converted to |
| 202 | `torch.Tensor`. Else, metadata will be propagated as necessary (see |
| 203 | :py:func:`MetaTensor._copy_meta`). |
| 204 | """ |
| 205 | out = [] |
| 206 | metas = None # optional output metadicts for each of the return value in `rets` |
| 207 | is_batch = any(x.is_batch for x in MetaObj.flatten_meta_objs(args, kwargs.values()) if hasattr(x, "is_batch")) |
| 208 | for idx, ret in enumerate(rets): |
| 209 | # if not `MetaTensor`, nothing to do. |
| 210 | if not isinstance(ret, MetaTensor): |
| 211 | pass |
| 212 | # if not tracking, convert to `torch.Tensor`. |
| 213 | elif not get_track_meta(): |
| 214 | ret = ret.as_tensor() |
| 215 | # else, handle the `MetaTensor` metadata. |
| 216 | else: |
| 217 | meta_args = MetaObj.flatten_meta_objs(args, kwargs.values()) |
| 218 | ret.is_batch = is_batch |
| 219 | ret.copy_meta_from(meta_args, copy_attr=not is_batch) |
| 220 | # the following is not implemented but the network arch may run into this case: |
| 221 | # if func == torch.cat and any(m.is_batch if hasattr(m, "is_batch") else False for m in meta_args): |
| 222 | # raise NotImplementedError("torch.cat is not implemented for batch of MetaTensors.") |
| 223 | if is_batch: |
| 224 | ret = MetaTensor._handle_batched(ret, idx, metas, func, args, kwargs) |
| 225 | out.append(ret) |
| 226 | # if the input was a tuple, then return it as a tuple |
| 227 | return tuple(out) if isinstance(rets, tuple) else out |
| 228 | |
| 229 | @classmethod |
| 230 | def _handle_batched(cls, ret, idx, metas, func, args, kwargs): |
no test coverage detected