use std::collections::HashMap;
use crate::array::Array;
use crate::error::{Error, Result};
use crate::ops;
use crate::quant::{QuantParams, Quantization};
pub struct WeightMap {
tensors: HashMap<String, Array>,
quant: Quantization,
}
impl WeightMap {
pub fn new(tensors: HashMap<String, Array>, quant: Quantization) -> Self {
WeightMap { tensors, quant }
}
pub fn quantization(&self) -> &Quantization {
&self.quant
}
pub fn contains(&self, key: &str) -> bool {
self.tensors.contains_key(key)
}
pub fn keys(&self) -> impl Iterator<Item = &String> {
self.tensors.keys()
}
pub fn take(&mut self, key: &str) -> Result<Array> {
self.tensors
.remove(key)
.ok_or_else(|| Error::Model(format!("missing weight: {key}")))
}
pub fn take_optional(&mut self, key: &str) -> Option<Array> {
self.tensors.remove(key)
}
pub fn insert(&mut self, key: String, value: Array) {
self.tensors.insert(key, value);
}
pub fn rename_keys(&mut self, f: impl Fn(&str) -> Option<String>) {
let keys: Vec<String> = self.tensors.keys().cloned().collect();
for key in keys {
if let Some(new_key) = f(&key) {
if new_key != key {
let value = self.tensors.remove(&key).unwrap();
self.tensors.insert(new_key, value);
}
} else {
self.tensors.remove(&key);
}
}
}
pub fn normalize_quant_keys(&mut self, f: impl Fn(&str) -> Option<String>) {
let entries: Vec<(String, crate::quant::LayerOverride)> =
self.quant.per_layer.drain().collect();
for (key, value) in entries {
let new_key = f(&key).unwrap_or(key);
self.quant.per_layer.insert(new_key, value);
}
}
pub fn linear(&mut self, path: &str) -> Result<Linear> {
let has_scales = self.contains(&format!("{path}.scales"));
let params = self.quant.resolve(path, has_scales);
let weight = self.take(&format!("{path}.weight"))?;
let bias = self.take_optional(&format!("{path}.bias"));
match params {
Some(q) if has_scales => {
let scales = self.take(&format!("{path}.scales"))?;
let biases = self.take_optional(&format!("{path}.biases"));
Ok(Linear::Quantized(QuantizedLinear {
weight,
scales,
biases,
params: q,
bias,
}))
}
_ => Ok(Linear::Dense(DenseLinear { weight, bias })),
}
}
pub fn embedding(&mut self, path: &str) -> Result<Embedding> {
let has_scales = self.contains(&format!("{path}.scales"));
let params = self.quant.resolve(path, has_scales);
let weight = self.take(&format!("{path}.weight"))?;
match params {
Some(q) if has_scales => {
let scales = self.take(&format!("{path}.scales"))?;
let biases = self.take_optional(&format!("{path}.biases"));
Ok(Embedding::Quantized {
weight,
scales,
biases,
params: q,
})
}
_ => Ok(Embedding::Dense { weight }),
}
}
pub fn rms_norm(&mut self, path: &str, eps: f32) -> Result<RmsNorm> {
Ok(RmsNorm {
weight: self.take(&format!("{path}.weight"))?,
eps,
})
}
pub fn layer_norm(&mut self, path: &str, eps: f32) -> Result<LayerNorm> {
Ok(LayerNorm {
weight: self.take(&format!("{path}.weight"))?,
bias: self.take_optional(&format!("{path}.bias")),
eps,
})
}
}
pub struct DenseLinear {
pub weight: Array,
pub bias: Option<Array>,
}
pub struct QuantizedLinear {
pub weight: Array,
pub scales: Array,
pub biases: Option<Array>,
pub params: QuantParams,
pub bias: Option<Array>,
}
pub enum Linear {
Dense(DenseLinear),
Quantized(QuantizedLinear),
}
impl Linear {
pub fn forward(&self, x: &Array) -> Result<Array> {
let out = match self {
Linear::Dense(l) => {
let wt = ops::swapaxes(&l.weight, -1, -2)?;
ops::matmul(x, &wt)?
}
Linear::Quantized(l) => ops::quantized_matmul(
x,
&l.weight,
&l.scales,
l.biases.as_ref(),
true,
l.params.group_size,
l.params.bits,
l.params.mode,
)?,
};
match self.bias() {
Some(b) => ops::add(&out, b),
None => Ok(out),
}
}
fn bias(&self) -> Option<&Array> {
match self {
Linear::Dense(l) => l.bias.as_ref(),
Linear::Quantized(l) => l.bias.as_ref(),
}
}
pub fn output_dims(&self) -> i32 {
match self {
Linear::Dense(l) => l.weight.dim(0),
Linear::Quantized(l) => l.weight.dim(0),
}
}
pub fn weight_dtype(&self, input_dtype: crate::array::Dtype) -> crate::array::Dtype {
match self {
Linear::Dense(l) => l.weight.dtype(),
Linear::Quantized(_) => input_dtype,
}
}
}
pub enum Embedding {
Dense {
weight: Array,
},
Quantized {
weight: Array,
scales: Array,
biases: Option<Array>,
params: QuantParams,
},
}
impl Embedding {
pub fn forward(&self, ids: &Array) -> Result<Array> {
match self {
Embedding::Dense { weight } => ops::take_axis(weight, ids, 0),
Embedding::Quantized {
weight,
scales,
biases,
params,
} => {
let w = ops::take_axis(weight, ids, 0)?;
let s = ops::take_axis(scales, ids, 0)?;
let b = match biases {
Some(b) => Some(ops::take_axis(b, ids, 0)?),
None => None,
};
ops::dequantize(
&w,
&s,
b.as_ref(),
params.group_size,
params.bits,
params.mode,
)
}
}
}
pub fn as_linear(&self, x: &Array) -> Result<Array> {
match self {
Embedding::Dense { weight } => {
let wt = ops::swapaxes(weight, -1, -2)?;
ops::matmul(x, &wt)
}
Embedding::Quantized {
weight,
scales,
biases,
params,
} => ops::quantized_matmul(
x,
weight,
scales,
biases.as_ref(),
true,
params.group_size,
params.bits,
params.mode,
),
}
}
}
pub struct RmsNorm {
pub weight: Array,
pub eps: f32,
}
impl RmsNorm {
pub fn forward(&self, x: &Array) -> Result<Array> {
ops::rms_norm(x, Some(&self.weight), self.eps)
}
}
pub struct LayerNorm {
pub weight: Array,
pub bias: Option<Array>,
pub eps: f32,
}
impl LayerNorm {
pub fn forward(&self, x: &Array) -> Result<Array> {
ops::layer_norm(x, Some(&self.weight), self.bias.as_ref(), self.eps)
}
}