Skip to content

Commit de51459

Browse files
committed
Merge remote-tracking branch 'upstream/main' into osq-offheap-vector
2 parents 151d239 + 602bfbd commit de51459

25 files changed

+635
-357
lines changed

.github/workflows/actions.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
with:
2525
persist-credentials: false
2626
- name: Install the latest version of uv
27-
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
27+
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
2828
- name: Check workflows with actionlint
2929
run: uvx --from actionlint-py actionlint -color
3030
zizmor:
@@ -38,15 +38,15 @@ jobs:
3838
with:
3939
persist-credentials: false
4040
- name: Install the latest version of uv
41-
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
41+
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
4242
- name: Run zizmor
4343
run: uvx zizmor --pedantic --format=sarif . > results.sarif
4444
env:
4545
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4646
# TODO: offline checks only to avoid any rate-limiting issues, maybe enable nightly?
4747
ZIZMOR_OFFLINE: true
4848
- name: Upload SARIF file
49-
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
49+
uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
5050
with:
5151
sarif_file: results.sarif
5252
category: zizmor

.github/workflows/codeql.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ jobs:
4242

4343
# Initializes the CodeQL tools for scanning.
4444
- name: Initialize CodeQL
45-
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
45+
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
4646
with:
4747
languages: ${{ matrix.language }}
4848
build-mode: ${{ matrix.build-mode }}
4949
queries: security-extended
5050
config-file: ./.github/codeql-config.yml
5151

5252
- name: Perform CodeQL Analysis
53-
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
53+
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
5454
with:
5555
category: "/language:${{ matrix.language }}"

.github/workflows/dependency-submission.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
uses: ./.github/actions/prepare-for-build
2828

2929
- name: Generate and submit dependency graph
30-
uses: gradle/actions/dependency-submission@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
30+
uses: gradle/actions/dependency-submission@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
3131
env:
3232
DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS: "(?i)(^|:)(compileClasspath|runtimeClasspath|testCompileClasspath|testRuntimeClasspath)$"
3333
DEPENDENCY_GRAPH_EXCLUDE_CONFIGURATIONS: "(?i)(^|:)(classpath|.*PluginClasspath|kotlinCompilerClasspath|kaptClasspath|annotationProcessor|detachedConfiguration.*)$"

.github/workflows/mark-stale-PRs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222

2323
steps:
2424
- name: Run stale PR action
25-
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
25+
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
2626
with:
2727
repo-token: ${{ secrets.GITHUB_TOKEN }}
2828

.github/workflows/run-checks-python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
persist-credentials: false
3535

3636
- name: Setup Python
37-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
37+
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
3838
with:
3939
python-version: "3.12.6"
4040

dev-tools/scripts/pyproject.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ reportUnnecessaryComparison = "hint" # Requires cleaning up some dead co
2222
reportUntypedNamedTuple = "hint" # Requires moving to different type of named tuple
2323
reportUnusedCallResult = "none" # Forces you to assign unused retvals to '_', very noisy.
2424

25-
# Intentionally disabled because it slows pyright by 2x
26-
reportShadowedImports = "none" # Extremely slow check
27-
2825
[tool.ruff]
2926
target-version = "py312"
3027
line-length = 200

dev-tools/scripts/requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
# jinja template processing of releaseWizard.yaml
22
Jinja2==3.1.6
33
# parsing and processing of releaseWizard.yaml
4-
PyYAML==6.0.2
4+
PyYAML==6.0.3
55
# international holidays in releaseWizard
6-
holidays==0.80
6+
holidays==0.81
77
# calendar processing in releaseWizard
88
ics==0.7.2
99
# terminal processing in releaseWizard
1010
console-menu==0.8.0
1111
# pull request processing in githubPRs
12-
PyGithub==2.7.0
12+
PyGithub==2.8.1
1313
# JIRA processing in githubPRs
1414
jira==3.8.0
1515
# type-checking in "make lint"
16-
basedpyright==1.31.4
16+
basedpyright==1.31.6
1717
# linting in "make lint"
18-
ruff==0.12.11
18+
ruff==0.13.2

lucene/CHANGES.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ New Features
156156
`Lucene104HnswScalarQuantizedVectorsFormat` replaces the now legacy `Lucene99HnswScalarQuantizedVectorsFormat`
157157
(Trevor McCulloch)
158158

159+
* GITHUB#15271: Extend `Lucene104ScalarQuantizedVectorsFormat` and `Lucene104HnswScalarQuantizedVectorsFormat` to
160+
allow asymmetric quantization. The initially supported bits are single bit with 4 bit queries. This is a replacement
161+
for the now legacy `Lucene102HnswBinaryQuantizedVectorsFormat` and `Lucene102BinaryQuantizedVectorsFormat`.
162+
(Ben Trent)
163+
159164
Improvements
160165
---------------------
161166
* GITHUB#15148: Add support uint8 distance and allow 8 bit scalar quantization (Trevor McCulloch)

lucene/core/src/java/org/apache/lucene/codecs/hnsw/FlatVectorScorerUtil.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.lucene.codecs.hnsw;
1919

20+
import org.apache.lucene.codecs.lucene104.AsymmetricScalarQuantizeFlatVectorsScorer;
2021
import org.apache.lucene.internal.vectorization.VectorizationProvider;
2122

2223
/**
@@ -47,7 +48,8 @@ public static FlatVectorsScorer getLucene99ScalarQuantizedVectorsScorer() {
4748
* retrieved through this method may be optimized on certain platforms. Otherwise, a
4849
* DefaultFlatVectorScorer is returned.
4950
*/
50-
public static FlatVectorsScorer getLucene104ScalarQuantizedFlatVectorsScorer() {
51+
public static AsymmetricScalarQuantizeFlatVectorsScorer
52+
getLucene104ScalarQuantizedFlatVectorsScorer() {
5153
return IMPL.getLucene104ScalarQuantizedVectorsScorer();
5254
}
5355
}

lucene/core/src/java/org/apache/lucene/codecs/lucene102/Lucene102BinaryFlatVectorsScorer.java

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
*/
1717
package org.apache.lucene.codecs.lucene102;
1818

19-
import static org.apache.lucene.codecs.lucene102.Lucene102BinaryQuantizedVectorsFormat.INDEX_BITS;
2019
import static org.apache.lucene.codecs.lucene102.Lucene102BinaryQuantizedVectorsFormat.QUERY_BITS;
2120
import static org.apache.lucene.index.VectorSimilarityFunction.COSINE;
21+
import static org.apache.lucene.index.VectorSimilarityFunction.EUCLIDEAN;
22+
import static org.apache.lucene.index.VectorSimilarityFunction.MAXIMUM_INNER_PRODUCT;
2223
import static org.apache.lucene.util.quantization.OptimizedScalarQuantizer.transposeHalfByte;
2324

2425
import java.io.IOException;
@@ -30,13 +31,13 @@
3031
import org.apache.lucene.util.hnsw.RandomVectorScorer;
3132
import org.apache.lucene.util.hnsw.RandomVectorScorerSupplier;
3233
import org.apache.lucene.util.hnsw.UpdateableRandomVectorScorer;
33-
import org.apache.lucene.util.quantization.OptimizedScalarQuantizedVectorSimilarity;
3434
import org.apache.lucene.util.quantization.OptimizedScalarQuantizer;
3535
import org.apache.lucene.util.quantization.OptimizedScalarQuantizer.QuantizationResult;
3636

3737
/** Vector scorer over binarized vector values */
3838
public class Lucene102BinaryFlatVectorsScorer implements FlatVectorsScorer {
3939
private final FlatVectorsScorer nonQuantizedDelegate;
40+
private static final float FOUR_BIT_SCALE = 1f / ((1 << 4) - 1);
4041

4142
public Lucene102BinaryFlatVectorsScorer(FlatVectorsScorer nonQuantizedDelegate) {
4243
this.nonQuantizedDelegate = nonQuantizedDelegate;
@@ -72,20 +73,10 @@ public RandomVectorScorer getRandomVectorScorer(
7273
quantizer.scalarQuantize(target, initial, (byte) 4, centroid);
7374
transposeHalfByte(initial, quantized);
7475
return new RandomVectorScorer.AbstractRandomVectorScorer(binarizedVectors) {
75-
private final OptimizedScalarQuantizedVectorSimilarity similarity =
76-
new OptimizedScalarQuantizedVectorSimilarity(
77-
similarityFunction,
78-
binarizedVectors.dimension(),
79-
binarizedVectors.getCentroidDP(),
80-
QUERY_BITS,
81-
INDEX_BITS);
82-
8376
@Override
8477
public float score(int node) throws IOException {
85-
var indexVector = binarizedVectors.vectorValue(node);
86-
var indexCorrections = binarizedVectors.getCorrectiveTerms(node);
87-
float dotProduct = VectorUtil.int4BitDotProduct(quantized, indexVector);
88-
return similarity.score(dotProduct, queryCorrections, indexCorrections);
78+
return quantizedScore(
79+
quantized, queryCorrections, binarizedVectors, node, similarityFunction);
8980
}
9081
};
9182
}
@@ -102,8 +93,7 @@ public RandomVectorScorer getRandomVectorScorer(
10293
RandomVectorScorerSupplier getRandomVectorScorerSupplier(
10394
VectorSimilarityFunction similarityFunction,
10495
Lucene102BinaryQuantizedVectorsWriter.OffHeapBinarizedQueryVectorValues scoringVectors,
105-
BinarizedByteVectorValues targetVectors)
106-
throws IOException {
96+
BinarizedByteVectorValues targetVectors) {
10797
return new BinarizedRandomVectorScorerSupplier(
10898
scoringVectors, targetVectors, similarityFunction);
10999
}
@@ -118,31 +108,15 @@ static class BinarizedRandomVectorScorerSupplier implements RandomVectorScorerSu
118108
private final Lucene102BinaryQuantizedVectorsWriter.OffHeapBinarizedQueryVectorValues
119109
queryVectors;
120110
private final BinarizedByteVectorValues targetVectors;
121-
private final OptimizedScalarQuantizedVectorSimilarity similarity;
122-
123-
BinarizedRandomVectorScorerSupplier(
124-
Lucene102BinaryQuantizedVectorsWriter.OffHeapBinarizedQueryVectorValues queryVectors,
125-
BinarizedByteVectorValues targetVectors,
126-
VectorSimilarityFunction similarityFunction)
127-
throws IOException {
128-
this.queryVectors = queryVectors;
129-
this.targetVectors = targetVectors;
130-
this.similarity =
131-
new OptimizedScalarQuantizedVectorSimilarity(
132-
similarityFunction,
133-
targetVectors.dimension(),
134-
targetVectors.getCentroidDP(),
135-
QUERY_BITS,
136-
INDEX_BITS);
137-
}
111+
private final VectorSimilarityFunction similarityFunction;
138112

139113
BinarizedRandomVectorScorerSupplier(
140114
Lucene102BinaryQuantizedVectorsWriter.OffHeapBinarizedQueryVectorValues queryVectors,
141115
BinarizedByteVectorValues targetVectors,
142-
OptimizedScalarQuantizedVectorSimilarity similarity) {
116+
VectorSimilarityFunction similarityFunction) {
143117
this.queryVectors = queryVectors;
144118
this.targetVectors = targetVectors;
145-
this.similarity = similarity;
119+
this.similarityFunction = similarityFunction;
146120
}
147121

148122
@Override
@@ -165,20 +139,57 @@ public float score(int node) throws IOException {
165139
if (vector == null || queryCorrections == null) {
166140
throw new IllegalStateException("setScoringOrdinal was not called");
167141
}
168-
var indexVector = targetVectors.vectorValue(node);
169-
var indexCorrections = targetVectors.getCorrectiveTerms(node);
170-
return similarity.score(
171-
(float) VectorUtil.int4BitDotProduct(vector, indexVector),
172-
queryCorrections,
173-
indexCorrections);
142+
return quantizedScore(vector, queryCorrections, targetVectors, node, similarityFunction);
174143
}
175144
};
176145
}
177146

178147
@Override
179148
public RandomVectorScorerSupplier copy() throws IOException {
180149
return new BinarizedRandomVectorScorerSupplier(
181-
queryVectors.copy(), targetVectors.copy(), similarity);
150+
queryVectors.copy(), targetVectors.copy(), similarityFunction);
151+
}
152+
}
153+
154+
static float quantizedScore(
155+
byte[] quantizedQuery,
156+
OptimizedScalarQuantizer.QuantizationResult queryCorrections,
157+
BinarizedByteVectorValues targetVectors,
158+
int targetOrd,
159+
VectorSimilarityFunction similarityFunction)
160+
throws IOException {
161+
byte[] binaryCode = targetVectors.vectorValue(targetOrd);
162+
float qcDist = VectorUtil.int4BitDotProduct(quantizedQuery, binaryCode);
163+
OptimizedScalarQuantizer.QuantizationResult indexCorrections =
164+
targetVectors.getCorrectiveTerms(targetOrd);
165+
float x1 = indexCorrections.quantizedComponentSum();
166+
float ax = indexCorrections.lowerInterval();
167+
// Here we assume `lx` is simply bit vectors, so the scaling isn't necessary
168+
float lx = indexCorrections.upperInterval() - ax;
169+
float ay = queryCorrections.lowerInterval();
170+
float ly = (queryCorrections.upperInterval() - ay) * FOUR_BIT_SCALE;
171+
float y1 = queryCorrections.quantizedComponentSum();
172+
float score =
173+
ax * ay * targetVectors.dimension() + ay * lx * x1 + ax * ly * y1 + lx * ly * qcDist;
174+
// For euclidean, we need to invert the score and apply the additional correction, which is
175+
// assumed to be the squared l2norm of the centroid centered vectors.
176+
if (similarityFunction == EUCLIDEAN) {
177+
score =
178+
queryCorrections.additionalCorrection()
179+
+ indexCorrections.additionalCorrection()
180+
- 2 * score;
181+
return Math.max(1 / (1f + score), 0);
182+
} else {
183+
// For cosine and max inner product, we need to apply the additional correction, which is
184+
// assumed to be the non-centered dot-product between the vector and the centroid
185+
score +=
186+
queryCorrections.additionalCorrection()
187+
+ indexCorrections.additionalCorrection()
188+
- targetVectors.getCentroidDP();
189+
if (similarityFunction == MAXIMUM_INNER_PRODUCT) {
190+
return VectorUtil.scaleMaxInnerProductScore(score);
191+
}
192+
return Math.max((1f + score) / 2f, 0);
182193
}
183194
}
184195
}

0 commit comments

Comments
 (0)