Skip to main content

CosineSimilarity

Calculates the cosine similarity between two embedding vectors. Returns a value between -1 (opposite) and 1 (identical).

Signature​

func CosineSimilarity(a, b []float64) (float64, error)

Parameters​

ParameterTypeDescription
a[]float64First embedding vector
b[]float64Second embedding vector

Return Value​

Returns a float64 between -1 and 1:

  • 1.0: Vectors are identical
  • 0.0: Vectors are orthogonal (no similarity)
  • -1.0: Vectors are opposite

Zero and empty vectors​

A zero vector (all-zero components) or an empty (len == 0) vector on either side returns (0, nil) — not an error — matching the TypeScript AI SDK. Only a length mismatch between a and b returns an error, a typed *errors.InvalidArgumentError with message "Vectors must have the same length (vector1Length=…, vector2Length=…)".

Examples​

Basic Similarity Calculation​

package main

import (
"context"
"fmt"
"log"

"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
)

func main() {
provider := openai.New(openai.Config{
APIKey: "your-api-key",
})
model, _ := provider.EmbeddingModel("text-embedding-3-small")

// Embed two texts
result1, _ := ai.Embed(context.Background(), ai.EmbedOptions{
Model: model,
Input: "machine learning",
})

result2, _ := ai.Embed(context.Background(), ai.EmbedOptions{
Model: model,
Input: "artificial intelligence",
})

// Calculate similarity
similarity, err := ai.CosineSimilarity(result1.Embedding, result2.Embedding)
if err != nil {
log.Fatal(err)
}

fmt.Printf("Similarity: %.4f\n", similarity)
}
// Query embedding
queryResult, _ := ai.Embed(ctx, ai.EmbedOptions{
Model: model,
Input: "neural networks",
})

// Document embeddings
documents := []string{
"Deep learning uses neural networks",
"The weather is sunny today",
"Artificial neural networks are computational models",
}

// Find most similar
var bestMatch string
var bestScore float64

for _, doc := range documents {
docResult, _ := ai.Embed(ctx, ai.EmbedOptions{
Model: model,
Input: doc,
})

similarity, _ := ai.CosineSimilarity(queryResult.Embedding, docResult.Embedding)

if similarity > bestScore {
bestScore = similarity
bestMatch = doc
}
}

fmt.Printf("Best match (%.4f): %s\n", bestScore, bestMatch)

Similarity Threshold​

similarity, err := ai.CosineSimilarity(embedding1, embedding2)
if err != nil {
log.Fatal(err)
}

switch {
case similarity > 0.9:
fmt.Println("Highly similar")
case similarity > 0.7:
fmt.Println("Moderately similar")
case similarity > 0.5:
fmt.Println("Somewhat similar")
default:
fmt.Println("Not similar")
}

Pairwise Similarity Matrix​

embeddings := [][]float64{
embedding1,
embedding2,
embedding3,
}

fmt.Println("Similarity Matrix:")
for i := 0; i < len(embeddings); i++ {
for j := 0; j < len(embeddings); j++ {
sim, _ := ai.CosineSimilarity(embeddings[i], embeddings[j])
fmt.Printf("%.3f ", sim)
}
fmt.Println()
}

Error Handling​

CosineSimilarity only returns an error for a length mismatch; a zero or empty vector is not an error condition.

similarity, err := ai.CosineSimilarity(a, b)
if err != nil {
var invalidArg *providererrors.InvalidArgumentError
if errors.As(err, &invalidArg) {
log.Printf("Dimension mismatch: %v", invalidArg)
} else {
log.Println("Unknown error:", err)
}
return
}
// similarity is 0 (not an error) if either vector is all-zero or empty.

See Also​

  • Embed - Generate embeddings
  • EmbedMany - Batch embeddings
  • EuclideanDistance - Alternative distance metric
  • DotProduct - Dot product similarity
  • RAG Guide