Batched (matrix) right-hand sides: LinearProblem(A, B) ≡ A \ B (v4.0.0)#1072
Batched (matrix) right-hand sides: LinearProblem(A, B) ≡ A \ B (v4.0.0)#1072ChrisRackauckas-Claude wants to merge 7 commits into
Conversation
`solve(LinearProblem(A, B))` with `B::AbstractMatrix` now computes the equivalent of `A \ B`: `u0` is initialized as a `size(A, 2) × size(B, 2)` matrix and the factorization-based algorithms solve all columns against a single factorization of `A`. This is a breaking change (v4.0.0): previously a matrix `b` initialized a flattened vector `u` and errored downstream. - `__init_u0_from_Ab` gains matrix-`b` methods (including SMatrix disambiguation). - Iterative/Krylov algorithms (`KrylovJL`, `SimpleGMRES`, and the other `AbstractKrylovSubspaceMethod`s, plus the default algorithm when it selects a Krylov method for an operator) throw an informative `ArgumentError` at `init` time for matrix `b`; `SimpleLUFactorization` (vector-only workspace) does the same. - The default polyalgorithm's unused Krylov cacheval slots initialize to `nothing` for matrix `b` so structured-matrix defaults (Diagonal, SymTridiagonal, ...) work. - `_check_residual_safety` sizes its residual buffer shape-generically. - Default-algorithm size heuristics use `size(b, 1)` instead of `length(b)` so batched right-hand sides don't inflate the apparent problem size (same for the sparse `use_klulike_sparse_structure` heuristic). - MKL/OpenBLAS/AppleAccelerate LU copy solution columns correctly for matrix `b` in the rectangular (`m > n`) path. - SparseArrays extension: CHOLMOD pre-1.12 `_ldiv!` fallback widened to matrices, SPQR gains a matrix `_ldiv!` via `\` (no in-place matrix ldiv! on any Julia version), and SparseColumnPivotedQR solves matrix `b` column-by-column against the one factorization. Part of SciML#552 Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYp371jx6LurezUDhKcYRh
Covers dense LU/GenericLU/QR (NoPivot + ColumnNorm)/SVD/Cholesky/ BunchKaufman/NormalCholesky vs A \ B for Float64 and ComplexF64, the default algorithm (dense, sparse, structured, non-square with k != n), sparse UMFPACK/KLU/CHOLMOD and the non-square sparse QR default, cache reuse via `cache.b = B2` and `cache.A = A2`, failure retcodes on singular A (including the default's QR fallback), residual-safety with matrix B, informative errors for Krylov methods and SimpleLUFactorization, static arrays, BigFloat, and single-column-matrix vs vector consistency. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYp371jx6LurezUDhKcYRh
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYp371jx6LurezUDhKcYRh
All sublibrary, test, qa, GPU, Trim, and docs environments pinned
LinearSolve = "3[.x.y]", so every CI job died at resolution against the
4.0.0 bump ("empty intersection between LinearSolve@4.0.0 and project
compatibility"). Widen each to include 4. The docs environment also gains
[sources] entries for LinearSolve and LinearSolveAutotune (the registered
Autotune restricts LinearSolve to 3, so the docs build must use the local
copies, matching the pattern the test environments already use).
Verified locally: docs, lib/LinearSolveAutotune, and test/qa environments
all instantiate against 4.0.0.
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
CI fallout was the predictable major-bump resolution failure: 18 in-repo environments (sublibraries, test envs, qa, GPU, Trim, docs) pinned The Runic check failure is pre-existing on |
The repo-wide Runic Format Check fails on main because this file landed unformatted in aabf007 (SciML#1069); format it here alongside the dispatch fix for the same commit's fallout. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every registered AlgebraicMultigrid >= 0.6 caps LinearSolve at 3, so the root test target (which all CI test groups resolve) was unsatisfiable against the 4.0.0 bump. AMG 0.5.x predates its LinearSolve dependency and exposes the identical API surface the extension uses (SmoothedAggregationAMG, RugeStubenAMG, aspreconditioner, ruge_stuben); the AlgebraicMultigridJL testset passes on 0.5.1 (verified locally, including the tight-tolerance case). The resolver will pick AMG 2.x again automatically once AlgebraicMultigrid widens its own compat; the compat comment marks the entry for removal then. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Two more CI fixes pushed (0c5e242, d64bad8):
Registry scan says these are the only three packages in any in-repo environment that cap LinearSolve: AlgebraicMultigrid (handled above), and the two in-repo sublibraries LinearSolveAutotune/LinearSolvePyAMG (handled via widened compat + |
|
Upstream compat PR opened at Chris's direction: JuliaLinearAlgebra/AlgebraicMultigrid.jl#135 (verified against this branch — AMG algorithms and the preconditioner path give bit-identical results under v3.87.0 and v4.0.0). Once that merges and tags, the temporary |
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
CI status: 61 pass, 1 fail — and the one failure ("Downgrade / Downgrade Tests - Core") is pre-existing infrastructure, not this branch:
So the v4 branch is green on everything that is green on |
Closes #552.
Summary
solve(LinearProblem(A, B::AbstractMatrix))and theinit/solve!cache path now behave likeA \ B, returning ann×ksolution, for all factorization-based algorithms. Iterative/Krylov algorithms throw an informativeArgumentErroratinittime for matrixbinstead of the previous internalMethodError(or silently wrong shapes).Root cause of the old behavior:
__init_u0_from_Ab(A, b) = similar(b, size(A, 2))flattened a matrixbinto a length-nvectoru, so factorizationsolve!hit_ldiv!(::Vector, ::LU, ::Matrix)with no method.What changed
src/common.jl: matrix-bmethod for__init_u0_from_Ab(zero-filled(size(A,2), size(b,2)), + SMatrix disambiguations); new_check_batched_rhs_supportguard at__initforAbstractKrylovSubspaceMethodand Krylov-choosingDefaultLinearSolver.src/factorization.jl:_check_residual_safetybuffer made shape-generic.src/default.jl: size heuristics usesize(b, 1)instead oflength(b)(3 sites) so batchedbdoesn't inflate the apparent problem size.src/iterative_wrappers.jl:init_cacheval(::KrylovJL, A, b::AbstractMatrix, ...) = nothingso the default polyalgorithm's unused Krylov slots initialize for structured defaults.src/mkl.jl/src/openblas.jl/src/appleaccelerate.jl: rectangular (m>n) solution copies made matrix-aware.src/simplelu.jl: informative error (vector-only workspace).ext/LinearSolveSparseArraysExt.jl: CHOLMOD pre-1.12 fallback widened toAbstractVecOrMat; SPQR matrix solve via\; SparseColumnPivotedQR column-by-column;size(b,1)heuristic.test/Core/batch.jl(new, 85 tests, wired into Core), docs tutorial section + release note.Project.toml: 3.87.0 → 4.0.0.Batch support matrix (all verified by running)
Works (≡
A\B): LU, GenericLU (incl. BigFloat), QR (NoPivot/ColumnNorm), SVD, Cholesky (Symmetric/Hermitian), BunchKaufman, NormalCholesky, LDLt, Diagonal, DirectLdiv!, MKL/OpenBLAS/AppleAccelerate LU, UMFPACK, KLU, PureKLU, CHOLMOD, SparseColumnPivotedQR, the DEFAULT algorithm (dense/sparse/structured/non-square), static A+B, cache reuse (cache.A=,cache.b=), residual safety, singular-A Failure retcode.Informative
ArgumentError: all KrylovJL_*, SimpleGMRES, IterativeSolversJL/KrylovKitJL, SimpleLUFactorization, default→Krylov for operatorA.Left as-is:
SMatrixA + heapMatrixB (StaticArrays.LU has no matrixldiv!; SMatrix+SMatrix works);adjoint.jlmatrix-badjoints out of scope.Why breaking (v4.0.0)
The
u0shape for matrixbchanges from a flattened length-nvector ton×k, and matrix-b+ iterative now errors atinit. Downstream audit (OrdinaryDiffEq.jl, NonlinearSolve.jl): functionally unaffected — every call site passes_vec(...)/safe_vec-flattened vectors with explicitu0(~60 sites audited in OrdinaryDiffEq libs, 1 production site in NonlinearSolveBase). They need only compat bumps for the major version (OrdinaryDiffEq: 10 lib Project.tomls at"3.75.0", RosenbrockTableaus"3.46", 2 test envs; NonlinearSolve: top-level + Base"3.48", FirstOrder/QuasiNewton"2.36.1, 3", gpu-test/docs envs).Tests
GROUP=Core Pkg.test()on Julia 1.12.4 and 1.10.11: 1506 pass, 0 new failures (identical counts on both), including the 85 new batch tests. Pre-existing on unmodifiedmain: 1@test_brokenin return codes, 2 in resize, and 1 error inforwarddiff_overloads.jl("PureKLU direct dual path") — the latter bisected to aabf007 (#1069) and fixed separately in #1073.Note: tests were run with the version field temporarily at 3.87.0 solely so temp test environments resolve; docs example manually verified equivalent to a passing test.
First adopter once released:
phi!/PhiPadeCachein SciML/ExponentialUtilities.jl#236 (currently usinglu!/ldiv!directly because the v3 cache path only solves vectorb).🤖 Generated with Claude Code