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