sourcelibs/Accel/src/Accel.xtl
1⍝# Accel: the acceleratable subset of X_eTaL: elementwise arithmetic, masks, reductions, products, transposes, windows, lookups, choices, a layer
2⍝#
3⍝# Import it with an alias of your choice: "ac:" u_se< "Accel".
4⍝# Put libs/Accel/src on XETAL_PATH ("just path"); the reference is libs/Accel/docs.
5⍝#
6⍝# Every function here is one whole-array expression in the subset a
7⍝# GPU (or an FPGA) can take on directly: static scalar types, pure
8⍝# functions, arithmetic and comparisons item by item with scalar
9⍝# extension, a select by mask, reductions, inner products, tables and
10⍝# transposes.
11⍝# No recursion, no nested arrays, no input or output. The X_eTaL
12⍝# evaluator is the reference: X_eTaL-gpu runs the same functions on a
13⍝# GPU and must give the same answers (the demos are the programs it
14⍝# checks). Names with l: are exported; the others are private.
15
16⍝## Elementwise
17
18⍝# The sum, item by item: the first kernel, one work-item per element.
19⍝# >> "ac:" u_se< "Accel"
20⍝# >> 1 2 3 4 ac:v_add 10 20 30 40
21⍝# 11 22 33 44
22ˡv̲add ← { x y → x + y }
23
24⍝# Every item of the right times the number on the left.
25⍝# >> "ac:" u_se< "Accel"
26⍝# >> 3 ac:s_cale 1 2 3
27⍝# 3 6 9
28ˡs̲cale ← { a x → a × x }
29
30⍝# The items where positive, 0 elsewhere (a select by comparison with
31⍝# a constant), for Ints or Floats.
32⍝# >> "ac:" u_se< "Accel"
33⍝# >> ac:r_elu 1 -2 3 -4
34⍝# 1 0 3 0
35ˡr̲elu ← { x → x m̲ax 0 }
36
37⍝## Masks and select
38
39⍝# t ac:t_hreshold x: x where x > t, 0.0 elsewhere, for Floats: the
40⍝# mask x > t made Float and multiplied in, the select a GPU does
41⍝# with a conditional move. A masked-out negative comes back as -0.0.
42⍝# >> "ac:" u_se< "Accel"
43⍝# >> 0.5 ac:t_hreshold 0.25 0.5 0.75 1.0
44⍝# 0.0 0.0 0.75 1.0
45ˡt̲hreshold ← { t x → x × f̲loat x > t }
46
47⍝# m ac:k_eep x: x where the Int mask m is 1, 0 elsewhere.
48⍝# >> "ac:" u_se< "Accel"
49⍝# >> (1 0 1 0) ac:k_eep 5 6 7 8
50⍝# 5 0 7 0
51ˡk̲eep ← { m x → x × m }
52
53⍝## Reductions
54
55⍝# The sum of the items of a vector: a parallel reduction on a GPU
56⍝# (work-group partials, then a second pass).
57⍝# >> "ac:" u_se< "Accel"
58⍝# >> ac:s_um 1 2 3 4
59⍝# 10
60ˡs̲um ← { x → '+ r̲/ x }
61
62⍝# The largest item of a vector.
63⍝# >> "ac:" u_se< "Accel"
64⍝# >> ac:l_argest 3 9 2
65⍝# 9
66ˡl̲argest ← { x → 'm̲ax r̲/ x }
67
68⍝# The inner product of two vectors: a multiply, then a reduce.
69⍝# >> "ac:" u_se< "Accel"
70⍝# >> 1 2 3 ac:d_ot 4 5 6
71⍝# 32
72ˡd̲ot ← { x y → '+ r̲/ x × y }
73
74⍝## Along an axis
75
76⍝# The sum of each row of a matrix (along axis 2: one work-item per
77⍝# row on a GPU).
78⍝# >> "ac:" u_se< "Accel"
79⍝# >> ac:r_owSums 2 3 r_eshape 1 20 3 4 5 60
80⍝# 24 69
81ˡr̲owSums ← { m → '+ r̲/₂ m }
82
83⍝# The sum of each column (along axis 1, the default of r_/).
84⍝# >> "ac:" u_se< "Accel"
85⍝# >> ac:c_olSums 2 3 r_eshape 1 20 3 4 5 60
86⍝# 5 25 63
87ˡc̲olSums ← { m → '+ r̲/ m }
88
89⍝# The largest item of each row.
90⍝# >> "ac:" u_se< "Accel"
91⍝# >> ac:r_owMax 2 3 r_eshape 1 20 3 4 5 60
92⍝# 20 60
93ˡr̲owMax ← { m → 'm̲ax r̲/₂ m }
94
95⍝## Inner products
96
97⍝# x ac:m_atmul w: the inner product of x's last axis with w's first
98⍝# (APL's +.x): a matrix times a matrix, a matrix times a vector, a
99⍝# vector times a matrix. Each result item is a sum of products, one
100⍝# GPU work-item each (or a tile of them, as the schedule says).
101⍝# >> "ac:" u_se< "Accel"
102⍝# >> (2 3 r_eshape r_ange 6) ac:m_atmul 3 2 r_eshape r_ange 6
103⍝# 22 28
104⍝# 49 64
105⍝# >> (2 3 r_eshape r_ange 6) ac:m_atmul 1 0 2
106⍝# 7 16
107ˡm̲atmul ← { x w → x '+ '× i̲nner w }
108
109⍝## Transposes
110
111⍝# q ac:s_cores k: attention scores, every query row against every
112⍝# key row (q k^T): the inner product of q with the transpose of k. On
113⍝# a GPU the transpose is a gather, one work-item per item, and the
114⍝# product follows (research7: attention decomposes into array
115⍝# operations; it is not an intrinsic).
116⍝# >> "ac:" u_se< "Accel"
117⍝# >> (2 3 r_eshape r_ange 6) ac:s_cores 4 3 r_eshape r_ange 12
118⍝# 14 32 50 68
119⍝# 32 77 122 167
120ˡs̲cores ← { q k → q '+ '× i̲nner o̲\ k }
121
122⍝# x ac:w_eightGrad dy: the gradient of a dense layer's weights, for
123⍝# a batch x (a row per example) and the gradient dy at the layer's
124⍝# output: x^T dy, the product every backward pass is made of.
125⍝# >> "ac:" u_se< "Accel"
126⍝# >> (4 2 r_eshape r_ange 8) ac:w_eightGrad 4 3 r_eshape r_ange 12
127⍝# 118 134 150
128⍝# 140 160 180
129ˡw̲eightGrad ← { x dy → (o̲\ x) '+ '× i̲nner dy }
130
131⍝## Lookups
132
133⍝# t ac:e_mbed ids: the rows of the table t at the positions ids
134⍝# (Ints from 1), in the shape of ids: a batch of token ids picks a
135⍝# batch of embeddings (ids s_elect t). On a GPU a gather, one
136⍝# work-item per item of the result, the table resident; the ids are
137⍝# all that a prompt uploads.
138⍝# >> "ac:" u_se< "Accel"
139⍝# >> t := 4 3 r_eshape 0.0 0.1 0.2 1.0 1.1 1.2 2.0 2.1 2.2 3.0 3.1 3.2
140⍝# >> t ac:e_mbed 4 1 2
141⍝# 3.0 3.1 3.2
142⍝# 0.0 0.1 0.2
143⍝# 1.0 1.1 1.2
144ˡe̲mbed ← { t ids → ids s̲elect t }
145
146⍝## Windows
147
148⍝# ac:w_indows x: every 3 by 3 window of every picture of the batch x
149⍝# (n pictures of h by w) as 9 rows, a column per picture and place:
150⍝# the picture's nine shifted copies (a rotation along each of the
151⍝# last two axes, one copy per shift), their wrapped edges dropped,
152⍝# reshaped; the expression is X_eTaL-ML's Conv (cv:w_indows). On a
153⍝# GPU each rotation and each drop is a gather of its own and the
154⍝# reshape is nothing.
155⍝# >> "ac:" u_se< "Accel"
156⍝# >> ac:w_indows 1 3 4 r_eshape r_ange 12
157⍝# 1 2
158⍝# 2 3
159⍝# 3 4
160⍝# 5 6
161⍝# 6 7
162⍝# 7 8
163⍝# 9 10
164⍝# 10 11
165⍝# 11 12
166ˡw̲indows ← { x →
167 places ← ((2 s̲elect s̲hape x) − 2) × (3 s̲elect s̲hape x) − 2
168 (9 c̲at places × t̲ally x) r̲eshape 1 d̲rop₅ -1 d̲rop₅ 1 d̲rop₄ -1 d̲rop₄ -1 0 1 o̲-₃ -1 0 1 o̲-₃ x
169}
170
171⍝# k ac:c_onv x: the f filters k (f rows of 9, a 3 by 3 filter row by
172⍝# row) over every window of every picture of x, as one matrix
173⍝# product: the maps, n by f by (h - 2) by (w - 2) (a valid
174⍝# convolution without the bias; X_eTaL-ML's Conv adds its biases).
175⍝# >> "ac:" u_se< "Accel"
176⍝# >> (1 9 r_eshape 0 0 0 0 1 0 0 0 0) ac:c_onv 1 3 4 r_eshape r_ange 12
177⍝# 6 7
178ˡc̲onv ← { k x →
179 n ← t̲ally x
180 h ← (2 s̲elect s̲hape x) − 2
181 w ← (3 s̲elect s̲hape x) − 2
182 (n c̲at (t̲ally k) c̲at h c̲at w) r̲eshape 2 1 3 t̲ranspose ((t̲ally k) c̲at n c̲at h × w) r̲eshape k '+ '× i̲nner ˡw̲indows x
183}
184
185⍝## Choices
186
187⍝# ac:a_rgmax m: for each row of m, the position (from 1) of its
188⍝# largest item, the first on a tie: the decision of a classifier,
189⍝# the next token of a greedy sampler. X_eTaL-ML's nn:a_rgmax, as
190⍝# array operations the IR has: the row's largest spread along the
191⍝# row, a mask where the row equals it, the smallest position under
192⍝# the mask (n + 1 elsewhere). No new GPU operation: a row reduction,
193⍝# a table, a fused mask and a second row reduction.
194⍝# >> "ac:" u_se< "Accel"
195⍝# >> ac:a_rgmax 3 4 r_eshape 5.0 1.0 9.0 9.0 2.0 2.0 2.0 2.0 7.0 8.0 6.0 8.0
196⍝# 3 1 2
197ˡa̲rgmax ← { m →
198 n ← f̲irst -1 t̲ake s̲hape m
199 hit ← f̲loat m = ('m̲ax r̲/₂ m) 'l̲eft t̲able o̲ffsets n
200 f̲loor 'm̲in r̲/₂ (hit × (o̲ffsets t̲ally m) 'r̲ight t̲able f̲loat r̲ange n) + (1.0 − hit) × f̲loat n + 1
201}
202
203⍝# k ac:t_opk m: a mask of each row's k largest items (1.0 there,
204⍝# 0.0 elsewhere): the row's largest taken out k times, as X_eTaL-ML's
205⍝# Sample (sm:t_op) and its mixture-of-experts router choose; items
206⍝# tied with a taken one go with it, so a tie at the k-th keeps all
207⍝# tied. The choice of a router, the candidates of a retrieval. A
208⍝# taken item is pushed below its row by more than the row's range
209⍝# (Sample subtracts 1e300, which is infinity in a GPU's f32 and
210⍝# poisons the row with NaN).
211⍝# >> "ac:" u_se< "Accel"
212⍝# >> 2 ac:t_opk 2 4 r_eshape 0.1 0.5 0.15 0.25 7.0 8.0 6.0 8.0
213⍝# 0.0 1.0 0.0 1.0
214⍝# 1.0 1.0 0.0 1.0
215ˡt̲opk ← { k m →
216 n ← f̲irst -1 t̲ake s̲hape m
217 shift ← (1.0 + ('m̲ax r̲/₂ m) − 'm̲in r̲/₂ m) 'l̲eft t̲able o̲ffsets n
218 left ← k '{ q → q − shift × f̲loat q = ('m̲ax r̲/₂ q) 'l̲eft t̲able o̲ffsets n } p̲ower m
219 f̲loat (m − left) > 0.0
220}
221
222⍝## A layer of a network
223
224⍝# x ac:d_ense wb: the layer x W + b for a batch x (one example per
225⍝# row), the weights W (a row per input, a column per output) with the
226⍝# bias b as one more row at the bottom of wb, as X_eTaL-ML's NN has
227⍝# it. The bias row is spread down the batch by a table (r_ight).
228⍝# >> "ac:" u_se< "Accel"
229⍝# >> x := 2 3 r_eshape 1.0 2.0 3.0 -1.0 0.5 0.0
230⍝# >> wb := 4 2 r_eshape 0.5 -1.0 1.0 0.25 -0.5 2.0 0.1 0.2
231⍝# >> x ac:d_ense wb
232⍝# 1.1 5.7
233⍝# 0.1 1.325
234ˡd̲ense ← { x wb → (x '+ '× i̲nner -1 d̲rop wb) + (o̲ffsets t̲ally x) 'r̲ight t̲able r̲avel -1 t̲ake wb }
235
236⍝# Each row of a matrix into probabilities: the row's largest taken
237⍝# off (the same result, no overflow), e to each, each divided by its
238⍝# row's sum; a row's value spread across the row by a table (l_eft).
239⍝# >> "ac:" u_se< "Accel"
240⍝# >> ac:s_oftmax 2 3 r_eshape 0.0 0.0 0.0 1.0 2.0 3.0
241⍝# 0.3333333333333333 0.3333333333333333 0.3333333333333333
242⍝# 0.09003057317038046 0.24472847105479764 0.6652409557748218
243ˡs̲oftmax ← { m →
244 c ← o̲ffsets f̲irst -1 t̲ake s̲hape m
245 e ← e̲xp m − ('m̲ax r̲/₂ m) 'l̲eft t̲able c
246 e ÷ ('+ r̲/₂ e) 'l̲eft t̲able c
247}
248
249⍝## The acceptance pipeline
250
251⍝# x ac:p_ipeline w: multiply, add, select, reduce in one expression,
252⍝# the sum of the positive items of x + x * w: the first end-to-end
253⍝# test research7 asks for (arithmetic, map, zip, comparison,
254⍝# selection, reduction), for Ints or Floats.
255⍝# >> "ac:" u_se< "Accel"
256⍝# >> 1 2 3 4 ac:p_ipeline 1 -2 1 -2
257⍝# 8
258ˡp̲ipeline ← { x w → ˡs̲um ˡr̲elu x + x × w }