torch-einops-utils 0.0.34__tar.gz → 0.1.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: torch-einops-utils
3
- Version: 0.0.34
3
+ Version: 0.1.0
4
4
  Summary: Personal utility functions
5
5
  Project-URL: Homepage, https://pypi.org/project/torch-einops-utils/
6
6
  Project-URL: Repository, https://github.com/lucidrains/torch-einops-utils
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "torch-einops-utils"
3
- version = "0.0.34"
3
+ version = "0.1.0"
4
4
  description = "Personal utility functions"
5
5
  authors = [
6
6
  { name = "Phil Wang", email = "lucidrains@gmail.com" }
@@ -10,7 +10,7 @@ def test_sequential():
10
10
  nn.ReLU()
11
11
  )
12
12
  assert len(seq) == 2
13
-
13
+
14
14
  # Test forward pass
15
15
  x = torch.randn(2, 10)
16
16
  out = seq(x)
@@ -19,22 +19,22 @@ class SimpleNet(nn.Module):
19
19
  def test_save_load():
20
20
  model = SimpleNet(10, 20)
21
21
  path = Path('test_model.pt')
22
-
22
+
23
23
  # Save the model
24
24
  model.save(str(path))
25
-
25
+
26
26
  # Create another model with different weights
27
27
  model2 = SimpleNet(10, 20)
28
-
28
+
29
29
  # Ensure weights are different initially
30
30
  assert not torch.allclose(model.net.weight, model2.net.weight)
31
-
31
+
32
32
  # Load back
33
33
  model2.load(str(path))
34
-
34
+
35
35
  # Validate weights are the same
36
36
  assert torch.allclose(model.net.weight, model2.net.weight)
37
-
37
+
38
38
  # Cleanup
39
39
  if path.exists():
40
40
  os.remove(path)
@@ -43,18 +43,18 @@ def test_init_and_load():
43
43
  dim, hidden_dim = 16, 32
44
44
  model = SimpleNet(dim, hidden_dim)
45
45
  path = Path('test_model_init.pt')
46
-
46
+
47
47
  # Save the model
48
48
  model.save(str(path))
49
-
49
+
50
50
  # Initialize and load from file
51
51
  model2 = SimpleNet.init_and_load(str(path))
52
-
52
+
53
53
  # Validate attributes and weights
54
54
  assert model2.dim == dim
55
55
  assert model2.hidden_dim == hidden_dim
56
56
  assert torch.allclose(model.net.weight, model2.net.weight)
57
-
57
+
58
58
  # Cleanup
59
59
  if path.exists():
60
60
  os.remove(path)
@@ -97,32 +97,32 @@ def test_sophisticated_nested_save_load():
97
97
  c1 = Child(name = "c1")
98
98
  c2 = Child(name = "c2")
99
99
  c_nest = Child(grandchild = gc, name = "c_nest")
100
-
100
+
101
101
  p1 = Parent(child1 = c1, child2 = c2)
102
102
  p2 = Parent(child1 = c_nest)
103
-
103
+
104
104
  gp = GrandParent(p1 = p1, p2 = p2)
105
-
105
+
106
106
  path = Path('sophisticated_test.pt')
107
-
107
+
108
108
  # Save
109
109
  gp.save(str(path))
110
-
110
+
111
111
  # Load
112
112
  gp2 = GrandParent.init_and_load(str(path))
113
-
113
+
114
114
  # Verify structure
115
115
  assert gp2.p1.child1.name == "c1"
116
116
  assert gp2.p1.child2.name == "c2"
117
117
  assert gp2.p2.child1.name == "c_nest"
118
118
  assert gp2.p2.child1.grandchild.dim == 8
119
-
119
+
120
120
  # Verify weight parity
121
121
  assert torch.allclose(gp.param, gp2.param)
122
122
  assert torch.allclose(gp.p1.param, gp2.p1.param)
123
123
  assert torch.allclose(gp.p1.child1.param, gp2.p1.child1.param)
124
124
  assert torch.allclose(gp.p2.child1.grandchild.param, gp2.p2.child1.grandchild.param)
125
-
125
+
126
126
  if path.exists():
127
127
  os.remove(path)
128
128
 
@@ -30,7 +30,12 @@ from torch_einops_utils.torch_einops_utils import (
30
30
  slice_left_at_dim,
31
31
  slice_right_at_dim,
32
32
  safe_stack,
33
- safe_cat
33
+ safe_cat,
34
+ mask_after,
35
+ mask_before,
36
+ shift_right,
37
+ shift_left,
38
+ reverse_cumsum
34
39
  )
35
40
 
36
41
  def test_exist():
@@ -250,3 +255,58 @@ def test_safe_functions():
250
255
  assert (safe_cat([t1]) == t1).all()
251
256
  assert (safe_cat([t1, None]) == t1).all()
252
257
  assert safe_cat([t1, t2]).shape == (4, 3)
258
+
259
+ def test_mask_after_before():
260
+ t = tensor([[1, 2, 3, 4, 5], [1, 3, 2, 3, 5]])
261
+
262
+ assert mask_after(t, 3).tolist() == [
263
+ [True, True, True, False, False],
264
+ [True, True, False, False, False]
265
+ ]
266
+
267
+ assert mask_after(t, 3, inclusive = False).tolist() == [
268
+ [True, True, False, False, False],
269
+ [True, False, False, False, False]
270
+ ]
271
+
272
+ assert mask_before(t, 3).tolist() == [
273
+ [False, False, True, True, True],
274
+ [False, False, False, True, True]
275
+ ]
276
+
277
+ assert mask_before(t, 3, inclusive = False).tolist() == [
278
+ [False, False, False, True, True],
279
+ [False, False, False, False, True]
280
+ ]
281
+
282
+ assert mask_after(t.T, 3, dim = 0).tolist() == mask_after(t, 3).T.tolist()
283
+ assert mask_before(t.T, 3, dim = 0).tolist() == mask_before(t, 3).T.tolist()
284
+
285
+ def test_eos_id_masking():
286
+ seq = tensor([
287
+ [1, 4, 5, 2, 0, 0],
288
+ [1, 6, 2, 0, 0, 0],
289
+ [1, 7, 8, 9, 2, 0]
290
+ ])
291
+
292
+ assert mask_after(seq, 2).tolist() == [
293
+ [True, True, True, True, False, False],
294
+ [True, True, True, False, False, False],
295
+ [True, True, True, True, True, False]
296
+ ]
297
+
298
+ assert mask_after(seq, 2, inclusive = False).tolist() == [
299
+ [True, True, True, False, False, False],
300
+ [True, True, False, False, False, False],
301
+ [True, True, True, True, False, False]
302
+ ]
303
+
304
+ def test_shift():
305
+ t = tensor([1, 2, 3])
306
+ assert shift_right(t).tolist() == [0, 1, 2]
307
+ assert shift_left(t).tolist() == [2, 3, 0]
308
+ assert shift_right(t, pad_value = -1).tolist() == [-1, 1, 2]
309
+
310
+ def test_reverse_cumsum():
311
+ t = tensor([1, 2, 3])
312
+ assert reverse_cumsum(t).tolist() == [6, 5, 3]
@@ -20,7 +20,9 @@ from torch_einops_utils.torch_einops_utils import (
20
20
  lens_to_mask,
21
21
  reduce_masks,
22
22
  and_masks,
23
- or_masks
23
+ or_masks,
24
+ mask_after,
25
+ mask_before
24
26
  )
25
27
 
26
28
  from torch_einops_utils.torch_einops_utils import (
@@ -29,7 +31,8 @@ from torch_einops_utils.torch_einops_utils import (
29
31
  )
30
32
 
31
33
  from torch_einops_utils.torch_einops_utils import (
32
- exclusive_cumsum
34
+ exclusive_cumsum,
35
+ reverse_cumsum
33
36
  )
34
37
 
35
38
  from torch_einops_utils.torch_einops_utils import (
@@ -39,7 +42,10 @@ from torch_einops_utils.torch_einops_utils import (
39
42
  pad_left_at_dim_to,
40
43
  pad_right_at_dim_to,
41
44
  pad_sequence,
42
- pad_sequence_and_cat
45
+ pad_sequence_and_cat,
46
+ shift,
47
+ shift_right,
48
+ shift_left
43
49
  )
44
50
 
45
51
  from torch_einops_utils.torch_einops_utils import (
@@ -87,6 +87,9 @@ def masked_mean(
87
87
  def exclusive_cumsum(t, dim = -1):
88
88
  return t.cumsum(dim = dim) - t
89
89
 
90
+ def reverse_cumsum(t, dim = -1):
91
+ return t.sum(dim = dim, keepdim = True) - t.cumsum(dim = dim) + t
92
+
90
93
  # shapes
91
94
 
92
95
  def shape_with_replace(
@@ -210,6 +213,18 @@ def and_masks(masks):
210
213
  def or_masks(masks):
211
214
  return reduce_masks(masks, torch.logical_or)
212
215
 
216
+ def mask_after(t, value, dim = -1, inclusive = True):
217
+ mask = t == value
218
+ if inclusive:
219
+ mask = shift_right(mask, amount = 1, dim = dim, pad_value = False)
220
+ return mask.float().cumsum(dim = dim) == 0.
221
+
222
+ def mask_before(t, value, dim = -1, inclusive = True):
223
+ mask = t == value
224
+ if inclusive:
225
+ mask = shift_left(mask, amount = 1, dim = dim, pad_value = False)
226
+ return reverse_cumsum(mask.float(), dim = dim) == 0.
227
+
213
228
  # padding
214
229
 
215
230
  def pad_at_dim(
@@ -243,6 +258,17 @@ def pad_right_at_dim_to(t, length: int, dim = -1, **kwargs):
243
258
 
244
259
  return pad_right_at_dim(t, length - curr_len, dim = dim, **kwargs)
245
260
 
261
+ # shifting
262
+
263
+ def shift(t, amount = 1, dim = -1, pad_value = 0.):
264
+ return pad_at_dim(t, (amount, -amount), dim = dim, value = pad_value)
265
+
266
+ def shift_right(t, amount = 1, dim = -1, pad_value = 0.):
267
+ return shift(t, amount, dim = dim, pad_value = pad_value)
268
+
269
+ def shift_left(t, amount = 1, dim = -1, pad_value = 0.):
270
+ return shift(t, -amount, dim = dim, pad_value = pad_value)
271
+
246
272
  # better pad sequence
247
273
 
248
274
  def pad_sequence(