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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

---

## [0.8.0] — Tensors & Randomness

### Added
- `src/tensor.rs` — New N-dimensional generic `Tensor<T>` implementation
- Row-major flat storage with stride-based indexing
- Generic over any type `T` (f32, f64, i32, etc.)
- Support for `+`, `-`, scalar `*`, and `.hadamard()`
- Recursive `Display` implementation for any rank
- **Randomness Support** (for both `Matrix` and `Tensor`)
- `.rand()` / `.rand_seeded()` — Uniform distribution
- `.randn()` / `.randn_seeded()` — Standard normal distribution (floats only)
- Deterministic seeded constructors for reproducible benchmarks and tests
- `rand` and `rand_distr` dependencies

### Changed
- `Matrix::det` and `Matrix::inverse` updated with better internal pivot selection
- Internal `new_unchecked` constructor for `Matrix` to avoid redundant checks in internal math routines

---

## [0.7.0] — Parallel Execution

### Added
Expand Down
169 changes: 169 additions & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# NumRS Examples

This guide provides full, runnable examples of how to use NumRS for common linear algebra tasks.

---

## 1. Solving Linear Systems ($Ax = b$)

To solve a system of linear equations like:
$3x + y = 10$
$2x + 4y = 20$

We can represent this as $Ax = b$ and solve for $x$ using $x = A^{-1}b$.

```rust
use numrs::{ns_array, Matrix};

fn main() {
let a = ns_array![[3.0, 1.0], [2.0, 4.0]];
let b = ns_array![[10.0], [20.0]];

// Solve x = A⁻¹ * b
let a_inv = a.inverse().expect("Matrix must be invertible");
let x = &a_inv * &b;

println!("Solution x:\n{}", x);
// Expected: x ≈ 2, y ≈ 4
}
```

---

## 2. Markov Chain Simulation

Simulate a simple 2-state Markov chain (e.g., Weather: Sunny/Rainy).

```rust
use numrs::{ns_array, Matrix};

fn main() {
// Transition matrix:
// Sunny Rainy
// Sunny [ 0.9, 0.1 ]
// Rainy [ 0.5, 0.5 ]
let p = ns_array![[0.9, 0.1], [0.5, 0.5]];

// Initial state: 100% Sunny
let mut state = ns_array![[1.0, 0.0]];

println!("Initial state: {}", state);

for i in 1..=5 {
state = &state * &p;
println!("After step {}: {}", i, state);
}
}
```

---

## 3. Least Squares Fitting

Find the best-fit line $y = mx + c$ for a set of points $(1, 2), (2, 3), (3, 5), (4, 4)$.
The normal equation is $x = (A^T A)^{-1} A^T b$.

```rust
use numrs::{ns_array, Matrix};

fn main() {
// Design matrix A (ones in second column for intercept c)
let a = ns_array![
[1.0, 1.0],
[2.0, 1.0],
[3.0, 1.0],
[4.0, 1.0]
];

// Observation vector b
let b = ns_array![[2.0], [3.0], [5.0], [4.0]];

let at = a.transpose();
let at_a_inv = (&at * &a).inverse().expect("Matrix not invertible");
let weights = &(&at_a_inv * &at) * &b;

println!("Weights (m, c):\n{}", weights);
}
```

---

## 4. Graph Path Counting

Given an adjacency matrix $A$ of a graph, $(A^k)_{ij}$ gives the number of paths of length $k$ from node $i$ to node $j$.

```rust
use numrs::{ns_array, Matrix};

fn main() {
// A directed graph: 0 -> 1, 1 -> 2, 2 -> 0, 2 -> 1
let a = ns_array![
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 1.0, 0.0]
];

// Paths of length 3
let a2 = &a * &a;
let a3 = &a2 * &a;

println!("Number of paths of length 3:\n{}", a3);
}
```

---

## 5. 2D Transformations

Applying rotation and scaling to a 2D point.

```rust
use numrs::{ns_array, Matrix};

fn main() {
let point = ns_array![[1.0], [0.0]]; // Point (1, 0)

// 90-degree rotation matrix
let angle = std::f64::consts::FRAC_PI_2;
let rotation = ns_array![
[angle.cos(), -angle.sin()],
[angle.sin(), angle.cos()]
];

// Scaling matrix (2x)
let scale = ns_array![
[2.0, 0.0],
[0.0, 2.0]
];

let transformed = &scale * &(&rotation * &point);
println!("Transformed point:\n{}", transformed);
}
```

---

## 6. Hill Cipher (Simplified)

Encryption using a key matrix. Note: For traditional Hill Cipher, operations are performed modulo 26. Since NumRS uses `f64`, this example demonstrates the linear algebra core.

```rust
use numrs::{ns_array, Matrix};

fn main() {
// Key matrix (must be invertible)
let key = ns_array![[6.0, 24.0, 1.0], [13.0, 16.0, 10.0], [20.0, 17.0, 15.0]];

// Plaintext "ACT" -> [0, 2, 19]
let plaintext = ns_array![[0.0], [2.0], [19.0]];

// Encrypt: C = K * P
let ciphertext = &key * &plaintext;
println!("Raw ciphertext:\n{}", ciphertext);

// Decrypt: P = K⁻¹ * C
let key_inv = key.inverse().expect("Key must be invertible");
let decrypted = &key_inv * &ciphertext;
println!("Decrypted plaintext:\n{}", decrypted);
}
```
156 changes: 24 additions & 132 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,166 +1,58 @@
# NumRS 🦀

A lightweight linear algebra library built from scratch in Rust —
with an intuitive macro syntax, colored terminal output, and clean modular design.
A lightweight, high-performance linear algebra and tensor library built from scratch in Rust. NumRS provides an intuitive API, automatic parallelism for large matrices, and a clean modular design.

---

## Features
## Key Features

- `ns_array!` macro — write matrices like Python
- Colored terminal display with per-column alignment
- Full operator support for both owned and reference types (`+` `-` `*`)
- Scalar multiplication in both directions (`matrix * 2.0` and `2.0 * matrix`)
- Utility constructors: `zeros`, `ones`, `eye`, `fill`
- Direct element access via `matrix[(i, j)]`
- Mathematical operations: transpose, Hadamard, norm, determinant, inverse
- **Automatic parallelism** via Rayon for large matrices
- **2D Matrix & N-D Tensor**: Specialized `Matrix` for linear algebra and generic `Tensor<T>` for multi-dimensional data.
- **Intuitive Syntax**: Create matrices with the `ns_array!` macro — just like Python/NumPy.
- **Automatic Parallelism**: Silently scales across CPU cores using Rayon for large-scale computations.
- **Robust Math**: Transpose, Determinant, Inverse (Gauss-Jordan), Norm, and Hadamard product.
- **Beautiful Output**: Colored terminal display with automatic column alignment.
- **Reference-First Operators**: Clean arithmetic (`a + b`, `a * b`) with zero unnecessary clones.

---

## Quick Start

```bash
git clone https://github.com/sinamsv/NumRS.git
```

```bash
cargo run
```

---

## API Reference

### Constructors

| Method | Description |
|---------------------------|------------------------------------|
| `ns_array![[...], [...]]` | Create matrix from literal values |
| `Matrix::zeros(r, c)` | Matrix of zeros |
| `Matrix::ones(r, c)` | Matrix of ones |
| `Matrix::eye(n)` | n×n identity matrix |
| `Matrix::fill(r, c, v)` | Matrix filled with a specific value|

### Operators

| Expression | Returns | Description |
|---------------|------------------------------|-------------------------------------|
| `&a + &b` | `Matrix` | Matrix addition (panics on error) |
| `a.try_add(&b)`| `Result<Matrix, MatrixError>`| Safe matrix addition |
| `&a - &b` | `Matrix` | Matrix subtraction (panics on error)|
| `a.try_sub(&b)`| `Result<Matrix, MatrixError>`| Safe matrix subtraction |
| `&a * &b` | `Matrix` | Matrix multiplication (panics) |
| `a.try_mul(&b)`| `Result<Matrix, MatrixError>`| Safe matrix multiplication |
| `&a * 2.0` | `Matrix` | Scalar multiplication |
| `2.0 * &a` | `Matrix` | Scalar multiplication (commutative) |

All operators are also available in owned form (`a + b`, `a * b`, etc.)
so you can choose based on whether you need to reuse the original.

### Indexing

```rust
let val = matrix[(i, j)]; // read element
matrix[(i, j)] = 3.14; // write element
```

### Mathematical Operations

| Method | Returns | Description |
|------------------|-------------------------------|------------------------------------------|
| `.transpose()` | `Matrix` | Swap rows and columns |
| `.hadamard(&b)` | `Result<Matrix, MatrixError>` | Element-wise multiplication |
| `.norm()` | `f64` | Frobenius norm |
| `.det()` | `Result<f64, MatrixError>` | Determinant (square matrices only) |
| `.inverse()` | `Result<Matrix, MatrixError>` | Inverse via Gauss-Jordan |

---

## Error Handling

NumRS uses a custom `MatrixError` enum for robust error handling. While standard operators (`+`, `-`, `*`) will panic with a descriptive message on invalid input (like shape mismatches), the library provides `try_*` variants and `Result`-returning methods for safe contexts.

```rust
use numrs::{ns_array, MatrixError};
use numrs::{ns_array, Matrix};

let a = ns_array![[1, 2]];
let b = ns_array![[1, 2, 3]];
fn main() {
let a = ns_array![[1, 2], [3, 4]];
let b = Matrix::eye(2);

match a.try_add(&b) {
Err(MatrixError::ShapeMismatch { expected, found }) => {
println!("Error: expected {:?}, got {:?}", expected, found);
}
_ => unreachable!()
let result = &a * &b;
println!("A x I =\n{}", result);
}
```

---

## Parallel Execution
## Documentation & Learning

NumRS automatically parallelizes operations on large matrices using [Rayon](https://github.com/rayon-rs/rayon).

- **Threshold**: Operations switch to parallel mode when the number of elements reaches `PAR_THRESHOLD` (currently 1024).
- **Tuning**: You can find this constant in `src/parallel.rs`.
- **Operations**: Addition, subtraction, multiplication, transposition, Hadamard product, and Frobenius norm are all parallelized.

---

## Project Structure

```
src/
├── main.rs # Entry point and demos
├── lib.rs # Crate root and exports
├── matrix.rs # Matrix struct, Display, ns_array! macro, constructors
├── math.rs # Mathematical operations
├── error.rs # MatrixError enum and Error implementation
├── parallel.rs # Parallelization utilities and thresholds
└── ops/
├── mod.rs
├── add.rs # Add trait and try_add
├── sub.rs # Sub trait and try_sub
├── mul.rs # Mul trait (matrix × matrix) and try_mul
├── scalar.rs # Mul<f64> and f64 * Matrix
└── index.rs # Index and IndexMut traits
```

---

## What You Can Build With This

| Use Case | Operations needed |
|---------------------------|------------------------------------|
| Solve linear systems Ax=b | `inverse()` + `*` |
| Hill Cipher (cryptography)| `*`, `det()`, `inverse()` |
| 2D/3D transformations | `*`, constructors |
| Markov chain simulation | `*` repeatedly on state vector |
| Least squares fitting | `transpose()`, `inverse()`, `*` |
| Graph path counting | Matrix powers via `*` |
- **[Usage Guide](USAGE.md)** — Detailed API documentation for all modules.
- **[Examples](EXAMPLES.md)** — Practical recipes for solving linear systems, Markov chains, transformations, and more.
- **[Changelog](CHANGELOG.md)** — History of releases and recent changes.

---

## Roadmap

- [x] Matrix struct with colored Display
- [x] Add, Sub, Mul operators (owned + reference)
- [x] Scalar multiplication
- [x] Constructors: zeros, ones, eye, fill
- [x] Index / IndexMut
- [x] Transpose, Hadamard, Norm, Determinant, Inverse
- [x] Full operator support (Add, Sub, Mul, Scalar)
- [x] Constructors (zeros, ones, eye, rand)
- [x] Determinant and Inverse
- [x] Automatic parallelism via Rayon
- [x] Comprehensive error handling with `MatrixError`
- [x] Comprehensive error handling
- [/] **Tensor Support** (In Development)
- [ ] Eigenvalues and eigenvectors
- [ ] LU / QR decomposition
- [ ] Tensor support
- [ ] ML layer: ReLU, softmax, sigmoid, gradient tracking
- [ ] ML layer: ReLU, softmax, gradient tracking

---
## Documentation

- [USAGE.md](USAGE.md)
- [CHANGELOG.md](CHANGELOG.md)

## License

Expand Down
Loading
Loading