Skip to content

Commit 98c1708

Browse files
authored
Add javadocs for BBQ dot product method (#129419)
1 parent 236ae2b commit 98c1708

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

libs/simdvec/src/main/java/org/elasticsearch/simdvec/internal/vectorization/DefaultESVectorUtilSupport.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,19 +204,58 @@ static float ipFloatBitImpl(float[] q, byte[] d, int start) {
204204
return acc0 + acc1 + acc2 + acc3;
205205
}
206206

207+
/**
208+
* Returns the inner product (aka dot product) between the query vector {@code q}, and the data vector {@code d}.
209+
* <p>
210+
* The query vector should be {@link #B_QUERY}-bit quantized and striped, so that the first {@code n} bits
211+
* of the array are the initial bits of each of the {@code n} vector dimensions; the next {@code n}
212+
* bits are the second bits of each of the {@code n} vector dimensions, and so on
213+
* (this algorithm is only valid for vectors with dimensions a multiple of 8).
214+
* The striping is usually done by {@code BQSpaceUtils.transposeHalfByte}.
215+
* <p>
216+
* The data vector should be single-bit quantized.
217+
*
218+
* <h4>Dot products with bit quantization</h4>
219+
*
220+
* The dot product of any vector with a bit vector is a simple selector - each query vector dimension is multiplied
221+
* by the 0 or 1 in the corresponding data vector dimension; the result is that each dimension value
222+
* is either kept or ignored, with the dimensions that are kept then summed together.
223+
*
224+
* <h4>The algorithm</h4>
225+
*
226+
* The transposition already applied to the query vector ensures there's a 1-to-1 correspondence
227+
* between the data vector bits and query vector bits (see {@code BQSpaceUtils.transposeHalfByte)};
228+
* this means we can use a bitwise {@code &} to keep only the bits of the vector elements we want to sum.
229+
* Essentially, the data vector is used as a selector for each of the striped bits of each vector dimension
230+
* as stored, concatenated together, in {@code q}.
231+
* <p>
232+
* The final dot product result can be obtained by observing that the sum of each stripe of {@code n} bits
233+
* can be computed using the bit count of that stripe. Similar to
234+
* <a href="https://en.wikipedia.org/wiki/Multiplication_algorithm#Long_multiplication">long multiplication</a>,
235+
* the result of each stripe of {@code n} bits can be added together by shifting the value {@code s} bits to the left,
236+
* where {@code s} is the stripe number (0-3), then adding to the overall result. Any carry is handled by the add operation.
237+
*
238+
* @param q query vector, {@link #B_QUERY}-bit quantized and striped (see {@code BQSpaceUtils.transposeHalfByte})
239+
* @param d data vector, 1-bit quantized
240+
* @return inner product result
241+
*/
207242
public static long ipByteBinByteImpl(byte[] q, byte[] d) {
208243
long ret = 0;
209244
int size = d.length;
210-
for (int i = 0; i < B_QUERY; i++) {
245+
for (int s = 0; s < B_QUERY; s++) { // for each stripe of B_QUERY-bit quantization in q...
211246
int r = 0;
212-
long subRet = 0;
247+
long stripeRet = 0;
248+
// bitwise & the query and data vectors together, 32-bits at a time, and counting up the bits still set
213249
for (final int upperBound = d.length & -Integer.BYTES; r < upperBound; r += Integer.BYTES) {
214-
subRet += Integer.bitCount((int) BitUtil.VH_NATIVE_INT.get(q, i * size + r) & (int) BitUtil.VH_NATIVE_INT.get(d, r));
250+
stripeRet += Integer.bitCount((int) BitUtil.VH_NATIVE_INT.get(q, s * size + r) & (int) BitUtil.VH_NATIVE_INT.get(d, r));
215251
}
252+
// handle any tail
253+
// Java operations on bytes automatically extend to int, so we need to mask back down again in case it sign-extends the int
216254
for (; r < d.length; r++) {
217-
subRet += Integer.bitCount((q[i * size + r] & d[r]) & 0xFF);
255+
stripeRet += Integer.bitCount((q[s * size + r] & d[r]) & 0xFF);
218256
}
219-
ret += subRet << i;
257+
// shift the result of the s'th stripe s to the left and add to the result
258+
ret += stripeRet << s;
220259
}
221260
return ret;
222261
}

libs/simdvec/src/main/java/org/elasticsearch/simdvec/internal/vectorization/ESVectorUtilSupport.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@
1111

1212
public interface ESVectorUtilSupport {
1313

14+
/**
15+
* The number of bits in bit-quantized query vectors
16+
*/
1417
short B_QUERY = 4;
1518

19+
/**
20+
* Compute dot product between {@code q} and {@code d}
21+
* @param q query vector, {@link #B_QUERY}-bit quantized and striped (see {@code BQSpaceUtils.transposeHalfByte})
22+
* @param d data vector, 1-bit quantized
23+
*/
1624
long ipByteBinByte(byte[] q, byte[] d);
1725

1826
int ipByteBit(byte[] q, byte[] d);

0 commit comments

Comments
 (0)