This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

# Matrix

A simple linear algebra library using flat-arrays

# The Matrix type

type Matrix a = Matrix { nrows : Int , ncols : Int , mvect : Array a }

Representation of a matrix. You can create matrices of any type but arithmetic operations in Matrix.Operations requires the matrices to have numeric types.

# Creating matrices

empty : Matrix a

Create an empty matrix.

``````size empty == (0, 0)
``````
repeat : Int -> Int -> a -> Matrix a

Create a matrix with a given size, filled with a default value.

``````repeat 2 3 0 ~= [ [ 0, 0, 0 ], [ 0, 0, 0 ] ]
``````
initialize : Int -> Int -> (( Int, Int ) -> a) -> Matrix a

Createsa matrix with a given size, with the elements at index `(i, j)` initialized to the result of `f (i, j)`.

``````initialize 3 3 (\(i,j) -> if i == j then 1 else 0) == identity 3
``````
identity : Int -> Matrix number

Create the identity matrix of dimension `n`.

fromList : Int -> Int -> List a -> Maybe (Matrix a)

Create a matrix from a list given the desired size. If the list has a length inferior to `n * m`, returns `Nothing`.

``````fromList 2 2 [1,1,1,1,1] == Just <| repeat 2 2 1
fromList 3 3 [0,1,2] == Nothing
``````
fromLists : List (List a) -> Maybe (Matrix a)

Create a matrix from a list of lists. If any inner list is shorter than the first, returns `Nothing`. Otherwise, the length of the first list determines the width of the matrix.

``````fromLists [] == Just empty
fromLists [[]] == Just empty
fromLists [ [1,2,3], [1,2] ] == Nothing
fromLists [ [1,0], [0, 1] ] == Just <| identity 2
``````

# Get matrix dimensions

height : Matrix a -> Int

Return the number of rows in a given matrix.

width : Matrix a -> Int

Return the number of columns in a given matrix.

size : Matrix a -> ( Int, Int )

Return the dimensions of a given matrix in the form `(rows, columns)`.

# Working with individual elements

get : Int -> Int -> Matrix a -> Maybe a

Return `Just` the element at the index or `Nothing` if the index is out of bounds.

# Matrix manipulation

map : (a -> b) -> Matrix a -> Matrix b

Apply a function on every element of a matrix

map2 : (a -> b -> c) -> Matrix a -> Matrix b -> Maybe (Matrix c)

Apply a function between pairwise elements of two matrices. If the matrices are of differents sizes, returns `Nothing`.

transpose : Matrix a -> Matrix a

Return the transpose of a matrix.

dot : Matrix number -> Matrix number -> Maybe (Matrix number)

Perform the standard matrix multiplication. If the dimensions of the matrices are incompatible, returns `Nothing`.

# Matrix representation

toList : Matrix a -> List a

Convert the matrix to a flat list.

``````toList (identity 3) == [1,0,0,0,1,0,0,0,1]
``````
toLists : Matrix a -> List (List a)

Convert the matrix to a list of lists.

``````toLists (identity 3) = [ [1,0,0], [0,1,0], [0,0,1] ]
``````
pretty : Matrix a -> String

Convert a matrix to a formatted string.

``````pretty (identity 3) = """
[ [ 1, 0, 0 ]
, [ 0, 1, 0 ]
, [ 0, 0, 1 ] ]
"""
``````
``````module Matrix
exposing
( Matrix
, empty
, repeat
, initialize
, identity
, fromList
, fromLists
, height
, width
, size
, get
, map
, map2
, transpose
, dot
, toList
, toLists
, pretty
)

{-| A simple linear algebra library using flat-arrays

# The Matrix type

@docs Matrix

# Creating matrices

@docs empty, repeat, initialize, identity, fromList, fromLists

# Get matrix dimensions

@docs height, width, size

# Working with individual elements

@docs get

# Matrix manipulation

@docs map, map2, transpose, dot

# Matrix representation

@docs toList, toLists, pretty

-}

import Array.Hamt as Array exposing (Array)

{-| Representation of a matrix. You can create matrices of any type
but arithmetic operations in Matrix.Operations requires the matrices
to have numeric types.
-}
type Matrix a
= Matrix
{ nrows : Int
, ncols : Int
, mvect : Array a
}

{-| Create an empty matrix.

size empty == (0, 0)

-}
empty : Matrix a
empty =
Matrix
{ nrows = 0
, ncols = 0
, mvect = Array.empty
}

{-| Create a matrix with a given size, filled with a default value.

repeat 2 3 0 ~= [ [ 0, 0, 0 ], [ 0, 0, 0 ] ]

-}
repeat : Int -> Int -> a -> Matrix a
repeat nrows ncols value =
Matrix
{ nrows = nrows
, ncols = ncols
, mvect = Array.repeat (nrows * ncols) value
}

{-| Createsa matrix with a given size, with the elements at index `(i, j)` initialized to the result of `f (i, j)`.

initialize 3 3 (\(i,j) -> if i == j then 1 else 0) == identity 3

-}
initialize : Int -> Int -> (( Int, Int ) -> a) -> Matrix a
initialize nrows ncols f =
let
f_ i =
f (decode ncols i)

data =
Array.initialize (nrows * ncols) f_
in
Matrix
{ nrows = nrows
, ncols = ncols
, mvect = data
}

{-| Create the identity matrix of dimension `n`.
-}
identity : Int -> Matrix number
identity n =
let
f ( i, j ) =
if i == j then
1
else
0
in
initialize n n f

{-| Return the number of rows in a given matrix.
-}
height : Matrix a -> Int
height (Matrix { nrows }) =
nrows

{-| Return the number of columns in a given matrix.
-}
width : Matrix a -> Int
width (Matrix { ncols }) =
ncols

{-| Return the dimensions of a given matrix in the form `(rows, columns)`.
-}
size : Matrix a -> ( Int, Int )
size m =
( height m, width m )

{-| Return `Just` the element at the index or `Nothing` if the index is out of bounds.
-}
get : Int -> Int -> Matrix a -> Maybe a
get i j (Matrix { ncols, mvect }) =
Array.get (encode ncols ( i, j )) mvect

{-| Create a matrix from a list given the desired size.
If the list has a length inferior to `n * m`, returns `Nothing`.

fromList 2 2 [1,1,1,1,1] == Just <| repeat 2 2 1
fromList 3 3 [0,1,2] == Nothing

-}
fromList : Int -> Int -> List a -> Maybe (Matrix a)
fromList n m list =
if List.length list < n * m then
Nothing
else
Just <| initialize n m (\i -> unsafeGetFromList (encode m i) list)

{-| Create a matrix from a list of lists.
If any inner list is shorter than the first, returns `Nothing`.
Otherwise, the length of the first list determines the width of the matrix.

fromLists [] == Just empty
fromLists [[]] == Just empty
fromLists [ [1,2,3], [1,2] ] == Nothing
fromLists [ [1,0], [0, 1] ] == Just <| identity 2

-}
fromLists : List (List a) -> Maybe (Matrix a)
fromLists list =
case list of
[] ->
Just empty

xs :: xss ->
let
n =
1 + List.length xss

m =
List.length xs
in
if m == 0 then
Just empty
else
fromList n m <| List.concat <| xs :: List.map (List.take m) xss

{-| Apply a function on every element of a matrix
-}
map : (a -> b) -> Matrix a -> Matrix b
map f (Matrix m) =
Matrix { m | mvect = Array.map f m.mvect }

{-| Apply a function between pairwise elements of two matrices.
If the matrices are of differents sizes, returns `Nothing`.
-}
map2 : (a -> b -> c) -> Matrix a -> Matrix b -> Maybe (Matrix c)
map2 f m1 m2 =
if size m1 == size m2 then
Just <|
initialize (height m1) (width m1) <|
\x -> f (m1 !! x) (m2 !! x)
else
Nothing

{-| Return the transpose of a matrix.
-}
transpose : Matrix a -> Matrix a
transpose m =
initialize (width m) (height m) <| \( i, j ) -> m !! ( j, i )

{-| Perform the standard matrix multiplication.
If the dimensions of the matrices are incompatible, returns `Nothing`.
-}
dot : Matrix number -> Matrix number -> Maybe (Matrix number)
dot m1 m2 =
let
n =
width m1

m =
height m2
in
if n == m then
Just <| multStd m1 m2
else
Nothing

{-| Convert the matrix to a flat list.

toList (identity 3) == [1,0,0,0,1,0,0,0,1]

-}
toList : Matrix a -> List a
toList (Matrix { mvect }) =
Array.toList mvect

{-| Convert the matrix to a list of lists.

toLists (identity 3) = [ [1,0,0], [0,1,0], [0,0,1] ]

-}
toLists : Matrix a -> List (List a)
toLists m =
List.range 1 (height m)
|> List.concatMap
(\i ->
[ List.range 1 (width m)
|> List.concatMap (\j -> [ m !! ( i, j ) ])
]
)

{-| Convert a matrix to a formatted string.

pretty (identity 3) = """
[ [ 1, 0, 0 ]
, [ 0, 1, 0 ]
, [ 0, 0, 1 ] ]
"""

-}
pretty : Matrix a -> String
pretty m =
let
list =
toLists m
in
"[ " ++ prettyPrint list ++ " ]"

{- Utilities -}

encode : Int -> ( Int, Int ) -> Int
encode ncols ( i, j ) =
(i - 1) * ncols + j - 1

decode : Int -> Int -> ( Int, Int )
decode ncols index =
let
q =
index // ncols

r =
rem index ncols
in
( q + 1, r + 1 )

itemAt : Int -> List a -> Maybe a
itemAt index list =
if index < 0 then
Nothing
else
case list of
[] ->
Nothing

x :: xs ->
if index == 0 then
Just x
else
itemAt (index - 1) xs

unsafeGetFromList : Int -> List a -> a
unsafeGetFromList index list =
case itemAt index list of
Just value ->
value

Nothing ->
unsafeGetFromList index list

unsafeGet : Int -> Int -> Matrix a -> a
unsafeGet i j m =
case get i j m of
Just v ->
v

Nothing ->
unsafeGet i j m

(!!) : Matrix a -> ( Int, Int ) -> a
(!!) m ( i, j ) =
unsafeGet i j m

multStd : Matrix number -> Matrix number -> Matrix number
multStd m1 m2 =
let
( n, m ) =
size m1

( _, m_ ) =
size m2

f ( i, j ) =
List.foldr (+) 0 <| List.map (\k -> (m1 !! ( i, k )) * (m2 !! ( k, j ))) (List.range 1 m)
in
initialize n m_ f

prettyPrint : List (List a) -> String
prettyPrint list =
case list of
[] ->
""

x :: [] ->
"[ " ++ prettyList x ++ " ]"

x :: xs ->
"[ " ++ prettyList x ++ " ]\n, " ++ prettyPrint xs

prettyList : List a -> String
prettyList list =
case list of
[] ->
""

x :: [] ->
toString x

x :: xs ->
toString x ++ ", " ++ prettyList xs
```
```