diff --git a/build/Makefile b/build/Makefile index 78f3ec6..a62aaeb 100644 --- a/build/Makefile +++ b/build/Makefile @@ -5,7 +5,7 @@ CXX := g++ CXXFLAGS := -Wall -Werror -fpic # Source files -SRCS := ../src/tensor.cpp +SRCS := ../csrc/tensor.cpp # Object files OBJS := $(SRCS:.cpp=.o) diff --git a/build/libtensor.so b/build/libtensor.so index 1711f4e..dd8d235 100755 Binary files a/build/libtensor.so and b/build/libtensor.so differ diff --git a/csrc/tensor.cpp b/csrc/tensor.cpp new file mode 100644 index 0000000..4b734ac --- /dev/null +++ b/csrc/tensor.cpp @@ -0,0 +1,138 @@ +#include +#include +#include "tensor.h" + +extern "C" { + + Tensor* create_tensor(float* data, int* shape, int ndim) { + Tensor* tensor = (Tensor*)malloc(sizeof(Tensor)); + if (tensor == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + tensor->data = data; + tensor->shape = shape; + tensor->ndim = ndim; + tensor->size = 1; + + for (int i = 0; i < ndim; i++) { + tensor->size *= shape[i]; + } + + tensor->strides = (int*)malloc(ndim * sizeof(int)); + if (tensor->strides == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + int stride = 1; + for (int i = ndim - 1; i >= 0; i--) { + tensor->strides[i] = stride; + stride *= shape[i]; + } + + printf("Tensor created successfully\n"); + printf("Tensor information:\n"); + printf("Number of dimensions: %d\n", tensor->ndim); + printf("Number size: %d\n", tensor->size); + printf("Shape: ["); + for (int i = 0; i < ndim; i++) { + printf("%d", tensor->shape[i]); + if (i < ndim - 1) { + printf(", "); + } + } + printf("]\n"); + + printf("Data:\n["); + for (int i = 0; i < stride; i++) { + printf("%.2f", tensor->data[i]); + if (i < stride - 1) { + printf(", "); + } + } + printf("]\n\n\n"); + + + return tensor; + } + + float get_item(Tensor* tensor, int* indices) { + int index = 0; + for (int i = 0; i < tensor->ndim; i++) { + index += indices[i] * tensor->strides[i]; + } + return tensor->data[index]; + } + + Tensor* add_tensor(Tensor* tensor1, Tensor* tensor2) { + 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); + 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 addition\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); + } +} diff --git a/csrc/tensor.h b/csrc/tensor.h new file mode 100644 index 0000000..0ce9d86 --- /dev/null +++ b/csrc/tensor.h @@ -0,0 +1,19 @@ +#ifndef TENSOR_H +#define TENSOR_H + +typedef struct { + float *data; + int* strides; + int* shape; + int ndim; + int size; +} Tensor; + +extern "C" { + Tensor* create_tensor(float* data, int* shape, int ndim); + float get_item(Tensor* tensor, int* indices); + Tensor* create_tensor(float* data, int* shape, int ndim); + Tensor* add_tensor(Tensor* tensor1, Tensor* tensor2); +} + +#endif /* TENSOR_H */ diff --git a/csrc/tensor.o b/csrc/tensor.o new file mode 100644 index 0000000..0e5799c Binary files /dev/null and b/csrc/tensor.o differ diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc new file mode 100644 index 0000000..8523f4f Binary files /dev/null and b/norch/__pycache__/tensor.cpython-38.pyc differ diff --git a/norch/__pycache__/tensor.cpython-39.pyc b/norch/__pycache__/tensor.cpython-39.pyc new file mode 100644 index 0000000..16c0798 Binary files /dev/null and b/norch/__pycache__/tensor.cpython-39.pyc differ diff --git a/norch/tensor.py b/norch/tensor.py new file mode 100644 index 0000000..b319598 --- /dev/null +++ b/norch/tensor.py @@ -0,0 +1,89 @@ +import ctypes + +class CTensor(ctypes.Structure): + _fields_ = [ + ('data', ctypes.POINTER(ctypes.c_float)), + ('strides', ctypes.POINTER(ctypes.c_int)), + ('shape', ctypes.POINTER(ctypes.c_int)), + ('ndim', ctypes.c_int), + ('size', ctypes.c_int), + ] + +class Tensor: + _C = ctypes.CDLL("../build/libtensor.so") + + def __init__(self, data): + data, shape = self.flatten(data) + # Adjust the path to the shared library + self.data = (ctypes.c_float * len(data))(*data) + self.shape = shape + self.ndim = len(shape) + + Tensor._C.create_tensor.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_int), ctypes.c_int] + Tensor._C.create_tensor.restype = ctypes.POINTER(CTensor) + + self.tensor = Tensor._C.create_tensor( + self.data, + (ctypes.c_int * len(shape))(*shape), + ctypes.c_int(len(shape)) + ) + + 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) + return flat_data, shape + + def __getitem__(self, indices): + if len(indices) != self.ndim: + raise ValueError("Number of indices must match the number of dimensions") + + Tensor._C.get_item.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(ctypes.c_int)] + Tensor._C.get_item.restype = ctypes.c_float + + indices = (ctypes.c_int * len(indices))(*indices) + 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 __repr__(self): + return self.__str__() + + def __add__(self, other): + if self.shape != other.shape: + raise ValueError("Tensors must have the same shape for addition") + + Tensor._C.add_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.add_tensor.restype = ctypes.POINTER(CTensor) + + result_tensor_ptr = Tensor._C.add_tensor(self.tensor, other.tensor) + + result_data = [result_tensor_ptr.contents.data[i] for i in range(self.shape[0] * self.shape[1])] + result_shape = [result_tensor_ptr.contents.shape[i] for i in range(result_tensor_ptr.contents.ndim)] + + return Tensor(result_data, result_shape) + + +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]]) + + + c = a + b + fim = time.time() + + print(fim-ini) \ No newline at end of file diff --git a/norch/tests.ipynb b/norch/tests.ipynb new file mode 100644 index 0000000..ca51d7d --- /dev/null +++ b/norch/tests.ipynb @@ -0,0 +1,98 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 0, 2, 2] [2, 3, 3]\n", + "Tensor shape: [2, 3, 3]\n", + "Tensor created successfully\n", + "Tensor information:\n", + "Number of dimensions: 3\n", + "Shape: [2, 3, 3]\n", + "Data:\n", + "[0.00, 1.00, 2.00, 0.00, 2.00, 2.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00]\n" + ] + } + ], + "source": [ + "from tensor import Tensor\n", + "\n", + "data = [[0, 1, 2], [0, 2, 2], [0, 1, 2]]\n", + "tensor = Tensor(data)\n", + "print(\"Tensor shape:\", tensor.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tensor created successfully\n", + "Tensor information:\n", + "Number of dimensions: 2\n", + "Shape: [3, 3]\n", + "Data:\n", + "[0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90]\n" + ] + }, + { + "data": { + "text/plain": [ + "0.10000000149011612 0.20000000298023224 0.30000001192092896 \n", + "0.4000000059604645 0.5 0.6000000238418579 \n", + "0.699999988079071 0.800000011920929 0.8999999761581421" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "data = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]\n", + "shape = [3, 3]\n", + "tensor = Tensor(data, shape)\n", + "tensor\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..f8353cf --- /dev/null +++ b/test.sh @@ -0,0 +1,5 @@ +cd build +make +cd .. +cd norch +python3 tensor.py \ No newline at end of file