Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ddfd343

Browse files
authoredMay 14, 2025
Merge pull request #9 from Advaitgaur004/main
Fixed Main.c
2 parents 02b89bb + ac1deb4 commit ddfd343

File tree

7 files changed

+394
-57
lines changed

7 files changed

+394
-57
lines changed
 

‎build.bat

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@echo off
2+
if not exist build mkdir build
3+
cd build
4+
cmake .. -G "MinGW Makefiles"
5+
cmake --build .
6+
cd ..

‎include/cten.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ typedef struct GradNode {
2424
struct Tensor (*grad_fn)(struct Tensor self, int i);
2525
struct Tensor inputs[4];
2626
int n_inputs;
27+
const char* name;
2728
} GradNode;
2829

2930
void cten_initilize();
@@ -39,11 +40,10 @@ int TensorShape_tostring(TensorShape shape, char* buf, int size);
3940
Tensor Tensor_new(TensorShape shape, bool requires_grad);
4041
Tensor Tensor_zeros(TensorShape shape, bool requires_grad);
4142
Tensor Tensor_ones(TensorShape shape, bool requires_grad);
43+
Tensor Tensor_transpose(Tensor self);
4244

4345
float Tensor_get(Tensor self, int i, int j, int k, int l);
4446
void Tensor_set(Tensor self, int i, int j, int k, int l, float value);
45-
46-
Tensor Tensor_detach(Tensor self);
4747
void Tensor_backward(Tensor self, Tensor grad);
4848
int Tensor_backward_apply(Tensor self, void (*f)(Tensor, void*), void* ctx);
4949

@@ -87,8 +87,9 @@ Tensor nn_relu(Tensor input);
8787
Tensor nn_sigmoid(Tensor input);
8888
Tensor nn_tanh(Tensor input);
8989
Tensor nn_softmax(Tensor input);
90-
90+
Tensor Glorot_init(TensorShape shape, bool requires_grad);
9191
Tensor nn_crossentropy(Tensor y_true, Tensor y_pred);
92+
Tensor nn_softmax_crossentropy(Tensor y_true, Tensor logits);
9293

9394
/* Memory Management */
9495
typedef int64_t PoolId;
@@ -111,6 +112,9 @@ void cten_begin_eval();
111112
bool cten_is_eval();
112113
void cten_end_eval();
113114

115+
/* Utils */
116+
void Tensor_normalize_dataset(const float (*X)[4], float (*X_norm)[4], int n_samples, int n_train_samples, int n_features);Tensor Tensor_detach(Tensor self);
117+
void Tensor_shuffle_dataset(const float (*X)[4], const int *y,float (*X_shuffled)[4], int *y_shuffled, int n_samples, int n_features);
114118
void cten_assert(bool cond, const char* fmt, ...);
115119
void cten_assert_shape(const char* title, TensorShape a, TensorShape b);
116120
void cten_assert_dim(const char* title, int a, int b);

‎src/basic.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <stdio.h>
66
#include <stdlib.h>
77
#include <string.h>
8+
#include <math.h>
9+
#include <time.h>
810

911
int TensorShape_numel(TensorShape shape) {
1012
int numel = 1;
@@ -39,6 +41,13 @@ Tensor Tensor_new(TensorShape shape, bool requires_grad) {
3941
int numel = TensorShape_numel(shape);
4042
self.data = _cten_malloc(sizeof(FloatBuffer) + sizeof(float) * numel);
4143
self.data->numel = numel;
44+
45+
//Initialize tensor with random values
46+
float* data_ptr = self.data->flex;
47+
for (int i = 0; i < numel; i++) {
48+
data_ptr[i] = ((float)rand() / RAND_MAX) * 2.0f - 1.0f;
49+
}
50+
4251
if(requires_grad) {
4352
self.node = _cten_malloc(sizeof(GradNode));
4453
memset(self.node, 0, sizeof(GradNode));
@@ -61,6 +70,27 @@ Tensor Tensor_ones(TensorShape shape, bool requires_grad) {
6170
}
6271
return self;
6372
}
73+
Tensor Tensor_transpose(Tensor self) {
74+
int dim = TensorShape_dim(self.shape);
75+
if(dim < 2){
76+
return self;
77+
}
78+
TensorShape new_shape;
79+
new_shape[0] = self.shape[1];
80+
new_shape[1] = self.shape[0];
81+
for(int i = 2; i < 4; i++) {
82+
new_shape[i] = self.shape[i];
83+
}
84+
Tensor result = Tensor_new(new_shape, false);
85+
int rows = self.shape[0];
86+
int cols = self.shape[1];
87+
for(int i = 0; i < rows; i++) {
88+
for(int j = 0; j < cols; j++) {
89+
result.data->flex[j * rows + i] = self.data->flex[i * cols + j];
90+
}
91+
}
92+
return result;
93+
}
6494

6595
float Tensor_get(Tensor self, int i, int j, int k, int l) {
6696
assert((self.shape[0] == 0 && i == 0) || (i >= 0 && i < self.shape[0]));
@@ -90,17 +120,32 @@ void Tensor_backward(Tensor self, Tensor grad) {
90120
if(self.node == NULL) return;
91121
if(grad.data == NULL) {
92122
assert(self.data->numel == 1);
93-
grad = Tensor_ones((TensorShape){0}, false);
123+
grad = Tensor_ones((TensorShape){1}, false);
94124
}
125+
95126
assert(grad.node == NULL);
96127
if(self.node->grad.data == NULL) {
97128
self.node->grad = grad;
98129
} else {
99130
self.node->grad = Tensor_add(self.node->grad, grad);
100131
}
132+
101133
for(int i = 0; i < self.node->n_inputs; i++) {
102-
grad = Tensor_mul(grad, self.node->grad_fn(self, i));
103-
Tensor_backward(self.node->inputs[i], grad);
134+
if (self.node->inputs[i].data == NULL) continue;
135+
Tensor combined_grad;
136+
Tensor input_grad = self.node->grad_fn(self, i);
137+
if(strcmp(self.node->name, "Matmul") == 0){
138+
if (i == 0){
139+
combined_grad = Tensor_matmul(grad, input_grad);
140+
}
141+
else{
142+
combined_grad = Tensor_matmul(input_grad, grad);
143+
}
144+
}
145+
else{
146+
combined_grad = Tensor_mul(grad, input_grad);
147+
}
148+
Tensor_backward(self.node->inputs[i], combined_grad);
104149
}
105150
}
106151

‎src/nn.c

Lines changed: 163 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include <assert.h>
55
#include <math.h>
66
#include <stddef.h>
7+
#include <stdlib.h>
8+
#include <time.h>
9+
#include <stdio.h>
710

811
Tensor nn_linear(Tensor input, Tensor weight, Tensor bias) {
912
Tensor tmp = Tensor_matmul(input, weight);
@@ -16,14 +19,14 @@ static Tensor GradFn_relu(Tensor self, int i) {
1619
Tensor input = self.node->inputs[i];
1720
Tensor res = Tensor_new(input.shape, false);
1821
for(int i = 0; i < input.data->numel; i++) {
19-
res.data->flex[i] = input.data->flex[i] > 0 ? 1 : 0;
22+
res.data->flex[i] = input.data->flex[i] > 0 ? 1.0f : 0.0f;
2023
}
2124
return res;
2225
}
2326

2427
Tensor nn_relu(Tensor self) {
2528
bool requires_grad = !cten_is_eval() && self.node != NULL;
26-
Tensor res = Tensor_new(self.shape, requires_grad);
29+
Tensor res = Tensor_zeros(self.shape, requires_grad);
2730
for(int i = 0; i < self.data->numel; i++) {
2831
res.data->flex[i] = fmaxf(0, self.data->flex[i]);
2932
}
@@ -32,23 +35,52 @@ Tensor nn_relu(Tensor self) {
3235
res.node->grad_fn = GradFn_relu;
3336
res.node->inputs[0] = self;
3437
res.node->n_inputs = 1;
38+
res.node->name = "Relu";
39+
40+
}
41+
return res;
42+
}
43+
44+
Tensor Glorot_init(TensorShape shape, bool requires_grad) {
45+
Tensor res = Tensor_new(shape, requires_grad);
46+
int fan_in = shape[0];
47+
int fan_out = shape[1];
48+
float scale = sqrtf(6.0f / (fan_in + fan_out));
49+
50+
for(int i = 0; i < res.data->numel; i++) {
51+
float r = (float)rand() / RAND_MAX * 2.0f - 1.0f;
52+
res.data->flex[i] = r * scale;
3553
}
3654
return res;
3755
}
3856

39-
/* nn.softmax */
4057
static Tensor GradFn_softmax(Tensor self, int i) {
4158
Tensor input = self.node->inputs[i];
42-
Tensor res = Tensor_new(input.shape, false);
43-
for(int j = 0; j < input.data->numel; j++) {
44-
float softmax_j = self.data->flex[j];
45-
for(int k = 0; k < input.data->numel; k++) {
46-
float softmax_k = self.data->flex[k];
47-
float delta_jk = (j == k) ? 1.0f : 0.0f;
48-
res.data->flex[j * input.data->numel + k] = softmax_j * (delta_jk - softmax_k);
59+
Tensor grad = Tensor_new(input.shape, false);
60+
61+
int dim = TensorShape_dim(self.shape);
62+
int batch_size = self.shape[0];
63+
int num_classes = self.shape[1];
64+
for(int b = 0; b < batch_size; b++){
65+
for(int i = 0; i < num_classes; i++) {
66+
for(int j = 0; j < num_classes; j++) {
67+
float softmax_i = self.data->flex[b * num_classes + i];
68+
float softmax_j = self.data->flex[b * num_classes + j];
69+
float value;
70+
if(i == j){
71+
value = softmax_i * (1.0f - softmax_i);
72+
}
73+
else{
74+
value = -softmax_i * softmax_j;
75+
}
76+
77+
if(i == j){
78+
grad.data->flex[b * num_classes + i] = value;
79+
}
80+
}
4981
}
5082
}
51-
return res;
83+
return grad;
5284
}
5385

5486
Tensor nn_softmax(Tensor self) {
@@ -83,12 +115,38 @@ Tensor nn_softmax(Tensor self) {
83115
if(requires_grad) {
84116
res.node->grad_fn = GradFn_softmax;
85117
res.node->inputs[0] = self;
86-
res.node->n_inputs = 1;
118+
res.node->n_inputs = 1;
119+
res.node->name = "Softmax";
87120
}
88121
return res;
89122
}
90123

91124
/* nn.cross_entropy */
125+
static Tensor GradFn_crossentropy(Tensor self, int i) {
126+
if (i == 1) { // Gradient w.r.t. y_pred
127+
Tensor y_true = self.node->inputs[0];
128+
Tensor y_pred = self.node->inputs[1];
129+
int n_samples = y_true.shape[0];
130+
int n_classes = y_true.shape[1];
131+
132+
Tensor grad = Tensor_new(y_pred.shape, false);
133+
134+
for (int i = 0; i < n_samples; i++) {
135+
for (int j = 0; j < n_classes; j++) {
136+
float y_true_val = y_true.data->flex[i * n_classes + j];
137+
float y_pred_val = y_pred.data->flex[i * n_classes + j];
138+
if (y_true_val == 0) {
139+
grad.data->flex[i * n_classes + j] = 0;
140+
} else {
141+
grad.data->flex[i * n_classes + j] = -y_true_val / y_pred_val;
142+
}
143+
}
144+
}
145+
return grad;
146+
}
147+
return Tensor_zeros((TensorShape){1}, false);
148+
}
149+
92150
Tensor nn_crossentropy(Tensor y_true, Tensor y_pred) {
93151
// y_true: [None, n_classes]
94152
// y_pred: [None, n_classes]
@@ -100,15 +158,101 @@ Tensor nn_crossentropy(Tensor y_true, Tensor y_pred) {
100158
assert(n_samples == y_pred.shape[0]);
101159
assert(n_classes == y_pred.shape[1]);
102160

103-
bool requires_grad = !cten_is_eval() && (y_true.node != NULL || y_pred.node != NULL);
104-
Tensor res = Tensor_new((TensorShape){n_samples}, requires_grad);
161+
bool requires_grad = !cten_is_eval() && (y_true.node != NULL || y_pred.node != NULL); //No eval but rather training so requires grad is True
162+
Tensor res = Tensor_zeros((TensorShape){1}, requires_grad);
163+
164+
// Calculate cross-entropy loss
165+
float total_loss = 0.0f;
105166
for(int i = 0; i < n_samples; i++) {
106-
float loss = 0;
167+
float sample_loss = 0.0f;
107168
for(int j = 0; j < n_classes; j++) {
108-
loss +=
109-
y_true.data->flex[i * n_classes + j] * logf(y_pred.data->flex[i * n_classes + j]);
169+
float true_val = y_true.data->flex[i * n_classes + j];
170+
float pred_val = y_pred.data->flex[i * n_classes + j];
171+
float epsilon = 1e-8f; // avoid log(0) so we add a small epsilon
172+
if (true_val > 0) { // one-hot encoding
173+
sample_loss -= true_val * logf(pred_val + epsilon);
174+
}
110175
}
111-
res.data->flex[i] = -loss;
176+
total_loss += sample_loss;
177+
}
178+
179+
res.data->flex[0] = total_loss / n_samples;
180+
181+
if(requires_grad) {
182+
res.node->grad_fn = GradFn_crossentropy;
183+
res.node->inputs[0] = y_true;
184+
res.node->inputs[1] = y_pred;
185+
res.node->n_inputs = 2;
186+
res.node->name = "Cross-entropy";
112187
}
113-
return Tensor_mean(res);
188+
189+
return res;
190+
}
191+
192+
static Tensor GradFn_softmax_crossentropy(Tensor self, int i) {
193+
if (i == 1) {
194+
Tensor y_true = self.node->inputs[0];
195+
Tensor logits = self.node->inputs[1];
196+
197+
Tensor y_pred = Tensor_new(logits.shape, false);
198+
int self_dim = TensorShape_dim(logits.shape);
199+
int last_dim_size = logits.shape[self_dim - 1];
200+
int outer_size = logits.data->numel / last_dim_size;
201+
202+
for(int outer = 0; outer < outer_size; outer++) {
203+
float max_val = -INFINITY;
204+
float sum = 0;
205+
206+
for(int d = 0; d < last_dim_size; d++) {
207+
int index = outer * last_dim_size + d;
208+
max_val = fmaxf(max_val, logits.data->flex[index]);
209+
}
210+
211+
for(int d = 0; d < last_dim_size; d++) {
212+
int index = outer * last_dim_size + d;
213+
y_pred.data->flex[index] = expf(logits.data->flex[index] - max_val);
214+
sum += y_pred.data->flex[index];
215+
}
216+
217+
for(int d = 0; d < last_dim_size; d++) {
218+
int index = outer * last_dim_size + d;
219+
y_pred.data->flex[index] /= sum;
220+
}
221+
}
222+
223+
Tensor grad = Tensor_new(y_pred.shape, false);
224+
int n_samples = y_pred.shape[0];
225+
int n_classes = y_pred.shape[1];
226+
227+
for (int i = 0; i < n_samples; i++) {
228+
for (int j = 0; j < n_classes; j++) {
229+
grad.data->flex[i * n_classes + j] =
230+
y_pred.data->flex[i * n_classes + j] - y_true.data->flex[i * n_classes + j];
231+
}
232+
}
233+
234+
return grad;
235+
}
236+
return Tensor_zeros((TensorShape){1}, false);
237+
}
238+
239+
Tensor nn_softmax_crossentropy(Tensor y_true, Tensor logits) {
240+
bool requires_grad = !cten_is_eval() && logits.node != NULL;
241+
//disable gradient computation
242+
cten_begin_eval();
243+
Tensor y_pred = nn_softmax(logits);
244+
Tensor loss = nn_crossentropy(y_true, y_pred);
245+
cten_end_eval();
246+
Tensor res = Tensor_zeros((TensorShape){1}, requires_grad);
247+
res.data->flex[0] = loss.data->flex[0];
248+
249+
if(requires_grad) {
250+
res.node->grad_fn = GradFn_softmax_crossentropy;
251+
res.node->inputs[0] = y_true;
252+
res.node->inputs[1] = logits;
253+
res.node->n_inputs = 2;
254+
res.node->name = "SoftmaxCrossEntropy";
255+
}
256+
257+
return res;
114258
}

0 commit comments

Comments
 (0)
Please sign in to comment.