use std::ffi::{CString, NulError};
use std::path::Path;
use ik_llama_cpp_sys as sys;
#[derive(Debug, thiserror::Error)]
pub enum QuantizeError {
#[error("quantize path was not valid UTF-8")]
InvalidPath,
#[error("quantize path contained an interior NUL byte")]
Nul(#[from] NulError),
#[error("llama_model_quantize failed with status {0}")]
Quantize(u32),
}
pub mod ftype {
pub use ik_llama_cpp_sys::llama_ftype;
pub use ik_llama_cpp_sys::{
LLAMA_FTYPE_ALL_F32, LLAMA_FTYPE_MOSTLY_BF16, LLAMA_FTYPE_MOSTLY_F16,
LLAMA_FTYPE_MOSTLY_Q2_K, LLAMA_FTYPE_MOSTLY_Q2_K_S, LLAMA_FTYPE_MOSTLY_Q3_K_L,
LLAMA_FTYPE_MOSTLY_Q3_K_M, LLAMA_FTYPE_MOSTLY_Q3_K_S, LLAMA_FTYPE_MOSTLY_Q4_0,
LLAMA_FTYPE_MOSTLY_Q4_1, LLAMA_FTYPE_MOSTLY_Q4_K_M, LLAMA_FTYPE_MOSTLY_Q4_K_S,
LLAMA_FTYPE_MOSTLY_Q5_0, LLAMA_FTYPE_MOSTLY_Q5_1, LLAMA_FTYPE_MOSTLY_Q5_K_M,
LLAMA_FTYPE_MOSTLY_Q5_K_S, LLAMA_FTYPE_MOSTLY_Q6_K, LLAMA_FTYPE_MOSTLY_Q8_0,
};
pub use ik_llama_cpp_sys::{
LLAMA_FTYPE_MOSTLY_IQ1_M, LLAMA_FTYPE_MOSTLY_IQ1_S, LLAMA_FTYPE_MOSTLY_IQ2_M,
LLAMA_FTYPE_MOSTLY_IQ2_S, LLAMA_FTYPE_MOSTLY_IQ2_XS, LLAMA_FTYPE_MOSTLY_IQ2_XXS,
LLAMA_FTYPE_MOSTLY_IQ3_M, LLAMA_FTYPE_MOSTLY_IQ3_S, LLAMA_FTYPE_MOSTLY_IQ3_XXS,
LLAMA_FTYPE_MOSTLY_IQ4_NL, LLAMA_FTYPE_MOSTLY_IQ4_XS,
};
pub use ik_llama_cpp_sys::{
LLAMA_FTYPE_MOSTLY_IQ1_KT, LLAMA_FTYPE_MOSTLY_IQ2_K, LLAMA_FTYPE_MOSTLY_IQ2_KL,
LLAMA_FTYPE_MOSTLY_IQ2_KS, LLAMA_FTYPE_MOSTLY_IQ2_KT, LLAMA_FTYPE_MOSTLY_IQ3_K,
LLAMA_FTYPE_MOSTLY_IQ3_KL, LLAMA_FTYPE_MOSTLY_IQ3_KS, LLAMA_FTYPE_MOSTLY_IQ3_KT,
LLAMA_FTYPE_MOSTLY_IQ4_K, LLAMA_FTYPE_MOSTLY_IQ4_KS, LLAMA_FTYPE_MOSTLY_IQ4_KSS,
LLAMA_FTYPE_MOSTLY_IQ4_KT, LLAMA_FTYPE_MOSTLY_IQ5_K, LLAMA_FTYPE_MOSTLY_IQ5_KS,
LLAMA_FTYPE_MOSTLY_IQ6_K, LLAMA_FTYPE_MOSTLY_Q8_KV,
};
}
#[derive(Debug, Clone)]
pub struct LlamaQuantizeParams {
pub(crate) raw: sys::llama_model_quantize_params,
}
impl Default for LlamaQuantizeParams {
fn default() -> Self {
Self {
raw: unsafe { sys::llama_model_quantize_default_params() },
}
}
}
impl LlamaQuantizeParams {
#[must_use]
pub fn with_ftype(mut self, ftype: sys::llama_ftype) -> Self {
self.raw.ftype = ftype;
self
}
#[must_use]
pub fn with_n_threads(mut self, n_threads: i32) -> Self {
self.raw.nthread = n_threads;
self
}
#[must_use]
pub fn with_allow_requantize(mut self, allow_requantize: bool) -> Self {
self.raw.allow_requantize = allow_requantize;
self
}
#[must_use]
pub fn with_quantize_output_tensor(mut self, quantize_output_tensor: bool) -> Self {
self.raw.quantize_output_tensor = quantize_output_tensor;
self
}
#[must_use]
pub fn as_raw(&self) -> &sys::llama_model_quantize_params {
&self.raw
}
#[must_use]
pub fn as_raw_mut(&mut self) -> &mut sys::llama_model_quantize_params {
&mut self.raw
}
}
pub fn quantize(
input: &Path,
output: &Path,
params: &LlamaQuantizeParams,
) -> Result<(), QuantizeError> {
let inp = CString::new(input.to_str().ok_or(QuantizeError::InvalidPath)?)?;
let out = CString::new(output.to_str().ok_or(QuantizeError::InvalidPath)?)?;
let ret = unsafe { sys::llama_model_quantize(inp.as_ptr(), out.as_ptr(), ¶ms.raw) };
if ret == 0 {
Ok(())
} else {
Err(QuantizeError::Quantize(ret))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn params_default_and_builders_round_trip() {
let default = LlamaQuantizeParams::default();
let _ = default.as_raw();
let params = LlamaQuantizeParams::default()
.with_ftype(ftype::LLAMA_FTYPE_MOSTLY_Q4_K_M)
.with_n_threads(4)
.with_allow_requantize(true)
.with_quantize_output_tensor(true);
assert_eq!(params.as_raw().nthread, 4);
assert_eq!(params.as_raw().ftype, ftype::LLAMA_FTYPE_MOSTLY_Q4_K_M);
assert!(params.as_raw().allow_requantize);
assert!(params.as_raw().quantize_output_tensor);
}
#[test]
fn ik_sota_ftype_is_exposed() {
let params = LlamaQuantizeParams::default().with_ftype(ftype::LLAMA_FTYPE_MOSTLY_IQ4_K);
assert_eq!(params.as_raw().ftype, ftype::LLAMA_FTYPE_MOSTLY_IQ4_K);
}
}