Skip to content

Commit 903eab9

Browse files
committed
vector type first draft
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent 20070d2 commit 903eab9

3 files changed

Lines changed: 164 additions & 0 deletions

File tree

vortex-tensor/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
//! similarity.
77
88
pub mod fixed_shape;
9+
pub mod vector;
910

1011
pub mod scalar_fns;

vortex-tensor/src/vector/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Vector extension type for fixed-length float vectors (e.g., embeddings).
5+
6+
/// The VTable for the vector extension type.
7+
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
8+
pub struct Vector;
9+
10+
mod vtable;

vortex-tensor/src/vector/vtable.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex::dtype::DType;
5+
use vortex::dtype::extension::ExtDType;
6+
use vortex::dtype::extension::ExtId;
7+
use vortex::dtype::extension::ExtVTable;
8+
use vortex::error::VortexResult;
9+
use vortex::error::vortex_bail;
10+
use vortex::error::vortex_ensure;
11+
use vortex::extension::EmptyMetadata;
12+
use vortex::scalar::ScalarValue;
13+
14+
use crate::vector::Vector;
15+
16+
impl ExtVTable for Vector {
17+
type Metadata = EmptyMetadata;
18+
19+
// TODO(connor): This is just a placeholder for now.
20+
type NativeValue<'a> = &'a ScalarValue;
21+
22+
fn id(&self) -> ExtId {
23+
ExtId::new_ref("vortex.vector")
24+
}
25+
26+
fn serialize_metadata(&self, _metadata: &Self::Metadata) -> VortexResult<Vec<u8>> {
27+
Ok(Vec::new())
28+
}
29+
30+
fn deserialize_metadata(&self, metadata: &[u8]) -> VortexResult<Self::Metadata> {
31+
vortex_ensure!(
32+
metadata.is_empty(),
33+
"Vector metadata must be empty, got {} bytes",
34+
metadata.len()
35+
);
36+
Ok(EmptyMetadata)
37+
}
38+
39+
fn validate_dtype(&self, ext_dtype: &ExtDType<Self>) -> VortexResult<()> {
40+
let storage_dtype = ext_dtype.storage_dtype();
41+
let DType::FixedSizeList(element_dtype, _list_size, _nullability) = storage_dtype else {
42+
vortex_bail!("Vector storage dtype must be a FixedSizeList, got {storage_dtype}");
43+
};
44+
45+
vortex_ensure!(
46+
element_dtype.is_float(),
47+
"Vector element dtype must be a float, got {element_dtype}"
48+
);
49+
vortex_ensure!(
50+
!element_dtype.is_nullable(),
51+
"Vector element dtype must be non-nullable"
52+
);
53+
54+
Ok(())
55+
}
56+
57+
fn unpack_native<'a>(
58+
&self,
59+
_ext_dtype: &'a ExtDType<Self>,
60+
storage_value: &'a ScalarValue,
61+
) -> VortexResult<Self::NativeValue<'a>> {
62+
Ok(storage_value)
63+
}
64+
}
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use std::sync::Arc;
69+
70+
use rstest::rstest;
71+
use vortex::dtype::DType;
72+
use vortex::dtype::Nullability;
73+
use vortex::dtype::PType;
74+
use vortex::dtype::extension::ExtDType;
75+
use vortex::dtype::extension::ExtVTable;
76+
use vortex::error::VortexResult;
77+
use vortex::extension::EmptyMetadata;
78+
79+
use crate::vector::Vector;
80+
81+
/// Constructs a `FixedSizeList` storage dtype with the given float [`PType`], list size, and
82+
/// [`Nullability`].
83+
fn vector_storage_dtype(ptype: PType, size: u32, nullability: Nullability) -> DType {
84+
DType::FixedSizeList(
85+
Arc::new(DType::Primitive(ptype, Nullability::NonNullable)),
86+
size,
87+
nullability,
88+
)
89+
}
90+
91+
#[rstest]
92+
#[case::f16(PType::F16)]
93+
#[case::f32(PType::F32)]
94+
#[case::f64(PType::F64)]
95+
fn validate_accepts_float_types(#[case] ptype: PType) -> VortexResult<()> {
96+
let storage = vector_storage_dtype(ptype, 128, Nullability::NonNullable);
97+
ExtDType::<Vector>::try_new(EmptyMetadata, storage)?;
98+
Ok(())
99+
}
100+
101+
#[rstest]
102+
#[case::nullable(Nullability::Nullable)]
103+
#[case::non_nullable(Nullability::NonNullable)]
104+
fn validate_accepts_any_outer_nullability(
105+
#[case] nullability: Nullability,
106+
) -> VortexResult<()> {
107+
let storage = vector_storage_dtype(PType::F32, 128, nullability);
108+
ExtDType::<Vector>::try_new(EmptyMetadata, storage)?;
109+
Ok(())
110+
}
111+
112+
#[test]
113+
fn validate_rejects_non_fsl() {
114+
let storage = DType::Primitive(PType::F32, Nullability::NonNullable);
115+
assert!(ExtDType::<Vector>::try_new(EmptyMetadata, storage).is_err());
116+
}
117+
118+
#[test]
119+
fn validate_rejects_integer_elements() {
120+
let storage = DType::FixedSizeList(
121+
Arc::new(DType::Primitive(PType::U32, Nullability::NonNullable)),
122+
128,
123+
Nullability::NonNullable,
124+
);
125+
assert!(ExtDType::<Vector>::try_new(EmptyMetadata, storage).is_err());
126+
}
127+
128+
#[test]
129+
fn validate_rejects_nullable_elements() {
130+
let storage = DType::FixedSizeList(
131+
Arc::new(DType::Primitive(PType::F32, Nullability::Nullable)),
132+
128,
133+
Nullability::NonNullable,
134+
);
135+
assert!(ExtDType::<Vector>::try_new(EmptyMetadata, storage).is_err());
136+
}
137+
138+
#[test]
139+
fn roundtrip_metadata() -> VortexResult<()> {
140+
let vtable = Vector;
141+
let bytes = vtable.serialize_metadata(&EmptyMetadata)?;
142+
assert!(bytes.is_empty());
143+
let deserialized = vtable.deserialize_metadata(&bytes)?;
144+
assert_eq!(deserialized, EmptyMetadata);
145+
Ok(())
146+
}
147+
148+
#[test]
149+
fn deserialize_rejects_non_empty_bytes() {
150+
let vtable = Vector;
151+
assert!(vtable.deserialize_metadata(&[0x01]).is_err());
152+
}
153+
}

0 commit comments

Comments
 (0)