On Hsu's Suggestivity and Idioms

Posted on July 24, 2023 by Brandon Wilson
Tags:

Recently, Aaron Hsu posted a nice article discussing a way in which APL’s algebraic properties absolutely shine. The article ends up exhibits two expressions—⍸2<⌿0⍪⍵ and ⍸2>⌿⍵⍪0—which respectively pick the first and last elements of groups of \(1\)s in a boolean mask.

The punchline being how the simple algebraic change—< into >—corresponds to meaningful semantics. This is precisely what Iverson’s “Notation as a Tool of Thought” is about, also echoing the “power of good notation” idea in 3Blue1Brown’s Triangle of Power video.

So, what about replacing < with an arbitrary boolean function? Hsu gives a nice table with the corresponding output, but the semantics of each are not spelled out. Paul Mansour makes a head start in his Boolean Techniques article, but I think we can go all the way:

Y            0 1 1 1 0 0 0 0  0 1 1 1 0 0 0 1  1 1 1 1 0 0 0 0  1 1 1 1 0 0 0 1  Description
{2<⌿0⍪⍵}¨Y   0 1 0 0 0 0 0 0  0 1 0 0 0 0 0 1  1 0 0 0 0 0 0 0  1 0 0 0 0 0 0 1  1-group head
{2<⌿1⍪⍵}¨Y   0 1 0 0 0 0 0 0  0 1 0 0 0 0 0 1  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 1  1-group head, 1-boundary
{2<⌿⍵⍪0}¨Y   1 0 0 0 0 0 0 0  1 0 0 0 0 0 1 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 1 0  0-group terminus
{2<⌿⍵⍪1}¨Y   1 0 0 0 0 0 0 1  1 0 0 0 0 0 1 0  0 0 0 0 0 0 0 1  0 0 0 0 0 0 1 0  0-group terminus, 1-boundary
{2≤⌿0⍪⍵}¨Y   1 1 1 1 0 1 1 1  1 1 1 1 0 1 1 1  1 1 1 1 0 1 1 1  1 1 1 1 0 1 1 1  not 0-group head
{2≤⌿1⍪⍵}¨Y   0 1 1 1 0 1 1 1  0 1 1 1 0 1 1 1  1 1 1 1 0 1 1 1  1 1 1 1 0 1 1 1  not 0-group head, 1-boundary
{2≤⌿⍵⍪0}¨Y   1 1 1 0 1 1 1 1  1 1 1 0 1 1 1 0  1 1 1 0 1 1 1 1  1 1 1 0 1 1 1 0  not 1-group terminus
{2≤⌿⍵⍪1}¨Y   1 1 1 0 1 1 1 1  1 1 1 0 1 1 1 1  1 1 1 0 1 1 1 1  1 1 1 0 1 1 1 1  not 1-group terminus, 1-boundary
{2=⌿0⍪⍵}¨Y   1 0 1 1 0 1 1 1  1 0 1 1 0 1 1 0  0 1 1 1 0 1 1 1  0 1 1 1 0 1 1 0  tail
{2=⌿1⍪⍵}¨Y   0 0 1 1 0 1 1 1  0 0 1 1 0 1 1 0  1 1 1 1 0 1 1 1  1 1 1 1 0 1 1 0  tail, 1-boundary
{2=⌿⍵⍪0}¨Y   0 1 1 0 1 1 1 1  0 1 1 0 1 1 0 0  1 1 1 0 1 1 1 1  1 1 1 0 1 1 0 0  not terminus
{2=⌿⍵⍪1}¨Y   0 1 1 0 1 1 1 0  0 1 1 0 1 1 0 1  1 1 1 0 1 1 1 0  1 1 1 0 1 1 0 1  not terminus, 1-boundary
{2≠⌿0⍪⍵}¨Y   0 1 0 0 1 0 0 0  0 1 0 0 1 0 0 1  1 0 0 0 1 0 0 0  1 0 0 0 1 0 0 1  head
{2≠⌿1⍪⍵}¨Y   1 1 0 0 1 0 0 0  1 1 0 0 1 0 0 1  0 0 0 0 1 0 0 0  0 0 0 0 1 0 0 1  head, 1-boundary
{2≠⌿⍵⍪0}¨Y   1 0 0 1 0 0 0 0  1 0 0 1 0 0 1 1  0 0 0 1 0 0 0 0  0 0 0 1 0 0 1 1  terminus
{2≠⌿⍵⍪1}¨Y   1 0 0 1 0 0 0 1  1 0 0 1 0 0 1 0  0 0 0 1 0 0 0 1  0 0 0 1 0 0 1 0  terminus, 1-boundary
{2≥⌿0⍪⍵}¨Y   1 0 1 1 1 1 1 1  1 0 1 1 1 1 1 0  0 1 1 1 1 1 1 1  0 1 1 1 1 1 1 0  not 1-group head
{2≥⌿1⍪⍵}¨Y   1 0 1 1 1 1 1 1  1 0 1 1 1 1 1 0  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 0  not 1-group head, 1-boundary
{2≥⌿⍵⍪0}¨Y   0 1 1 1 1 1 1 1  0 1 1 1 1 1 0 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 0 1  not 0-group terminus
{2≥⌿⍵⍪1}¨Y   0 1 1 1 1 1 1 0  0 1 1 1 1 1 0 1  1 1 1 1 1 1 1 0  1 1 1 1 1 1 0 1  not 0-group terminus, 1-boundary
{2>⌿0⍪⍵}¨Y   0 0 0 0 1 0 0 0  0 0 0 0 1 0 0 0  0 0 0 0 1 0 0 0  0 0 0 0 1 0 0 0  0-group head
{2>⌿1⍪⍵}¨Y   1 0 0 0 1 0 0 0  1 0 0 0 1 0 0 0  0 0 0 0 1 0 0 0  0 0 0 0 1 0 0 0  0-group head, 1-boundary
{2>⌿⍵⍪0}¨Y   0 0 0 1 0 0 0 0  0 0 0 1 0 0 0 1  0 0 0 1 0 0 0 0  0 0 0 1 0 0 0 1  1-group terminus
{2>⌿⍵⍪1}¨Y   0 0 0 1 0 0 0 0  0 0 0 1 0 0 0 0  0 0 0 1 0 0 0 0  0 0 0 1 0 0 0 0  1-group terminus, 1-boundary
{2∧⌿0⍪⍵}¨Y   0 0 1 1 0 0 0 0  0 0 1 1 0 0 0 0  0 1 1 1 0 0 0 0  0 1 1 1 0 0 0 0  1-group tail
{2∧⌿1⍪⍵}¨Y   0 0 1 1 0 0 0 0  0 0 1 1 0 0 0 0  1 1 1 1 0 0 0 0  1 1 1 1 0 0 0 0  1-group tail, 1-boundary
{2∧⌿⍵⍪0}¨Y   0 1 1 0 0 0 0 0  0 1 1 0 0 0 0 0  1 1 1 0 0 0 0 0  1 1 1 0 0 0 0 0  1-group non-termius
{2∧⌿⍵⍪1}¨Y   0 1 1 0 0 0 0 0  0 1 1 0 0 0 0 1  1 1 1 0 0 0 0 0  1 1 1 0 0 0 0 1  1-group non-terminus, 1-boundary
{2⍲⌿0⍪⍵}¨Y   1 1 0 0 1 1 1 1  1 1 0 0 1 1 1 1  1 0 0 0 1 1 1 1  1 0 0 0 1 1 1 1  not 1-group tail
{2⍲⌿1⍪⍵}¨Y   1 1 0 0 1 1 1 1  1 1 0 0 1 1 1 1  0 0 0 0 1 1 1 1  0 0 0 0 1 1 1 1  not 1-group tail, 1-boundary
{2⍲⌿⍵⍪0}¨Y   1 0 0 1 1 1 1 1  1 0 0 1 1 1 1 1  0 0 0 1 1 1 1 1  0 0 0 1 1 1 1 1  not 1-group non-terminus
{2⍲⌿⍵⍪1}¨Y   1 0 0 1 1 1 1 1  1 0 0 1 1 1 1 0  0 0 0 1 1 1 1 1  0 0 0 1 1 1 1 0  not 1-group non-terminus, 1-boundary
{2∨⌿0⍪⍵}¨Y   0 1 1 1 1 0 0 0  0 1 1 1 1 0 0 1  1 1 1 1 1 0 0 0  1 1 1 1 1 0 0 1  not 0-group tail
{2∨⌿1⍪⍵}¨Y   1 1 1 1 1 0 0 0  1 1 1 1 1 0 0 1  1 1 1 1 1 0 0 0  1 1 1 1 1 0 0 1  not 0-group tail, 1-boundary
{2∨⌿⍵⍪0}¨Y   1 1 1 1 0 0 0 0  1 1 1 1 0 0 1 1  1 1 1 1 0 0 0 0  1 1 1 1 0 0 1 1  not 0-group non-terminus
{2∨⌿⍵⍪1}¨Y   1 1 1 1 0 0 0 1  1 1 1 1 0 0 1 1  1 1 1 1 0 0 0 1  1 1 1 1 0 0 1 1  not 0-group non-terminus, 1-boundary
{2⍱⌿0⍪⍵}¨Y   1 0 0 0 0 1 1 1  1 0 0 0 0 1 1 0  0 0 0 0 0 1 1 1  0 0 0 0 0 1 1 0  0-group tail
{2⍱⌿1⍪⍵}¨Y   0 0 0 0 0 1 1 1  0 0 0 0 0 1 1 0  0 0 0 0 0 1 1 1  0 0 0 0 0 1 1 0  0-group tail, 1-boundary
{2⍱⌿⍵⍪0}¨Y   0 0 0 0 1 1 1 1  0 0 0 0 1 1 0 0  0 0 0 0 1 1 1 1  0 0 0 0 1 1 0 0  0-group non-terminus
{2⍱⌿⍵⍪1}¨Y   0 0 0 0 1 1 1 0  0 0 0 0 1 1 0 0  0 0 0 0 1 1 1 0  0 0 0 0 1 1 0 0  0-group non-terminus, 1-boundary
{2⊢⌿0⍪⍵}¨Y   0 1 1 1 0 0 0 0  0 1 1 1 0 0 0 1  1 1 1 1 0 0 0 0  1 1 1 1 0 0 0 1  id
{2⊢⌿1⍪⍵}¨Y   0 1 1 1 0 0 0 0  0 1 1 1 0 0 0 1  1 1 1 1 0 0 0 0  1 1 1 1 0 0 0 1  id
{2⊢⌿⍵⍪0}¨Y   1 1 1 0 0 0 0 0  1 1 1 0 0 0 1 0  1 1 1 0 0 0 0 0  1 1 1 0 0 0 1 0  left-shift, 0-insert
{2⊢⌿⍵⍪1}¨Y   1 1 1 0 0 0 0 1  1 1 1 0 0 0 1 1  1 1 1 0 0 0 0 1  1 1 1 0 0 0 1 1  left-shift, 1-insert
{2⊣⌿0⍪⍵}¨Y   0 0 1 1 1 0 0 0  0 0 1 1 1 0 0 0  0 1 1 1 1 0 0 0  0 1 1 1 1 0 0 0  right-shift, 0-insert
{2⊣⌿1⍪⍵}¨Y   1 0 1 1 1 0 0 0  1 0 1 1 1 0 0 0  1 1 1 1 1 0 0 0  1 1 1 1 1 0 0 0  right-shift, 1-insert
{2⊣⌿⍵⍪0}¨Y   0 1 1 1 0 0 0 0  0 1 1 1 0 0 0 1  1 1 1 1 0 0 0 0  1 1 1 1 0 0 0 1  id
{2⊣⌿⍵⍪1}¨Y   0 1 1 1 0 0 0 0  0 1 1 1 0 0 0 1  1 1 1 1 0 0 0 0  1 1 1 1 0 0 0 1  id

It is interesting to note that the naming scheme can be thought as a simple re-parameterization of the algebraic phrases. In particular, we have

\[ \begin{aligned} 48 &= \textrm{#functions} \\ &= B \cdot e \cdot d \\ &= 12 \cdot 2 \cdot 2 \end{aligned} \]

where

Whereas the semantic descriptions partition the collection differently:

\[ \begin{aligned} 48 &= \textrm{#functions} \\ &= g \cdot p \cdot d \cdot b \cdot i + A + S + I \\ &= 2 \cdot 2 \cdot 2 \cdot 2 \cdot 2 + 8 + 4 + 4 \end{aligned} \]

where

Just for fun, we can actually complete the table above, with the missing boolean functions:

Y                0 1 1 1 0 0 0 0  0 1 1 1 0 0 0 1  1 1 1 1 0 0 0 0  1 1 1 1 0 0 0 1
{2(0⍨)⌿⍵⍪0}¨Y    0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  constant 0
{2(0⍨)⌿⍵⍪1}¨Y    0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  constant 0
{2(0⍨)⌿0⍪⍵}¨Y    0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  constant 0
{2(0⍨)⌿1⍪⍵}¨Y    0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0  constant 0
{2(1⍨)⌿⍵⍪0}¨Y    1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  constant 1
{2(1⍨)⌿⍵⍪1}¨Y    1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  constant 1
{2(1⍨)⌿0⍪⍵}¨Y    1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  constant 1
{2(1⍨)⌿1⍪⍵}¨Y    1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  1 1 1 1 1 1 1 1  constant 1
{2~⍤⊢⌿⍵⍪0}¨Y     0 0 0 1 1 1 1 1  0 0 0 1 1 1 0 1  0 0 0 1 1 1 1 1  0 0 0 1 1 1 0 1  invert left-shift, 1-insert
{2~⍤⊢⌿⍵⍪1}¨Y     0 0 0 1 1 1 1 0  0 0 0 1 1 1 0 0  0 0 0 1 1 1 1 0  0 0 0 1 1 1 0 0  invert left-shift, 0-insert
{2~⍤⊢⌿0⍪⍵}¨Y     1 0 0 0 1 1 1 1  1 0 0 0 1 1 1 0  0 0 0 0 1 1 1 1  0 0 0 0 1 1 1 0  invert
{2~⍤⊢⌿1⍪⍵}¨Y     1 0 0 0 1 1 1 1  1 0 0 0 1 1 1 0  0 0 0 0 1 1 1 1  0 0 0 0 1 1 1 0  invert
{2~⍤⊣⌿⍵⍪0}¨Y     1 0 0 0 1 1 1 1  1 0 0 0 1 1 1 0  0 0 0 0 1 1 1 1  0 0 0 0 1 1 1 0  invert
{2~⍤⊣⌿⍵⍪1}¨Y     1 0 0 0 1 1 1 1  1 0 0 0 1 1 1 0  0 0 0 0 1 1 1 1  0 0 0 0 1 1 1 0  invert
{2~⍤⊣⌿0⍪⍵}¨Y     1 1 0 0 0 1 1 1  1 1 0 0 0 1 1 1  1 0 0 0 0 1 1 1  1 0 0 0 0 1 1 1  invert right-shift, 1-insert
{2~⍤⊣⌿1⍪⍵}¨Y     0 1 0 0 0 1 1 1  0 1 0 0 0 1 1 1  0 0 0 0 0 1 1 1  0 0 0 0 0 1 1 1  invert right-shift, 0-insert

Which makes it manifest why they can be safely ignored.