Current section
Files
Jump to
Current section
Files
lib/matrix_operation.ex
defmodule MatrixOperation do
@moduledoc """
Documentation for Matrix operation library.
"""
@doc """
Numbers of row and column of a matrix are got.
#### Examples
iex> MatrixOperation.row_column_matrix([[3, 2, 3], [2, 1, 2]])
[2, 3]
"""
def row_column_matrix(a) when is_list(hd(a)) do
columns_number = Enum.map(a, & row_column_matrix_sub(&1, 0))
max_number = Enum.max(columns_number)
if( max_number == Enum.min(columns_number), do: [length(a), max_number] , else: nil)
end
def row_column_matrix(a) do
nil
end
defp row_column_matrix_sub(row_a, i) when i != length(row_a) do
if(is_number(Enum.at(row_a, i)), do: row_column_matrix_sub(row_a, i + 1) , else: nil)
end
defp row_column_matrix_sub(row_a, i) when i == length(row_a) do
i
end
@doc """
A element of a matrix is got.
#### Examples
iex> MatrixOperation.get_one_element([[1, 2, 3], [4, 5, 6], [7, 8, 9] ], [1, 1])
1
"""
def get_one_element(matrix, [row_index, column_index]) do
matrix
|> Enum.at(row_index - 1)
|> Enum.at(column_index - 1)
end
@doc """
A row of a matrix is got.
#### Examples
iex> MatrixOperation.get_one_row([[1, 2, 3], [4, 5, 6], [7, 8, 9] ], 1)
[1, 2, 3]
"""
def get_one_row(matrix, row_index) do
matrix
|> Enum.at(row_index - 1)
end
@doc """
A column of a matrix is got.
#### Examples
iex> MatrixOperation.get_one_column([[1, 2, 3], [4, 5, 6], [7, 8, 9] ], 1)
[1, 4, 7]
"""
def get_one_column(matrix, column_index) do
matrix
|> transpose
|> Enum.at(column_index - 1)
end
@doc """
A row of a matrix is deleted.
#### Examples
iex> MatrixOperation.delete_one_row([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 3)
[[1, 2, 3], [4, 5, 6]]
"""
def delete_one_row(matrix, delete_index) do
matrix
|> Enum.with_index
|> Enum.reject(fn {_, i} -> i == (delete_index - 1) end)
|> Enum.map(fn {x, _} -> x end)
end
@doc """
Transpose of a matrix
#### Examples
iex> MatrixOperation.transpose([[1.0, 2.0], [3.0, 4.0]])
[[1.0, 3.0], [2.0, 4.0]]
"""
def transpose(a) do
Enum.zip(a)
|> Enum.map(& Tuple.to_list(&1))
end
@doc """
Trace of a matrix
#### Examples
iex> MatrixOperation.trace([[1.0, 2.0], [3.0, 4.0]])
5.0
"""
def trace(a) do
[row, column] = row_column_matrix(a)
a_index = add_index(a)
Enum.map(a_index, & trace_sub(&1, row, column))
|> Enum.sum
end
defp trace_sub(_, row, column) when row != column do
nil
end
defp trace_sub([index, row_list], _, _) do
Enum.at(row_list, index-1)
end
@doc """
A determinant of a n×n square matrix is got.
#### Examples
iex> MatrixOperation.determinant([[1, 2, 1], [2, 1, 0], [1, 1, 2]])
-5
iex> MatrixOperation.determinant([[1, 2, 1, 1], [2, 1, 0, 1], [1, 1, 2, 1], [1, 2, 3, 4]])
-13
iex> MatrixOperation.determinant([ [3,1,1,2,1], [5,1,3,4,1], [2,0,1,0,1], [1,3,2,1,1], [1,1,1,1,1] ])
-14
"""
def determinant(a) do
determinant_sub(1, a)
end
# minor_matrix
defp minor_matrix(a_with_index, row) do
a_with_index -- [row]
|> Enum.map(& Enum.at(&1, 1))
|> Enum.map(& Enum.drop(&1, 1))
end
# 1×1 matrix
defp determinant_sub(_, a) when length(a) == 1 do
nil
end
# 2×2 matrix
defp determinant_sub(co, [[a11, a12], [a21, a22]]) do
co * ( a11 * a22 - a12 * a21 )
end
# 3×3 or over matrix
defp determinant_sub(co, a) do
a_with_index = add_index(a)
Enum.map(a_with_index, & determinant_sub((-1 + 2 * rem(hd(&1), 2)) * co * hd(Enum.at(&1, 1)), minor_matrix(a_with_index, &1)))
|> Enum.sum
end
# add index
defp add_index(a) do
Stream.iterate(1, & (&1 + 1))
|> Enum.zip(a)
|> Enum.map(& &1 |> Tuple.to_list)
end
@doc """
Cramer's rule
#### Examples
iex> MatrixOperation.cramer([1, 0, 0], [[1, 0, 0], [0, 1, 0], [0, 0, 1]], 0)
1.0
"""
def cramer(t, a, select_index) do
det_a = determinant(a)
cramer_sub(t, a, select_index, det_a)
end
defp cramer_sub(_, _, _, nil), do: nil
defp cramer_sub(_, _, _, 0), do: nil
defp cramer_sub(t, a, select_index, det_a) do
rep_det_a = transpose(a) |> replace_element_in_list(select_index, t, 0, []) |> determinant
rep_det_a / det_a
end
defp replace_element_in_list(list, i, replace_element, i, output) when i < length(list) do
replace_element_in_list(list, i, replace_element, i + 1, output ++ [replace_element])
end
defp replace_element_in_list(list, select_index, replace_element, i, output) when i < length(list) do
replace_element_in_list(list, select_index, replace_element, i + 1, output ++ [Enum.at(list, i)])
end
defp replace_element_in_list(list, select_index, replace_element, i, output) when i == length(list), do: output
@doc """
Liner equations are solved.
#### Examples
iex> MatrixOperation.linear_equations([1, 0, 0], [[1, 0, 0], [0, 1, 0], [0, 0, 1]])
[1.0, 0.0, 0.0]
iex> MatrixOperation.linear_equations([3, -7, 4], [[0, -2, 1], [-1, 1, -4], [3, 3, 1]])
[2.0, -1.0, 1.0]
"""
def linear_equations(t, a) do
det_a = determinant(a)
condition_det(t, a, det_a)
end
defp condition_det(_, _, 0) do
nil
end
defp condition_det(t, a, det_a) do
linear_equations_sub(t, a, 0, [])
end
defp linear_equations_sub(t, a, i, output) when i < length(a) do
linear_equations_sub(t, a, i + 1, output ++ [cramer(t, a, i)])
end
defp linear_equations_sub(t, a, i, output) when i == length(a) do
output
end
@doc """
A matrix is multiplied by a constant.
#### Examples
iex> MatrixOperation.const_multiple(-1, [1.0, 2.0, 3.0])
[-1.0, -2.0, -3.0]
iex> MatrixOperation.const_multiple(2, [[1, 2, 3], [2, 2, 2], [3, 8, 9]])
[[2, 4, 6], [4, 4, 4], [6, 16, 18]]
"""
def const_multiple(const, a) when is_number(a) do
const * a
end
def const_multiple(const, a) when is_list(a) do
Enum.map(a, & const_multiple(const, &1))
end
@doc """
Inverse Matrix
#### Examples
iex> MatrixOperation.inverse_matrix([[1, 1, -1], [-2, -1, 1], [-1, -2, 1]])
[[-1.0, -1.0, 0.0], [-1.0, 0.0, -1.0], [-3.0, -1.0, -1.0]]
"""
def inverse_matrix(a) when is_list(hd(a)) do
det_a = determinant(a)
create_index_matrix(a)
|> Enum.map(& map_index_row(a, det_a, &1))
|> transpose
end
def inverse_matrix(_) do
nil
end
defp create_index_matrix(a) do
index_list = Enum.to_list(1..length(a))
Enum.map(index_list, fn x -> Enum.map(index_list, & [x, &1]) end)
end
defp map_index_row(_, 0, _) do
nil
end
defp map_index_row(a, det_a, row) do
Enum.map(row, & minor_matrix(a, det_a, &1))
end
# minor_matrix
defp minor_matrix(a, det_a, [row_number, column_number]) do
det_temp_a = delete_one_row(a, row_number)
|> transpose
|> delete_one_row(column_number)
|> determinant
if(rem(row_number + column_number, 2) == 0, do: det_temp_a / det_a , else: -1 * det_temp_a / det_a)
end
@doc """
Matrix product
#### Examples
iex> MatrixOperation.product([[3, 2, 3], [2, 1, 2]], [[2, 3], [2, 1], [3, 5]])
[[19, 26], [12, 17]]
"""
def product(a, b) do
check_product(a, b)
end
defp check_product(a, b) do
column_number_a = row_column_matrix(a) |> Enum.at(1)
row_number_b = row_column_matrix(b) |> Enum.at(0)
if( column_number_a == row_number_b, do: product_sub(a, b), else: nil)
end
defp product_sub(a, b) do
Enum.map(a, fn row_a ->
transpose(b)
|> Enum.map(& inner_product(row_a, &1))
end)
end
defp inner_product(row_a, column_b) do
Enum.zip(row_a, column_b)
|> Enum.map(& Tuple.to_list(&1))
|> Enum.map(& Enum.reduce(&1, fn x, acc -> x * acc end))
|> Enum.sum
end
@doc """
Matrix addition
#### Examples
iex> MatrixOperation.add([[3, 2, 3], [2, 1, 2]], [[2, 3, 1], [3, 2, 2]])
[[5, 5, 4], [5, 3, 4]]
"""
def add(a, b) do
check_add(a, b)
end
defp check_add(a, b) do
row_column_a = row_column_matrix(a)
row_column_b = row_column_matrix(b)
if( row_column_a == row_column_b, do: add_sub(a, b), else: nil)
end
defp add_sub(a, b) do
Enum.zip(a, b)
|> Enum.map(fn {x, y} ->
Enum.zip(x, y)
|> Enum.map(& Tuple.to_list(&1))
|> Enum.map(& Enum.reduce(&1, fn x, acc -> x + acc end))
end)
end
@doc """
Hadamard product
#### Examples
iex> MatrixOperation.hadamard_product([[3, 2, 3], [2, 1, 2]], [[2, 3, 1], [3, 2, 2]])
[[6, 6, 3], [6, 2, 4]]
"""
def hadamard_product(a, b) do
Enum.zip(a, b)
|> Enum.map(fn {x, y} -> hadamard_product_sub(x, y) end)
end
defp hadamard_product_sub(row_a, row_b) do
Enum.zip(row_a, row_b)
|> Enum.map(& Tuple.to_list(&1))
|> Enum.map(& Enum.reduce(&1, fn x, acc -> x * acc end))
end
@doc """
Matrix subtraction
#### Examples
iex> MatrixOperation.subtract([[3, 2, 3], [2, 1, 2]], [[2, 3, 1], [3, 2, 2]])
[[1, -1, 2], [-1, -1, 0]]
"""
def subtract(a, b) do
check_subtract(a, b)
end
defp check_subtract(a, b) do
row_column_a = row_column_matrix(a)
row_column_b = row_column_matrix(b)
if( row_column_a == row_column_b, do: subtract_sub(a, b), else: nil)
end
defp subtract_sub(a, b) do
Enum.zip(a, b)
|> Enum.map(fn {x, y} ->
Enum.zip(x, y)
|> Enum.map(& Tuple.to_list(&1))
|> Enum.map(& Enum.reduce(&1, fn x, acc -> acc - x end))
end)
end
@doc """
Hadamard division
#### Examples
iex> MatrixOperation.hadamard_division([[3, 2, 3], [2, 1, 2]], [[2, 3, 1], [3, 2, 2]])
[[1.5, 0.6666666666666666, 3.0], [0.6666666666666666, 0.5, 1.0]]
"""
def hadamard_division(a, b) do
Enum.zip(a, b)
|> Enum.map(fn {x, y} -> hadamard_division_sub(x, y) end)
end
defp hadamard_division_sub(row_a, row_b) do
Enum.zip(row_a, row_b)
|> Enum.map(& Tuple.to_list(&1))
|> Enum.map(& Enum.reduce(&1, fn x, acc -> acc / x end))
end
@doc """
Hadamard power
#### Examples
iex> MatrixOperation.hadamard_power([[3, 2, 3], [2, 1, 2]], 2)
[[9.0, 4.0, 9.0], [4.0, 1.0, 4.0]]
"""
def hadamard_power(a, n) do
Enum.map(a, & Enum.map(&1, fn x -> :math.pow(x, n) end))
end
@doc """
Tensor product
#### Examples
iex> MatrixOperation.tensor_product([[3, 2, 3], [2, 1, 2]], [[2, 3, 1], [2, 1, 2], [3, 5, 3]])
[
[
[[6, 9, 3], [6, 3, 6], [9, 15, 9]],
[[4, 6, 2], [4, 2, 4], [6, 10, 6]],
[[6, 9, 3], [6, 3, 6], [9, 15, 9]]
],
[
[[4, 6, 2], [4, 2, 4], [6, 10, 6]],
[[2, 3, 1], [2, 1, 2], [3, 5, 3]],
[[4, 6, 2], [4, 2, 4], [6, 10, 6]]
]
]
"""
def tensor_product(a, b) when is_list(a) do
Enum.map(a, & tensor_product(&1, b))
end
def tensor_product(a, b) when is_number(a) do
const_multiple(a, b)
end
end