use fuel_ir::Shape;
use fuel_tensor::lazy::Tensor;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct WuerstchenConfig {
pub prior_c_in: usize,
pub prior_c: usize,
pub prior_c_cond: usize,
pub c_r: usize,
pub prior_depth: usize,
pub prior_nhead: usize,
pub diffnext_c_in: usize,
pub diffnext_c_out: usize,
pub diffnext_c_cond: usize,
pub patch_size: usize,
pub diffnext_c_hidden: Vec<usize>,
pub diffnext_blocks: Vec<usize>,
pub diffnext_nhead: Vec<usize>,
pub paella_latent_channels: usize,
pub paella_levels: Vec<usize>,
pub paella_bottleneck_blocks: usize,
pub paella_out_channels: usize,
pub clip_embed: usize,
pub image_size: usize,
}
impl WuerstchenConfig {
pub fn tiny() -> Self {
Self {
prior_c_in: 4,
prior_c: 16,
prior_c_cond: 16,
c_r: 16,
prior_depth: 1,
prior_nhead: 2,
diffnext_c_in: 4,
diffnext_c_out: 4,
diffnext_c_cond: 16,
patch_size: 2,
diffnext_c_hidden: vec![8, 16],
diffnext_blocks: vec![1, 1],
diffnext_nhead: vec![0, 2],
paella_latent_channels: 4,
paella_levels: vec![8, 8],
paella_bottleneck_blocks: 1,
paella_out_channels: 3,
clip_embed: 16,
image_size: 32,
}
}
}
#[derive(Debug, Clone)]
pub struct GrnWeights {
pub gamma: Arc<[f32]>,
pub beta: Arc<[f32]>,
}
#[derive(Debug, Clone)]
pub struct ResBlockWeights {
pub dw_w: Arc<[f32]>,
pub dw_b: Arc<[f32]>,
pub fc1_w: Arc<[f32]>,
pub fc1_b: Arc<[f32]>,
pub grn: GrnWeights,
pub fc2_w: Arc<[f32]>,
pub fc2_b: Arc<[f32]>,
pub c: usize,
pub c_skip: usize,
pub ksize: usize,
}
#[derive(Debug, Clone)]
pub struct TimestepBlockWeights {
pub w: Arc<[f32]>,
pub b: Arc<[f32]>,
pub c: usize,
pub c_timestep: usize,
}
#[derive(Debug, Clone)]
pub struct AttnBlockWeights {
pub kv_mapper_w: Arc<[f32]>,
pub kv_mapper_b: Arc<[f32]>,
pub to_q_w: Arc<[f32]>,
pub to_q_b: Arc<[f32]>,
pub to_k_w: Arc<[f32]>,
pub to_k_b: Arc<[f32]>,
pub to_v_w: Arc<[f32]>,
pub to_v_b: Arc<[f32]>,
pub to_out_w: Arc<[f32]>,
pub to_out_b: Arc<[f32]>,
pub c: usize,
pub c_cond: usize,
pub heads: usize,
}
#[derive(Debug, Clone)]
pub struct PriorBlockWeights {
pub res: ResBlockWeights,
pub ts: TimestepBlockWeights,
pub attn: AttnBlockWeights,
}
#[derive(Debug, Clone)]
pub struct PriorWeights {
pub projection_w: Arc<[f32]>,
pub projection_b: Arc<[f32]>,
pub cond1_w: Arc<[f32]>,
pub cond1_b: Arc<[f32]>,
pub cond2_w: Arc<[f32]>,
pub cond2_b: Arc<[f32]>,
pub blocks: Vec<PriorBlockWeights>,
pub out_conv_w: Arc<[f32]>,
pub out_conv_b: Arc<[f32]>,
}
#[derive(Debug, Clone)]
pub struct DiffNextSubBlockWeights {
pub res: ResBlockWeights,
pub ts: TimestepBlockWeights,
pub attn: Option<AttnBlockWeights>,
}
#[derive(Debug, Clone)]
pub struct DiffNextLevelWeights {
pub down_w: Option<Arc<[f32]>>,
pub down_b: Option<Arc<[f32]>>,
pub up_w: Option<Arc<[f32]>>,
pub up_b: Option<Arc<[f32]>>,
pub subs: Vec<DiffNextSubBlockWeights>,
}
#[derive(Debug, Clone)]
pub struct DiffNextWeights {
pub clip_mapper_w: Arc<[f32]>,
pub clip_mapper_b: Arc<[f32]>,
pub embed_w: Arc<[f32]>,
pub embed_b: Arc<[f32]>,
pub down_levels: Vec<DiffNextLevelWeights>,
pub up_levels: Vec<DiffNextLevelWeights>,
pub clf_w: Arc<[f32]>,
pub clf_b: Arc<[f32]>,
}
#[derive(Debug, Clone)]
pub struct PaellaMixingResWeights {
pub gammas: [f32; 6],
pub dw_w: Arc<[f32]>,
pub dw_b: Arc<[f32]>,
pub fc1_w: Arc<[f32]>,
pub fc1_b: Arc<[f32]>,
pub fc2_w: Arc<[f32]>,
pub fc2_b: Arc<[f32]>,
pub c: usize,
}
#[derive(Debug, Clone)]
pub struct PaellaVqWeights {
pub up_in_w: Arc<[f32]>,
pub up_in_b: Arc<[f32]>,
pub up_levels: Vec<PaellaUpLevelWeights>,
pub out_w: Arc<[f32]>,
pub out_b: Arc<[f32]>,
}
#[derive(Debug, Clone)]
pub struct PaellaUpLevelWeights {
pub res_blocks: Vec<PaellaMixingResWeights>,
pub upsample_w: Option<Arc<[f32]>>,
pub upsample_b: Option<Arc<[f32]>>,
}
fn w_layer_norm(
x: &Tensor,
c: usize,
h: usize,
w: usize,
eps: f64,
) -> fuel_ir::error::Result<Tensor> {
let x_nhwc = x.permute([0, 2, 3, 1_usize])?;
let x_flat = x_nhwc.reshape(Shape::from_dims(&[1, h * w, c]))?;
let normed = x_flat.layer_norm_last_dim(eps)?;
normed
.reshape(Shape::from_dims(&[1, h, w, c]))?
.permute([0, 3, 1, 2_usize])
}
fn ln_last_no_affine(x: &Tensor, eps: f64) -> fuel_ir::error::Result<Tensor> {
x.layer_norm_last_dim(eps)
}
fn linear(
x: &Tensor,
w: &Arc<[f32]>,
b: Option<&Arc<[f32]>>,
in_f: usize,
out_f: usize,
batch: usize,
seq: usize,
) -> fuel_ir::error::Result<Tensor> {
let w_t = x.const_f32_like(w.clone(), Shape::from_dims(&[in_f, out_f]))?;
let proj = x.matmul(&w_t)?;
match b {
Some(b) => {
let bias = x
.const_f32_like(b.clone(), Shape::from_dims(&[out_f]))?
.reshape(Shape::from_dims(&[1, 1, out_f]))?
.broadcast_to(Shape::from_dims(&[batch, seq, out_f]))?;
proj.add(&bias)
}
None => Ok(proj),
}
}
pub fn global_response_norm(
x: &Tensor,
gamma: &Arc<[f32]>,
beta: &Arc<[f32]>,
c: usize,
h: usize,
w: usize,
) -> fuel_ir::error::Result<Tensor> {
let sq = x.mul(x)?; let agg = sq.reduce_sum_to(Shape::from_dims(&[1, c, 1, 1])).sqrt(); let agg_sum = agg.reduce_sum_to(Shape::from_dims(&[1, 1, 1, 1]));
let mean_c = agg_sum.mul_scalar(1.0_f64 / c as f64); let denom = mean_c
.add_scalar(1e-6_f64)
.broadcast_to(Shape::from_dims(&[1, c, 1, 1]))?;
let stand = agg.div(&denom)?; let stand_b = stand.broadcast_to(Shape::from_dims(&[1, c, h, w]))?;
let g = x
.const_f32_like(gamma.clone(), Shape::from_dims(&[c]))?
.reshape(Shape::from_dims(&[1, c, 1, 1]))?
.broadcast_to(Shape::from_dims(&[1, c, h, w]))?;
let b = x
.const_f32_like(beta.clone(), Shape::from_dims(&[c]))?
.reshape(Shape::from_dims(&[1, c, 1, 1]))?
.broadcast_to(Shape::from_dims(&[1, c, h, w]))?;
let scaled = x.mul(&stand_b)?.mul(&g)?;
let with_beta = scaled.add(&b)?;
with_beta.add(x)
}
fn apply_res_block(
x: &Tensor,
rw: &ResBlockWeights,
skip: Option<&Tensor>,
h: usize,
w: usize,
eps: f64,
) -> fuel_ir::error::Result<Tensor> {
let c = rw.c;
let c_skip = rw.c_skip;
let k = rw.ksize;
let residual = x.clone();
let xs = match skip {
Some(s) => x.concat(s, 1_usize)?,
None => x.clone(),
};
let _ = c_skip;
let dw_w = x.const_f32_like(rw.dw_w.clone(), Shape::from_dims(&[c, 1, k, k]))?;
let dw_b = x.const_f32_like(rw.dw_b.clone(), Shape::from_dims(&[c]))?;
let conv = xs.conv2d(&dw_w, Some(&dw_b), (1, 1), (k / 2, k / 2), c)?;
let norm = w_layer_norm(&conv, c, h, w, eps)?;
let nhwc = norm.permute([0, 2, 3, 1_usize])?; let flat = nhwc.reshape(Shape::from_dims(&[1, h * w, c]))?;
let fc1 = linear(&flat, &rw.fc1_w, Some(&rw.fc1_b), c, 4 * c, 1, h * w)?.gelu_tanh();
let fc1_chw = fc1
.reshape(Shape::from_dims(&[1, h, w, 4 * c]))?
.permute([0, 3, 1, 2_usize])?;
let grn = global_response_norm(&fc1_chw, &rw.grn.gamma, &rw.grn.beta, 4 * c, h, w)?;
let grn_nhwc = grn.permute([0, 2, 3, 1_usize])?;
let grn_flat = grn_nhwc.reshape(Shape::from_dims(&[1, h * w, 4 * c]))?;
let fc2 = linear(&grn_flat, &rw.fc2_w, Some(&rw.fc2_b), 4 * c, c, 1, h * w)?;
let out_chw = fc2
.reshape(Shape::from_dims(&[1, h, w, c]))?
.permute([0, 3, 1, 2_usize])?;
residual.add(&out_chw)
}
fn apply_res_block_stage_b(
x: &Tensor,
rw: &ResBlockWeights,
skip: Option<&Tensor>,
h: usize,
w: usize,
eps: f64,
) -> fuel_ir::error::Result<Tensor> {
let c = rw.c;
let c_skip = rw.c_skip;
let k = rw.ksize;
let residual = x.clone();
let dw_w = x.const_f32_like(rw.dw_w.clone(), Shape::from_dims(&[c, 1, k, k]))?;
let dw_b = x.const_f32_like(rw.dw_b.clone(), Shape::from_dims(&[c]))?;
let conv = x.conv2d(&dw_w, Some(&dw_b), (1, 1), (k / 2, k / 2), c)?;
let norm = w_layer_norm(&conv, c, h, w, eps)?;
let merged = match skip {
Some(s) => norm.concat(s, 1_usize)?,
None => norm,
};
let merged_nhwc = merged.permute([0, 2, 3, 1_usize])?;
let merged_flat = merged_nhwc.reshape(Shape::from_dims(&[1, h * w, c + c_skip]))?;
let fc1 = linear(
&merged_flat,
&rw.fc1_w,
Some(&rw.fc1_b),
c + c_skip,
4 * c,
1,
h * w,
)?
.gelu_tanh();
let fc1_chw = fc1
.reshape(Shape::from_dims(&[1, h, w, 4 * c]))?
.permute([0, 3, 1, 2_usize])?;
let grn = global_response_norm(&fc1_chw, &rw.grn.gamma, &rw.grn.beta, 4 * c, h, w)?;
let grn_nhwc = grn.permute([0, 2, 3, 1_usize])?;
let grn_flat = grn_nhwc.reshape(Shape::from_dims(&[1, h * w, 4 * c]))?;
let fc2 = linear(&grn_flat, &rw.fc2_w, Some(&rw.fc2_b), 4 * c, c, 1, h * w)?;
let out_chw = fc2
.reshape(Shape::from_dims(&[1, h, w, c]))?
.permute([0, 3, 1, 2_usize])?;
residual.add(&out_chw)
}
fn apply_timestep_block(
x: &Tensor,
tw: &TimestepBlockWeights,
t: &Tensor, h: usize,
w: usize,
) -> fuel_ir::error::Result<Tensor> {
let c = tw.c;
let mapped = linear(t, &tw.w, Some(&tw.b), tw.c_timestep, 2 * c, 1, 1)?;
let halves = mapped.chunk(2, 2_usize)?;
let a = halves[0]
.reshape(Shape::from_dims(&[1, c, 1, 1]))?
.broadcast_to(Shape::from_dims(&[1, c, h, w]))?;
let b = halves[1]
.reshape(Shape::from_dims(&[1, c, 1, 1]))?
.broadcast_to(Shape::from_dims(&[1, c, h, w]))?;
let one_plus_a = a.add_scalar(1.0_f64);
x.mul(&one_plus_a)?.add(&b)
}
fn apply_attn_block(
x: &Tensor,
aw: &AttnBlockWeights,
kv_raw: &Tensor, s_kv: usize,
h: usize,
w: usize,
eps: f64,
self_attn: bool,
) -> fuel_ir::error::Result<Tensor> {
let c = aw.c;
let heads = aw.heads;
let d_head = c / heads;
let norm = w_layer_norm(x, c, h, w, eps)?;
let kv = linear(
&kv_raw.silu(),
&aw.kv_mapper_w,
Some(&aw.kv_mapper_b),
aw.c_cond,
c,
1,
s_kv,
)?;
let norm_seq = norm
.reshape(Shape::from_dims(&[1, c, h * w]))?
.transpose()?;
let (kv_full, s_total) = if self_attn {
(norm_seq.concat(&kv, 1_usize)?, h * w + s_kv)
} else {
(kv, s_kv)
};
let q = linear(&norm_seq, &aw.to_q_w, Some(&aw.to_q_b), c, c, 1, h * w)?;
let k = linear(&kv_full, &aw.to_k_w, Some(&aw.to_k_b), c, c, 1, s_total)?;
let v = linear(&kv_full, &aw.to_v_w, Some(&aw.to_v_b), c, c, 1, s_total)?;
let q_h = q
.reshape(Shape::from_dims(&[1, h * w, heads, d_head]))?
.permute([0, 2, 1, 3_usize])?;
let k_h = k
.reshape(Shape::from_dims(&[1, s_total, heads, d_head]))?
.permute([0, 2, 1, 3_usize])?;
let v_h = v
.reshape(Shape::from_dims(&[1, s_total, heads, d_head]))?
.permute([0, 2, 1, 3_usize])?;
let k_t = k_h.permute([0, 1, 3, 2_usize])?; let scale = 1.0_f64 / (d_head as f64).sqrt();
let scores = q_h.matmul(&k_t)?.mul_scalar(scale); let probs = scores.softmax_last_dim()?;
let out_h = probs.matmul(&v_h)?; let out = out_h
.permute([0, 2, 1, 3_usize])?
.reshape(Shape::from_dims(&[1, h * w, c]))?;
let proj = linear(&out, &aw.to_out_w, Some(&aw.to_out_b), c, c, 1, h * w)?;
let proj_chw = proj.transpose()?.reshape(Shape::from_dims(&[1, c, h, w]))?;
x.add(&proj_chw)
}
fn r_embedding_host(r: f32, c_r: usize) -> Vec<f32> {
let half_dim = c_r / 2;
let max_pos = 10000.0_f64;
let denom = ((half_dim as f64) - 1.0).max(1.0);
let log_max = max_pos.ln() / denom;
let mut out = vec![0.0_f32; c_r];
let r64 = r as f64 * max_pos;
for i in 0..half_dim {
let freq = (-(i as f64) * log_max).exp();
let arg = r64 * freq;
out[i] = arg.sin() as f32;
out[i + half_dim] = arg.cos() as f32;
}
out
}
#[derive(Debug, Clone)]
pub struct PriorModel {
pub config: WuerstchenConfig,
pub weights: PriorWeights,
}
impl PriorModel {
pub fn forward(
&self,
xs: &Tensor,
r: f32,
c_embed: &Tensor,
h: usize,
w: usize,
) -> fuel_ir::error::Result<Tensor> {
let cfg = &self.config;
let c = cfg.prior_c;
let c_in = cfg.prior_c_in;
let c_cond = cfg.prior_c_cond;
let s_kv = c_embed.dim(1_usize)?;
let eps = 1e-6_f64;
let x_in = xs.clone();
let proj_w = xs.const_f32_like(
self.weights.projection_w.clone(),
Shape::from_dims(&[c, c_in, 1, 1]),
)?;
let proj_b =
xs.const_f32_like(self.weights.projection_b.clone(), Shape::from_dims(&[c]))?;
let mut x = xs.conv2d(&proj_w, Some(&proj_b), (1, 1), (0, 0), 1)?;
let c1 = linear(
c_embed,
&self.weights.cond1_w,
Some(&self.weights.cond1_b),
c_cond,
c,
1,
s_kv,
)?;
let c1_neg = c1.mul_scalar(0.2_f64);
let zero = c1.const_f32_like(vec![0.0_f32; c1.elem_count()], c1.shape())?;
let mask = c1.gt(&zero)?;
let c1_act = mask.where_cond(&c1, &c1_neg)?;
let c_embed_mapped = linear(
&c1_act,
&self.weights.cond2_w,
Some(&self.weights.cond2_b),
c,
c,
1,
s_kv,
)?;
let r_vec = r_embedding_host(r, cfg.c_r);
let r_embed = xs
.const_f32_like(r_vec, Shape::from_dims(&[cfg.c_r]))?
.reshape(Shape::from_dims(&[1, 1, cfg.c_r]))?;
for bw in &self.weights.blocks {
x = apply_res_block(&x, &bw.res, None, h, w, eps)?;
x = apply_timestep_block(&x, &bw.ts, &r_embed, h, w)?;
x = apply_attn_block(&x, &bw.attn, &c_embed_mapped, s_kv, h, w, eps, true)?;
}
let normed = w_layer_norm(&x, c, h, w, eps)?;
let out_w = xs.const_f32_like(
self.weights.out_conv_w.clone(),
Shape::from_dims(&[c_in * 2, c, 1, 1]),
)?;
let out_b = xs.const_f32_like(
self.weights.out_conv_b.clone(),
Shape::from_dims(&[c_in * 2]),
)?;
let out = normed.conv2d(&out_w, Some(&out_b), (1, 1), (0, 0), 1)?;
let ab = out.chunk(2, 1_usize)?;
let a0 = &ab[0];
let a1 = &ab[1];
let denom = a1.add_scalar(-1.0_f64).abs().add_scalar(1e-5_f64);
let num = x_in.sub(a0)?;
num.div(&denom)
}
}
#[derive(Debug, Clone)]
pub struct DiffNextModel {
pub config: WuerstchenConfig,
pub weights: DiffNextWeights,
}
impl DiffNextModel {
pub fn forward(
&self,
xs: &Tensor,
r: f32,
clip: &Tensor,
h_in: usize,
w_in: usize,
) -> fuel_ir::error::Result<Tensor> {
let cfg = &self.config;
let c_in = cfg.diffnext_c_in;
let c_out = cfg.diffnext_c_out;
let c_cond = cfg.diffnext_c_cond;
let p = cfg.patch_size;
let eps = 1e-6_f64;
let levels = &cfg.diffnext_c_hidden;
if levels.is_empty() {
return Err(fuel_ir::error::Error::Msg(
"DiffNextModel.forward: diffnext_c_hidden is empty".into(),
)
.bt());
}
if !h_in.is_multiple_of(p) || !w_in.is_multiple_of(p) {
return Err(fuel_ir::error::Error::Msg(format!(
"DiffNextModel.forward: spatial dims ({h_in}, {w_in}) must be divisible by patch_size {p}",
)).bt());
}
let x_in = xs.clone();
let s_kv = clip.dim(1_usize)?;
let c_embed = linear(
clip,
&self.weights.clip_mapper_w,
Some(&self.weights.clip_mapper_b),
cfg.clip_embed,
c_cond,
1,
s_kv,
)?;
let c_embed = ln_last_no_affine(&c_embed, eps)?;
let r_vec = r_embedding_host(r, cfg.c_r);
let r_embed = xs
.const_f32_like(r_vec, Shape::from_dims(&[cfg.c_r]))?
.reshape(Shape::from_dims(&[1, 1, cfg.c_r]))?;
let mut h = h_in / p;
let mut w = w_in / p;
let xu = pixel_unshuffle(xs, c_in, h_in, w_in, p)?;
let emb_w = xs.const_f32_like(
self.weights.embed_w.clone(),
Shape::from_dims(&[levels[0], c_in * p * p, 1, 1]),
)?;
let emb_b =
xs.const_f32_like(self.weights.embed_b.clone(), Shape::from_dims(&[levels[0]]))?;
let mut x = xu.conv2d(&emb_w, Some(&emb_b), (1, 1), (0, 0), 1)?;
x = w_layer_norm(&x, levels[0], h, w, eps)?;
let mut skips: Vec<Tensor> = Vec::new();
for (i, lvl) in self.weights.down_levels.iter().enumerate() {
let c_lvl = levels[i];
if let (Some(dw), Some(db)) = (&lvl.down_w, &lvl.down_b) {
let prev = levels[i - 1];
x = w_layer_norm(&x, prev, h, w, eps)?;
let dw_t = xs.const_f32_like(dw.clone(), Shape::from_dims(&[c_lvl, prev, 2, 2]))?;
let db_t = xs.const_f32_like(db.clone(), Shape::from_dims(&[c_lvl]))?;
x = x.conv2d(&dw_t, Some(&db_t), (2, 2), (0, 0), 1)?;
h /= 2;
w /= 2;
}
for sub in &lvl.subs {
x = apply_res_block_stage_b(&x, &sub.res, None, h, w, eps)?;
x = apply_timestep_block(&x, &sub.ts, &r_embed, h, w)?;
if let Some(aw) = &sub.attn {
x = apply_attn_block(&x, aw, &c_embed, s_kv, h, w, eps, true)?;
}
}
skips.push(x.clone());
}
skips.reverse();
let mut x = skips[0].clone();
let n_levels = levels.len();
for (i, lvl) in self.weights.up_levels.iter().enumerate() {
let lvl_idx = n_levels - 1 - i;
let c_lvl = levels[lvl_idx];
for (j, sub) in lvl.subs.iter().enumerate() {
let skip_ref = if j == 0 && i > 0 {
Some(&skips[i])
} else {
None
};
x = apply_res_block_stage_b(&x, &sub.res, skip_ref, h, w, eps)?;
x = apply_timestep_block(&x, &sub.ts, &r_embed, h, w)?;
if let Some(aw) = &sub.attn {
x = apply_attn_block(&x, aw, &c_embed, s_kv, h, w, eps, true)?;
}
}
if let (Some(uw), Some(ub)) = (&lvl.up_w, &lvl.up_b) {
let next = levels[lvl_idx - 1];
x = w_layer_norm(&x, c_lvl, h, w, eps)?;
let uw_t = xs.const_f32_like(uw.clone(), Shape::from_dims(&[c_lvl, next, 2, 2]))?;
let ub_t = xs.const_f32_like(ub.clone(), Shape::from_dims(&[next]))?;
x = x.conv_transpose2d(&uw_t, (2, 2), (0, 0), (0, 0), (1, 1), 1)?;
let _ = ub_t;
let bias = xs
.const_f32_like(ub.clone(), Shape::from_dims(&[next]))?
.reshape(Shape::from_dims(&[1, next, 1, 1]))?
.broadcast_to(Shape::from_dims(&[1, next, h * 2, w * 2]))?;
x = x.add(&bias)?;
h *= 2;
w *= 2;
}
}
let last_h = levels[0];
x = w_layer_norm(&x, last_h, h, w, eps)?;
let clf_w = xs.const_f32_like(
self.weights.clf_w.clone(),
Shape::from_dims(&[2 * c_out * p * p, last_h, 1, 1]),
)?;
let clf_b = xs.const_f32_like(
self.weights.clf_b.clone(),
Shape::from_dims(&[2 * c_out * p * p]),
)?;
let out = x.conv2d(&clf_w, Some(&clf_b), (1, 1), (0, 0), 1)?;
let out = pixel_shuffle(&out, 2 * c_out * p * p, h, w, p)?;
let ab = out.chunk(2, 1_usize)?;
let a = &ab[0];
let b_raw = &ab[1];
let eps_b = 1e-3_f64;
let b = b_raw.sigmoid().affine(1.0 - 2.0 * eps_b, eps_b);
let num = x_in.sub(a)?;
num.div(&b)
}
}
fn pixel_unshuffle(
x: &Tensor,
c: usize,
h: usize,
w: usize,
p: usize,
) -> fuel_ir::error::Result<Tensor> {
let h_out = h / p;
let w_out = w / p;
let r = x.reshape(Shape::from_dims(&[1, c, h_out, p, w_out, p]))?;
let pm = r.permute([0, 1, 3, 5, 2, 4_usize])?;
pm.reshape(Shape::from_dims(&[1, c * p * p, h_out, w_out]))
}
fn pixel_shuffle(
x: &Tensor,
c: usize,
h: usize,
w: usize,
p: usize,
) -> fuel_ir::error::Result<Tensor> {
let c_out = c / (p * p);
let r = x.reshape(Shape::from_dims(&[1, c_out, p, p, h, w]))?;
let pm = r.permute([0, 1, 4, 2, 5, 3_usize])?;
pm.reshape(Shape::from_dims(&[1, c_out, h * p, w * p]))
}
#[derive(Debug, Clone)]
pub struct PaellaVqModel {
pub config: WuerstchenConfig,
pub weights: PaellaVqWeights,
}
impl PaellaVqModel {
pub fn decode(&self, latents: &Tensor) -> fuel_ir::error::Result<Tensor> {
let cfg = &self.config;
let dims = latents.shape().dims().to_vec();
if dims.len() != 4 {
return Err(fuel_ir::error::Error::Msg(format!(
"PaellaVqModel.decode: latents must be rank 4 [1, C, H, W], got {dims:?}",
))
.bt());
}
if dims[1] != cfg.paella_latent_channels {
return Err(fuel_ir::error::Error::Msg(format!(
"PaellaVqModel.decode: latent channel mismatch (have {}, cfg {})",
dims[1], cfg.paella_latent_channels,
))
.bt());
}
let (mut h, mut w) = (dims[2], dims[3]);
let levels = &cfg.paella_levels;
let in_w = latents.const_f32_like(
self.weights.up_in_w.clone(),
Shape::from_dims(&[levels[0], cfg.paella_latent_channels, 1, 1]),
)?;
let in_b =
latents.const_f32_like(self.weights.up_in_b.clone(), Shape::from_dims(&[levels[0]]))?;
let mut x = latents.conv2d(&in_w, Some(&in_b), (1, 1), (0, 0), 1)?;
for (i, lw) in self.weights.up_levels.iter().enumerate() {
let c_lvl = levels[i];
for rb in &lw.res_blocks {
x = apply_paella_mixing_res(&x, rb, h, w)?;
}
if let (Some(uw), Some(ub)) = (&lw.upsample_w, &lw.upsample_b) {
let next = levels[i + 1];
let uw_t =
latents.const_f32_like(uw.clone(), Shape::from_dims(&[c_lvl, next, 4, 4]))?;
let x_t = x.conv_transpose2d(&uw_t, (2, 2), (1, 1), (0, 0), (1, 1), 1)?;
let new_h = h * 2;
let new_w = w * 2;
let bias = latents
.const_f32_like(ub.clone(), Shape::from_dims(&[next]))?
.reshape(Shape::from_dims(&[1, next, 1, 1]))?
.broadcast_to(Shape::from_dims(&[1, next, new_h, new_w]))?;
x = x_t.add(&bias)?;
h = new_h;
w = new_w;
}
}
let last_c = *levels.last().ok_or_else(|| {
fuel_ir::Error::Msg("wuerstchen: levels must not be empty".to_string())
})?;
let oc = cfg.paella_out_channels;
let ow = latents.const_f32_like(
self.weights.out_w.clone(),
Shape::from_dims(&[oc * 4, last_c, 1, 1]),
)?;
let ob = latents.const_f32_like(self.weights.out_b.clone(), Shape::from_dims(&[oc * 4]))?;
let out = x.conv2d(&ow, Some(&ob), (1, 1), (0, 0), 1)?;
let out = pixel_shuffle(&out, oc * 4, h, w, 2)?;
Ok(out.tanh())
}
}
fn apply_paella_mixing_res(
x: &Tensor,
bw: &PaellaMixingResWeights,
h: usize,
w: usize,
) -> fuel_ir::error::Result<Tensor> {
let c = bw.c;
let g = &bw.gammas;
let eps = 1e-6_f64;
let temp = w_layer_norm(x, c, h, w, eps)?;
let temp = temp.affine(1.0 + g[0] as f64, g[1] as f64);
let padded = replicate_pad_2d(&temp, c, h, w, 1)?;
let dw_w = x.const_f32_like(bw.dw_w.clone(), Shape::from_dims(&[c, 1, 3, 3]))?;
let dw_b = x.const_f32_like(bw.dw_b.clone(), Shape::from_dims(&[c]))?;
let conv = padded.conv2d(&dw_w, Some(&dw_b), (1, 1), (0, 0), c)?;
let xs = x.add(&conv.mul_scalar(g[2] as f64))?;
let temp = w_layer_norm(&xs, c, h, w, eps)?.affine(1.0 + g[3] as f64, g[4] as f64);
let nhwc = temp.permute([0, 2, 3, 1_usize])?;
let flat = nhwc.reshape(Shape::from_dims(&[1, h * w, c]))?;
let embed_dim = bw.fc1_b.len();
let mid = linear(&flat, &bw.fc1_w, Some(&bw.fc1_b), c, embed_dim, 1, h * w)?.gelu_tanh();
let out = linear(&mid, &bw.fc2_w, Some(&bw.fc2_b), embed_dim, c, 1, h * w)?;
let out_chw = out
.reshape(Shape::from_dims(&[1, h, w, c]))?
.permute([0, 3, 1, 2_usize])?;
xs.add(&out_chw.mul_scalar(g[5] as f64))
}
fn replicate_pad_2d(
x: &Tensor,
_c: usize,
h: usize,
w: usize,
pad: usize,
) -> fuel_ir::error::Result<Tensor> {
if pad == 0 {
return Ok(x.clone());
}
let left_col = x.narrow(3_usize, 0, 1)?;
let right_col = x.narrow(3_usize, w - 1, 1)?;
let left_block = left_col.repeat(Shape::from_dims(&[1, 1, 1, pad]))?;
let right_block = right_col.repeat(Shape::from_dims(&[1, 1, 1, pad]))?;
let padded_w = left_block
.concat(x, 3_usize)?
.concat(&right_block, 3_usize)?;
let h2 = padded_w.dim(2_usize)?;
let _ = (h, w);
let top_row = padded_w.narrow(2_usize, 0, 1)?;
let bot_row = padded_w.narrow(2_usize, h2 - 1, 1)?;
let top_block = top_row.repeat(Shape::from_dims(&[1, 1, pad, 1]))?;
let bot_block = bot_row.repeat(Shape::from_dims(&[1, 1, pad, 1]))?;
top_block
.concat(&padded_w, 2_usize)?
.concat(&bot_block, 2_usize)
}
pub fn generate(
prior: &PriorModel,
diffnext: &DiffNextModel,
paella: &PaellaVqModel,
text_embed: &Tensor,
prior_steps: usize,
b_steps: usize,
prior_h: usize,
prior_w: usize,
b_h: usize,
b_w: usize,
) -> fuel_ir::error::Result<Tensor> {
let cfg = &prior.config;
let mut z_c = noise_on_graph(
text_embed,
Shape::from_dims(&[1, cfg.prior_c_in, prior_h, prior_w]),
0xC0DEC0DE,
)?;
for s in 0..=prior_steps {
let r = 1.0 - (s as f32 / (prior_steps as f32 + 1.0));
z_c = prior.forward(&z_c, r, text_embed, prior_h, prior_w)?;
}
let mut z_b = noise_on_graph(
text_embed,
Shape::from_dims(&[1, cfg.diffnext_c_in, b_h, b_w]),
0xB0DEB0DE,
)?;
for s in 0..=b_steps {
let r = 1.0 - (s as f32 / (b_steps as f32 + 1.0));
z_b = diffnext.forward(&z_b, r, text_embed, b_h, b_w)?;
}
let img = paella.decode(&z_b)?;
Ok(img)
}
fn noise_on_graph(anchor: &Tensor, shape: Shape, seed: u64) -> fuel_ir::error::Result<Tensor> {
let n = shape.elem_count();
let data = small_normal_vec(n, seed);
anchor.const_f32_like(data, shape)
}
fn small_normal_vec(n: usize, seed: u64) -> Vec<f32> {
let mut s = seed.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut out = Vec::with_capacity(n);
for _ in 0..n {
s = s
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let v = (((s >> 33) as u32 as f32) / (u32::MAX as f32) - 0.5) * 0.05;
out.push(v);
}
out
}
fn arc_zeros(n: usize) -> Arc<[f32]> {
Arc::from(vec![0.0_f32; n])
}
fn arc_ones(n: usize) -> Arc<[f32]> {
Arc::from(vec![1.0_f32; n])
}
fn small_normal(n: usize, seed: u64) -> Arc<[f32]> {
let mut s = seed.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut out = Vec::with_capacity(n);
for _ in 0..n {
s = s
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let v = (((s >> 33) as u32 as f32) / (u32::MAX as f32) - 0.5) * 0.05;
out.push(v);
}
Arc::from(out)
}
fn make_grn(c: usize) -> GrnWeights {
GrnWeights {
gamma: arc_ones(c),
beta: arc_zeros(c),
}
}
fn make_res_block(c: usize, c_skip: usize, ksize: usize, seed: u64) -> ResBlockWeights {
ResBlockWeights {
dw_w: small_normal(c * 1 * ksize * ksize, seed),
dw_b: arc_zeros(c),
fc1_w: small_normal((c + c_skip) * 4 * c, seed + 1),
fc1_b: arc_zeros(4 * c),
grn: make_grn(4 * c),
fc2_w: small_normal(4 * c * c, seed + 2),
fc2_b: arc_zeros(c),
c,
c_skip,
ksize,
}
}
fn make_ts_block(c: usize, c_timestep: usize, seed: u64) -> TimestepBlockWeights {
TimestepBlockWeights {
w: small_normal(c_timestep * 2 * c, seed),
b: arc_zeros(2 * c),
c,
c_timestep,
}
}
fn make_attn_block(c: usize, c_cond: usize, heads: usize, seed: u64) -> AttnBlockWeights {
AttnBlockWeights {
kv_mapper_w: small_normal(c_cond * c, seed),
kv_mapper_b: arc_zeros(c),
to_q_w: small_normal(c * c, seed + 1),
to_q_b: arc_zeros(c),
to_k_w: small_normal(c * c, seed + 2),
to_k_b: arc_zeros(c),
to_v_w: small_normal(c * c, seed + 3),
to_v_b: arc_zeros(c),
to_out_w: small_normal(c * c, seed + 4),
to_out_b: arc_zeros(c),
c,
c_cond,
heads,
}
}
pub fn make_prior_weights(cfg: &WuerstchenConfig) -> PriorWeights {
let c = cfg.prior_c;
let c_in = cfg.prior_c_in;
let c_cond = cfg.prior_c_cond;
let mut blocks = Vec::with_capacity(cfg.prior_depth);
for i in 0..cfg.prior_depth {
let base = 100 + (i as u64) * 100;
blocks.push(PriorBlockWeights {
res: make_res_block(c, 0, 3, base),
ts: make_ts_block(c, cfg.c_r, base + 10),
attn: make_attn_block(c, c, cfg.prior_nhead, base + 20),
});
}
PriorWeights {
projection_w: small_normal(c * c_in * 1 * 1, 1),
projection_b: arc_zeros(c),
cond1_w: small_normal(c_cond * c, 2),
cond1_b: arc_zeros(c),
cond2_w: small_normal(c * c, 3),
cond2_b: arc_zeros(c),
blocks,
out_conv_w: small_normal(c * c_in * 2, 4),
out_conv_b: arc_zeros(c_in * 2),
}
}
pub fn make_diffnext_weights(cfg: &WuerstchenConfig) -> DiffNextWeights {
let levels = &cfg.diffnext_c_hidden;
let c_in = cfg.diffnext_c_in;
let c_out = cfg.diffnext_c_out;
let c_cond = cfg.diffnext_c_cond;
let p = cfg.patch_size;
let mut down_levels = Vec::with_capacity(levels.len());
for (i, &c_lvl) in levels.iter().enumerate() {
let mut subs = Vec::with_capacity(cfg.diffnext_blocks[i]);
for j in 0..cfg.diffnext_blocks[i] {
let seed = 1000 + (i as u64) * 100 + (j as u64) * 10;
subs.push(DiffNextSubBlockWeights {
res: make_res_block(c_lvl, 0, 3, seed),
ts: make_ts_block(c_lvl, cfg.c_r, seed + 1),
attn: if cfg.diffnext_nhead[i] > 0 {
Some(make_attn_block(
c_lvl,
c_cond,
cfg.diffnext_nhead[i],
seed + 2,
))
} else {
None
},
});
}
let (down_w, down_b) = if i > 0 {
let prev = levels[i - 1];
(
Some(small_normal(c_lvl * prev * 4, 2000 + i as u64)),
Some(arc_zeros(c_lvl)),
)
} else {
(None, None)
};
down_levels.push(DiffNextLevelWeights {
down_w,
down_b,
up_w: None,
up_b: None,
subs,
});
}
let mut up_levels = Vec::with_capacity(levels.len());
for i in 0..levels.len() {
let lvl_idx = levels.len() - 1 - i;
let c_lvl = levels[lvl_idx];
let mut subs = Vec::with_capacity(cfg.diffnext_blocks[lvl_idx]);
for j in 0..cfg.diffnext_blocks[lvl_idx] {
let seed = 3000 + (i as u64) * 100 + (j as u64) * 10;
let c_skip = if j == 0 && i > 0 { c_lvl } else { 0 };
subs.push(DiffNextSubBlockWeights {
res: make_res_block(c_lvl, c_skip, 3, seed),
ts: make_ts_block(c_lvl, cfg.c_r, seed + 1),
attn: if cfg.diffnext_nhead[lvl_idx] > 0 {
Some(make_attn_block(
c_lvl,
c_cond,
cfg.diffnext_nhead[lvl_idx],
seed + 2,
))
} else {
None
},
});
}
let (up_w, up_b) = if lvl_idx > 0 {
let next = levels[lvl_idx - 1];
(
Some(small_normal(c_lvl * next * 4, 4000 + i as u64)),
Some(arc_zeros(next)),
)
} else {
(None, None)
};
up_levels.push(DiffNextLevelWeights {
down_w: None,
down_b: None,
up_w,
up_b,
subs,
});
}
DiffNextWeights {
clip_mapper_w: small_normal(cfg.clip_embed * c_cond, 10),
clip_mapper_b: arc_zeros(c_cond),
embed_w: small_normal(levels[0] * c_in * p * p, 11),
embed_b: arc_zeros(levels[0]),
down_levels,
up_levels,
clf_w: small_normal(2 * c_out * p * p * levels[0], 12),
clf_b: arc_zeros(2 * c_out * p * p),
}
}
pub fn make_paella_weights(cfg: &WuerstchenConfig) -> PaellaVqWeights {
let levels = &cfg.paella_levels;
let lc = cfg.paella_latent_channels;
let oc = cfg.paella_out_channels;
let mut up_levels = Vec::with_capacity(levels.len());
for (i, &c_lvl) in levels.iter().enumerate() {
let mut res_blocks = Vec::new();
let n_bottleneck = if i == 0 {
cfg.paella_bottleneck_blocks
} else {
1
};
for j in 0..n_bottleneck {
let seed = 5000 + (i as u64) * 100 + (j as u64) * 10;
let embed_dim = c_lvl * 4;
res_blocks.push(PaellaMixingResWeights {
gammas: [0.05, 0.0, 0.05, 0.05, 0.0, 0.05],
dw_w: small_normal(c_lvl * 1 * 3 * 3, seed),
dw_b: arc_zeros(c_lvl),
fc1_w: small_normal(c_lvl * embed_dim, seed + 1),
fc1_b: arc_zeros(embed_dim),
fc2_w: small_normal(embed_dim * c_lvl, seed + 2),
fc2_b: arc_zeros(c_lvl),
c: c_lvl,
});
}
let (upsample_w, upsample_b) = if i < levels.len() - 1 {
let next = levels[i + 1];
(
Some(small_normal(c_lvl * next * 16, 6000 + i as u64)),
Some(arc_zeros(next)),
)
} else {
(None, None)
};
up_levels.push(PaellaUpLevelWeights {
res_blocks,
upsample_w,
upsample_b,
});
}
let last_c = *levels.last().expect("wuerstchen: levels must not be empty");
PaellaVqWeights {
up_in_w: small_normal(levels[0] * lc * 1 * 1, 7000),
up_in_b: arc_zeros(levels[0]),
up_levels,
out_w: small_normal(oc * 4 * last_c, 7001),
out_b: arc_zeros(oc * 4),
}
}
fn load_arc_f32(
st: &fuel_loaders::safetensors::MmapedSafetensors,
name: &str,
expected: usize,
) -> fuel_ir::error::Result<Arc<[f32]>> {
use fuel_tensor::lazy::load_tensor_as_f32;
let v = load_tensor_as_f32(st, name)?;
if v.len() != expected {
return Err(fuel_ir::error::Error::Msg(format!(
"wuerstchen load: tensor {name:?} has {} elements, expected {expected}",
v.len(),
))
.bt());
}
Ok(Arc::from(v))
}
fn load_linear_f32(
st: &fuel_loaders::safetensors::MmapedSafetensors,
name: &str,
out_f: usize,
in_f: usize,
) -> fuel_ir::error::Result<Arc<[f32]>> {
use fuel_tensor::lazy::load_transposed_matrix;
Ok(Arc::from(load_transposed_matrix(st, name, out_f, in_f)?))
}
fn load_grn(
st: &fuel_loaders::safetensors::MmapedSafetensors,
prefix: &str,
c: usize,
) -> fuel_ir::error::Result<GrnWeights> {
Ok(GrnWeights {
gamma: load_arc_f32(st, &format!("{prefix}.gamma"), c)?,
beta: load_arc_f32(st, &format!("{prefix}.beta"), c)?,
})
}
fn load_res_block(
st: &fuel_loaders::safetensors::MmapedSafetensors,
prefix: &str,
c: usize,
c_skip: usize,
ksize: usize,
) -> fuel_ir::error::Result<ResBlockWeights> {
let dw_w = load_arc_f32(st, &format!("{prefix}.depthwise.weight"), c * ksize * ksize)?;
let dw_b = load_arc_f32(st, &format!("{prefix}.depthwise.bias"), c)?;
let fc1_w = load_linear_f32(
st,
&format!("{prefix}.channelwise.0.weight"),
4 * c,
c + c_skip,
)?;
let fc1_b = load_arc_f32(st, &format!("{prefix}.channelwise.0.bias"), 4 * c)?;
let grn = load_grn(st, &format!("{prefix}.channelwise.2"), 4 * c)?;
let fc2_w = load_linear_f32(st, &format!("{prefix}.channelwise.4.weight"), c, 4 * c)?;
let fc2_b = load_arc_f32(st, &format!("{prefix}.channelwise.4.bias"), c)?;
Ok(ResBlockWeights {
dw_w,
dw_b,
fc1_w,
fc1_b,
grn,
fc2_w,
fc2_b,
c,
c_skip,
ksize,
})
}
fn load_ts_block(
st: &fuel_loaders::safetensors::MmapedSafetensors,
prefix: &str,
c: usize,
c_timestep: usize,
) -> fuel_ir::error::Result<TimestepBlockWeights> {
let w = load_linear_f32(st, &format!("{prefix}.mapper.weight"), 2 * c, c_timestep)?;
let b = load_arc_f32(st, &format!("{prefix}.mapper.bias"), 2 * c)?;
Ok(TimestepBlockWeights {
w,
b,
c,
c_timestep,
})
}
fn load_attn_block(
st: &fuel_loaders::safetensors::MmapedSafetensors,
prefix: &str,
c: usize,
c_cond: usize,
heads: usize,
) -> fuel_ir::error::Result<AttnBlockWeights> {
let kv_mapper_w = load_linear_f32(st, &format!("{prefix}.kv_mapper.1.weight"), c, c_cond)?;
let kv_mapper_b = load_arc_f32(st, &format!("{prefix}.kv_mapper.1.bias"), c)?;
let to_q_w = load_linear_f32(st, &format!("{prefix}.attention.to_q.weight"), c, c)?;
let to_q_b = load_arc_f32(st, &format!("{prefix}.attention.to_q.bias"), c)?;
let to_k_w = load_linear_f32(st, &format!("{prefix}.attention.to_k.weight"), c, c)?;
let to_k_b = load_arc_f32(st, &format!("{prefix}.attention.to_k.bias"), c)?;
let to_v_w = load_linear_f32(st, &format!("{prefix}.attention.to_v.weight"), c, c)?;
let to_v_b = load_arc_f32(st, &format!("{prefix}.attention.to_v.bias"), c)?;
let to_out_w = load_linear_f32(st, &format!("{prefix}.attention.to_out.0.weight"), c, c)?;
let to_out_b = load_arc_f32(st, &format!("{prefix}.attention.to_out.0.bias"), c)?;
Ok(AttnBlockWeights {
kv_mapper_w,
kv_mapper_b,
to_q_w,
to_q_b,
to_k_w,
to_k_b,
to_v_w,
to_v_b,
to_out_w,
to_out_b,
c,
c_cond,
heads,
})
}
impl PriorWeights {
pub fn load_from_mmapped(
st: &fuel_loaders::safetensors::MmapedSafetensors,
cfg: &WuerstchenConfig,
) -> fuel_ir::error::Result<Self> {
let c = cfg.prior_c;
let c_in = cfg.prior_c_in;
let c_cond = cfg.prior_c_cond;
let projection_w = load_arc_f32(st, "projection.weight", c * c_in)?;
let projection_b = load_arc_f32(st, "projection.bias", c)?;
let cond1_w = load_linear_f32(st, "cond_mapper.0.weight", c, c_cond)?;
let cond1_b = load_arc_f32(st, "cond_mapper.0.bias", c)?;
let cond2_w = load_linear_f32(st, "cond_mapper.2.weight", c, c)?;
let cond2_b = load_arc_f32(st, "cond_mapper.2.bias", c)?;
let mut blocks = Vec::with_capacity(cfg.prior_depth);
for i in 0..cfg.prior_depth {
blocks.push(PriorBlockWeights {
res: load_res_block(st, &format!("blocks.{i}.0"), c, 0, 3)?,
ts: load_ts_block(st, &format!("blocks.{i}.1"), c, cfg.c_r)?,
attn: load_attn_block(st, &format!("blocks.{i}.2"), c, c, cfg.prior_nhead)?,
});
}
let out_conv_w = load_arc_f32(st, "out.0.weight", c_in * 2 * c)?;
let out_conv_b = load_arc_f32(st, "out.0.bias", c_in * 2)?;
Ok(PriorWeights {
projection_w,
projection_b,
cond1_w,
cond1_b,
cond2_w,
cond2_b,
blocks,
out_conv_w,
out_conv_b,
})
}
}
impl DiffNextWeights {
pub fn load_from_mmapped(
st: &fuel_loaders::safetensors::MmapedSafetensors,
cfg: &WuerstchenConfig,
) -> fuel_ir::error::Result<Self> {
let levels = &cfg.diffnext_c_hidden;
let c_in = cfg.diffnext_c_in;
let c_out = cfg.diffnext_c_out;
let c_cond = cfg.diffnext_c_cond;
let p = cfg.patch_size;
if levels.is_empty() {
return Err(fuel_ir::error::Error::Msg(
"DiffNextWeights.load_from_mmapped: diffnext_c_hidden empty".into(),
)
.bt());
}
let clip_mapper_w = load_linear_f32(st, "clip_mapper.weight", c_cond, cfg.clip_embed)?;
let clip_mapper_b = load_arc_f32(st, "clip_mapper.bias", c_cond)?;
let embed_w = load_arc_f32(st, "embedding.1.weight", levels[0] * c_in * p * p)?;
let embed_b = load_arc_f32(st, "embedding.1.bias", levels[0])?;
let mut down_levels = Vec::with_capacity(levels.len());
for (i, &c_lvl) in levels.iter().enumerate() {
let (down_w, down_b) = if i > 0 {
let prev = levels[i - 1];
let w = load_arc_f32(st, &format!("down_blocks.{i}.0.1.weight"), c_lvl * prev * 4)?;
let b = load_arc_f32(st, &format!("down_blocks.{i}.0.1.bias"), c_lvl)?;
(Some(w), Some(b))
} else {
(None, None)
};
let sub_offset = if i > 0 { 1 } else { 0 };
let mut subs = Vec::with_capacity(cfg.diffnext_blocks[i]);
for j in 0..cfg.diffnext_blocks[i] {
let sub_idx = sub_offset + j;
let sub_pfx = format!("down_blocks.{i}.{sub_idx}");
subs.push(DiffNextSubBlockWeights {
res: load_res_block(st, &format!("{sub_pfx}.0"), c_lvl, 0, 3)?,
ts: load_ts_block(st, &format!("{sub_pfx}.1"), c_lvl, cfg.c_r)?,
attn: if cfg.diffnext_nhead[i] > 0 {
Some(load_attn_block(
st,
&format!("{sub_pfx}.2"),
c_lvl,
c_cond,
cfg.diffnext_nhead[i],
)?)
} else {
None
},
});
}
down_levels.push(DiffNextLevelWeights {
down_w,
down_b,
up_w: None,
up_b: None,
subs,
});
}
let mut up_levels = Vec::with_capacity(levels.len());
for i in 0..levels.len() {
let lvl_idx = levels.len() - 1 - i;
let c_lvl = levels[lvl_idx];
let mut subs = Vec::with_capacity(cfg.diffnext_blocks[lvl_idx]);
for j in 0..cfg.diffnext_blocks[lvl_idx] {
let sub_pfx = format!("up_blocks.{i}.{j}");
let c_skip = if j == 0 && i > 0 { c_lvl } else { 0 };
subs.push(DiffNextSubBlockWeights {
res: load_res_block(st, &format!("{sub_pfx}.0"), c_lvl, c_skip, 3)?,
ts: load_ts_block(st, &format!("{sub_pfx}.1"), c_lvl, cfg.c_r)?,
attn: if cfg.diffnext_nhead[lvl_idx] > 0 {
Some(load_attn_block(
st,
&format!("{sub_pfx}.2"),
c_lvl,
c_cond,
cfg.diffnext_nhead[lvl_idx],
)?)
} else {
None
},
});
}
let (up_w, up_b) = if lvl_idx > 0 {
let next = levels[lvl_idx - 1];
let after_subs = cfg.diffnext_blocks[lvl_idx];
let w = load_arc_f32(
st,
&format!("up_blocks.{i}.{after_subs}.1.weight"),
c_lvl * next * 4,
)?;
let b = load_arc_f32(st, &format!("up_blocks.{i}.{after_subs}.1.bias"), next)?;
(Some(w), Some(b))
} else {
(None, None)
};
up_levels.push(DiffNextLevelWeights {
down_w: None,
down_b: None,
up_w,
up_b,
subs,
});
}
let clf_w = load_arc_f32(st, "clf.1.weight", 2 * c_out * p * p * levels[0])?;
let clf_b = load_arc_f32(st, "clf.1.bias", 2 * c_out * p * p)?;
Ok(DiffNextWeights {
clip_mapper_w,
clip_mapper_b,
embed_w,
embed_b,
down_levels,
up_levels,
clf_w,
clf_b,
})
}
}
fn load_paella_mixing_res(
st: &fuel_loaders::safetensors::MmapedSafetensors,
prefix: &str,
c: usize,
) -> fuel_ir::error::Result<PaellaMixingResWeights> {
let embed_dim = 4 * c;
let gammas_vec = load_arc_f32(st, &format!("{prefix}.gammas"), 6)?;
let gammas = [
gammas_vec[0],
gammas_vec[1],
gammas_vec[2],
gammas_vec[3],
gammas_vec[4],
gammas_vec[5],
];
Ok(PaellaMixingResWeights {
gammas,
dw_w: load_arc_f32(st, &format!("{prefix}.depthwise.1.weight"), c * 9)?,
dw_b: load_arc_f32(st, &format!("{prefix}.depthwise.1.bias"), c)?,
fc1_w: load_linear_f32(st, &format!("{prefix}.channelwise.0.weight"), embed_dim, c)?,
fc1_b: load_arc_f32(st, &format!("{prefix}.channelwise.0.bias"), embed_dim)?,
fc2_w: load_linear_f32(st, &format!("{prefix}.channelwise.2.weight"), c, embed_dim)?,
fc2_b: load_arc_f32(st, &format!("{prefix}.channelwise.2.bias"), c)?,
c,
})
}
impl PaellaVqWeights {
pub fn load_from_mmapped(
st: &fuel_loaders::safetensors::MmapedSafetensors,
cfg: &WuerstchenConfig,
) -> fuel_ir::error::Result<Self> {
let levels = &cfg.paella_levels;
let lc = cfg.paella_latent_channels;
let oc = cfg.paella_out_channels;
if levels.is_empty() {
return Err(fuel_ir::error::Error::Msg(
"PaellaVqWeights.load_from_mmapped: paella_levels empty".into(),
)
.bt());
}
let up_in_w = load_arc_f32(st, "up_blocks.0.0.0.weight", levels[0] * lc)?;
let up_in_b = load_arc_f32(st, "up_blocks.0.0.0.bias", levels[0])?;
let mut up_levels = Vec::with_capacity(levels.len());
for (i, &c_lvl) in levels.iter().enumerate() {
let mut res_blocks = Vec::new();
let n_res = if i == 0 {
cfg.paella_bottleneck_blocks
} else {
1
};
for j in 0..n_res {
let pfx = format!("up_blocks.{i}.{j}.1");
res_blocks.push(load_paella_mixing_res(st, &pfx, c_lvl)?);
}
let (upsample_w, upsample_b) = if i < levels.len() - 1 {
let next = levels[i + 1];
let w_off = n_res; let w = load_arc_f32(
st,
&format!("up_blocks.{i}.{w_off}.0.weight"),
c_lvl * next * 16,
)?;
let b = load_arc_f32(st, &format!("up_blocks.{i}.{w_off}.0.bias"), next)?;
(Some(w), Some(b))
} else {
(None, None)
};
up_levels.push(PaellaUpLevelWeights {
res_blocks,
upsample_w,
upsample_b,
});
}
let last_c = *levels.last().ok_or_else(|| {
fuel_ir::Error::Msg("wuerstchen: levels must not be empty".to_string())
})?;
let out_w = load_arc_f32(st, "out_block.1.weight", oc * 4 * last_c)?;
let out_b = load_arc_f32(st, "out_block.1.bias", oc * 4)?;
Ok(PaellaVqWeights {
up_in_w,
up_in_b,
up_levels,
out_w,
out_b,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn dev() -> fuel_tensor::Device {
fuel_tensor::Device::cpu()
}
#[test]
fn global_response_norm_hand_computed() {
let x_data = vec![1.0_f32, 0.0, 0.0, 0.0]; let x = Tensor::from_f32(x_data.clone(), Shape::from_dims(&[1, 1, 2, 2]), &dev()).unwrap();
let gamma = arc_ones(1);
let beta = arc_zeros(1);
let out = global_response_norm(&x, &gamma, &beta, 1, 2, 2)
.unwrap()
.realize_f32();
let expected: Vec<f32> = x_data.iter().map(|&v| 2.0 * v).collect();
for (i, (a, e)) in out.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 1e-4, "GRN[{i}]: expected {e}, got {a}");
}
}
#[test]
fn paella_vq_decoder_shape() {
let cfg = WuerstchenConfig::tiny();
let weights = make_paella_weights(&cfg);
let model = PaellaVqModel {
config: cfg.clone(),
weights,
};
let lat_data = vec![0.01_f32; 1 * 4 * 8 * 8];
let lat = Tensor::from_f32(lat_data, Shape::from_dims(&[1, 4, 8, 8]), &dev()).unwrap();
let img = model.decode(&lat).unwrap();
assert_eq!(img.shape().dims(), &[1, 3, 32, 32]);
let flat = img.realize_f32();
for v in &flat {
assert!(v.is_finite(), "non-finite paella decoder pixel: {v}");
}
for v in &flat {
assert!(v.abs() <= 1.0 + 1e-5, "tanh out-of-range: {v}");
}
}
#[test]
fn prior_forward_shape_finite_tiny() {
let cfg = WuerstchenConfig::tiny();
let weights = make_prior_weights(&cfg);
let model = PriorModel {
config: cfg.clone(),
weights,
};
let xs_data = vec![0.01_f32; 1 * cfg.prior_c_in * 2 * 2];
let xs = Tensor::from_f32(
xs_data,
Shape::from_dims(&[1, cfg.prior_c_in, 2, 2]),
&dev(),
)
.unwrap();
let txt_data = vec![0.01_f32; 1 * 4 * cfg.prior_c_cond];
let txt = xs
.const_f32_like(txt_data, Shape::from_dims(&[1, 4, cfg.prior_c_cond]))
.unwrap();
let out = model.forward(&xs, 0.5, &txt, 2, 2).unwrap();
assert_eq!(out.shape().dims(), &[1, cfg.prior_c_in, 2, 2]);
for v in &out.realize_f32() {
assert!(v.is_finite(), "non-finite prior output: {v}");
}
}
#[test]
fn diffnext_forward_shape_finite_tiny() {
let cfg = WuerstchenConfig::tiny();
let weights = make_diffnext_weights(&cfg);
let model = DiffNextModel {
config: cfg.clone(),
weights,
};
let h = 4;
let w = 4;
let xs_data = vec![0.01_f32; 1 * cfg.diffnext_c_in * h * w];
let xs = Tensor::from_f32(
xs_data,
Shape::from_dims(&[1, cfg.diffnext_c_in, h, w]),
&dev(),
)
.unwrap();
let txt_data = vec![0.01_f32; 1 * 4 * cfg.clip_embed];
let txt = xs
.const_f32_like(txt_data, Shape::from_dims(&[1, 4, cfg.clip_embed]))
.unwrap();
let out = model.forward(&xs, 0.5, &txt, h, w).unwrap();
assert_eq!(out.shape().dims(), &[1, cfg.diffnext_c_out, h, w]);
for v in &out.realize_f32() {
assert!(v.is_finite(), "non-finite diffnext output: {v}");
}
}
fn write_tmp_safetensors_w(tensors: &[(String, Vec<usize>, Vec<f32>)]) -> std::path::PathBuf {
use safetensors::tensor::TensorView;
use std::collections::HashMap;
let bytes_store: Vec<Vec<u8>> = tensors
.iter()
.map(|(_, _, data)| data.iter().flat_map(|f| f.to_le_bytes()).collect())
.collect();
let views: HashMap<String, TensorView<'_>> = tensors
.iter()
.zip(bytes_store.iter())
.map(|((name, shape, _), bytes)| {
let v = TensorView::new(safetensors::Dtype::F32, shape.clone(), bytes)
.expect("TensorView::new");
(name.clone(), v)
})
.collect();
let metadata: Option<HashMap<String, String>> = None;
let bytes_out = safetensors::serialize(&views, metadata).unwrap();
let path = std::env::temp_dir().join(format!(
"fuel_lazy_wuerst_test_{}_{}.safetensors",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos(),
std::process::id(),
));
std::fs::write(&path, bytes_out).unwrap();
path
}
#[test]
#[allow(clippy::vec_init_then_push)]
fn load_prior_weights_from_mmapped_tiny() {
let cfg = WuerstchenConfig::tiny();
let c = cfg.prior_c;
let c_in = cfg.prior_c_in;
let c_cond = cfg.prior_c_cond;
let mut t: Vec<(String, Vec<usize>, Vec<f32>)> = Vec::new();
t.push((
"projection.weight".into(),
vec![c, c_in, 1, 1],
vec![0.0; c * c_in],
));
t.push(("projection.bias".into(), vec![c], vec![0.0; c]));
t.push((
"cond_mapper.0.weight".into(),
vec![c, c_cond],
vec![0.0; c * c_cond],
));
t.push(("cond_mapper.0.bias".into(), vec![c], vec![0.0; c]));
t.push(("cond_mapper.2.weight".into(), vec![c, c], vec![0.0; c * c]));
t.push(("cond_mapper.2.bias".into(), vec![c], vec![0.0; c]));
let pfx = "blocks.0";
t.push((
format!("{pfx}.0.depthwise.weight"),
vec![c, 1, 3, 3],
vec![0.0; c * 9],
));
t.push((format!("{pfx}.0.depthwise.bias"), vec![c], vec![0.0; c]));
t.push((
format!("{pfx}.0.channelwise.0.weight"),
vec![4 * c, c],
vec![0.0; 4 * c * c],
));
t.push((
format!("{pfx}.0.channelwise.0.bias"),
vec![4 * c],
vec![0.0; 4 * c],
));
t.push((
format!("{pfx}.0.channelwise.2.gamma"),
vec![4 * c],
vec![1.0; 4 * c],
));
t.push((
format!("{pfx}.0.channelwise.2.beta"),
vec![4 * c],
vec![0.0; 4 * c],
));
t.push((
format!("{pfx}.0.channelwise.4.weight"),
vec![c, 4 * c],
vec![0.0; 4 * c * c],
));
t.push((format!("{pfx}.0.channelwise.4.bias"), vec![c], vec![0.0; c]));
t.push((
format!("{pfx}.1.mapper.weight"),
vec![2 * c, cfg.c_r],
vec![0.0; 2 * c * cfg.c_r],
));
t.push((
format!("{pfx}.1.mapper.bias"),
vec![2 * c],
vec![0.0; 2 * c],
));
t.push((
format!("{pfx}.2.kv_mapper.1.weight"),
vec![c, c],
vec![0.0; c * c],
));
t.push((format!("{pfx}.2.kv_mapper.1.bias"), vec![c], vec![0.0; c]));
for kind in ["q", "k", "v"] {
t.push((
format!("{pfx}.2.attention.to_{kind}.weight"),
vec![c, c],
vec![0.0; c * c],
));
t.push((
format!("{pfx}.2.attention.to_{kind}.bias"),
vec![c],
vec![0.0; c],
));
}
t.push((
format!("{pfx}.2.attention.to_out.0.weight"),
vec![c, c],
vec![0.0; c * c],
));
t.push((
format!("{pfx}.2.attention.to_out.0.bias"),
vec![c],
vec![0.0; c],
));
t.push((
"out.0.weight".into(),
vec![c_in * 2, c, 1, 1],
vec![0.0; c_in * 2 * c],
));
t.push(("out.0.bias".into(), vec![c_in * 2], vec![0.0; c_in * 2]));
let path = write_tmp_safetensors_w(&t);
let st = unsafe { fuel_loaders::safetensors::MmapedSafetensors::new(&path).unwrap() };
let w = PriorWeights::load_from_mmapped(&st, &cfg).unwrap();
assert_eq!(w.blocks.len(), cfg.prior_depth);
assert_eq!(w.projection_w.len(), c * c_in);
let _ = std::fs::remove_file(&path);
}
#[test]
fn end_to_end_generate_tiny() {
let cfg = WuerstchenConfig::tiny();
let prior = PriorModel {
config: cfg.clone(),
weights: make_prior_weights(&cfg),
};
let diffnext = DiffNextModel {
config: cfg.clone(),
weights: make_diffnext_weights(&cfg),
};
let paella = PaellaVqModel {
config: cfg.clone(),
weights: make_paella_weights(&cfg),
};
let txt_data = vec![0.01_f32; 1 * 4 * cfg.clip_embed];
let txt =
Tensor::from_f32(txt_data, Shape::from_dims(&[1, 4, cfg.clip_embed]), &dev()).unwrap();
let img = generate(&prior, &diffnext, &paella, &txt, 0, 0, 2, 2, 8, 8).unwrap();
assert_eq!(img.shape().dims(), &[1, 3, 32, 32]);
let flat = img.realize_f32();
for v in &flat {
assert!(v.is_finite(), "non-finite generated pixel: {v}");
assert!(v.abs() <= 1.0 + 1e-5, "generated pixel out of [-1,1]: {v}");
}
}
}