A fast tensor library in Rust
Project description
ferrix
A fast, memory-safe tensor library implemented in Rust and exposed as a Python package.
ferrix provides an NDArray core with stride-based indexing, vectorized math operations, slicing/reshaping utilities, and Python bindings via pyo3.
Why ferrix
- Rust core for predictable performance and memory safety.
- Python-friendly API for quick experimentation.
- Multi-dimensional arrays with row-major strides.
- Useful tensor operations for ML and numerical workloads.
Installation
Install from PyPI:
python -m pip install ferrix
Quick Start
import ferrix
# Create two 2x2 arrays
a = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
b = ferrix.PyNDArray([5.0, 6.0, 7.0, 8.0], [2, 2])
# Core arithmetic
print(a.add(b).get([0, 0])) # 6.0
print(a.mul(b).get([1, 1])) # 32.0
print(a.scale(0.5).get([1, 0])) # 1.5
# Matrix multiplication
print(a.matmul(b).get([0, 0])) # 19.0
print(a.matmul_blas(b).get([0, 0])) # Compatibility API; currently same as matmul
# Activations and reductions
print(a.relu().get([0, 0]))
print(a.softmax().sum())
print(a.sum(), a.mean(), a.argmax())
# Shape transforms
print(a.transpose().shape())
print(a.reshape([4]).shape())
Complete API Reference (Python)
ferrix exposes two classes:
ferrix.PyNDArrayfor numeric tensors (f64)ferrix.PyBoolArrayfor boolean masks
PyNDArray
Constructor:
PyNDArray(data: list[float], shape: list[int]) -> PyNDArray
x = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
Introspection and element access:
shape() -> list[int]get(index: list[int]) -> float
x = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
print(x.shape()) # [2, 2]
print(x.get([1, 0])) # 3.0
Reductions:
sum() -> floatmean() -> floatargmax() -> int(index in flattened row-major order)
x = ferrix.PyNDArray([1.0, 5.0, 3.0, 4.0], [2, 2])
print(x.sum()) # 13.0
print(x.mean()) # 3.25
print(x.argmax()) # 1
Element-wise math:
add(other: PyNDArray) -> PyNDArraymul(other: PyNDArray) -> PyNDArrayscale(scalar: float) -> PyNDArray
a = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
b = ferrix.PyNDArray([5.0, 6.0, 7.0, 8.0], [2, 2])
print(a.add(b).get([0, 1])) # 8.0
print(a.mul(b).get([1, 0])) # 21.0
print(a.scale(10).get([1, 1])) # 40.0
Activation functions:
relu() -> PyNDArraysigmoid() -> PyNDArraysoftmax() -> PyNDArray
x = ferrix.PyNDArray([-1.0, 0.0, 1.0], [1, 3])
print(x.relu().get([0, 0]))
print(x.sigmoid().get([0, 2]))
print(x.softmax().sum())
Matrix operations:
matmul(other: PyNDArray) -> PyNDArraymatmul_blas(other: PyNDArray) -> PyNDArray
a = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
b = ferrix.PyNDArray([5.0, 6.0, 7.0, 8.0], [2, 2])
print(a.matmul(b).get([0, 0]))
print(a.matmul_blas(b).get([0, 0]))
Note: matmul_blas is currently a compatibility API that uses the same backend behavior as matmul.
Reshape and transpose:
reshape(new_shape: list[int]) -> PyNDArraytranspose() -> PyNDArray(2D transpose)
x = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
print(x.reshape([4]).shape())
print(x.transpose().shape())
Slicing and indexing:
slice_row(row: int) -> PyNDArray(2D)slice_col(col: int) -> PyNDArray(2D)slice_range(axis: int, start: int, end: int) -> PyNDArrayfancy_index(indices: list[int]) -> PyNDArray(1D input)gather(axis: int, indices: list[int]) -> PyNDArray
m = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
v = ferrix.PyNDArray([10.0, 20.0, 30.0, 40.0], [4])
print(m.slice_row(1).shape())
print(m.slice_col(2).shape())
print(m.slice_range(1, 0, 2).shape())
print(v.fancy_index([3, 1, 1]).shape())
print(m.gather(1, [2, 0]).shape())
Masking and conditional operations:
boolean_mask(mask: PyBoolArray) -> PyNDArraymasked_fill(mask: PyBoolArray, value: float) -> None(in-place)where_(condition: PyBoolArray, other: PyNDArray) -> PyNDArray
x = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
y = ferrix.PyNDArray([9.0, 9.0, 9.0, 9.0], [2, 2])
mask = ferrix.PyBoolArray([True, False, True, False], [2, 2])
print(x.boolean_mask(mask).shape())
x.masked_fill(mask, -1.0)
print(x.where_(mask, y).shape())
Mutation and cumulative operations:
set_slice(axis: int, start: int, end: int, value: float) -> None(in-place)cumsum() -> PyNDArray(flattened cumulative sum)
x = ferrix.PyNDArray([1.0, 2.0, 3.0, 4.0], [2, 2])
x.set_slice(0, 0, 1, 0.0)
print(x.get([0, 1]))
print(x.cumsum().shape())
PyBoolArray
Constructor and methods:
PyBoolArray(data: list[bool], shape: list[int]) -> PyBoolArrayshape() -> list[int]
mask = ferrix.PyBoolArray([True, False, True, False], [2, 2])
print(mask.shape())
Error Behavior
- Invalid shapes, indices, or axis values raise Python exceptions backed by Rust panics.
- Most binary operations require shape compatibility.
fancy_indexis for 1D arrays.transposeandslice_row/slice_colrequire 2D arrays.
Feature Notes
- Arrays are row-major and stride-aware.
- Shape checks and index checks panic on invalid inputs in the Rust core.
matmul_blasis currently a compatibility method that falls back to the same implementation asmatmul.- Parallel execution is used for selected element-wise operations through
rayon.
Benchmarks (Indicative)
The following numbers are from repository examples and should be treated as indicative (hardware and build mode dependent):
| Operation | ferrix | NumPy |
|---|---|---|
| matmul 512x512 | 6.5 ms | 2.1 ms |
| relu 1M elements | 0.52 ms | 0.76 ms |
Build From Source (Optional)
This project uses maturin to build the Python extension from Rust.
Prerequisites
- Rust toolchain (
cargo,rustc) - Python
>=3.8 maturin
Local development build
python -m pip install --upgrade pip maturin
maturin develop
After this, import ferrix uses your local build in the active virtual environment.
Build wheel/sdist
rm -rf dist
maturin build --release --out dist
maturin sdist --out dist
Release and Publishing
For a complete PyPI release runbook, see:
docs/pypi-guide.md
It includes versioning rules, artifact validation, upload options, and post-release checks.
Repository Layout
Key files and directories:
src/lib.rs: Rust tensor core (NDArrayandNDArrayView)src/python.rs: Python bindings (PyNDArray,PyBoolArray)src/tests/: Rust unit teststest_ferrix.py: Python API testspyproject.toml: Python packaging metadata and build backendCargo.toml: Rust crate configuration
Contributing
Contributions are welcome.
For contributions and release process details, start from docs/pypi-guide.md and open a PR with clear change notes.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
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 ferrix-0.1.6-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: ferrix-0.1.6-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 198.8 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57bae39558d6b8f312c19ea2e39ce9793eeae881f97f2b240ae63d8469ae921b
|
|
| MD5 |
9580dfbf0d57bc05645b7e40addc305a
|
|
| BLAKE2b-256 |
f13d9f25dc17004bef954b88f32dfa278eef1289ec5079989fea7cef3e7fac07
|
File details
Details for the file ferrix-0.1.6-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.
File metadata
- Download URL: ferrix-0.1.6-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
- Upload date:
- Size: 621.8 kB
- Tags: CPython 3.11, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b69b1c7e9274d7b1df522f5fdb44c3b2acb619e6d2a8382634f9fc7ffb36ae7e
|
|
| MD5 |
0e1c762b5da85d6ece12f6f1fed43bff
|
|
| BLAKE2b-256 |
40d3c6af234437dbbd939c4c0a280871f675e93de7c3da39724b44cc538d0d2d
|
File details
Details for the file ferrix-0.1.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: ferrix-0.1.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 362.6 kB
- Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: maturin/1.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c2799b10d312116081bfa9c43bf07ce43f2efe24aa2648c80832910fb84c570
|
|
| MD5 |
07cdf396e339a91574be6873cc29f76c
|
|
| BLAKE2b-256 |
530bbba9ab98240d5b15bb8ef6e1ff1bd84d32d554e1cdb17162b12443e20a2c
|