-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Integrate Panorama into IndexHNSWFlatPanorama
#4621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
mdouze
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR.
About Panorama in general: would it be feasible to make an IndexRefine that supports FlatPanorma as a refinement index?
The reason is because it may be more efficient to do all the non-exhaustive searches in low dimension and refine the result list in the end.
This would also make it possible to apply panorama to low-accuracy & fast indexes like FastScan and RabitQ indexes.
| for (int j = start_idx; j < end_idx; j++) { | ||
| sum += x[j] * x[j]; | ||
| } | ||
| dst_cum_sums[level] = sqrt(sum); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a reason to do a sqrt (ie. use L2 distance instead of squared L2 distance as usual)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
faiss/IndexHNSW.h
Outdated
| * in a random order, which makes cache misses dominate the distance computation | ||
| * time. | ||
| * | ||
| * The num_levels parameter controls the granularity of progressive distance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to call it something else than num_levels? HNSW has its notion of levels -- the number of hierachy levels.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, perhaps num_panorama_levels?
| * the algorithm to prune unpromising candidates early using Cauchy-Schwarz | ||
| * bounds on partial inner products. Hence, recall is not guaranteed to be the | ||
| * same as vanilla HNSW due to the heterogeneous precision within the search | ||
| * beam (exact vs. partial distance estimates affecting traversal order). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean that the batch size on which the distances are computed is at most the out-degree of the HNSW graph (set to 64 by default)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Precisely.
|
Please share any performance comparison you have with this code vs. the HNSWFlat implementation. |
|
@mdouze thanks for the review
|
|
@AlSchlo is it worth allowing configuring a default |
|
@alexanderguzhva excellent suggestion! so we actually used to have an epsilon knob there, but we ended up not talking about it in the paper. It's a knob that just adds confusion IMO and makes the workload more unpredictable. We did not study it in more detail as the paper was getting too dense. |
|
Will write tests sometime this week. I also realize I need to change the |
|
Once this is done, I will focus on getting the |
|
Hi @AlSchlo and @aknayar , much thanks for the contributions! The stats PR has been merged. After discussing internally, it sounds like the priority is having the |
|
Hi @mnorris11,
In our paper, we integrate Panorama into This PR instead targets the common scenario where the dimensionality is large and no downstream index is used to refine results after HNSW. That setup is quite typical in practice — for instance, it would benefit us internally at Databricks. So, my take would be to include both implementations: they address different use cases. Longer-term, the goal is to adapt Panorama into quantization-based techniques like |
|
TL;DR @mnorris11
|
|
@AlSchlo the problem with rabitq that I see is that the overhead of storing additional coefficient is going to be significant, unlike 32-bit or even 16-bit floats for the refinement |
|
@alexanderguzhva Yes, this is one issue that will need clever engineering. I was thinking of perhaps quantizing those coefficients. Also rabitq theory assumes random projection. We need to adapt the theory to be able to make it work with Cayley & PCA. From a computational point of view however, even if the vector is binary quantized, Panorama can still be applied. |
|
@mnorris11 has imported this pull request. If you are a Meta employee, you can view this in D85902427. |
|
Sorry for the delay on these PRs, I'm still conducting some benchmarking. |
|
@mnorris11 No worries, and thank you so much for the reviews! As an update, after #4645 is confirmed, I have a local build of
|




This PR introduces Panorama into
HNSWFlat, following our paper. Panorama achieves up to 4× lower latency on higher-dimensional data, making it a great option for medium-sized datasets that don't benefit much from quantization.Below are some benchmarks on SIFT-128, GIST-960, and synthetic 2048-dimensional data. I recommend checking out the paper for more results. As expected, Panorama is not a silver bullet when combined with HNSW—it’s only worthwhile for high-dimensional data.
It might be worth considering, in the future, adding a function that dynamically sets the number of levels. However, this would require reorganizing the cumulative sums.
SIFT-128
Note: SIFT-128 performs slightly worse here than in our paper because we use 8 levels, whereas the paper explored several level configurations. Eight levels introduce quite a bit of overhead for 128-dimensional data, but I kept it consistent across all benchmarks for comparison.
GIST-960
Synthetic-2048