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