saloon: Cholesky, conjugate gradient, and one-sided Jacobi SVD (Hoon) - #88
Merged
Merged
Conversation
Step 2 of #86's foundation list, stacked on #87 (whose +argsort-dim gives +svd its descending sort). Hoon only, no jet hints, pure addition to the %linalg section. Factorization and direct solves: +chol (with +chol-unit reporting a non-positive-definite matrix as ~ rather than crashing), +trsv-lo/+trsv-up forward and back substitution, and +chol-solve. Iterative: +cg from x0 = 0, returning [x iter rnorm] so a caller can tell convergence from exhaustion, and +pcg with the Jacobi (diagonal) preconditioner. Each iteration is one +matvec and two +dotv, so neither touches the matrix except through products. +svd is the thin one-sided Jacobi decomposition: rotations orthogonalize the COLUMNS of a working copy, so at convergence the column norms are the singular values and the normalized columns are U, with V the accumulated rotations. Its output is sorted descending (unlike +eig, whose order is arbitrary) because singular values have a natural order. +svd-vals takes just the values. Vector helpers on rank-1 rays, as +diag already returns: +matvec, +dotv, +nrm2, +axpyv, +col-dot. Every inner product sums LEFT TO RIGHT from the kind's +0 -- the order a C or Rust kernel walks, so these stay jettable, and not NumPy's pairwise summation. Tolerances come from the core's .rtol exactly as +eig takes them, including the +feps substitution for the unusable bare default. Tests: 44 arms in saloon-linalg. The exact cases (integer Cholesky factors, one-step CG on diagonal systems, singular values of matrices whose columns are already orthogonal) compare whole rays, since every intermediate is exactly representable at any width; the rest compare against NumPy within a tolerance. saloon/tools/linalg_check.py re-derives all of it (46 checks): exact rational arithmetic asserts op-by-op that the exact cases never round, and numpy supplies the approximate references. It also verifies the worked examples in the doccords, one of which it corrected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgBnKsUPYzkoPZonjePnaq
saloon-eig and saloon-eigh have not built on a ship since /lib/math's precision
doors gained a third sample field: their sort comparators pass
`~(lth rd:math [%n .~1e-10])`, and clay reports
-need.[rtol=@rd atol=@rd]
-have.@rd
nest-fail
FAILED /tests/lib/saloon-eig/hoon (build)
so 23 regression tests have been silently unrunnable. Add the missing atol
field at the three call sites and in the two generators that emit them (the
files are generated; fixing only the output would regress on the next run).
Verified on a fakezod at hoon-136: saloon-eig 18 tests and saloon-eigh 5 tests,
both green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgBnKsUPYzkoPZonjePnaq
This was referenced Sep 21, 2026
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.
Step 2 of the foundation work list in #86: Saloon's direct and iterative solvers, Hoon only, no jet hints.
Stacked on #87 — base branch is
sigilante/lagoon-foundation, and+svduses that PR's+argsort-dimfor its descending sort. Review #87 first; this diff is only thesaloon/files.What this adds (all
%i754, bloq 4/5/6/7)Cholesky and direct solves
++chol— the lower-triangularLwithA = L*L^T, right-looking and column by column.++chol-unitis the same computation returning(unit ray), reporting a non-positive-definite matrix as~instead of crashing, which is the honest outcome for a numerical routine;++cholis the crashing wrapper.++trsv-lo/++trsv-up— forward and back substitution. The latter readsLtransposed rather than materializingL^T.++chol-solve— one factorization plus the two substitutions.Conjugate gradient
++cg— fromx0 = 0, stopping at|r| <= rtol*|v|ormaxit, returning[x iter rnorm]so the caller can distinguish convergence from exhaustion. One+matvecand two+dotvper iteration; the matrix is touched only through products, never factored. A flat search direction (p.Ap = 0, i.e. not positive definite) returns the current iterate instead of dividing by zero.++pcg— the same with the Jacobi (diagonal) preconditioner; a zero diagonal entry is left unscaled.One-sided Jacobi SVD
++svd— the thin decompositionA = U*diag(S)*V^Tforrows >= cols. Rotations orthogonalize the columns of a working copy ofA, so at convergence the column norms are the singular values and the normalized columns areU, withVthe accumulated rotations. The rotation is the one that zeroes a column inner product:t^2 + 2*zeta*t - 1 = 0,zeta = (beta-alpha)/(2*gamma)— the same shape as the symmetric sweep already in+eig, and it reuses+rot-cols, which already worked on rectangular input. Output is sorted descending, unlike+eigwhose order is arbitrary, because singular values have a natural order.++svd-valsfor the values alone.~&trace on the cap, as+eigdoes.Vector helpers on rank-1 rays (the shape
+diagalready returns, notn x 1matrices):++matvec,++dotv,++nrm2,++axpyv,++col-dot.Two conventions carried over deliberately: every inner product sums left to right from the kind's
+0(the order a C or Rust kernel walks, so these stay jettable, and not NumPy's pairwise summation), and tolerances come from the core's.rtolexactly as+eigtakes them,+fepssubstitution for the unusable bare default included.Tests: 44 arms
saloon/desk/tests/lib/saloon-linalg.hoon, in the style of the existing suites.The exact cases compare whole rays: integer Cholesky factors (
[[4,2],[2,5]] -> [[2,0],[1,2]], a 3x3, and the same factorization at bloq 5),chol-solve, the triangular solves, one-step CG and PCG on identity and diagonal systems, and singular values of matrices whose columns are already orthogonal (so Jacobi performs no rotation and the norms are exact). Every intermediate there is exactly representable, so they hold at any width and in any rounding mode.The approximate cases compare within a tolerance against NumPy: singular values of a 2x2 and a 4x3, reconstruction
U*diag(S)*V^T ≈ A,V^T V ≈ IandU^T U ≈ I, CG convergence, CG agreeing withchol-solve, and PCG on a badly scaleddiag(1e6, 1). Edge cases too:maxit=0, a zero right-hand side, a zero singular value leaving itsUcolumn zero, and the crash paths (asymmetric input tochol/cg, a wide matrix tosvd).saloon/tools/linalg_check.pyre-derives all of it — 46 checks, 0 failures. Like #87's oracle it mirrors every operation infractions.Fractionand asserts that the exact cases never round, rather than just comparing final answers. It also checks the worked examples in the doccords, and corrected one of them (+cg's example converges to1.0000000000000004, not what I first wrote).The suite avoids
+transpose:la— it has a local+tpose— because that jet crashes on runtimes older than urbit/vere#1057.Verification
Ship: fakezod at hoon-136 /
[%zuse 409],%numdesk carrying lagoon, saloon, math, complex, twoc, unum, fixed, rand, i754rand and the whole test tree.Same 27 failures on both, +44 OK — exactly the new tests, no regression. Of those 27, 21 are the pre-existing lagoon Hoon-vs-C jet gap documented in #87 (this runtime predates urbit/vere#1057), and 6 are pre-existing on the Saloon side: 4 posit transcendental tests in
saloon-unum, plus the two build failures the second commit fixes.Second commit: the eig suites have been unrunnable
saloon-eigandsaloon-eighhave not built on a ship since/lib/math's precision doors gained a third sample field. Their sort comparators pass~(lth rd:math [%n .~1e-10])and clay answers:so 23 regression tests were silently dead. I hit the identical error writing my own comparator, which is how I noticed. Fixed at the three call sites and in the two generators that emit them (the files are generated, so fixing only the output would regress on the next run). Both suites are now green on the ship:
saloon-eig18 tests,saloon-eigh5.Happy to split that commit out if you would rather keep this PR to the new arms.
Not in this PR
Jets for any of it (the C mirror and NockVM both), LU with pivoting, Householder QR, and the
%unum/%cplxkinds. Upstreaming the Hoon tourbit/urbitis separate, per AGENTS.md.Refs #86.
🤖 Generated with Claude Code
https://claude.ai/code/session_01TgBnKsUPYzkoPZonjePnaq