Search code examples
arraysaplinner-product

How does inner product generalize to higher-dimensional arrays?


How does the APL-style inner product operator (higher-order function), which accepts two function arguments and two arrays, generalize to more than two dimensions? I see that the result array has a number of dimensions equal to the sum of the dimensions of the arrays minus 2, and that the size of the last dimension of the first array m must equal the size of the first dimension of the second array n.

I'll assume without loss of generality that the functions are addition and multiplication. In one dimension, then, the corresponding elements of the vectors are multiplied and these products are added to produce the vector dot product (a scalar).

By the same token, in two dimensions the [m, n] element of the result matrix is the dot product of the mth column of the first matrix and the nth row of the second matrix.

But when I get to this point in descriptions of the inner product function, they usually say "with the obvious generalization to more dimensions" or else simply don't mention higher dimensions at all. That isn't very helpful. Can anyone explain how it is computed?


Solution

  • Suppose I have two conformable matrices and want to do an inner product.

          a ← 5 2 ⍴ ⍳ 10
          b ← 2 6 ⍴ ⍳ 10
          a
    1  2
    3  4
    5  6
    7  8
    9 10
          b
    1 2 3  4 5 6
    7 8 9 10 1 2
          a +.= b
    1 0 0 0 0 1
    0 0 1 0 0 0
    0 0 0 0 1 0
    0 1 0 0 0 0
    0 0 0 1 0 0
          a +.× b
    15 18  21  24  7 10
    31 38  45  52 19 26
    47 58  69  80 31 42
    63 78  93 108 43 58
    79 98 117 136 55 74
    

    What's important here is that the trailing dimension of a, that is, ¯1↑⍴a, matches the leading dimension of b, or 1↑⍴b. Similarly, the shape of the result is the catenation of the shapes of both arguments, less the trailing dimension of a and the leading dimension of b, or (¯1↓⍴a),1↓⍴b.

    Suppose now I had higher-dimensional arrays, then the same rules would apply. The trailing dimension of a must match the leading dimension of b etc.

    The non-obvious generalisation to higher dimensions is that this operation is no different than an inner product of two matrices, provided you collapse the relevant dimensions.

          a ← 5 1 2 1 2 1 2 ⍴ ⍳ 40 
          b ← 2 3 4 5 ⍴ ⍳ 120
    

    To collapse the dimensions, simply multiply together all but the last dimension of a, and all but the first dimension of b.

          a1 ← 20 2 ⍴ ⍳ 40
          b1 = 2 60 ⍴ ⍳ 120
    

    Do the operation

          r1 ← a1 +.× b1
    

    Lastly, put the collapsed dimensions back.

          r ← 5 1 2 1 2 1 3 4 5 ⍴ r1
    

    Try it!