Skip to main content

ferrum_quantization/gguf/source/
hadamard.rs

1//! Explicit metadata-component registry and complete source/layout binding.
2
3use std::collections::{BTreeMap, BTreeSet};
4use std::num::NonZeroU32;
5
6use ferrum_interfaces::vnext::{
7    ElementType, GroupedFeatureTranspose, HadamardApplication, HadamardSigns,
8    HadamardTransformSpec, ModelFamilyId, PhysicalWeightComponentBinding, PhysicalWeightLayout,
9    VNextError, WeightComponentRole, WeightComponentSpec, WeightEncoding, WeightId, WeightSchema,
10};
11
12use crate::gguf::{GgufHadamard, GgufHadamardDirection, GgufHadamardSigns, NativeGgufFile};
13
14pub fn hadamard_sign_component(width: u64) -> Result<WeightComponentSpec, VNextError> {
15    if width == 0 {
16        return Err(invalid("sign width must be nonzero"));
17    }
18    Ok(WeightComponentSpec {
19        id: WeightId::new(format!("gguf.hadamard.signs.width-{width}"))?,
20        role: WeightComponentRole::TransformSigns,
21        external_names: vec![format!("gguf.metadata.prism.hadamard.signs.{width}")],
22        dimensions: vec![width],
23        encoding: WeightEncoding::Dense {
24            element_type: ElementType::F32,
25        },
26        required: true,
27    })
28}
29
30pub fn hadamard_transform_spec(
31    metadata: &GgufHadamard,
32    name: &str,
33) -> Result<Option<HadamardTransformSpec>, VNextError> {
34    let Some(weight) = metadata.weight(name) else {
35        return Ok(None);
36    };
37    let signs = match metadata.signs() {
38        GgufHadamardSigns::Identity => HadamardSigns::Identity,
39        GgufHadamardSigns::Explicit(_) => {
40            HadamardSigns::Explicit(PhysicalWeightComponentBinding::exact_contiguous(
41                hadamard_sign_component(weight.input_width())?.id,
42            ))
43        }
44    };
45    let application = match weight.direction() {
46        GgufHadamardDirection::BeforeMatmul => HadamardApplication::BeforeMatmul {
47            input_permutation: weight.gdn_permutation().map(|permutation| {
48                GroupedFeatureTranspose {
49                    inner_extent: permutation.head_dim,
50                    first_outer_extent: permutation.key_heads,
51                    second_outer_extent: permutation.repeats,
52                }
53            }),
54        },
55        GgufHadamardDirection::AfterEmbeddingLookup => HadamardApplication::AfterEmbeddingLookup,
56    };
57    Ok(Some(HadamardTransformSpec {
58        block_size: NonZeroU32::new(metadata.block_size())
59            .ok_or_else(|| invalid("zero block size"))?,
60        signs,
61        application,
62    }))
63}
64
65#[derive(Default)]
66pub(super) struct HadamardRegistry {
67    pub(super) components: BTreeMap<WeightId, (WeightComponentSpec, Vec<u8>)>,
68}
69
70impl HadamardRegistry {
71    pub(super) fn bind(file: &NativeGgufFile, schema: &WeightSchema) -> Result<Self, VNextError> {
72        schema.validate(&ModelFamilyId::new("source.gguf.hadamard")?)?;
73        let components: BTreeMap<_, _> = schema
74            .components
75            .iter()
76            .map(|component| (component.id.clone(), component))
77            .collect();
78        let mut consumed = BTreeSet::new();
79        for tensor in &schema.tensors {
80            bind_layout(
81                file,
82                &components,
83                &tensor.physical_layout,
84                None,
85                &mut consumed,
86            )?;
87        }
88        let expected: BTreeSet<_> = file
89            .hadamard()
90            .map(|metadata| metadata.weights().keys().map(String::as_str).collect())
91            .unwrap_or_default();
92        if consumed != expected {
93            return Err(invalid(
94                "weight schema does not consume every declared source transform",
95            ));
96        }
97        let mut registry = Self::default();
98        if let Some(metadata) = file.hadamard() {
99            if let GgufHadamardSigns::Explicit(signs) = metadata.signs() {
100                for weight in metadata.weights().values() {
101                    let width = weight.input_width();
102                    let spec = hadamard_sign_component(width)?;
103                    if components.get(&spec.id).copied() != Some(&spec) {
104                        return Err(invalid(
105                            "sign component differs from its validated source declaration",
106                        ));
107                    }
108                    if registry.components.contains_key(&spec.id) {
109                        continue;
110                    }
111                    let values = signs
112                        .get(&width)
113                        .ok_or_else(|| invalid("missing source signs"))?;
114                    let bytes = values
115                        .iter()
116                        .flat_map(|sign| f32::from(*sign).to_le_bytes())
117                        .collect();
118                    registry.components.insert(spec.id.clone(), (spec, bytes));
119                }
120            }
121        }
122        for component in &schema.components {
123            if component.role == WeightComponentRole::TransformSigns
124                && !registry.components.contains_key(&component.id)
125            {
126                return Err(invalid(
127                    "sign component has no source metadata registry entry",
128                ));
129            }
130        }
131        Ok(registry)
132    }
133}
134
135fn bind_layout<'a>(
136    file: &'a NativeGgufFile,
137    components: &BTreeMap<WeightId, &WeightComponentSpec>,
138    layout: &PhysicalWeightLayout,
139    transform: Option<&HadamardTransformSpec>,
140    consumed: &mut BTreeSet<&'a str>,
141) -> Result<(), VNextError> {
142    match layout {
143        PhysicalWeightLayout::Hadamard {
144            values,
145            transform: declared,
146        } => {
147            if transform.is_some() {
148                return Err(invalid("nested source transforms are unsupported"));
149            }
150            bind_layout(file, components, values, Some(declared), consumed)
151        }
152        PhysicalWeightLayout::Composite { parts } if transform.is_none() => {
153            for part in parts {
154                bind_layout(file, components, &part.layout, None, consumed)?;
155            }
156            Ok(())
157        }
158        PhysicalWeightLayout::Dense { component_id } => {
159            bind_leaf(file, components, component_id, transform, consumed)
160        }
161        PhysicalWeightLayout::Stored { component } => bind_leaf(
162            file,
163            components,
164            &component.component_id,
165            transform,
166            consumed,
167        ),
168        PhysicalWeightLayout::BlockQuantized { blocks, .. } => {
169            bind_leaf(file, components, &blocks.component_id, transform, consumed)
170        }
171        _ => Err(invalid(
172            "GGUF transform binding requires per-projection dense or block leaves",
173        )),
174    }
175}
176
177fn bind_leaf<'a>(
178    file: &'a NativeGgufFile,
179    components: &BTreeMap<WeightId, &WeightComponentSpec>,
180    component_id: &WeightId,
181    transform: Option<&HadamardTransformSpec>,
182    consumed: &mut BTreeSet<&'a str>,
183) -> Result<(), VNextError> {
184    let component = components
185        .get(component_id)
186        .ok_or_else(|| invalid("missing weight component"))?;
187    let [name] = component.external_names.as_slice() else {
188        return Err(invalid("GGUF leaf must bind one physical tensor"));
189    };
190    let expected = file
191        .hadamard()
192        .map(|metadata| hadamard_transform_spec(metadata, name))
193        .transpose()?
194        .flatten();
195    if transform != expected.as_ref() {
196        return Err(invalid(format!(
197            "source transform for {name:?} is missing or differs from its declared layout"
198        )));
199    }
200    if expected.is_some() {
201        let (name, _) = file
202            .hadamard()
203            .unwrap()
204            .weights()
205            .get_key_value(name)
206            .unwrap();
207        if !consumed.insert(name.as_str()) {
208            return Err(invalid("source transform is consumed more than once"));
209        }
210    }
211    Ok(())
212}
213
214fn invalid(reason: impl std::fmt::Display) -> VNextError {
215    VNextError::InvalidExecutionPlan {
216        reason: format!("GGUF Hadamard source binding: {reason}"),
217    }
218}