hedgefund

rust_autograd_01

Nov 27th, 2024
353
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 4.14 KB | Software | 0 0
  1. use std::time::Instant;
  2.  
  3. // Define the NaiveVar struct
  4. #[derive(Debug, Clone, Copy)]
  5. struct NaiveVar {
  6.     val: f64,
  7.     grad: f64,
  8. }
  9.  
  10. // Define operation types
  11. #[derive(Clone, Copy)]
  12. enum OpType {
  13.     Sum,
  14.     Prod,
  15.     Softplus,
  16. }
  17.  
  18. // Define the Operation struct
  19. struct Operation {
  20.     op_type: OpType,
  21.     input_indices: Vec<usize>,
  22.     output_index: usize,
  23. }
  24.  
  25. // Define the NaiveTape struct
  26. struct NaiveTape {
  27.     ops: Vec<Operation>,
  28.     vars: Vec<NaiveVar>,
  29. }
  30.  
  31. impl NaiveTape {
  32.     fn new() -> Self {
  33.         NaiveTape {
  34.             ops: Vec::with_capacity(16),
  35.             vars: Vec::with_capacity(16),
  36.         }
  37.     }
  38.  
  39.     fn add_var(&mut self, val: f64) -> usize {
  40.         let idx = self.vars.len();
  41.         self.vars.push(NaiveVar { val, grad: 0.0 });
  42.         idx
  43.     }
  44.  
  45.     fn sum(&mut self, input_indices: Vec<usize>) -> usize {
  46.         let sum_val: f64 = input_indices.iter()
  47.             .map(|&idx| self.vars[idx].val)
  48.             .sum();
  49.         let output_index = self.add_var(sum_val);
  50.        
  51.         self.ops.push(Operation {
  52.             op_type: OpType::Sum,
  53.             input_indices,
  54.             output_index,
  55.         });
  56.         output_index
  57.     }
  58.  
  59.     fn prod(&mut self, input1_idx: usize, input2_idx: usize) -> usize {
  60.         let prod_val = self.vars[input1_idx].val * self.vars[input2_idx].val;
  61.         let output_index = self.add_var(prod_val);
  62.        
  63.         self.ops.push(Operation {
  64.             op_type: OpType::Prod,
  65.             input_indices: vec![input1_idx, input2_idx],
  66.             output_index,
  67.         });
  68.         output_index
  69.     }
  70.  
  71.     fn softplus(&mut self, input_idx: usize) -> usize {
  72.         let softplus_val = (self.vars[input_idx].val.exp() + 1.0).ln();
  73.         let output_index = self.add_var(softplus_val);
  74.        
  75.         self.ops.push(Operation {
  76.             op_type: OpType::Softplus,
  77.             input_indices: vec![input_idx],
  78.             output_index,
  79.         });
  80.         output_index
  81.     }
  82.  
  83.     fn backward(&mut self, output_idx: usize) {
  84.         self.vars[output_idx].grad = 1.0;
  85.        
  86.         for op in self.ops.iter().rev() {
  87.             let output_grad = self.vars[op.output_index].grad;
  88.            
  89.             match op.op_type {
  90.                 OpType::Sum => {
  91.                     for &input_idx in &op.input_indices {
  92.                         self.vars[input_idx].grad += output_grad;
  93.                     }
  94.                 }
  95.                 OpType::Prod => {
  96.                     let input1_val = self.vars[op.input_indices[0]].val;
  97.                     let input2_val = self.vars[op.input_indices[1]].val;
  98.                     self.vars[op.input_indices[0]].grad += input2_val * output_grad;
  99.                     self.vars[op.input_indices[1]].grad += input1_val * output_grad;
  100.                 }
  101.                 OpType::Softplus => {
  102.                     let input_val = self.vars[op.input_indices[0]].val;
  103.                     self.vars[op.input_indices[0]].grad += output_grad / (1.0 + (-input_val).exp());
  104.                 }
  105.             }
  106.         }
  107.     }
  108. }
  109.  
  110. fn main() {
  111.     let iterations: usize = 1000000;
  112.  
  113.     let start = Instant::now();
  114.     for i in 0..iterations {
  115.         let mut tape = NaiveTape::new();
  116.  
  117.         // Create variables
  118.         let var1_idx = tape.add_var(1.0);
  119.         let var2_idx = tape.add_var(2.0);
  120.  
  121.         // Build computational graph
  122.         let sum_idx = tape.sum(vec![var1_idx, var2_idx]);
  123.         let prod_idx = tape.prod(sum_idx, sum_idx);
  124.         let softplus_idx = tape.softplus(prod_idx);
  125.  
  126.         // Perform backward pass
  127.         tape.backward(softplus_idx);
  128.  
  129.         if i == iterations - 1 {
  130.             println!("sum_var val: {}", tape.vars[sum_idx].val);
  131.             println!("prod_var val: {}", tape.vars[prod_idx].val);
  132.             println!("softplus_var val: {}", tape.vars[softplus_idx].val);
  133.             println!("sum_var grad: {}", tape.vars[sum_idx].grad);
  134.             println!("var1 grad: {}", tape.vars[var1_idx].grad);
  135.             println!("var2 grad: {}", tape.vars[var2_idx].grad);
  136.         }
  137.     }
  138.     let duration = start.elapsed();
  139.     println!("\nElapsed time: {:?}\n", duration);
  140. }
  141.  
Advertisement
Add Comment
Please, Sign In to add comment