Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e6438d7
Disallow dot_product and max_inner_product for int8_hnsw GPU type
mayya-sharipova Oct 21, 2025
79d8e02
Update docs/changelog/136881.yaml
mayya-sharipova Oct 21, 2025
95882d0
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 21, 2025
34ad4cf
Fix errors
mayya-sharipova Oct 21, 2025
ef30f28
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 21, 2025
60e1dc9
Substitute dot_product with cosine for int8
mayya-sharipova Oct 21, 2025
3cb0849
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 21, 2025
0a3a354
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 21, 2025
02b9a7b
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
3af91ae
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
0fac65c
Update x-pack/plugin/gpu/src/main/java/org/elasticsearch/xpack/gpu/co…
mayya-sharipova Oct 22, 2025
fe79abd
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
33909ff
Add assertion
mayya-sharipova Oct 22, 2025
6f43a34
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
5150339
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
774bf13
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
7bb554b
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
bbc8465
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
345af15
Merge branch 'main' into gpu-avoid-dot-product-int8
mayya-sharipova Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/136881.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 136881
summary: Disallow `dot_product` and `max_inner_product` for int8_hnsw GPU type
area: Search
type: bug
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -2286,6 +2286,10 @@ public DenseVectorFieldType(
this.isSyntheticSource = isSyntheticSource;
}

public VectorSimilarity similarity() {
return similarity;
}

@Override
public String typeName() {
return CONTENT_TYPE;
Expand Down Expand Up @@ -2890,7 +2894,7 @@ public KnnVectorsFormat getKnnVectorsFormatForField(KnnVectorsFormat defaultForm
// if plugins provided alternative KnnVectorsFormat for this indexOptions, use it instead of standard
KnnVectorsFormat extraKnnFormat = null;
for (VectorsFormatProvider vectorsFormatProvider : extraVectorsFormatProviders) {
extraKnnFormat = vectorsFormatProvider.getKnnVectorsFormat(indexSettings, indexOptions);
extraKnnFormat = vectorsFormatProvider.getKnnVectorsFormat(indexSettings, indexOptions, fieldType().similarity());
if (extraKnnFormat != null) {
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ public interface VectorsFormatProvider {
*
* @param indexSettings The index settings.
* @param indexOptions The dense vector index options.
* @param similarity The vector similarity function.
* @return A KnnVectorsFormat instance.
*/
KnnVectorsFormat getKnnVectorsFormat(IndexSettings indexSettings, DenseVectorFieldMapper.DenseVectorIndexOptions indexOptions);
KnnVectorsFormat getKnnVectorsFormat(
IndexSettings indexSettings,
DenseVectorFieldMapper.DenseVectorIndexOptions indexOptions,
DenseVectorFieldMapper.VectorSimilarity similarity
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ public static DenseVectorFieldMapper.DenseVectorIndexOptions randomGpuSupportedI
);
}

public static DenseVectorFieldMapper.VectorSimilarity randomGPUSupportedSimilarity(
DenseVectorFieldMapper.DenseVectorIndexOptions indexOptions
) {
if (indexOptions.getType() == DenseVectorFieldMapper.VectorIndexType.INT8_HNSW) {
return randomFrom(VectorSimilarity.L2_NORM, VectorSimilarity.COSINE);
}
return randomFrom(VectorSimilarity.values());
}

public static DenseVectorFieldMapper.DenseVectorIndexOptions randomIndexOptionsAll() {
List<DenseVectorFieldMapper.DenseVectorIndexOptions> options = new ArrayList<>(
Arrays.asList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
import java.util.List;
import java.util.Locale;

import static org.apache.lucene.tests.index.BaseKnnVectorsFormatTestCase.randomNormalizedVector;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
import static org.hamcrest.Matchers.containsString;

@LuceneTestCase.SuppressCodecs("*") // use our custom codec
public class GPUIndexIT extends ESIntegTestCase {
Expand Down Expand Up @@ -160,6 +162,44 @@ public void testSearchWithoutGPU() {
assertSearch(indexName, randomFloatVector(dims), numDocs);
}

public void testInt8HnswDotProductFails() {
String indexName = "index_int8_dot_product_fails";
final int dims = randomIntBetween(4, 128);

Settings.Builder settingsBuilder = Settings.builder().put(indexSettings());
settingsBuilder.put("index.number_of_shards", 1);
settingsBuilder.put("index.vectors.indexing.use_gpu", true);

String mapping = String.format(Locale.ROOT, """
{
"properties": {
"my_vector": {
"type": "dense_vector",
"dims": %d,
"similarity": "dot_product",
"index_options": {
"type": "int8_hnsw"
}
}
}
}
""", dims);

// Index creation should succeed
assertAcked(prepareCreate(indexName).setSettings(settingsBuilder.build()).setMapping(mapping));
ensureGreen();

// Attempt to index a document and expect it to fail
IllegalArgumentException ex = expectThrows(
IllegalArgumentException.class,
() -> client().prepareIndex(indexName).setId("1").setSource("my_vector", randomNormalizedVector(dims)).get()
);
assertThat(
ex.getMessage(),
containsString("GPU vector indexing does not support [dot_product] similarity for [int8_hnsw] index type.")
);
}

private void createIndex(String indexName, int dims, boolean sorted) {
var settings = Settings.builder().put(indexSettings());
settings.put("index.number_of_shards", 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public void testFFOff() {
GPUPlugin gpuPlugin = internalCluster().getInstance(GPUPlugin.class);
VectorsFormatProvider vectorsFormatProvider = gpuPlugin.getVectorsFormatProvider();

var format = vectorsFormatProvider.getKnnVectorsFormat(null, null);
var format = vectorsFormatProvider.getKnnVectorsFormat(null, null, null);
assertNull(format);
}

Expand All @@ -136,7 +136,11 @@ public void testFFOffGPUFormatNull() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomGpuSupportedIndexOptions();

var format = vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions);
var format = vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
);
assertNull(format);
}

Expand All @@ -151,7 +155,11 @@ public void testIndexSettingOnIndexTypeSupportedGPUSupported() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomGpuSupportedIndexOptions();

var format = vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions);
var format = vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
);
assertNotNull(format);
}

Expand All @@ -166,7 +174,14 @@ public void testIndexSettingOnIndexTypeNotSupportedThrows() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomFlatIndexOptions();

var ex = expectThrows(IllegalArgumentException.class, () -> vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions));
var ex = expectThrows(
IllegalArgumentException.class,
() -> vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
)
);
assertThat(ex.getMessage(), startsWith("[index.vectors.indexing.use_gpu] doesn't support [index_options.type] of"));
}

Expand All @@ -181,7 +196,14 @@ public void testIndexSettingOnGPUNotSupportedThrows() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomGpuSupportedIndexOptions();

var ex = expectThrows(IllegalArgumentException.class, () -> vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions));
var ex = expectThrows(
IllegalArgumentException.class,
() -> vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
)
);
assertThat(
ex.getMessage(),
equalTo("[index.vectors.indexing.use_gpu] was set to [true], but GPU resources are not accessible on the node.")
Expand All @@ -200,7 +222,14 @@ public void testIndexSettingOnGPUSupportThrowsRethrows() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomGpuSupportedIndexOptions();

var ex = expectThrows(IllegalArgumentException.class, () -> vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions));
var ex = expectThrows(
IllegalArgumentException.class,
() -> vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
)
);
assertThat(
ex.getMessage(),
equalTo("[index.vectors.indexing.use_gpu] was set to [true], but GPU resources are not accessible on the node.")
Expand All @@ -218,7 +247,11 @@ public void testIndexSettingAutoIndexTypeSupportedGPUSupported() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomGpuSupportedIndexOptions();

var format = vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions);
var format = vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
);
assertNotNull(format);
}

Expand All @@ -233,7 +266,11 @@ public void testIndexSettingAutoGPUNotSupported() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomGpuSupportedIndexOptions();

var format = vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions);
var format = vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
);
assertNull(format);
}

Expand All @@ -248,7 +285,11 @@ public void testIndexSettingAutoIndexTypeNotSupported() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomFlatIndexOptions();

var format = vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions);
var format = vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
);
assertNull(format);
}

Expand All @@ -263,7 +304,11 @@ public void testIndexSettingOff() {
IndexSettings settings = getIndexSettings();
final var indexOptions = DenseVectorFieldTypeTests.randomGpuSupportedIndexOptions();

var format = vectorsFormatProvider.getKnnVectorsFormat(settings, indexOptions);
var format = vectorsFormatProvider.getKnnVectorsFormat(
settings,
indexOptions,
DenseVectorFieldTypeTests.randomGPUSupportedSimilarity(indexOptions)
);
assertNull(format);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public List<Setting<?>> getSettings() {

@Override
public VectorsFormatProvider getVectorsFormatProvider() {
return (indexSettings, indexOptions) -> {
return (indexSettings, indexOptions, similarity) -> {
if (GPU_FORMAT.isEnabled()) {
GpuMode gpuMode = indexSettings.getValue(VECTORS_INDEXING_USE_GPU_SETTING);
if (gpuMode == GpuMode.TRUE) {
Expand All @@ -76,10 +76,10 @@ public VectorsFormatProvider getVectorsFormatProvider() {
"[index.vectors.indexing.use_gpu] was set to [true], but GPU resources are not accessible on the node."
);
}
return getVectorsFormat(indexOptions);
return getVectorsFormat(indexOptions, similarity);
}
if (gpuMode == GpuMode.AUTO && vectorIndexTypeSupported(indexOptions.getType()) && isGpuSupported) {
return getVectorsFormat(indexOptions);
return getVectorsFormat(indexOptions, similarity);
}
}
return null;
Expand All @@ -90,7 +90,10 @@ private boolean vectorIndexTypeSupported(DenseVectorFieldMapper.VectorIndexType
return type == DenseVectorFieldMapper.VectorIndexType.HNSW || type == DenseVectorFieldMapper.VectorIndexType.INT8_HNSW;
}

private static KnnVectorsFormat getVectorsFormat(DenseVectorFieldMapper.DenseVectorIndexOptions indexOptions) {
private static KnnVectorsFormat getVectorsFormat(
DenseVectorFieldMapper.DenseVectorIndexOptions indexOptions,
DenseVectorFieldMapper.VectorSimilarity similarity
) {
if (indexOptions.getType() == DenseVectorFieldMapper.VectorIndexType.HNSW) {
DenseVectorFieldMapper.HnswIndexOptions hnswIndexOptions = (DenseVectorFieldMapper.HnswIndexOptions) indexOptions;
int efConstruction = hnswIndexOptions.efConstruction();
Expand All @@ -99,6 +102,18 @@ private static KnnVectorsFormat getVectorsFormat(DenseVectorFieldMapper.DenseVec
}
return new ES92GpuHnswVectorsFormat(hnswIndexOptions.m(), efConstruction);
} else if (indexOptions.getType() == DenseVectorFieldMapper.VectorIndexType.INT8_HNSW) {
if (similarity == DenseVectorFieldMapper.VectorSimilarity.DOT_PRODUCT
Copy link
Member

Choose a reason for hiding this comment

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

I think this should just silently change to cosine when using the GPU integration to build the index, then using DOT_PRODUCT on search would work just fine.

this way we only disallow max-inner-product.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion, I will follow up on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 60e1dc9

|| similarity == DenseVectorFieldMapper.VectorSimilarity.MAX_INNER_PRODUCT) {
throw new IllegalArgumentException(
"GPU vector indexing does not support ["
+ similarity
+ "] similarity for [int8_hnsw] index type. "
+ "Instead, consider using ["
+ DenseVectorFieldMapper.VectorSimilarity.COSINE
+ "] or "
+ " [hnsw] index type."
);
}
DenseVectorFieldMapper.Int8HnswIndexOptions int8HnswIndexOptions = (DenseVectorFieldMapper.Int8HnswIndexOptions) indexOptions;
int efConstruction = int8HnswIndexOptions.efConstruction();
if (efConstruction == HnswGraphBuilder.DEFAULT_BEAM_WIDTH) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private KnnVectorsFormat getKnnVectorsFormat(String indexOptionsType) throws IOE
b.field("type", "dense_vector");
b.field("dims", dims);
b.field("index", true);
b.field("similarity", "dot_product");
b.field("similarity", "cosine");
b.startObject("index_options");
b.field("type", indexOptionsType);
b.endObject();
Expand Down