ruvector 0.1.29 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,373 @@
1
+ /**
2
+ * GNN Performance Benchmark Suite
3
+ *
4
+ * Tests performance of GNN operations and identifies bottlenecks
5
+ */
6
+
7
+ const { performance } = require('perf_hooks');
8
+
9
+ // Try to load native GNN module directly
10
+ let gnnNative;
11
+ let gnnWrapper;
12
+
13
+ try {
14
+ gnnNative = require('@ruvector/gnn');
15
+ console.log('✅ @ruvector/gnn loaded');
16
+ } catch (e) {
17
+ console.log('❌ @ruvector/gnn not available:', e.message);
18
+ }
19
+
20
+ // Benchmark utilities
21
+ function generateRandomVector(dim) {
22
+ const arr = new Array(dim);
23
+ for (let i = 0; i < dim; i++) {
24
+ arr[i] = Math.random();
25
+ }
26
+ return arr;
27
+ }
28
+
29
+ function generateRandomFloat32(dim) {
30
+ const arr = new Float32Array(dim);
31
+ for (let i = 0; i < dim; i++) {
32
+ arr[i] = Math.random();
33
+ }
34
+ return arr;
35
+ }
36
+
37
+ function benchmark(name, fn, iterations = 1000) {
38
+ // Warmup
39
+ for (let i = 0; i < 10; i++) fn();
40
+
41
+ const times = [];
42
+ for (let i = 0; i < iterations; i++) {
43
+ const start = performance.now();
44
+ fn();
45
+ times.push(performance.now() - start);
46
+ }
47
+
48
+ times.sort((a, b) => a - b);
49
+ const avg = times.reduce((a, b) => a + b, 0) / times.length;
50
+ const p50 = times[Math.floor(times.length * 0.5)];
51
+ const p95 = times[Math.floor(times.length * 0.95)];
52
+ const p99 = times[Math.floor(times.length * 0.99)];
53
+
54
+ return { name, avg, p50, p95, p99, iterations };
55
+ }
56
+
57
+ function formatMs(ms) {
58
+ if (ms < 0.001) return `${(ms * 1000000).toFixed(2)}ns`;
59
+ if (ms < 1) return `${(ms * 1000).toFixed(2)}µs`;
60
+ return `${ms.toFixed(2)}ms`;
61
+ }
62
+
63
+ function printResult(result) {
64
+ console.log(` ${result.name}:`);
65
+ console.log(` avg: ${formatMs(result.avg)} | p50: ${formatMs(result.p50)} | p95: ${formatMs(result.p95)} | p99: ${formatMs(result.p99)}`);
66
+ }
67
+
68
+ // Array conversion benchmarks
69
+ function benchmarkArrayConversion() {
70
+ console.log('\n📊 Array Conversion Overhead Benchmarks');
71
+ console.log('=========================================');
72
+
73
+ const dims = [128, 256, 512, 768, 1024];
74
+
75
+ for (const dim of dims) {
76
+ console.log(`\n Dimension: ${dim}`);
77
+
78
+ const regularArray = generateRandomVector(dim);
79
+ const float32Array = generateRandomFloat32(dim);
80
+
81
+ // Test Array.from on Float32Array
82
+ printResult(benchmark(`Array.from(Float32Array)`, () => {
83
+ return Array.from(float32Array);
84
+ }));
85
+
86
+ // Test spread operator
87
+ printResult(benchmark(`[...Float32Array]`, () => {
88
+ return [...float32Array];
89
+ }));
90
+
91
+ // Test slice (for regular arrays - noop baseline)
92
+ printResult(benchmark(`Array.slice() (baseline)`, () => {
93
+ return regularArray.slice();
94
+ }));
95
+
96
+ // Test Float32Array.from
97
+ printResult(benchmark(`Float32Array.from(Array)`, () => {
98
+ return Float32Array.from(regularArray);
99
+ }));
100
+
101
+ // Test new Float32Array
102
+ printResult(benchmark(`new Float32Array(Array)`, () => {
103
+ return new Float32Array(regularArray);
104
+ }));
105
+ }
106
+ }
107
+
108
+ // GNN operation benchmarks
109
+ function benchmarkGnnOperations() {
110
+ if (!gnnNative) {
111
+ console.log('\n⚠️ Skipping GNN benchmarks - module not available');
112
+ return;
113
+ }
114
+
115
+ console.log('\n📊 GNN Operation Benchmarks');
116
+ console.log('===========================');
117
+
118
+ const dims = [128, 256, 512];
119
+ const candidateCounts = [100, 1000, 10000];
120
+
121
+ for (const dim of dims) {
122
+ for (const count of candidateCounts) {
123
+ console.log(`\n Dimension: ${dim}, Candidates: ${count}`);
124
+
125
+ // Prepare data as regular arrays (user input)
126
+ const queryArray = generateRandomVector(dim);
127
+ const candidatesArray = Array.from({ length: count }, () => generateRandomVector(dim));
128
+
129
+ // Prepare data as Float32Array (pre-converted for max performance)
130
+ const queryFloat32 = new Float32Array(queryArray);
131
+ const candidatesFloat32 = candidatesArray.map(arr => new Float32Array(arr));
132
+
133
+ const iters = Math.min(100, Math.floor(10000 / count));
134
+
135
+ // Measure Float32Array conversion overhead (Array -> Float32Array)
136
+ const conversionOverheadResult = benchmark(`Array→Float32 conversion`, () => {
137
+ const q = new Float32Array(queryArray);
138
+ const c = candidatesArray.map(arr => new Float32Array(arr));
139
+ return { q, c };
140
+ }, iters);
141
+ printResult(conversionOverheadResult);
142
+
143
+ // Wrapped interface with regular arrays (tests full conversion + native)
144
+ try {
145
+ const wrappedArrayResult = benchmark(`Wrapped (from Array)`, () => {
146
+ return gnnNative.differentiableSearch(queryArray, candidatesArray, 10, 1.0);
147
+ }, iters);
148
+ printResult(wrappedArrayResult);
149
+ } catch (e) {
150
+ console.log(` Wrapped (from Array): Error - ${e.message}`);
151
+ }
152
+
153
+ // Wrapped interface with Float32Array (tests zero-copy path)
154
+ try {
155
+ const wrappedFloat32Result = benchmark(`Wrapped (from Float32)`, () => {
156
+ return gnnNative.differentiableSearch(queryFloat32, candidatesFloat32, 10, 1.0);
157
+ }, iters);
158
+ printResult(wrappedFloat32Result);
159
+ } catch (e) {
160
+ console.log(` Wrapped (from Float32): Error - ${e.message}`);
161
+ }
162
+
163
+ // Native direct with Float32Array (bypasses wrapper, max performance)
164
+ try {
165
+ const nativeResult = benchmark(`Native direct (Float32)`, () => {
166
+ return gnnNative.nativeDifferentiableSearch(queryFloat32, candidatesFloat32, 10, 1.0);
167
+ }, iters);
168
+ printResult(nativeResult);
169
+ } catch (e) {
170
+ console.log(` Native direct (Float32): Error - ${e.message}`);
171
+ }
172
+
173
+ console.log('');
174
+ }
175
+ }
176
+ }
177
+
178
+ // Batch operation benchmarks
179
+ function benchmarkBatchOperations() {
180
+ if (!gnnNative) return;
181
+
182
+ console.log('\n📊 Batch vs Sequential Benchmarks');
183
+ console.log('==================================');
184
+
185
+ const dim = 256;
186
+ const batchSizes = [10, 50, 100];
187
+ const candidateCount = 1000;
188
+
189
+ const candidates = Array.from({ length: candidateCount }, () => generateRandomVector(dim));
190
+
191
+ for (const batchSize of batchSizes) {
192
+ console.log(`\n Batch size: ${batchSize}, Candidates: ${candidateCount}`);
193
+
194
+ const queries = Array.from({ length: batchSize }, () => generateRandomVector(dim));
195
+
196
+ // Sequential search
197
+ const sequentialResult = benchmark(`Sequential search`, () => {
198
+ const results = [];
199
+ for (const query of queries) {
200
+ results.push(gnnNative.differentiableSearch(query, candidates, 10, 1.0));
201
+ }
202
+ return results;
203
+ }, 10);
204
+ printResult(sequentialResult);
205
+
206
+ // Note: batch search would need to be implemented in native
207
+ console.log(` Batch search: Not implemented (potential ${batchSize}x improvement)`);
208
+ }
209
+ }
210
+
211
+ // RuvectorLayer benchmarks
212
+ function benchmarkRuvectorLayer() {
213
+ if (!gnnNative) return;
214
+
215
+ console.log('\n📊 RuvectorLayer Benchmarks');
216
+ console.log('===========================');
217
+
218
+ const dims = [128, 256, 512];
219
+ const neighborCounts = [5, 10, 20, 50];
220
+
221
+ for (const dim of dims) {
222
+ for (const neighborCount of neighborCounts) {
223
+ console.log(`\n Dimension: ${dim}, Neighbors: ${neighborCount}`);
224
+
225
+ const layer = new gnnNative.RuvectorLayer(dim, dim, 4, 0.1);
226
+
227
+ // Test with regular arrays (triggers conversion)
228
+ const nodeArray = generateRandomVector(dim);
229
+ const neighborsArray = Array.from({ length: neighborCount }, () => generateRandomVector(dim));
230
+ const weightsArray = generateRandomVector(neighborCount);
231
+
232
+ // Test with Float32Arrays (zero-copy)
233
+ const nodeFloat32 = new Float32Array(nodeArray);
234
+ const neighborsFloat32 = neighborsArray.map(arr => new Float32Array(arr));
235
+ const weightsFloat32 = new Float32Array(weightsArray);
236
+
237
+ try {
238
+ const arrayResult = benchmark(`Layer forward (Array)`, () => {
239
+ return layer.forward(nodeArray, neighborsArray, weightsArray);
240
+ }, 1000);
241
+ printResult(arrayResult);
242
+ } catch (e) {
243
+ console.log(` Layer forward (Array): Error - ${e.message}`);
244
+ }
245
+
246
+ try {
247
+ const float32Result = benchmark(`Layer forward (Float32)`, () => {
248
+ return layer.forward(nodeFloat32, neighborsFloat32, weightsFloat32);
249
+ }, 1000);
250
+ printResult(float32Result);
251
+ } catch (e) {
252
+ console.log(` Layer forward (Float32): Error - ${e.message}`);
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ // TensorCompress benchmarks
259
+ function benchmarkTensorCompress() {
260
+ if (!gnnNative) return;
261
+
262
+ console.log('\n📊 TensorCompress Benchmarks');
263
+ console.log('============================');
264
+
265
+ const dims = [128, 256, 512, 768, 1024];
266
+
267
+ const compressor = new gnnNative.TensorCompress();
268
+
269
+ for (const dim of dims) {
270
+ console.log(`\n Dimension: ${dim}`);
271
+
272
+ const embeddingArray = generateRandomVector(dim);
273
+ const embeddingFloat32 = new Float32Array(embeddingArray);
274
+
275
+ // Test with Array (triggers conversion)
276
+ try {
277
+ const arrayResult = benchmark(`Compress Array (freq=0.5)`, () => {
278
+ return compressor.compress(embeddingArray, 0.5);
279
+ }, 1000);
280
+ printResult(arrayResult);
281
+ } catch (e) {
282
+ console.log(` Compress Array: Error - ${e.message}`);
283
+ }
284
+
285
+ // Test with Float32Array (zero-copy)
286
+ try {
287
+ const float32Result = benchmark(`Compress Float32 (freq=0.5)`, () => {
288
+ return compressor.compress(embeddingFloat32, 0.5);
289
+ }, 1000);
290
+ printResult(float32Result);
291
+ } catch (e) {
292
+ console.log(` Compress Float32: Error - ${e.message}`);
293
+ }
294
+
295
+ // Decompress benchmark
296
+ try {
297
+ const compressed = compressor.compress(embeddingFloat32, 0.5);
298
+ const decompressResult = benchmark(`Decompress`, () => {
299
+ return compressor.decompress(compressed);
300
+ }, 1000);
301
+ printResult(decompressResult);
302
+ } catch (e) {
303
+ console.log(` Decompress: Error - ${e.message}`);
304
+ }
305
+ }
306
+ }
307
+
308
+ // Memory allocation benchmarks
309
+ function benchmarkMemoryAllocation() {
310
+ console.log('\n📊 Memory Allocation Patterns');
311
+ console.log('=============================');
312
+
313
+ const dim = 256;
314
+ const count = 1000;
315
+
316
+ // Regular array creation
317
+ printResult(benchmark(`Create ${count} regular arrays (${dim}d)`, () => {
318
+ const arrays = [];
319
+ for (let i = 0; i < count; i++) {
320
+ arrays.push(new Array(dim).fill(0).map(() => Math.random()));
321
+ }
322
+ return arrays;
323
+ }, 100));
324
+
325
+ // Float32Array creation
326
+ printResult(benchmark(`Create ${count} Float32Arrays (${dim}d)`, () => {
327
+ const arrays = [];
328
+ for (let i = 0; i < count; i++) {
329
+ const arr = new Float32Array(dim);
330
+ for (let j = 0; j < dim; j++) arr[j] = Math.random();
331
+ arrays.push(arr);
332
+ }
333
+ return arrays;
334
+ }, 100));
335
+
336
+ // Pre-allocated buffer
337
+ printResult(benchmark(`Pre-allocated buffer (${count * dim} floats)`, () => {
338
+ const buffer = new Float32Array(count * dim);
339
+ for (let i = 0; i < buffer.length; i++) {
340
+ buffer[i] = Math.random();
341
+ }
342
+ return buffer;
343
+ }, 100));
344
+ }
345
+
346
+ // Main
347
+ async function main() {
348
+ console.log('🚀 RuVector GNN Performance Benchmark Suite');
349
+ console.log('============================================\n');
350
+
351
+ console.log('System Info:');
352
+ console.log(` Platform: ${process.platform}`);
353
+ console.log(` Node.js: ${process.version}`);
354
+ console.log(` CPU: ${require('os').cpus()[0].model}`);
355
+ console.log(` Memory: ${Math.round(require('os').totalmem() / 1024 / 1024 / 1024)}GB`);
356
+
357
+ benchmarkArrayConversion();
358
+ benchmarkMemoryAllocation();
359
+ benchmarkGnnOperations();
360
+ benchmarkRuvectorLayer();
361
+ benchmarkTensorCompress();
362
+ benchmarkBatchOperations();
363
+
364
+ console.log('\n\n📋 Performance Optimization Recommendations');
365
+ console.log('============================================');
366
+ console.log('1. Avoid Array.from() conversion - use typed arrays directly');
367
+ console.log('2. Cache converted arrays when possible');
368
+ console.log('3. Use pre-allocated buffers for batch operations');
369
+ console.log('4. Implement native batch search for multiple queries');
370
+ console.log('5. Consider zero-copy operations with SharedArrayBuffer');
371
+ }
372
+
373
+ main().catch(console.error);
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Integration test for ruvector package
5
+ * Tests the smart loader and basic functionality
6
+ */
7
+
8
+ const assert = require('assert');
9
+ const path = require('path');
10
+
11
+ console.log('ruvector Integration Test\n');
12
+ console.log('='.repeat(50));
13
+
14
+ // Test 1: Load ruvector module
15
+ console.log('\n1. Testing module loading...');
16
+ try {
17
+ const ruvector = require('../dist/index.js');
18
+ console.log(' ✓ Module loaded successfully');
19
+
20
+ // Check exports
21
+ assert(typeof ruvector.VectorDB === 'function', 'VectorDB should be a function');
22
+ assert(typeof ruvector.getImplementationType === 'function', 'getImplementationType should be a function');
23
+ assert(typeof ruvector.isNative === 'function', 'isNative should be a function');
24
+ assert(typeof ruvector.isWasm === 'function', 'isWasm should be a function');
25
+ assert(typeof ruvector.getVersion === 'function', 'getVersion should be a function');
26
+ console.log(' ✓ All exports present');
27
+ } catch (error) {
28
+ console.error(' ✗ Failed to load module:', error.message);
29
+ process.exit(1);
30
+ }
31
+
32
+ // Test 2: Check implementation detection
33
+ console.log('\n2. Testing implementation detection...');
34
+ try {
35
+ const { getImplementationType, isNative, isWasm, getVersion } = require('../dist/index.js');
36
+
37
+ const implType = getImplementationType();
38
+ console.log(` Implementation type: ${implType}`);
39
+
40
+ assert(['native', 'wasm'].includes(implType), 'Implementation type should be native or wasm');
41
+ console.log(' ✓ Valid implementation type');
42
+
43
+ const version = getVersion();
44
+ console.log(` Version: ${version.version}`);
45
+ console.log(` Using: ${version.implementation}`);
46
+ assert(version.version === '0.1.1', 'Version should be 0.1.1');
47
+ console.log(' ✓ Version info correct');
48
+
49
+ assert(isNative() !== isWasm(), 'Should be either native OR wasm, not both');
50
+ console.log(' ✓ Implementation flags consistent');
51
+ } catch (error) {
52
+ console.error(' ✗ Implementation detection failed:', error.message);
53
+ // This is expected to fail until we have the actual implementations
54
+ console.log(' ⚠ This is expected until @ruvector/core and @ruvector/wasm are built');
55
+ }
56
+
57
+ // Test 3: Type definitions
58
+ console.log('\n3. Testing TypeScript type definitions...');
59
+ try {
60
+ const fs = require('fs');
61
+
62
+ const typeDefsExist = fs.existsSync(path.join(__dirname, '../dist/types.d.ts'));
63
+ assert(typeDefsExist, 'Type definitions should exist');
64
+ console.log(' ✓ Type definitions file exists');
65
+
66
+ const indexDefsExist = fs.existsSync(path.join(__dirname, '../dist/index.d.ts'));
67
+ assert(indexDefsExist, 'Index type definitions should exist');
68
+ console.log(' ✓ Index type definitions exist');
69
+
70
+ // Check type definitions content
71
+ const typeDefs = fs.readFileSync(path.join(__dirname, '../dist/types.d.ts'), 'utf8');
72
+ assert(typeDefs.includes('VectorEntry'), 'Should include VectorEntry interface');
73
+ assert(typeDefs.includes('SearchQuery'), 'Should include SearchQuery interface');
74
+ assert(typeDefs.includes('SearchResult'), 'Should include SearchResult interface');
75
+ assert(typeDefs.includes('DbOptions'), 'Should include DbOptions interface');
76
+ assert(typeDefs.includes('VectorDB'), 'Should include VectorDB interface');
77
+ console.log(' ✓ All type definitions present');
78
+ } catch (error) {
79
+ console.error(' ✗ Type definitions test failed:', error.message);
80
+ process.exit(1);
81
+ }
82
+
83
+ // Test 4: Package structure
84
+ console.log('\n4. Testing package structure...');
85
+ try {
86
+ const fs = require('fs');
87
+
88
+ const packageJson = require('../package.json');
89
+ assert(packageJson.name === 'ruvector', 'Package name should be ruvector');
90
+ assert(packageJson.version === '0.1.1', 'Version should be 0.1.1');
91
+ assert(packageJson.main === 'dist/index.js', 'Main entry should be dist/index.js');
92
+ assert(packageJson.types === 'dist/index.d.ts', 'Types entry should be dist/index.d.ts');
93
+ assert(packageJson.bin.ruvector === './bin/cli.js', 'CLI bin should be ./bin/cli.js');
94
+ console.log(' ✓ package.json structure correct');
95
+
96
+ const cliExists = fs.existsSync(path.join(__dirname, '../bin/cli.js'));
97
+ assert(cliExists, 'CLI script should exist');
98
+ console.log(' ✓ CLI script exists');
99
+
100
+ const cliContent = fs.readFileSync(path.join(__dirname, '../bin/cli.js'), 'utf8');
101
+ assert(cliContent.startsWith('#!/usr/bin/env node'), 'CLI should have shebang');
102
+ console.log(' ✓ CLI has proper shebang');
103
+ } catch (error) {
104
+ console.error(' ✗ Package structure test failed:', error.message);
105
+ process.exit(1);
106
+ }
107
+
108
+ // Test 5: CLI functionality (basic)
109
+ console.log('\n5. Testing CLI basic functionality...');
110
+ try {
111
+ const { execSync } = require('child_process');
112
+
113
+ // Test CLI help
114
+ try {
115
+ const output = execSync('node bin/cli.js --help', {
116
+ cwd: path.join(__dirname, '..'),
117
+ encoding: 'utf8'
118
+ });
119
+ assert(output.includes('ruvector'), 'Help should mention ruvector');
120
+ assert(output.includes('create'), 'Help should include create command');
121
+ assert(output.includes('search'), 'Help should include search command');
122
+ console.log(' ✓ CLI help works');
123
+ } catch (error) {
124
+ // CLI might fail if dependencies aren't available
125
+ console.log(' ⚠ CLI help test skipped (dependencies not available)');
126
+ }
127
+
128
+ // Test info command
129
+ try {
130
+ const output = execSync('node bin/cli.js info', {
131
+ cwd: path.join(__dirname, '..'),
132
+ encoding: 'utf8'
133
+ });
134
+ assert(output.includes('0.1.1'), 'Info should show version');
135
+ console.log(' ✓ CLI info command works');
136
+ } catch (error) {
137
+ console.log(' ⚠ CLI info test skipped (dependencies not available)');
138
+ }
139
+ } catch (error) {
140
+ console.error(' ✗ CLI test failed:', error.message);
141
+ }
142
+
143
+ // Summary
144
+ console.log('\n' + '='.repeat(50));
145
+ console.log('\n✓ Core package structure tests passed!');
146
+ console.log('\nPackage ready for:');
147
+ console.log(' - Platform detection and smart loading');
148
+ console.log(' - TypeScript type definitions');
149
+ console.log(' - CLI tools (create, insert, search, stats, benchmark)');
150
+ console.log(' - Integration with @ruvector/core and @ruvector/wasm');
151
+ console.log('\nNext steps:');
152
+ console.log(' 1. Build @ruvector/core (native Rust bindings)');
153
+ console.log(' 2. Build @ruvector/wasm (WebAssembly module)');
154
+ console.log(' 3. Test full integration with real implementations');
155
+ console.log('\nPackage location: /workspaces/ruvector/npm/packages/ruvector');
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Mock VectorDB implementation for testing
3
+ * This simulates the interface that @ruvector/core and @ruvector/wasm will provide
4
+ */
5
+
6
+ class VectorDB {
7
+ constructor(options) {
8
+ this.options = options;
9
+ this.dimension = options.dimension;
10
+ this.metric = options.metric || 'cosine';
11
+ this.vectors = new Map();
12
+ }
13
+
14
+ insert(entry) {
15
+ if (!entry.id || !entry.vector) {
16
+ throw new Error('Entry must have id and vector');
17
+ }
18
+ if (entry.vector.length !== this.dimension) {
19
+ throw new Error(`Vector dimension must be ${this.dimension}`);
20
+ }
21
+ this.vectors.set(entry.id, {
22
+ id: entry.id,
23
+ vector: entry.vector,
24
+ metadata: entry.metadata || {}
25
+ });
26
+ }
27
+
28
+ insertBatch(entries) {
29
+ for (const entry of entries) {
30
+ this.insert(entry);
31
+ }
32
+ }
33
+
34
+ search(query) {
35
+ const results = [];
36
+ const k = query.k || 10;
37
+ const threshold = query.threshold || 0.0;
38
+
39
+ for (const [id, entry] of this.vectors.entries()) {
40
+ const score = this._computeSimilarity(query.vector, entry.vector);
41
+ if (score >= threshold) {
42
+ results.push({
43
+ id: entry.id,
44
+ score,
45
+ vector: entry.vector,
46
+ metadata: entry.metadata
47
+ });
48
+ }
49
+ }
50
+
51
+ // Sort by score descending
52
+ results.sort((a, b) => b.score - a.score);
53
+
54
+ return results.slice(0, k);
55
+ }
56
+
57
+ get(id) {
58
+ return this.vectors.get(id) || null;
59
+ }
60
+
61
+ delete(id) {
62
+ return this.vectors.delete(id);
63
+ }
64
+
65
+ updateMetadata(id, metadata) {
66
+ const entry = this.vectors.get(id);
67
+ if (entry) {
68
+ entry.metadata = { ...entry.metadata, ...metadata };
69
+ }
70
+ }
71
+
72
+ stats() {
73
+ return {
74
+ count: this.vectors.size,
75
+ dimension: this.dimension,
76
+ metric: this.metric,
77
+ memoryUsage: this.vectors.size * this.dimension * 8, // rough estimate
78
+ indexType: 'flat'
79
+ };
80
+ }
81
+
82
+ save(path) {
83
+ // Mock save
84
+ const data = {
85
+ dimension: this.dimension,
86
+ metric: this.metric,
87
+ vectors: Array.from(this.vectors.values())
88
+ };
89
+ return JSON.stringify(data);
90
+ }
91
+
92
+ load(path) {
93
+ // Mock load - would read from file
94
+ this.vectors.clear();
95
+ }
96
+
97
+ clear() {
98
+ this.vectors.clear();
99
+ }
100
+
101
+ buildIndex() {
102
+ // Mock index building
103
+ }
104
+
105
+ optimize() {
106
+ // Mock optimization
107
+ }
108
+
109
+ _computeSimilarity(a, b) {
110
+ if (this.metric === 'cosine') {
111
+ return this._cosineSimilarity(a, b);
112
+ } else if (this.metric === 'euclidean') {
113
+ return 1 / (1 + this._euclideanDistance(a, b));
114
+ } else {
115
+ return this._dotProduct(a, b);
116
+ }
117
+ }
118
+
119
+ _cosineSimilarity(a, b) {
120
+ let dot = 0;
121
+ let magA = 0;
122
+ let magB = 0;
123
+
124
+ for (let i = 0; i < a.length; i++) {
125
+ dot += a[i] * b[i];
126
+ magA += a[i] * a[i];
127
+ magB += b[i] * b[i];
128
+ }
129
+
130
+ return dot / (Math.sqrt(magA) * Math.sqrt(magB));
131
+ }
132
+
133
+ _euclideanDistance(a, b) {
134
+ let sum = 0;
135
+ for (let i = 0; i < a.length; i++) {
136
+ const diff = a[i] - b[i];
137
+ sum += diff * diff;
138
+ }
139
+ return Math.sqrt(sum);
140
+ }
141
+
142
+ _dotProduct(a, b) {
143
+ let sum = 0;
144
+ for (let i = 0; i < a.length; i++) {
145
+ sum += a[i] * b[i];
146
+ }
147
+ return sum;
148
+ }
149
+ }
150
+
151
+ module.exports = { VectorDB };