Skip to content

Commit c6e04fd

Browse files
authored
Merge pull request #99 from acgetchell/release/v0.4.1
chore(release): release v0.4.1
2 parents 150678a + 1478c19 commit c6e04fd

9 files changed

Lines changed: 216 additions & 105 deletions

File tree

AGENTS.md

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,98 @@ When making changes in this repo, prioritize (in order):
1010
- Speed
1111
- Coverage (but keep the code idiomatic Rust)
1212

13+
## Design Principles
14+
15+
This is a scientific linear-algebra library. Design decisions trade off in
16+
roughly this priority: mathematical correctness → API stability →
17+
composability → idiomatic Rust → performance within scope. The sections
18+
below spell out what each means in practice; when in doubt, favour the
19+
invariant over the convenient edit.
20+
21+
### Mathematical correctness as an invariant
22+
23+
- Exact paths (`*_exact`) never silently lose precision. When f64 output
24+
is required, a separate `*_exact_f64` method returns
25+
[`LaError::Overflow`] on unrepresentability — not a truncation.
26+
- Any f64 operation that can accumulate rounding error either documents
27+
its absolute bound (`det_errbound`, `ERR_COEFF_*`) or explicitly states
28+
that no bound is provided.
29+
- Non-finite values (NaN, ±∞) always surface as
30+
`LaError::NonFinite { row, col }` with source-location metadata. No
31+
silent NaN propagation, no `unwrap_or(f64::NAN)`.
32+
- Algorithms cite their source (Shewchuk, Bareiss, Goldberg, …) via
33+
`REFERENCES.md` and document their conditioning behaviour.
34+
35+
### Public-API stability
36+
37+
- Error enums are `#[non_exhaustive]`; public wrapper types are
38+
`#[must_use]`.
39+
- New functionality is additive: use the prelude for ergonomic re-exports;
40+
never silently rename or remove a public item.
41+
- Pre-1.0 semver: `0.x.Y` is a patch-level additive bump, `0.X.y` is a
42+
minor bump that may include breaking changes. Conventional-commit
43+
types (`feat`, `fix`, `refactor`, …) mirror this convention.
44+
45+
### Composability
46+
47+
- Const-generic `D` for every core type (`Matrix<D>`, `Vector<D>`,
48+
`Lu<D>`, `Ldlt<D>`). No runtime dimension.
49+
- Stack allocation by default; heap only behind a feature flag or where
50+
exact arithmetic inherently requires it (`BigInt` / `BigRational`).
51+
- Feature flags isolate optional dependency weight; default builds stay
52+
dep-minimal.
53+
54+
### Idiomatic Rust as a proxy for mathematical clarity
55+
56+
- `const fn` wherever possible — not for micro-optimisation, but because
57+
compile-time evaluation forces a pure function of inputs.
58+
- `Result<_, LaError>` for all fallible operations. Panics are reserved
59+
for debug-only precondition violations (e.g. LDLT symmetry check) and
60+
documented on the method.
61+
- Borrow by default (`&T`, `&[T]`); return borrowed views when possible.
62+
- Type and function names match textbook vocabulary (`Matrix`, `Vector`,
63+
`Lu`, `Ldlt`, `solve_vec`, `det`, `inf_norm`). Avoid Rust-ecosystem
64+
abstractions that obscure the math.
65+
66+
### Scientific notation in docs
67+
68+
- Unicode math (×, ≤, ≥, ∈, Σ, ², `2^-50`, …) is welcome in doc
69+
comments — readability trumps ASCII-only preference.
70+
- Reference literature via `REFERENCES.md` numbered citations (e.g.
71+
`\[8\]`, `\[9-10\]`).
72+
- State invariants mathematically where possible
73+
(`|A[i][i]| > Σ_{j≠i} |A[i][j]|`) rather than prose-only.
74+
75+
### Performance within scope
76+
77+
- Performance is a design goal, but strictly subordinate to the
78+
principles above. Never trade correctness, stability, or clarity for
79+
speed; if the two conflict, re-scope the problem rather than
80+
compromise the invariant.
81+
- The library earns its speed through *deliberate scope restriction*:
82+
fixed small dimensions via const generics, stack-allocated storage,
83+
and closed-form algorithms where available (D ≤ 4 for `det_direct` /
84+
`det_errbound`). Problems outside this scope — large or dynamic
85+
dimensions, sparse matrices, parallelism — belong to `nalgebra` or
86+
`faer` (see anti-goals in `README.md`).
87+
- Within scope, prefer allocation-free paths, `const fn` wherever the
88+
inputs allow, and FMA where applicable. Validate any performance
89+
claim against the `bench-vs-linalg` (vs nalgebra / faer) or
90+
`bench-exact` (exact-arithmetic) suites before relying on it.
91+
92+
### Testing mirrors the principles
93+
94+
- Unit tests cover known values, error paths, and dimension-generic
95+
correctness across D=2..=5 (see **Dimension Coverage** below).
96+
- Proptests under `tests/proptest_*.rs` cover algebraic invariants
97+
(round-trip, residual, sign agreement) — not just "does it not panic".
98+
- Adversarial inputs (near-singular, large-entry, Hilbert-style
99+
ill-conditioning) accompany well-conditioned inputs in both tests and
100+
benchmarks.
101+
- When a public API has two paths for the same question (fast filter +
102+
exact fallback), a proptest verifies they agree on the domain where
103+
both are defined.
104+
13105
## Core Rules
14106

15107
### Git Operations
@@ -124,17 +216,19 @@ just examples # Run all examples
124216
### Detailed Command Reference
125217

126218
- All tests (Rust + Python): `just test-all`
127-
- Benchmark comparison (generate `docs/PERFORMANCE.md`): `just bench-compare` (snapshot) or `just bench-compare v0.3.0` (vs baseline)
219+
- Benchmark comparison (generate `docs/PERFORMANCE.md`): `just bench-compare` (snapshot) or `just bench-compare v0.4.1` (vs baseline)
128220
- Benchmarks: `cargo bench` (or `just bench`)
129221
- Benchmarks (exact arithmetic): `just bench-exact`
130-
- Benchmarks (save baseline): `just bench-save-baseline v0.3.0`
222+
- Benchmarks (la-stack vs nalgebra/faer): `just bench-vs-linalg [filter]` (full run) or `just bench-vs-linalg-quick [filter]` (reduced)
223+
- Benchmarks (plot vs_linalg CSV/SVG): `just plot-vs-linalg [metric] [stat] [sample] [update_readme]` / `just plot-vs-linalg-readme [metric] [stat] [sample] [update_readme]`
224+
- Benchmarks (save baseline): `just bench-save-baseline v0.4.1`
131225
- Build (debug): `cargo build` (or `just build`)
132226
- Build (release): `cargo build --release` (or `just build-release`)
133227
- Changelog (generate full): `just changelog` (runs `git-cliff -o CHANGELOG.md` + post-processing)
134-
- Changelog (prepend unreleased): `just changelog-unreleased v0.3.0`
228+
- Changelog (prepend unreleased): `just changelog-unreleased v0.4.1`
135229
- Coverage (CI XML): `just coverage-ci`
136230
- Coverage (HTML): `just coverage`
137-
- Create release tag: `just tag v0.3.0` (creates annotated tag from CHANGELOG.md section)
231+
- Create release tag: `just tag v0.4.1` (creates annotated tag from CHANGELOG.md section) / `just tag-force v0.4.1` (recreate if the tag already exists)
138232
- Fast compile check (no binary produced): `cargo check` (or `just check-fast`)
139233
- Fast Rust tests (lib + doc): `just test`
140234
- Format: `cargo fmt` (or `just fmt`)
@@ -195,7 +289,7 @@ When using `gh` to view issues, PRs, or other GitHub objects:
195289
Use the `gh` CLI to read, create, and edit issues:
196290
197291
- **Read**: `gh issue view <number> --json title,body,labels,milestone | cat`
198-
- **List**: `gh issue list --json number,title,labels --jq '.[] | "#\(.number) \(.title)"' | cat` (add `--label enhancement`, `--milestone v0.4.0`, etc. to filter)
292+
- **List**: `gh issue list --json number,title,labels --jq '.[] | "#\(.number) \(.title)"' | cat` (add `--label enhancement`, `--milestone v0.4.1`, etc. to filter)
199293
- **Create**: `gh issue create --title "..." --body "..." --label enhancement --label rust`
200294
- **Edit**: `gh issue edit <number> --add-label "..."`, `--milestone "..."`, `--title "..."`
201295
- **Comment**: `gh issue comment <number> --body "..."`
@@ -204,7 +298,7 @@ Use the `gh` CLI to read, create, and edit issues:
204298
When creating or updating issues:
205299
206300
- **Labels**: Use appropriate labels: `enhancement`, `bug`, `performance`, `documentation`, `rust`, `python`, etc.
207-
- **Milestones**: Assign to the appropriate milestone (e.g., `v0.3.0`, `v0.4.0`)
301+
- **Milestones**: Assign to the appropriate milestone (e.g., `v0.4.1`, `v0.5.0`)
208302
- **Dependencies**: Document relationships in issue body and comments:
209303
- "Depends on: #XXX" - this issue cannot start until #XXX is complete
210304
- "Blocks: #YYY" - #YYY cannot start until this issue is complete
@@ -243,7 +337,12 @@ When creating or updating issues:
243337
f64 filter for fast sign resolution
244338
- Linear system solve: `solve_exact()`, `solve_exact_f64()` via Gaussian elimination
245339
with first-non-zero pivoting in `BigRational`
246-
- Rust tests are inline `#[cfg(test)]` modules in each `src/*.rs` file.
340+
- Rust unit tests are inline `#[cfg(test)]` modules in each `src/*.rs` file.
341+
- Property-based tests live under `tests/proptest_*.rs` (uses the `proptest`
342+
dev-dependency): `proptest_matrix.rs`, `proptest_vector.rs`,
343+
`proptest_factorizations.rs`, and `proptest_exact.rs` (the last gated on
344+
the `exact` feature). They run as integration tests via
345+
`just test-integration` or `just test-all`.
247346
- Python tests live in `scripts/tests/` and run via `just test-python` (`uv run pytest`).
248347
- The public API re-exports these items from `src/lib.rs`.
249348
- The `justfile` defines all dev workflows (see `just --list`).

CHANGELOG.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,25 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [0.4.1] - 2026-04-21
99

1010
### Added
1111

1212
- Regression tests for solver and determinant overflow handling [`f763b11`](https://github.com/acgetchell/la-stack/commit/f763b119bcc57276b83f370b0bf7abce654c7eb8)
1313
- Defensive-path test coverage for LU and LDLT solve_vec [`87d426f`](https://github.com/acgetchell/la-stack/commit/87d426fca1627445b804fd26b62fc7d9d4f0ae48)
1414
- Const-ify Lu/Ldlt det + solve_vec and Matrix inf_norm + det_errbound [`81ecb35`](https://github.com/acgetchell/la-stack/commit/81ecb35bdaf159f1f44d1eb24274ecf82c6567d5)
15+
- Fast-filter boundary proptests for exact determinant sign [`6357db3`](https://github.com/acgetchell/la-stack/commit/6357db35c70bca1b93e6bbf9a4fd231913631950)
1516

1617
### Changed
1718

1819
- Report infinite vs finite off-diagonal pairs as asymmetric [`1805779`](https://github.com/acgetchell/la-stack/commit/1805779dbca49183fbfa95c68ec00984966aa551)
20+
- Finalize documentation, benchmarks, and error handling [`0b98d3f`](https://github.com/acgetchell/la-stack/commit/0b98d3f6dbdd74699c318c4744a2b2f9a1b78481)
21+
- Consolidate and expand const-evaluability tests via macros [`f8d80a0`](https://github.com/acgetchell/la-stack/commit/f8d80a01e9913f87e1b19b2ad5ffbc0994e2bfdb)
22+
- Refactor solve_exact to use hybrid Bareiss forward elimination [`ecbbe8a`](https://github.com/acgetchell/la-stack/commit/ecbbe8a571ccaeb9cfedbf0269b8db44d43a5773)
23+
- Polish exact module (Component struct, errors, perf) [`53a5be6`](https://github.com/acgetchell/la-stack/commit/53a5be6abecc0af332398236ed6803ed75564b03)
24+
- Add adversarial-input coverage for exact arithmetic [#80](https://github.com/acgetchell/la-stack/pull/80) [`5bf5815`](https://github.com/acgetchell/la-stack/commit/5bf5815cb165c3b6145c5592420a58085a66efaa)
25+
- Expand exact-arithmetic re-exports and adversarial benchmarks [`b1a491d`](https://github.com/acgetchell/la-stack/commit/b1a491d902ccdaba6f9cd2e6f8e05514b6dfa3de)
26+
- Update AGENTS.md [`1e0648d`](https://github.com/acgetchell/la-stack/commit/1e0648dad3147ef127c775d8969b7cd214a2a6ed)
1927

2028
### Documentation
2129

@@ -38,6 +46,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3846
- Bump taiki-e/install-action from 2.75.7 to 2.75.18 [`433cfc1`](https://github.com/acgetchell/la-stack/commit/433cfc1b01c9128e93c82cb553aa63d4091bace3)
3947
- Bump MSRV to Rust 1.95 and adopt new stable features [`0ab3c33`](https://github.com/acgetchell/la-stack/commit/0ab3c336074f2b866256fbe5db8a8ec5306d580a)
4048

49+
### Performance
50+
51+
- Integer-only forward elimination for gauss_solve [#72](https://github.com/acgetchell/la-stack/pull/72) [`a1d3bdb`](https://github.com/acgetchell/la-stack/commit/a1d3bdbdb6fcb778a78a2a3d0cb66b79484e1472)
52+
4153
## [0.4.0] - 2026-04-11
4254

4355
### Added
@@ -262,7 +274,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
262274

263275
- Add tarpaulin coverage upload [`7486dfd`](https://github.com/acgetchell/la-stack/commit/7486dfd54e16a6dbde41575c3f35a1acb65f57d2)
264276

265-
[unreleased]: https://github.com/acgetchell/la-stack/compare/v0.4.0..HEAD
277+
[0.4.1]: https://github.com/acgetchell/la-stack/compare/v0.4.0..v0.4.1
266278
[0.4.0]: https://github.com/acgetchell/la-stack/compare/v0.3.0..v0.4.0
267279
[0.3.0]: https://github.com/acgetchell/la-stack/compare/v0.2.2..v0.3.0
268280
[0.2.2]: https://github.com/acgetchell/la-stack/compare/v0.2.1..v0.2.2

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cff-version: 1.2.0
22
message: "If you use this software, please cite it as below."
33
type: software
44
title: "la-stack: Fast, stack-allocated linear algebra for fixed dimensions in Rust"
5-
version: 0.4.0
5+
version: 0.4.1
66
url: "https://github.com/acgetchell/la-stack"
77
repository-code: "https://github.com/acgetchell/la-stack"
88
identifiers:

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "la-stack"
3-
version = "0.4.0"
3+
version = "0.4.1"
44
edition = "2024"
55
rust-version = "1.95"
66
license = "BSD-3-Clause"

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Add this to your `Cargo.toml`:
5757

5858
```toml
5959
[dependencies]
60-
la-stack = "0.4.0"
60+
la-stack = "0.4.1"
6161
```
6262

6363
Solve a 5×5 system via LU:
@@ -154,7 +154,7 @@ rationals (this pulls in `num-bigint`, `num-rational`, and `num-traits` for
154154

155155
```toml
156156
[dependencies]
157-
la-stack = { version = "0.4.0", features = ["exact"] }
157+
la-stack = { version = "0.4.1", features = ["exact"] }
158158
```
159159

160160
**Determinants:**
@@ -258,14 +258,14 @@ Summary (median time; lower is better). The “la-stack vs nalgebra/faer” colu
258258
<!-- BENCH_TABLE:lu_solve:median:new:BEGIN -->
259259
| D | la-stack median (ns) | nalgebra median (ns) | faer median (ns) | la-stack vs nalgebra | la-stack vs faer |
260260
|---:|--------------------:|--------------------:|----------------:|---------------------:|----------------:|
261-
| 2 | 2.309 | 4.365 | 140.156 | +47.1% | +98.4% |
262-
| 3 | 18.331 | 22.706 | 181.074 | +19.3% | +89.9% |
263-
| 4 | 27.430 | 51.372 | 210.451 | +46.6% | +87.0% |
264-
| 5 | 53.819 | 70.722 | 276.064 | +23.9% | +80.5% |
265-
| 8 | 143.611 | 160.309 | 356.960 | +10.4% | +59.8% |
266-
| 16 | 611.393 | 580.793 | 871.704 | -5.3% | +29.9% |
267-
| 32 | 2,631.241 | 2,733.946 | 2,832.816 | +3.8% | +7.1% |
268-
| 64 | 17,233.345 | 14,112.678 | 12,164.571 | -22.1% | -41.7% |
261+
| 2 | 2.173 | 4.448 | 139.923 | +51.2% | +98.4% |
262+
| 3 | 13.989 | 34.607 | 180.026 | +59.6% | +92.2% |
263+
| 4 | 27.580 | 48.435 | 203.163 | +43.1% | +86.4% |
264+
| 5 | 53.517 | 75.935 | 274.375 | +29.5% | +80.5% |
265+
| 8 | 134.859 | 162.859 | 371.463 | +17.2% | +63.7% |
266+
| 16 | 635.775 | 576.171 | 846.189 | -10.3% | +24.9% |
267+
| 32 | 2,704.570 | 2,731.740 | 2,589.494 | +1.0% | -4.4% |
268+
| 64 | 17,381.460 | 13,744.505 | 11,276.642 | -26.5% | -54.1% |
269269
<!-- BENCH_TABLE:lu_solve:median:new:END -->
270270

271271
## 📋 Examples

docs/BENCHMARKING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ just bench-vs-linalg
3939
just bench-exact
4040

4141
# Save an exact baseline (e.g., before optimising)
42-
just bench-save-baseline v0.4.0
42+
just bench-save-baseline v0.4.1
4343

4444
# Compare current code against a saved baseline
45-
just bench-compare v0.4.0
45+
just bench-compare v0.4.1
4646

4747
# Generate a snapshot without comparison
4848
just bench-compare
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
D,la_stack,la_lo,la_hi,nalgebra,na_lo,na_hi,faer,fa_lo,fa_hi
2-
2,2.309084094214292,2.303508072051772,2.320556696513248,4.364518872374484,4.358154936085015,4.36964789592874,140.15575262034162,139.53334588117198,140.4495844508752
3-
3,18.33097673394614,18.273359518783604,18.361403406211608,22.705897034612143,22.564093728463128,22.91072188569287,181.0737171931479,180.38682460200042,181.70129206459293
4-
4,27.430461775038097,27.41326480258085,27.462758331001304,51.37175309721522,51.29468685146327,51.477167992692316,210.45129217481627,210.11591462124417,211.12191423331922
5-
5,53.8187502446859,53.70850909928479,53.85944288329581,70.72209914126024,70.58461080060759,70.80642561266758,276.0642099442515,275.26718428973214,277.08908382769255
6-
8,143.61107101616628,143.08566824669077,144.23973111184426,160.3091383596412,157.74277868119285,162.4765631219522,356.9597107252403,356.09333193008604,357.7579293922214
7-
16,611.393474121212,607.0554516008083,614.0988895347266,580.7933953429945,578.7449824562907,581.0957836912207,871.7039621195331,869.4837470449172,873.6488504064271
8-
32,2631.241121426466,2626.669908635426,2635.1795337149238,2733.9455793946936,2732.3185346416067,2737.926103179753,2832.8157108843534,2829.2559523809523,2837.7406015037595
9-
64,17233.344659711875,17191.217013729976,17341.914285714287,14112.678414368063,13965.669014084508,14162.695618153364,12164.571092017737,12137.462174452254,12181.512446567765
2+
2,2.172623282439358,2.165444772092298,2.185506739324025,4.448408979470227,4.439782928520427,4.459719277058991,139.9226022608816,139.06474375246668,140.51212151197308
3+
3,13.989121746672993,13.955023991167852,14.013050521832692,34.606732435908626,33.499470537742226,34.761521044572746,180.02609852620088,179.59708359326262,180.8791974100286
4+
4,27.58037208037632,27.526517442670773,27.627054765320764,48.435129151777026,48.360737894391505,48.549664199225454,203.162599789916,202.79373021936047,203.32348086504715
5+
5,53.5167292278733,53.2079241828104,53.645503456564526,75.93470878300764,75.4146378575776,76.19896610821027,274.374828180443,272.8463967468175,275.1594136908799
6+
8,134.8587910191369,134.33353258195461,135.23630002147613,162.85903692402766,162.5110280122886,163.16671953470214,371.4632572777341,369.98617150369097,373.2859170186846
7+
16,635.7754523403698,630.758052532669,638.095305070158,576.1714016094243,575.8381924198251,577.8533527696793,846.1888291695698,843.6908420833043,847.8866115184559
8+
32,2704.5696137095592,2696.7101307744565,2715.9278532608696,2731.7404900064475,2726.3126052188554,2735.1429635561008,2589.4939744827334,2587.8085641182724,2594.9184784361632
9+
64,17381.45994623656,17346.594885060753,17418.868905879215,13744.504879275653,13711.504555743551,13761.18247949234,11276.642192271553,11263.305944055945,11293.149440751858

0 commit comments

Comments
 (0)