use crate::op::Activation;
use crate::quant::{QuantScheme, ScaleLayout, ScaledFormat};
use crate::{DType, Graph, NodeId, Op, Shape};
impl Graph {
pub fn matmul(&mut self, lhs: NodeId, rhs: NodeId, out_shape: Shape) -> NodeId {
self.push(Op::MatMul, vec![lhs, rhs], out_shape, None)
}
pub fn scaled_quantize(
&mut self,
x: NodeId,
fmt: ScaledFormat,
layout: ScaleLayout,
) -> (NodeId, NodeId) {
let xs = self.node(x).shape.clone();
let cols = xs.dim(xs.rank() - 1).unwrap_static();
let rows = xs.num_elements().unwrap() / cols.max(1);
let scale_shape = match layout {
ScaleLayout::PerTensor => Shape::new(&[1], layout.scale_dtype()),
_ => Shape::new(
&[rows, cols.div_ceil(layout.block() as usize)],
layout.scale_dtype(),
),
};
let scale = self.push(
Op::ScaledQuantScale {
format: fmt,
scale_layout: layout,
},
vec![x],
scale_shape,
None,
);
let codes = self.push(
Op::ScaledQuantize {
format: fmt,
scale_layout: layout,
},
vec![x, scale],
xs.with_dtype(DType::U8),
None,
);
(codes, scale)
}
pub fn scaled_dequantize(
&mut self,
codes: NodeId,
scale: NodeId,
fmt: ScaledFormat,
layout: ScaleLayout,
) -> NodeId {
let shape = self.node(codes).shape.clone().with_dtype(DType::F32);
self.push(
Op::ScaledDequantize {
format: fmt,
scale_layout: layout,
},
vec![codes, scale],
shape,
None,
)
}
pub fn scaled_matmul(
&mut self,
lhs: NodeId,
rhs: NodeId,
fmt: ScaledFormat,
layout: ScaleLayout,
) -> NodeId {
self.scaled_matmul_bias(lhs, rhs, None, fmt, layout)
}
pub fn scaled_matmul_bias(
&mut self,
lhs: NodeId,
rhs: NodeId,
bias: Option<NodeId>,
fmt: ScaledFormat,
layout: ScaleLayout,
) -> NodeId {
let m = self.node(lhs).shape.dim(0).unwrap_static();
let n = self.node(rhs).shape.dim(0).unwrap_static();
let (lq, ls) = self.scaled_quantize(lhs, fmt, layout);
let (rq, rs) = self.scaled_quantize(rhs, fmt, layout);
let mut inputs = vec![lq, rq, ls, rs];
if let Some(b) = bias {
inputs.push(b);
}
self.push(
Op::ScaledMatMul {
lhs_format: fmt,
rhs_format: fmt,
scale_layout: layout,
has_bias: bias.is_some(),
},
inputs,
Shape::new(&[m, n], DType::F32),
None,
)
}
pub fn dense_solve(&mut self, a: NodeId, b: NodeId, out_shape: Shape) -> NodeId {
self.push(Op::DenseSolve, vec![a, b], out_shape, None)
}
pub fn batched_dense_solve(&mut self, a: NodeId, b: NodeId, out_shape: Shape) -> NodeId {
self.push(Op::BatchedDenseSolve, vec![a, b], out_shape, None)
}
pub fn lora_matmul(
&mut self,
x: NodeId,
w: NodeId,
a: NodeId,
b: NodeId,
scale: f32,
shape: Shape,
) -> NodeId {
self.push(Op::LoraMatMul { scale }, vec![x, w, a, b], shape, None)
}
pub fn dequant_matmul(
&mut self,
x: NodeId,
w_q: NodeId,
scale: NodeId,
zp: NodeId,
scheme: QuantScheme,
shape: Shape,
) -> NodeId {
self.push(
Op::DequantMatMul { scheme },
vec![x, w_q, scale, zp],
shape,
None,
)
}
pub fn dequant_matmul_packed(
&mut self,
x: NodeId,
packed_w: NodeId,
scheme: QuantScheme,
shape: Shape,
) -> NodeId {
debug_assert!(
scheme.is_gguf(),
"dequant_matmul_packed requires a GGUF QuantScheme"
);
self.push(Op::DequantMatMul { scheme }, vec![x, packed_w], shape, None)
}
pub fn dequant_matmul_nvfp4(
&mut self,
x: NodeId,
w_q: NodeId,
block_scales: NodeId,
global_scale: NodeId,
shape: Shape,
) -> NodeId {
self.dequant_matmul(
x,
w_q,
block_scales,
global_scale,
QuantScheme::Nvfp4Block,
shape,
)
}
pub fn fused_matmul_bias_act(
&mut self,
input: NodeId,
weight: NodeId,
bias: NodeId,
activation: Option<Activation>,
shape: Shape,
) -> NodeId {
self.push(
Op::FusedMatMulBiasAct { activation },
vec![input, weight, bias],
shape,
None,
)
}
pub fn q_matmul(
&mut self,
x: NodeId,
w: NodeId,
bias: NodeId,
x_zp: i32,
w_zp: i32,
out_zp: i32,
mult: f32,
out_shape: Shape,
) -> NodeId {
debug_assert_eq!(
out_shape.dtype(),
crate::DType::I8,
"q_matmul output dtype must be I8"
);
self.push(
Op::QMatMul {
x_zp,
w_zp,
out_zp,
mult,
},
vec![x, w, bias],
out_shape,
None,
)
}
#[allow(clippy::too_many_arguments)]
pub fn q_conv2d(
&mut self,
x: NodeId,
w: NodeId,
bias: NodeId,
kernel_size: Vec<usize>,
stride: Vec<usize>,
padding: Vec<usize>,
dilation: Vec<usize>,
groups: usize,
x_zp: i32,
w_zp: i32,
out_zp: i32,
mult: f32,
out_shape: Shape,
) -> NodeId {
debug_assert_eq!(
out_shape.dtype(),
crate::DType::I8,
"q_conv2d output dtype must be I8"
);
self.push(
Op::QConv2d {
kernel_size,
stride,
padding,
dilation,
groups,
x_zp,
w_zp,
out_zp,
mult,
},
vec![x, w, bias],
out_shape,
None,
)
}
}