1use crate::Engine;
13use crate::model::GpuTensor;
14use cudarc::driver::CudaSlice;
15
16pub struct DflashCfg {
17 pub hidden: usize, pub n_head: usize, pub n_kv: usize, pub head_dim: usize, pub n_ff: usize, pub n_layer: usize, pub eps: f32, pub rope_theta: f32, pub block_size: usize, pub mask_token_id: u32, pub target_layer_ids: Vec<usize>, pub sliding_window: usize, pub layer_sliding: Vec<bool>,
31}
32
33pub struct DflashLayer {
34 pub wq: GpuTensor, pub wk: GpuTensor, pub wv: GpuTensor, pub wo: GpuTensor, pub w_gate: GpuTensor, pub w_up: GpuTensor, pub w_down: GpuTensor, pub ln_in: CudaSlice<f32>, pub ln_post: CudaSlice<f32>, pub q_norm: CudaSlice<f32>, pub k_norm: CudaSlice<f32>, }
46
47pub struct DflashDraft {
48 pub cfg: DflashCfg,
49 pub layers: Vec<DflashLayer>,
50 pub fc: GpuTensor, pub hidden_norm: CudaSlice<f32>, pub norm: CudaSlice<f32>, pub markov: Option<MarkovHead>,
58}
59
60pub struct MarkovHead {
61 pub w1_bf16: CudaSlice<u8>, pub w2: GpuTensor, pub rank: usize,
64 pub vocab: usize,
65}
66
67fn bf16_to_f32(bytes: &[u8]) -> Vec<f32> {
68 bytes
69 .chunks_exact(2)
70 .map(|c| f32::from_bits((u16::from_le_bytes([c[0], c[1]]) as u32) << 16))
71 .collect()
72}
73
74fn encode_q8_0(vals: &[f32]) -> Vec<u8> {
79 let mut out = Vec::with_capacity(vals.len() / 32 * 34);
80 for blk in vals.chunks_exact(32) {
81 let amax = blk.iter().fold(0f32, |a, v| a.max(v.abs()));
82 let d = amax / 127.0;
83 let id = if d > 0.0 { 1.0 / d } else { 0.0 };
84 let dh = half_from_f32(d);
85 out.extend_from_slice(&dh.to_le_bytes());
86 for &v in blk {
87 out.push(((v * id).round().clamp(-127.0, 127.0)) as i8 as u8);
88 }
89 }
90 out
91}
92
93fn encode_q4_0(vals: &[f32]) -> Vec<u8> {
98 let mut out = Vec::with_capacity(vals.len() / 32 * 18);
99 for blk in vals.chunks_exact(32) {
100 let mut amax = 0f32;
102 let mut mx = 0f32;
103 for &v in blk {
104 if v.abs() > amax {
105 amax = v.abs();
106 mx = v;
107 }
108 }
109 let d = mx / -8.0;
110 let id = if d != 0.0 { 1.0 / d } else { 0.0 };
111 out.extend_from_slice(&half_from_f32(d).to_le_bytes());
112 for j in 0..16 {
113 let x0 = (blk[j] * id + 8.5).clamp(0.0, 15.0) as u8;
114 let x1 = (blk[j + 16] * id + 8.5).clamp(0.0, 15.0) as u8;
115 out.push(x0 | (x1 << 4));
116 }
117 }
118 out
119}
120
121fn half_from_f32(v: f32) -> u16 {
122 let b = v.to_bits();
124 let sign = ((b >> 16) & 0x8000) as u16;
125 let exp = ((b >> 23) & 0xff) as i32 - 127 + 15;
126 let man = b & 0x7fffff;
127 if exp <= 0 {
128 return sign;
129 } if exp >= 31 {
131 return sign | 0x7c00;
132 } let mut h = sign | ((exp as u16) << 10) | ((man >> 13) as u16);
134 let rem = man & 0x1fff;
136 if rem > 0x1000 || (rem == 0x1000 && (h & 1) == 1) {
137 h += 1;
138 }
139 h
140}
141
142impl DflashDraft {
143 pub fn load(e: &Engine, dir: &std::path::Path) -> Result<Self, Box<dyn std::error::Error>> {
146 let txt = std::fs::read_to_string(dir.join("config.json"))?;
147 fn num(txt: &str, key: &str) -> Option<f64> {
148 let i = txt.find(&format!("\"{key}\""))?;
149 let rest = &txt[i..];
150 let colon = rest.find(':')?;
151 let val: String = rest[colon + 1..]
152 .trim_start()
153 .chars()
154 .take_while(|c| {
155 c.is_ascii_digit()
156 || *c == '.'
157 || *c == '-'
158 || *c == 'e'
159 || *c == 'E'
160 || *c == '+'
161 })
162 .collect();
163 val.parse().ok()
164 }
165 fn num_list(txt: &str, key: &str) -> Vec<usize> {
166 let Some(i) = txt.find(&format!("\"{key}\"")) else {
167 return Vec::new();
168 };
169 let rest = &txt[i..];
170 let (Some(a), Some(b)) = (rest.find('['), rest.find(']')) else {
171 return Vec::new();
172 };
173 rest[a + 1..b]
174 .split(',')
175 .filter_map(|s| s.trim().parse().ok())
176 .collect()
177 }
178 let g = |k: &str| num(&txt, k).unwrap_or_else(|| panic!("config missing {k}")) as usize;
179 let layer_sliding: Vec<bool> = {
181 let i = txt.find("\"layer_types\"").expect("layer_types");
182 let rest = &txt[i..];
183 let (a, b) = (rest.find('[').unwrap(), rest.find(']').unwrap());
184 rest[a + 1..b]
185 .split(',')
186 .map(|s| s.contains("sliding_attention"))
187 .collect()
188 };
189 let cfg = DflashCfg {
190 hidden: g("hidden_size"),
191 n_head: g("num_attention_heads"),
192 n_kv: g("num_key_value_heads"),
193 head_dim: g("head_dim"),
194 n_ff: g("intermediate_size"),
195 n_layer: g("num_hidden_layers"),
196 eps: num(&txt, "rms_norm_eps").expect("rms_norm_eps") as f32,
197 rope_theta: num(&txt, "rope_theta").expect("rope_theta") as f32,
198 block_size: g("block_size"),
199 mask_token_id: g("mask_token_id") as u32,
200 target_layer_ids: num_list(&txt, "target_layer_ids"),
201 sliding_window: g("sliding_window"),
202 layer_sliding,
203 };
204 let st = memra_gguf::safetensors::StModel::open(&dir.join("model.safetensors"))?;
205 let up = |name: &str| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
208 let (_info, bytes) = st
209 .raw(name)
210 .ok_or_else(|| format!("missing tensor {name}"))?;
211 Ok(e.htod(&bf16_to_f32(bytes))?)
212 };
213 let prec = std::env::var("MEMRA_DFLASH_PREC").unwrap_or_else(|_| "q8".into());
217 let upw = |name: &str| -> Result<GpuTensor, Box<dyn std::error::Error>> {
218 let (info, bytes) = st
219 .raw(name)
220 .ok_or_else(|| format!("missing tensor {name}"))?;
221 let shape = info.ne(); let in_f = shape[0] as usize;
223 let is_ffn = name.contains(".mlp.");
224 let bf16 = prec == "bf16"
225 || (prec == "mixed" && !is_ffn)
226 || (prec == "fc" && name == "fc.weight");
227 if bf16 {
228 return Ok(GpuTensor::FloatBf16 {
229 data: e.upload_u8(bytes)?,
230 ne: shape.to_vec(),
231 });
232 }
233 let f32s = bf16_to_f32(bytes);
234 if prec == "q4" {
235 let q = encode_q4_0(&f32s);
236 return Ok(GpuTensor::Quant {
237 bytes: e.upload_u8(&q)?,
238 qtype: crate::QT_Q4_0,
239 row_bytes: in_f / 32 * 18,
240 ne: shape.to_vec(),
241 scale: 1.0,
242 rp: false,
243 #[cfg(memra_cutlass)]
244 cutlass: None,
245 fp8: None,
246 blk: None,
247 rp4: None,
248 f16: None,
249 });
250 }
251 let q = encode_q8_0(&f32s);
252 Ok(GpuTensor::Quant {
253 bytes: e.upload_u8(&q)?,
254 qtype: crate::QT_Q8_0,
255 row_bytes: in_f / 32 * 34,
256 ne: shape.to_vec(),
257 scale: 1.0,
258 rp: false,
259 #[cfg(memra_cutlass)]
260 cutlass: None,
261 fp8: None,
262 blk: None,
263 rp4: None,
264 f16: None,
265 })
266 };
267 let mut layers = Vec::with_capacity(cfg.n_layer);
268 for i in 0..cfg.n_layer {
269 let p = |s: &str| format!("layers.{i}.{s}");
270 layers.push(DflashLayer {
271 wq: upw(&p("self_attn.q_proj.weight"))?,
272 wk: upw(&p("self_attn.k_proj.weight"))?,
273 wv: upw(&p("self_attn.v_proj.weight"))?,
274 wo: upw(&p("self_attn.o_proj.weight"))?,
275 w_gate: upw(&p("mlp.gate_proj.weight"))?,
276 w_up: upw(&p("mlp.up_proj.weight"))?,
277 w_down: upw(&p("mlp.down_proj.weight"))?,
278 ln_in: up(&p("input_layernorm.weight"))?,
279 ln_post: up(&p("post_attention_layernorm.weight"))?,
280 q_norm: up(&p("self_attn.q_norm.weight"))?,
281 k_norm: up(&p("self_attn.k_norm.weight"))?,
282 });
283 }
284 let markov = if let Some((info, bytes)) = st.raw("markov_head.markov_w1.weight") {
285 let sh = info.ne(); let (rank, vocab) = (sh[0] as usize, sh[1] as usize);
287 let (_i2, b2) = st
288 .raw("markov_head.markov_w2.weight")
289 .ok_or("markov_w2 missing beside markov_w1")?;
290 let w2f = bf16_to_f32(b2);
291 let w2q = encode_q8_0(&w2f);
292 Some(MarkovHead {
293 w1_bf16: e.upload_u8(bytes)?,
294 w2: GpuTensor::Quant {
295 bytes: e.upload_u8(&w2q)?,
296 qtype: crate::QT_Q8_0,
297 row_bytes: rank / 32 * 34,
298 ne: vec![rank as u64, vocab as u64],
299 scale: 1.0,
300 rp: false,
301 #[cfg(memra_cutlass)]
302 cutlass: None,
303 fp8: None,
304 blk: None,
305 rp4: None,
306 f16: None,
307 },
308 rank,
309 vocab,
310 })
311 } else {
312 None
313 };
314 Ok(Self {
315 fc: upw("fc.weight")?,
316 hidden_norm: up("hidden_norm.weight")?,
317 norm: up("norm.weight")?,
318 cfg,
319 layers,
320 markov,
321 })
322 }
323
324 fn mm(
326 &self,
327 e: &Engine,
328 w: &GpuTensor,
329 x: &CudaSlice<f32>,
330 t: usize,
331 _in_f: usize,
332 _out_f: usize,
333 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
334 Ok(e.matmul(w, x, t)?)
335 }
336
337 pub fn ctx_features(
348 &self,
349 e: &Engine,
350 taps: &CudaSlice<f32>,
351 t: usize,
352 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
353 let c = &self.cfg;
354 let n_taps = c.target_layer_ids.len();
355 let fc_out = self.mm(e, &self.fc, taps, t, n_taps * c.hidden, c.hidden)?;
356 let mut out = e.uninit(t * c.hidden)?;
357 e.rms_norm(&fc_out, &self.hidden_norm, &mut out, c.hidden, t, c.eps)?;
358 Ok(out)
359 }
360
361 pub fn forward(
362 &self,
363 e: &Engine,
364 target_hidden: &CudaSlice<f32>,
365 noise_emb: &CudaSlice<f32>,
366 pos: &[i32],
367 ctx: usize,
368 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
369 let ctx_f = self.ctx_features(e, target_hidden, ctx)?;
370 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
371 let v = e.dtoh(&ctx_f)?;
372 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
373 std::fs::write(format!("{dir}/memra-ctx_features.f32"), bytes)?;
374 }
375 self.forward_block(e, &ctx_f, noise_emb, pos, ctx)
376 }
377
378 pub fn forward_block(
381 &self,
382 e: &Engine,
383 ctx_f: &CudaSlice<f32>,
384 noise_emb: &CudaSlice<f32>,
385 pos: &[i32],
386 ctx: usize,
387 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
388 let c = &self.cfg;
389 let (h, nh, nkv, hd) = (c.hidden, c.n_head, c.n_kv, c.head_dim);
390 let b = c.block_size;
391 assert_eq!(pos.len(), ctx + b, "pos covers ctx rows then block rows");
392
393 let pos_blk = e.htod_i32(&pos[ctx..])?;
394
395 let mut x = e.clone_dtod(noise_emb)?; for (li, l) in self.layers.iter().enumerate() {
397 let _ = li;
398 let mut xn = e.uninit(b * h)?;
401 e.rms_norm(&x, &l.ln_in, &mut xn, h, b, c.eps)?;
402
403 let q0 = self.mm(e, &l.wq, &xn, b, h, nh * hd)?;
405 let k0c = self.mm(e, &l.wk, ctx_f, ctx, h, nkv * hd)?;
406 let v0c = self.mm(e, &l.wv, ctx_f, ctx, h, nkv * hd)?;
407 let k0b = self.mm(e, &l.wk, &xn, b, h, nkv * hd)?;
408 let v0b = self.mm(e, &l.wv, &xn, b, h, nkv * hd)?;
409
410 let mut k0 = e.uninit((ctx + b) * nkv * hd)?;
413 e.copy_into(&mut k0, 0, &k0c, ctx * nkv * hd)?;
414 e.copy_into(&mut k0, ctx * nkv * hd, &k0b, b * nkv * hd)?;
415 let mut v = e.uninit((ctx + b) * nkv * hd)?;
416 e.copy_into(&mut v, 0, &v0c, ctx * nkv * hd)?;
417 e.copy_into(&mut v, ctx * nkv * hd, &v0b, b * nkv * hd)?;
418
419 if li == 0 {
420 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
421 let v = e.dtoh(&q0)?;
422 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
423 std::fs::write(format!("{dir}/memra-l0_q0.f32"), bytes)?;
424 }
425 }
426 let mut q = e.uninit(b * nh * hd)?;
427 let mut k = e.uninit((ctx + b) * nkv * hd)?;
428 e.rms_norm(&q0, &l.q_norm, &mut q, hd, b * nh, c.eps)?;
430 if li == 0 {
431 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
432 let v = e.dtoh(&q)?;
433 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
434 std::fs::write(format!("{dir}/memra-l0_qn.f32"), bytes)?;
435 }
436 }
437 e.rms_norm(&k0, &l.k_norm, &mut k, hd, (ctx + b) * nkv, c.eps)?;
438
439 let norope = std::env::var("MEMRA_DFLASH_NOROPE").is_ok();
441 if !norope {
442 e.rope_neox(&mut q, &pos_blk, hd, hd, nh, b, c.rope_theta, 1.0)?;
443 }
444 if li == 0 {
445 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
446 let dump = |name: &str,
447 t: &cudarc::driver::CudaSlice<f32>|
448 -> Result<(), Box<dyn std::error::Error>> {
449 let v = e.dtoh(t)?;
450 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
451 std::fs::write(format!("{dir}/memra-l0_{name}.f32"), bytes)?;
452 Ok(())
453 };
454 dump("xn", &xn)?;
455 dump("q_prerope", &q)?;
456 }
457 }
458 let pos_all = e.htod_i32(pos)?;
461 if !norope {
462 e.rope_neox(&mut k, &pos_all, hd, hd, nkv, ctx + b, c.rope_theta, 1.0)?;
463 }
464
465 let mut attn = e.uninit(b * nh * hd)?;
467 let scale = 1.0f32 / (hd as f32).sqrt();
468 if std::env::var("MEMRA_DFLASH_FA").is_ok() {
474 e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, b, ctx + b, scale, false)?;
475 } else {
476 e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, b, ctx + b, scale, false)?;
477 }
478
479 let o = self.mm(e, &l.wo, &attn, b, nh * hd, h)?;
480 let mut x1 = e.uninit(b * h)?;
481 e.add(&o, &x, &mut x1, b * h)?;
482 if li == 0 {
483 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
484 let dump = |name: &str,
485 t: &cudarc::driver::CudaSlice<f32>|
486 -> Result<(), Box<dyn std::error::Error>> {
487 let v = e.dtoh(t)?;
488 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
489 std::fs::write(format!("{dir}/memra-l0_{name}.f32"), bytes)?;
490 Ok(())
491 };
492 dump("q", &q)?;
493 dump("k", &k)?;
494 dump("attn", &attn)?;
495 dump("x1", &x1)?;
496 }
497 }
498
499 let mut x1n = e.uninit(b * h)?;
501 e.rms_norm(&x1, &l.ln_post, &mut x1n, h, b, c.eps)?;
502 let gate = self.mm(e, &l.w_gate, &x1n, b, h, c.n_ff)?;
503 let up_ = self.mm(e, &l.w_up, &x1n, b, h, c.n_ff)?;
504 let mut act = e.uninit(b * c.n_ff)?;
505 e.silu_mul(&gate, &up_, &mut act, b * c.n_ff)?;
506 let down = self.mm(e, &l.w_down, &act, b, c.n_ff, h)?;
507 let mut x2 = e.uninit(b * h)?;
508 e.add(&down, &x1, &mut x2, b * h)?;
509 x = x2;
510 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
511 let v = e.dtoh(&x)?;
512 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
513 std::fs::write(format!("{dir}/memra-layer{li}_out.f32"), bytes)?;
514 }
515 }
516 let mut out = e.uninit(b * h)?;
517 e.rms_norm(&x, &self.norm, &mut out, h, b, c.eps)?;
518 Ok(out)
519 }
520}
521
522pub struct DflashKv {
527 pub k: Vec<CudaSlice<f32>>, pub v: Vec<CudaSlice<f32>>,
529 pub len: usize,
530 pub cap: usize,
531}
532
533impl DflashKv {
534 pub fn new(
535 e: &Engine,
536 cfg: &DflashCfg,
537 cap: usize,
538 ) -> Result<Self, Box<dyn std::error::Error>> {
539 let rowsz = cfg.n_kv * cfg.head_dim;
540 let mut k = Vec::with_capacity(cfg.n_layer);
541 let mut v = Vec::with_capacity(cfg.n_layer);
542 for _ in 0..cfg.n_layer {
543 k.push(e.uninit((cap + cfg.block_size) * rowsz)?);
544 v.push(e.uninit((cap + cfg.block_size) * rowsz)?);
545 }
546 Ok(Self { k, v, len: 0, cap })
547 }
548}
549
550impl DflashDraft {
551 pub fn ingest_ctx(
554 &self,
555 e: &Engine,
556 kv: &mut DflashKv,
557 feats: &CudaSlice<f32>,
558 pos_new: &[i32],
559 t: usize,
560 ) -> Result<(), Box<dyn std::error::Error>> {
561 let c = &self.cfg;
562 let (h, nkv, hd) = (c.hidden, c.n_kv, c.head_dim);
563 assert!(kv.len + t <= kv.cap, "draft kv overflow");
564 let pos_d = e.htod_i32(pos_new)?;
565 for (li, l) in self.layers.iter().enumerate() {
566 let k0 = self.mm(e, &l.wk, feats, t, h, nkv * hd)?;
567 let v0 = self.mm(e, &l.wv, feats, t, h, nkv * hd)?;
568 let mut kn = e.uninit(t * nkv * hd)?;
569 e.rms_norm(&k0, &l.k_norm, &mut kn, hd, t * nkv, c.eps)?;
570 e.rope_neox(&mut kn, &pos_d, hd, hd, nkv, t, c.rope_theta, 1.0)?;
571 e.copy_into(&mut kv.k[li], kv.len * nkv * hd, &kn, t * nkv * hd)?;
572 e.copy_into(&mut kv.v[li], kv.len * nkv * hd, &v0, t * nkv * hd)?;
573 }
574 kv.len += t;
575 Ok(())
576 }
577
578 pub fn forward_round(
582 &self,
583 e: &Engine,
584 kv: &mut DflashKv,
585 noise_emb: &CudaSlice<f32>,
586 pos_block: &[i32],
587 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
588 let c = &self.cfg;
589 let (h, nh, nkv, hd) = (c.hidden, c.n_head, c.n_kv, c.head_dim);
590 let b = c.block_size;
591 assert_eq!(pos_block.len(), b);
592 let ctx = kv.len;
593 let pos_blk = e.htod_i32(pos_block)?;
594 let mut x = e.clone_dtod(noise_emb)?;
595 for (li, l) in self.layers.iter().enumerate() {
596 let mut xn = e.uninit(b * h)?;
597 e.rms_norm(&x, &l.ln_in, &mut xn, h, b, c.eps)?;
598 let q0 = self.mm(e, &l.wq, &xn, b, h, nh * hd)?;
599 let k0b = self.mm(e, &l.wk, &xn, b, h, nkv * hd)?;
600 let v0b = self.mm(e, &l.wv, &xn, b, h, nkv * hd)?;
601 let mut q = e.uninit(b * nh * hd)?;
602 let mut kb = e.uninit(b * nkv * hd)?;
603 e.rms_norm(&q0, &l.q_norm, &mut q, hd, b * nh, c.eps)?;
604 e.rms_norm(&k0b, &l.k_norm, &mut kb, hd, b * nkv, c.eps)?;
605 e.rope_neox(&mut q, &pos_blk, hd, hd, nh, b, c.rope_theta, 1.0)?;
606 e.rope_neox(&mut kb, &pos_blk, hd, hd, nkv, b, c.rope_theta, 1.0)?;
607 e.copy_into(&mut kv.k[li], ctx * nkv * hd, &kb, b * nkv * hd)?;
608 e.copy_into(&mut kv.v[li], ctx * nkv * hd, &v0b, b * nkv * hd)?;
609 let mut attn = e.uninit(b * nh * hd)?;
610 let scale = 1.0f32 / (hd as f32).sqrt();
611 if std::env::var("MEMRA_DFLASH_FA").is_ok() {
612 e.fa_prefill(
613 &q,
614 &kv.k[li],
615 &kv.v[li],
616 &mut attn,
617 hd,
618 nh,
619 nkv,
620 b,
621 ctx + b,
622 scale,
623 false,
624 )?;
625 } else {
626 e.sdpa_naive(
627 &q,
628 &kv.k[li],
629 &kv.v[li],
630 &mut attn,
631 hd,
632 nh,
633 nkv,
634 b,
635 ctx + b,
636 scale,
637 false,
638 )?;
639 }
640 let o = self.mm(e, &l.wo, &attn, b, nh * hd, h)?;
641 let mut x1 = e.uninit(b * h)?;
642 e.add(&o, &x, &mut x1, b * h)?;
643 let mut x1n = e.uninit(b * h)?;
644 e.rms_norm(&x1, &l.ln_post, &mut x1n, h, b, c.eps)?;
645 let gate = self.mm(e, &l.w_gate, &x1n, b, h, c.n_ff)?;
646 let up_ = self.mm(e, &l.w_up, &x1n, b, h, c.n_ff)?;
647 let mut act = e.uninit(b * c.n_ff)?;
648 e.silu_mul(&gate, &up_, &mut act, b * c.n_ff)?;
649 let down = self.mm(e, &l.w_down, &act, b, c.n_ff, h)?;
650 let mut x2 = e.uninit(b * h)?;
651 e.add(&down, &x1, &mut x2, b * h)?;
652 x = x2;
653 }
654 let mut out = e.uninit(b * h)?;
655 e.rms_norm(&x, &self.norm, &mut out, h, b, c.eps)?;
656 Ok(out)
657 }
658}
659
660impl crate::hybrid::HybridModel {
666 pub fn generate_spec_dflash(
667 &self,
668 e: &Engine,
669 draft: &DflashDraft,
670 prompt: &[u32],
671 max_new: usize,
672 eos: &[u32],
673 ) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
674 use crate::cache::{Cache, DflashTapSink};
675 let n_embd = self.cfg.n_embd as usize;
676 let c = &draft.cfg;
677 assert_eq!(n_embd, c.hidden, "draft hidden must match target n_embd");
678 let b = c.block_size;
679 let n_taps = c.target_layer_ids.len();
680 let max_ctx = prompt.len() + max_new + b + 8;
681 assert!(
685 max_ctx <= c.sliding_window,
686 "first-light dflash round is windowless — ctx cap {} exceeds the draft window {}",
687 max_ctx,
688 c.sliding_window
689 );
690 let mut cache = Cache::new(e, &self.cfg, max_ctx)?;
691
692 let tp = prompt.len();
694 cache.dflash_taps = Some(DflashTapSink {
695 layer_ids: c.target_layer_ids.clone(),
696 buf: e.uninit(tp * n_taps * n_embd)?,
697 hidden: n_embd,
698 t: tp,
699 });
700 let t_prime = std::time::Instant::now();
701 let (logits, _h_seed, _hiddens) = self.prime_cache(e, prompt, &mut cache, 0)?;
702 let mut last = crate::forward::argmax(&logits) as u32;
703 let mut dkv = DflashKv::new(e, &draft.cfg, max_ctx)?;
706 {
707 let taps = cache.dflash_taps.take().unwrap();
712 let n_taps_h = n_taps * n_embd;
713 let mut r0 = 0usize;
714 while r0 < tp {
715 let t_c = (tp - r0).min(256);
716 let tv = e.view(&taps.buf, tp * n_taps_h);
717 let win = tv.slice(r0 * n_taps_h..(r0 + t_c) * n_taps_h);
718 let mut chunk = e.uninit(t_c * n_taps_h)?;
719 e.copy_view_into(&mut chunk, 0, &win, t_c * n_taps_h)?;
720 let f = draft.ctx_features(e, &chunk, t_c)?;
721 let pos_c: Vec<i32> = ((r0 as i32)..(r0 + t_c) as i32).collect();
722 draft.ingest_ctx(e, &mut dkv, &f, &pos_c, t_c)?;
723 r0 += t_c;
724 }
725 }
726 let mut ctx_len = tp;
727 e.stream().synchronize()?;
728 crate::PRIME_NANOS.store(
730 t_prime.elapsed().as_nanos() as u64,
731 std::sync::atomic::Ordering::Relaxed,
732 );
733
734 let emb_scale = if std::env::var("MEMRA_DFLASH_EMB_SCALE").as_deref() == Ok("1") {
739 (n_embd as f32).sqrt()
740 } else {
741 1.0
742 };
743
744 let mut out = Vec::with_capacity(max_new);
745 let n_vocab = self.output.out_features();
746 let vt_cap: usize = std::env::var("MEMRA_DFLASH_VERIFY_T")
753 .ok()
754 .and_then(|v| v.parse().ok())
755 .unwrap_or(8)
756 .clamp(2, b);
757 let adapt = std::env::var("MEMRA_DFLASH_ADAPT").as_deref() != Ok("0");
760 let mut vt = vt_cap;
761 let mut attempted = 0usize;
762 let mut accepted = 0usize;
763 e.set_verify_exact(true);
767 'outer: while out.len() < max_new {
768 let start = cache.pos; let mut block: Vec<u32> = vec![c.mask_token_id; b];
771 block[0] = last;
772 let mut noise = e.htod(&self.embd.gather(n_embd, &block))?;
773 if emb_scale != 1.0 {
774 e.scale_inplace(&mut noise, emb_scale, b * n_embd)?;
775 }
776 if std::env::var("MEMRA_DFLASH_DEBUG").as_deref() == Ok("1") && start == cache.pos {
777 let nv = e.dtoh(&noise)?;
778 let r0: f32 = nv[..n_embd].iter().map(|x| x * x).sum::<f32>().sqrt();
779 let r1: f32 = nv[n_embd..2 * n_embd]
780 .iter()
781 .map(|x| x * x)
782 .sum::<f32>()
783 .sqrt();
784 eprintln!(
785 "[dflash noise] |row0(last)|={r0:.3} |row1(MASK id {})|={r1:.3}",
786 c.mask_token_id
787 );
788 }
789 let pos_block: Vec<i32> = ((start as i32)..(start + b) as i32).collect();
790 let dh = draft.forward_round(e, &mut dkv, &noise, &pos_block)?;
791 let mut rows = e.uninit((b - 1) * n_embd)?;
793 {
794 let dv = e.view(&dh, b * n_embd);
795 let tail = dv.slice(n_embd..b * n_embd);
796 e.copy_view_into(&mut rows, 0, &tail, (b - 1) * n_embd)?;
797 }
798 let mut dl = e.matmul(&self.output, &rows, b - 1)?;
799 let markov_on = std::env::var("MEMRA_DFLASH_MARKOV").as_deref() != Ok("0");
805 let mut chain_d = e.stream().alloc_zeros::<u32>(b)?;
806 if let (Some(mk), true) = (&draft.markov, markov_on) {
807 e.set_u32_one(&mut chain_d, last)?;
808 for k in 0..(b - 1) {
809 let mut f = e.uninit(mk.rank)?;
810 e.gather_row_bf16(&mk.w1_bf16, &chain_d, k, &mut f, mk.rank)?;
811 let bias = e.matmul(&mk.w2, &f, 1)?;
812 e.add_row_inplace(&mut dl, &bias, n_vocab, k * n_vocab)?;
813 e.argmax_token_device_col(&dl, k, n_vocab, &mut chain_d, k + 1)?;
814 }
815 } else {
816 for i in 0..(b - 1) {
817 e.argmax_token_device_col(&dl, i, n_vocab, &mut chain_d, i + 1)?;
818 }
819 }
820 let chain = e.dtoh_u32(&chain_d)?;
821 let dtoks = &chain[1..];
822 for (i, &dt) in dtoks.iter().enumerate() {
823 block[i + 1] = dt;
824 }
825 let dbg = std::env::var("MEMRA_DFLASH_DEBUG").as_deref() == Ok("1");
826
827 let vblock = &block[..vt];
829 cache.dflash_taps = Some(DflashTapSink {
830 layer_ids: c.target_layer_ids.clone(),
831 buf: e.uninit(vt * n_taps * n_embd)?,
832 hidden: n_embd,
833 t: vt,
834 });
835 let (vam, _vh) = self.gemma4_decode_step_t_am(e, vblock, start, &mut cache)?;
836 let taps = cache.dflash_taps.take().unwrap();
837 if dbg {
838 eprintln!(
839 "[dflash r] start={start} last={last}\n draft={:?}\n vam ={:?}",
840 &block[1..],
841 &vam
842 );
843 }
844
845 let mut m = 0usize;
847 while m < vt - 1 && block[m + 1] as usize == vam[m] as usize {
848 m += 1;
849 }
850 attempted += vt - 1;
851 accepted += m;
852 out.push(last);
853 if eos.contains(&last) {
854 break 'outer;
855 }
856 for &dt in &block[1..=m] {
857 out.push(dt);
858 if eos.contains(&dt) {
859 break 'outer;
860 }
861 if out.len() >= max_new {
862 break 'outer;
863 }
864 }
865 let next = vam[m] as u32;
866
867 let keep = m + 1;
869 for kvl in cache.kv.iter_mut().flatten() {
870 kvl.len -= vt - keep;
871 e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
872 }
873 cache.pos -= vt - keep;
874
875 {
877 let tv = e.view(&taps.buf, vt * n_taps * n_embd);
878 let keep_view = tv.slice(0..keep * n_taps * n_embd);
879 let mut kept = e.uninit(keep * n_taps * n_embd)?;
880 e.copy_view_into(&mut kept, 0, &keep_view, keep * n_taps * n_embd)?;
881 let f = draft.ctx_features(e, &kept, keep)?;
882 let pos_k: Vec<i32> = ((ctx_len as i32)..(ctx_len + keep) as i32).collect();
883 draft.ingest_ctx(e, &mut dkv, &f, &pos_k, keep)?;
884 ctx_len += keep;
885 }
886 last = next;
887 if adapt {
888 vt = (m + 2).clamp(3, vt_cap);
889 }
890 }
891 e.set_verify_exact(false);
892 if std::env::var("MEMRA_SPEC_STATS").as_deref() == Ok("1") {
893 eprintln!(
894 "[dflash] acceptance {accepted}/{attempted} = {:.3}",
895 accepted as f64 / attempted.max(1) as f64
896 );
897 }
898 Ok(out)
899 }
900}