1#![doc = include_str!("../README.md")]
2
3use faiss_next_sys as sys;
4use std::ffi::CString;
5use std::ptr::{addr_of_mut, null_mut};
6use std::time::Instant;
7use tracing::trace;
8
9pub use sys::FaissMetricType;
12
13#[derive(Debug, thiserror::Error)]
15pub enum Error {
16 #[error("{0}")]
17 NulErr(#[from] std::ffi::NulError),
18 #[error("faiss error,code={},message={}", .code, .message)]
19 Faiss { code: i32, message: String },
20}
21
22impl Error {
23 pub fn from_rc(rc: i32) -> Self {
24 match rc {
25 0 => unimplemented!(),
26 _ => {
27 let message = unsafe { std::ffi::CStr::from_ptr(sys::faiss_get_last_error()) };
28 let message = message
29 .to_str()
30 .unwrap_or("unknown error, failed to decode error message from bytes")
31 .to_string();
32 Error::Faiss { code: rc, message }
33 }
34 }
35 }
36}
37
38pub type Result<T> = std::result::Result<T, Error>;
39
40macro_rules! faiss_rc {
41 ($blk: block) => {
42 match unsafe { $blk } {
43 0 => Ok(()),
44 rc @ _ => Err(Error::from_rc(rc)),
45 }
46 };
47}
48
49#[derive(Debug)]
51pub struct SearchResult {
52 pub labels: Vec<i64>,
54 pub distances: Vec<f32>,
56}
57
58pub trait IndexInner {
60 fn inner(&self) -> *mut sys::FaissIndex;
62}
63
64pub trait Index: IndexInner {
66 fn add<T: AsRef<[f32]> + ?Sized>(&mut self, x: &T) -> Result<()> {
68 let x = x.as_ref();
69 faiss_rc!({
70 sys::faiss_Index_add(self.inner(), (x.len() / self.d()) as sys::idx_t, x.as_ptr())
71 })?;
72 trace!("add: index={:?}, x.len={}", self.inner(), x.as_ref().len());
73 Ok(())
74 }
75
76 fn search<T: AsRef<[f32]> + ?Sized>(&self, x: &T, k: usize) -> Result<SearchResult> {
78 let x = x.as_ref();
79 let n = (x.len() / self.d()) as sys::idx_t;
80 let mut labels = vec![0 as sys::idx_t; n as usize * k];
81 let mut distances = vec![0.0f32; n as usize * k];
82 let tm = Instant::now();
83 faiss_rc!({
84 {
85 sys::faiss_Index_search(
86 self.inner(),
87 n,
88 x.as_ptr(),
89 k as sys::idx_t,
90 distances.as_mut_ptr(),
91 labels.as_mut_ptr(),
92 )
93 }
94 })?;
95 trace!(
96 "search index d={}, n={}, k={}, tm={:?}",
97 self.d(),
98 n,
99 k,
100 tm.elapsed()
101 );
102 Ok(SearchResult { labels, distances })
103 }
104
105 fn d(&self) -> usize {
107 unsafe { sys::faiss_Index_d(self.inner()) as usize }
108 }
109
110 fn is_trained(&self) -> bool {
112 unsafe { sys::faiss_Index_is_trained(self.inner()) != 0 }
113 }
114
115 fn train<T: AsRef<[f32]> + ?Sized>(&mut self, x: &T) -> Result<()> {
117 let x = x.as_ref();
118 let n = (x.len() / self.d()) as sys::idx_t;
119 faiss_rc!({ sys::faiss_Index_train(self.inner(), n, x.as_ptr()) })?;
120 Ok(())
121 }
122
123 fn remove_ids(&mut self, sel: IDSelector) -> Result<usize> {
125 let mut n_removed = 0usize;
126 faiss_rc!({
127 sys::faiss_Index_remove_ids(self.inner(), sel.inner, addr_of_mut!(n_removed))
128 })?;
129 Ok(n_removed)
130 }
131
132 fn save<P: AsRef<str>>(&self, pth: P) -> Result<()> {
134 let pth = pth.as_ref();
135 let pth = CString::new(pth)?;
136 faiss_rc!({ sys::faiss_write_index_fname(self.inner() as *const _, pth.as_ptr()) })?;
137 todo!()
138 }
139
140 fn load<P: AsRef<str>>(pth: P) -> Result<CpuIndex> {
142 let pth = pth.as_ref();
143 let pth = CString::new(pth)?;
144 let mut inner = null_mut();
145 faiss_rc!({ sys::faiss_read_index_fname(pth.as_ptr(), 0, addr_of_mut!(inner)) })?;
146 Ok(CpuIndex { inner })
147 }
148}
149
150pub struct IDSelector {
152 pub inner: *mut sys::FaissIDSelector,
153}
154
155impl IDSelector {
156 pub fn batch(ids: &[i64]) -> Result<Self> {
159 let mut inner = null_mut();
160 faiss_rc!({
161 sys::faiss_IDSelectorBatch_new(addr_of_mut!(inner), ids.len(), ids.as_ptr())
162 })?;
163 Ok(Self {
164 inner: inner as *mut _,
165 })
166 }
167}
168
169impl Drop for IDSelector {
170 fn drop(&mut self) {
171 unsafe { sys::faiss_IDSelector_free(self.inner) }
172 }
173}
174
175#[derive(Debug)]
185pub struct CpuIndex {
186 pub inner: *mut sys::FaissIndex,
187}
188
189impl Drop for CpuIndex {
190 fn drop(&mut self) {
191 unsafe {
192 sys::faiss_Index_free(self.inner);
193 }
194 trace!("drop: index={:?}", self.inner);
195 }
196}
197
198impl IndexInner for CpuIndex {
199 fn inner(&self) -> *mut sys::FaissIndex {
200 self.inner
201 }
202}
203
204impl Index for CpuIndex {}
205
206impl CpuIndex {
207 #[cfg(feature = "gpu")]
209 pub fn to_multi_gpu(&self, devices: &[i32], shard: bool) -> Result<gpu::GpuIndex> {
210 let mut p_out = 0 as *mut _;
211 let providers = (0..devices.len())
212 .map(|_| -> Result<_> { gpu::GpuResourcesProvider::new() })
213 .collect::<Result<Vec<_>>>()?;
214 let mut options = 0 as *mut sys::FaissGpuClonerOptions;
215 faiss_rc!({ sys::faiss_GpuClonerOptions_new(addr_of_mut!(options)) })?;
216 if shard {
217 unsafe { sys::faiss_GpuMultipleClonerOptions_set_shard(options, 1) };
218 }
219 let providers_ = providers.iter().map(|p| p.inner).collect::<Vec<_>>();
220 faiss_rc!({
221 sys::faiss_index_cpu_to_gpu_multiple_with_options(
222 providers_.as_ptr(),
223 providers.len(),
224 devices.as_ptr(),
225 devices.len(),
226 self.inner,
227 options,
228 addr_of_mut!(p_out),
229 )
230 })?;
231 Ok(gpu::GpuIndex {
232 inner: p_out,
233 providers: providers,
234 })
235 }
236
237 #[cfg(feature = "gpu")]
239 pub fn into_multi_gpu(self, devices: &[i32], shard: bool) -> Result<gpu::GpuIndex> {
240 self.to_multi_gpu(devices, shard)
241 }
242
243 #[cfg(feature = "gpu")]
245 pub fn to_gpu(&self, device: i32) -> Result<gpu::GpuIndex> {
246 let mut p_out = 0 as *mut _;
247 let provider = gpu::GpuResourcesProvider::new()?;
248 trace!(?provider, "create gpu provider");
249 faiss_rc!({
250 sys::faiss_index_cpu_to_gpu(provider.inner, device, self.inner, addr_of_mut!(p_out))
251 })?;
252 trace!(
253 "into_gpu: from {:?} to index={:?}, device={}",
254 self.inner,
255 p_out,
256 device
257 );
258 Ok(gpu::GpuIndex {
259 inner: p_out,
260 providers: vec![provider],
261 })
262 }
263
264 #[cfg(feature = "gpu")]
266 pub fn into_gpu(self, device: i32) -> Result<gpu::GpuIndex> {
267 self.to_gpu(device)
268 }
269}
270
271impl Clone for CpuIndex {
272 fn clone(&self) -> Self {
273 let mut inner = null_mut();
274 faiss_rc!({ sys::faiss_clone_index(self.inner, addr_of_mut!(inner)) })
275 .unwrap_or_else(|_| panic!("failed to clone index with inner={:?}", self.inner));
276 Self { inner }
277 }
278}
279
280pub fn index_factory(d: i32, description: &str, metric: FaissMetricType) -> Result<CpuIndex> {
282 let mut p_index = null_mut();
283 let description_ = CString::new(description)?;
284 faiss_rc! {{sys::faiss_index_factory(addr_of_mut!(p_index), d, description_.as_ptr(), metric)}}?;
285 trace!(
286 "index_factory: create index={:?}, description={}, metric={:?}",
287 p_index,
288 description,
289 metric
290 );
291 Ok(CpuIndex { inner: p_index })
292}
293
294#[cfg(feature = "gpu")]
296pub mod gpu {
297 use super::{addr_of_mut, null_mut, sys, Error, Index, IndexInner};
298 use tracing::trace;
299
300 pub struct GpuIndex {
302 pub providers: Vec<GpuResourcesProvider>,
304 pub inner: *mut sys::FaissGpuIndex,
306 }
307
308 #[derive(Debug)]
310 pub struct GpuResourcesProvider {
311 pub inner: *mut sys::FaissGpuResourcesProvider,
312 }
313
314 impl GpuResourcesProvider {
315 pub fn new() -> super::Result<Self> {
316 let mut inner = null_mut();
317 faiss_rc!({ sys::faiss_StandardGpuResources_new(addr_of_mut!(inner)) })?;
318 trace!(?inner, "create gpu provider");
319 Ok(Self { inner })
320 }
321 }
322
323 impl Drop for GpuResourcesProvider {
324 fn drop(&mut self) {
325 unsafe { sys::faiss_GpuResourcesProvider_free(self.inner) }
326 trace!("drop: gpu provider={:?}", self.inner);
327 }
328 }
329
330 impl IndexInner for GpuIndex {
331 fn inner(&self) -> *mut sys::FaissIndex {
332 self.inner
333 }
334 }
335
336 impl Index for GpuIndex {}
337
338 impl GpuIndex {
339 pub fn to_cpu(&self) -> super::Result<super::CpuIndex> {
340 let mut inner = null_mut();
341 faiss_rc!({ sys::faiss_index_gpu_to_cpu(self.inner, addr_of_mut!(inner)) })?;
342 Ok(super::CpuIndex { inner })
343 }
344
345 pub fn into_cpu(self) -> super::Result<super::CpuIndex> {
346 self.to_cpu()
347 }
348 }
349
350 impl Drop for GpuIndex {
351 fn drop(&mut self) {
352 unsafe {
353 sys::faiss_Index_free(self.inner);
354 trace!("drop: index={:?}", self.inner);
355 }
356 }
357 }
358}