package adapter import ( "fmt" "math" "math/rand" "time" ) type randomAdapter struct { sourceDim int targetDim int matrix [][]float32 } func newRandomAdapter(sourceDim, targetDim int, seed int64) *randomAdapter { if seed == 0 { seed = time.Now().UnixNano() } //nolint:gosec // deterministic seeded RNG for projection matrix generation, not security use rng := rand.New(rand.NewSource(seed)) // Gaussian N(0, 1/targetDim) — preserves expected squared norms (Johnson-Lindenstrauss) stddev := 1.0 / math.Sqrt(float64(targetDim)) matrix := make([][]float32, targetDim) for i := range matrix { row := make([]float32, sourceDim) for j := range row { row[j] = float32(rng.NormFloat64() * stddev) } matrix[i] = row } return &randomAdapter{sourceDim: sourceDim, targetDim: targetDim, matrix: matrix} } func (a *randomAdapter) SourceDim() int { return a.sourceDim } func (a *randomAdapter) TargetDim() int { return a.targetDim } func (a *randomAdapter) Adapt(vec []float32) ([]float32, error) { if len(vec) != a.sourceDim { return nil, fmt.Errorf("random adapt: %w: got %d, want %d", ErrDimMismatch, len(vec), a.sourceDim) } return L2Norm(matVecMul(a.matrix, vec)), nil }