Indexing arrays in APL: Squad indexing on multi-dimensional arrays

James HeslipAPLLeave a Comment

I had a question for the team in our regular meeting on Wednesday, but it relied on an understanding on squad indexing, and nobody felt comfortable enough with to answer my question.

I’ve since reach a conclusion and thought I’d share.

Squad indexing vs. Square bracket indexing

 ⎕←cube←?3 4 5⍴7
4 3 4 1 1
5 5 7 6 3
2 2 5 1 4
4 6 5 4 3
1 6 1 6 6
3 1 5 3 3
2 5 5 4 7
1 5 6 5 2
5 6 6 4 1
7 1 5 1 1
1 1 4 1 1
6 6 4 1 1

For a given 3-dimensional object, I can use square bracket indexing to retrieve the indices I want. I can even omit a dimension completely to receive all items in that dimension, i.e. here I am asking for the first plane, the second row, and all columns:

5 5 7 6 3

In this example, you can do the same with squad indexing- just omit any indices for that axis:

      1 2⌷cube
5 5 7 6 3

Square bracket indexing has a bit more of an advantage for some cases, though. If the axes you want are non-contiguous, you can ask for [x;;y]. With squad indexing it’s not so simple. See the below example:

3 5 2 6
1 ⍬ 2⌷cube
           ⍝ empty result

My thoughts were with [x;;y], ;; ←→ ⍬, representing an empty numeric array. This is not the case, however. As with typical indexing, the shape of the index is the shape of your result. Zilde is of shape 0 (⍬←→ 0⍴0), and therefore I would get back a shape 0 result:

      ⍴1 ⍬ 2⌷cube

My question to the team was how would I perform an operation like this, similar to [x;;y]?

Gilgamesh Athoraya has since told me the only way of performing this kind of indexing is to use axis specification:

      1 2⌷[1 3]cube
3 5 2 6

I feel like there should be a way to do it using without specifying axis. Zilde may not be appropriate, but it was my immediate go-to answer. In fact, what we’re after is not an empty array, but an array whose shape is that of the actual object- identity () would achieve something like this. There is a slight issue with using identity, though:

	1 ⊢ 2⌷cube 

Here, right tack will be used dyadically as a function, and return the right argument. Let me know your thoughts: how should squad indexing behave? Should there be a way of achieving this in APL without axis specification (identity, zilde, etc), or do you have a different opinion entirely?

Why am I against axis specification with squad indexing?

I have an answer to my original question so I’ve quenched my thirst for knowledge and I have a workaround. The issue lies in performance. If we consider the same cube which was defined earlier (cube←?3 4 5⍴7) there are several ways of getting at all of the rows, some more efficient than others:

      ]runtime 'cube[1;;2]' '1 2⌷[1 3]cube' '1(1 2 3 4)2⌷cube' '1(⍳4)2⌷cube' -compare

  cube[1;;2]       → 6.1E¯7 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕                   
  1 2⌷[1 3]cube    → 8.7E¯7 | +43% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕          
  1(1 2 3 4)2⌷cube → 9.5E¯7 | +55% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕       
  1(⍳4)2⌷cube      → 1.1E¯6 | +84% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
Figure 1. Version: 17.0.36114.0 32 Unicode

Using axis-specification I am immediately hit by a 43% speed penalty and this is worsened further if I actually specify all rows by hand! Naturally, using ⍳4 in place of (1 2 3 4) yields a slower result still as a function is taking place to generate the indices.

Something about the omission of the indices with square-bracket indexing allows for a fast execution time, and this is something I’d like to see in squad indexing if it’s not already a feature.

About the Author

A picture of James Heslip the author of this blog

James Heslip

APL Team Leader

James is an APL Programmer with a keen interest in mathematics. His love for computing was almost an accident. From a young age he always enjoyed using them- playing video games and such- but it was never considered that anything more would come from it. James originally had plans to pursue a career in finance. More about James.

More from James

Other Posts