diff --git a/crates/base/src/index.rs b/crates/base/src/index.rs index fa1556414..13a13807e 100644 --- a/crates/base/src/index.rs +++ b/crates/base/src/index.rs @@ -172,9 +172,6 @@ impl VectorOptions { (VectorKind::BVecf32, DistanceKind::Cos, 1..65536) => Ok(()), (VectorKind::BVecf32, DistanceKind::Dot, 1..65536) => Ok(()), (VectorKind::BVecf32, DistanceKind::Jaccard, 1..65536) => Ok(()), - (VectorKind::Veci8, DistanceKind::L2, 1..65536) => Ok(()), - (VectorKind::Veci8, DistanceKind::Cos, 1..65536) => Ok(()), - (VectorKind::Veci8, DistanceKind::Dot, 1..65536) => Ok(()), _ => Err(ValidationError::new("not valid vector options")), } } diff --git a/crates/base/src/operator/mod.rs b/crates/base/src/operator/mod.rs index 57c53a2d9..14aae1fd3 100644 --- a/crates/base/src/operator/mod.rs +++ b/crates/base/src/operator/mod.rs @@ -11,9 +11,6 @@ mod vecf16_l2; mod vecf32_cos; mod vecf32_dot; mod vecf32_l2; -mod veci8_cos; -mod veci8_dot; -mod veci8_l2; pub use bvecf32_cos::BVecf32Cos; pub use bvecf32_dot::BVecf32Dot; @@ -28,9 +25,6 @@ pub use vecf16_l2::Vecf16L2; pub use vecf32_cos::Vecf32Cos; pub use vecf32_dot::Vecf32Dot; pub use vecf32_l2::Vecf32L2; -pub use veci8_cos::Veci8Cos; -pub use veci8_dot::Veci8Dot; -pub use veci8_l2::Veci8L2; use crate::distance::*; use crate::scalar::*; diff --git a/crates/base/src/operator/veci8_cos.rs b/crates/base/src/operator/veci8_cos.rs deleted file mode 100644 index 2cccf7107..000000000 --- a/crates/base/src/operator/veci8_cos.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::distance::*; -use crate::operator::*; -use crate::scalar::*; -use crate::vector::*; - -#[derive(Debug, Clone, Copy)] -pub enum Veci8Cos {} - -impl Operator for Veci8Cos { - type VectorOwned = Veci8Owned; - - const DISTANCE_KIND: DistanceKind = DistanceKind::Cos; - - fn distance(lhs: Borrowed<'_, Self>, rhs: Borrowed<'_, Self>) -> F32 { - F32(1.0) - veci8::cosine(&lhs, &rhs) - } -} diff --git a/crates/base/src/operator/veci8_dot.rs b/crates/base/src/operator/veci8_dot.rs deleted file mode 100644 index f6c5dd1df..000000000 --- a/crates/base/src/operator/veci8_dot.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::distance::*; -use crate::operator::*; -use crate::scalar::*; -use crate::vector::*; - -#[derive(Debug, Clone, Copy)] -pub enum Veci8Dot {} - -impl Operator for Veci8Dot { - type VectorOwned = Veci8Owned; - - const DISTANCE_KIND: DistanceKind = DistanceKind::Dot; - - fn distance(lhs: Borrowed<'_, Self>, rhs: Borrowed<'_, Self>) -> F32 { - veci8::dot(&lhs, &rhs) * (-1.0) - } -} diff --git a/crates/base/src/operator/veci8_l2.rs b/crates/base/src/operator/veci8_l2.rs deleted file mode 100644 index f856b0be2..000000000 --- a/crates/base/src/operator/veci8_l2.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::distance::*; -use crate::operator::*; -use crate::scalar::*; -use crate::vector::*; - -#[derive(Debug, Clone, Copy)] -pub enum Veci8L2 {} - -impl Operator for Veci8L2 { - type VectorOwned = Veci8Owned; - - const DISTANCE_KIND: DistanceKind = DistanceKind::Dot; - - fn distance(lhs: Borrowed<'_, Self>, rhs: Borrowed<'_, Self>) -> F32 { - veci8::sl2(&lhs, &rhs) - } -} diff --git a/crates/base/src/vector/mod.rs b/crates/base/src/vector/mod.rs index 1793ff85f..96d251dce 100644 --- a/crates/base/src/vector/mod.rs +++ b/crates/base/src/vector/mod.rs @@ -2,13 +2,11 @@ pub mod bvecf32; pub mod svecf32; pub mod vecf16; pub mod vecf32; -pub mod veci8; pub use bvecf32::{BVecf32Borrowed, BVecf32Owned, BVECF32_WIDTH}; pub use svecf32::{SVecf32Borrowed, SVecf32Owned}; pub use vecf16::{Vecf16Borrowed, Vecf16Owned}; pub use vecf32::{Vecf32Borrowed, Vecf32Owned}; -pub use veci8::{Veci8Borrowed, Veci8Owned}; use crate::scalar::ScalarLike; use crate::scalar::F32; @@ -22,7 +20,6 @@ pub enum VectorKind { Vecf16, SVecf32, BVecf32, - Veci8, } pub trait VectorOwned: Clone + Serialize + for<'a> Deserialize<'a> + 'static { @@ -71,7 +68,6 @@ pub enum OwnedVector { Vecf16(Vecf16Owned), SVecf32(SVecf32Owned), BVecf32(BVecf32Owned), - Veci8(Veci8Owned), } impl OwnedVector { @@ -81,7 +77,6 @@ impl OwnedVector { OwnedVector::Vecf16(x) => BorrowedVector::Vecf16(x.as_borrowed()), OwnedVector::SVecf32(x) => BorrowedVector::SVecf32(x.as_borrowed()), OwnedVector::BVecf32(x) => BorrowedVector::BVecf32(x.as_borrowed()), - OwnedVector::Veci8(x) => BorrowedVector::Veci8(x.as_borrowed()), } } } @@ -104,7 +99,6 @@ pub enum BorrowedVector<'a> { Vecf16(Vecf16Borrowed<'a>), SVecf32(SVecf32Borrowed<'a>), BVecf32(BVecf32Borrowed<'a>), - Veci8(Veci8Borrowed<'a>), } impl PartialEq for BorrowedVector<'_> { @@ -115,7 +109,6 @@ impl PartialEq for BorrowedVector<'_> { (Vecf16(lhs), Vecf16(rhs)) => lhs == rhs, (SVecf32(lhs), SVecf32(rhs)) => lhs == rhs, (BVecf32(lhs), BVecf32(rhs)) => lhs == rhs, - (Veci8(lhs), Veci8(rhs)) => lhs == rhs, _ => false, } } @@ -129,7 +122,6 @@ impl PartialOrd for BorrowedVector<'_> { (Vecf16(lhs), Vecf16(rhs)) => lhs.partial_cmp(rhs), (SVecf32(lhs), SVecf32(rhs)) => lhs.partial_cmp(rhs), (BVecf32(lhs), BVecf32(rhs)) => lhs.partial_cmp(rhs), - (Veci8(lhs), Veci8(rhs)) => lhs.partial_cmp(rhs), _ => None, } } diff --git a/crates/base/src/vector/veci8.rs b/crates/base/src/vector/veci8.rs deleted file mode 100644 index 31c438c3d..000000000 --- a/crates/base/src/vector/veci8.rs +++ /dev/null @@ -1,586 +0,0 @@ -use super::{VectorBorrowed, VectorKind, VectorOwned}; -use crate::scalar::{F32, I8}; -use num_traits::Float; -use serde::{Deserialize, Serialize}; -use std::ops::RangeBounds; - -/// Veci8 utilizes int8 for data storage, originally derived from Vecf32. -/// Given a vector of F32, [a_0, a_1, a_2, ..., a_n], we aim to find the maximum and minimum values. The maximum value, max, is the greatest among {a_0, a_1, a_2, ..., a_n}, and the minimum value, min, is the smallest. -/// We can transform F32 to I8 using the formula (a - (max + min) / 2) / (max - min) * 254, resulting in a vector of I8, [b_0, b_1, b_2, ..., b_n]. Here 254 is the range size that the int8 type can cover, which is the difference between -127 and 127. -/// Converting I8 back to F32 can be achieved by using the formula b * (max - min) / 254 + (max + min) / 2, which gives us a vector of F32, albeit with a slight loss of precision. -/// We use alpha to represent (max - min) / 254, and offset to represent (max + min) / 2 here. -/// We choose [-127, 127] rather than [-128, 127] to avoid overflow when we need to calculate (-a_i) in dot_i8_avx512vnni. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Veci8Owned { - data: Vec, - alpha: F32, - offset: F32, - // sum of a_i * alpha, precomputed for dot - sum: F32, - // l2 norm of original f_i, precomputed for l2 - l2_norm: F32, -} - -impl Veci8Owned { - #[inline(always)] - pub fn new(data: Vec, alpha: F32, offset: F32) -> Self { - Self::new_checked(data, alpha, offset).expect("invalid data") - } - - #[inline(always)] - pub fn new_checked(data: Vec, alpha: F32, offset: F32) -> Option { - if !(1..=65535).contains(&data.len()) { - return None; - } - let (sum, l2_norm) = i8_precompute(&data, alpha, offset); - Some(unsafe { Self::new_unchecked(data, alpha, offset, sum, l2_norm) }) - } - - /// # Safety - /// - /// * `dims` must be in `1..=65535`. - /// * `dims` must be equal to `values.len()`. - #[inline(always)] - pub unsafe fn new_unchecked( - data: Vec, - alpha: F32, - offset: F32, - sum: F32, - l2_norm: F32, - ) -> Self { - Veci8Owned { - data, - alpha, - offset, - sum, - l2_norm, - } - } - - #[inline(always)] - pub fn data(&self) -> &[I8] { - &self.data - } - - #[inline(always)] - pub fn alpha(&self) -> F32 { - self.alpha - } - - #[inline(always)] - pub fn offset(&self) -> F32 { - self.offset - } - - #[inline(always)] - pub fn sum(&self) -> F32 { - self.sum - } - - #[inline(always)] - pub fn l2_norm(&self) -> F32 { - self.l2_norm - } -} - -impl VectorOwned for Veci8Owned { - // For i8 quantization, the scalar type is used for type in kmeans, it use F32 to store the centroids and examples in the kmeans. - type Scalar = F32; - type Borrowed<'a> = Veci8Borrowed<'a>; - - const VECTOR_KIND: VectorKind = VectorKind::Veci8; - - fn as_borrowed(&self) -> Veci8Borrowed<'_> { - Veci8Borrowed { - data: &self.data, - alpha: self.alpha, - offset: self.offset, - sum: self.sum, - l2_norm: self.l2_norm, - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Veci8Borrowed<'a> { - data: &'a [I8], - alpha: F32, - offset: F32, - // sum of a_i * alpha, precomputed for dot - sum: F32, - // l2 norm of original f_i, precomputed for l2 - l2_norm: F32, -} - -impl<'a> Veci8Borrowed<'a> { - #[inline(always)] - pub fn new(data: &'a [I8], alpha: F32, offset: F32) -> Veci8Borrowed<'a> { - Self::new_checked(data, alpha, offset).expect("invalid data") - } - - #[inline(always)] - pub fn new_checked(data: &'a [I8], alpha: F32, offset: F32) -> Option { - if !(1..=65535).contains(&data.len()) { - return None; - } - let (sum, l2_norm) = i8_precompute(data, alpha, offset); - Some(unsafe { Self::new_unchecked(data, alpha, offset, sum, l2_norm) }) - } - - /// # Safety - /// - /// * `dims` must be in `1..=65535`. - /// * `dims` must be equal to `values.len()`. - /// * precomputed result must be correct - #[inline(always)] - pub unsafe fn new_unchecked( - data: &'a [I8], - alpha: F32, - offset: F32, - sum: F32, - l2_norm: F32, - ) -> Self { - Veci8Borrowed { - data, - alpha, - offset, - sum, - l2_norm, - } - } - - pub fn to_owned(&self) -> Veci8Owned { - Veci8Owned { - data: self.data.to_vec(), - alpha: self.alpha, - offset: self.offset, - sum: self.sum, - l2_norm: self.l2_norm, - } - } - - pub fn data(&self) -> &[I8] { - self.data - } - - pub fn alpha(&self) -> F32 { - self.alpha - } - - pub fn offset(&self) -> F32 { - self.offset - } - - pub fn sum(&self) -> F32 { - self.sum - } - - pub fn l2_norm(&self) -> F32 { - self.l2_norm - } - - pub fn index_checked(&self, i: u32) -> Option { - if i < self.dims() { - Some(self.data[i as usize].to_f32() * self.alpha + self.offset) - } else { - None - } - } - - pub fn index(&self, i: u32) -> F32 { - self.index_checked(i).expect("out of bound") - } -} - -impl VectorBorrowed for Veci8Borrowed<'_> { - // For i8 quantization, the scalar type is used for type in kmeans, it use F32 to store the centroids and examples in the kmeans. - type Scalar = F32; - type Owned = Veci8Owned; - - #[inline(always)] - fn dims(&self) -> u32 { - self.data.len() as u32 - } - - #[inline(always)] - fn own(&self) -> Veci8Owned { - Veci8Owned { - data: self.data.to_vec(), - alpha: self.alpha, - offset: self.offset, - sum: self.sum, - l2_norm: self.l2_norm, - } - } - - #[inline(always)] - fn to_index_vec(&self) -> Vec<(u32, Self::Scalar)> { - self.to_vec() - .iter() - .enumerate() - .map(|(i, x)| (i as u32, *x)) - .collect() - } - - #[inline(always)] - fn to_vec(&self) -> Vec { - i8_dequantization(self.data, self.alpha, self.offset) - } - - #[inline(always)] - fn length(&self) -> F32 { - self.l2_norm - } - - #[inline(always)] - fn function_normalize(&self) -> Veci8Owned { - let l = self.l2_norm; - let alpha = self.alpha / l; - let offset = self.offset / l; - let sum = self.sum / l; - let l2_norm = F32(1.0); - unsafe { Veci8Owned::new_unchecked(self.data.to_vec(), alpha, offset, sum, l2_norm) } - } - - fn operator_add(&self, rhs: Self) -> Self::Owned { - assert_eq!(self.data.len(), rhs.data.len()); - let n = self.dims(); - let data = (0..n) - .map(|i| self.index(i) + rhs.index(i)) - .collect::>(); - let (data, alpha, offset) = i8_quantization(&data); - Veci8Owned::new(data, alpha, offset) - } - - fn operator_minus(&self, rhs: Self) -> Self::Owned { - assert_eq!(self.data.len(), rhs.data.len()); - let n = self.dims(); - let data = (0..n) - .map(|i| self.index(i) - rhs.index(i)) - .collect::>(); - let (data, alpha, offset) = i8_quantization(&data); - Veci8Owned::new(data, alpha, offset) - } - - fn operator_mul(&self, rhs: Self) -> Self::Owned { - assert_eq!(self.data.len(), rhs.data.len()); - let n = self.dims(); - let data = (0..n) - .map(|i| self.index(i) * rhs.index(i)) - .collect::>(); - let (data, alpha, offset) = i8_quantization(&data); - Veci8Owned::new(data, alpha, offset) - } - - fn operator_and(&self, _: Self) -> Self::Owned { - unimplemented!() - } - - fn operator_or(&self, _: Self) -> Self::Owned { - unimplemented!() - } - - fn operator_xor(&self, _: Self) -> Self::Owned { - unimplemented!() - } - - #[inline(always)] - fn subvector(&self, bounds: impl RangeBounds) -> Option { - let start_bound = bounds.start_bound().map(|x| *x as usize); - let end_bound = bounds.end_bound().map(|x| *x as usize); - let slice = self.data.get((start_bound, end_bound))?; - if slice.is_empty() { - return None; - } - Self::Owned::new_checked(slice.to_vec(), self.alpha, self.offset) - } -} - -impl<'a> PartialEq for Veci8Borrowed<'a> { - fn eq(&self, other: &Self) -> bool { - if self.data.len() != other.data.len() { - return false; - } - let dims = self.dims(); - for i in 0..dims { - if self.index_checked(i) != other.index_checked(i) { - return false; - } - } - true - } -} - -impl<'a> PartialOrd for Veci8Borrowed<'a> { - fn partial_cmp(&self, other: &Self) -> Option { - use std::cmp::Ordering; - if self.data.len() != other.data.len() { - return None; - } - let dims = self.dims(); - for i in 0..dims { - match self.index_checked(i).cmp(&other.index_checked(i)) { - Ordering::Equal => (), - x => return Some(x), - } - } - Some(Ordering::Equal) - } -} - -#[detect::multiversion(v4, v3, v2, neon, fallback)] -pub fn i8_quantization(vector: &[F32]) -> (Vec, F32, F32) { - let min = vector.iter().copied().fold(F32::infinity(), Float::min); - let max = vector.iter().copied().fold(F32::neg_infinity(), Float::max); - let alpha = (max - min) / 254.0; - let offset = (max + min) / 2.0; - let result = vector - .iter() - .map(|&x| ((x - offset) / alpha).into()) - .collect(); - (result, alpha, offset) -} - -#[detect::multiversion(v4, v3, v2, neon, fallback)] -pub fn i8_dequantization(vector: &[I8], alpha: F32, offset: F32) -> Vec { - vector - .iter() - .map(|&x| (x.to_f32() * alpha + offset)) - .collect() -} - -#[detect::multiversion(v4, v3, v2, neon, fallback)] -pub fn i8_precompute(data: &[I8], alpha: F32, offset: F32) -> (F32, F32) { - let sum = data.iter().map(|&x| x.to_f32() * alpha).sum(); - let l2_norm = data - .iter() - .map(|&x| (x.to_f32() * alpha + offset) * (x.to_f32() * alpha + offset)) - .sum::() - .sqrt(); - (sum, l2_norm) -} - -#[inline] -#[cfg(target_arch = "x86_64")] -#[detect::target_cpu(enable = "v4_avx512vnni")] -unsafe fn dot_internal_v4_avx512vnni(x: &[I8], y: &[I8]) -> F32 { - use std::arch::x86_64::*; - assert_eq!(x.len(), y.len()); - let mut sum = 0; - let mut i = x.len(); - let mut p_x = x.as_ptr() as *const i8; - let mut p_y = y.as_ptr() as *const i8; - let mut vec_x; - let mut vec_y; - unsafe { - let mut result = _mm512_setzero_si512(); - let zero = _mm512_setzero_si512(); - while i > 0 { - if i < 64 { - let mask = _bzhi_u64(0xFFFF_FFFF_FFFF_FFFF, i as u32); - vec_x = _mm512_maskz_loadu_epi8(mask, p_x); - vec_y = _mm512_maskz_loadu_epi8(mask, p_y); - i = 0; - } else { - vec_x = _mm512_loadu_epi8(p_x); - vec_y = _mm512_loadu_epi8(p_y); - i -= 64; - p_x = p_x.add(64); - p_y = p_y.add(64); - } - // There are only _mm512_dpbusd_epi32 support, dpbusd will zeroextend a[i] and signextend b[i] first, so we need to convert a[i] positive and change corresponding b[i] to get right result. - // And because we use -b[i] here, the range of quantization should be [-127, 127] instead of [-128, 127] to avoid overflow. - let neg_mask = _mm512_movepi8_mask(vec_x); - vec_x = _mm512_mask_abs_epi8(vec_x, neg_mask, vec_x); - // Get -b[i] here, use saturating sub to avoid overflow. There are some precision loss here. - vec_y = _mm512_mask_subs_epi8(vec_y, neg_mask, zero, vec_y); - result = _mm512_dpbusd_epi32(result, vec_x, vec_y); - } - sum += _mm512_reduce_add_epi32(result); - } - F32(sum as f32) -} - -#[cfg(all(target_arch = "x86_64", test))] -#[test] -fn dot_internal_v4_avx512vnni_test() { - // A large epsilon is set for loss of precision caused by saturation arithmetic - const EPSILON: F32 = F32(512.0); - detect::init(); - if !detect::v4_avx512vnni::detect() { - println!("test {} ... skipped (v4_avx512vnni)", module_path!()); - return; - } - for _ in 0..300 { - let lhs = std::array::from_fn::<_, 400, _>(|_| I8(rand::random())); - let rhs = std::array::from_fn::<_, 400, _>(|_| I8(rand::random())); - let specialized = unsafe { dot_internal_v4_avx512vnni(&lhs, &rhs) }; - let fallback = unsafe { dot_internal_fallback(&lhs, &rhs) }; - assert!( - (specialized - fallback).abs() < EPSILON, - "specialized = {specialized}, fallback = {fallback}." - ); - } -} - -#[detect::multiversion(v4_avx512vnni = import, v4, v3, v2, neon, fallback = export)] -fn dot_internal(x: &[I8], y: &[I8]) -> F32 { - // i8 * i8 fall in range of i16. Since our length is less than (2^16 - 1), the result won't overflow. - let mut sum = 0; - assert_eq!(x.len(), y.len()); - let length = x.len(); - // according to https://godbolt.org/z/ff48vW4es, this loop will be autovectorized - for i in 0..length { - sum += (x[i].0 as i16 * y[i].0 as i16) as i32; - } - F32(sum as f32) -} - -pub fn dot(x: &Veci8Borrowed<'_>, y: &Veci8Borrowed<'_>) -> F32 { - // (alpha_x * x[i] + offset_x) * (alpha_y * y[i] + offset_y) - // = alpha_x * alpha_y * x[i] * y[i] + alpha_x * offset_y * x[i] + alpha_y * offset_x * y[i] + offset_x * offset_y - // Sum(dot(origin_x[i] , origin_y[i])) = alpha_x * alpha_y * Sum(dot(x[i], y[i])) + offset_y * Sum(alpha_x * x[i]) + offset_x * Sum(alpha_y * y[i]) + offset_x * offset_y * dims - let dot_xy = dot_internal(x.data(), y.data()); - x.alpha() * y.alpha() * dot_xy - + x.offset() * y.sum() - + y.offset() * x.sum() - + x.offset() * y.offset() * F32(x.dims() as f32) -} - -pub fn sl2(x: &Veci8Borrowed<'_>, y: &Veci8Borrowed<'_>) -> F32 { - // Sum(l2(origin_x[i] - origin_y[i])) = sum(x[i] ^ 2 - 2 * x[i] * y[i] + y[i] ^ 2) - // = dot(x, x) - 2 * dot(x, y) + dot(y, y) - x.l2_norm() * x.l2_norm() - F32(2.0) * dot(x, y) + y.l2_norm() * y.l2_norm() -} - -pub fn cosine(x: &Veci8Borrowed<'_>, y: &Veci8Borrowed<'_>) -> F32 { - // dot(x, y) / (l2(x) * l2(y)) - let dot_xy = dot(x, y); - let l2_x = x.l2_norm(); - let l2_y = y.l2_norm(); - dot_xy / (l2_x * l2_y) -} - -#[detect::multiversion(v4, v3, v2, neon, fallback)] -pub fn l2_2<'a>(lhs: Veci8Borrowed<'a>, rhs: &[F32]) -> F32 { - let data = lhs.data(); - assert_eq!(data.len(), rhs.len()); - data.iter() - .zip(rhs.iter()) - .map(|(&x, &y)| { - (x.to_f32() * lhs.alpha() + lhs.offset() - y) - * (x.to_f32() * lhs.alpha() + lhs.offset() - y) - }) - .sum::() -} - -#[detect::multiversion(v4, v3, v2, neon, fallback)] -pub fn dot_2<'a>(lhs: Veci8Borrowed<'a>, rhs: &[F32]) -> F32 { - let data = lhs.data(); - assert_eq!(data.len(), rhs.len()); - data.iter() - .zip(rhs.iter()) - .map(|(&x, &y)| (x.to_f32() * lhs.alpha() + lhs.offset()) * y) - .sum::() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_quantization_roundtrip() { - let vector = vec![F32(0.0), F32(1.0), F32(2.0), F32(3.0), F32(4.0)]; - let (result, alpha, offset) = i8_quantization(&vector); - assert_eq!(result, vec![I8(-127), I8(-63), I8(0), I8(63), I8(127)]); - assert_eq!(alpha, F32(4.0 / 254.0)); - assert_eq!(offset, F32(2.0)); - let vector = i8_dequantization(result.as_slice(), alpha, offset); - for (i, x) in vector.iter().enumerate() { - assert!((x.0 - (i as f32)).abs() < 0.05); - } - } - - fn new_random_vec_f32(size: usize) -> Vec { - use rand::Rng; - let mut rng = rand::thread_rng(); - (0..size) - .map(|_| F32(rng.gen_range(-100000.0..100000.0))) - .collect() - } - - fn vec_to_owned(vec: Vec) -> Veci8Owned { - let (data, alpha, offset) = i8_quantization(&vec); - Veci8Owned::new(data, alpha, offset) - } - - #[test] - fn test_dot_i8() { - let x = vec![F32(1.0), F32(2.0), F32(3.0)]; - let y = vec![F32(3.0), F32(2.0), F32(1.0)]; - let x_owned = vec_to_owned(x); - let ref_x = x_owned.as_borrowed(); - let y_owned = vec_to_owned(y); - let ref_y = y_owned.as_borrowed(); - let result = dot(&ref_x, &ref_y); - assert!((result.0 - 10.0).abs() < 0.1); - } - - #[test] - fn test_cos_i8() { - let x = vec![F32(1.0), F32(2.0), F32(3.0)]; - let y = vec![F32(3.0), F32(2.0), F32(1.0)]; - let x_owned = vec_to_owned(x); - let ref_x = x_owned.as_borrowed(); - let y_owned = vec_to_owned(y); - let ref_y = y_owned.as_borrowed(); - let result = cosine(&ref_x, &ref_y); - assert!((result.0 - (10.0 / 14.0)).abs() < 0.1); - // test cos_i8 using random generated data, check the precision - let x = new_random_vec_f32(1000); - let y = new_random_vec_f32(1000); - let xy = x.iter().zip(y.iter()).map(|(&x, &y)| x * y).sum::().0; - let l2_x = x.iter().map(|&x| x * x).sum::().0.sqrt(); - let l2_y = y.iter().map(|&y| y * y).sum::().0.sqrt(); - let result_expected = xy / (l2_x * l2_y); - let x_owned = vec_to_owned(x); - let ref_x = x_owned.as_borrowed(); - let y_owned = vec_to_owned(y); - let ref_y = y_owned.as_borrowed(); - let result = cosine(&ref_x, &ref_y); - assert!( - result_expected < 0.01 - || (result.0 - result_expected).abs() < 0.01 - || (result.0 - result_expected).abs() / result_expected < 0.1 - ); - } - - #[test] - fn test_l2_i8() { - let x = vec![F32(1.0), F32(2.0), F32(3.0)]; - let y = vec![F32(3.0), F32(2.0), F32(1.0)]; - let x_owned = vec_to_owned(x); - let ref_x = x_owned.as_borrowed(); - let y_owned = vec_to_owned(y); - let ref_y = y_owned.as_borrowed(); - let result = sl2(&ref_x, &ref_y); - assert!((result.0 - 8.0).abs() < 0.1); - // test l2_i8 using random generated data, check the precision - let x = new_random_vec_f32(1000); - let y = new_random_vec_f32(1000); - let result_expected = x - .iter() - .zip(y.iter()) - .map(|(&x, &y)| (x - y) * (x - y)) - .sum::() - .0; - let x_owned = vec_to_owned(x); - let ref_x = x_owned.as_borrowed(); - let y_owned = vec_to_owned(y); - let ref_y = y_owned.as_borrowed(); - let result = sl2(&ref_x, &ref_y); - assert!( - result_expected < 1.0 || (result.0 - result_expected).abs() / result_expected < 0.05 - ); - } -} diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 6532768ee..723e6b71f 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -15,7 +15,6 @@ fn vec_type_from_str(s: &str) -> Result { "Vecf16" => Ok(VectorKind::Vecf16), "SVecf32" => Ok(VectorKind::SVecf32), "BVecf32" => Ok(VectorKind::BVecf32), - "Veci8" => Ok(VectorKind::Veci8), _ => Err(ArgumentParseError), } } diff --git a/crates/ivf/src/ivf_naive.rs b/crates/ivf/src/ivf_naive.rs index 3793631ac..765c6a285 100644 --- a/crates/ivf/src/ivf_naive.rs +++ b/crates/ivf/src/ivf_naive.rs @@ -52,11 +52,7 @@ impl IvfNaive { opts: &'a SearchOptions, ) -> (Vec, Box + 'a>) { let lists = select( - { - let mut vector = vector.to_vec(); - O::elkan_k_means_normalize(&mut vector); - k_means_lookup_many(&vector, &self.centroids) - }, + k_means_lookup_many(&vector.to_vec(), &self.centroids), opts.ivf_nprobe as usize, ); let mut reranker = self.quantization.ivf_naive_rerank(vector, opts, move |u| { @@ -96,22 +92,11 @@ fn from_nothing( } = options.indexing.clone().unwrap_ivf(); let samples = common::sample::sample(collection); rayon::check(); - let centroids = { - let mut samples = samples; - for i in 0..samples.shape_0() { - O::elkan_k_means_normalize(&mut samples[(i,)]); - } - k_means(nlist as usize, samples) - }; + let centroids = k_means(nlist as usize, samples, O::spherical); rayon::check(); let mut ls = vec![Vec::new(); nlist as usize]; for i in 0..collection.len() { - ls[{ - let mut vector = collection.vector(i).to_vec(); - O::elkan_k_means_normalize(&mut vector); - k_means_lookup(&vector, ¢roids) - }] - .push(i); + ls[k_means_lookup(&collection.vector(i).to_vec(), ¢roids)].push(i); } let mut offsets = vec![0u32; nlist as usize + 1]; for i in 0..nlist { diff --git a/crates/ivf/src/ivf_residual.rs b/crates/ivf/src/ivf_residual.rs index b878d1fcd..5d83ac382 100644 --- a/crates/ivf/src/ivf_residual.rs +++ b/crates/ivf/src/ivf_residual.rs @@ -52,16 +52,12 @@ impl IvfResidual { opts: &'a SearchOptions, ) -> (Vec, Box + 'a>) { let lists = select( - { - let mut vector = vector.to_vec(); - O::elkan_k_means_normalize(&mut vector); - k_means_lookup_many(&vector, &self.centroids) - }, + k_means_lookup_many(&vector.to_vec(), &self.centroids), opts.ivf_nprobe as usize, ); let vectors = lists .iter() - .map(|&(_, i)| O::vector_sub(vector, &self.centroids[(i,)])) + .map(|&(_, i)| O::residual(vector, &self.centroids[(i,)])) .collect::>(); let mut reranker = self .quantization @@ -102,22 +98,11 @@ fn from_nothing( } = options.indexing.clone().unwrap_ivf(); let samples = common::sample::sample(collection); rayon::check(); - let centroids = { - let mut samples = samples; - for i in 0..samples.shape_0() { - O::elkan_k_means_normalize(&mut samples[(i,)]); - } - k_means(nlist as usize, samples) - }; + let centroids = k_means(nlist as usize, samples, |_| ()); rayon::check(); let mut ls = vec![Vec::new(); nlist as usize]; for i in 0..collection.len() { - ls[{ - let mut vector = collection.vector(i).to_vec(); - O::elkan_k_means_normalize(&mut vector); - k_means_lookup(&vector, ¢roids) - }] - .push(i); + ls[k_means_lookup(&collection.vector(i).to_vec(), ¢roids)].push(i); } let mut offsets = vec![0u32; nlist as usize + 1]; for i in 0..nlist { @@ -136,12 +121,8 @@ fn from_nothing( quantization_options, &collection, |vector| { - let target = { - let mut vector = vector.to_vec(); - O::elkan_k_means_normalize(&mut vector); - k_means_lookup(&vector, ¢roids) - }; - O::vector_sub(vector, ¢roids[(target,)]) + let target = k_means_lookup(&vector.to_vec(), ¢roids); + O::residual(vector, ¢roids[(target,)]) }, ); let payloads = MmapArray::create( diff --git a/crates/ivf/src/lib.rs b/crates/ivf/src/lib.rs index 78119899f..0fdfc3324 100644 --- a/crates/ivf/src/lib.rs +++ b/crates/ivf/src/lib.rs @@ -7,7 +7,6 @@ pub mod operator; use self::ivf_naive::IvfNaive; use crate::operator::OperatorIvf; -use base::distance::DistanceKind; use base::index::*; use base::operator::*; use base::search::*; @@ -27,21 +26,20 @@ impl Ivf { .. } = options.indexing.clone().unwrap_ivf(); std::fs::create_dir(path.as_ref()).unwrap(); - let this = if matches!(quantization_options, QuantizationOptions::Trivial(_)) - || O::DISTANCE_KIND != DistanceKind::L2 - { - Self::Naive(IvfNaive::create( - path.as_ref().join("ivf_naive"), - options, - source, - )) - } else { - Self::Residual(IvfResidual::create( - path.as_ref().join("ivf_residual"), - options, - source, - )) - }; + let this = + if matches!(quantization_options, QuantizationOptions::Trivial(_)) || !O::RESIDUAL { + Self::Naive(IvfNaive::create( + path.as_ref().join("ivf_naive"), + options, + source, + )) + } else { + Self::Residual(IvfResidual::create( + path.as_ref().join("ivf_residual"), + options, + source, + )) + }; this } diff --git a/crates/ivf/src/operator.rs b/crates/ivf/src/operator.rs index 23a5aaada..aaac37bac 100644 --- a/crates/ivf/src/operator.rs +++ b/crates/ivf/src/operator.rs @@ -7,18 +7,32 @@ use quantization::operator::OperatorQuantization; use storage::OperatorStorage; pub trait OperatorIvf: OperatorQuantization + OperatorStorage { - fn elkan_k_means_normalize(_: &mut [Scalar]) {} - fn vector_sub(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned; + const RESIDUAL: bool; + fn spherical(_: &mut [Scalar]); + fn residual(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned; } impl OperatorIvf for BVecf32Dot { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { + let n = vector.len(); + let mut dot = F32::zero(); + for i in 0..n { + dot += vector[i].to_f() * vector[i].to_f(); + } + let l = dot.sqrt(); + for i in 0..n { + vector[i] /= Scalar::::from_f(l); + } + } + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for BVecf32Cos { - fn elkan_k_means_normalize(vector: &mut [Scalar]) { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { let n = vector.len(); let mut dot = F32::zero(); for i in 0..n { @@ -29,31 +43,48 @@ impl OperatorIvf for BVecf32Cos { vector[i] /= Scalar::::from_f(l); } } - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for BVecf32Jaccard { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + const RESIDUAL: bool = false; + fn spherical(_: &mut [Scalar]) {} + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for BVecf32L2 { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + const RESIDUAL: bool = true; + fn spherical(_: &mut [Scalar]) {} + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for SVecf32Dot { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { + let n = vector.len(); + let mut dot = F32::zero(); + for i in 0..n { + dot += vector[i].to_f() * vector[i].to_f(); + } + let l = dot.sqrt(); + for i in 0..n { + vector[i] /= Scalar::::from_f(l); + } + } + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for SVecf32Cos { - fn elkan_k_means_normalize(vector: &mut [Scalar]) { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { let n = vector.len(); let mut dot = F32::zero(); for i in 0..n { @@ -64,13 +95,15 @@ impl OperatorIvf for SVecf32Cos { vector[i] /= Scalar::::from_f(l); } } - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for SVecf32L2 { - fn vector_sub(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned { + const RESIDUAL: bool = true; + fn spherical(_: &mut [Scalar]) {} + fn residual(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned { assert_eq!(lhs.dims() as usize, rhs.len()); let n = lhs.dims(); let mut indexes = Vec::new(); @@ -97,13 +130,26 @@ impl OperatorIvf for SVecf32L2 { } impl OperatorIvf for Vecf32Dot { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { + let n = vector.len(); + let mut dot = F32::zero(); + for i in 0..n { + dot += vector[i].to_f() * vector[i].to_f(); + } + let l = dot.sqrt(); + for i in 0..n { + vector[i] /= Scalar::::from_f(l); + } + } + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for Vecf32Cos { - fn elkan_k_means_normalize(vector: &mut [Scalar]) { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { let n = vector.len(); let mut dot = F32::zero(); for i in 0..n { @@ -114,25 +160,22 @@ impl OperatorIvf for Vecf32Cos { vector[i] /= Scalar::::from_f(l); } } - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } impl OperatorIvf for Vecf32L2 { - fn vector_sub(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned { + const RESIDUAL: bool = true; + fn spherical(_: &mut [Scalar]) {} + fn residual(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned { lhs.operator_minus(Vecf32Borrowed::new(rhs)) } } impl OperatorIvf for Vecf16Dot { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { - unimplemented!() - } -} - -impl OperatorIvf for Vecf16Cos { - fn elkan_k_means_normalize(vector: &mut [Scalar]) { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { let n = vector.len(); let mut dot = F32::zero(); for i in 0..n { @@ -143,25 +186,14 @@ impl OperatorIvf for Vecf16Cos { vector[i] /= Scalar::::from_f(l); } } - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { - unimplemented!() - } -} - -impl OperatorIvf for Vecf16L2 { - fn vector_sub(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned { - lhs.operator_minus(Vecf16Borrowed::new(rhs)) - } -} - -impl OperatorIvf for Veci8Dot { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } -impl OperatorIvf for Veci8Cos { - fn elkan_k_means_normalize(vector: &mut [Scalar]) { +impl OperatorIvf for Vecf16Cos { + const RESIDUAL: bool = false; + fn spherical(vector: &mut [Scalar]) { let n = vector.len(); let mut dot = F32::zero(); for i in 0..n { @@ -172,13 +204,15 @@ impl OperatorIvf for Veci8Cos { vector[i] /= Scalar::::from_f(l); } } - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { + fn residual(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { unimplemented!() } } -impl OperatorIvf for Veci8L2 { - fn vector_sub(_lhs: Borrowed<'_, Self>, _rhs: &[Scalar]) -> Owned { - unimplemented!() +impl OperatorIvf for Vecf16L2 { + const RESIDUAL: bool = true; + fn spherical(_: &mut [Scalar]) {} + fn residual(lhs: Borrowed<'_, Self>, rhs: &[Scalar]) -> Owned { + lhs.operator_minus(Vecf16Borrowed::new(rhs)) } } diff --git a/crates/k_means/src/elkan.rs b/crates/k_means/src/elkan.rs index 161d3b0b3..1647314a3 100644 --- a/crates/k_means/src/elkan.rs +++ b/crates/k_means/src/elkan.rs @@ -5,9 +5,10 @@ use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use std::ops::{Index, IndexMut}; -pub struct ElkanKMeans { +pub struct ElkanKMeans { dims: usize, c: usize, + spherical: F, centroids: Vec2, lowerbound: Square, upperbound: Vec, @@ -19,8 +20,8 @@ pub struct ElkanKMeans { const DELTA: f32 = 1.0 / 1024.0; -impl ElkanKMeans { - pub fn new(c: usize, samples: Vec2) -> Self { +impl ElkanKMeans { + pub fn new(c: usize, samples: Vec2, spherical: F) -> Self { let n = samples.shape_0(); let dims = samples.shape_1(); @@ -79,6 +80,7 @@ impl ElkanKMeans { Self { dims, c, + spherical, centroids, lowerbound, upperbound, @@ -203,7 +205,7 @@ impl ElkanKMeans { count[o] = count[o] - count[i]; } for i in 0..c { - spherical_normalize(&mut centroids[(i,)]); + (self.spherical)(&mut centroids[(i,)]); } // Step 5, 6 @@ -266,15 +268,3 @@ impl IndexMut<(usize, usize)> for Square { &mut self.v[x * self.y + y] } } - -fn spherical_normalize(vector: &mut [S]) { - let n = vector.len(); - let mut dot = F32::zero(); - for i in 0..n { - dot += vector[i].to_f() * vector[i].to_f(); - } - let l = dot.sqrt(); - for i in 0..n { - vector[i] /= S::from_f(l); - } -} diff --git a/crates/k_means/src/lib.rs b/crates/k_means/src/lib.rs index c158b168a..40fb74a24 100644 --- a/crates/k_means/src/lib.rs +++ b/crates/k_means/src/lib.rs @@ -12,18 +12,27 @@ use stoppable_rayon as rayon; const ITERATIONS: usize = 400; -pub fn k_means(c: usize, samples: Vec2) -> Vec2 { +pub fn k_means( + c: usize, + mut samples: Vec2, + mut spherical: F, +) -> Vec2 { assert!(c > 0); let n = samples.shape_0(); let dims = samples.shape_1(); assert!(dims > 0); + if dims > 1 { + for i in 0..n { + spherical(&mut samples[(i,)]); + } + } if n <= c { return quick_centers::quick_centers(c, samples); } if dims == 1 { return Vec2::from_vec((c, 1), kmeans1d(c, samples.as_slice())); } - let mut elkan_k_means = elkan::ElkanKMeans::::new(c, samples); + let mut elkan_k_means = elkan::ElkanKMeans::::new(c, samples, spherical); for _ in 0..ITERATIONS { rayon::check(); if elkan_k_means.iterate() { diff --git a/crates/quantization/src/operator.rs b/crates/quantization/src/operator.rs index db1da199f..da6f623c8 100644 --- a/crates/quantization/src/operator.rs +++ b/crates/quantization/src/operator.rs @@ -186,10 +186,6 @@ unimpl_operator_quantization_process!(SVecf32Cos, SVecf32L2); unimpl_operator_quantization_process!(SVecf32Dot, SVecf32L2); unimpl_operator_quantization_process!(SVecf32L2, SVecf32L2); -unimpl_operator_quantization_process!(Veci8Cos, Veci8L2); -unimpl_operator_quantization_process!(Veci8Dot, Veci8L2); -unimpl_operator_quantization_process!(Veci8L2, Veci8L2); - pub trait OperatorQuantization: OperatorQuantizationProcess + OperatorTrivialQuantization @@ -211,6 +207,3 @@ impl OperatorQuantization for Vecf16L2 {} impl OperatorQuantization for Vecf32Cos {} impl OperatorQuantization for Vecf32Dot {} impl OperatorQuantization for Vecf32L2 {} -impl OperatorQuantization for Veci8Cos {} -impl OperatorQuantization for Veci8Dot {} -impl OperatorQuantization for Veci8L2 {} diff --git a/crates/quantization/src/product/mod.rs b/crates/quantization/src/product/mod.rs index b723a0a81..62cb14c2b 100644 --- a/crates/quantization/src/product/mod.rs +++ b/crates/quantization/src/product/mod.rs @@ -44,7 +44,7 @@ impl ProductQuantizer { let start = (p * ratio) as usize; let end = start + subdims as usize; let subsamples = sample_subvector_transform(vectors, start, end, transform); - k_means(1 << bits, subsamples) + k_means(1 << bits, subsamples, |_| ()) }) .collect::>(); let mut centroids = Vec2::zeros((1 << bits, dims as usize)); @@ -84,7 +84,6 @@ impl ProductQuantizer { let target = k_means::k_means_lookup(left, &self.originals[p as usize]); codes.push(target as u8); } - codes.extend(std::iter::repeat(0).take((8 / self.bits) as usize - 1)); let bytes = (self.dims.div_ceil(self.ratio) * self.bits).div_ceil(8); let codes = codes.into_iter().chain(std::iter::repeat(0)); fn merge_8([b0, b1, b2, b3, b4, b5, b6, b7]: [u8; 8]) -> u8 { @@ -128,19 +127,19 @@ impl ProductQuantizer { } pub fn process(&self, preprocessed: &O::QuantizationPreprocessed, rhs: &[u8]) -> F32 { + let dims = self.dims; + let ratio = self.ratio; match self.bits { - 1 => O::quantization_process(self.dims, self.ratio, 1, preprocessed, |i| { - ((rhs[i >> 3] >> ((i & 7) << 1)) & 1) as usize - }), - 2 => O::quantization_process(self.dims, self.ratio, 2, preprocessed, |i| { - ((rhs[i >> 2] >> ((i & 3) << 2)) & 3) as usize + 1 => O::quantization_process(dims, ratio, 1, preprocessed, |i| { + ((rhs[i >> 3] >> ((i & 7) << 0)) & 1) as usize }), - 4 => O::quantization_process(self.dims, self.ratio, 4, preprocessed, |i| { - ((rhs[i >> 1] >> ((i & 1) << 4)) & 15) as usize + 2 => O::quantization_process(dims, ratio, 2, preprocessed, |i| { + ((rhs[i >> 2] >> ((i & 3) << 1)) & 3) as usize }), - 8 => O::quantization_process(self.dims, self.ratio, 8, preprocessed, |i| { - ((rhs[i >> 0] >> (0 << 8)) & 255) as usize + 4 => O::quantization_process(dims, ratio, 4, preprocessed, |i| { + ((rhs[i >> 1] >> ((i & 1) << 2)) & 15) as usize }), + 8 => O::quantization_process(dims, ratio, 8, preprocessed, |i| rhs[i] as usize), _ => unreachable!(), } } diff --git a/crates/quantization/src/product/operator.rs b/crates/quantization/src/product/operator.rs index d42900262..ceab56c95 100644 --- a/crates/quantization/src/product/operator.rs +++ b/crates/quantization/src/product/operator.rs @@ -224,7 +224,3 @@ unimpl_operator_product_quantization!(BVecf32Jaccard, BVecf32L2); unimpl_operator_product_quantization!(SVecf32Cos, SVecf32L2); unimpl_operator_product_quantization!(SVecf32Dot, SVecf32L2); unimpl_operator_product_quantization!(SVecf32L2, SVecf32L2); - -unimpl_operator_product_quantization!(Veci8Cos, Veci8L2); -unimpl_operator_product_quantization!(Veci8Dot, Veci8L2); -unimpl_operator_product_quantization!(Veci8L2, Veci8L2); diff --git a/crates/quantization/src/scalar/mod.rs b/crates/quantization/src/scalar/mod.rs index ceba9c06d..529b8fea3 100644 --- a/crates/quantization/src/scalar/mod.rs +++ b/crates/quantization/src/scalar/mod.rs @@ -112,19 +112,18 @@ impl ScalarQuantizer { } pub fn process(&self, preprocessed: &O::QuantizationPreprocessed, rhs: &[u8]) -> F32 { + let dims = self.dims; match self.bits { - 1 => O::quantization_process(self.dims, 1, 1, preprocessed, |i| { - ((rhs[i >> 3] >> ((i & 7) << 1)) & 1) as usize - }), - 2 => O::quantization_process(self.dims, 1, 2, preprocessed, |i| { - ((rhs[i >> 2] >> ((i & 3) << 2)) & 3) as usize + 1 => O::quantization_process(dims, 1, 1, preprocessed, |i| { + ((rhs[i >> 3] >> ((i & 7) << 0)) & 1) as usize }), - 4 => O::quantization_process(self.dims, 1, 4, preprocessed, |i| { - ((rhs[i >> 1] >> ((i & 1) << 4)) & 15) as usize + 2 => O::quantization_process(dims, 1, 2, preprocessed, |i| { + ((rhs[i >> 2] >> ((i & 3) << 1)) & 3) as usize }), - 8 => O::quantization_process(self.dims, 1, 8, preprocessed, |i| { - ((rhs[i >> 0] >> (0 << 8)) & 255) as usize + 4 => O::quantization_process(dims, 1, 4, preprocessed, |i| { + ((rhs[i >> 1] >> ((i & 1) << 2)) & 15) as usize }), + 8 => O::quantization_process(dims, 1, 8, preprocessed, |i| rhs[i] as usize), _ => unreachable!(), } } diff --git a/crates/quantization/src/scalar/operator.rs b/crates/quantization/src/scalar/operator.rs index aa748fa98..24d7a51c9 100644 --- a/crates/quantization/src/scalar/operator.rs +++ b/crates/quantization/src/scalar/operator.rs @@ -192,7 +192,3 @@ unimpl_operator_scalar_quantization!(BVecf32Jaccard, BVecf32L2); unimpl_operator_scalar_quantization!(SVecf32Cos, SVecf32L2); unimpl_operator_scalar_quantization!(SVecf32Dot, SVecf32L2); unimpl_operator_scalar_quantization!(SVecf32L2, SVecf32L2); - -unimpl_operator_scalar_quantization!(Veci8Cos, Veci8L2); -unimpl_operator_scalar_quantization!(Veci8Dot, Veci8L2); -unimpl_operator_scalar_quantization!(Veci8L2, Veci8L2); diff --git a/crates/service/src/instance.rs b/crates/service/src/instance.rs index fdade99e8..4f4558cd0 100644 --- a/crates/service/src/instance.rs +++ b/crates/service/src/instance.rs @@ -27,9 +27,6 @@ pub enum Instance { BVecf32Dot(Arc>), BVecf32L2(Arc>), BVecf32Jaccard(Arc>), - Veci8L2(Arc>), - Veci8Cos(Arc>), - Veci8Dot(Arc>), } impl Instance { @@ -91,18 +88,6 @@ impl Instance { let index = Index::create(path.clone(), options, alterable_options)?; Ok(Self::BVecf32Jaccard(index)) } - (DistanceKind::L2, VectorKind::Veci8) => { - let index = Index::create(path.clone(), options, alterable_options)?; - Ok(Self::Veci8L2(index)) - } - (DistanceKind::Cos, VectorKind::Veci8) => { - let index = Index::create(path.clone(), options, alterable_options)?; - Ok(Self::Veci8Cos(index)) - } - (DistanceKind::Dot, VectorKind::Veci8) => { - let index = Index::create(path.clone(), options, alterable_options)?; - Ok(Self::Veci8Dot(index)) - } (DistanceKind::Jaccard, _) => Err(CreateError::InvalidIndexOptions { reason: "Jaccard distance is only supported for BVecf32 vectors".to_string(), }), @@ -126,9 +111,6 @@ impl Instance { (DistanceKind::Dot, VectorKind::BVecf32) => Self::BVecf32Dot(Index::open(path)), (DistanceKind::L2, VectorKind::BVecf32) => Self::BVecf32L2(Index::open(path)), (DistanceKind::Jaccard, VectorKind::BVecf32) => Self::BVecf32Jaccard(Index::open(path)), - (DistanceKind::L2, VectorKind::Veci8) => Self::Veci8L2(Index::open(path)), - (DistanceKind::Cos, VectorKind::Veci8) => Self::Veci8Cos(Index::open(path)), - (DistanceKind::Dot, VectorKind::Veci8) => Self::Veci8Dot(Index::open(path)), _ => unreachable!(), } } @@ -147,9 +129,6 @@ impl Instance { Instance::BVecf32Dot(x) => x.refresh(), Instance::BVecf32L2(x) => x.refresh(), Instance::BVecf32Jaccard(x) => x.refresh(), - Instance::Veci8L2(x) => x.refresh(), - Instance::Veci8Cos(x) => x.refresh(), - Instance::Veci8Dot(x) => x.refresh(), } } pub fn view(&self) -> InstanceView { @@ -167,9 +146,6 @@ impl Instance { Instance::BVecf32Dot(x) => InstanceView::BVecf32Dot(x.view()), Instance::BVecf32L2(x) => InstanceView::BVecf32L2(x.view()), Instance::BVecf32Jaccard(x) => InstanceView::BVecf32Jaccard(x.view()), - Instance::Veci8L2(x) => InstanceView::Veci8L2(x.view()), - Instance::Veci8Cos(x) => InstanceView::Veci8Cos(x.view()), - Instance::Veci8Dot(x) => InstanceView::Veci8Dot(x.view()), } } pub fn stat(&self) -> IndexStat { @@ -187,9 +163,6 @@ impl Instance { Instance::BVecf32Dot(x) => x.stat(), Instance::BVecf32L2(x) => x.stat(), Instance::BVecf32Jaccard(x) => x.stat(), - Instance::Veci8L2(x) => x.stat(), - Instance::Veci8Cos(x) => x.stat(), - Instance::Veci8Dot(x) => x.stat(), } } pub fn alter(&self, key: &str, value: &str) -> Result<(), AlterError> { @@ -207,9 +180,6 @@ impl Instance { Instance::BVecf32Dot(x) => x.alter(key, value), Instance::BVecf32L2(x) => x.alter(key, value), Instance::BVecf32Jaccard(x) => x.alter(key, value), - Instance::Veci8L2(x) => x.alter(key, value), - Instance::Veci8Cos(x) => x.alter(key, value), - Instance::Veci8Dot(x) => x.alter(key, value), } } pub fn delete(&self, pointer: Pointer) -> Result<(), DeleteError> { @@ -227,9 +197,6 @@ impl Instance { Instance::BVecf32Dot(x) => x.delete(pointer), Instance::BVecf32L2(x) => x.delete(pointer), Instance::BVecf32Jaccard(x) => x.delete(pointer), - Instance::Veci8Cos(x) => x.delete(pointer), - Instance::Veci8Dot(x) => x.delete(pointer), - Instance::Veci8L2(x) => x.delete(pointer), } } pub fn start(&self) { @@ -247,9 +214,6 @@ impl Instance { Instance::BVecf32Dot(x) => x.start(), Instance::BVecf32L2(x) => x.start(), Instance::BVecf32Jaccard(x) => x.start(), - Instance::Veci8Cos(x) => x.start(), - Instance::Veci8Dot(x) => x.start(), - Instance::Veci8L2(x) => x.start(), } } pub fn stop(&self) { @@ -267,9 +231,6 @@ impl Instance { Instance::BVecf32Dot(x) => x.stop(), Instance::BVecf32L2(x) => x.stop(), Instance::BVecf32Jaccard(x) => x.stop(), - Instance::Veci8Cos(x) => x.stop(), - Instance::Veci8Dot(x) => x.stop(), - Instance::Veci8L2(x) => x.stop(), } } pub fn wait(&self) -> Arc { @@ -287,9 +248,6 @@ impl Instance { Instance::BVecf32Dot(x) => x.wait(), Instance::BVecf32L2(x) => x.wait(), Instance::BVecf32Jaccard(x) => x.wait(), - Instance::Veci8Cos(x) => x.wait(), - Instance::Veci8Dot(x) => x.wait(), - Instance::Veci8L2(x) => x.wait(), } } } @@ -308,9 +266,6 @@ pub enum InstanceView { BVecf32Dot(Arc>), BVecf32L2(Arc>), BVecf32Jaccard(Arc>), - Veci8Cos(Arc>), - Veci8Dot(Arc>), - Veci8L2(Arc>), } impl ViewVbaseOperations for InstanceView { @@ -360,15 +315,6 @@ impl ViewVbaseOperations for InstanceView { (InstanceView::BVecf32Jaccard(x), OwnedVector::BVecf32(vector)) => { Ok(Box::new(x.vbase(vector.as_borrowed(), opts)?)) } - (InstanceView::Veci8Cos(x), OwnedVector::Veci8(vector)) => { - Ok(Box::new(x.vbase(vector.as_borrowed(), opts)?)) - } - (InstanceView::Veci8Dot(x), OwnedVector::Veci8(vector)) => { - Ok(Box::new(x.vbase(vector.as_borrowed(), opts)?)) - } - (InstanceView::Veci8L2(x), OwnedVector::Veci8(vector)) => { - Ok(Box::new(x.vbase(vector.as_borrowed(), opts)?)) - } _ => Err(VbaseError::InvalidVector), } } @@ -392,9 +338,6 @@ impl ViewListOperations for InstanceView { InstanceView::BVecf32Dot(x) => Ok(Box::new(x.list()?)), InstanceView::BVecf32L2(x) => Ok(Box::new(x.list()?)), InstanceView::BVecf32Jaccard(x) => Ok(Box::new(x.list()?)), - InstanceView::Veci8Cos(x) => Ok(Box::new(x.list()?)), - InstanceView::Veci8Dot(x) => Ok(Box::new(x.list()?)), - InstanceView::Veci8L2(x) => Ok(Box::new(x.list()?)), } } } @@ -429,9 +372,6 @@ impl InstanceView { (InstanceView::BVecf32Jaccard(x), OwnedVector::BVecf32(vector)) => { x.insert(vector, pointer) } - (InstanceView::Veci8Cos(x), OwnedVector::Veci8(vector)) => x.insert(vector, pointer), - (InstanceView::Veci8Dot(x), OwnedVector::Veci8(vector)) => x.insert(vector, pointer), - (InstanceView::Veci8L2(x), OwnedVector::Veci8(vector)) => x.insert(vector, pointer), _ => Err(InsertError::InvalidVector), } } @@ -450,9 +390,6 @@ impl InstanceView { InstanceView::BVecf32Dot(x) => x.flush(), InstanceView::BVecf32L2(x) => x.flush(), InstanceView::BVecf32Jaccard(x) => x.flush(), - InstanceView::Veci8Cos(x) => x.flush(), - InstanceView::Veci8Dot(x) => x.flush(), - InstanceView::Veci8L2(x) => x.flush(), } } } diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index 3d7e69c1c..74bd96b52 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -1,7 +1,6 @@ mod bvector; mod svec; mod vec; -mod veci8; use base::operator::*; use base::scalar::*; @@ -68,15 +67,3 @@ impl OperatorStorage for BVecf32L2 { impl OperatorStorage for BVecf32Jaccard { type Storage = bvector::BVectorStorage; } - -impl OperatorStorage for Veci8Cos { - type Storage = veci8::Veci8Storage; -} - -impl OperatorStorage for Veci8Dot { - type Storage = veci8::Veci8Storage; -} - -impl OperatorStorage for Veci8L2 { - type Storage = veci8::Veci8Storage; -} diff --git a/crates/storage/src/operator.rs b/crates/storage/src/operator.rs deleted file mode 100644 index 9a31ecc1b..000000000 --- a/crates/storage/src/operator.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::bvector::BVectorStorage; -use super::svec::SVecStorage; -use super::vec::VecStorage; -use super::veci8::Veci8Storage; -use crate::Storage; -use base::operator::*; -use base::scalar::*; - -pub trait OperatorStorage: Operator { - type Storage: Storage + Send + Sync; -} - -impl OperatorStorage for SVecf32Cos { - type Storage = SVecStorage; -} - -impl OperatorStorage for SVecf32Dot { - type Storage = SVecStorage; -} - -impl OperatorStorage for SVecf32L2 { - type Storage = SVecStorage; -} - -impl OperatorStorage for Vecf16Cos { - type Storage = VecStorage; -} - -impl OperatorStorage for Vecf16Dot { - type Storage = VecStorage; -} - -impl OperatorStorage for Vecf16L2 { - type Storage = VecStorage; -} - -impl OperatorStorage for Vecf32Cos { - type Storage = VecStorage; -} - -impl OperatorStorage for Vecf32Dot { - type Storage = VecStorage; -} - -impl OperatorStorage for Vecf32L2 { - type Storage = VecStorage; -} - -impl OperatorStorage for BVecf32Cos { - type Storage = BVectorStorage; -} - -impl OperatorStorage for BVecf32Dot { - type Storage = BVectorStorage; -} - -impl OperatorStorage for BVecf32L2 { - type Storage = BVectorStorage; -} - -impl OperatorStorage for BVecf32Jaccard { - type Storage = BVectorStorage; -} - -impl OperatorStorage for Veci8Cos { - type Storage = Veci8Storage; -} - -impl OperatorStorage for Veci8Dot { - type Storage = Veci8Storage; -} - -impl OperatorStorage for Veci8L2 { - type Storage = Veci8Storage; -} diff --git a/crates/storage/src/veci8.rs b/crates/storage/src/veci8.rs deleted file mode 100644 index b70d4ef0e..000000000 --- a/crates/storage/src/veci8.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::Storage; -use base::operator::Operator; -use base::scalar::*; -use base::search::*; -use base::vector::*; -use common::json::Json; -use common::mmap_array::MmapArray; -use std::path::Path; - -pub struct Veci8Storage { - dims: Json, - len: Json, - slice: MmapArray, - alphas: MmapArray, - offsets: MmapArray, - sums: MmapArray, - l2_norms: MmapArray, -} - -impl> Vectors for Veci8Storage { - fn dims(&self) -> u32 { - *self.dims - } - - fn len(&self) -> u32 { - *self.len - } - - fn vector(&self, i: u32) -> Veci8Borrowed<'_> { - let s = i as usize * *self.dims as usize; - let e = (i + 1) as usize * *self.dims as usize; - unsafe { - Veci8Borrowed::new_unchecked( - &self.slice[s..e], - self.alphas[i as usize], - self.offsets[i as usize], - self.sums[i as usize], - self.l2_norms[i as usize], - ) - } - } -} - -impl> Storage for Veci8Storage { - fn create(path: impl AsRef, vectors: &impl Vectors) -> Self { - std::fs::create_dir(path.as_ref()).unwrap(); - let dims = Json::create(path.as_ref().join("dims"), vectors.dims()); - let len = Json::create(path.as_ref().join("len"), vectors.len()); - let slice = MmapArray::create( - path.as_ref().join("slice"), - (0..*len).flat_map(|i| vectors.vector(i).data().to_vec()), - ); - let alphas = MmapArray::create( - path.as_ref().join("alphas"), - (0..*len).map(|i| vectors.vector(i).alpha()), - ); - let offsets = MmapArray::create( - path.as_ref().join("offsets"), - (0..*len).map(|i| vectors.vector(i).offset()), - ); - let sums = MmapArray::create( - path.as_ref().join("sums"), - (0..*len).map(|i| vectors.vector(i).sum()), - ); - let l2_norms = MmapArray::create( - path.as_ref().join("l2_norms"), - (0..*len).map(|i| vectors.vector(i).l2_norm()), - ); - Self { - dims, - len, - slice, - alphas, - offsets, - sums, - l2_norms, - } - } - - fn open(path: impl AsRef) -> Self { - let dims = Json::open(path.as_ref().join("dims")); - let len = Json::open(path.as_ref().join("len")); - let slice = MmapArray::open(path.as_ref().join("slice")); - let alphas = MmapArray::open(path.as_ref().join("alphas")); - let offsets = MmapArray::open(path.as_ref().join("offsets")); - let sums = MmapArray::open(path.as_ref().join("sums")); - let l2_norms = MmapArray::open(path.as_ref().join("l2_norms")); - Self { - dims, - len, - slice, - alphas, - offsets, - sums, - l2_norms, - } - } -} diff --git a/src/datatype/binary_veci8.rs b/src/datatype/binary_veci8.rs deleted file mode 100644 index 2f55251c9..000000000 --- a/src/datatype/binary_veci8.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::binary::Bytea; -use super::memory_veci8::{Veci8Input, Veci8Output}; -use base::scalar::{F32, I8}; -use base::vector::Veci8Borrowed; -use pgrx::datum::Internal; -use pgrx::datum::IntoDatum; -use pgrx::pg_sys::Oid; -use std::ffi::c_char; - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_send(vector: Veci8Input<'_>) -> Bytea { - use pgrx::pg_sys::StringInfoData; - unsafe { - let mut buf = StringInfoData::default(); - let dims = vector.dims(); - let alpha = vector.alpha(); - let offset = vector.offset(); - let sum = vector.sum(); - let l2_norm = vector.l2_norm(); - let bytes = std::mem::size_of::() * dims as usize; - pgrx::pg_sys::pq_begintypsend(&mut buf); - pgrx::pg_sys::pq_sendbytes(&mut buf, (&dims) as *const u32 as _, 4); - pgrx::pg_sys::pq_sendbytes(&mut buf, (&alpha) as *const F32 as _, 4); - pgrx::pg_sys::pq_sendbytes(&mut buf, (&offset) as *const F32 as _, 4); - pgrx::pg_sys::pq_sendbytes(&mut buf, (&sum) as *const F32 as _, 4); - pgrx::pg_sys::pq_sendbytes(&mut buf, (&l2_norm) as *const F32 as _, 4); - pgrx::pg_sys::pq_sendbytes(&mut buf, vector.data().as_ptr() as _, bytes as _); - Bytea::new(pgrx::pg_sys::pq_endtypsend(&mut buf)) - } -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_recv(internal: Internal, oid: Oid, typmod: i32) -> Veci8Output { - let _ = (oid, typmod); - use pgrx::pg_sys::StringInfo; - unsafe { - let buf: StringInfo = internal.into_datum().unwrap().cast_mut_ptr(); - let dims = (pgrx::pg_sys::pq_getmsgbytes(buf, 4) as *const u32).read_unaligned(); - let alpha = (pgrx::pg_sys::pq_getmsgbytes(buf, 4) as *const F32).read_unaligned(); - let offset = (pgrx::pg_sys::pq_getmsgbytes(buf, 4) as *const F32).read_unaligned(); - let _sum = (pgrx::pg_sys::pq_getmsgbytes(buf, 4) as *const F32).read_unaligned(); - let _l2_norm = (pgrx::pg_sys::pq_getmsgbytes(buf, 4) as *const F32).read_unaligned(); - let bytes = std::mem::size_of::() * dims as usize; - let ptr = pgrx::pg_sys::pq_getmsgbytes(buf, bytes as _); - let mut slice = Vec::::with_capacity(dims as usize); - std::ptr::copy(ptr, slice.as_mut_ptr().cast::(), bytes); - slice.set_len(dims as usize); - if let Some(x) = Veci8Borrowed::new_checked(&slice, alpha, offset) { - Veci8Output::new(x) - } else { - pgrx::error!("detect data corruption"); - } - } -} diff --git a/src/datatype/casts.rs b/src/datatype/casts.rs index 43a5bd47e..53d834135 100644 --- a/src/datatype/casts.rs +++ b/src/datatype/casts.rs @@ -2,7 +2,6 @@ use crate::datatype::memory_bvecf32::{BVecf32Input, BVecf32Output}; use crate::datatype::memory_svecf32::{SVecf32Input, SVecf32Output}; use crate::datatype::memory_vecf16::{Vecf16Input, Vecf16Output}; use crate::datatype::memory_vecf32::{Vecf32Input, Vecf32Output}; -use crate::datatype::memory_veci8::{Veci8Input, Veci8Output}; use crate::error::*; use base::scalar::*; use base::vector::*; @@ -113,25 +112,3 @@ fn _vectors_cast_bvecf32_to_vecf32( .collect(); Vecf32Output::new(Vecf32Borrowed::new(&data)) } - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_cast_veci8_to_vecf32( - vector: Veci8Input<'_>, - _typmod: i32, - _explicit: bool, -) -> Vecf32Output { - let data = (0..vector.dims()) - .map(|i| vector.get(i)) - .collect::>(); - Vecf32Output::new(Vecf32Borrowed::new(&data)) -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_cast_vecf32_to_veci8( - vector: Vecf32Input<'_>, - _typmod: i32, - _explicit: bool, -) -> Veci8Output { - let (data, alpha, offset) = veci8::i8_quantization(vector.slice()); - Veci8Output::new(Veci8Borrowed::new(&data, alpha, offset)) -} diff --git a/src/datatype/functions_veci8.rs b/src/datatype/functions_veci8.rs deleted file mode 100644 index 6e651996f..000000000 --- a/src/datatype/functions_veci8.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::datatype::memory_veci8::*; -use crate::error::*; -use base::scalar::*; -use base::vector::*; - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_dims(vector: Veci8Input<'_>) -> i32 { - vector.as_borrowed().dims() as i32 -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_norm(vector: Veci8Input<'_>) -> f32 { - vector.as_borrowed().length().to_f32() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_normalize(vector: Veci8Input<'_>) -> Veci8Output { - Veci8Output::new(vector.as_borrowed().function_normalize().as_borrowed()) -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_to_veci8(len: i32, alpha: f32, offset: f32, values: pgrx::Array) -> Veci8Output { - check_value_dims_65535(len as u32); - if (len as usize) != values.len() { - bad_literal("Lengths of values and len are not matched."); - } - if values.contains_nulls() { - bad_literal("Index or value contains nulls."); - } - let data = values - .iter() - .map(|x| I8(x.unwrap() as i8)) - .collect::>(); - let alpha = F32(alpha); - let offset = F32(offset); - Veci8Output::new(Veci8Borrowed::new(&data, alpha, offset)) -} diff --git a/src/datatype/memory_veci8.rs b/src/datatype/memory_veci8.rs deleted file mode 100644 index cf170700d..000000000 --- a/src/datatype/memory_veci8.rs +++ /dev/null @@ -1,271 +0,0 @@ -use base::scalar::*; -use base::vector::*; -use pgrx::pg_sys::Datum; -use pgrx::pg_sys::Oid; -use pgrx::pgrx_sql_entity_graph::metadata::ArgumentError; -use pgrx::pgrx_sql_entity_graph::metadata::Returns; -use pgrx::pgrx_sql_entity_graph::metadata::ReturnsError; -use pgrx::pgrx_sql_entity_graph::metadata::SqlMapping; -use pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable; -use pgrx::FromDatum; -use pgrx::IntoDatum; -use pgrx::UnboxDatum; -use std::alloc::Layout; -use std::ops::Deref; -use std::ops::DerefMut; -use std::ptr::NonNull; - -pub const HEADER_MAGIC: u16 = 4; - -#[repr(C, align(8))] -pub struct Veci8Header { - varlena: u32, - dims: u16, - magic: u16, - alpha: F32, - offset: F32, - sum: F32, - l2_norm: F32, - phantom: [I8; 0], -} - -impl Veci8Header { - fn varlena(size: usize) -> u32 { - // varattrib_4b type with compress is not set - (size << 2) as u32 - } - - fn layout(len: usize) -> Layout { - u16::try_from(len).expect("Vector is too large."); - // size of struct VecI8 - let layout_alpha = Layout::new::(); - // size of data in VecI8 - let layout_beta = Layout::array::(len).unwrap(); - let layout = layout_alpha.extend(layout_beta).unwrap().0; - layout.pad_to_align() - } - - pub fn dims(&self) -> u32 { - self.dims as u32 - } - - pub fn alpha(&self) -> F32 { - self.alpha - } - - pub fn offset(&self) -> F32 { - self.offset - } - - pub fn sum(&self) -> F32 { - self.sum - } - - pub fn l2_norm(&self) -> F32 { - self.l2_norm - } - - pub fn dequantization(&self) -> Vec { - self.data() - .iter() - .map(|&x| x.to_f32() * self.alpha() + self.offset()) - .collect() - } - - /// return value after dequantization by index - /// since `index` return &Output, we can't create a new Output and return it as a reference, so we need to use this function to return a new Output directly - #[inline(always)] - pub fn get(&self, index: u32) -> F32 { - self.data()[index as usize].to_f32() * self.alpha + self.offset - } - - pub fn data(&self) -> &[I8] { - unsafe { std::slice::from_raw_parts(self.phantom.as_ptr(), self.dims as usize) } - } - - pub fn as_borrowed(&self) -> Veci8Borrowed<'_> { - unsafe { - Veci8Borrowed::new_unchecked( - self.data(), - self.alpha, - self.offset, - self.sum, - self.l2_norm, - ) - } - } -} - -pub enum Veci8Input<'a> { - Owned(Veci8Output), - Borrowed(&'a Veci8Header), -} - -impl<'a> Veci8Input<'a> { - pub unsafe fn new(p: NonNull) -> Self { - let q = unsafe { - NonNull::new(pgrx::pg_sys::pg_detoast_datum(p.cast().as_ptr()).cast()).unwrap() - }; - if p != q { - Veci8Input::Owned(Veci8Output(q)) - } else { - unsafe { Veci8Input::Borrowed(p.as_ref()) } - } - } -} - -impl Deref for Veci8Input<'_> { - type Target = Veci8Header; - - fn deref(&self) -> &Self::Target { - match self { - Veci8Input::Owned(x) => x, - Veci8Input::Borrowed(x) => x, - } - } -} - -pub struct Veci8Output(NonNull); - -impl Veci8Output { - pub fn new(vector: Veci8Borrowed<'_>) -> Veci8Output { - unsafe { - let slice = vector.data(); - let layout = Veci8Header::layout(slice.len()); - let dims = vector.dims(); - let internal_dims = dims as u16; - let ptr = pgrx::pg_sys::palloc(layout.size()) as *mut Veci8Header; - ptr.cast::().add(layout.size() - 8).write_bytes(0, 8); - std::ptr::addr_of_mut!((*ptr).varlena).write(Veci8Header::varlena(layout.size())); - std::ptr::addr_of_mut!((*ptr).dims).write(internal_dims); - std::ptr::addr_of_mut!((*ptr).magic).write(HEADER_MAGIC); - std::ptr::addr_of_mut!((*ptr).alpha).write(vector.alpha()); - std::ptr::addr_of_mut!((*ptr).offset).write(vector.offset()); - std::ptr::addr_of_mut!((*ptr).sum).write(vector.sum()); - std::ptr::addr_of_mut!((*ptr).l2_norm).write(vector.l2_norm()); - std::ptr::copy_nonoverlapping(slice.as_ptr(), (*ptr).phantom.as_mut_ptr(), slice.len()); - Veci8Output(NonNull::new(ptr).unwrap()) - } - } - - pub fn into_raw(self) -> *mut Veci8Header { - let result = self.0.as_ptr(); - std::mem::forget(self); - result - } -} - -impl Deref for Veci8Output { - type Target = Veci8Header; - - fn deref(&self) -> &Self::Target { - unsafe { self.0.as_ref() } - } -} - -impl DerefMut for Veci8Output { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { self.0.as_mut() } - } -} - -impl Drop for Veci8Output { - fn drop(&mut self) { - unsafe { - pgrx::pg_sys::pfree(self.0.as_ptr() as _); - } - } -} - -impl<'a> FromDatum for Veci8Input<'a> { - unsafe fn from_polymorphic_datum(datum: Datum, is_null: bool, _typoid: Oid) -> Option { - if is_null { - None - } else { - let ptr = NonNull::new(datum.cast_mut_ptr::()).unwrap(); - unsafe { Some(Veci8Input::new(ptr)) } - } - } -} - -impl IntoDatum for Veci8Output { - fn into_datum(self) -> Option { - Some(Datum::from(self.into_raw() as *mut ())) - } - - fn type_oid() -> Oid { - let namespace = - pgrx::pg_catalog::PgNamespace::search_namespacename(crate::SCHEMA_C_STR).unwrap(); - let namespace = namespace.get().expect("pgvecto.rs is not installed."); - let t = pgrx::pg_catalog::PgType::search_typenamensp(c"veci8", namespace.oid()).unwrap(); - let t = t.get().expect("pg_catalog is broken."); - t.oid() - } -} - -impl FromDatum for Veci8Output { - unsafe fn from_polymorphic_datum(datum: Datum, is_null: bool, _typoid: Oid) -> Option { - if is_null { - None - } else { - let p = NonNull::new(datum.cast_mut_ptr::())?; - let q = - unsafe { NonNull::new(pgrx::pg_sys::pg_detoast_datum(p.cast().as_ptr()).cast())? }; - if p != q { - Some(Veci8Output(q)) - } else { - let header = p.as_ptr(); - let vector = unsafe { (*header).as_borrowed() }; - Some(Veci8Output::new(vector)) - } - } - } -} - -unsafe impl UnboxDatum for Veci8Output { - type As<'src> = Veci8Output; - #[inline] - unsafe fn unbox<'src>(d: pgrx::Datum<'src>) -> Self::As<'src> - where - Self: 'src, - { - let p = NonNull::new(d.sans_lifetime().cast_mut_ptr::()).unwrap(); - let q = unsafe { - NonNull::new(pgrx::pg_sys::pg_detoast_datum(p.cast().as_ptr()).cast()).unwrap() - }; - if p != q { - Veci8Output(q) - } else { - let header = p.as_ptr(); - let vector = unsafe { (*header).as_borrowed() }; - Veci8Output::new(vector) - } - } -} - -unsafe impl SqlTranslatable for Veci8Input<'_> { - fn argument_sql() -> Result { - Ok(SqlMapping::As(String::from("veci8"))) - } - - fn return_sql() -> Result { - Ok(Returns::One(SqlMapping::As(String::from("veci8")))) - } -} - -unsafe impl SqlTranslatable for Veci8Output { - fn argument_sql() -> Result { - Ok(SqlMapping::As(String::from("veci8"))) - } - - fn return_sql() -> Result { - Ok(Returns::One(SqlMapping::As(String::from("veci8")))) - } -} - -unsafe impl pgrx::callconv::BoxRet for Veci8Output { - unsafe fn box_in_fcinfo(self, fcinfo: pgrx::pg_sys::FunctionCallInfo) -> Datum { - self.into_datum() - .unwrap_or_else(|| unsafe { pgrx::fcinfo::pg_return_null(fcinfo) }) - } -} diff --git a/src/datatype/mod.rs b/src/datatype/mod.rs index a62c7ee07..9149b7ab1 100644 --- a/src/datatype/mod.rs +++ b/src/datatype/mod.rs @@ -5,31 +5,25 @@ pub mod binary_bvecf32; pub mod binary_svecf32; pub mod binary_vecf16; pub mod binary_vecf32; -pub mod binary_veci8; pub mod casts; pub mod functions_bvecf32; pub mod functions_svecf32; pub mod functions_vecf16; pub mod functions_vecf32; -pub mod functions_veci8; pub mod memory_bvecf32; pub mod memory_svecf32; pub mod memory_vecf16; pub mod memory_vecf32; -pub mod memory_veci8; pub mod operators_bvecf32; pub mod operators_svecf32; pub mod operators_vecf16; pub mod operators_vecf32; -pub mod operators_veci8; pub mod subscript_bvecf32; pub mod subscript_svecf32; pub mod subscript_vecf16; pub mod subscript_vecf32; -pub mod subscript_veci8; pub mod text_bvecf32; pub mod text_svecf32; pub mod text_vecf16; pub mod text_vecf32; -pub mod text_veci8; pub mod typmod; diff --git a/src/datatype/operators_veci8.rs b/src/datatype/operators_veci8.rs deleted file mode 100644 index 24490b83e..000000000 --- a/src/datatype/operators_veci8.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::datatype::memory_veci8::{Veci8Input, Veci8Output}; -use crate::error::*; -use base::operator::*; -use base::scalar::*; -use base::vector::*; -use std::num::NonZero; -use std::ops::Deref; - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_add(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> Veci8Output { - check_matched_dims(lhs.dims(), rhs.dims()); - Veci8Output::new( - lhs.as_borrowed() - .operator_add(rhs.as_borrowed()) - .as_borrowed(), - ) -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_minus(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> Veci8Output { - check_matched_dims(lhs.dims(), rhs.dims()); - Veci8Output::new( - lhs.as_borrowed() - .operator_minus(rhs.as_borrowed()) - .as_borrowed(), - ) -} - -/// Calculate the element-wise multiplication of two i8 vectors. -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_mul(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> Veci8Output { - check_matched_dims(lhs.dims(), rhs.dims()); - Veci8Output::new( - lhs.as_borrowed() - .operator_mul(rhs.as_borrowed()) - .as_borrowed(), - ) -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_lt(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> bool { - check_matched_dims(lhs.dims(), rhs.dims()); - lhs.deref().as_borrowed() < rhs.deref().as_borrowed() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_lte(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> bool { - check_matched_dims(lhs.dims(), rhs.dims()); - lhs.deref().as_borrowed() <= rhs.deref().as_borrowed() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_gt(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> bool { - check_matched_dims(lhs.dims(), rhs.dims()); - lhs.deref().as_borrowed() > rhs.deref().as_borrowed() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_gte(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> bool { - check_matched_dims(lhs.dims(), rhs.dims()); - lhs.deref().as_borrowed() >= rhs.deref().as_borrowed() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_eq(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> bool { - check_matched_dims(lhs.dims(), rhs.dims()); - lhs.deref().as_borrowed() == rhs.deref().as_borrowed() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_neq(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> bool { - check_matched_dims(lhs.dims(), rhs.dims()); - lhs.deref().dequantization().as_slice() != rhs.deref().dequantization().as_slice() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_cosine(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> f32 { - check_matched_dims(lhs.dims(), rhs.dims()); - Veci8Cos::distance(lhs.as_borrowed(), rhs.as_borrowed()).to_f32() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_dot(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> f32 { - check_matched_dims(lhs.dims(), rhs.dims()); - Veci8Dot::distance(lhs.as_borrowed(), rhs.as_borrowed()).to_f32() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_operator_l2(lhs: Veci8Input<'_>, rhs: Veci8Input<'_>) -> f32 { - check_matched_dims(lhs.dims(), rhs.dims()); - Veci8L2::distance(lhs.as_borrowed(), rhs.as_borrowed()).to_f32() -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_sphere_l2_in( - lhs: Veci8Input<'_>, - rhs: pgrx::composite_type!("sphere_veci8"), -) -> bool { - let center: Veci8Output = match rhs.get_by_index(NonZero::new(1).unwrap()) { - Ok(Some(s)) => s, - Ok(None) => pgrx::error!("Bad input: empty center at sphere"), - Err(_) => unreachable!(), - }; - check_matched_dims(lhs.dims(), center.dims()); - let radius: f32 = match rhs.get_by_index(NonZero::new(2).unwrap()) { - Ok(Some(s)) => s, - Ok(None) => pgrx::error!("Bad input: empty radius at sphere"), - Err(_) => unreachable!(), - }; - Veci8L2::distance(lhs.as_borrowed(), center.as_borrowed()) < F32(radius) -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_sphere_dot_in( - lhs: Veci8Input<'_>, - rhs: pgrx::composite_type!("sphere_veci8"), -) -> bool { - let center: Veci8Output = match rhs.get_by_index(NonZero::new(1).unwrap()) { - Ok(Some(s)) => s, - Ok(None) => pgrx::error!("Bad input: empty center at sphere"), - Err(_) => unreachable!(), - }; - check_matched_dims(lhs.dims(), center.dims()); - let radius: f32 = match rhs.get_by_index(NonZero::new(2).unwrap()) { - Ok(Some(s)) => s, - Ok(None) => pgrx::error!("Bad input: empty radius at sphere"), - Err(_) => unreachable!(), - }; - Veci8Dot::distance(lhs.as_borrowed(), center.as_borrowed()) < F32(radius) -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_sphere_cos_in( - lhs: Veci8Input<'_>, - rhs: pgrx::composite_type!("sphere_veci8"), -) -> bool { - let center: Veci8Output = match rhs.get_by_index(NonZero::new(1).unwrap()) { - Ok(Some(s)) => s, - Ok(None) => pgrx::error!("Bad input: empty center at sphere"), - Err(_) => unreachable!(), - }; - check_matched_dims(lhs.dims(), center.dims()); - let radius: f32 = match rhs.get_by_index(NonZero::new(2).unwrap()) { - Ok(Some(s)) => s, - Ok(None) => pgrx::error!("Bad input: empty radius at sphere"), - Err(_) => unreachable!(), - }; - Veci8Cos::distance(lhs.as_borrowed(), center.as_borrowed()) < F32(radius) -} diff --git a/src/datatype/subscript_veci8.rs b/src/datatype/subscript_veci8.rs deleted file mode 100644 index 0fffed2d2..000000000 --- a/src/datatype/subscript_veci8.rs +++ /dev/null @@ -1,181 +0,0 @@ -use std::ops::Bound; - -use crate::datatype::memory_veci8::{Veci8Input, Veci8Output}; -use base::vector::VectorBorrowed; -use base::vector::VectorOwned; -use pgrx::datum::FromDatum; -use pgrx::datum::Internal; -use pgrx::pg_sys::Datum; - -#[pgrx::pg_extern(sql = "\ -CREATE FUNCTION _vectors_veci8_subscript(internal) RETURNS internal -IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '@FUNCTION_NAME@';")] -fn _vectors_veci8_subscript(_fcinfo: pgrx::pg_sys::FunctionCallInfo) -> Internal { - #[pgrx::pg_guard] - unsafe extern "C" fn transform( - subscript: *mut pgrx::pg_sys::SubscriptingRef, - indirection: *mut pgrx::pg_sys::List, - pstate: *mut pgrx::pg_sys::ParseState, - is_slice: bool, - is_assignment: bool, - ) { - unsafe { - if (*indirection).length != 1 { - pgrx::pg_sys::error!("type veci8 does only support one subscript"); - } - if !is_slice { - pgrx::pg_sys::error!("type veci8 does only support slice fetch"); - } - if is_assignment { - pgrx::pg_sys::error!("type veci8 does not support subscripted assignment"); - } - let subscript = &mut *subscript; - let ai = (*(*indirection).elements.add(0)).ptr_value as *mut pgrx::pg_sys::A_Indices; - subscript.refupperindexpr = pgrx::pg_sys::lappend( - std::ptr::null_mut(), - if !(*ai).uidx.is_null() { - let subexpr = - pgrx::pg_sys::transformExpr(pstate, (*ai).uidx, (*pstate).p_expr_kind); - let subexpr = pgrx::pg_sys::coerce_to_target_type( - pstate, - subexpr, - pgrx::pg_sys::exprType(subexpr), - pgrx::pg_sys::INT4OID, - -1, - pgrx::pg_sys::CoercionContext_COERCION_ASSIGNMENT, - pgrx::pg_sys::CoercionForm_COERCE_IMPLICIT_CAST, - -1, - ); - if subexpr.is_null() { - pgrx::error!("veci8 subscript must have type integer"); - } - subexpr.cast() - } else { - std::ptr::null_mut() - }, - ); - subscript.reflowerindexpr = pgrx::pg_sys::lappend( - std::ptr::null_mut(), - if !(*ai).lidx.is_null() { - let subexpr = - pgrx::pg_sys::transformExpr(pstate, (*ai).lidx, (*pstate).p_expr_kind); - let subexpr = pgrx::pg_sys::coerce_to_target_type( - pstate, - subexpr, - pgrx::pg_sys::exprType(subexpr), - pgrx::pg_sys::INT4OID, - -1, - pgrx::pg_sys::CoercionContext_COERCION_ASSIGNMENT, - pgrx::pg_sys::CoercionForm_COERCE_IMPLICIT_CAST, - -1, - ); - if subexpr.is_null() { - pgrx::error!("veci8 subscript must have type integer"); - } - subexpr.cast() - } else { - std::ptr::null_mut() - }, - ); - subscript.refrestype = subscript.refcontainertype; - } - } - #[pgrx::pg_guard] - unsafe extern "C" fn exec_setup( - _subscript: *const pgrx::pg_sys::SubscriptingRef, - state: *mut pgrx::pg_sys::SubscriptingRefState, - steps: *mut pgrx::pg_sys::SubscriptExecSteps, - ) { - #[derive(Default)] - struct Workspace { - range: Option<(Bound, Bound)>, - } - #[pgrx::pg_guard] - unsafe extern "C" fn sbs_check_subscripts( - _state: *mut pgrx::pg_sys::ExprState, - op: *mut pgrx::pg_sys::ExprEvalStep, - _econtext: *mut pgrx::pg_sys::ExprContext, - ) -> bool { - unsafe { - let state = &mut *(*op).d.sbsref.state; - let workspace = &mut *(state.workspace as *mut Workspace); - workspace.range = None; - let mut end = Bound::Unbounded; - let mut start = Bound::Unbounded; - if state.upperprovided.read() { - if !state.upperindexnull.read() { - let upper = state.upperindex.read().value() as i32; - if upper >= 0 { - end = Bound::Excluded(upper as u32); - } else { - (*op).resnull.write(true); - return false; - } - } else { - (*op).resnull.write(true); - return false; - } - } - if state.lowerprovided.read() { - if !state.lowerindexnull.read() { - let lower = state.lowerindex.read().value() as i32; - if lower >= 0 { - start = Bound::Included(lower as u32); - } else { - (*op).resnull.write(true); - return false; - } - } else { - (*op).resnull.write(true); - return false; - } - } - workspace.range = Some((start, end)); - true - } - } - #[pgrx::pg_guard] - unsafe extern "C" fn sbs_fetch( - _state: *mut pgrx::pg_sys::ExprState, - op: *mut pgrx::pg_sys::ExprEvalStep, - _econtext: *mut pgrx::pg_sys::ExprContext, - ) { - unsafe { - let state = &mut *(*op).d.sbsref.state; - let workspace = &mut *(state.workspace as *mut Workspace); - let input = - Veci8Input::from_datum((*op).resvalue.read(), (*op).resnull.read()).unwrap(); - let v = workspace - .range - .and_then(|i| input.as_borrowed().subvector(i)); - if let Some(v) = v { - let output = Veci8Output::new(v.as_borrowed()); - (*op).resnull.write(false); - (*op).resvalue.write(Datum::from(output.into_raw())); - } else { - (*op).resnull.write(true); - } - } - } - unsafe { - let state = &mut *state; - let steps = &mut *steps; - assert!(state.numlower == 1); - assert!(state.numupper == 1); - state.workspace = pgrx::pg_sys::palloc(std::mem::size_of::()); - std::ptr::write::(state.workspace.cast(), Workspace::default()); - steps.sbs_check_subscripts = Some(sbs_check_subscripts); - steps.sbs_fetch = Some(sbs_fetch); - steps.sbs_assign = None; - steps.sbs_fetch_old = None; - } - } - static SBSROUTINES: pgrx::pg_sys::SubscriptRoutines = pgrx::pg_sys::SubscriptRoutines { - transform: Some(transform), - exec_setup: Some(exec_setup), - fetch_strict: true, - fetch_leakproof: false, - store_leakproof: false, - }; - Internal::from(Some(Datum::from(std::ptr::addr_of!(SBSROUTINES)))) -} diff --git a/src/datatype/text_veci8.rs b/src/datatype/text_veci8.rs deleted file mode 100644 index 2e7bea465..000000000 --- a/src/datatype/text_veci8.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::datatype::memory_veci8::{Veci8Input, Veci8Output}; -use crate::datatype::typmod::Typmod; -use crate::error::*; -use base::vector::*; -use pgrx::pg_sys::Oid; -use std::ffi::{CStr, CString}; - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_in(input: &CStr, _oid: Oid, typmod: i32) -> Veci8Output { - use crate::utils::parse::parse_vector; - let reserve = Typmod::parse_from_i32(typmod) - .unwrap() - .dims() - .map(|x| x.get()) - .unwrap_or(0); - let v = parse_vector(input.to_bytes(), reserve as usize, |s| s.parse().ok()); - match v { - Err(e) => { - bad_literal(&e.to_string()); - } - Ok(vector) => { - let dims = u32::try_from(vector.len()).expect("input is too large"); - check_value_dims_65535(dims); - let (data, alpha, offset) = veci8::i8_quantization(&vector); - Veci8Output::new(Veci8Borrowed::new(&data, alpha, offset)) - } - } -} - -#[pgrx::pg_extern(immutable, strict, parallel_safe)] -fn _vectors_veci8_out(vector: Veci8Input<'_>) -> CString { - let vector = veci8::i8_dequantization(vector.data(), vector.alpha(), vector.offset()); - let mut buffer = String::new(); - buffer.push('['); - if let Some(&x) = vector.first() { - buffer.push_str(format!("{}", x).as_str()); - } - for &x in vector.iter().skip(1) { - buffer.push_str(format!(", {}", x).as_str()); - } - buffer.push(']'); - CString::new(buffer).unwrap() -} diff --git a/src/index/am_options.rs b/src/index/am_options.rs index db02e2fbf..48457cda3 100644 --- a/src/index/am_options.rs +++ b/src/index/am_options.rs @@ -6,8 +6,6 @@ use crate::datatype::memory_vecf16::Vecf16Input; use crate::datatype::memory_vecf16::Vecf16Output; use crate::datatype::memory_vecf32::Vecf32Input; use crate::datatype::memory_vecf32::Vecf32Output; -use crate::datatype::memory_veci8::Veci8Input; -use crate::datatype::memory_veci8::Veci8Output; use crate::datatype::typmod::Typmod; use crate::error::*; use base::distance::*; @@ -91,9 +89,6 @@ fn convert_name_to_vd(name: &str) -> Option<(VectorKind, DistanceKind)> { Some("bvector_dot") => Some((VectorKind::BVecf32, DistanceKind::Dot)), Some("bvector_cos") => Some((VectorKind::BVecf32, DistanceKind::Cos)), Some("bvector_jaccard") => Some((VectorKind::BVecf32, DistanceKind::Jaccard)), - Some("veci8_l2") => Some((VectorKind::Veci8, DistanceKind::L2)), - Some("veci8_dot") => Some((VectorKind::Veci8, DistanceKind::Dot)), - Some("veci8_cos") => Some((VectorKind::Veci8, DistanceKind::Cos)), _ => None, } } @@ -174,10 +169,6 @@ impl Opfamily { let vector = unsafe { BVecf32Input::from_datum(datum, false).unwrap() }; OwnedVector::BVecf32(vector.as_borrowed().own()) } - VectorKind::Veci8 => { - let vector = unsafe { Veci8Input::from_datum(datum, false).unwrap() }; - OwnedVector::Veci8(vector.as_borrowed().own()) - } }; Some(vector) } @@ -207,10 +198,6 @@ impl Opfamily { .get_by_index::(NonZero::new(1).unwrap()) .unwrap() .map(|vector| OwnedVector::BVecf32(vector.as_borrowed().own())), - VectorKind::Veci8 => tuple - .get_by_index::(NonZero::new(1).unwrap()) - .unwrap() - .map(|vector| OwnedVector::Veci8(vector.as_borrowed().own())), }; let radius = tuple.get_by_index::(NonZero::new(2).unwrap()).unwrap(); (center, radius) diff --git a/src/sql/bootstrap.sql b/src/sql/bootstrap.sql index 556692cdc..34940e1f9 100644 --- a/src/sql/bootstrap.sql +++ b/src/sql/bootstrap.sql @@ -6,7 +6,6 @@ CREATE TYPE vector; CREATE TYPE vecf16; CREATE TYPE svector; CREATE TYPE bvector; -CREATE TYPE veci8; CREATE TYPE vector_index_stat; @@ -14,6 +13,5 @@ CREATE TYPE sphere_vector; CREATE TYPE sphere_vecf16; CREATE TYPE sphere_svector; CREATE TYPE sphere_bvector; -CREATE TYPE sphere_veci8; -- bootstrap end diff --git a/src/sql/finalize.sql b/src/sql/finalize.sql index b6ac97007..9d579b81a 100644 --- a/src/sql/finalize.sql +++ b/src/sql/finalize.sql @@ -54,19 +54,6 @@ CREATE TYPE bvector ( ALIGNMENT = double ); -CREATE TYPE veci8 ( - INPUT = _vectors_veci8_in, - OUTPUT = _vectors_veci8_out, - RECEIVE = _vectors_veci8_recv, - SEND = _vectors_veci8_send, - SUBSCRIPT = _vectors_veci8_subscript, - TYPMOD_IN = _vectors_typmod_in_65535, - TYPMOD_OUT = _vectors_typmod_out, - STORAGE = EXTERNAL, - INTERNALLENGTH = VARIABLE, - ALIGNMENT = double -); - CREATE TYPE vector_index_stat AS ( idx_status TEXT, idx_indexing BOOL, @@ -98,11 +85,6 @@ CREATE TYPE sphere_bvector AS ( radius REAL ); -CREATE TYPE sphere_veci8 AS ( - center veci8, - radius REAL -); - -- List of operators CREATE OPERATOR + ( @@ -126,13 +108,6 @@ CREATE OPERATOR + ( COMMUTATOR = + ); -CREATE OPERATOR + ( - PROCEDURE = _vectors_veci8_operator_add, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = + -); - CREATE OPERATOR - ( PROCEDURE = _vectors_vecf32_operator_minus, LEFTARG = vector, @@ -151,12 +126,6 @@ CREATE OPERATOR - ( RIGHTARG = svector ); -CREATE OPERATOR - ( - PROCEDURE = _vectors_veci8_operator_minus, - LEFTARG = veci8, - RIGHTARG = veci8 -); - CREATE OPERATOR * ( PROCEDURE = _vectors_vecf32_operator_mul, LEFTARG = vector, @@ -178,13 +147,6 @@ CREATE OPERATOR * ( COMMUTATOR = * ); -CREATE OPERATOR * ( - PROCEDURE = _vectors_veci8_operator_mul, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = * -); - CREATE OPERATOR & ( PROCEDURE = _vectors_bvecf32_operator_and, LEFTARG = bvector, @@ -243,16 +205,6 @@ CREATE OPERATOR = ( JOIN = eqjoinsel ); -CREATE OPERATOR = ( - PROCEDURE = _vectors_veci8_operator_eq, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = =, - NEGATOR = <>, - RESTRICT = eqsel, - JOIN = eqjoinsel -); - CREATE OPERATOR <> ( PROCEDURE = _vectors_vecf32_operator_neq, LEFTARG = vector, @@ -293,16 +245,6 @@ CREATE OPERATOR <> ( JOIN = eqjoinsel ); -CREATE OPERATOR <> ( - PROCEDURE = _vectors_veci8_operator_neq, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = <>, - NEGATOR = =, - RESTRICT = eqsel, - JOIN = eqjoinsel -); - CREATE OPERATOR < ( PROCEDURE = _vectors_vecf32_operator_lt, LEFTARG = vector, @@ -343,16 +285,6 @@ CREATE OPERATOR < ( JOIN = scalarltjoinsel ); -CREATE OPERATOR < ( - PROCEDURE = _vectors_veci8_operator_lt, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = >, - NEGATOR = >=, - RESTRICT = scalarltsel, - JOIN = scalarltjoinsel -); - CREATE OPERATOR > ( PROCEDURE = _vectors_vecf32_operator_gt, LEFTARG = vector, @@ -393,16 +325,6 @@ CREATE OPERATOR > ( JOIN = scalargtjoinsel ); -CREATE OPERATOR > ( - PROCEDURE = _vectors_veci8_operator_gt, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = <, - NEGATOR = <=, - RESTRICT = scalargtsel, - JOIN = scalargtjoinsel -); - CREATE OPERATOR <= ( PROCEDURE = _vectors_vecf32_operator_lte, LEFTARG = vector, @@ -443,16 +365,6 @@ CREATE OPERATOR <= ( JOIN = scalarltjoinsel ); -CREATE OPERATOR <= ( - PROCEDURE = _vectors_veci8_operator_lte, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = >=, - NEGATOR = >, - RESTRICT = scalarltsel, - JOIN = scalarltjoinsel -); - CREATE OPERATOR >= ( PROCEDURE = _vectors_vecf32_operator_gte, LEFTARG = vector, @@ -493,16 +405,6 @@ CREATE OPERATOR >= ( JOIN = scalargtjoinsel ); -CREATE OPERATOR >= ( - PROCEDURE = _vectors_veci8_operator_gte, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = <=, - NEGATOR = <, - RESTRICT = scalargtsel, - JOIN = scalargtjoinsel -); - CREATE OPERATOR <-> ( PROCEDURE = _vectors_vecf32_operator_l2, LEFTARG = vector, @@ -531,13 +433,6 @@ CREATE OPERATOR <-> ( COMMUTATOR = <-> ); -CREATE OPERATOR <-> ( - PROCEDURE = _vectors_veci8_operator_l2, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = <-> -); - CREATE OPERATOR <#> ( PROCEDURE = _vectors_vecf32_operator_dot, LEFTARG = vector, @@ -566,13 +461,6 @@ CREATE OPERATOR <#> ( COMMUTATOR = <#> ); -CREATE OPERATOR <#> ( - PROCEDURE = _vectors_veci8_operator_dot, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = <#> -); - CREATE OPERATOR <=> ( PROCEDURE = _vectors_vecf32_operator_cosine, LEFTARG = vector, @@ -601,13 +489,6 @@ CREATE OPERATOR <=> ( COMMUTATOR = <=> ); -CREATE OPERATOR <=> ( - PROCEDURE = _vectors_veci8_operator_cosine, - LEFTARG = veci8, - RIGHTARG = veci8, - COMMUTATOR = <=> -); - CREATE OPERATOR <~> ( PROCEDURE = _vectors_bvecf32_operator_jaccard, LEFTARG = bvector, @@ -643,13 +524,6 @@ CREATE OPERATOR <<->> ( COMMUTATOR = <<->> ); -CREATE OPERATOR <<->> ( - PROCEDURE = _vectors_veci8_sphere_l2_in, - LEFTARG = veci8, - RIGHTARG = sphere_veci8, - COMMUTATOR = <<->> -); - CREATE OPERATOR <<#>> ( PROCEDURE = _vectors_vecf32_sphere_dot_in, LEFTARG = vector, @@ -678,13 +552,6 @@ CREATE OPERATOR <<#>> ( COMMUTATOR = <<#>> ); -CREATE OPERATOR <<#>> ( - PROCEDURE = _vectors_veci8_sphere_dot_in, - LEFTARG = veci8, - RIGHTARG = sphere_veci8, - COMMUTATOR = <<#>> -); - CREATE OPERATOR <<=>> ( PROCEDURE = _vectors_vecf32_sphere_cos_in, LEFTARG = vector, @@ -713,13 +580,6 @@ CREATE OPERATOR <<=>> ( COMMUTATOR = <<=>> ); -CREATE OPERATOR <<=>> ( - PROCEDURE = _vectors_veci8_sphere_cos_in, - LEFTARG = veci8, - RIGHTARG = sphere_veci8, - COMMUTATOR = <<=>> -); - CREATE OPERATOR <<~>> ( PROCEDURE = _vectors_bvecf32_sphere_jaccard_in, LEFTARG = bvector, @@ -764,9 +624,6 @@ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_svecf3 CREATE FUNCTION vector_dims(bvector) RETURNS INT IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_bvecf32_dims_wrapper'; -CREATE FUNCTION vector_dims(veci8) RETURNS INT -IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_veci8_dims_wrapper'; - CREATE FUNCTION vector_norm(vector) RETURNS real IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_vecf32_norm_wrapper'; @@ -779,9 +636,6 @@ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_svecf3 CREATE FUNCTION vector_norm(bvector) RETURNS real IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_bvecf32_norm_wrapper'; -CREATE FUNCTION vector_norm(veci8) RETURNS real -IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_veci8_norm_wrapper'; - CREATE FUNCTION vector_normalize(vector) RETURNS vector IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_vecf32_normalize_wrapper'; @@ -791,18 +645,12 @@ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_vecf16 CREATE FUNCTION vector_normalize(svector) RETURNS svector IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_svecf32_normalize_wrapper'; -CREATE FUNCTION vector_normalize(veci8) RETURNS veci8 -IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_veci8_normalize_wrapper'; - CREATE FUNCTION to_svector("dims" INT, "indexes" INT[], "values" real[]) RETURNS svector IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_to_svector_wrapper'; CREATE FUNCTION binarize("vector" vector) RETURNS bvector IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_binarize_wrapper'; -CREATE FUNCTION to_veci8("len" INT, "alpha" real, "offset" real, "values" INT[]) RETURNS veci8 -IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c AS 'MODULE_PATHNAME', '_vectors_to_veci8_wrapper'; - CREATE FUNCTION sphere(vector, real) RETURNS sphere_vector IMMUTABLE PARALLEL SAFE LANGUAGE sql AS 'SELECT ROW($1, $2)'; @@ -815,9 +663,6 @@ IMMUTABLE PARALLEL SAFE LANGUAGE sql AS 'SELECT ROW($1, $2)'; CREATE FUNCTION sphere(bvector, real) RETURNS sphere_bvector IMMUTABLE PARALLEL SAFE LANGUAGE sql AS 'SELECT ROW($1, $2)'; -CREATE FUNCTION sphere(veci8, real) RETURNS sphere_veci8 -IMMUTABLE PARALLEL SAFE LANGUAGE sql AS 'SELECT ROW($1, $2)'; - -- List of aggregates CREATE AGGREGATE avg(vector) ( @@ -878,12 +723,6 @@ CREATE CAST (vector AS bvector) CREATE CAST (bvector AS vector) WITH FUNCTION _vectors_cast_bvecf32_to_vecf32(bvector, integer, boolean); -CREATE CAST (veci8 AS vector) - WITH FUNCTION _vectors_cast_veci8_to_vecf32(veci8, integer, boolean); - -CREATE CAST (vector AS veci8) - WITH FUNCTION _vectors_cast_vecf32_to_veci8(vector, integer, boolean); - -- List of access methods CREATE ACCESS METHOD vectors TYPE INDEX HANDLER _vectors_amhandler; @@ -917,12 +756,6 @@ CREATE OPERATOR FAMILY bvector_cos_ops USING vectors; CREATE OPERATOR FAMILY bvector_jaccard_ops USING vectors; -CREATE OPERATOR FAMILY veci8_l2_ops USING vectors; - -CREATE OPERATOR FAMILY veci8_dot_ops USING vectors; - -CREATE OPERATOR FAMILY veci8_cos_ops USING vectors; - -- List of operator classes CREATE OPERATOR CLASS vector_l2_ops @@ -990,21 +823,6 @@ CREATE OPERATOR CLASS bvector_jaccard_ops OPERATOR 1 <~> (bvector, bvector) FOR ORDER BY float_ops, OPERATOR 2 <<~>> (bvector, sphere_bvector) FOR SEARCH; -CREATE OPERATOR CLASS veci8_l2_ops - FOR TYPE veci8 USING vectors FAMILY veci8_l2_ops AS - OPERATOR 1 <-> (veci8, veci8) FOR ORDER BY float_ops, - OPERATOR 2 <<->> (veci8, sphere_veci8) FOR SEARCH; - -CREATE OPERATOR CLASS veci8_dot_ops - FOR TYPE veci8 USING vectors FAMILY veci8_dot_ops AS - OPERATOR 1 <#> (veci8, veci8) FOR ORDER BY float_ops, - OPERATOR 2 <<#>> (veci8, sphere_veci8) FOR SEARCH; - -CREATE OPERATOR CLASS veci8_cos_ops - FOR TYPE veci8 USING vectors FAMILY veci8_cos_ops AS - OPERATOR 1 <=> (veci8, veci8) FOR ORDER BY float_ops, - OPERATOR 2 <<=>> (veci8, sphere_veci8) FOR SEARCH; - -- List of views CREATE VIEW pg_vector_index_stat AS diff --git a/src/upgrade/symbols.rs b/src/upgrade/symbols.rs index 1a6a05fb1..ab7411da4 100644 --- a/src/upgrade/symbols.rs +++ b/src/upgrade/symbols.rs @@ -31,3 +31,28 @@ macro_rules! symbol { // 0.2.1--0.3.0 symbol!(_vectors_ai_embedding_vector); symbol!(_vectors_typmod_in); + +// 0.3.0--0.4.0 +symbol!(_vectors_veci8_subscript); +symbol!(_vectors_veci8_send); +symbol!(_vectors_veci8_recv); +symbol!(_vectors_veci8_out); +symbol!(_vectors_veci8_operator_neq); +symbol!(_vectors_veci8_operator_mul); +symbol!(_vectors_veci8_operator_minus); +symbol!(_vectors_veci8_operator_lte); +symbol!(_vectors_veci8_operator_lt); +symbol!(_vectors_veci8_operator_l2); +symbol!(_vectors_veci8_operator_gte); +symbol!(_vectors_veci8_operator_gt); +symbol!(_vectors_veci8_operator_eq); +symbol!(_vectors_veci8_operator_dot); +symbol!(_vectors_veci8_operator_cosine); +symbol!(_vectors_veci8_operator_add); +symbol!(_vectors_veci8_normalize); +symbol!(_vectors_veci8_norm); +symbol!(_vectors_veci8_in); +symbol!(_vectors_veci8_dims); +symbol!(_vectors_to_veci8); +symbol!(_vectors_cast_veci8_to_vecf32); +symbol!(_vectors_cast_vecf32_to_veci8); diff --git a/tests/sqllogictest/int8.slt b/tests/sqllogictest/int8.slt deleted file mode 100644 index 2aa25a5bd..000000000 --- a/tests/sqllogictest/int8.slt +++ /dev/null @@ -1,55 +0,0 @@ -statement ok -SET search_path TO pg_temp, vectors; - -statement ok -CREATE TABLE t (val veci8(3)); - -statement ok -INSERT INTO t (val) SELECT ARRAY[random(), random(), random()]::real[]::vector::veci8 FROM generate_series(1, 1000); - -statement ok -CREATE INDEX ON t USING vectors (val veci8_l2_ops) -WITH (options = "[indexing.hnsw]"); - -statement ok -CREATE INDEX ON t USING vectors (val veci8_dot_ops) -WITH (options = "[indexing.hnsw]"); - -statement ok -CREATE INDEX ON t USING vectors (val veci8_cos_ops) -WITH (options = "[indexing.hnsw]"); - - -query I -SELECT COUNT(1) FROM (SELECT 1 FROM t ORDER BY val <-> '[0.5,0.5,0.5]'::veci8 limit 10) t2; ----- -10 - -query I -SELECT COUNT(1) FROM (SELECT 1 FROM t ORDER BY val <#> '[0.5,0.5,0.5]'::veci8 limit 10) t2; ----- -10 - -query I -SELECT COUNT(1) FROM (SELECT 1 FROM t ORDER BY val <=> '[0.5,0.5,0.5]'::veci8 limit 10) t2; ----- -10 - -statement ok -DROP TABLE t; - -query I -SELECT to_veci8(5, 1, 0, '{0,1,2,0,0}'); ----- -[0, 1, 2, 0, 0] - -query I -SELECT '[2,2,2]'::veci8 * '[2,2,2]'::veci8; ----- -[4, 4, 4] - -statement error Lengths of values and len are not matched. -SELECT to_veci8(5, 1, 0, '{0,1,2,0,0,0}'); - -statement error Index or value contains nulls. -SELECT to_veci8(5, 1, 0, '{0,1,2,NULL,0}'); \ No newline at end of file diff --git a/tests/sqllogictest/pushdown_range.slt b/tests/sqllogictest/pushdown_range.slt index a5b6b8a95..a47ff3feb 100644 --- a/tests/sqllogictest/pushdown_range.slt +++ b/tests/sqllogictest/pushdown_range.slt @@ -2,14 +2,14 @@ statement ok SET search_path TO pg_temp, vectors; statement ok -CREATE TABLE t (val0 vector(3), val1 vecf16(3), val2 svector(3), val3 bvector(3), val4 veci8(3)); +CREATE TABLE t (val0 vector(3), val1 vecf16(3), val2 svector(3), val3 bvector(3)); statement ok -INSERT INTO t (val0, val1, val2, val3, val4) VALUES - ('[0.1, 0.1, 0.1]', '[0.1, 0.1, 0.1]', '{0:-0.1, 1:0.1, 2:0.1}/3', '[0, 0, 0]', '[1, 1, 1]'), - ('[0.2, 0.2, 0.2]', '[-0.2, 0.2, 0.2]', '{0:0.2, 1:-0.2, 2:0.2}/3', '[0, 0, 1]', '[2, 2, 2]'), - ('[0.3, 0.3, 0.3]', '[0.3, 0.3, -0.3]', '{0:-0.3, 1:0.3, 2:-0.3}/3', '[1, 1, 0]', '[4, 4, 4]'), - ('[0.4, 0.4, 0.4]', '[0.4, -0.4, 0.4]', '{0:-0.4, 1:-0.4, 2:-0.4}/3', '[1, 1, 1]', '[5, 5, 5]'); +INSERT INTO t (val0, val1, val2, val3) VALUES + ('[0.1, 0.1, 0.1]', '[0.1, 0.1, 0.1]', '{0:-0.1, 1:0.1, 2:0.1}/3', '[0, 0, 0]'), + ('[0.2, 0.2, 0.2]', '[-0.2, 0.2, 0.2]', '{0:0.2, 1:-0.2, 2:0.2}/3', '[0, 0, 1]'), + ('[0.3, 0.3, 0.3]', '[0.3, 0.3, -0.3]', '{0:-0.3, 1:0.3, 2:-0.3}/3', '[1, 1, 0]'), + ('[0.4, 0.4, 0.4]', '[0.4, -0.4, 0.4]', '{0:-0.4, 1:-0.4, 2:-0.4}/3', '[1, 1, 1]'); statement ok CREATE INDEX ON t USING vectors (val0 vector_l2_ops) @@ -84,22 +84,6 @@ SELECT val3 FROM t WHERE val3 <<~>> sphere('[1, 1, 1]'::bvector, 0.4) ORDER BY v [1, 1, 1] [1, 1, 0] -statement ok -CREATE INDEX ON t USING vectors (val4 veci8_l2_ops) -WITH (options = "[indexing.hnsw]"); - -# original style -query I -SELECT val4 FROM t WHERE val4 <-> '[2, 2, 2]' < 3 ORDER BY val4 <-> '[2, 2, 2]'; ----- -[2, 2, 2] - -# sphere style -query I -SELECT val4 FROM t WHERE val4 <<->> sphere('[2, 2, 2]'::veci8, 3) ORDER BY val4 <-> '[2, 2, 2]'; ----- -[2, 2, 2] - # sphere style: multiple vector keys and no order-by key query I SELECT val0 FROM t WHERE val0 <<->> sphere('[0.24, 0.24, 0.24]'::vector, 0.012) diff --git a/tests/sqllogictest/veci8_binary.slt b/tests/sqllogictest/veci8_binary.slt deleted file mode 100644 index 966b2536f..000000000 --- a/tests/sqllogictest/veci8_binary.slt +++ /dev/null @@ -1,34 +0,0 @@ -statement ok -SET search_path TO pg_temp, vectors; - -statement ok -CREATE TABLE t (id bigserial, val veci8); - -statement ok -INSERT INTO t (val) SELECT NULL FROM generate_series(1, 1000); - -statement ok -INSERT INTO t (val) SELECT ARRAY[random()]::real[]::vector::veci8 FROM generate_series(1, 1000); - -statement ok -INSERT INTO t (val) SELECT ARRAY[random(), random()]::real[]::vector::veci8 FROM generate_series(1, 1000); - -statement ok -INSERT INTO t (val) SELECT ARRAY[random(), random(), random()]::real[]::vector::veci8 FROM generate_series(1, 1000); - -statement ok -COPY t TO '/tmp/data.bin' WITH (FORMAT binary); - -statement ok -CREATE TABLE t2 (id bigserial, val veci8); - -statement ok -COPY t2 FROM '/tmp/data.bin' WITH (FORMAT binary); - -query I -SELECT SUM(((t.val = t2.val) OR (t.val IS NULL and t2.val IS NULL))::int) FROM t FULL OUTER JOIN t2 ON t.id = t2.id; ----- -4000 - -statement ok -DROP TABLE t, t2; \ No newline at end of file diff --git a/tests/sqllogictest/veci8_storage.slt b/tests/sqllogictest/veci8_storage.slt deleted file mode 100644 index 5fdc7e6d3..000000000 --- a/tests/sqllogictest/veci8_storage.slt +++ /dev/null @@ -1,23 +0,0 @@ -statement ok -SET search_path TO pg_temp, vectors; - -statement ok -CREATE TABLE t (val veci8); - -statement ok -INSERT INTO t (val) SELECT ARRAY[random(), random(), random()]::real[]::vector::veci8 FROM generate_series(1, 1000); - -statement ok -ALTER TABLE t ALTER COLUMN val SET STORAGE PLAIN; - -statement ok -ALTER TABLE t ALTER COLUMN val SET STORAGE EXTERNAL; - -statement ok -ALTER TABLE t ALTER COLUMN val SET STORAGE EXTENDED; - -statement ok -ALTER TABLE t ALTER COLUMN val SET STORAGE MAIN; - -statement ok -DROP TABLE t; \ No newline at end of file diff --git a/tests/sqllogictest/veci8_subscript.slt b/tests/sqllogictest/veci8_subscript.slt deleted file mode 100644 index f30923e56..000000000 --- a/tests/sqllogictest/veci8_subscript.slt +++ /dev/null @@ -1,73 +0,0 @@ -statement ok -SET search_path TO pg_temp, vectors; - -query I -SELECT (to_veci8(8, 1, 0, '{0,1,2,3,4,5,6,7}'))[3:6]; ----- -[3, 4, 5] - -query I -SELECT (to_veci8(8, 1, 0, '{0,1,2,3,4,5,6,7}'))[:4]; ----- -[0, 1, 2, 3] - -query I -SELECT (to_veci8(8, 1, 0, '{0,1,2,3,4,5,6,7}'))[5:]; ----- -[5, 6, 7] - -query I -SELECT (to_veci8(8, 1, 0, '{0,1,2,3,4,5,6,7}'))[1:8]; ----- -[1, 2, 3, 4, 5, 6, 7] - -statement error type veci8 does only support one subscript -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[3:3][1:1]; - -statement error type veci8 does only support slice fetch -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[3]; - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[5:4]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[9:]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[:0]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[:-1]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[NULL:NULL]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[NULL:8]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[1:NULL]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[NULL:]; ----- -NULL - -query I -SELECT ('[0, 1, 2, 3, 4, 5, 6, 7]'::veci8)[:NULL]; ----- -NULL