"""Small, dependency-free similarity functions used by the Academy lesson.""" from math import sqrt from typing import Callable, Iterable, Sequence, TypeVar Vector = Sequence[float] T = TypeVar("T") def dot(a: Vector, b: Vector) -> float: if len(a) != len(b): raise ValueError("dimension mismatch") return sum(x * y for x, y in zip(a, b)) def l2(vector: Vector) -> float: return sqrt(dot(vector, vector)) def cosine(a: Vector, b: Vector) -> float: denominator = l2(a) * l2(b) if denominator == 0: raise ValueError("cosine is undefined for a zero vector") return dot(a, b) / denominator def normalize(vector: Vector) -> list[float]: norm = l2(vector) if norm == 0: raise ValueError("cannot normalize a zero vector") return [value / norm for value in vector] def rank( query: Vector, candidates: Iterable[tuple[T, Vector]], score: Callable[[Vector, Vector], float], ) -> list[tuple[T, Vector]]: return sorted(candidates, key=lambda item: score(query, item[1]), reverse=True)