A pure Python linear algebra and tensor library for educational purposes
Project description
PotatoNumPy ๐ฅ
A pure Python linear algebra and tensor library built entirely from scratch. No NumPy, no C extensions, no shortcuts โ just potatoes all the way down.
It took me 3 weeks alone and 2 hours of Claude documentation refinement to make this.
What is this?
PotatoNumPy is a learning project that answers three questions I kept asking myself:
-
How does linear algebra actually work under the hood? Every matrix multiplication here is three nested loops. Every determinant is recursive cofactor expansion. Nothing hides behind an opaque C function โ you can read every line and follow along.
-
Why is NumPy so ridiculously fast? After watching PotatoNumPy chug through a 50x50 matrix multiply, you'll feel what NumPy's C/Fortran backends, SIMD vectorization, and contiguous memory layouts buy you. It's not subtle.
-
What does Python's interpreter overhead actually cost? Dynamic type checking, function call overhead, pointer-chasing through Python lists โ it all adds up. This library makes that cost painfully tangible.
Installation
pip install potatonumpy
Or clone and install locally:
git clone https://github.com/Madhavyamjala/potatonumpy.git
cd potatonumpy
pip install -e .
No dependencies needed โ that's the whole point.
Quick Start
import potatonumpy as pp
# Create arrays
a = pp.array([1, 2, 3])
b = pp.array([4, 5, 6])
# Elementwise math, just like you'd expect
print(a + b) # [5, 7, 9]
print(a * b) # [4, 10, 18]
print(a ** 2) # [1, 4, 9]
print(a + 10) # [11, 12, 13]
# Linear algebra
print(pp.dot(a, b)) # 32
print(pp.cross(a, b)) # [-3, 6, -3]
print(pp.magnitude(a)) # 3.7416...
print(pp.normalize(a)) # [0.2672, 0.5345, 0.8017]
Matrix Operations
import potatonumpy as pp
A = pp.array([[1, 2], [3, 4]])
B = pp.array([[5, 6], [7, 8]])
print(pp.matmul(A, B)) # [[19, 22], [43, 50]]
print(pp.transpose(A)) # [[1, 3], [2, 4]]
print(pp.determinant(A)) # -2.0
print(pp.inverse(A)) # [[-2, 1], [1.5, -0.5]]
print(pp.trace(A)) # 5
print(pp.diagonal(A)) # [1, 4]
# Special matrices
print(pp.identity(3))
print(pp.zeros((2, 3)))
Array Manipulation & Attributes
You can inspect array metadata and modify structural dimensions.
import potatonumpy as pp
a = pp.array([[1, 2, 3], [4, 5, 6]])
print(a.ndim)
print(a.size)
print(a.shape)
flat = a.flatten()
print(flat)
reshaped = flat.reshape((3, 2))
print(reshaped)
raw_list = reshaped.tolist()
print(raw_list)
Tensors? Yeah, We Got Those
import potatonumpy as pp
# 3D tensor โ go wild
t = pp.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(t.shape) # (2, 2, 2)
# Reductions
print(pp.tensor_sum(t)) # 36
print(pp.tensor_sum(t, axis=0)) # [[6, 8], [10, 12]]
print(pp.tensor_mean(t)) # 4.5
print(pp.tensor_min(t)) # 1
print(pp.tensor_max(t)) # 8
Type Support
Works with int, float, and complex:
c = pp.array([1+2j, 3+4j])
print(c + 1) # [(2+2j), (4+4j)]
Error Handling
PotatoNumPy doesn't silently do the wrong thing. It yells at you with specific exceptions:
from potatonumpy import (
ShapeMismatchError, # tried to add (2,) and (3,)?
InvalidTensorError, # ragged array? nope
SingularMatrixError, # can't invert that matrix, sorry
InvalidOperationError, # division by zero, etc.
)
pp.array([1, 2]) + pp.array([1, 2, 3]) # ShapeMismatchError
pp.array([[1, 2], [3, 4, 5]]) # InvalidTensorError
pp.inverse(pp.array([[1, 2], [2, 4]])) # SingularMatrixError
pp.array([1, 2]) / 0 # InvalidOperationError
Benchmarks (Prepare to Cringe)
python examples/benchmarking.py
Here's what you'll see (roughly):
| Operation | Pure Loops | PotatoNumPy | NumPy | Slowdown vs NumPy |
|---|---|---|---|---|
| Vector Add (10k) | ~2ms | ~5ms | ~0.01ms | ~500x |
| Matrix Mul (50x50) | ~50ms | ~80ms | ~0.05ms | ~1600x |
| Scalar Mul (10k) | ~1ms | ~3ms | ~0.005ms | ~600x |
Yeah. NumPy is that much faster. Here's why:
| What | PotatoNumPy | NumPy |
|---|---|---|
| Inner loops | Python bytecode | Compiled C/Fortran |
| Memory layout | Scattered Python objects | Contiguous typed arrays |
| Vectorization | Nope | SIMD instructions |
| Type checking | Every single operation | Once at array creation |
| Function calls | Python stack frames | Inlined C calls |
| Math backend | Hand-rolled loops | BLAS/LAPACK |
Running Tests
python -m unittest discover tests -v
99 tests (One Short ๐ฅฒ) covering all operations, edge cases, and error conditions.
Project Structure
.
โโโ src/
โ โโโ potatonumpy/
โ โโโ __init__.py
โ โโโ core.py
โ โโโ linalg.py
โ โโโ tensor.py
โ โโโ exceptions.py
โ โโโ utils.py
โ โโโ benchmarks.py
โโโ tests/
โ โโโ test_core.py
โ โโโ test_linalg.py
โ โโโ test_tensor.py
โโโ examples/
โ โโโ vectors.py
โ โโโ matrices.py
โ โโโ benchmarking.py
โโโ README.md
โโโ pyproject.toml
โโโ setup.py
Design Philosophy
- No magic. Every operation is explicit loops you can step through in a debugger. Want to understand how matrix inverse works? Read the code.
- Recursion over cleverness. Determinants use cofactor expansion. Shape validation walks the full tree. This is intentional โ clarity over performance.
- The slowness is a feature. Seriously. The massive performance gap between PotatoNumPy and NumPy is the lesson.
- Zero dependencies. Standard library only. If Python doesn't ship it, we don't use it.
Limitations (by Design)
- Slow. Orders of magnitude slower than NumPy. That's the point.
- Scalar broadcasting only. No fancy NumPy-style shape broadcasting.
- Memory hungry. Python lists use ~8x more memory per element than NumPy arrays.
- Determinant is O(n!). Don't try matrices bigger than ~12x12 unless you have time to spare.
- No eigenvalues, SVD, FFT, or sparse matrices. This is an educational tool, not a production library.
License
MIT License. See LICENSE for details.
Built with love, frustration, and an unreasonable number of nested for loops. ๐ฅ
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file potatonumpy-0.1.0.tar.gz.
File metadata
- Download URL: potatonumpy-0.1.0.tar.gz
- Upload date:
- Size: 18.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d58c8c710981d4ff54459caef2e66064134d867bcabd236630c03fd5dd9b169
|
|
| MD5 |
b9efb04e38ae20c80228d4469ee46b29
|
|
| BLAKE2b-256 |
717dc0f2efe19cbd6445fa0718631782eb43304ca4fd92e5ac2d9dbf84f1db20
|
Provenance
The following attestation bundles were made for potatonumpy-0.1.0.tar.gz:
Publisher:
publish.yml on Madhavyamjala/potatonumpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
potatonumpy-0.1.0.tar.gz -
Subject digest:
3d58c8c710981d4ff54459caef2e66064134d867bcabd236630c03fd5dd9b169 - Sigstore transparency entry: 1547120763
- Sigstore integration time:
-
Permalink:
Madhavyamjala/potatonumpy@dd792f012a611cd0fbcc3860ed76b0076ee8de05 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Madhavyamjala
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dd792f012a611cd0fbcc3860ed76b0076ee8de05 -
Trigger Event:
push
-
Statement type:
File details
Details for the file potatonumpy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: potatonumpy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08276c0a5fa5bbd6f69a704aa312be9e199b27249bf92e3c13a4322e43d372a7
|
|
| MD5 |
d42bcfc04303f7876b51c471217424bf
|
|
| BLAKE2b-256 |
8d4c5f27d097b8684593082ac684834a19eccbcc4b982d2969ea355719dfa85f
|
Provenance
The following attestation bundles were made for potatonumpy-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Madhavyamjala/potatonumpy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
potatonumpy-0.1.0-py3-none-any.whl -
Subject digest:
08276c0a5fa5bbd6f69a704aa312be9e199b27249bf92e3c13a4322e43d372a7 - Sigstore transparency entry: 1547120815
- Sigstore integration time:
-
Permalink:
Madhavyamjala/potatonumpy@dd792f012a611cd0fbcc3860ed76b0076ee8de05 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Madhavyamjala
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dd792f012a611cd0fbcc3860ed76b0076ee8de05 -
Trigger Event:
push
-
Statement type: