matrix-lens
Optics for the matrix
package.
That’s how it is with people. Nobody cares how it works as long as it works.
Sparked by this reddit post.
Examples
The examples below make use of the following three matrices:
exampleInt :: Matrix Int
exampleInt = Matrix.fromLists
[ [1, 2, 3]
, [4, 5, 6]
, [7, 8, 9]
]
exampleInvertible :: Matrix (Ratio Int)
exampleInvertible = Matrix.fromLists
[ [ -3, 1 ]
, [ 5, 0 ]
]
exampleNotSquare :: Matrix Int
exampleNotSquare = Matrix.fromLists
[ [10, 20, 30]
, [40, 50, 60]
, [70, 80, 90]
, [100, 110, 120]
]
Accessing individual elements:
λ> exampleNotSquare ^. elemAt (4, 3)
120
λ> exampleInt & elemAt (2, 2) *~ 10
┌ ┐
│ 1 2 3 │
│ 4 50 6 │
│ 7 8 9 │
└ ┘
Accessing individual columns:
λ> exampleInt ^. col 1
[1,4,7]
λ> exampleInt & col 2 . each *~ 10
┌ ┐
│ 1 20 3 │
│ 4 50 6 │
│ 7 80 9 │
└ ┘
Accessing individual rows:
λ> exampleInt ^. row 1
[1,2,3]
λ> exampleInt & row 2 . each *~ 100
┌ ┐
│ 1 2 3 │
│ 400 500 600 │
│ 7 8 9 │
└ ┘
Manipulating all columns as a list:
λ> exampleInt ^. cols
[[1,4,7],[2,5,8],[3,6,9]]
λ> exampleInt & cols %~ reverse
┌ ┐
│ 3 2 1 │
│ 6 5 4 │
│ 9 8 7 │
└ ┘
Accessing all rows as a list:
λ> exampleInt ^. rows
[[1,2,3],[4,5,6],[7,8,9]]
λ> exampleInt & rows %~ map reverse
┌ ┐
│ 3 2 1 │
│ 6 5 4 │
│ 9 8 7 │
└ ┘
λ> exampleInt & partsOf (dropping 1 (rows . each)) %~ reverse
┌ ┐
│ 1 2 3 │
│ 7 8 9 │
│ 4 5 6 │
└ ┘
In addition to the above there are also switching
and sliding
Isos for both
rows and columns which allow you to swap two arbitrary rows or columns or slide
a row or column through the matrix to a different row or column (moving all
intervening rows or columns over in the direction of the source row or column):
λ> exampleNotSquare ^. switchingRows 1 4
┌ ┐
│ 100 110 120 │
│ 40 50 60 │
│ 70 80 90 │
│ 10 20 30 │
└ ┘
λ> exampleNotSquare ^. slidingRows 1 4
┌ ┐
│ 40 50 60 │
│ 70 80 90 │
│ 100 110 120 │
│ 10 20 30 │
└ ┘
..and similary for switchingCols
and switchingRows
.
An Iso exists for accessing the matrix with a given row scaled:
λ> exampleInt ^. scalingRow 1 10
┌ ┐
│ 10 20 30 │
│ 4 5 6 │
│ 7 8 9 │
└ ┘
λ> exampleInt & scalingRow 1 10 . flattened *~ 2
┌ ┐
│ -200 -400 -600 │
│ 8 10 12 │
│ 14 16 18 │
└ ┘
Any valid sub matrix can be accessed via the sub
lens:
λ> exampleNotSquare ^. sub (2, 1) (3, 2)
┌ ┐
│ 40 50 │
│ 70 80 │
└ ┘
λ> exampleNotSquare & sub (2, 1) (3, 2) . rows %~ reverse
┌ ┐
│ 10 20 30 │
│ 70 80 60 │
│ 40 50 90 │
│ 100 110 120 │
└ ┘
The transposition of the matrix can be accessed via the transposed
Iso:
λ> exampleInt ^. transposed
┌ ┐
│ 1 4 7 │
│ 2 5 8 │
│ 3 6 9 │
└ ┘
λ> exampleInt & transposed . taking 4 flattened *~ 10
┌ ┐
│ 10 20 3 │
│ 40 5 6 │
│ 70 8 9 │
└ ┘
You can also traverse the flattened
matrix:
λ> exampleInt ^.. flattened
[1,2,3,4,5,6,7,8,9]
which is more useful for making modifications:
λ> exampleInt & flattened . filtered even *~ 10
┌ ┐
│ 1 20 3 │
│ 40 5 60 │
│ 7 80 9 │
└ ┘
λ> exampleInt & dropping 4 flattened *~ 10
┌ ┐
│ 1 2 3 │
│ 4 50 60 │
│ 70 80 90 │
└ ┘
Accessing the diagonal:
λ> exampleInt ^. diag
[1,5,9]
λ> exampleInt & diag %~ reverse
┌ ┐
│ 9 2 3 │
│ 4 5 6 │
│ 7 8 1 │
└ ┘
λ> exampleInt & diag . each *~ 10
┌ ┐
│ 10 2 3 │
│ 4 50 6 │
│ 7 8 90 │
└ ┘
Accessing inverse matrix is possible via the inverted
optic. Since not all
matrices have inverses inverted
is a prism:
λ> exampleInvertible ^? inverted
Just ┌ ┐
│ 0 % 1 1 % 5 │
│ 1 % 1 3 % 5 │
└ ┘
λ> exampleInvertible & inverted . flattened *~ 2
┌ ┐
│ (-3) % 2 1 % 2 │
│ 5 % 2 0 % 1 │
└ ┘
Minor matrices can be accessed by specifying the (r, c) to be removed:
λ> exampleInt ^. minor (1, 2)
┌ ┐
│ 4 6 │
│ 7 9 │
└ ┘
λ> exampleInt & minor (1, 2) . flattened *~ 10
┌ ┐
│ 1 2 3 │
│ 40 5 60 │
│ 70 8 90 │
└ ┘
An Iso exists for accessing a scaled version of a matrix:
λ> exampleInt ^. scaled 10
┌ ┐
│ 10 20 30 │
│ 40 50 60 │
│ 70 80 90 │
└ ┘
λ> exampleInt & minor (1, 1) . scaled 10 . flattened +~ 1
┌ ┐
│ 1 2 3 │
│ 4 -510 -610 │
│ 7 -810 -910 │
└ ┘
Getters for the matrix determinant and size are also provided:
λ> exampleInt ^. determinant
Just 0
λ> exampleInvertible ^. determinant
Just ((-5) % 1)
λ> exampleNotSquare ^. determinant
Nothing
λ> exampleInt ^. size
(3,3)
λ> exampleInvertible ^. size
(2,2)
λ> exampleNotSquare ^. size
(4,3)