singe_kernel/cpu/
recurrent.rs1#[cfg(feature = "dtype-bf16")]
4use half::bf16;
5#[cfg(feature = "dtype-f16")]
6use half::f16;
7
8pub fn gated_delta_rule_preprocess(
14 b: &[f32],
15 a: &[f32],
16 a_log: &[f32],
17 dt_bias: &[f32],
18 rows: usize,
19 hidden: usize,
20) -> (Vec<f32>, Vec<f32>) {
21 let mut beta = vec![0.0f32; rows * hidden];
22 let mut g = vec![0.0f32; rows * hidden];
23 for row in 0..rows {
24 for column in 0..hidden {
25 let offset = row * hidden + column;
26 beta[offset] = 1.0 / (1.0 + (-b[offset]).exp());
27 g[offset] = -a_log[column].exp() * stable_softplus(a[offset] + dt_bias[column]);
28 }
29 }
30 (beta, g)
31}
32
33fn stable_softplus(value: f32) -> f32 {
34 if value > 20.0 {
35 value
36 } else {
37 value.exp().ln_1p()
38 }
39}
40
41#[cfg(feature = "dtype-f16")]
42pub fn half_vec(values: &[f32]) -> Vec<f16> {
43 values.iter().copied().map(f16::from_f32).collect()
44}
45
46#[cfg(feature = "dtype-f16")]
47pub fn half_to_f32(values: &[f16]) -> Vec<f32> {
48 values.iter().map(|value| value.to_f32()).collect()
49}
50
51#[cfg(feature = "dtype-f16")]
52pub fn round_half_vec(values: &[f32]) -> Vec<f32> {
53 values
54 .iter()
55 .copied()
56 .map(f16::from_f32)
57 .map(|value| value.to_f32())
58 .collect()
59}
60
61#[cfg(feature = "dtype-bf16")]
62pub fn bfloat_vec(values: &[f32]) -> Vec<bf16> {
63 values.iter().copied().map(bf16::from_f32).collect()
64}
65
66#[cfg(feature = "dtype-bf16")]
67pub fn bfloat_to_f32(values: &[bf16]) -> Vec<f32> {
68 values.iter().map(|value| value.to_f32()).collect()
69}
70
71#[cfg(feature = "dtype-bf16")]
72pub fn round_bfloat_vec(values: &[f32]) -> Vec<f32> {
73 values
74 .iter()
75 .copied()
76 .map(bf16::from_f32)
77 .map(|value| value.to_f32())
78 .collect()
79}
80
81pub fn recurrent_gated_delta_rule(
82 query: &[f32],
83 key: &[f32],
84 value: &[f32],
85 gate: &[f32],
86 beta: &[f32],
87 initial_state: Option<&[f32]>,
88 batch: usize,
89 time: usize,
90 query_heads: usize,
91 value_heads: usize,
92 qk_dim: usize,
93 value_dim: usize,
94 use_qk_l2norm: bool,
95) -> (Vec<f32>, Vec<f32>) {
96 let mut out = vec![0.0f32; batch * time * value_heads * value_dim];
97 let mut final_state = vec![0.0f32; batch * value_heads * qk_dim * value_dim];
98 let heads_per_group = value_heads / query_heads;
99 for b in 0..batch {
100 for hv in 0..value_heads {
101 let h = hv / heads_per_group;
102 for v in 0..value_dim {
103 let mut state = vec![0.0f32; qk_dim];
104 if let Some(initial_state) = initial_state {
105 for k in 0..qk_dim {
106 state[k] =
107 initial_state[((b * value_heads + hv) * qk_dim + k) * value_dim + v];
108 }
109 }
110 for t in 0..time {
111 let mut query_t = vec![0.0f32; qk_dim];
112 let mut key_t = vec![0.0f32; qk_dim];
113 for k in 0..qk_dim {
114 let offset = ((b * time + t) * query_heads + h) * qk_dim + k;
115 query_t[k] = query[offset];
116 key_t[k] = key[offset];
117 }
118 if use_qk_l2norm {
119 normalize(&mut query_t);
120 normalize(&mut key_t);
121 }
122 let scale = 1.0 / (qk_dim as f32).sqrt();
123 for value in &mut query_t {
124 *value *= scale;
125 }
126 let gate_offset = (b * time + t) * value_heads + hv;
127 let gamma = gate[gate_offset].exp();
128 let beta_t = beta[gate_offset];
129 for value in &mut state {
130 *value *= gamma;
131 }
132 let kv_memory = dot(&state, &key_t);
133 let value_t = value[((b * time + t) * value_heads + hv) * value_dim + v];
134 let delta = (value_t - kv_memory) * beta_t;
135 for k in 0..qk_dim {
136 state[k] += key_t[k] * delta;
137 }
138 out[((b * time + t) * value_heads + hv) * value_dim + v] =
139 dot(&state, &query_t);
140 }
141 for k in 0..qk_dim {
142 final_state[((b * value_heads + hv) * qk_dim + k) * value_dim + v] = state[k];
143 }
144 }
145 }
146 }
147 (out, final_state)
148}
149
150fn normalize(values: &mut [f32]) {
151 let norm = values
152 .iter()
153 .map(|value| value * value)
154 .sum::<f32>()
155 .sqrt()
156 .max(1e-6);
157 for value in values {
158 *value /= norm;
159 }
160}
161
162fn dot(lhs: &[f32], rhs: &[f32]) -> f32 {
163 lhs.iter().zip(rhs).map(|(lhs, rhs)| lhs * rhs).sum()
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn gdr_preprocess_uses_stable_softplus_for_large_values() {
172 let b = vec![0.0; 4];
173 let a = vec![-50.0, 0.0, 25.0, 100.0];
174 let a_log = vec![0.0, 0.25, -0.5, 0.5];
175 let dt_bias = vec![0.0; 4];
176
177 let (_, g) = gated_delta_rule_preprocess(&b, &a, &a_log, &dt_bias, 1, 4);
178
179 let expected = a
180 .iter()
181 .zip(&a_log)
182 .map(|(a, a_log)| -a_log.exp() * stable_softplus(*a))
183 .collect::<Vec<_>>();
184 singe_core::assert_close!(&g, &expected, 1e-5);
185 assert!(g.iter().all(|value| value.is_finite()));
186 }
187}