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; let mut mx = 0f32;
102 for &v in blk { if v.abs() > amax { amax = v.abs(); mx = v; } }
103 let d = mx / -8.0;
104 let id = if d != 0.0 { 1.0 / d } else { 0.0 };
105 out.extend_from_slice(&half_from_f32(d).to_le_bytes());
106 for j in 0..16 {
107 let x0 = (blk[j] * id + 8.5).clamp(0.0, 15.0) as u8;
108 let x1 = (blk[j + 16] * id + 8.5).clamp(0.0, 15.0) as u8;
109 out.push(x0 | (x1 << 4));
110 }
111 }
112 out
113}
114
115fn half_from_f32(v: f32) -> u16 {
116 let b = v.to_bits();
118 let sign = ((b >> 16) & 0x8000) as u16;
119 let exp = ((b >> 23) & 0xff) as i32 - 127 + 15;
120 let man = b & 0x7fffff;
121 if exp <= 0 { return sign; } if exp >= 31 { return sign | 0x7c00; } let mut h = sign | ((exp as u16) << 10) | ((man >> 13) as u16);
124 let rem = man & 0x1fff;
126 if rem > 0x1000 || (rem == 0x1000 && (h & 1) == 1) { h += 1; }
127 h
128}
129
130impl DflashDraft {
131 pub fn load(e: &Engine, dir: &std::path::Path) -> Result<Self, Box<dyn std::error::Error>> {
134 let txt = std::fs::read_to_string(dir.join("config.json"))?;
135 fn num(txt: &str, key: &str) -> Option<f64> {
136 let i = txt.find(&format!("\"{key}\""))?;
137 let rest = &txt[i..];
138 let colon = rest.find(':')?;
139 let val: String = rest[colon + 1..].trim_start().chars()
140 .take_while(|c| c.is_ascii_digit() || *c == '.' || *c == '-' || *c == 'e' || *c == 'E' || *c == '+')
141 .collect();
142 val.parse().ok()
143 }
144 fn num_list(txt: &str, key: &str) -> Vec<usize> {
145 let Some(i) = txt.find(&format!("\"{key}\"")) else { return Vec::new() };
146 let rest = &txt[i..];
147 let (Some(a), Some(b)) = (rest.find('['), rest.find(']')) else { return Vec::new() };
148 rest[a + 1..b].split(',').filter_map(|s| s.trim().parse().ok()).collect()
149 }
150 let g = |k: &str| num(&txt, k).unwrap_or_else(|| panic!("config missing {k}")) as usize;
151 let layer_sliding: Vec<bool> = {
153 let i = txt.find("\"layer_types\"").expect("layer_types");
154 let rest = &txt[i..];
155 let (a, b) = (rest.find('[').unwrap(), rest.find(']').unwrap());
156 rest[a + 1..b].split(',').map(|s| s.contains("sliding_attention")).collect()
157 };
158 let cfg = DflashCfg {
159 hidden: g("hidden_size"),
160 n_head: g("num_attention_heads"),
161 n_kv: g("num_key_value_heads"),
162 head_dim: g("head_dim"),
163 n_ff: g("intermediate_size"),
164 n_layer: g("num_hidden_layers"),
165 eps: num(&txt, "rms_norm_eps").expect("rms_norm_eps") as f32,
166 rope_theta: num(&txt, "rope_theta").expect("rope_theta") as f32,
167 block_size: g("block_size"),
168 mask_token_id: g("mask_token_id") as u32,
169 target_layer_ids: num_list(&txt, "target_layer_ids"),
170 sliding_window: g("sliding_window"),
171 layer_sliding,
172 };
173 let st = memra_gguf::safetensors::StModel::open(&dir.join("model.safetensors"))?;
174 let up = |name: &str| -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
177 let (_info, bytes) = st.raw(name).ok_or_else(|| format!("missing tensor {name}"))?;
178 Ok(e.htod(&bf16_to_f32(bytes))?)
179 };
180 let prec = std::env::var("MEMRA_DFLASH_PREC").unwrap_or_else(|_| "q8".into());
184 let upw = |name: &str| -> Result<GpuTensor, Box<dyn std::error::Error>> {
185 let (info, bytes) = st.raw(name).ok_or_else(|| format!("missing tensor {name}"))?;
186 let shape = info.ne(); let in_f = shape[0] as usize;
188 let is_ffn = name.contains(".mlp.");
189 let bf16 = prec == "bf16" || (prec == "mixed" && !is_ffn)
190 || (prec == "fc" && name == "fc.weight");
191 if bf16 {
192 return Ok(GpuTensor::FloatBf16 { data: e.upload_u8(bytes)?, ne: shape.to_vec() });
193 }
194 let f32s = bf16_to_f32(bytes);
195 if prec == "q4" {
196 let q = encode_q4_0(&f32s);
197 return Ok(GpuTensor::Quant {
198 bytes: e.upload_u8(&q)?, qtype: crate::QT_Q4_0,
199 row_bytes: in_f / 32 * 18, ne: shape.to_vec(), scale: 1.0, rp: false,
200 #[cfg(memra_cutlass)]
201 cutlass: None,
202 fp8: None, blk: None, rp4: None, f16: None,
203 });
204 }
205 let q = encode_q8_0(&f32s);
206 Ok(GpuTensor::Quant {
207 bytes: e.upload_u8(&q)?, qtype: crate::QT_Q8_0,
208 row_bytes: in_f / 32 * 34, ne: shape.to_vec(), scale: 1.0, rp: false,
209 #[cfg(memra_cutlass)]
210 cutlass: None,
211 fp8: None, blk: None, rp4: None, f16: None,
212 })
213 };
214 let mut layers = Vec::with_capacity(cfg.n_layer);
215 for i in 0..cfg.n_layer {
216 let p = |s: &str| format!("layers.{i}.{s}");
217 layers.push(DflashLayer {
218 wq: upw(&p("self_attn.q_proj.weight"))?,
219 wk: upw(&p("self_attn.k_proj.weight"))?,
220 wv: upw(&p("self_attn.v_proj.weight"))?,
221 wo: upw(&p("self_attn.o_proj.weight"))?,
222 w_gate: upw(&p("mlp.gate_proj.weight"))?,
223 w_up: upw(&p("mlp.up_proj.weight"))?,
224 w_down: upw(&p("mlp.down_proj.weight"))?,
225 ln_in: up(&p("input_layernorm.weight"))?,
226 ln_post: up(&p("post_attention_layernorm.weight"))?,
227 q_norm: up(&p("self_attn.q_norm.weight"))?,
228 k_norm: up(&p("self_attn.k_norm.weight"))?,
229 });
230 }
231 let markov = if let Some((info, bytes)) = st.raw("markov_head.markov_w1.weight") {
232 let sh = info.ne(); let (rank, vocab) = (sh[0] as usize, sh[1] as usize);
234 let (_i2, b2) = st.raw("markov_head.markov_w2.weight")
235 .ok_or("markov_w2 missing beside markov_w1")?;
236 let w2f = bf16_to_f32(b2);
237 let w2q = encode_q8_0(&w2f);
238 Some(MarkovHead {
239 w1_bf16: e.upload_u8(bytes)?,
240 w2: GpuTensor::Quant {
241 bytes: e.upload_u8(&w2q)?, qtype: crate::QT_Q8_0,
242 row_bytes: rank / 32 * 34, ne: vec![rank as u64, vocab as u64],
243 scale: 1.0, rp: false,
244 #[cfg(memra_cutlass)]
245 cutlass: None,
246 fp8: None, blk: None, rp4: None, f16: None,
247 },
248 rank, vocab,
249 })
250 } else { None };
251 Ok(Self {
252 fc: upw("fc.weight")?,
253 hidden_norm: up("hidden_norm.weight")?,
254 norm: up("norm.weight")?,
255 cfg,
256 layers,
257 markov,
258 })
259 }
260
261 fn mm(&self, e: &Engine, w: &GpuTensor, x: &CudaSlice<f32>, t: usize, _in_f: usize,
263 _out_f: usize) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
264 Ok(e.matmul(w, x, t)?)
265 }
266
267 pub fn ctx_features(&self, e: &Engine, taps: &CudaSlice<f32>, t: usize)
278 -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
279 let c = &self.cfg;
280 let n_taps = c.target_layer_ids.len();
281 let fc_out = self.mm(e, &self.fc, taps, t, n_taps * c.hidden, c.hidden)?;
282 let mut out = e.uninit(t * c.hidden)?;
283 e.rms_norm(&fc_out, &self.hidden_norm, &mut out, c.hidden, t, c.eps)?;
284 Ok(out)
285 }
286
287 pub fn forward(
288 &self, e: &Engine, target_hidden: &CudaSlice<f32>, noise_emb: &CudaSlice<f32>,
289 pos: &[i32], ctx: usize,
290 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
291 let ctx_f = self.ctx_features(e, target_hidden, ctx)?;
292 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
293 let v = e.dtoh(&ctx_f)?;
294 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
295 std::fs::write(format!("{dir}/memra-ctx_features.f32"), bytes)?;
296 }
297 self.forward_block(e, &ctx_f, noise_emb, pos, ctx)
298 }
299
300 pub fn forward_block(
303 &self, e: &Engine, ctx_f: &CudaSlice<f32>, noise_emb: &CudaSlice<f32>,
304 pos: &[i32], ctx: usize,
305 ) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
306 let c = &self.cfg;
307 let (h, nh, nkv, hd) = (c.hidden, c.n_head, c.n_kv, c.head_dim);
308 let b = c.block_size;
309 assert_eq!(pos.len(), ctx + b, "pos covers ctx rows then block rows");
310
311 let pos_blk = e.htod_i32(&pos[ctx..])?;
312
313 let mut x = e.clone_dtod(noise_emb)?; for (li, l) in self.layers.iter().enumerate() {
315 let _ = li;
316 let mut xn = e.uninit(b * h)?;
319 e.rms_norm(&x, &l.ln_in, &mut xn, h, b, c.eps)?;
320
321 let q0 = self.mm(e, &l.wq, &xn, b, h, nh * hd)?;
323 let k0c = self.mm(e, &l.wk, ctx_f, ctx, h, nkv * hd)?;
324 let v0c = self.mm(e, &l.wv, ctx_f, ctx, h, nkv * hd)?;
325 let k0b = self.mm(e, &l.wk, &xn, b, h, nkv * hd)?;
326 let v0b = self.mm(e, &l.wv, &xn, b, h, nkv * hd)?;
327
328 let mut k0 = e.uninit((ctx + b) * nkv * hd)?;
331 e.copy_into(&mut k0, 0, &k0c, ctx * nkv * hd)?;
332 e.copy_into(&mut k0, ctx * nkv * hd, &k0b, b * nkv * hd)?;
333 let mut v = e.uninit((ctx + b) * nkv * hd)?;
334 e.copy_into(&mut v, 0, &v0c, ctx * nkv * hd)?;
335 e.copy_into(&mut v, ctx * nkv * hd, &v0b, b * nkv * hd)?;
336
337 if li == 0 { if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
338 let v = e.dtoh(&q0)?;
339 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
340 std::fs::write(format!("{dir}/memra-l0_q0.f32"), bytes)?;
341 }}
342 let mut q = e.uninit(b * nh * hd)?;
343 let mut k = e.uninit((ctx + b) * nkv * hd)?;
344 e.rms_norm(&q0, &l.q_norm, &mut q, hd, b * nh, c.eps)?;
346 if li == 0 { if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
347 let v = e.dtoh(&q)?;
348 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
349 std::fs::write(format!("{dir}/memra-l0_qn.f32"), bytes)?;
350 }}
351 e.rms_norm(&k0, &l.k_norm, &mut k, hd, (ctx + b) * nkv, c.eps)?;
352
353 let norope = std::env::var("MEMRA_DFLASH_NOROPE").is_ok();
355 if !norope { e.rope_neox(&mut q, &pos_blk, hd, hd, nh, b, c.rope_theta, 1.0)?; }
356 if li == 0 { if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
357 let dump = |name: &str, t: &cudarc::driver::CudaSlice<f32>| -> Result<(), Box<dyn std::error::Error>> {
358 let v = e.dtoh(t)?;
359 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
360 std::fs::write(format!("{dir}/memra-l0_{name}.f32"), bytes)?;
361 Ok(())
362 };
363 dump("xn", &xn)?; dump("q_prerope", &q)?;
364 }}
365 let pos_all = e.htod_i32(pos)?;
368 if !norope { e.rope_neox(&mut k, &pos_all, hd, hd, nkv, ctx + b, c.rope_theta, 1.0)?; }
369
370 let mut attn = e.uninit(b * nh * hd)?;
372 let scale = 1.0f32 / (hd as f32).sqrt();
373 if std::env::var("MEMRA_DFLASH_FA").is_ok() {
379 e.fa_prefill(&q, &k, &v, &mut attn, hd, nh, nkv, b, ctx + b, scale, false)?;
380 } else {
381 e.sdpa_naive(&q, &k, &v, &mut attn, hd, nh, nkv, b, ctx + b, scale, false)?;
382 }
383
384 let o = self.mm(e, &l.wo, &attn, b, nh * hd, h)?;
385 let mut x1 = e.uninit(b * h)?;
386 e.add(&o, &x, &mut x1, b * h)?;
387 if li == 0 { if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
388 let dump = |name: &str, t: &cudarc::driver::CudaSlice<f32>| -> Result<(), Box<dyn std::error::Error>> {
389 let v = e.dtoh(t)?;
390 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
391 std::fs::write(format!("{dir}/memra-l0_{name}.f32"), bytes)?;
392 Ok(())
393 };
394 dump("q", &q)?; dump("k", &k)?; dump("attn", &attn)?; dump("x1", &x1)?;
395 }}
396
397 let mut x1n = e.uninit(b * h)?;
399 e.rms_norm(&x1, &l.ln_post, &mut x1n, h, b, c.eps)?;
400 let gate = self.mm(e, &l.w_gate, &x1n, b, h, c.n_ff)?;
401 let up_ = self.mm(e, &l.w_up, &x1n, b, h, c.n_ff)?;
402 let mut act = e.uninit(b * c.n_ff)?;
403 e.silu_mul(&gate, &up_, &mut act, b * c.n_ff)?;
404 let down = self.mm(e, &l.w_down, &act, b, c.n_ff, h)?;
405 let mut x2 = e.uninit(b * h)?;
406 e.add(&down, &x1, &mut x2, b * h)?;
407 x = x2;
408 if let Ok(dir) = std::env::var("MEMRA_DFLASH_DUMP") {
409 let v = e.dtoh(&x)?;
410 let bytes: Vec<u8> = v.iter().flat_map(|f| f.to_le_bytes()).collect();
411 std::fs::write(format!("{dir}/memra-layer{li}_out.f32"), bytes)?;
412 }
413 }
414 let mut out = e.uninit(b * h)?;
415 e.rms_norm(&x, &self.norm, &mut out, h, b, c.eps)?;
416 Ok(out)
417 }
418}
419
420pub struct DflashKv {
425 pub k: Vec<CudaSlice<f32>>, pub v: Vec<CudaSlice<f32>>,
427 pub len: usize,
428 pub cap: usize,
429}
430
431impl DflashKv {
432 pub fn new(e: &Engine, cfg: &DflashCfg, cap: usize) -> Result<Self, Box<dyn std::error::Error>> {
433 let rowsz = cfg.n_kv * cfg.head_dim;
434 let mut k = Vec::with_capacity(cfg.n_layer);
435 let mut v = Vec::with_capacity(cfg.n_layer);
436 for _ in 0..cfg.n_layer {
437 k.push(e.uninit((cap + cfg.block_size) * rowsz)?);
438 v.push(e.uninit((cap + cfg.block_size) * rowsz)?);
439 }
440 Ok(Self { k, v, len: 0, cap })
441 }
442}
443
444impl DflashDraft {
445 pub fn ingest_ctx(&self, e: &Engine, kv: &mut DflashKv, feats: &CudaSlice<f32>,
448 pos_new: &[i32], t: usize) -> Result<(), Box<dyn std::error::Error>> {
449 let c = &self.cfg;
450 let (h, nkv, hd) = (c.hidden, c.n_kv, c.head_dim);
451 assert!(kv.len + t <= kv.cap, "draft kv overflow");
452 let pos_d = e.htod_i32(pos_new)?;
453 for (li, l) in self.layers.iter().enumerate() {
454 let k0 = self.mm(e, &l.wk, feats, t, h, nkv * hd)?;
455 let v0 = self.mm(e, &l.wv, feats, t, h, nkv * hd)?;
456 let mut kn = e.uninit(t * nkv * hd)?;
457 e.rms_norm(&k0, &l.k_norm, &mut kn, hd, t * nkv, c.eps)?;
458 e.rope_neox(&mut kn, &pos_d, hd, hd, nkv, t, c.rope_theta, 1.0)?;
459 e.copy_into(&mut kv.k[li], kv.len * nkv * hd, &kn, t * nkv * hd)?;
460 e.copy_into(&mut kv.v[li], kv.len * nkv * hd, &v0, t * nkv * hd)?;
461 }
462 kv.len += t;
463 Ok(())
464 }
465
466 pub fn forward_round(&self, e: &Engine, kv: &mut DflashKv, noise_emb: &CudaSlice<f32>,
470 pos_block: &[i32]) -> Result<CudaSlice<f32>, Box<dyn std::error::Error>> {
471 let c = &self.cfg;
472 let (h, nh, nkv, hd) = (c.hidden, c.n_head, c.n_kv, c.head_dim);
473 let b = c.block_size;
474 assert_eq!(pos_block.len(), b);
475 let ctx = kv.len;
476 let pos_blk = e.htod_i32(pos_block)?;
477 let mut x = e.clone_dtod(noise_emb)?;
478 for (li, l) in self.layers.iter().enumerate() {
479 let mut xn = e.uninit(b * h)?;
480 e.rms_norm(&x, &l.ln_in, &mut xn, h, b, c.eps)?;
481 let q0 = self.mm(e, &l.wq, &xn, b, h, nh * hd)?;
482 let k0b = self.mm(e, &l.wk, &xn, b, h, nkv * hd)?;
483 let v0b = self.mm(e, &l.wv, &xn, b, h, nkv * hd)?;
484 let mut q = e.uninit(b * nh * hd)?;
485 let mut kb = e.uninit(b * nkv * hd)?;
486 e.rms_norm(&q0, &l.q_norm, &mut q, hd, b * nh, c.eps)?;
487 e.rms_norm(&k0b, &l.k_norm, &mut kb, hd, b * nkv, c.eps)?;
488 e.rope_neox(&mut q, &pos_blk, hd, hd, nh, b, c.rope_theta, 1.0)?;
489 e.rope_neox(&mut kb, &pos_blk, hd, hd, nkv, b, c.rope_theta, 1.0)?;
490 e.copy_into(&mut kv.k[li], ctx * nkv * hd, &kb, b * nkv * hd)?;
491 e.copy_into(&mut kv.v[li], ctx * nkv * hd, &v0b, b * nkv * hd)?;
492 let mut attn = e.uninit(b * nh * hd)?;
493 let scale = 1.0f32 / (hd as f32).sqrt();
494 if std::env::var("MEMRA_DFLASH_FA").is_ok() {
495 e.fa_prefill(&q, &kv.k[li], &kv.v[li], &mut attn, hd, nh, nkv, b, ctx + b,
496 scale, false)?;
497 } else {
498 e.sdpa_naive(&q, &kv.k[li], &kv.v[li], &mut attn, hd, nh, nkv, b, ctx + b,
499 scale, false)?;
500 }
501 let o = self.mm(e, &l.wo, &attn, b, nh * hd, h)?;
502 let mut x1 = e.uninit(b * h)?;
503 e.add(&o, &x, &mut x1, b * h)?;
504 let mut x1n = e.uninit(b * h)?;
505 e.rms_norm(&x1, &l.ln_post, &mut x1n, h, b, c.eps)?;
506 let gate = self.mm(e, &l.w_gate, &x1n, b, h, c.n_ff)?;
507 let up_ = self.mm(e, &l.w_up, &x1n, b, h, c.n_ff)?;
508 let mut act = e.uninit(b * c.n_ff)?;
509 e.silu_mul(&gate, &up_, &mut act, b * c.n_ff)?;
510 let down = self.mm(e, &l.w_down, &act, b, c.n_ff, h)?;
511 let mut x2 = e.uninit(b * h)?;
512 e.add(&down, &x1, &mut x2, b * h)?;
513 x = x2;
514 }
515 let mut out = e.uninit(b * h)?;
516 e.rms_norm(&x, &self.norm, &mut out, h, b, c.eps)?;
517 Ok(out)
518 }
519}
520
521impl crate::hybrid::HybridModel {
527 pub fn generate_spec_dflash(
528 &self, e: &Engine, draft: &DflashDraft, prompt: &[u32], max_new: usize, eos: &[u32],
529 ) -> Result<Vec<u32>, Box<dyn std::error::Error>> {
530 use crate::cache::{Cache, DflashTapSink};
531 let n_embd = self.cfg.n_embd as usize;
532 let c = &draft.cfg;
533 assert_eq!(n_embd, c.hidden, "draft hidden must match target n_embd");
534 let b = c.block_size;
535 let n_taps = c.target_layer_ids.len();
536 let max_ctx = prompt.len() + max_new + b + 8;
537 assert!(max_ctx <= c.sliding_window,
541 "first-light dflash round is windowless — ctx cap {} exceeds the draft window {}",
542 max_ctx, c.sliding_window);
543 let mut cache = Cache::new(e, &self.cfg, max_ctx)?;
544
545 let tp = prompt.len();
547 cache.dflash_taps = Some(DflashTapSink {
548 layer_ids: c.target_layer_ids.clone(),
549 buf: e.uninit(tp * n_taps * n_embd)?,
550 hidden: n_embd, t: tp,
551 });
552 let t_prime = std::time::Instant::now();
553 let (logits, _h_seed, _hiddens) = self.prime_cache(e, prompt, &mut cache, 0)?;
554 let mut last = crate::forward::argmax(&logits) as u32;
555 let mut dkv = DflashKv::new(e, &draft.cfg, max_ctx)?;
558 {
559 let taps = cache.dflash_taps.take().unwrap();
564 let n_taps_h = n_taps * n_embd;
565 let mut r0 = 0usize;
566 while r0 < tp {
567 let t_c = (tp - r0).min(256);
568 let tv = e.view(&taps.buf, tp * n_taps_h);
569 let win = tv.slice(r0 * n_taps_h..(r0 + t_c) * n_taps_h);
570 let mut chunk = e.uninit(t_c * n_taps_h)?;
571 e.copy_view_into(&mut chunk, 0, &win, t_c * n_taps_h)?;
572 let f = draft.ctx_features(e, &chunk, t_c)?;
573 let pos_c: Vec<i32> = ((r0 as i32)..(r0 + t_c) as i32).collect();
574 draft.ingest_ctx(e, &mut dkv, &f, &pos_c, t_c)?;
575 r0 += t_c;
576 }
577 }
578 let mut ctx_len = tp;
579 e.stream().synchronize()?;
580 crate::PRIME_NANOS.store(t_prime.elapsed().as_nanos() as u64,
582 std::sync::atomic::Ordering::Relaxed);
583
584 let emb_scale = if std::env::var("MEMRA_DFLASH_EMB_SCALE").as_deref() == Ok("1") {
589 (n_embd as f32).sqrt()
590 } else { 1.0 };
591
592 let mut out = Vec::with_capacity(max_new);
593 let n_vocab = self.output.out_features();
594 let vt_cap: usize = std::env::var("MEMRA_DFLASH_VERIFY_T").ok()
601 .and_then(|v| v.parse().ok()).unwrap_or(8).clamp(2, b);
602 let adapt = std::env::var("MEMRA_DFLASH_ADAPT").as_deref() != Ok("0");
605 let mut vt = vt_cap;
606 let mut attempted = 0usize;
607 let mut accepted = 0usize;
608 e.set_verify_exact(true);
612 'outer: while out.len() < max_new {
613 let start = cache.pos; let mut block: Vec<u32> = vec![c.mask_token_id; b];
616 block[0] = last;
617 let mut noise = e.htod(&self.embd.gather(n_embd, &block))?;
618 if emb_scale != 1.0 { e.scale_inplace(&mut noise, emb_scale, b * n_embd)?; }
619 if std::env::var("MEMRA_DFLASH_DEBUG").as_deref() == Ok("1") && start == cache.pos {
620 let nv = e.dtoh(&noise)?;
621 let r0: f32 = nv[..n_embd].iter().map(|x| x * x).sum::<f32>().sqrt();
622 let r1: f32 = nv[n_embd..2 * n_embd].iter().map(|x| x * x).sum::<f32>().sqrt();
623 eprintln!("[dflash noise] |row0(last)|={r0:.3} |row1(MASK id {})|={r1:.3}",
624 c.mask_token_id);
625 }
626 let pos_block: Vec<i32> = ((start as i32)..(start + b) as i32).collect();
627 let dh = draft.forward_round(e, &mut dkv, &noise, &pos_block)?;
628 let mut rows = e.uninit((b - 1) * n_embd)?;
630 {
631 let dv = e.view(&dh, b * n_embd);
632 let tail = dv.slice(n_embd..b * n_embd);
633 e.copy_view_into(&mut rows, 0, &tail, (b - 1) * n_embd)?;
634 }
635 let mut dl = e.matmul(&self.output, &rows, b - 1)?;
636 let markov_on = std::env::var("MEMRA_DFLASH_MARKOV").as_deref() != Ok("0");
642 let mut chain_d = e.stream().alloc_zeros::<u32>(b)?;
643 if let (Some(mk), true) = (&draft.markov, markov_on) {
644 e.set_u32_one(&mut chain_d, last)?;
645 for k in 0..(b - 1) {
646 let mut f = e.uninit(mk.rank)?;
647 e.gather_row_bf16(&mk.w1_bf16, &chain_d, k, &mut f, mk.rank)?;
648 let bias = e.matmul(&mk.w2, &f, 1)?;
649 e.add_row_inplace(&mut dl, &bias, n_vocab, k * n_vocab)?;
650 e.argmax_token_device_col(&dl, k, n_vocab, &mut chain_d, k + 1)?;
651 }
652 } else {
653 for i in 0..(b - 1) {
654 e.argmax_token_device_col(&dl, i, n_vocab, &mut chain_d, i + 1)?;
655 }
656 }
657 let chain = e.dtoh_u32(&chain_d)?;
658 let dtoks = &chain[1..];
659 for (i, &dt) in dtoks.iter().enumerate() { block[i + 1] = dt; }
660 let dbg = std::env::var("MEMRA_DFLASH_DEBUG").as_deref() == Ok("1");
661
662 let vblock = &block[..vt];
664 cache.dflash_taps = Some(DflashTapSink {
665 layer_ids: c.target_layer_ids.clone(),
666 buf: e.uninit(vt * n_taps * n_embd)?,
667 hidden: n_embd, t: vt,
668 });
669 let (vam, _vh) = self.gemma4_decode_step_t_am(e, vblock, start, &mut cache)?;
670 let taps = cache.dflash_taps.take().unwrap();
671 if dbg {
672 eprintln!("[dflash r] start={start} last={last}\n draft={:?}\n vam ={:?}",
673 &block[1..], &vam);
674 }
675
676 let mut m = 0usize;
678 while m < vt - 1 && block[m + 1] as usize == vam[m] as usize { m += 1; }
679 attempted += vt - 1;
680 accepted += m;
681 out.push(last);
682 if eos.contains(&last) { break 'outer; }
683 for &dt in &block[1..=m] {
684 out.push(dt);
685 if eos.contains(&dt) { break 'outer; }
686 if out.len() >= max_new { break 'outer; }
687 }
688 let next = vam[m] as u32;
689
690 let keep = m + 1;
692 for kvl in cache.kv.iter_mut().flatten() {
693 kvl.len -= vt - keep;
694 e.set_i32_one(&mut kvl.len_d, kvl.len as i32)?;
695 }
696 cache.pos -= vt - keep;
697
698 {
700 let tv = e.view(&taps.buf, vt * n_taps * n_embd);
701 let keep_view = tv.slice(0..keep * n_taps * n_embd);
702 let mut kept = e.uninit(keep * n_taps * n_embd)?;
703 e.copy_view_into(&mut kept, 0, &keep_view, keep * n_taps * n_embd)?;
704 let f = draft.ctx_features(e, &kept, keep)?;
705 let pos_k: Vec<i32> = ((ctx_len as i32)..(ctx_len + keep) as i32).collect();
706 draft.ingest_ctx(e, &mut dkv, &f, &pos_k, keep)?;
707 ctx_len += keep;
708 }
709 last = next;
710 if adapt { vt = (m + 2).clamp(3, vt_cap); }
711 }
712 e.set_verify_exact(false);
713 if std::env::var("MEMRA_SPEC_STATS").as_deref() == Ok("1") {
714 eprintln!("[dflash] acceptance {accepted}/{attempted} = {:.3}",
715 accepted as f64 / attempted.max(1) as f64);
716 }
717 Ok(out)
718 }
719}