1use kime_model::laya::{Affine, LayaGraph, LayaSpec, TORCH_EPS};
10use kime_model::{Model, Tensors};
11use kime_tensor::{Buckets, Executor, HostTensor};
12
13use crate::attention::{HEAD, attention};
14use crate::gemm::linear;
15use crate::ops::{Rope, add, geglu, gelu, layer_norm};
16use crate::par;
17use crate::plan::CpuBackend;
18
19pub fn executor(model: &Model, threads: usize) -> kime_tensor::Result<Executor<CpuBackend>> {
26 executor_with(model, CpuBackend::new(threads))
27}
28
29pub fn executor_with(
35 model: &Model,
36 backend: CpuBackend,
37) -> kime_tensor::Result<Executor<CpuBackend>> {
38 build(&model.spec, &model.graph, &model.tensors, backend)
39}
40
41pub fn executor_from(
47 spec: &LayaSpec,
48 graph: &LayaGraph,
49 tensors: &Tensors,
50 threads: usize,
51) -> kime_tensor::Result<Executor<CpuBackend>> {
52 build(spec, graph, tensors, CpuBackend::new(threads))
53}
54
55fn build(
56 spec: &LayaSpec,
57 graph: &LayaGraph,
58 tensors: &Tensors,
59 backend: CpuBackend,
60) -> kime_tensor::Result<Executor<CpuBackend>> {
61 let host: Vec<HostTensor<'_>> = (0..tensors.entries().len())
62 .map(|i| {
63 let v = tensors.view(i);
64 HostTensor { dtype: v.dtype, shape: v.shape, bytes: v.bytes }
65 })
66 .collect();
67 let (plan, vocab) = (graph.plan(spec), spec.encoder.vocab);
68 Executor::new(backend, &host, plan, &Buckets::default(), "compat", vocab, 3)
69}
70
71#[derive(Debug, Clone, Copy)]
73pub struct Input<'a> {
74 pub ids: &'a [u32],
76 pub markers: &'a [u32],
78 pub qtype: usize,
80}
81
82#[derive(Debug, Clone, PartialEq)]
84pub struct Output {
85 pub logits: Vec<f32>,
87 pub act: [f32; 2],
89}
90
91#[derive(Debug)]
93pub struct Compat {
94 spec: LayaSpec,
95 graph: LayaGraph,
96 w: Vec<Vec<f32>>,
97 threads: usize,
98}
99
100impl Compat {
101 #[must_use]
104 pub fn new(model: &Model, threads: usize) -> Self {
105 Self::from_parts(&model.spec, &model.graph, &model.tensors, threads)
106 }
107
108 #[must_use]
110 pub fn from_parts(spec: &LayaSpec, graph: &LayaGraph, t: &Tensors, threads: usize) -> Self {
111 let w = par::map(t.entries().len(), threads, |i| t.view(i).to_f32());
112 Self { spec: spec.clone(), graph: graph.clone(), w, threads: threads.max(1) }
113 }
114
115 pub fn set_threads(&mut self, threads: usize) {
117 self.threads = threads.max(1);
118 }
119
120 #[must_use]
122 pub fn spec(&self) -> &LayaSpec {
123 &self.spec
124 }
125
126 fn w(&self, i: usize) -> &[f32] {
127 &self.w[i]
128 }
129
130 fn linear(&self, x: &[f32], k: usize, w: usize, b: Option<usize>) -> Vec<f32> {
132 let m = x.len() / k;
133 let n = self.w[w].len() / k;
134 let mut y = vec![0f32; m * n];
135 linear(x, m, k, self.w(w), n, b.map(|b| self.w(b)), &mut y, self.threads);
136 y
137 }
138
139 fn affine(&self, x: &[f32], k: usize, a: Affine) -> Vec<f32> {
140 self.linear(x, k, a.w, Some(a.b))
141 }
142
143 #[must_use]
150 pub fn forward(&self, batch: &[Input<'_>]) -> Vec<Output> {
151 let e = &self.spec.encoder;
152 let g = &self.graph;
153 let d = e.d;
154 let heads = e.heads;
155 assert_eq!(d, heads * HEAD);
156 let mut cu = vec![0usize];
157 for x in batch {
158 cu.push(cu.last().unwrap() + x.ids.len());
159 }
160 let t = *cu.last().unwrap();
161 let longest = batch.iter().map(|x| x.ids.len()).max().unwrap_or(0);
162
163 let emb = self.w(g.tok_embeddings);
165 let mut h = Vec::with_capacity(t * d);
166 for x in batch {
167 for &id in x.ids {
168 let id = id as usize;
169 assert!(id < e.vocab, "token id {id} is past the vocabulary");
170 h.extend_from_slice(&emb[id * d..(id + 1) * d]);
171 }
172 }
173 let mut x = vec![0f32; t * d];
174 layer_norm(&h, d, self.w(g.embed_norm), None, e.norm_eps, &mut x);
175 std::mem::swap(&mut h, &mut x);
176
177 let ropes: Vec<(f64, Rope)> = [e.rope_global, e.rope_local]
179 .iter()
180 .map(|&theta| (theta, Rope::new(theta, HEAD, longest)))
181 .collect();
182 let mut att = vec![0f32; t * d];
183 let mut act = vec![0f32; t * e.inter];
184 for layer in &g.layers {
185 let xin = match layer.attn_norm {
186 Some(n) => {
187 layer_norm(&h, d, self.w(n), None, e.norm_eps, &mut x);
188 &x
189 }
190 None => &h,
191 };
192 let mut qkv = self.linear(xin, d, layer.wqkv, None);
193 let rope =
194 &ropes.iter().find(|r| r.0.to_bits() == layer.rope_theta.to_bits()).unwrap().1;
195 for s in 0..batch.len() {
196 for (pos, i) in (cu[s]..cu[s + 1]).enumerate() {
197 let row = &mut qkv[i * 3 * d..(i + 1) * 3 * d];
198 for head in row[..2 * d].as_chunks_mut::<HEAD>().0 {
199 rope.apply(head, pos);
200 }
201 }
202 }
203 let window = (!layer.global).then_some(e.window / 2);
204 attention(&qkv, heads, &cu, window, &mut att, self.threads);
205 add(&mut h, &self.linear(&att, d, layer.wo, None));
206 layer_norm(&h, d, self.w(layer.mlp_norm), None, e.norm_eps, &mut x);
207 let u = self.linear(&x, d, layer.wi, None);
208 geglu(&u, e.inter, &mut act);
209 add(&mut h, &self.linear(&act, e.inter, layer.mlp_wo, None));
210 }
211 layer_norm(&h, d, self.w(g.final_norm), None, e.norm_eps, &mut x);
212 std::mem::swap(&mut h, &mut x);
213
214 let types = self.w(g.type_emb);
216 for (s, input) in batch.iter().enumerate() {
217 assert!(input.qtype < 3, "qtype {} is not 0, 1 or 2", input.qtype);
218 let te = &types[input.qtype * d..(input.qtype + 1) * d];
219 for i in cu[s]..cu[s + 1] {
220 add(&mut h[i * d..(i + 1) * d], te);
221 }
222 }
223
224 for l in &g.head {
226 layer_norm(&h, d, self.w(l.norm1_w), Some(self.w(l.norm1_b)), TORCH_EPS, &mut x);
227 let qkv = self.linear(&x, d, l.in_proj_w, Some(l.in_proj_b));
228 attention(&qkv, heads, &cu, None, &mut att, self.threads);
229 add(&mut h, &self.linear(&att, d, l.out_proj_w, Some(l.out_proj_b)));
230 layer_norm(&h, d, self.w(l.norm2_w), Some(self.w(l.norm2_b)), TORCH_EPS, &mut x);
231 let mut f = self.linear(&x, d, l.linear1_w, Some(l.linear1_b));
232 f.iter_mut().for_each(|v| *v = v.max(0.0));
233 let ffn = f.len() / t.max(1);
234 add(&mut h, &self.linear(&f, ffn, l.linear2_w, Some(l.linear2_b)));
235 }
236
237 let mut m = Vec::new();
239 for (s, input) in batch.iter().enumerate() {
240 for &p in input.markers {
241 let p = p as usize;
242 assert!(p < input.ids.len(), "marker {p} is past its sequence");
243 m.extend_from_slice(&h[(cu[s] + p) * d..(cu[s] + p + 1) * d]);
244 }
245 }
246 let mut mn = vec![0f32; m.len()];
247 let sn = g.scorer_norm;
248 layer_norm(&m, d, self.w(sn.w), Some(self.w(sn.b)), TORCH_EPS, &mut mn);
249 let mut z = self.affine(&mn, d, g.scorer_in);
250 z.iter_mut().for_each(|v| *v = gelu(*v));
251 let logits = self.affine(&z, d, g.scorer_out);
252
253 let mut feats = Vec::with_capacity(batch.len() * (d + 4));
255 let mut at = 0;
256 let mut out = Vec::with_capacity(batch.len());
257 for (s, input) in batch.iter().enumerate() {
258 let k = input.markers.len();
259 let l = logits[at..at + k].to_vec();
260 at += k;
261 let first = cu[s] * d;
262 if cu[s + 1] > cu[s] {
263 feats.extend_from_slice(&h[first..first + d]);
264 } else {
265 feats.extend(std::iter::repeat_n(0.0, d));
266 }
267 feats.extend_from_slice(&act_features(&l));
268 out.push(Output { logits: l, act: [0.0; 2] });
269 }
270 let a = self.affine(&feats, d + 4, g.act_in);
271 let a: Vec<f32> = a.iter().map(|&v| gelu(v)).collect();
272 let a = self.affine(&a, a.len() / batch.len().max(1), g.act_out);
273 for (s, o) in out.iter_mut().enumerate() {
274 o.act = [a[2 * s], a[2 * s + 1]];
275 }
276 out
277 }
278}
279
280#[must_use]
284pub fn act_features(logits: &[f32]) -> [f32; 4] {
285 let kf = logits.len().max(2) as f32;
286 if logits.is_empty() {
287 return [0.0, 0.0, 0.0, kf / 255.0];
288 }
289 let mx = logits.iter().copied().fold(f32::NEG_INFINITY, f32::max);
290 let e: Vec<f32> = logits.iter().map(|&l| (l - mx).exp()).collect();
291 let sum: f32 = e.iter().sum();
292 let p: Vec<f32> = e.iter().map(|x| x / sum).collect();
293 let ent = -p.iter().map(|&q| q * q.max(1e-9).ln()).sum::<f32>() / kf.ln();
294 let mut sorted = p.clone();
295 sorted.sort_by(|a, b| b.total_cmp(a));
296 let top1 = sorted[0];
297 let top2 = sorted.get(1).copied().unwrap_or(0.0);
298 [top1, top1 - top2, ent, kf / 255.0]
299}