Consider a softmax layer from Aditya Srinivas Menon's tutorial:
def softmax(x):
exp\_element=np.exp(x-x.max())
return exp\_element/np.sum(exp\_element,axis=0)
This uses some of NumPy's broadcast facilities ("spooky action at a distance"); it is instructive to compare Apple's rank facility, which is entirely explicit (to a fault).
λxs.
{ m ⟜ (⋉)/* _1 xs; a ⟜ [e:(x-m)]\`{0} xs
; n ⟜ ((+)/)\`\{1\} (a::M float)
; ⍉([(%x)'y]\`{0,1} n a)
}
((+)/)`{1} takes sums over the array's 1-cells, defaulting to iterating over the first
axis. Rank in Apple was designed with type inference in mind; one has
> :ty \f.\x.\y. f\`{0,1} x y
(a → Vec i b → c) → Arr sh a → Arr (i \`Cons\` sh) b → Arr sh c
([(%x)'y]`{0,1} n a) selects 0-cells (floats) from n and 1-cells from a
and normalizes these 1-cells. A transpose (⍉) is necessary to massage the
dimensions at the end, which suggests that the design of Apple's rank facilities
was ill-considered.
