Skip to content

T36: solve_alg = "algo1", the partitioned-inverse fused solve on DirectSolver - #187

Open
michel2323 wants to merge 1 commit into
mainfrom
task/T36-solve-alg-algo1
Open

michel2323 wants to merge 1 commit into
mainfrom
task/T36-solve-alg-algo1

Conversation

@michel2323

Copy link
Copy Markdown
Member

Closes #186

Task: T36 — solve_alg = "algo1": the partitioned-inverse fused solve on DirectSolver (refs #82 solve half)

What was built: solve_alg = "algo1" on DirectSolver after the stock factorization. src/fused.jl: a FusedSolve object (L₁₁ inverses, their invert! launch, the dependency-counter solve segments, byte count, graph switch) built by fused_solve_plan at the analysis; fused_sweeps!/fused_solve! are the single solve driver, used by FusedSolver too (its old fields stay readable through getproperty, test_fused.jl unchanged). src/solver.jl: the fsolve field, the inverses computed at the end of every factorization/refactorization under "algo1" (no host sync, no allocation), "solve" and the sub-phases through the fused sweeps, refinement (IR and FGMRES, src/solve/refinement.jl) and matching composed with it, memory_estimates slot 14, the scope checks (NotSupportedError at the analysis for CPU()/Metal, complex T, "G", pivoted "S"/"H", batches, Schur; at the solve for nrhs > 1, solve_mode ≠ 0, "algo1" set after the analysis, stale inverses). New capability forward_progress(backend) (src/dense/capabilities.jl; true in the CUDA, AMDGPU and oneAPI extensions). Docs: man/parameters.md, man/performance.md ("Partitioned-inverse solve"), README.

Tests: SDS_TEST_ONLY=test_solve_algo1,test_fused,test_api,test_refinement,test_fgmres,test_options,test_solve,test_madnlp,test_aqua PTR_NUM_JOBS=4 julia --project=. -e 'using Pkg; Pkg.test()', KA CPU backend + CUDA (H200), all four element types: 7407 pass, 0 fail, 0 broken (new test/test_solve_algo1.jl: 314). Full suite, AMDGPU, oneAPI: pending CI. One CPU test_api[ComplexF32] allocation failure on the level-sweep path in an earlier 6-worker run did not reproduce (noted in the Report).

Measurements (H200 sm_90, one exclusive Slurm GPU, DirectSolver default options, medians of 21, cuDSS 0.8 in its own process): solve of the 78k condensed KKT 10.14 ms (level sweeps) → 2.12 ms ("algo1") vs cuDSS 1.40; case1354 0.886 → 0.269 vs 0.193; bcsstk38 1.540 → 0.327 vs 0.353; lap3d_40 11.72 → 6.99 vs 1.251. The inverse launch adds 0.2–1.3 % to a refactorization. memory_estimates slot 1 on the 78k: 235.7 → 289.5 MiB (slot 14: 53.8), lap3d_40 182.7 → 191.8. Full table in the Report.

Deviations from PLAN.md / TASKS.md: "default" stays the level sweeps although "algo1" is faster on every harness matrix, because the explicit inverses are less accurate (relres 2× on the 78k, 35× on bcsstk38; refinement removes the gap), the default "S" uses Bunch–Kaufman pivoting (out of scope), only sm_90 was measured, and it costs ~23 % more device memory on the 78k. DirectSolver runs one counter segment per run of plan entries ≤ 256 wide (with a single segment lap3d_40 took 21.5 ms); FusedSolver keeps its single segment. "algo1" set after the analysis raises instead of building the plan late. FusedSolver now also refuses Metal (no forward-progress guarantee). Matching is supported. Graph replay of the "algo1" solve on DirectSolver is off (T38). Proposed PLAN §1.3/§2.5/§2.7 wording in the Report.

Follow-up issues opened: none (Bunch–Kaufman under "algo1", sm_89/AMD timing of the multi-segment solve and the accuracy of the explicit inverses are listed under "Open issues" in the Report).

🤖 Generated with Claude Code

https://claude.ai/code/session_01MsLzHn17wzxqT2iMbuJyUh

…ctSolver

- Fused.FusedSolve / fused_solve_plan / fused_solve!: one solve driver for
  DirectSolver (solve_alg = "algo1", after the stock factorization) and
  FusedSolver; algo2_sweeps! split into forward / diagonal / backward parts
- DirectSolver: plan built at the analysis (slot 14 of memory_estimates),
  inverses at every factorization, solve and sub-phases run the fused
  solve, refinement (plain and FGMRES) and matching compose with it; one
  dependency-counter segment per run of narrow plan entries
- scope checks raise NotSupportedError (CPU/Metal via forward_progress,
  complex, "G", pivoting, batch, Schur, nrhs > 1, solve_mode)
- forward_progress capability (CUDA, AMDGPU, oneAPI extensions)
- test/test_solve_algo1.jl; docs and the T36 Report

Refs #82

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MsLzHn17wzxqT2iMbuJyUh
@michel2323 michel2323 added this to the M11 — Performance milestone Nov 5, 2026
@michel2323 michel2323 added the claude:pr PR opened by the Claude implementer; handled by the Claude pipeline label Nov 5, 2026
Comment thread test/test_solve_algo1.jl
("random_spd 300", () -> random_spd(T, 300, 0.02)),
("schur_example_cholesky 5×5", () -> schur_example_cholesky(T).A),
)
algo1_indefinite_generators(::Type{T}) where {T} = (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit (non-blocking): none of the "S" generators here is likely to have a front wider than 256, so the one combination this PR newly enables and that FusedSolver never exercised — the vendor dense sweeps (_fwd_dense!/_bwd_dense! called with _SV_CHOLESKY from algo2_forward!/algo2_backward!) on a factor written by the stock regime-C LDLᵀ kernels — has no test. It works because the stock LDLᵀ stores a unit 1 on the diagonal and pivot_type = 'N' applies no local pivot order, but that is exactly the kind of layout coupling a test should pin down. Suggested fix: add one no-pivot "S" matrix whose root front exceeds 256 (a larger grid_kkt_matrix, or a kkt_matrix with a denser Hessian; assert the width with maximum(s.symbolic.schedule.width) > 256 as the 34³ case asserts its segment count), and keep the comparison against the level sweeps on the same factorization.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: APPROVE (head a4bb8bb)

Checked (T36 task text on main, AGENTS.md, PLAN §1.3/§2.5/§2.7/§3.9, git diff origin/main...HEAD):

  • Scope: only the four T36 deliverables. PLAN.md untouched, no Manifest.toml, TASKS.md changed only in the T36 section (marker [!], Report). test_fused.jl unchanged (the old FusedSolver fields are forwarded by getproperty). Parameter and phase strings as in PLAN §1.3/§3.1; "default" keeps the level sweeps with a stated justification. README/docs edits are the feature's own documentation.
  • Tests: test/test_solve_algo1.jl covers every listed item: all SPD and no-pivot "S" generators on the forward-progress backends of BACKENDS, Float32/Float64 via eltypes_among, residual and agreement with the level sweeps within tol(T), bitwise-equal solves and sub-phases under deterministic_mode = 1, ir_n_steps = 2, FGMRES, matching, refactorization with new values, memory_estimates slot 14, no device allocation plus the fused_cycle_alloc_budget host budget, every unsupported combination (CPU backend included) raising NotSupportedError. Random.seed!(666), shared helpers only, no @test_broken. test_options already accepted "algo1".
  • Conventions: generic T/INT; new buffers sized on the host and uploaded once at the analysis; invert! is one asynchronous launch and the solve path adds no host synchronization (the counter reset is a device-to-device copyto!); dense calls go through _fwd_dense!/_bwd_dense!; backend facts (forward_progress) live in ext/; errors from src/errors.jl.
  • Correctness: the no-pivot LDLᵀ path (_lt_select! with r == 0) produces only 1×1 pivots, an identity local order and a unit stored diagonal, so algo2_diagonal! (Y ./= d[1:n]) and the Cholesky-kind dense calls are exact for the enabled scope; the per-run segments (merged = false) count children outside the range as arrived/done, consistent with stream order; the graph capture on FusedSolver records without executing, so the eager sweeps of the capturing solve are not doubled; getproperty forwarding and _clear_plans! are consistent; the workspace regrown for a refused nrhs > 1 rebuilds the same SolvePlan from the symbolic, so the segment indices stay valid.
  • Report: matches the diff; counts plausible (314 new); the first-run failure and the re-run are recorded honestly. CI is pending at review time (CUDA, AMDGPU, Aurora): the owner's call.

Blocking findings: none.

Non-blocking:

  1. Inline nit on test/test_solve_algo1.jl: no "S" test has a front wider than 256, so the vendor dense sweeps on a stock-LDLᵀ factor are untested in this PR.
  2. The task asked for the full SPD and KKT harness; apache2 was not measured (stated in the Report).
  3. The follow-ups under "Open issues" (Bunch–Kaufman under "algo1", sm_89/AMD timing, accuracy of the explicit inverses, the _probe_atomic log noise found in earlier code) have no found-by-agent issues; AGENTS.md asks for them.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude:pr PR opened by the Claude implementer; handled by the Claude pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T36 — solve_alg = "algo1": the partitioned-inverse fused solve on DirectSolver (refs #82 solve half)

1 participant