Repository navigation
Tests: growth_tol for LDLᵀ, seeds, SparseMatrixCSC constructors, blocked regime-B coverage, refinement bound - #100
Conversation
…ked regime-B coverage, refinement bound - test_numeric_ldlt.jl: panel/D checks against the reference use growth_tol (#86), plus ldlt_error ≤ tol(T) on the device factor; piv, pivot_kind and perturbed D entries stay exact - utils.jl: factor_growth takes 2×2 blocks (σ_min bound) and skips perturbed pivots; the naive min |d| was 0 on every 2×2 block, which made growth_tol infinite - new testset: regime-B fronts on the blocked LDLᵀ path (ldlt_blocked_path && !takes_c_path, #89) - Random.seed!(666) at the start of every drawing testset in the numeric files (#76, #85) - test_api.jl: DirectSolver(::SparseMatrixCSC) and update!(::SparseMatrixCSC) on the CPU backend (#63) - test_refinement.jl: the fragile @test_broken r1 <= r0/100 becomes r0 ≤ 100 eps and r1 ≤ r0 (#79); T16 Report updated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wzx8WtMEXnAfyzyujdySFh
There was a problem hiding this comment.
VERDICT: CHANGES_REQUESTED (head ec646ac)
Scope. Owner clean-up PR ("Post-T21 clean-up PR 2", STATE.md §3 "Tests and docs"), tests only: no src/, no ext/, no Manifest.toml, PLAN.md untouched. The only TASKS.md edit updates the two T16 Report sentences that named the removed @test_broken, as the description says. All six STATE.md §3 test items (#86, #63, #76/#85, #79, #89, item 6/7) are addressed.
Checked.
factor_growth:d[n + k]is the 2×2 subdiagonal in the reference layout (src/reference/ldlt.jl,d_errordocstring);|det| / ‖B‖_F ≤ σ_min, and the smaller of the symmetric/Hermitian determinants only loosens, never tightens, the bound. The LU reference also carriespivot_kind(PIVOT_KIND_PERTURBED), so excluding perturbed pivots can only tightengrowth_tolintest_numeric_lu.jl; the LU cases that use it carry none.test_numeric_ldlt.jl: everypanel_tol→growth_tolswap keepspiv,pivot_kind, the perturbed D entries and±εexact, and addsldlt_error ≤ tol(T)as the growth-independent check (same trade-off as T19 for LU). The new "regime B on the blocked path" testset uses existing predicates (ldlt_blocked_path,takes_c_path,sncols); withregime_c_width = REGIME_B_MAX_WIDTHsuch fronts cannot take the C path, and the schedule is backend-independent so!isempty(blocked_b)cannot flip between CPU and CUDA. Testset loops overBACKENDSandELTYPES, seeded.test_api.jl: the CSC constructor andupdate!(::SparseMatrixCSC)paths (src/solver.jl,src/matrix.jl) raiseInvalidValueErrorfor every case asserted (size/nnz, index type viaA isa M, rectangular via the square check). Loops overELTYPES/INTTYPES, CPU only by design, seeded.test_refinement.jl:@test_brokenreplaced byr0 ≤ 100 epsandr1 ≤ r0; nothing weakened, no@test_brokenleft in the suite.Random.seed!(666)additions in 13 files.- CI:
buildgreen, CPU and CUDA test jobs pending (handled by the pipeline). I could not execute Julia in this review sandbox.
Blocking.
- Report vs diff: the description claims
Random.seed!(666)"now opens every testset that draws random numbers" in the listed files, but four such testsets are still unseeded:test_solve.jl"solve plan and workspace" (l. 18), "residuals, all regimes" (l. 77), "deterministic and atomic variants" (l. 124), andtest_numeric_cholesky_a.jl"plan: regime-A groups" (l. 33). All drawrandom_spd(andsolve_rhs). Add the seeds (one line each) or correct the sentence. Inline comments ontest/test_solve.jlandtest/test_numeric_cholesky_a.jl.
Non-blocking nits (inline): r1 ≤ r0 is empirical at rounding level, watch the CUDA margin; d_error docstring still points at panel_tol.
…ric_cholesky_a; d_error/factor_growth docstrings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wzx8WtMEXnAfyzyujdySFh
There was a problem hiding this comment.
VERDICT: APPROVE (head e8864b7)
Reviewed against STATE.md §3 "Tests and docs" (the task text this clean-up PR implements), AGENTS.md and PLAN.md.
Scope. Tests and test helpers only; no library code, no PLAN.md, no Manifest.toml. The only TASKS.md edit is inside the T16 Report (status line and the deviation sentence that named the removed @test_broken), which keeps that Report truthful. Public names, parameter strings and phase strings untouched.
Tests. Every item of the "Tests and docs" list that is a test item is covered:
test_numeric_ldlt.jlmoves every device-vs-reference D/panel comparison togrowth_tol(T, Nr)and addsldlt_error ≤ tol(T)next to each;piv,pivot_kind, perturbed D entries and±εstay exact (#86).factor_growth(test/utils.jl):|det| / ‖·‖_F ≤ σ_minis a valid lower bound for a 2×2 block, taking the smaller of the symmetric and Hermitian determinants is conservative, and excluding perturbed pivots only tightens the LU tolerance (LUNr.pivot_kindis written by_lu_pivot!, so the helper is well defined there). Previouslymin |d[1:n]|was 0 on δ = 0 KKT 2×2 blocks, sogrowth_tolwasInfthere; this PR makes those checks real.- New "regime B on the blocked path" testset: the predicate
regime == REGIME_B && ldlt_blocked_path && !takes_c_pathmatches the definitions insrc/symbolic/layout.jl/schedule.jl;kkt_matrixis Hermitian for complexT, so the defaultherm = trueofldlt_erroris correct; loops overBACKENDSandELTYPES(#89). Random.seed!(666)opens every random testset in the 13 files listed in the PR body (#76, #85).test_api.jl:DirectSolver(::SparseMatrixCSC, …; index)andupdate!(::SparseMatrixCSC)on the CPU backend, views L/U/F, bases O/Z, solve + refactorization totol(T), andInvalidValueErrorfor wrong size, different nnz, different index type (A isa Mcheck) and a rectangular matrix (the "must be square" check in the CSR constructor) (#63).test_refinement.jl: the@test_brokenis replaced byr0 ≤ 100 epsandr1 ≤ r0(#79). Not weakened: the 100× reduction is still asserted on the perturbed LDLᵀ case.- No
@test_brokenremains; tolerances come fromtol/panel_tol/growth_tolin test/utils.jl.
Report. Matches the diff: the described changes, the "no follow-up issues" statement and the SPLIT_FILES assessment are consistent with the code. CPU counts (69799 pass / 0 broken) are plausible; CUDA is pending CI on this PR, as stated.
CI. All four checks are still pending at review time; handled by the pipeline.
Blocking findings: none.
Non-blocking: one inline nit on r1 <= r0 in test_refinement.jl (the atomic forward sweep on CUDA makes the two residuals run-dependent; deterministic_mode = 1 would make the assertion reproducible).
Refs #86, #63, #76, #79, #85, #89
Task: Post-T21 clean-up PR 2: test-suite hardening (STATE.md §3, "Tests and docs"). Tests only, no library code.
What was built:
test/utils.jl:factor_growthnow handles 2×2 pivots and perturbed pivots. The old denominatormin |d[1:n]|is 0 on the diagonal of a 2×2 block (KKT with δ = 0), sogrowth_tolwasInfon every 2×2 case and checked nothing. A 2×2 block now contributes|det| / ‖block‖_F, a lower bound on its smallest singular value; the smaller determinant of the symmetric and Hermitian forms is used. Perturbed (±ε) pivots are left out of the minimum because they are compared bitwise. LU is unchanged: its cases have no 2×2 blocks and no perturbed pivots (checked withpivot_stats).test/test_numeric_ldlt.jl(Device LDLᵀ differs from ref_ldlt! on the K2 dumps (pivot sequence, nperturbed; inertia equal) #86):"diag") usesgrowth_tol(T, Nr).ldlt_error(A, S, Nh) ≤ tol(T)is added next to them.piv,pivot_kind, the perturbed D entries and the±εvalue are still compared exactly.regime_c_width = REGIME_B_MAX_WIDTH,regime_c_rows = 1024.regime == REGIME_B && ldlt_blocked_path && !takes_c_pathand that such a front contains 2×2 pivots.piv,pivot_kind, D, panels, stats and totals with the reference, plusldlt_errorand a solve.Random.seed!(666)now opens every testset that draws random numbers intest_numeric_{cholesky_a,b,c,ldlt,lu},test_solve,test_refinement,test_ubatch,test_schur,test_matching,test_reference_{cholesky,ldlt}andtest_fgmres. "Draws" includes the generators inmatrices.jl.test/test_api.jl(T13: public API v0.1 (DirectSolver, execute! phases, generic Cholesky, ported tests) #63): new testset forDirectSolver(::SparseMatrixCSC, structure, view; index)andupdate!(solver, ::SparseMatrixCSC)on the CPU backend.tol(T).InvalidValueErrorfor a wrong size, a different nnz, a different index type and a rectangular matrix.test/test_refinement.jl(T16: iterative refinement, solve_mode, interrupt, logging #79):@test_broken r1 <= r0 / 100is replaced byr0 ≤ 100 epsandr1 ≤ r0. Measured on CPU over 16 seeds: r0 ≈ 1.3 eps, and one step gains 3× to 5.4×. The two T16 Report sentences that named the@test_brokenare updated; that is the only TASKS.md edit.Tests:
SDS_TEST_GPU=0 julia --project=. -e 'using Pkg; Pkg.test()'(Julia 1.13.1, KA CPU backend, 4 threads): 69799 pass / 0 fail / 0 broken, 23m48s. Before this PR there was 1 broken, the T16@test_broken. No "Method definition … overwritten" warnings in the log (item 7: nothing to remove). After the review fix (e8864b7),test_solve+test_numeric_cholesky_are-run on CPU: 1446 / 1446 pass.test-gpu,SDS_TEST_CPU=0 SDS_TEST_SKIP=test_aqua, head e8864b7): 76084 pass / 0 fail / 0 broken, 18m20s.Measured on CPU with the new growth (Float32/Float64/ComplexF32): device-vs-reference D and panel errors are ≤ 0.05·
panel_toleverywhere, andldlt_erroris ≤ 0.02·tol(T). Growth is ≈ 1 onrandom_symindef, so nothing is loosened there. On high-growth KKTs the growth is large (Float32 kkt 1e-8: 2.5e8), so the elementwise check becomes weak there andldlt_erroris the effective check. That is the same trade-off astest_numeric_lu.Item 6 (SPLIT_FILES): no change.
test_numeric_luis already inSPLIT_FILES(#93), and every testset loops overELTYPES. Per-part wall times in this run, which ran in parallel with other files: Float64 10.8 s, ComplexF64 20.1 s, Float32 233.6 s, ComplexF32 336.2 s.Deviations from PLAN.md / TASKS.md: none. The
factor_growthchange is test-only and is documented in its docstring.Follow-up issues opened: none.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Wzx8WtMEXnAfyzyujdySFh
Generated by Claude Code