diff --git a/build/libtensor.so b/build/libtensor.so index d7ec513..7d6f264 100755 Binary files a/build/libtensor.so and b/build/libtensor.so differ diff --git a/csrc/tensor.cpp b/csrc/tensor.cpp index b3cfb3e..1a283ec 100644 --- a/csrc/tensor.cpp +++ b/csrc/tensor.cpp @@ -142,7 +142,7 @@ extern "C" { Tensor* sub_tensor(Tensor* tensor1, Tensor* tensor2) { printf("Adding tensor\n"); if (tensor1->ndim != tensor2->ndim) { - fprintf(stderr, "Tensors must have the same number of dimensions %d and %d for addition\n", tensor1->ndim, tensor2->ndim); + fprintf(stderr, "Tensors must have the same number of dimensions %d and %d for subtraction\n", tensor1->ndim, tensor2->ndim); exit(1); } @@ -193,7 +193,7 @@ extern "C" { for (int i = 0; i < ndim; i++) { if (tensor1->shape[i] != tensor2->shape[i]) { - fprintf(stderr, "Tensors must have the same shape %d and %d at index %d for addition\n", tensor1->shape[i], tensor2->shape[i], i); + fprintf(stderr, "Tensors must have the same shape %d and %d at index %d for subtraction\n", tensor1->shape[i], tensor2->shape[i], i); exit(1); } shape[i] = tensor1->shape[i]; @@ -211,4 +211,161 @@ extern "C" { return create_tensor(result_data, shape, ndim); } + + Tensor* elementwise_mul_tensor(Tensor* tensor1, Tensor* tensor2) { + printf("Adding tensor\n"); + if (tensor1->ndim != tensor2->ndim) { + fprintf(stderr, "Tensors must have the same number of dimensions %d and %d for element-wise multiplication\n", tensor1->ndim, tensor2->ndim); + exit(1); + } + + int ndim = tensor1->ndim; + int* shape = (int*)malloc(ndim * sizeof(int)); + if (shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + printf("Size: %d\n", tensor1->size); + printf("Data: ["); + for (int i = 0; i < tensor1->size; i++) { + printf("%.2f", tensor1->data[i]); + if (i < tensor1->size - 1) { + printf(", "); + } + } + printf("]\n"); + + printf("Size: %d\n", tensor2->size); + printf("Data: ["); + for (int i = 0; i < tensor2->size; i++) { + printf("%.2f", tensor2->data[i]); + if (i < tensor2->size - 1) { + printf(", "); + } + } + printf("]\n"); + + printf("Shapes : ["); + for (int i = 0; i < tensor1->ndim; i++) { + printf("%d", tensor1->shape[i]); + if (i < tensor1->ndim - 1) { + printf(", "); + } + } + printf("]\n"); + + printf("Shapes : ["); + for (int i = 0; i < tensor2->ndim; i++) { + printf("%d", tensor2->shape[i]); + if (i < tensor2->ndim - 1) { + printf(", "); + } + } + printf("]\n"); + + for (int i = 0; i < ndim; i++) { + if (tensor1->shape[i] != tensor2->shape[i]) { + fprintf(stderr, "Tensors must have the same shape %d and %d at index %d for element-wise multiplication\n", tensor1->shape[i], tensor2->shape[i], i); + exit(1); + } + shape[i] = tensor1->shape[i]; + } + + float* result_data = (float*)malloc(tensor1->size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + for (int i = 0; i < tensor1->size; i++) { + result_data[i] = tensor1->data[i] * tensor2->data[i]; + } + + return create_tensor(result_data, shape, ndim); + } + + Tensor* matmul_tensor(Tensor* tensor1, Tensor* tensor2) { + // Check if tensors have compatible shapes for matrix multiplication + if (tensor1->shape[1] != tensor2->shape[0]) { + fprintf(stderr, "Incompatible shapes for matrix multiplication\n"); + exit(1); + } + + // Calculate the shape of the result tensor + int ndim = tensor1->ndim + tensor2->ndim - 2; + int* shape = (int*)malloc(ndim * sizeof(int)); + if (shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + for (int i = 0; i < tensor1->ndim - 1; i++) { + shape[i] = tensor1->shape[i]; + } + for (int i = tensor1->ndim - 1; i < ndim; i++) { + shape[i] = tensor2->shape[i - tensor1->ndim + 2]; + } + + int size = 1; + for (int i = 0; i < ndim; i++) { + size *= shape[i]; + } + + float* result_data = (float*)malloc(size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + for (int i = 0; i < tensor1->shape[0]; i++) { + for (int j = 0; j < tensor2->shape[1]; j++) { + float sum = 0.0; + for (int k = 0; k < tensor1->shape[1]; k++) { + sum += tensor1->data[i * tensor1->shape[1] + k] * tensor2->data[k * tensor2->shape[1] + j]; + } + result_data[i * tensor2->shape[1] + j] = sum; + } + } + + return create_tensor(result_data, shape, ndim); + } + + void reshape_tensor(Tensor* tensor, int* new_shape, int new_ndim) { + // Calculate the total number of elements in the new shape + int new_size = 1; + for (int i = 0; i < new_ndim; i++) { + new_size *= new_shape[i]; + } + + // Check if the total number of elements matches the current tensor's size + if (new_size != tensor->size) { + fprintf(stderr, "Cannot reshape tensor. Total number of elements in new shape does not match the current size of the tensor.\n"); + exit(1); + } + + // Update the shape + tensor->shape = (int*)malloc(new_ndim * sizeof(int)); + if (tensor->shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + for (int i = 0; i < new_ndim; i++) { + tensor->shape[i] = new_shape[i]; + } + tensor->ndim = new_ndim; + + // Update the strides + tensor->strides = (int*)malloc(new_ndim * sizeof(int)); + if (tensor->strides == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + int stride = 1; + for (int i = new_ndim - 1; i >= 0; i--) { + tensor->strides[i] = stride; + stride *= new_shape[i]; + } + } } + diff --git a/csrc/tensor.h b/csrc/tensor.h index 58e79a2..08052cb 100644 --- a/csrc/tensor.h +++ b/csrc/tensor.h @@ -15,6 +15,8 @@ extern "C" { Tensor* create_tensor(float* data, int* shape, int ndim); Tensor* add_tensor(Tensor* tensor1, Tensor* tensor2); Tensor* sub_tensor(Tensor* tensor1, Tensor* tensor2); + Tensor* elementwise_mul_tensor(Tensor* tensor1, Tensor* tensor2); + void reshape_tensor(Tensor* tensor, int* new_shape, int new_ndim); } #endif /* TENSOR_H */ diff --git a/csrc/tensor.o b/csrc/tensor.o index 363e2a1..e5df588 100644 Binary files a/csrc/tensor.o and b/csrc/tensor.o differ diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index d8a3609..8858bf5 100644 Binary files a/norch/__pycache__/tensor.cpython-38.pyc and b/norch/__pycache__/tensor.cpython-38.pyc differ diff --git a/norch/tensor.py b/norch/tensor.py index 62ad203..2b0c40c 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -39,13 +39,36 @@ class Tensor: self.ndim = None def flatten(self, nested_list): - flat_data = [] - shape = [len(nested_list), len(nested_list[0])] - for sublist in nested_list: - for item in sublist: - flat_data.append(item) + def flatten_recursively(nested_list): + flat_data = [] + shape = [] + if isinstance(nested_list, list): + for sublist in nested_list: + inner_data, inner_shape = flatten_recursively(sublist) + flat_data.extend(inner_data) + shape.append(len(nested_list)) + shape.extend(inner_shape) + else: + flat_data.append(nested_list) + return flat_data, shape + + flat_data, shape = flatten_recursively(nested_list) return flat_data, shape + def reshape(self, new_shape): + + new_shape_ctype = (ctypes.c_int * len(new_shape))(*new_shape) + new_ndim_ctype = ctypes.c_int(len(new_shape)) + + Tensor._C.reshape_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(ctypes.c_int), ctypes.c_int] + Tensor._C.reshape_tensor.restype = None + Tensor._C.reshape_tensor(self.tensor, new_shape_ctype, new_ndim_ctype) + + self.shape = new_shape + self.ndim = len(new_shape) + + return self + def __getitem__(self, indices): if len(indices) != self.ndim: raise ValueError("Number of indices must match the number of dimensions") @@ -57,14 +80,32 @@ class Tensor: value = Tensor._C.get_item(self.tensor, indices) return value - + def __str__(self): - result = "" - for i in range(self.shape[0]): - for j in range(self.shape[1]): - result += str(self[i, j]) + " " - result += "\n" - return result.strip() + def print_recursively(tensor, depth, index): + if depth == tensor.ndim - 1: + result = "" + for i in range(tensor.shape[-1]): + index[-1] = i + result += str(tensor[tuple(index)]) + ", " + return result.strip() + else: + result = "" + if depth > 0: + result += "\n" + " " * ((depth - 1) * 4) + for i in range(tensor.shape[depth]): + index[depth] = i + result += "[" + result += print_recursively(tensor, depth + 1, index) + "]," + if i < tensor.shape[depth] - 1: + result += "\n" + " " * (depth * 4) + return result.strip(",") + + index = [0] * self.ndim + result = "tensor([" + result += print_recursively(self, 0, index) + result += "])" + return result def __repr__(self): return self.__str__() @@ -87,10 +128,10 @@ class Tensor: def __sub__(self, other): if self.shape != other.shape: - raise ValueError("Tensors must have the same shape for addition") + raise ValueError("Tensors must have the same shape for subtraction") - Tensor._C.add_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] - Tensor._C.add_tensor.restype = ctypes.POINTER(CTensor) + Tensor._C.sub_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.sub_tensor.restype = ctypes.POINTER(CTensor) result_tensor_ptr = Tensor._C.sub_tensor(self.tensor, other.tensor) @@ -100,18 +141,56 @@ class Tensor: result_data.ndim = self.ndim return result_data + + def __mul__(self, other): + if self.shape != other.shape: + raise ValueError("Tensors must have the same shape for element-wise multiplication") + + Tensor._C.elementwise_mul_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.elementwise_mul_tensor.restype = ctypes.POINTER(CTensor) + + result_tensor_ptr = Tensor._C.elementwise_mul_tensor(self.tensor, other.tensor) + + result_data = Tensor() + result_data.tensor = result_tensor_ptr + result_data.shape = self.shape.copy() + result_data.ndim = self.ndim + + return result_data + + def __matmul__(self, other): + if self.ndim != 2 or other.ndim != 2: + raise ValueError("Matrix multiplication requires 2D tensors") + + if self.shape[1] != other.shape[0]: + raise ValueError("Incompatible shapes for matrix multiplication") + + Tensor._C.matmul_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.matmul_tensor.restype = ctypes.POINTER(CTensor) + + result_tensor_ptr = Tensor._C.matmul_tensor(self.tensor, other.tensor) + + result_data = Tensor() + result_data.tensor = result_tensor_ptr + result_data.shape = [self.shape[0], other.shape[1]] + result_data.ndim = 2 + + return result_data if __name__ == "__main__": from tensor import Tensor import time ini = time.time() - a = Tensor([[1, 2, 3], [1, 2, 3]]) - b = Tensor([[1, 2, 3], [1, 2, 3]]) + a = Tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]],[[13, 14, 15], [16, 17, 18], [19, 20, 21], [22, 23, 24]]]) + print(a) + b = a.reshape([4, 3, 2]) + #b = Tensor([[1, 2, 3], [1, 2, 3], [1, 2, 3]]) + #a = Tensor([[[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]]]) + #b = Tensor([[[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]]]) + #c = a @ b - c = a + b - b - - print(c) + print("\n###########", b) fim = time.time()