Skip to content

Commit 52dd92c

Browse files
authored
[Feat] Add Support for Index merge in CAGRA (#618)
Authors: - rhdong (https://github.com/rhdong) Approvers: - James Lamb (https://github.com/jameslamb) - Corey J. Nolet (https://github.com/cjnolet) - Ishan Chattopadhyaya (https://github.com/chatman) URL: #618
1 parent 961e60c commit 52dd92c

14 files changed

Lines changed: 703 additions & 1 deletion

File tree

cpp/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,10 @@ if(BUILD_SHARED_LIBS)
396396
src/neighbors/cagra_serialize_half.cu
397397
src/neighbors/cagra_serialize_int8.cu
398398
src/neighbors/cagra_serialize_uint8.cu
399+
src/neighbors/cagra_merge_float.cu
400+
src/neighbors/cagra_merge_half.cu
401+
src/neighbors/cagra_merge_int8.cu
402+
src/neighbors/cagra_merge_uint8.cu
399403
src/neighbors/iface/iface_cagra_float_uint32_t.cu
400404
src/neighbors/iface/iface_cagra_half_uint32_t.cu
401405
src/neighbors/iface/iface_cagra_int8_t_uint32_t.cu

cpp/include/cuvs/neighbors/cagra.hpp

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,51 @@ struct extend_params {
264264
* 0. */
265265
uint32_t max_chunk_size = 0;
266266
};
267+
/**
268+
* @}
269+
*/
270+
271+
/**
272+
* @defgroup cagra_cpp_merge_params CAGRA index merge parameters
273+
* @{
274+
*/
275+
276+
/**
277+
* @brief Determines the strategy for merging CAGRA graphs.
278+
*
279+
* @note Currently, only the PHYSICAL strategy is supported.
280+
*/
281+
enum MergeStrategy {
282+
/**
283+
* @brief Physical merge: Builds a new CAGRA graph from the union of dataset points
284+
* in existing CAGRA graphs.
285+
*
286+
* This is expensive to build but does not impact search latency or quality.
287+
* Preferred for many smaller CAGRA graphs.
288+
*
289+
* @note Currently, this is the only supported strategy.
290+
*/
291+
PHYSICAL
292+
};
293+
294+
/**
295+
* @brief Parameters for merging CAGRA indexes.
296+
*/
297+
struct merge_params {
298+
merge_params() = default;
299+
300+
/**
301+
* @brief Constructs merge parameters with given index parameters.
302+
* @param params Parameters for creating the output index.
303+
*/
304+
explicit merge_params(const cagra::index_params& params) : output_index_params(params) {}
305+
306+
/// Parameters for creating the output index.
307+
cagra::index_params output_index_params;
308+
309+
/// Strategy for merging. Defaults to `MergeStrategy::PHYSICAL`.
310+
MergeStrategy strategy = MergeStrategy::PHYSICAL;
311+
};
267312

268313
/**
269314
* @}
@@ -1794,6 +1839,150 @@ void serialize_to_hnswlib(
17941839
std::optional<raft::host_matrix_view<const uint8_t, int64_t, raft::row_major>> dataset =
17951840
std::nullopt);
17961841

1842+
/**
1843+
* @defgroup cagra_cpp_index_merge CAGRA index build functions
1844+
* @{
1845+
*/
1846+
1847+
/** @brief Merge multiple CAGRA indices into a single index.
1848+
*
1849+
* This function merges multiple CAGRA indices into one, combining both the datasets and graph
1850+
* structures.
1851+
*
1852+
* @note: When device memory is sufficient, the dataset attached to the returned index is allocated
1853+
* in device memory by default; otherwise, host memory is used automatically.
1854+
*
1855+
* Usage example:
1856+
* @code{.cpp}
1857+
* using namespace raft::neighbors;
1858+
* auto dataset0 = raft::make_host_matrix<float, int64_t>(handle, size0, dim);
1859+
* auto dataset1 = raft::make_host_matrix<float, int64_t>(handle, size1, dim);
1860+
*
1861+
* auto index0 = cagra::build(res, index_params, dataset0);
1862+
* auto index1 = cagra::build(res, index_params, dataset1);
1863+
*
1864+
* std::vector<cagra::index<float, uint32_t>*> indices{&index0, &index1};
1865+
* cagra::merge_params params{index_params};
1866+
*
1867+
* auto merged_index = cagra::merge(res, params, indices);
1868+
* @endcode
1869+
*
1870+
* @param[in] res RAFT resources used for the merge operation.
1871+
* @param[in] params Parameters that control the merging process.
1872+
* @param[in] indices A vector of pointers to the CAGRA indices to merge. All indices must:
1873+
* - Have attached datasets with the same dimension.
1874+
*
1875+
* @return A new CAGRA index containing the merged indices, graph, and dataset.
1876+
*/
1877+
auto merge(raft::resources const& res,
1878+
const cuvs::neighbors::cagra::merge_params& params,
1879+
std::vector<cuvs::neighbors::cagra::index<float, uint32_t>*>& indices)
1880+
-> cuvs::neighbors::cagra::index<float, uint32_t>;
1881+
1882+
/** @brief Merge multiple CAGRA indices into a single index.
1883+
*
1884+
* This function merges multiple CAGRA indices into one, combining both the datasets and graph
1885+
* structures.
1886+
*
1887+
* @note: When device memory is sufficient, the dataset attached to the returned index is allocated
1888+
* in device memory by default; otherwise, host memory is used automatically.
1889+
*
1890+
* Usage example:
1891+
* @code{.cpp}
1892+
* using namespace raft::neighbors;
1893+
* auto dataset0 = raft::make_host_matrix<half, int64_t>(handle, size0, dim);
1894+
* auto dataset1 = raft::make_host_matrix<half, int64_t>(handle, size1, dim);
1895+
*
1896+
* auto index0 = cagra::build(res, index_params, dataset0);
1897+
* auto index1 = cagra::build(res, index_params, dataset1);
1898+
*
1899+
* std::vector<cagra::index<half, uint32_t>*> indices{&index0, &index1};
1900+
* cagra::merge_params params{index_params};
1901+
*
1902+
* auto merged_index = cagra::merge(res, params, indices);
1903+
* @endcode
1904+
*
1905+
* @param[in] res RAFT resources used for the merge operation.
1906+
* @param[in] params Parameters that control the merging process.
1907+
* @param[in] indices A vector of pointers to the CAGRA indices to merge. All indices must:
1908+
* - Have attached datasets with the same dimension.
1909+
*
1910+
* @return A new CAGRA index containing the merged indices, graph, and dataset.
1911+
*/
1912+
auto merge(raft::resources const& res,
1913+
const cuvs::neighbors::cagra::merge_params& params,
1914+
std::vector<cuvs::neighbors::cagra::index<half, uint32_t>*>& indices)
1915+
-> cuvs::neighbors::cagra::index<half, uint32_t>;
1916+
1917+
/** @brief Merge multiple CAGRA indices into a single index.
1918+
*
1919+
* This function merges multiple CAGRA indices into one, combining both the datasets and graph
1920+
* structures.
1921+
*
1922+
* @note: When device memory is sufficient, the dataset attached to the returned index is allocated
1923+
* in device memory by default; otherwise, host memory is used automatically.
1924+
*
1925+
* Usage example:
1926+
* @code{.cpp}
1927+
* using namespace raft::neighbors;
1928+
* auto dataset0 = raft::make_host_matrix<int8_t, int64_t>(handle, size0, dim);
1929+
* auto dataset1 = raft::make_host_matrix<int8_t, int64_t>(handle, size1, dim);
1930+
*
1931+
* auto index0 = cagra::build(res, index_params, dataset0);
1932+
* auto index1 = cagra::build(res, index_params, dataset1);
1933+
*
1934+
* std::vector<cagra::index<int8_t, uint32_t>*> indices{&index0, &index1};
1935+
* cagra::merge_params params{index_params};
1936+
*
1937+
* auto merged_index = cagra::merge(res, params, indices);
1938+
* @endcode
1939+
*
1940+
* @param[in] res RAFT resources used for the merge operation.
1941+
* @param[in] params Parameters that control the merging process.
1942+
* @param[in] indices A vector of pointers to the CAGRA indices to merge. All indices must:
1943+
* - Have attached datasets with the same dimension.
1944+
*
1945+
* @return A new CAGRA index containing the merged indices, graph, and dataset.
1946+
*/
1947+
auto merge(raft::resources const& res,
1948+
const cuvs::neighbors::cagra::merge_params& params,
1949+
std::vector<cuvs::neighbors::cagra::index<int8_t, uint32_t>*>& indices)
1950+
-> cuvs::neighbors::cagra::index<int8_t, uint32_t>;
1951+
1952+
/** @brief Merge multiple CAGRA indices into a single index.
1953+
*
1954+
* This function merges multiple CAGRA indices into one, combining both the datasets and graph
1955+
* structures.
1956+
*
1957+
* @note: When device memory is sufficient, the dataset attached to the returned index is allocated
1958+
* in device memory by default; otherwise, host memory is used automatically.
1959+
*
1960+
* Usage example:
1961+
* @code{.cpp}
1962+
* using namespace raft::neighbors;
1963+
* auto dataset0 = raft::make_host_matrix<uint8_t, int64_t>(handle, size0, dim);
1964+
* auto dataset1 = raft::make_host_matrix<uint8_t, int64_t>(handle, size1, dim);
1965+
*
1966+
* auto index0 = cagra::build(res, index_params, dataset0);
1967+
* auto index1 = cagra::build(res, index_params, dataset1);
1968+
*
1969+
* std::vector<cagra::index<uint8_t, uint32_t>*> indices{&index0, &index1};
1970+
* cagra::merge_params params{index_params};
1971+
*
1972+
* auto merged_index = cagra::merge(res, params, indices);
1973+
* @endcode
1974+
*
1975+
* @param[in] res RAFT resources used for the merge operation.
1976+
* @param[in] params Parameters that control the merging process.
1977+
* @param[in] indices A vector of pointers to the CAGRA indices to merge. All indices must:
1978+
* - Have attached datasets with the same dimension.
1979+
*
1980+
* @return A new CAGRA index containing the merged indices, graph, and dataset.
1981+
*/
1982+
auto merge(raft::resources const& res,
1983+
const cuvs::neighbors::cagra::merge_params& params,
1984+
std::vector<cuvs::neighbors::cagra::index<uint8_t, uint32_t>*>& indices)
1985+
-> cuvs::neighbors::cagra::index<uint8_t, uint32_t>;
17971986
/**
17981987
* @}
17991988
*/

cpp/src/neighbors/cagra.cuh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "detail/cagra/add_nodes.cuh"
2020
#include "detail/cagra/cagra_build.cuh"
21+
#include "detail/cagra/cagra_merge.cuh"
2122
#include "detail/cagra/cagra_search.cuh"
2223
#include "detail/cagra/graph_core.cuh"
2324

@@ -380,6 +381,14 @@ void extend(
380381
cagra::extend_core<T, IdxT, Accessor>(handle, additional_dataset, index, params, ndv, ngv);
381382
}
382383

384+
template <class T, class IdxT>
385+
index<T, IdxT> merge(raft::resources const& handle,
386+
const cagra::merge_params& params,
387+
std::vector<cuvs::neighbors::cagra::index<T, IdxT>*>& indices)
388+
{
389+
return cagra::detail::merge<T, IdxT>(handle, params, indices);
390+
}
391+
383392
/** @} */ // end group cagra
384393

385394
} // namespace cuvs::neighbors::cagra
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "cagra.cuh"
18+
#include <cuvs/neighbors/cagra.hpp>
19+
20+
namespace cuvs::neighbors::cagra {
21+
22+
#define RAFT_INST_CAGRA_MERGE(T, IdxT) \
23+
auto merge(raft::resources const& handle, \
24+
const cuvs::neighbors::cagra::merge_params& params, \
25+
std::vector<cuvs::neighbors::cagra::index<T, IdxT>*>& indices) \
26+
->cuvs::neighbors::cagra::index<T, IdxT> \
27+
{ \
28+
return cuvs::neighbors::cagra::merge<T, IdxT>(handle, params, indices); \
29+
}
30+
31+
RAFT_INST_CAGRA_MERGE(float, uint32_t);
32+
33+
#undef RAFT_INST_CAGRA_MERGE
34+
35+
} // namespace cuvs::neighbors::cagra
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "cagra.cuh"
18+
#include <cuvs/neighbors/cagra.hpp>
19+
20+
namespace cuvs::neighbors::cagra {
21+
22+
#define RAFT_INST_CAGRA_MERGE(T, IdxT) \
23+
auto merge(raft::resources const& handle, \
24+
const cuvs::neighbors::cagra::merge_params& params, \
25+
std::vector<cuvs::neighbors::cagra::index<T, IdxT>*>& indices) \
26+
->cuvs::neighbors::cagra::index<T, IdxT> \
27+
{ \
28+
return cuvs::neighbors::cagra::merge<T, IdxT>(handle, params, indices); \
29+
}
30+
31+
RAFT_INST_CAGRA_MERGE(half, uint32_t);
32+
33+
#undef RAFT_INST_CAGRA_MERGE
34+
35+
} // namespace cuvs::neighbors::cagra
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "cagra.cuh"
18+
#include <cuvs/neighbors/cagra.hpp>
19+
20+
namespace cuvs::neighbors::cagra {
21+
22+
#define RAFT_INST_CAGRA_MERGE(T, IdxT) \
23+
auto merge(raft::resources const& handle, \
24+
const cuvs::neighbors::cagra::merge_params& params, \
25+
std::vector<cuvs::neighbors::cagra::index<T, IdxT>*>& indices) \
26+
->cuvs::neighbors::cagra::index<T, IdxT> \
27+
{ \
28+
return cuvs::neighbors::cagra::merge<T, IdxT>(handle, params, indices); \
29+
}
30+
31+
RAFT_INST_CAGRA_MERGE(int8_t, uint32_t);
32+
33+
#undef RAFT_INST_CAGRA_MERGE
34+
35+
} // namespace cuvs::neighbors::cagra
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "cagra.cuh"
18+
#include <cuvs/neighbors/cagra.hpp>
19+
20+
namespace cuvs::neighbors::cagra {
21+
22+
#define RAFT_INST_CAGRA_MERGE(T, IdxT) \
23+
auto merge(raft::resources const& handle, \
24+
const cuvs::neighbors::cagra::merge_params& params, \
25+
std::vector<cuvs::neighbors::cagra::index<T, IdxT>*>& indices) \
26+
->cuvs::neighbors::cagra::index<T, IdxT> \
27+
{ \
28+
return cuvs::neighbors::cagra::merge<T, IdxT>(handle, params, indices); \
29+
}
30+
31+
RAFT_INST_CAGRA_MERGE(uint8_t, uint32_t);
32+
33+
#undef RAFT_INST_CAGRA_MERGE
34+
35+
} // namespace cuvs::neighbors::cagra

0 commit comments

Comments
 (0)