cherenkov_model_data/
mlx.rs1use crate::*;
4use anyhow::{Context, Result, ensure};
5use serde_json::{Map, Value};
6use std::collections::HashMap;
7
8pub fn tensors(inventory: &Inventory, config: &Value) -> Result<Vec<Tensor>> {
12 let entries: HashMap<_, _> = inventory
13 .tensors
14 .iter()
15 .map(|t| (t.name.as_str(), t))
16 .collect();
17 let mut tensors = Vec::new();
18
19 for tensor in &inventory.tensors {
20 if is_component(&entries, &tensor.name) {
21 continue;
22 }
23
24 let Some(prefix) = tensor.name.strip_suffix(".weight") else {
25 tensors.push(tensor.clone());
26
27 continue;
28 };
29 let TensorEncoding::Dense { tensor: codes } = &tensor.encoding else {
30 tensors.push(tensor.clone());
31
32 continue;
33 };
34
35 if codes.dtype != Dtype::U32 {
36 tensors.push(tensor.clone());
37
38 continue;
39 }
40
41 let get = |suffix| -> Result<StoredTensor> {
42 let entry = entries
43 .get(format!("{prefix}.{suffix}").as_str())
44 .with_context(|| format!("{prefix}: missing {suffix}"))?;
45 let TensorEncoding::Dense { tensor } = &entry.encoding else {
46 anyhow::bail!("{prefix}: unsupported {suffix} storage");
47 };
48
49 Ok(tensor.clone())
50 };
51 let quantization = quantization(config, prefix);
52 let bits = quantization["bits"].as_u64();
53 let mode = quantization["mode"].as_str().unwrap_or("affine");
54
55 if !matches!(bits, Some(2 | 4 | 8)) || mode != "affine" {
56 let mut data = vec![codes.data.clone()];
57
58 for suffix in ["scales", "biases"] {
59 if let Some(entry) = entries.get(format!("{prefix}.{suffix}").as_str()) {
60 data.extend(entry.encoding.data().into_iter().cloned());
61 }
62 }
63
64 tensors.push(Tensor::new(
65 tensor.name.clone(),
66 tensor.role,
67 None,
68 TensorEncoding::Opaque {
69 name: "unrecognized MLX weight encoding".into(),
70 metadata: quantization,
71 data,
72 },
73 ));
74
75 continue;
76 }
77
78 let scales = get("scales")?;
79 let biases = get("biases")?;
80 let bits = bits.context("missing quantization bits")? as u8;
81
82 tensors.push(affine(tensor, codes.clone(), scales, biases, bits)?);
83 }
84
85 Ok(tensors)
86}
87
88fn affine(
89 source: &Tensor,
90 codes: StoredTensor,
91 scales: StoredTensor,
92 biases: StoredTensor,
93 bits: u8,
94) -> Result<Tensor> {
95 let mut shape = codes.shape.clone();
96 let axis = shape
97 .len()
98 .checked_sub(1)
99 .context("scalar quantized weight")?;
100 shape[axis] = shape[axis]
101 .checked_mul(32 / u64::from(bits))
102 .context("quantized shape overflow")?;
103
104 ensure!(
105 scales.shape.len() == shape.len(),
106 "affine scale rank mismatch"
107 );
108
109 let groups = scales.shape[axis];
110
111 ensure!(
112 groups > 0 && shape[axis].is_multiple_of(groups),
113 "invalid affine scale count"
114 );
115
116 let encoding = TensorEncoding::Affine {
117 bits,
118 group_size: shape[axis] / groups,
119 group_axis: axis,
120 packing: BitPacking::LowFirstU32,
121 codes,
122 scales,
123 offset: AffineOffset::Bias { tensor: biases },
124 };
125 let tensor = Tensor::new(source.name.clone(), source.role, Some(shape), encoding);
126
127 tensor.validate()?;
128
129 Ok(tensor)
130}
131
132fn is_component(entries: &HashMap<&str, &Tensor>, name: &str) -> bool {
133 let Some(prefix) = name
134 .strip_suffix(".scales")
135 .or_else(|| name.strip_suffix(".biases"))
136 else {
137 return false;
138 };
139
140 entries
141 .get(format!("{prefix}.weight").as_str())
142 .is_some_and(|entry| {
143 matches!(
144 &entry.encoding, TensorEncoding::Dense { tensor } if tensor.dtype == Dtype::U32
145 )
146 })
147}
148
149fn quantization(config: &Value, prefix: &str) -> Value {
150 let Some(root) = config
151 .get("quantization")
152 .or_else(|| config.get("quantization_config"))
153 else {
154 return Value::Null;
155 };
156 let mut fields = Map::new();
157
158 for name in ["bits", "group_size", "mode"] {
159 if let Some(value) = root.get(name) {
160 fields.insert(name.into(), value.clone());
161 }
162 }
163
164 if let Some(local) = root.get(prefix).and_then(Value::as_object) {
165 fields.extend(local.clone());
166 }
167
168 Value::Object(fields)
169}