Skip to content

Add scalar quantization support for Fp16 vector encoding - #16473

Merged
msokolov merged 16 commits into
apache:mainfrom
Pulkitg64:fp16-quantization
Aug 26, 2026
Merged

Add scalar quantization support for Fp16 vector encoding#16473
msokolov merged 16 commits into
apache:mainfrom
Pulkitg64:fp16-quantization

Conversation

@Pulkitg64

Copy link
Copy Markdown
Contributor

Description

As part of this PR (#16383) we added support for FP16 vector encoding. This PR target support for adding scalar quantization to it.

@Pulkitg64 Pulkitg64 changed the title Add scalar quantization support to Fp16 vector encoding Add scalar quantization support for Fp16 vector encoding Aug 2, 2026

@msokolov msokolov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall looks good: i just have some small comments and questions

// This is asymmetric quantization, we will pack the vector
targetQuantized = new byte[scalarEncoding.getQueryPackedLength(scratch.length)];
}
// Inflate the fp16 query to fp32 and normalize there; quantization operates on fp32.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add a TODO: linking to an issue for implementing quantization directly over fp16?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW I just saw https://opensearch.org/blog/accelerating-fp16-vector-search-performance-using-bulk-simd-in-opensearch-3-5/ maybe there is some goodness there we can incorporate?

@Pulkitg64 Pulkitg64 Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add a TODO:

Created this issue: #16533

maybe there is some goodness there we can incorporate?

Thanks Mike, I will check how are they doing the quantization with Fp16

targetQuantized = new byte[scalarEncoding.getQueryPackedLength(scratch.length)];
}
// Inflate the fp16 query to fp32 and normalize there; quantization operates on fp32.
float[] copy = new float[target.length];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should all be a copy of the logic above in float[] case right? can we factor out into a utility method?

@Override
public RandomVectorScorer getRandomVectorScorer(String field, short[] target) throws IOException {
return rawVectorsReader.getRandomVectorScorer(field, target);
FieldEntry fi = fields.get(field);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, can we DRY this up?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure Mike

*
* <p>Used for read-only indexes whose raw float16 vectors have been dropped to save storage: only
* the scalar-quantized bytes remain, so {@link #vectorValue(int)} reconstructs float16 values by
* dequantizing them, with some precision loss.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the precision loss is relative to the original fp16 vectors I guess, not relative to the quantized vectors. Maybe just add "relative to the original fp16 vectors" to be explicit

this.correctiveValues = new float[3];
this.encoding = encoding;
int docPackedLength = encoding.getDocPackedLength(dimension);
this.byteSize = docPackedLength + (Float.BYTES * 3) + Integer.BYTES;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this calculation about?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I guess we must read corrective values, and ... an int

}
}
@Override
public T copyValue(T vectorValue) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did this get added?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it existed before as well. We just made it generic too and the position got changed.

@Override
public void addValue(int docID, float[] vectorValue) throws IOException {
flatFieldVectorsWriter.addValue(docID, vectorValue);
/** The ordinal's stored vector as fp32, ready for quantization (unit-length for COSINE). */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will also be expected to be unit-length for DOT_PRODUCT? It's just not guaranteed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding in comments


@Override
float[] floatVectorValue(int ord) {
float[] vector = flatFieldVectorsWriter.getVectors().get(ord);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should be normalizing "on the way in" -- in addValue?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I don't really care about stupid COSINE

String fieldName = "field";
int numVectors = random().nextInt(99, 500);
int dims = random().nextInt(4, 65);
if (dims % 2 == 1) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or 2 * random().nextInt(2, 33)?

/**
* fp16 counterpart of {@link #testQuantizedVectorsWriteAndRead()}: indexes float16 vectors and
* verifies the persisted quantized bytes + corrective terms match a reference re-quantization.
* The reference mirrors the writer's fp16 path exactly &mdash; inflate fp16-&gt;fp32 (normalizing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait! I see the dreaded mdash! Did AI write this??!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I asked AI to replicate existing fp32 methods and test cases. Sorry for the ignoring the comments here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh funny, it actually seemed fine and I thought you wrote it. I was just poking fun at the emdash

@Pulkitg64
Pulkitg64 requested a review from msokolov August 25, 2026 23:33

@msokolov msokolov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one tiny nit left!

}
};
return getRandomQuantizedVectorScorer(
similarityFunction, qv, ArrayUtil.copyOfSubArray(target, 0, target.length));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have copyArray as sugar for this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure Mike, fixed in next revision.

@msokolov msokolov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, looks good!

@msokolov
msokolov merged commit 50c1e6a into apache:main Aug 26, 2026
@Pulkitg64

Copy link
Copy Markdown
Contributor Author

Thank you Mike for all the feedbacks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants