faiss 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +2 -0
  4. data/ext/faiss/index.cpp +8 -0
  5. data/lib/faiss/version.rb +1 -1
  6. data/vendor/faiss/faiss/IVFlib.cpp +25 -49
  7. data/vendor/faiss/faiss/Index.cpp +11 -0
  8. data/vendor/faiss/faiss/Index.h +24 -1
  9. data/vendor/faiss/faiss/IndexAdditiveQuantizer.cpp +1 -0
  10. data/vendor/faiss/faiss/IndexBinaryHNSW.cpp +5 -1
  11. data/vendor/faiss/faiss/IndexFastScan.cpp +1 -1
  12. data/vendor/faiss/faiss/IndexFastScan.h +3 -8
  13. data/vendor/faiss/faiss/IndexFlat.cpp +374 -4
  14. data/vendor/faiss/faiss/IndexFlat.h +80 -0
  15. data/vendor/faiss/faiss/IndexHNSW.cpp +90 -1
  16. data/vendor/faiss/faiss/IndexHNSW.h +57 -1
  17. data/vendor/faiss/faiss/IndexIVFFlatPanorama.cpp +34 -149
  18. data/vendor/faiss/faiss/IndexIVFRaBitQ.cpp +86 -2
  19. data/vendor/faiss/faiss/IndexIVFRaBitQ.h +3 -1
  20. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.cpp +293 -115
  21. data/vendor/faiss/faiss/IndexIVFRaBitQFastScan.h +52 -16
  22. data/vendor/faiss/faiss/IndexPQ.cpp +4 -1
  23. data/vendor/faiss/faiss/IndexPreTransform.cpp +14 -0
  24. data/vendor/faiss/faiss/IndexPreTransform.h +9 -0
  25. data/vendor/faiss/faiss/IndexRaBitQ.cpp +96 -16
  26. data/vendor/faiss/faiss/IndexRaBitQ.h +5 -1
  27. data/vendor/faiss/faiss/IndexRaBitQFastScan.cpp +238 -93
  28. data/vendor/faiss/faiss/IndexRaBitQFastScan.h +35 -9
  29. data/vendor/faiss/faiss/IndexRefine.cpp +49 -0
  30. data/vendor/faiss/faiss/IndexRefine.h +17 -0
  31. data/vendor/faiss/faiss/clone_index.cpp +2 -0
  32. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +3 -1
  33. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +1 -1
  34. data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +1 -1
  35. data/vendor/faiss/faiss/impl/DistanceComputer.h +74 -3
  36. data/vendor/faiss/faiss/impl/HNSW.cpp +294 -15
  37. data/vendor/faiss/faiss/impl/HNSW.h +31 -2
  38. data/vendor/faiss/faiss/impl/IDSelector.h +3 -3
  39. data/vendor/faiss/faiss/impl/Panorama.cpp +193 -0
  40. data/vendor/faiss/faiss/impl/Panorama.h +204 -0
  41. data/vendor/faiss/faiss/impl/RaBitQStats.cpp +29 -0
  42. data/vendor/faiss/faiss/impl/RaBitQStats.h +56 -0
  43. data/vendor/faiss/faiss/impl/RaBitQUtils.cpp +54 -6
  44. data/vendor/faiss/faiss/impl/RaBitQUtils.h +183 -6
  45. data/vendor/faiss/faiss/impl/RaBitQuantizer.cpp +269 -84
  46. data/vendor/faiss/faiss/impl/RaBitQuantizer.h +71 -4
  47. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.cpp +362 -0
  48. data/vendor/faiss/faiss/impl/RaBitQuantizerMultiBit.h +112 -0
  49. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +6 -9
  50. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +1 -3
  51. data/vendor/faiss/faiss/impl/index_read.cpp +156 -12
  52. data/vendor/faiss/faiss/impl/index_write.cpp +142 -19
  53. data/vendor/faiss/faiss/impl/platform_macros.h +12 -0
  54. data/vendor/faiss/faiss/impl/svs_io.cpp +86 -0
  55. data/vendor/faiss/faiss/impl/svs_io.h +67 -0
  56. data/vendor/faiss/faiss/index_factory.cpp +182 -15
  57. data/vendor/faiss/faiss/invlists/BlockInvertedLists.h +1 -1
  58. data/vendor/faiss/faiss/invlists/DirectMap.cpp +1 -1
  59. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +18 -109
  60. data/vendor/faiss/faiss/invlists/InvertedLists.h +2 -18
  61. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +1 -1
  62. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.h +1 -1
  63. data/vendor/faiss/faiss/svs/IndexSVSFaissUtils.h +261 -0
  64. data/vendor/faiss/faiss/svs/IndexSVSFlat.cpp +117 -0
  65. data/vendor/faiss/faiss/svs/IndexSVSFlat.h +66 -0
  66. data/vendor/faiss/faiss/svs/IndexSVSVamana.cpp +245 -0
  67. data/vendor/faiss/faiss/svs/IndexSVSVamana.h +137 -0
  68. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.cpp +39 -0
  69. data/vendor/faiss/faiss/svs/IndexSVSVamanaLVQ.h +42 -0
  70. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.cpp +149 -0
  71. data/vendor/faiss/faiss/svs/IndexSVSVamanaLeanVec.h +58 -0
  72. data/vendor/faiss/faiss/utils/distances.cpp +0 -3
  73. data/vendor/faiss/faiss/utils/utils.cpp +4 -0
  74. metadata +18 -1
@@ -48,6 +48,13 @@
48
48
  #include <faiss/IndexRaBitQFastScan.h>
49
49
  #include <faiss/IndexRefine.h>
50
50
  #include <faiss/IndexRowwiseMinMax.h>
51
+ #ifdef FAISS_ENABLE_SVS
52
+ #include <faiss/impl/svs_io.h>
53
+ #include <faiss/svs/IndexSVSFlat.h>
54
+ #include <faiss/svs/IndexSVSVamana.h>
55
+ #include <faiss/svs/IndexSVSVamanaLVQ.h>
56
+ #include <faiss/svs/IndexSVSVamanaLeanVec.h>
57
+ #endif
51
58
  #include <faiss/IndexScalarQuantizer.h>
52
59
  #include <faiss/MetaIndexes.h>
53
60
  #include <faiss/VectorTransform.h>
@@ -607,11 +614,19 @@ ProductQuantizer* read_ProductQuantizer(IOReader* reader) {
607
614
  return pq;
608
615
  }
609
616
 
610
- static void read_RaBitQuantizer(RaBitQuantizer* rabitq, IOReader* f) {
611
- // don't care about rabitq->centroid
617
+ static void read_RaBitQuantizer(
618
+ RaBitQuantizer* rabitq,
619
+ IOReader* f,
620
+ bool multi_bit = true) {
612
621
  READ1(rabitq->d);
613
622
  READ1(rabitq->code_size);
614
623
  READ1(rabitq->metric_type);
624
+
625
+ if (multi_bit) {
626
+ READ1(rabitq->nb_bits);
627
+ } else {
628
+ rabitq->nb_bits = 1;
629
+ }
615
630
  }
616
631
 
617
632
  void read_direct_map(DirectMap* dm, IOReader* f) {
@@ -713,6 +728,20 @@ Index* read_index(IOReader* f, int io_flags) {
713
728
  if (h == fourcc("null")) {
714
729
  // denotes a missing index, useful for some cases
715
730
  return nullptr;
731
+ } else if (h == fourcc("IxFP")) {
732
+ int d;
733
+ size_t n_levels, batch_size;
734
+ READ1(d);
735
+ READ1(n_levels);
736
+ READ1(batch_size);
737
+ IndexFlatL2Panorama* idxp =
738
+ new IndexFlatL2Panorama(d, n_levels, batch_size);
739
+ READ1(idxp->ntotal);
740
+ READ1(idxp->is_trained);
741
+ READVECTOR(idxp->codes);
742
+ READVECTOR(idxp->cum_sums);
743
+ idxp->verbose = false;
744
+ idx = idxp;
716
745
  } else if (
717
746
  h == fourcc("IxFI") || h == fourcc("IxF2") || h == fourcc("IxFl")) {
718
747
  IndexFlat* idxf;
@@ -1097,13 +1126,19 @@ Index* read_index(IOReader* f, int io_flags) {
1097
1126
  read_index_header(imiq, f);
1098
1127
  read_ProductQuantizer(&imiq->pq, f);
1099
1128
  idx = imiq;
1100
- } else if (h == fourcc("IxRF")) {
1129
+ } else if (h == fourcc("IxRF") || h == fourcc("IxRP")) {
1101
1130
  IndexRefine* idxrf = new IndexRefine();
1102
1131
  read_index_header(idxrf, f);
1103
1132
  idxrf->base_index = read_index(f, io_flags);
1104
1133
  idxrf->refine_index = read_index(f, io_flags);
1105
1134
  READ1(idxrf->k_factor);
1106
- if (dynamic_cast<IndexFlat*>(idxrf->refine_index)) {
1135
+ if (h == fourcc("IxRP")) {
1136
+ // then make a RefineFlatPanorama with it
1137
+ IndexRefine* idxrf_old = idxrf;
1138
+ idxrf = new IndexRefinePanorama();
1139
+ *idxrf = *idxrf_old;
1140
+ delete idxrf_old;
1141
+ } else if (dynamic_cast<IndexFlat*>(idxrf->refine_index)) {
1107
1142
  // then make a RefineFlat with it
1108
1143
  IndexRefine* idxrf_old = idxrf;
1109
1144
  idxrf = new IndexRefineFlat();
@@ -1138,11 +1173,15 @@ Index* read_index(IOReader* f, int io_flags) {
1138
1173
  idx = idxp;
1139
1174
  } else if (
1140
1175
  h == fourcc("IHNf") || h == fourcc("IHNp") || h == fourcc("IHNs") ||
1141
- h == fourcc("IHN2") || h == fourcc("IHNc") || h == fourcc("IHc2")) {
1176
+ h == fourcc("IHN2") || h == fourcc("IHNc") || h == fourcc("IHc2") ||
1177
+ h == fourcc("IHfP")) {
1142
1178
  IndexHNSW* idxhnsw = nullptr;
1143
1179
  if (h == fourcc("IHNf")) {
1144
1180
  idxhnsw = new IndexHNSWFlat();
1145
1181
  }
1182
+ if (h == fourcc("IHfP")) {
1183
+ idxhnsw = new IndexHNSWFlatPanorama();
1184
+ }
1146
1185
  if (h == fourcc("IHNp")) {
1147
1186
  idxhnsw = new IndexHNSWPQ();
1148
1187
  }
@@ -1159,6 +1198,15 @@ Index* read_index(IOReader* f, int io_flags) {
1159
1198
  idxhnsw = new IndexHNSWCagra();
1160
1199
  }
1161
1200
  read_index_header(idxhnsw, f);
1201
+ if (h == fourcc("IHfP")) {
1202
+ auto idx_panorama = dynamic_cast<IndexHNSWFlatPanorama*>(idxhnsw);
1203
+ size_t nlevels;
1204
+ READ1(nlevels);
1205
+ const_cast<size_t&>(idx_panorama->num_panorama_levels) = nlevels;
1206
+ const_cast<size_t&>(idx_panorama->panorama_level_width) =
1207
+ (idx_panorama->d + nlevels - 1) / nlevels;
1208
+ READVECTOR(idx_panorama->cum_sums);
1209
+ }
1162
1210
  if (h == fourcc("IHNc") || h == fourcc("IHc2")) {
1163
1211
  READ1(idxhnsw->keep_max_size_level0);
1164
1212
  auto idx_hnsw_cagra = dynamic_cast<IndexHNSWCagra*>(idxhnsw);
@@ -1171,6 +1219,7 @@ Index* read_index(IOReader* f, int io_flags) {
1171
1219
  }
1172
1220
  }
1173
1221
  read_HNSW(&idxhnsw->hnsw, f);
1222
+ idxhnsw->hnsw.is_panorama = (h == fourcc("IHfP"));
1174
1223
  idxhnsw->storage = read_index(f, io_flags);
1175
1224
  idxhnsw->own_fields = idxhnsw->storage != nullptr;
1176
1225
  if (h == fourcc("IHNp") && !(io_flags & IO_FLAG_PQ_SKIP_SDC_TABLE)) {
@@ -1266,16 +1315,16 @@ Index* read_index(IOReader* f, int io_flags) {
1266
1315
  } else if (h == fourcc("Irfs")) {
1267
1316
  IndexRaBitQFastScan* idxqfs = new IndexRaBitQFastScan();
1268
1317
  read_index_header(idxqfs, f);
1269
- read_RaBitQuantizer(&idxqfs->rabitq, f);
1318
+ read_RaBitQuantizer(&idxqfs->rabitq, f, true);
1270
1319
  READVECTOR(idxqfs->center);
1271
1320
  READ1(idxqfs->qb);
1272
- READVECTOR(idxqfs->factors_storage);
1321
+ READVECTOR(idxqfs->flat_storage);
1322
+
1273
1323
  READ1(idxqfs->bbs);
1274
1324
  READ1(idxqfs->ntotal2);
1275
1325
  READ1(idxqfs->M2);
1276
1326
  READ1(idxqfs->code_size);
1277
1327
 
1278
- // Need to initialize the FastScan base class fields
1279
1328
  const size_t M_fastscan = (idxqfs->d + 3) / 4;
1280
1329
  constexpr size_t nbits_fastscan = 4;
1281
1330
  idxqfs->M = M_fastscan;
@@ -1287,22 +1336,115 @@ Index* read_index(IOReader* f, int io_flags) {
1287
1336
  } else if (h == fourcc("Ixrq")) {
1288
1337
  IndexRaBitQ* idxq = new IndexRaBitQ();
1289
1338
  read_index_header(idxq, f);
1290
- read_RaBitQuantizer(&idxq->rabitq, f);
1339
+ read_RaBitQuantizer(&idxq->rabitq, f, false);
1291
1340
  READVECTOR(idxq->codes);
1292
1341
  READVECTOR(idxq->center);
1293
1342
  READ1(idxq->qb);
1343
+
1344
+ // rabitq.nb_bits is already set to 1 by read_RaBitQuantizer
1345
+ idxq->code_size = idxq->rabitq.code_size;
1346
+ idx = idxq;
1347
+ } else if (h == fourcc("Ixrr")) {
1348
+ // Ixrr = multi-bit format (new)
1349
+ IndexRaBitQ* idxq = new IndexRaBitQ();
1350
+ read_index_header(idxq, f);
1351
+ read_RaBitQuantizer(&idxq->rabitq, f, true); // Reads nb_bits from file
1352
+ READVECTOR(idxq->codes);
1353
+ READVECTOR(idxq->center);
1354
+ READ1(idxq->qb);
1355
+
1294
1356
  idxq->code_size = idxq->rabitq.code_size;
1295
1357
  idx = idxq;
1296
1358
  } else if (h == fourcc("Iwrq")) {
1297
1359
  IndexIVFRaBitQ* ivrq = new IndexIVFRaBitQ();
1298
1360
  read_ivf_header(ivrq, f);
1299
- read_RaBitQuantizer(&ivrq->rabitq, f);
1361
+ read_RaBitQuantizer(&ivrq->rabitq, f, false);
1362
+ READ1(ivrq->code_size);
1363
+ READ1(ivrq->by_residual);
1364
+ READ1(ivrq->qb);
1365
+
1366
+ // rabitq.nb_bits is already set to 1 by read_RaBitQuantizer
1367
+ // Update rabitq to match nb_bits
1368
+ ivrq->rabitq.code_size =
1369
+ ivrq->rabitq.compute_code_size(ivrq->d, ivrq->rabitq.nb_bits);
1370
+ ivrq->code_size = ivrq->rabitq.code_size;
1371
+ read_InvertedLists(ivrq, f, io_flags);
1372
+ idx = ivrq;
1373
+ } else if (h == fourcc("Iwrr")) {
1374
+ // Iwrr = multi-bit format (new)
1375
+ IndexIVFRaBitQ* ivrq = new IndexIVFRaBitQ();
1376
+ read_ivf_header(ivrq, f);
1377
+ read_RaBitQuantizer(&ivrq->rabitq, f, true); // Reads nb_bits from file
1300
1378
  READ1(ivrq->code_size);
1301
1379
  READ1(ivrq->by_residual);
1302
1380
  READ1(ivrq->qb);
1381
+
1382
+ // Update rabitq to match nb_bits
1383
+ ivrq->rabitq.code_size =
1384
+ ivrq->rabitq.compute_code_size(ivrq->d, ivrq->rabitq.nb_bits);
1385
+ ivrq->code_size = ivrq->rabitq.code_size;
1303
1386
  read_InvertedLists(ivrq, f, io_flags);
1304
1387
  idx = ivrq;
1305
- } else if (h == fourcc("Iwrf")) {
1388
+ }
1389
+ #ifdef FAISS_ENABLE_SVS
1390
+ else if (
1391
+ h == fourcc("ILVQ") || h == fourcc("ISVL") || h == fourcc("ISVD")) {
1392
+ IndexSVSVamana* svs;
1393
+ if (h == fourcc("ILVQ")) {
1394
+ svs = new IndexSVSVamanaLVQ();
1395
+ } else if (h == fourcc("ISVL")) {
1396
+ svs = new IndexSVSVamanaLeanVec();
1397
+ } else if (h == fourcc("ISVD")) {
1398
+ svs = new IndexSVSVamana();
1399
+ }
1400
+
1401
+ read_index_header(svs, f);
1402
+ READ1(svs->graph_max_degree);
1403
+ READ1(svs->alpha);
1404
+ READ1(svs->search_window_size);
1405
+ READ1(svs->search_buffer_capacity);
1406
+ READ1(svs->construction_window_size);
1407
+ READ1(svs->max_candidate_pool_size);
1408
+ READ1(svs->prune_to);
1409
+ READ1(svs->use_full_search_history);
1410
+ READ1(svs->storage_kind);
1411
+ if (h == fourcc("ISVL")) {
1412
+ READ1(dynamic_cast<IndexSVSVamanaLeanVec*>(svs)->leanvec_d);
1413
+ }
1414
+
1415
+ bool initialized;
1416
+ READ1(initialized);
1417
+ if (initialized) {
1418
+ faiss::svs_io::ReaderStreambuf rbuf(f);
1419
+ std::istream is(&rbuf);
1420
+ svs->deserialize_impl(is);
1421
+ }
1422
+ if (h == fourcc("ISVL")) {
1423
+ bool trained;
1424
+ READ1(trained);
1425
+ if (trained) {
1426
+ faiss::svs_io::ReaderStreambuf rbuf(f);
1427
+ std::istream is(&rbuf);
1428
+ dynamic_cast<IndexSVSVamanaLeanVec*>(svs)
1429
+ ->deserialize_training_data(is);
1430
+ }
1431
+ }
1432
+ idx = svs;
1433
+ } else if (h == fourcc("ISVF")) {
1434
+ IndexSVSFlat* svs = new IndexSVSFlat();
1435
+ read_index_header(svs, f);
1436
+
1437
+ bool initialized;
1438
+ READ1(initialized);
1439
+ if (initialized) {
1440
+ faiss::svs_io::ReaderStreambuf rbuf(f);
1441
+ std::istream is(&rbuf);
1442
+ svs->deserialize_impl(is);
1443
+ idx = svs;
1444
+ }
1445
+ }
1446
+ #endif // FAISS_ENABLE_SVS
1447
+ else if (h == fourcc("Iwrf")) {
1306
1448
  IndexIVFRaBitQFastScan* ivrqfs = new IndexIVFRaBitQFastScan();
1307
1449
  read_ivf_header(ivrqfs, f);
1308
1450
  read_RaBitQuantizer(&ivrqfs->rabitq, f);
@@ -1314,7 +1456,7 @@ Index* read_index(IOReader* f, int io_flags) {
1314
1456
  READ1(ivrqfs->implem);
1315
1457
  READ1(ivrqfs->qb);
1316
1458
  READ1(ivrqfs->centered);
1317
- READVECTOR(ivrqfs->factors_storage);
1459
+ READVECTOR(ivrqfs->flat_storage);
1318
1460
 
1319
1461
  // Initialize FastScan base class fields
1320
1462
  const size_t M_fastscan = (ivrqfs->d + 3) / 4;
@@ -1480,6 +1622,7 @@ IndexBinary* read_index_binary(IOReader* f, int io_flags) {
1480
1622
  IndexBinaryHNSW* idxhnsw = new IndexBinaryHNSW();
1481
1623
  read_index_binary_header(idxhnsw, f);
1482
1624
  read_HNSW(&idxhnsw->hnsw, f);
1625
+ idxhnsw->hnsw.is_panorama = false;
1483
1626
  idxhnsw->storage = read_index_binary(f, io_flags);
1484
1627
  idxhnsw->own_fields = true;
1485
1628
  idx = idxhnsw;
@@ -1490,6 +1633,7 @@ IndexBinary* read_index_binary(IOReader* f, int io_flags) {
1490
1633
  READ1(idxhnsw->base_level_only);
1491
1634
  READ1(idxhnsw->num_base_level_search_entrypoints);
1492
1635
  read_HNSW(&idxhnsw->hnsw, f);
1636
+ idxhnsw->hnsw.is_panorama = false;
1493
1637
  idxhnsw->storage = read_index_binary(f, io_flags);
1494
1638
  idxhnsw->own_fields = true;
1495
1639
  idx = idxhnsw;
@@ -46,6 +46,13 @@
46
46
  #include <faiss/IndexRaBitQFastScan.h>
47
47
  #include <faiss/IndexRefine.h>
48
48
  #include <faiss/IndexRowwiseMinMax.h>
49
+ #ifdef FAISS_ENABLE_SVS
50
+ #include <faiss/impl/svs_io.h>
51
+ #include <faiss/svs/IndexSVSFlat.h>
52
+ #include <faiss/svs/IndexSVSVamana.h>
53
+ #include <faiss/svs/IndexSVSVamanaLVQ.h>
54
+ #include <faiss/svs/IndexSVSVamanaLeanVec.h>
55
+ #endif
49
56
  #include <faiss/IndexScalarQuantizer.h>
50
57
  #include <faiss/MetaIndexes.h>
51
58
  #include <faiss/VectorTransform.h>
@@ -396,11 +403,17 @@ static void write_NNDescent(const NNDescent* nnd, IOWriter* f) {
396
403
  WRITEVECTOR(nnd->final_graph);
397
404
  }
398
405
 
399
- static void write_RaBitQuantizer(const RaBitQuantizer* rabitq, IOWriter* f) {
400
- // don't care about rabitq->centroid
406
+ // Write RaBitQuantizer for 1-bit format (backward compatible)
407
+ static void write_RaBitQuantizer(
408
+ const RaBitQuantizer* rabitq,
409
+ IOWriter* f,
410
+ bool multi_bit = true) {
401
411
  WRITE1(rabitq->d);
402
412
  WRITE1(rabitq->code_size);
403
413
  WRITE1(rabitq->metric_type);
414
+ if (multi_bit) {
415
+ WRITE1(rabitq->nb_bits);
416
+ }
404
417
  }
405
418
 
406
419
  static void write_direct_map(const DirectMap* dm, IOWriter* f) {
@@ -432,6 +445,18 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
432
445
  // eg. for a storage component of HNSW that is set to nullptr
433
446
  uint32_t h = fourcc("null");
434
447
  WRITE1(h);
448
+ } else if (
449
+ const IndexFlatL2Panorama* idxpan =
450
+ dynamic_cast<const IndexFlatL2Panorama*>(idx)) {
451
+ uint32_t h = fourcc("IxFP");
452
+ WRITE1(h);
453
+ WRITE1(idxpan->d);
454
+ WRITE1(idxpan->n_levels);
455
+ WRITE1(idxpan->batch_size);
456
+ WRITE1(idxpan->ntotal);
457
+ WRITE1(idxpan->is_trained);
458
+ WRITEVECTOR(idxpan->codes);
459
+ WRITEVECTOR(idxpan->cum_sums);
435
460
  } else if (const IndexFlat* idxf = dynamic_cast<const IndexFlat*>(idx)) {
436
461
  uint32_t h =
437
462
  fourcc(idxf->metric_type == METRIC_INNER_PRODUCT ? "IxFI"
@@ -790,7 +815,9 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
790
815
  write_ProductQuantizer(&imiq->pq, f);
791
816
  } else if (
792
817
  const IndexRefine* idxrf = dynamic_cast<const IndexRefine*>(idx)) {
793
- uint32_t h = fourcc("IxRF");
818
+ bool is_pano =
819
+ dynamic_cast<const IndexRefinePanorama*>(idxrf) != nullptr;
820
+ uint32_t h = is_pano ? fourcc("IxRP") : fourcc("IxRF");
794
821
  WRITE1(h);
795
822
  write_index_header(idxrf, f);
796
823
  write_index(idxrf->base_index, f);
@@ -806,15 +833,23 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
806
833
  write_index(idxmap->index, f);
807
834
  WRITEVECTOR(idxmap->id_map);
808
835
  } else if (const IndexHNSW* idxhnsw = dynamic_cast<const IndexHNSW*>(idx)) {
809
- uint32_t h = dynamic_cast<const IndexHNSWFlat*>(idx) ? fourcc("IHNf")
810
- : dynamic_cast<const IndexHNSWPQ*>(idx) ? fourcc("IHNp")
811
- : dynamic_cast<const IndexHNSWSQ*>(idx) ? fourcc("IHNs")
812
- : dynamic_cast<const IndexHNSW2Level*>(idx) ? fourcc("IHN2")
813
- : dynamic_cast<const IndexHNSWCagra*>(idx) ? fourcc("IHc2")
814
- : 0;
836
+ uint32_t h = dynamic_cast<const IndexHNSWFlatPanorama*>(idx)
837
+ ? fourcc("IHfP")
838
+ : dynamic_cast<const IndexHNSWFlat*>(idx) ? fourcc("IHNf")
839
+ : dynamic_cast<const IndexHNSWPQ*>(idx) ? fourcc("IHNp")
840
+ : dynamic_cast<const IndexHNSWSQ*>(idx) ? fourcc("IHNs")
841
+ : dynamic_cast<const IndexHNSW2Level*>(idx) ? fourcc("IHN2")
842
+ : dynamic_cast<const IndexHNSWCagra*>(idx) ? fourcc("IHc2")
843
+ : 0;
815
844
  FAISS_THROW_IF_NOT(h != 0);
816
845
  WRITE1(h);
817
846
  write_index_header(idxhnsw, f);
847
+ if (h == fourcc("IHfP")) {
848
+ auto idx_panorama =
849
+ dynamic_cast<const IndexHNSWFlatPanorama*>(idxhnsw);
850
+ WRITE1(idx_panorama->num_panorama_levels);
851
+ WRITEVECTOR(idx_panorama->cum_sums);
852
+ }
818
853
  if (h == fourcc("IHc2")) {
819
854
  WRITE1(idxhnsw->keep_max_size_level0);
820
855
  auto idx_hnsw_cagra = dynamic_cast<const IndexHNSWCagra*>(idxhnsw);
@@ -908,7 +943,7 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
908
943
  write_RaBitQuantizer(&idxqfs->rabitq, f);
909
944
  WRITEVECTOR(idxqfs->center);
910
945
  WRITE1(idxqfs->qb);
911
- WRITEVECTOR(idxqfs->factors_storage);
946
+ WRITEVECTOR(idxqfs->flat_storage);
912
947
  WRITE1(idxqfs->bbs);
913
948
  WRITE1(idxqfs->ntotal2);
914
949
  WRITE1(idxqfs->M2);
@@ -916,25 +951,113 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
916
951
  WRITEVECTOR(idxqfs->codes);
917
952
  } else if (
918
953
  const IndexRaBitQ* idxq = dynamic_cast<const IndexRaBitQ*>(idx)) {
919
- uint32_t h = fourcc("Ixrq");
920
- WRITE1(h);
921
- write_index_header(idx, f);
922
- write_RaBitQuantizer(&idxq->rabitq, f);
954
+ // Use different fourcc codes for 1-bit vs multi-bit
955
+ if (idxq->rabitq.nb_bits == 1) {
956
+ uint32_t h = fourcc("Ixrq"); // 1-bit (backward compatible)
957
+ WRITE1(h);
958
+ write_index_header(idx, f);
959
+ write_RaBitQuantizer(&idxq->rabitq, f, false);
960
+ } else {
961
+ uint32_t h = fourcc("Ixrr"); // multi-bit (new format)
962
+ WRITE1(h);
963
+ write_index_header(idx, f);
964
+ write_RaBitQuantizer(&idxq->rabitq, f, true);
965
+ }
923
966
  WRITEVECTOR(idxq->codes);
924
967
  WRITEVECTOR(idxq->center);
925
968
  WRITE1(idxq->qb);
926
969
  } else if (
927
970
  const IndexIVFRaBitQ* ivrq =
928
971
  dynamic_cast<const IndexIVFRaBitQ*>(idx)) {
929
- uint32_t h = fourcc("Iwrq");
930
- WRITE1(h);
931
- write_ivf_header(ivrq, f);
932
- write_RaBitQuantizer(&ivrq->rabitq, f);
972
+ // Use different fourcc codes for 1-bit vs multi-bit
973
+ if (ivrq->rabitq.nb_bits == 1) {
974
+ uint32_t h = fourcc("Iwrq"); // 1-bit (backward compatible)
975
+ WRITE1(h);
976
+ write_ivf_header(ivrq, f);
977
+ write_RaBitQuantizer(&ivrq->rabitq, f, false);
978
+ } else {
979
+ uint32_t h = fourcc("Iwrr"); // multi-bit (new format)
980
+ WRITE1(h);
981
+ write_ivf_header(ivrq, f);
982
+ write_RaBitQuantizer(&ivrq->rabitq, f, true);
983
+ }
933
984
  WRITE1(ivrq->code_size);
934
985
  WRITE1(ivrq->by_residual);
935
986
  WRITE1(ivrq->qb);
936
987
  write_InvertedLists(ivrq->invlists, f);
988
+ }
989
+ #ifdef FAISS_ENABLE_SVS
990
+ else if (
991
+ const IndexSVSVamana* svs =
992
+ dynamic_cast<const IndexSVSVamana*>(idx)) {
993
+ uint32_t h;
994
+ auto* lvq = dynamic_cast<const IndexSVSVamanaLVQ*>(svs);
995
+ auto* lean = dynamic_cast<const IndexSVSVamanaLeanVec*>(svs);
996
+ if (lvq != nullptr) {
997
+ h = fourcc("ILVQ"); // LVQ
998
+ } else if (lean != nullptr) {
999
+ h = fourcc("ISVL"); // LeanVec
1000
+ } else {
1001
+ h = fourcc("ISVD"); // uncompressed
1002
+ }
1003
+
1004
+ WRITE1(h);
1005
+ write_index_header(svs, f);
1006
+ WRITE1(svs->graph_max_degree);
1007
+ WRITE1(svs->alpha);
1008
+ WRITE1(svs->search_window_size);
1009
+ WRITE1(svs->search_buffer_capacity);
1010
+ WRITE1(svs->construction_window_size);
1011
+ WRITE1(svs->max_candidate_pool_size);
1012
+ WRITE1(svs->prune_to);
1013
+ WRITE1(svs->use_full_search_history);
1014
+ WRITE1(svs->storage_kind);
1015
+
1016
+ if (lean != nullptr) {
1017
+ WRITE1(lean->leanvec_d);
1018
+ }
1019
+
1020
+ bool initialized = (svs->impl != nullptr);
1021
+ WRITE1(initialized);
1022
+ if (initialized) {
1023
+ faiss::BufferedIOWriter bwr(f);
1024
+ faiss::svs_io::WriterStreambuf wbuf(&bwr);
1025
+ std::ostream os(&wbuf);
1026
+ svs->serialize_impl(os);
1027
+ os.flush();
1028
+ }
1029
+
1030
+ if (lean != nullptr) {
1031
+ // Store training data info
1032
+ bool trained = (lean->training_data != nullptr);
1033
+ WRITE1(trained);
1034
+ if (trained) {
1035
+ faiss::BufferedIOWriter bwr(f);
1036
+ faiss::svs_io::WriterStreambuf wbuf(&bwr);
1037
+ std::ostream os(&wbuf);
1038
+ lean->serialize_training_data(os);
1039
+ os.flush();
1040
+ }
1041
+ }
937
1042
  } else if (
1043
+ const IndexSVSFlat* svs = dynamic_cast<const IndexSVSFlat*>(idx)) {
1044
+ uint32_t h = fourcc("ISVF");
1045
+ WRITE1(h);
1046
+ write_index_header(idx, f);
1047
+
1048
+ bool initialized = (svs->impl != nullptr);
1049
+ WRITE1(initialized);
1050
+ if (initialized) {
1051
+ // Wrap SVS I/O and stream to IOWriter
1052
+ faiss::BufferedIOWriter bwr(f);
1053
+ faiss::svs_io::WriterStreambuf wbuf(&bwr);
1054
+ std::ostream os(&wbuf);
1055
+ svs->serialize_impl(os);
1056
+ os.flush();
1057
+ }
1058
+ }
1059
+ #endif // FAISS_ENABLE_SVS
1060
+ else if (
938
1061
  const IndexIVFRaBitQFastScan* ivrqfs =
939
1062
  dynamic_cast<const IndexIVFRaBitQFastScan*>(idx)) {
940
1063
  uint32_t h = fourcc("Iwrf");
@@ -949,7 +1072,7 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
949
1072
  WRITE1(ivrqfs->implem);
950
1073
  WRITE1(ivrqfs->qb);
951
1074
  WRITE1(ivrqfs->centered);
952
- WRITEVECTOR(ivrqfs->factors_storage);
1075
+ WRITEVECTOR(ivrqfs->flat_storage);
953
1076
  write_InvertedLists(ivrqfs->invlists, f);
954
1077
  } else {
955
1078
  FAISS_THROW_MSG("don't know how to serialize this type of index");
@@ -107,6 +107,12 @@ inline int __builtin_clzll(uint64_t x) {
107
107
 
108
108
  #define FAISS_ALWAYS_INLINE __forceinline
109
109
 
110
+ // MSVC uses pragma pack instead of __attribute__((packed))
111
+ // Use FAISS_PACK_STRUCTS_BEGIN/END to wrap packed structure definitions
112
+ #define FAISS_PACKED
113
+ #define FAISS_PACK_STRUCTS_BEGIN __pragma(pack(push, 1))
114
+ #define FAISS_PACK_STRUCTS_END __pragma(pack(pop))
115
+
110
116
  #else
111
117
  /*******************************************************
112
118
  * Linux and OSX
@@ -119,10 +125,16 @@ inline int __builtin_clzll(uint64_t x) {
119
125
  // windows
120
126
  #ifdef SWIG
121
127
  #define ALIGNED(x)
128
+ #define FAISS_PACKED
122
129
  #else
123
130
  #define ALIGNED(x) __attribute__((aligned(x)))
131
+ #define FAISS_PACKED __attribute__((packed))
124
132
  #endif
125
133
 
134
+ // On non-Windows, FAISS_PACKED handles packing, so these are no-ops
135
+ #define FAISS_PACK_STRUCTS_BEGIN
136
+ #define FAISS_PACK_STRUCTS_END
137
+
126
138
  #define FAISS_ALWAYS_INLINE __attribute__((always_inline)) inline
127
139
 
128
140
  #endif
@@ -0,0 +1,86 @@
1
+ /*
2
+ * Portions Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ /*
9
+ * Portions Copyright 2025 Intel Corporation
10
+ *
11
+ * Licensed under the Apache License, Version 2.0 (the "License");
12
+ * you may not use this file except in compliance with the License.
13
+ * You may obtain a copy of the License at
14
+ *
15
+ * http://www.apache.org/licenses/LICENSE-2.0
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+
24
+ #include <faiss/impl/svs_io.h>
25
+
26
+ #include <algorithm>
27
+ #include <cstdlib>
28
+
29
+ #include <faiss/impl/FaissAssert.h>
30
+
31
+ namespace faiss {
32
+ namespace svs_io {
33
+
34
+ WriterStreambuf::WriterStreambuf(IOWriter* w_) : w(w_) {}
35
+
36
+ WriterStreambuf::~WriterStreambuf() = default;
37
+
38
+ std::streamsize WriterStreambuf::xsputn(const char* s, std::streamsize n) {
39
+ if (n <= 0)
40
+ return 0;
41
+ size_t wrote = (*w)(s, 1, static_cast<size_t>(n));
42
+ return static_cast<std::streamsize>(wrote);
43
+ }
44
+
45
+ int WriterStreambuf::overflow(int ch) {
46
+ if (ch == traits_type::eof())
47
+ return 0;
48
+ char c = static_cast<char>(ch);
49
+ size_t wrote = (*w)(&c, 1, 1);
50
+ return wrote == 1 ? ch : traits_type::eof();
51
+ }
52
+
53
+ ReaderStreambuf::ReaderStreambuf(IOReader* rr) : r(rr), single_char_buffer(0) {
54
+ // Initialize with empty get area
55
+ setg(nullptr, nullptr, nullptr);
56
+ }
57
+
58
+ ReaderStreambuf::~ReaderStreambuf() = default;
59
+
60
+ std::streambuf::int_type ReaderStreambuf::underflow() {
61
+ // Called by std::istream for single-character operations (get, peek, etc.)
62
+ // when the get area is exhausted. Reads one byte from IOReader.
63
+ size_t got = (*r)(&single_char_buffer, 1, 1);
64
+ if (got == 0) {
65
+ return traits_type::eof();
66
+ }
67
+
68
+ // Configure get area to expose the single buffered character
69
+ setg(&single_char_buffer, &single_char_buffer, &single_char_buffer + 1);
70
+ return traits_type::to_int_type(single_char_buffer);
71
+ }
72
+
73
+ std::streamsize ReaderStreambuf::xsgetn(char* s, std::streamsize n) {
74
+ // Called by std::istream for bulk reads (read, readsome, etc.).
75
+ // Forwards directly to IOReader without intermediate buffering to avoid
76
+ // advancing IOReader beyond what the stream consumer requested.
77
+ if (n <= 0) {
78
+ return 0;
79
+ }
80
+
81
+ size_t got = (*r)(s, 1, n);
82
+ return static_cast<std::streamsize>(got);
83
+ }
84
+
85
+ } // namespace svs_io
86
+ } // namespace faiss