From fd6d55f09de0701197e954002d0b6ae5a2af763e Mon Sep 17 00:00:00 2001 From: Kiana Sheibani Date: Mon, 6 May 2024 02:35:07 -0400 Subject: [PATCH] Update guide --- docs/DataTypes.md | 6 +-- docs/Operations.md | 21 +++++--- docs/VectorsMatrices.md | 115 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 10 deletions(-) diff --git a/docs/DataTypes.md b/docs/DataTypes.md index a2d8d63..a083eff 100644 --- a/docs/DataTypes.md +++ b/docs/DataTypes.md @@ -79,8 +79,6 @@ Vector n a = Array [n] a A vector's type signature and stored data is effectively identical to that of the standard library type `Vect`, whose elements are confusingly also called "vectors"; we often refer to those as "vects" to differentiate. -Indices are typically written as lists of integers, but for vectors it is occasionally acceptable to write the single index number without putting it inside a list. This is mostly the case for indexing, where each indexing function has an alternate definition specifically for vectors. - ### Matrices As mentioned before, a matrix is a rank-2 array: @@ -145,7 +143,7 @@ The `TransType` value is obtained by prepending a capital T to these names. For #### The Point Type -Transforms behave differently from regular matrices when applied to a vector. When a non-linear transform is used, the transform is first linearized, so that vectors only have linear transformations applied to them. This is not a bug. +Transforms behave differently from regular matrices when applied to a vector. When a non-linear transform is used, the transform is first linearized, so that vectors only have linear transformations applied to them. **This is not a bug!** In order to properly apply these transforms, the `Point` type must be used, which is a wrapper around the `Vector` type that supports these transforms. This separation between points and vectors is intended to make working with affine transformations more convenient, as it mirrors the separation between points and vectors in affine algebra. @@ -153,4 +151,6 @@ In order to properly apply these transforms, the `Point` type must be used, whic The type `Permutation n` represents a permutation of `n` elements. Permutations are mostly used internally for various algorithms, but they are also an input in various operations, such as those that permute the axes of an array. +Permutations can be composed using `(*.)`, and a permutation can be converted into a matrix using `permuteM`. + [Contents](Intro.md) | [Next](Operations.md) diff --git a/docs/Operations.md b/docs/Operations.md index 774b86b..55f131a 100644 --- a/docs/Operations.md +++ b/docs/Operations.md @@ -1,5 +1,10 @@ # Basic Operations on Arrays +> [!WARNING] +> Arrays and their associated functions are not intended to be evaluated at compile-time. If you try to compute an array in the REPL, you will not get the output you expect! +> +> If you really need to use the REPL to test array code, use `:exec`. + ## Constructing Arrays The most important array constructor is `array`, which returns an array of the specified values: @@ -27,7 +32,7 @@ ones [2, 2, 3] There are a few simple functions for accessing basic properties of arrays: `shape` and `rank`, which are self-explanatory, and `size`, which returns the total number of elements in the array. -The `shape` accessor is sufficient for most uses, but it can cause problems with the type-checker, as for an array `arr : Array s a` the type checker does not know that `shape arr` and `s` are equal. To solve this problem, a view for accessing the shape is provided: +The `shape` accessor is sufficient for most uses, but it can cause problems with the type-checker, as for an array `arr : Array s a` the type checker does not know that `shape arr` and `s` are equal. To solve this problem, there is a view: ```idris example {s} arr with (viewShape arr) @@ -62,7 +67,7 @@ Not all combinations of these categories are defined by the library. Here are th | **Update** | `indexUpdate` | `indexUpdateRange` | `indexUpdateNB` | | | | | **Set** | `indexSet` | `indexSetRange` | `indexSetNB` | | | | -The accessor functions have operator forms for convenience, also specified within the table. +The accessor functions have operator forms for convenience. ### Specifying Coordinates @@ -77,7 +82,7 @@ arr !! [1, 0] With ranged indexing, a sub-array of the original array is accessed or modified. This sub-array is given by a list of _range specifiers_, one for each axis, which can be one of the following: -- `Bounds x y` - Every index from `x` to `y` +- `Bounds x y` - Every index from `x` (inclusive) to `y` (exclusive) - `StartBound x` - Every index from `x` to the end of the axis - `EndBound y` - Every index from the start of the axis to `y` - `All` - Every index in the axis @@ -117,7 +122,7 @@ When folding or traversing the elements of an array, these elements are ordered ### Concatenation and Stacking -Two arrays can be concatenated along an axis, so long as all other axes have the same dimensions. Two matrices being concatenated along the row axis requires that they must have the same number of columns. +Two arrays can be concatenated along an axis (`concat`), so long as all other axes have the same dimensions. Two matrices being concatenated along the row axis requires that they must have the same number of columns. ```idris -- 0 is the first axis i.e. the row axis @@ -128,7 +133,7 @@ concat 0 (matrix [[1, 2], [3, 4]]) (matrix [[5, 6], [7, 8]]) [7, 8]] ``` -Stacking is similar to concatenation, but slightly different. Stacking combines arrays with the exact same shape into a single array that is one rank higher. For example, vectors can be stacked along the row axis to obtain a matrix whose rows are the original vectors. +Stacking (`stack`) is similar to concatenation, but slightly different. Stacking combines arrays with the exact same shape into a single array that is one rank higher. For example, vectors can be stacked along the row axis to obtain a matrix whose rows are the original vectors. ```idris stack 0 [vector [1, 2], vector [3, 4]] @@ -149,7 +154,7 @@ reshape [3, 2] (vector [1, 2, 3, 4, 5, 6]) [5, 6]] ``` -Arrays can also be resized, which changes their shape while keeping every element at the same index. A default element must be provided to fill any indices that did not exist in the original array. +Arrays can also be resized with `resize`, which changes their shape while keeping every element at the same index. A default element must be provided to fill any indices that did not exist in the original array. ```idris resize [2, 4] 10 (matrix [[1, 2], @@ -163,8 +168,10 @@ Instead of the `resize` function, one can also use the `resizeLTE` function, whi ### Transpose -The `transpose` function reverses the axis order of an array. For matrices, this corresponds to the usual definition of switching rows and columns. There is also a postfix form `(.T)`. +The `transpose` function reverses the axis order of an array: For `arr : Array [3,2,4] Int`, we have `transpose arr : Array [4,2,3] Int`. For matrices, this corresponds to the usual definition of switching rows and columns. There is also a postfix form `(.T)`. For more fine-grained control when rearranging arrays, there are the `swapAxes` and `permuteAxes` functions, where the first swaps only two axes and the second takes an arbitrary [permutation](DataTypes.md#Permutations). There are also `swapInAxis` and `permuteInAxis`, which permute inside an axis, e.g. swapping rows or columns in a matrix. +Like with concatenation and stacking, the swap and permute functions have forms specific to vectors and matrices: `swapCoords` and `permuteCoords` for vectors, and `swapRows`, `permuteRows`, `swapColumns`, and `permuteColumns` for matrices. + [Previous](DataTypes.md) | [Contents](Intro.md) | [Next](VectorsMatrices.md) diff --git a/docs/VectorsMatrices.md b/docs/VectorsMatrices.md index c49de2f..b92d17e 100644 --- a/docs/VectorsMatrices.md +++ b/docs/VectorsMatrices.md @@ -1,4 +1,119 @@ # Working with Vectors and Matrices +As linear algebra is one of the main concerns of NumIdr, most of its provided functions are dedicated to vectors (rank-1 arrays) and matrices (rank-2 arrays). + +## The Generalized Multiplication Operator + +A linear algebra library wouldn't be very useful without matrix multiplication! While Idris's standard `(*)` operator would be a natural choice for this, the `Num` interface only allows for homogeneous multiplication, in which the inputs and output are all of the same type. To get around this, `(*)` is used for element-wise multiplication (a.k.a. the Hadamard product), and NumIdr defines a new interface `Mult`: + +```idris +interface Mult a b c where + (*.) : a -> b -> c + +-- Synonym for homogeneous cases: +Mult' : Type -> Type +Mult' a = Mult a a a +``` + +The generalized multiplication operator `(*.)` covers matrix multiplication, scalar-vector multiplication, and any other operation that's vaguely multiplication-like. + +## Vectors + +### Algebraic Operations + +Vectors can be added together with `(+)`, which performs element-wise addition. Scalar-vector multiplication is done with the generalized multiplication operator `(*.)`. + +```idris +2 *. (vector [1, 1] + vector [2, 3]) + == vector [6, 8] +``` + +A few other basic linear algebra operations are available: + +- `dot`, The dot product +- `cross`, The cross product +- `perp`, The perpendicular product (sometimes called the 2D cross product) +- `triple`, The scalar triple product + +### Indexing + +NumIdr provides special versions of `index` and `indexNB` and their infix forms `(!!)` and `(!?)` for use with vectors. These take a single numeric index instead of a list. + +```idris +Vector.index 2 v == index [2] v + +v !! 2 == v !! [2] +``` + +For convenience, when working with two- or three-dimensional vectors, there are postfix accessors `(.x)`, `(.y)`, and `(.z)`: + +```idris +v = vector [5, 6, 2] + +v.x == 5 +v.y == 6 +v.z == 2 +``` + +### Other Operations + +- `toVect` - Convert a vector into a `Vect` +- `dim` - Returns the vector's length +- `(++)` - Concatenate two vectors + +## Matrices + +### Arithmetic Operations + +Like vectors, matrices can be added together using `(+)`. Matrix multiplication, as well as matrix-vector and matrix-scalar multiplication, are performed using `(*.)`. + +For the purposes of working with matrices and matrix-like objects, the sub-interfaces `MultMonoid` and `MultGroup` are defined: + +```idris +interface Mult' a => MultMonoid a where + identity : a + +interface MultMonoid a => MultGroup a where + inverse : a -> a +``` + +The `identity` function returns an identity matrix, and `inverse` calculates a matrix's inverse. Note that `inverse` cannot tell you if an inverse of your matrix does not exist; if you want to handle that case, use `tryInverse` instead. + +```idris +tryInverse : FieldCmp a => Matrix' n a -> Maybe (Matrix' n a) +``` + +#### LU and LUP Decomposition + +The functions `decompLU` and `decompLUP` compute LU and LUP decomposition on a matrix. + +```idris +decompLU : Field a => (mat : Matrix m n a) -> Maybe (DecompLU mat) + +decompLUP : FieldCmp a => (mat : Matrix m n a) -> DecompLUP mat +``` + +`DecompLU` and `DecompLUP` are record types holding the results of the corresponding decomposition. The accessors `lower`, `upper` and `permute` can be applied to get each component of the decomposition; `lower` and `upper` return matrices, and `permute` returns a `Permutation` value. + +#### Other Algebraic Operations + +- `trace` - The sum of the matrix's diagonal +- `outer` - The matrix-valued outer product (or tensor product) of two vectors +- `det` - Determinant of the matrix +- `solve` - Apply an inverse matrix to a vector, useful for solving linear equations + +The `det` and `solve` operations require computing an LUP decomposition, which can be expensive. To avoid duplicating work, the variants `detWithLUP` and `solveWithLUP` allow a pre-computed LUP decomposition to be passed in. + +```idris +det m == detWithLUP m (decompLUP m) +``` + +### Indexing + +Aside from the usual array indexing functions, there are a few functions specialized to matrix indexing: + +- `getRow` and `getColumn` - Returns a specific row or column of the matrix +- `diagonal` - Returns the diagonal elements of the matrix as a vector +- `minor` - Removes a single row and column from the matrix [Previous](Operations.md) | [Contents](Intro.md) | [Next](Transforms.md)