torch-einops-kit 0.1.3__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/PKG-INFO +1 -1
  2. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/pyproject.toml +1 -1
  3. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_padding.py +26 -10
  4. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/scaleValues.py +24 -11
  5. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/README.md +0 -0
  6. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/__init__.py +0 -0
  7. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_cat_and_stack.py +0 -0
  8. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_dimensions.py +0 -0
  9. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_helpers.py +0 -0
  10. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_masking.py +0 -0
  11. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_semiotics.py +0 -0
  12. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_slicing.py +0 -0
  13. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/_theTypes.py +0 -0
  14. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/device.py +0 -0
  15. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/einops.py +0 -0
  16. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/nn.py +0 -0
  17. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/py.typed +0 -0
  18. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/save_load.py +0 -0
  19. {torch_einops_kit-0.1.3 → torch_einops_kit-0.1.4}/src/torch_einops_kit/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: torch-einops-kit
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Typed tensor-shaping, masking, padding, device-routing, and checkpoint utilities for PyTorch and `einops`. A superset of `lucidrains/torch-einops-utils` with similar utilities from other lucidrains packages. Adds strict typing, extensive tests, and comprehensive docstrings.
5
5
  Keywords: artificial intelligence,einops,machine learning,pytorch,torch
6
6
  Author: Phil Wang, Hunter Hogan
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "torch-einops-kit"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  description = "Typed tensor-shaping, masking, padding, device-routing, and checkpoint utilities for PyTorch and `einops`. A superset of `lucidrains/torch-einops-utils` with similar utilities from other lucidrains packages. Adds strict typing, extensive tests, and comprehensive docstrings."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -289,32 +289,42 @@ def pad_sequence(
289
289
  return_stacked: Literal[True] = True,
290
290
  return_lens: Literal[False] = False,
291
291
  pad_lens: bool = False
292
- ) -> Tensor | None: ...
293
- # `list[Tensor] | None` could create confusion: see the tuple | None overloads.
292
+ ) -> Tensor: ...
294
293
  @overload
295
294
  def pad_sequence(
296
295
  tensors: Sequence[Tensor], *, dim: int = -1, value: float = 0., left: bool = False, dim_stack: int = 0,
297
296
  return_stacked: Literal[False],
298
297
  return_lens: Literal[False] = False,
299
298
  pad_lens: bool = False
300
- ) -> list[Tensor] | None: ...
301
- # TODO return `tuple | None` is problematic. See `test_better_pad_sequence`. Pyright gives a confusing error: "None is not iterable" when trying to unpack the tuple.
299
+ ) -> list[Tensor]: ...
302
300
  @overload
303
301
  def pad_sequence(
304
302
  tensors: Sequence[Tensor], *, dim: int = -1, value: float = 0., left: bool = False, dim_stack: int = 0,
305
303
  return_stacked: Literal[True] = True,
306
304
  return_lens: Literal[True],
307
305
  pad_lens: bool = False
308
- ) -> tuple[Tensor, Tensor] | None: ...
306
+ ) -> tuple[Tensor, Tensor]: ...
309
307
  @overload
310
308
  def pad_sequence(
311
309
  tensors: Sequence[Tensor], *, dim: int = -1, value: float = 0., left: bool = False, dim_stack: int = 0,
312
310
  return_stacked: Literal[False],
313
311
  return_lens: Literal[True],
314
312
  pad_lens: bool = False
315
- ) -> tuple[list[Tensor], Tensor] | None: ...
313
+ ) -> tuple[list[Tensor], Tensor]: ...
314
+ @overload
315
+ def pad_sequence(
316
+ tensors: list[Tensor] = [], # noqa: B006
317
+ *, dim: int = -1, value: float = 0.0, left: bool = False, dim_stack: int = 0,
318
+ return_stacked: bool = True, return_lens: bool = False, pad_lens: bool = False
319
+ ) -> None: ...
320
+ @overload
321
+ def pad_sequence(
322
+ tensors: tuple[()] = (),
323
+ *, dim: int = -1, value: float = 0.0, left: bool = False, dim_stack: int = 0,
324
+ return_stacked: bool = True, return_lens: bool = False, pad_lens: bool = False
325
+ ) -> None: ...
316
326
  def pad_sequence(
317
- tensors: Sequence[Tensor],
327
+ tensors: Sequence[Tensor] = (),
318
328
  *,
319
329
  dim: int = -1,
320
330
  value: float = 0.0,
@@ -443,7 +453,13 @@ def pad_sequence(
443
453
 
444
454
  return output
445
455
 
446
- def pad_sequence_and_cat(tensors: Sequence[Tensor], *, dim_cat: int = 0, dim: int = -1, value: float = 0., left: bool = False) -> Tensor | None:
456
+ @overload
457
+ def pad_sequence_and_cat(tensors: Sequence[Tensor], *, dim_cat: int = 0, dim: int = -1, value: float = 0., left: bool = False) -> Tensor:...
458
+ @overload
459
+ def pad_sequence_and_cat(tensors: list[Tensor] = [], *, dim_cat: int = 0, dim: int = -1, value: float = 0., left: bool = False) -> None:... # noqa: B006
460
+ @overload
461
+ def pad_sequence_and_cat(tensors: tuple[()]=(), *, dim_cat: int = 0, dim: int = -1, value: float = 0., left: bool = False) -> None:...
462
+ def pad_sequence_and_cat(tensors: Sequence[Tensor]=(), *, dim_cat: int = 0, dim: int = -1, value: float = 0., left: bool = False) -> Tensor | None:
447
463
  """Pad `tensors` to a shared length along `dim` and concatenate along `dim_cat`.
448
464
 
449
465
  You can use `pad_sequence_and_cat` to align and merge a heterogeneous-length sequence of `Tensor`
@@ -498,12 +514,12 @@ def pad_sequence_and_cat(tensors: Sequence[Tensor], *, dim_cat: int = 0, dim: in
498
514
  [3] tests/test_utils.py
499
515
  """
500
516
  padded: Tensor | list[Tensor] | None = pad_sequence(tensors, dim=dim, value=value, left=left, return_stacked=False, return_lens=False)
501
- if padded is not None:
517
+ if padded is not None: # pyright: ignore[reportUnnecessaryComparison]
502
518
  return cat(padded, dim=dim_cat)
503
519
  return padded
504
520
 
505
521
  """
506
- Some or all of the logic in this module may be protected by the following.
522
+ Some of the logic in this module may be protected by the following.
507
523
 
508
524
  MIT License
509
525
 
@@ -107,7 +107,8 @@ def l2norm(t: Tensor) -> Tensor:
107
107
  """
108
108
  return F.normalize(t, dim=-1, p=2)
109
109
 
110
- def masked_mean(t: Tensor, mask: Tensor | None = None, dim: torch.Size | list[int] | tuple[int, ...] | int | None = None, eps: float = 1e-5) -> Tensor:
110
+ def masked_mean(t: Tensor, mask: Tensor | None = None, dim: torch.Size | list[int] | tuple[int, ...] | int | None = None, eps: float = 1e-5
111
+ , *, keepdim: bool = False) -> Tensor:
111
112
  """Compute the mean of `t` over positions selected by `mask`.
112
113
 
113
114
  You can use this function to average only the elements of `t` where `mask` is `True`, ignoring
@@ -131,6 +132,9 @@ def masked_mean(t: Tensor, mask: Tensor | None = None, dim: torch.Size | list[in
131
132
  eps : float = 1e-5
132
133
  A small value added to the denominator to prevent division by zero when computing the masked
133
134
  mean along a dimension.
135
+ keepdim : bool = False
136
+ When `True`, the reduced dimension is retained as a size-one dimension in `result`, allowing
137
+ `result` to broadcast against `t`.
134
138
 
135
139
  Returns
136
140
  -------
@@ -172,6 +176,15 @@ def masked_mean(t: Tensor, mask: Tensor | None = None, dim: torch.Size | list[in
172
176
  # result == tensor([1.0, 3.5])
173
177
  ```
174
178
 
179
+ Compute the sequence-level mean of transformer activations with `keepdim=True` to preserve the
180
+ sequence dimension for broadcasting, used in the mean-variance split residual update [4][5]:
181
+
182
+ ```python
183
+ mean_x = masked_mean(x, mask=mask, dim=-2, keepdim=True)
184
+ mean_residual = masked_mean(residual, mask=mask, dim=-2, keepdim=True)
185
+ centered_x = x - mean_x
186
+ ```
187
+
175
188
  References
176
189
  ----------
177
190
  [1] torch.Tensor.mean - PyTorch documentation
@@ -180,20 +193,20 @@ def masked_mean(t: Tensor, mask: Tensor | None = None, dim: torch.Size | list[in
180
193
 
181
194
  [3] tests.test_utils.test_masked_mean
182
195
 
196
+ [4] Lu, P. (2026). Mean Mode Screaming: Mean-Variance Split Residuals for 1000-Layer Diffusion
197
+ Transformers. arXiv:2605.06169.
198
+ https://arxiv.org/abs/2605.06169
199
+ [5] x_transformers.x_transformers.MVSplitResidualUpdate.forward - lucidrains/x-transformers
200
+ https://github.com/lucidrains/x-transformers
201
+
183
202
  """
184
203
  if not exists(mask):
185
- return t.mean(dim=dim) if exists(dim) else t.mean()
186
-
187
- if mask.ndim < t.ndim:
188
- mask = pad_right_ndim(mask, t.ndim - mask.ndim)
189
-
190
- mask = mask.expand_as(t)
204
+ mask = torch.ones_like(t, dtype=torch.bool)
191
205
 
192
- if not exists(dim):
193
- return t[mask].mean() if mask.any() else t[mask].sum()
206
+ mask = pad_right_ndim(mask, max(0, t.ndim - mask.ndim)).expand_as(t)
194
207
 
195
- num: Tensor = (t * mask).sum(dim=dim)
196
- den: Tensor = mask.sum(dim=dim)
208
+ num: Tensor = (t * mask).sum(dim=dim, keepdim=keepdim)
209
+ den: Tensor = mask.sum(dim=dim, keepdim=keepdim)
197
210
 
198
211
  return num / den.clamp(min=eps)
199
212