faiss 0.3.1 → 0.3.2

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/faiss/version.rb +1 -1
  4. data/vendor/faiss/faiss/AutoTune.h +1 -1
  5. data/vendor/faiss/faiss/Clustering.cpp +35 -4
  6. data/vendor/faiss/faiss/Clustering.h +10 -1
  7. data/vendor/faiss/faiss/IVFlib.cpp +4 -1
  8. data/vendor/faiss/faiss/Index.h +21 -6
  9. data/vendor/faiss/faiss/IndexBinaryHNSW.h +1 -1
  10. data/vendor/faiss/faiss/IndexBinaryIVF.cpp +1 -1
  11. data/vendor/faiss/faiss/IndexFastScan.cpp +22 -4
  12. data/vendor/faiss/faiss/IndexFlat.cpp +11 -7
  13. data/vendor/faiss/faiss/IndexFlatCodes.cpp +159 -5
  14. data/vendor/faiss/faiss/IndexFlatCodes.h +20 -3
  15. data/vendor/faiss/faiss/IndexHNSW.cpp +143 -90
  16. data/vendor/faiss/faiss/IndexHNSW.h +52 -3
  17. data/vendor/faiss/faiss/IndexIVF.cpp +3 -3
  18. data/vendor/faiss/faiss/IndexIVF.h +9 -1
  19. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.cpp +15 -0
  20. data/vendor/faiss/faiss/IndexIVFAdditiveQuantizer.h +3 -0
  21. data/vendor/faiss/faiss/IndexIVFFastScan.cpp +130 -57
  22. data/vendor/faiss/faiss/IndexIVFFastScan.h +14 -7
  23. data/vendor/faiss/faiss/IndexIVFPQ.cpp +1 -3
  24. data/vendor/faiss/faiss/IndexIVFPQFastScan.cpp +21 -2
  25. data/vendor/faiss/faiss/IndexLattice.cpp +1 -19
  26. data/vendor/faiss/faiss/IndexLattice.h +3 -22
  27. data/vendor/faiss/faiss/IndexNNDescent.cpp +0 -29
  28. data/vendor/faiss/faiss/IndexNNDescent.h +1 -1
  29. data/vendor/faiss/faiss/IndexNSG.h +1 -1
  30. data/vendor/faiss/faiss/IndexNeuralNetCodec.cpp +56 -0
  31. data/vendor/faiss/faiss/IndexNeuralNetCodec.h +49 -0
  32. data/vendor/faiss/faiss/IndexPreTransform.h +1 -1
  33. data/vendor/faiss/faiss/IndexRefine.cpp +5 -5
  34. data/vendor/faiss/faiss/IndexScalarQuantizer.cpp +3 -1
  35. data/vendor/faiss/faiss/MetricType.h +7 -2
  36. data/vendor/faiss/faiss/cppcontrib/detail/UintReader.h +95 -17
  37. data/vendor/faiss/faiss/cppcontrib/factory_tools.cpp +152 -0
  38. data/vendor/faiss/faiss/cppcontrib/factory_tools.h +24 -0
  39. data/vendor/faiss/faiss/cppcontrib/sa_decode/Level2-inl.h +83 -30
  40. data/vendor/faiss/faiss/gpu/GpuCloner.cpp +36 -4
  41. data/vendor/faiss/faiss/gpu/GpuClonerOptions.h +6 -0
  42. data/vendor/faiss/faiss/gpu/GpuFaissAssert.h +1 -1
  43. data/vendor/faiss/faiss/gpu/GpuIndex.h +2 -8
  44. data/vendor/faiss/faiss/gpu/GpuIndexCagra.h +282 -0
  45. data/vendor/faiss/faiss/gpu/GpuIndexIVF.h +6 -0
  46. data/vendor/faiss/faiss/gpu/GpuIndexIVFFlat.h +2 -0
  47. data/vendor/faiss/faiss/gpu/StandardGpuResources.cpp +25 -0
  48. data/vendor/faiss/faiss/gpu/impl/InterleavedCodes.cpp +26 -21
  49. data/vendor/faiss/faiss/gpu/perf/PerfClustering.cpp +6 -0
  50. data/vendor/faiss/faiss/gpu/test/TestCodePacking.cpp +8 -5
  51. data/vendor/faiss/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +65 -0
  52. data/vendor/faiss/faiss/gpu/test/demo_ivfpq_indexing_gpu.cpp +1 -1
  53. data/vendor/faiss/faiss/gpu/utils/DeviceUtils.h +6 -0
  54. data/vendor/faiss/faiss/gpu/utils/Timer.cpp +4 -1
  55. data/vendor/faiss/faiss/gpu/utils/Timer.h +1 -1
  56. data/vendor/faiss/faiss/impl/AuxIndexStructures.cpp +25 -0
  57. data/vendor/faiss/faiss/impl/AuxIndexStructures.h +9 -1
  58. data/vendor/faiss/faiss/impl/DistanceComputer.h +46 -0
  59. data/vendor/faiss/faiss/impl/FaissAssert.h +4 -2
  60. data/vendor/faiss/faiss/impl/HNSW.cpp +358 -190
  61. data/vendor/faiss/faiss/impl/HNSW.h +43 -22
  62. data/vendor/faiss/faiss/impl/LocalSearchQuantizer.cpp +8 -8
  63. data/vendor/faiss/faiss/impl/LookupTableScaler.h +34 -0
  64. data/vendor/faiss/faiss/impl/NNDescent.cpp +13 -8
  65. data/vendor/faiss/faiss/impl/NSG.cpp +0 -29
  66. data/vendor/faiss/faiss/impl/ProductQuantizer.cpp +1 -0
  67. data/vendor/faiss/faiss/impl/ProductQuantizer.h +5 -1
  68. data/vendor/faiss/faiss/impl/ResultHandler.h +151 -32
  69. data/vendor/faiss/faiss/impl/ScalarQuantizer.cpp +719 -102
  70. data/vendor/faiss/faiss/impl/ScalarQuantizer.h +3 -0
  71. data/vendor/faiss/faiss/impl/code_distance/code_distance-avx2.h +5 -0
  72. data/vendor/faiss/faiss/impl/code_distance/code_distance-avx512.h +248 -0
  73. data/vendor/faiss/faiss/impl/index_read.cpp +29 -15
  74. data/vendor/faiss/faiss/impl/index_read_utils.h +37 -0
  75. data/vendor/faiss/faiss/impl/index_write.cpp +28 -10
  76. data/vendor/faiss/faiss/impl/io.cpp +13 -5
  77. data/vendor/faiss/faiss/impl/io.h +4 -4
  78. data/vendor/faiss/faiss/impl/io_macros.h +6 -0
  79. data/vendor/faiss/faiss/impl/platform_macros.h +22 -0
  80. data/vendor/faiss/faiss/impl/pq4_fast_scan.cpp +11 -0
  81. data/vendor/faiss/faiss/impl/pq4_fast_scan_search_1.cpp +1 -1
  82. data/vendor/faiss/faiss/impl/pq4_fast_scan_search_qbs.cpp +448 -1
  83. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.cpp +5 -5
  84. data/vendor/faiss/faiss/impl/residual_quantizer_encode_steps.h +1 -1
  85. data/vendor/faiss/faiss/impl/simd_result_handlers.h +143 -59
  86. data/vendor/faiss/faiss/index_factory.cpp +31 -13
  87. data/vendor/faiss/faiss/index_io.h +12 -5
  88. data/vendor/faiss/faiss/invlists/BlockInvertedLists.cpp +28 -8
  89. data/vendor/faiss/faiss/invlists/BlockInvertedLists.h +3 -0
  90. data/vendor/faiss/faiss/invlists/DirectMap.cpp +9 -1
  91. data/vendor/faiss/faiss/invlists/InvertedLists.cpp +55 -17
  92. data/vendor/faiss/faiss/invlists/InvertedLists.h +18 -9
  93. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.cpp +21 -6
  94. data/vendor/faiss/faiss/invlists/OnDiskInvertedLists.h +2 -1
  95. data/vendor/faiss/faiss/python/python_callbacks.cpp +3 -3
  96. data/vendor/faiss/faiss/utils/Heap.h +105 -0
  97. data/vendor/faiss/faiss/utils/NeuralNet.cpp +342 -0
  98. data/vendor/faiss/faiss/utils/NeuralNet.h +147 -0
  99. data/vendor/faiss/faiss/utils/bf16.h +36 -0
  100. data/vendor/faiss/faiss/utils/distances.cpp +58 -88
  101. data/vendor/faiss/faiss/utils/distances.h +5 -5
  102. data/vendor/faiss/faiss/utils/distances_simd.cpp +997 -9
  103. data/vendor/faiss/faiss/utils/extra_distances-inl.h +70 -0
  104. data/vendor/faiss/faiss/utils/extra_distances.cpp +85 -137
  105. data/vendor/faiss/faiss/utils/extra_distances.h +3 -2
  106. data/vendor/faiss/faiss/utils/hamming.cpp +1 -1
  107. data/vendor/faiss/faiss/utils/hamming_distance/generic-inl.h +4 -1
  108. data/vendor/faiss/faiss/utils/hamming_distance/hamdis-inl.h +2 -1
  109. data/vendor/faiss/faiss/utils/random.cpp +43 -0
  110. data/vendor/faiss/faiss/utils/random.h +25 -0
  111. data/vendor/faiss/faiss/utils/simdlib.h +10 -1
  112. data/vendor/faiss/faiss/utils/simdlib_avx512.h +296 -0
  113. data/vendor/faiss/faiss/utils/simdlib_neon.h +5 -2
  114. data/vendor/faiss/faiss/utils/simdlib_ppc64.h +1084 -0
  115. data/vendor/faiss/faiss/utils/transpose/transpose-avx512-inl.h +176 -0
  116. data/vendor/faiss/faiss/utils/utils.cpp +10 -3
  117. data/vendor/faiss/faiss/utils/utils.h +3 -0
  118. metadata +16 -4
  119. data/vendor/faiss/faiss/impl/code_distance/code_distance_avx512.h +0 -102
@@ -5,8 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- // -*- c++ -*-
9
-
10
8
  #include <faiss/invlists/InvertedLists.h>
11
9
 
12
10
  #include <cstdio>
@@ -24,18 +22,10 @@ InvertedListsIterator::~InvertedListsIterator() {}
24
22
  ******************************************/
25
23
 
26
24
  InvertedLists::InvertedLists(size_t nlist, size_t code_size)
27
- : nlist(nlist), code_size(code_size), use_iterator(false) {}
25
+ : nlist(nlist), code_size(code_size) {}
28
26
 
29
27
  InvertedLists::~InvertedLists() {}
30
28
 
31
- bool InvertedLists::is_empty(size_t list_no, void* inverted_list_context)
32
- const {
33
- return use_iterator ? !std::unique_ptr<InvertedListsIterator>(
34
- get_iterator(list_no, inverted_list_context))
35
- ->is_available()
36
- : list_size(list_no) == 0;
37
- }
38
-
39
29
  idx_t InvertedLists::get_single_id(size_t list_no, size_t offset) const {
40
30
  assert(offset < list_size(list_no));
41
31
  const idx_t* ids = get_ids(list_no);
@@ -78,12 +68,6 @@ void InvertedLists::reset() {
78
68
  }
79
69
  }
80
70
 
81
- InvertedListsIterator* InvertedLists::get_iterator(
82
- size_t /*list_no*/,
83
- void* /*inverted_list_context*/) const {
84
- FAISS_THROW_MSG("get_iterator is not supported");
85
- }
86
-
87
71
  void InvertedLists::merge_from(InvertedLists* oivf, size_t add_id) {
88
72
  #pragma omp parallel for
89
73
  for (idx_t i = 0; i < nlist; i++) {
@@ -233,6 +217,54 @@ size_t InvertedLists::compute_ntotal() const {
233
217
  return tot;
234
218
  }
235
219
 
220
+ bool InvertedLists::is_empty(size_t list_no, void* inverted_list_context)
221
+ const {
222
+ if (use_iterator) {
223
+ return !std::unique_ptr<InvertedListsIterator>(
224
+ get_iterator(list_no, inverted_list_context))
225
+ ->is_available();
226
+ } else {
227
+ FAISS_THROW_IF_NOT(inverted_list_context == nullptr);
228
+ return list_size(list_no) == 0;
229
+ }
230
+ }
231
+
232
+ // implemnent iterator on top of get_codes / get_ids
233
+ namespace {
234
+
235
+ struct CodeArrayIterator : InvertedListsIterator {
236
+ size_t list_size;
237
+ size_t code_size;
238
+ InvertedLists::ScopedCodes codes;
239
+ InvertedLists::ScopedIds ids;
240
+ size_t idx = 0;
241
+
242
+ CodeArrayIterator(const InvertedLists* il, size_t list_no)
243
+ : list_size(il->list_size(list_no)),
244
+ code_size(il->code_size),
245
+ codes(il, list_no),
246
+ ids(il, list_no) {}
247
+
248
+ bool is_available() const override {
249
+ return idx < list_size;
250
+ }
251
+ void next() override {
252
+ idx++;
253
+ }
254
+ std::pair<idx_t, const uint8_t*> get_id_and_codes() override {
255
+ return {ids[idx], codes.get() + code_size * idx};
256
+ }
257
+ };
258
+
259
+ } // namespace
260
+
261
+ InvertedListsIterator* InvertedLists::get_iterator(
262
+ size_t list_no,
263
+ void* inverted_list_context) const {
264
+ FAISS_THROW_IF_NOT(inverted_list_context == nullptr);
265
+ return new CodeArrayIterator(this, list_no);
266
+ }
267
+
236
268
  /*****************************************
237
269
  * ArrayInvertedLists implementation
238
270
  ******************************************/
@@ -264,6 +296,12 @@ size_t ArrayInvertedLists::list_size(size_t list_no) const {
264
296
  return ids[list_no].size();
265
297
  }
266
298
 
299
+ bool ArrayInvertedLists::is_empty(size_t list_no, void* inverted_list_context)
300
+ const {
301
+ FAISS_THROW_IF_NOT(inverted_list_context == nullptr);
302
+ return ids[list_no].size() == 0;
303
+ }
304
+
267
305
  const uint8_t* ArrayInvertedLists::get_codes(size_t list_no) const {
268
306
  assert(list_no < nlist);
269
307
  return codes[list_no].data();
@@ -37,7 +37,9 @@ struct InvertedListsIterator {
37
37
  struct InvertedLists {
38
38
  size_t nlist; ///< number of possible key values
39
39
  size_t code_size; ///< code size per vector in bytes
40
- bool use_iterator;
40
+
41
+ /// request to use iterator rather than get_codes / get_ids
42
+ bool use_iterator = false;
41
43
 
42
44
  InvertedLists(size_t nlist, size_t code_size);
43
45
 
@@ -50,17 +52,9 @@ struct InvertedLists {
50
52
  /*************************
51
53
  * Read only functions */
52
54
 
53
- // check if the list is empty
54
- bool is_empty(size_t list_no, void* inverted_list_context) const;
55
-
56
55
  /// get the size of a list
57
56
  virtual size_t list_size(size_t list_no) const = 0;
58
57
 
59
- /// get iterable for lists that use_iterator
60
- virtual InvertedListsIterator* get_iterator(
61
- size_t list_no,
62
- void* inverted_list_context) const;
63
-
64
58
  /** get the codes for an inverted list
65
59
  * must be released by release_codes
66
60
  *
@@ -92,6 +86,18 @@ struct InvertedLists {
92
86
  /// a list can be -1 hence the signed long
93
87
  virtual void prefetch_lists(const idx_t* list_nos, int nlist) const;
94
88
 
89
+ /*****************************************
90
+ * Iterator interface (with context) */
91
+
92
+ /// check if the list is empty
93
+ virtual bool is_empty(size_t list_no, void* inverted_list_context = nullptr)
94
+ const;
95
+
96
+ /// get iterable for lists that use_iterator
97
+ virtual InvertedListsIterator* get_iterator(
98
+ size_t list_no,
99
+ void* inverted_list_context = nullptr) const;
100
+
95
101
  /*************************
96
102
  * writing functions */
97
103
 
@@ -262,6 +268,9 @@ struct ArrayInvertedLists : InvertedLists {
262
268
  /// permute the inverted lists, map maps new_id to old_id
263
269
  void permute_invlists(const idx_t* map);
264
270
 
271
+ bool is_empty(size_t list_no, void* inverted_list_context = nullptr)
272
+ const override;
273
+
265
274
  ~ArrayInvertedLists() override;
266
275
  };
267
276
 
@@ -394,8 +394,8 @@ const idx_t* OnDiskInvertedLists::get_ids(size_t list_no) const {
394
394
  return nullptr;
395
395
  }
396
396
 
397
- return (
398
- const idx_t*)(ptr + lists[list_no].offset + code_size * lists[list_no].capacity);
397
+ return (const idx_t*)(ptr + lists[list_no].offset +
398
+ code_size * lists[list_no].capacity);
399
399
  }
400
400
 
401
401
  void OnDiskInvertedLists::update_entries(
@@ -565,15 +565,16 @@ void OnDiskInvertedLists::free_slot(size_t offset, size_t capacity) {
565
565
  /*****************************************
566
566
  * Compact form
567
567
  *****************************************/
568
-
569
- size_t OnDiskInvertedLists::merge_from(
568
+ size_t OnDiskInvertedLists::merge_from_multiple(
570
569
  const InvertedLists** ils,
571
570
  int n_il,
571
+ bool shift_ids,
572
572
  bool verbose) {
573
573
  FAISS_THROW_IF_NOT_MSG(
574
574
  totsize == 0, "works only on an empty InvertedLists");
575
575
 
576
576
  std::vector<size_t> sizes(nlist);
577
+ std::vector<size_t> shift_id_offsets(n_il);
577
578
  for (int i = 0; i < n_il; i++) {
578
579
  const InvertedLists* il = ils[i];
579
580
  FAISS_THROW_IF_NOT(il->nlist == nlist && il->code_size == code_size);
@@ -581,6 +582,10 @@ size_t OnDiskInvertedLists::merge_from(
581
582
  for (size_t j = 0; j < nlist; j++) {
582
583
  sizes[j] += il->list_size(j);
583
584
  }
585
+
586
+ size_t il_totsize = il->compute_ntotal();
587
+ shift_id_offsets[i] =
588
+ (shift_ids && i > 0) ? shift_id_offsets[i - 1] + il_totsize : 0;
584
589
  }
585
590
 
586
591
  size_t cums = 0;
@@ -605,11 +610,21 @@ size_t OnDiskInvertedLists::merge_from(
605
610
  const InvertedLists* il = ils[i];
606
611
  size_t n_entry = il->list_size(j);
607
612
  l.size += n_entry;
613
+ ScopedIds scope_ids(il, j);
614
+ const idx_t* scope_ids_data = scope_ids.get();
615
+ std::vector<idx_t> new_ids;
616
+ if (shift_ids) {
617
+ new_ids.resize(n_entry);
618
+ for (size_t k = 0; k < n_entry; k++) {
619
+ new_ids[k] = scope_ids[k] + shift_id_offsets[i];
620
+ }
621
+ scope_ids_data = new_ids.data();
622
+ }
608
623
  update_entries(
609
624
  j,
610
625
  l.size - n_entry,
611
626
  n_entry,
612
- ScopedIds(il, j).get(),
627
+ scope_ids_data,
613
628
  ScopedCodes(il, j).get());
614
629
  }
615
630
  assert(l.size == l.capacity);
@@ -638,7 +653,7 @@ size_t OnDiskInvertedLists::merge_from(
638
653
  size_t OnDiskInvertedLists::merge_from_1(
639
654
  const InvertedLists* ils,
640
655
  bool verbose) {
641
- return merge_from(&ils, 1, verbose);
656
+ return merge_from_multiple(&ils, 1, verbose);
642
657
  }
643
658
 
644
659
  void OnDiskInvertedLists::crop_invlists(size_t l0, size_t l1) {
@@ -101,9 +101,10 @@ struct OnDiskInvertedLists : InvertedLists {
101
101
 
102
102
  // copy all inverted lists into *this, in compact form (without
103
103
  // allocating slots)
104
- size_t merge_from(
104
+ size_t merge_from_multiple(
105
105
  const InvertedLists** ils,
106
106
  int n_il,
107
+ bool shift_ids = false,
107
108
  bool verbose = false);
108
109
 
109
110
  /// same as merge_from for a single invlist
@@ -46,7 +46,7 @@ size_t PyCallbackIOWriter::operator()(
46
46
  size_t wi = ws > bs ? bs : ws;
47
47
  PyObject* result = PyObject_CallFunction(
48
48
  callback, "(N)", PyBytes_FromStringAndSize(ptr, wi));
49
- if (result == NULL) {
49
+ if (result == nullptr) {
50
50
  FAISS_THROW_MSG("py err");
51
51
  }
52
52
  // TODO check nb of bytes written
@@ -77,7 +77,7 @@ size_t PyCallbackIOReader::operator()(void* ptrv, size_t size, size_t nitems) {
77
77
  while (rs > 0) {
78
78
  size_t ri = rs > bs ? bs : rs;
79
79
  PyObject* result = PyObject_CallFunction(callback, "(n)", ri);
80
- if (result == NULL) {
80
+ if (result == nullptr) {
81
81
  FAISS_THROW_MSG("propagate py error");
82
82
  }
83
83
  if (!PyBytes_Check(result)) {
@@ -122,7 +122,7 @@ bool PyCallbackIDSelector::is_member(faiss::idx_t id) const {
122
122
  FAISS_THROW_IF_NOT((id >> 32) == 0);
123
123
  PyThreadLock gil;
124
124
  PyObject* result = PyObject_CallFunction(callback, "(n)", int(id));
125
- if (result == NULL) {
125
+ if (result == nullptr) {
126
126
  FAISS_THROW_MSG("propagate py error");
127
127
  }
128
128
  bool b = PyObject_IsTrue(result);
@@ -30,6 +30,7 @@
30
30
  #include <cstdio>
31
31
 
32
32
  #include <limits>
33
+ #include <utility>
33
34
 
34
35
  #include <faiss/utils/ordered_key_value.h>
35
36
 
@@ -200,6 +201,110 @@ inline void maxheap_replace_top(
200
201
  heap_replace_top<CMax<T, int64_t>>(k, bh_val, bh_ids, val, ids);
201
202
  }
202
203
 
204
+ /*******************************************************************
205
+ * Basic heap<std:pair<>> ops: push and pop
206
+ *******************************************************************/
207
+
208
+ // This section contains a heap implementation that works with
209
+ // std::pair<Priority, Value> elements.
210
+
211
+ /** Pops the top element from the heap defined by bh_val[0..k-1] and
212
+ * bh_ids[0..k-1]. on output the element at k-1 is undefined.
213
+ */
214
+ template <class C>
215
+ inline void heap_pop(size_t k, std::pair<typename C::T, typename C::TI>* bh) {
216
+ bh--; /* Use 1-based indexing for easier node->child translation */
217
+ typename C::T val = bh[k].first;
218
+ typename C::TI id = bh[k].second;
219
+ size_t i = 1, i1, i2;
220
+ while (1) {
221
+ i1 = i << 1;
222
+ i2 = i1 + 1;
223
+ if (i1 > k)
224
+ break;
225
+ if ((i2 == k + 1) ||
226
+ C::cmp2(bh[i1].first, bh[i2].first, bh[i1].second, bh[i2].second)) {
227
+ if (C::cmp2(val, bh[i1].first, id, bh[i1].second)) {
228
+ break;
229
+ }
230
+ bh[i] = bh[i1];
231
+ i = i1;
232
+ } else {
233
+ if (C::cmp2(val, bh[i2].first, id, bh[i2].second)) {
234
+ break;
235
+ }
236
+ bh[i] = bh[i2];
237
+ i = i2;
238
+ }
239
+ }
240
+ bh[i] = bh[k];
241
+ }
242
+
243
+ /** Pushes the element (val, ids) into the heap bh_val[0..k-2] and
244
+ * bh_ids[0..k-2]. on output the element at k-1 is defined.
245
+ */
246
+ template <class C>
247
+ inline void heap_push(
248
+ size_t k,
249
+ std::pair<typename C::T, typename C::TI>* bh,
250
+ typename C::T val,
251
+ typename C::TI id) {
252
+ bh--; /* Use 1-based indexing for easier node->child translation */
253
+ size_t i = k, i_father;
254
+ while (i > 1) {
255
+ i_father = i >> 1;
256
+ auto bh_v = bh[i_father];
257
+ if (!C::cmp2(val, bh_v.first, id, bh_v.second)) {
258
+ /* the heap structure is ok */
259
+ break;
260
+ }
261
+ bh[i] = bh_v;
262
+ i = i_father;
263
+ }
264
+ bh[i] = std::make_pair(val, id);
265
+ }
266
+
267
+ /**
268
+ * Replaces the top element from the heap defined by bh_val[0..k-1] and
269
+ * bh_ids[0..k-1], and for identical bh_val[] values also sorts by bh_ids[]
270
+ * values.
271
+ */
272
+ template <class C>
273
+ inline void heap_replace_top(
274
+ size_t k,
275
+ std::pair<typename C::T, typename C::TI>* bh,
276
+ typename C::T val,
277
+ typename C::TI id) {
278
+ bh--; /* Use 1-based indexing for easier node->child translation */
279
+ size_t i = 1, i1, i2;
280
+ while (1) {
281
+ i1 = i << 1;
282
+ i2 = i1 + 1;
283
+ if (i1 > k) {
284
+ break;
285
+ }
286
+
287
+ // Note that C::cmp2() is a bool function answering
288
+ // `(a1 > b1) || ((a1 == b1) && (a2 > b2))` for max
289
+ // heap and same with the `<` sign for min heap.
290
+ if ((i2 == k + 1) ||
291
+ C::cmp2(bh[i1].first, bh[i2].first, bh[i1].second, bh[i2].second)) {
292
+ if (C::cmp2(val, bh[i1].first, id, bh[i1].second)) {
293
+ break;
294
+ }
295
+ bh[i] = bh[i1];
296
+ i = i1;
297
+ } else {
298
+ if (C::cmp2(val, bh[i2].first, id, bh[i2].second)) {
299
+ break;
300
+ }
301
+ bh[i] = bh[i2];
302
+ i = i2;
303
+ }
304
+ }
305
+ bh[i] = std::make_pair(val, id);
306
+ }
307
+
203
308
  /*******************************************************************
204
309
  * Heap initialization
205
310
  *******************************************************************/