Adde some stuff to Vector
This commit is contained in:
parent
d5482eae70
commit
ef28e84ecc
|
@ -9,8 +9,12 @@ 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
|
# # Vector Creation
|
||||||
## ## ## ## ## ## ## ## ## ## ## ## ##
|
## ## ## ## ## ## ## ## ## ## ## ## ##
|
||||||
|
@ -110,4 +114,74 @@ module Apatite
|
||||||
def scalar(n, value)
|
def scalar(n, value)
|
||||||
Matrix.scalar(n, value)
|
Matrix.scalar(n, value)
|
||||||
end
|
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 between two vectors.
|
||||||
|
def similarity(x, y)
|
||||||
|
dot(x, y) / (
|
||||||
|
Math.sqrt(dot(x, x)) *
|
||||||
|
Math.sqrt(dot(y, 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
|
||||||
|
|
|
@ -371,6 +371,10 @@ module Apatite::LinearAlgebra
|
||||||
multiply(other)
|
multiply(other)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def /(other)
|
||||||
|
divide(other)
|
||||||
|
end
|
||||||
|
|
||||||
def clone
|
def clone
|
||||||
Vector.create(@elements.clone)
|
Vector.create(@elements.clone)
|
||||||
end
|
end
|
||||||
|
@ -435,51 +439,35 @@ module Apatite::LinearAlgebra
|
||||||
r == 0 ? dup : map { |x| x.to_f64 / r }
|
r == 0 ? dup : map { |x| x.to_f64 / r }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# ditto
|
||||||
|
def normalize
|
||||||
|
to_unit_vector
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the angle between this vector and another in radians.
|
# Returns the angle between this vector and another in radians.
|
||||||
# If the vectors are mirrored across their axes this will return `nil`.
|
# If the vectors are mirrored across their axes this will return `nil`.
|
||||||
def angle_from(vector)
|
def angle_from(vector)
|
||||||
v = vector.is_a?(Vector) ? vector : Vector.create(vector)
|
Apatite.angle_from(self, vector)
|
||||||
|
|
||||||
unless size == v.size
|
|
||||||
raise "Cannot compute the angle between vectors with different dimensionality"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
dot = 0_f64
|
# Compute the cosine similarity between this vector and another.
|
||||||
mod1 = 0_f64
|
def similarity(other)
|
||||||
mod2 = 0_f64
|
Apatite.similarity(self, other)
|
||||||
|
|
||||||
zip(vector).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
|
end
|
||||||
|
|
||||||
# Returns whether the vectors are parallel to each other.
|
# Returns whether the vectors are parallel to each other.
|
||||||
def parallel_to?(vector)
|
def parallel_to?(vector)
|
||||||
angle = angle_from(vector)
|
Apatite.parallel_to?(self, vector)
|
||||||
angle <= Apatite.precision
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns whether the vectors are antiparallel to each other.
|
# Returns whether the vectors are antiparallel to each other.
|
||||||
def antiparallel_to?(vector)
|
def antiparallel_to?(vector)
|
||||||
angle = angle_from(vector)
|
Apatite.antiparallel_to?(self, vector)
|
||||||
(angle - Math::PI).abs <= Apatite.precision
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns whether the vectors are perpendicular to each other.
|
# Returns whether the vectors are perpendicular to each other.
|
||||||
def perpendicular_to?(vector)
|
def perpendicular_to?(vector)
|
||||||
(dot(vector)).abs <= Apatite.precision
|
Apatite.perpendicular_to?(self, vector)
|
||||||
end
|
end
|
||||||
|
|
||||||
# When the input is a number, this returns the result of adding
|
# When the input is a number, this returns the result of adding
|
||||||
|
@ -503,6 +491,13 @@ module Apatite::LinearAlgebra
|
||||||
run_binary_op(value) { |a, b| a * b }
|
run_binary_op(value) { |a, b| a * b }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# When the input is a number, this returns the result of dividing
|
||||||
|
# it to all vector elements. When it's a vector, the vectors
|
||||||
|
# will be element-wise divided.
|
||||||
|
def divide(value)
|
||||||
|
run_binary_op(value) { |a, b| a / b }
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the sum of all elements in the vector.
|
# Returns the sum of all elements in the vector.
|
||||||
def sum
|
def sum
|
||||||
reduce(0) { |acc, i| acc + i }
|
reduce(0) { |acc, i| acc + i }
|
||||||
|
@ -573,16 +568,7 @@ module Apatite::LinearAlgebra
|
||||||
# [https://en.wikipedia.org/wiki/Scalar_product](https://en.wikipedia.org/wiki/Scalar_product)
|
# [https://en.wikipedia.org/wiki/Scalar_product](https://en.wikipedia.org/wiki/Scalar_product)
|
||||||
def dot(other)
|
def dot(other)
|
||||||
other = other.is_a?(Vector) ? other : Vector.create(other)
|
other = other.is_a?(Vector) ? other : Vector.create(other)
|
||||||
unless size == other.size
|
Apatite.dot(self, other)
|
||||||
raise "Cannot compute the dot product of vectors with different dimensionality"
|
|
||||||
end
|
|
||||||
|
|
||||||
product = 0
|
|
||||||
(0...size).each do |i|
|
|
||||||
product += self[i] * other[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
product
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the (absolute) largest element in this vector.
|
# Returns the (absolute) largest element in this vector.
|
||||||
|
@ -811,8 +797,9 @@ module Apatite::LinearAlgebra
|
||||||
end
|
end
|
||||||
|
|
||||||
# Call a block on the value
|
# Call a block on the value
|
||||||
private def run_binary_op(value, &block : (T, T) -> T)
|
private def run_binary_op(value, &block : (Float64, Float64) -> Float64)
|
||||||
if value.is_a?(Number)
|
if value.is_a?(Number)
|
||||||
|
value = value.to_f64
|
||||||
return map { |v| yield(v, value) }
|
return map { |v| yield(v, value) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue