3,038
edits
m (→Examples) |
m (→Differences between dialects: ISO/IEC 13751:2001 link) |
||
(28 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
{{Built-in|Inner Product|<nowiki>.</nowiki>}} | {{Built-in|Inner Product|<nowiki>.</nowiki>}} is a [[dyadic operator]] that produces a [[dyadic function]] when applied with two dyadic functions. It's a generalisation of the [[wikipedia:Matrix multiplication|matrix product]], allowing not just addition-multiplication, but any [[dyadic function]]s given as [[operand]]s. | ||
== Description == | |||
For each rank-1 cell in the arguments, inner product applies <syntaxhighlight lang=apl inline>⍵⍵</syntaxhighlight> between the two and then applies <syntaxhighlight lang=apl inline>⍺⍺⌿</syntaxhighlight> to the results of those invocations. If the arguments themselves are simply vectors, there is only one rank-1 cell in each argument, so this results in the following application pattern: | |||
<syntaxhighlight lang=apl> | |||
1 2 3 4 F.G 5 6 7 8 | |||
(1 G 5) F (2 G 6) F (3 G 7) F (4 G 8) | |||
</syntaxhighlight> | |||
The second line is an illustration of how the first will be evaluated. Note that this is precisely the [[wikipedia:Dot product|vector dot product]] when used as <syntaxhighlight lang=apl inline>+.×</syntaxhighlight>. (This simple invocation with vector arguments will be referred to as the "vector inner product" below, but it is just a simple case of the general inner product.) | |||
For matrix arguments, there may be more than one rank-1 cell. Considering the case of rank-2 matrices as arguments, if there are N rows in <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> and M columns in <syntaxhighlight lang=apl inline>⍵</syntaxhighlight>, the result will be a matrix with N rows and M columns. Each row of the resulting matrix will correspond to the elements of that same row in <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> being paired up with elements in a column of <syntaxhighlight lang=apl inline>⍵</syntaxhighlight>. Likewise, each column of the resulting matrix will correspond to the elements of that same column of <syntaxhighlight lang=apl inline>⍵</syntaxhighlight> being paired up with elements in a row of <syntaxhighlight lang=apl inline>⍺</syntaxhighlight>. '''Important: This means that the inner product will be applied for each ''row'' of <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> but each ''column'' of <syntaxhighlight lang=apl inline>⍵</syntaxhighlight>!''' | |||
In practice, this means that the upper left element of the resulting matrix will correspond to performing the vector inner product on the first row of <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> and the first column of <syntaxhighlight lang=apl inline>⍵</syntaxhighlight>, the upper right element will correspond to performing the vector inner product on the first row of <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> and the last column of <syntaxhighlight lang=apl inline>⍵</syntaxhighlight>, the lower right element will correspond to the vector inner product on the last row of <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> and the last column of <syntaxhighlight lang=apl inline>⍵</syntaxhighlight>, and so on and so on. Pictorially, then, for a 2x2 result we can represent the resulting matrix as: | |||
<syntaxhighlight lang=apl> | |||
┌──────────────────────────────┬──────────────────────────────┐ | |||
│(Row 1 of ⍺) F.G (Col. 1 of ⍵)│(Row 1 of ⍺) F.G (Col. 2 of ⍵)│ | |||
├──────────────────────────────┼──────────────────────────────┤ | |||
│(Row 2 of ⍺) F.G (Col. 1 of ⍵)│(Row 2 of ⍺) F.G (Col. 2 of ⍵)│ | |||
└──────────────────────────────┴──────────────────────────────┘ | |||
</syntaxhighlight> | |||
Note how the columns of <syntaxhighlight lang=apl inline>⍵</syntaxhighlight> align with the columns of the matrix, and the rows of <syntaxhighlight lang=apl inline>⍺</syntaxhighlight> align with the rows of the matrix. | |||
The concept readily generalizes to matrices of higher rank. | |||
== Examples == | == Examples == | ||
< | <syntaxhighlight lang=apl> | ||
x ← 1 2 3 | x ← 1 2 3 | ||
y ← 4 5 6 | y ← 4 5 6 | ||
x ,.( | x ,.(⊂,) y ⍝ visualizing of pairing | ||
┌─────────────┐ | ┌─────────────┐ | ||
│┌───┬───┬───┐│ | │┌───┬───┬───┐│ | ||
Line 11: | Line 36: | ||
│└───┴───┴───┘│ | │└───┴───┴───┘│ | ||
└─────────────┘ | └─────────────┘ | ||
x {⊂⍺,'+',⍵}.{⊂⍺,'×',⍵} y ⍝ visualizing function application in matrix multiplication | |||
┌───────────────────────────┐ | |||
│┌─────────────────────────┐│ | |||
││┌─────┬─┬───────────────┐││ | |||
│││1 × 4│+│┌─────┬─┬─────┐│││ | |||
│││ │ ││2 × 5│+│3 × 6││││ | |||
│││ │ │└─────┴─┴─────┘│││ | |||
││└─────┴─┴───────────────┘││ | |||
│└─────────────────────────┘│ | |||
└───────────────────────────┘ | |||
x+.×y ⍝ matrix multiplication | x+.×y ⍝ matrix multiplication | ||
32 | 32 | ||
</ | </syntaxhighlight> | ||
The [[shape]]s of the arguments must be compatible with each other: The last [[axis]] of the left argument must have the same length as the first axis of the right argument, or formally, for <syntaxhighlight lang=apl inline>X f.g Y</syntaxhighlight> it must be that <syntaxhighlight lang=apl inline>(¯1↑⍴X)≡(1↑⍴Y)</syntaxhighlight>. Although this rule differs from [[conformability]], the arguments may also be subject to [[scalar extension|scalar]] or [[singleton extension]]. The shape of the result is <syntaxhighlight lang=apl inline>(¯1↓⍴X),(1↓⍴Y)</syntaxhighlight>. | |||
For example, when applying inner | For example, when applying inner product on two [[matrix|matrices]], the number of columns in the left array must match with number of rows in the right array, otherwise we will get an error. | ||
< | <syntaxhighlight lang=apl> | ||
⎕ ← x ← 2 3⍴⍳10 | ⎕ ← x ← 2 3⍴⍳10 | ||
1 2 3 | 1 2 3 | ||
Line 26: | Line 61: | ||
3 4 | 3 4 | ||
5 6 | 5 6 | ||
7 8 | 7 8 | ||
x+.×y | x+.×y | ||
LENGTH ERROR | LENGTH ERROR | ||
Line 35: | Line 70: | ||
22 28 | 22 28 | ||
49 64 | 49 64 | ||
</ | </syntaxhighlight> | ||
== History == | |||
Inner product appeared in early [[Iverson Notation]] as <math>^f_g</math> and applied even to non-[[scalar function]]s, like [[Compress]], Iverson bringing:<ref>[[Ken Iverson]]. [[A Programming Language]]. §1.11 ''The language''.</ref> | |||
:<math> | |||
\begin{align} | |||
\text{For example, if}\\ | |||
\boldsymbol{A}&=\begin{pmatrix} | |||
1&3&2&0\\ | |||
2&1&0&1\\ | |||
4&0&0&2\\ | |||
\end{pmatrix} | |||
\qquad\text{and}\qquad | |||
\boldsymbol{B}=\begin{pmatrix} | |||
4&1\\ | |||
0&3\\ | |||
0&2\\ | |||
2&0\\ | |||
\end{pmatrix}\\ | |||
\text{then}\qquad\boldsymbol{A}\;^+_\times\,\boldsymbol{B}&=\begin{pmatrix} | |||
4&14\\ | |||
10&5\\ | |||
20&4\\ | |||
\end{pmatrix}, | |||
\quad\boldsymbol{A}\;^\land_=\,\boldsymbol{B}=\begin{pmatrix} | |||
0&1\\ | |||
0&0\\ | |||
1&0\\ | |||
\end{pmatrix}\text{,}\\ | |||
\boldsymbol{A}\;^\lor_\neq\;\boldsymbol{B}&=\begin{pmatrix} | |||
1&0\\ | |||
1&1\\ | |||
0&1\\ | |||
\end{pmatrix}, | |||
\qquad\text{and}\qquad(\boldsymbol{A}\neq0)\;^+_{\,/}\,\boldsymbol{B}=\begin{pmatrix} | |||
4&6\\ | |||
6&4\\ | |||
6&1\\ | |||
\end{pmatrix}\text{.} | |||
\end{align} | |||
</math> | |||
When the inner product notation was linearised (made to fit on a single line of code) the [[glyph]] <syntaxhighlight lang=apl inline>.</syntaxhighlight> was chosed to denote what was previously indicated by positioning the two [[operand]]s vertically aligned. Thus, the above correspond to the following modern APL: | |||
<syntaxhighlight lang=apl> | |||
⍝ For example, if | |||
A←3 4⍴1 3 2 0 2 1 0 1 4 0 0 2 | |||
B←4 2⍴4 1 0 3 0 2 2 0 | |||
⍝ then | |||
A +.× B | |||
4 14 | |||
10 5 | |||
20 4 | |||
A ∧.= B | |||
0 1 | |||
0 0 | |||
1 0 | |||
A ∨.≠ B | |||
1 0 | |||
1 1 | |||
0 1 | |||
(A ≠ 0) +./ B | |||
4 6 | |||
6 4 | |||
6 1 | |||
</syntaxhighlight> | |||
Note that some dialects implement [[Compress]] (<syntaxhighlight lang=apl inline>/</syntaxhighlight>) as a [[monadic operator]] rather than as a function, which means it cannot be an operand in the inner product. Instead, a cover function is necessary: | |||
<syntaxhighlight lang=apl> | |||
∇z←a Compress b | |||
z←a/b | |||
∇ | |||
</syntaxhighlight> | |||
== Differences between dialects == | |||
Implementations differ on the exact behaviour of inner product when the right operand is not a [[scalar function]]. Page 121 of the [[ISO/IEC 13751:2001]] standard specifies that <syntaxhighlight lang=apl inline>X f.g Y</syntaxhighlight> is equivalent to <syntaxhighlight lang=apl>f/¨ (⊂[⍴⍴x]x)∘.g ⊂[1]y</syntaxhighlight> (assuming [[index origin]] 1). This is indeed what [[APL2]], [[APLX]], [[APL+Win]], and [[ngn/apl]] follow, while [[Dyalog APL]], [[NARS2000]] and [[GNU APL]] differ as described by [[Roger Hui]]:<ref>[[Roger Hui]]. ''inner product''. Internal Dyalog email. 24 July 2020.</ref> | |||
<blockquote> | |||
The following dop models inner product in Dyalog APL, with caveats. If you find a case where <syntaxhighlight lang=apl inline>f.g</syntaxhighlight> differs from <syntaxhighlight lang=apl inline>f IP g</syntaxhighlight>, not covered by the caveats, I'd be interested. | |||
<syntaxhighlight lang=apl> | |||
IP←{ | |||
assert((⊃⌽⍴⍺)≡≢⍵)∨(1=×/⍴⍺)∨1=×/⍴⍵: | |||
⊃⍤0 ⊢ (↓⍺) ∘.(⍺⍺/⍵⍵¨) ↓(¯1⌽⍳⍴⍴⍵)⍉⍵ | |||
} | |||
assert←{⍺←'assertion failure' ⋄ 0∊⍵:⍺ ⎕SIGNAL 8 ⋄ shy←0} | |||
</syntaxhighlight> | |||
(Explanation: What's with the <syntaxhighlight lang=apl inline>⊃⍤0</syntaxhighlight> in <syntaxhighlight lang=apl inline>IP</syntaxhighlight>? It's because <syntaxhighlight lang=apl inline>∘.f</syntaxhighlight> has an implicit each, applying <syntaxhighlight lang=apl inline>⊂</syntaxhighlight> to each item of its result. But the <syntaxhighlight lang=apl inline>⍺⍺/</syntaxhighlight> in <syntaxhighlight lang=apl inline>(⍺⍺/⍵⍵¨)</syntaxhighlight> also has an implicit each. So the <syntaxhighlight lang=apl inline>⊃⍤0</syntaxhighlight> gets rid of one of those encloses.) | |||
Caveats: | |||
* You can not use the hybrid <syntaxhighlight lang=apl inline>/</syntaxhighlight> directly as an operand as it runs afoul of the parser in weird and wonderful ways. Instead, you have to use <syntaxhighlight lang=apl inline>{⍺/⍵}</syntaxhighlight>. The same goes for <syntaxhighlight lang=apl inline>\</syntaxhighlight> and <syntaxhighlight lang=apl inline>{⍺\⍵}</syntaxhighlight> I guess. | |||
* It differs from ISO/IEC 13751:2001(E) in using <syntaxhighlight lang=apl inline>⍵⍵¨</syntaxhighlight> instead of just <syntaxhighlight lang=apl inline>⍵⍵</syntaxhighlight> in the central key expression (i.e. <syntaxhighlight lang=apl inline>(⍺⍺/⍵⍵¨)</syntaxhighlight> instead of <syntaxhighlight lang=apl inline>(⍺⍺/⍵⍵)</syntaxhighlight>). So does the primitive <syntaxhighlight lang=apl inline>f.g</syntaxhighlight>. | |||
* It differs from ISO/IEC 13751:2001(E) in doing full-blown single extension instead of just scalar and 1-element vector extension (as in APL2). So does the primitive <syntaxhighlight lang=apl inline>f.g</syntaxhighlight>. e.g.<syntaxhighlight lang=apl> | |||
(3 4⍴5)+.×1 1 1 1⍴6 ⍝ works in Dyalog, not in ISO or APL2</syntaxhighlight> | |||
* It differs from NARS2000 or APL\360 in not permitting unit axis extension. So does the primitive <syntaxhighlight lang=apl inline>f.g</syntaxhighlight>. e.g.<syntaxhighlight lang=apl> | |||
(3 4⍴5)+.×1 5⍴6 ⍝ works in NARS2000 or APL\360, not in Dyalog APL</syntaxhighlight> | |||
</blockquote> | |||
The <syntaxhighlight lang=apl>⊃⍤0⊢(↓⍺)∘.(⍺⍺/⍵⍵¨)↓(¯1⌽⍳⍴⍴⍵)⍉⍵</syntaxhighlight> line of IP above can be rewritten as <syntaxhighlight lang=apl>⍺(⍺⍺⌿⍵⍵¨⍤¯1)⍤1 99⊢⍵</syntaxhighlight> which uses the more efficient [[item]]-at-a-time algorithm (rather than row-by-column). The ISO/IEC 13751:2001(E) inner product, conversely, can only be calculated row-by-column, as computing the results one item (of the right argument) at a time relies on each application of the right operand being done between two scalars and producing a scalar result—that is, on the [[Each]] operator being applied to the right operand. | |||
Some implementations extend the inner product by implementing Iverson's monadic variant<ref>[[Ken Iverson|K.E. Iverson]]. [https://www.jsoftware.com/papers/satn42.htm Determinant-Like Functions Produced by the Dot Operator.] SHARP APL Technical Note 42. 1982-04-01.</ref>, which takes a single argument and performs the operation of computing the alternant, as modelled by [https://dfns.dyalog.com/n_alt.htm dfns.alt]. | |||
== External links == | |||
=== Documentation === | |||
* [https://help.dyalog.com/latest/#Language/Primitive%20Operators/Inner%20Product.htm Dyalog] | |||
* [https://microapl.com/apl_help/ch_020_020_880.htm APLX] | |||
* J [https://www.jsoftware.com/help/dictionary/d300.htm Dictionary], [https://code.jsoftware.com/wiki/Vocabulary/dot#dyadic NuVoc] | |||
=== Publications === | |||
* [https://www.jsoftware.com/papers/innerproduct/ip1.htm Inner Product: An Old/New Problem] by [[Roger Hui]] | |||
=== Discussion of differences between dialects === | |||
* [https://groups.google.com/g/comp.lang.apl/c/23LrwRZKmPs Dyalog / APL2000 discrepancy] (Google Groups) | |||
* [https://lists.gnu.org/archive/html/bug-apl/2016-07/msg00020.html multiple inner product] (GNU APL mailing list) | |||
* [https://lists.gnu.org/archive/html/bug-apl/2018-05/msg00003.html an other inner product ,., bug] (GNU APL mailing list) | |||
== References == | |||
<references/> | |||
{{APL built-ins}}[[Category:Primitive operators]] |