Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
13 changes: 8 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ just examples # Run all examples
- Run a single test (by name filter): `cargo test solve_2x2_basic` (or the full path: `cargo test lu::tests::solve_2x2_basic`)
- Run exact-feature tests: `cargo test --features exact --verbose` (or `just test-exact`)
- Run examples: `just examples` (or `cargo run --example det_5x5` / `cargo run --example solve_5x5` /
`cargo run --example const_det_4x4` / `cargo run --features exact --example exact_sign_3x3`)
`cargo run --example const_det_4x4` / `cargo run --features exact --example exact_det_3x3` /
`cargo run --features exact --example exact_sign_3x3`)
- Spell check: `just spell-check` (uses `typos.toml` at repo root; add false positives to `[default.extend-words]`)

### Changelog
Expand Down Expand Up @@ -127,8 +128,10 @@ When creating or updating issues:

## Feature flags

- `exact` — enables `det_sign_exact()` (adaptive-precision determinant sign via `BigRational`).
Gates `src/exact.rs`, additional tests, and the `exact_sign_3x3` example.
- `exact` — enables exact determinant methods via `BigRational` arithmetic:
`det_exact()`, `det_exact_f64()`, and `det_sign_exact()`.
Also re-exports `BigRational` from the crate root and prelude.
Gates `src/exact.rs`, additional tests, and the `exact_det_3x3`/`exact_sign_3x3` examples.
Clippy, doc builds, and test commands have dedicated `--features exact` variants.

## Code structure (big picture)
Expand All @@ -140,8 +143,8 @@ When creating or updating issues:
- `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`)
- `src/exact.rs`: `det_sign_exact()` — adaptive-precision determinant sign
(Shewchuk-style f64 filter + Bareiss in `BigRational`); `features = ["exact"]`
- `src/exact.rs`: `det_exact()`, `det_exact_f64()`, `det_sign_exact()` — exact determinant
value and sign (Shewchuk-style f64 filter + Bareiss in `BigRational`); `features = ["exact"]`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- Rust tests are inline `#[cfg(test)]` modules in each `src/*.rs` file.
- Python tests live in `scripts/tests/` and run via `just test-python` (`uv run pytest`).
- The public API re-exports these items from `src/lib.rs`.
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ faer = { version = "0.24.0", default-features = false, features = [ "std", "lina
nalgebra = { version = "0.34.1", optional = true }
num-bigint = { version = "0.4.6", optional = true }
num-rational = { version = "0.4.2", features = [ "num-bigint-std" ], optional = true }
num-traits = { version = "0.2.19", optional = true }

[dev-dependencies]
approx = "0.5.1"
Expand All @@ -28,7 +29,11 @@ proptest = "1.10.0"
[features]
default = [ ]
bench = [ "criterion", "faer", "nalgebra" ]
exact = [ "num-bigint", "num-rational" ]
exact = [ "num-bigint", "num-rational", "num-traits" ]

[[example]]
name = "exact_det_3x3"
required-features = [ "exact" ]

[[example]]
name = "exact_sign_3x3"
Expand Down
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,22 @@ 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.

## 🔬 Exact determinant sign (`"exact"` feature)
## 🔬 Exact arithmetic (`"exact"` feature)

The default build has **zero runtime dependencies**. Enable the optional
`exact` Cargo feature to add `det_sign_exact()`, which returns the provably
correct sign (−1, 0, or +1) of the determinant using adaptive-precision
arithmetic (this pulls in `num-bigint` and `num-rational` for `BigRational`):
`exact` Cargo feature to add exact determinant methods using adaptive-precision
arithmetic (this pulls in `num-bigint`, `num-rational`, and `num-traits` for
`BigRational`):

```toml
[dependencies]
la-stack = { version = "0.2.2", features = ["exact"] }
```

- **`det_exact()`** — returns the exact determinant as a `BigRational`
- **`det_exact_f64()`** — returns the exact determinant converted to the nearest `f64`
- **`det_sign_exact()`** — returns the provably correct sign (−1, 0, or +1)

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

Expand All @@ -154,11 +158,17 @@ let m = Matrix::<3>::from_rows([
[7.0, 8.0, 9.0],
]);
assert_eq!(m.det_sign_exact().unwrap(), 0); // exactly singular

let det = m.det_exact().unwrap();
assert_eq!(det, BigRational::from_integer(0.into())); // exact zero
```

For D ≤ 4, a fast f64 filter (error-bounded `det_direct()`) resolves the sign
without allocating. Only near-degenerate or large (D ≥ 5) matrices fall through
to the exact Bareiss algorithm in `BigRational`.
`BigRational` is re-exported from the crate root and prelude when the `exact`
feature is enabled, so consumers don't need to depend on `num-rational` directly.

For `det_sign_exact()`, D ≤ 4 matrices use a fast f64 filter (error-bounded
`det_direct()`) that resolves the sign without allocating. Only near-degenerate
or large (D ≥ 5) matrices fall through to the exact Bareiss algorithm.

### Adaptive precision with `det_errbound()`

Expand Down Expand Up @@ -194,7 +204,7 @@ exposed for advanced use cases.
| Type | Storage | Purpose | Key methods |
|---|---|---|---|
| `Vector<D>` | `[f64; D]` | Fixed-length vector | `new`, `zero`, `dot`, `norm2_sq` |
| `Matrix<D>` | `[[f64; D]; D]` | Square matrix | `from_rows`, `zero`, `identity`, `lu`, `ldlt`, `det`, `det_direct`, `det_errbound`, `det_sign_exact`¹ |
| `Matrix<D>` | `[[f64; D]; D]` | Square matrix | `lu`, `ldlt`, `det`, `det_direct`, `det_errbound`, `det_exact`¹, `det_exact_f64`¹, `det_sign_exact`¹ |
| `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 @@ -209,6 +219,7 @@ The `examples/` directory contains small, runnable programs:
- **`solve_5x5`** — solve a 5×5 system via LU with partial pivoting
- **`det_5x5`** — determinant of a 5×5 matrix via LU
- **`const_det_4x4`** — compile-time 4×4 determinant via `det_direct()`
- **`exact_det_3x3`** — exact determinant value of a near-singular 3×3 matrix (requires `exact` feature)
- **`exact_sign_3x3`** — exact determinant sign of a near-singular 3×3 matrix (requires `exact` feature)

```bash
Expand All @@ -217,6 +228,7 @@ just examples
cargo run --example solve_5x5
cargo run --example det_5x5
cargo run --example const_det_4x4
cargo run --features exact --example exact_det_3x3
cargo run --features exact --example exact_sign_3x3
```

Expand Down
2 changes: 1 addition & 1 deletion REFERENCES.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ elimination [7] in `BigRational`. See `src/exact.rs` for the full architecture d
*Numerische Mathematik* 27 (1976/1977): 95–110.
[Full text](https://eudml.org/doc/132435)

### Exact determinant sign (`det_sign_exact`)
### Exact determinant (`det_exact`, `det_exact_f64`, `det_sign_exact`)

7. Bareiss, Erwin H. "Sylvester's Identity and Multistep Integer-Preserving Gaussian
Elimination." *Mathematics of Computation* 22.103 (1968): 565–578.
Expand Down
48 changes: 48 additions & 0 deletions examples/exact_det_3x3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Exact determinant value for a near-singular 3×3 matrix.
//!
//! This example demonstrates `det_exact()` and `det_exact_f64()`, which use
//! arbitrary-precision rational arithmetic to compute the provably correct
//! determinant value — even when the matrix is so close to singular that f64
//! rounding could lose significant digits.
//!
//! Run with: `cargo run --features exact --example exact_det_3x3`

use la_stack::prelude::*;

fn main() {
// Base matrix: rows in arithmetic progression → exactly singular (det = 0).
// [[1, 2, 3],
// [4, 5, 6],
// [7, 8, 9]]
//
// Perturb entry (0,0) by 2^-50 ≈ 8.9e-16.
// Exact det = 2^-50 × cofactor(0,0) = 2^-50 × (5×9 − 6×8) = −3 × 2^-50.
let perturbation = f64::from_bits(0x3CD0_0000_0000_0000); // 2^-50
let m = Matrix::<3>::from_rows([
[1.0 + perturbation, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
]);

let det_f64_approx = m.det(DEFAULT_PIVOT_TOL).unwrap();
let det_exact = m.det_exact().unwrap();
let det_exact_as_f64 = m.det_exact_f64().unwrap();

println!("Near-singular 3×3 matrix (perturbation = 2^-50 ≈ {perturbation:.2e}):");
for r in 0..3 {
print!(" [");
for c in 0..3 {
if c > 0 {
print!(", ");
}
print!("{:22.18}", m.get(r, c).unwrap());
}
println!("]");
}
println!();
println!("f64 det() = {det_f64_approx:+.6e}");
println!("det_exact() = {det_exact}");
println!("det_exact_f64() = {det_exact_as_f64:+.6e}");
println!();
println!("The exact determinant is −3/2^50 ≈ −2.66e-15.");
}
Loading
Loading