Skip to content

Commit 02dc6d0

Browse files
authored
Merge pull request #29 from Advaitgaur004/operator-6
[Feat] : Implement Tensor_abs operator and tests
2 parents a40ecf3 + ec9b7f8 commit 02dc6d0

File tree

4 files changed

+267
-0
lines changed

4 files changed

+267
-0
lines changed

src/operator.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,5 +581,37 @@ Tensor Tensor_min(Tensor self) {
581581
res.node->name = "MinAll";
582582
}
583583

584+
return res;
585+
}
586+
587+
static Tensor GradFn_abs(Tensor self, int i) {
588+
Tensor input = self.node->inputs[i];
589+
Tensor res = Tensor_new(input.shape, false);
590+
for(int j = 0; j < input.data->numel; j++) {
591+
float val = input.data->flex[j];
592+
if (val > 0) {
593+
res.data->flex[j] = 1.0f;
594+
} else if (val < 0) {
595+
res.data->flex[j] = -1.0f;
596+
} else {
597+
res.data->flex[j] = 0.0f;
598+
}
599+
}
600+
return res;
601+
}
602+
603+
Tensor Tensor_abs(Tensor self) {
604+
bool requires_grad = !cten_is_eval() && self.node != NULL;
605+
Tensor res = Tensor_new(self.shape, requires_grad);
606+
for(int i = 0; i < self.data->numel; i++) {
607+
res.data->flex[i] = fabsf(self.data->flex[i]);
608+
}
609+
610+
if(requires_grad) {
611+
res.node->grad_fn = GradFn_abs;
612+
res.node->inputs[0] = self;
613+
res.node->n_inputs = 1;
614+
res.node->name = "Abs";
615+
}
584616
return res;
585617
}

tests/Backward/test_abs_backward.c

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#include "../../include/cten.h"
2+
#include "../test_utils.h"
3+
#include "../csv_reporter.h"
4+
#include "../test_config.h"
5+
#include <stdio.h>
6+
7+
void test_abs_backward() {
8+
const char* op_name = "abs_backward";
9+
PoolId pool_id = 0;
10+
cten_begin_malloc(pool_id);
11+
12+
// Test Case 1: Simple backward
13+
{
14+
const char* tc_name = "Simple_backward";
15+
// Sub-test 1: Scalar backward
16+
{
17+
TensorShape s_shape = {1};
18+
float d1[] = {-5.0f};
19+
float exp_grad[] = {-1.0f};
20+
21+
Tensor t1 = create_test_tensor(s_shape, d1, true);
22+
Tensor z = Tensor_abs(t1);
23+
24+
Tensor_backward(z, (Tensor){0});
25+
26+
Tensor expected_grad = create_test_tensor(s_shape, exp_grad, false);
27+
compare_tensors(&t1.node->grad, &expected_grad, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
28+
}
29+
30+
// Sub-test 2: Vector with mixed values
31+
{
32+
TensorShape v_shape = {5};
33+
float d1[] = {10.0f, -2.0f, 0.0f, 5.5f, -0.1f};
34+
float exp_grad[] = {1.0f, -1.0f, 0.0f, 1.0f, -1.0f};
35+
36+
Tensor t1 = create_test_tensor(v_shape, d1, true);
37+
Tensor z = Tensor_abs(t1);
38+
Tensor l = Tensor_sum(z);
39+
40+
Tensor_backward(l, (Tensor){0});
41+
42+
Tensor expected_grad = create_test_tensor(v_shape, exp_grad, false);
43+
compare_tensors(&t1.node->grad, &expected_grad, op_name, tc_name, 2, TEST_FLOAT_TOLERANCE);
44+
}
45+
46+
// Sub-test 3: Matrix backward
47+
{
48+
TensorShape m_shape = {2, 2};
49+
float d1[] = {1.0f, -2.0f, 0.0f, -4.0f};
50+
float exp_grad[] = {1.0f, -1.0f, 0.0f, -1.0f};
51+
52+
Tensor t1 = create_test_tensor(m_shape, d1, true);
53+
Tensor z = Tensor_abs(t1);
54+
Tensor l = Tensor_sum(z);
55+
56+
Tensor_backward(l, (Tensor){0});
57+
58+
Tensor expected_grad = create_test_tensor(m_shape, exp_grad, false);
59+
compare_tensors(&t1.node->grad, &expected_grad, op_name, tc_name, 3, TEST_FLOAT_TOLERANCE);
60+
}
61+
}
62+
63+
// Test Case 2: Multi-dimensional Tensor backward
64+
{
65+
const char* tc_name = "Multidim_backward";
66+
TensorShape shape_3d = {2, 2, 2};
67+
float data[] = {1.5f, -2.5f, 0.0f, -4.5f, 5.5f, 6.5f, -7.5f, -8.5f};
68+
float exp_grad[] = {1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f};
69+
70+
Tensor t = create_test_tensor(shape_3d, data, true);
71+
Tensor z = Tensor_abs(t);
72+
Tensor l = Tensor_sum(z);
73+
Tensor_backward(l, (Tensor){0});
74+
75+
Tensor expected_grad = create_test_tensor(shape_3d, exp_grad, false);
76+
compare_tensors(&t.node->grad, &expected_grad, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
77+
}
78+
79+
80+
// Test Case 3: Chained and Complex Graphs
81+
{
82+
const char* tc_name = "Chained_and_Complex_Graphs_backward";
83+
// Sub-test 1: z = abs(x) * w
84+
{
85+
TensorShape shape = {2};
86+
float x_data[] = {-2.0f, 3.0f};
87+
float w_data[] = {5.0f, 10.0f};
88+
// z = abs(x)*w = {2, 3} * {5, 10} = {10, 30}
89+
// l = sum(z) = 40
90+
// dl/dx = (dl/dz) * (dz/dx) = {1, 1} * (w * sign(x)) = {5*(-1), 10*1} = {-5, 10}
91+
// dl/dw = (dl/dz) * (dz/dw) = {1, 1} * abs(x) = {2, 3}
92+
float exp_grad_x[] = {-5.0f, 10.0f};
93+
float exp_grad_w[] = {2.0f, 3.0f};
94+
95+
Tensor x = create_test_tensor(shape, x_data, true);
96+
Tensor w = create_test_tensor(shape, w_data, true);
97+
98+
Tensor abs_x = Tensor_abs(x);
99+
Tensor prod = Tensor_mul(abs_x, w);
100+
Tensor l = Tensor_sum(prod);
101+
102+
Tensor_backward(l, (Tensor){0});
103+
104+
Tensor expected_grad_x = create_test_tensor(shape, exp_grad_x, false);
105+
Tensor expected_grad_w = create_test_tensor(shape, exp_grad_w, false);
106+
107+
compare_tensors(&x.node->grad, &expected_grad_x, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
108+
compare_tensors(&w.node->grad, &expected_grad_w, op_name, tc_name, 2, TEST_FLOAT_TOLERANCE);
109+
}
110+
111+
// Sub-test 2: z = abs(x * w)
112+
{
113+
TensorShape shape = {2};
114+
float x_data[] = {-2.0f, 3.0f};
115+
float w_data[] = {5.0f, -1.0f};
116+
// y = x*w = {-10, -3}
117+
// z = abs(y) = {10, 3}
118+
// l = sum(z) = 13
119+
// dl/dx = (dl/dz)(dz/dy)(dy/dx) = {1,1} * sign(y) * w = {-1,-1} * {5,-1} = {-5, 1}
120+
// dl/dw = (dl/dz)(dz/dy)(dy/dw) = {1,1} * sign(y) * x = {-1,-1} * {-2,3} = {2, -3}
121+
float exp_grad_x[] = {-5.0f, 1.0f};
122+
float exp_grad_w[] = {2.0f, -3.0f};
123+
124+
Tensor x = create_test_tensor(shape, x_data, true);
125+
Tensor w = create_test_tensor(shape, w_data, true);
126+
127+
Tensor prod = Tensor_mul(x, w);
128+
Tensor abs_prod = Tensor_abs(prod);
129+
Tensor l = Tensor_sum(abs_prod);
130+
131+
Tensor_backward(l, (Tensor){0});
132+
133+
Tensor expected_grad_x = create_test_tensor(shape, exp_grad_x, false);
134+
Tensor expected_grad_w = create_test_tensor(shape, exp_grad_w, false);
135+
136+
compare_tensors(&x.node->grad, &expected_grad_x, op_name, tc_name, 3, TEST_FLOAT_TOLERANCE);
137+
compare_tensors(&w.node->grad, &expected_grad_w, op_name, tc_name, 4, TEST_FLOAT_TOLERANCE);
138+
}
139+
}
140+
141+
cten_free(pool_id);
142+
}

tests/Operator/test_abs.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "../../include/cten.h"
2+
#include "../test_utils.h"
3+
#include "../csv_reporter.h"
4+
#include "../test_config.h"
5+
#include <stdio.h>
6+
#include <math.h>
7+
8+
void test_abs_operator() {
9+
const char* op_name = "abs";
10+
PoolId pool_id = 0;
11+
cten_begin_malloc(pool_id);
12+
13+
// Test Case 1: Basic test with mixed positive, negative and zero values
14+
{
15+
const char* tc_name = "abs_basic_mixed_values";
16+
TensorShape shape = {6};
17+
float d1[] = {-2.5f, -1.0f, 0.0f, 1.0f, 2.5f, -3.0f};
18+
float exp_d[] = {2.5f, 1.0f, 0.0f, 1.0f, 2.5f, 3.0f};
19+
20+
Tensor t1 = create_test_tensor(shape, d1, false);
21+
Tensor expected_res = create_test_tensor(shape, exp_d, false);
22+
Tensor actual_res = Tensor_abs(t1);
23+
24+
compare_tensors(&actual_res, &expected_res, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
25+
}
26+
27+
// Test Case 2: All negative values
28+
{
29+
const char* tc_name = "abs_all_negative";
30+
TensorShape shape = {4};
31+
float d1[] = {-1.5f, -2.0f, -0.5f, -10.0f};
32+
float exp_d[] = {1.5f, 2.0f, 0.5f, 10.0f};
33+
34+
Tensor t1 = create_test_tensor(shape, d1, false);
35+
Tensor expected_res = create_test_tensor(shape, exp_d, false);
36+
Tensor actual_res = Tensor_abs(t1);
37+
38+
compare_tensors(&actual_res, &expected_res, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
39+
}
40+
41+
// Test Case 3: All positive values (should be unchanged)
42+
{
43+
const char* tc_name = "abs_all_positive";
44+
TensorShape shape = {3};
45+
float d1[] = {1.0f, 5.5f, 0.1f};
46+
float exp_d[] = {1.0f, 5.5f, 0.1f};
47+
48+
Tensor t1 = create_test_tensor(shape, d1, false);
49+
Tensor expected_res = create_test_tensor(shape, exp_d, false);
50+
Tensor actual_res = Tensor_abs(t1);
51+
52+
compare_tensors(&actual_res, &expected_res, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
53+
}
54+
55+
// Test Case 4: 2D Tensor (Matrix)
56+
{
57+
const char* tc_name = "abs_matrix_2x3";
58+
TensorShape m_shape = {2, 3};
59+
float d1[] = {1.0f, -2.0f, 0.0f, -4.0f, 5.0f, -6.0f};
60+
float exp_d[] = {1.0f, 2.0f, 0.0f, 4.0f, 5.0f, 6.0f};
61+
62+
Tensor t1 = create_test_tensor(m_shape, d1, false);
63+
Tensor expected_res = create_test_tensor(m_shape, exp_d, false);
64+
Tensor actual_res = Tensor_abs(t1);
65+
66+
compare_tensors(&actual_res, &expected_res, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
67+
}
68+
69+
// Test Case 5: Large and near-zero values
70+
{
71+
const char* tc_name = "abs_large_and_near_zero";
72+
TensorShape shape = {5};
73+
float d1[] = {-1e8f, 1e-8f, 0.0f, 1e8f, -1e-8f};
74+
float exp_d[] = {1e8f, 1e-8f, 0.0f, 1e8f, 1e-8f};
75+
76+
Tensor t1 = create_test_tensor(shape, d1, false);
77+
Tensor expected_res = create_test_tensor(shape, exp_d, false);
78+
Tensor actual_res = Tensor_abs(t1);
79+
80+
compare_tensors(&actual_res, &expected_res, op_name, tc_name, 1, TEST_FLOAT_TOLERANCE);
81+
}
82+
83+
cten_free(pool_id);
84+
}

tests/cten_tests.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ void test_square_operator();
2727
void test_div_operator();
2828
void test_max_operator();
2929
void test_min_operator();
30+
void test_abs_operator();
3031

3132
// Backward tests
3233
void test_add_backward();
@@ -41,6 +42,7 @@ void test_sum_backward();
4142
void test_mean_backward();
4243
void test_div_backward();
4344
void test_pow_backward();
45+
void test_abs_backward();
4446

4547
int main() {
4648
printf("Starting cTensor Test Suite on %s...\n", PLATFORM_NAME);
@@ -110,6 +112,9 @@ int main() {
110112
test_min_operator();
111113
printf("Min operator tests finished.\n");
112114

115+
test_abs_operator();
116+
printf("Abs operator tests finished.\n");
117+
113118
// Backward tests
114119
test_add_backward();
115120
printf("Add backward tests finished.\n");
@@ -146,6 +151,10 @@ int main() {
146151

147152
test_pow_backward();
148153
printf("Pow backward tests finished.\n");
154+
155+
test_abs_backward();
156+
printf("Abs backward tests finished.\n");
157+
149158
// other tests
150159

151160
csv_reporter_close();

0 commit comments

Comments
 (0)