Skip to content

Commit ab4b72d

Browse files
authored
Merge pull request #2374 from NVIDIA/release/26.08
Forward-merge release/26.08 into main
2 parents 6858b29 + e77eea2 commit ab4b72d

18 files changed

Lines changed: 777 additions & 13 deletions

‎cpp/CMakeLists.txt‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,9 @@ if(NOT BUILD_CPU_ONLY)
365365
"$<$<COMPILE_LANGUAGE:CUDA>:${CUVS_CUDA_FLAGS}>"
366366
)
367367
target_compile_features(jit_lto_kernel_usage_requirements INTERFACE cuda_std_20)
368-
target_link_libraries(jit_lto_kernel_usage_requirements INTERFACE rmm::rmm raft::raft CCCL::CCCL)
368+
target_link_libraries(
369+
jit_lto_kernel_usage_requirements INTERFACE rmm::rmm raft::raft CCCL::CCCL cuco::cuco
370+
)
369371

370372
block(PROPAGATE jit_lto_files)
371373
set(jit_lto_files)
@@ -1326,6 +1328,7 @@ if(NOT BUILD_CPU_ONLY)
13261328
src/cluster/single_linkage_float.cu
13271329
src/cluster/spectral.cu
13281330
src/core/bitset.cu
1331+
src/core/bloom_filter.cu
13291332
src/core/omp_wrapper.cpp
13301333
src/util/file_io.cpp
13311334
src/util/host_memory.cpp
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#pragma once
7+
8+
#include <cuvs/core/export.hpp>
9+
#include <raft/core/device_mdarray.hpp>
10+
#include <raft/core/resources.hpp>
11+
12+
#include <cstddef>
13+
#include <cstdint>
14+
#include <memory>
15+
16+
namespace CUVS_EXPORT cuvs {
17+
namespace core {
18+
19+
/**
20+
* @brief cuVS-owned Bloom filter wrapper with opaque implementation.
21+
*
22+
* This class intentionally hides cuCollections types from the cuVS public API.
23+
* The wrapper supports the expected bulk host APIs used by ANN workflows.
24+
*/
25+
class CUVS_EXPORT bloom_filter {
26+
private:
27+
struct impl;
28+
29+
public:
30+
using key_type = std::uint32_t;
31+
32+
/**
33+
* @brief Construct a Bloom filter with user-facing quality knobs.
34+
*
35+
* @p dataset_rows is the number of rows in the indexed dataset. The filter uses it with
36+
* @p filtering_rate to estimate the number of inserted valid ids and compute a target filter
37+
* size that satisfies the requested false-positive rate.
38+
*
39+
* The primary tuning knobs are:
40+
* - @p filtering_rate: expected fraction of dataset rows that will be inserted as valid ids.
41+
* - @p target_false_positive_rate: desired Bloom filter false-positive probability.
42+
*
43+
* Sizing math used internally:
44+
* - `expected_insertions = ceil(dataset_rows * filtering_rate)`
45+
* - The default policy uses 256-bit blocks split into eight 32-bit words and sets one bit in each
46+
* word per inserted key.
47+
* - For each candidate block count, the expected false-positive rate accounts for the binomial
48+
* distribution of inserted keys across blocks and the fixed eight-bit fingerprint.
49+
* - The smallest block count whose expected false-positive rate meets
50+
* @p target_false_positive_rate is selected.
51+
*
52+
* Practical knob behavior:
53+
* - Lower @p target_false_positive_rate -> larger filter, fewer false positives, typically higher
54+
* filtered-search recall.
55+
* - Higher @p filtering_rate -> larger filter for the same target false-positive rate.
56+
*/
57+
bloom_filter(raft::resources const& res,
58+
std::size_t dataset_rows,
59+
float filtering_rate = 1.0f,
60+
float target_false_positive_rate = 0.01f);
61+
~bloom_filter();
62+
63+
bloom_filter(bloom_filter const&) = delete;
64+
bloom_filter& operator=(bloom_filter const&) = delete;
65+
bloom_filter(bloom_filter&&) noexcept;
66+
bloom_filter& operator=(bloom_filter&&) noexcept;
67+
68+
void clear(raft::resources const& res);
69+
void clear_async(raft::resources const& res);
70+
71+
void add(raft::resources const& res, raft::device_vector_view<const key_type, int64_t> keys);
72+
void add_async(raft::resources const& res,
73+
raft::device_vector_view<const key_type, int64_t> keys);
74+
75+
void contains(raft::resources const& res,
76+
raft::device_vector_view<const key_type, int64_t> keys,
77+
raft::device_vector_view<std::uint8_t, int64_t> output) const;
78+
void contains_async(raft::resources const& res,
79+
raft::device_vector_view<const key_type, int64_t> keys,
80+
raft::device_vector_view<std::uint8_t, int64_t> output) const;
81+
82+
[[nodiscard]] std::size_t num_blocks() const noexcept;
83+
84+
/**
85+
* @brief Return the estimated fraction of dataset rows rejected by this filter.
86+
*
87+
* The estimate is derived at construction from the configured valid-row fraction and the
88+
* expected false-positive rate of the selected filter geometry. It performs no device work.
89+
*/
90+
[[nodiscard]] float estimate_filtering_rate() const noexcept;
91+
92+
private:
93+
friend impl const& get_bloom_filter_impl(bloom_filter const& filter) noexcept;
94+
95+
std::unique_ptr<impl> impl_;
96+
};
97+
98+
} // namespace core
99+
} // namespace CUVS_EXPORT cuvs

‎cpp/include/cuvs/detail/jit_lto/common_fragments.hpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION.
2+
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

@@ -14,6 +14,7 @@ struct tag_i8 {};
1414
struct tag_u8 {};
1515
struct tag_filter_none {};
1616
struct tag_filter_bitset {};
17+
struct tag_filter_bloom_filter {};
1718
struct tag_filter_udf {};
1819

1920
struct tag_bitset_u32 {};

‎cpp/include/cuvs/neighbors/common.hpp‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION.
2+
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

@@ -33,6 +33,9 @@
3333
#endif
3434

3535
namespace CUVS_EXPORT cuvs {
36+
namespace core {
37+
class bloom_filter;
38+
}
3639
namespace neighbors {
3740
/**
3841
* @addtogroup cagra_cpp_index_params
@@ -497,7 +500,7 @@ namespace filtering {
497500
* @{
498501
*/
499502

500-
enum class FilterType { None, Bitmap, Bitset, UDF };
503+
enum class FilterType : int { None = 0, Bitmap = 1, Bitset = 2, Bloom = 3, UDF = 100 };
501504

502505
struct base_filter {
503506
~base_filter() = default;
@@ -617,6 +620,34 @@ struct bitset_filter : public base_filter {
617620
void to_csr(raft::resources const& handle, csr_matrix_t& csr);
618621
};
619622

623+
/**
624+
* @brief Filter CAGRA candidates with a global @c cuvs::core::bloom_filter over the index.
625+
*
626+
* Build the filter once on the host with bulk @c add() over the allowed dataset row ids and pass
627+
* the owning @c cuvs::core::bloom_filter to this wrapper. CAGRA internals build/cache the device
628+
* payload, similar to @ref bitset_filter, and the linked JIT-LTO fragment probes the same filter
629+
* for every query and candidate with probabilistic membership tests.
630+
*
631+
* Bloom filters have no false negatives: if a row was inserted, @c contains returns @c true. False
632+
* positives are possible, so highly selective predicates may still need a bitset or UDF for exact
633+
* filtering.
634+
*
635+
* This adapter is non-owning. The referenced @c cuvs::core::bloom_filter must outlive the adapter
636+
* and any searches that use it, and must not be moved or mutated concurrently with a search.
637+
*/
638+
struct bloom_filter : public base_filter {
639+
void* filter_data{nullptr};
640+
641+
bloom_filter() = default;
642+
643+
explicit bloom_filter(const cuvs::core::bloom_filter& bloom_filter)
644+
: filter_data(const_cast<cuvs::core::bloom_filter*>(&bloom_filter))
645+
{
646+
}
647+
648+
FilterType get_filter_type() const override { return FilterType::Bloom; }
649+
};
650+
620651
/**
621652
* @brief JIT-LTO user-defined filter predicate.
622653
*

0 commit comments

Comments
 (0)