MCPcopy Create free account
hub / github.com/dcharatan/flowmap / Manipulable

Class Manipulable

flowmap/misc/manipulable.py:19–123  ·  view source on GitHub ↗

Give a dataclass that bundles tensors, arrays, and lists functionality that mimics regular tensor manipulation. For example, this allows you to slice every element of a dataclass at once.

Source from the content-addressed store, hash-verified

17
18
19class Manipulable:
20 """Give a dataclass that bundles tensors, arrays, and lists functionality that
21 mimics regular tensor manipulation. For example, this allows you to slice every
22 element of a dataclass at once.
23 """
24
25 def to(self: T, device: torch.device) -> T:
26 """Return a shallow copy of this instance in which all tensors have been moved
27 to the specified device.
28 """
29
30 replacements = {}
31
32 for field in fields(self):
33 # Move fields that are torch.Tensor to the specified device.
34 value = getattr(self, field.name)
35 if isinstance(value, Tensor):
36 replacements[field.name] = value.to(device)
37
38 return replace(self, **replacements)
39
40 def __getitem__(self: T, slices: slice | tuple[SliceLike, ...]) -> T:
41 """Return a shallow copy of this instance in which all tensors, arrays, and
42 lists have been sliced according to the specified slices. If the number of
43 slices exceeds the number of dimensions of a particular dataclass element,
44 ignore the trailing slices. For now, ellipses are not supported.
45 """
46
47 # To simplify the implementation below, ensure that slices is a tuple of slices.
48 if not isinstance(slices, tuple):
49 slices = (slices,)
50
51 replacements = {}
52
53 # Recursively slice any sliceable fields.
54 for field in fields(self):
55 value = getattr(self, field.name)
56 value = Manipulable.recursive_slice(value, slices)
57 replacements[field.name] = value
58
59 return replace(self, **replacements)
60
61 @staticmethod
62 def recursive_slice(value: Any, slices: tuple[SliceLike, ...]) -> Any:
63 if not isinstance(value, Sliceable):
64 return value
65
66 # Torch tensors and NumPy arrays don't need to be recursively sliced, since they
67 # already support tuples of slices.
68 if isinstance(value, Tensor) or isinstance(value, np.ndarray):
69 # If the number of slices exceeds the number of dimensions, drop the
70 # trailing slices.
71 slices = slices[: value.ndim]
72
73 # Apply the remaining slices.
74 return value[slices]
75
76 # Lists and tuples need to be recursively sliced. To check if we should recurse

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected