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
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ When user requests commit message generation:
- The current MSRV and pinned contributor/CI toolchain are Rust 1.98.0. Keep
`Cargo.toml`, `rust-toolchain.toml`, and `clippy.toml` aligned when that
baseline changes deliberately.
- Rust's `f64::algebraic_*` operations are forbidden in all repository-owned
Rust code, including tests, examples, and benchmarks.
Their unspecified reassociation, precision, and special-value behavior can
invalidate defined operation order, error bounds, non-finite classification,
exact fallbacks, and reproducibility. Ordinary operators remain allowed.
Deliberate fused multiply-add through `f64::mul_add` is explicitly allowed;
existing numerical kernels and error bounds may rely on its single-rounding
evaluation. Any fast-math design requires a separate issue, opt-in contract,
correctness analysis, and benchmark evidence.
- Prefer borrowed APIs by default:
take references (`&T`, `&mut T`, `&[T]`) as arguments and return borrowed views (`&T`, `&[T]`) when possible.
Only take ownership or return `Vec`/allocated data when required.
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

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

29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,28 @@ With `features = ["exact"]`, stored binary64 inputs are lifted losslessly to
rationals for exact determinant signs, determinant values, and solves. Exactness
starts at the stored values and cannot recover information rounded away before
construction. See the
[mathematical basis](https://github.com/acgetchell/la-stack/blob/main/docs/mathematical_basis.md)
[mathematical basis](https://github.com/acgetchell/la-stack/blob/v0.4.5/docs/mathematical_basis.md)
for the algorithms, validity boundaries, and supporting references.

## ✨ Design goals

- ✅ `Copy` types where possible
- ✅ Const-generic storage (no dynamically sized matrix or vector representation)
- ✅ `const fn` where possible (compile-time evaluation of determinants, dot products, etc.)
- ✅ Explicit algorithms (LU, solve, determinant)
- ✅ Const-generic storage (no dynamically sized matrix or vector representation)
- ✅ `Copy` types where possible
- ✅ Defined binary64 arithmetic semantics: Rust's `f64::algebraic_*`
operations are forbidden because their unspecified reassociation, precision,
and special-value behavior is incompatible with the crate's error bounds,
non-finite classification, exact fallbacks, and reproducibility contract;
deliberate `f64::mul_add` remains allowed for its defined single-rounding
semantics
- ✅ Error-bounded f64 determinant filtering plus optional exact signs
(`det_errbound`, `det_sign_exact`)
- ✅ Exact determinant values and linear solves via optional arbitrary-precision
arithmetic (`det_exact`, `solve_exact`, strict/rounded f64 conversions)
- ✅ No runtime dependencies by default (optional features may add deps)
- ✅ Explicit algorithms (LU, solve, determinant)
- ✅ Inline, stack-backed storage for core types; optional arbitrary-precision
exact values allocate as required
- ✅ No runtime dependencies by default (optional features may add deps)
- ✅ `unsafe` forbidden

See [CHANGELOG.md](https://github.com/acgetchell/la-stack/blob/v0.4.5/CHANGELOG.md)
Expand All @@ -67,11 +73,14 @@ for current release planning.

## 🚫 Anti-goals

- Bare-metal performance: see [`blas-src`](https://crates.io/crates/blas-src),
[`lapack-src`](https://crates.io/crates/lapack-src), or [`openblas-src`](https://crates.io/crates/openblas-src)
- Alternate floating-point scalar families: `la-stack` supports `f64` and optional exact arithmetic, not `f32` / `f16` APIs
- Bare-metal performance: use [`blas`](https://crates.io/crates/blas) or
[`lapack`](https://crates.io/crates/lapack) with a native backend selected
through [`blas-src`](https://crates.io/crates/blas-src),
[`lapack-src`](https://crates.io/crates/lapack-src), or
[`openblas-src`](https://crates.io/crates/openblas-src)
- Broad general-purpose linear algebra: use [`nalgebra`](https://crates.io/crates/nalgebra)
- Large matrices/dimensions with parallelism: use [`faer`](https://crates.io/crates/faer)
- Alternate floating-point scalar families: `la-stack` supports `f64` and optional exact arithmetic, not `f32` / `f16` APIs

## ✅ Use this crate when

Expand All @@ -98,6 +107,8 @@ cases better served by broader linear-algebra libraries.

## 🚀 Quickstart

The minimum supported Rust version (MSRV) is 1.98.0.

Add this to your `Cargo.toml`:

```toml
Expand Down Expand Up @@ -445,7 +456,7 @@ fn main() -> Result<(), LaError> {

The error coefficients (`ERR_COEFF_2`, `ERR_COEFF_3`, `ERR_COEFF_4`) are
conservative, dimension-specific constants, not caller-tunable tolerances. The
[mathematical basis](https://github.com/acgetchell/la-stack/blob/main/docs/mathematical_basis.md#determinants-and-certified-sign-filtering)
[mathematical basis](https://github.com/acgetchell/la-stack/blob/v0.4.5/docs/mathematical_basis.md#determinants-and-certified-sign-filtering)
documents the bound and states its range preconditions. The constants are explicit
crate-root exports for advanced users who want to compose the same bound:
`use la_stack::{ERR_COEFF_2, ERR_COEFF_3, ERR_COEFF_4};`. They intentionally stay
Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ cargo_machete_version := "0.9.2"
cargo_nextest_version := "0.9.143"
cargo_update_version := "22.1.1"
clippy_sarif_version := "0.8.0"
dprint_version := "0.56.0"
dprint_version := "0.56.1"
git_cliff_version := "2.13.1"
just_version := "1.58.0"
rumdl_version := "0.2.58"
rumdl_version := "0.2.60"
sarif_fmt_version := "0.8.0"
taplo_version := "0.10.0"
typos_version := "1.49.0"
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,6 @@ dev = [
"semgrep==1.174.0",
"shellcheck-py==0.11.0.1",
"shfmt-py==4.0.0",
"ty==0.0.73",
"ty==0.0.74",
"yamllint==1.38.0",
]
4 changes: 2 additions & 2 deletions scripts/subprocess_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,8 @@ def run_git_command_with_input(
"""
git_path = get_safe_executable("git")
run_kwargs = _build_run_kwargs("run_git_command_with_input", **kwargs)
encoding: str = run_kwargs.get("encoding") or "utf-8"
errors: str = run_kwargs.get("errors") or "strict"
encoding = cast("str", run_kwargs.get("encoding") or "utf-8")
errors = cast("str", run_kwargs.get("errors") or "strict")
payload = input_data if isinstance(input_data, bytes) else input_data.encode(encoding, errors)
with tempfile.TemporaryFile() as stdin:
stdin.write(payload)
Expand Down
25 changes: 23 additions & 2 deletions semgrep.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,36 @@ rules:
- "/src/**/*.rs"
- "/examples/**/*.rs"
- "/benches/**/*.rs"
# Test-only violation fixture: `just semgrep` excludes it, while
# Includes integration/property tests and the deliberate violation
# fixture. `just semgrep` excludes that fixture, while
# `just semgrep-test` scans it directly.
- "/tests/semgrep/src/project_rules/algebraic_float.rs"
- "/tests/**/*.rs"
pattern-either:
- pattern: $VALUE.algebraic_add(...)
- pattern: $VALUE.algebraic_sub(...)
- pattern: $VALUE.algebraic_mul(...)
- pattern: $VALUE.algebraic_div(...)
- pattern: $VALUE.algebraic_rem(...)
- patterns:
- pattern: <f64>::$METHOD(...)
- metavariable-regex:
metavariable: $METHOD
regex: ^algebraic_(?:add|sub|mul|div|rem)$
- patterns:
- pattern: <f64>::$METHOD
- metavariable-regex:
metavariable: $METHOD
regex: ^algebraic_(?:add|sub|mul|div|rem)$
- pattern-not-inside: <f64>::$METHOD(...)
- patterns:
- pattern: $FLOAT::$METHOD
- metavariable-regex:
metavariable: $FLOAT
regex: ^f64$
- metavariable-regex:
metavariable: $METHOD
regex: ^algebraic_(?:add|sub|mul|div|rem)$
- pattern-not-inside: $FLOAT::$METHOD(...)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- id: la-stack.rust.no-public-infallible-raw-f64-constructors
languages:
Expand Down
44 changes: 40 additions & 4 deletions tests/semgrep/src/project_rules/algebraic_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,29 @@ pub fn forbidden_f64_operations(left: f64, right: f64) -> [f64; 5] {
]
}

pub fn forbidden_f32_associated_operation(left: f32, right: f32) -> f32 {
pub fn forbidden_f64_associated_operation(left: f64, right: f64) -> f64 {
// ruleid: la-stack.rust.no-algebraic-float-operations
f32::algebraic_add(left, right)
f64::algebraic_add(left, right)
}

pub fn forbidden_f64_associated_operation(left: f64, right: f64) -> f64 {
pub fn forbidden_f64_function_item() -> fn(f64, f64) -> f64 {
// ruleid: la-stack.rust.no-algebraic-float-operations
f64::algebraic_add(left, right)
f64::algebraic_sub
}

pub fn forbidden_f64_qualified_call(left: f64, right: f64) -> f64 {
// ruleid: la-stack.rust.no-algebraic-float-operations
<f64>::algebraic_add(left, right)
}

pub fn forbidden_f64_qualified_function_item() -> fn(f64, f64) -> f64 {
// ruleid: la-stack.rust.no-algebraic-float-operations
<f64>::algebraic_sub
}

pub fn forbidden_f64_reduction(values: &[f64]) -> Option<f64> {
// ruleid: la-stack.rust.no-algebraic-float-operations
values.iter().copied().reduce(f64::algebraic_mul)
}

pub fn permitted_f64_operations(left: f64, right: f64) -> [f64; 6] {
Expand All @@ -39,3 +54,24 @@ pub fn permitted_f64_operations(left: f64, right: f64) -> [f64; 6] {
left.mul_add(right, 1.0),
]
}

pub fn permitted_f64_function_item() -> fn(f64, f64) -> f64 {
fn add(left: f64, right: f64) -> f64 {
left + right
}

// ok: la-stack.rust.no-algebraic-float-operations
add
}

pub fn permitted_f64_qualified_fma(
left: f64,
right: f64,
) -> (f64, fn(f64, f64, f64) -> f64) {
(
// ok: la-stack.rust.no-algebraic-float-operations
<f64>::mul_add(left, right, 1.0),
// ok: la-stack.rust.no-algebraic-float-operations
<f64>::mul_add,
)
}
Loading
Loading