///|
/// Add two vectors coordinate by coordinate.
pub fn vector_add(
a : Array[Double],
b : Array[Double],
) -> Array[Double] raise VectorError {
if a.length() != b.length() {
raise DimensionMismatch("Cannot add vectors with different dimensions")
}
if a.length() == 0 {
raise EmptyVector
}
let result = Array::make(a.length(), 0.0)
for i = 0; i < a.length(); i = i + 1 {
result[i] = a[i] + b[i]
}
result
}
///|
/// Subtract one vector from another.
pub fn vector_subtract(
a : Array[Double],
b : Array[Double],
) -> Array[Double] raise VectorError {
if a.length() != b.length() {
raise DimensionMismatch("Cannot subtract vectors with different dimensions")
}
if a.length() == 0 {
raise EmptyVector
}
let result = Array::make(a.length(), 0.0)
for i = 0; i < a.length(); i = i + 1 {
result[i] = a[i] - b[i]
}
result
}
///|
/// Scale a vector by a scalar.
pub fn vector_scale(
vector : Array[Double],
scalar : Double,
) -> Array[Double] raise VectorError {
if vector.length() == 0 {
raise EmptyVector
}
let result = Array::make(vector.length(), 0.0)
for i = 0; i < vector.length(); i = i + 1 {
result[i] = vector[i] * scalar
}
result
}
///|
/// Compute the arithmetic mean of a non-empty vector set.
pub fn vector_mean(
vectors : Array[Array[Double]],
) -> Array[Double] raise VectorError {
let summary = summarize_vectors(vectors)
summary.mean_vector
}
///|
/// Compute a centroid from selected documents.
pub fn document_centroid(
docs : Array[Document],
) -> Array[Double] raise VectorError {
let vectors = []
for doc in docs {
vectors.push(doc.vector)
}
vector_mean(vectors)
}
///|
/// Compute the sum of squared coordinate error around a centroid.
pub fn within_cluster_sse(
docs : Array[Document],
centroid : Array[Double],
) -> Double raise VectorError {
if docs.length() == 0 {
return 0.0
}
let mut total = 0.0
for doc in docs {
let diff = vector_subtract(doc.vector, centroid)
total = total + dot_product(diff, diff)
}
total
}
///|
/// Return the index of the largest coordinate, or no value for an empty vector.
pub fn argmax(vector : Array[Double]) -> Int? {
if vector.length() == 0 {
return None
}
let mut best = 0
for i = 1; i < vector.length(); i = i + 1 {
if vector[i] > vector[best] {
best = i
}
}
Some(best)
}
///|
/// Return the index of the smallest coordinate, or no value for an empty vector.
pub fn argmin(vector : Array[Double]) -> Int? {
if vector.length() == 0 {
return None
}
let mut best = 0
for i = 1; i < vector.length(); i = i + 1 {
if vector[i] < vector[best] {
best = i
}
}
Some(best)
}
///|
/// Clamp every coordinate to an inclusive interval.
pub fn vector_clip(
vector : Array[Double],
low : Double,
high : Double,
) -> Array[Double] {
let result = Array::make(vector.length(), 0.0)
for i = 0; i < vector.length(); i = i + 1 {
result[i] = if vector[i] < low {
low
} else if vector[i] > high {
high
} else {
vector[i]
}
}
result
}
///|
/// Return a matrix of pairwise distances for small diagnostic datasets.
pub fn pairwise_distances(
vectors : Array[Array[Double]],
metric : DistanceMetric,
) -> Array[Array[Double]] raise VectorError {
let matrix = Array::make(vectors.length(), [])
for i = 0; i < vectors.length(); i = i + 1 {
let row = Array::make(vectors.length(), 0.0)
for j = 0; j < vectors.length(); j = j + 1 {
row[j] = calculate_distance(vectors[i], vectors[j], metric)
}
matrix[i] = row
}
matrix
}