⍉ — Transpose / Reorder axes
Function. Keys: Alt-o Backslash. Ranks: ∞ monadic, 1 ∞ dyadic
⍉Y reverses axis order.
⍉2 3⍴⍳6 ⍝ [0 3⋄1 4⋄2 5]In A⍉Y, each item of A is a number that gives the result axis for the corresponding leading axis of Y. The axes of Y that A doesn’t reach fill the unused result axes in order, as in BQN. Together, the selected and filled axes run from 0 with no gaps. A key on an item names its result axis. A name in place of a number gives DOMAIN ERROR. To select axes by name, use ⍉⁻¹.
1 0⍉2 3⍴⍳6 ⍝ [0 3⋄1 4⋄2 5]
⍴1⍉2 3 4⍴⍳24 ⍝ [3 2 4]ₓ
["rows":1 "cols":0]⍉2 3⍴⍳6
⍝ ["cols":3 "rows":2]⍴[0 3⋄1 4⋄2 5]Repeated destinations select diagonals, taking the smallest participating dimension. A diagonal keeps the keys and name that its axes agree on, as mix does.
0 0⍉3 3⍴⍳9 ⍝ 0 4 8
⍴["i":0 0]⍉3 3⍴⍳9 ⍝ ["i":3]ₓA left argument of higher rank transposes Y once for each row, and stacks the results.
[0 1⋄1 0]⍉2 3⍴⍳6 ⍝ [[0 1 2⋄3 4 5]⋄[0 3⋄1 4⋄2 5]]Split and route
A nested list splits one input axis. Each of its items is a [destination size] pair, optionally keyed by the new axis’s name. One size can be ∞, which ⍉ infers. The sizes must multiply to exactly the input axis’s length. Unlike ⍴, a split never cycles or truncates data.
x←2 3 8⍴⍳48
⍴[0 2 [1 2;3 ∞]]⍉x ⍝ [2 2 3 4]ₓThe last axis splits into sizes 2 4, which go to result axes 1 3. This turns batch tokens packed into batch heads tokens features. A key on an unsplit item names its result axis. Keys within a group name the factors. A key on a group names the axis that the inverse merges.
x←["b":2 "t":3 "d":8]⍴⍳48
f←["b":0 "t":2 "d":["h":1 2;"f":3 ∞]]⍉
⍴f x ⍝ ["b":2 "h":2 "t":3 "f":4]ₓ
(f⁻¹f x)≡x ⍝ $tKeys only name axes. Item i still describes input axis i, whatever its key.
Inverse
⍉⁻¹Y is ⍉Y. In X⍉⁻¹Y, X lists the source axes in output order, as einops does. A numeric X reverses X⍉. A name in X selects that axis of Y.
2 0 1⍉⁻¹2 0 1⍉2 3 4⍴⍳24 ⍝ 2 3 4⍴⍳24
⍴2⍉⁻¹2 3 4⍴⍳24 ⍝ [4 2 3]ₓ
x←["b":2 "h":3 "w":4]⍴⍳24
⍴["w" "b" "h"]⍉⁻¹x ⍝ ["w":4 "b":2 "h":3]ₓA nested list merges the axes it selects, in that order. Its items can also be [source size] pairs, as a split’s are. Each size must match its axis’s length. A size of ∞ matches any length. The inverse of a bound split reads its pairs this way.
x←2 3 8⍴⍳48
f←[0 2 [1 2;3 ∞]]⍉
(f⁻¹f x)≡x ⍝ $t
[0 2 [1 3]]⍉⁻¹f x ⍝ 2 3 8⍴⍳48Names let ⍉⁻¹ reorder and merge the factors of a split. For 16 by 16 image patches, split height and width into patch and pixel axes, then merge them into one patch axis and one pixel axis:
images←["b":8 "h":224 "w":224 "c":3]⍴0
patches←["b":0 "h":["i":1 ∞;"y":3 16] "w":["j":2 ∞;"x":4 16] "c":5]⍉images
⍴patches ⍝ ["b":8 "i":14 "j":14 "y":16 "x":16 "c":3]ₓ
⍴["b" "patch":["i" "j"] "pixel":["y" "x" "c"]]⍉⁻¹patches
⍝ ["b":8 "patch":196 "pixel":768]ₓSplitting or merging drops position keys on the changed axes. A changed axis takes its name from a key in the left argument, and has none without one. Untouched axes keep their keys and names, unless a key renames them. A named X⍉⁻¹ doesn’t record the original axis order. Inverting it gives DOMAIN ERROR. A numeric left argument inverts in both directions when each group is a list of pairs with sizes. Under writes back through every form of ⍉ and ⍉⁻¹.