Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Test Build

on:
push:
branches: [ main, master, dev ]
pull_request:
branches: [ main, master, dev ]

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install cffi

- name: Build C extension (Linux/macOS)
if: matrix.os != 'windows-latest'
run: |
cd tensor/src
gcc -O3 -shared -fPIC -o ../_tensor1d.so tensor1d.c -lm

- name: Build C extension (Windows)
if: matrix.os == 'windows-latest'
run: |
cd tensor/src
gcc -O3 -shared -o ../_tensor1d.dll tensor1d.c

- name: Test basic functionality
run: |
python -c "
import sys
sys.path.insert(0, '.')
from tensor.tensor1d import Tensor
t = Tensor.arange(5)
print('Basic test passed:', t)
"
env:
PYTHONIOENCODING: utf-8

- name: Run tests
run: |
python tensor/tests/test_simple.py
env:
PYTHONPATH: .
PYTHONIOENCODING: utf-8
64 changes: 64 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# IDE and Editor files
.vscode/
.idea/

# Virtual environments
.venv/
venv/
env/
uv.lock

# Python cache and compiled files
__pycache__/
*.pyc
*.py[cod]
*$py.class

# Environment variables
.env

# System files
.DS_Store
.AppleDouble
.LSOverride
._*

# Build artifacts
dist/
build/
*.egg-info/
*.log

# Backup files
*.bak

# C extensions and build artifacts
*.so
*.dylib
*.dll
*.o
*.a
*.out
tensor1d
libtensor1d.so
_tensor1d*

# Test and coverage files
.pytest_cache/
.coverage
htmlcov/
.hypothesis/
coverage.xml
*.cover

# Python version management
.python-version

# Package management
.pdm.toml
poetry.lock

# mypy
.mypy_cache/
.dmypy.json
dmypy.json
107 changes: 97 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
CC = gcc
# Cross-platform build settings
ifeq ($(OS),Windows_NT)
CC = gcc
SHARED_EXT = .dll
SHARED_FLAGS = -shared
LDFLAGS =
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
CC = gcc
SHARED_EXT = .so
SHARED_FLAGS = -shared -fPIC
LDFLAGS = -lm
endif
ifeq ($(UNAME_S),Darwin)
CC = gcc
SHARED_EXT = .so
SHARED_FLAGS = -shared -fPIC
LDFLAGS = -lm
endif
endif

CFLAGS = -Wall -O3
LDFLAGS = -lm
PYTHON = python3

# turn on all the warnings
# https://github.com/mcinglis/c-style
Expand All @@ -9,23 +30,89 @@ CFLAGS += -Wall -Wextra -Wpedantic \
-Wwrite-strings -Wstrict-prototypes -Wold-style-definition \
-Wredundant-decls -Wnested-externs -Wmissing-include-dirs

# Source files
SRC_DIR = tensor/src
C_FILES = $(SRC_DIR)/tensor1d.c
H_FILES = $(SRC_DIR)/tensor1d.h

# Main targets
all: tensor1d libtensor1d.so

# Compile the main executable
tensor1d: tensor1d.c tensor1d.h
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
tensor1d: $(C_FILES) $(H_FILES)
cd $(SRC_DIR) && $(CC) $(CFLAGS) -o ../../$@ tensor1d.c $(LDFLAGS)

# Create shared library
libtensor1d.so: tensor1d.c tensor1d.h
$(CC) $(CFLAGS) -shared -fPIC -o $@ $< $(LDFLAGS)
libtensor1d$(SHARED_EXT): $(C_FILES) $(H_FILES)
cd $(SRC_DIR) && $(CC) $(CFLAGS) $(SHARED_FLAGS) -o ../../libtensor1d$(SHARED_EXT) tensor1d.c $(LDFLAGS)

# Build CFFI extension
cffi: $(C_FILES) $(H_FILES)
cd $(SRC_DIR) && $(PYTHON) build_cffi.py

# Build Python package
build: cffi
$(PYTHON) -m build

# Install package in development mode
install-dev:
$(PYTHON) -m pip install -e .[dev]

# Install package
install:
$(PYTHON) -m pip install .

# Test using pytest
test:
$(PYTHON) -m pytest tensor/tests/ -v

# Run tests with coverage
test-cov:
$(PYTHON) -m pytest tensor/tests/ --cov=tensor --cov-report=html --cov-report=term

# Format code
format:
$(PYTHON) -m black tensor/
$(PYTHON) -m isort tensor/

# Type checking
typecheck:
$(PYTHON) -m mypy tensor/

# Lint code
lint: format typecheck

# Clean up build artifacts
clean:
rm -f tensor1d libtensor1d.so
rm -rf build/ dist/ *.egg-info/
rm -rf tensor/__pycache__/ tensor/tests/__pycache__/
rm -rf .pytest_cache/ .coverage htmlcov/
find . -name "*.pyc" -delete
find . -name "*.so" -delete
find . -name "_tensor1d*" -delete

# Test using pytest
test:
pytest
# Clean everything including virtual environments
clean-all: clean
rm -rf .venv/ venv/

# Help
help:
@echo "Available targets:"
@echo " all - Build C executable and shared library"
@echo " tensor1d - Build C executable"
@echo " libtensor1d.so - Build shared library"
@echo " cffi - Build CFFI extension"
@echo " build - Build Python package"
@echo " install-dev - Install package in development mode"
@echo " install - Install package"
@echo " test - Run tests"
@echo " test-cov - Run tests with coverage"
@echo " format - Format code with black and isort"
@echo " typecheck - Run mypy type checking"
@echo " lint - Run formatting and type checking"
@echo " clean - Clean build artifacts"
@echo " clean-all - Clean everything including virtual environments"
@echo " help - Show this help message"

.PHONY: all clean test tensor1d
.PHONY: all clean clean-all test test-cov tensor1d cffi build install-dev install format typecheck lint help
64 changes: 60 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,70 @@
# tensor
# Tensor Library

[![Test Build](https://github.com/SermetPekin/tensor/actions/workflows/test.yml/badge.svg?1)](https://github.com/SermetPekin/tensor/actions/workflows/test.yml?1)

A simple 1D tensor library with C extensions for high-performance computing, inspired by PyTorch.

In this module we build a small `Tensor` in C, along the lines of `torch.Tensor` or `numpy.ndarray`. The current code implements a simple 1-dimensional float tensor that we can access and slice. We get to see that the tensor object maintains both a `Storage` that holds the 1-dimensional data as it is in physical memory, and a `View` over that memory that has some start, end, and stride. This allows us to efficiently slice into a Tensor without creating any additional memory, because the `Storage` is re-used, while the `View` is updated to reflect the new start, end, and stride. We then get to see how we can wrap our C tensor into a Python module, just like PyTorch and numpy do.

The source code of the 1D Tensor is in [tensor1d.h](tensor1d.h) and [tensor1d.c](tensor1d.c). You can compile and run this simply as:
## Building and Testing

The source code of the 1D Tensor is in [tensor/src/tensor1d.h](tensor/src/tensor1d.h) and [tensor/src/tensor1d.c](tensor/src/tensor1d.c).

### Build C extension:

```bash
# Linux/macOS
cd tensor/src
gcc -O3 -shared -fPIC -o ../_tensor1d.so tensor1d.c -lm

# Windows
cd tensor/src
gcc -O3 -shared -o ../_tensor1d.dll tensor1d.c
```

### Test:
```bash
python tensor/tests/test_simple.py
```

## Usage

```python
from tensor.tensor1d import Tensor

# Create tensors
t1 = Tensor.empty(5) # [0.0, 0.0, 0.0, 0.0, 0.0]
t2 = Tensor.arange(5) # [0.0, 1.0, 2.0, 3.0, 4.0]

# Indexing and slicing
print(t2[0]) # [0.0]
print(t2[1:4]) # [1.0, 2.0, 3.0]
print(t2[::2]) # [0.0, 2.0, 4.0]

# Arithmetic
t3 = t2.addf(5.0) # [5.0, 6.0, 7.0, 8.0, 9.0]
t4 = t2.add(t2) # [0.0, 2.0, 4.0, 6.0, 8.0]
```

### Standalone C program:

You can also compile and run the C code directly:

```bash
gcc -Wall -O3 tensor1d.c -o tensor1d
cd tensor/src
gcc -Wall -O3 tensor1d.c -o tensor1d -lm
./tensor1d
```

## Authors

- **Andrej Karpathy** - Original C implementation
- **SermetPekin** - Python packaging and cross-platform support

## License

MIT

The code contains both the `Tensor` class, and also a short `int main` that just has a toy example. We can now wrap up this C code into a Python module so we can access it there. For that, compile it as a shared library:

```bash
Expand Down Expand Up @@ -64,4 +120,4 @@ Good related resources:

### License

MIT
MIT
Loading