Split the catch-all values.go into one file per concept, each mirrored
by its test: distance.go, norm.go, stats.go, product.go, centroid.go,
plus the mean methods folded into mean.go and Copy/Dim/Sum into
vector.go. Remove values.go, values_test.go, and values_more_test.go so
functionality and tests live where developers expect them.
Hoist the two 512-dimensional face embeddings shared by the distance,
norm, and cosine tests into fixtures_test.go, removing the previous
triplication, and decompose the monolithic TestVector into per-concept
tests. Close pre-existing coverage gaps in the integer converters and
the GeometricMean/HarmonicMean method wrappers, bringing the package to
100% statement coverage. Pure code movement; no behavior change.
Prepare pkg/vector for a future refactoring of internal/ai/face without
touching the face package yet. Normalize scales a vector to unit length
(L2) in place and leaves a zero vector unchanged; Normalized returns a
normalized copy. Centroid returns the element-wise mean of a set of
vectors, ignoring entries whose length differs from the first and
averaging over the vectors actually included. Together with the existing
EuclideanDist, EuclideanNorm, and NewVector helpers these cover the math
currently hand-rolled in the face embeddings code. Fully covered by
tests (100% of the new functions).
The root file of the vector package was missing the standard package
description and AGPL header required by the Go style rules. The vendored
alg subpackage keeps its own MIT license and is left unchanged.
CosineDist now returns a true cosine distance (1 - similarity), so
identical vectors yield 0; the previous similarity behavior moves to a
new CosineSimilarity function/method. GeometricMean no longer treats a
zero element as an uninitialized accumulator: zeros now correctly yield
0 and negative inputs return NaN. NewVector copies float64 and Vector
inputs so the result is independent of the source slice, matching the
other converters. Norm uses absolute values so odd powers (L1) stay
well-defined for negative components, variance is simplified, and the
numeric error paths return NaN instead of the Epsilon sentinel. Add
focused tests for the mean, distance, norm, and copy helpers.
Extract the per-worker scan range size into partitionSize() and floor
it at 1 so the nearest() dispatch loop always advances and terminates,
even if the worker count exceeds the number of data points. The size
based numWorkers buckets keep points >= workers today, so this is a
defensive guard against future tuning. Add focused tests for the helper
and a high-worker end-to-end case.
The vendored OPTICS implementation was never invoked: face clustering
uses DBSCAN (alg.DBSCANWithProgress), and OPTICS() had no callers or
tests. Removing it also drops a latent no-op loop (PVS-Studio V8016)
in extract(). Drop the now-orphaned errZeroXi and update the rangeJob
comment and README to match. See discussion #5628.
Run `go fix ./...` and keep mechanical modernization updates.
- Replace `interface{}` with `any` in signatures and local types
- Apply formatter/style cleanups from go1.26 tooling
- Keep `omitempty` behavior-preserving simplifications suggested by fix
- No functional feature changes intended
Validation:
- go test ./... -run '^$' -count=1 (Go 1.26.0)
- GOTOOLCHAIN=go1.24.10 go test ./... -run '^$' -count=1
Signed-off-by: Michael Mayer <michael@photoprism.app>