1use crate::{Module, ModuleBase, Parameter};
7use torsh_core::device::DeviceType;
8use torsh_core::error::Result;
9use torsh_tensor::{creation::*, Tensor};
10
11#[cfg(feature = "std")]
13use std::{boxed::Box, collections::HashMap, string::String, vec::Vec};
14
15#[cfg(not(feature = "std"))]
16use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
17
18#[cfg(not(feature = "std"))]
19use hashbrown::HashMap;
20
21pub struct NeuralODE {
28 base: ModuleBase,
29 func: Box<dyn Module>,
30 solver: ODESolver,
31 #[allow(dead_code)]
32 atol: f32,
33 #[allow(dead_code)]
34 rtol: f32,
35 max_steps: usize,
36}
37
38#[derive(Debug, Clone, Copy)]
40pub enum ODESolver {
41 Euler,
43 RK4,
45 Dopri5,
47}
48
49impl NeuralODE {
50 pub fn new(
59 func: Box<dyn Module>,
60 solver: ODESolver,
61 atol: f32,
62 rtol: f32,
63 max_steps: usize,
64 ) -> Self {
65 Self {
66 base: ModuleBase::new(),
67 func,
68 solver,
69 atol,
70 rtol,
71 max_steps,
72 }
73 }
74
75 fn solve_ode(&self, y0: &Tensor, t0: f32, t1: f32) -> Result<Tensor> {
77 match self.solver {
78 ODESolver::Euler => self.euler_solve(y0, t0, t1),
79 ODESolver::RK4 => self.rk4_solve(y0, t0, t1),
80 ODESolver::Dopri5 => self.dopri5_solve(y0, t0, t1),
81 }
82 }
83
84 fn euler_solve(&self, y0: &Tensor, t0: f32, t1: f32) -> Result<Tensor> {
86 let h = (t1 - t0) / self.max_steps as f32;
87 let mut y = y0.clone();
88 let mut t = t0;
89
90 for _ in 0..self.max_steps {
91 let dy_dt = self.func.forward(&y)?;
92 let h_tensor = full(y.shape().dims(), h)?;
93 let delta_y = dy_dt.mul_op(&h_tensor)?;
94 y = y.add_op(&delta_y)?;
95 t += h;
96
97 if t >= t1 {
98 break;
99 }
100 }
101
102 Ok(y)
103 }
104
105 fn rk4_solve(&self, y0: &Tensor, t0: f32, t1: f32) -> Result<Tensor> {
107 let h = (t1 - t0) / self.max_steps as f32;
108 let mut y = y0.clone();
109 let mut t = t0;
110
111 for _ in 0..self.max_steps {
112 let k1 = self.func.forward(&y)?;
113
114 let h_tensor = full(y.shape().dims(), h)?;
115 let half_tensor = full(y.shape().dims(), 0.5)?;
116 let two_tensor = full(y.shape().dims(), 2.0)?;
117 let six_tensor = full(y.shape().dims(), 6.0)?;
118
119 let k1_half_h = k1.mul_op(&h_tensor)?.mul_op(&half_tensor)?;
120 let y_k1 = y.add_op(&k1_half_h)?;
121 let k2 = self.func.forward(&y_k1)?;
122
123 let k2_half_h = k2.mul_op(&h_tensor)?.mul_op(&half_tensor)?;
124 let y_k2 = y.add_op(&k2_half_h)?;
125 let k3 = self.func.forward(&y_k2)?;
126
127 let k3_h = k3.mul_op(&h_tensor)?;
128 let y_k3 = y.add_op(&k3_h)?;
129 let k4 = self.func.forward(&y_k3)?;
130
131 let k2_times_2 = k2.mul_op(&two_tensor)?;
133 let k3_times_2 = k3.mul_op(&two_tensor)?;
134 let sum = k1.add_op(&k2_times_2)?.add_op(&k3_times_2)?.add_op(&k4)?;
135 let weighted_sum = sum.mul_op(&h_tensor)?.div(&six_tensor)?;
136 y = y.add_op(&weighted_sum)?;
137
138 t += h;
139 if t >= t1 {
140 break;
141 }
142 }
143
144 Ok(y)
145 }
146
147 fn dopri5_solve(&self, y0: &Tensor, t0: f32, t1: f32) -> Result<Tensor> {
149 self.rk4_solve(y0, t0, t1)
152 }
153}
154
155impl Module for NeuralODE {
156 fn forward(&self, input: &Tensor) -> Result<Tensor> {
157 self.solve_ode(input, 0.0, 1.0)
159 }
160
161 fn parameters(&self) -> HashMap<String, Parameter> {
162 self.base.parameters.clone()
163 }
164
165 fn training(&self) -> bool {
166 self.base.training()
167 }
168
169 fn train(&mut self) {
170 self.base.set_training(true);
171 }
172
173 fn eval(&mut self) {
174 self.base.set_training(false);
175 }
176
177 fn set_training(&mut self, training: bool) {
178 self.base.set_training(training);
179 }
180
181 fn to_device(&mut self, device: DeviceType) -> Result<()> {
182 self.base.to_device(device)
183 }
184
185 fn named_parameters(&self) -> HashMap<String, Parameter> {
186 self.base.named_parameters()
187 }
188}
189
190pub struct DARTSCell {
195 base: ModuleBase,
196 operations: Vec<Box<dyn Module>>,
197 alpha: Parameter, #[allow(dead_code)]
199 num_nodes: usize,
200 #[allow(dead_code)]
201 num_ops: usize,
202}
203
204impl DARTSCell {
205 pub fn new(operations: Vec<Box<dyn Module>>, num_nodes: usize) -> Result<Self> {
211 let num_ops = operations.len();
212 let mut base = ModuleBase::new();
213
214 let alpha_size = num_nodes * (num_nodes + 1) / 2 * num_ops;
216 let alpha_data = zeros(&[alpha_size])?;
217 base.register_parameter("alpha".to_string(), Parameter::new(alpha_data));
218
219 let alpha = base.parameters["alpha"].clone();
221
222 Ok(Self {
223 base,
224 operations,
225 alpha,
226 num_nodes,
227 num_ops,
228 })
229 }
230
231 fn get_architecture_weights(&self) -> Result<Tensor> {
233 let alpha_tensor = self.alpha.tensor().read().clone();
234 alpha_tensor.softmax(-1)
235 }
236}
237
238impl Module for DARTSCell {
239 fn forward(&self, input: &Tensor) -> Result<Tensor> {
240 let weights = self.get_architecture_weights()?;
241 let weight_data = weights.to_vec()?;
242
243 let mut output = input.clone();
246
247 for (i, op) in self.operations.iter().enumerate() {
248 let op_output = op.forward(input)?;
249 let weight = weight_data[i % weight_data.len()];
250 let weight_tensor = full(op_output.shape().dims(), weight)?;
251 let weighted_output = op_output.mul_op(&weight_tensor)?;
252
253 if i == 0 {
254 output = weighted_output;
255 } else {
256 output = output.add_op(&weighted_output)?;
257 }
258 }
259
260 Ok(output)
261 }
262
263 fn parameters(&self) -> HashMap<String, Parameter> {
264 let mut params = self.base.parameters.clone();
265
266 for (i, op) in self.operations.iter().enumerate() {
268 let op_params = op.parameters();
269 for (name, param) in op_params {
270 params.insert(format!("op_{}_{}", i, name), param);
271 }
272 }
273
274 params
275 }
276
277 fn training(&self) -> bool {
278 self.base.training()
279 }
280
281 fn train(&mut self) {
282 self.base.set_training(true);
283 }
284
285 fn eval(&mut self) {
286 self.base.set_training(false);
287 }
288
289 fn set_training(&mut self, training: bool) {
290 self.base.set_training(training);
291 }
292
293 fn to_device(&mut self, device: DeviceType) -> Result<()> {
294 self.base.to_device(device)
295 }
296
297 fn named_parameters(&self) -> HashMap<String, Parameter> {
298 self.base.named_parameters()
299 }
300}
301
302pub struct MAMLModule {
307 base: ModuleBase,
308 inner_model: Box<dyn Module>,
309 #[allow(dead_code)]
310 inner_lr: f32,
311 inner_steps: usize,
312}
313
314impl MAMLModule {
315 pub fn new(inner_model: Box<dyn Module>, inner_lr: f32, inner_steps: usize) -> Self {
322 Self {
323 base: ModuleBase::new(),
324 inner_model,
325 inner_lr,
326 inner_steps,
327 }
328 }
329
330 pub fn adapt(&mut self, support_x: &Tensor, support_y: &Tensor) -> Result<()> {
332 for _ in 0..self.inner_steps {
336 let prediction = self.inner_model.forward(support_x)?;
337
338 let diff = prediction.sub(support_y)?;
340 let squared_diff = diff.mul_op(&diff)?;
341 let loss = squared_diff; let _ = loss; }
348
349 Ok(())
350 }
351
352 pub fn meta_forward(
354 &mut self,
355 support_x: &Tensor,
356 support_y: &Tensor,
357 query_x: &Tensor,
358 ) -> Result<Tensor> {
359 let original_params = self.inner_model.parameters();
361
362 self.adapt(support_x, support_y)?;
364
365 let query_prediction = self.inner_model.forward(query_x)?;
367
368 let _ = original_params; Ok(query_prediction)
373 }
374}
375
376impl Module for MAMLModule {
377 fn forward(&self, input: &Tensor) -> Result<Tensor> {
378 self.inner_model.forward(input)
379 }
380
381 fn parameters(&self) -> HashMap<String, Parameter> {
382 self.inner_model.parameters()
383 }
384
385 fn training(&self) -> bool {
386 self.base.training()
387 }
388
389 fn train(&mut self) {
390 self.base.set_training(true);
391 }
392
393 fn eval(&mut self) {
394 self.base.set_training(false);
395 }
396
397 fn set_training(&mut self, training: bool) {
398 self.base.set_training(training);
399 self.inner_model.set_training(training);
400 }
401
402 fn to_device(&mut self, device: DeviceType) -> Result<()> {
403 self.base.to_device(device)
404 }
405
406 fn named_parameters(&self) -> HashMap<String, Parameter> {
407 self.inner_model.named_parameters()
408 }
409}
410
411pub struct CapsuleLayer {
416 base: ModuleBase,
417 in_capsules: usize,
418 out_capsules: usize,
419 #[allow(dead_code)]
420 in_dim: usize,
421 #[allow(dead_code)]
422 out_dim: usize,
423 num_routing: usize,
424}
425
426impl CapsuleLayer {
427 pub fn new(
436 in_capsules: usize,
437 out_capsules: usize,
438 in_dim: usize,
439 out_dim: usize,
440 num_routing: usize,
441 ) -> Result<Self> {
442 let mut base = ModuleBase::new();
443
444 let weight_shape = vec![out_capsules, in_capsules, out_dim, in_dim];
446 let weight = randn(&weight_shape)?;
447 base.register_parameter("weight".to_string(), Parameter::new(weight));
448
449 Ok(Self {
450 base,
451 in_capsules,
452 out_capsules,
453 in_dim,
454 out_dim,
455 num_routing,
456 })
457 }
458
459 fn squash(&self, tensor: &Tensor) -> Result<Tensor> {
461 let squared = tensor.mul_op(tensor)?;
463 let squared_sum = squared.sum()?;
464 let norm = squared_sum.sqrt()?;
465 let norm_squared = squared_sum.clone();
466
467 let one = ones(&[1])?;
468 let denominator = one.add_op(&norm_squared)?;
469 let scale = norm_squared.div(&denominator)?;
470
471 let unit_vector = tensor.div(&norm)?;
472 scale.mul_op(&unit_vector)
473 }
474
475 fn routing(&self, u_hat: &Tensor) -> Result<Tensor> {
477 let batch_size = u_hat.shape().dims()[0];
479 let mut b = zeros(&[batch_size, self.in_capsules, self.out_capsules])?;
480
481 for _ in 0..self.num_routing {
482 let _c = b.softmax(-1)?;
484
485 let s = u_hat.clone(); let _v = self.squash(&s)?;
491
492 let update = full(b.shape().dims(), 0.1)?;
495 b = b.add_op(&update)?;
496 }
497
498 let _c_final = b.softmax(-1)?;
499 let s_final = u_hat.clone(); self.squash(&s_final)
501 }
502}
503
504impl Module for CapsuleLayer {
505 fn forward(&self, input: &Tensor) -> Result<Tensor> {
506 let weight = self.base.parameters["weight"].tensor().read().clone();
508
509 let u_hat = input.matmul(&weight)?;
512
513 self.routing(&u_hat)
515 }
516
517 fn parameters(&self) -> HashMap<String, Parameter> {
518 self.base.parameters.clone()
519 }
520
521 fn training(&self) -> bool {
522 self.base.training()
523 }
524
525 fn train(&mut self) {
526 self.base.set_training(true);
527 }
528
529 fn eval(&mut self) {
530 self.base.set_training(false);
531 }
532
533 fn set_training(&mut self, training: bool) {
534 self.base.set_training(training);
535 }
536
537 fn to_device(&mut self, device: DeviceType) -> Result<()> {
538 self.base.to_device(device)
539 }
540
541 fn named_parameters(&self) -> HashMap<String, Parameter> {
542 self.base.named_parameters()
543 }
544}
545
546pub struct GraphConvLayer {
551 base: ModuleBase,
552 #[allow(dead_code)]
553 in_features: usize,
554 #[allow(dead_code)]
555 out_features: usize,
556 use_bias: bool,
557}
558
559impl GraphConvLayer {
560 pub fn new(in_features: usize, out_features: usize, use_bias: bool) -> Result<Self> {
567 let mut base = ModuleBase::new();
568
569 let weight = randn(&[in_features, out_features])?;
571 base.register_parameter("weight".to_string(), Parameter::new(weight));
572
573 if use_bias {
575 let bias = zeros(&[out_features])?;
576 base.register_parameter("bias".to_string(), Parameter::new(bias));
577 }
578
579 Ok(Self {
580 base,
581 in_features,
582 out_features,
583 use_bias,
584 })
585 }
586
587 #[allow(dead_code)]
589 fn normalize_adjacency(&self, adj: &Tensor) -> Result<Tensor> {
590 let num_nodes = adj.shape().dims()[0];
592 let identity = eye(num_nodes)?;
593 let adj_with_self_loops = adj.add_op(&identity)?;
594
595 let degrees = adj_with_self_loops.sum_dim(&[1], false)?;
597
598 let _degrees_sqrt = degrees.pow(-0.5)?;
600
601 let normalized_adj = adj_with_self_loops.clone(); Ok(normalized_adj)
606 }
607}
608
609impl Module for GraphConvLayer {
610 fn forward(&self, input: &Tensor) -> Result<Tensor> {
611 let weight = self.base.parameters["weight"].tensor().read().clone();
616
617 let transformed = input.matmul(&weight)?;
619
620 let mut output = transformed;
623
624 if self.use_bias {
626 let bias = self.base.parameters["bias"].tensor().read().clone();
627 output = output.add_op(&bias)?;
628 }
629
630 Ok(output)
631 }
632
633 fn parameters(&self) -> HashMap<String, Parameter> {
634 self.base.parameters.clone()
635 }
636
637 fn training(&self) -> bool {
638 self.base.training()
639 }
640
641 fn train(&mut self) {
642 self.base.set_training(true);
643 }
644
645 fn eval(&mut self) {
646 self.base.set_training(false);
647 }
648
649 fn set_training(&mut self, training: bool) {
650 self.base.set_training(training);
651 }
652
653 fn to_device(&mut self, device: DeviceType) -> Result<()> {
654 self.base.to_device(device)
655 }
656
657 fn named_parameters(&self) -> HashMap<String, Parameter> {
658 self.base.named_parameters()
659 }
660}
661
662pub struct GraphAttentionLayer {
667 base: ModuleBase,
668 #[allow(dead_code)]
669 in_features: usize,
670 #[allow(dead_code)]
671 out_features: usize,
672 num_heads: usize,
673 #[allow(dead_code)]
674 dropout: f32,
675 #[allow(dead_code)]
676 alpha: f32, }
678
679impl GraphAttentionLayer {
680 pub fn new(
689 in_features: usize,
690 out_features: usize,
691 num_heads: usize,
692 dropout: f32,
693 alpha: f32,
694 ) -> Result<Self> {
695 let mut base = ModuleBase::new();
696
697 for h in 0..num_heads {
699 let weight = randn(&[in_features, out_features])?;
700 base.register_parameter(format!("weight_{}", h), Parameter::new(weight));
701
702 let att = randn(&[2 * out_features, 1])?;
704 base.register_parameter(format!("att_{}", h), Parameter::new(att));
705 }
706
707 Ok(Self {
708 base,
709 in_features,
710 out_features,
711 num_heads,
712 dropout,
713 alpha,
714 })
715 }
716
717 #[allow(dead_code)]
719 fn attention(&self, h_i: &Tensor, h_j: &Tensor, head: usize) -> Result<Tensor> {
720 let att = self.base.parameters[&format!("att_{}", head)]
721 .tensor()
722 .read()
723 .clone();
724
725 let concat = Tensor::cat(&[h_i, h_j], -1)?;
727
728 let e = concat.matmul(&att)?;
730
731 let alpha_tensor = full(e.shape().dims(), self.alpha)?;
733 let zero = zeros(e.shape().dims())?;
734 let positive = e.maximum(&zero)?;
735 let negative = e.minimum(&zero)?;
736 let leaky_negative = negative.mul_op(&alpha_tensor)?;
737 positive.add_op(&leaky_negative)
738 }
739}
740
741impl Module for GraphAttentionLayer {
742 fn forward(&self, input: &Tensor) -> Result<Tensor> {
743 let mut head_outputs = Vec::new();
745
746 for h in 0..self.num_heads {
747 let weight = self.base.parameters[&format!("weight_{}", h)]
748 .tensor()
749 .read()
750 .clone();
751
752 let h_transformed = input.matmul(&weight)?;
754
755 head_outputs.push(h_transformed);
758 }
759
760 if head_outputs.len() == 1 {
762 Ok(head_outputs
763 .into_iter()
764 .next()
765 .expect("head_outputs should have at least one element"))
766 } else {
767 let mut sum = head_outputs[0].clone();
769 for head in head_outputs.iter().skip(1) {
770 sum = sum.add_op(head)?;
771 }
772 let num_heads_tensor = full(sum.shape().dims(), self.num_heads as f32)?;
773 sum.div(&num_heads_tensor)
774 }
775 }
776
777 fn parameters(&self) -> HashMap<String, Parameter> {
778 self.base.parameters.clone()
779 }
780
781 fn training(&self) -> bool {
782 self.base.training()
783 }
784
785 fn train(&mut self) {
786 self.base.set_training(true);
787 }
788
789 fn eval(&mut self) {
790 self.base.set_training(false);
791 }
792
793 fn set_training(&mut self, training: bool) {
794 self.base.set_training(training);
795 }
796
797 fn to_device(&mut self, device: DeviceType) -> Result<()> {
798 self.base.to_device(device)
799 }
800
801 fn named_parameters(&self) -> HashMap<String, Parameter> {
802 self.base.named_parameters()
803 }
804}
805
806#[cfg(test)]
807mod tests {
808 use super::*;
809 use crate::layers::linear::Linear;
810
811 #[test]
812 fn test_neural_ode_creation() {
813 let inner_model = Box::new(Linear::new(10, 10, true));
814 let node = NeuralODE::new(inner_model, ODESolver::Euler, 1e-3, 1e-3, 100);
815
816 assert_eq!(node.max_steps, 100);
817 assert!((node.atol - 1e-3).abs() < 1e-6);
818 }
819
820 #[test]
821 fn test_capsule_layer_creation() {
822 let capsule = CapsuleLayer::new(10, 5, 8, 16, 3).unwrap();
823
824 assert_eq!(capsule.in_capsules, 10);
825 assert_eq!(capsule.out_capsules, 5);
826 assert_eq!(capsule.in_dim, 8);
827 assert_eq!(capsule.out_dim, 16);
828 assert_eq!(capsule.num_routing, 3);
829 }
830
831 #[test]
832 fn test_graph_conv_creation() {
833 let gcn = GraphConvLayer::new(64, 32, true).unwrap();
834
835 assert_eq!(gcn.in_features, 64);
836 assert_eq!(gcn.out_features, 32);
837 assert!(gcn.use_bias);
838 }
839
840 #[test]
841 fn test_graph_attention_creation() {
842 let gat = GraphAttentionLayer::new(64, 32, 8, 0.1, 0.2).unwrap();
843
844 assert_eq!(gat.in_features, 64);
845 assert_eq!(gat.out_features, 32);
846 assert_eq!(gat.num_heads, 8);
847 assert!((gat.dropout - 0.1).abs() < 1e-6);
848 assert!((gat.alpha - 0.2).abs() < 1e-6);
849 }
850}