Skip to content

Conversation

@dian-lun-lin
Copy link

@dian-lun-lin dian-lun-lin commented Aug 23, 2025

Pull Request resolved: #4450

This pull request introduces support for Intel ScalableVectorSearch, integrating Intel's proprietary LVQ and LeanVec technologies in binary form. The following index types are now supported: IndexSVSVamana, IndexSVSVamanaLVQ, IndexSVSVamanaLeanVec, and IndexSVSFlat. IndexSVSVamana and IndexSVSFlat utilizes SVS open-source float32/float16/int8 implementation.

Key features and enhancements include:

  • Implemented search, add, and remove_ids
  • Implemented filter search and range_search
  • Enabled SVS open-source fp16 and scalar quantization implementation
  • Enabled factory methods
  • Enabled save/load
  • Enabled Python bindings
  • Enable fallback mechanism that fallbacks to 8-bit scalar quantization if LVQ/LeanVec is used on non-intel hardware CPUs
  • Added examples in both Python and C++ under the tutorial/ directory
  • Added FAISS_ENABLE_SVS flag to allows users to optionally enable SVS

TODOs:

  • Support more indices such as IndexSVSIVF and IndexSVSFlat with LVQ and LeanVec
  • Documentation

@dian-lun-lin dian-lun-lin marked this pull request as ready for review October 7, 2025 23:37
@mnorris11
Copy link

The format step is requesting these changes (let me know if you can see logs, then I won't paste a huge block in the future):

diff --git a/faiss/svs/IndexSVSFlat.cpp b/faiss/svs/IndexSVSFlat.cpp
index d33ff3b..5bfd00a 100644
--- a/faiss/svs/IndexSVSFlat.cpp
+++ b/faiss/svs/IndexSVSFlat.cpp
@@ -127,14 +127,12 @@ void IndexSVSFlat::deserialize_impl(std::istream& in) {
 
     switch (metric_type) {
         case METRIC_INNER_PRODUCT:
-            impl = new svs::Flat(
-                    svs::Flat::assemble<float, storage_type>(
-                            in, svs::DistanceIP(), std::move(threadpool)));
+            impl = new svs::Flat(svs::Flat::assemble<float, storage_type>(
+                    in, svs::DistanceIP(), std::move(threadpool)));
             break;
         case METRIC_L2:
-            impl = new svs::Flat(
-                    svs::Flat::assemble<float, storage_type>(
-                            in, svs::DistanceL2(), std::move(threadpool)));
+            impl = new svs::Flat(svs::Flat::assemble<float, storage_type>(
+                    in, svs::DistanceL2(), std::move(threadpool)));
             break;
         default:
             FAISS_ASSERT(!"not supported SVS distance");
diff --git a/faiss/svs/IndexSVSVamana.cpp b/faiss/svs/IndexSVSVamana.cpp
index 2465c2c..4d94f53 100644
--- a/faiss/svs/IndexSVSVamana.cpp
+++ b/faiss/svs/IndexSVSVamana.cpp
@@ -74,10 +74,10 @@ template <
         typename Alloc = svs::data::Blocked<svs::lib::Allocator<T>>,
         svs::data::ImmutableMemoryDataset Dataset,
         svs::threads::ThreadPool Pool>
-requires std::is_floating_point_v<T> || std::is_same_v<T, svs::Float16>
-        svs::data::SimpleData<T, svs::Dynamic, Alloc> make_storage(
-                const Dataset& data,
-                Pool& pool) {
+    requires std::is_floating_point_v<T> || std::is_same_v<T, svs::Float16>
+svs::data::SimpleData<T, svs::Dynamic, Alloc> make_storage(
+        const Dataset& data,
+        Pool& pool) {
     svs::data::SimpleData<T, svs::Dynamic, Alloc> result(
             data.size(), data.dimensions(), Alloc{});
     svs::threads::parallel_for(
@@ -96,9 +96,10 @@ template <
         typename Alloc = svs::data::Blocked<svs::lib::Allocator<T>>,
         svs::data::ImmutableMemoryDataset Dataset,
         svs::threads::ThreadPool Pool>
-requires std::is_integral_v<T> svs::quantization::scalar::
-        SQDataset<T, svs::Dynamic, Alloc>
-        make_storage(const Dataset& data, Pool& pool) {
+    requires std::is_integral_v<T>
+svs::quantization::scalar::SQDataset<T, svs::Dynamic, Alloc> make_storage(
+        const Dataset& data,
+        Pool& pool) {
     return svs::quantization::scalar::SQDataset<T, svs::Dynamic, Alloc>::
             compress(data, pool, Alloc{});
 }
@@ -165,12 +166,10 @@ svs::DynamicVamana* deserialize_impl_t(
 
     return std::visit(
             [&](auto&& distance) {
-                return new svs::DynamicVamana(
-                        svs::DynamicVamana::
-                                assemble<float, storage_type_t<ElementType>>(
-                                        stream,
-                                        std::move(distance),
-                                        std::move(threadpool)));
+                return new svs::DynamicVamana(svs::DynamicVamana::assemble<
+                                              float,
+                                              storage_type_t<ElementType>>(
+                        stream, std::move(distance), std::move(threadpool)));
             },
             get_svs_distance(metric));
 }
diff --git a/faiss/svs/IndexSVSVamanaLVQ.cpp b/faiss/svs/IndexSVSVamanaLVQ.cpp
index 1a1b3ed..c4d6610 100644
--- a/faiss/svs/IndexSVSVamanaLVQ.cpp
+++ b/faiss/svs/IndexSVSVamanaLVQ.cpp
@@ -181,12 +181,10 @@ void IndexSVSVamanaLVQ::deserialize_impl(std::istream& in) {
                             FAISS_ASSERT(!"not supported SVS LVQ level");
                     }
                 } else {
-                    impl = new svs::DynamicVamana(
-                            svs::DynamicVamana::
-                                    assemble<float, storage_type_sq>(
-                                            in,
-                                            svs_distance,
-                                            std::move(threadpool)));
+                    impl = new svs::DynamicVamana(svs::DynamicVamana::assemble<
+                                                  float,
+                                                  storage_type_sq>(
+                            in, svs_distance, std::move(threadpool)));
                 }
             },
             svs_distance);
diff --git a/faiss/svs/IndexSVSVamanaLeanVec.cpp b/faiss/svs/IndexSVSVamanaLeanVec.cpp
index 3f95adb..992470c 100644
--- a/faiss/svs/IndexSVSVamanaLeanVec.cpp
+++ b/faiss/svs/IndexSVSVamanaLeanVec.cpp
@@ -228,12 +228,10 @@ void IndexSVSVamanaLeanVec::deserialize_impl(std::istream& in) {
                             FAISS_ASSERT(!"not supported SVS LeanVec level");
                     }
                 } else {
-                    impl = new svs::DynamicVamana(
-                            svs::DynamicVamana::
-                                    assemble<float, storage_type_sq>(
-                                            in,
-                                            svs_distance,
-                                            std::move(threadpool)));
+                    impl = new svs::DynamicVamana(svs::DynamicVamana::assemble<
+                                                  float,
+                                                  storage_type_sq>(
+                            in, svs_distance, std::move(threadpool)));
                 }
             },
             svs_distance);
diff --git a/tests/test_svs.cpp b/tests/test_svs.cpp
index ff72fd8..f5556ab 100644
--- a/tests/test_svs.cpp
+++ b/tests/test_svs.cpp
@@ -21,13 +21,13 @@
  */
 
 #include <faiss/Index.h>
+#include <faiss/impl/AuxIndexStructures.h>
+#include <faiss/impl/IDSelector.h>
+#include <faiss/index_io.h>
 #include <faiss/svs/IndexSVSFlat.h>
 #include <faiss/svs/IndexSVSVamana.h>
 #include <faiss/svs/IndexSVSVamanaLVQ.h>
 #include <faiss/svs/IndexSVSVamanaLeanVec.h>
-#include <faiss/impl/AuxIndexStructures.h>
-#include <faiss/impl/IDSelector.h>
-#include <faiss/index_io.h>
 #include <gtest/gtest.h>
 #include <type_traits>

@mnorris11 mnorris11 self-assigned this Oct 8, 2025
@dian-lun-lin
Copy link
Author

The format step is requesting these changes (let me know if you can see logs, then I won't paste a huge block in the future):

diff --git a/faiss/svs/IndexSVSFlat.cpp b/faiss/svs/IndexSVSFlat.cpp
index d33ff3b..5bfd00a 100644
--- a/faiss/svs/IndexSVSFlat.cpp
+++ b/faiss/svs/IndexSVSFlat.cpp
@@ -127,14 +127,12 @@ void IndexSVSFlat::deserialize_impl(std::istream& in) {
 
     switch (metric_type) {
         case METRIC_INNER_PRODUCT:
-            impl = new svs::Flat(
-                    svs::Flat::assemble<float, storage_type>(
-                            in, svs::DistanceIP(), std::move(threadpool)));
+            impl = new svs::Flat(svs::Flat::assemble<float, storage_type>(
+                    in, svs::DistanceIP(), std::move(threadpool)));
             break;
         case METRIC_L2:
-            impl = new svs::Flat(
-                    svs::Flat::assemble<float, storage_type>(
-                            in, svs::DistanceL2(), std::move(threadpool)));
+            impl = new svs::Flat(svs::Flat::assemble<float, storage_type>(
+                    in, svs::DistanceL2(), std::move(threadpool)));
             break;
         default:
             FAISS_ASSERT(!"not supported SVS distance");
diff --git a/faiss/svs/IndexSVSVamana.cpp b/faiss/svs/IndexSVSVamana.cpp
index 2465c2c..4d94f53 100644
--- a/faiss/svs/IndexSVSVamana.cpp
+++ b/faiss/svs/IndexSVSVamana.cpp
@@ -74,10 +74,10 @@ template <
         typename Alloc = svs::data::Blocked<svs::lib::Allocator<T>>,
         svs::data::ImmutableMemoryDataset Dataset,
         svs::threads::ThreadPool Pool>
-requires std::is_floating_point_v<T> || std::is_same_v<T, svs::Float16>
-        svs::data::SimpleData<T, svs::Dynamic, Alloc> make_storage(
-                const Dataset& data,
-                Pool& pool) {
+    requires std::is_floating_point_v<T> || std::is_same_v<T, svs::Float16>
+svs::data::SimpleData<T, svs::Dynamic, Alloc> make_storage(
+        const Dataset& data,
+        Pool& pool) {
     svs::data::SimpleData<T, svs::Dynamic, Alloc> result(
             data.size(), data.dimensions(), Alloc{});
     svs::threads::parallel_for(
@@ -96,9 +96,10 @@ template <
         typename Alloc = svs::data::Blocked<svs::lib::Allocator<T>>,
         svs::data::ImmutableMemoryDataset Dataset,
         svs::threads::ThreadPool Pool>
-requires std::is_integral_v<T> svs::quantization::scalar::
-        SQDataset<T, svs::Dynamic, Alloc>
-        make_storage(const Dataset& data, Pool& pool) {
+    requires std::is_integral_v<T>
+svs::quantization::scalar::SQDataset<T, svs::Dynamic, Alloc> make_storage(
+        const Dataset& data,
+        Pool& pool) {
     return svs::quantization::scalar::SQDataset<T, svs::Dynamic, Alloc>::
             compress(data, pool, Alloc{});
 }
@@ -165,12 +166,10 @@ svs::DynamicVamana* deserialize_impl_t(
 
     return std::visit(
             [&](auto&& distance) {
-                return new svs::DynamicVamana(
-                        svs::DynamicVamana::
-                                assemble<float, storage_type_t<ElementType>>(
-                                        stream,
-                                        std::move(distance),
-                                        std::move(threadpool)));
+                return new svs::DynamicVamana(svs::DynamicVamana::assemble<
+                                              float,
+                                              storage_type_t<ElementType>>(
+                        stream, std::move(distance), std::move(threadpool)));
             },
             get_svs_distance(metric));
 }
diff --git a/faiss/svs/IndexSVSVamanaLVQ.cpp b/faiss/svs/IndexSVSVamanaLVQ.cpp
index 1a1b3ed..c4d6610 100644
--- a/faiss/svs/IndexSVSVamanaLVQ.cpp
+++ b/faiss/svs/IndexSVSVamanaLVQ.cpp
@@ -181,12 +181,10 @@ void IndexSVSVamanaLVQ::deserialize_impl(std::istream& in) {
                             FAISS_ASSERT(!"not supported SVS LVQ level");
                     }
                 } else {
-                    impl = new svs::DynamicVamana(
-                            svs::DynamicVamana::
-                                    assemble<float, storage_type_sq>(
-                                            in,
-                                            svs_distance,
-                                            std::move(threadpool)));
+                    impl = new svs::DynamicVamana(svs::DynamicVamana::assemble<
+                                                  float,
+                                                  storage_type_sq>(
+                            in, svs_distance, std::move(threadpool)));
                 }
             },
             svs_distance);
diff --git a/faiss/svs/IndexSVSVamanaLeanVec.cpp b/faiss/svs/IndexSVSVamanaLeanVec.cpp
index 3f95adb..992470c 100644
--- a/faiss/svs/IndexSVSVamanaLeanVec.cpp
+++ b/faiss/svs/IndexSVSVamanaLeanVec.cpp
@@ -228,12 +228,10 @@ void IndexSVSVamanaLeanVec::deserialize_impl(std::istream& in) {
                             FAISS_ASSERT(!"not supported SVS LeanVec level");
                     }
                 } else {
-                    impl = new svs::DynamicVamana(
-                            svs::DynamicVamana::
-                                    assemble<float, storage_type_sq>(
-                                            in,
-                                            svs_distance,
-                                            std::move(threadpool)));
+                    impl = new svs::DynamicVamana(svs::DynamicVamana::assemble<
+                                                  float,
+                                                  storage_type_sq>(
+                            in, svs_distance, std::move(threadpool)));
                 }
             },
             svs_distance);
diff --git a/tests/test_svs.cpp b/tests/test_svs.cpp
index ff72fd8..f5556ab 100644
--- a/tests/test_svs.cpp
+++ b/tests/test_svs.cpp
@@ -21,13 +21,13 @@
  */
 
 #include <faiss/Index.h>
+#include <faiss/impl/AuxIndexStructures.h>
+#include <faiss/impl/IDSelector.h>
+#include <faiss/index_io.h>
 #include <faiss/svs/IndexSVSFlat.h>
 #include <faiss/svs/IndexSVSVamana.h>
 #include <faiss/svs/IndexSVSVamanaLVQ.h>
 #include <faiss/svs/IndexSVSVamanaLeanVec.h>
-#include <faiss/impl/AuxIndexStructures.h>
-#include <faiss/impl/IDSelector.h>
-#include <faiss/index_io.h>
 #include <gtest/gtest.h>
 #include <type_traits>

solved. Thanks!

@meta-codesync
Copy link
Contributor

meta-codesync bot commented Oct 15, 2025

@mnorris11 has imported this pull request. If you are a Meta employee, you can view this in D84713772.

@@ -0,0 +1,72 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.

Choose a reason for hiding this comment

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

Our internal linter is flagging that this copyright has 2 headers (which is OK), but we should prepend "Portions", so it should just read as "Portions Copyright ...etc" instead of starting with just "Copyright". I think this would be applicable to any new file added.

Comment on lines +43 to +46
if(FAISS_ENABLE_SVS)
list(APPEND FAISS_TEST_SRC test_svs.cpp)
endif()

Choose a reason for hiding this comment

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

For some reason the build is failing, even though there is a condition here for including test_svs.cpp. (Ideally no build will change from this PR, and we can add a CI for SVS enabled in a second PR.)

[100%] Building CXX object tests/CMakeFiles/faiss_test.dir/test_svs.cpp.o

https://github.com/facebookresearch/faiss/actions/runs/18384740136/job/52824514737?pr=4548

option(FAISS_ENABLE_C_API "Build C API." OFF)
option(FAISS_ENABLE_EXTRAS "Build extras like benchmarks and demos" ON)
option(FAISS_USE_LTO "Enable Link-Time optimization" OFF)
option(FAISS_ENABLE_SVS "Enable SVS (Intel(R) Scalable Vector Search) integration." ON)

Choose a reason for hiding this comment

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

Is the build failure due to this by default being ON?

Copy link

@subhadeepkaran subhadeepkaran Oct 20, 2025

Choose a reason for hiding this comment

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

by default do we want to keep it ON or we want o keep it OFF? (concern stems from whether we are confident that SVS works on all platforms that we care to support, with the expected performance levels)

Choose a reason for hiding this comment

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

Yeah after discussion, we plan to set OFF by default, and we will create a new CI which will enable it. If it does not break other platforms, eventually we can set ON by default, but that can be a separate PR.

svs->deserialize_impl(is);
idx = svs;
} else if (h == fourcc("ISVF")) {
// SVS Flat

Choose a reason for hiding this comment

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

nit: comment needed?

find_package(svs REQUIRED)
target_compile_options(svs::svs INTERFACE "-DSVS_ENABLE_OMP=1")

target_link_libraries(faiss PUBLIC svs::svs svs::svs_shared_library)

Choose a reason for hiding this comment

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

nit: maybe we can use a loop?

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.

Integration of Intel Scalable Vector Search (SVS) with FAISS

8 participants