1use std::collections::BTreeSet;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use super::super::{
6 CanonicalRational, ContractVersion, QuantizationFormatId, VNextError, WeightFormatId, WeightId,
7 WeightLayoutId,
8};
9use super::ElementType;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum QuantizationPacking {
14 Linear,
15 Interleaved,
16 Tiled,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(tag = "kind", rename_all = "snake_case")]
26pub enum QuantizationGrouping {
27 Fixed { size: u32 },
28 WholeAxis,
29}
30
31impl QuantizationGrouping {
32 pub const fn fixed(size: u32) -> Self {
33 Self::Fixed { size }
34 }
35
36 pub const fn fixed_size(self) -> Option<u32> {
37 match self {
38 Self::Fixed { size } => Some(size),
39 Self::WholeAxis => None,
40 }
41 }
42
43 pub const fn resolved_size(self, axis_extent: u64) -> u64 {
44 match self {
45 Self::Fixed { size } => size as u64,
46 Self::WholeAxis => axis_extent,
47 }
48 }
49
50 const fn is_valid(self) -> bool {
51 match self {
52 Self::Fixed { size } => size != 0 && size.is_power_of_two(),
53 Self::WholeAxis => true,
54 }
55 }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct QuantizationSpec {
60 pub format_id: QuantizationFormatId,
61 pub bits_per_weight: u8,
62 pub grouping: QuantizationGrouping,
63 pub packing: QuantizationPacking,
64 pub scale_type: ElementType,
65 pub zero_point_type: Option<ElementType>,
66}
67
68impl QuantizationSpec {
69 pub fn validate(&self) -> Result<(), VNextError> {
70 if !(1..=8).contains(&self.bits_per_weight)
71 || !self.grouping.is_valid()
72 || !matches!(
73 self.scale_type,
74 ElementType::F16 | ElementType::Bf16 | ElementType::F32
75 )
76 || self.zero_point_type.is_some_and(|element_type| {
77 !matches!(
78 element_type,
79 ElementType::U8 | ElementType::U32 | ElementType::I8 | ElementType::I32
80 )
81 })
82 {
83 return Err(VNextError::InvalidExecutionPlan {
84 reason: format!("invalid quantization format `{}`", self.format_id),
85 });
86 }
87 Ok(())
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96pub struct BlockQuantizationSpec {
97 pub format_id: QuantizationFormatId,
98 pub logical_values_per_block: u32,
99 pub bytes_per_block: u32,
100}
101
102impl BlockQuantizationSpec {
103 pub fn validate(&self) -> Result<(), VNextError> {
104 if self.logical_values_per_block == 0 || self.bytes_per_block == 0 {
105 return Err(VNextError::InvalidExecutionPlan {
106 reason: format!("invalid block quantization format `{}`", self.format_id),
107 });
108 }
109 Ok(())
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(rename_all = "snake_case")]
115pub enum WeightEncoding {
116 Dense {
117 element_type: ElementType,
118 },
119 DenseAffine {
124 element_type: ElementType,
125 scale: CanonicalRational,
126 bias: CanonicalRational,
127 },
128 Quantized(QuantizationSpec),
129 BlockQuantized(BlockQuantizationSpec),
130}
131
132impl WeightEncoding {
133 pub const fn dense_element_type(&self) -> Option<ElementType> {
134 match self {
135 Self::Dense { element_type } | Self::DenseAffine { element_type, .. } => {
136 Some(*element_type)
137 }
138 Self::Quantized(_) | Self::BlockQuantized(_) => None,
139 }
140 }
141
142 pub(crate) fn physical_bytes(
143 &self,
144 dimensions: &[u64],
145 component_id: &WeightId,
146 ) -> Result<u64, VNextError> {
147 let elements =
148 checked_elements(dimensions).ok_or_else(|| VNextError::InvalidExecutionPlan {
149 reason: format!("physical component `{component_id}` size overflows u64"),
150 })?;
151 match self {
152 Self::Dense { element_type } | Self::DenseAffine { element_type, .. } => elements
153 .checked_mul(element_type.size_bytes())
154 .ok_or_else(|| VNextError::InvalidExecutionPlan {
155 reason: format!("physical component `{component_id}` byte size overflows u64"),
156 }),
157 Self::Quantized(_) => Ok(elements),
158 Self::BlockQuantized(spec) => {
159 spec.validate()?;
160 elements
161 .checked_mul(u64::from(spec.bytes_per_block))
162 .ok_or_else(|| VNextError::InvalidExecutionPlan {
163 reason: format!(
164 "physical block component `{component_id}` byte size overflows u64"
165 ),
166 })
167 }
168 }
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
175#[serde(rename_all = "snake_case")]
176pub enum WeightComponentRole {
177 Values,
178 PackedValues,
179 Scales,
180 ZeroPoints,
181 Indices,
182 Permutation,
183 Codebook,
184 Metadata,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
192#[serde(rename_all = "snake_case")]
193pub enum PhysicalWeightPadding {
194 Exact,
195 ZeroFill { padded_dimensions: Vec<u64> },
196}
197
198#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
205#[serde(rename_all = "snake_case")]
206pub enum PhysicalStorageLayout {
207 Contiguous {
208 padding: PhysicalWeightPadding,
209 },
210 Strided {
211 strides_in_elements: Vec<u64>,
212 padding: PhysicalWeightPadding,
213 },
214 Tiled {
215 tile_shape: Vec<u64>,
216 axis_order: Vec<u32>,
218 tile_strides_in_elements: Vec<u64>,
219 padding: PhysicalWeightPadding,
220 },
221}
222
223impl PhysicalStorageLayout {
224 pub fn exact_contiguous() -> Self {
225 Self::Contiguous {
226 padding: PhysicalWeightPadding::Exact,
227 }
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
232pub struct PhysicalWeightComponentBinding {
233 pub component_id: WeightId,
234 pub storage: PhysicalStorageLayout,
235}
236
237impl PhysicalWeightComponentBinding {
238 pub fn exact_contiguous(component_id: WeightId) -> Self {
239 Self {
240 component_id,
241 storage: PhysicalStorageLayout::exact_contiguous(),
242 }
243 }
244}
245
246#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
247pub struct AxisWeightComponent {
248 pub component: PhysicalWeightComponentBinding,
249 pub axis: u32,
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253pub struct CompositeWeightPart {
254 pub layout: Box<PhysicalWeightLayout>,
255 pub logical_offsets: Vec<u64>,
256 pub extents: Vec<u64>,
257}
258
259pub const MAX_PHYSICAL_WEIGHT_LAYOUT_DEPTH: usize = 16;
263pub const MAX_PHYSICAL_WEIGHT_LAYOUT_NODES: usize = 4096;
264
265#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270#[serde(rename_all = "snake_case")]
271pub enum PhysicalWeightLayout {
272 Dense {
275 component_id: WeightId,
276 },
277 Stored {
278 component: PhysicalWeightComponentBinding,
279 },
280 Composite {
281 parts: Vec<CompositeWeightPart>,
282 },
283 Quantized {
284 packed_values: PhysicalWeightComponentBinding,
285 packed_dimensions: Vec<u64>,
289 scales: PhysicalWeightComponentBinding,
290 zero_points: Option<PhysicalWeightComponentBinding>,
291 axis_indices: Option<AxisWeightComponent>,
295 permutation: Option<AxisWeightComponent>,
296 codebook: Option<PhysicalWeightComponentBinding>,
297 group_axis: u32,
298 group_padding: PhysicalWeightPadding,
299 },
300 BlockQuantized {
304 blocks: PhysicalWeightComponentBinding,
305 block_axis: u32,
306 block_padding: PhysicalWeightPadding,
307 },
308 AxisReshapePermutation {
313 values: Box<PhysicalWeightLayout>,
314 axis: u32,
315 logical_offset: u64,
316 extent: u64,
317 reshape: Vec<u64>,
318 stored_axis_order: Vec<u32>,
321 },
322 Indexed {
323 indices: AxisWeightComponent,
324 values: Box<PhysicalWeightLayout>,
325 source_axis_extent: u64,
326 },
327 ExpertStack {
328 experts: Vec<PhysicalWeightLayout>,
329 expert_axis: u32,
330 },
331}
332
333impl PhysicalWeightLayout {
334 pub(crate) fn normalize(&mut self) {
335 match self {
336 Self::Composite { parts } => {
337 for part in parts.iter_mut() {
338 part.layout.normalize();
339 }
340 parts.sort_by(|left, right| {
344 left.logical_offsets
345 .cmp(&right.logical_offsets)
346 .then_with(|| left.extents.cmp(&right.extents))
347 });
348 }
349 Self::AxisReshapePermutation { values, .. } | Self::Indexed { values, .. } => {
350 values.normalize()
351 }
352 Self::ExpertStack { experts, .. } => {
353 for expert in experts {
356 expert.normalize();
357 }
358 }
359 Self::Dense { .. }
360 | Self::Stored { .. }
361 | Self::Quantized { .. }
362 | Self::BlockQuantized { .. } => {}
363 }
364 }
365}
366
367#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
371pub struct ResolvedWeightComponentLayout {
372 component_id: WeightId,
373 role: WeightComponentRole,
374 physical_dimensions: Vec<u64>,
375 encoding: WeightEncoding,
376}
377
378impl ResolvedWeightComponentLayout {
379 pub(crate) fn from_parts(
380 component_id: WeightId,
381 role: WeightComponentRole,
382 physical_dimensions: Vec<u64>,
383 encoding: WeightEncoding,
384 ) -> Self {
385 Self {
386 component_id,
387 role,
388 physical_dimensions,
389 encoding,
390 }
391 }
392
393 pub fn component_id(&self) -> &WeightId {
394 &self.component_id
395 }
396
397 pub const fn role(&self) -> WeightComponentRole {
398 self.role
399 }
400
401 pub fn physical_dimensions(&self) -> &[u64] {
402 &self.physical_dimensions
403 }
404
405 pub fn encoding(&self) -> &WeightEncoding {
406 &self.encoding
407 }
408
409 pub fn physical_bytes(&self) -> Result<u64, VNextError> {
410 self.encoding
411 .physical_bytes(&self.physical_dimensions, &self.component_id)
412 }
413
414 pub fn physical_element_type(&self) -> ElementType {
415 self.encoding
416 .dense_element_type()
417 .unwrap_or(ElementType::U8)
418 }
419}
420
421#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
430pub struct ResolvedWeightBinding {
431 weight_id: WeightId,
432 #[serde(rename = "format_id")]
433 schema_format_id: WeightFormatId,
434 layout_id: WeightLayoutId,
435 schema_version: ContractVersion,
436 physical_layout: PhysicalWeightLayout,
437 components: Vec<ResolvedWeightComponentLayout>,
438}
439
440pub(crate) trait ResolvedWeightLogicalValidation {
447 fn validate_logical_contract(
448 &self,
449 logical_dimensions: &[u64],
450 logical_element_type: ElementType,
451 ) -> Result<(), VNextError>;
452}
453
454#[derive(Deserialize)]
455#[serde(deny_unknown_fields)]
456struct ResolvedWeightBindingWire {
457 weight_id: WeightId,
458 format_id: WeightFormatId,
459 layout_id: WeightLayoutId,
460 schema_version: ContractVersion,
461 physical_layout: PhysicalWeightLayout,
462 components: Vec<ResolvedWeightComponentLayout>,
463}
464
465impl<'de> Deserialize<'de> for ResolvedWeightBinding {
466 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
467 where
468 D: Deserializer<'de>,
469 {
470 let wire = ResolvedWeightBindingWire::deserialize(deserializer)?;
471 Self::from_parts(
472 wire.weight_id,
473 wire.format_id,
474 wire.layout_id,
475 wire.schema_version,
476 wire.physical_layout,
477 wire.components,
478 )
479 .map_err(serde::de::Error::custom)
480 }
481}
482
483impl ResolvedWeightBinding {
484 pub(crate) fn from_parts(
485 weight_id: WeightId,
486 schema_format_id: WeightFormatId,
487 layout_id: WeightLayoutId,
488 schema_version: ContractVersion,
489 physical_layout: PhysicalWeightLayout,
490 components: Vec<ResolvedWeightComponentLayout>,
491 ) -> Result<Self, VNextError> {
492 let binding = Self {
493 weight_id,
494 schema_format_id,
495 layout_id,
496 schema_version,
497 physical_layout,
498 components,
499 };
500 binding.validate_structure()?;
501 Ok(binding)
502 }
503
504 pub(crate) fn validate_structure(&self) -> Result<(), VNextError> {
505 validate_physical_layout_budget(&self.physical_layout).map_err(|reason| {
506 VNextError::InvalidExecutionPlan {
507 reason: format!("resolved weight `{}` layout: {reason}", self.weight_id),
508 }
509 })?;
510 let referenced = physical_component_ids(&self.physical_layout).map_err(|reason| {
511 VNextError::InvalidExecutionPlan {
512 reason: format!("resolved weight `{}` layout: {reason}", self.weight_id),
513 }
514 })?;
515 let component_ids = self
516 .components
517 .iter()
518 .map(|component| component.component_id.clone())
519 .collect::<BTreeSet<_>>();
520 let canonical_components = self
521 .components
522 .windows(2)
523 .all(|pair| pair[0].component_id < pair[1].component_id);
524 if self.schema_version.major == 0
525 || self.components.is_empty()
526 || !canonical_components
527 || component_ids.len() != self.components.len()
528 || component_ids != referenced
529 || self.components.iter().any(|component| {
530 component.physical_dimensions.is_empty()
531 || component
532 .physical_dimensions
533 .iter()
534 .any(|extent| *extent == 0)
535 || component.physical_bytes().is_err()
536 })
537 {
538 return Err(VNextError::InvalidExecutionPlan {
539 reason: format!(
540 "resolved weight `{}` physical identity is invalid or non-canonical",
541 self.weight_id
542 ),
543 });
544 }
545 Ok(())
546 }
547
548 pub fn weight_id(&self) -> &WeightId {
549 &self.weight_id
550 }
551
552 pub(crate) fn schema_format_id(&self) -> &WeightFormatId {
556 &self.schema_format_id
557 }
558
559 pub fn layout_id(&self) -> &WeightLayoutId {
560 &self.layout_id
561 }
562
563 pub const fn schema_version(&self) -> ContractVersion {
564 self.schema_version
565 }
566
567 pub fn physical_layout(&self) -> &PhysicalWeightLayout {
568 &self.physical_layout
569 }
570
571 pub fn components(&self) -> &[ResolvedWeightComponentLayout] {
572 &self.components
573 }
574
575 pub fn quantization_formats(&self) -> BTreeSet<QuantizationFormatId> {
576 self.components
577 .iter()
578 .filter_map(|component| match &component.encoding {
579 WeightEncoding::Quantized(spec) => Some(spec.format_id.clone()),
580 WeightEncoding::BlockQuantized(spec) => Some(spec.format_id.clone()),
581 WeightEncoding::Dense { .. } | WeightEncoding::DenseAffine { .. } => None,
582 })
583 .collect()
584 }
585}
586
587fn push_physical_layout_child<'a>(
588 stack: &mut Vec<(&'a PhysicalWeightLayout, usize)>,
589 child: &'a PhysicalWeightLayout,
590 child_depth: usize,
591 visited: usize,
592) -> Result<(), String> {
593 if visited
594 .checked_add(stack.len())
595 .is_none_or(|pending| pending >= MAX_PHYSICAL_WEIGHT_LAYOUT_NODES)
596 {
597 return Err(format!(
598 "physical layout node count exceeds {MAX_PHYSICAL_WEIGHT_LAYOUT_NODES}"
599 ));
600 }
601 stack.push((child, child_depth));
602 Ok(())
603}
604
605pub(crate) fn validate_physical_layout_budget(layout: &PhysicalWeightLayout) -> Result<(), String> {
606 let mut stack = vec![(layout, 1_usize)];
607 let mut visited = 0_usize;
608 while let Some((node, depth)) = stack.pop() {
609 if depth > MAX_PHYSICAL_WEIGHT_LAYOUT_DEPTH {
610 return Err(format!(
611 "physical layout depth exceeds {MAX_PHYSICAL_WEIGHT_LAYOUT_DEPTH}"
612 ));
613 }
614 let direct_bindings = match node {
615 PhysicalWeightLayout::Dense { .. } | PhysicalWeightLayout::Stored { .. } => 1,
616 PhysicalWeightLayout::Quantized {
617 zero_points,
618 axis_indices,
619 permutation,
620 codebook,
621 ..
622 } => {
623 2 + usize::from(zero_points.is_some())
624 + usize::from(axis_indices.is_some())
625 + usize::from(permutation.is_some())
626 + usize::from(codebook.is_some())
627 }
628 PhysicalWeightLayout::BlockQuantized { .. } => 1,
629 PhysicalWeightLayout::AxisReshapePermutation { .. } => 0,
630 PhysicalWeightLayout::Indexed { .. } => 1,
631 PhysicalWeightLayout::Composite { .. } | PhysicalWeightLayout::ExpertStack { .. } => 0,
632 };
633 visited = visited
634 .checked_add(1 + direct_bindings)
635 .ok_or_else(|| "physical layout node count overflows usize".to_owned())?;
636 if visited > MAX_PHYSICAL_WEIGHT_LAYOUT_NODES {
637 return Err(format!(
638 "physical layout node count exceeds {MAX_PHYSICAL_WEIGHT_LAYOUT_NODES}"
639 ));
640 }
641 let child_depth = depth
642 .checked_add(1)
643 .ok_or_else(|| "physical layout depth overflows usize".to_owned())?;
644 match node {
645 PhysicalWeightLayout::Composite { parts } => {
646 for part in parts {
647 push_physical_layout_child(&mut stack, &part.layout, child_depth, visited)?;
648 }
649 }
650 PhysicalWeightLayout::AxisReshapePermutation { values, .. }
651 | PhysicalWeightLayout::Indexed { values, .. } => {
652 push_physical_layout_child(&mut stack, values, child_depth, visited)?;
653 }
654 PhysicalWeightLayout::ExpertStack { experts, .. } => {
655 for expert in experts {
656 push_physical_layout_child(&mut stack, expert, child_depth, visited)?;
657 }
658 }
659 PhysicalWeightLayout::Dense { .. }
660 | PhysicalWeightLayout::Stored { .. }
661 | PhysicalWeightLayout::Quantized { .. }
662 | PhysicalWeightLayout::BlockQuantized { .. } => {}
663 }
664 }
665 Ok(())
666}
667
668pub(crate) fn physical_component_ids(
669 layout: &PhysicalWeightLayout,
670) -> Result<BTreeSet<WeightId>, String> {
671 validate_physical_layout_budget(layout)?;
672 let mut ids = BTreeSet::new();
673 let mut stack = vec![layout];
674 while let Some(node) = stack.pop() {
675 let mut insert_binding = |binding: &PhysicalWeightComponentBinding| {
676 ids.insert(binding.component_id.clone());
677 };
678 match node {
679 PhysicalWeightLayout::Dense { component_id } => {
680 ids.insert(component_id.clone());
681 }
682 PhysicalWeightLayout::Stored { component } => insert_binding(component),
683 PhysicalWeightLayout::Composite { parts } => {
684 stack.extend(parts.iter().map(|part| part.layout.as_ref()));
685 }
686 PhysicalWeightLayout::Quantized {
687 packed_values,
688 scales,
689 zero_points,
690 axis_indices,
691 permutation,
692 codebook,
693 ..
694 } => {
695 insert_binding(packed_values);
696 insert_binding(scales);
697 if let Some(binding) = zero_points {
698 insert_binding(binding);
699 }
700 if let Some(axis_component) = axis_indices {
701 insert_binding(&axis_component.component);
702 }
703 if let Some(axis_component) = permutation {
704 insert_binding(&axis_component.component);
705 }
706 if let Some(binding) = codebook {
707 insert_binding(binding);
708 }
709 }
710 PhysicalWeightLayout::BlockQuantized { blocks, .. } => insert_binding(blocks),
711 PhysicalWeightLayout::AxisReshapePermutation { values, .. } => stack.push(values),
712 PhysicalWeightLayout::Indexed {
713 indices, values, ..
714 } => {
715 insert_binding(&indices.component);
716 stack.push(values);
717 }
718 PhysicalWeightLayout::ExpertStack { experts, .. } => {
719 stack.extend(experts);
720 }
721 }
722 }
723 Ok(ids)
724}
725
726pub(crate) fn checked_elements(dimensions: &[u64]) -> Option<u64> {
727 dimensions
728 .iter()
729 .try_fold(1_u64, |elements, extent| elements.checked_mul(*extent))
730}