diff --git a/build/Makefile b/build/Makefile new file mode 100644 index 0000000..78f3ec6 --- /dev/null +++ b/build/Makefile @@ -0,0 +1,29 @@ +# Compiler +CXX := g++ + +# Compiler flags +CXXFLAGS := -Wall -Werror -fpic + +# Source files +SRCS := ../src/tensor.cpp + +# Object files +OBJS := $(SRCS:.cpp=.o) + +# Target shared object file +TARGET := libtensor.so + +# Default target +all: $(TARGET) + +# Rule to build the shared object file +$(TARGET): $(OBJS) + $(CXX) -shared -o $@ $^ + +# Rule to compile each source file into an object file +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $^ -o $@ # Use -o $@ to specify the output file + +# Clean rule +clean: + $(RM) $(OBJS) $(TARGET) diff --git a/build/libtensor.so b/build/libtensor.so new file mode 100755 index 0000000..01c1618 Binary files /dev/null and b/build/libtensor.so differ diff --git a/python/__pycache__/tensor.cpython-39.pyc b/python/__pycache__/tensor.cpython-39.pyc new file mode 100644 index 0000000..27d992d Binary files /dev/null and b/python/__pycache__/tensor.cpython-39.pyc differ diff --git a/python/tensor.py b/python/tensor.py new file mode 100644 index 0000000..fb468b9 --- /dev/null +++ b/python/tensor.py @@ -0,0 +1,54 @@ +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) + ] + +class Tensor: + def __init__(self, data, shape): + self.lib = ctypes.CDLL("../build/libtensor.so") # Adjust the path to the shared library + self.data = (ctypes.c_float * len(data))(*data) + self.shape = shape + self.ndim = len(shape) + + self.lib.create_tensor.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_int), ctypes.c_int] + self.lib.create_tensor.restype = ctypes.POINTER(CTensor) + + self.tensor = self.lib.create_tensor( + self.data, + (ctypes.c_int * len(shape))(*shape), + ctypes.c_int(len(shape)) + ) + + def __getitem__(self, indices): + if len(indices) != self.ndim: + raise ValueError("Number of indices must match the number of dimensions") + + self.lib.get_item.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(ctypes.c_int)] + self.lib.get_item.restype = ctypes.c_float + + indices = (ctypes.c_int * len(indices))(*indices) + value = self.lib.get_item(self.tensor, indices) + + return value + + def __del__(self): + self.lib.free_tensor(self.tensor) + + def shape(self): + return f"Tensor(shape={self.shape})" + + 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__() diff --git a/python/tests.ipynb b/python/tests.ipynb new file mode 100644 index 0000000..f56bde6 --- /dev/null +++ b/python/tests.ipynb @@ -0,0 +1,90 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "kkkk @@ c_int(2)\n", + "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" + ] + }, + { + "ename": "ValueError", + "evalue": "Number of indices must match the number of dimensions", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 6\u001b[0m\n\u001b[1;32m 4\u001b[0m shape \u001b[38;5;241m=\u001b[39m [\u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m3\u001b[39m]\n\u001b[1;32m 5\u001b[0m tensor \u001b[38;5;241m=\u001b[39m Tensor(data, shape)\n\u001b[0;32m----> 6\u001b[0m \u001b[43mTensor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__getitem__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtensor\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Documentos/recreate_pytorch/foo/python/tensor.py:15\u001b[0m, in \u001b[0;36mTensor.__getitem__\u001b[0;34m(self, indices)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, indices):\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(indices) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mndim:\n\u001b[0;32m---> 15\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNumber of indices must match the number of dimensions\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m indices \u001b[38;5;241m=\u001b[39m (ctypes\u001b[38;5;241m.\u001b[39mc_int \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mlen\u001b[39m(indices))(\u001b[38;5;241m*\u001b[39mindices)\n\u001b[1;32m 17\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlib\u001b[38;5;241m.\u001b[39mget_item(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtensor, indices) \n", + "\u001b[0;31mValueError\u001b[0m: Number of indices must match the number of dimensions" + ] + } + ], + "source": [ + "from tensor import Tensor\n", + "\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.shape\n", + "Tensor.__getitem__(tensor, [0, 0])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-648269760\n" + ] + } + ], + "source": [ + "print(tensor[1, 1]) # Accessing an element" + ] + }, + { + "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/src/tensor.cpp b/src/tensor.cpp new file mode 100644 index 0000000..bcdbcb9 --- /dev/null +++ b/src/tensor.cpp @@ -0,0 +1,67 @@ +#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->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("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"); + + + 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]; + } + + void free_tensor(Tensor *tensor) { + free(tensor->data); + free(tensor->shape); + free(tensor->strides); + free(tensor); + } +} diff --git a/src/tensor.h b/src/tensor.h new file mode 100644 index 0000000..f93b22a --- /dev/null +++ b/src/tensor.h @@ -0,0 +1,17 @@ +#ifndef TENSOR_H +#define TENSOR_H + +typedef struct { + float *data; + int *strides; + int *shape; + int ndim; +} Tensor; + +extern "C" { + Tensor *create_tensor(float *data, int *shape, int ndim); + float get_item(Tensor *tensor, int *indices); + void delete_tensor(Tensor* tensor); +} + +#endif /* TENSOR_H */ diff --git a/src/tensor.o b/src/tensor.o new file mode 100644 index 0000000..237b41a Binary files /dev/null and b/src/tensor.o differ