Current section
Files
Jump to
Current section
Files
lib/matrix.ex
defmodule ELA.Matrix do
alias ELA.Vector, as: Vector
@moduledoc"""
Contains operations for working with matrices.
"""
@doc"""
Returns an identity matrix with the provided dimension.
## Examples
iex> Matrix.identity(3)
[[1, 0, 0],
[0, 1, 0],
[0, 0, 1]]
"""
@spec identity(number) ::[[number]]
def identity(n) do
for i <- 1..n, do: for j <- 1..n, do: (fn
i, j when i === j -> 1
_, _ -> 0
end).(i, j)
end
@doc"""
Returns a matrix filled wiht zeroes as with n rows and m columns.
## Examples
iex> Matrix.new(3, 2)
[[0, 0],
[0, 0],
[0, 0]]
"""
@spec new(number, number) :: [[number]]
def new(n, m) do
for _ <- 1..n, do: for _ <- 1..m, do: 0
end
@docs"""
Transposes a matrix.
## Examples
iex> Matrix.transp([[1, 2, 3],
[4, 5, 6]])
[[1, 4],
[2, 5],
[3, 6]]
"""
@spec transp([[number]]) :: [[number]]
def transp(a) do
List.zip(a) |> Enum.map(&Tuple.to_list(&1))
end
@doc"""
Performs elmentwise addition
## Examples
iex> Matrix.add([[1, 2, 3],
[1, 1, 1]],
[[1, 2, 2],
[1, 2, 1]])
[[2, 4, 5],
[2, 3, 2]]
"""
@spec add([[number]], [[number]]) :: [[number]]
def add(a, b) do
if dim(a) !== dim(b) do
raise(ArgumentError, "Matrices must have same dimensions for addition.")
end
Enum.map(Enum.zip(a, b), fn({a, b}) -> Vector.add(a, b) end)
end
@doc"""
Performs elementwise subtraction
## Examples
iex> Matrix.sub([[1, 2, 3],
[1, 2, 2]],
[[1, 2, 3],
[2, 2, 2]])
[[0, 0, 0],
[-1, 0, 0]]
"""
@spec sub([[number]], [[number]]) :: [[number]]
def sub(a, b) when length(a) !== length(b),
do: raise(ArgumentError, "The number of rows in the matrices must match.")
def sub(a, b) do
Enum.map(Enum.zip(a, b), fn({a, b}) -> Vector.add(a, Vector.scalar(b, -1)) end)
end
@doc"""
Elementwise mutiplication with a scalar.
## Examples
iex> Matrix.scalar([[2, 2, 2],
[1, 1, 1]], 2)
[[4, 4, 4],
[2, 2, 2]]
"""
@spec scalar([[number]], number) :: [[number]]
def scalar(a, s) do
Enum.map(a, fn(r) -> Vector.scalar(r, s) end)
end
@doc"""
Elementwise multiplication with two matrices.
This is known as the Hadmard product.
## Examples
iex> Matrix.hadmard([[1, 2],
[1, 1]],
[[1, 2],
[0, 2]])
[[1, 4],
[0, 2]]
"""
def hadmard(a, b) when length(a) !== length(b),
do: raise(ArgumentError, "The number of rows in the matrices must match.")
def hadmard(a, b) do
Enum.map(Enum.zip(a, b), fn({u, v}) -> Vector.hadmard(u, v) end)
end
@doc"""
Matrix multiplication. Can also multiply matrices with vectors.
Always returns a matrix.
## Examples
iex> Matrix.mult([1, 1], [[1, 0, 1],
[1, 1, 1]])
[[2, 1, 2]]
iex> Matrix.mult([[1, 0, 1],
[1, 1, 1]],
[[1],
[1],
[1]])
[[2],
[3]]
"""
def mult(v, b) when is_number(hd(v)) and is_list(hd(b)), do: mult([v], b)
def mult(a, v) when is_number(hd(v)) and is_list(hd(a)), do: mult(a, [v])
def mult(a, b) do
Enum.map(a, fn(r) ->
Enum.map(transp(b), &Vector.dot(r, &1))
end)
end
@doc"""
Returns a tuple with the matrix dimensions as {rows, cols}.
## Examples
Matrix.dim([[1, 1, 1],
[2, 2, 2]])
{2, 3}
"""
@spec dim([[number]]) :: {integer, integer}
def dim(a) when length(a) === 0, do: 0
def dim(a) do
{length(a), length(Enum.at(a, 0))}
end
@doc"""
Pivots them matrix a on the element on row n, column m (zero indexed).
Pivoting performs row operations to make the
pivot element 1 and all others in the same column 0.
## Examples
iex> Matrix.pivot([[2.0, 3.0],
[2.0, 3.0],
[3.0, 6.0]], 1, 0)
[[0.0, 0.0],
[1.0, 1.5],
[0.0, 1.5]]
"""
@spec pivot([[number]], number, number) :: [[number]]
def pivot(a, n, m) do
pr = Enum.at(a, n) #Pivot row
pe = Enum.at(pr, m) #Pivot element
a
|> List.delete_at(n)
|> Enum.map(&Vector.sub(&1, Vector.scalar(pr, Enum.at(&1, m) / pe)))
|> List.insert_at(n, Vector.scalar(pr, 1 / pe))
end
@doc"""
Returns a row equivalent matrix on reduced row echelon form.
## Examples
iex> Matrix.reduce([[1.0, 1.0, 2.0, 1.0],
[2.0, 1.0, 6.0, 4.0],
[1.0, 2.0, 2.0, 3.0]])
[[1.0, 0.0, 0.0, -5.0],
[0.0, 1.0, 0.0, 2.0],
[0.0, 0.0, 1.0, 2.0]]
"""
@spec reduce([[number]]) :: [[number]]
def reduce(a), do: reduce(a, 0)
defp reduce(a, i) do
r = Enum.at(a, i)
j = Enum.find_index(r, fn(e) -> e != 0 end)
a = pivot(a, i, j)
unless j === length(r) - 1 or
i === length(a) - 1
do
reduce(a, i + 1)
else
a
end
end
end