Skip to main content

ferrum_interfaces/vnext/operation/
weight_contract.rs

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/// How values are partitioned by a quantized physical layout.
21///
22/// `WholeAxis` is shape-relative by design: all values on the group axis
23/// share one scale. This represents channelwise quantization without making a
24/// matrix dimension part of the otherwise stable quantization-format ABI.
25/// `Block2d` carries the exact rectangular source block shape; the physical
26/// layout separately binds its two dimensions to ordered logical axes.
27#[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            // Multidimensional grouping has no meaningful single-axis size.
63            // Zero makes accidental use by a legacy single-axis validator
64            // fail closed instead of silently choosing one block dimension.
65            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/// Self-contained fixed-size quantization blocks such as GGML/GGUF Q4_K and
114/// Q6_K. Per-block scales, minima, and packed values are part of the opaque
115/// block ABI identified by `format_id`; providers must not reinterpret these
116/// bytes as the separate-scale [`QuantizationSpec`] representation.
117#[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    /// Dense floating-point values materialized after applying
142    /// `logical = physical * scale + bias` element-wise. This keeps checkpoint
143    /// representation semantics in the typed weight schema rather than in a
144    /// backend provider or model-name branch.
145    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/// Structural role of a physical component in a weight format. The role is
195/// intentionally independent of any named quantization or model family.
196#[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/// Padding is always explicit and carries the exact semantic padded shape.
210/// `Exact` has no hidden storage extension. `ZeroFill` must increase at least
211/// one dimension and, for tiled or grouped storage, must be the unique minimal
212/// shape implied by that contract.
213#[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/// Storage geometry for one physical component binding. Strides are measured
221/// in the component's schema storage unit: elements for dense encodings,
222/// bytes for separate-component packing, and blocks for block quantization.
223/// The component's declared dimensions describe its raw stored span, while
224/// this geometry maps the semantic component shape onto that span without
225/// inference or hidden padding.
226#[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        /// Physical tile-grid axis -> semantic component axis.
239        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
281/// Hard bounds keep directly constructed and deserialized recursive schemas
282/// cheap to validate. Ownership makes cycles unrepresentable; these limits
283/// additionally bound adversarial depth and fan-out.
284pub const MAX_PHYSICAL_WEIGHT_LAYOUT_DEPTH: usize = 16;
285pub const MAX_PHYSICAL_WEIGHT_LAYOUT_NODES: usize = 4096;
286
287/// Typed physical storage tree for one logical weight. Every leaf binds one
288/// physical component exactly once. Recursive composition allows indexing or
289/// expert stacking around dense, tiled, strided, or quantized values without
290/// architecture-specific cases.
291#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
292#[serde(rename_all = "snake_case")]
293pub enum PhysicalWeightLayout {
294    /// Exact, contiguous dense values. This common leaf deliberately remains
295    /// compact; use `Stored` for explicit stride, tile, or padding geometry.
296    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        /// Semantic packed-storage shape before the binding's optional
308        /// stride/tile mapping. Its element product must equal the exact byte
309        /// count implied by logical elements and `bits_per_weight`.
310        packed_dimensions: Vec<u64>,
311        scales: PhysicalWeightComponentBinding,
312        zero_points: Option<PhysicalWeightComponentBinding>,
313        /// Optional packed storage shape for asymmetric zero points. When
314        /// absent, one dense zero-point scalar is stored per quantization
315        /// group. When present, it must contain exactly `bits_per_weight`
316        /// bits per group and the bound component dtype describes the packed
317        /// storage word (for example I32 words containing eight INT4 values).
318        zero_point_packed_dimensions: Option<Vec<u64>>,
319        /// Per-coordinate group assignment. Its semantic shape is the one
320        /// dimensional logical axis extent, not the multi-dimensional group
321        /// tensor shape.
322        axis_indices: Option<AxisWeightComponent>,
323        permutation: Option<AxisWeightComponent>,
324        codebook: Option<PhysicalWeightComponentBinding>,
325        group_axis: u32,
326        group_padding: PhysicalWeightPadding,
327    },
328    /// Separate-scale quantization over a rectangular two-dimensional block
329    /// grid. `block_axes` are ordered and canonical; the corresponding block
330    /// sizes live losslessly in [`QuantizationGrouping::Block2d`]. Boundary
331    /// blocks may be partial, so scale extents use ceiling division without
332    /// implying padded packed-value storage.
333    QuantizedBlockGrid {
334        packed_values: PhysicalWeightComponentBinding,
335        /// Semantic packed-storage shape. Its element product must equal the
336        /// exact byte count implied by the unpadded logical tensor.
337        packed_dimensions: Vec<u64>,
338        scales: PhysicalWeightComponentBinding,
339        block_axes: [u32; 2],
340    },
341    /// One opaque, self-contained quantization block represents a fixed
342    /// number of logical values along `block_axis`. The bound component shape
343    /// is the padded logical shape with that axis divided by the block width.
344    BlockQuantized {
345        blocks: PhysicalWeightComponentBinding,
346        block_axis: u32,
347        block_padding: PhysicalWeightPadding,
348    },
349    /// A contiguous logical subrange on one axis is stored by reshaping that
350    /// subrange and permuting the reshape axes. This captures checkpoint
351    /// layouts such as grouped-to-tiled head order without a model flag,
352    /// synthetic index tensor, or eager repack.
353    AxisReshapePermutation {
354        values: Box<PhysicalWeightLayout>,
355        axis: u32,
356        logical_offset: u64,
357        extent: u64,
358        reshape: Vec<u64>,
359        /// Stored axis position -> reshaped logical axis, matching an
360        /// n-dimensional transpose/permute order.
361        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                // Offsets make composite placement semantic and order-free.
382                // Validation subsequently proves that no two placements
383                // overlap, so this order cannot reorder an ordered sequence.
384                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                // Expert vector position is the expert index and is therefore
395                // semantic. Normalize descendants without sorting the vector.
396                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/// Provider-visible physical identity for one component of a resolved weight.
410/// Source file names are intentionally excluded: source provenance belongs to
411/// the prepared family fingerprint, while providers need shape, role, and ABI.
412#[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/// Immutable physical weight contract carried by an execution-plan binding.
464/// This prevents the provider boundary from collapsing a quantized/composite
465/// layout into only resource ranges and a synthetic `u8` dtype.
466///
467/// `schema_format_id` identifies the enclosing source or materialized schema.
468/// It is a planning compatibility key, not the physical ABI of every component
469/// referenced by this binding. Providers must decode components from
470/// `physical_layout` and `components`.
471#[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
482/// Operation-owned capability for validating a resolved physical binding
483/// against the logical tensor contract supplied by the model layer.
484///
485/// The physical ABI stays independent of model schemas; the model owner
486/// provides the schema-aware implementation without introducing an
487/// operation-to-model dependency.
488pub(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    /// Enclosing schema/container identity used by provider selection.
595    ///
596    /// This must not be used to infer a bound component's physical encoding.
597    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}