Add documentation

This commit is contained in:
Kiana Sheibani 2022-06-25 00:58:36 -04:00
parent 11d771b926
commit 59af31cdd7
Signed by: toki
GPG key ID: 6CB106C25E86A9F7
10 changed files with 188 additions and 37 deletions

View file

@ -1,5 +1,6 @@
module Data.NumIdr.Array
import public Data.NP
import public Data.NumIdr.Array.Array
import public Data.NumIdr.Array.Coords
import public Data.NumIdr.Array.Order

View file

@ -4,6 +4,7 @@ import Data.List
import Data.List1
import Data.Vect
import Data.Zippable
import Data.NP
import Data.NumIdr.Multiply
import Data.NumIdr.PrimArray
import Data.NumIdr.Array.Order
@ -63,6 +64,7 @@ strides : Array {rk} s a -> Vect rk Nat
strides (MkArray _ sts _ _) = sts
||| The total number of elements of the array
|||
||| This is equivalent to `product s`.
export
size : Array s a -> Nat
@ -175,6 +177,26 @@ export
fromStream : (s : Vect rk Nat) -> Stream a -> Array s a
fromStream s = fromStream' s COrder
||| Create an array given a function to generate its elements.
|||
||| @ s The shape of the constructed array
||| @ ord The order to interpret the elements
export
fromFunctionNB' : (s : Vect rk Nat) -> (ord : Order) -> (Vect rk Nat -> a) -> Array s a
fromFunctionNB' s ord f = let sts = calcStrides ord s
in MkArray ord sts s (unsafeFromIns (product s) $
map (\is => (getLocation' sts is, f is)) $ getAllCoords' s)
||| Create an array given a function to generate its elements.
||| To specify the order of the array, use `fromFunctionNB'`.
|||
||| @ s The shape of the constructed array
||| @ ord The order to interpret the elements
export
fromFunctionNB : (s : Vect rk Nat) -> (Vect rk Nat -> a) -> Array s a
fromFunctionNB s = fromFunctionNB' s COrder
||| Create an array given a function to generate its elements.
|||
||| @ s The shape of the constructed array
@ -219,32 +241,39 @@ array v = MkArray COrder (calcStrides COrder s) s (fromList $ collapse v)
-- Indexing
--------------------------------------------------------------------------------
infix 10 !!
infix 10 !?
infixl 10 !!
infixl 10 !?
infixl 11 !!..
infix 11 !?..
infixl 11 !?..
||| Index the array using the given `Coords` object.
||| Index the array using the given coordinates.
export
index : Coords s -> Array s a -> a
index is arr = index (getLocation (strides arr) is) (getPrim arr)
||| Index the array using the given coordinates.
|||
||| This is the operator form of `index`.
export
(!!) : Array s a -> Coords s -> a
(!!) = flip index
-- TODO: Create set/update at index functions
||| Update the value at the given coordinates using the function.
export
indexUpdate : Coords s -> (a -> a) -> Array s a -> Array s a
indexUpdate is f (MkArray ord sts s arr) =
MkArray ord sts s (updateAt (getLocation sts is) f arr)
||| Set the value at the given coordinates to the given value.
export
indexSet : Coords s -> a -> Array s a -> Array s a
indexSet is = indexUpdate is . const
||| Index the array using the given coordinates, returning `Nothing` if the
||| coordinates are out of bounds.
export
indexNB : Vect rk Nat -> Array {rk} s a -> Maybe a
indexNB is arr with (viewShape arr)
@ -252,21 +281,29 @@ indexNB is arr with (viewShape arr)
then Just $ index (getLocation' (strides arr) is) (getPrim arr)
else Nothing
||| Index the array using the given coordinates, returning `Nothing` if the
||| coordinates are out of bounds.
|||
||| This is the operator form of `indexNB`.
export
(!?) : Array {rk} s a -> Vect rk Nat -> Maybe a
(!?) = flip indexNB
||| Update the value at the given coordinates using the function. The array is
||| returned unchanged if the coordinate is out of bounds.
export
indexUpdateNB : Vect rk Nat -> (a -> a) -> Array {rk} s a -> Array s a
indexUpdateNB is f (MkArray ord sts s arr) =
MkArray ord sts s (updateAt (getLocation' sts is) f arr)
||| Set the value at the given coordinates to the given value. The array is
||| returned unchanged if the coordinate is out of bounds.
export
indexSetNB : Vect rk Nat -> a -> Array {rk} s a -> Array s a
indexSetNB is = indexUpdateNB is . const
||| Index the array using the given `CoordsRange` object.
||| Index the array using the given range of coordinates, returning a new array.
export
indexRange : (rs : CoordsRange s) -> Array s a -> Array (newShape rs) a
indexRange rs arr with (viewShape arr)
@ -281,6 +318,9 @@ indexRange rs arr with (viewShape arr)
where s' : Vect ? Nat
s' = newShape rs
||| Index the array using the given range of coordinates, returning a new array.
|||
||| This is the operator form of `indexRange`.
export
(!!..) : Array s a -> (rs : CoordsRange s) -> Array (newShape rs) a
arr !!.. rs = indexRange rs arr
@ -320,10 +360,20 @@ reorder ord' arr with (viewShape arr)
getAllCoords' s)
||| Resize the array to a new shape, preserving the coordinates of the original
||| elements. New coordinates are filled with a default value.
|||
||| @ s' The shape to resize the array to
||| @ def The default value to fill the array with
export
resize : (s' : Vect rk Nat) -> a -> Array {rk} s a -> Array s' a
resize s' x arr = fromFunction' s' (getOrder arr) (fromMaybe x . (arr !?) . toNB)
resize : (s' : Vect rk Nat) -> (def : a) -> Array {rk} s a -> Array s' a
resize s' def arr = fromFunction' s' (getOrder arr) (fromMaybe def . (arr !?) . toNB)
||| Resize the array to a new shape, preserving the coordinates of the original
||| elements. This function requires a proof that the new shape is strictly
||| smaller than the current shape of the array.
|||
||| @ s' The shape to resize the array to
export
-- TODO: Come up with a solution that doesn't use `believe_me` or trip over some
-- weird bug in the type-checker
@ -332,17 +382,23 @@ resizeLTE : (s' : Vect rk Nat) -> (0 ok : NP Prelude.id (zipWith LTE s' s)) =>
resizeLTE s' arr = resize s' (believe_me ()) arr
||| List all of the values in an array along with their coordinates.
export
enumerateNB : Array {rk} s a -> List (Vect rk Nat, a)
enumerateNB (MkArray _ sts sh p) =
map (\is => (is, index (getLocation' sts is) p)) (getAllCoords' sh)
||| List all of the values in an array along with their coordinates.
export
enumerate : Array {rk} s a -> List (Coords s, a)
enumerate : Array s a -> List (Coords s, a)
enumerate arr with (viewShape arr)
_ | Shape s = map (\is => (is, index is arr)) (getAllCoords s)
||| Join two arrays along a particular axis, e.g. combining two matrices
||| vertically or horizontally. The arrays must have the same shape on all other axes.
|||
||| @ axis The axis to join the arrays on
export
concat : (axis : Fin rk) -> Array {rk} s a -> Array (replaceAt axis d s) a ->
Array (updateAt axis (+d) s) a
@ -357,6 +413,9 @@ concat axis a b = let sA = shape a
-- TODO: prove that the type-level shape and `s` are equivalent
in believe_me $ MkArray COrder sts s (unsafeFromIns (product s) ins)
||| Stack multiple arrays along a new axis, e.g. stacking vectors to form a matrix.
|||
||| @ axis The axis to stack the arrays along
export
stack : {s : _} -> (axis : Fin (S rk)) -> Vect n (Array {rk} s a) -> Array (insertAt axis n s) a
stack axis arrs = rewrite sym (lengthCorrect arrs) in
@ -415,7 +474,7 @@ export
export
{s : _} -> Monad (Array s) where
join arr = fromFunction s (\is => index is $ index is arr)
join arr = fromFunction s (\is => arr !! is !! is)
-- Foldable and Traversable operate on the primitive array directly. This means
@ -466,6 +525,7 @@ export
negate = map negate
(-) = zipWith (-)
export
{s : _} -> Fractional a => Fractional (Array s a) where
recip = map recip
(/) = zipWith (/)
@ -508,23 +568,30 @@ Show a => Show (Array s a) where
--------------------------------------------------------------------------------
||| Linearly interpolate between two arrays.
export
lerp : Neg a => a -> Array s a -> Array s a -> Array s a
lerp t a b = zipWith (+) (a *. (1 - t)) (b *. t)
||| Calculate the square of an array's Eulidean norm.
export
normSq : Num a => Array s a -> a
normSq arr = sum $ zipWith (*) arr arr
||| Calculate an array's Eucliean norm.
export
norm : Array s Double -> Double
norm = sqrt . normSq
||| Normalize the array to a norm of 1.
|||
||| If the array contains all zeros, then it is returned unchanged.
export
normalize : Array s Double -> Array s Double
normalize arr = if all (==0) arr then arr else map (/ norm arr) arr
||| Calculate the Lp-norm of an array.
export
pnorm : (p : Double) -> Array s Double -> Double
pnorm p = (`pow` recip p) . sum . map (`pow` p)

View file

@ -2,7 +2,7 @@ module Data.NumIdr.Array.Coords
import Data.Either
import Data.Vect
import public Data.NP
import Data.NP
%default total

View file

@ -29,7 +29,6 @@ Eq Order where
FOrder == COrder = False
scanr : (el -> res -> res) -> res -> Vect len el -> Vect (S len) res
scanr _ q0 [] = [q0]
scanr f q0 (x::xs) = f x (head qs) :: qs

View file

@ -8,14 +8,33 @@ import Data.NumIdr.Matrix
%default total
public export
HVector : Nat -> Type -> Type
HVector n = Vector (S n)
||| A homogeneous matrix type.
|||
||| Homogeneous matrices are matrices that encode an affine transformation, as
||| opposed to the more restriced linear transformations of normal matrices.
||| Their multiplication is identical to regular matrix multiplication, but the
||| extra dimension in each axis allows the homogeneous matrix to store a
||| translation vector that is applied during multiplication.
|||
||| ```hmatrix mat tr *. v == mat *. v + tr```
public export
HMatrix : Nat -> Nat -> Type -> Type
HMatrix m n = Matrix (S m) (S n)
||| A synonym for a square homogeneous matrix.
public export
HMatrix' : Nat -> Type -> Type
HMatrix' n = HMatrix n n
||| An `n`-dimensional homogeneous vector type.
|||
||| Homogeneous vectors are vectors intended to be multiplied with homogeneous
||| matrices (see `HMatrix`). They have an extra dimension compared to regular
||| vectors.
public export
HVector : Nat -> Type -> Type
HVector n = Vector (S n)
export

View file

@ -8,10 +8,12 @@ import Data.NumIdr.Vector
%default total
||| A matrix is a rank-2 array.
public export
Matrix : Nat -> Nat -> Type -> Type
Matrix m n = Array [m,n]
||| A synonym for a square matrix with dimensions of length `n`.
public export
Matrix' : Nat -> Type -> Type
Matrix' n = Matrix n n
@ -22,26 +24,30 @@ Matrix' n = Matrix n n
--------------------------------------------------------------------------------
||| Construct a matrix with the given order and elements.
export
matrix' : {m, n : _} -> Order -> Vect m (Vect n a) -> Matrix m n a
matrix' ord x = array' [m,n] ord x
||| Construct a matrix with the given elements.
export
matrix : {m, n : _} -> Vect m (Vect n a) -> Matrix m n a
matrix = matrix' COrder
||| Construct a matrix with a specific value along the diagonal.
|||
||| @ diag The value to repeat along the diagonal
||| @ other The value to repeat elsewhere
export
repeatDiag : {m, n : _} -> (diag, other : a) -> Matrix m n a
repeatDiag d o = fromFunction [m,n]
(\[i,j] => if i `eq` j then d else o)
where
eq : {0 m,n : Nat} -> Fin m -> Fin n -> Bool
eq FZ FZ = True
eq (FS x) (FS y) = eq x y
eq FZ (FS _) = False
eq (FS _) FZ = False
repeatDiag d o = fromFunctionNB [m,n]
(\[i,j] => if i == j then d else o)
||| Construct a matrix given its diagonal elements.
|||
||| @ diag The elements of the matrix's diagonal
||| @ other The value to repeat elsewhere
export
fromDiag : {m, n : _} -> (diag : Vect (minimum m n) a) -> (other : a) -> Matrix m n a
fromDiag ds o = fromFunction [m,n] (\[i,j] => maybe o (`index` ds) $ i `eq` j)
@ -53,15 +59,18 @@ fromDiag ds o = fromFunction [m,n] (\[i,j] => maybe o (`index` ds) $ i `eq` j)
eq (FS _) FZ = Nothing
||| The `n`-dimensional identity matrix.
export
identity : Num a => {n : _} -> Matrix' n a
identity = repeatDiag 1 0
||| Calculate the matrix that scales a vector by the given value.
export
scaling : Num a => {n : _} -> a -> Matrix' n a
scaling x = repeatDiag x 0
||| Calculate the rotation matrix of an angle.
export
rotation2D : Double -> Matrix' 2 Double
rotation2D a = matrix [[cos a, - sin a], [sin a, cos a]]
@ -72,19 +81,24 @@ rotation2D a = matrix [[cos a, - sin a], [sin a, cos a]]
--------------------------------------------------------------------------------
||| Index the matrix at the given coordinates.
export
index : Fin m -> Fin n -> Matrix m n a -> a
index m n = index [m,n]
||| Index the matrix at the given coordinates, returning `Nothing` if the
||| coordinates are out of bounds.
export
indexNB : Nat -> Nat -> Matrix m n a -> Maybe a
indexNB m n = indexNB [m,n]
||| Return a row of the matrix as a vector.
export
getRow : Fin m -> Matrix m n a -> Vector n a
getRow r mat = rewrite sym (minusZeroRight n) in indexRange [One r, All] mat
||| Return a column of the matrix as a vector.
export
getColumn : Fin n -> Matrix m n a -> Vector m a
getColumn c mat = rewrite sym (minusZeroRight m) in indexRange [All, One c] mat
@ -94,19 +108,22 @@ getColumn c mat = rewrite sym (minusZeroRight m) in indexRange [All, One c] mat
-- Operations
--------------------------------------------------------------------------------
||| Concatenate two matrices vertically.
export
vconcat : Matrix m n a -> Matrix m' n a -> Matrix (m + m') n a
vconcat = concat 0
||| Concatenate two matrices horizontally.
export
hconcat : Matrix m n a -> Matrix m n' a -> Matrix m (n + n') a
hconcat = concat 1
||| Calculate the kronecker product of two vectors as a matrix.
export
kronecker : Num a => Vector m a -> Vector n a -> Matrix m n a
kronecker a b with (viewShape a, viewShape b)
_ | (Shape [m], Shape [n]) = fromFunction [m,n] (\[i,j] => index i a * index j b)
_ | (Shape [m], Shape [n]) = fromFunction [m,n] (\[i,j] => a !! i * b !! j)
--------------------------------------------------------------------------------

View file

@ -6,32 +6,49 @@ module Data.NumIdr.Multiply
infixr 9 *.
infixr 10 ^
||| A generalized multiplication/transformation operator. This interface is
||| A generalized multiplication/application operator. This interface is
||| necessary since the standard multiplication operator is homogenous.
|||
||| All instances of this interface must collectively satisfy these axioms:
||| * If `(x *. y) *. z` is defined, then `x *. (y *. z)` is defined and equal.
||| * If `x *. (y *. z)` is defined, then `(x *. y) *. z` is defined and equal.
public export
interface Mult a b c | a,b where
(*.) : a -> b -> c
||| An interface for monoids using the `*.` operator.
|||
||| An instance of this interface must satisfy:
||| * `x *. neutral == x`
||| * `neutral *. x == x`
public export
interface (Mult a a a) => MultNeutral a where
interface Mult a a a => MultNeutral a where
neutral : a
||| Multiplication forms a semigroup
public export
[MultSemigroup] Mult a a a => Semigroup a where
(<+>) = (*.)
||| Multiplication with a neutral element forms a monoid
public export
[MultMonoid] MultNeutral a => Monoid a using MultSemigroup where
neutral = Multiply.neutral
||| Raise a multiplicative value (e.g. a matrix or a transformation) to a natural
||| number power.
public export
power : MultNeutral a => Nat -> a -> a
power 0 _ = neutral
power 1 x = x
power (S n@(S _)) x = x *. power n x
||| Raise a multiplicative value (e.g. a matrix or a transformation) to a natural
||| number power.
|||
||| This is the operator form of `power`.
public export
(^) : MultNeutral a => a -> Nat -> a
(^) = flip power

View file

@ -35,8 +35,8 @@ export
constant : Nat -> a -> PrimArray a
constant size x = MkPrimArray size $ unsafePerformIO $ newArrayData size x
||| Construct an array from a list of "instructions" to write a
||| value to a particular index.
||| Construct an array from a list of "instructions" to write a value to a
||| particular index.
export
unsafeFromIns : Nat -> List (Nat, a) -> PrimArray a
unsafeFromIns size ins = unsafePerformIO $ do
@ -44,8 +44,8 @@ unsafeFromIns size ins = unsafePerformIO $ do
for_ ins $ \(i,x) => arrayDataSet i x arr
pure $ MkPrimArray size arr
||| Create an array given its size and a function to generate
||| its elements by its index.
||| Create an array given its size and a function to generate its elements by
||| its index.
export
create : Nat -> (Nat -> a) -> PrimArray a
create size f = unsafePerformIO $ do
@ -59,8 +59,8 @@ create size f = unsafePerformIO $ do
= do arrayDataSet loc (f loc) arr
addToArray (S loc) n arr
||| Index into a primitive array. This function is unsafe, as it
||| performs no boundary check on the index given.
||| Index into a primitive array. This function is unsafe, as it performs no
||| boundary check on the index given.
export
index : Nat -> PrimArray a -> a
index n arr = unsafePerformIO $ arrayDataGet n $ content arr
@ -156,8 +156,8 @@ traverse : Applicative f => (a -> f b) -> PrimArray a -> f (PrimArray b)
traverse f = map fromList . traverse f . toList
||| Compares two primitive arrays for equal elements. This function assumes
||| the arrays same the same length; it must not be used in any other case.
||| Compares two primitive arrays for equal elements. This function assumes the
||| arrays have the same length; it must not be used in any other case.
export
unsafeEq : Eq a => PrimArray a -> PrimArray a -> Bool
unsafeEq a b = unsafePerformIO $

View file

@ -15,10 +15,12 @@ Scalar : Type -> Type
Scalar = Array []
||| Convert a value to a scalar.
export
scalar : a -> Scalar a
scalar x = fromVect _ [x]
||| Unwrap the single value from a scalar.
export
unwrap : Scalar a -> a
unwrap = index 0 . getPrim

View file

@ -6,11 +6,14 @@ import public Data.NumIdr.Array
%default total
||| A vector is a rank-1 array.
public export
Vector : Nat -> Type -> Type
Vector n = Array [n]
||| The length (number of dimensions) of the vector
public export
dim : Vector n a -> Nat
dim = head . shape
@ -25,6 +28,7 @@ dimEq v = cong head $ shapeEq v
--------------------------------------------------------------------------------
||| Construct a vector from a `Vect`.
export
vector : Vect n a -> Vector n a
vector v = rewrite sym (lengthCorrect v)
@ -32,20 +36,28 @@ vector v = rewrite sym (lengthCorrect v)
rewrite lengthCorrect v in -- there is only 1 axis
rewrite multOneLeftNeutral n in v
||| Convert a vector into a `Vect`.
export
toVect : Vector n a -> Vect n a
toVect v = believe_me $ Vect.fromList $ toList v
||| Return the `i`-th basis vector.
export
basis : Num a => {n : _} -> (i : Fin n) -> Vector n a
basis i = fromFunction _ (\[j] => if i == j then 1 else 0)
||| Calculate the 2D unit vector with the given angle off the x-axis.
export
unit2D : (ang : Double) -> Vector 2 Double
unit2D ang = vector [cos ang, sin ang]
||| Calculate the 3D unit vector corresponding to the given spherical coordinates,
||| where the polar axis is the z-axis.
|||
||| @ pol The polar angle of the vector
||| @ az The azimuthal angle of the vector
export
unit3D : (pol, az : Double) -> Vector 3 Double
unit3D pol az = vector [cos az * sin pol, sin az * sin pol, cos pol]
@ -56,35 +68,49 @@ unit3D pol az = vector [cos az * sin pol, sin az * sin pol, cos pol]
-- Indexing
--------------------------------------------------------------------------------
infix 10 !!
infix 10 !?
infixl 10 !!
infixl 10 !?
||| Index the vector at the given coordinate.
export
index : Fin n -> Vector n a -> a
index n = Array.index [n]
||| Index the vector at the given coordinate.
|||
||| This is the operator form of `index`.
export
(!!) : Vector n a -> Fin n -> a
(!!) = flip index
||| Index the vector at the given coordinate, returning `Nothing` if the
||| coordinate is out of bounds.
export
indexNB : Nat -> Vector n a -> Maybe a
indexNB n = Array.indexNB [n]
||| Index the vector at the given coordinate, returning `Nothing` if the
||| coordinate is out of bounds.
|||
||| This is the operator form of `indexNB`.
export
(!?) : Vector n a -> Nat -> Maybe a
(!?) = flip indexNB
-- Named projections
||| Return the x-coordinate (the first value) of the vector.
export
(.x) : Vector (1 + n) a -> a
(.x) = index FZ
||| Return the y-coordinate (the second value) of the vector.
export
(.y) : Vector (2 + n) a -> a
(.y) = index (FS FZ)
||| Return the z-coordinate (the third value) of the vector.
export
(.z) : Vector (3 + n) a -> a
(.z) = index (FS (FS FZ))
@ -109,16 +135,19 @@ swizzle p v = rewrite sym (lengthCorrect p)
--------------------------------------------------------------------------------
||| Concatenate one vector with another.
export
(++) : Vector m a -> Vector n a -> Vector (m + n) a
(++) = concat 0
||| Calculate the dot product of the two vectors.
export
dot : Num a => Vector n a -> Vector n a -> a
dot = sum .: zipWith (*)
||| Calculate the perpendicular product of the two vectors.
export
perp : Neg a => Vector 2 a -> Vector 2 a -> a
perp a b = a.x * b.y - a.y * b.x