Skip to main content

Module klu_native

Module klu_native 

Source
Expand description

A from-scratch Rust port of the vendored SuiteSparse KLU solver (vendor/suitesparse/{BTF,AMD,KLU}/, v7.12.2 — see vendor/suitesparse/PROVENANCE.md for the upstream tag/commit this was ported from) — a fourth, always-built JacobianBackend alongside Scalar, Block, and the FFI-backed Klu (sparse_klu.rs).

Why this exists, separately from sparse_klu.rs: sparse_klu.rs links the vendored C directly via FFI, which needs a C compiler and libclang (for bindgen) at build time and is opt-in behind the klu Cargo feature specifically to isolate that burden and the LGPL-2.1-or- later license KLU/BTF carry (see vendor/suitesparse/PROVENANCE.md). This module is a faithful translation of the same algorithm into Rust instead — no C toolchain needed to build it.

Licensing: a close translation of licensed C source is reasonably a derivative work regardless of language, so this module carries forward its upstream licenses — not uniformly one license: amd/ translates AMD, which is BSD-3-Clause upstream (confirmed directly from its source headers’ SPDX-License-Identifier, same as vendor/suitesparse/PROVENANCE.md’s own table), while everything else here (btf/, analyze.rs, kernel.rs, factor.rs, scale.rs, refactor.rs, solve.rs) translates BTF/KLU, both LGPL-2.1-or-later. See PROVENANCE.md (this directory) for the full per-file mapping. This whole module is always built (no feature gate), per a deliberate, confirmed choice — unlike the FFI klu feature, there’s no C-toolchain burden left to gate against — so a default cargo build now always bundles this BSD/LGPL content alongside gridoxide’s own Apache-2.0 code. See Cargo.toml’s license field and docs/src/reference/provenance.md for that crate-wide consequence.

Scope: real (f64) matrices only, single right-hand side, int32- range indices — gridoxide’s own Newton-Raphson Jacobian solve never needs anything else (confirmed against every call site in sparse_klu.rs). Faithfully ports what real KLU actually does for gridoxide’s one fixed configuration (Options::default(), mirroring klu_defaults.c exactly): BTF block-triangular preprocessing, per-block AMD ordering, a partial-pivoting Gilbert-Peierls left-looking LU kernel with Eisenstat-Liu symmetric pruning, and cheap numeric-only refactorization. Explicitly out of scope (confirmed dead — no gridoxide call path reaches them): COLAMD/user-supplied ordering (Options/klu_common’s ordering is always AMD here), multi-RHS solve, complex arithmetic, and int64 (DLONG) indices.

Rows are scaled by default, as SuiteSparse’s KLU scales them (KluNativeSystem::new; with_scale(.., 0) turns it off). Scaling divides every row by its largest magnitude before each factorization; it decides which pivots partial pivoting sees as large and how the arithmetic rounds, never what the solution is. Scaled, the port solves as SuiteSparse’s KLU does — the same pivots and, on most matrices, the same bits (tests::scaled_matches_real_klu).

Power-flow Jacobians do not need it: examples/klu_scaling_stress.rs runs the worst-scaled networks at hand, with rows spanning up to 24 orders of magnitude, and unscaled the backward error stays at machine precision and every Newton solve takes the iterations SuiteSparse’s KLU takes. State estimation does: its system weights every measurement by 1/σ², and on the three-phase transmission case with one sensor fifteen σ off (se_three_phase_test::bad_data_detection_finds_the_corrupted_measurement) the unscaled port’s steps stall at 6e-8 and Gauss-Newton runs out of iterations, where scaled — like SuiteSparse’s KLU and faer — it converges in 8. Scaling costs 11–15% of a power-flow solve; a caller that knows its matrices are well scaled can switch it off.

Module layout (mirrors the phased port plan; each submodule’s doc comment names the specific upstream .c file(s) it was translated from): types (shared sentinel/Options plumbing) → btf → amd → kernel/ factor → scale → refactor → solve, with the public API (KluNativeSystem) assembled here.

Modules§

types
Sentinel-index encoding and fixed solver configuration shared across every klu_native submodule — mirrors vendor/suitesparse/KLU/Include/ klu_internal.h’s EMPTY/FLIP/UNFLIP (identical, by inspection, to AMD/Include/amd_internal.h’s and BTF/Include/btf.h’s own BTF_FLIP/BTF_UNFLIP/BTF_ISFLIPPED) and KLU/Source/klu_defaults.c’s KLU_common defaults.

Structs§

KluNativeSystem
A real sparse system whose sparsity pattern is fixed across repeated solves — the pure-Rust twin of sparse_klu::KluRealSystem, same public shape (new/factor_and_solve) so solver.rs can dispatch to either backend mechanically. Caches the symbolic analysis (analyze::analyze) and numeric factorization (factor::Numeric) across calls, using refactor::refactor for cheap numeric-only re-factorization on repeat calls — mirrors KluRealSystem’s own doc comment and the same pattern validated for RealSparseSystem/BlockLu::refactor elsewhere in this crate.