1use crate::optimizers::GradApplyer;
2use crate::{Error, Gradients};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct LoadSaveError {
8 pub path: String,
9 pub err: String,
10}
11
12pub trait Module<X> {
14 type Output;
16
17 fn forward(&self, x: &X) -> Result<Self::Output, Error>;
18}
19
20pub trait RevModule<X>: Module<X> {
25 type SelfGrads: Gradients;
27
28 fn reverse(
31 &self,
32 inputs: &X,
33 grads_wrt_output: &<Self as Module<X>>::Output,
34 ) -> (X, Self::SelfGrads);
35
36 fn apply(
39 &mut self,
40 applyer: &mut impl GradApplyer,
41 updates: Self::SelfGrads,
42 ) -> Result<(), Error>;
43}
44
45pub trait TracedModule<X>: Module<X> {
53 type Trace;
57
58 fn traced_forward(&self, x: X) -> Result<(<Self as Module<X>>::Output, Self::Trace), Error>;
61}
62
63impl<Input, M: Module<Input> + BaseModule> TracedModule<Input> for M {
64 type Trace = Input;
65
66 fn traced_forward(
67 &self,
68 x: Input,
69 ) -> Result<(<Self as crate::Module<Input>>::Output, Self::Trace), Error> {
70 Ok((Module::forward(self, &x)?, (x)))
71 }
72}
73
74pub trait BackpropModule<X>: TracedModule<X> {
81 type SelfGrads;
83
84 fn backprop(
90 &self,
91 trace: &<Self as TracedModule<X>>::Trace,
92 grads_wrt_output: <Self as Module<X>>::Output,
93 ) -> (X, Self::SelfGrads);
94
95 fn update(
100 &mut self,
101 applyer: &mut impl GradApplyer,
102 updates: Self::SelfGrads,
103 ) -> Result<(), Error>;
104
105 fn new_momentum(
107 &self,
108 params: crate::optimizers::TrainParams,
109 momentum_coefficient: f32,
110 ) -> crate::optimizers::Momentum<Self::SelfGrads>
111 where
112 <Self as BackpropModule<X>>::SelfGrads: Gradients,
113 {
114 crate::optimizers::Momentum::new(params, momentum_coefficient)
115 }
116
117 fn new_rmsprop(
119 &self,
120 params: crate::optimizers::TrainParams,
121 beta: f32,
122 ) -> crate::optimizers::RMSProp<Self::SelfGrads>
123 where
124 <Self as BackpropModule<X>>::SelfGrads: Gradients,
125 <Self::SelfGrads as Gradients>::Concrete: crate::Float,
126 {
127 crate::optimizers::RMSProp::new(params, beta)
128 }
129
130 fn new_rmsprop_with_momentum(
132 &self,
133 params: crate::optimizers::TrainParams,
134 momentum_coefficient: f32,
135 beta: f32,
136 ) -> crate::optimizers::RMSProp<Self::SelfGrads>
137 where
138 <Self as BackpropModule<X>>::SelfGrads: Gradients,
139 <Self::SelfGrads as Gradients>::Concrete: crate::Float,
140 {
141 crate::optimizers::RMSProp::new_with_momentum(params, momentum_coefficient, beta)
142 }
143}
144
145impl<Input, M: TracedModule<Input, Trace = Input> + RevModule<Input> + BaseModule>
146 BackpropModule<Input> for M
147{
148 type SelfGrads = <M as RevModule<Input>>::SelfGrads;
149
150 fn backprop(
151 &self,
152 trace: &<M as TracedModule<Input>>::Trace,
153 grads_wrt_output: <M as Module<Input>>::Output,
154 ) -> (Input, Self::SelfGrads) {
155 M::reverse(self, trace, &grads_wrt_output)
156 }
157
158 fn update(
159 &mut self,
160 applyer: &mut impl GradApplyer,
161 updates: Self::SelfGrads,
162 ) -> Result<(), Error> {
163 M::apply(self, applyer, updates)
164 }
165}
166
167pub trait LoadableModule {
169 fn save(&self, path: String, dict: &mut HashMap<String, Vec<f64>>)
173 -> Result<(), LoadSaveError>;
174
175 fn load(&mut self, path: String, dict: &HashMap<String, Vec<f64>>)
179 -> Result<(), LoadSaveError>;
180}
181
182pub(crate) trait BaseModule {}
187
188pub trait ResetParams {
190 fn rand_params<RNG: rand::Rng>(&mut self, rng: &mut RNG, scale: f32) -> Result<(), Error>;
199}
200
201macro_rules! fwd_tuple_impls {
202 ([$($name:ident),+] [$($idx:tt),*], $last:ident, [$($rev_tail:ident),*], [$($trace_name:ident),*]) => {
203 impl<
204 Input,
205 $last:
206 $(crate::Module::<$rev_tail ::Output>, $rev_tail: )*
207 crate::Module<Input>
208 > crate::Module<Input> for ($($name,)+) {
209 type Output = $last ::Output;
210
211 fn forward(&self, x: &Input) -> Result<Self::Output, Error> {
213 let x = self.0.forward(x)?;
214 $(let x = self.$idx.forward(&x)?;)*
215 Ok(x)
216 }
217 }
218
219 impl<Input,
220 $last:
221 $(TracedModule::<$rev_tail ::Output>, $rev_tail: )*
222 TracedModule<Input>
223 > TracedModule<Input> for ($($name,)+)
224 {
225 type Trace = ($($name::Trace,)+);
226
227 fn traced_forward(
228 &self,
229 x: Input,
230 ) -> Result<(<Self as Module<Input>>::Output, Self::Trace), Error> {
231 let (x, m1t) = self.0.traced_forward(x)?;
232 $(let (x, $trace_name) = self.$idx.traced_forward(x)?;)*
233 Ok((x, (m1t, $($trace_name,)*)))
234 }
235 }
236
237 impl<
238 $last:
239 $(crate::ResetParams, $rev_tail: )*
240 crate::ResetParams
241 > crate::ResetParams for ($($name,)+) {
242 fn rand_params<RNG: rand::Rng>(&mut self, rng: &mut RNG, scale: f32) -> Result<(), Error> {
243 self.0.rand_params(rng, scale)?;
244 $(self.$idx.rand_params(rng, scale)?;)*
245 Ok(())
246 }
247 }
248
249 impl<
250 $last:
251 $(crate::LoadableModule, $rev_tail: )*
252 crate::LoadableModule
253 > crate::LoadableModule for ($($name,)+) {
254 fn save(&self, path: String, dict: &mut HashMap<String, Vec<f64>>) -> Result<(), LoadSaveError> {
255 self.0.save(path.clone() + ".0", dict)?;
256 $(self.$idx.save(format!("{}.{}", path, $idx), dict)?;)*
257 Ok(())
258 }
259
260 fn load(&mut self, path: String, dict: &HashMap<String, Vec<f64>>) -> Result<(), LoadSaveError> {
261 self.0.load(path.clone() + ".0", dict)?;
262 $(self.$idx.load(format!("{}.{}", path, $idx), dict)?;)*
263 Ok(())
264 }
265 }
266 };
267}
268
269fwd_tuple_impls!([M1][], M1, [], []);
270fwd_tuple_impls!([M1, M2][1], M2, [M1], [m2t]);
271fwd_tuple_impls!([M1, M2, M3] [1, 2], M3, [M2, M1], [m2t, m3t]);
272fwd_tuple_impls!([M1, M2, M3, M4] [1, 2, 3], M4, [M3, M2, M1], [m2t, m3t, m4t]);
273fwd_tuple_impls!([M1, M2, M3, M4, M5] [1, 2, 3, 4], M5, [M4, M3, M2, M1], [m2t, m3t, m4t, m5t]);
274fwd_tuple_impls!([M1, M2, M3, M4, M5, M6] [1, 2, 3, 4, 5], M6, [M5, M4, M3, M2, M1], [m2t, m3t, m4t, m5t, m6t]);
275
276macro_rules! backwd_tuple_impls {
277 ([$($all:ident),+] [$($idx:tt),*] [$($rev_idx:tt),*], $first:ident, [$($rev_grads:ident),+], [$($fwd_grads:ident),+], [$(($mod_for:ident, $mod_from:ident)),*]) => {
278 impl<Input,
279 $first: BackpropModule<Input>,
280 $($mod_for: BackpropModule::<$mod_from ::Output>,)*
281 > BackpropModule<Input>
282 for ($($all,)+)
283 {
284 type SelfGrads = (
285 <$first as BackpropModule<Input>>::SelfGrads,
286 $(<$mod_for as BackpropModule::<$mod_from ::Output>>::SelfGrads,)*
287 );
288
289 fn backprop(
290 &self,
291 trace: &<Self as TracedModule<Input>>::Trace,
292 next_grads: <Self as Module<Input>>::Output,
293 ) -> (Input, Self::SelfGrads) {
294 $(let (next_grads, $rev_grads) = self.$rev_idx.backprop(&trace.$rev_idx, next_grads);)+
295 (next_grads, ($($fwd_grads,)+))
296 }
297
298 fn update(&mut self, applyer: &mut impl GradApplyer, updates: Self::SelfGrads) -> Result<(), Error> {
299 $(self.$idx.update(applyer, updates.$idx)?;)+
300 Ok(())
301 }
302
303 }
304 };
305}
306
307backwd_tuple_impls!([M1][0][0], M1, [u1], [u1], []);
308backwd_tuple_impls!([M1, M2][0, 1][1, 0], M1, [u2, u1], [u1, u2], [(M2, M1)]);
309backwd_tuple_impls!([M1, M2, M3][0, 1, 2][2, 1, 0], M1, [u3, u2, u1], [u1, u2, u3], [(M2, M1), (M3, M2)]);
310backwd_tuple_impls!([M1, M2, M3, M4][0, 1, 2, 3][3, 2, 1, 0], M1, [u4, u3, u2, u1], [u1, u2, u3, u4], [(M2, M1), (M3, M2), (M4, M3)]);
311backwd_tuple_impls!([M1, M2, M3, M4, M5][0, 1, 2, 3, 4][4, 3, 2, 1, 0], M1, [u5, u4, u3, u2, u1], [u1, u2, u3, u4, u5], [(M2, M1), (M3, M2), (M4, M3), (M5, M4)]);
312backwd_tuple_impls!([M1, M2, M3, M4, M5, M6][0, 1, 2, 3, 4, 5][5, 4, 3, 2, 1, 0], M1, [u6, u5, u4, u3, u2, u1], [u1, u2, u3, u4, u5, u6], [(M2, M1), (M3, M2), (M4, M3), (M5, M4), (M6, M5)]);
313
314#[cfg(test)]
315mod tests {
316 use super::*;
317 use crate::layers;
318 use rand::rngs::SmallRng;
319 use rand::SeedableRng;
320
321 #[test]
322 fn test_module_forward() {
323 let mut network = (
324 layers::Dense::<f32, 2, 3>::default(),
325 layers::Activation::Relu,
326 layers::Bias1d::<f32, 3>::default(),
327 );
328 network.0.weights[0][0] = 2.5;
329 network.0.weights[0][1] = 0.5;
330 network.2.bias.grad_iter_mut().for_each(|x| *x += 1.0);
331
332 let output = network.forward(&[1.0, 2.0]);
333 assert_eq!(output, Ok([3.5, 1.5, 1.0]));
334
335 let (output2, _) = network.traced_forward([1.0, 2.0]).unwrap();
337 assert_eq!(output.unwrap(), output2);
338 }
339
340 #[test]
341 fn test_module_backward() {
342 let network = (
343 layers::Dense::<f32, 2, 3>::default(),
344 layers::Activation::Relu,
345 layers::Bias1d::<f32, 3>::default(),
346 );
347
348 let (_, trace) = network.traced_forward([1.0, 2.0]).unwrap();
349 let (grad_wrt_input, _gradient_updates) = network.backprop(&trace, [0.0, 0.0, 0.0]);
350 assert_eq!(grad_wrt_input, [0.0, 0.0]);
351
352 let network = (
353 layers::Dense::<f32, 2, 3>::default(),
354 layers::Activation::Relu,
355 layers::Bias1d::<f32, 3>::default(),
356 layers::Bias1d::<f32, 3>::default(),
357 layers::Bias1d::<f32, 3>::default(),
358 );
359 let (_, trace) = network.traced_forward([1.0, 2.0]).unwrap();
360 let (grad_wrt_input, _gradient_updates) = network.backprop(&trace, [0.0, 0.0, 0.0]);
361 assert_eq!(grad_wrt_input, [0.0, 0.0]);
362 }
363
364 #[test]
365 fn test_nested_module() {
366 let network = (
367 (
368 layers::Dense::<f32, 2, 3>::default(),
369 layers::Bias1d::<f32, 3>::default(),
370 ),
371 layers::Activation::Relu,
372 );
373
374 let (_, trace) = network.traced_forward([1.0, 2.0]).unwrap();
375 let (grad_wrt_input, _gradient_updates) = network.backprop(&trace, [5.0, 1.0, 1.0]);
376 assert_eq!(grad_wrt_input, [0.0, 0.0]);
377 }
378
379 #[test]
380 fn test_reset_params() {
381 let mut network = (
382 layers::Dense::<f32, 2, 3>::default(),
383 layers::Bias1d::<f32, 3>::default(),
384 layers::Activation::<f32>::Relu,
385 );
386 let mut rng = SmallRng::seed_from_u64(1);
387 network.rand_params(&mut rng, 1.0).unwrap();
388
389 for x in network.0.weights.iter().flatten() {
390 assert!(*x != 0.0);
391 }
392 for x in network.1.bias.grad_iter() {
393 assert!(*x != 0.0);
394 }
395 }
396
397 #[test]
398 fn test_dense_backprop() {
399 let network = layers::Dense::<f32, 2, 3>::default();
400
401 let (_, trace) = network.traced_forward([1.0, 2.0]).unwrap();
402 let (grad_wrt_input, gradient_updates) = network.backprop(&trace, [0.0, 0.0, 0.0]);
403 assert_eq!(grad_wrt_input, [0.0, 0.0]);
404 assert_eq!(gradient_updates, [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]);
405
406 let (_, gradient_updates) = network.backprop(&trace, [1.0, 0.0, 0.0]);
407 assert_eq!(gradient_updates, [[1.0, 0.0], [0.0, 2.0], [0.0, 0.0]]); }
409
410 #[test]
411 fn test_bias1d_backprop() {
412 let network = layers::Bias1d::<f32, 2>::default();
413
414 let (_, trace) = network.traced_forward([1.0, 2.0]).unwrap();
415 let (grad_wrt_input, gradient_updates) = network.backprop(&trace, [0.0, 0.0]);
416 assert_eq!(grad_wrt_input, [0.0, 0.0]);
417 assert_eq!(gradient_updates.raw_grads(), [0.0, 0.0]);
418
419 let (_, gradient_updates) = network.backprop(&trace, [2.0, 7.0]);
420 assert_eq!(gradient_updates.raw_grads(), [2.0, 7.0]);
421 }
422
423 #[test]
424 fn test_activation_backprop() {
425 let network = layers::Activation::Relu;
426
427 let (_, trace) = network.traced_forward([1.0, 2.0]).unwrap();
428 let (grad_wrt_input, gradient_updates) = network.backprop(&trace, [0.0, 0.0]);
429 assert_eq!(grad_wrt_input, [0.0, 0.0]);
430 assert_eq!(gradient_updates, ());
431 }
432}