From d704252c52e194c374ed59da009def479b91d0be Mon Sep 17 00:00:00 2001 From: Eli Date: Tue, 11 Aug 2026 03:06:10 -0300 Subject: [PATCH] Release 0.5.0 --- CHANGELOG.md | 28 +++++++++++++++++++++++++--- Cargo.lock | 2 +- Cargo.toml | 13 ++++++++++++- README.md | 10 +++++----- tests/firmware/Cargo.lock | 2 +- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 600c3db..b75931f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,17 +5,39 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/). -## [Unreleased] +## [Released] + +## [0.5.0] - 2026-08-03 ### Added - **Lanczos iteration.** `krylov::lanczos` tridiagonalizes a real symmetric matrix over a compile-time-sized Krylov subspace, returning the projection as a new `TridiagonalMatrix` and filling an orthonormal basis. Uses full reorthogonalization, so the basis stays orthonormal to working precision. +- **Arnoldi iteration.** `krylov::arnoldi` reduces a general, possibly non-symmetric matrix to upper Hessenberg form + over a compile-time-sized Krylov subspace, returning the projection as a new `HessenbergMatrix` and filling an + orthonormal basis. Unlike Lanczos, there's no three-term recurrence to exploit, so every step orthogonalizes + (via modified Gram-Schmidt) against the entire basis built so far, not just the two previous vectors. +- **Conjugate Gradient.** `krylov::conjugate_gradient` solves a symmetric positive-definite (SPD) linear system + `A x = b` iteratively, without factorizing `A`, converging in at most `n` steps in exact arithmetic. Detects + non-SPD input operationally — a search direction whose curvature `p · (A p)` isn't positive — rather than assuming + the caller already validated it. +- **GMRES(m), restarted.** `krylov::gmres`, gated behind the `alloc` feature, solves a general (possibly + non-symmetric) linear system `A x = b`: Arnoldi builds an `M`-dimensional Krylov basis from the current residual, + the resulting least-squares problem is solved via Givens rotations, and the cycle restarts from the improved + iterate until the residual meets tolerance or the restart budget runs out. Takes `A` as a `SparseLinearOp` so + restart cycles reuse the same workspace instead of allocating on every application. - `storage::Basis`, a const-generic view over caller-provided memory holding the `K` basis vectors a Krylov - method builds up — the storage-layer piece Arnoldi and GMRES(m) will reuse. + method builds up — the storage-layer piece Arnoldi and GMRES(m) reuse. - `ConvergenceError::Breakdown`, reported when the Krylov subspace turns out to be invariant before reaching the - requested dimension (e.g. a repeated eigenvalue, or a starting vector inside a small invariant subspace). + requested dimension (e.g. a repeated eigenvalue, or a starting vector inside a small invariant subspace). For + Arnoldi and GMRES specifically, this is instead reported as a successful, smaller-than-requested basis, since the + invariant subspace found is still exact and useful. +- Property tests for `qr`, `cholesky_decompose`, and `svd` that check each decomposition's defining mathematical + invariants directly (orthogonality, triangularity, reconstruction, positive-definiteness) instead of comparing + against nalgebra, so they also cover shapes nalgebra wouldn't be a fair oracle for. +- Edge-case tests for dimension extremes — degenerate `0xn`/`nx0`/`0x0` shapes, the smallest nontrivial shape + (`1x1`), and very rectangular shapes — across the dense decompositions and sparse matrix construction. ## [Released] diff --git a/Cargo.lock b/Cargo.lock index d2b28cd..93b4156 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,7 +320,7 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rustebra" -version = "0.4.0" +version = "0.5.0" dependencies = [ "nalgebra", "proptest", diff --git a/Cargo.toml b/Cargo.toml index 5f44193..14bccc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustebra" -version = "0.4.0" +version = "0.5.0" edition = "2024" rust-version = "1.85" license = "Apache-2.0" @@ -11,6 +11,17 @@ keywords = ["linear-algebra", "matrix", "vector", "no-std", "numerical"] categories = ["algorithms", "mathematics", "no-std"] documentation = "https://docs.rs/rustebra" readme = "README.md" +exclude = [ + ".all-contributorsrc", + ".claudeignore", + ".github/", + "CLAUDE.md", + "CONTEXT.md", + "book.toml", + "docs/", + "sonar-project.properties", + "theme/", +] [dependencies] diff --git a/README.md b/README.md index 64e6c35..1f762e2 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ A hybrid `no_std`/`alloc` library. Stack-first by default. Scales to sparse matr ## Status -Early development (v0.4.0). Core features implemented: static/dynamic vectors and matrices, matrix decompositions (LU, QR, SVD, Cholesky), sparse matrix support (COO, CSR, CSC), and Krylov eigenvalue solvers (power iteration, inverse power iteration). See +Early development (v0.5.0). Core features implemented: static/dynamic vectors and matrices, matrix decompositions (LU, QR, SVD, Cholesky), sparse matrix support (COO, CSR, CSC), Krylov eigenvalue solvers (power iteration, inverse power iteration, Lanczos, Arnoldi), and Krylov linear solvers (Conjugate Gradient, GMRES(m)). See [specs](docs/specs/) for architecture details. ## Why this exists @@ -75,10 +75,10 @@ constrained environments. rustebra aims to close that gap. | **Stack-only (no heap required)** | ✅ Default | ❌ No | ✅ For fixed-size | | **Sparse matrices** | ✅ v0.3.0+ (COO, CSR, CSC) | ❌ Separate `sprs` crate | ⚠️ Limited (optional feature) | | **GPU/SIMD acceleration** | ❌ Not planned | ⚠️ Limited SIMD | ⚠️ SIMD support available | -| **Krylov solvers** | ✅ v0.4.0+ (power iteration, inverse power iteration) | ⚠️ Via `ndarray-linalg` | ❌ Not in core | +| **Krylov solvers** | ✅ v0.5.0+ (power iteration, inverse power iteration, Lanczos, Arnoldi, CG, GMRES(m)) | ⚠️ Via `ndarray-linalg` | ❌ Not in core | | **3D math/graphics primitives** | ❌ Not focused | ❌ Not provided | ✅ Excellent (Isometry, Rotation, etc.) | | **BLAS/LAPACK integration** | ❌ No | ✅ Excellent bindings | ❌ Pure Rust | -| **Maturity & stability** | 🟡 Early (v0.4.0) | ✅ Mature & stable | ✅ Mature & stable | +| **Maturity & stability** | 🟡 Early (v0.5.0) | ✅ Mature & stable | ✅ Mature & stable | | **Large matrices (100k+)** | ⚠️ With sparse | ✅ Optimized | ⚠️ Fixed-size limits | | **Embedded systems** | ✅ Best choice | ❌ Poor fit | ⚠️ For fixed-size only | @@ -106,10 +106,10 @@ constrained environments. rustebra aims to close that gap. ```toml [dependencies] -rustebra = "0.4.0" +rustebra = "0.5.0" # Optional: heap-backed structures and Krylov solvers -rustebra = { version = "0.4.0", features = ["alloc"] } +rustebra = { version = "0.5.0", features = ["alloc"] } ``` Build and test locally: diff --git a/tests/firmware/Cargo.lock b/tests/firmware/Cargo.lock index f8d7011..663f81f 100644 --- a/tests/firmware/Cargo.lock +++ b/tests/firmware/Cargo.lock @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "rustebra" -version = "0.4.0" +version = "0.5.0" [[package]] name = "semver"