Recently, I started to learn some APL and J. In particular, I really liked the idea of verb rank. This package JJ.jl attempts to enable something similar in Julia.
In a nutshell, verb rank is related to the split-apply-combine strategy and allows to apply a function (called verb in APL/J) to sub-arrays by specifying the desired number of dimensions the function should act on. As an example, consider a 2 3 4
sized array
arr = reshape(1:24, 2, 3, 4)
and a function providing some information about a value:
info(x) = "Some scalar $x"
info(x::AbstractArray) = "Array of shape $(size(x))"
Then, we can obtain information about each (scalar) element of an
array via broadcasting, i.e.,
julia> info.(arr)
2×3×4 Array{String, 3}:
[:, :, 1] =
"Some scalar 1" "Some scalar 3" "Some scalar 5"
"Some scalar 2" "Some scalar 4" "Some scalar 6"
[:, :, 2] =
"Some scalar 7" "Some scalar 9" "Some scalar 11"
"Some scalar 8" "Some scalar 10" "Some scalar 12"
[:, :, 3] =
"Some scalar 13" "Some scalar 15" "Some scalar 17"
"Some scalar 14" "Some scalar 16" "Some scalar 18"
[:, :, 4] =
"Some scalar 19" "Some scalar 21" "Some scalar 23"
"Some scalar 20" "Some scalar 22" "Some scalar 24"
Alternatively, the info
function can be “ranked” and applied to sub-arrays in a systematic fashion:
-
rank"info 0"(arr)
is the same asinfo.(arr)
and applies the function to each scalar element of rank 0. -
rank"info 2"(arr)
now applies the function to all sub-arrays of rank 2, i.e., withndims
= 2 in Julia
julia> rank"info 2"(arr)
4-element Vector{String}:
"Array of shape (2, 3)"
"Array of shape (2, 3)"
"Array of shape (2, 3)"
"Array of shape (2, 3)"
Using this technique, many algorithms on arrays can be expressed quite easily. As a teaser, batched matrix multiplication is straight-forward with this approach
using Flux
A = randn(2, 3, 5) # a batch of 2x3 matrices
B = randn(3, 4, 5) # another batch
A ⊠ B # special batched matrix multiplication
rank"2 * 2"(A, B) # just rank the standard one!
and as slightly bigger examples, I have implemented K-Means clustering and a very basic Transformer layer.
The library tries to be fast (building on JuliennedArrays.jl), but has some overhead and also the syntax could still be improved. Hope, you still find it interesting or useful …