use std::{
collections::HashMap,
fmt::Display,
path::Path,
sync::{Arc, RwLock},
};
use indexmap::IndexMap;
use crate::{
Parameter,
paramstore::{ParamStore, ParamStoreError},
};
use nove_tensor::{Device, Tensor};
#[derive(Debug)]
struct SafeTensorsParamStoreInner {
name: RwLock<String>,
params: RwLock<IndexMap<String, Parameter>>,
modules: RwLock<IndexMap<String, SafeTensorsParamStore>>,
}
#[derive(Debug, Clone)]
pub struct SafeTensorsParamStore {
inner: Arc<SafeTensorsParamStoreInner>,
}
impl ParamStore for SafeTensorsParamStore {
fn new(name: &str) -> Result<Self, ParamStoreError> {
Ok(Self {
inner: Arc::new(SafeTensorsParamStoreInner {
name: RwLock::new(name.to_string()),
params: RwLock::new(IndexMap::new()),
modules: RwLock::new(IndexMap::new()),
}),
})
}
fn set_name(&self, name: &str) -> Result<(), ParamStoreError> {
*self.inner.name.write()? = name.to_string();
Ok(())
}
fn name(&self) -> Result<String, ParamStoreError> {
Ok(self.inner.name.read()?.clone())
}
fn save(&self, folder_path: &str) -> Result<(), ParamStoreError> {
std::fs::create_dir_all(folder_path)?;
let file_path = Path::new(folder_path)
.join(self.name()?.as_str())
.with_extension("safetensors");
let all_params = Self::all_params_with_full_name(self, &self.name()?)?
.iter()
.map(|(k, v)| {
Ok::<(String, candle_core::Tensor), ParamStoreError>((
k.clone(),
v.1.to_candle_tensor()?,
))
})
.collect::<Result<HashMap<_, _>, _>>()?;
candle_core::safetensors::save(&all_params, file_path)
.map_err(|e| ParamStoreError::OtherError(e.to_string()))?;
Ok(())
}
fn load<F>(
&self,
folder_path: &str,
device: &Device,
mut process_fn: F,
) -> Result<(), ParamStoreError>
where
F: FnMut(&str, &Self) -> Result<(), ParamStoreError>,
{
let file_path = Path::new(folder_path)
.join(self.name()?.as_str())
.with_extension("safetensors");
let loaded_params = candle_core::safetensors::load(&file_path, device)
.map_err(|e| ParamStoreError::OtherError(e.to_string()))?;
let all_params = Self::all_params_with_full_name(self, &self.name()?)?;
if loaded_params.len() != all_params.len() {
return Err(ParamStoreError::OtherError(format!(
"The number({}) of parameters in the file({}) does not match the number({}) of parameters in the store",
loaded_params.len(),
file_path.display(),
all_params.len()
)));
}
for (full_name, param) in all_params {
let loaded_param = loaded_params.get(full_name.as_str()).ok_or_else(|| {
ParamStoreError::OtherError(format!(
"Parameter {} not found in the file({}).",
full_name,
file_path.display()
))
})?;
let loaded_tensor = Tensor::from_candle_tensor(loaded_param.clone(), device, false)?;
param.1.update_from_tensor(&loaded_tensor)?;
}
self.process_all_modules_with_full_name(self.name()?.as_str(), &mut process_fn)?;
Ok(())
}
fn set_module(&self, module: Self) -> Result<(), ParamStoreError> {
self.inner.modules.write()?.insert(module.name()?, module);
Ok(())
}
fn modules(&self) -> Result<Vec<Self>, ParamStoreError> {
Ok(self
.inner
.modules
.read()?
.iter()
.map(|(_, v)| v.clone())
.collect())
}
fn set_parameter(&self, param: Parameter) -> Result<(), ParamStoreError> {
self.inner.params.write()?.insert(param.0.clone(), param);
Ok(())
}
fn parameters(&self) -> Result<Vec<Parameter>, ParamStoreError> {
Ok(self
.inner
.params
.read()?
.iter()
.map(|(_, v)| v.clone())
.collect())
}
}
impl SafeTensorsParamStore {
fn all_params_with_full_name(
&self,
prefix: &str,
) -> Result<HashMap<String, Parameter>, ParamStoreError> {
let mut all_params = HashMap::new();
for (name, param) in &*self.inner.params.read()? {
let full_name = format!("{}.{}", prefix, name);
all_params.insert(full_name, param.clone());
}
for (module_name, module) in &*self.inner.modules.read()? {
let new_prefix = format!("{}.{}", prefix, module_name);
let module_params = Self::all_params_with_full_name(module, &new_prefix)?;
all_params.extend(module_params);
}
Ok(all_params)
}
fn process_all_modules_with_full_name<F>(
&self,
prefix: &str,
process_fn: &mut F,
) -> Result<(), ParamStoreError>
where
F: FnMut(&str, &Self) -> Result<(), ParamStoreError>,
{
process_fn(prefix, self)?;
let modules = self.inner.modules.read()?;
for (module_name, module) in &*modules {
let new_prefix = format!("{}.{}", prefix, module_name);
module.process_all_modules_with_full_name(new_prefix.as_str(), process_fn)?;
}
Ok(())
}
}
impl SafeTensorsParamStore {
fn fmt_with_indent(
&self,
f: &mut std::fmt::Formatter<'_>,
indent: &mut String,
) -> std::fmt::Result {
write!(f, "{}{}", indent, self.name().map_err(|_| std::fmt::Error)?)?;
let submodules = self.inner.modules.read().map_err(|_| std::fmt::Error)?;
if !submodules.is_empty() {
writeln!(f, "(")?;
indent.push_str(" ");
for (_, module) in &*submodules {
module.fmt_with_indent(f, indent)?;
}
indent.truncate(indent.len().saturating_sub(2));
writeln!(f, "{})", indent)?;
} else {
writeln!(f, "")?;
}
Ok(())
}
}
impl Display for SafeTensorsParamStore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut indent = String::new();
self.fmt_with_indent(f, &mut indent)?;
Ok(())
}
}