use anyhow::{Context, Result, bail, ensure};
use cortiq_core::format::CmfStreamWriter;
use cortiq_core::{CmfModel, TensorDtype};
use serde_json::json;
use std::path::Path;
use crate::convert::{Quant, quantize_2d};
use crate::gguf::{Gguf, GgufTensor, parse};
const COPY_CHUNK: usize = 16 << 20;
const GGML_F32: u32 = 0;
const GGML_Q8_0: u32 = 8;
const GGML_BF16: u32 = 30;
const GGML_NVFP4: u32 = 40;
fn f16(h: u16) -> f32 {
cortiq_core::quant::f16_to_f32(h)
}
fn q8_0(raw: &[u8], n: usize) -> Result<Vec<f32>> {
ensure!(n % 32 == 0 && raw.len() >= n / 32 * 34, "q8_0: {} bytes for {n} values", raw.len());
let mut out = Vec::with_capacity(n);
for b in raw[..n / 32 * 34].chunks_exact(34) {
let d = f16(u16::from_le_bytes([b[0], b[1]]));
out.extend(b[2..].iter().map(|&q| (q as i8) as f32 * d));
}
Ok(out)
}
fn bf16(raw: &[u8], n: usize) -> Vec<f32> {
raw[..2 * n].chunks_exact(2).map(|b| f32::from_bits((u16::from_le_bytes([b[0], b[1]]) as u32) << 16)).collect()
}
fn e4m3(b: u8) -> f32 {
let (s, e, m) = (b >> 7, (b >> 3) & 0xF, b & 7);
let v = if e == 0xF && m == 7 {
0.0
} else if e == 0 {
m as f32 * 2f32.powi(-9)
} else {
(1.0 + m as f32 / 8.0) * 2f32.powi(e as i32 - 7)
};
if s == 1 { -v } else { v }
}
struct Native {
dim: usize,
ff: usize,
layers: Vec<(u64, usize)>,
}
fn read_native(dir: &Path) -> Result<Native> {
let p = dir.join("native_experts.txt");
let s = std::fs::read_to_string(&p).with_context(|| format!("reading {}", p.display()))?;
let num = |key: &str| -> Result<usize> {
let i = s.find(key).with_context(|| format!("{}: no `{key}`", p.display()))? + key.len();
let d: String = s[i..].chars().take_while(|c| c.is_ascii_digit()).collect();
d.parse().with_context(|| format!("{}: `{key}` is not a number", p.display()))
};
let (dim, ff) = (num("expert_embd ")?, num("expert_ff ")?);
let mut layers = Vec::new();
for line in s.lines().filter(|l| !l.starts_with('#') && !l.trim().is_empty()) {
let f: Vec<&str> = line.split_whitespace().collect();
ensure!(f.len() == 5, "{}: bad line `{line}`", p.display());
let (li, gu, dt): (usize, u32, u32) = (f[0].parse()?, f[1].parse()?, f[2].parse()?);
ensure!(li == layers.len(), "{}: layers out of order at `{line}`", p.display());
if gu != GGML_NVFP4 || dt != GGML_NVFP4 {
bail!(
"{}: layer {li} experts are ggml types {gu}/{dt}; only the NVFP4 build (type {GGML_NVFP4}) imports without loss",
p.display()
);
}
layers.push((f[3].parse()?, f[4].parse()?));
}
Ok(Native { dim, ff, layers })
}
fn tensor<'a>(g: &'a Gguf, name: &str) -> Result<&'a GgufTensor> {
g.tensors.iter().find(|t| t.name == name).with_context(|| format!("GGUF tensor {name} missing"))
}
fn raw<'a>(g: &'a Gguf, t: &GgufTensor, bytes: usize) -> Result<&'a [u8]> {
let a = g.data_start + t.offset as usize;
g.bytes.get(a..a + bytes).with_context(|| format!("{}: {bytes} bytes past the end of the GGUF", t.name))
}
fn matrix(g: &Gguf, name: &str) -> Result<(Vec<f32>, usize, usize)> {
let t = tensor(g, name)?;
ensure!(t.dims.len() == 2, "{name}: {:?} is not a matrix", t.dims);
let (cols, rows) = (t.dims[0] as usize, t.dims[1] as usize);
let n = rows * cols;
let v = match t.ggml_type {
GGML_Q8_0 => q8_0(raw(g, t, n / 32 * 34)?, n)?,
GGML_BF16 => bf16(raw(g, t, 2 * n)?, n),
GGML_F32 => raw(g, t, 4 * n)?.chunks_exact(4).map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]])).collect(),
ty => bail!("{name}: ggml type {ty} not expected in the skeleton"),
};
Ok((v, rows, cols))
}
fn mmap(p: &Path) -> Result<memmap2::Mmap> {
let f = std::fs::File::open(p).with_context(|| format!("opening {}", p.display()))?;
Ok(unsafe { memmap2::Mmap::map(&f)? })
}
fn cosine(a: &[f32], b: &[f32]) -> f64 {
let (mut ab, mut aa, mut bb) = (0f64, 0f64, 0f64);
for (&x, &y) in a.iter().zip(b) {
ab += x as f64 * y as f64;
aa += x as f64 * x as f64;
bb += y as f64 * y as f64;
}
ab / (aa.sqrt() * bb.sqrt()).max(1e-30)
}
pub fn cmd_edge0_import(base_path: &str, dir: &str, output: &str, ple: &str) -> Result<()> {
ensure!(matches!(ple, "base" | "edge0"), "--ple must be base or edge0");
ensure!(
!crate::tube::paths_collide(Path::new(output), Path::new(base_path)),
"edge0-import: the output must differ from the base"
);
let dir = Path::new(dir);
let base_arc = std::sync::Arc::new(CmfModel::open_sharded(base_path)?);
let base: &CmfModel = &base_arc;
let arch = base.arch().clone();
ensure!(arch.arch_name == "qwen4_exp", "{base_path}: a Qwen3.8-Flash-Next CMF is the base, this is {}", arch.arch_name);
let (nl, hidden) = (arch.num_layers, arch.hidden_size);
let native = read_native(dir)?;
ensure!(native.layers.len() == nl, "native_experts.txt lists {} layers, the base has {nl}", native.layers.len());
let (dim, ff) = (native.dim, native.ff);
ensure!(dim % 64 == 0 && ff % 64 == 0, "latent {dim} / expert ff {ff} not multiples of 64");
let gu_bytes = ff * (dim / 64) * cortiq_core::nvfp4::GGML_BLOCK_BYTES;
let d_bytes = dim * (ff / 64) * cortiq_core::nvfp4::GGML_BLOCK_BYTES;
let blob = 2 * gu_bytes + d_bytes + 16;
let skel = parse(&dir.join("model-skeleton.gguf"))?;
let ne = tensor(&skel, "blk.0.ffn_gate_inp.weight")?.dims[1] as usize;
for (li, &(off, stride)) in native.layers.iter().enumerate() {
ensure!(stride == blob, "layer {li}: expert stride {stride}, NVFP4 {dim}/{ff} wants {blob}");
ensure!(off == (li * ne * blob) as u64, "layer {li}: offset {off} is not layer-major");
}
let experts = mmap(&dir.join("experts.bin"))?;
ensure!(experts.len() == nl * ne * blob, "experts.bin: {} bytes, want {}", experts.len(), nl * ne * blob);
let labels_raw = std::fs::read(dir.join("labels.bin")).context("reading labels.bin")?;
ensure!(labels_raw.len() == nl * ne * 4, "labels.bin: {} bytes for {nl} x {ne} labels", labels_raw.len());
let labels: Vec<u8> = labels_raw
.chunks_exact(4)
.map(|b| i32::from_le_bytes([b[0], b[1], b[2], b[3]]))
.map(|v| u8::try_from(v).map_err(|_| anyhow::anyhow!("labels.bin: label {v} out of range")))
.collect::<Result<_>>()?;
let groups = labels.iter().copied().max().map_or(0, |g| g as usize + 1);
ensure!(groups > 0, "labels.bin: no groups");
let gl = groups * dim;
let dense = mmap(&dir.join("latent_dense_q8.bin"))?;
let d_layer = gl * (hidden / 32) * 34;
let u_layer = groups * hidden * (dim / 32) * 34;
ensure!(
dense.len() == nl * (d_layer + u_layer),
"latent_dense_q8.bin: {} bytes, {nl} layers of D {gl}x{hidden} and U {groups}x{hidden}x{dim} in Q8_0 want {}",
dense.len(),
nl * (d_layer + u_layer)
);
eprintln!("edge0-import: {nl} layers, {ne} experts in {groups} groups, latent {dim}, expert ff {ff}, NVFP4 blob {blob} B");
let ple_src = if ple == "edge0" {
let g = parse(&dir.join("ple-fp8.gguf"))?;
let t = tensor(&g, "per_layer_token_embd.weight")?;
let scale = match g.md.get("strata.ple.scale") {
Some(v) => v.as_f64().context("strata.ple.scale is not a number")? as f32,
None => bail!("ple-fp8.gguf: no strata.ple.scale"),
};
let (w, rows) = (t.dims[0] as usize, t.dims[1] as usize);
let off = g.data_start + t.offset as usize;
Some((g, off, w, rows, scale))
} else {
None
};
let is_routed = |n: &str| -> bool {
n.strip_prefix("model.layers.").is_some_and(|r| r.split_once('.').is_some_and(|(_, r)| r.starts_with("mlp.experts.")))
};
let replaced = |n: &str| -> bool {
let Some(rest) = n.strip_prefix("model.layers.") else { return false };
let Some((_, r)) = rest.split_once('.') else { return false };
r == "mlp.gate.weight" || r.starts_with("mlp.shared_expert.") && r.ends_with("_proj.weight")
};
let is_ple_shard = |n: &str| n.contains(".ple.ple_embedding.ngram_embedding.shard_");
let mut kept = 0usize;
let n_new = nl * (3 * ne + 3 + 4);
let avg = base.tensors.iter().map(|e| e.name.len()).sum::<usize>() / base.tensors.len().max(1);
let mut w = CmfStreamWriter::new(output, CmfStreamWriter::head_reserve_for(base.tensors.len() + n_new, avg + 12))?;
let t0 = std::time::Instant::now();
let mut ple_rows_done = 0usize;
let mut ple_cos_min = 1f64;
for e in &base.tensors {
if is_routed(&e.name) || replaced(&e.name) {
continue;
}
if let Some((g, off, width, rows, scale)) = ple_src.as_ref().filter(|_| is_ple_shard(&e.name)) {
ensure!(e.shape.len() == 2 && e.shape[1] == *width, "{}: {:?} vs the folder's rows of {width}", e.name, e.shape);
let s: usize = e.name.rsplit("shard_").next().and_then(|v| v.split('.').next()).and_then(|v| v.parse().ok()).context("shard index")?;
let r0 = s * e.shape[0];
ensure!(r0 + e.shape[0] <= *rows, "{}: rows {r0}.. past the folder's {rows}", e.name);
let bytes = &g.bytes[off + r0 * width..off + (r0 + e.shape[0]) * width];
let vals: Vec<f32> = bytes.iter().map(|&b| e4m3(b) * scale).collect();
let mut tmpl = vec![0f32; *width];
let q = cortiq_engine::qtensor::QTensor::from_model(&base_arc, &e.name).ok();
if let Some(qt) = q.as_ref() {
for k in 0..8 {
let r = (k * 7919 + 13) % e.shape[0];
qt.row_f32(r, &mut tmpl);
let c = cosine(&vals[r * width..(r + 1) * width], &tmpl);
ple_cos_min = ple_cos_min.min(c);
ensure!(c > 0.9, "{} row {r}: cosine {c:.3} with the base's table: not the same rows", e.name);
}
}
let (dt, data) = quantize_2d(Quant::Q8_2f, &vals, e.shape[0], *width);
w.push(&e.name, dt, &e.shape, &data)?;
ple_rows_done += e.shape[0];
continue;
}
w.push_bounded(&e.name, e.dtype, &e.shape, base.entry_bytes(e), COPY_CHUNK)?;
kept += 1;
}
eprintln!("edge0-import: {kept} skeleton tensors copied, {:.0} s", t0.elapsed().as_secs_f64());
let mut nv_bytes = 0usize;
for li in 0..nl {
let p = format!("model.layers.{li}.mlp.");
let (r, rows, cols) = matrix(&skel, &format!("blk.{li}.ffn_gate_inp.weight"))?;
ensure!(rows == ne && cols == hidden, "layer {li} router {rows}x{cols}");
let (dt, data) = quantize_2d(Quant::F16, &r, rows, cols);
w.push(&format!("{p}gate.weight"), dt, &[rows, cols], &data)?;
for (gn, part) in [("ffn_gate_shexp", "gate_proj"), ("ffn_up_shexp", "up_proj"), ("ffn_down_shexp", "down_proj")] {
let (v, rows, cols) = matrix(&skel, &format!("blk.{li}.{gn}.weight"))?;
let (dt, data) = quantize_2d(Quant::Q8_2f, &v, rows, cols);
w.push(&format!("{p}shared_expert.{part}.weight"), dt, &[rows, cols], &data)?;
}
let lo = li * (d_layer + u_layer);
let dv = q8_0(&dense[lo..lo + d_layer], gl * hidden)?;
let (dt, data) = quantize_2d(Quant::Q8_2f, &dv, gl, hidden);
w.push(&format!("{p}latent_down.weight"), dt, &[gl, hidden], &data)?;
drop(dv);
let uv = q8_0(&dense[lo + d_layer..lo + d_layer + u_layer], groups * hidden * dim)?;
let mut u = vec![0f32; hidden * gl];
for g in 0..groups {
for h in 0..hidden {
let src = &uv[(g * hidden + h) * dim..(g * hidden + h + 1) * dim];
u[h * gl + g * dim..h * gl + (g + 1) * dim].copy_from_slice(src);
}
}
drop(uv);
let (dt, data) = quantize_2d(Quant::Q8_2f, &u, hidden, gl);
w.push(&format!("{p}latent_up.weight"), dt, &[hidden, gl], &data)?;
drop(u);
w.push(&format!("{p}latent_groups"), TensorDtype::U8, &[ne], &labels[li * ne..(li + 1) * ne])?;
for e in 0..ne {
let b = &experts[(li * ne + e) * blob..(li * ne + e + 1) * blob];
let tail = &b[blob - 16..];
let sc = |i: usize| f32::from_le_bytes([tail[4 * i], tail[4 * i + 1], tail[4 * i + 2], tail[4 * i + 3]]);
let parts = [
("gate_proj", &b[..gu_bytes], ff, dim, sc(0)),
("up_proj", &b[gu_bytes..2 * gu_bytes], ff, dim, sc(1)),
("down_proj", &b[2 * gu_bytes..2 * gu_bytes + d_bytes], dim, ff, sc(2)),
];
for (part, bytes, rows, cols, s) in parts {
let data = cortiq_core::nvfp4::repack_ggml(bytes, rows, cols, s)
.map_err(|m| anyhow::anyhow!("layer {li} expert {e} {part}: {m}"))?;
nv_bytes += data.len();
w.push(&format!("{p}experts.{e}.{part}.weight"), TensorDtype::Nvfp4, &[rows, cols], &data)?;
}
}
if (li + 1) % 8 == 0 || li + 1 == nl {
eprintln!("edge0-import: {}/{nl} layers, {:.0} s", li + 1, t0.elapsed().as_secs_f64());
}
}
let mut header = base.header.clone();
let q = header.arch.qwen4_exp.as_mut().context("base header has no qwen4_exp block")?;
q.latent_dim = Some(dim);
q.latent_groups = Some(groups);
if let Some(m) = header.arch.moe.as_mut() {
m.num_experts = ne;
}
let model_json: serde_json::Value = std::fs::read(dir.join("model.json"))
.ok()
.and_then(|b| serde_json::from_slice(&b).ok())
.unwrap_or(json!({}));
let mut prov = json!({});
prov["edge0"] = json!({
"source": model_json.get("display_name").cloned().unwrap_or(json!(null)),
"build": model_json.get("build").cloned().unwrap_or(json!(null)),
"latent_dim": dim,
"latent_groups": groups,
"experts": "NVFP4, re-packed bit for bit",
"latent_dense": "Q8_0 -> q8_2f",
"router": "bf16 -> f16",
"shared_expert": "Q8_0 -> q8_2f",
"ngram_table": if ple == "edge0" { "FP8 -> q8_2f" } else { "the base's" },
"skeleton": Path::new(base_path).file_name().and_then(|s| s.to_str()),
});
header.provenance = Some(prov);
w.finish(&header, (!base.masks.masks.is_empty()).then_some(&base.masks), base.vocab.as_deref())?;
let gb = |p: &str| std::fs::metadata(p).map(|m| m.len() as f64 / 1e9).unwrap_or(0.0);
println!(
"edge0-import: {output} {:.2} GB ({:.2} GB of NVFP4 experts){}",
gb(output),
nv_bytes as f64 / 1e9,
if ple_rows_done > 0 { format!(", n-gram table from the folder: {ple_rows_done} rows, min cosine to the base {ple_cos_min:.4}") } else { String::new() }
);
Ok(())
}