First part of refactor finished

This commit is contained in:
Chris 2019-07-08 21:14:03 -07:00
parent feafd6df36
commit 3354307427
No known key found for this signature in database
GPG Key ID: 37DAEF5F446370A4
5 changed files with 210 additions and 973 deletions

View File

@ -9,189 +9,6 @@ require "./apatite/linear_algebra"
# of Crystal. # of Crystal.
module Apatite module Apatite
extend self extend self
include Apatite::LinearAlgebra include Apatite::LinearAlgebra
class_property precision = 1e-6
class_property approx_precision = 1e-5
## ## ## ## ## ## ## ## ## ## ## ## ##
# # Vector Creation
## ## ## ## ## ## ## ## ## ## ## ## ##
# Cartesian unit vector I
#
# `Vector{1.0, 0.0, 0.0}`
I = Vector::I
# Cartesian unit vector J
#
# `Vector{0.0, 1.0, 0.0}`
J = Vector::J
# Cartesian unit vector K
#
# `Vector{0.0, 0.0, 1.0}`
K = Vector::K
# Returns a new empty `Vector`
def empty
Vector.new
end
# Returns a new vector filled with `n` ones.
def ones(n)
Vector.ones(n)
end
# Returns a new vector filled with `n` zeros.
def zeros(n)
Vector.zeros(n)
end
# Returns a new vector of size `n` filled with `i`
def full(n, i)
Vector.new(n, i)
end
# Creates a new vector of size `n`, and invokes the block once
# for each index of `self`, assigning the block's value in that index.
def vector(n, &block)
Vector.new(n) { |i| yield i }
end
# Creates a new vector from the given `input`. Input can be any
# `Indexable` type.
def as_vector(input : Indexable)
Vector.create(input)
end
# Creates a standard basis-n vector of the given `size` and `index`.
def basis(size, index)
Vector.basis(size, index)
end
# Creates a new vector of size `n` filled with random numbers. A `range`
# can optionally be passed in if you want to limit the random numbers
# to a given range.
def random(n, range = nil)
Vector.random(n, range)
end
## ## ## ## ## ## ## ## ## ## ## ##
# # Matrix Creation
## ## ## ## ## ## ## ## ## ## ## ##
# Creates a new empty matrix with the given `row_count` and
# `column_count`. At lease one of `row_count` or
# `column_count` must be zero.
def empty_matrix(row_count = 0, column_count = 0)
Matrix.empty(row_count, column_count)
end
# Creates a matrix of the given shape with random vectors.
def random_matrix(row_count, column_count, range = nil)
Matrix.random(row_count, column_count, range)
end
# Creates a matrix where the diagonal elements are composed of `values`.
def diagonal(values)
Matrix.diagonal(values)
end
# Creates a new diagonal matrix of size `n` with ones in the diagonal
# and zeros elsewhere.
def eye(n)
Matrix.eye(n)
end
# Creates a `n x n` identity matrix.
def identity(n)
Matrix.identity(n)
end
# Creates a single-row matrix where the values of that row are as given in `row`.
def row_vector(row)
Matrix.row_vector(row)
end
# Creates an `n` by `n` diagonal matrix where each diagonal element is value.
def scalar(n, value)
Matrix.scalar(n, value)
end
## ## ## ## ## ## ## ## ## ## ## ##
# # Vector Manipulation
## ## ## ## ## ## ## ## ## ## ## ##
# Get the scalar (dot) product of two vectors.
#
# [https://en.wikipedia.org/wiki/Scalar_product](https://en.wikipedia.org/wiki/Scalar_product
def dot(x, y)
unless x.size == y.size
raise "Cannot compute the dot product of vectors with different dimensionality"
end
(0...x.size).reduce(0) do |acc, i|
acc + x[i] * y[i]
end
end
# Compute the cosine similarity of two vectors.
def similarity(x, y)
dot(x, y) / (
Math.sqrt(dot(x, x)) *
Math.sqrt(dot(y, y))
)
end
# Compute the cosine distance between two vectors.
def cosine(x, y)
1.0 - similarity(x, y)
end
# Returns the angle between this vector and another in radians.
# If the vectors are mirrored across their axes this will return `nil`.
def angle_from(a, b)
unless a.size == b.size
raise "Cannot compute the angle between vectors with different dimensionality"
end
dot = 0_f64
mod1 = 0_f64
mod2 = 0_f64
a.zip(b).each do |x, v|
dot += x * v
mod1 += x * x
mod2 += v * v
end
mod1 = Math.sqrt(mod1)
mod2 = Math.sqrt(mod2)
if mod2 * mod2 == 0
return 0.0
end
theta = (dot / (mod1 * mod2)).clamp(-1, 1)
Math.acos(theta)
end
# Returns whether the vectors are parallel to each other.
def parallel_to?(a, b)
angle = angle_from(a, b)
angle <= precision
end
# Returns whether the vectors are antiparallel to each other.
def antiparallel_to?(a, b)
angle = angle_from(a, b)
(angle - Math::PI).abs <= precision
end
# Returns whether the vectors are perpendicular to each other.
def perpendicular_to?(a, b)
(dot(a, b)).abs <= precision
end
end end

View File

@ -1,5 +1,6 @@
require "./linear_algebra/ndarray" require "./linear_algebra/error"
require "./linear_algebra/matrix" # require "./linear_algebra/ndarray"
# require "./linear_algebra/matrix"
require "./linear_algebra/vector" require "./linear_algebra/vector"
module Apatite module Apatite

View File

@ -0,0 +1,8 @@
module Apatite
module LinearAlgebra
class Error < Exception; end
class ErrDimensionMismatch < Error; end
class ZeroVectorError < Error; end
class ErrOperationNotDefined < Error; end
end
end

View File

@ -278,19 +278,13 @@ module Apatite::LinearAlgebra
# Returns column vector number `j` of the matrix as a `Vector` (starting at 0 like an array). # Returns column vector number `j` of the matrix as a `Vector` (starting at 0 like an array).
def column?(j) def column?(j)
return nil if j >= column_count || j < -column_count return nil if j >= column_count || j < -column_count
col = Array(Float64).new(row_count) { |i| Vector.new(row_count) { |i| rows[i][j] }
rows[i][j]
}
Vector.create(col)
end end
# Returns column vector number `j` of the matrix as a `Vector` (starting at 0 like an array). # Returns column vector number `j` of the matrix as a `Vector` (starting at 0 like an array).
def column(j) def column(j)
raise "Index out of range" if j >= column_count || j < -column_count raise "Index out of range" if j >= column_count || j < -column_count
col = Array(Float64).new(row_count) { |i| Vector.new(row_count) { |i| rows[i][j] }
rows[i][j]
}
Vector.create(col)
end end
# Iterates over the specified column in the matrix, returning the Vector's items. # Iterates over the specified column in the matrix, returning the Vector's items.
@ -304,7 +298,7 @@ module Apatite::LinearAlgebra
# Returns an array of the column vectors of the matrix. See `Vector`. # Returns an array of the column vectors of the matrix. See `Vector`.
def column_vectors def column_vectors
Array(Vector).new(column_count) { |i| Matrix.new(column_count) { |i|
column(i) column(i)
} }
end end
@ -509,18 +503,13 @@ module Apatite::LinearAlgebra
end end
end end
def row(i, &block)
rows.fetch(i) { return self }.each(&block)
self
end
def row(i) def row(i)
Vector.create(rows.fetch(i) { [] of Float64 }) self[i - 1].dup
end end
def rows def rows(start = 0, stop = row_count)
rows = [] of Vector rows = [] of Vector
row_count.times do |i| start.upto(stop) do |i|
rows << self[i - 1] rows << self[i - 1]
end end
rows rows

File diff suppressed because it is too large Load Diff