Rank (operator): Difference between revisions

From APL Wiki
Jump to navigation Jump to search
(add missing built-in box)
(Mention differing semantics of rank in NARS2000.)
 
(6 intermediate revisions by 3 users not shown)
Line 1: Line 1:
:''This article is about the operator. See [[Rank]] for the number associated with every array. For numbers associated with a function specifying its argument rank, see [[function rank]].''
:''This article is about the operator. See [[Rank]] for the number associated with every array. For numbers associated with a function specifying its argument rank, see [[function rank]].''


{{Built-in|Depth|⍤}} is a [[primitive operator|primitive]] [[dyadic operator]] which applies its left [[operand]] function to [[cells]] of its arguments specified by its right operand array.  
{{Built-in|Rank|⍤}} is a [[primitive operator|primitive]] [[dyadic operator]] which applies its left [[operand]] function to [[cells]] of its arguments specified by its right operand array.  


== Rank specification ==
== Rank specification ==
The right operand specifies the [[rank]] of subarrays to which the left operand function is applied as follows:
The right operand specifies the [[rank]] of subarrays to which the left operand function is applied as follows:
For left argument <source lang=apl inline>⍺</source> and right argument <source lang=apl inline>⍵</source>,
For left argument <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> and right argument <syntaxhighlight lang=apl inline>⍵</syntaxhighlight>,
<source lang=apl>
<syntaxhighlight lang=apl>
   ⍤    c  ⍝ Rank-c cells of ⍵ (monadic) or both arguments (dyadic)
   ⍤    c  ⍝ Rank-c cells of ⍵ (monadic) or both arguments (dyadic)
   ⍤  b c  ⍝ Rank-b cells of ⍺ and rank-c cells of ⍵ (dyadic)
   ⍤  b c  ⍝ Rank-b cells of ⍺ and rank-c cells of ⍵ (dyadic)
   ⍤a b c  ⍝ Rank-a cells of ⍵ (monadic), b-cells of ⍺ and c-cells of ⍵ (dyadic)
   ⍤a b c  ⍝ Rank-a cells of ⍵ (monadic), b-cells of ⍺ and c-cells of ⍵ (dyadic)
</source>
</syntaxhighlight>


A non-negative right operand specifies the number of ''final'' axes to which the function applies. A negative right operand specifies ''complementary'' rank, i.e. the number of leading axes to be ''excluded''. Negative rank can also be thought of as rank specification ''relative to'' the overall rank of the argument array.
A non-negative right operand specifies the number of ''final'' axes to which the function applies. A negative right operand specifies ''complementary'' rank, i.e. the number of leading axes to be ''excluded''. Negative rank can also be thought of as rank specification ''relative to'' the overall rank of the argument array.


Since a rank specification greater than the rank of the argument array means to apply the function to the whole array, <source lang=apl inline>99</source>, <source lang=apl inline>(⌊/⍬)</source> or <source lang=apl inline>∞</source>, depending on the implementation, is "rank infinity" and always specifies the whole argument array.
Since a rank specification greater than the rank of the argument array means to apply the function to the whole array, <syntaxhighlight lang=apl inline>99</syntaxhighlight>, <syntaxhighlight lang=apl inline>(⌊/⍬)</syntaxhighlight> or <syntaxhighlight lang=apl inline>∞</syntaxhighlight>, depending on the implementation, is "rank infinity" and always specifies the whole argument array.


== Examples ==
== Examples ==
Rotate rows in matrices of a 3D array:
Reverse order of rows in matrices of a 3D array:
<source lang=apl>
<syntaxhighlight lang=apl>
       ⊖⍤2⊢3 2 4⍴⎕A
       ⊖⍤2⊢3 2 4⍴⎕A
EFGH
EFGH
Line 28: Line 28:
UVWX
UVWX
QRST
QRST
</source>
</syntaxhighlight>


Laminate scalars from arrays of differing ranks:
Laminate scalars from arrays of differing ranks:
<source lang=apl>
<syntaxhighlight lang=apl>
       'ABCD',⍤0⍤1⊢2 4⍴⍳8
       'ABCD',⍤0⍤1⊢2 4⍴⍳8
A 1
A 1
Line 42: Line 42:
C 7
C 7
D 8
D 8
</source>
</syntaxhighlight>


Flat outer product:
Flat outer product:
<source lang=apl>
<syntaxhighlight lang=apl>
       -⍤1⍤1 99⍨3 2⍴6 7 1 1 2 4  ⍝ ↑∘.-⍨↓
       -⍤1⍤1 99⍨3 2⍴6 7 1 1 2 4  ⍝ ↑∘.-⍨↓
  0  0
  0  0
Line 58: Line 58:
  1  3
  1  3
  0  0
  0  0
</source>
</syntaxhighlight>


== History ==
== History ==
Line 64: Line 64:


== Rank vs Axis ==
== Rank vs Axis ==
Due to its ability to apply functions to specified subarrays, rank is frequently contrasted with [https://aplwiki.com/wiki/Function_axis bracket-axis]. It provides nearly all of the functionality of the anomalous axis operator (<source lang=apl inline>f[a]</source>) without its draw-backs.<ref name="intro2rank">[[Robert Bernecky]]. [https://doi.org/10.1145/55626.55632 An introduction to function rank] at [[APL88]]. ACM SIGAPL APL Quote Quad, 18(2), pp.39-43. 1987.</ref>
Due to its ability to apply functions to specified subarrays, rank is frequently contrasted with [https://aplwiki.com/wiki/Function_axis bracket-axis]. It provides nearly all of the functionality of the anomalous axis operator (<syntaxhighlight lang=apl inline>f[a]</syntaxhighlight>) without its draw-backs.<ref name="intro2rank">[[Robert Bernecky]]. [https://doi.org/10.1145/55626.55632 An introduction to function rank] at [[APL88]]. ACM SIGAPL APL Quote Quad, 18(2), pp.39-43. 1987.</ref>


One of these draw-backs is that bracket-axis is specified ad hoc for each of the specific primitives on which it applies. Rank benefits from consistent behaviour when applied to any function, including [[user-defined functions]]. The ad hoc nature of bracket-axis definitions means that a generalised axis operator which works on any function, but behaves just as bracket-axis on those particular primitives, is impossible to formulate.
One of these draw-backs is that bracket-axis is specified ad hoc for each of the specific primitives on which it applies. Rank benefits from consistent behaviour when applied to any function, including [[user-defined functions]]. The ad hoc nature of bracket-axis definitions means that a generalised axis operator which works on any function, but behaves just as bracket-axis on those particular primitives, is impossible to formulate.
Line 72: Line 72:
===Sum along axis===
===Sum along axis===
Rank k-cells are defined for k trailing axes, whereas axes are numbered from most major (first axis i.e. axis number 1) to least major (last axis). This leads to a simple and symmetrical relationship.
Rank k-cells are defined for k trailing axes, whereas axes are numbered from most major (first axis i.e. axis number 1) to least major (last axis). This leads to a simple and symmetrical relationship.
<source lang=apl>
<syntaxhighlight lang=apl>
+/[1+⍺-⍨≢⍴⍵] ≡ +⌿⍤⍺            ⍝ For scalar ⍺
+/[1+⍺-⍨≢⍴⍵] ≡ +⌿⍤⍺            ⍝ For scalar ⍺
+/[⍺]        ≡ +⌿⍤(1+⍺-⍨≢⍴⍵)  ⍝ For scalar ⍺
+/[⍺]        ≡ +⌿⍤(1+⍺-⍨≢⍴⍵)  ⍝ For scalar ⍺
</source>
</syntaxhighlight>


===Enclose axes===
===Enclose axes===
Enclose-with-axis is equivalent to transposing desired axes to the end of the array's shape and enclosing subarrays of a rank matching the number of axes.
Enclose-with-axis is equivalent to transposing desired axes to the end of the array's shape and enclosing subarrays of a rank matching the number of axes.
<source lang=apl>
<syntaxhighlight lang=apl>
EncloseAxes←{
EncloseAxes←{
     axes←⍳≢⍴⍵
     axes←⍳≢⍴⍵
Line 86: Line 86:
}
}
⊂[⍺] ≡ ⍺∘EncloseAxes
⊂[⍺] ≡ ⍺∘EncloseAxes
</source>
</syntaxhighlight>


===Merge axes===
===Merge axes===
Line 92: Line 92:


Merging trailing axes is trivial.
Merging trailing axes is trivial.
<source lang=apl>
<syntaxhighlight lang=apl>
,[(-⍺)↑⍴⍵] ≡ ,⍤⍺
,[(-⍺)↑⍴⍵] ≡ ,⍤⍺
</source>
</syntaxhighlight>


Merging leading axes is more involved, but can be expressed in one line.
Merging leading axes is more involved, but can be expressed in one line.
<source lang=apl>
<syntaxhighlight lang=apl>
,[⍳⍺] ←→ {(1⌽⍳≢⍴z)⍉z←,⍤⍺((-⍺)⌽⍳≢⍴⍵)⍉⍵}
,[⍳⍺] ←→ {(1⌽⍳≢⍴z)⍉z←,⍤⍺((-⍺)⌽⍳≢⍴⍵)⍉⍵}
</source>
</syntaxhighlight>


The general treatment benefits from being expanded.
The general treatment benefits from being expanded.
<source lang=apl>
<syntaxhighlight lang=apl>
MergeAxes←{
MergeAxes←{
     axes←⍳≢⍴⍵
     axes←⍳≢⍴⍵
Line 112: Line 112:


,[⍺] ←→ ⍺∘MergeAxes  ⍝ for ∧/1=¯2-/⍺
,[⍺] ←→ ⍺∘MergeAxes  ⍝ for ∧/1=¯2-/⍺
</source>
</syntaxhighlight>
 
==Difference among dialects==
Uniquely to [[NARS2000]], negative rank k specifies taking the <syntaxhighlight lang=apl inline>∣k</syntaxhighlight>-cells from the array that results from permuting the k leading axes to the trailing end of the shape; the shape of the cells of argument <syntaxhighlight lang=apl inline>a</syntaxhighlight> specified by rank k is <syntaxhighlight lang=apl inline>(-k)↑⍴a</syntaxhighlight> whether k is negative or positive. It also allows axis specification for the rank operator, denoting what axis the results will go to in the frame; <syntaxhighlight lang=apl inline>x f⍤[ax] l r⊢y</syntaxhighlight> is equivalent to <syntaxhighlight lang=apl inline>⊃[ax] x f⍤l r⊢y</syntaxhighlight> (noting that <syntaxhighlight lang=apl inline>⊃</syntaxhighlight> in NARS2000 is [[Mix]]).


== External links ==
== External links ==
Line 119: Line 122:
* [http://wiki.nars2000.org/index.php?title=Rank NARS2000]
* [http://wiki.nars2000.org/index.php?title=Rank NARS2000]
* [https://mlochbaum.github.io/BQN/doc/rank.html#rank BQN]
* [https://mlochbaum.github.io/BQN/doc/rank.html#rank BQN]
* [https://code.jsoftware.com/wiki/Vocabulary/quote NuVoc]


=== Publications ===
=== Publications ===

Latest revision as of 23:54, 20 July 2023

This article is about the operator. See Rank for the number associated with every array. For numbers associated with a function specifying its argument rank, see function rank.

Rank () is a primitive dyadic operator which applies its left operand function to cells of its arguments specified by its right operand array.

Rank specification

The right operand specifies the rank of subarrays to which the left operand function is applied as follows: For left argument and right argument ,

   ⍤    c   ⍝ Rank-c cells of ⍵ (monadic) or both arguments (dyadic)
   ⍤  b c   ⍝ Rank-b cells of ⍺ and rank-c cells of ⍵ (dyadic)
   ⍤a b c   ⍝ Rank-a cells of ⍵ (monadic), b-cells of ⍺ and c-cells of ⍵ (dyadic)

A non-negative right operand specifies the number of final axes to which the function applies. A negative right operand specifies complementary rank, i.e. the number of leading axes to be excluded. Negative rank can also be thought of as rank specification relative to the overall rank of the argument array.

Since a rank specification greater than the rank of the argument array means to apply the function to the whole array, 99, (⌊/⍬) or , depending on the implementation, is "rank infinity" and always specifies the whole argument array.

Examples

Reverse order of rows in matrices of a 3D array:

      ⊖⍤2⊢3 2 4⍴⎕A
EFGH
ABCD
    
MNOP
IJKL
    
UVWX
QRST

Laminate scalars from arrays of differing ranks:

      'ABCD',⍤0⍤1⊢2 4⍴⍳8
A 1
B 2
C 3
D 4
   
A 5
B 6
C 7
D 8

Flat outer product:

      -⍤1⍤1 99⍨3 2⍴6 7 1 1 2 4   ⍝ ↑∘.-⍨↓
 0  0
 5  6
 4  3
     
¯5 ¯6
 0  0
¯1 ¯3
     
¯4 ¯3
 1  3
 0  0

History

The rank operator was invented by Arthur Whitney in 1982 and first implemented in SHARP APL in 1983. It has been described as "a microcosm of APL history"[1], its evolution a progression from scalar extension, which has been in APL since its inception, through leading axis theory to a construct which is a generalisation of scalar extension, inner (matrix) product, outer product, maplist in LISP, map in modern functional programming languages and the broadcast facility in NumPy.

Rank vs Axis

Due to its ability to apply functions to specified subarrays, rank is frequently contrasted with bracket-axis. It provides nearly all of the functionality of the anomalous axis operator (f[a]) without its draw-backs.[2]

One of these draw-backs is that bracket-axis is specified ad hoc for each of the specific primitives on which it applies. Rank benefits from consistent behaviour when applied to any function, including user-defined functions. The ad hoc nature of bracket-axis definitions means that a generalised axis operator which works on any function, but behaves just as bracket-axis on those particular primitives, is impossible to formulate.

Here we show some bracket-axis constructs and their equivalent expressions which use rank but do not use bracket-axis.

Sum along axis

Rank k-cells are defined for k trailing axes, whereas axes are numbered from most major (first axis i.e. axis number 1) to least major (last axis). This leads to a simple and symmetrical relationship.

+/[1+⍺-⍨≢⍴⍵] ≡ +⌿⍤⍺            ⍝ For scalar ⍺
+/[⍺]        ≡ +⌿⍤(1+⍺-⍨≢⍴⍵)   ⍝ For scalar ⍺

Enclose axes

Enclose-with-axis is equivalent to transposing desired axes to the end of the array's shape and enclosing subarrays of a rank matching the number of axes.

EncloseAxes←{
    axes←⍳≢⍴⍵
    move←⍋(axes~⍺),⍺
    ⊂⍤(≢⍺)⊢move⍉⍺
}
⊂[⍺] ≡ ⍺∘EncloseAxes

Merge axes

Ravel-with-axis allows data to be merged along specified consecutive axes. The requirement that axes be consecutive is so that the data can remain in its original relative order.

Merging trailing axes is trivial.

,[(-⍺)↑⍴⍵] ≡ ,⍤⍺

Merging leading axes is more involved, but can be expressed in one line.

,[⍳⍺] ←→ {(1⌽⍳≢⍴z)⍉z←,⍤⍺((-⍺)⌽⍳≢⍴⍵)⍉⍵}

The general treatment benefits from being expanded.

MergeAxes←{
    axes←⍳≢⍴⍵
    move←⍋(axes~⍺),⍺
    merged←,⍤(≢⍺)⊢move⍉⍵
    restore←((⍳≢⍴merged)~⊃⍺),⊃⍺
    restored⍉merged
}

,[⍺] ←→ ⍺∘MergeAxes   ⍝ for ∧/1=¯2-/⍺

Difference among dialects

Uniquely to NARS2000, negative rank k specifies taking the ∣k-cells from the array that results from permuting the k leading axes to the trailing end of the shape; the shape of the cells of argument a specified by rank k is (-k)↑⍴a whether k is negative or positive. It also allows axis specification for the rank operator, denoting what axis the results will go to in the frame; x f⍤[ax] l r⊢y is equivalent to ⊃[ax] x f⍤l r⊢y (noting that in NARS2000 is Mix).

External links

Documentation

Publications

References

  1. Roger Hui and Morten Kromberg. APL since 1978. ACM HOPL IV. 2020-06.
  2. Robert Bernecky. An introduction to function rank at APL88. ACM SIGAPL APL Quote Quad, 18(2), pp.39-43. 1987.
APL built-ins [edit]
Primitives (Timeline) Functions
Scalar
Monadic ConjugateNegateSignumReciprocalMagnitudeExponentialNatural LogarithmFloorCeilingFactorialNotPi TimesRollTypeImaginarySquare RootRound
Dyadic AddSubtractTimesDivideResiduePowerLogarithmMinimumMaximumBinomialComparison functionsBoolean functions (And, Or, Nand, Nor) ∙ GCDLCMCircularComplexRoot
Non-Scalar
Structural ShapeReshapeTallyDepthRavelEnlistTableCatenateReverseRotateTransposeRazeMixSplitEncloseNestCut (K)PairLinkPartitioned EnclosePartition
Selection FirstPickTakeDropUniqueIdentityStopSelectReplicateExpandSet functions (IntersectionUnionWithout) ∙ Bracket indexingIndexCartesian ProductSort
Selector Index generatorGradeIndex OfInterval IndexIndicesDealPrefix and suffix vectors
Computational MatchNot MatchMembershipFindNub SieveEncodeDecodeMatrix InverseMatrix DivideFormatExecuteMaterialiseRange
Operators Monadic EachCommuteConstantReplicateExpandReduceWindowed ReduceScanOuter ProductKeyI-BeamSpawnFunction axisIdentity (Null, Ident)
Dyadic BindCompositions (Compose, Reverse Compose, Beside, Withe, Atop, Over) ∙ Inner ProductDeterminantPowerAtUnderRankDepthVariantStencilCutDirect definition (operator)Identity (Lev, Dex)
Quad names Index originComparison toleranceMigration levelAtomic vector