Open source vector database with HNSW indexing, graph relationships, and metadata facets. Features CLI with professional augmentation registry integration for discovering extensions and capabilities.
166 lines
No EOL
6.8 KiB
JavaScript
166 lines
No EOL
6.8 KiB
JavaScript
/**
|
|
* Distance functions for vector similarity calculations
|
|
* Optimized pure JavaScript implementations using enhanced array methods
|
|
* Faster than GPU for small vectors (384 dims) due to no transfer overhead
|
|
*/
|
|
/**
|
|
* Calculates the Euclidean distance between two vectors
|
|
* Lower values indicate higher similarity
|
|
* Optimized using array methods for Node.js 23.11+
|
|
*/
|
|
export const euclideanDistance = (a, b) => {
|
|
if (a.length !== b.length) {
|
|
throw new Error('Vectors must have the same dimensions');
|
|
}
|
|
// Use array.reduce for better performance in Node.js 23.11+
|
|
const sum = a.reduce((acc, val, i) => {
|
|
const diff = val - b[i];
|
|
return acc + diff * diff;
|
|
}, 0);
|
|
return Math.sqrt(sum);
|
|
};
|
|
/**
|
|
* Calculates the cosine distance between two vectors
|
|
* Lower values indicate higher similarity
|
|
* Range: 0 (identical) to 2 (opposite)
|
|
* Optimized using array methods for Node.js 23.11+
|
|
*/
|
|
export const cosineDistance = (a, b) => {
|
|
if (a.length !== b.length) {
|
|
throw new Error('Vectors must have the same dimensions');
|
|
}
|
|
// Use array.reduce to calculate all values in a single pass
|
|
const { dotProduct, normA, normB } = a.reduce((acc, val, i) => {
|
|
return {
|
|
dotProduct: acc.dotProduct + val * b[i],
|
|
normA: acc.normA + val * val,
|
|
normB: acc.normB + b[i] * b[i]
|
|
};
|
|
}, { dotProduct: 0, normA: 0, normB: 0 });
|
|
if (normA === 0 || normB === 0) {
|
|
return 2; // Maximum distance for zero vectors
|
|
}
|
|
const similarity = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
// Convert cosine similarity (-1 to 1) to distance (0 to 2)
|
|
return 1 - similarity;
|
|
};
|
|
/**
|
|
* Calculates the Manhattan (L1) distance between two vectors
|
|
* Lower values indicate higher similarity
|
|
* Optimized using array methods for Node.js 23.11+
|
|
*/
|
|
export const manhattanDistance = (a, b) => {
|
|
if (a.length !== b.length) {
|
|
throw new Error('Vectors must have the same dimensions');
|
|
}
|
|
// Use array.reduce for better performance in Node.js 23.11+
|
|
return a.reduce((sum, val, i) => sum + Math.abs(val - b[i]), 0);
|
|
};
|
|
/**
|
|
* Calculates the dot product similarity between two vectors
|
|
* Higher values indicate higher similarity
|
|
* Converted to a distance metric (lower is better)
|
|
* Optimized using array methods for Node.js 23.11+
|
|
*/
|
|
export const dotProductDistance = (a, b) => {
|
|
if (a.length !== b.length) {
|
|
throw new Error('Vectors must have the same dimensions');
|
|
}
|
|
// Use array.reduce for better performance in Node.js 23.11+
|
|
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
|
|
// Convert to a distance metric (lower is better)
|
|
return -dotProduct;
|
|
};
|
|
/**
|
|
* Batch distance calculation using optimized JavaScript
|
|
* More efficient than GPU for small vectors due to no memory transfer overhead
|
|
*
|
|
* @param queryVector The query vector to compare against all vectors
|
|
* @param vectors Array of vectors to compare against
|
|
* @param distanceFunction The distance function to use
|
|
* @returns Promise resolving to array of distances
|
|
*/
|
|
export async function calculateDistancesBatch(queryVector, vectors, distanceFunction = euclideanDistance) {
|
|
// For small batches, use the standard distance function
|
|
if (vectors.length < 10) {
|
|
return vectors.map((vector) => distanceFunction(queryVector, vector));
|
|
}
|
|
try {
|
|
// Function for optimized batch distance calculation
|
|
const distanceCalculator = (args) => {
|
|
const { queryVector, vectors, distanceFnString } = args;
|
|
// Optimized JavaScript implementations for different distance functions
|
|
let distances;
|
|
if (distanceFnString.includes('euclideanDistance')) {
|
|
// Euclidean distance: sqrt(sum((a - b)^2))
|
|
distances = vectors.map((vector) => {
|
|
let sum = 0;
|
|
for (let i = 0; i < queryVector.length; i++) {
|
|
const diff = queryVector[i] - vector[i];
|
|
sum += diff * diff;
|
|
}
|
|
return Math.sqrt(sum);
|
|
});
|
|
}
|
|
else if (distanceFnString.includes('cosineDistance')) {
|
|
// Cosine distance: 1 - (a·b / (||a|| * ||b||))
|
|
distances = vectors.map((vector) => {
|
|
let dotProduct = 0;
|
|
let queryNorm = 0;
|
|
let vectorNorm = 0;
|
|
for (let i = 0; i < queryVector.length; i++) {
|
|
dotProduct += queryVector[i] * vector[i];
|
|
queryNorm += queryVector[i] * queryVector[i];
|
|
vectorNorm += vector[i] * vector[i];
|
|
}
|
|
queryNorm = Math.sqrt(queryNorm);
|
|
vectorNorm = Math.sqrt(vectorNorm);
|
|
if (queryNorm === 0 || vectorNorm === 0) {
|
|
return 1; // Maximum distance for zero vectors
|
|
}
|
|
const cosineSimilarity = dotProduct / (queryNorm * vectorNorm);
|
|
return 1 - cosineSimilarity;
|
|
});
|
|
}
|
|
else if (distanceFnString.includes('manhattanDistance')) {
|
|
// Manhattan distance: sum(|a - b|)
|
|
distances = vectors.map((vector) => {
|
|
let sum = 0;
|
|
for (let i = 0; i < queryVector.length; i++) {
|
|
sum += Math.abs(queryVector[i] - vector[i]);
|
|
}
|
|
return sum;
|
|
});
|
|
}
|
|
else if (distanceFnString.includes('dotProductDistance')) {
|
|
// Dot product distance: -sum(a * b)
|
|
distances = vectors.map((vector) => {
|
|
let dotProduct = 0;
|
|
for (let i = 0; i < queryVector.length; i++) {
|
|
dotProduct += queryVector[i] * vector[i];
|
|
}
|
|
return -dotProduct;
|
|
});
|
|
}
|
|
else {
|
|
// For unknown distance functions, use the provided function
|
|
const distanceFunction = new Function('return ' + distanceFnString)();
|
|
distances = vectors.map((vector) => distanceFunction(queryVector, vector));
|
|
}
|
|
return { distances };
|
|
};
|
|
// Use the optimized distance calculator
|
|
const result = distanceCalculator({
|
|
queryVector,
|
|
vectors,
|
|
distanceFnString: distanceFunction.toString()
|
|
});
|
|
return result.distances;
|
|
}
|
|
catch (error) {
|
|
// If anything fails, fall back to the standard distance function
|
|
console.error('Batch distance calculation failed:', error);
|
|
return vectors.map((vector) => distanceFunction(queryVector, vector));
|
|
}
|
|
}
|
|
//# sourceMappingURL=distance.js.map
|