diff --git a/la/vector.v b/la/vector.v index d45b978f8..42fb36484 100644 --- a/la/vector.v +++ b/la/vector.v @@ -1,6 +1,7 @@ module la import math +import vsl.errors // vector_apply sets this []T with the scaled components of another []T // this := a * another ⇒ this[i] := a * another[i] @@ -80,3 +81,20 @@ pub fn vector_largest[T](o []T, den T) T { } return largest / den } + +// vector_cosine_similarity calculates the cosine similarity between two vectors +pub fn vector_cosine_similarity(a []f64, b []f64) f64 { + if a.len != b.len { + errors.vsl_panic('Vectors must have the same length', .efailed) + } + + dot_product := vector_dot(a, b) + norm_a := vector_norm(a) + norm_b := vector_norm(b) + + if norm_a == 0 || norm_b == 0 { + return 0 + } + + return dot_product / (norm_a * norm_b) +} diff --git a/la/vector_test.v b/la/vector_test.v index 65898c113..6fb801347 100644 --- a/la/vector_test.v +++ b/la/vector_test.v @@ -70,3 +70,23 @@ fn test_vector_largest() { l := 2.0 assert float64.close(vector_largest(a, 2), l) } + +fn test_vector_cosine_similarity() { + a := [1.0, 2.0, 3.0] + b := [4.0, 5.0, 6.0] + c := [1.0, 0.0, 0.0] + d := [0.0, 1.0, 0.0] + + // Cosine similarity of a and b (should be close to 0.974631846) + assert float64.tolerance(vector_cosine_similarity(a, b), 0.974631846, 1e-8) + + // Cosine similarity of a with itself (should be exactly 1) + assert float64.close(vector_cosine_similarity(a, a), 1.0) + + // Cosine similarity of perpendicular vectors (should be 0) + assert float64.close(vector_cosine_similarity(c, d), 0.0) + + // Cosine similarity of a zero vector with another vector (should be 0) + zero_vector := [0.0, 0.0, 0.0] + assert float64.close(vector_cosine_similarity(zero_vector, a), 0.0) +}