prolly/prolly/proximity/accelerator/
mod.rs1mod r#async;
2pub mod catalog;
3pub mod composite;
4pub mod hnsw;
5pub mod pq;
6pub(crate) mod sq8;
7
8use self::composite::CompositeAccelerator;
9use self::hnsw::HnswIndex;
10use self::pq::ProductQuantizer;
11use super::ProximityTree;
12use crate::prolly::error::Error;
13use crate::prolly::store::Store;
14pub(crate) use r#async::AsyncCompositeBase;
15pub use r#async::{
16 AsyncAcceleratorCatalog, AsyncAcceleratorSet, AsyncCompositeAccelerator, AsyncHnswIndex,
17 AsyncProductQuantizer,
18};
19
20pub struct AcceleratorSet<S: Store> {
22 hnsw: Option<HnswIndex<S>>,
23 pq: Option<ProductQuantizer<S>>,
24 composite: Option<CompositeAccelerator<S>>,
25}
26
27impl<S: Store> Default for AcceleratorSet<S> {
28 fn default() -> Self {
29 Self {
30 hnsw: None,
31 pq: None,
32 composite: None,
33 }
34 }
35}
36
37impl<S: Store> AcceleratorSet<S> {
38 pub fn try_new(
39 source: &ProximityTree,
40 hnsw: Option<HnswIndex<S>>,
41 pq: Option<ProductQuantizer<S>>,
42 ) -> Result<Self, Error> {
43 if let Some(index) = &hnsw {
44 validate_binding(
45 source,
46 &index.source,
47 index.dimensions,
48 index.metric,
49 index.count,
50 "HNSW",
51 )?;
52 }
53 if let Some(index) = &pq {
54 validate_binding(
55 source,
56 &index.source,
57 index.dimensions,
58 index.metric,
59 index.count,
60 "product quantization",
61 )?;
62 }
63 Ok(Self {
64 hnsw,
65 pq,
66 composite: None,
67 })
68 }
69
70 pub fn empty() -> Self {
71 Self::default()
72 }
73
74 pub fn with_hnsw(mut self, source: &ProximityTree, index: HnswIndex<S>) -> Result<Self, Error> {
75 if self.hnsw.is_some() {
76 return Err(invalid("duplicate HNSW accelerator"));
77 }
78 validate_binding(
79 source,
80 &index.source,
81 index.dimensions,
82 index.metric,
83 index.count,
84 "HNSW",
85 )?;
86 self.hnsw = Some(index);
87 Ok(self)
88 }
89
90 pub fn with_pq(
91 mut self,
92 source: &ProximityTree,
93 index: ProductQuantizer<S>,
94 ) -> Result<Self, Error> {
95 if self.pq.is_some() {
96 return Err(invalid("duplicate product-quantization accelerator"));
97 }
98 validate_binding(
99 source,
100 &index.source,
101 index.dimensions,
102 index.metric,
103 index.count,
104 "product quantization",
105 )?;
106 self.pq = Some(index);
107 Ok(self)
108 }
109
110 pub fn with_composite(
111 mut self,
112 source: &ProximityTree,
113 accelerator: CompositeAccelerator<S>,
114 ) -> Result<Self, Error> {
115 if self.composite.is_some() {
116 return Err(invalid("duplicate composite accelerator"));
117 }
118 if accelerator.current_source != source.descriptor
119 || accelerator.dimensions != source.config.dimensions
120 || accelerator.metric != source.config.metric
121 || accelerator.current_count != source.count
122 {
123 return Err(invalid(
124 "composite accelerator is bound to a different source snapshot",
125 ));
126 }
127 self.composite = Some(accelerator);
128 Ok(self)
129 }
130
131 pub(crate) fn hnsw(&self) -> Option<&HnswIndex<S>> {
132 self.hnsw.as_ref()
133 }
134
135 pub(crate) fn pq(&self) -> Option<&ProductQuantizer<S>> {
136 self.pq.as_ref()
137 }
138
139 pub(crate) fn composite(&self) -> Option<&CompositeAccelerator<S>> {
140 self.composite.as_ref()
141 }
142}
143
144fn validate_binding(
145 source: &ProximityTree,
146 descriptor: &crate::prolly::cid::Cid,
147 dimensions: u32,
148 metric: super::DistanceMetric,
149 count: u64,
150 kind: &'static str,
151) -> Result<(), Error> {
152 if descriptor != &source.descriptor
153 || dimensions != source.config.dimensions
154 || metric != source.config.metric
155 || count != source.count
156 {
157 return Err(invalid(format!(
158 "{kind} accelerator is bound to a different source snapshot"
159 )));
160 }
161 Ok(())
162}
163
164fn invalid(reason: impl Into<String>) -> Error {
165 Error::InvalidProximitySearch {
166 reason: reason.into(),
167 }
168}