Repository navigation
LAPACK-style test suites (ratios, dimension sweeps, cross-routine checks) and a side/layout-general portable ?ormqr_compact (0.2.0) - #62
Merged
Conversation
LAPACK's linear-equation suites take M and N from a list that starts
0 1 2 3 5 and run every path over the full M x N cross product, so the
empty operand, the single row or column, and the orders around a block
boundary are exercised in every routine. The portable suites only ran
them where someone had thought of them (gels had m = 0 and 1 x n, geqrf
and potrf started at 3, ormqr ran one fixed m).
Every portable suite now sweeps its run_case over small_dims = {0, 1, 2,
3, 5} (test_compact_util.hpp): geqrf, trsm and gels over m x n (gels per
layout and trans), ormqr over m x nrhs, orgqr over m >= n with LAPACK's
K values {n, 0, 1, n/2}, potrf and sytrfnp over n through the
factorization checks and n x nrhs through the solve pipeline -- each with
one full group plus a padded one, and the existing checks unchanged, so
a zero extent is checked as the routine's quick return (info = 0, every
error 0).
What that needed:
- run_case passes ld = max(1, extent), as LAPACK's ld >= max(1, m)
requires and the C API validates.
- compact_buffer never returns an empty vector: a zero-extent operand is
still an argument the routine may require present (posv's bp at
nrhs = 0, gels's A at m = 0, asserted in cbk.cpp), so it keeps the one
element a Fortran caller's dummy would be.
- is_rowmajor accepts an empty view (mat_view of a 0 x n matrix has
sj = 0), leaving ld >= max(1, extent) to lapack_ld.
- The LAPACKE-backed references take LAPACK's own quick return on an
empty operand before the call, so an empty batch's possibly null
pointer never reaches a stack's NaN check; ref_gels's is B := 0, the
?gels contract, and the gels suite's residual-rows identity is skipped
at min(m, n) = 0 for the same reason.
Passes gcc Release, clang Debug (the view asserts on the empty shapes)
and the MKL-extension build.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_014ZZ76AgYwQ3vtuyZ46tyMX
The portable suites gated each check on its own hand-picked tolerance -- 200 eps m, 20 n eps, 1e5 eps m, 1e4 eps p, 40 eps n ||A||, ... -- so no two checks were comparable, a near miss was invisible, and the cond(A)-dependent forward errors carried a guessed headroom. LAPACK's TESTING suites use one convention instead: every check is a dimensionless ratio, the error over what backward stability allows it to be, (order) * (operand norm) * eps, and a single threshold decides. test_compact_util.hpp now carries that convention: test_ratio<T>(err, dim, norm), the residual form of dqrt01 / dpot01 / dget02; forward_ratio<T>(err, normx, rcond), dget04's forward error discounted by rcond; THRESH = 30, the value of LAPACK's dtest.in; and passes(), written so NaN fails. test_lapack_util.hpp adds rcond1 -- 1 / (||A|| ||A^-1||) by LAPACKE ?getrf + ?gecon on the dense, unpacked input, since a condition number cannot be taken from the packed format -- and solve_ratios, the dget02 + dget04 pair the solve pipelines gate on. Every check in the seven portable suites is now such a ratio, printed with its case: the factorization residuals and reconstructions relative to ||A||, the elementwise comparisons against LAPACKE relative to the operand (1 for an orthogonal Q), the solve residuals to ||A|| ||X||, and the forward errors -- geqrf's and ormqr's back substitution, potrf's and sytrfnp's solve pipelines, trsm's solve vs the library's (rcond of the operator actually applied, the unit diagonal honored), gels's Gram-formed minimum-norm solution (rcond of the Gram matrix, the cond(A)^2 the old 1e4 headroom stood for) -- discounted by rcond. The structural contracts (bit-for-bit fused == two-step, untouched triangles, identity padding lanes) stay exact. Observed ratios: at most 2.2 across the suites, in both precisions, against THRESH = 30. The MKL suites keep their relative gates for now (solve_errors stays for them). CLAUDE.md records the convention. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_014ZZ76AgYwQ3vtuyZ46tyMX
The ratio refactor kept LAPACK's denominators but not its numerators: max-abs entries where dqrt01 / dpot01 / dsyt01 take the 1-norm of the residual matrix, and a whole-matrix ||X||_1 with an order factor where dget02 / dpot02 / dtrt02 are per right-hand side without one -- up to n times more lenient than the checkers they cite. Every ratio now has the checker's exact form (test_compact_util.hpp): test_ratio over diff_norm1 / orth_norm1 (the 1-norm of R - Q^T A, L L^T - A, Q^T Q - I, ...); residual_ratio, dget02's max_j ||b_j - A x_j||_1 / (||A||_1 ||x_j||_1 eps); forward_ratio, dget04's max_j ||x_j - x_j^true||_inf / ||x_j^true||_inf * rcond / eps, on views rather than a scalar error. solve_ratios forms the residual explicitly and hands it to those. Observed ratios rose accordingly, to at most 4.9 against THRESH = 30. The PO, SY and TR paths were audited against dchkpo / dchksy / dchktr. Present already: dpot01 / dsyt01 reconstruction, dpot02 residual, dget04 forward error, dtrt02 residual, the fused drivers bit-for-bit against their two-step forms, plus the lane-isolation and untouched-triangle contracts LAPACK has no analogue for. Not applicable: dpot03 / dtrt01 (no inverse routine), dpocon / dtrcon (no condition estimator), the refinement bounds (no ?rfs). Missing, and added: - trsm had no exact-solution forward error, only agreement with cblas_?trsm: the right-hand side is now B = op(A) X for a random X (dlarhs), so dget04 runs against the exact alpha X as well as against the library's solution, and dtrt02's residual reads side 'R' as the transposed system its rows are the right-hand sides of. - No check tied potrs / sytrsnp to the trsm sweeps they are built from: run_compose in both suites solves the same system through the public ?trsm_compact (two non-unit sweeps for potrs; two unit sweeps around D^-1 for sytrsnp) and gates dget04 of the one solution against the other. potrs comes out bit-identical to its two sweeps in every configuration (reported, not gated); sytrsnp's ratio is ~0.03. CLAUDE.md's convention entry names the three forms and the cross-routine checks. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_014ZZ76AgYwQ3vtuyZ46tyMX
The kernel (cbk_ormqr_compact.hpp) covers every layout, side and trans in one strided body -- side 'R' is the kernel over the transposed view -- and the MKL-style cbk_?ormqr_compact exposes all of it, but the portable ?ormqr_compact hardcoded side 'L', column-major, so the portable surface (the one every stack builds) could only apply Q^T from the left, and its suite could only test that. ?ormqr_compact now takes (layout, side, trans, m, n, k, ap, ldap, taup, cp, ldcp, V, nm), LAPACK ?ormqr's argument order behind the layout, like the other portable entry points and the MKL-style one. Validation in argument order: -1 layout, -2 side, -3 trans, -4 m, -5 n, -6 k (k <= nq, nq = m for side 'L' and n for 'R', the order of Q and the rows of A), -8 ldap (>= nq column-major, >= k row-major), -11 ldcp, -12 V, -13 nm. The compat<T> and compact<T> dispatchers, ref_ormqr (a side-aware overload, the side-'L' one forwarding to it) and the geqrf suite's call follow. tests/test_ormqr_compact.cpp runs the full layout x side x trans product on every stack: vs LAPACKE_?ormqr, vs ?gemm with the Q that ?orgqr forms explicitly -- LAPACK's dqrt03, ||CC - op(Q) C||_1 / (nq ||C||_1 eps) -- and the round trip op(Q)^T op(Q) C = C, at the suite's shapes (k < nq and a padded group among them), the small-dimension cross product with LAPACK's K values on the tuned route and k = nq on the transposed one, and the solve pipelines (?geqr2's and ?geqp3's reflectors) as before. Observed ratios: at most 1.6, the tiny shapes; 0.06-0.6 at the representative ones. PLANS.md and CLAUDE.md record the generalized surface and the dqrt03 check. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_014ZZ76AgYwQ3vtuyZ46tyMX
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_014ZZ76AgYwQ3vtuyZ46tyMX
The portable ?ormqr_compact changed signature (layout, side and trans), which under the changelog's pre-1.0 rule is a minor release. CBK_VERSION in cbk.h and project(VERSION) in CMakeLists.txt move to 0.2.0 together (cbk.cpp's static_assert keeps them in step; the shared library becomes libcbk.so.0.2.0, soname unchanged), and the changelog's Unreleased section -- the LAPACKE-backed suites, the test-ratio convention, and the ?ormqr_compact change -- becomes the 0.2.0 entry. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_014ZZ76AgYwQ3vtuyZ46tyMX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the portable test suites onto the practices of LAPACK's
TESTINGtree, and closes the one gap that audit found in the API: the portable?ormqr_compactcould only apply Q from the left in column-major, while the kernel and the MKL-style entry point covered every side and layout. Six commits; the API change makes this 0.2.0.Test suites (
tests/)413cf8b,a379fc8). Every gate in the seven portable suites is a dimensionless test ratio in the exact form of the corresponding LAPACK checker, compared against the singleTHRESH = 30ofdtest.in— no per-checkk * eps * ntolerances remain. The three forms live intest_compact_util.hpp:test_ratio<T>for factorization residuals (dqrt01 / dpot01 / dsyt01: the 1-norm ofR - Q^T A,L L^T - A, ... over(order) * ||A||_1 * eps),residual_ratiofor solves (dget02 / dpot02 / dtrt02: per right-hand side, no order factor), andforward_ratiofor forward errors (dget04: per right-hand side, discounted byrcond, whichrcond1takes from LAPACKE?getrf+?geconon the dense, unpacked input — never from the packed format). Each case prints its ratios next to its verdict; observed ratios are O(1), at most ~5 across the suites in both precisions. Only structural contracts (bit-for-bit reproduction, untouched storage, identity padding lanes) stay exact.7f67361). As LAPACK sweepsM, Nover0 1 2 3 5 ..., every suite runs itsrun_caseoversmall_dims = {0, 1, 2, 3, 5}—m x n(geqrf, trsm, gels per layout/trans),m x nrhs(ormqr),m >= nwith LAPACK's K values (orgqr, ormqr),nandn x nrhsthrough the solve pipelines (potrf, sytrfnp) — so the empty operand, the single row or column, and the orders around a register block are checked in every routine, not only where someone thought of them. The empty shapes neededld = max(1, extent), a never-emptycompact_buffer(Fortran dummy semantics forposv'sbpatnrhs = 0,gels'sAatm = 0), anis_rowmajorthat accepts an empty view, and LAPACK's quick returns in the LAPACKE-backed references (ref_gels's isB := 0).a379fc8):potrsagainst the twotrsmsweeps it is built from (bit-identical in every configuration, reported not gated),sytrsnpagainsttrsm,D^-1,trsm; the trsm suite gained dget04 against the exact solution (B = op(A) Xfor a random X, as dlarhs) next to its comparison withcblas_?trsm. The PO / SY / TR paths were audited againstdchkpo/dchksy/dchktr: reconstruction, solve residual and forward error were already present; inverse, condition-estimate and refinement checks have no cbk routine to test.API:
?ormqr_compact(33ddb90)Breaking. The portable
?ormqr_compactnow takes(layout, side, trans, m, n, k, ap, ldap, taup, cp, ldcp, V, nm)— LAPACK?ormqr's argument order behind the layout, as the MKL-stylecbk_?ormqr_compactalready did — so Q is applied from either side in either layout; it was side'L', column-major only, with the adapter hardcodingleft = true, rowmajor = falseover a kernel that already handled all of it (side'R'is the kernel over the transposed view). A caller of the old(trans, m, nrhs, k, ...)writes('C', 'L', trans, m, nrhs, k, ...). Validation renumbers in argument order (-1 layout ... -13 nm), withk <= nqfornq = m(side'L') orn(side'R'),ldap >= nqcolumn-major />= krow-major. The examples already use the compat API; the only in-tree callers were the tests.tests/test_ormqr_compact.cppnow runs the full layout x side x trans product on every stack: vsLAPACKE_?ormqr, vs?gemmwith the Q that?orgqrforms explicitly (LAPACK's dqrt03,||CC - op(Q) C||_1 / (nq ||C||_1 eps)), the round tripop(Q)^T op(Q) C = C, and the solve pipelines (?geqr2's and?geqp3's reflectors) as before. With this, the QR path's LAPACK inventory is complete on the portable stack: dqrt01 (geqrf), dqrt02 (orgqr), dqrt03 (ormqr), dget02 / dqrt14-style properties (gels).Release: 0.2.0 (
4fc740b,27d1e41)CHANGELOG.mdrecords the breaking signature and the test conventions; under its pre-1.0 rule the API change is a minor release, soCBK_VERSION(cbk.h) andproject(VERSION)(CMakeLists.txt) move to 0.2.0 together —cbk.cpp'sstatic_assertkeeps them in step; soname unchanged — and the Unreleased section becomes the 0.2.0 entry. Thev0.2.0tag is left for after the merge.Verification
gcc Release, clang Debug (the view asserts on the empty shapes) and the MKL-extension tree (
-DCBK_WITH_MKL=ON, 20 tests) pass locally; pre-commit clean. CI: gcc/clang x OpenBLAS/Netlib/MKL (lp64 and ilp64), macOS Accelerate, the install check both ways.The MKL suites keep their own relative gates for now (
solve_errorsremains for them).🤖 Generated with Claude Code
https://claude.ai/code/session_014ZZ76AgYwQ3vtuyZ46tyMX