#![allow(unused_imports)]
use rlx_ir::infer::GraphExt;
use rlx_ir::op::{AttentionBwdWrt, CmpOp, MaskKind, SteKind};
use rlx_ir::shape;
use rlx_ir::shape::Dim;
use rlx_ir::{DType, Graph, NodeId, Op, Shape};
use super::*;
pub fn compose_layer_norm_backward_input(
g: &mut Graph,
x: NodeId,
gamma: NodeId,
dy: NodeId,
axis: i32,
eps: f32,
out_shape: &Shape,
) -> NodeId {
assert_eq!(axis, -1, "compose_layer_norm_backward_input: only axis=-1");
let rank = out_shape.rank();
let ax = axis_pos(axis, rank);
let axes = vec![ax];
let ones = broadcast_eps(g, 1.0, out_shape);
let x = g.mul(x, ones);
let dy = g.mul(dy, ones);
let mean = g.mean(x, axes.clone(), true);
let mean_b = broadcast_scalar(g, mean, out_shape);
let xc = g.sub(x, mean_b);
let xc2 = g.mul(xc, xc);
let var = g.mean(xc2, axes.clone(), true);
let var_shape = g.node(var).shape.clone();
let eps_b = broadcast_eps(g, eps, &var_shape);
let var_eps = g.add(var, eps_b);
let inv_std = g.add_node(
Op::Activation(rlx_ir::op::Activation::Rsqrt),
vec![var_eps],
g.node(var_eps).shape.clone(),
);
let inv_std_b = broadcast_scalar(g, inv_std, out_shape);
let x_hat = g.mul(xc, inv_std_b);
let g_b = broadcast_scalar(g, gamma, out_shape);
let sy = g.mul(dy, g_b);
let m_sy = g.mean(sy, axes.clone(), true);
let m_sy_b = broadcast_scalar(g, m_sy, out_shape);
let sy_xh = g.mul(sy, x_hat);
let m_sxh = g.mean(sy_xh, axes, true);
let m_sxh_b = broadcast_scalar(g, m_sxh, out_shape);
let t1 = g.sub(sy, m_sy_b);
let t2 = g.mul(x_hat, m_sxh_b);
let t3 = g.sub(t1, t2);
g.mul(inv_std_b, t3)
}
pub fn compose_rms_norm_backward_input(
g: &mut Graph,
x: NodeId,
gamma: NodeId,
_beta: NodeId,
dy: NodeId,
axis: i32,
eps: f32,
out_shape: &Shape,
) -> NodeId {
assert_eq!(axis, -1, "compose_rms_norm_backward_input: only axis=-1");
let rank = out_shape.rank();
let ax = axis_pos(axis, rank);
let axes = vec![ax];
let ones = broadcast_eps(g, 1.0, out_shape);
let x = g.mul(x, ones);
let dy = g.mul(dy, ones);
let x2 = g.mul(x, x);
let mean_x2 = g.mean(x2, axes.clone(), true);
let mean_x2_shape = g.node(mean_x2).shape.clone();
let eps_b = broadcast_eps(g, eps, &mean_x2_shape);
let var_eps = g.add(mean_x2, eps_b);
let inv_r = g.add_node(
Op::Activation(rlx_ir::op::Activation::Rsqrt),
vec![var_eps],
g.node(var_eps).shape.clone(),
);
let inv_r2 = g.mul(inv_r, inv_r);
let inv_r_b = broadcast_scalar(g, inv_r, out_shape);
let inv_r2_full = broadcast_scalar(g, inv_r2, out_shape);
let g_b = broadcast_scalar(g, gamma, out_shape);
let dy_g = g.mul(dy, g_b);
let x_dy_g = g.mul(x, dy_g);
let dot = g.mean(x_dy_g, axes, true);
let dot_b = broadcast_scalar(g, dot, out_shape);
let term1 = g.mul(g_b, dy);
let x_dot = g.mul(x, dot_b);
let term2 = g.mul(x_dot, inv_r2_full);
let diff = g.sub(term1, term2);
g.mul(diff, inv_r_b)
}
pub fn compose_layer_norm_backward_gamma(
g: &mut Graph,
x: NodeId,
dy: NodeId,
axis: i32,
eps: f32,
gamma_shape: &Shape,
) -> NodeId {
assert_eq!(axis, -1, "compose_layer_norm_backward_gamma: only axis=-1");
let x_shape = g.node(x).shape.clone();
let rank = x_shape.rank();
let ax = axis_pos(axis, rank);
let axes = vec![ax];
let mean = g.mean(x, axes.clone(), true);
let xc = g.sub(x, mean);
let xc2 = g.mul(xc, xc);
let var = g.mean(xc2, axes.clone(), true);
let var_shape = g.node(var).shape.clone();
let eps_b = broadcast_eps(g, eps, &var_shape);
let var_eps = g.add(var, eps_b);
let inv_std = g.add_node(
Op::Activation(rlx_ir::op::Activation::Rsqrt),
vec![var_eps],
var_shape,
);
let x_hat = g.mul(xc, inv_std);
let prod = g.mul(dy, x_hat);
g.reduce(
prod,
rlx_ir::op::ReduceOp::Sum,
batch_reduce_axes(rank, ax),
false,
gamma_shape.clone(),
)
}
pub fn compose_layer_norm_backward_beta(g: &mut Graph, dy: NodeId, beta_shape: &Shape) -> NodeId {
let dy_shape = g.node(dy).shape.clone();
let rank = dy_shape.rank();
let ax = rank.saturating_sub(1);
g.reduce(
dy,
rlx_ir::op::ReduceOp::Sum,
batch_reduce_axes(rank, ax),
false,
beta_shape.clone(),
)
}
pub fn compose_rms_norm_backward_gamma(
g: &mut Graph,
x: NodeId,
dy: NodeId,
axis: i32,
eps: f32,
gamma_shape: &Shape,
) -> NodeId {
assert_eq!(axis, -1, "compose_rms_norm_backward_gamma: only axis=-1");
let x_shape = g.node(x).shape.clone();
let rank = x_shape.rank();
let ax = axis_pos(axis, rank);
let axes = vec![ax];
let x2 = g.mul(x, x);
let mean_x2 = g.mean(x2, axes.clone(), true);
let mean_x2_shape = g.node(mean_x2).shape.clone();
let eps_b = broadcast_eps(g, eps, &mean_x2_shape);
let mean_eps = g.add(mean_x2, eps_b);
let inv_r = g.add_node(
Op::Activation(rlx_ir::op::Activation::Rsqrt),
vec![mean_eps],
mean_x2_shape,
);
let x_scaled = g.mul(x, inv_r);
let prod = g.mul(dy, x_scaled);
g.reduce(
prod,
rlx_ir::op::ReduceOp::Sum,
batch_reduce_axes(rank, ax),
false,
gamma_shape.clone(),
)
}
pub fn compose_rms_norm_backward_beta(g: &mut Graph, dy: NodeId, beta_shape: &Shape) -> NodeId {
compose_layer_norm_backward_beta(g, dy, beta_shape)
}
pub fn compose_group_norm_backward_input(
g: &mut Graph,
x: NodeId,
gamma: NodeId,
_beta: NodeId,
dy: NodeId,
num_groups: usize,
eps: f32,
out_shape: &Shape,
) -> NodeId {
let [n, c, h, w] = static_dim4(out_shape).expect("static NCHW out");
let cpg = c / num_groups;
let ones = broadcast_eps(g, 1.0, out_shape);
let x = g.mul(x, ones);
let dy = g.mul(dy, ones);
let mut dx_groups: Vec<NodeId> = Vec::with_capacity(num_groups);
for gi in 0..num_groups {
let c0 = gi * cpg;
let x_g = g.narrow_(x, 1, c0, cpg);
let dy_g = g.narrow_(dy, 1, c0, cpg);
let gamma_g = g.narrow_(gamma, 0, c0, cpg);
let g_shape = g.node(x_g).shape.clone();
let gamma_r = g.reshape_(gamma_g, vec![1, cpg as i64, 1, 1]);
let gamma_b = broadcast_scalar(g, gamma_r, &g_shape);
let elems = cpg * h * w;
let flat_shape = Shape::new(&[n, elems], g_shape.dtype());
let flat_x = g.reshape_(x_g, vec![n as i64, elems as i64]);
let flat_dy = g.reshape_(dy_g, vec![n as i64, elems as i64]);
let flat_gamma = g.reshape_(gamma_b, vec![n as i64, elems as i64]);
let mean = g.mean(flat_x, vec![1], true);
let mean_b = broadcast_scalar(g, mean, &flat_shape);
let xc = g.sub(flat_x, mean_b);
let xc2 = g.mul(xc, xc);
let var = g.mean(xc2, vec![1], true);
let var_shape = g.node(var).shape.clone();
let eps_b = broadcast_eps(g, eps, &var_shape);
let var_eps = g.add(var, eps_b);
let inv_std = g.add_node(
Op::Activation(rlx_ir::op::Activation::Rsqrt),
vec![var_eps],
var_shape,
);
let inv_std_b = broadcast_scalar(g, inv_std, &flat_shape);
let x_hat = g.mul(xc, inv_std_b);
let sy = g.mul(flat_dy, flat_gamma);
let mean_sy = g.mean(sy, vec![1], true);
let mean_sy_b = broadcast_scalar(g, mean_sy, &flat_shape);
let sy_xhat = g.mul(sy, x_hat);
let mean_sy_xhat = g.mean(sy_xhat, vec![1], true);
let mean_sy_xhat_b = broadcast_scalar(g, mean_sy_xhat, &flat_shape);
let t1 = g.sub(sy, mean_sy_b);
let t2 = g.mul(x_hat, mean_sy_xhat_b);
let term = g.sub(t1, t2);
let flat_dx = g.mul(term, inv_std_b);
dx_groups.push(g.reshape_(flat_dx, vec![n as i64, cpg as i64, h as i64, w as i64]));
}
g.concat_(dx_groups, 1)
}
pub fn compose_group_norm_backward_gamma(
g: &mut Graph,
x: NodeId,
dy: NodeId,
num_groups: usize,
eps: f32,
gamma_shape: &Shape,
) -> NodeId {
let x_shape = g.node(x).shape.clone();
let [n, c, h, w] = static_dim4(&x_shape).expect("static NCHW x");
let cpg = c / num_groups;
let dt = gamma_shape.dtype();
let ones = broadcast_eps(g, 1.0, &x_shape);
let x = g.mul(x, ones);
let dy = g.mul(dy, ones);
let mut dgamma: Vec<NodeId> = Vec::with_capacity(c);
for gi in 0..num_groups {
let c0 = gi * cpg;
let x_g = g.narrow_(x, 1, c0, cpg);
let dy_g = g.narrow_(dy, 1, c0, cpg);
let elems = cpg * h * w;
let flat_shape = Shape::new(&[n, elems], dt);
let flat_x = g.reshape_(x_g, vec![n as i64, elems as i64]);
let flat_dy = g.reshape_(dy_g, vec![n as i64, elems as i64]);
let mean = g.mean(flat_x, vec![1], true);
let mean_b = broadcast_scalar(g, mean, &flat_shape);
let xc = g.sub(flat_x, mean_b);
let xc2 = g.mul(xc, xc);
let var = g.mean(xc2, vec![1], true);
let var_shape = g.node(var).shape.clone();
let eps_b = broadcast_eps(g, eps, &var_shape);
let var_eps = g.add(var, eps_b);
let inv_std = g.add_node(
Op::Activation(rlx_ir::op::Activation::Rsqrt),
vec![var_eps],
var_shape,
);
let inv_std_b = broadcast_scalar(g, inv_std, &flat_shape);
let x_hat = g.mul(xc, inv_std_b);
let prod = g.mul(flat_dy, x_hat);
let prod_g = g.reshape_(prod, vec![cpg as i64, (n * h * w) as i64]);
let summed_g = g.reduce(
prod_g,
rlx_ir::op::ReduceOp::Sum,
vec![1],
false,
Shape::new(&[cpg], dt),
);
dgamma.push(summed_g);
}
g.concat_(dgamma, 0)
}
pub fn compose_group_norm_backward_beta(g: &mut Graph, dy: NodeId, beta_shape: &Shape) -> NodeId {
let dy_shape = g.node(dy).shape.clone();
let [n, c, h, w] = static_dim4(&dy_shape).expect("static NCHW dy");
let flat = g.reshape_(dy, vec![c as i64, (n * h * w) as i64]);
g.reduce(
flat,
rlx_ir::op::ReduceOp::Sum,
vec![1],
false,
beta_shape.clone(),
)
}