hedgefund

c_autograd_01

Nov 27th, 2024
231
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 4.80 KB | Software | 0 0
  1. #include <stddef.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <math.h>
  5. #include <string.h>
  6. #include <time.h>
  7.  
  8. typedef struct {
  9.     double val;
  10.     double grad;
  11. } NaiveVar;
  12.  
  13. typedef enum {
  14.     OP_SUM,
  15.     OP_PROD,
  16.     OP_SOFTMAX,
  17.     OP_SOFTPLUS
  18. } OpType;
  19.  
  20. typedef struct {
  21.     OpType type;
  22.     int n_inputs;
  23.     NaiveVar *inputs[8];
  24.     NaiveVar *outputs[8];
  25. } MyOperation;
  26.  
  27. typedef struct {
  28.     int n_ops;
  29.     int max_ops;
  30.     MyOperation *ops;  // Changed from MyOperation** to MyOperation*
  31.     int ops_capacity;
  32. } NaiveTape;
  33.  
  34. // Use a memory pool for NaiveVar allocations
  35. #define POOL_SIZE 1000
  36. static NaiveVar var_pool[POOL_SIZE];
  37. static int pool_index = 0;
  38.  
  39. inline NaiveVar* naive_var(double val) {
  40.     NaiveVar* var = &var_pool[pool_index++];
  41.     var->val = val;
  42.     var->grad = 0.0;
  43.     return var;
  44. }
  45.  
  46. NaiveTape* naive_tape_create(int max_ops) {
  47.     NaiveTape *tape = (NaiveTape*) malloc(sizeof(NaiveTape));
  48.     tape->n_ops = 0;
  49.     tape->max_ops = max_ops;
  50.     tape->ops = (MyOperation*) calloc(max_ops, sizeof(MyOperation));
  51.     tape->ops_capacity = max_ops;
  52.     return tape;
  53. }
  54.  
  55. inline void naive_tape_add_op(NaiveTape *tape, MyOperation *op) {
  56.     if (tape->n_ops >= tape->max_ops) {
  57.         tape->max_ops *= 2;
  58.         tape->ops = (MyOperation*) realloc(tape->ops, tape->max_ops * sizeof(MyOperation));
  59.     }
  60.     memcpy(&tape->ops[tape->n_ops++], op, sizeof(MyOperation));
  61. }
  62.  
  63. inline NaiveVar* naive_tape_sum(NaiveTape *tape, NaiveVar **vars, int n_vars) {
  64.     double sum_val = 0.0;
  65.     #pragma unroll
  66.     for (int i = 0; i < n_vars; i++) {
  67.         sum_val += vars[i]->val;
  68.     }
  69.    
  70.     NaiveVar *res = naive_var(sum_val);
  71.     MyOperation op;
  72.     op.type = OP_SUM;
  73.     op.n_inputs = n_vars;
  74.    
  75.     memcpy(op.inputs, vars, n_vars * sizeof(NaiveVar*));
  76.     op.outputs[0] = res;
  77.     naive_tape_add_op(tape, &op);
  78.     return res;
  79. }
  80.  
  81. inline NaiveVar* naive_tape_prod(NaiveTape *tape, NaiveVar *var1, NaiveVar *var2) {
  82.     NaiveVar *res = naive_var(var1->val * var2->val);
  83.     MyOperation op;
  84.     op.type = OP_PROD;
  85.     op.n_inputs = 2;
  86.     op.inputs[0] = var1;
  87.     op.inputs[1] = var2;
  88.     op.outputs[0] = res;
  89.     naive_tape_add_op(tape, &op);
  90.     return res;
  91. }
  92.  
  93. inline NaiveVar* naive_tape_softplus(NaiveTape *tape, NaiveVar *var) {
  94.     NaiveVar *res = naive_var(log1p(exp(var->val)));
  95.     MyOperation op;
  96.     op.type = OP_SOFTPLUS;
  97.     op.n_inputs = 1;
  98.     op.inputs[0] = var;
  99.     op.outputs[0] = res;
  100.     naive_tape_add_op(tape, &op);
  101.     return res;
  102. }
  103.  
  104. void naive_tape_backward(NaiveTape *tape, NaiveVar *var) {
  105.     var->grad = 1.0;
  106.    
  107.     MyOperation *op;
  108.     for (int i = tape->n_ops - 1; i >= 0; i--) {
  109.         op = &tape->ops[i];
  110.        
  111.         switch (op->type) {
  112.             case OP_SUM: {
  113.                 double grad = op->outputs[0]->grad;
  114.                 #pragma unroll
  115.                 for (int j = 0; j < op->n_inputs; j++) {
  116.                     op->inputs[j]->grad += grad;
  117.                 }
  118.                 break;
  119.             }
  120.             case OP_PROD: {
  121.                 double grad = op->outputs[0]->grad;
  122.                 NaiveVar *in1 = op->inputs[0];
  123.                 NaiveVar *in2 = op->inputs[1];
  124.                 in1->grad += in2->val * grad;
  125.                 in2->grad += in1->val * grad;
  126.                 break;
  127.             }
  128.             case OP_SOFTPLUS:
  129.                 op->inputs[0]->grad += op->outputs[0]->grad / (1.0 + exp(-op->inputs[0]->val));
  130.                 break;
  131.         }
  132.     }
  133. }
  134.  
  135. void naive_tape_free(NaiveTape *tape) {
  136.     free(tape->ops);
  137.     free(tape);
  138. }
  139.  
  140. int main() {
  141.     clock_t start, end;
  142.     double elapsed_time;
  143.     size_t iterations = 1000000;
  144.  
  145.     start = clock();
  146.     for (size_t i = 0; i < iterations; i++) {
  147.         pool_index = 0;  // Reset pool index for each iteration
  148.        
  149.         NaiveVar *x = naive_var(1.0);
  150.         NaiveVar *y = naive_var(2.0);
  151.         NaiveTape *tape = naive_tape_create(10);
  152.  
  153.         NaiveVar *vars[] = {x, y};
  154.         NaiveVar *sum_xy = naive_tape_sum(tape, vars, 2);
  155.         NaiveVar *prod_sum_xy = naive_tape_prod(tape, sum_xy, sum_xy);
  156.         NaiveVar *softplus_prod = naive_tape_softplus(tape, prod_sum_xy);
  157.  
  158.         naive_tape_backward(tape, softplus_prod);
  159.  
  160.         if (i == iterations - 1) {
  161.             printf("sum_xy->val: %f\n", sum_xy->val);
  162.             printf("prod_sum_xy->val: %f\n", prod_sum_xy->val);
  163.             printf("softplus_prod->val: %f\n", softplus_prod->val);
  164.             printf("sum_xy->grad: %f\n", sum_xy->grad);
  165.             printf("x->grad: %f\n", x->grad);
  166.             printf("y->grad: %f\n", y->grad);
  167.         }
  168.  
  169.         naive_tape_free(tape);
  170.     }
  171.     end = clock();
  172.     elapsed_time = ((double) (end - start)) * 1000 / CLOCKS_PER_SEC;
  173.     printf("\nElapsed time: %f ms\n", elapsed_time);
  174.  
  175.     return 0;
  176. }
  177.  
Advertisement
Add Comment
Please, Sign In to add comment