Update.
[pytorch.git] / eingather.py
1 #!/usr/bin/env python
2
3 # Any copyright is dedicated to the Public Domain.
4 # https://creativecommons.org/publicdomain/zero/1.0/
5
6 # Written by Francois Fleuret <francois@fleuret.org>
7
8 import re, torch
9
10 #####################
11
12
13 def eingather(op, src, *indexes):
14     s_src, s_dst = re.search("^([^ ]*) *-> *(.*)", op).groups()
15     s_indexes = re.findall("\(([^)]*)\)", s_src)
16     s_src = re.sub("\([^)]*\)", "_", s_src)
17
18     all_sizes = tuple(d for s in (src,) + indexes for d in s.size())
19     s_all = "".join([s_src] + s_indexes)
20     shape = tuple(all_sizes[s_all.index(v)] for v in s_dst)
21
22     def do(x, s_x):
23         idx = []
24         n_index = 0
25
26         for i in range(x.dim()):
27             v = s_x[i]
28             if v == "_":
29                 idx.append(do(indexes[n_index], s_indexes[n_index]))
30                 n_index += 1
31             else:
32                 j = s_dst.index(v)
33                 a = (
34                     torch.arange(x.size(i))
35                     .reshape((1,) * j + (-1,) + (1,) * (len(s_dst) - j - 1))
36                     .expand(shape)
37                 )
38                 idx.append(a)
39
40         return x[idx]
41
42     return do(src, s_src)
43
44
45 def lambda_eingather(op, src_shape, *indexes_shape):
46     s_src, s_dst = re.search("^([^ ]*) *-> *(.*)", op).groups()
47     s_indexes = re.findall("\(([^)]*)\)", s_src)
48     s_src = re.sub("\([^)]*\)", "_", s_src)
49
50     all_sizes = tuple(d for s in (src_shape,) + indexes_shape for d in s)
51     s_all = "".join([s_src] + s_indexes)
52     shape = tuple(all_sizes[s_all.index(v)] for v in s_dst)
53
54     def do(x_shape, s_x):
55         idx = []
56         n_index = 0
57
58         for i in range(len(x_shape)):
59             v = s_x[i]
60             if v == "_":
61                 f = do(indexes_shape[n_index], s_indexes[n_index])
62                 idx.append(lambda indexes: indexes[n_index][f(indexes)])
63                 n_index += 1
64             else:
65                 j = s_dst.index(v)
66                 a = (
67                     torch.arange(x_shape[i])
68                     .reshape((1,) * j + (-1,) + (1,) * (len(s_dst) - j - 1))
69                     .expand(shape)
70                 )
71                 idx.append(lambda indexes: a)
72
73         print(f"{idx=}")
74         return lambda indexes: [f(indexes) for f in idx]
75
76     f = do(src_shape, s_src)
77     print(f"{f(0)=}")
78     return lambda src, *indexes: src[f(indexes)]
79
80
81 ######################################################################
82
83 # src = torch.rand(3, 5, 3)
84
85 # print(eingather("aba -> ab", src))
86
87 # f = lambda_eingather("aba -> ab", src.shape)
88
89 # print(f(src))
90
91 # exit(0)
92
93 ######################################################################
94
95 src = torch.rand(3, 5, 7, 11)
96 index1 = torch.randint(src.size(2), (src.size(3), src.size(1), src.size(3)))
97 index2 = torch.randint(src.size(3), (src.size(1),))
98
99 # f = lambda_eingather("ca(eae)(a) -> ace", src.shape, index1.shape, index2.shape)
100
101 # print(f(src, index1, index2))
102
103 # result[a, c, e] = src[c, a, index1[e, a, e], index2[a]]
104
105 # result = eingather("ca(eae)(a) -> ace", src, index1, index2)
106
107 from functorch.dim import dims
108
109 a, c, e = dims(3)
110 result = src[c, a, index1[e, a, e], index2[a]].order(a, c, e)
111
112 # Check
113
114 error = 0
115
116 for a in range(result.size(0)):
117     for c in range(result.size(1)):
118         for e in range(result.size(2)):
119             error += (result[a, c, e] - src[c, a, index1[e, a, e], index2[a]]).abs()
120
121 print(error.item())