1use anyhow::{Result, ensure};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(transparent)]
8pub struct ObjectId(pub usize);
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct DataSpan {
13 pub object: ObjectId,
15 pub offset: u64,
17 pub length: u64,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Tensor {
24 pub name: String,
26 pub role: TensorRole,
28 pub logical: Option<TensorType>,
30 pub encoding: TensorEncoding,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct TensorType {
37 pub shape: Vec<u64>,
39 pub dtype: Option<Dtype>,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(transparent)]
47pub struct TensorId(pub usize);
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "snake_case")]
52pub enum TensorRole {
53 Projection,
54 Embedding,
55 Expert,
56 Router,
57 Convolution,
58 Norm,
59 NgramEmbedding,
60 Buffer,
61 Opaque,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
66#[serde(rename_all = "snake_case")]
67pub enum Dtype {
68 Bool,
69 U8,
70 I8,
71 U16,
72 I16,
73 I32,
74 U64,
75 F64,
76 U32,
77 I64,
78 F32,
79 F16,
80 Bf16,
81}
82
83impl Dtype {
84 pub fn bytes(self) -> u64 {
86 match self {
87 Self::I64 | Self::U64 | Self::F64 => 8,
88 Self::U32 | Self::I32 | Self::F32 => 4,
89 Self::F16 | Self::Bf16 | Self::U16 | Self::I16 => 2,
90 Self::Bool | Self::U8 | Self::I8 => 1,
91 }
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct StoredTensor {
99 pub dtype: Dtype,
101 pub shape: Vec<u64>,
103 pub byte_strides: Vec<u64>,
105 pub data: DataSpan,
107}
108
109impl StoredTensor {
110 pub fn contiguous(dtype: Dtype, shape: Vec<u64>, data: DataSpan) -> Result<Self> {
113 let mut stride = dtype.bytes();
114 let mut byte_strides = vec![0; shape.len()];
115
116 for (dim, out) in shape.iter().zip(&mut byte_strides).rev() {
117 *out = stride;
118 stride = stride
119 .checked_mul(*dim)
120 .ok_or_else(|| anyhow::anyhow!("tensor size overflow"))?;
121 }
122
123 let tensor = Self {
124 dtype,
125 shape,
126 byte_strides,
127 data,
128 };
129
130 tensor.validate()?;
131
132 Ok(tensor)
133 }
134
135 pub fn validate(&self) -> Result<()> {
138 ensure!(
139 self.shape.len() == self.byte_strides.len(),
140 "tensor stride rank mismatch"
141 );
142
143 ensure!(
144 self.data.offset.checked_add(self.data.length).is_some(),
145 "data span overflow"
146 );
147
148 if self.shape.contains(&0) {
149 return Ok(());
150 }
151
152 let mut extent = self.dtype.bytes();
153
154 for (&dim, &stride) in self.shape.iter().zip(&self.byte_strides) {
155 let last = (dim - 1)
156 .checked_mul(stride)
157 .ok_or_else(|| anyhow::anyhow!("tensor stride overflow"))?;
158 extent = extent
159 .checked_add(last)
160 .ok_or_else(|| anyhow::anyhow!("tensor extent overflow"))?;
161 }
162
163 ensure!(
164 extent <= self.data.length,
165 "tensor view exceeds its data span"
166 );
167
168 Ok(())
169 }
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174#[serde(tag = "kind", rename_all = "snake_case")]
175pub enum TensorEncoding {
176 Dense {
178 tensor: StoredTensor,
180 },
181 Affine {
183 bits: u8,
185 group_size: u64,
187 group_axis: usize,
189 packing: BitPacking,
191 codes: StoredTensor,
193 scales: StoredTensor,
195 offset: AffineOffset,
197 },
198 Ggml {
200 encoding: GgmlEncoding,
201 data: DataSpan,
202 },
203 Opaque {
205 name: String,
206 metadata: serde_json::Value,
207 data: Vec<DataSpan>,
208 },
209}
210
211#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
213#[serde(rename_all = "snake_case")]
214pub enum BitPacking {
215 LowFirstU32,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(tag = "kind", rename_all = "snake_case")]
222pub enum AffineOffset {
223 Bias { tensor: StoredTensor },
225 ZeroPoint { tensor: StoredTensor },
227}
228
229#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
231#[serde(tag = "kind", rename_all = "snake_case")]
232pub enum GgmlEncoding {
233 F32,
234 F16,
235 Q4_0,
236 Q4_1,
237 Q4K,
238 Q5K,
239 Q6K,
240 Q8_0,
241 Bf16,
242 Opaque { code: u32 },
243}
244
245impl TensorEncoding {
246 pub fn data(&self) -> Vec<&DataSpan> {
248 match self {
249 Self::Dense { tensor } => vec![&tensor.data],
250 Self::Affine {
251 codes,
252 scales,
253 offset,
254 ..
255 } => {
256 let (AffineOffset::Bias { tensor } | AffineOffset::ZeroPoint { tensor }) = offset;
257
258 vec![&codes.data, &scales.data, &tensor.data]
259 }
260 Self::Ggml { data, .. } => vec![data],
261 Self::Opaque { data, .. } => data.iter().collect(),
262 }
263 }
264}
265
266impl Tensor {
267 pub fn new(
270 name: String,
271 role: TensorRole,
272 shape: Option<Vec<u64>>,
273 encoding: TensorEncoding,
274 ) -> Self {
275 let dtype = match &encoding {
276 TensorEncoding::Dense { tensor } => Some(tensor.dtype),
277 _ => None,
278 };
279
280 Self {
281 name,
282 role,
283 logical: shape.map(|shape| TensorType { shape, dtype }),
284 encoding,
285 }
286 }
287
288 pub fn shape(&self) -> Option<&[u64]> {
290 self.logical.as_ref().map(|t| t.shape.as_slice())
291 }
292
293 pub fn validate(&self) -> Result<()> {
295 self.encoding.validate()?;
296
297 if let TensorEncoding::Dense { tensor } = &self.encoding {
298 ensure!(
299 self.shape() == Some(tensor.shape.as_slice()),
300 "dense logical shape mismatch"
301 );
302 ensure!(
303 self.logical.as_ref().and_then(|t| t.dtype) == Some(tensor.dtype),
304 "dense logical dtype mismatch"
305 );
306 }
307
308 if let TensorEncoding::Affine {
309 codes,
310 bits,
311 group_axis,
312 ..
313 } = &self.encoding
314 {
315 let mut shape = codes.shape.clone();
316 shape[*group_axis] = shape[*group_axis]
317 .checked_mul(32 / u64::from(*bits))
318 .ok_or_else(|| anyhow::anyhow!("affine shape overflow"))?;
319
320 ensure!(
321 self.shape() == Some(shape.as_slice()),
322 "affine logical shape mismatch"
323 );
324 }
325
326 Ok(())
327 }
328}
329
330impl TensorEncoding {
331 pub fn validate(&self) -> Result<()> {
334 match self {
335 Self::Dense { tensor } => tensor.validate(),
336 Self::Affine {
337 bits,
338 group_size,
339 group_axis,
340 codes,
341 scales,
342 offset,
343 ..
344 } => {
345 let (AffineOffset::Bias { tensor: offsets }
346 | AffineOffset::ZeroPoint { tensor: offsets }) = offset;
347
348 codes.validate()?;
349 scales.validate()?;
350 offsets.validate()?;
351 ensure!(
352 matches!(bits, 2 | 4 | 8) && codes.dtype == Dtype::U32,
353 "unsupported affine word packing"
354 );
355 ensure!(
356 codes.shape.len().checked_sub(1) == Some(*group_axis),
357 "affine groups must span the final axis"
358 );
359 ensure!(
360 scales.shape.len() == codes.shape.len() && offsets.shape == scales.shape,
361 "affine component rank mismatch"
362 );
363 ensure!(
364 scales.shape[..*group_axis] == codes.shape[..*group_axis],
365 "affine component row mismatch"
366 );
367
368 let width = codes.shape[*group_axis]
369 .checked_mul(32 / u64::from(*bits))
370 .ok_or_else(|| anyhow::anyhow!("affine shape overflow"))?;
371
372 ensure!(
373 *group_size > 0 && width > 0 && width.is_multiple_of(*group_size),
374 "invalid affine group size"
375 );
376 ensure!(
377 scales.shape[*group_axis] == width / *group_size,
378 "affine scale count mismatch"
379 );
380
381 Ok(())
382 }
383 Self::Ggml { data, .. } => check_span(data),
384 Self::Opaque { data, .. } => data.iter().try_for_each(check_span),
385 }
386 }
387}
388
389fn check_span(span: &DataSpan) -> Result<()> {
390 ensure!(
391 span.offset.checked_add(span.length).is_some(),
392 "data span overflow"
393 );
394
395 Ok(())
396}