photoprism/pkg/vector/vector.go
Michael Mayer 03129c9129 Vector: Reorganize package into topic-based files #4669
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.
2026-05-30 09:44:03 +00:00

215 lines
4.7 KiB
Go

/*
Package vector provides floating-point vector types and math, including
distances, norms, means, and correlation.
Copyright (c) 2018 - 2026 PhotoPrism UG. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under Version 3 of the GNU Affero General Public License (the "AGPL"):
<https://docs.photoprism.app/license/agpl>
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
The AGPL is supplemented by our Trademark and Brand Guidelines,
which describe how our Brand Assets may be used:
<https://www.photoprism.app/trademark>
Feel free to send an email to hello@photoprism.app if you have questions,
want to support our work, or just want to say hello.
Additional information can be found in our Developer Guide:
<https://docs.photoprism.app/developer-guide/>
*/
package vector
import (
"fmt"
)
// Vector represents a set of floating-point values.
type Vector []float64
// Vectors represents a set of vectors.
type Vectors = []Vector
// NewVector creates a new vector from the given values.
func NewVector(values any) (Vector, error) {
switch v := values.(type) {
case []uint8:
return uint8ToVector(v), nil
case []uint16:
return uint16ToVector(v), nil
case []uint32:
return uint32ToVector(v), nil
case []uint64:
return uint64ToVector(v), nil
case []int:
return intToVector(v), nil
case []int8:
return int8ToVector(v), nil
case []int16:
return int16ToVector(v), nil
case []int32:
return int32ToVector(v), nil
case []int64:
return int64ToVector(v), nil
case []float32:
return float32ToVector(v), nil
case []float64:
return float64ToVector(v), nil
case Vector:
return v.Copy(), nil
default:
return nil, fmt.Errorf("cannot create vector from type %T", values)
}
}
// NullVector creates a new null vector with the given dimension.
func NullVector(dim int) Vector {
return make(Vector, dim)
}
// Copy returns a copy of the vector.
func (v Vector) Copy() Vector {
y := make(Vector, len(v))
copy(y, v)
return y
}
// Dim returns the number of values (dimension).
func (v Vector) Dim() int {
return len(v)
}
// Sum returns the sum of the vector values.
func (v Vector) Sum() float64 {
s := 0.0
for _, f := range v {
s += f
}
return s
}
// uint8ToVector creates a new vector from a uint8 slice.
func uint8ToVector(values []uint8) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// uint16ToVector creates a new vector from a uint16 slice.
func uint16ToVector(values []uint16) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// uint32ToVector creates a new vector from a uint32 slice.
func uint32ToVector(values []uint32) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// uint64ToVector creates a new vector from a uint64 slice.
func uint64ToVector(values []uint64) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// intToVector creates a new vector from a int slice.
func intToVector(values []int) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// int8ToVector creates a new vector from a int8 slice.
func int8ToVector(values []int8) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// int16ToVector creates a new vector from a int16 slice.
func int16ToVector(values []int16) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// int32ToVector creates a new vector from a int32 slice.
func int32ToVector(values []int32) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// int64ToVector creates a new vector from a int64 slice.
func int64ToVector(values []int64) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// float32ToVector creates a new vector from a float32 slice.
func float32ToVector(values []float32) Vector {
v := make(Vector, len(values))
for i := range values {
v[i] = float64(values[i])
}
return v
}
// float64ToVector creates a new vector from a float64 slice.
// The values are copied so the result is independent of the input slice,
// consistent with the other type-specific converters.
func float64ToVector(values []float64) Vector {
v := make(Vector, len(values))
copy(v, values)
return v
}