Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ When making changes in this repo, prioritize (in order):
- Pre-commit validation: `just ci`
- Python tests: `just test-python`
- Run a single test (by name filter): `cargo test solve_2x2_basic` (or the full path: `cargo test lu::tests::solve_2x2_basic`)
- Run examples: `just examples` (or `cargo run --example det_5x5` / `cargo run --example solve_5x5`)
- Run examples: `just examples` (or `cargo run --example det_5x5` / `cargo run --example solve_5x5` / `cargo run --example const_det_4x4`)
- Spell check: `just spell-check` (uses `typos.toml` at repo root; add false positives to `[default.extend-words]`)

## Code structure (big picture)
Expand All @@ -37,7 +37,7 @@ When making changes in this repo, prioritize (in order):
- The linear algebra implementation is split across:
- `src/lib.rs`: crate root + shared items (`LaError`, `DEFAULT_SINGULAR_TOL`, `DEFAULT_PIVOT_TOL`) + re-exports
- `src/vector.rs`: `Vector<const D: usize>` (`[f64; D]`)
- `src/matrix.rs`: `Matrix<const D: usize>` (`[[f64; D]; D]`) + helpers (`get`, `set`, `inf_norm`, `det`)
- `src/matrix.rs`: `Matrix<const D: usize>` (`[[f64; D]; D]`) + helpers (`get`, `set`, `inf_norm`, `det`, `det_direct`)
- `src/lu.rs`: `Lu<const D: usize>` factorization with partial pivoting (`solve_vec`, `det`)
- `src/ldlt.rs`: `Ldlt<const D: usize>` factorization without pivoting for symmetric SPD/PSD matrices (`solve_vec`, `det`)
- A minimal `justfile` exists for common workflows (see `just --list`).
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ name = "vs_linalg"
harness = false
required-features = [ "bench" ]

[profile.release]
lto = "fat"
codegen-units = 1

[lints.rust]
unsafe_code = "forbid"
missing_docs = "warn"
Expand Down
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ while keeping the API intentionally small and explicit.

- ✅ `Copy` types where possible
- ✅ Const-generic dimensions (no dynamic sizes)
- ✅ `const fn` where possible (compile-time evaluation of determinants, dot products, etc.)
- ✅ Explicit algorithms (LU, solve, determinant)
- ✅ No runtime dependencies (dev-dependencies are for contributors only)
- ✅ Stack storage only (no heap allocation in core types)
Expand Down Expand Up @@ -103,12 +104,36 @@ let det = a.ldlt(DEFAULT_SINGULAR_TOL).unwrap().det();
assert!((det - 1.0).abs() <= 1e-12);
```

## ⚡ Compile-time determinants (D ≤ 4)

`det_direct()` is a `const fn` that computes closed-form determinants for D=1–4
using fused multiply-add, bypassing LU factorization entirely. This enables
compile-time evaluation when inputs are known at compile time:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

```rust
use la_stack::prelude::*;

// Evaluated entirely at compile time — no runtime cost.
const DET: Option<f64> = {
let m = Matrix::<3>::from_rows([
[2.0, 0.0, 0.0],
[0.0, 3.0, 0.0],
[0.0, 0.0, 5.0],
]);
m.det_direct()
};
assert_eq!(DET, Some(30.0));
```

The public `det()` method automatically dispatches through the closed-form path
for D ≤ 4 and falls back to LU for D ≥ 5 — no API change needed.

## 🧩 API at a glance

| Type | Storage | Purpose | Key methods |
|---|---|---|---|
| `Vector<D>` | `[f64; D]` | Fixed-length vector | `new`, `zero`, `dot`, `norm2_sq` |
| `Matrix<D>` | `[[f64; D]; D]` | Fixed-size square matrix | `from_rows`, `zero`, `identity`, `lu`, `ldlt`, `det` |
| `Matrix<D>` | `[[f64; D]; D]` | Fixed-size square matrix | `from_rows`, `zero`, `identity`, `lu`, `ldlt`, `det`, `det_direct` |
| `Lu<D>` | `Matrix<D>` + pivot array | Factorization for solves/det | `solve_vec`, `det` |
| `Ldlt<D>` | `Matrix<D>` | Factorization for symmetric SPD/PSD solves/det | `solve_vec`, `det` |

Expand All @@ -123,6 +148,7 @@ just examples
# or:
cargo run --example solve_5x5
cargo run --example det_5x5
cargo run --example const_det_4x4
```

## 🤝 Contributing
Expand Down
10 changes: 10 additions & 0 deletions benches/vs_linalg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ macro_rules! gen_vs_linalg_benches_for_dim {
});
});

// === Determinant via det() (closed-form for D≤4, LU for D≥5) ===
[<group_d $d>].bench_function("la_stack_det", |bencher| {
bencher.iter(|| {
let det = black_box(a)
.det(la_stack::DEFAULT_PIVOT_TOL)
.expect("matrix should be non-singular");
black_box(det);
});
});

// === LU factorization ===
[<group_d $d>].bench_function("la_stack_lu", |bencher| {
bencher.iter(|| {
Expand Down
36 changes: 36 additions & 0 deletions examples/const_det_4x4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Compile-time 4×4 determinant via `det_direct()`.
//!
//! Because `det_direct` is a `const fn` (Rust 1.94+), the determinant is
//! evaluated entirely at compile time — zero runtime cost.

use la_stack::prelude::*;

/// A 4×4 Hilbert-like matrix with exact rational entries scaled to integers.
const MAT: Matrix<4> = Matrix::<4>::from_rows([
[1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0],
[2.0, 6.0, 1.0, 5.0],
[3.0, 8.0, 2.0, 9.0],
]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

/// Determinant computed at compile time.
const DET: f64 = match MAT.det_direct() {
Some(d) => d,
None => panic!("det_direct only supports D <= 4"),
};

fn main() {
println!("4×4 matrix:");
for r in 0..4 {
print!(" [");
for c in 0..4 {
if c > 0 {
print!(", ");
}
print!("{:5.1}", MAT.get(r, c).unwrap());
}
println!("]");
}
println!();
println!("det (computed at compile time) = {DET}");
}
1 change: 1 addition & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ doc-check:
examples:
cargo run --quiet --example det_5x5
cargo run --quiet --example solve_5x5
cargo run --quiet --example const_det_4x4

# Fix (mutating): apply formatters/auto-fixes
fix: toml-fmt fmt python-fix shell-fmt markdown-fix yaml-fix
Expand Down
Loading
Loading