use lift_core::context::Context;
use lift_core::pass::{AnalysisCache, Pass, PassResult};
#[derive(Debug)]
pub struct QuantisationPass {
pub target_dtype: QuantTarget,
pub mode: QuantMode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuantTarget {
Int8,
Int4,
Fp8E4M3,
Fp8E5M2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuantMode {
Dynamic,
Static,
}
impl Default for QuantisationPass {
fn default() -> Self {
Self {
target_dtype: QuantTarget::Int8,
mode: QuantMode::Dynamic,
}
}
}
impl Pass for QuantisationPass {
fn name(&self) -> &str {
"quantisation"
}
fn run(&self, ctx: &mut Context, _cache: &mut AnalysisCache) -> PassResult {
let mut quantised = 0usize;
let op_keys: Vec<_> = ctx.ops.keys().collect();
let target_ops: Vec<_> = op_keys
.into_iter()
.filter(|&ok| {
if let Some(op) = ctx.ops.get(ok) {
let name = ctx.strings.resolve(op.name);
matches!(
name,
"tensor.matmul"
| "tensor.linear"
| "tensor.conv2d"
| "tensor.conv1d"
| "tensor.multi_head_attention"
| "tensor.attention"
)
} else {
false
}
})
.collect();
let _quant_name = match self.target_dtype {
QuantTarget::Int8 => "tensor.quantize",
QuantTarget::Int4 => "tensor.quantize_int4",
QuantTarget::Fp8E4M3 | QuantTarget::Fp8E5M2 => "tensor.quantize_fp8",
};
let _dequant_name = match self.target_dtype {
QuantTarget::Int8 => "tensor.dequantize",
QuantTarget::Int4 => "tensor.dequantize_int4",
QuantTarget::Fp8E4M3 | QuantTarget::Fp8E5M2 => "tensor.dequantize_fp8",
};
for op_key in target_ops {
if let Some(op) = ctx.ops.get_mut(op_key) {
if op.attrs.get_bool("quantised").unwrap_or(false) {
continue;
}
op.attrs
.set("quantised", lift_core::attributes::Attribute::Bool(true));
op.attrs.set(
"quant_method",
lift_core::attributes::Attribute::Integer(match self.target_dtype {
QuantTarget::Int8 => 8,
QuantTarget::Int4 => 4,
QuantTarget::Fp8E4M3 => 83,
QuantTarget::Fp8E5M2 => 82,
}),
);
op.attrs.set(
"quant_bits",
lift_core::attributes::Attribute::Integer(match self.target_dtype {
QuantTarget::Int8 | QuantTarget::Fp8E4M3 | QuantTarget::Fp8E5M2 => 8,
QuantTarget::Int4 => 4,
}),
);
quantised += 1;
}
}
if quantised > 0 {
tracing::info!(
pass = "quantisation",
quantised = quantised,
target = ?self.target_dtype,
mode = ?self.mode,
"Quantisation annotations applied"
);
PassResult::Changed
} else {
PassResult::Unchanged
}
}
fn invalidates(&self) -> Vec<&str> {
vec!["analysis"]
}
}