From 57c858f7b278fcf6bdc1d83485979c45deb1a8c8 Mon Sep 17 00:00:00 2001 From: Yoonseok Kim Date: Sat, 14 Mar 2026 02:35:13 +0900 Subject: [PATCH 1/2] fix: use fresh vector for updated slot in refine_ --- include/usearch/index.hpp | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/include/usearch/index.hpp b/include/usearch/index.hpp index a1fbbe6f..eb147011 100644 --- a/include/usearch/index.hpp +++ b/include/usearch/index.hpp @@ -3901,8 +3901,9 @@ class index_gt { // If `new_slot` is already present in the neighboring connections of `close_slot` // then no need to modify any connections or run the heuristics. if (close_header.size() < connectivity_max) { - if (std::find_if(close_header.begin(), close_header.end(), - [new_slot](compressed_slot_t slot) { return slot == new_slot; }) == close_header.end()) { + if (std::find_if(close_header.begin(), close_header.end(), [new_slot](compressed_slot_t slot) { + return slot == new_slot; + }) == close_header.end()) { close_header.push_back(new_slot); } continue; @@ -3917,7 +3918,7 @@ class index_gt { // Export the results: close_header.clear(); candidates_view_t top_view = refine_(metric, connectivity_max, top_for_refine, context, - context.computed_distances_in_reverse_refines); + context.computed_distances_in_reverse_refines, new_slot, value); usearch_assert_m(top_view.size(), "This would lead to isolated nodes"); for (std::size_t idx = 0; idx != top_view.size(); idx++) close_header.push_back(top_view[idx].slot); @@ -4307,12 +4308,21 @@ class index_gt { * @brief This algorithm from the original paper implements a heuristic, * that massively reduces the number of connections a point has, * to keep only the neighbors, that are from each other. + * + * @param[in] override_slot Optional slot whose stored vector is stale (e.g. during update, + * where the callback has not yet committed the new vector). + * When set, inter-result distances involving this slot will use + * @p override_value instead of reading from `citerator_at()`. + * @param[in] override_value The up-to-date vector for @p override_slot. Only used when + * @p override_value_at is not `std::nullptr_t`. */ - template + template candidates_view_t refine_( // metric_at&& metric, // std::size_t needed, top_candidates_t& top, context_t& context, // - std::size_t& refines_counter) const noexcept { + std::size_t& refines_counter, // + compressed_slot_t override_slot = (std::numeric_limits::max)(), + override_value_at override_value = {}) const noexcept { // Avoid expensive computation, if the set is already small candidate_t* top_data = top.data(); @@ -4331,10 +4341,19 @@ class index_gt { std::size_t idx = 0; for (; idx < submitted_count; idx++) { candidate_t submitted = top_data[idx]; - distance_t inter_result_dist = context.measure( // - citerator_at(candidate.slot), // - citerator_at(submitted.slot), // - metric); + distance_t inter_result_dist; + if constexpr (!std::is_null_pointer_v>) { + if (candidate.slot == override_slot) + inter_result_dist = context.measure(override_value, citerator_at(submitted.slot), metric); + else if (submitted.slot == override_slot) + inter_result_dist = context.measure(override_value, citerator_at(candidate.slot), metric); + else + inter_result_dist = context.measure( // + citerator_at(candidate.slot), citerator_at(submitted.slot), metric); + } else { + inter_result_dist = context.measure( // + citerator_at(candidate.slot), citerator_at(submitted.slot), metric); + } if (inter_result_dist < candidate.distance) { good = false; break; From 5a126a5c4b6c494526e9d7cc66d3d992589d59eb Mon Sep 17 00:00:00 2001 From: Yoonseok Kim Date: Mon, 16 Mar 2026 13:48:33 +0900 Subject: [PATCH 2/2] fix: replace `if constexpr` with overload dispatch in `refine_` for C++11/14 compatibility --- include/usearch/index.hpp | 40 ++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/include/usearch/index.hpp b/include/usearch/index.hpp index eb147011..b5699836 100644 --- a/include/usearch/index.hpp +++ b/include/usearch/index.hpp @@ -4304,6 +4304,31 @@ class index_gt { } } + /// @brief Helper for `refine_()`: computes inter-neighbor distance, substituting + /// @p override_value when either slot matches @p override_slot. + /// The `std::nullptr_t` overload below avoids instantiating the override + /// branch when no override is provided, keeping the code C++11 compatible. + template + distance_t inter_neighbor_distance_( // + candidate_t const& candidate, candidate_t const& submitted, // + compressed_slot_t override_slot, override_value_at override_value, // + metric_at&& metric, context_t& context) const noexcept { + if (candidate.slot == override_slot) + return context.measure(override_value, citerator_at(submitted.slot), metric); + else if (submitted.slot == override_slot) + return context.measure(override_value, citerator_at(candidate.slot), metric); + else + return context.measure(citerator_at(candidate.slot), citerator_at(submitted.slot), metric); + } + + template + distance_t inter_neighbor_distance_( // + candidate_t const& candidate, candidate_t const& submitted, // + compressed_slot_t, std::nullptr_t, // + metric_at&& metric, context_t& context) const noexcept { + return context.measure(citerator_at(candidate.slot), citerator_at(submitted.slot), metric); + } + /** * @brief This algorithm from the original paper implements a heuristic, * that massively reduces the number of connections a point has, @@ -4341,19 +4366,8 @@ class index_gt { std::size_t idx = 0; for (; idx < submitted_count; idx++) { candidate_t submitted = top_data[idx]; - distance_t inter_result_dist; - if constexpr (!std::is_null_pointer_v>) { - if (candidate.slot == override_slot) - inter_result_dist = context.measure(override_value, citerator_at(submitted.slot), metric); - else if (submitted.slot == override_slot) - inter_result_dist = context.measure(override_value, citerator_at(candidate.slot), metric); - else - inter_result_dist = context.measure( // - citerator_at(candidate.slot), citerator_at(submitted.slot), metric); - } else { - inter_result_dist = context.measure( // - citerator_at(candidate.slot), citerator_at(submitted.slot), metric); - } + distance_t inter_result_dist = inter_neighbor_distance_( // + candidate, submitted, override_slot, override_value, metric, context); if (inter_result_dist < candidate.distance) { good = false; break;