uqa_storage/diskann_index/
metric.rs1use uqa_core::memory::BudgetedVec;
8
9use crate::read_control::StorageReadControl;
10use crate::vector_index::validate_vector_values_controlled;
11use crate::{StorageBackendError, StorageBackendResult};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum ExactVectorReason {
15 ZeroNorm,
16 NonFiniteNorm,
17}
18
19#[derive(Debug)]
21pub enum NavigationInput {
22 Navigable(NavigationVector),
23 Exact(ExactVectorReason),
24}
25
26#[derive(Debug)]
27pub struct NavigationVector {
28 coordinates: BudgetedVec<f64>,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
33pub struct SquaredNavigationDistance(f64);
34
35impl SquaredNavigationDistance {
36 pub fn get(self) -> f64 {
37 self.0
38 }
39}
40
41impl NavigationInput {
42 pub fn from_raw(
43 dimensions: u32,
44 raw: &[f32],
45 control: &StorageReadControl,
46 ) -> StorageBackendResult<Self> {
47 let (canonical_norm, norm) = norms(dimensions, raw, control)?;
48 if let Some(reason) = exact_reason(canonical_norm) {
49 return Ok(Self::Exact(reason));
50 }
51 let mut coordinates = BudgetedVec::new(control.memory());
52 coordinates.reserve(raw.len())?;
53 for (offset, &value) in raw.iter().enumerate() {
54 checkpoint(offset, control)?;
55 coordinates.push(f64::from(value) / norm)?;
56 }
57 control.check()?;
58 Ok(Self::Navigable(NavigationVector { coordinates }))
59 }
60}
61
62pub(super) fn exact_reason(canonical_norm: f32) -> Option<ExactVectorReason> {
63 if canonical_norm == 0.0 {
64 Some(ExactVectorReason::ZeroNorm)
65 } else if !canonical_norm.is_finite() {
66 Some(ExactVectorReason::NonFiniteNorm)
67 } else {
68 None
69 }
70}
71
72pub(super) fn norms(
73 dimensions: u32,
74 raw: &[f32],
75 control: &StorageReadControl,
76) -> StorageBackendResult<(f32, f64)> {
77 validate_vector_values_controlled(dimensions, raw, Some(control))?;
78 if dimensions == 0 {
79 return Err(StorageBackendError::Other(
80 "DiskANN dimensions must be positive".into(),
81 ));
82 }
83 let mut canonical_squared_norm = 0.0_f32;
84 let mut squared_norm = 0.0_f64;
85 for (offset, &value) in raw.iter().enumerate() {
86 checkpoint(offset, control)?;
87 canonical_squared_norm += value * value;
88 let value = f64::from(value);
89 squared_norm += value * value;
90 }
91 Ok((canonical_squared_norm.sqrt(), squared_norm.sqrt()))
92}
93
94impl NavigationVector {
95 pub fn coordinates(&self) -> &[f64] {
96 &self.coordinates
97 }
98
99 pub(super) fn copy(&self, control: &StorageReadControl) -> StorageBackendResult<Self> {
100 control.check()?;
101 let mut coordinates = BudgetedVec::new(control.memory());
102 coordinates.reserve(self.coordinates.len())?;
103 for (offset, &value) in self.coordinates.iter().enumerate() {
104 checkpoint(offset, control)?;
105 coordinates.push(value)?;
106 }
107 control.check()?;
108 Ok(Self { coordinates })
109 }
110
111 pub(super) fn squared_distance_to_raw(
113 &self,
114 raw: &[f32],
115 control: &StorageReadControl,
116 ) -> StorageBackendResult<SquaredNavigationDistance> {
117 let (canonical_norm, norm) = norms(self.coordinates.len() as u32, raw, control)?;
118 if exact_reason(canonical_norm).is_some() {
119 return Err(StorageBackendError::Other(
120 "DiskANN physical node is not navigable".into(),
121 ));
122 }
123 let mut distance = 0.0;
124 for (offset, (&query, &value)) in self.coordinates.iter().zip(raw).enumerate() {
125 checkpoint(offset, control)?;
126 let difference = query - f64::from(value) / norm;
127 distance += difference * difference;
128 }
129 control.check()?;
130 Ok(SquaredNavigationDistance(distance))
131 }
132
133 pub fn squared_distance(
134 &self,
135 other: &Self,
136 control: &StorageReadControl,
137 ) -> StorageBackendResult<SquaredNavigationDistance> {
138 control.check()?;
139 if self.coordinates.len() != other.coordinates.len() {
140 return Err(StorageBackendError::Other(
141 "DiskANN navigation dimensions differ".into(),
142 ));
143 }
144 Ok(SquaredNavigationDistance(squared_distance(
145 &self.coordinates,
146 &other.coordinates,
147 control,
148 )?))
149 }
150}
151
152pub(super) fn squared_distance(
153 left: &[f64],
154 right: &[f64],
155 control: &StorageReadControl,
156) -> StorageBackendResult<f64> {
157 debug_assert_eq!(left.len(), right.len());
158 let mut distance = 0.0;
159 for (offset, (left, right)) in left.iter().zip(right).enumerate() {
160 checkpoint(offset, control)?;
161 let difference = left - right;
162 distance += difference * difference;
163 }
164 Ok(distance)
165}
166
167pub(super) fn checkpoint(offset: usize, control: &StorageReadControl) -> StorageBackendResult<()> {
168 if offset.is_multiple_of(1024) {
169 control.check()?;
170 }
171 Ok(())
172}
173
174#[cfg(test)]
175mod tests;