Skip to main content

eredu_checkpoint/
recipe.rs

1//! Backend-neutral derived-weight recipes and shape inference.
2
3use std::collections::{BTreeMap, BTreeSet};
4
5use crate::store::{CheckpointSource, StoreError, TensorMetadata, TensorSelection, WeightStore};
6use crate::StoredDtype;
7
8/// Metadata-only catalog used to validate a derived-weight recipe.
9pub trait RecipeCatalog {
10    /// Returns source tensor metadata without reading its payload.
11    fn tensor_metadata(&self, key: &str) -> Result<TensorMetadata, StoreError>;
12}
13
14/// Cold-path capability for proving that every recipe source can be read with
15/// its declared physical bound.
16pub trait BoundedRecipeSource: RecipeCatalog {
17    /// Acquires and immediately releases one source under bounded-read policy.
18    fn verify_bounded_source(
19        &self,
20        key: &str,
21        selection: TensorSelection,
22    ) -> Result<(), StoreError>;
23}
24
25impl<T: WeightStore> BoundedRecipeSource for T {
26    fn verify_bounded_source(
27        &self,
28        key: &str,
29        selection: TensorSelection,
30    ) -> Result<(), StoreError> {
31        drop(self.acquire(crate::store::TensorReadRequest {
32            key: key.to_owned(),
33            selection,
34            policy: crate::store::ReadPolicy::RequireBounded,
35        })?);
36        Ok(())
37    }
38}
39
40impl<T: WeightStore> RecipeCatalog for T {
41    fn tensor_metadata(&self, key: &str) -> Result<TensorMetadata, StoreError> {
42        self.metadata(key)
43    }
44}
45
46impl RecipeCatalog for dyn CheckpointSource + '_ {
47    fn tensor_metadata(&self, key: &str) -> Result<TensorMetadata, StoreError> {
48        self.source_metadata(key)
49    }
50}
51
52impl BoundedRecipeSource for dyn CheckpointSource + '_ {
53    fn verify_bounded_source(
54        &self,
55        key: &str,
56        selection: TensorSelection,
57    ) -> Result<(), StoreError> {
58        drop(self.acquire_lease(crate::store::TensorReadRequest {
59            key: key.to_owned(),
60            selection,
61            policy: crate::store::ReadPolicy::RequireBounded,
62        })?);
63        Ok(())
64    }
65}
66
67/// Scalar representation produced by a recipe operation.
68#[derive(Debug, Clone, Eq, PartialEq)]
69#[non_exhaustive]
70#[allow(missing_docs)]
71pub enum RecipeDtype {
72    Bool,
73    U8,
74    I8,
75    I16,
76    U16,
77    F16,
78    BF16,
79    I32,
80    U32,
81    F32,
82    F64,
83    I64,
84    U64,
85    C64,
86    F8E4M3,
87    F8E5M2,
88    F4,
89    F8E8M0,
90    Other(String),
91}
92
93impl RecipeDtype {
94    /// Returns the exact scalar representation width in bits.
95    pub fn bit_width(&self) -> Result<u64, RecipeError> {
96        match self {
97            Self::F4 => Ok(4),
98            Self::Bool | Self::U8 | Self::I8 | Self::F8E4M3 | Self::F8E5M2 | Self::F8E8M0 => Ok(8),
99            Self::I16 | Self::U16 | Self::F16 | Self::BF16 => Ok(16),
100            Self::I32 | Self::U32 | Self::F32 => Ok(32),
101            Self::F64 | Self::I64 | Self::U64 | Self::C64 => Ok(64),
102            Self::Other(dtype) => Err(RecipeError::UnsupportedDtype {
103                dtype: dtype.clone(),
104            }),
105        }
106    }
107}
108
109impl From<StoredDtype> for RecipeDtype {
110    fn from(value: StoredDtype) -> Self {
111        match value {
112            StoredDtype::Bool => Self::Bool,
113            StoredDtype::U8 => Self::U8,
114            StoredDtype::I8 => Self::I8,
115            StoredDtype::I16 => Self::I16,
116            StoredDtype::U16 => Self::U16,
117            StoredDtype::F16 => Self::F16,
118            StoredDtype::BF16 => Self::BF16,
119            StoredDtype::I32 => Self::I32,
120            StoredDtype::U32 => Self::U32,
121            StoredDtype::F32 => Self::F32,
122            StoredDtype::F64 => Self::F64,
123            StoredDtype::I64 => Self::I64,
124            StoredDtype::U64 => Self::U64,
125            StoredDtype::C64 => Self::C64,
126            StoredDtype::F8E4M3 => Self::F8E4M3,
127            StoredDtype::F8E5M2 => Self::F8E5M2,
128            StoredDtype::F4 => Self::F4,
129            StoredDtype::F8E8M0 => Self::F8E8M0,
130            StoredDtype::Other(dtype) => Self::Other(dtype),
131        }
132    }
133}
134
135/// Shape, representation, and byte size inferred for a recipe.
136#[derive(Debug, Clone, Eq, PartialEq)]
137pub struct RecipeMetadata {
138    /// Inferred logical output shape.
139    pub shape: Vec<usize>,
140    /// Inferred output scalar representation.
141    pub dtype: RecipeDtype,
142    /// Exact encoded or materialized output byte count.
143    pub byte_len: u64,
144}
145
146/// A named recipe collection that becomes observable only after every output
147/// has passed metadata inference. This is the atomic unit used for fused
148/// weights and their affine or FP8 companions.
149#[derive(Debug, Clone, Eq, PartialEq)]
150pub struct AtomicRecipeSet {
151    outputs: BTreeMap<String, DerivedWeightRecipe>,
152    aliases: BTreeMap<String, String>,
153}
154
155impl AtomicRecipeSet {
156    /// Validates all target names, rejects collisions, and infers every recipe
157    /// before returning any bindable output.
158    pub fn new<C: RecipeCatalog + ?Sized>(
159        catalog: &C,
160        outputs: impl IntoIterator<Item = (String, DerivedWeightRecipe)>,
161    ) -> Result<Self, RecipeError> {
162        Self::new_with_aliases(catalog, outputs, std::iter::empty())
163    }
164
165    /// Validates canonical outputs and logical aliases as one publication.
166    ///
167    /// Alias destinations may name another alias in the input declarations,
168    /// but the returned map always points directly at a canonical output.
169    /// No output or alias is observable when any recipe or alias is invalid.
170    pub fn new_with_aliases<C: RecipeCatalog + ?Sized>(
171        catalog: &C,
172        outputs: impl IntoIterator<Item = (String, DerivedWeightRecipe)>,
173        aliases: impl IntoIterator<Item = RecipeAlias>,
174    ) -> Result<Self, RecipeError> {
175        let mut validated = BTreeMap::new();
176        for (target, recipe) in outputs {
177            if target.trim().is_empty() {
178                return Err(RecipeError::EmptyOutputName);
179            }
180            if validated.insert(target.clone(), recipe).is_some() {
181                return Err(RecipeError::DuplicateOutput { target });
182            }
183        }
184        if validated.is_empty() {
185            return Err(RecipeError::EmptyOutputs);
186        }
187        for recipe in validated.values() {
188            recipe.infer(catalog)?;
189        }
190        let aliases = validate_recipe_aliases(validated.keys(), aliases)?;
191        Ok(Self {
192            outputs: validated,
193            aliases,
194        })
195    }
196
197    /// Returns the validated recipe for one canonical output.
198    pub fn get(&self, target: &str) -> Option<&DerivedWeightRecipe> {
199        self.outputs.get(target)
200    }
201
202    /// Resolves a canonical output or logical alias without cloning its recipe.
203    pub fn get_resolved(&self, target: &str) -> Option<(&str, &DerivedWeightRecipe)> {
204        let owner = self
205            .aliases
206            .get(target)
207            .map(String::as_str)
208            .unwrap_or(target);
209        self.outputs
210            .get_key_value(owner)
211            .map(|(owner, recipe)| (owner.as_str(), recipe))
212    }
213
214    /// Iterates canonical outputs in stable sorted order.
215    pub fn iter(&self) -> impl Iterator<Item = (&str, &DerivedWeightRecipe)> {
216        self.outputs
217            .iter()
218            .map(|(target, recipe)| (target.as_str(), recipe))
219    }
220
221    /// Iterates logical alias and canonical-owner identities in stable order.
222    pub fn aliases(&self) -> impl Iterator<Item = (&str, &str)> {
223        self.aliases
224            .iter()
225            .map(|(alias, owner)| (alias.as_str(), owner.as_str()))
226    }
227
228    /// Consumes the validated set for a backend binding plan.
229    pub fn into_outputs(self) -> BTreeMap<String, DerivedWeightRecipe> {
230        self.outputs
231    }
232
233    /// Consumes the publication into canonical recipes and logical aliases.
234    pub fn into_parts(
235        self,
236    ) -> (
237        BTreeMap<String, DerivedWeightRecipe>,
238        BTreeMap<String, String>,
239    ) {
240        (self.outputs, self.aliases)
241    }
242}
243
244/// One logical parameter alias published alongside canonical recipes.
245#[derive(Debug, Clone, Eq, PartialEq)]
246pub struct RecipeAlias {
247    /// Logical alias identity.
248    pub alias: String,
249    /// Canonical output or another declared alias.
250    pub destination: String,
251}
252
253impl RecipeAlias {
254    /// Creates an alias declaration validated when its recipe set is published.
255    pub fn new(alias: impl Into<String>, destination: impl Into<String>) -> Self {
256        Self {
257            alias: alias.into(),
258            destination: destination.into(),
259        }
260    }
261}
262
263fn validate_recipe_aliases<'a>(
264    outputs: impl IntoIterator<Item = &'a String>,
265    aliases: impl IntoIterator<Item = RecipeAlias>,
266) -> Result<BTreeMap<String, String>, RecipeError> {
267    let outputs = outputs.into_iter().cloned().collect::<BTreeSet<_>>();
268    let mut declarations = BTreeMap::new();
269    for declaration in aliases {
270        if declaration.alias.trim().is_empty() {
271            return Err(RecipeError::EmptyAliasName);
272        }
273        if declaration.destination.trim().is_empty() {
274            return Err(RecipeError::InvalidAliasDestination {
275                alias: declaration.alias,
276                destination: declaration.destination,
277            });
278        }
279        if outputs.contains(&declaration.alias) {
280            return Err(RecipeError::AliasOutputCollision {
281                alias: declaration.alias,
282            });
283        }
284        if declarations
285            .insert(declaration.alias.clone(), declaration.destination)
286            .is_some()
287        {
288            return Err(RecipeError::DuplicateAlias {
289                alias: declaration.alias,
290            });
291        }
292    }
293
294    fn resolve(
295        alias: &str,
296        outputs: &BTreeSet<String>,
297        declarations: &BTreeMap<String, String>,
298        resolved: &mut BTreeMap<String, String>,
299        visiting: &mut BTreeSet<String>,
300    ) -> Result<String, RecipeError> {
301        if let Some(owner) = resolved.get(alias) {
302            return Ok(owner.clone());
303        }
304        if !visiting.insert(alias.to_owned()) {
305            return Err(RecipeError::AliasCycle {
306                alias: alias.to_owned(),
307            });
308        }
309        let destination = declarations
310            .get(alias)
311            .expect("resolver receives a declared alias");
312        let owner = if outputs.contains(destination) {
313            destination.clone()
314        } else if declarations.contains_key(destination) {
315            resolve(destination, outputs, declarations, resolved, visiting)?
316        } else {
317            return Err(RecipeError::InvalidAliasDestination {
318                alias: alias.to_owned(),
319                destination: destination.clone(),
320            });
321        };
322        visiting.remove(alias);
323        resolved.insert(alias.to_owned(), owner.clone());
324        Ok(owner)
325    }
326
327    let mut resolved = BTreeMap::new();
328    for alias in declarations.keys() {
329        resolve(
330            alias,
331            &outputs,
332            &declarations,
333            &mut resolved,
334            &mut BTreeSet::new(),
335        )?;
336    }
337    Ok(resolved)
338}
339
340/// One named weight or quantization-companion recipe in a matrix family.
341#[derive(Debug, Clone, Eq, PartialEq)]
342pub struct MatrixRecipeMember {
343    /// Canonical target identity published by the family.
344    pub target: String,
345    /// Recipe producing the target value.
346    pub recipe: DerivedWeightRecipe,
347}
348
349impl MatrixRecipeMember {
350    /// Creates one member validated when its family is constructed.
351    pub fn new(target: impl Into<String>, recipe: DerivedWeightRecipe) -> Self {
352        Self {
353            target: target.into(),
354            recipe,
355        }
356    }
357}
358
359/// Atomic weight, scale, and optional affine-bias recipe family.
360///
361/// Every companion must have the weight's rank and leading matrix geometry;
362/// only the final packed/group dimension may differ. Transformations return a
363/// new validated family, so malformed members never become partially visible.
364#[derive(Debug, Clone, Eq, PartialEq)]
365pub struct AtomicMatrixRecipeFamily {
366    weight: MatrixRecipeMember,
367    scales: Option<MatrixRecipeMember>,
368    biases: Option<MatrixRecipeMember>,
369}
370
371impl AtomicMatrixRecipeFamily {
372    /// Validates a complete dense or packed matrix family.
373    pub fn new<C: RecipeCatalog + ?Sized>(
374        catalog: &C,
375        weight: MatrixRecipeMember,
376        scales: Option<MatrixRecipeMember>,
377        biases: Option<MatrixRecipeMember>,
378    ) -> Result<Self, RecipeError> {
379        if biases.is_some() && scales.is_none() {
380            return Err(RecipeError::MatrixBiasWithoutScales);
381        }
382        let family = Self {
383            weight,
384            scales,
385            biases,
386        };
387        family.validate(catalog)?;
388        Ok(family)
389    }
390
391    /// Canonical weight member.
392    pub const fn weight(&self) -> &MatrixRecipeMember {
393        &self.weight
394    }
395
396    /// Optional quantization-scale member.
397    pub const fn scales(&self) -> Option<&MatrixRecipeMember> {
398        self.scales.as_ref()
399    }
400
401    /// Optional affine-bias member.
402    pub const fn biases(&self) -> Option<&MatrixRecipeMember> {
403        self.biases.as_ref()
404    }
405
406    /// Applies one bounded range or ordered-index selection on leading axis 0.
407    ///
408    /// The identical logical selection is pushed through the weight and every
409    /// present companion before the resulting family is validated atomically.
410    pub fn select_leading_axis<C: RecipeCatalog + ?Sized>(
411        &self,
412        catalog: &C,
413        selection: TensorSelection,
414    ) -> Result<Self, RecipeError> {
415        match &selection {
416            TensorSelection::Full
417            | TensorSelection::Range { axis: 0, .. }
418            | TensorSelection::Indices { axis: 0, .. } => {}
419            TensorSelection::Range { axis, .. } | TensorSelection::Indices { axis, .. } => {
420                return Err(RecipeError::MatrixFamilySelectionAxis { axis: *axis });
421            }
422            TensorSelection::Contiguous { .. } => {
423                return Err(RecipeError::MatrixFamilyContiguousSelection);
424            }
425        }
426        let select = |member: &MatrixRecipeMember| -> Result<MatrixRecipeMember, RecipeError> {
427            Ok(MatrixRecipeMember {
428                target: member.target.clone(),
429                recipe: member.recipe.select_bounded(catalog, selection.clone())?,
430            })
431        };
432        Self::new(
433            catalog,
434            select(&self.weight)?,
435            self.scales.as_ref().map(select).transpose()?,
436            self.biases.as_ref().map(select).transpose()?,
437        )
438    }
439
440    /// Publishes this family and its aliases as one atomic recipe set.
441    pub fn publish<C: RecipeCatalog + ?Sized>(
442        &self,
443        catalog: &C,
444        aliases: impl IntoIterator<Item = RecipeAlias>,
445    ) -> Result<AtomicRecipeSet, RecipeError> {
446        self.validate(catalog)?;
447        AtomicRecipeSet::new_with_aliases(
448            catalog,
449            std::iter::once(&self.weight)
450                .chain(self.scales.iter())
451                .chain(self.biases.iter())
452                .map(|member| (member.target.clone(), member.recipe.clone())),
453            aliases,
454        )
455    }
456
457    fn validate<C: RecipeCatalog + ?Sized>(&self, catalog: &C) -> Result<(), RecipeError> {
458        let members = std::iter::once(&self.weight)
459            .chain(self.scales.iter())
460            .chain(self.biases.iter())
461            .collect::<Vec<_>>();
462        let mut targets = BTreeSet::new();
463        for member in &members {
464            if member.target.trim().is_empty() {
465                return Err(RecipeError::EmptyOutputName);
466            }
467            if !targets.insert(member.target.clone()) {
468                return Err(RecipeError::DuplicateOutput {
469                    target: member.target.clone(),
470                });
471            }
472        }
473        let weight = self.weight.recipe.infer(catalog)?;
474        if weight.shape.len() < 2 {
475            return Err(RecipeError::InvalidMatrixFamilyWeight {
476                shape: weight.shape,
477            });
478        }
479        let leading = &weight.shape[..weight.shape.len() - 1];
480        let mut scale_shape = None;
481        for (kind, member) in [
482            ("scales", self.scales.as_ref()),
483            ("biases", self.biases.as_ref()),
484        ] {
485            let Some(member) = member else { continue };
486            let metadata = member.recipe.infer(catalog)?;
487            if metadata.shape.len() != weight.shape.len()
488                || metadata.shape[..metadata.shape.len() - 1] != *leading
489            {
490                return Err(RecipeError::MatrixCompanionGeometry {
491                    member: kind,
492                    weight: weight.shape.clone(),
493                    companion: metadata.shape,
494                });
495            }
496            if kind == "scales" {
497                scale_shape = Some(metadata.shape);
498            } else if scale_shape.as_ref() != Some(&metadata.shape) {
499                return Err(RecipeError::MatrixScaleBiasGeometry {
500                    scales: scale_shape.unwrap_or_default(),
501                    biases: metadata.shape,
502                });
503            }
504        }
505        Ok(())
506    }
507}
508
509/// One canonical output range cut from a fused source axis.
510#[derive(Debug, Clone, Eq, PartialEq)]
511pub struct FusedSplitOutput {
512    /// Canonical target parameter identity.
513    pub target: String,
514    /// Positive width along the split axis.
515    pub width: usize,
516}
517
518/// One physical fused tensor participating in an atomic split family.
519///
520/// Weight, bias, affine companions, and inverse scales are represented by
521/// separate members so each may declare its actual physical axis and widths.
522#[derive(Debug, Clone, Eq, PartialEq)]
523pub struct FusedSplitMember {
524    /// Physical source tensor identity.
525    pub source: String,
526    /// Axis partitioned into ordered output ranges.
527    pub axis: usize,
528    /// Canonical targets in physical row order.
529    pub outputs: Vec<FusedSplitOutput>,
530}
531
532/// Builds an atomic grouped split for one or more fused physical members.
533pub fn atomic_fused_split_recipes<C: RecipeCatalog + ?Sized>(
534    catalog: &C,
535    members: impl IntoIterator<Item = FusedSplitMember>,
536) -> Result<AtomicRecipeSet, RecipeError> {
537    let mut recipes = Vec::new();
538    for member in members {
539        if member.source.trim().is_empty() {
540            return Err(RecipeError::EmptySourceKey);
541        }
542        if member.outputs.is_empty()
543            || member
544                .outputs
545                .iter()
546                .any(|output| output.target.trim().is_empty() || output.width == 0)
547        {
548            return Err(RecipeError::InvalidFusedSplit {
549                tensor: member.source,
550            });
551        }
552        let metadata = catalog.tensor_metadata(&member.source)?;
553        let dimension = metadata.logical_shape.get(member.axis).copied().ok_or(
554            RecipeError::InvalidSelectionAxis {
555                axis: member.axis,
556                rank: metadata.logical_shape.len(),
557            },
558        )?;
559        let total = member.outputs.iter().try_fold(0usize, |total, output| {
560            total
561                .checked_add(output.width)
562                .ok_or(RecipeError::ArithmeticOverflow("fused split widths"))
563        })?;
564        if total != dimension {
565            return Err(RecipeError::FusedSplitWidthMismatch {
566                tensor: member.source,
567                axis: member.axis,
568                dimension,
569                outputs: total,
570            });
571        }
572        let mut start = 0usize;
573        for output in member.outputs {
574            let end = start + output.width;
575            recipes.push((
576                output.target,
577                DerivedWeightRecipe::source(
578                    &member.source,
579                    TensorSelection::Range {
580                        axis: member.axis,
581                        start,
582                        end,
583                    },
584                ),
585            ));
586            start = end;
587        }
588    }
589    AtomicRecipeSet::new(catalog, recipes)
590}
591
592/// Creates and validates an ordered gather/permutation along one source axis.
593/// Duplicate indices are intentionally admitted for broadcast-style layouts;
594/// callers that require a permutation must supply unique indices.
595pub fn ordered_axis_selection<C: RecipeCatalog + ?Sized>(
596    catalog: &C,
597    source: impl Into<String>,
598    axis: usize,
599    indices: Vec<usize>,
600) -> Result<DerivedWeightRecipe, RecipeError> {
601    let recipe = DerivedWeightRecipe::source(source, TensorSelection::Indices { axis, indices });
602    recipe.infer(catalog)?;
603    Ok(recipe)
604}
605
606impl RecipeMetadata {
607    /// Returns the inferred output shape.
608    pub fn shape(&self) -> &[usize] {
609        &self.shape
610    }
611
612    /// Returns the inferred scalar representation.
613    pub const fn dtype(&self) -> &RecipeDtype {
614        &self.dtype
615    }
616
617    /// Returns the exact inferred byte count.
618    pub const fn byte_len(&self) -> u64 {
619        self.byte_len
620    }
621}
622
623/// Typed operations needed to derive a runtime parameter from checkpoint tensors.
624#[derive(Debug, Clone, Eq, PartialEq)]
625#[allow(missing_docs)]
626pub enum DerivedWeightRecipe {
627    Source {
628        key: String,
629        selection: TensorSelection,
630    },
631    Select {
632        input: Box<Self>,
633        selection: TensorSelection,
634    },
635    Concatenate {
636        axis: usize,
637        inputs: Vec<Self>,
638    },
639    Stack {
640        axis: usize,
641        inputs: Vec<Self>,
642    },
643    Reshape {
644        input: Box<Self>,
645        shape: Vec<usize>,
646    },
647    Transpose {
648        input: Box<Self>,
649        axes: Vec<usize>,
650    },
651    Cast {
652        input: Box<Self>,
653        dtype: RecipeDtype,
654    },
655    View {
656        input: Box<Self>,
657        dtype: RecipeDtype,
658        shape: Vec<usize>,
659    },
660    NegLog {
661        input: Box<Self>,
662    },
663    SubtractOne {
664        input: Box<Self>,
665    },
666}
667
668impl DerivedWeightRecipe {
669    /// Creates a recipe reading one selected checkpoint tensor.
670    pub fn source(key: impl Into<String>, selection: TensorSelection) -> Self {
671        Self::Source {
672            key: key.into(),
673            selection,
674        }
675    }
676
677    /// Proves that every physical source can honor its declared bounded read.
678    pub fn preflight_bounded<S: BoundedRecipeSource + ?Sized>(
679        &self,
680        source: &S,
681    ) -> Result<(), RecipeError> {
682        match self {
683            Self::Source { key, selection } => {
684                source.verify_bounded_source(key, selection.clone())?;
685            }
686            Self::Concatenate { inputs, .. } | Self::Stack { inputs, .. } => {
687                for input in inputs {
688                    input.preflight_bounded(source)?;
689                }
690            }
691            Self::Select { input, .. }
692            | Self::Reshape { input, .. }
693            | Self::Transpose { input, .. }
694            | Self::Cast { input, .. }
695            | Self::View { input, .. }
696            | Self::NegLog { input }
697            | Self::SubtractOne { input } => input.preflight_bounded(source)?,
698        }
699        Ok(())
700    }
701
702    /// Rewrites an output selection toward physically bounded sources.
703    pub fn select_bounded<C: RecipeCatalog + ?Sized>(
704        &self,
705        catalog: &C,
706        selection: TensorSelection,
707    ) -> Result<Self, RecipeError> {
708        let metadata = self.infer(catalog)?;
709        let selection = normalize_selection(selection, &metadata.shape)?;
710        let expected_shape = selected_shape(metadata.shape.clone(), &selection)?;
711        let expanded = expand_indexed_sources(self.clone());
712        let rewritten = normalize_bounded_source_ranges(
713            expand_indexed_sources(push_selection(&expanded, catalog, selection)?),
714            catalog,
715        )?;
716        let actual = rewritten.infer(catalog)?;
717        if actual.shape != expected_shape || actual.dtype != metadata.dtype {
718            return Err(RecipeError::SelectionPushdownUnsupported {
719                operation: "recipe",
720                reason: format!(
721                    "rewrite produced shape {:?} and dtype {:?}, expected {:?} and {:?}",
722                    actual.shape, actual.dtype, expected_shape, metadata.dtype
723                ),
724            });
725        }
726        Ok(rewritten)
727    }
728
729    /// Selects rows from one matrix while retaining leading singleton axes.
730    pub fn select_bounded_matrix_rows<C: RecipeCatalog + ?Sized>(
731        &self,
732        catalog: &C,
733        leading_index: usize,
734        start: usize,
735        end: usize,
736    ) -> Result<Self, RecipeError> {
737        let metadata = self.infer(catalog)?;
738        if metadata.shape.len() < 2 {
739            return Err(RecipeError::SelectionPushdownUnsupported {
740                operation: "matrix row selection",
741                reason: format!("rank {} has no matrix row axis", metadata.shape.len()),
742            });
743        }
744        let row_axis = metadata.shape.len() - 2;
745        let leading = usize::try_from(element_count(
746            &metadata.shape[..row_axis],
747            "leading matrix dimensions",
748        )?)
749        .map_err(|_| RecipeError::ArithmeticOverflow("leading matrix dimensions"))?;
750        if leading_index >= leading {
751            return Err(RecipeError::InvalidIndices {
752                axis: 0,
753                dimension: leading,
754            });
755        }
756        let mut coordinates = vec![0usize; row_axis];
757        let mut remainder = leading_index;
758        for axis in (0..row_axis).rev() {
759            let dimension = metadata.shape[axis];
760            coordinates[axis] = remainder % dimension;
761            remainder /= dimension;
762        }
763        let mut selected = self.clone();
764        for (axis, coordinate) in coordinates.into_iter().enumerate() {
765            selected = selected.select_bounded(
766                catalog,
767                TensorSelection::Range {
768                    axis,
769                    start: coordinate,
770                    end: coordinate + 1,
771                },
772            )?;
773        }
774        selected.select_bounded(
775            catalog,
776            TensorSelection::Range {
777                axis: row_axis,
778                start,
779                end,
780            },
781        )
782    }
783
784    /// Returns a conservative bound for simultaneously live recipe values.
785    pub fn peak_materialization_bytes<C: RecipeCatalog + ?Sized>(
786        &self,
787        catalog: &C,
788    ) -> Result<u64, RecipeError> {
789        let output_bytes = self.infer(catalog)?.byte_len();
790        match self {
791            Self::Source { .. } => Ok(output_bytes),
792            Self::Select { input, .. }
793            | Self::Reshape { input, .. }
794            | Self::Transpose { input, .. }
795            | Self::Cast { input, .. }
796            | Self::View { input, .. }
797            | Self::NegLog { input }
798            | Self::SubtractOne { input } => {
799                let input_bytes = input.infer(catalog)?.byte_len();
800                let child_peak = input.peak_materialization_bytes(catalog)?;
801                Ok(child_peak.max(input_bytes.checked_add(output_bytes).ok_or(
802                    RecipeError::ArithmeticOverflow("unary recipe peak materialization bytes"),
803                )?))
804            }
805            Self::Concatenate { inputs, .. } | Self::Stack { inputs, .. } => {
806                let mut retained = 0u64;
807                let mut peak = 0u64;
808                for input in inputs {
809                    let child_peak = input.peak_materialization_bytes(catalog)?;
810                    peak = peak.max(retained.checked_add(child_peak).ok_or(
811                        RecipeError::ArithmeticOverflow("joined recipe child peak bytes"),
812                    )?);
813                    retained = retained
814                        .checked_add(input.infer(catalog)?.byte_len())
815                        .ok_or(RecipeError::ArithmeticOverflow(
816                            "joined recipe retained input bytes",
817                        ))?;
818                }
819                Ok(peak.max(retained.checked_add(output_bytes).ok_or(
820                    RecipeError::ArithmeticOverflow("joined recipe output peak bytes"),
821                )?))
822            }
823        }
824    }
825
826    /// Returns every source checkpoint key in deterministic order.
827    pub fn source_keys(&self) -> Vec<&str> {
828        let mut keys = BTreeSet::new();
829        self.collect_source_keys(&mut keys);
830        keys.into_iter().collect()
831    }
832
833    /// Returns source checkpoint keys in recipe traversal order with repetitions.
834    ///
835    /// This is the exact source-occurrence contract for consumers whose
836    /// execution or identity depends on operand order. Use [`Self::source_keys`]
837    /// when only a deterministic unique dependency set is required.
838    pub fn source_occurrences(&self) -> Vec<&str> {
839        let mut occurrences = Vec::new();
840        self.collect_source_occurrences(&mut occurrences);
841        occurrences
842    }
843
844    fn collect_source_keys<'a>(&'a self, keys: &mut BTreeSet<&'a str>) {
845        match self {
846            Self::Source { key, .. } => {
847                keys.insert(key);
848            }
849            Self::Concatenate { inputs, .. } | Self::Stack { inputs, .. } => {
850                for input in inputs {
851                    input.collect_source_keys(keys);
852                }
853            }
854            Self::Select { input, .. }
855            | Self::Reshape { input, .. }
856            | Self::Transpose { input, .. }
857            | Self::Cast { input, .. }
858            | Self::View { input, .. }
859            | Self::NegLog { input }
860            | Self::SubtractOne { input } => input.collect_source_keys(keys),
861        }
862    }
863
864    fn collect_source_occurrences<'a>(&'a self, occurrences: &mut Vec<&'a str>) {
865        match self {
866            Self::Source { key, .. } => occurrences.push(key),
867            Self::Concatenate { inputs, .. } | Self::Stack { inputs, .. } => {
868                for input in inputs {
869                    input.collect_source_occurrences(occurrences);
870                }
871            }
872            Self::Select { input, .. }
873            | Self::Reshape { input, .. }
874            | Self::Transpose { input, .. }
875            | Self::Cast { input, .. }
876            | Self::View { input, .. }
877            | Self::NegLog { input }
878            | Self::SubtractOne { input } => input.collect_source_occurrences(occurrences),
879        }
880    }
881
882    /// Validates every operation and infers its exact output metadata.
883    pub fn infer<C: RecipeCatalog + ?Sized>(
884        &self,
885        catalog: &C,
886    ) -> Result<RecipeMetadata, RecipeError> {
887        match self {
888            Self::Source { key, selection } => {
889                if key.trim().is_empty() {
890                    return Err(RecipeError::EmptySourceKey);
891                }
892                let metadata = catalog.tensor_metadata(key)?;
893                metadata_for(
894                    selected_shape(metadata.logical_shape, selection)?,
895                    metadata.stored_dtype.into(),
896                )
897            }
898            Self::Select { input, selection } => {
899                let metadata = input.infer(catalog)?;
900                metadata_for(selected_shape(metadata.shape, selection)?, metadata.dtype)
901            }
902            Self::Concatenate { axis, inputs } => infer_join(catalog, *axis, inputs, false),
903            Self::Stack { axis, inputs } => infer_join(catalog, *axis, inputs, true),
904            Self::Reshape { input, shape } => {
905                let metadata = input.infer(catalog)?;
906                let old_count = element_count(&metadata.shape, "reshape input")?;
907                let new_count = element_count(shape, "reshape output")?;
908                if old_count != new_count {
909                    return Err(RecipeError::ElementCountMismatch {
910                        input: old_count,
911                        output: new_count,
912                    });
913                }
914                metadata_for(shape.clone(), metadata.dtype)
915            }
916            Self::Transpose { input, axes } => {
917                let metadata = input.infer(catalog)?;
918                let unique = axes.iter().copied().collect::<BTreeSet<_>>();
919                if axes.len() != metadata.shape.len()
920                    || unique.len() != axes.len()
921                    || axes.iter().any(|axis| *axis >= axes.len())
922                {
923                    return Err(RecipeError::InvalidPermutation {
924                        axes: axes.clone(),
925                        rank: metadata.shape.len(),
926                    });
927                }
928                metadata_for(
929                    axes.iter().map(|axis| metadata.shape[*axis]).collect(),
930                    metadata.dtype,
931                )
932            }
933            Self::Cast { input, dtype } => metadata_for(input.infer(catalog)?.shape, dtype.clone()),
934            Self::View {
935                input,
936                dtype,
937                shape,
938            } => {
939                let input = input.infer(catalog)?;
940                let output = metadata_for(shape.clone(), dtype.clone())?;
941                if input.byte_len != output.byte_len {
942                    return Err(RecipeError::ByteCountMismatch {
943                        input: input.byte_len,
944                        output: output.byte_len,
945                    });
946                }
947                Ok(output)
948            }
949            Self::NegLog { input } | Self::SubtractOne { input } => input.infer(catalog),
950        }
951    }
952}
953
954fn selected_shape(
955    mut shape: Vec<usize>,
956    selection: &TensorSelection,
957) -> Result<Vec<usize>, RecipeError> {
958    match selection {
959        TensorSelection::Full => {}
960        TensorSelection::Range { axis, start, end } => {
961            let rank = shape.len();
962            let dimension = shape
963                .get_mut(*axis)
964                .ok_or(RecipeError::InvalidSelectionAxis { axis: *axis, rank })?;
965            if start >= end || *end > *dimension {
966                return Err(RecipeError::InvalidRange {
967                    axis: *axis,
968                    start: *start,
969                    end: *end,
970                    dimension: *dimension,
971                });
972            }
973            *dimension = end - start;
974        }
975        TensorSelection::Indices { axis, indices } => {
976            let rank = shape.len();
977            let dimension = shape
978                .get_mut(*axis)
979                .ok_or(RecipeError::InvalidSelectionAxis { axis: *axis, rank })?;
980            if indices.is_empty() || indices.iter().any(|index| *index >= *dimension) {
981                return Err(RecipeError::InvalidIndices {
982                    axis: *axis,
983                    dimension: *dimension,
984                });
985            }
986            *dimension = indices.len();
987        }
988        TensorSelection::Contiguous {
989            offset_elements,
990            shape: selected,
991        } => {
992            if selected.is_empty() || selected.contains(&0) {
993                return Err(RecipeError::InvalidContiguousSelection);
994            }
995            let full = element_count(&shape, "contiguous source")?;
996            let count = element_count(selected, "contiguous selection")?;
997            let end = u64::try_from(*offset_elements)
998                .map_err(|_| RecipeError::ArithmeticOverflow("contiguous offset"))?
999                .checked_add(count)
1000                .ok_or(RecipeError::ArithmeticOverflow("contiguous end"))?;
1001            if end > full {
1002                return Err(RecipeError::InvalidContiguousSelection);
1003            }
1004            shape = selected.clone();
1005        }
1006    }
1007    Ok(shape)
1008}
1009
1010fn expand_indexed_sources(recipe: DerivedWeightRecipe) -> DerivedWeightRecipe {
1011    match recipe {
1012        DerivedWeightRecipe::Source {
1013            key,
1014            selection: TensorSelection::Indices { axis, indices },
1015        } => {
1016            let mut runs = Vec::<(usize, usize)>::new();
1017            for index in indices {
1018                if let Some((_, end)) = runs.last_mut() {
1019                    if *end == index {
1020                        *end += 1;
1021                        continue;
1022                    }
1023                }
1024                runs.push((index, index + 1));
1025            }
1026            let mut inputs = runs
1027                .into_iter()
1028                .map(|(start, end)| {
1029                    DerivedWeightRecipe::source(
1030                        key.clone(),
1031                        TensorSelection::Range { axis, start, end },
1032                    )
1033                })
1034                .collect::<Vec<_>>();
1035            if inputs.len() == 1 {
1036                inputs.pop().unwrap()
1037            } else {
1038                DerivedWeightRecipe::Concatenate { axis, inputs }
1039            }
1040        }
1041        DerivedWeightRecipe::Source { .. } => recipe,
1042        DerivedWeightRecipe::Select { input, selection } => DerivedWeightRecipe::Select {
1043            input: Box::new(expand_indexed_sources(*input)),
1044            selection,
1045        },
1046        DerivedWeightRecipe::Concatenate { axis, inputs } => DerivedWeightRecipe::Concatenate {
1047            axis,
1048            inputs: inputs.into_iter().map(expand_indexed_sources).collect(),
1049        },
1050        DerivedWeightRecipe::Stack { axis, inputs } => DerivedWeightRecipe::Stack {
1051            axis,
1052            inputs: inputs.into_iter().map(expand_indexed_sources).collect(),
1053        },
1054        DerivedWeightRecipe::Reshape { input, shape } => DerivedWeightRecipe::Reshape {
1055            input: Box::new(expand_indexed_sources(*input)),
1056            shape,
1057        },
1058        DerivedWeightRecipe::Transpose { input, axes } => DerivedWeightRecipe::Transpose {
1059            input: Box::new(expand_indexed_sources(*input)),
1060            axes,
1061        },
1062        DerivedWeightRecipe::Cast { input, dtype } => DerivedWeightRecipe::Cast {
1063            input: Box::new(expand_indexed_sources(*input)),
1064            dtype,
1065        },
1066        DerivedWeightRecipe::View {
1067            input,
1068            dtype,
1069            shape,
1070        } => DerivedWeightRecipe::View {
1071            input: Box::new(expand_indexed_sources(*input)),
1072            dtype,
1073            shape,
1074        },
1075        DerivedWeightRecipe::NegLog { input } => DerivedWeightRecipe::NegLog {
1076            input: Box::new(expand_indexed_sources(*input)),
1077        },
1078        DerivedWeightRecipe::SubtractOne { input } => DerivedWeightRecipe::SubtractOne {
1079            input: Box::new(expand_indexed_sources(*input)),
1080        },
1081    }
1082}
1083
1084fn normalize_bounded_source_ranges<C: RecipeCatalog + ?Sized>(
1085    recipe: DerivedWeightRecipe,
1086    store: &C,
1087) -> Result<DerivedWeightRecipe, RecipeError> {
1088    Ok(match recipe {
1089        DerivedWeightRecipe::Source {
1090            key,
1091            selection: TensorSelection::Range { axis, start, end },
1092        } if axis > 0 => {
1093            let shape = store.tensor_metadata(&key)?.logical_shape;
1094            if shape[..axis].iter().product::<usize>() == 1 {
1095                let trailing = shape[axis + 1..]
1096                    .iter()
1097                    .try_fold(1usize, |count, dimension| {
1098                        count
1099                            .checked_mul(*dimension)
1100                            .ok_or(RecipeError::ArithmeticOverflow(
1101                                "bounded source range trailing span",
1102                            ))
1103                    })?;
1104                let offset_elements =
1105                    start
1106                        .checked_mul(trailing)
1107                        .ok_or(RecipeError::ArithmeticOverflow(
1108                            "bounded source range offset",
1109                        ))?;
1110                let mut selected_shape = shape;
1111                selected_shape[axis] = end - start;
1112                DerivedWeightRecipe::source(
1113                    key,
1114                    TensorSelection::Contiguous {
1115                        offset_elements,
1116                        shape: selected_shape,
1117                    },
1118                )
1119            } else {
1120                DerivedWeightRecipe::source(key, TensorSelection::Range { axis, start, end })
1121            }
1122        }
1123        DerivedWeightRecipe::Source { .. } => recipe,
1124        DerivedWeightRecipe::Select { input, selection } => DerivedWeightRecipe::Select {
1125            input: Box::new(normalize_bounded_source_ranges(*input, store)?),
1126            selection,
1127        },
1128        DerivedWeightRecipe::Concatenate { axis, inputs } => DerivedWeightRecipe::Concatenate {
1129            axis,
1130            inputs: inputs
1131                .into_iter()
1132                .map(|input| normalize_bounded_source_ranges(input, store))
1133                .collect::<Result<Vec<_>, _>>()?,
1134        },
1135        DerivedWeightRecipe::Stack { axis, inputs } => DerivedWeightRecipe::Stack {
1136            axis,
1137            inputs: inputs
1138                .into_iter()
1139                .map(|input| normalize_bounded_source_ranges(input, store))
1140                .collect::<Result<Vec<_>, _>>()?,
1141        },
1142        DerivedWeightRecipe::Reshape { input, shape } => DerivedWeightRecipe::Reshape {
1143            input: Box::new(normalize_bounded_source_ranges(*input, store)?),
1144            shape,
1145        },
1146        DerivedWeightRecipe::Transpose { input, axes } => DerivedWeightRecipe::Transpose {
1147            input: Box::new(normalize_bounded_source_ranges(*input, store)?),
1148            axes,
1149        },
1150        DerivedWeightRecipe::Cast { input, dtype } => DerivedWeightRecipe::Cast {
1151            input: Box::new(normalize_bounded_source_ranges(*input, store)?),
1152            dtype,
1153        },
1154        DerivedWeightRecipe::View {
1155            input,
1156            dtype,
1157            shape,
1158        } => DerivedWeightRecipe::View {
1159            input: Box::new(normalize_bounded_source_ranges(*input, store)?),
1160            dtype,
1161            shape,
1162        },
1163        DerivedWeightRecipe::NegLog { input } => DerivedWeightRecipe::NegLog {
1164            input: Box::new(normalize_bounded_source_ranges(*input, store)?),
1165        },
1166        DerivedWeightRecipe::SubtractOne { input } => DerivedWeightRecipe::SubtractOne {
1167            input: Box::new(normalize_bounded_source_ranges(*input, store)?),
1168        },
1169    })
1170}
1171
1172fn push_selection<C: RecipeCatalog + ?Sized>(
1173    recipe: &DerivedWeightRecipe,
1174    store: &C,
1175    selection: TensorSelection,
1176) -> Result<DerivedWeightRecipe, RecipeError> {
1177    if matches!(selection, TensorSelection::Full) {
1178        return Ok(recipe.clone());
1179    }
1180    match recipe {
1181        DerivedWeightRecipe::Source {
1182            key,
1183            selection: source_selection,
1184        } => {
1185            let source_shape = store.tensor_metadata(key)?.logical_shape;
1186            let source_selection = normalize_selection(source_selection.clone(), &source_shape)?;
1187            let selected_source_shape = selected_shape(source_shape.clone(), &source_selection)?;
1188            let selection = normalize_selection(selection, &selected_source_shape)?;
1189            if matches!(selection, TensorSelection::Full) {
1190                return Ok(DerivedWeightRecipe::source(key.clone(), source_selection));
1191            }
1192            if matches!(source_selection, TensorSelection::Full) {
1193                return Ok(DerivedWeightRecipe::source(key.clone(), selection));
1194            }
1195            if let Some(selection) = select_from_contiguous_span(&source_selection, &selection)? {
1196                return Ok(DerivedWeightRecipe::source(key.clone(), selection));
1197            }
1198            if selection_axis(&source_selection) == selection_axis(&selection) {
1199                return Ok(DerivedWeightRecipe::source(
1200                    key.clone(),
1201                    compose_same_axis_selection(&source_selection, &selection)?,
1202                ));
1203            }
1204            if let Some(contiguous) =
1205                combine_independent_ranges(&source_shape, &source_selection, &selection)?
1206            {
1207                return Ok(DerivedWeightRecipe::source(key.clone(), contiguous));
1208            }
1209            Ok(DerivedWeightRecipe::Select {
1210                input: Box::new(DerivedWeightRecipe::source(key.clone(), source_selection)),
1211                selection,
1212            })
1213        }
1214        DerivedWeightRecipe::Select {
1215            input,
1216            selection: existing,
1217        } => {
1218            if matches!(existing, TensorSelection::Full) {
1219                return push_selection(input, store, selection);
1220            }
1221            if selection_axis(existing) == selection_axis(&selection) {
1222                return push_selection(
1223                    input,
1224                    store,
1225                    compose_same_axis_selection(existing, &selection)?,
1226                );
1227            }
1228            // Independent axis selections commute. Push the newest selection
1229            // toward the source first so a dynamic row tile can combine with
1230            // an expert range into one contiguous checkpoint span before the
1231            // pre-existing TP column selection is reapplied.
1232            let selected_input = push_selection(input, store, selection)?;
1233            push_selection(&selected_input, store, existing.clone())
1234        }
1235        DerivedWeightRecipe::Concatenate { axis, inputs } => {
1236            push_concatenate_selection(*axis, inputs, store, selection)
1237        }
1238        DerivedWeightRecipe::Stack { axis, inputs } => {
1239            push_stack_selection(*axis, inputs, store, selection)
1240        }
1241        DerivedWeightRecipe::Reshape { input, shape } => {
1242            let input_metadata = input.infer(store)?;
1243            let output_metadata = recipe.infer(store)?;
1244            let input_selection = map_reinterpret_selection(
1245                &input_metadata,
1246                &output_metadata,
1247                &selection,
1248                "reshape",
1249            )?;
1250            Ok(DerivedWeightRecipe::Reshape {
1251                input: Box::new(push_selection(input, store, input_selection)?),
1252                shape: selected_shape(shape.clone(), &selection)?,
1253            })
1254        }
1255        DerivedWeightRecipe::Transpose { input, axes } => {
1256            let output_axis = selection_axis(&selection).expect("non-full selection");
1257            let input_axis = *axes
1258                .get(output_axis)
1259                .ok_or(RecipeError::InvalidSelectionAxis {
1260                    axis: output_axis,
1261                    rank: axes.len(),
1262                })?;
1263            Ok(DerivedWeightRecipe::Transpose {
1264                input: Box::new(push_selection(
1265                    input,
1266                    store,
1267                    selection_with_axis(selection, input_axis),
1268                )?),
1269                axes: axes.clone(),
1270            })
1271        }
1272        DerivedWeightRecipe::Cast { input, dtype } => Ok(DerivedWeightRecipe::Cast {
1273            input: Box::new(push_selection(input, store, selection)?),
1274            dtype: dtype.clone(),
1275        }),
1276        DerivedWeightRecipe::View {
1277            input,
1278            dtype,
1279            shape,
1280        } => {
1281            let input_metadata = input.infer(store)?;
1282            let output_metadata = recipe.infer(store)?;
1283            let input_selection =
1284                map_reinterpret_selection(&input_metadata, &output_metadata, &selection, "view")?;
1285            Ok(DerivedWeightRecipe::View {
1286                input: Box::new(push_selection(input, store, input_selection)?),
1287                dtype: dtype.clone(),
1288                shape: selected_shape(shape.clone(), &selection)?,
1289            })
1290        }
1291        DerivedWeightRecipe::NegLog { input } => Ok(DerivedWeightRecipe::NegLog {
1292            input: Box::new(push_selection(input, store, selection)?),
1293        }),
1294        DerivedWeightRecipe::SubtractOne { input } => Ok(DerivedWeightRecipe::SubtractOne {
1295            input: Box::new(push_selection(input, store, selection)?),
1296        }),
1297    }
1298}
1299
1300fn push_concatenate_selection<C: RecipeCatalog + ?Sized>(
1301    axis: usize,
1302    inputs: &[DerivedWeightRecipe],
1303    store: &C,
1304    selection: TensorSelection,
1305) -> Result<DerivedWeightRecipe, RecipeError> {
1306    let selected_axis = selection_axis(&selection).expect("non-full selection");
1307    if selected_axis != axis {
1308        let inputs = inputs
1309            .iter()
1310            .map(|input| push_selection(input, store, selection.clone()))
1311            .collect::<Result<Vec<_>, _>>()?;
1312        return Ok(DerivedWeightRecipe::Concatenate { axis, inputs });
1313    }
1314    let metadata = inputs
1315        .iter()
1316        .map(|input| input.infer(store))
1317        .collect::<Result<Vec<_>, _>>()?;
1318    let dimensions = metadata
1319        .iter()
1320        .map(|item| item.shape[axis])
1321        .collect::<Vec<_>>();
1322    let mut rewritten = Vec::new();
1323    match selection {
1324        TensorSelection::Range { start, end, .. } => {
1325            let mut offset = 0usize;
1326            for (input, dimension) in inputs.iter().zip(dimensions) {
1327                let child_end =
1328                    offset
1329                        .checked_add(dimension)
1330                        .ok_or(RecipeError::ArithmeticOverflow(
1331                            "concatenate selection offset",
1332                        ))?;
1333                let overlap_start = start.max(offset);
1334                let overlap_end = end.min(child_end);
1335                if overlap_start < overlap_end {
1336                    let child_selection = normalize_selection(
1337                        TensorSelection::Range {
1338                            axis,
1339                            start: overlap_start - offset,
1340                            end: overlap_end - offset,
1341                        },
1342                        &input.infer(store)?.shape,
1343                    )?;
1344                    rewritten.push(push_selection(input, store, child_selection)?);
1345                }
1346                offset = child_end;
1347            }
1348        }
1349        TensorSelection::Indices { indices, .. } => {
1350            let mut offsets = Vec::with_capacity(dimensions.len() + 1);
1351            offsets.push(0usize);
1352            for dimension in dimensions {
1353                let next = offsets
1354                    .last()
1355                    .copied()
1356                    .unwrap()
1357                    .checked_add(dimension)
1358                    .ok_or(RecipeError::ArithmeticOverflow(
1359                        "concatenate selection offset",
1360                    ))?;
1361                offsets.push(next);
1362            }
1363            let mut runs = Vec::<(usize, Vec<usize>)>::new();
1364            for index in indices {
1365                let child = offsets
1366                    .windows(2)
1367                    .position(|bounds| index >= bounds[0] && index < bounds[1])
1368                    .ok_or(RecipeError::InvalidIndices {
1369                        axis,
1370                        dimension: *offsets.last().unwrap(),
1371                    })?;
1372                let local = index - offsets[child];
1373                if let Some((last_child, local_indices)) = runs.last_mut() {
1374                    if *last_child == child {
1375                        local_indices.push(local);
1376                        continue;
1377                    }
1378                }
1379                runs.push((child, vec![local]));
1380            }
1381            for (child, indices) in runs {
1382                rewritten.push(push_selection(
1383                    &inputs[child],
1384                    store,
1385                    TensorSelection::Indices { axis, indices },
1386                )?);
1387            }
1388        }
1389        TensorSelection::Full => unreachable!(),
1390        TensorSelection::Contiguous { .. } => {
1391            return Err(RecipeError::SelectionPushdownUnsupported {
1392                operation: "concatenate",
1393                reason: "a storage-contiguous span has no concatenate-axis semantics".into(),
1394            })
1395        }
1396    }
1397    match rewritten.len() {
1398        0 => Err(RecipeError::SelectionPushdownUnsupported {
1399            operation: "concatenate",
1400            reason: "selection did not intersect any child".into(),
1401        }),
1402        1 => Ok(rewritten.pop().unwrap()),
1403        _ => Ok(DerivedWeightRecipe::Concatenate {
1404            axis,
1405            inputs: rewritten,
1406        }),
1407    }
1408}
1409
1410fn push_stack_selection<C: RecipeCatalog + ?Sized>(
1411    axis: usize,
1412    inputs: &[DerivedWeightRecipe],
1413    store: &C,
1414    selection: TensorSelection,
1415) -> Result<DerivedWeightRecipe, RecipeError> {
1416    let selected_axis = selection_axis(&selection).expect("non-full selection");
1417    if selected_axis == axis {
1418        let selected = match selection {
1419            TensorSelection::Range { start, end, .. } => inputs[start..end].to_vec(),
1420            TensorSelection::Indices { indices, .. } => indices
1421                .into_iter()
1422                .map(|index| inputs[index].clone())
1423                .collect(),
1424            TensorSelection::Full => unreachable!(),
1425            TensorSelection::Contiguous { .. } => {
1426                return Err(RecipeError::SelectionPushdownUnsupported {
1427                    operation: "stack",
1428                    reason: "a storage-contiguous span has no stack-axis semantics".into(),
1429                })
1430            }
1431        };
1432        return Ok(DerivedWeightRecipe::Stack {
1433            axis,
1434            inputs: selected,
1435        });
1436    }
1437    let input_axis = if selected_axis < axis {
1438        selected_axis
1439    } else {
1440        selected_axis - 1
1441    };
1442    let input_selection = selection_with_axis(selection, input_axis);
1443    let inputs = inputs
1444        .iter()
1445        .map(|input| push_selection(input, store, input_selection.clone()))
1446        .collect::<Result<Vec<_>, _>>()?;
1447    Ok(DerivedWeightRecipe::Stack { axis, inputs })
1448}
1449
1450fn map_reinterpret_selection(
1451    input: &RecipeMetadata,
1452    output: &RecipeMetadata,
1453    selection: &TensorSelection,
1454    operation: &'static str,
1455) -> Result<TensorSelection, RecipeError> {
1456    let output_axis = selection_axis(selection).expect("non-full selection");
1457    let output_unit = axis_unit_bytes(&output.shape, output.dtype.bit_width()?, output_axis)?;
1458    let output_cycle = output_unit
1459        .checked_mul(output.shape[output_axis] as u64)
1460        .ok_or(RecipeError::ArithmeticOverflow("selection output cycle"))?;
1461    for input_axis in 0..input.shape.len() {
1462        let input_unit = axis_unit_bytes(&input.shape, input.dtype.bit_width()?, input_axis)?;
1463        let input_cycle = input_unit
1464            .checked_mul(input.shape[input_axis] as u64)
1465            .ok_or(RecipeError::ArithmeticOverflow("selection input cycle"))?;
1466        if input_cycle != output_cycle {
1467            continue;
1468        }
1469        if let Some(mapped) = map_selection_units(
1470            selection,
1471            input_axis,
1472            input.shape[input_axis],
1473            output_unit,
1474            input_unit,
1475        )? {
1476            return normalize_selection(mapped, &input.shape);
1477        }
1478    }
1479    Err(RecipeError::SelectionPushdownUnsupported {
1480        operation,
1481        reason: format!(
1482            "axis {output_axis} selection cannot be expressed as a single-axis bounded selection from shape {:?} to {:?}",
1483            input.shape, output.shape
1484        ),
1485    })
1486}
1487
1488fn map_selection_units(
1489    selection: &TensorSelection,
1490    input_axis: usize,
1491    input_dimension: usize,
1492    output_unit: u64,
1493    input_unit: u64,
1494) -> Result<Option<TensorSelection>, RecipeError> {
1495    let map_interval = |start: usize, end: usize| -> Result<Option<(usize, usize)>, RecipeError> {
1496        let start_bytes = (start as u64)
1497            .checked_mul(output_unit)
1498            .ok_or(RecipeError::ArithmeticOverflow("selection interval start"))?;
1499        let end_bytes = (end as u64)
1500            .checked_mul(output_unit)
1501            .ok_or(RecipeError::ArithmeticOverflow("selection interval end"))?;
1502        if start_bytes % input_unit != 0 || end_bytes % input_unit != 0 {
1503            return Ok(None);
1504        }
1505        let start = usize::try_from(start_bytes / input_unit)
1506            .map_err(|_| RecipeError::ArithmeticOverflow("mapped selection start"))?;
1507        let end = usize::try_from(end_bytes / input_unit)
1508            .map_err(|_| RecipeError::ArithmeticOverflow("mapped selection end"))?;
1509        Ok((end <= input_dimension).then_some((start, end)))
1510    };
1511    match selection {
1512        TensorSelection::Range { start, end, .. } => Ok(map_interval(*start, *end)?.map(
1513            |(start, end)| TensorSelection::Range {
1514                axis: input_axis,
1515                start,
1516                end,
1517            },
1518        )),
1519        TensorSelection::Indices { indices, .. } => {
1520            let mut mapped = Vec::new();
1521            let mut run_start = indices[0];
1522            let mut run_end = run_start + 1;
1523            for index in indices.iter().copied().skip(1) {
1524                if index == run_end {
1525                    run_end += 1;
1526                    continue;
1527                }
1528                let Some((start, end)) = map_interval(run_start, run_end)? else {
1529                    return Ok(None);
1530                };
1531                mapped.extend(start..end);
1532                run_start = index;
1533                run_end = index + 1;
1534            }
1535            let Some((start, end)) = map_interval(run_start, run_end)? else {
1536                return Ok(None);
1537            };
1538            mapped.extend(start..end);
1539            Ok(Some(TensorSelection::Indices {
1540                axis: input_axis,
1541                indices: mapped,
1542            }))
1543        }
1544        TensorSelection::Full => Ok(Some(TensorSelection::Full)),
1545        TensorSelection::Contiguous { .. } => Ok(None),
1546    }
1547}
1548
1549fn axis_unit_bytes(shape: &[usize], dtype_width: u64, axis: usize) -> Result<u64, RecipeError> {
1550    shape[axis + 1..]
1551        .iter()
1552        .try_fold(dtype_width, |bytes, dimension| {
1553            bytes
1554                .checked_mul(*dimension as u64)
1555                .ok_or(RecipeError::ArithmeticOverflow("selection axis unit"))
1556        })
1557}
1558
1559fn selection_axis(selection: &TensorSelection) -> Option<usize> {
1560    match selection {
1561        TensorSelection::Full => None,
1562        TensorSelection::Range { axis, .. } | TensorSelection::Indices { axis, .. } => Some(*axis),
1563        TensorSelection::Contiguous { .. } => None,
1564    }
1565}
1566
1567fn selection_with_axis(selection: TensorSelection, axis: usize) -> TensorSelection {
1568    match selection {
1569        TensorSelection::Full => TensorSelection::Full,
1570        TensorSelection::Range { start, end, .. } => TensorSelection::Range { axis, start, end },
1571        TensorSelection::Indices { indices, .. } => TensorSelection::Indices { axis, indices },
1572        selection @ TensorSelection::Contiguous { .. } => selection,
1573    }
1574}
1575
1576fn normalize_selection(
1577    selection: TensorSelection,
1578    shape: &[usize],
1579) -> Result<TensorSelection, RecipeError> {
1580    selected_shape(shape.to_vec(), &selection)?;
1581    match selection {
1582        TensorSelection::Range {
1583            axis,
1584            start: 0,
1585            end,
1586        } if end == shape[axis] => Ok(TensorSelection::Full),
1587        TensorSelection::Indices { axis, indices }
1588            if indices.windows(2).all(|pair| pair[1] == pair[0] + 1) =>
1589        {
1590            let start = indices[0];
1591            let end = indices[indices.len() - 1] + 1;
1592            if start == 0 && end == shape[axis] {
1593                Ok(TensorSelection::Full)
1594            } else {
1595                Ok(TensorSelection::Range { axis, start, end })
1596            }
1597        }
1598        selection => Ok(selection),
1599    }
1600}
1601
1602fn compose_same_axis_selection(
1603    existing: &TensorSelection,
1604    requested: &TensorSelection,
1605) -> Result<TensorSelection, RecipeError> {
1606    debug_assert_eq!(selection_axis(existing), selection_axis(requested));
1607    let axis = selection_axis(existing).expect("non-full selections");
1608    match (existing, requested) {
1609        (
1610            TensorSelection::Range { start, .. },
1611            TensorSelection::Range {
1612                start: requested_start,
1613                end: requested_end,
1614                ..
1615            },
1616        ) => Ok(TensorSelection::Range {
1617            axis,
1618            start: start + requested_start,
1619            end: start + requested_end,
1620        }),
1621        (TensorSelection::Range { start, .. }, TensorSelection::Indices { indices, .. }) => {
1622            Ok(TensorSelection::Indices {
1623                axis,
1624                indices: indices.iter().map(|index| start + index).collect(),
1625            })
1626        }
1627        (TensorSelection::Indices { indices, .. }, TensorSelection::Range { start, end, .. }) => {
1628            Ok(TensorSelection::Indices {
1629                axis,
1630                indices: indices[*start..*end].to_vec(),
1631            })
1632        }
1633        (
1634            TensorSelection::Indices {
1635                indices: existing, ..
1636            },
1637            TensorSelection::Indices { indices, .. },
1638        ) => Ok(TensorSelection::Indices {
1639            axis,
1640            indices: indices.iter().map(|index| existing[*index]).collect(),
1641        }),
1642        _ => Err(RecipeError::SelectionPushdownUnsupported {
1643            operation: "selection composition",
1644            reason: "full selections must be normalized before composition".into(),
1645        }),
1646    }
1647}
1648
1649fn combine_independent_ranges(
1650    source_shape: &[usize],
1651    existing: &TensorSelection,
1652    requested: &TensorSelection,
1653) -> Result<Option<TensorSelection>, RecipeError> {
1654    let (
1655        TensorSelection::Range {
1656            axis: existing_axis,
1657            start: existing_start,
1658            end: existing_end,
1659        },
1660        TensorSelection::Range {
1661            axis: requested_axis,
1662            start: requested_start,
1663            end: requested_end,
1664        },
1665    ) = (existing, requested)
1666    else {
1667        return Ok(None);
1668    };
1669    if existing_axis == requested_axis {
1670        return Ok(None);
1671    }
1672    let mut starts = vec![0usize; source_shape.len()];
1673    let mut ends = source_shape.to_vec();
1674    starts[*existing_axis] = *existing_start;
1675    ends[*existing_axis] = *existing_end;
1676    starts[*requested_axis] = *requested_start;
1677    ends[*requested_axis] = *requested_end;
1678    let selected_shape = starts
1679        .iter()
1680        .zip(&ends)
1681        .map(|(start, end)| end - start)
1682        .collect::<Vec<_>>();
1683    let Some(last_partial) = (0..source_shape.len())
1684        .rev()
1685        .find(|axis| starts[*axis] != 0 || ends[*axis] != source_shape[*axis])
1686    else {
1687        return Ok(Some(TensorSelection::Full));
1688    };
1689    if selected_shape[..last_partial]
1690        .iter()
1691        .any(|dimension| *dimension != 1)
1692        || (last_partial + 1..source_shape.len())
1693            .any(|axis| starts[axis] != 0 || ends[axis] != source_shape[axis])
1694    {
1695        return Ok(None);
1696    }
1697    let mut offset_elements = 0usize;
1698    let mut stride = 1usize;
1699    for axis in (0..source_shape.len()).rev() {
1700        offset_elements = offset_elements
1701            .checked_add(starts[axis].checked_mul(stride).ok_or(
1702                RecipeError::ArithmeticOverflow("contiguous selection offset"),
1703            )?)
1704            .ok_or(RecipeError::ArithmeticOverflow(
1705                "contiguous selection offset",
1706            ))?;
1707        stride = stride
1708            .checked_mul(source_shape[axis])
1709            .ok_or(RecipeError::ArithmeticOverflow(
1710                "contiguous selection stride",
1711            ))?;
1712    }
1713    Ok(Some(TensorSelection::Contiguous {
1714        offset_elements,
1715        shape: selected_shape,
1716    }))
1717}
1718
1719fn select_from_contiguous_span(
1720    existing: &TensorSelection,
1721    requested: &TensorSelection,
1722) -> Result<Option<TensorSelection>, RecipeError> {
1723    let TensorSelection::Contiguous {
1724        offset_elements,
1725        shape,
1726    } = existing
1727    else {
1728        return Ok(None);
1729    };
1730    let (axis, start, end) = match requested {
1731        TensorSelection::Range { axis, start, end } => (*axis, *start, *end),
1732        TensorSelection::Indices { axis, indices }
1733            if indices.windows(2).all(|pair| pair[1] == pair[0] + 1) =>
1734        {
1735            (*axis, indices[0], indices[indices.len() - 1] + 1)
1736        }
1737        _ => return Ok(None),
1738    };
1739    if shape[..axis].iter().product::<usize>() != 1 {
1740        return Ok(None);
1741    }
1742    let trailing = shape[axis + 1..]
1743        .iter()
1744        .try_fold(1usize, |count, dimension| {
1745            count
1746                .checked_mul(*dimension)
1747                .ok_or(RecipeError::ArithmeticOverflow(
1748                    "contiguous selection trailing span",
1749                ))
1750        })?;
1751    let offset_elements = offset_elements
1752        .checked_add(
1753            start
1754                .checked_mul(trailing)
1755                .ok_or(RecipeError::ArithmeticOverflow(
1756                    "contiguous selection offset",
1757                ))?,
1758        )
1759        .ok_or(RecipeError::ArithmeticOverflow(
1760            "contiguous selection offset",
1761        ))?;
1762    let mut selected_shape = shape.clone();
1763    selected_shape[axis] = end - start;
1764    Ok(Some(TensorSelection::Contiguous {
1765        offset_elements,
1766        shape: selected_shape,
1767    }))
1768}
1769
1770fn infer_join<C: RecipeCatalog + ?Sized>(
1771    catalog: &C,
1772    axis: usize,
1773    inputs: &[DerivedWeightRecipe],
1774    stack: bool,
1775) -> Result<RecipeMetadata, RecipeError> {
1776    if inputs.is_empty() {
1777        return Err(RecipeError::EmptyInputs);
1778    }
1779    let metadata = inputs
1780        .iter()
1781        .map(|input| input.infer(catalog))
1782        .collect::<Result<Vec<_>, _>>()?;
1783    let first = &metadata[0];
1784    if metadata.iter().any(|item| item.dtype != first.dtype) {
1785        return Err(RecipeError::DtypeMismatch);
1786    }
1787    let rank = first.shape.len();
1788    if axis > rank || (!stack && axis == rank) {
1789        return Err(RecipeError::InvalidJoinAxis { axis, rank, stack });
1790    }
1791    if stack {
1792        if metadata.iter().any(|item| item.shape != first.shape) {
1793            return Err(RecipeError::ShapeMismatch);
1794        }
1795        let mut shape = first.shape.clone();
1796        shape.insert(axis, metadata.len());
1797        metadata_for(shape, first.dtype.clone())
1798    } else {
1799        let mut shape = first.shape.clone();
1800        shape[axis] = 0;
1801        for item in &metadata {
1802            if item.shape.len() != rank
1803                || item
1804                    .shape
1805                    .iter()
1806                    .enumerate()
1807                    .any(|(index, dimension)| index != axis && *dimension != first.shape[index])
1808            {
1809                return Err(RecipeError::ShapeMismatch);
1810            }
1811            shape[axis] = shape[axis]
1812                .checked_add(item.shape[axis])
1813                .ok_or(RecipeError::ArithmeticOverflow("concatenate dimension"))?;
1814        }
1815        metadata_for(shape, first.dtype.clone())
1816    }
1817}
1818
1819fn metadata_for(shape: Vec<usize>, dtype: RecipeDtype) -> Result<RecipeMetadata, RecipeError> {
1820    let bits = element_count(&shape, "recipe output")?
1821        .checked_mul(dtype.bit_width()?)
1822        .ok_or(RecipeError::ArithmeticOverflow("recipe output bits"))?;
1823    let byte_len = bits
1824        .checked_add(7)
1825        .ok_or(RecipeError::ArithmeticOverflow("recipe output bytes"))?
1826        / 8;
1827    if byte_len == 0 {
1828        return Err(RecipeError::ZeroSizedOutput);
1829    }
1830    Ok(RecipeMetadata {
1831        shape,
1832        dtype,
1833        byte_len,
1834    })
1835}
1836
1837fn element_count(shape: &[usize], context: &'static str) -> Result<u64, RecipeError> {
1838    shape.iter().try_fold(1u64, |count, dimension| {
1839        count
1840            .checked_mul(
1841                u64::try_from(*dimension).map_err(|_| RecipeError::ArithmeticOverflow(context))?,
1842            )
1843            .ok_or(RecipeError::ArithmeticOverflow(context))
1844    })
1845}
1846
1847/// Structured neutral recipe validation failures.
1848#[derive(Debug, thiserror::Error)]
1849#[allow(missing_docs)]
1850pub enum RecipeError {
1851    #[error("derived-weight source key must not be empty")]
1852    EmptySourceKey,
1853    #[error("derived-weight output name must not be empty")]
1854    EmptyOutputName,
1855    #[error("derived-weight recipe family requires at least one output")]
1856    EmptyOutputs,
1857    #[error("derived-weight output {target:?} is declared more than once")]
1858    DuplicateOutput { target: String },
1859    #[error("derived-weight alias name must not be empty")]
1860    EmptyAliasName,
1861    #[error("derived-weight alias {alias:?} is declared more than once")]
1862    DuplicateAlias { alias: String },
1863    #[error("derived-weight alias {alias:?} collides with a canonical output")]
1864    AliasOutputCollision { alias: String },
1865    #[error("derived-weight alias {alias:?} has unknown destination {destination:?}")]
1866    InvalidAliasDestination { alias: String, destination: String },
1867    #[error("derived-weight alias cycle contains {alias:?}")]
1868    AliasCycle { alias: String },
1869    #[error("matrix-family affine biases require a scale companion")]
1870    MatrixBiasWithoutScales,
1871    #[error("matrix-family weight must have rank at least two, got {shape:?}")]
1872    InvalidMatrixFamilyWeight { shape: Vec<usize> },
1873    #[error(
1874        "matrix-family {member} geometry {companion:?} is incompatible with weight {weight:?}"
1875    )]
1876    MatrixCompanionGeometry {
1877        member: &'static str,
1878        weight: Vec<usize>,
1879        companion: Vec<usize>,
1880    },
1881    #[error("matrix-family scale geometry {scales:?} differs from affine biases {biases:?}")]
1882    MatrixScaleBiasGeometry {
1883        scales: Vec<usize>,
1884        biases: Vec<usize>,
1885    },
1886    #[error("matrix-family leading selection must use axis 0, got axis {axis}")]
1887    MatrixFamilySelectionAxis { axis: usize },
1888    #[error("matrix-family leading selection does not accept a scalar contiguous span")]
1889    MatrixFamilyContiguousSelection,
1890    #[error("fused source {tensor:?} requires positive, named output segments")]
1891    InvalidFusedSplit { tensor: String },
1892    #[error(
1893        "fused source {tensor:?} axis {axis} has dimension {dimension}, but output widths sum to {outputs}"
1894    )]
1895    FusedSplitWidthMismatch {
1896        tensor: String,
1897        axis: usize,
1898        dimension: usize,
1899        outputs: usize,
1900    },
1901    #[error("selection axis {axis} is outside rank {rank}")]
1902    InvalidSelectionAxis { axis: usize, rank: usize },
1903    #[error("range {start}..{end} is invalid for axis {axis} dimension {dimension}")]
1904    InvalidRange {
1905        axis: usize,
1906        start: usize,
1907        end: usize,
1908        dimension: usize,
1909    },
1910    #[error("ordered indices are empty or outside axis {axis} dimension {dimension}")]
1911    InvalidIndices { axis: usize, dimension: usize },
1912    #[error("contiguous selection is empty or outside its source tensor")]
1913    InvalidContiguousSelection,
1914    #[error("concatenate and stack recipes require at least one input")]
1915    EmptyInputs,
1916    #[error("derived-weight inputs have different dtypes")]
1917    DtypeMismatch,
1918    #[error("derived-weight inputs have incompatible shapes")]
1919    ShapeMismatch,
1920    #[error("axis {axis} is invalid for rank {rank} (stack={stack})")]
1921    InvalidJoinAxis {
1922        axis: usize,
1923        rank: usize,
1924        stack: bool,
1925    },
1926    #[error("reshape changes element count from {input} to {output}")]
1927    ElementCountMismatch { input: u64, output: u64 },
1928    #[error("bitwise view changes byte count from {input} to {output}")]
1929    ByteCountMismatch { input: u64, output: u64 },
1930    #[error("axes {axes:?} are not a permutation of rank {rank}")]
1931    InvalidPermutation { axes: Vec<usize>, rank: usize },
1932    #[error("derived-weight output must contain at least one byte")]
1933    ZeroSizedOutput,
1934    #[error("derived-weight dtype {dtype} is unsupported")]
1935    UnsupportedDtype { dtype: String },
1936    #[error("derived-weight arithmetic overflow: {0}")]
1937    ArithmeticOverflow(&'static str),
1938    #[error("cannot push selection through {operation}: {reason}")]
1939    SelectionPushdownUnsupported {
1940        operation: &'static str,
1941        reason: String,
1942    },
1943    #[error(transparent)]
1944    Store(#[from] StoreError),
1945}
1946
1947#[cfg(test)]
1948mod tests {
1949    use super::*;
1950    use crate::store::{EncodedTensorLease, TensorReadRequest, WeightStoreDiagnostics};
1951    use std::path::Path;
1952    use std::sync::Mutex;
1953
1954    struct Catalog;
1955    struct Lease;
1956
1957    #[derive(Default)]
1958    struct BoundedCatalog {
1959        requests: Mutex<Vec<(String, TensorSelection)>>,
1960    }
1961
1962    impl RecipeCatalog for BoundedCatalog {
1963        fn tensor_metadata(&self, key: &str) -> Result<TensorMetadata, StoreError> {
1964            Catalog.metadata(key)
1965        }
1966    }
1967
1968    impl BoundedRecipeSource for BoundedCatalog {
1969        fn verify_bounded_source(
1970            &self,
1971            key: &str,
1972            selection: TensorSelection,
1973        ) -> Result<(), StoreError> {
1974            self.requests
1975                .lock()
1976                .unwrap()
1977                .push((key.to_owned(), selection));
1978            Ok(())
1979        }
1980    }
1981
1982    impl EncodedTensorLease for Lease {
1983        fn metadata(&self) -> &TensorMetadata {
1984            unreachable!()
1985        }
1986        fn selection(&self) -> &TensorSelection {
1987            unreachable!()
1988        }
1989        fn output_shape(&self) -> &[usize] {
1990            unreachable!()
1991        }
1992        fn bounded_read_proof(&self) -> &crate::store::BoundedReadProof {
1993            unreachable!()
1994        }
1995        fn backing_path(&self) -> Option<&Path> {
1996            None
1997        }
1998        fn encoded_bytes(&self) -> Option<&[u8]> {
1999            None
2000        }
2001    }
2002
2003    impl WeightStore for Catalog {
2004        type Lease = Lease;
2005
2006        fn keys(&self) -> Vec<String> {
2007            vec!["left".into(), "right".into()]
2008        }
2009        fn metadata(&self, key: &str) -> Result<TensorMetadata, StoreError> {
2010            if !self.keys().iter().any(|candidate| candidate == key) {
2011                return Err(StoreError::UnknownTensor { key: key.into() });
2012            }
2013            Ok(TensorMetadata {
2014                name: key.into(),
2015                logical_shape: vec![2, 3],
2016                physical_shape: vec![2, 3],
2017                stored_dtype: StoredDtype::F16,
2018                encoded_byte_len: 12,
2019                backing_shard: None,
2020            })
2021        }
2022        fn acquire(&self, _: TensorReadRequest) -> Result<Self::Lease, StoreError> {
2023            unreachable!()
2024        }
2025        fn diagnostics(&self) -> Result<WeightStoreDiagnostics, StoreError> {
2026            unreachable!()
2027        }
2028    }
2029
2030    #[test]
2031    fn nested_recipe_inference_is_backend_independent() {
2032        let recipe = DerivedWeightRecipe::Transpose {
2033            input: Box::new(DerivedWeightRecipe::Concatenate {
2034                axis: 0,
2035                inputs: vec![
2036                    DerivedWeightRecipe::source("left", TensorSelection::Full),
2037                    DerivedWeightRecipe::source(
2038                        "right",
2039                        TensorSelection::Range {
2040                            axis: 0,
2041                            start: 0,
2042                            end: 1,
2043                        },
2044                    ),
2045                ],
2046            }),
2047            axes: vec![1, 0],
2048        };
2049        let metadata = recipe.infer(&Catalog).unwrap();
2050        assert_eq!(metadata.shape(), &[3, 3]);
2051        assert_eq!(metadata.dtype(), &RecipeDtype::F16);
2052        assert_eq!(metadata.byte_len(), 18);
2053        assert_eq!(recipe.source_keys(), ["left", "right"]);
2054
2055        let ordered = DerivedWeightRecipe::Stack {
2056            axis: 0,
2057            inputs: vec![
2058                DerivedWeightRecipe::source("right", TensorSelection::Full),
2059                DerivedWeightRecipe::source("left", TensorSelection::Full),
2060                DerivedWeightRecipe::source("right", TensorSelection::Full),
2061            ],
2062        };
2063        assert_eq!(ordered.source_keys(), ["left", "right"]);
2064        assert_eq!(ordered.source_occurrences(), ["right", "left", "right"]);
2065    }
2066
2067    #[test]
2068    fn bounded_preflight_walks_exact_physical_source_selections() {
2069        let catalog = BoundedCatalog::default();
2070        let recipe = DerivedWeightRecipe::Concatenate {
2071            axis: 0,
2072            inputs: vec![
2073                DerivedWeightRecipe::source(
2074                    "left",
2075                    TensorSelection::Range {
2076                        axis: 0,
2077                        start: 0,
2078                        end: 1,
2079                    },
2080                ),
2081                DerivedWeightRecipe::Reshape {
2082                    input: Box::new(DerivedWeightRecipe::source("right", TensorSelection::Full)),
2083                    shape: vec![2, 3],
2084                },
2085            ],
2086        };
2087
2088        recipe.preflight_bounded(&catalog).unwrap();
2089        assert_eq!(
2090            *catalog.requests.lock().unwrap(),
2091            vec![
2092                (
2093                    "left".into(),
2094                    TensorSelection::Range {
2095                        axis: 0,
2096                        start: 0,
2097                        end: 1,
2098                    },
2099                ),
2100                ("right".into(), TensorSelection::Full),
2101            ]
2102        );
2103    }
2104
2105    #[test]
2106    fn bounded_selection_pushdown_is_backend_independent() {
2107        let recipe = DerivedWeightRecipe::Concatenate {
2108            axis: 0,
2109            inputs: vec![
2110                DerivedWeightRecipe::source("left", TensorSelection::Full),
2111                DerivedWeightRecipe::source("right", TensorSelection::Full),
2112            ],
2113        };
2114        let selected = recipe
2115            .select_bounded(
2116                &Catalog,
2117                TensorSelection::Range {
2118                    axis: 0,
2119                    start: 1,
2120                    end: 3,
2121                },
2122            )
2123            .unwrap();
2124        assert_eq!(selected.infer(&Catalog).unwrap().shape(), &[2, 3]);
2125        assert_eq!(
2126            selected,
2127            DerivedWeightRecipe::Concatenate {
2128                axis: 0,
2129                inputs: vec![
2130                    DerivedWeightRecipe::source(
2131                        "left",
2132                        TensorSelection::Range {
2133                            axis: 0,
2134                            start: 1,
2135                            end: 2,
2136                        },
2137                    ),
2138                    DerivedWeightRecipe::source(
2139                        "right",
2140                        TensorSelection::Range {
2141                            axis: 0,
2142                            start: 0,
2143                            end: 1,
2144                        },
2145                    ),
2146                ],
2147            }
2148        );
2149    }
2150
2151    #[test]
2152    fn fused_members_and_companions_validate_as_one_atomic_recipe_set() {
2153        let split = atomic_fused_split_recipes(
2154            &Catalog,
2155            [
2156                FusedSplitMember {
2157                    source: "left".into(),
2158                    axis: 1,
2159                    outputs: vec![
2160                        FusedSplitOutput {
2161                            target: "weight.query".into(),
2162                            width: 1,
2163                        },
2164                        FusedSplitOutput {
2165                            target: "weight.key_value".into(),
2166                            width: 2,
2167                        },
2168                    ],
2169                },
2170                FusedSplitMember {
2171                    source: "right".into(),
2172                    axis: 1,
2173                    outputs: vec![
2174                        FusedSplitOutput {
2175                            target: "scale.query".into(),
2176                            width: 1,
2177                        },
2178                        FusedSplitOutput {
2179                            target: "scale.key_value".into(),
2180                            width: 2,
2181                        },
2182                    ],
2183                },
2184            ],
2185        )
2186        .unwrap();
2187        assert_eq!(split.iter().count(), 4);
2188        assert_eq!(
2189            split
2190                .get("weight.key_value")
2191                .unwrap()
2192                .infer(&Catalog)
2193                .unwrap()
2194                .shape(),
2195            &[2, 2]
2196        );
2197
2198        let duplicate = atomic_fused_split_recipes(
2199            &Catalog,
2200            [FusedSplitMember {
2201                source: "left".into(),
2202                axis: 1,
2203                outputs: vec![
2204                    FusedSplitOutput {
2205                        target: "same".into(),
2206                        width: 1,
2207                    },
2208                    FusedSplitOutput {
2209                        target: "same".into(),
2210                        width: 2,
2211                    },
2212                ],
2213            }],
2214        );
2215        assert!(matches!(
2216            duplicate,
2217            Err(RecipeError::DuplicateOutput { .. })
2218        ));
2219
2220        let mismatch = atomic_fused_split_recipes(
2221            &Catalog,
2222            [FusedSplitMember {
2223                source: "left".into(),
2224                axis: 1,
2225                outputs: vec![FusedSplitOutput {
2226                    target: "short".into(),
2227                    width: 2,
2228                }],
2229            }],
2230        );
2231        assert!(matches!(
2232            mismatch,
2233            Err(RecipeError::FusedSplitWidthMismatch { .. })
2234        ));
2235    }
2236
2237    #[test]
2238    fn ordered_axis_selection_validates_value_head_layout_without_payload_reads() {
2239        let recipe = ordered_axis_selection(&Catalog, "left", 1, vec![2, 0, 1]).unwrap();
2240        assert_eq!(recipe.infer(&Catalog).unwrap().shape(), &[2, 3]);
2241        assert!(ordered_axis_selection(&Catalog, "left", 1, vec![3]).is_err());
2242    }
2243
2244    struct FamilyCatalog(BTreeMap<String, TensorMetadata>);
2245
2246    impl RecipeCatalog for FamilyCatalog {
2247        fn tensor_metadata(&self, key: &str) -> Result<TensorMetadata, StoreError> {
2248            self.0
2249                .get(key)
2250                .cloned()
2251                .ok_or_else(|| StoreError::UnknownTensor { key: key.into() })
2252        }
2253    }
2254
2255    fn family_catalog(entries: &[(&str, &[usize], StoredDtype)]) -> FamilyCatalog {
2256        FamilyCatalog(
2257            entries
2258                .iter()
2259                .map(|(name, shape, dtype)| {
2260                    (
2261                        (*name).to_owned(),
2262                        TensorMetadata {
2263                            name: (*name).to_owned(),
2264                            logical_shape: shape.to_vec(),
2265                            physical_shape: shape.to_vec(),
2266                            stored_dtype: dtype.clone(),
2267                            encoded_byte_len: 1,
2268                            backing_shard: Some("synthetic.safetensors".into()),
2269                        },
2270                    )
2271                })
2272                .collect(),
2273        )
2274    }
2275
2276    fn member(target: &str, source: &str) -> MatrixRecipeMember {
2277        MatrixRecipeMember::new(
2278            target,
2279            DerivedWeightRecipe::source(source, TensorSelection::Full),
2280        )
2281    }
2282
2283    #[test]
2284    fn dense_matrix_family_selects_leading_rows_atomically() {
2285        let catalog = family_catalog(&[("dense.weight", &[4, 6], StoredDtype::F16)]);
2286        let family = AtomicMatrixRecipeFamily::new(
2287            &catalog,
2288            member("model.weight", "dense.weight"),
2289            None,
2290            None,
2291        )
2292        .unwrap();
2293        let selected = family
2294            .select_leading_axis(
2295                &catalog,
2296                TensorSelection::Range {
2297                    axis: 0,
2298                    start: 1,
2299                    end: 3,
2300                },
2301            )
2302            .unwrap();
2303        assert_eq!(
2304            selected.weight().recipe.infer(&catalog).unwrap().shape(),
2305            &[2, 6]
2306        );
2307        let published = selected.publish(&catalog, []).unwrap();
2308        assert_eq!(published.iter().count(), 1);
2309        assert_eq!(published.aliases().count(), 0);
2310        assert_eq!(
2311            published.get("model.weight").unwrap(),
2312            &DerivedWeightRecipe::source(
2313                "dense.weight",
2314                TensorSelection::Range {
2315                    axis: 0,
2316                    start: 1,
2317                    end: 3,
2318                }
2319            )
2320        );
2321    }
2322
2323    #[test]
2324    fn affine_matrix_family_selects_weight_scales_and_biases_coherently() {
2325        let catalog = family_catalog(&[
2326            ("affine.weight", &[4, 3], StoredDtype::U32),
2327            ("affine.scales", &[4, 2], StoredDtype::F16),
2328            ("affine.biases", &[4, 2], StoredDtype::F16),
2329        ]);
2330        let family = AtomicMatrixRecipeFamily::new(
2331            &catalog,
2332            member("model.weight", "affine.weight"),
2333            Some(member("model.scales", "affine.scales")),
2334            Some(member("model.biases", "affine.biases")),
2335        )
2336        .unwrap();
2337        let selected = family
2338            .select_leading_axis(
2339                &catalog,
2340                TensorSelection::Indices {
2341                    axis: 0,
2342                    indices: vec![3, 1],
2343                },
2344            )
2345            .unwrap();
2346        let published = selected.publish(&catalog, []).unwrap();
2347        assert_eq!(published.iter().count(), 3);
2348        assert_eq!(
2349            published
2350                .get("model.weight")
2351                .unwrap()
2352                .infer(&catalog)
2353                .unwrap()
2354                .shape(),
2355            &[2, 3]
2356        );
2357        for companion in ["model.scales", "model.biases"] {
2358            assert_eq!(
2359                published
2360                    .get(companion)
2361                    .unwrap()
2362                    .infer(&catalog)
2363                    .unwrap()
2364                    .shape(),
2365                &[2, 2]
2366            );
2367        }
2368    }
2369
2370    #[test]
2371    fn mxfp4_matrix_family_preserves_scale_companion_without_biases() {
2372        let catalog = family_catalog(&[
2373            ("mxfp4.weight", &[4, 32], StoredDtype::F4),
2374            ("mxfp4.scales", &[4, 1], StoredDtype::F8E8M0),
2375        ]);
2376        let family = AtomicMatrixRecipeFamily::new(
2377            &catalog,
2378            member("model.weight", "mxfp4.weight"),
2379            Some(member("model.scales", "mxfp4.scales")),
2380            None,
2381        )
2382        .unwrap();
2383        let selected = family
2384            .select_leading_axis(
2385                &catalog,
2386                TensorSelection::Range {
2387                    axis: 0,
2388                    start: 0,
2389                    end: 1,
2390                },
2391            )
2392            .unwrap();
2393        assert_eq!(
2394            selected.weight().recipe.infer(&catalog).unwrap().dtype(),
2395            &RecipeDtype::F4
2396        );
2397        assert_eq!(
2398            selected
2399                .scales()
2400                .unwrap()
2401                .recipe
2402                .infer(&catalog)
2403                .unwrap()
2404                .shape(),
2405            &[1, 1]
2406        );
2407        assert!(selected.biases().is_none());
2408    }
2409
2410    #[test]
2411    fn matrix_family_rejects_malformed_companions_before_publication() {
2412        let catalog = family_catalog(&[
2413            ("weight", &[4, 8], StoredDtype::U32),
2414            ("bad.scales", &[3, 2], StoredDtype::F16),
2415            ("scales", &[4, 2], StoredDtype::F16),
2416            ("bad.biases", &[4, 1], StoredDtype::F16),
2417        ]);
2418        assert!(matches!(
2419            AtomicMatrixRecipeFamily::new(
2420                &catalog,
2421                member("model.weight", "weight"),
2422                Some(member("model.scales", "bad.scales")),
2423                None,
2424            ),
2425            Err(RecipeError::MatrixCompanionGeometry { .. })
2426        ));
2427        assert!(matches!(
2428            AtomicMatrixRecipeFamily::new(
2429                &catalog,
2430                member("model.weight", "weight"),
2431                None,
2432                Some(member("model.biases", "bad.biases")),
2433            ),
2434            Err(RecipeError::MatrixBiasWithoutScales)
2435        ));
2436        assert!(matches!(
2437            AtomicMatrixRecipeFamily::new(
2438                &catalog,
2439                member("model.weight", "weight"),
2440                Some(member("model.scales", "scales")),
2441                Some(member("model.biases", "bad.biases")),
2442            ),
2443            Err(RecipeError::MatrixScaleBiasGeometry { .. })
2444        ));
2445
2446        let valid = AtomicMatrixRecipeFamily::new(
2447            &catalog,
2448            member("model.weight", "weight"),
2449            Some(member("model.scales", "scales")),
2450            None,
2451        )
2452        .unwrap();
2453        assert!(matches!(
2454            valid.select_leading_axis(
2455                &catalog,
2456                TensorSelection::Range {
2457                    axis: 1,
2458                    start: 0,
2459                    end: 1,
2460                }
2461            ),
2462            Err(RecipeError::MatrixFamilySelectionAxis { axis: 1 })
2463        ));
2464    }
2465
2466    #[test]
2467    fn aliases_resolve_to_one_canonical_owner_without_recipe_duplication() {
2468        let catalog = family_catalog(&[("shared", &[2, 3], StoredDtype::F16)]);
2469        let family = AtomicMatrixRecipeFamily::new(
2470            &catalog,
2471            member("canonical.weight", "shared"),
2472            None,
2473            None,
2474        )
2475        .unwrap();
2476        let published = family
2477            .publish(
2478                &catalog,
2479                [
2480                    RecipeAlias::new("slice.1.weight", "canonical.weight"),
2481                    RecipeAlias::new("slice.2.weight", "slice.1.weight"),
2482                ],
2483            )
2484            .unwrap();
2485        assert_eq!(
2486            published.aliases().collect::<Vec<_>>(),
2487            vec![
2488                ("slice.1.weight", "canonical.weight"),
2489                ("slice.2.weight", "canonical.weight"),
2490            ]
2491        );
2492        let (_, canonical) = published.get_resolved("canonical.weight").unwrap();
2493        let (owner, aliased) = published.get_resolved("slice.2.weight").unwrap();
2494        assert_eq!(owner, "canonical.weight");
2495        assert!(std::ptr::eq(canonical, aliased));
2496        let (outputs, aliases) = published.into_parts();
2497        assert_eq!(outputs.len(), 1);
2498        assert_eq!(aliases.len(), 2);
2499    }
2500
2501    #[test]
2502    fn alias_validation_rejects_collision_cycle_and_unknown_destination_atomically() {
2503        let catalog = family_catalog(&[("shared", &[2, 3], StoredDtype::F16)]);
2504        let outputs = || {
2505            [(
2506                "canonical.weight".to_owned(),
2507                DerivedWeightRecipe::source("shared", TensorSelection::Full),
2508            )]
2509        };
2510        assert!(matches!(
2511            AtomicRecipeSet::new_with_aliases(
2512                &catalog,
2513                outputs(),
2514                [RecipeAlias::new("canonical.weight", "canonical.weight")],
2515            ),
2516            Err(RecipeError::AliasOutputCollision { .. })
2517        ));
2518        assert!(matches!(
2519            AtomicRecipeSet::new_with_aliases(
2520                &catalog,
2521                outputs(),
2522                [
2523                    RecipeAlias::new("first", "second"),
2524                    RecipeAlias::new("second", "first"),
2525                ],
2526            ),
2527            Err(RecipeError::AliasCycle { .. })
2528        ));
2529        assert!(matches!(
2530            AtomicRecipeSet::new_with_aliases(
2531                &catalog,
2532                outputs(),
2533                [RecipeAlias::new("orphan", "missing.weight")],
2534            ),
2535            Err(RecipeError::InvalidAliasDestination { .. })
2536        ));
2537        assert!(matches!(
2538            AtomicRecipeSet::new_with_aliases(
2539                &catalog,
2540                outputs(),
2541                [
2542                    RecipeAlias::new("duplicate", "canonical.weight"),
2543                    RecipeAlias::new("duplicate", "canonical.weight"),
2544                ],
2545            ),
2546            Err(RecipeError::DuplicateAlias { .. })
2547        ));
2548    }
2549}