Skip to content

Commit 6ea5e99

Browse files
committed
RDBC-899 Refactor VectorSearch utilities by removing QueryFieldUtil and introducing VectorQuantizer with updated tests
1 parent 672d805 commit 6ea5e99

File tree

8 files changed

+108
-94
lines changed

8 files changed

+108
-94
lines changed

src/Documents/Queries/VectorSearch/Fields/VectorField.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { IVectorField } from "../../../Session/IVectorFieldFactory.js";
22
import { VectorFieldBase } from "../Common/VectorFieldBase.js";
3+
import { Field } from "../../../../Types/index.js";
34

45
export class VectorField<T> extends VectorFieldBase<T> implements IVectorField {
5-
constructor(fieldName: string) {
6+
constructor(fieldName: Field<T>) {
67
super(fieldName);
78
this._byFieldMethodUsed = true;
89
this.fieldName = fieldName;

src/Documents/Queries/VectorSearch/Utils/PropertyPathUtil.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/Documents/Queries/VectorSearch/Utils/PropertyPathUtilTest.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/Documents/Queries/VectorSearch/VectorSearch.ts renamed to src/Documents/Queries/VectorSearch/VectorQuantizer.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,28 @@ export class VectorQuantizer {
99
*/
1010
public static toInt8(rawEmbedding: number[] | Float32Array): number[] {
1111
const length = rawEmbedding.length;
12-
const result = new Int8Array(length + 4); // Extra 4 bytes for the float
12+
const result = new Array(length + 4);
1313

14-
// Find the maximum absolute value
1514
let maxAbsValue = 0;
1615
for (let i = 0; i < length; i++) {
1716
maxAbsValue = Math.max(maxAbsValue, Math.abs(rawEmbedding[i]));
1817
}
1918

20-
// Scale factor to fit in int8 range
2119
const scaleFactor = maxAbsValue === 0 ? 1 : 127 / maxAbsValue;
2220

23-
// Convert and scale values
2421
for (let i = 0; i < length; i++) {
2522
result[i] = Math.round(rawEmbedding[i] * scaleFactor);
2623
}
2724

28-
// Append the scale factor as a float32 at the end
29-
const scaleFactorView = new DataView(result.buffer, length, 4);
30-
scaleFactorView.setFloat32(0, maxAbsValue, true); // true for little-endian
25+
const buffer = new ArrayBuffer(4);
26+
const dataView = new DataView(buffer);
27+
dataView.setFloat32(0, maxAbsValue, true);
3128

32-
return Array.from(result);
29+
for (let i = 0; i < 4; i++) {
30+
result[length + i] = dataView.getInt8(i);
31+
}
32+
33+
return result;
3334
}
3435

3536
/**

src/Documents/Session/AbstractDocumentQuery.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ import {
9696
IVectorFieldFactory, IVectorFieldValueFactory, VectorEmbeddingFieldValueFactory
9797
} from "./IVectorFieldFactory.js";
9898
import { VectorEmbeddingFieldFactory } from "../Queries/VectorSearch/VectorEmbeddingFieldFactory.js";
99-
import { VectorFieldFactory } from "./VectorFieldFactory.js";
10099
import { Field } from "../../Types/index.js";
101100

102101
/**

src/Documents/Session/DocumentQuery/QueryFieldUtil.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ export * from "./Documents/Indexes/TimeSeries/AbstractRawJavaScriptTimeSeriesInd
419419
export * from "./Documents/Indexes/TimeSeries/TimeSeriesIndexDefinition.js";
420420
export * from "./Documents/Indexes/TimeSeries/TimeSeriesIndexDefinitionBuilder.js";
421421
// VECTOR SEARCH
422-
export * from "./Documents/Queries/VectorSearch/VectorSearch.js"
422+
export * from "./Documents/Queries/VectorSearch/VectorQuantizer.js"
423423
export * from "./Utility/VectorSearchUtil.js"
424424

425425
// REPLICATION
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import assert from "node:assert";
2+
import {VectorQuantizer} from "../../../../src/Documents/Queries/VectorSearch/VectorQuantizer.js";
3+
4+
describe("VectorQuantizer", function () {
5+
describe("toInt8", function () {
6+
it("should correctly quantize vector elements and store scale factor", function () {
7+
const input = [0.1, 0.2];
8+
const result = VectorQuantizer.toInt8(input);
9+
10+
const expectedResult = [64, 127, -51, -52, 76, 62];
11+
12+
for (let i = 0; i < expectedResult.length; i++) {
13+
assert.equal(result[i], expectedResult[i]);
14+
}
15+
});
16+
17+
it("should maintain expected byte values in quantized output", function () {
18+
const input = [0.1, 0.2];
19+
const result = VectorQuantizer.toInt8(input);
20+
21+
const expectedResult = [64, 127, -51, -52, 76, 62];
22+
23+
for (let i = 0; i < expectedResult.length; i++) {
24+
assert.equal(result[i], expectedResult[i]);
25+
}
26+
});
27+
28+
it("should handle zero vector input correctly", function () {
29+
const input = [0, 0, 0, 0];
30+
const result = VectorQuantizer.toInt8(input);
31+
32+
const expectedResult = Array(8).fill(0);
33+
34+
for (let i = 0; i < expectedResult.length; i++) {
35+
assert.equal(result[i], expectedResult[i]);
36+
}
37+
});
38+
39+
it("should correctly quantize array of floats with positive and negative values", function () {
40+
const input = [0.5, -1.5, 2.5, -3.5];
41+
const result = VectorQuantizer.toInt8(input);
42+
43+
const expectedResult = [18, -54, 91, -127, 0, 0, 96, 64];
44+
45+
for (let i = 0; i < expectedResult.length; i++) {
46+
assert.equal(result[i], expectedResult[i]);
47+
}
48+
});
49+
});
50+
51+
describe("toInt1", function () {
52+
it("should correctly convert vector to binary bit representation", function () {
53+
const input = [1.0, -2.0, 3.0, -4.0, 5.0, -6.0, 7.0, -8.0, 9.0];
54+
const result = VectorQuantizer.toInt1(input);
55+
56+
const expectedResult = [0xAA, 0x80];
57+
58+
for (let i = 0; i < expectedResult.length; i++) {
59+
assert.equal(result[i], expectedResult[i]);
60+
}
61+
});
62+
63+
it("should represent zero values as positive bits", function () {
64+
const input = [0, 0, 0, 0, 0, 0, 0, 0];
65+
const result = VectorQuantizer.toInt1(input);
66+
67+
const expectedResult = [0xFF];
68+
69+
for (let i = 0; i < expectedResult.length; i++) {
70+
assert.equal(result[i], expectedResult[i]);
71+
}
72+
});
73+
74+
it("should properly pad vectors with length not divisible by 8", function () {
75+
const input = [1, 2, 3, 4, 5];
76+
const result = VectorQuantizer.toInt1(input);
77+
78+
const expectedResult = [0xF8];
79+
80+
for (let i = 0; i < expectedResult.length; i++) {
81+
assert.equal(result[i], expectedResult[i]);
82+
}
83+
});
84+
85+
it("should correctly convert Float32Array with alternating signs", function () {
86+
const input = [-1, 2, -3, 4, -5, 6, -7, 8];
87+
const result = VectorQuantizer.toInt1(input);
88+
89+
const expectedResult = [0x55];
90+
91+
for (let i = 0; i < expectedResult.length; i++) {
92+
assert.equal(result[i], expectedResult[i]);
93+
}
94+
});
95+
});
96+
});

0 commit comments

Comments
 (0)