storage_kit/core/
vector.rs1use serde::{Deserialize, Serialize};
2
3use super::error::StorageError;
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub enum DenseVector {
15 F32(Vec<f32>),
17 F64(Vec<f64>),
19 I16(Vec<i16>),
21 I32(Vec<i32>),
23 I64(Vec<i64>),
25}
26
27#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
39pub enum VectorCoercionPolicy {
40 Exact,
42 Lossless,
44 Lossy,
46}
47
48impl DenseVector {
49 pub fn len(&self) -> usize {
58 match self {
59 Self::F32(v) => v.len(),
60 Self::F64(v) => v.len(),
61 Self::I16(v) => v.len(),
62 Self::I32(v) => v.len(),
63 Self::I64(v) => v.len(),
64 }
65 }
66
67 pub fn is_empty(&self) -> bool {
76 self.len() == 0
77 }
78
79 pub fn to_f32(&self, policy: VectorCoercionPolicy) -> Result<Vec<f32>, StorageError> {
96 match self {
97 Self::F32(v) => Ok(v.clone()),
98 Self::F64(v) => Self::convert_f64_to_f32(v, policy),
99 Self::I16(v) => Self::convert_i16_to_f32(v, policy),
100 Self::I32(v) => Self::convert_i32_to_f32(v, policy),
101 Self::I64(v) => Self::convert_i64_to_f32(v, policy),
102 }
103 }
104
105 fn conversion_rejected(source: &'static str, policy: VectorCoercionPolicy) -> StorageError {
106 StorageError::InvalidRequest(format!(
107 "cannot convert {source} dense vector to f32 under {policy:?} policy"
108 ))
109 }
110
111 fn value_rejected(
112 source: &'static str,
113 index: usize,
114 value: impl std::fmt::Display,
115 policy: VectorCoercionPolicy,
116 ) -> StorageError {
117 StorageError::InvalidRequest(format!(
118 "cannot convert {source} dense vector value at index {index} (`{value}`) to f32 without precision loss under {policy:?} policy"
119 ))
120 }
121
122 fn convert_f64_to_f32(
123 values: &[f64],
124 policy: VectorCoercionPolicy,
125 ) -> Result<Vec<f32>, StorageError> {
126 if !policy.allows_representation_change() {
127 return Err(Self::conversion_rejected("f64", policy));
128 }
129
130 values
131 .iter()
132 .enumerate()
133 .map(|(index, value)| {
134 let converted = *value as f32;
135 if policy.allows_lossy()
136 || (converted.is_finite() && f64::from(converted) == *value)
137 {
138 Ok(converted)
139 } else {
140 Err(Self::value_rejected("f64", index, value, policy))
141 }
142 })
143 .collect()
144 }
145
146 fn convert_i16_to_f32(
147 values: &[i16],
148 policy: VectorCoercionPolicy,
149 ) -> Result<Vec<f32>, StorageError> {
150 if !policy.allows_representation_change() {
151 return Err(Self::conversion_rejected("i16", policy));
152 }
153
154 Ok(values.iter().map(|value| f32::from(*value)).collect())
155 }
156
157 fn convert_i32_to_f32(
158 values: &[i32],
159 policy: VectorCoercionPolicy,
160 ) -> Result<Vec<f32>, StorageError> {
161 if !policy.allows_representation_change() {
162 return Err(Self::conversion_rejected("i32", policy));
163 }
164
165 values
166 .iter()
167 .enumerate()
168 .map(|(index, value)| {
169 let converted = *value as f32;
170 if policy.allows_lossy() || converted as i128 == i128::from(*value) {
171 Ok(converted)
172 } else {
173 Err(Self::value_rejected("i32", index, value, policy))
174 }
175 })
176 .collect()
177 }
178
179 fn convert_i64_to_f32(
180 values: &[i64],
181 policy: VectorCoercionPolicy,
182 ) -> Result<Vec<f32>, StorageError> {
183 if !policy.allows_representation_change() {
184 return Err(Self::conversion_rejected("i64", policy));
185 }
186
187 values
188 .iter()
189 .enumerate()
190 .map(|(index, value)| {
191 let converted = *value as f32;
192 if policy.allows_lossy() || converted as i128 == i128::from(*value) {
193 Ok(converted)
194 } else {
195 Err(Self::value_rejected("i64", index, value, policy))
196 }
197 })
198 .collect()
199 }
200}
201
202impl VectorCoercionPolicy {
203 fn allows_representation_change(self) -> bool {
204 matches!(self, Self::Lossless | Self::Lossy)
205 }
206
207 fn allows_lossy(self) -> bool {
208 matches!(self, Self::Lossy)
209 }
210}
211
212#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
221pub enum DistanceMetric {
222 L2,
224 Cosine,
226 Hamming,
228}