1extern crate alloc;
13use alloc::vec;
14use alloc::vec::Vec;
15
16use crate::kernels::rms_unit;
17use crate::math::{exp, sqrt};
18
19pub const HEAD_EMBEDDING: u8 = 1;
21pub const HEAD_CONFIDENCE: u8 = 2;
22pub const HEAD_ROUTER: u8 = 3;
23
24pub struct ProbeHead {
29 pub probes: Vec<f32>,
31 pub gain: Vec<f32>,
33 pub query: Vec<f32>,
35 pub row_bias: Vec<f32>,
37 pub proj: Vec<f32>,
39 pub bias: Vec<f32>,
41 pub cells: usize,
42 pub probes_per_cell: usize,
43 pub queries: usize,
44 pub out_dim: usize,
45}
46
47impl ProbeHead {
48 pub fn forward(&self, cells: &[f32], seq: usize, d: usize) -> Vec<f32> {
53 debug_assert_eq!(cells.len(), seq * self.cells * d);
54 let (l1, k, q) = (self.cells, self.probes_per_cell, self.queries);
55 let scale = 1.0 / sqrt(d as f32);
56
57 let mut r = vec![0.0f32; l1 * k * d];
60 let mut scores = vec![0.0f32; seq];
61 for l in 0..l1 {
62 for kk in 0..k {
63 let pr = &self.probes[(l * k + kk) * d..(l * k + kk + 1) * d];
64 let mut max = f32::NEG_INFINITY;
65 for (t, sc) in scores.iter_mut().enumerate() {
66 let cell = &cells[(t * l1 + l) * d..(t * l1 + l + 1) * d];
67 let mut acc = 0.0f32;
68 for (a, b) in cell.iter().zip(pr) {
69 acc += a * b;
70 }
71 *sc = acc * scale;
72 if *sc > max {
73 max = *sc;
74 }
75 }
76 let mut sum = 0.0f32;
77 for sc in scores.iter_mut() {
78 *sc = exp(*sc - max);
79 sum += *sc;
80 }
81 let inv = 1.0 / sum;
82
83 let dst = &mut r[(l * k + kk) * d..(l * k + kk + 1) * d];
84 dst.fill(0.0);
85 for (t, &sc) in scores.iter().enumerate() {
86 let w = sc * inv;
87 let cell = &cells[(t * l1 + l) * d..(t * l1 + l + 1) * d];
88 for (o, &c) in dst.iter_mut().zip(cell) {
89 *o += w * c;
90 }
91 }
92 rms_unit(dst);
94 let g = self.gain[l * k + kk];
95 for o in dst.iter_mut() {
96 *o *= g;
97 }
98 }
99 }
100
101 self.pool_and_project(&r, l1, k, q, d)
102 }
103
104 fn pool_and_project(&self, r: &[f32], l1: usize, k: usize, q: usize, d: usize) -> Vec<f32> {
108 let scale = 1.0 / sqrt(d as f32);
109 let m = l1 * k;
111 let mut pooled = vec![0.0f32; q * d];
112 let mut u = vec![0.0f32; m];
113 for qi in 0..q {
114 let qv = &self.query[qi * d..(qi + 1) * d];
115 let mut max = f32::NEG_INFINITY;
116 for (idx, uu) in u.iter_mut().enumerate() {
117 let rv = &r[idx * d..(idx + 1) * d];
118 let mut acc = 0.0f32;
119 for (a, b) in rv.iter().zip(qv) {
120 acc += a * b;
121 }
122 *uu = acc * scale + self.row_bias[qi * m + idx];
123 if *uu > max {
124 max = *uu;
125 }
126 }
127 let mut sum = 0.0f32;
128 for uu in u.iter_mut() {
129 *uu = exp(*uu - max);
130 sum += *uu;
131 }
132 let inv = 1.0 / sum;
133
134 let dst = &mut pooled[qi * d..(qi + 1) * d];
135 dst.fill(0.0);
136 for (idx, &uu) in u.iter().enumerate() {
137 let w = uu * inv;
138 let rv = &r[idx * d..(idx + 1) * d];
139 for (o, &v) in dst.iter_mut().zip(rv) {
140 *o += w * v;
141 }
142 }
143 }
144
145 let mut out = vec![0.0f32; self.out_dim];
147 for (o, dst) in out.iter_mut().enumerate() {
148 let row = &self.proj[o * q * d..(o + 1) * q * d];
149 let mut acc = 0.0f32;
150 for (a, b) in row.iter().zip(&pooled) {
151 acc += a * b;
152 }
153 *dst = acc + self.bias.get(o).copied().unwrap_or(0.0);
154 }
155 out
156 }
157}
158
159pub struct ProbePool<'a> {
171 head: &'a ProbeHead,
172 d: usize,
173 max: Vec<f32>,
175 den: Vec<f32>,
177 num: Vec<f32>,
179}
180
181impl<'a> ProbePool<'a> {
182 pub fn new(head: &'a ProbeHead, d_model: usize) -> Self {
183 let m = head.cells * head.probes_per_cell;
184 Self {
185 head,
186 d: d_model,
187 max: vec![f32::NEG_INFINITY; m],
188 den: vec![0.0; m],
189 num: vec![0.0; m * d_model],
190 }
191 }
192
193 pub fn observe(&mut self, cell_index: usize, cell: &[f32]) {
199 debug_assert_eq!(cell.len(), self.d);
200 let (k, d) = (self.head.probes_per_cell, self.d);
201 let scale = 1.0 / sqrt(d as f32);
202 for kk in 0..k {
203 let idx = cell_index * k + kk;
204 let pr = &self.head.probes[idx * d..(idx + 1) * d];
205 let mut dot = 0.0f32;
206 for (a, b) in cell.iter().zip(pr) {
207 dot += a * b;
208 }
209 let s = dot * scale;
210
211 let m0 = self.max[idx];
212 let m1 = if s > m0 { s } else { m0 };
213 let shift = if m0.is_finite() { exp(m0 - m1) } else { 0.0 };
215 let w = exp(s - m1);
216 self.den[idx] = self.den[idx] * shift + w;
217 let dst = &mut self.num[idx * d..(idx + 1) * d];
218 for (o, &c) in dst.iter_mut().zip(cell) {
219 *o = *o * shift + w * c;
220 }
221 self.max[idx] = m1;
222 }
223 }
224
225 pub fn finish(self) -> Vec<f32> {
227 let (l1, k, q, d) = (
228 self.head.cells,
229 self.head.probes_per_cell,
230 self.head.queries,
231 self.d,
232 );
233 let mut r = self.num;
234 for idx in 0..l1 * k {
235 let inv = 1.0 / self.den[idx];
236 let dst = &mut r[idx * d..(idx + 1) * d];
237 for o in dst.iter_mut() {
238 *o *= inv;
239 }
240 rms_unit(dst);
241 let g = self.head.gain[idx];
242 for o in dst.iter_mut() {
243 *o *= g;
244 }
245 }
246 self.head.pool_and_project(&r, l1, k, q, d)
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 fn trivial(d: usize) -> ProbeHead {
257 ProbeHead {
258 probes: vec![0.0; d],
259 gain: vec![1.0],
260 query: vec![0.0; d],
261 row_bias: vec![0.0],
262 proj: vec![1.0; d],
263 bias: vec![0.5],
264 cells: 1,
265 probes_per_cell: 1,
266 queries: 1,
267 out_dim: 1,
268 }
269 }
270
271 #[test]
272 fn a_zero_probe_averages_the_sequence() {
273 let d = 4;
276 let h = trivial(d);
277 let cells = vec![
278 1.0f32, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, ];
281 let out = h.forward(&cells, 2, d);
282 assert!(
285 (out[0] - (2.0 / (4.0f32 / 4.0).sqrt() + 0.5)).abs() < 1e-5,
286 "{out:?}"
287 );
288 }
289
290 #[test]
291 fn the_gain_scales_the_summary() {
292 let d = 4;
293 let mut h = trivial(d);
294 let cells = vec![2.0f32, 0.0, 0.0, 0.0];
295 let base = h.forward(&cells, 1, d)[0] - 0.5;
296 h.gain[0] = 3.0;
297 let scaled = h.forward(&cells, 1, d)[0] - 0.5;
298 assert!((scaled - 3.0 * base).abs() < 1e-4, "{scaled} vs 3 * {base}");
299 }
300}