Skip to main content

cortiq_engine/
moss_image.rs

1//! Native MOSS-Image denoiser.
2//!
3//! MOSS-Image is deliberately not routed through Z-Image: its 42 blocks use
4//! joint [image,text] attention, four-axis RoPE, six-way AdaRMS modulation
5//! and SiTU.  This module keeps source tensor suffixes under `moss.` and
6//! uses CMF's mapped quantized projections.  The hot dense products therefore
7//! use the existing q4tp/q8 device path on Metal/Vulkan, while the portable
8//! f32 implementation remains an exact-shape fallback.
9
10use crate::dit::{Proj, cmf_f32};
11use crate::pool::Pool;
12use cortiq_core::CmfModel;
13use std::sync::Arc;
14
15/// Native MOSS transformer geometry, read from the source config stored in
16/// `moss.config_json`.
17#[derive(Clone, Debug)]
18pub struct MossConfig {
19    pub dim: usize,
20    pub ffn_dim: usize,
21    pub freq_dim: usize,
22    pub latent_channels: usize,
23    pub num_heads: usize,
24    pub num_layers: usize,
25    pub text_dim: usize,
26    pub rope_base: f64,
27}
28
29impl MossConfig {
30    pub fn from_json(v: &serde_json::Value) -> Result<Self, String> {
31        let u = |name: &str| -> Result<usize, String> {
32            v[name]
33                .as_u64()
34                .map(|v| v as usize)
35                .ok_or_else(|| format!("MOSS config missing integer {name}"))
36        };
37        let dim = u("dim")?;
38        let num_heads = u("num_heads")?;
39        let freq_dim = u("freq_dim")?;
40        if dim == 0 || num_heads == 0 || dim % num_heads != 0 || (dim / num_heads) % 8 != 0 {
41            return Err(format!(
42                "invalid MOSS dim/head geometry: dim={dim}, num_heads={num_heads}"
43            ));
44        }
45        if freq_dim == 0 || freq_dim % 2 != 0 {
46            return Err(format!(
47                "MOSS freq_dim must be positive and even, got {freq_dim}"
48            ));
49        }
50        Ok(Self {
51            dim,
52            ffn_dim: u("ffn_dim")?,
53            freq_dim,
54            latent_channels: u("latent_channels")?,
55            num_heads,
56            num_layers: u("num_layers")?,
57            text_dim: u("text_dim")?,
58            rope_base: v["rope_base"].as_f64().unwrap_or(10_000.0),
59        })
60    }
61
62    pub fn head_dim(&self) -> usize {
63        self.dim / self.num_heads
64    }
65}
66
67struct TextBlock {
68    norm1: Vec<f32>,
69    norm2: Vec<f32>,
70    q_norm: Vec<f32>,
71    k_norm: Vec<f32>,
72    qkv: Proj,
73    qkv_b: Vec<f32>,
74    proj: Proj,
75    proj_b: Vec<f32>,
76    ffn_gate: Proj,
77    ffn_gate_b: Vec<f32>,
78    ffn_up: Proj,
79    ffn_up_b: Vec<f32>,
80    ffn_down: Proj,
81    ffn_down_b: Vec<f32>,
82}
83
84struct Block {
85    norm1: Vec<f32>,
86    norm2: Vec<f32>,
87    q_norm: Vec<f32>,
88    k_norm: Vec<f32>,
89    q: Proj,
90    k: Proj,
91    v: Proj,
92    o: Proj,
93    ffn_up_gate: Proj,
94    ffn_down: Proj,
95}
96
97/// The MOSS DiT loaded directly from one CMF.  Quantized projection tensors
98/// remain mmap-backed, rather than being expanded to f32 at load time.
99pub struct MossDit {
100    pub cfg: MossConfig,
101    video_embedder: Proj,
102    video_embedder_b: Vec<f32>,
103    text_proj: Proj,
104    text_proj_b: Vec<f32>,
105    text_blocks: Vec<TextBlock>,
106    time_0_w: Vec<f32>,
107    time_0_b: Vec<f32>,
108    time_2_w: Vec<f32>,
109    time_2_b: Vec<f32>,
110    shared_mod_w: Vec<f32>,
111    shared_mod_b: Vec<f32>,
112    head_mod_w: Vec<f32>,
113    head_mod_b: Vec<f32>,
114    layers: Vec<Block>,
115    final_norm_video: Vec<f32>,
116    final_linear_video: Proj,
117    pool: Option<Arc<Pool>>,
118}
119
120impl MossDit {
121    /// Load the MOSS DiT from `moss.*` tensors in a packaged model.
122    pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
123        let cfg: serde_json::Value = serde_json::from_slice(
124            model
125                .tensor_bytes("moss.config_json")
126                .map_err(|e| e.to_string())?,
127        )
128        .map_err(|e| format!("moss.config_json: {e}"))?;
129        let cfg = MossConfig::from_json(&cfg)?;
130        let f = |suffix: &str| cmf_f32(model, &format!("moss.{suffix}"));
131        let p = |suffix: &str| Proj::from_model(model, &format!("moss.{suffix}"));
132        let text_blocks = (0..2)
133            .map(|i| {
134                let x = format!("text_embedder.blocks.{i}");
135                Ok(TextBlock {
136                    norm1: f(&format!("{x}.norm1.weight"))?,
137                    norm2: f(&format!("{x}.norm2.weight"))?,
138                    q_norm: f(&format!("{x}.q_norm.weight"))?,
139                    k_norm: f(&format!("{x}.k_norm.weight"))?,
140                    qkv: p(&format!("{x}.qkv.weight"))?,
141                    qkv_b: f(&format!("{x}.qkv.bias"))?,
142                    proj: p(&format!("{x}.proj.weight"))?,
143                    proj_b: f(&format!("{x}.proj.bias"))?,
144                    ffn_gate: p(&format!("{x}.ffn_gate.weight"))?,
145                    ffn_gate_b: f(&format!("{x}.ffn_gate.bias"))?,
146                    ffn_up: p(&format!("{x}.ffn_up.weight"))?,
147                    ffn_up_b: f(&format!("{x}.ffn_up.bias"))?,
148                    ffn_down: p(&format!("{x}.ffn_down.weight"))?,
149                    ffn_down_b: f(&format!("{x}.ffn_down.bias"))?,
150                })
151            })
152            .collect::<Result<Vec<_>, String>>()?;
153        let layers = (0..cfg.num_layers)
154            .map(|i| {
155                let x = format!("layers.{i}");
156                Ok(Block {
157                    norm1: f(&format!("{x}.norm1.weight"))?,
158                    norm2: f(&format!("{x}.norm2.weight"))?,
159                    q_norm: f(&format!("{x}.q_norm.weight"))?,
160                    k_norm: f(&format!("{x}.k_norm.weight"))?,
161                    q: p(&format!("{x}.proj.q.weight"))?,
162                    k: p(&format!("{x}.proj.k.weight"))?,
163                    v: p(&format!("{x}.proj.v.weight"))?,
164                    o: p(&format!("{x}.proj.o.weight"))?,
165                    ffn_up_gate: p(&format!("{x}.proj.ffn_up_gate.weight"))?,
166                    ffn_down: p(&format!("{x}.proj.ffn_down.weight"))?,
167                })
168            })
169            .collect::<Result<Vec<_>, String>>()?;
170        let dit = Self {
171            video_embedder: p("video_embedder.weight")?,
172            video_embedder_b: f("video_embedder.bias")?,
173            text_proj: p("text_embedder.proj.weight")?,
174            text_proj_b: f("text_embedder.proj.bias")?,
175            time_0_w: f("time_embedding.0.weight")?,
176            time_0_b: f("time_embedding.0.bias")?,
177            time_2_w: f("time_embedding.2.weight")?,
178            time_2_b: f("time_embedding.2.bias")?,
179            shared_mod_w: f("shared_block_modulation.weight")?,
180            shared_mod_b: f("shared_block_modulation.bias")?,
181            head_mod_w: f("video_head_modulation.weight")?,
182            head_mod_b: f("video_head_modulation.bias")?,
183            final_norm_video: f("final_norm_video.weight")?,
184            final_linear_video: p("final_linear_video.weight")?,
185            text_blocks,
186            layers,
187            pool: Pool::from_env(),
188            cfg,
189        };
190        dit.validate()?;
191        Ok(dit)
192    }
193
194    fn validate(&self) -> Result<(), String> {
195        let d = self.cfg.dim;
196        if self.layers.len() != self.cfg.num_layers || self.text_blocks.len() != 2 {
197            return Err("MOSS block count mismatch".into());
198        }
199        if self.video_embedder.cols() != self.cfg.latent_channels
200            || self.video_embedder.rows() != d
201            || self.text_proj.cols() != self.cfg.text_dim
202            || self.text_proj.rows() != d
203            || self.final_linear_video.cols() != d
204            || self.final_linear_video.rows() != self.cfg.latent_channels
205            || self.final_norm_video.len() != d
206        {
207            return Err("MOSS top-level projection geometry mismatch".into());
208        }
209        if self.shared_mod_w.len() != 6 * d * d || self.shared_mod_b.len() != 6 * d {
210            return Err("MOSS shared modulation geometry mismatch".into());
211        }
212        if self.head_mod_w.len() != 2 * d * d || self.head_mod_b.len() != 2 * d {
213            return Err("MOSS head modulation geometry mismatch".into());
214        }
215        Ok(())
216    }
217
218    fn pool(&self) -> Option<&Pool> {
219        self.pool.as_deref()
220    }
221
222    /// Prepare the encoder's final Qwen3.5 hidden states once per prompt.
223    /// MOSS's text projection/refiner does not depend on the latent or the
224    /// timestep, so retaining this result avoids repeating two blocks on
225    /// every CFG/denoising forward.
226    pub fn prepare_text(&self, text: &[f32], tokens: usize) -> Result<Vec<f32>, String> {
227        if tokens == 0 || text.len() != tokens * self.cfg.text_dim {
228            return Err(format!(
229                "MOSS text conditioning has {} values for {tokens} tokens of width {}",
230                text.len(),
231                self.cfg.text_dim
232            ));
233        }
234        let mut x = linear(
235            &self.text_proj,
236            text,
237            tokens,
238            Some(&self.text_proj_b),
239            self.pool(),
240        );
241        let rope = Rope::new(&self.cfg, 0, 0, tokens);
242        for b in &self.text_blocks {
243            text_block(b, &mut x, tokens, &rope, &self.cfg, self.pool());
244        }
245        Ok(x)
246    }
247
248    /// One MOSS velocity forward.  `latents` is channels-first
249    /// `[latent_channels, h, w]`; `text` is the result of
250    /// [`Self::prepare_text`].  Passing a skip set reproduces MOSS's
251    /// negative-branch layer skip without changing sequence geometry.
252    pub fn forward(
253        &self,
254        latents: &[f32],
255        h: usize,
256        w: usize,
257        timestep: f32,
258        text: &[f32],
259        text_tokens: usize,
260        skip_layers: &[usize],
261    ) -> Result<Vec<f32>, String> {
262        if h == 0 || w == 0 || latents.len() != self.cfg.latent_channels * h * w {
263            return Err("MOSS latent shape is not [C,H,W]".into());
264        }
265        if text_tokens == 0 || text.len() != text_tokens * self.cfg.dim {
266            return Err("MOSS prepared text shape mismatch".into());
267        }
268        if skip_layers.iter().any(|&i| i >= self.layers.len()) {
269            return Err("MOSS skip layer out of range".into());
270        }
271        let image_tokens = h * w;
272        let mut image_in = vec![0f32; image_tokens * self.cfg.latent_channels];
273        for token in 0..image_tokens {
274            for c in 0..self.cfg.latent_channels {
275                image_in[token * self.cfg.latent_channels + c] = latents[c * image_tokens + token];
276            }
277        }
278        let video = linear(
279            &self.video_embedder,
280            &image_in,
281            image_tokens,
282            Some(&self.video_embedder_b),
283            self.pool(),
284        );
285        let mut x = Vec::with_capacity((image_tokens + text_tokens) * self.cfg.dim);
286        x.extend_from_slice(&video);
287        x.extend_from_slice(text);
288        let rope = Rope::new(&self.cfg, h, w, text_tokens);
289        let time = self.time_embedding(timestep);
290        let modulation = linear_small(&time, &self.shared_mod_w, &self.shared_mod_b);
291        let head = linear_small(&time, &self.head_mod_w, &self.head_mod_b);
292        for (i, block) in self.layers.iter().enumerate() {
293            if !skip_layers.contains(&i) {
294                main_block(
295                    block,
296                    &mut x,
297                    image_tokens + text_tokens,
298                    &rope,
299                    &modulation,
300                    &self.cfg,
301                    self.pool(),
302                );
303            }
304        }
305        let d = self.cfg.dim;
306        let mut image = x[..image_tokens * d].to_vec();
307        rms_rows_inplace(
308            &mut image,
309            image_tokens,
310            d,
311            &self.final_norm_video,
312            None,
313            None,
314            self.pool(),
315        );
316        let (shift, scale) = (&head[..d], &head[d..]);
317        for row in image.chunks_exact_mut(d) {
318            for ((v, &s), &a) in row.iter_mut().zip(scale).zip(shift) {
319                *v = *v * (1.0 + s) + a;
320            }
321        }
322        let out = linear(
323            &self.final_linear_video,
324            &image,
325            image_tokens,
326            None,
327            self.pool(),
328        );
329        let mut velocity = vec![0f32; self.cfg.latent_channels * image_tokens];
330        for token in 0..image_tokens {
331            for c in 0..self.cfg.latent_channels {
332                velocity[c * image_tokens + token] = out[token * self.cfg.latent_channels + c];
333            }
334        }
335        Ok(velocity)
336    }
337
338    fn time_embedding(&self, t: f32) -> Vec<f32> {
339        // The source deliberately computes phase in f64, then sends the
340        // concatenated [cos, sin] embedding through f32 Linear modules.
341        let half = self.cfg.freq_dim / 2;
342        let mut input = vec![0f32; self.cfg.freq_dim];
343        for i in 0..half {
344            let freq = self.cfg.rope_base.powf(-(i as f64) / half as f64);
345            let p = t as f64 * freq;
346            input[i] = p.cos() as f32;
347            input[half + i] = p.sin() as f32;
348        }
349        let mut h = linear_small(&input, &self.time_0_w, &self.time_0_b);
350        for v in &mut h {
351            *v = silu(*v);
352        }
353        linear_small(&h, &self.time_2_w, &self.time_2_b)
354    }
355}
356
357/// Four-axis MOSS RoPE, represented as fully expanded sin/cos factors for
358/// source `_rotate`: the first 64 dimensions rotate the first half, and the
359/// same factors repeat for the second half.
360struct Rope {
361    sin: Vec<f32>,
362    cos: Vec<f32>,
363}
364
365impl Rope {
366    fn new(cfg: &MossConfig, h: usize, w: usize, text_len: usize) -> Self {
367        let hd = cfg.head_dim();
368        let bands = hd / 8;
369        let n_img = h * w;
370        let n = n_img + text_len;
371        let mut sin = vec![0f32; n * hd];
372        let mut cos = vec![0f32; n * hd];
373        for token in 0..n {
374            let coords = if token < n_img {
375                let yy = token / w;
376                let xx = token % w;
377                [
378                    0.0,
379                    text_len as f64,
380                    yy as f64 - (h.saturating_sub(1) as f64) / 2.0,
381                    xx as f64 - (w.saturating_sub(1) as f64) / 2.0,
382                ]
383            } else {
384                [0.0, (token - n_img) as f64, 0.0, 0.0]
385            };
386            for axis in 0..4 {
387                for band in 0..bands {
388                    let freq = 1.0 / cfg.rope_base.powf(band as f64 / bands as f64);
389                    let phase = coords[axis] * freq;
390                    let j = axis * bands + band;
391                    sin[token * hd + j] = phase.sin() as f32;
392                    cos[token * hd + j] = phase.cos() as f32;
393                    sin[token * hd + hd / 2 + j] = phase.sin() as f32;
394                    cos[token * hd + hd / 2 + j] = phase.cos() as f32;
395                }
396            }
397        }
398        Self { sin, cos }
399    }
400}
401
402// ── portable f32 kernels ────────────────────────────────────────────────
403
404struct SendRows(*mut f32);
405unsafe impl Send for SendRows {}
406unsafe impl Sync for SendRows {}
407impl SendRows {
408    /// SAFETY: callers divide the buffer into non-overlapping row ranges.
409    ///
410    /// The `&self` receiver is deliberate: the wrapper is shared across
411    /// worker closures (`Sync`) while each call carves out a disjoint slice
412    /// of the same backing buffer, so no two live `&mut` ever alias —
413    /// exactly the case `mut_from_ref` cannot see through.
414    #[allow(clippy::mut_from_ref)]
415    unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
416        unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
417    }
418}
419
420fn rows(pool: Option<&Pool>, n: usize, f: &(dyn Fn(usize, usize) + Sync)) {
421    match pool {
422        Some(p) => p.run_rows(n, f),
423        None => f(0, n),
424    }
425}
426
427fn silu(x: f32) -> f32 {
428    x / (1.0 + (-x).exp())
429}
430
431fn situ(hidden: f32, gate: f32) -> f32 {
432    (4.0 * (gate / 4.0).tanh() * gate.sigmoid()) * (25.0 * (hidden / 25.0).tanh())
433}
434
435trait Sigmoid {
436    fn sigmoid(self) -> Self;
437}
438impl Sigmoid for f32 {
439    fn sigmoid(self) -> Self {
440        1.0 / (1.0 + (-self).exp())
441    }
442}
443
444fn linear_small(x: &[f32], w: &[f32], bias: &[f32]) -> Vec<f32> {
445    assert_eq!(w.len() % x.len(), 0, "small MOSS linear geometry");
446    let rows = w.len() / x.len();
447    assert_eq!(bias.len(), rows, "small MOSS linear bias geometry");
448    w.chunks_exact(x.len())
449        .zip(bias)
450        .map(|(r, &b)| b + r.iter().zip(x).map(|(&a, &v)| a * v).sum::<f32>())
451        .collect()
452}
453
454fn linear(p: &Proj, x: &[f32], n: usize, bias: Option<&[f32]>, pool: Option<&Pool>) -> Vec<f32> {
455    assert_eq!(x.len(), n * p.cols(), "MOSS projection input geometry");
456    let mut out = vec![0f32; n * p.rows()];
457    p.matmat(x, n, &mut out, pool);
458    if let Some(b) = bias {
459        assert_eq!(b.len(), p.rows(), "MOSS projection bias geometry");
460        for row in out.chunks_exact_mut(p.rows()) {
461            for (v, &b) in row.iter_mut().zip(b) {
462                *v += b;
463            }
464        }
465    }
466    out
467}
468
469/// Plain Diffusers RMSNorm, optionally affine-modulated after normalization.
470fn rms_rows_inplace(
471    x: &mut [f32],
472    n: usize,
473    d: usize,
474    weight: &[f32],
475    scale: Option<&[f32]>,
476    shift: Option<&[f32]>,
477    pool: Option<&Pool>,
478) {
479    assert_eq!(x.len(), n * d);
480    assert_eq!(weight.len(), d);
481    if let Some(s) = scale {
482        assert_eq!(s.len(), d);
483    }
484    if let Some(s) = shift {
485        assert_eq!(s.len(), d);
486    }
487    let ptr = SendRows(x.as_mut_ptr());
488    rows(pool, n, &|lo, hi| {
489        for r in lo..hi {
490            // SAFETY: each worker owns distinct row indices.
491            let row = unsafe { ptr.row(r * d, d) };
492            let mean = row.iter().map(|&v| v * v).sum::<f32>() / d as f32;
493            let inv = 1.0 / (mean + 1e-6).sqrt();
494            for j in 0..d {
495                let mut v = row[j] * inv * weight[j];
496                if let Some(s) = scale {
497                    v *= 1.0 + s[j];
498                }
499                if let Some(a) = shift {
500                    v += a[j];
501                }
502                row[j] = v;
503            }
504        }
505    });
506}
507
508fn qk_norm_rope(
509    q: &mut [f32],
510    n: usize,
511    heads: usize,
512    hd: usize,
513    weight: &[f32],
514    rope: &Rope,
515    pool: Option<&Pool>,
516) {
517    assert_eq!(q.len(), n * heads * hd);
518    assert_eq!(weight.len(), hd);
519    let ptr = SendRows(q.as_mut_ptr());
520    rows(pool, n, &|lo, hi| {
521        for t in lo..hi {
522            for h in 0..heads {
523                // SAFETY: distinct token ranges are disjoint.
524                let row = unsafe { ptr.row((t * heads + h) * hd, hd) };
525                let mean = row.iter().map(|&v| v * v).sum::<f32>() / hd as f32;
526                let inv = 1.0 / (mean + 1e-6).sqrt();
527                for j in 0..hd {
528                    row[j] *= inv * weight[j];
529                }
530                for j in 0..hd / 2 {
531                    let a = row[j];
532                    let b = row[j + hd / 2];
533                    let c = rope.cos[t * hd + j];
534                    let s = rope.sin[t * hd + j];
535                    row[j] = a * c - b * s;
536                    row[j + hd / 2] = b * c + a * s;
537                }
538            }
539        }
540    });
541}
542
543fn softmax(row: &mut [f32]) {
544    let max = row.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
545    let mut sum = 0.0f32;
546    for v in row.iter_mut() {
547        *v = (*v - max).exp();
548        sum += *v;
549    }
550    if sum > 0.0 {
551        for v in row.iter_mut() {
552            *v /= sum;
553        }
554    }
555}
556
557/// Full bidirectional attention.  The GPU path consumes head-major q/k/v
558/// planes and returns token-major rows; portable fallback holds one score
559/// matrix per head (never one per layer) to bound memory.
560fn attention(
561    q: &[f32],
562    k: &[f32],
563    v: &[f32],
564    n: usize,
565    heads: usize,
566    hd: usize,
567    pool: Option<&Pool>,
568) -> Vec<f32> {
569    let hidden = heads * hd;
570    let mut qh = vec![0f32; heads * n * hd];
571    let mut kh = vec![0f32; heads * n * hd];
572    let mut vh = vec![0f32; heads * n * hd];
573    for t in 0..n {
574        for h in 0..heads {
575            let src = &q[(t * heads + h) * hd..(t * heads + h + 1) * hd];
576            qh[(h * n + t) * hd..(h * n + t + 1) * hd].copy_from_slice(src);
577            let src = &k[(t * heads + h) * hd..(t * heads + h + 1) * hd];
578            kh[(h * n + t) * hd..(h * n + t + 1) * hd].copy_from_slice(src);
579            let src = &v[(t * heads + h) * hd..(t * heads + h + 1) * hd];
580            vh[(h * n + t) * hd..(h * n + t + 1) * hd].copy_from_slice(src);
581        }
582    }
583    let mut out = vec![0f32; n * hidden];
584    if n >= 128
585        && crate::gpu::enabled_here()
586        && !crate::gpu::mm_killed()
587        && crate::gpu::dit_attention(
588            &qh,
589            &kh,
590            &vh,
591            heads,
592            heads,
593            n,
594            hd,
595            1.0 / (hd as f32).sqrt(),
596            &mut out,
597        )
598    {
599        return out;
600    }
601    let mut scores = vec![0f32; n * n];
602    let mut vals_t = vec![0f32; hd * n];
603    let mut head_out = vec![0f32; n * hd];
604    for h in 0..heads {
605        let qs = &qh[h * n * hd..(h + 1) * n * hd];
606        let ks = &kh[h * n * hd..(h + 1) * n * hd];
607        let vs = &vh[h * n * hd..(h + 1) * n * hd];
608        // GEMM wants the right operand laid out by output row.  Each score
609        // row is q[t] dot k[j], so transpose logically into k's rows works.
610        crate::fcd_ops::gemm_nt(qs, ks, &mut scores, n, hd, n, pool);
611        let sp = SendRows(scores.as_mut_ptr());
612        rows(pool, n, &|lo, hi| {
613            for r in lo..hi {
614                // SAFETY: row ranges do not overlap.
615                softmax(unsafe { sp.row(r * n, n) });
616            }
617        });
618        for d in 0..hd {
619            for t in 0..n {
620                vals_t[d * n + t] = vs[t * hd + d];
621            }
622        }
623        crate::fcd_ops::gemm_nt(&scores, &vals_t, &mut head_out, n, n, hd, pool);
624        for t in 0..n {
625            out[t * hidden + h * hd..t * hidden + (h + 1) * hd]
626                .copy_from_slice(&head_out[t * hd..(t + 1) * hd]);
627        }
628    }
629    out
630}
631
632fn text_block(
633    b: &TextBlock,
634    x: &mut [f32],
635    n: usize,
636    rope: &Rope,
637    cfg: &MossConfig,
638    pool: Option<&Pool>,
639) {
640    let d = cfg.dim;
641    let mut norm = x.to_vec();
642    rms_rows_inplace(&mut norm, n, d, &b.norm1, None, None, pool);
643    let qkv = linear(&b.qkv, &norm, n, Some(&b.qkv_b), pool);
644    let mut q = qkv[..n * d].to_vec();
645    let mut k = qkv[n * d..2 * n * d].to_vec();
646    let v = qkv[2 * n * d..].to_vec();
647    qk_norm_rope(
648        &mut q,
649        n,
650        cfg.num_heads,
651        cfg.head_dim(),
652        &b.q_norm,
653        rope,
654        pool,
655    );
656    qk_norm_rope(
657        &mut k,
658        n,
659        cfg.num_heads,
660        cfg.head_dim(),
661        &b.k_norm,
662        rope,
663        pool,
664    );
665    let attn = attention(&q, &k, &v, n, cfg.num_heads, cfg.head_dim(), pool);
666    let out = linear(&b.proj, &attn, n, Some(&b.proj_b), pool);
667    for (a, &v) in x.iter_mut().zip(&out) {
668        *a += v;
669    }
670    let mut norm = x.to_vec();
671    rms_rows_inplace(&mut norm, n, d, &b.norm2, None, None, pool);
672    let gate = linear(&b.ffn_gate, &norm, n, Some(&b.ffn_gate_b), pool);
673    let mut up = linear(&b.ffn_up, &norm, n, Some(&b.ffn_up_b), pool);
674    for (a, &g) in up.iter_mut().zip(&gate) {
675        *a = situ(*a, g);
676    }
677    let out = linear(&b.ffn_down, &up, n, Some(&b.ffn_down_b), pool);
678    for (a, &v) in x.iter_mut().zip(&out) {
679        *a += v;
680    }
681}
682
683fn main_block(
684    b: &Block,
685    x: &mut [f32],
686    n: usize,
687    rope: &Rope,
688    modulate: &[f32],
689    cfg: &MossConfig,
690    pool: Option<&Pool>,
691) {
692    let d = cfg.dim;
693    assert_eq!(modulate.len(), 6 * d);
694    let (shift_a, rest) = modulate.split_at(d);
695    let (scale_a, rest) = rest.split_at(d);
696    let (gate_a, rest) = rest.split_at(d);
697    let (shift_f, rest) = rest.split_at(d);
698    let (scale_f, gate_f) = rest.split_at(d);
699    let mut norm = x.to_vec();
700    rms_rows_inplace(
701        &mut norm,
702        n,
703        d,
704        &b.norm1,
705        Some(scale_a),
706        Some(shift_a),
707        pool,
708    );
709    let mut q = linear(&b.q, &norm, n, None, pool);
710    let mut k = linear(&b.k, &norm, n, None, pool);
711    let v = linear(&b.v, &norm, n, None, pool);
712    qk_norm_rope(
713        &mut q,
714        n,
715        cfg.num_heads,
716        cfg.head_dim(),
717        &b.q_norm,
718        rope,
719        pool,
720    );
721    qk_norm_rope(
722        &mut k,
723        n,
724        cfg.num_heads,
725        cfg.head_dim(),
726        &b.k_norm,
727        rope,
728        pool,
729    );
730    let attn = attention(&q, &k, &v, n, cfg.num_heads, cfg.head_dim(), pool);
731    let out = linear(&b.o, &attn, n, None, pool);
732    for row in 0..n {
733        for j in 0..d {
734            x[row * d + j] += out[row * d + j] * gate_a[j];
735        }
736    }
737    let mut norm = x.to_vec();
738    rms_rows_inplace(
739        &mut norm,
740        n,
741        d,
742        &b.norm2,
743        Some(scale_f),
744        Some(shift_f),
745        pool,
746    );
747    let up_gate = linear(&b.ffn_up_gate, &norm, n, None, pool);
748    let inter = cfg.ffn_dim;
749    let mut ffn = vec![0f32; n * inter];
750    for row in 0..n {
751        for i in 0..inter {
752            ffn[row * inter + i] = situ(
753                up_gate[row * 2 * inter + i],
754                up_gate[row * 2 * inter + inter + i],
755            );
756        }
757    }
758    let out = linear(&b.ffn_down, &ffn, n, None, pool);
759    for row in 0..n {
760        for j in 0..d {
761            x[row * d + j] += out[row * d + j] * gate_f[j];
762        }
763    }
764}
765
766#[cfg(test)]
767mod tests {
768    use super::*;
769
770    #[test]
771    fn rope_layout_repeats_quarter_axis_frequencies() {
772        let cfg = MossConfig {
773            dim: 64,
774            ffn_dim: 64,
775            freq_dim: 8,
776            latent_channels: 4,
777            num_heads: 1,
778            num_layers: 1,
779            text_dim: 8,
780            rope_base: 10_000.0,
781        };
782        let r = Rope::new(&cfg, 2, 2, 3);
783        assert_eq!(r.sin.len(), 7 * 64);
784        for t in 0..7 {
785            assert_eq!(
786                &r.sin[t * 64..t * 64 + 32],
787                &r.sin[t * 64 + 32..(t + 1) * 64]
788            );
789            assert_eq!(
790                &r.cos[t * 64..t * 64 + 32],
791                &r.cos[t * 64 + 32..(t + 1) * 64]
792            );
793        }
794    }
795
796    #[test]
797    fn situ_matches_definition() {
798        let h = 2.5f32;
799        let g = -1.25f32;
800        let got = situ(h, g);
801        let want =
802            (4.0 * (g / 4.0).tanh() * (1.0 / (1.0 + (-g).exp()))) * (25.0 * (h / 25.0).tanh());
803        assert!((got - want).abs() < 1e-7);
804    }
805}