uqa_storage/vector_index/
canonical.rs1use crate::{read_control::StorageReadControl, StorageBackendResult};
10use std::sync::Arc;
11use uqa_core::{
12 memory::{Budgeted, BudgetedVec},
13 DocId,
14};
15
16mod fingerprint;
17mod ordered;
18mod selection;
19pub(crate) use fingerprint::selected_fingerprint;
20pub(crate) use ordered::{copy_vector, ordinal_count};
21pub use selection::SelectedVectorRead;
22
23#[cfg(test)]
24mod tests;
25
26pub trait VectorRead: Send + Sync {
28 fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()>;
29 fn dimensions(&self) -> u32;
30 fn corpus_fingerprint(
32 &self,
33 control: &StorageReadControl,
34 ) -> StorageBackendResult<Option<[u8; 32]>> {
35 self.check_control(control)?;
36 Ok(None)
37 }
38 fn next_document_after(
39 &self,
40 after: Option<DocId>,
41 control: &StorageReadControl,
42 ) -> StorageBackendResult<Option<DocId>>;
43 fn document_vector_count(
45 &self,
46 document: DocId,
47 control: &StorageReadControl,
48 ) -> StorageBackendResult<u64>;
49 fn read_vector(
51 &self,
52 document: DocId,
53 ordinal: u32,
54 control: &StorageReadControl,
55 ) -> StorageBackendResult<Option<BudgetedVec<f32>>>;
56}
57
58pub type VectorReadSnapshot = Arc<dyn VectorRead>;
59
60pub(crate) fn canonical_vectors_equal(
62 source: &dyn VectorRead,
63 document: DocId,
64 vectors: &[Vec<f32>],
65 control: &StorageReadControl,
66) -> StorageBackendResult<bool> {
67 source.check_control(control)?;
68 for vector in vectors {
69 super::validate_vector_values_controlled(source.dimensions(), vector, Some(control))?;
70 }
71 if source.document_vector_count(document, control)? != vectors.len() as u64 {
72 return Ok(false);
73 }
74 for (ordinal, expected) in vectors.iter().enumerate() {
75 let ordinal =
76 u32::try_from(ordinal).map_err(|_| uqa_core::memory::MemoryError::SizeOverflow)?;
77 let Some(actual) = source.read_vector(document, ordinal, control)? else {
78 return Ok(false);
79 };
80 if actual.len() != expected.len() {
81 return Ok(false);
82 }
83 for (actual, expected) in actual.chunks(1024).zip(expected.chunks(1024)) {
84 source.check_control(control)?;
85 if actual
86 .iter()
87 .zip(expected)
88 .any(|(a, b)| a.to_bits() != b.to_bits())
89 {
90 return Ok(false);
91 }
92 }
93 }
94 source.check_control(control)?;
95 Ok(true)
96}
97
98pub fn decode_vector_bytes(
100 bytes: &[u8],
101 dimensions: u32,
102 control: &StorageReadControl,
103) -> StorageBackendResult<BudgetedVec<f32>> {
104 control.check()?;
105 let expected = usize::try_from(dimensions)
106 .map_err(|_| uqa_core::memory::MemoryError::SizeOverflow)?
107 .checked_mul(4)
108 .ok_or(uqa_core::memory::MemoryError::SizeOverflow)?;
109 if bytes.len() != expected {
110 return Err(
111 crate::mvcc::VersionError::InvalidEncoding("canonical vector width mismatch")
112 .into_storage_error(),
113 );
114 }
115 let mut vector = BudgetedVec::new(control.memory());
116 vector.reserve(bytes.len() / 4)?;
117 for (position, bytes) in bytes.as_chunks::<4>().0.iter().enumerate() {
118 if position.is_multiple_of(1024) {
119 control.check()?;
120 }
121 vector.push(f32::from_le_bytes(*bytes))?;
122 }
123 super::validate_vector_values_controlled(dimensions, &vector, Some(control))?;
124 Ok(vector)
125}
126
127impl<T: VectorRead> VectorRead for Budgeted<T> {
128 fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()> {
129 (**self).check_control(control)
130 }
131 fn dimensions(&self) -> u32 {
132 (**self).dimensions()
133 }
134 fn corpus_fingerprint(
135 &self,
136 control: &StorageReadControl,
137 ) -> StorageBackendResult<Option<[u8; 32]>> {
138 (**self).corpus_fingerprint(control)
139 }
140 fn next_document_after(
141 &self,
142 after: Option<DocId>,
143 control: &StorageReadControl,
144 ) -> StorageBackendResult<Option<DocId>> {
145 (**self).next_document_after(after, control)
146 }
147 fn document_vector_count(
148 &self,
149 document: DocId,
150 control: &StorageReadControl,
151 ) -> StorageBackendResult<u64> {
152 (**self).document_vector_count(document, control)
153 }
154 fn read_vector(
155 &self,
156 document: DocId,
157 ordinal: u32,
158 control: &StorageReadControl,
159 ) -> StorageBackendResult<Option<BudgetedVec<f32>>> {
160 (**self).read_vector(document, ordinal, control)
161 }
162}