diff options
| author | Marshall Lochbaum <mwlochbaum@gmail.com> | 2020-12-20 15:04:05 -0500 |
|---|---|---|
| committer | Marshall Lochbaum <mwlochbaum@gmail.com> | 2020-12-20 15:04:05 -0500 |
| commit | 9c04cd5285572698f8393cb2f1110bb92b3ea5d3 (patch) | |
| tree | df321ed1390e01c3c664e7dc99f63cafdaf4c088 /docs | |
| parent | bcff427614bf4e7f377a1c90408c9b1c3f6d70b0 (diff) | |
Update transpose doc and add a more basic introduction
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/doc/transpose.html | 35 |
1 files changed, 28 insertions, 7 deletions
diff --git a/docs/doc/transpose.html b/docs/doc/transpose.html index ef7b4fcb..f86029a7 100644 --- a/docs/doc/transpose.html +++ b/docs/doc/transpose.html @@ -6,15 +6,36 @@ <div class="nav"><a href="https://github.com/mlochbaum/BQN">BQN</a> / <a href="../index.html">main</a> / <a href="index.html">doc</a></div> <h1 id="transpose">Transpose</h1> <p>As in APL, Transpose (<code><span class='Function'>⍉</span></code>) is a tool for rearranging the axes of an array. BQN's version is tweaked to align better with the leading axis model and make common operations easier.</p> +<h2 id="transpose-basics">Transpose basics</h2> +<p>The name for the primitive <code><span class='Function'>⍉</span></code> comes from the <a href="https://en.wikipedia.org/wiki/Transpose">Transpose</a> operation on matrices. Given a matrix as an array of rank 2, <code><span class='Function'>⍉</span></code> will transpose it:</p> +<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4oqiIG1hdCDihpAgMuKAvzMg4qWKIOKGlTYK4o2JIG1hdA==">↗️</a><pre> <span class='Function'>⊢</span> <span class='Value'>mat</span> <span class='Gets'>←</span> <span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>3</span> <span class='Function'>⥊</span> <span class='Function'>↕</span><span class='Number'>6</span> +┌─ +╵ 0 1 2 + 3 4 5 + ┘ + <span class='Function'>⍉</span> <span class='Value'>mat</span> +┌─ +╵ 0 3 + 1 4 + 2 5 + ┘ +</pre> +<p>Transpose is named this way because it exchanges the two axes of the matrix. Above you can see that while <code><span class='Value'>mat</span></code> has shape <code><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>3</span></code>, <code><span class='Function'>⍉</span><span class='Value'>mat</span></code> has shape <code><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>2</span></code>, and we can also check that the element at <a href="indices.html">index</a> <code><span class='Value'>i</span><span class='Ligature'>‿</span><span class='Value'>j</span></code> in <code><span class='Value'>mat</span></code> is the same as the one at <code><span class='Value'>j</span><span class='Ligature'>‿</span><span class='Value'>i</span></code> in <code><span class='Function'>⍉</span><span class='Value'>mat</span></code>:</p> +<a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=MeKAvzAg4oqRIG1hdAow4oC/MSDiipEg4o2JIG1hdA==">↗️</a><pre> <span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>0</span> <span class='Function'>⊑</span> <span class='Value'>mat</span> +3 + <span class='Number'>0</span><span class='Ligature'>‿</span><span class='Number'>1</span> <span class='Function'>⊑</span> <span class='Function'>⍉</span> <span class='Value'>mat</span> +3 +</pre> +<p>With two axes the only interesting operation of this sort is to swap them (and with one or zero axes there's nothing interesting to do, and <code><span class='Function'>⍉</span></code> just returns the argument array). But a BQN programmer may well want to work with higher-rank arrays—although such a programmer might call them "tensors"—and this means there are many more ways to rearrange the axes. Transpose extends to high-rank arrays to allow some useful special cases as well as completely general axis rearrangement, as described below.</p> <h2 id="monadic-transpose">Monadic Transpose</h2> -<p>Transposing a matrix exchanges its axes, mirroring it across the diagonal. APL extends the function to any rank by reversing all axes, but this generalization isn't very natural and is almost never used. The main reason for it is to maintain the equivalence <code><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>←→</span> <span class='Value'>a</span> <span class='Function'>MP</span><span class='Modifier2'>⌾</span><span class='Function'>⍉</span> <span class='Value'>b</span></code>, where <code><span class='Function'>MP</span> <span class='Gets'>←</span> <span class='Function'>+</span><span class='Modifier'>˝</span><span class='Modifier2'>∘</span><span class='Function'>×</span><span class='Modifier2'>⎉</span><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>∞</span></code> is the generalized matrix product. But even here APL's Transpose is suspect. It does much more work than it needs to, as we'll see.</p> +<p>APL extends matrix transposition to any rank by reversing all axes for its monadic <code><span class='Function'>⍉</span></code>, but this generalization isn't very natural and is almost never used. The main reason for it is to maintain the equivalence <code><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>←→</span> <span class='Value'>b</span> <span class='Function'>MP</span><span class='Modifier2'>⌾</span><span class='Function'>⍉</span> <span class='Value'>a</span></code>, where <code><span class='Function'>MP</span> <span class='Gets'>←</span> <span class='Function'>+</span><span class='Modifier'>˝</span><span class='Modifier2'>∘</span><span class='Function'>×</span><span class='Modifier2'>⎉</span><span class='Number'>1</span><span class='Ligature'>‿</span><span class='Number'>∞</span></code> is the generalized matrix product. But even here APL's Transpose is suspect. It does much more work than it needs to, as we'll see.</p> <p>BQN's transpose takes the first axis of its argument and moves it to the end.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omiIGEyMzQ1NiDihpAg4oaVMuKAvzPigL804oC/NeKAvzYK4omiIOKNiSBhMjM0NTY=">↗️</a><pre> <span class='Function'>≢</span> <span class='Value'>a23456</span> <span class='Gets'>←</span> <span class='Function'>↕</span><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>4</span><span class='Ligature'>‿</span><span class='Number'>5</span><span class='Ligature'>‿</span><span class='Number'>6</span> ⟨ 2 3 4 5 6 ⟩ <span class='Function'>≢</span> <span class='Function'>⍉</span> <span class='Value'>a23456</span> ⟨ 3 4 5 6 2 ⟩ </pre> -<p>On the argument's ravel, this looks like a simple 2-dimensional transpose: one axis is exchanged with a compound axis made up of the other axes. Here we transpose a rank 3 matrix:</p> +<p>In terms of the argument data as given by Deshape (<code><span class='Function'>⥊</span></code>), this looks like a simple 2-dimensional transpose: one axis is exchanged with a compound axis made up of the other axes. Here we transpose a rank 3 matrix:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=YTMyMiDihpAgM+KAvzLigL8y4qWK4oaVMTIK4omN4peLPOKfnOKNiSBhMzIy">↗️</a><pre> <span class='Value'>a322</span> <span class='Gets'>←</span> <span class='Number'>3</span><span class='Ligature'>‿</span><span class='Number'>2</span><span class='Ligature'>‿</span><span class='Number'>2</span><span class='Function'>⥊↕</span><span class='Number'>12</span> <span class='Function'>≍</span><span class='Modifier2'>○</span><span class='Function'><</span><span class='Modifier2'>⟜</span><span class='Function'>⍉</span> <span class='Value'>a322</span> ┌─ @@ -30,7 +51,7 @@ ┘ ┘ </pre> -<p>But, reading in ravel order, the argument and result have exactly the same element ordering as for the rank 2 matrix <code><span class='Function'>⥊</span><span class='Modifier'>˘</span> <span class='Value'>a322</span></code>:</p> +<p>But, ignoring the whitespace and going in reading order, the argument and result have exactly the same element ordering as for the rank 2 matrix <code><span class='Function'>⥊</span><span class='Modifier'>˘</span> <span class='Value'>a322</span></code>:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omN4peLPOKfnOKNiSDipYrLmCBhMzIy">↗️</a><pre> <span class='Function'>≍</span><span class='Modifier2'>○</span><span class='Function'><</span><span class='Modifier2'>⟜</span><span class='Function'>⍉</span> <span class='Function'>⥊</span><span class='Modifier'>˘</span> <span class='Value'>a322</span> ┌─ · ┌─ ┌─ @@ -41,13 +62,13 @@ ┘ ┘ </pre> -<p>To exchange multiple axes, use the Power modifier. Like Rotate, a negative power will move axes in the other direction. In particular, to move the last axis to the front, use Inverse (as you might expect, this exactly inverts <code><span class='Function'>⍉</span></code>).</p> +<p>To exchange multiple axes, use the Power modifier. A negative power moves axes in the other direction, just like how Rotate handles negative left arguments. In particular, to move the last axis to the front, use Undo (as you might expect, this exactly inverts <code><span class='Function'>⍉</span></code>).</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omiIOKNieKNnzMgYTIzNDU2CuKJoiDijYnigbwgYTIzNDU2">↗️</a><pre> <span class='Function'>≢</span> <span class='Function'>⍉</span><span class='Modifier2'>⍟</span><span class='Number'>3</span> <span class='Value'>a23456</span> ⟨ 5 6 2 3 4 ⟩ <span class='Function'>≢</span> <span class='Function'>⍉</span><span class='Modifier'>⁼</span> <span class='Value'>a23456</span> ⟨ 6 2 3 4 5 ⟩ </pre> -<p>In fact, we have <code><span class='Function'>≢⍉</span><span class='Modifier2'>⍟</span><span class='Value'>k</span> <span class='Value'>a</span> <span class='Gets'>←→</span> <span class='Value'>k</span><span class='Function'>⌽≢</span><span class='Value'>a</span></code> for any number <code><span class='Value'>k</span></code> and array <code><span class='Value'>a</span></code>.</p> +<p>In fact, we have <code><span class='Function'>≢⍉</span><span class='Modifier2'>⍟</span><span class='Value'>k</span> <span class='Value'>a</span> <span class='Gets'>←→</span> <span class='Value'>k</span><span class='Function'>⌽≢</span><span class='Value'>a</span></code> for any whole number <code><span class='Value'>k</span></code> and array <code><span class='Value'>a</span></code>.</p> <p>To move axes other than the first, use the Rank modifier in order to leave initial axes untouched. A rank of <code><span class='Value'>k</span><span class='Function'>></span><span class='Number'>0</span></code> transposes only the last <code><span class='Value'>k</span></code> axes while a rank of <code><span class='Value'>k</span><span class='Function'><</span><span class='Number'>0</span></code> ignores the first <code><span class='Function'>|</span><span class='Value'>k</span></code> axes.</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omiIOKNieKOiTMgYTIzNDU2">↗️</a><pre> <span class='Function'>≢</span> <span class='Function'>⍉</span><span class='Modifier2'>⎉</span><span class='Number'>3</span> <span class='Value'>a23456</span> ⟨ 2 3 5 6 4 ⟩ @@ -57,9 +78,9 @@ ⟨ 2 6 3 4 5 ⟩ </pre> <p>Using these forms, we can state BQN's generalized matrix product swapping rule:</p> -<pre><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>←→</span> <span class='Function'>⍉</span><span class='Modifier2'>⍟</span><span class='Paren'>(</span><span class='Function'>=</span><span class='Value'>a</span><span class='Paren'>)</span> <span class='Value'>a</span> <span class='Function'>⍉</span><span class='Modifier'>⁼</span><span class='Modifier2'>⊸</span><span class='Function'>MP</span><span class='Modifier2'>⟜</span><span class='Function'>⍉</span> <span class='Value'>b</span> +<pre><span class='Value'>a</span> <span class='Function'>MP</span> <span class='Value'>b</span> <span class='Gets'>←→</span> <span class='Function'>⍉</span><span class='Modifier2'>⍟</span><span class='Paren'>(</span><span class='Number'>1</span><span class='Function'>-=</span><span class='Value'>a</span><span class='Paren'>)</span> <span class='Paren'>(</span><span class='Function'>⍉</span><span class='Value'>b</span><span class='Paren'>)</span> <span class='Function'>MP</span> <span class='Paren'>(</span><span class='Function'>⍉</span><span class='Modifier'>⁼</span><span class='Value'>a</span><span class='Paren'>)</span> </pre> -<p>Certainly not as concise as APL's version, but not a horror either. BQN's rule is actually more parsimonious in that it only performs the axis exchanges necessary for the computation: it moves the two axes that will be paired with the matrix product into place before the product, and directly exchanges all axes afterwards. Each of these steps is equivalent in terms of data movement to a matrix transpose, the simplest nontrivial transpose to perform. Also remember that for two-dimensional matrices both kinds of transposition are the same, and APL's rule holds in BQN.</p> +<p>Certainly not as concise as APL's version, but not a horror either. BQN's rule is actually more parsimonious in that it only performs the axis exchanges necessary for the computation: it moves the two axes that will be paired with the matrix product into place before the product, and directly exchanges all axes afterwards. Each of these steps is equivalent in terms of data movement to a matrix transpose, the simplest nontrivial transpose to perform. Also remember that for two-dimensional matrices both kinds of transposition are the same, so that APL's simpler rule <code><span class='Function'>MP</span> <span class='Function'>≡</span> <span class='Function'>MP</span><span class='Modifier2'>⌾</span><span class='Function'>⍉</span><span class='Modifier'>˜</span></code> holds in BQN.</p> <p>Axis permutations of the types we've shown generate the complete permutation group on any number of axes, so you could produce any transposition you want with the right sequence of monadic transpositions with Rank. However, this can be unintuitive and tedious. What if you want to transpose the first three axes, leaving the rest alone? With monadic Transpose you have to send some axes to the end, then bring them back to the beginning. For example [following four or five failed tries]:</p> <a class="replLink" title="Open in the REPL" target="_blank" href="https://mlochbaum.github.io/BQN/try.html#code=4omiIOKNieKBvOKOicKvMiDijYkgYTIzNDU2ICAjIFJlc3RyaWN0IFRyYW5zcG9zZSB0byB0aGUgZmlyc3QgdGhyZWUgYXhlcw==">↗️</a><pre> <span class='Function'>≢</span> <span class='Function'>⍉</span><span class='Modifier'>⁼</span><span class='Modifier2'>⎉</span><span class='Number'>¯2</span> <span class='Function'>⍉</span> <span class='Value'>a23456</span> <span class='Comment'># Restrict Transpose to the first three axes </span>⟨ 3 4 2 5 6 ⟩ |
