Packages

The ExAlgebra library is a collection of functions that are commonly used in linear algebra.

Current section

Files

Jump to
exalgebra lib Vector vector.ex
Raw

lib/Vector/vector.ex

defmodule ExAlgebra.Vector do
@moduledoc """
*ExAlgebra.Vector* contains functions used in vector algebra.
"""
import :math, only: [sqrt: 1]
alias ExAlgebra.Matrix, as: Matrix
@doc """
Returns the addition of two vectors.
##### Examples
iex> vector = [1, 2, 3]
[1, 2, 3]
iex> vector |> ExAlgebra.Vector.add([2, 3, 4])
[3, 5, 7]
"""
@spec add([number], [number]) :: [number]
def add([], []), do: []
def add([h1 | t1], [h2 | t2]) do
[h1 + h2 | add(t1, t2)]
end
@doc """
Returns the subtraction of two vectors.
##### Examples
iex> vector = [1, 2, 3]
[1, 2, 3]
iex> vector |> ExAlgebra.Vector.subtract([2, 3, 4])
[-1, -1, -1]
"""
@spec subtract([number], [number]) :: [number]
def subtract([], []), do: []
def subtract([h1 | t1], [h2 | t2]) do
[h1 - h2 | subtract(t1, t2)]
end
@doc """
Returns the multiple of a vector by a scalar.
##### Examples
iex> vector = [1, 2, 3]
[1, 2, 3]
iex> vector |> ExAlgebra.Vector.scalar_multiply(2.5)
[2.5, 5, 7.5]
"""
@spec scalar_multiply([number], number) :: [number]
def scalar_multiply(vector, scalar), do: vector |> Enum.map(&(&1 * scalar))
@doc """
Returns the dot product of two vectors.
##### Examples
iex> vector = [1, 2, 3]
[1, 2, 3]
iex> vector |> ExAlgebra.Vector.dot([2, 3, 4])
[2, 6, 12]
"""
@spec dot([number], [number]) :: number
def dot([], []), do: 0
def dot([h1 | t1], [h2 | t2]) do
h1 * h2 + dot(t1, t2)
end
@doc """
Returns the magnitude of a vector.
##### Examples
iex> vector = [1, 2, 3, 4]
[1, 2, 3, 4]
iex> vector |> ExAlgebra.Vector.magnitude
5.4772255751
"""
@spec magnitude([number]) :: number
def magnitude(vector), do: vector |> sqr_magnitude |> sqrt
@doc """
Returns the square of the magnitude of a vector.
##### Examples
iex> vector = [1, 2, 3, 4]
[1, 2, 3, 4]
iex> vector |> ExAlgebra.Vector.sqr_magnitude
30
"""
@spec sqr_magnitude([number]) :: number
def sqr_magnitude(vector), do: vector |> dot(vector)
@doc """
Returns the normalization of a vector.
##### Examples
iex> vector = [1, 2, 3, 4]
[1, 2, 3, 4]
iex> vector |> ExAlgebra.Vector.sqr_magnitude
[0.1825741858, 0.3651483717, 0.5477225575, 0.7302967433]
"""
@spec normalize([number]) :: [number]
def normalize(vector), do: vector |> scalar_multiply(1 / magnitude(vector))
@doc """
Returns the distance between two vectors.
##### Examples
iex> vector = [1, 2, 3]
[1, 2, 3]
iex> vector |> ExAlgebra.Vector.sqr_magnitude([4, 5, 6])
5.1961524227
"""
@spec distance([number], [number]) :: number
def distance(vector_one, vector_two), do: (vector_one |> subtract(vector_two)) |> magnitude
@doc """
Returns true iff two vectors are orthogonal.
##### Examples
iex> vector = [1, 1, 1]
[1, 1, 1]
iex> vector |> ExAlgebra.Vector.is_orthogonal?([-2, 1, 1])
true
iex> vector = [1, 1, 1]
[1, 1, 1]
iex> vector |> ExAlgebra.Vector.is_orthogonal?([1, 1, 1])
false
"""
@spec is_orthogonal?([number], [number]) :: boolean
def is_orthogonal?(vector_one, vector_two), do: vector_one |> dot(vector_two) == 0
@doc """
Returns the projection of one vector by another.
##### Examples
iex> vector = [1, 2, 4, 0]
[1, 2, 4, 0]
iex> vector |> ExAlgebra.Vector.project([0, 1, 1, 0])
[2/7, 4/7, 8/7, 0]
"""
@spec project([number], [number]) :: [number]
def project(vector_one, vector_two) do
vector_one |> scalar_multiply(dot(vector_one, vector_two) / dot(vector_one, vector_one))
end
@doc """
Returns an orthogonal vector from a vector and a set of linearly
independent vectors.
##### Examples
iex> vector = [0, 1, 1, 0]
[0, 1, 1, 0]
iex> vector |> ExAlgebra.Vector.create_orthogonal_vector([[1, 2, 4, 0]])
[-2/7, 3/7, -1/7, 0]
"""
@spec project([number], [[number]]) :: [number]
def create_orthogonal_vector(vector, linearly_independent_vectors) do
linearly_independent_vectors |> List.foldl(vector, &subtract(&2, project(&1, &2)))
end
@doc """
Returns an orthogonal basis from a set of linearly independent vectors.
##### Examples
iex> vectors = [[1, 2, 4, 0], [0, 1, 1, 0], [0, 3, 1, 4]]
[[1, 2, 4, 0], [0, 1, 1, 0], [0, 3, 1, 4]]
iex> vectors |> ExAlgebra.Vector.create_orthogonal_basis
[[1, 2, 4, 0], [-2/7, 3/7, -1/7, 0], [2/3, 1/3, -1/3, 4]]
"""
@spec create_orthogonal_basis([[number]]) :: [[number]]
def create_orthogonal_basis([first_vector | remaining_vectors] = _linearly_independent_vectors) do
remaining_vectors |> List.foldl([first_vector], &(&2 ++ [create_orthogonal_vector(&1, &2)]))
end
@doc """
Returns an orthonormal basis from a set of linearly independent vectors.
This uses the modified Gram Schmidt algorithm.
##### Examples
iex> vectors = [[1, 1, 1], [2, 1, 0], [5, 1, 3]]
[[1, 1, 1], [2, 1, 0], [5, 1, 3]]
iex> vectors |> ExAlgebra.Vector.create_orthonormal_basis
[[0.57735026919, 0.57735026919, 0.57735026919], [0.70710678118, 0, -0.70710678118], [0.40824829046, -0.81649658092, 0.40824829046]]
"""
@spec create_orthogonal_basis([[number]]) :: [[number]]
def create_orthonormal_basis(linearly_independent_vectors) do
linearly_independent_vectors |> create_orthogonal_basis |> Enum.map(&normalize(&1))
end
@doc """
Returns true if and only if a set of vectors are linearly independent.
##### Examples
iex> vectors = [[1, 1, 1], [2, 1, 0], [5, 1, 3]]
[[1, 1, 1], [2, 1, 0], [5, 1, 3]]
iex> vector |> ExAlgebra.Vector.is_linearly_independent?
true
iex> vectors = [[2, 3, 5], [-1, -4, -10], [1, -2, -8]]
[[2, 3, 5], [-1, -4, -10], [1, -2, -8]]
iex> vector |> ExAlgebra.Vector.is_linearly_independent?
false
"""
@spec is_linearly_independent?([[number]]) :: boolean
def is_linearly_independent?(vectors), do: Matrix.det(vectors) != 0
end