1use ndarray::{Array2, Dimension};
2use serde::{Deserialize, Serialize};
3use std::fs::File;
4use std::io::{Read, Write};
5use std::path::Path;
6
7use crate::layers::lstm_cell::LSTMCell;
8use crate::models::lstm_network::LSTMNetwork;
9
10#[derive(Serialize, Deserialize, Debug, Clone)]
12struct SerializableArray2 {
13 data: Vec<f64>,
14 shape: (usize, usize),
15}
16
17impl From<&Array2<f64>> for SerializableArray2 {
18 fn from(array: &Array2<f64>) -> Self {
19 Self {
20 data: array.iter().cloned().collect(),
21 shape: array.raw_dim().into_pattern(),
22 }
23 }
24}
25
26impl From<SerializableArray2> for Array2<f64> {
27 fn from(val: SerializableArray2) -> Self {
28 Array2::from_shape_vec(val.shape, val.data)
29 .expect("Failed to reconstruct Array2 from serialized data")
30 }
31}
32
33#[derive(Serialize, Deserialize, Debug, Clone)]
35pub struct SerializableLSTMCell {
36 w_ih: SerializableArray2,
37 w_hh: SerializableArray2,
38 b_ih: SerializableArray2,
39 b_hh: SerializableArray2,
40 hidden_size: usize,
41}
42
43impl From<&LSTMCell> for SerializableLSTMCell {
44 fn from(cell: &LSTMCell) -> Self {
45 Self {
46 w_ih: (&cell.w_ih).into(),
47 w_hh: (&cell.w_hh).into(),
48 b_ih: (&cell.b_ih).into(),
49 b_hh: (&cell.b_hh).into(),
50 hidden_size: cell.hidden_size,
51 }
52 }
53}
54
55impl From<SerializableLSTMCell> for LSTMCell {
56 fn from(val: SerializableLSTMCell) -> Self {
57 LSTMCell {
58 w_ih: val.w_ih.into(),
59 w_hh: val.w_hh.into(),
60 b_ih: val.b_ih.into(),
61 b_hh: val.b_hh.into(),
62 hidden_size: val.hidden_size,
63 input_dropout: None,
64 recurrent_dropout: None,
65 output_dropout: None,
66 cell_update_dropout: None,
67 zoneout: None,
68 is_training: true,
69 }
70 }
71}
72
73#[derive(Serialize, Deserialize, Debug, Clone)]
75pub struct SerializableLSTMNetwork {
76 cells: Vec<SerializableLSTMCell>,
77 input_size: usize,
78 hidden_size: usize,
79 num_layers: usize,
80}
81
82impl From<&LSTMNetwork> for SerializableLSTMNetwork {
83 fn from(network: &LSTMNetwork) -> Self {
84 Self {
85 cells: network.get_cells().iter().map(|cell| cell.into()).collect(),
86 input_size: network.input_size,
87 hidden_size: network.hidden_size,
88 num_layers: network.num_layers,
89 }
90 }
91}
92
93impl From<SerializableLSTMNetwork> for LSTMNetwork {
94 fn from(val: SerializableLSTMNetwork) -> Self {
95 LSTMNetwork::from_cells(
96 val.cells.into_iter().map(|cell| cell.into()).collect(),
97 val.input_size,
98 val.hidden_size,
99 val.num_layers,
100 )
101 }
102}
103
104#[derive(Serialize, Deserialize, Clone)]
106pub struct ModelMetadata {
107 pub model_name: String,
108 pub version: String,
109 pub created_at: String,
110 pub input_size: usize,
111 pub hidden_size: usize,
112 pub num_layers: usize,
113 pub total_epochs: usize,
114 pub final_loss: Option<f64>,
115 pub description: Option<String>,
116}
117
118#[derive(Serialize, Deserialize)]
120pub struct SavedModel {
121 pub network: SerializableLSTMNetwork,
122 pub metadata: ModelMetadata,
123}
124
125#[derive(Debug)]
127pub enum PersistenceError {
128 IoError(std::io::Error),
129 SerializationError(String),
130}
131
132impl std::fmt::Display for PersistenceError {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 match self {
135 PersistenceError::IoError(err) => write!(f, "IO error: {}", err),
136 PersistenceError::SerializationError(err) => write!(f, "Serialization error: {}", err),
137 }
138 }
139}
140
141impl std::error::Error for PersistenceError {
142 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
143 match self {
144 PersistenceError::IoError(err) => Some(err),
145 PersistenceError::SerializationError(_) => None,
146 }
147 }
148}
149
150impl From<std::io::Error> for PersistenceError {
151 fn from(error: std::io::Error) -> Self {
152 PersistenceError::IoError(error)
153 }
154}
155
156impl From<serde_json::Error> for PersistenceError {
157 fn from(error: serde_json::Error) -> Self {
158 PersistenceError::SerializationError(error.to_string())
159 }
160}
161
162impl From<bincode::Error> for PersistenceError {
163 fn from(error: bincode::Error) -> Self {
164 PersistenceError::SerializationError(error.to_string())
165 }
166}
167
168pub struct ModelPersistence;
170
171impl ModelPersistence {
172 pub fn save_to_json<P: AsRef<Path>>(
174 model: &SavedModel,
175 path: P,
176 ) -> Result<(), PersistenceError> {
177 let json = serde_json::to_string_pretty(model)?;
178 let mut file = File::create(path)?;
179 file.write_all(json.as_bytes())?;
180 Ok(())
181 }
182
183 pub fn load_from_json<P: AsRef<Path>>(path: P) -> Result<SavedModel, PersistenceError> {
185 let mut file = File::open(path)?;
186 let mut contents = String::new();
187 file.read_to_string(&mut contents)?;
188 let model = serde_json::from_str(&contents)?;
189 Ok(model)
190 }
191
192 pub fn save_to_binary<P: AsRef<Path>>(
194 model: &SavedModel,
195 path: P,
196 ) -> Result<(), PersistenceError> {
197 let encoded = bincode::serialize(model)?;
198 let mut file = File::create(path)?;
199 file.write_all(&encoded)?;
200 Ok(())
201 }
202
203 pub fn load_from_binary<P: AsRef<Path>>(path: P) -> Result<SavedModel, PersistenceError> {
205 let mut file = File::open(path)?;
206 let mut contents = Vec::new();
207 file.read_to_end(&mut contents)?;
208 let model = bincode::deserialize(&contents)?;
209 Ok(model)
210 }
211
212 pub fn create_saved_model(
214 network: &LSTMNetwork,
215 model_name: String,
216 total_epochs: usize,
217 final_loss: Option<f64>,
218 description: Option<String>,
219 ) -> SavedModel {
220 let metadata = ModelMetadata {
221 model_name,
222 version: env!("CARGO_PKG_VERSION").to_string(),
223 created_at: chrono::Utc::now().to_rfc3339(),
224 input_size: network.input_size,
225 hidden_size: network.hidden_size,
226 num_layers: network.num_layers,
227 total_epochs,
228 final_loss,
229 description,
230 };
231
232 SavedModel {
233 network: network.into(),
234 metadata,
235 }
236 }
237}
238
239pub trait PersistentModel {
241 fn save<P: AsRef<Path>>(
243 &self,
244 path: P,
245 metadata: ModelMetadata,
246 ) -> Result<(), PersistenceError>;
247
248 fn load<P: AsRef<Path>>(path: P) -> Result<(Self, ModelMetadata), PersistenceError>
250 where
251 Self: Sized;
252}
253
254impl PersistentModel for LSTMNetwork {
255 fn save<P: AsRef<Path>>(
256 &self,
257 path: P,
258 metadata: ModelMetadata,
259 ) -> Result<(), PersistenceError> {
260 let saved_model = SavedModel {
261 network: self.into(),
262 metadata,
263 };
264
265 let path_ref = path.as_ref();
266 match path_ref.extension().and_then(|s| s.to_str()) {
267 Some("json") => ModelPersistence::save_to_json(&saved_model, path),
268 Some("bin") | Some("model") => ModelPersistence::save_to_binary(&saved_model, path),
269 _ => ModelPersistence::save_to_binary(&saved_model, path), }
271 }
272
273 fn load<P: AsRef<Path>>(path: P) -> Result<(Self, ModelMetadata), PersistenceError> {
274 let path_ref = path.as_ref();
275 let saved_model = match path_ref.extension().and_then(|s| s.to_str()) {
276 Some("json") => ModelPersistence::load_from_json(path)?,
277 Some("bin") | Some("model") => ModelPersistence::load_from_binary(path)?,
278 _ => ModelPersistence::load_from_binary(path)?, };
280
281 Ok((saved_model.network.into(), saved_model.metadata))
282 }
283}