1use torsh_core::error::{Result, TorshError};
8use crate::AutogradTensor;
10use parking_lot::RwLock;
11use serde::{Deserialize, Serialize};
12use std::any::Any;
13use std::collections::HashMap;
14use std::path::Path;
15use std::sync::Arc;
16use std::time::SystemTime;
17
18pub trait DynFunction: Send + Sync {
20 fn name(&self) -> &str;
22
23 fn is_differentiable(&self) -> bool {
25 true
26 }
27
28 fn memory_complexity(&self) -> MemoryComplexity {
30 MemoryComplexity::Linear
31 }
32
33 fn computational_complexity(&self) -> ComputationalComplexity {
35 ComputationalComplexity::Linear
36 }
37
38 fn is_fusable(&self) -> bool {
40 false
41 }
42
43 fn metadata(&self) -> FunctionMetadata {
45 FunctionMetadata {
46 name: self.name().to_string(),
47 is_differentiable: self.is_differentiable(),
48 memory_complexity: self.memory_complexity(),
49 computational_complexity: self.computational_complexity(),
50 is_fusable: self.is_fusable(),
51 version: "1.0.0".to_string(),
52 description: "Custom autograd function".to_string(),
53 author: "Unknown".to_string(),
54 created_at: SystemTime::now()
55 .duration_since(SystemTime::UNIX_EPOCH)
56 .unwrap_or_else(|_| std::time::Duration::from_secs(0))
57 .as_secs()
58 .to_string(),
59 checksum: "".to_string(),
60 dependencies: vec![],
61 }
62 }
63}
64
65pub trait Function: Send + Sync + DynFunction {
67 fn forward<T>(
69 &self,
70 ctx: &mut FunctionContext,
71 inputs: &[&dyn AutogradTensor<T>],
72 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
73 where
74 T: torsh_core::dtype::TensorElement;
75
76 fn backward<T>(
78 &self,
79 ctx: &mut FunctionContext,
80 grad_outputs: &[&dyn AutogradTensor<T>],
81 ) -> Result<Vec<Option<Box<dyn AutogradTensor<T>>>>>
82 where
83 T: torsh_core::dtype::TensorElement;
84}
85
86pub trait SubgradientFunction: Send + Sync + DynFunction {
88 fn forward<T>(
90 &self,
91 ctx: &mut FunctionContext,
92 inputs: &[&dyn AutogradTensor<T>],
93 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
94 where
95 T: torsh_core::dtype::TensorElement + num_traits::Float;
96
97 fn subgradient<T>(
100 &self,
101 ctx: &mut FunctionContext,
102 grad_outputs: &[&dyn AutogradTensor<T>],
103 ) -> Result<Vec<Option<SubgradientSet<T>>>>
104 where
105 T: torsh_core::dtype::TensorElement + num_traits::Float;
106}
107
108pub struct SubgradientSet<T: torsh_core::dtype::TensorElement> {
110 pub primary: Box<dyn AutogradTensor<T>>,
112 pub alternatives: Vec<Box<dyn AutogradTensor<T>>>,
114 pub selection_strategy: SubgradientSelection,
116}
117
118impl<T: torsh_core::dtype::TensorElement> Clone for SubgradientSet<T> {
119 fn clone(&self) -> Self {
120 Self {
121 primary: self.primary.clone_tensor(),
122 alternatives: self.alternatives.iter().map(|t| t.clone_tensor()).collect(),
123 selection_strategy: self.selection_strategy,
124 }
125 }
126}
127
128impl<T: torsh_core::dtype::TensorElement> std::fmt::Debug for SubgradientSet<T> {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 f.debug_struct("SubgradientSet")
131 .field(
132 "primary",
133 &format!("Box<dyn AutogradTensor<{}>>", std::any::type_name::<T>()),
134 )
135 .field(
136 "alternatives",
137 &format!(
138 "Vec<Box<dyn AutogradTensor<{}>>> (len: {})",
139 std::any::type_name::<T>(),
140 self.alternatives.len()
141 ),
142 )
143 .field("selection_strategy", &self.selection_strategy)
144 .finish()
145 }
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum SubgradientSelection {
151 Primary,
153 Random,
155 MinNorm,
157 MaxNorm,
159 Clarke,
161 Generalized,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
167pub enum MemoryComplexity {
168 Constant,
169 Linear,
170 Quadratic,
171 Exponential,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
176pub enum ComputationalComplexity {
177 Constant,
178 Linear,
179 LogLinear,
180 Quadratic,
181 Cubic,
182 Exponential,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct FunctionMetadata {
188 pub name: String,
189 pub is_differentiable: bool,
190 pub memory_complexity: MemoryComplexity,
191 pub computational_complexity: ComputationalComplexity,
192 pub is_fusable: bool,
193 pub version: String,
194 pub description: String,
195 pub author: String,
196 pub created_at: String,
197 pub checksum: String,
198 pub dependencies: Vec<String>,
199}
200
201pub struct FunctionContext {
203 #[allow(dead_code)]
205 saved_tensors: Vec<Box<dyn Any + Send + Sync>>,
206 saved_values: Vec<Box<dyn Any + Send + Sync>>,
208 materialize_grads: bool,
210 context_id: usize,
212 function_name: String,
214}
215
216impl Default for FunctionContext {
217 fn default() -> Self {
218 Self::new()
219 }
220}
221
222impl FunctionContext {
223 pub fn new() -> Self {
225 static CONTEXT_COUNTER: std::sync::atomic::AtomicUsize =
226 std::sync::atomic::AtomicUsize::new(0);
227 Self {
228 saved_tensors: Vec::new(),
229 saved_values: Vec::new(),
230 materialize_grads: true,
231 context_id: CONTEXT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
232 function_name: "unknown".to_string(),
233 }
234 }
235
236 pub fn with_name(name: String) -> Self {
238 let mut ctx = Self::new();
239 ctx.function_name = name;
240 ctx
241 }
242
243 pub fn save_value<V: Any + Send + Sync + 'static>(&mut self, value: V) {
245 self.saved_values.push(Box::new(value));
246 }
247
248 pub fn get_saved_value<V: Any + 'static>(&self, index: usize) -> Result<&V> {
250 self.saved_values
251 .get(index)
252 .and_then(|v| v.downcast_ref::<V>())
253 .ok_or_else(|| {
254 TorshError::AutogradError(format!(
255 "Saved value at index {} not found or type mismatch in context {}",
256 index, self.context_id
257 ))
258 })
259 }
260
261 pub fn context_id(&self) -> usize {
263 self.context_id
264 }
265
266 pub fn function_name(&self) -> &str {
268 &self.function_name
269 }
270
271 pub fn set_materialize_grads(&mut self, materialize: bool) {
273 self.materialize_grads = materialize;
274 }
275
276 pub fn should_materialize_grads(&self) -> bool {
278 self.materialize_grads
279 }
280}
281
282pub struct FunctionRegistry {
284 functions: RwLock<HashMap<String, Arc<dyn DynFunction>>>,
285}
286
287impl Default for FunctionRegistry {
288 fn default() -> Self {
289 Self::new()
290 }
291}
292
293impl FunctionRegistry {
294 pub fn new() -> Self {
296 Self {
297 functions: RwLock::new(HashMap::new()),
298 }
299 }
300
301 pub fn register<F>(&self, name: String, function: F) -> Result<()>
303 where
304 F: Function + 'static,
305 {
306 let mut functions = self.functions.write();
307 if functions.contains_key(&name) {
308 return Err(TorshError::AutogradError(format!(
309 "Function '{name}' is already registered"
310 )));
311 }
312 functions.insert(name, Arc::new(function));
313 Ok(())
314 }
315
316 pub fn get(&self, name: &str) -> Option<Arc<dyn DynFunction>> {
318 self.functions.read().get(name).cloned()
319 }
320
321 pub fn list_functions(&self) -> Vec<String> {
323 self.functions.read().keys().cloned().collect()
324 }
325
326 pub fn unregister(&self, name: &str) -> bool {
328 self.functions.write().remove(name).is_some()
329 }
330
331 pub fn get_metadata(&self, name: &str) -> Option<FunctionMetadata> {
333 self.functions.read().get(name).map(|f| f.metadata())
334 }
335}
336
337static GLOBAL_REGISTRY: std::sync::OnceLock<FunctionRegistry> = std::sync::OnceLock::new();
339
340pub fn global_registry() -> &'static FunctionRegistry {
342 GLOBAL_REGISTRY.get_or_init(FunctionRegistry::new)
343}
344
345pub fn register_function<F>(name: String, function: F) -> Result<()>
347where
348 F: Function + 'static,
349{
350 global_registry().register(name, function)
351}
352
353pub fn get_function_metadata(name: &str) -> Result<FunctionMetadata> {
356 let function = global_registry()
357 .get(name)
358 .ok_or_else(|| TorshError::AutogradError(format!("Function '{name}' not found")))?;
359
360 Ok(function.metadata())
361}
362
363pub mod composition {
365 use super::*;
366
367 pub struct ComposedFunction {
370 #[allow(dead_code)]
371 function_names: Vec<String>,
372 name: String,
373 }
374
375 impl ComposedFunction {
376 pub fn new(function_names: Vec<String>) -> Self {
378 let name = format!("compose({})", function_names.join(", "));
379 Self {
380 function_names,
381 name,
382 }
383 }
384 }
385
386 impl DynFunction for ComposedFunction {
387 fn name(&self) -> &str {
388 &self.name
389 }
390
391 fn is_differentiable(&self) -> bool {
392 true
394 }
395
396 fn memory_complexity(&self) -> MemoryComplexity {
397 MemoryComplexity::Linear
399 }
400
401 fn computational_complexity(&self) -> ComputationalComplexity {
402 ComputationalComplexity::Linear
404 }
405 }
406
407 impl Function for ComposedFunction {
408 fn forward<T>(
409 &self,
410 _ctx: &mut FunctionContext,
411 _inputs: &[&dyn AutogradTensor<T>],
412 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
413 where
414 T: torsh_core::dtype::TensorElement,
415 {
416 Err(TorshError::AutogradError(
418 "Function composition with type erasure is not supported".to_string(),
419 ))
420 }
421
422 fn backward<T>(
423 &self,
424 _ctx: &mut FunctionContext,
425 _grad_outputs: &[&dyn AutogradTensor<T>],
426 ) -> Result<Vec<Option<Box<dyn AutogradTensor<T>>>>>
427 where
428 T: torsh_core::dtype::TensorElement,
429 {
430 Err(TorshError::AutogradError(
432 "Function composition with type erasure is not supported".to_string(),
433 ))
434 }
435 }
436
437 pub fn compose(function_names: Vec<String>) -> ComposedFunction {
439 ComposedFunction::new(function_names)
440 }
441}
442
443pub mod examples {
445 use super::*;
446
447 #[derive(Debug)]
449 pub struct ScaledAdd {
450 pub scale: f32,
451 }
452
453 impl DynFunction for ScaledAdd {
454 fn name(&self) -> &str {
455 "ScaledAdd"
456 }
457
458 fn is_fusable(&self) -> bool {
459 true }
461 }
462
463 impl Function for ScaledAdd {
464 fn forward<T>(
465 &self,
466 ctx: &mut FunctionContext,
467 inputs: &[&dyn AutogradTensor<T>],
468 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
469 where
470 T: torsh_core::dtype::TensorElement,
471 {
472 if inputs.len() != 2 {
473 return Err(TorshError::AutogradError(
474 "ScaledAdd expects exactly two inputs".to_string(),
475 ));
476 }
477
478 ctx.save_value(self.scale);
480
481 let a_data = inputs[0].to_vec();
483 let b_data = inputs[1].to_vec();
484
485 if a_data.len() != b_data.len() {
486 return Err(TorshError::AutogradError(
487 "ScaledAdd: input tensors must have the same number of elements".to_string(),
488 ));
489 }
490
491 let scale_f64 = self.scale as f64;
492 let out_data = a_data
493 .iter()
494 .zip(b_data.iter())
495 .map(|(a_elem, b_elem)| {
496 let a_f64 = a_elem.to_f64().ok_or_else(|| {
497 TorshError::AutogradError(
498 "ScaledAdd: failed to convert element to f64".to_string(),
499 )
500 })?;
501 let b_f64 = b_elem.to_f64().ok_or_else(|| {
502 TorshError::AutogradError(
503 "ScaledAdd: failed to convert element to f64".to_string(),
504 )
505 })?;
506 T::from_f64(a_f64 + scale_f64 * b_f64).ok_or_else(|| {
507 TorshError::AutogradError(
508 "ScaledAdd: failed to convert f64 result back to T".to_string(),
509 )
510 })
511 })
512 .collect::<Result<Vec<T>>>()?;
513
514 let output = inputs[0].with_data(out_data)?;
515 Ok(vec![output])
516 }
517
518 fn backward<T>(
519 &self,
520 ctx: &mut FunctionContext,
521 grad_outputs: &[&dyn AutogradTensor<T>],
522 ) -> Result<Vec<Option<Box<dyn AutogradTensor<T>>>>>
523 where
524 T: torsh_core::dtype::TensorElement,
525 {
526 if grad_outputs.len() != 1 {
527 return Err(TorshError::AutogradError(
528 "ScaledAdd backward expects exactly one gradient output".to_string(),
529 ));
530 }
531
532 let scale: f32 = *ctx.get_saved_value(0)?;
533 let grad_output = grad_outputs[0];
534
535 let grad_a = Some(grad_output.clone_tensor());
537
538 let grad_b = Some(grad_output.mul_scalar(scale as f64)?);
540
541 Ok(vec![grad_a, grad_b])
542 }
543 }
544}
545
546#[macro_export]
548macro_rules! define_custom_function {
549 (
550 $name:ident,
551 forward: $forward:expr,
552 backward: $backward:expr
553 ) => {
554 #[derive(Debug)]
555 pub struct $name;
556
557 impl $crate::function::Function for $name {
558 fn forward<T>(
559 &self,
560 ctx: &mut $crate::function::FunctionContext,
561 inputs: &[&dyn $crate::AutogradTensor<T>],
562 ) -> $crate::Result<Vec<Box<dyn $crate::AutogradTensor<T>>>>
563 where
564 T: torsh_core::dtype::TensorElement,
565 {
566 $forward(ctx, inputs)
567 }
568
569 fn backward<T>(
570 &self,
571 ctx: &mut $crate::function::FunctionContext,
572 grad_outputs: &[&dyn $crate::AutogradTensor<T>],
573 ) -> $crate::Result<Vec<Option<Box<dyn $crate::AutogradTensor<T>>>>>
574 where
575 T: torsh_core::dtype::TensorElement,
576 {
577 $backward(ctx, grad_outputs)
578 }
579
580 fn name(&self) -> &str {
581 stringify!($name)
582 }
583 }
584 };
585}
586
587pub mod subgradient_functions {
589 use super::*;
590
591 #[derive(Debug)]
594 pub struct AbsFunction;
595
596 impl DynFunction for AbsFunction {
597 fn name(&self) -> &str {
598 "abs"
599 }
600 fn is_differentiable(&self) -> bool {
601 false
602 }
603 }
604
605 impl SubgradientFunction for AbsFunction {
606 fn forward<T>(
607 &self,
608 _ctx: &mut FunctionContext,
609 inputs: &[&dyn AutogradTensor<T>],
610 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
611 where
612 T: torsh_core::dtype::TensorElement + num_traits::Float,
613 {
614 if inputs.len() != 1 {
615 return Err(TorshError::AutogradError(
616 "abs requires exactly 1 input".to_string(),
617 ));
618 }
619
620 Ok(vec![inputs[0].clone_tensor()])
622 }
623
624 fn subgradient<T>(
625 &self,
626 _ctx: &mut FunctionContext,
627 grad_outputs: &[&dyn AutogradTensor<T>],
628 ) -> Result<Vec<Option<SubgradientSet<T>>>>
629 where
630 T: torsh_core::dtype::TensorElement + num_traits::Float,
631 {
632 if grad_outputs.len() != 1 {
633 return Err(TorshError::AutogradError(
634 "abs grad requires exactly 1 output".to_string(),
635 ));
636 }
637
638 let grad_output = grad_outputs[0];
639
640 let primary = grad_output.clone_tensor();
642
643 let zero_grad = grad_output.zeros_like();
646 let neg_grad = grad_output.mul_scalar(-1.0_f64)?;
647
648 let subgrad_set = SubgradientSet {
649 primary,
650 alternatives: vec![zero_grad, neg_grad],
651 selection_strategy: SubgradientSelection::Primary,
652 };
653
654 Ok(vec![Some(subgrad_set)])
655 }
656 }
657
658 #[derive(Debug)]
661 pub struct ReLUFunction;
662
663 impl DynFunction for ReLUFunction {
664 fn name(&self) -> &str {
665 "relu"
666 }
667 fn is_differentiable(&self) -> bool {
668 false
669 }
670 }
671
672 impl SubgradientFunction for ReLUFunction {
673 fn forward<T>(
674 &self,
675 _ctx: &mut FunctionContext,
676 inputs: &[&dyn AutogradTensor<T>],
677 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
678 where
679 T: torsh_core::dtype::TensorElement + num_traits::Float,
680 {
681 if inputs.len() != 1 {
682 return Err(TorshError::AutogradError(
683 "relu requires exactly 1 input".to_string(),
684 ));
685 }
686
687 Ok(vec![inputs[0].clone_tensor()])
689 }
690
691 fn subgradient<T>(
692 &self,
693 _ctx: &mut FunctionContext,
694 grad_outputs: &[&dyn AutogradTensor<T>],
695 ) -> Result<Vec<Option<SubgradientSet<T>>>>
696 where
697 T: torsh_core::dtype::TensorElement + num_traits::Float,
698 {
699 if grad_outputs.len() != 1 {
700 return Err(TorshError::AutogradError(
701 "relu grad requires exactly 1 output".to_string(),
702 ));
703 }
704
705 let grad_output = grad_outputs[0];
706
707 let primary = grad_output.clone_tensor();
709
710 let zero_grad = grad_output.zeros_like();
712
713 let subgrad_set = SubgradientSet {
714 primary,
715 alternatives: vec![zero_grad],
716 selection_strategy: SubgradientSelection::Primary,
717 };
718
719 Ok(vec![Some(subgrad_set)])
720 }
721 }
722
723 #[derive(Debug)]
726 pub struct MaxFunction;
727
728 impl DynFunction for MaxFunction {
729 fn name(&self) -> &str {
730 "max"
731 }
732 fn is_differentiable(&self) -> bool {
733 false
734 }
735 }
736
737 impl SubgradientFunction for MaxFunction {
738 fn forward<T>(
739 &self,
740 _ctx: &mut FunctionContext,
741 inputs: &[&dyn AutogradTensor<T>],
742 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
743 where
744 T: torsh_core::dtype::TensorElement + num_traits::Float,
745 {
746 if inputs.len() != 2 {
747 return Err(TorshError::AutogradError(
748 "max requires exactly 2 inputs".to_string(),
749 ));
750 }
751
752 Ok(vec![inputs[0].clone_tensor()])
754 }
755
756 fn subgradient<T>(
757 &self,
758 _ctx: &mut FunctionContext,
759 grad_outputs: &[&dyn AutogradTensor<T>],
760 ) -> Result<Vec<Option<SubgradientSet<T>>>>
761 where
762 T: torsh_core::dtype::TensorElement + num_traits::Float,
763 {
764 if grad_outputs.len() != 1 {
765 return Err(TorshError::AutogradError(
766 "max grad requires exactly 1 output".to_string(),
767 ));
768 }
769
770 let grad_output = grad_outputs[0];
771
772 let grad_x = grad_output.clone_tensor();
774 let grad_y = grad_output.zeros_like();
775
776 let half_grad_x = grad_output.mul_scalar(0.5_f64)?;
778 let half_grad_y = grad_output.mul_scalar(0.5_f64)?;
779
780 let subgrad_set_x = SubgradientSet {
781 primary: grad_x,
782 alternatives: vec![half_grad_x],
783 selection_strategy: SubgradientSelection::Primary,
784 };
785
786 let subgrad_set_y = SubgradientSet {
787 primary: grad_y,
788 alternatives: vec![half_grad_y],
789 selection_strategy: SubgradientSelection::Primary,
790 };
791
792 Ok(vec![Some(subgrad_set_x), Some(subgrad_set_y)])
793 }
794 }
795
796 #[derive(Debug)]
799 pub struct L1NormFunction;
800
801 impl DynFunction for L1NormFunction {
802 fn name(&self) -> &str {
803 "l1_norm"
804 }
805 fn is_differentiable(&self) -> bool {
806 false
807 }
808 }
809
810 impl SubgradientFunction for L1NormFunction {
811 fn forward<T>(
812 &self,
813 _ctx: &mut FunctionContext,
814 inputs: &[&dyn AutogradTensor<T>],
815 ) -> Result<Vec<Box<dyn AutogradTensor<T>>>>
816 where
817 T: torsh_core::dtype::TensorElement + num_traits::Float,
818 {
819 if inputs.len() != 1 {
820 return Err(TorshError::AutogradError(
821 "l1_norm requires exactly 1 input".to_string(),
822 ));
823 }
824
825 Ok(vec![inputs[0].ones_like()])
827 }
828
829 fn subgradient<T>(
830 &self,
831 _ctx: &mut FunctionContext,
832 grad_outputs: &[&dyn AutogradTensor<T>],
833 ) -> Result<Vec<Option<SubgradientSet<T>>>>
834 where
835 T: torsh_core::dtype::TensorElement + num_traits::Float,
836 {
837 if grad_outputs.len() != 1 {
838 return Err(TorshError::AutogradError(
839 "l1_norm grad requires exactly 1 output".to_string(),
840 ));
841 }
842
843 let grad_output = grad_outputs[0];
844
845 let sign_data: Vec<T> = grad_output
847 .to_vec()
848 .into_iter()
849 .map(|x| {
850 let zero = <T as num_traits::Zero>::zero();
851 let one = <T as num_traits::One>::one();
852 if x > zero {
853 one
854 } else if x < zero {
855 -one
856 } else {
857 zero
858 }
859 })
860 .collect();
861 let primary = grad_output.with_data(sign_data)?;
862
863 let zero_grad = grad_output.zeros_like();
865 let neg_grad = grad_output.mul_scalar(-1.0_f64)?;
866
867 let subgrad_set = SubgradientSet {
868 primary,
869 alternatives: vec![zero_grad, neg_grad],
870 selection_strategy: SubgradientSelection::MinNorm, };
872
873 Ok(vec![Some(subgrad_set)])
874 }
875 }
876}
877
878pub mod serialization {
880 use super::deployment::compute_signature;
881 use super::subgradient_functions::*;
882 use super::*;
883 use std::fs::{self, File};
884 use std::io::{BufReader, BufWriter};
885
886 pub trait SerializableFunction: DynFunction {
888 fn serialize(&self) -> Result<Vec<u8>>;
890
891 fn deserialize(data: &[u8]) -> Result<Box<dyn SerializableFunction>>
893 where
894 Self: Sized;
895
896 fn format_version(&self) -> u32 {
898 1
899 }
900
901 fn validate(&self) -> Result<()> {
903 Ok(())
904 }
905 }
906
907 impl SerializableFunction for AbsFunction {
909 fn serialize(&self) -> Result<Vec<u8>> {
910 let data = serde_json::to_vec(&"abs_function")
911 .map_err(|e| TorshError::AutogradError(format!("Serialization error: {e}")))?;
912 Ok(data)
913 }
914
915 fn deserialize(data: &[u8]) -> Result<Box<dyn SerializableFunction>>
916 where
917 Self: Sized,
918 {
919 let _function_type: String = serde_json::from_slice(data)
920 .map_err(|e| TorshError::AutogradError(format!("Deserialization error: {e}")))?;
921 Ok(Box::new(AbsFunction))
922 }
923 }
924
925 impl SerializableFunction for ReLUFunction {
926 fn serialize(&self) -> Result<Vec<u8>> {
927 let data = serde_json::to_vec(&"relu_function")
928 .map_err(|e| TorshError::AutogradError(format!("Serialization error: {e}")))?;
929 Ok(data)
930 }
931
932 fn deserialize(data: &[u8]) -> Result<Box<dyn SerializableFunction>>
933 where
934 Self: Sized,
935 {
936 let _function_type: String = serde_json::from_slice(data)
937 .map_err(|e| TorshError::AutogradError(format!("Deserialization error: {e}")))?;
938 Ok(Box::new(ReLUFunction))
939 }
940 }
941
942 impl SerializableFunction for MaxFunction {
943 fn serialize(&self) -> Result<Vec<u8>> {
944 let data = serde_json::to_vec(&"max_function")
945 .map_err(|e| TorshError::AutogradError(format!("Serialization error: {e}")))?;
946 Ok(data)
947 }
948
949 fn deserialize(data: &[u8]) -> Result<Box<dyn SerializableFunction>>
950 where
951 Self: Sized,
952 {
953 let _function_type: String = serde_json::from_slice(data)
954 .map_err(|e| TorshError::AutogradError(format!("Deserialization error: {e}")))?;
955 Ok(Box::new(MaxFunction))
956 }
957 }
958
959 impl SerializableFunction for L1NormFunction {
960 fn serialize(&self) -> Result<Vec<u8>> {
961 let data = serde_json::to_vec(&"l1_norm_function")
962 .map_err(|e| TorshError::AutogradError(format!("Serialization error: {e}")))?;
963 Ok(data)
964 }
965
966 fn deserialize(data: &[u8]) -> Result<Box<dyn SerializableFunction>>
967 where
968 Self: Sized,
969 {
970 let _function_type: String = serde_json::from_slice(data)
971 .map_err(|e| TorshError::AutogradError(format!("Deserialization error: {e}")))?;
972 Ok(Box::new(L1NormFunction))
973 }
974 }
975
976 pub struct FunctionFactory;
978
979 impl FunctionFactory {
980 pub fn create_from_package(
982 package: &FunctionPackage,
983 ) -> Result<Box<dyn SerializableFunction>> {
984 let function_type: String = serde_json::from_slice(&package.function_data)
985 .map_err(|e| TorshError::AutogradError(format!("Deserialization error: {e}")))?;
986
987 match function_type.as_str() {
988 "abs_function" => AbsFunction::deserialize(&package.function_data),
989 "relu_function" => ReLUFunction::deserialize(&package.function_data),
990 "max_function" => MaxFunction::deserialize(&package.function_data),
991 "l1_norm_function" => L1NormFunction::deserialize(&package.function_data),
992 _ => Err(TorshError::AutogradError(format!(
993 "Unknown function type: {function_type}"
994 ))),
995 }
996 }
997
998 pub fn create_package_from_function(
1000 function: &dyn SerializableFunction,
1001 metadata: FunctionMetadata,
1002 ) -> Result<FunctionPackage> {
1003 let function_data = function.serialize()?;
1004 let signature = compute_signature(&metadata, &function_data);
1005 Ok(FunctionPackage::new(metadata, function_data, signature))
1006 }
1007 }
1008
1009 #[derive(Debug, Clone, Serialize, Deserialize)]
1011 pub struct FunctionPackage {
1012 pub metadata: FunctionMetadata,
1014 pub function_data: Vec<u8>,
1016 pub format_version: u32,
1018 pub signature: String,
1020 pub runtime_dependencies: Vec<String>,
1022 pub min_framework_version: String,
1024 }
1025
1026 impl FunctionPackage {
1027 pub fn new(metadata: FunctionMetadata, function_data: Vec<u8>, signature: String) -> Self {
1029 Self {
1030 metadata,
1031 function_data,
1032 format_version: 1,
1033 signature,
1034 runtime_dependencies: vec!["torsh-autograd".to_string()],
1035 min_framework_version: "0.1.0".to_string(),
1036 }
1037 }
1038
1039 pub fn verify(&self) -> Result<()> {
1041 let computed_checksum = self.compute_checksum();
1043 if computed_checksum != self.signature {
1044 return Err(TorshError::AutogradError(
1045 "Function package signature verification failed".to_string(),
1046 ));
1047 }
1048 Ok(())
1049 }
1050
1051 fn compute_checksum(&self) -> String {
1053 use std::collections::hash_map::DefaultHasher;
1054 use std::hash::{Hash, Hasher};
1055
1056 let mut hasher = DefaultHasher::new();
1057 self.metadata.name.hash(&mut hasher);
1058 self.metadata.version.hash(&mut hasher);
1059 self.function_data.hash(&mut hasher);
1060 format!("{:x}", hasher.finish())
1061 }
1062
1063 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1065 let file = File::create(path)?;
1066 let writer = BufWriter::new(file);
1067 serde_json::to_writer_pretty(writer, self)
1068 .map_err(|e| TorshError::AutogradError(format!("Serialization error: {e}")))?;
1069 Ok(())
1070 }
1071
1072 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
1074 let file = File::open(path)?;
1075 let reader = BufReader::new(file);
1076 let package: FunctionPackage = serde_json::from_reader(reader)
1077 .map_err(|e| TorshError::AutogradError(format!("Deserialization error: {e}")))?;
1078 package.verify()?;
1079 Ok(package)
1080 }
1081 }
1082
1083 pub struct FunctionDeploymentManager {
1085 deploy_dir: std::path::PathBuf,
1087 deployed_functions: RwLock<HashMap<String, FunctionPackage>>,
1089 }
1090
1091 impl FunctionDeploymentManager {
1092 pub fn new<P: AsRef<Path>>(deploy_dir: P) -> Result<Self> {
1094 let deploy_dir = deploy_dir.as_ref().to_path_buf();
1095 fs::create_dir_all(&deploy_dir)?;
1096
1097 Ok(Self {
1098 deploy_dir,
1099 deployed_functions: RwLock::new(HashMap::new()),
1100 })
1101 }
1102
1103 pub fn deploy(&self, package: FunctionPackage) -> Result<()> {
1105 package.verify()?;
1107
1108 if !self.is_compatible_version(&package.min_framework_version) {
1110 return Err(TorshError::AutogradError(format!(
1111 "Function {} requires framework version {}, but current version is incompatible",
1112 package.metadata.name, package.min_framework_version
1113 )));
1114 }
1115
1116 let package_path = self
1118 .deploy_dir
1119 .join(format!("{}.pkg", package.metadata.name));
1120 package.save(&package_path)?;
1121
1122 let mut deployed = self.deployed_functions.write();
1124 deployed.insert(package.metadata.name.clone(), package);
1125
1126 Ok(())
1127 }
1128
1129 pub fn undeploy(&self, name: &str) -> Result<()> {
1131 let package_path = self.deploy_dir.join(format!("{}.pkg", name));
1132 if package_path.exists() {
1133 fs::remove_file(package_path)?;
1134 }
1135
1136 let mut deployed = self.deployed_functions.write();
1137 deployed.remove(name);
1138
1139 Ok(())
1140 }
1141
1142 pub fn list_deployed(&self) -> Vec<String> {
1144 self.deployed_functions.read().keys().cloned().collect()
1145 }
1146
1147 pub fn get_deployed_metadata(&self, name: &str) -> Option<FunctionMetadata> {
1149 self.deployed_functions
1150 .read()
1151 .get(name)
1152 .map(|pkg| pkg.metadata.clone())
1153 }
1154
1155 pub fn load_deployed(&self, name: &str) -> Result<Vec<u8>> {
1157 let deployed = self.deployed_functions.read();
1158 let package = deployed.get(name).ok_or_else(|| {
1159 TorshError::AutogradError(format!("Deployed function '{}' not found", name))
1160 })?;
1161 Ok(package.function_data.clone())
1162 }
1163
1164 fn is_compatible_version(&self, required_version: &str) -> bool {
1166 let current_version = "0.1.0";
1168 required_version <= current_version
1169 }
1170
1171 pub fn import_from_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1173 let package = FunctionPackage::load(path)?;
1174 self.deploy(package)
1175 }
1176
1177 pub fn export_to_file<P: AsRef<Path>>(&self, name: &str, path: P) -> Result<()> {
1179 let deployed = self.deployed_functions.read();
1180 let package = deployed.get(name).ok_or_else(|| {
1181 TorshError::AutogradError(format!("Deployed function '{}' not found", name))
1182 })?;
1183 package.save(path)
1184 }
1185 }
1186
1187 #[derive(Debug, Clone, Serialize, Deserialize)]
1189 pub struct FunctionLibrary {
1190 pub name: String,
1192 pub version: String,
1194 pub description: String,
1196 pub functions: Vec<FunctionPackage>,
1198 pub dependencies: Vec<String>,
1200 }
1201
1202 impl FunctionLibrary {
1203 pub fn new(name: String, version: String, description: String) -> Self {
1205 Self {
1206 name,
1207 version,
1208 description,
1209 functions: Vec::new(),
1210 dependencies: Vec::new(),
1211 }
1212 }
1213
1214 pub fn add_function(&mut self, package: FunctionPackage) {
1216 self.functions.push(package);
1217 }
1218
1219 pub fn add_dependency(&mut self, dependency: String) {
1221 if !self.dependencies.contains(&dependency) {
1222 self.dependencies.push(dependency);
1223 }
1224 }
1225
1226 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
1228 let file = File::create(path)?;
1229 let writer = BufWriter::new(file);
1230 serde_json::to_writer_pretty(writer, self)
1231 .map_err(|e| TorshError::AutogradError(format!("Serialization error: {e}")))?;
1232 Ok(())
1233 }
1234
1235 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
1237 let file = File::open(path)?;
1238 let reader = BufReader::new(file);
1239 let library: FunctionLibrary = serde_json::from_reader(reader)
1240 .map_err(|e| TorshError::AutogradError(format!("Deserialization error: {e}")))?;
1241 Ok(library)
1242 }
1243
1244 pub fn deploy_all(&self, manager: &FunctionDeploymentManager) -> Result<()> {
1246 for package in &self.functions {
1247 manager.deploy(package.clone())?;
1248 }
1249 Ok(())
1250 }
1251
1252 pub fn list_functions(&self) -> Vec<String> {
1254 self.functions
1255 .iter()
1256 .map(|pkg| pkg.metadata.name.clone())
1257 .collect()
1258 }
1259 }
1260}
1261
1262pub mod deployment {
1264 use super::serialization::*;
1265 use super::*;
1266
1267 static DEPLOYMENT_MANAGER: std::sync::OnceLock<FunctionDeploymentManager> =
1269 std::sync::OnceLock::new();
1270
1271 pub fn global_deployment_manager() -> &'static FunctionDeploymentManager {
1273 DEPLOYMENT_MANAGER.get_or_init(|| {
1274 FunctionDeploymentManager::new("./torsh_functions").unwrap_or_else(|_| {
1275 let temp_dir = std::env::temp_dir().join("torsh_functions");
1277 FunctionDeploymentManager::new(temp_dir)
1278 .expect("Failed to create deployment manager")
1279 })
1280 })
1281 }
1282
1283 pub fn deploy_function(package: FunctionPackage) -> Result<()> {
1285 global_deployment_manager().deploy(package)
1286 }
1287
1288 pub fn undeploy_function(name: &str) -> Result<()> {
1290 global_deployment_manager().undeploy(name)
1291 }
1292
1293 pub fn list_deployed_functions() -> Vec<String> {
1295 global_deployment_manager().list_deployed()
1296 }
1297
1298 pub fn get_deployed_function_metadata(name: &str) -> Option<FunctionMetadata> {
1300 global_deployment_manager().get_deployed_metadata(name)
1301 }
1302
1303 pub fn create_function_package(
1305 metadata: FunctionMetadata,
1306 function_data: Vec<u8>,
1307 ) -> FunctionPackage {
1308 let signature = compute_signature(&metadata, &function_data);
1309 FunctionPackage::new(metadata, function_data, signature)
1310 }
1311
1312 pub fn compute_signature(metadata: &FunctionMetadata, data: &[u8]) -> String {
1314 use std::collections::hash_map::DefaultHasher;
1315 use std::hash::{Hash, Hasher};
1316
1317 let mut hasher = DefaultHasher::new();
1318 metadata.name.hash(&mut hasher);
1319 metadata.version.hash(&mut hasher);
1320 data.hash(&mut hasher);
1321 format!("{:x}", hasher.finish())
1322 }
1323
1324 pub struct FunctionDeploymentBuilder {
1326 metadata: FunctionMetadata,
1327 function_data: Option<Vec<u8>>,
1328 dependencies: Vec<String>,
1329 }
1330
1331 impl FunctionDeploymentBuilder {
1332 pub fn new(name: String) -> Self {
1334 Self {
1335 metadata: FunctionMetadata {
1336 name,
1337 is_differentiable: true,
1338 memory_complexity: MemoryComplexity::Linear,
1339 computational_complexity: ComputationalComplexity::Linear,
1340 is_fusable: false,
1341 version: "1.0.0".to_string(),
1342 description: "".to_string(),
1343 author: "".to_string(),
1344 created_at: SystemTime::now()
1345 .duration_since(SystemTime::UNIX_EPOCH)
1346 .unwrap_or_else(|_| std::time::Duration::from_secs(0))
1347 .as_secs()
1348 .to_string(),
1349 checksum: "".to_string(),
1350 dependencies: vec![],
1351 },
1352 function_data: None,
1353 dependencies: vec![],
1354 }
1355 }
1356
1357 pub fn version(mut self, version: String) -> Self {
1359 self.metadata.version = version;
1360 self
1361 }
1362
1363 pub fn description(mut self, description: String) -> Self {
1365 self.metadata.description = description;
1366 self
1367 }
1368
1369 pub fn author(mut self, author: String) -> Self {
1371 self.metadata.author = author;
1372 self
1373 }
1374
1375 pub fn data(mut self, data: Vec<u8>) -> Self {
1377 self.function_data = Some(data);
1378 self
1379 }
1380
1381 pub fn dependency(mut self, dependency: String) -> Self {
1383 self.dependencies.push(dependency);
1384 self
1385 }
1386
1387 pub fn memory_complexity(mut self, complexity: MemoryComplexity) -> Self {
1389 self.metadata.memory_complexity = complexity;
1390 self
1391 }
1392
1393 pub fn computational_complexity(mut self, complexity: ComputationalComplexity) -> Self {
1395 self.metadata.computational_complexity = complexity;
1396 self
1397 }
1398
1399 pub fn fusable(mut self, fusable: bool) -> Self {
1401 self.metadata.is_fusable = fusable;
1402 self
1403 }
1404
1405 pub fn differentiable(mut self, differentiable: bool) -> Self {
1407 self.metadata.is_differentiable = differentiable;
1408 self
1409 }
1410
1411 pub fn build(mut self) -> Result<FunctionPackage> {
1413 let function_data = self
1414 .function_data
1415 .ok_or_else(|| TorshError::AutogradError("Function data not set".to_string()))?;
1416
1417 self.metadata.dependencies = self.dependencies;
1418 Ok(create_function_package(self.metadata, function_data))
1419 }
1420 }
1421}
1422
1423#[cfg(test)]
1424mod tests {
1425 use super::*;
1426
1427 #[test]
1428 fn test_function_registry() {
1429 let registry = FunctionRegistry::new();
1430 let scaled_add = examples::ScaledAdd { scale: 2.0 };
1431
1432 assert!(registry
1434 .register("scaled_add".to_string(), scaled_add)
1435 .is_ok());
1436
1437 let scaled_add2 = examples::ScaledAdd { scale: 3.0 };
1439 assert!(registry
1440 .register("scaled_add".to_string(), scaled_add2)
1441 .is_err());
1442
1443 assert!(registry.get("scaled_add").is_some());
1445 assert!(registry.get("nonexistent").is_none());
1446
1447 let functions = registry.list_functions();
1449 assert_eq!(functions.len(), 1);
1450 assert!(functions.contains(&"scaled_add".to_string()));
1451
1452 let metadata = registry.get_metadata("scaled_add").unwrap();
1454 assert_eq!(metadata.name, "ScaledAdd");
1455 assert!(metadata.is_differentiable);
1456 assert!(metadata.is_fusable);
1457 }
1458
1459 #[test]
1460 fn test_function_context() {
1461 let mut ctx = FunctionContext::new();
1462
1463 ctx.save_value(42i32);
1465 ctx.save_value(3.14f64);
1466
1467 assert_eq!(*ctx.get_saved_value::<i32>(0).unwrap(), 42);
1468 assert_eq!(*ctx.get_saved_value::<f64>(1).unwrap(), 3.14);
1469
1470 assert!(ctx.get_saved_value::<f32>(0).is_err());
1472 }
1473
1474 #[test]
1475 fn test_function_serialization() {
1476 use super::deployment::create_function_package;
1477
1478 let metadata = FunctionMetadata {
1480 name: "test_function".to_string(),
1481 is_differentiable: true,
1482 memory_complexity: MemoryComplexity::Linear,
1483 computational_complexity: ComputationalComplexity::Linear,
1484 is_fusable: false,
1485 version: "1.0.0".to_string(),
1486 description: "Test function for serialization".to_string(),
1487 author: "Test Author".to_string(),
1488 created_at: "1640995200".to_string(), checksum: "".to_string(),
1490 dependencies: vec!["torsh-autograd".to_string()],
1491 };
1492
1493 let function_data = vec![1, 2, 3, 4, 5];
1495
1496 let package = create_function_package(metadata.clone(), function_data.clone());
1498
1499 assert!(package.verify().is_ok());
1501
1502 assert_eq!(package.metadata.name, "test_function");
1504 assert_eq!(package.metadata.version, "1.0.0");
1505 assert_eq!(package.function_data, function_data);
1506 }
1507
1508 #[test]
1509 fn test_function_deployment_builder() {
1510 use super::deployment::*;
1511
1512 let builder = FunctionDeploymentBuilder::new("test_func".to_string())
1513 .version("2.0.0".to_string())
1514 .description("Test function".to_string())
1515 .author("Test Author".to_string())
1516 .data(vec![1, 2, 3])
1517 .dependency("test_dep".to_string())
1518 .memory_complexity(MemoryComplexity::Constant)
1519 .computational_complexity(ComputationalComplexity::Quadratic)
1520 .fusable(true)
1521 .differentiable(false);
1522
1523 let package = builder.build().unwrap();
1524
1525 assert_eq!(package.metadata.name, "test_func");
1526 assert_eq!(package.metadata.version, "2.0.0");
1527 assert_eq!(package.metadata.description, "Test function");
1528 assert_eq!(package.metadata.author, "Test Author");
1529 assert_eq!(package.function_data, vec![1, 2, 3]);
1530 assert_eq!(package.metadata.dependencies, vec!["test_dep"]);
1531 assert_eq!(
1532 package.metadata.memory_complexity,
1533 MemoryComplexity::Constant
1534 );
1535 assert_eq!(
1536 package.metadata.computational_complexity,
1537 ComputationalComplexity::Quadratic
1538 );
1539 assert!(package.metadata.is_fusable);
1540 assert!(!package.metadata.is_differentiable);
1541 }
1542
1543 #[test]
1544 fn test_function_library() {
1545 use super::deployment::*;
1546 use super::serialization::*;
1547
1548 let mut library = FunctionLibrary::new(
1549 "test_library".to_string(),
1550 "1.0.0".to_string(),
1551 "Test function library".to_string(),
1552 );
1553
1554 let package1 = FunctionDeploymentBuilder::new("func1".to_string())
1556 .data(vec![1, 2, 3])
1557 .build()
1558 .unwrap();
1559
1560 let package2 = FunctionDeploymentBuilder::new("func2".to_string())
1561 .data(vec![4, 5, 6])
1562 .build()
1563 .unwrap();
1564
1565 library.add_function(package1);
1567 library.add_function(package2);
1568 library.add_dependency("dep1".to_string());
1569 library.add_dependency("dep2".to_string());
1570
1571 assert_eq!(library.name, "test_library");
1573 assert_eq!(library.version, "1.0.0");
1574 assert_eq!(library.functions.len(), 2);
1575 assert_eq!(library.dependencies, vec!["dep1", "dep2"]);
1576
1577 let function_names = library.list_functions();
1579 assert!(function_names.contains(&"func1".to_string()));
1580 assert!(function_names.contains(&"func2".to_string()));
1581 }
1582
1583 #[test]
1584 fn test_function_serialization_implementations() {
1585 use super::serialization::*;
1586 use super::subgradient_functions::*;
1587
1588 let abs_func = AbsFunction;
1590 let serialized = abs_func.serialize().unwrap();
1591 let _deserialized = AbsFunction::deserialize(&serialized).unwrap();
1592
1593 let function_type: String = serde_json::from_slice(&serialized).unwrap();
1595 assert_eq!(function_type, "abs_function");
1596
1597 let relu_func = ReLUFunction;
1599 let serialized = relu_func.serialize().unwrap();
1600 let _deserialized = ReLUFunction::deserialize(&serialized).unwrap();
1601
1602 let function_type: String = serde_json::from_slice(&serialized).unwrap();
1603 assert_eq!(function_type, "relu_function");
1604
1605 let max_func = MaxFunction;
1607 let serialized = max_func.serialize().unwrap();
1608 let _deserialized = MaxFunction::deserialize(&serialized).unwrap();
1609
1610 let function_type: String = serde_json::from_slice(&serialized).unwrap();
1611 assert_eq!(function_type, "max_function");
1612
1613 let l1_func = L1NormFunction;
1615 let serialized = l1_func.serialize().unwrap();
1616 let _deserialized = L1NormFunction::deserialize(&serialized).unwrap();
1617
1618 let function_type: String = serde_json::from_slice(&serialized).unwrap();
1619 assert_eq!(function_type, "l1_norm_function");
1620 }
1621
1622 #[test]
1623 fn test_function_factory() {
1624 use super::deployment::create_function_package;
1625 use super::serialization::*;
1626 use super::subgradient_functions::*;
1627
1628 let metadata = FunctionMetadata {
1630 name: "test_abs".to_string(),
1631 is_differentiable: true,
1632 memory_complexity: MemoryComplexity::Linear,
1633 computational_complexity: ComputationalComplexity::Linear,
1634 is_fusable: true,
1635 version: "1.0.0".to_string(),
1636 description: "Test absolute value function".to_string(),
1637 author: "Test Author".to_string(),
1638 created_at: "2024-01-01T00:00:00Z".to_string(),
1639 checksum: "".to_string(),
1640 dependencies: vec![],
1641 };
1642
1643 let abs_func = AbsFunction;
1644 let function_data = abs_func.serialize().unwrap();
1645 let package = create_function_package(metadata, function_data);
1646
1647 let created_function = FunctionFactory::create_from_package(&package).unwrap();
1649
1650 let re_serialized = created_function.serialize().unwrap();
1652 let function_type: String = serde_json::from_slice(&re_serialized).unwrap();
1653 assert_eq!(function_type, "abs_function");
1654 }
1655
1656 #[test]
1657 fn test_function_package_from_serializable() {
1658 use super::serialization::*;
1659 use super::subgradient_functions::*;
1660
1661 let metadata = FunctionMetadata {
1662 name: "test_relu".to_string(),
1663 is_differentiable: true,
1664 memory_complexity: MemoryComplexity::Constant,
1665 computational_complexity: ComputationalComplexity::Linear,
1666 is_fusable: true,
1667 version: "1.0.0".to_string(),
1668 description: "Test ReLU function".to_string(),
1669 author: "Test Author".to_string(),
1670 created_at: "2024-01-01T00:00:00Z".to_string(),
1671 checksum: "".to_string(),
1672 dependencies: vec![],
1673 };
1674
1675 let relu_func = ReLUFunction;
1676 let package = FunctionFactory::create_package_from_function(&relu_func, metadata).unwrap();
1677
1678 assert!(package.verify().is_ok());
1680 assert_eq!(package.metadata.name, "test_relu");
1681
1682 let recreated_function = FunctionFactory::create_from_package(&package).unwrap();
1684 let serialized_again = recreated_function.serialize().unwrap();
1685 let function_type: String = serde_json::from_slice(&serialized_again).unwrap();
1686 assert_eq!(function_type, "relu_function");
1687 }
1688}