1#[inline]
31pub fn softplus(x: f32) -> f32 {
32 if x > 20.0 {
33 x
34 } else {
35 (1.0 + x.exp()).ln()
36 }
37}
38
39pub fn conv_step(state: &mut [f32], taps: &[f32], d_conv: usize, x: &[f32], out: &mut [f32]) {
48 let width = x.len();
49 assert_eq!(state.len(), (d_conv - 1) * width);
50 assert_eq!(taps.len(), width * d_conv);
51 assert_eq!(out.len(), width);
52 for c in 0..width {
53 let t = &taps[c * d_conv..(c + 1) * d_conv];
54 let mut acc = 0.0f32;
57 for (i, tap) in t.iter().enumerate().take(d_conv - 1) {
58 acc += state[i * width + c] * tap;
59 }
60 acc += x[c] * t[d_conv - 1];
61 out[c] = acc;
62 }
63 if d_conv > 1 {
65 state.copy_within(width.., 0);
66 state[(d_conv - 2) * width..].copy_from_slice(x);
67 }
68}
69
70#[derive(Debug, Clone, Copy)]
72pub enum Decay<'a> {
73 PerHead(&'a [f32]),
76 PerState(&'a [f32]),
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub struct ScanDims {
84 pub n_head: usize,
85 pub head_dim: usize,
86 pub d_state: usize,
87 pub n_group: usize,
88}
89
90impl ScanDims {
91 pub fn state_len(self) -> usize {
93 self.n_head * self.head_dim * self.d_state
94 }
95}
96
97#[allow(clippy::too_many_arguments)] pub fn scan_step(
106 dims: ScanDims,
107 state: &mut [f32],
108 x: &[f32],
109 dt: &[f32],
110 a: Decay<'_>,
111 b: &[f32],
112 c: &[f32],
113 y: &mut [f32],
114) {
115 let ScanDims {
116 n_head,
117 head_dim,
118 d_state,
119 n_group,
120 } = dims;
121 assert_eq!(state.len(), dims.state_len());
122 assert_eq!(x.len(), n_head * head_dim);
123 assert_eq!(dt.len(), n_head);
124 match a {
125 Decay::PerHead(a) => assert_eq!(a.len(), n_head),
126 Decay::PerState(a) => assert_eq!(a.len(), n_head * d_state),
127 }
128 assert_eq!(b.len(), n_group * d_state);
129 assert_eq!(c.len(), n_group * d_state);
130 assert_eq!(y.len(), n_head * head_dim);
131 assert_eq!(n_head % n_group, 0, "ops.cpp:9659");
132 let heads_per_group = n_head / n_group;
133 for h in 0..n_head {
134 let dt_sp = softplus(dt[h]);
135 let g = h / heads_per_group;
136 let (bg, cg) = (
137 &b[g * d_state..(g + 1) * d_state],
138 &c[g * d_state..(g + 1) * d_state],
139 );
140 for d in 0..head_dim {
141 let ii = h * head_dim + d;
142 let x_dt = x[ii] * dt_sp;
143 let s = &mut state[ii * d_state..(ii + 1) * d_state];
144 let mut sum = 0.0f32;
145 match a {
146 Decay::PerHead(a) => {
147 let da = (dt_sp * a[h]).exp();
149 for k in 0..d_state {
150 let v = s[k] * da + bg[k] * x_dt;
151 sum += v * cg[k];
152 s[k] = v;
153 }
154 }
155 Decay::PerState(a) => {
156 let ah = &a[h * d_state..(h + 1) * d_state];
158 for k in 0..d_state {
159 let v = s[k] * (dt_sp * ah[k]).exp() + bg[k] * x_dt;
160 sum += v * cg[k];
161 s[k] = v;
162 }
163 }
164 }
165 y[ii] = sum;
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn softplus_is_ggml_s() {
176 assert!((softplus(0.0) - 2f32.ln()).abs() < 1e-7);
177 assert_eq!(softplus(25.0), 25.0);
178 assert!((softplus(-30.0)).abs() < 1e-6);
179 }
180
181 #[test]
184 fn conv_step_puts_the_newest_input_on_the_last_tap_and_shifts() {
185 let taps = [1.0, 2.0, 3.0, 0.0, 0.0, 1.0];
186 let mut state = vec![0.0f32; 2 * 2];
187 let mut out = [0.0f32; 2];
188 conv_step(&mut state, &taps, 3, &[1.0, 5.0], &mut out);
189 assert_eq!(out, [3.0, 5.0]);
190 assert_eq!(state, vec![0.0, 0.0, 1.0, 5.0]);
191 conv_step(&mut state, &taps, 3, &[2.0, 6.0], &mut out);
192 assert_eq!(out, [3.0 * 2.0 + 2.0 * 1.0, 6.0]);
193 conv_step(&mut state, &taps, 3, &[1.0, 7.0], &mut out);
194 assert_eq!(out, [3.0 + 4.0 + 1.0, 7.0]);
195 assert_eq!(state, vec![2.0, 6.0, 1.0, 7.0]);
196 }
197
198 #[test]
200 fn scan_step_is_the_recurrence() {
201 let dims = ScanDims {
202 n_head: 1,
203 head_dim: 1,
204 d_state: 2,
205 n_group: 1,
206 };
207 let mut state = vec![0.0f32; 2];
208 let mut y = [0.0f32];
209 let a = Decay::PerHead(&[-1.0f32]);
210 scan_step(
212 dims,
213 &mut state,
214 &[2.0],
215 &[0.0],
216 a,
217 &[1.0, 3.0],
218 &[1.0, 1.0],
219 &mut y,
220 );
221 let x_dt = 2.0 * 2f32.ln();
222 assert!((state[0] - x_dt).abs() < 1e-6 && (state[1] - 3.0 * x_dt).abs() < 1e-6);
223 assert!((y[0] - 4.0 * x_dt).abs() < 1e-5);
224 scan_step(
225 dims,
226 &mut state,
227 &[0.0],
228 &[0.0],
229 a,
230 &[1.0, 3.0],
231 &[1.0, 0.0],
232 &mut y,
233 );
234 assert!((state[0] - 0.5 * x_dt).abs() < 1e-6, "decayed by dA");
235 assert!((y[0] - 0.5 * x_dt).abs() < 1e-6, "C selects state 0");
236 }
237
238 #[test]
241 fn per_state_decay_is_one_exp_per_state_element() {
242 let dims = ScanDims {
243 n_head: 1,
244 head_dim: 1,
245 d_state: 2,
246 n_group: 1,
247 };
248 let mut state = vec![1.0f32; 2];
249 let mut y = [0.0f32];
250 let a = Decay::PerState(&[0.0, -(2f32.ln()) / 30.0]);
252 scan_step(
253 dims,
254 &mut state,
255 &[0.0],
256 &[30.0],
257 a,
258 &[0.0, 0.0],
259 &[1.0, 1.0],
260 &mut y,
261 );
262 assert!(
263 (state[0] - 1.0).abs() < 1e-6 && (state[1] - 0.5).abs() < 1e-5,
264 "{state:?}"
265 );
266 assert!((y[0] - 1.5).abs() < 1e-5);
267 }
268
269 #[test]
271 fn heads_read_their_group_s_b_and_c() {
272 let dims = ScanDims {
273 n_head: 4,
274 head_dim: 1,
275 d_state: 1,
276 n_group: 2,
277 };
278 let mut state = vec![0.0f32; 4];
279 let mut y = [0.0f32; 4];
280 scan_step(
281 dims,
282 &mut state,
283 &[1.0; 4],
284 &[30.0; 4],
285 Decay::PerHead(&[0.0; 4]),
286 &[1.0, 10.0],
287 &[1.0, 1.0],
288 &mut y,
289 );
290 assert_eq!(y, [30.0, 30.0, 300.0, 300.0]);
292 }
293}