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.
"""
@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.
"""
@spec new(number, number) :: [[number]]
def new(n, m) do
for _ <- 1..n, do: for _ <- 1..m, do: 0
end
@docs"""
Transposes a matrix.
"""
@spec transp([[number]]) :: [[number]]
def transp(a) do
List.zip(a) |> Enum.map(&Tuple.to_list(&1))
end
@doc"""
Performs elmentwise addition
"""
@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
"""
@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.
"""
@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.
"""
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.
"""
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}.
"""
@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.
"""
@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.
"""
@spec reduce([[number]]) :: [[number]]
def reduce(a, i \\ 0)
def 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
@doc"""
Returns the LU-decomposition of a matrix as a tuple {u, l, p}.
"""
def lu(a) do
p = lu_pivot(a)
a = mult(p, a)
a = memoize_matrix(a)
IO.puts "#{inspect map_size(a)}"
{u, l} = lu_rows(a)
{u, l, p}
'''
Enum.each(indexes, &Enum.each(&1, fn
{i, j} when i > j -> u = put_in u[i][j], a[i][j] - sum_upper.(i, j)
{i, j} when i == j -> l = put_in l[i][j], 1
{i, j} when i < j -> l = put_in l[i][j], a[i][j] - sum_lower.(i, j)
end))
u = Enum.map(u_i, &Enum.map(&1, fn
{1, j} -> a[1][j]
{i, j} when i <= j -> a[i][j] - sum_upper.(i, j)
end))
indexes = for i <- 1..map_size(a), do: for j <- 1..map_size(a), do: {i, j}
a = Enum.map(indexes, &Enum.map(&1, fn
1, j -> a[1][j]
i, 1 -> a[i][1]/a[1][1]
i, j -> a[i][j] - Enum.sum(for k <- 1..i-1, do: a[k][j] * a[i][k])
i, j -> (a[i][j] - Enum.sum(for k <- 1..j-1, do: a[k][j] * a[i][k]))/a[j][j]
end))
Börja med första raden och rekursera nedåt
Sätt första element a[i][0]
Generera indexmängd for k <- 1..map_size(a), {i, k}
mappa indexmängden mot rätt värden
modifiera a med en nya raden
passa vidare
avsluta när i == map_size(a)
'''
# {u, l, p}
end
defp lu_rows(a) do lu_rows(a, 1, memoize_matrix(a), memoize_matrix(a)) end
defp lu_rows(a, i, u, l) when i === map_size(a) + 1, do: {u, l}
defp lu_rows(a, i, u, l) do
lower_indexes = for j <- 1..i, do: {i, j}
upper_indexes = for j <- i..map_size(a), do: {i, j}
IO.puts "lower indexes #{inspect lower_indexes}"
IO.puts "upper indexes #{inspect upper_indexes}"
u = memoize_matrix(new(map_size(a), map_size(a)))
l = memoize_matrix(new(map_size(a), map_size(a)))
l = do_lower(a, lower_indexes, u, l)
u = do_upper(a, upper_indexes, u, l)
#IO.puts "lower #{inspect l}"
IO.puts "upper #{inspect u}"
# a = Enum.map(a[i], fn
# {j, v} when i <= j -> sum_upper.(i, j)
# {j, v} when i > j -> sum_lower.(i, j)
# end)))
lu_rows(a, i + 1, u, l)
end
defp do_lower(a, [], u, l), do: l
defp do_lower(a, [{i, j} | t], u, l) do
IO.puts "doing lower i #{i}, j #{j}"
IO.puts "a[i][j] #{a[i][j]}"
#IO.puts "mut #{a[k][j] * a[i][k]}"
#IO.puts " sum #{Enum.sum(for k <- 1..j-1, do: a[k][j] * a[i][k])/a[j][j]}"
sum_lower = fn
i, 1 -> a[i][1]/u[1][1]
i, j -> (a[i][j] - Enum.sum(for k <- 1..j-1, do: u[k][j] * i[i][k]))/u[j][j]
end
do_lower(a, t, u, put_in(l[i][j], sum_lower.(i, j)))
end
defp do_upper(a, [], u, l), do: u
defp do_upper(a, [{i, j} | t], u, l) do
sum_upper = fn
1, j -> a[1][j]
i, j -> a[i][j] - Enum.sum(for k <- 1..i-1, do: u[k][j] * i[i][k])
end
#IO.puts "doing upper i #{i}, j #{j}"
do_upper(a, t, put_in(u[i][j], sum_upper.(i, j)), l)
end
defp lu_decompose_rows(a, u, l, m, i \\ 1)
defp lu_decompose_rows(a, u, l, m, i) do
l = put_in l[i][0], u[0][0] / a[i][0]
m = put_in m[i][0], 0
{u, l, m} = lu_decompose_row(a, u, l, m, i, 1)
end
def lu_decompose_row(a, u, l, m, i, j) do
IO.puts "doing thing"
IO.puts "u[#{i}][#{j}] #{u[i][j]}"
IO.puts "a[#{i}][#{j}] #{a[i][j]}"
IO.puts "u[#{i} - 1][#{j}] #{u[i - 1][j]}"
IO.puts "l[#{i}][#{j} - 1}] #{l[i][j - 1]}"
IO.puts "u[#{i}][#{j}] #{u[i][j]}"
#IO.puts "#{u[j] |> Enum.slice(0, j - 1))}"
# u_k_j = a[i][j] - (u
# |> Enum.at(j)
# |> Enum.slice(0, j - 1))
# IO.puts "ukj #{u_k_j}"
# l_i_k = a[i][j] - (l
# |> Enum.at(j - 1)
# |> Enum.slice(0, i))
# s = Vector.scalar(u_k_j, l_i_k)
# u = put_in u[i][j], a[i][j] - s
#(u[i - 1][j] * l[i][j - 1]))
#xs IO.puts "lik #{l_i_k}"
end
defp lu_pivot(a), do: lu_pivot(transp(a), identity(length(a)), 0)
defp lu_pivot(a, p, i) when i === length(a) - 1, do: p
defp lu_pivot(a, p, i) do
r = Enum.drop(Enum.at(a, i), i)
j = i + Enum.find_index(r, fn(x) ->
x === abs(Enum.max(r))
end)
p =
case {i, j} do
{i, i} -> p
_ -> p
|> List.update_at(i, &(&1 = Enum.at(p, j)))
|> List.update_at(j, &(&1 = Enum.at(p, i)))
end
lu_pivot(a, p, i + 1)
end
defp memoize_matrix(list, map \\ %{}, index \\ 1)
defp memoize_matrix([], map, _), do: map
defp memoize_matrix([h|t], map, index) do
map = Map.put(map, index, memoize_matrix(h))
memoize_matrix(t, map, index + 1)
end
defp memoize_matrix(other, _, _), do: other
end