Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 lucene/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,11 @@ Optimizations

Bug Fixes
---------------------
* GITHUB#16251: Fix UpdateGraphsUtils#computeJoinSet to not request more coverage for a node than
the node's degree. Previously nodes with a degree of 1 were always forced into the join set,
which made the join set degenerate to the whole graph on hub-and-spoke shaped HNSW graphs.
(Mayya Sharipova)

* GITHUB#16350: Disable bulk-scoring in monitor queries. (Alan Woodward)

* GITHUB#16378: Accumulate join Total/Avg scores in double precision in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static IntHashSet computeJoinSet(HnswGraph graph) throws IOException {
for (int v = 0; v < size; v++) {
graph.seek(0, v);
int degree = graph.neighborCount();
k = degree < 9 ? 2 : Math.ceilDiv(degree, 4);
k = coverage(degree);
gExit += k;
int gain = k + degree;
heap.push(encode(gain, v));
Expand All @@ -67,7 +67,7 @@ public static IntHashSet computeJoinSet(HnswGraph graph) throws IOException {
for (int u = graph.nextNeighbor(); u != NO_MORE_DOCS; u = graph.nextNeighbor()) {
ns[i++] = u;
}
k = degree < 9 ? 2 : Math.ceilDiv(degree, 4);
k = coverage(degree);
if (stale[v]) { // if stale, recalculate gain
int newGain = Math.max(0, k - counts[v]);
for (int u : ns) {
Expand Down Expand Up @@ -101,6 +101,22 @@ public static IntHashSet computeJoinSet(HnswGraph graph) throws IOException {
return j;
}

/**
* How many times a node needs to be covered by the nodes of the join set before we consider it
* covered.
*
* <p>A node's neighbours are the only nodes that can cover it, so a node can never be covered
* more than {@code degree} times. Requesting a bigger coverage than that would make the coverage
* unsatisfiable, and the node would be forced to join the join set itself. This happens on
* degenerate graphs (e.g. hub-and-spoke shapes produced by highly duplicated or low dimensional
* vectors) where most of the nodes have a degree of 1: without clamping, every leaf ends up in
* the join set, and the join set degenerates to the whole graph.
*/
private static int coverage(int degree) {
int k = degree < 9 ? 2 : Math.ceilDiv(degree, 4);
return Math.min(k, degree);
}

private static long encode(int value1, int value2) {
return (((long) -value1) << 32) | (value2 & 0xFFFFFFFFL);
}
Expand Down
Loading