From 4875fd45f0ffea47132390bf8875ab8d9b972c7d Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 11 Jul 2026 07:14:59 -0400 Subject: [PATCH 1/6] Stop populating LinearSolution.cache in solve! returns Every build_linear_solution call in src/, ext/, and lib/ now passes nothing for the cache slot, and the direct LinearSolution construction in the HYPRE extension does the same. The returned solution is a lightweight value type: anyone needing the cache after solve!(cache) already holds it. The cleanup_petsc_cache!/cleanup_mumps_cache!/cleanup_superludist_cache! methods taking a LinearSolution are removed since they read sol.cache; use the LinearCache methods from an init/solve! cycle instead. Extension docstrings are migrated accordingly. With the cache no longer captured, the concretely-typed nothing-cache wrapper is fully elided by escape analysis on Julia >= 1.11: the warm aliased refactorize+solve loop is 0 bytes/call, and escaping returns shrink from 48 to 32 bytes/call. Co-Authored-By: Claude Fable 5 Co-Authored-By: Chris Rackauckas --- ext/LinearSolveAMDGPUExt.jl | 4 +-- ext/LinearSolveAlgebraicMultigridExt.jl | 2 +- ext/LinearSolveBLISExt.jl | 4 +-- ext/LinearSolveCUDAExt.jl | 8 ++--- ext/LinearSolveCUSOLVERRFExt.jl | 2 +- ext/LinearSolveCliqueTreesExt.jl | 2 +- ext/LinearSolveElementalExt.jl | 2 +- ext/LinearSolveFastLapackInterfaceExt.jl | 4 +-- ext/LinearSolveForwardDiffExt.jl | 4 +-- ext/LinearSolveGinkgoExt.jl | 2 +- ext/LinearSolveHSLExt.jl | 8 ++--- ext/LinearSolveHYPREExt.jl | 6 ++-- ext/LinearSolveIterativeSolversExt.jl | 2 +- ext/LinearSolveKrylovKitExt.jl | 2 +- ext/LinearSolveMUMPSExt.jl | 7 ++--- ext/LinearSolveMetalExt.jl | 4 +-- ext/LinearSolvePETScExt.jl | 4 +-- ext/LinearSolvePETScMPIExt.jl | 2 +- ext/LinearSolveParUExt.jl | 14 ++++----- ext/LinearSolvePardisoExt.jl | 2 +- ext/LinearSolvePartitionedSolversExt.jl | 2 +- ext/LinearSolvePureUMFPACKExt.jl | 4 +-- ext/LinearSolveRecursiveFactorizationExt.jl | 10 +++---- ext/LinearSolveSTRUMPACKExt.jl | 6 ++-- ext/LinearSolveSparseArraysExt.jl | 18 +++++------ ext/LinearSolveSparspakExt.jl | 2 +- ...inearSolveSpecializingFactorizationsExt.jl | 8 ++--- ext/LinearSolveSuperLUDISTExt.jl | 5 ++-- lib/LinearSolvePyAMG/src/LinearSolvePyAMG.jl | 2 +- src/appleaccelerate.jl | 8 ++--- src/common.jl | 4 +-- src/default.jl | 24 +++++++-------- src/extension_algs.jl | 28 ++++++++--------- src/factorization.jl | 30 +++++++++---------- src/iterative_wrappers.jl | 2 +- src/mkl.jl | 8 ++--- src/openblas.jl | 8 ++--- src/simplegmres.jl | 8 ++--- src/simplelu.jl | 2 +- src/solve_function.jl | 10 +++---- 40 files changed, 134 insertions(+), 140 deletions(-) diff --git a/ext/LinearSolveAMDGPUExt.jl b/ext/LinearSolveAMDGPUExt.jl index 7826f33c9..a9d067d55 100644 --- a/ext/LinearSolveAMDGPUExt.jl +++ b/ext/LinearSolveAMDGPUExt.jl @@ -23,7 +23,7 @@ function SciMLBase.solve!( y = Array(b_gpu) cache.u .= y - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end function LinearSolve.init_cacheval( @@ -59,7 +59,7 @@ function SciMLBase.solve!( y = Array(b_gpu[1:n]) cache.u .= y - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end function LinearSolve.init_cacheval( diff --git a/ext/LinearSolveAlgebraicMultigridExt.jl b/ext/LinearSolveAlgebraicMultigridExt.jl index 05d607456..86dce2054 100644 --- a/ext/LinearSolveAlgebraicMultigridExt.jl +++ b/ext/LinearSolveAlgebraicMultigridExt.jl @@ -41,7 +41,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::AlgebraicMultigridJL; kwargs. copyto!(cache.u, x) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/ext/LinearSolveBLISExt.jl b/ext/LinearSolveBLISExt.jl index 96a1da6d1..a290cbad0 100644 --- a/ext/LinearSolveBLISExt.jl +++ b/ext/LinearSolveBLISExt.jl @@ -324,7 +324,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact[1]) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -342,7 +342,7 @@ function SciMLBase.solve!( getrs!('N', A.factors, A.ipiv, cache.u; info) end - return SciMLBase.build_linear_solution(alg, cache.u, nothing, cache; retcode = ReturnCode.Success) + return SciMLBase.build_linear_solution(alg, cache.u, nothing, nothing; retcode = ReturnCode.Success) end end diff --git a/ext/LinearSolveCUDAExt.jl b/ext/LinearSolveCUDAExt.jl index a66359919..897cef980 100644 --- a/ext/LinearSolveCUDAExt.jl +++ b/ext/LinearSolveCUDAExt.jl @@ -73,7 +73,7 @@ function SciMLBase.solve!( fact = LinearSolve.@get_cacheval(cache, :CudaOffloadLUFactorization) y = Array(ldiv!(CUDACore.CuArray(cache.u), fact, CUDACore.CuArray(cache.b))) cache.u .= y - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end function LinearSolve.init_cacheval( @@ -105,7 +105,7 @@ function SciMLBase.solve!( end y = Array(ldiv!(CUDACore.CuArray(cache.u), cache.cacheval, CUDACore.CuArray(cache.b))) cache.u .= y - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end function LinearSolve.init_cacheval( @@ -133,7 +133,7 @@ function SciMLBase.solve!( end y = Array(ldiv!(CUDACore.CuArray(cache.u), cache.cacheval, CUDACore.CuArray(cache.b))) cache.u .= y - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end function LinearSolve.init_cacheval( @@ -206,7 +206,7 @@ function SciMLBase.solve!( # Convert back to original precision y = Array(u_gpu_f32) cache.u .= Torig.(y) - return SciMLBase.build_linear_solution(alg, cache.u, nothing, cache) + return SciMLBase.build_linear_solution(alg, cache.u, nothing, nothing) end function LinearSolve.init_cacheval( diff --git a/ext/LinearSolveCUSOLVERRFExt.jl b/ext/LinearSolveCUSOLVERRFExt.jl index 582312c2e..310efabcb 100644 --- a/ext/LinearSolveCUSOLVERRFExt.jl +++ b/ext/LinearSolveCUSOLVERRFExt.jl @@ -79,7 +79,7 @@ function SciMLBase.solve!(cache::LinearSolve.LinearCache, alg::LinearSolve.CUSOL copyto!(cache.u, u_gpu) end - return SciMLBase.build_linear_solution(alg, cache.u, nothing, cache; retcode = ReturnCode.Success) + return SciMLBase.build_linear_solution(alg, cache.u, nothing, nothing; retcode = ReturnCode.Success) end # Helper function for pattern checking diff --git a/ext/LinearSolveCliqueTreesExt.jl b/ext/LinearSolveCliqueTreesExt.jl index e77d24fc5..8937b6db3 100644 --- a/ext/LinearSolveCliqueTreesExt.jl +++ b/ext/LinearSolveCliqueTreesExt.jl @@ -41,7 +41,7 @@ function SciMLBase.solve!(cache::LinearSolve.LinearCache, alg::CliqueTreesFactor end ldiv!(u, cache.cacheval, b) - return SciMLBase.build_linear_solution(alg, u, nothing, cache) + return SciMLBase.build_linear_solution(alg, u, nothing, nothing) end LinearSolve.PrecompileTools.@compile_workload begin diff --git a/ext/LinearSolveElementalExt.jl b/ext/LinearSolveElementalExt.jl index 481ef606b..eef73b5e7 100644 --- a/ext/LinearSolveElementalExt.jl +++ b/ext/LinearSolveElementalExt.jl @@ -84,7 +84,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::ElementalJL; kwargs...) x_jl = convert(Base.Matrix{T}, fact \ _b_to_elemental(cache.b, T)) copyto!(cache.u, vec(x_jl)) - return SciMLBase.build_linear_solution(alg, cache.u, nothing, cache; retcode = ReturnCode.Success) + return SciMLBase.build_linear_solution(alg, cache.u, nothing, nothing; retcode = ReturnCode.Success) end end # module diff --git a/ext/LinearSolveFastLapackInterfaceExt.jl b/ext/LinearSolveFastLapackInterfaceExt.jl index c8b8680ff..3c7fb9a2f 100644 --- a/ext/LinearSolveFastLapackInterfaceExt.jl +++ b/ext/LinearSolveFastLapackInterfaceExt.jl @@ -39,7 +39,7 @@ function SciMLBase.solve!( cache.isfresh = false end y = ldiv!(cache.u, cache.cacheval.factors, cache.b) - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end function LinearSolve.init_cacheval( @@ -106,7 +106,7 @@ function SciMLBase.solve!( cache.isfresh = false end y = ldiv!(cache.u, cache.cacheval.factors, cache.b) - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end end diff --git a/ext/LinearSolveForwardDiffExt.jl b/ext/LinearSolveForwardDiffExt.jl index 03d73a871..e20f82a7a 100644 --- a/ext/LinearSolveForwardDiffExt.jl +++ b/ext/LinearSolveForwardDiffExt.jl @@ -471,7 +471,7 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - getfield(cache, :linear_cache).alg, getfield(cache, :dual_u), primal_sol.resid, cache; + getfield(cache, :linear_cache).alg, getfield(cache, :dual_u), primal_sol.resid, nothing; primal_sol.retcode, primal_sol.iters, primal_sol.stats ) end @@ -522,7 +522,7 @@ function _solve_direct_dual!( end return SciMLBase.build_linear_solution( - linear_cache.alg, getfield(cache, :dual_u), dual_sol.resid, cache; + linear_cache.alg, getfield(cache, :dual_u), dual_sol.resid, nothing; dual_sol.retcode, dual_sol.iters, dual_sol.stats ) end diff --git a/ext/LinearSolveGinkgoExt.jl b/ext/LinearSolveGinkgoExt.jl index e0df840c3..bac5ec0c8 100644 --- a/ext/LinearSolveGinkgoExt.jl +++ b/ext/LinearSolveGinkgoExt.jl @@ -142,7 +142,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::GinkgoJL; kwargs...) end resid = norm(cache.A * cache.u - cache.b) - return SciMLBase.build_linear_solution(alg, cache.u, resid, cache) + return SciMLBase.build_linear_solution(alg, cache.u, resid, nothing) end LinearSolve.update_tolerances_internal!(cache, alg::GinkgoJL, atol, rtol) = nothing diff --git a/ext/LinearSolveHSLExt.jl b/ext/LinearSolveHSLExt.jl index 7779acba4..b04c8d32c 100644 --- a/ext/LinearSolveHSLExt.jl +++ b/ext/LinearSolveHSLExt.jl @@ -120,7 +120,7 @@ function SciMLBase.solve!( HSL.ma57_solve!(hcache.ma57, cache.u, hcache.work) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) catch err if err isa HSL.Ma57Exception @@ -131,7 +131,7 @@ function SciMLBase.solve!( ) cache.isfresh = true return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end rethrow(err) @@ -169,7 +169,7 @@ function SciMLBase.solve!( HSL.ma97_solve!(hcache.ma97, cache.u) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) catch err if err isa HSL.Ma97Exception @@ -180,7 +180,7 @@ function SciMLBase.solve!( ) cache.isfresh = true return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end rethrow(err) diff --git a/ext/LinearSolveHYPREExt.jl b/ext/LinearSolveHYPREExt.jl index b8ec68ada..791c2b904 100644 --- a/ext/LinearSolveHYPREExt.jl +++ b/ext/LinearSolveHYPREExt.jl @@ -342,7 +342,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::HYPREAlgorithm, args...; kwar copy!(cache.u, hcache.u) end - # Note: Inlining SciMLBase.build_linear_solution(alg, u, resid, cache; retcode, iters) + # Note: Inlining SciMLBase.build_linear_solution(alg, u, resid, nothing; retcode, iters) # since some of the functions used in there does not play well with HYPREVector. T = cache.u isa HYPREVector ? HYPRE_Complex : eltype(cache.u) # eltype(u) @@ -354,10 +354,10 @@ function SciMLBase.solve!(cache::LinearCache, alg::HYPREAlgorithm, args...; kwar ret = SciMLBase.LinearSolution{ T, N, typeof(cache.u), typeof(resid), typeof(alg), - typeof(cache), typeof(stats), + Nothing, typeof(stats), }( cache.u, resid, alg, retc, - iters, cache, stats + iters, nothing, stats ) return ret diff --git a/ext/LinearSolveIterativeSolversExt.jl b/ext/LinearSolveIterativeSolversExt.jl index 6569f979b..05f4aa81a 100644 --- a/ext/LinearSolveIterativeSolversExt.jl +++ b/ext/LinearSolveIterativeSolversExt.jl @@ -173,7 +173,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::IterativeSolversJL; kwargs... resid = resid.current end - return SciMLBase.build_linear_solution(alg, cache.u, resid, cache; iters = i) + return SciMLBase.build_linear_solution(alg, cache.u, resid, nothing; iters = i) end purge_history!(iter, x, b) = nothing diff --git a/ext/LinearSolveKrylovKitExt.jl b/ext/LinearSolveKrylovKitExt.jl index 0ad0d3b23..949005f2a 100644 --- a/ext/LinearSolveKrylovKitExt.jl +++ b/ext/LinearSolveKrylovKitExt.jl @@ -54,7 +54,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::KrylovKitJL; kwargs...) iters = info.numiter return SciMLBase.build_linear_solution( - alg, cache.u, resid, cache; retcode = retcode, + alg, cache.u, resid, nothing; retcode = retcode, iters = iters ) end diff --git a/ext/LinearSolveMUMPSExt.jl b/ext/LinearSolveMUMPSExt.jl index 4e2f06b1a..1590a1da0 100644 --- a/ext/LinearSolveMUMPSExt.jl +++ b/ext/LinearSolveMUMPSExt.jl @@ -32,12 +32,9 @@ end cleanup_mumps_cache!(cache::MUMPSCache) = _finalize_mumps_cache!(cache) cleanup_mumps_cache!(cache::LinearSolve.LinearCache) = cleanup_mumps_cache!(cache.cacheval) -cleanup_mumps_cache!(sol::LinearSolution) = cleanup_mumps_cache!(sol.cache.cacheval) - """ cleanup_mumps_cache!(cache::MUMPSCache) cleanup_mumps_cache!(cache::LinearCache) - cleanup_mumps_cache!(sol::LinearSolution) Destroy the live `MUMPS.Mumps` object owned by a LinearSolve cache and reset the cache to an empty state. Safe to call multiple times. @@ -107,7 +104,7 @@ function _solve_failed_solution( ) @SciMLMessage(msg, cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -172,7 +169,7 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/ext/LinearSolveMetalExt.jl b/ext/LinearSolveMetalExt.jl index 2a989e582..fa5c02985 100644 --- a/ext/LinearSolveMetalExt.jl +++ b/ext/LinearSolveMetalExt.jl @@ -36,7 +36,7 @@ function SciMLBase.solve!( cache.isfresh = false end y = ldiv!(cache.u, @get_cacheval(cache, :MetalLUFactorization), cache.b) - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end # Mixed precision Metal LU implementation @@ -96,7 +96,7 @@ function SciMLBase.solve!( # Convert back to original precision cache.u .= Torig.(u_f32) - return SciMLBase.build_linear_solution(alg, cache.u, nothing, cache) + return SciMLBase.build_linear_solution(alg, cache.u, nothing, nothing) end end diff --git a/ext/LinearSolvePETScExt.jl b/ext/LinearSolvePETScExt.jl index fee6bc83d..89a5336c7 100644 --- a/ext/LinearSolvePETScExt.jl +++ b/ext/LinearSolvePETScExt.jl @@ -106,7 +106,6 @@ end """ cleanup_petsc_cache!(pcache::PETScCache) cleanup_petsc_cache!(cache::LinearCache) - cleanup_petsc_cache!(sol::LinearSolution) Destroy all PETSc objects owned by `pcache` and reset its state to empty. Safe to call multiple times — subsequent calls after the first are no-ops. @@ -144,7 +143,6 @@ function cleanup_petsc_cache!(pcache::PETScCache) end cleanup_petsc_cache!(cache::LinearCache) = cleanup_petsc_cache!(cache.cacheval) -cleanup_petsc_cache!(sol::LinearSolution) = cleanup_petsc_cache!(sol.cache.cacheval) # ── Cache initialisation ────────────────────────────────────────────────────── @@ -836,7 +834,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::PETScAlgorithm; kwargs...) failed !== nothing && return failed end - return build_linear_solution(alg, cache.u, resid, cache; retcode, iters) + return build_linear_solution(alg, cache.u, resid, nothing; retcode, iters) end end diff --git a/ext/LinearSolvePETScMPIExt.jl b/ext/LinearSolvePETScMPIExt.jl index 0925b10d6..d544d0cf0 100644 --- a/ext/LinearSolvePETScMPIExt.jl +++ b/ext/LinearSolvePETScMPIExt.jl @@ -386,7 +386,7 @@ function PETScExt.postsolve_solution_check(cache, alg, pcache, A::PSparseMatrix, if res_norm > tol return SciMLBase.build_linear_solution( - alg, u, nothing, cache; retcode = ReturnCode.APosterioriSafetyFailure + alg, u, nothing, nothing; retcode = ReturnCode.APosterioriSafetyFailure ) end return nothing diff --git a/ext/LinearSolveParUExt.jl b/ext/LinearSolveParUExt.jl index c4a179266..02ecd655f 100644 --- a/ext/LinearSolveParUExt.jl +++ b/ext/LinearSolveParUExt.jl @@ -225,7 +225,7 @@ function SciMLBase.solve!( paru_cache.last_factorize_info = info cache.isfresh = false return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end paru_cache.sym = sym_ref[] @@ -248,7 +248,7 @@ function SciMLBase.solve!( paru_cache.last_factorize_info = PARU_SINGULAR cache.isfresh = false return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) elseif info != PARU_SUCCESS @SciMLMessage( @@ -258,7 +258,7 @@ function SciMLBase.solve!( paru_cache.last_factorize_info = info cache.isfresh = false return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end paru_cache.num = num_ref[] @@ -274,7 +274,7 @@ function SciMLBase.solve!( cache.verbose, :solver_failure ) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) elseif paru_cache.last_factorize_info != PARU_SUCCESS @SciMLMessage( @@ -282,7 +282,7 @@ function SciMLBase.solve!( cache.verbose, :solver_failure ) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -297,13 +297,13 @@ function SciMLBase.solve!( if info != PARU_SUCCESS @SciMLMessage("ParU solve failed (code $info)", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.u .= x_vec return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/ext/LinearSolvePardisoExt.jl b/ext/LinearSolvePardisoExt.jl index 1c20ddec9..37248723c 100644 --- a/ext/LinearSolvePardisoExt.jl +++ b/ext/LinearSolvePardisoExt.jl @@ -158,7 +158,7 @@ function SciMLBase.solve!(cache::LinearSolve.LinearCache, alg::PardisoJL; kwargs cache.cacheval, u, SparseMatrixCSC(size(A)..., getcolptr(A), rowvals(A), nonzeros(A)), b ) - return SciMLBase.build_linear_solution(alg, cache.u, nothing, cache) + return SciMLBase.build_linear_solution(alg, cache.u, nothing, nothing) end # Add finalizer to release memory diff --git a/ext/LinearSolvePartitionedSolversExt.jl b/ext/LinearSolvePartitionedSolversExt.jl index 74b596ec0..2604b7802 100644 --- a/ext/LinearSolvePartitionedSolversExt.jl +++ b/ext/LinearSolvePartitionedSolversExt.jl @@ -156,7 +156,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::PartitionedSolversAlgorithm, retcode = isfinite(resid) ? ReturnCode.Success : ReturnCode.Failure end - return SciMLBase.build_linear_solution(alg, cache.u, resid, cache; retcode, iters) + return SciMLBase.build_linear_solution(alg, cache.u, resid, nothing; retcode, iters) end end # module LinearSolvePartitionedSolversExt diff --git a/ext/LinearSolvePureUMFPACKExt.jl b/ext/LinearSolvePureUMFPACKExt.jl index e28bd2c6f..2081d6301 100644 --- a/ext/LinearSolvePureUMFPACKExt.jl +++ b/ext/LinearSolvePureUMFPACKExt.jl @@ -79,12 +79,12 @@ function SciMLBase.solve!( y = PureUMFPACK.solve(F, cache.b) copyto!(cache.u, y) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) else @SciMLMessage("Solver failed", cache.verbose, :solver_failure) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) end end diff --git a/ext/LinearSolveRecursiveFactorizationExt.jl b/ext/LinearSolveRecursiveFactorizationExt.jl index bee4bb413..3de6dbf56 100644 --- a/ext/LinearSolveRecursiveFactorizationExt.jl +++ b/ext/LinearSolveRecursiveFactorizationExt.jl @@ -25,14 +25,14 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false end y = ldiv!(cache.u, LinearSolve.@get_cacheval(cache, :RFLUFactorization)[1], cache.b) - return SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing; retcode = ReturnCode.Success) end # Mixed precision RecursiveFactorization implementation @@ -86,7 +86,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -110,7 +110,7 @@ function SciMLBase.solve!( cache.u .= Torig.(u_32) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -147,7 +147,7 @@ function SciMLBase.solve!( mul!(b, V, tmp) out .= @view b[1:n] - return SciMLBase.build_linear_solution(alg, out, nothing, cache) + return SciMLBase.build_linear_solution(alg, out, nothing, nothing) end function LinearSolve.init_cacheval( diff --git a/ext/LinearSolveSTRUMPACKExt.jl b/ext/LinearSolveSTRUMPACKExt.jl index 369ca77a4..d909ef425 100644 --- a/ext/LinearSolveSTRUMPACKExt.jl +++ b/ext/LinearSolveSTRUMPACKExt.jl @@ -248,7 +248,7 @@ function SciMLBase.solve!( alg, cache.u, nothing, - cache; + nothing; retcode = _retcode_from_strumpack(info) ) end @@ -278,7 +278,7 @@ function SciMLBase.solve!( alg, cache.u, nothing, - cache; + nothing; retcode = _retcode_from_strumpack(info) ) end @@ -288,7 +288,7 @@ function SciMLBase.solve!( alg, cache.u, nothing, - cache; + nothing; retcode = ReturnCode.Success ) end diff --git a/ext/LinearSolveSparseArraysExt.jl b/ext/LinearSolveSparseArraysExt.jl index 379f9e52f..816866082 100644 --- a/ext/LinearSolveSparseArraysExt.jl +++ b/ext/LinearSolveSparseArraysExt.jl @@ -288,12 +288,12 @@ end if F.status == UMFPACK_OK y = ldiv!(cache.u, F, cache.b) SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.Success + alg, y, nothing, nothing; retcode = ReturnCode.Success ) else @SciMLMessage("Solver failed", cache.verbose, :solver_failure) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) end end @@ -485,7 +485,7 @@ function SciMLBase.solve!(cache::LinearSolve.LinearCache, alg::KLUFactorization; y = ldiv!(cache.u, F, cache.b) if all(isfinite, y) SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.Success + alg, y, nothing, nothing; retcode = ReturnCode.Success ) else # KLU can report `KLU_OK` on a numerically singular matrix (a @@ -498,13 +498,13 @@ function SciMLBase.solve!(cache::LinearSolve.LinearCache, alg::KLUFactorization; cache.verbose, :solver_failure ) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) end else @SciMLMessage("Solver failed", cache.verbose, :solver_failure) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) end end @@ -661,7 +661,7 @@ function SciMLBase.solve!( y = ldiv!(cache.u, F, cache.b) if all(isfinite, y) SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.Success + alg, y, nothing, nothing; retcode = ReturnCode.Success ) else # PureKLU (like SuiteSparse KLU) can report `KLU_OK` on a numerically @@ -674,13 +674,13 @@ function SciMLBase.solve!( cache.verbose, :solver_failure ) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) end else @SciMLMessage("Solver failed", cache.verbose, :solver_failure) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Infeasible + alg, cache.u, nothing, nothing; retcode = ReturnCode.Infeasible ) end end @@ -780,7 +780,7 @@ function SciMLBase.solve!( F = LinearSolve.@get_cacheval(cache, :SparseColumnPivotedQRFactorization) y = LinearSolve._ldiv!(cache.u, F, cache.b) return SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.Success + alg, y, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/ext/LinearSolveSparspakExt.jl b/ext/LinearSolveSparspakExt.jl index bd7681fdd..f2308e78a 100644 --- a/ext/LinearSolveSparspakExt.jl +++ b/ext/LinearSolveSparspakExt.jl @@ -86,7 +86,7 @@ function SciMLBase.solve!( cache.isfresh = false end y = ldiv!(cache.u, LinearSolve.@get_cacheval(cache, :SparspakFactorization), cache.b) - return SciMLBase.build_linear_solution(alg, y, nothing, cache) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing) end LinearSolve.PrecompileTools.@compile_workload begin diff --git a/ext/LinearSolveSpecializingFactorizationsExt.jl b/ext/LinearSolveSpecializingFactorizationsExt.jl index 24d4fdde6..b737dcf69 100644 --- a/ext/LinearSolveSpecializingFactorizationsExt.jl +++ b/ext/LinearSolveSpecializingFactorizationsExt.jl @@ -31,12 +31,12 @@ function SciMLBase.solve!( F = @get_cacheval(cache, :SpecializedLUFactorization) if !LinearAlgebra.issuccess(F) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end ldiv!(cache.u, F, cache.b) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -67,12 +67,12 @@ function SciMLBase.solve!( # issuccess only guards genuine numerical breakdown. if !LinearAlgebra.issuccess(F) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end ldiv!(cache.u, F, cache.b) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/ext/LinearSolveSuperLUDISTExt.jl b/ext/LinearSolveSuperLUDISTExt.jl index 811762cfc..eabb6ce58 100644 --- a/ext/LinearSolveSuperLUDISTExt.jl +++ b/ext/LinearSolveSuperLUDISTExt.jl @@ -23,7 +23,6 @@ end cleanup_superludist_cache!(cache::SuperLUDISTCache) = (cache.factor = nothing; cache) cleanup_superludist_cache!(cache::LinearSolve.LinearCache) = cleanup_superludist_cache!(cache.cacheval) -cleanup_superludist_cache!(sol::LinearSolution) = cleanup_superludist_cache!(sol.cache.cacheval) LinearSolve.needs_concrete_A(::LinearSolve.SuperLUDISTFactorization) = true @@ -132,7 +131,7 @@ function _solve_failed_solution( ) @SciMLMessage(msg, cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -189,7 +188,7 @@ function SciMLBase.solve!( _copy_solution!(cache.u, x) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/lib/LinearSolvePyAMG/src/LinearSolvePyAMG.jl b/lib/LinearSolvePyAMG/src/LinearSolvePyAMG.jl index c963323cb..43cd0974a 100644 --- a/lib/LinearSolvePyAMG/src/LinearSolvePyAMG.jl +++ b/lib/LinearSolvePyAMG/src/LinearSolvePyAMG.jl @@ -247,7 +247,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::PyAMG; kwargs...) resid = norm(cache.A * cache.u .- cache.b) return SciMLBase.build_linear_solution( - alg, cache.u, resid, cache; + alg, cache.u, resid, nothing; retcode = ReturnCode.Success ) end diff --git a/src/appleaccelerate.jl b/src/appleaccelerate.jl index 384b85b14..72bc773c4 100644 --- a/src/appleaccelerate.jl +++ b/src/appleaccelerate.jl @@ -368,7 +368,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact[1]) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -396,7 +396,7 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -449,7 +449,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact[1]) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -483,6 +483,6 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/src/common.jl b/src/common.jl index bb24eebe4..97bde2ff3 100644 --- a/src/common.jl +++ b/src/common.jl @@ -701,7 +701,7 @@ function SciMLBase.solve( ) u = prob.A \ prob.b return SciMLBase.build_linear_solution( - alg, u, nothing, prob; retcode = ReturnCode.Success + alg, u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -727,7 +727,7 @@ function SciMLBase.solve( return solve!(cache) end return SciMLBase.build_linear_solution( - alg, u, nothing, prob; retcode = ReturnCode.Success + alg, u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/src/default.jl b/src/default.jl index 69b27f7f3..06a695168 100644 --- a/src/default.jl +++ b/src/default.jl @@ -661,7 +661,7 @@ function _do_qr_fallback(cache::LinearCache, alg, sol, reason::Symbol) cache.verbose, :default_lu_fallback ) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = rc, iters = iters, stats = nothing + alg, cache.u, nothing, nothing; retcode = rc, iters = iters, stats = nothing ) end if cache.A === cache.cacheval.A_backup @@ -670,7 +670,7 @@ function _do_qr_fallback(cache::LinearCache, alg, sol, reason::Symbol) cache.verbose, :default_lu_fallback ) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = rc, iters = iters, stats = nothing + alg, cache.u, nothing, nothing; retcode = rc, iters = iters, stats = nothing ) end if reason === :residual_check @@ -690,7 +690,7 @@ function _do_qr_fallback(cache::LinearCache, alg, sol, reason::Symbol) qr_sol = SciMLBase.solve!(cache, QRFactorization(pivot)) cache.cacheval.fell_back_to_qr = true return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = qr_sol.retcode, iters = qr_sol.iters, stats = nothing ) end @@ -708,7 +708,7 @@ function _reuse_qr_fallback(cache::LinearCache, alg) qr_sol = SciMLBase.solve!(cache, QRFactorization(pivot)) # Use cache directly for type-stable inference (see _do_qr_fallback). return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = qr_sol.retcode, iters = qr_sol.iters, stats = nothing ) end @@ -763,7 +763,7 @@ function _do_sparse_qr_fallback(cache::LinearCache, alg, sol, reason::Symbol) cache.cacheval.fell_back_to_qr = true cache.isfresh = false return SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.Success, iters = 0, stats = nothing + alg, y, nothing, nothing; retcode = ReturnCode.Success, iters = 0, stats = nothing ) end @@ -785,7 +785,7 @@ function _reuse_sparse_qr_fallback(cache::LinearCache, alg) end y = _ldiv!(cache.u, qr_fact, cache.b) return SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.Success, iters = 0, stats = nothing + alg, y, nothing, nothing; retcode = ReturnCode.Success, iters = 0, stats = nothing ) end @@ -821,7 +821,7 @@ function _default_sparse_lu_solve_with_fallback( end end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = sol.retcode, iters = sol.iters, stats = nothing ) end @@ -857,7 +857,7 @@ function _default_lu_solve_with_fallback( end # Use cache directly for type-stable inference (see _do_qr_fallback). return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = sol.retcode, iters = sol.iters, stats = nothing ) end @@ -1014,7 +1014,7 @@ end if _Aop === cache.A sol = SciMLBase.solve!(cache, $(algchoice_to_alg(alg))) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = sol.retcode, iters = sol.iters, stats = nothing ) else @@ -1022,7 +1022,7 @@ end setfield!(cache, :A, _Aop) _rawsol = SciMLBase.solve!(cache, $(algchoice_to_alg(alg))) _result = SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = _rawsol.retcode, iters = _rawsol.iters, stats = nothing ) @@ -1041,7 +1041,7 @@ end if !(cache.A isa Array) sol = SciMLBase.solve!(cache, $(algchoice_to_alg(alg))) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = sol.retcode, iters = sol.iters, stats = nothing ) @@ -1057,7 +1057,7 @@ end newex = quote sol = SciMLBase.solve!(cache, $(algchoice_to_alg(alg))) SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = sol.retcode, iters = sol.iters, stats = nothing ) diff --git a/src/extension_algs.jl b/src/extension_algs.jl index 16f908943..74ce67233 100644 --- a/src/extension_algs.jl +++ b/src/extension_algs.jl @@ -69,12 +69,8 @@ resources promptly: ```julia PETScExt = Base.get_extension(LinearSolve, :LinearSolvePETScExt) -sol = solve(prob, PETScAlgorithm(:gmres)) -PETScExt.cleanup_petsc_cache!(sol) - -# Or via the cache directly: -cache = SciMLBase.init(prob, PETScAlgorithm(:cg)) -solve!(cache) +cache = SciMLBase.init(prob, PETScAlgorithm(:gmres)) +sol = solve!(cache) PETScExt.cleanup_petsc_cache!(cache) ``` @@ -95,12 +91,13 @@ n = 100 A = sprand(n, n, 0.1); A = A + A' + 20I b = rand(n) -sol = solve( +cache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:gmres; pc_type = :ilu, ksp_options = (ksp_monitor = "",)) ) +sol = solve!(cache) println("Residual: ", norm(A * sol.u - b) / norm(b)) -PETScExt.cleanup_petsc_cache!(sol) +PETScExt.cleanup_petsc_cache!(cache) ``` ## Distributed SparseMatrixCSC Example @@ -115,16 +112,17 @@ n = 12 A = spdiagm(-1 => -ones(n - 1), 0 => 4.0 .* ones(n), 1 => -ones(n - 1)) b = ones(n) -sol = solve( +cache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:gmres; comm = MPI.COMM_WORLD); abstol = 1.0e-10, reltol = 1.0e-10 ) +sol = solve!(cache) # sol.u is the full replicated solution on every rank. println(norm(A * sol.u - b) / norm(b)) -PETScExt.cleanup_petsc_cache!(sol) +PETScExt.cleanup_petsc_cache!(cache) ``` """ struct PETScAlgorithm <: SciMLLinearSolveAlgorithm @@ -1396,9 +1394,10 @@ MUMPSExt = Base.get_extension(LinearSolve, :LinearSolveMUMPSExt) A = sparse([4.0 1.0; 2.0 3.0]) b = [1.0, 2.0] -sol = solve(LinearProblem(A, b), MUMPSFactorization()) +cache = SciMLBase.init(LinearProblem(A, b), MUMPSFactorization()) +sol = solve!(cache) -MUMPSExt.cleanup_mumps_cache!(sol) +MUMPSExt.cleanup_mumps_cache!(cache) MPI.Finalize() ``` @@ -1414,8 +1413,9 @@ MPI.Init() MUMPSExt = Base.get_extension(LinearSolve, :LinearSolveMUMPSExt) A = sparse([4.0 1.0; 2.0 3.0]) b = [1.0, 2.0] -sol = solve(LinearProblem(A, b), MUMPSFactorization()) -MUMPSExt.cleanup_mumps_cache!(sol) +cache = SciMLBase.init(LinearProblem(A, b), MUMPSFactorization()) +sol = solve!(cache) +MUMPSExt.cleanup_mumps_cache!(cache) ``` """ struct MUMPSFactorization <: AbstractSparseFactorization diff --git a/src/factorization.jl b/src/factorization.jl index 5369c7234..4d31fd100 100644 --- a/src/factorization.jl +++ b/src/factorization.jl @@ -23,7 +23,7 @@ :solver_failure ) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -41,7 +41,7 @@ end return SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.Success + alg, y, nothing, nothing; retcode = ReturnCode.Success ) end end @@ -140,7 +140,7 @@ function _check_residual_safety(cache::LinearCache, alg, A_original, y) return "Residual safety check failed: ‖A*x - b‖ = $(res_norm), tol = $(tol) (abstol = $(cache.abstol), reltol = $(cache.reltol), ‖b‖ = $(b_norm), ratio = $(res_norm / tol))" end return SciMLBase.build_linear_solution( - alg, y, nothing, cache; retcode = ReturnCode.APosterioriSafetyFailure + alg, y, nothing, nothing; retcode = ReturnCode.APosterioriSafetyFailure ) end return nothing @@ -353,7 +353,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::LUFactorization; kwargs...) e isa LinearAlgebra.SingularException @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) else rethrow(e) @@ -365,7 +365,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::LUFactorization; kwargs...) !LinearAlgebra.issuccess(fact) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -380,7 +380,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::LUFactorization; kwargs...) failed !== nothing && return failed end - return SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing; retcode = ReturnCode.Success) end function do_factorization(alg::LUFactorization, A, b, u) @@ -446,7 +446,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -461,7 +461,7 @@ function SciMLBase.solve!( failed !== nothing && return failed end - return SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing; retcode = ReturnCode.Success) end function init_cacheval( @@ -588,7 +588,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::GESVFactorization; kwargs...) if !LinearAlgebra.issuccess(luf) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -597,7 +597,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::GESVFactorization; kwargs...) copyto!(cache.u, cache.b) ldiv!(luf, cache.u) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -1578,7 +1578,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::CHOLMODFactorization; kwargs. cache.u .= @get_cacheval(cache, :CHOLMODFactorization) \ cache.b return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -1671,7 +1671,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::NormalCholeskyFactorization; !LinearAlgebra.issuccess(fact) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end @@ -1686,7 +1686,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::NormalCholeskyFactorization; else y = ldiv!(cache.u, @get_cacheval(cache, :NormalCholeskyFactorization), A' * cache.b) end - return SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success) + return SciMLBase.build_linear_solution(alg, y, nothing, nothing; retcode = ReturnCode.Success) end ## NormalBunchKaufmanFactorization @@ -1734,7 +1734,7 @@ function SciMLBase.solve!( end y = ldiv!(cache.u, @get_cacheval(cache, :NormalBunchKaufmanFactorization), A' * cache.b) return SciMLBase.build_linear_solution( - alg, y, nothing, cache; + alg, y, nothing, nothing; retcode = ReturnCode.Success ) end @@ -1768,7 +1768,7 @@ function SciMLBase.solve!( cache.u .= A.diag .\ cache.b end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/src/iterative_wrappers.jl b/src/iterative_wrappers.jl index efda88eb9..72b38214a 100644 --- a/src/iterative_wrappers.jl +++ b/src/iterative_wrappers.jl @@ -441,7 +441,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::KrylovJL; kwargs...) end return SciMLBase.build_linear_solution( - alg, cache.u, Ref(resid), cache; + alg, cache.u, Ref(resid), nothing; iters = stats.niter, retcode, stats ) end diff --git a/src/mkl.jl b/src/mkl.jl index 95fe82845..872af65e7 100644 --- a/src/mkl.jl +++ b/src/mkl.jl @@ -373,7 +373,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact[1]) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -401,7 +401,7 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -452,7 +452,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact[1]) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -485,6 +485,6 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/src/openblas.jl b/src/openblas.jl index 8a158c191..f1a7db220 100644 --- a/src/openblas.jl +++ b/src/openblas.jl @@ -379,7 +379,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact[1]) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -407,7 +407,7 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -459,7 +459,7 @@ function SciMLBase.solve!( if !LinearAlgebra.issuccess(fact[1]) @SciMLMessage("Solver failed", cache.verbose, :solver_failure) return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Failure + alg, cache.u, nothing, nothing; retcode = ReturnCode.Failure ) end cache.isfresh = false @@ -492,6 +492,6 @@ function SciMLBase.solve!( end return SciMLBase.build_linear_solution( - alg, cache.u, nothing, cache; retcode = ReturnCode.Success + alg, cache.u, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/src/simplegmres.jl b/src/simplegmres.jl index 0d6a22973..a82a551fd 100644 --- a/src/simplegmres.jl +++ b/src/simplegmres.jl @@ -241,7 +241,7 @@ function SciMLBase.solve!(cache::SimpleGMRESCache{false}, lincache::LinearCache) if β == 0 return SciMLBase.build_linear_solution( - lincache.alg, x, r₀, lincache; + lincache.alg, x, r₀, nothing; retcode = ReturnCode.Success ) end @@ -406,7 +406,7 @@ function SciMLBase.solve!(cache::SimpleGMRESCache{false}, lincache::LinearCache) cache.warm_start = false return SciMLBase.build_linear_solution( - lincache.alg, x, rNorm, lincache; + lincache.alg, x, rNorm, nothing; retcode = status, iters = iter ) end @@ -488,7 +488,7 @@ function SciMLBase.solve!(cache::SimpleGMRESCache{true}, lincache::LinearCache) if β == 0 return SciMLBase.build_linear_solution( - lincache.alg, x, r₀, lincache; + lincache.alg, x, r₀, nothing; retcode = ReturnCode.Success ) end @@ -643,7 +643,7 @@ function SciMLBase.solve!(cache::SimpleGMRESCache{true}, lincache::LinearCache) warm_start && !restart && axpy!(one(T), Δx, x) return SciMLBase.build_linear_solution( - lincache.alg, x, rNorm, lincache; + lincache.alg, x, rNorm, nothing; retcode = status, iters = iter ) end diff --git a/src/simplelu.jl b/src/simplelu.jl index 4ebef9357..e8520fc54 100644 --- a/src/simplelu.jl +++ b/src/simplelu.jl @@ -215,7 +215,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::SimpleLUFactorization; kwargs cache.cacheval.x .= cache.u y = simplelu_solve!(cache.cacheval) return SciMLBase.build_linear_solution( - alg, y, nothing, cache; + alg, y, nothing, nothing; retcode = ReturnCode.Success ) end diff --git a/src/solve_function.jl b/src/solve_function.jl index ea4709def..0b9f0acdd 100644 --- a/src/solve_function.jl +++ b/src/solve_function.jl @@ -54,7 +54,7 @@ function SciMLBase.solve!( u = solve_func(A, b, u, p, isfresh, Pl, Pr, cacheval; kwargs...) return SciMLBase.build_linear_solution( - alg, u, nothing, cache; + alg, u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -106,7 +106,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::DirectLdiv!{false}, args...; (; A, b, u) = cache ldiv!(u, A, b) return SciMLBase.build_linear_solution( - alg, u, nothing, cache; + alg, u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -117,7 +117,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::DirectLdiv!{true}, args...; k (; A, b, u) = cache ldiv!(u, A, b) return SciMLBase.build_linear_solution( - alg, u, nothing, cache; + alg, u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -157,7 +157,7 @@ function SciMLBase.solve!( # Perform ldiv! on the copy, preserving the original A ldiv!(u, cacheval, b) return SciMLBase.build_linear_solution( - alg, u, nothing, cache; + alg, u, nothing, nothing; retcode = ReturnCode.Success ) end @@ -173,7 +173,7 @@ function SciMLBase.solve!( # Perform ldiv! on the copy, preserving the original A ldiv!(u, cacheval, b) return SciMLBase.build_linear_solution( - alg, u, nothing, cache; + alg, u, nothing, nothing; retcode = ReturnCode.Success ) end From 05ceea1069e756e7ea19929ec699e5ff73749dcb Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 11 Jul 2026 07:14:59 -0400 Subject: [PATCH 2/6] Bump to 5.0.0 with sublibrary and test env compat updates Breaking: reading .cache off the return of solve!/solve no longer works (always nothing). LinearSolveAutotune -> 1.13.0 and LinearSolvePyAMG -> 1.3.0 widen their LinearSolve compat to 5. Co-Authored-By: Claude Fable 5 Co-Authored-By: Chris Rackauckas --- Project.toml | 2 +- docs/Project.toml | 2 +- lib/LinearSolveAutotune/Project.toml | 4 ++-- lib/LinearSolveAutotune/test/qa/Project.toml | 2 +- lib/LinearSolvePyAMG/Project.toml | 4 ++-- lib/LinearSolvePyAMG/test/qa/Project.toml | 2 +- test/AD/Project.toml | 4 ++-- test/GPU/Project.toml | 2 +- test/LinearSolveElemental/Project.toml | 2 +- test/LinearSolveGinkgo/Project.toml | 2 +- test/LinearSolveHYPRE/Project.toml | 2 +- test/LinearSolveMUMPS/Project.toml | 2 +- test/LinearSolvePETSc/Project.toml | 2 +- test/LinearSolveParU/Project.toml | 2 +- test/LinearSolvePardiso/Project.toml | 2 +- test/LinearSolvePartitionedSolvers/Project.toml | 2 +- test/LinearSolveSuperLUDIST/Project.toml | 2 +- test/Trim/Project.toml | 2 +- test/qa/Project.toml | 4 ++-- 19 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Project.toml b/Project.toml index 29045d5a4..e0d9732c6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "LinearSolve" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "4.3.0" +version = "5.0.0" authors = ["SciML"] [deps] diff --git a/docs/Project.toml b/docs/Project.toml index 23f9b6c50..68982b136 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -7,7 +7,7 @@ SciMLOperators = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" [compat] Documenter = "1" -LinearSolve = "4" +LinearSolve = "5" LinearSolveAutotune = "1.1" LinearSolvePyAMG = "1" SciMLOperators = "1" diff --git a/lib/LinearSolveAutotune/Project.toml b/lib/LinearSolveAutotune/Project.toml index 6054ce65d..8e8e88bc5 100644 --- a/lib/LinearSolveAutotune/Project.toml +++ b/lib/LinearSolveAutotune/Project.toml @@ -1,7 +1,7 @@ name = "LinearSolveAutotune" uuid = "67398393-80e8-4254-b7e4-1b9a36a3c5b6" authors = ["SciML"] -version = "1.12.0" +version = "1.13.0" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -44,7 +44,7 @@ FastLapackInterface = "2.0.4" GitHub = "5" LAPACK_jll = "3.12" LinearAlgebra = "1" -LinearSolve = "4" +LinearSolve = "5" MKL_jll = "2025.2.0" Metal = "1.5" OpenBLAS_jll = "0.3" diff --git a/lib/LinearSolveAutotune/test/qa/Project.toml b/lib/LinearSolveAutotune/test/qa/Project.toml index 9083a6594..901ac4f06 100644 --- a/lib/LinearSolveAutotune/test/qa/Project.toml +++ b/lib/LinearSolveAutotune/test/qa/Project.toml @@ -13,7 +13,7 @@ LinearSolveAutotune = {path = "../.."} [compat] Aqua = "0.8" JET = "0.9, 0.10, 0.11" -LinearSolve = "4" +LinearSolve = "5" LinearSolveAutotune = "1" SciMLTesting = "1.6" Test = "1" diff --git a/lib/LinearSolvePyAMG/Project.toml b/lib/LinearSolvePyAMG/Project.toml index e8cb5a04b..84d045530 100644 --- a/lib/LinearSolvePyAMG/Project.toml +++ b/lib/LinearSolvePyAMG/Project.toml @@ -1,7 +1,7 @@ name = "LinearSolvePyAMG" uuid = "7a56c47d-7ab1-4e99-b0e3-2952e463d64a" authors = ["SciML"] -version = "1.2.0" +version = "1.3.0" [deps] CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab" @@ -17,7 +17,7 @@ LinearSolve = {path = "../.."} [compat] CondaPkg = "0.2" LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" Pkg = "1" PythonCall = "0.9" SafeTestsets = "0.1" diff --git a/lib/LinearSolvePyAMG/test/qa/Project.toml b/lib/LinearSolvePyAMG/test/qa/Project.toml index 0933d0eb5..c9db99199 100644 --- a/lib/LinearSolvePyAMG/test/qa/Project.toml +++ b/lib/LinearSolvePyAMG/test/qa/Project.toml @@ -13,7 +13,7 @@ LinearSolvePyAMG = {path = "../.."} [compat] Aqua = "0.8" JET = "0.9, 0.10, 0.11" -LinearSolve = "4" +LinearSolve = "5" LinearSolvePyAMG = "1" SciMLTesting = "1.6" Test = "1" diff --git a/test/AD/Project.toml b/test/AD/Project.toml index 80ccdf35a..dbab7ae4d 100644 --- a/test/AD/Project.toml +++ b/test/AD/Project.toml @@ -17,7 +17,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] -LinearSolve = {path = "../.."} +LinearSolve = {path = "/home/crackauc/sandbox/tmp_20260611_010705_2268/p16"} [compat] AllocCheck = "0.2" @@ -27,7 +27,7 @@ ForwardDiff = "0.10.38, 1" InteractiveUtils = "1.10" JET = "0.9, 0.11" LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" Mooncake = "0.5.15" RecursiveFactorization = "0.2.26" SafeTestsets = "0.1, 1" diff --git a/test/GPU/Project.toml b/test/GPU/Project.toml index 27f05c058..d12ab941e 100644 --- a/test/GPU/Project.toml +++ b/test/GPU/Project.toml @@ -19,7 +19,7 @@ LinearSolve = {path = "/home/crackauc/sandbox/tmp_20260531_035946_10202/dg/fr_Li BlockDiagonals = "0.2" CUDSS = "0.7" LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" SafeTestsets = "0.1, 1" SciMLTesting = "1" SparseArrays = "1.10" diff --git a/test/LinearSolveElemental/Project.toml b/test/LinearSolveElemental/Project.toml index 47d76b43f..652958bd0 100644 --- a/test/LinearSolveElemental/Project.toml +++ b/test/LinearSolveElemental/Project.toml @@ -14,7 +14,7 @@ LinearSolve = {path = "../.."} [compat] Elemental = "0.6.1" LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" Random = "1.10" SafeTestsets = "0.1, 1" SciMLBase = "2.148, 3" diff --git a/test/LinearSolveGinkgo/Project.toml b/test/LinearSolveGinkgo/Project.toml index 2c3afe028..1e2480658 100644 --- a/test/LinearSolveGinkgo/Project.toml +++ b/test/LinearSolveGinkgo/Project.toml @@ -13,7 +13,7 @@ LinearSolve = {path = "../.."} [compat] Ginkgo = "1" LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" SafeTestsets = "0.1, 1" SciMLTesting = "1" SparseArrays = "1.10" diff --git a/test/LinearSolveHYPRE/Project.toml b/test/LinearSolveHYPRE/Project.toml index c9a527f68..6fb369723 100644 --- a/test/LinearSolveHYPRE/Project.toml +++ b/test/LinearSolveHYPRE/Project.toml @@ -15,7 +15,7 @@ LinearSolve = {path = "../.."} [compat] HYPRE = "1.7" LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" MPI = "0.20" Random = "1.10" SafeTestsets = "0.1, 1" diff --git a/test/LinearSolveMUMPS/Project.toml b/test/LinearSolveMUMPS/Project.toml index 7f6d285b9..802fe798a 100644 --- a/test/LinearSolveMUMPS/Project.toml +++ b/test/LinearSolveMUMPS/Project.toml @@ -13,7 +13,7 @@ LinearSolve = {path = "../.."} [compat] LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" MPI = "0.20" MUMPS = "1.4" SafeTestsets = "0.1, 1" diff --git a/test/LinearSolvePETSc/Project.toml b/test/LinearSolvePETSc/Project.toml index 2f478ce65..937aa404f 100644 --- a/test/LinearSolvePETSc/Project.toml +++ b/test/LinearSolvePETSc/Project.toml @@ -17,7 +17,7 @@ LinearSolve = {path = "../.."} [compat] LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" MPI = "0.20" PETSc = "0.4.10" PartitionedArrays = "0.5" diff --git a/test/LinearSolveParU/Project.toml b/test/LinearSolveParU/Project.toml index 4891aab50..7e7cbd63b 100644 --- a/test/LinearSolveParU/Project.toml +++ b/test/LinearSolveParU/Project.toml @@ -14,7 +14,7 @@ LinearSolve = {path = "../.."} [compat] LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" ParU_jll = "1" Random = "1.10" SafeTestsets = "0.1, 1" diff --git a/test/LinearSolvePardiso/Project.toml b/test/LinearSolvePardiso/Project.toml index 267ca098d..bf3d56e66 100644 --- a/test/LinearSolvePardiso/Project.toml +++ b/test/LinearSolvePardiso/Project.toml @@ -13,7 +13,7 @@ LinearSolve = {path = "../.."} [compat] LinearAlgebra = "1.10" -LinearSolve = "4" +LinearSolve = "5" Pardiso = "1" Random = "1.10" SafeTestsets = "0.1, 1" diff --git a/test/LinearSolvePartitionedSolvers/Project.toml b/test/LinearSolvePartitionedSolvers/Project.toml index 5dd4533dc..900c44219 100644 --- a/test/LinearSolvePartitionedSolvers/Project.toml +++ b/test/LinearSolvePartitionedSolvers/Project.toml @@ -13,7 +13,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" LinearSolve = {path = "../.."} [compat] -LinearSolve = "4" +LinearSolve = "5" MPI = "0.20" PartitionedArrays = "0.5" PartitionedSolvers = "0.3" diff --git a/test/LinearSolveSuperLUDIST/Project.toml b/test/LinearSolveSuperLUDIST/Project.toml index bab0d4e45..a8a27632c 100644 --- a/test/LinearSolveSuperLUDIST/Project.toml +++ b/test/LinearSolveSuperLUDIST/Project.toml @@ -9,7 +9,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" LinearSolve = {path = "../.."} [compat] -LinearSolve = "4" +LinearSolve = "5" MPI = "0.20" SparseArrays = "1.10" SuperLUDIST = "1" diff --git a/test/Trim/Project.toml b/test/Trim/Project.toml index 9345e31f1..9dfc9ffb4 100644 --- a/test/Trim/Project.toml +++ b/test/Trim/Project.toml @@ -22,7 +22,7 @@ JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" test = ["JET"] [compat] -LinearSolve = "4" +LinearSolve = "5" RecursiveFactorization = "0.2" SafeTestsets = "0.1, 1" SciMLBase = "2" diff --git a/test/qa/Project.toml b/test/qa/Project.toml index b240834c5..f38367f30 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -7,12 +7,12 @@ SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] -LinearSolve = {path = "../.."} +LinearSolve = {path = "/home/crackauc/sandbox/tmp_20260611_010705_2268/p16"} [compat] Aqua = "0.8" JET = "0.9, 0.11" -LinearSolve = "4" +LinearSolve = "5" SafeTestsets = "0.1, 1" SciMLTesting = "1.6" Test = "<0.0.1, 1" From 43223045d127bca189470423e336daf8e36e85ab Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 11 Jul 2026 07:15:13 -0400 Subject: [PATCH 3/6] Add lightweight-solution regression tests; migrate tests off sol.cache New test/Core/lightweight_solution.jl asserts cache === nothing across solver paths (including failure and QR safety fallback returns), a 0-byte warm aliased refactorize+solve loop on Julia >= 1.11 (with the documented pre-existing getrf! ipiv floor on 1.10), and correctness across refactorizations. Migrates remaining sol.cache readers to held-cache init/solve! cycles: basictests Reuse precs, forwarddiff_overloads reinit!, and the PETSc/ MUMPS/SuperLUDIST/HYPRE extension suites (cleanup and reltol reads). The KLU JET optimization test now passes with the cache dropped from the solution, so its broken flag is removed. Co-Authored-By: Claude Fable 5 Co-Authored-By: Chris Rackauckas --- test/Core/basictests.jl | 5 +- test/Core/forwarddiff_overloads.jl | 4 +- test/Core/lightweight_solution.jl | 107 +++++++++++++++ test/LinearSolveHYPRE/hypretests.jl | 8 +- test/LinearSolveHYPRE/hypretests_mpi.jl | 5 +- test/LinearSolveMUMPS/mumps.jl | 10 +- test/LinearSolvePETSc/petsctests.jl | 150 ++++++++++++--------- test/LinearSolvePETSc/petsctests_mpi.jl | 106 +++++++++------ test/LinearSolveSuperLUDIST/superludist.jl | 5 +- test/qa/jet.jl | 4 +- test/runtests.jl | 1 + 11 files changed, 282 insertions(+), 123 deletions(-) create mode 100644 test/Core/lightweight_solution.jl diff --git a/test/Core/basictests.jl b/test/Core/basictests.jl index 335738ed7..a08b898fc 100644 --- a/test/Core/basictests.jl +++ b/test/Core/basictests.jl @@ -514,9 +514,8 @@ end A = spdiagm(-1 => -ones(n - 1), 0 => fill(10.0, n), 1 => -ones(n - 1)) b = rand(n) p = LinearProblem(A, b) - x0 = solve(p, KrylovJL_CG(precs = countingprecs, ldiv = false)) - cache = x0.cache - x0 = copy(x0) + cache = init(p, KrylovJL_CG(precs = countingprecs, ldiv = false)) + x0 = copy(solve!(cache)) for i in 4:(n - 3) A[i, i + 3] -= 1.0e-4 A[i - 3, i] -= 1.0e-4 diff --git a/test/Core/forwarddiff_overloads.jl b/test/Core/forwarddiff_overloads.jl index 2f68e04f3..748f3f8f3 100644 --- a/test/Core/forwarddiff_overloads.jl +++ b/test/Core/forwarddiff_overloads.jl @@ -340,13 +340,13 @@ backslash_x_p = A \ b A, b = h([ForwardDiff.Dual(5.0, 1.0, 0.0), ForwardDiff.Dual(5.0, 0.0, 1.0)]) prob = LinearProblem(sparse(A), sparse(b)) -overload_x_p = solve(prob, UMFPACKFactorization()) +cache = init(prob, UMFPACKFactorization()) +overload_x_p = solve!(cache) backslash_x_p = A \ b @test ≈(overload_x_p, backslash_x_p, rtol = 1.0e-9) A[1, 1] += 2 -cache = overload_x_p.cache reinit!(cache; A = sparse(A)) overload_x_p = solve!(cache, UMFPACKFactorization()) backslash_x_p = A \ b diff --git a/test/Core/lightweight_solution.jl b/test/Core/lightweight_solution.jl new file mode 100644 index 000000000..5f9cb2869 --- /dev/null +++ b/test/Core/lightweight_solution.jl @@ -0,0 +1,107 @@ +using LinearSolve, LinearAlgebra, SparseArrays, StableRNGs, Test + +# LinearSolve 5.0: `solve!`/`solve` return a lightweight `LinearSolution` that +# no longer carries the `LinearCache` (`sol.cache === nothing`). Anyone who +# needs the cache after `solve!(cache)` already holds it. This keeps the +# warm-path wrapper elidable so repeated refactorize+solve cycles are +# allocation-free. + +rng = StableRNG(7) + +@testset "solve!/solve returns have cache === nothing" begin + n = 12 + A = rand(rng, n, n) + n * I + b = rand(rng, n) + + @testset "$(name)" for (name, alg) in ( + ("default algorithm", nothing), + ("LUFactorization", LUFactorization()), + ("GenericLUFactorization", GenericLUFactorization()), + ("QRFactorization", QRFactorization()), + ("KrylovJL_GMRES", KrylovJL_GMRES()), + ("SVDFactorization", SVDFactorization()), + ) + # tight reltol so the iterative solvers converge well below the + # `isapprox` default tolerance; direct solvers ignore it + cache = init(LinearProblem(copy(A), copy(b)), alg; reltol = 1.0e-12) + sol = solve!(cache) + @test sol.retcode == ReturnCode.Success + @test sol.u ≈ A \ b + @test sol.cache === nothing + + sol2 = solve(LinearProblem(copy(A), copy(b)), alg; reltol = 1.0e-12) + @test sol2.cache === nothing + end + + # sparse path + As = sparse(A) + sol = solve!(init(LinearProblem(As, copy(b)), KLUFactorization())) + @test sol.u ≈ A \ b + @test sol.cache === nothing + + # failure-path solutions are lightweight too + Asing = zeros(n, n) + sol = solve!(init(LinearProblem(Asing, copy(b)), LUFactorization(); verbose = false)) + @test sol.retcode == ReturnCode.Failure + @test sol.cache === nothing + + # the default algorithm's singular -> column-pivoted QR safety fallback + # also returns a cache-free solution + sol = solve!(init(LinearProblem(copy(Asing), copy(b)), nothing; verbose = false)) + @test sol.cache === nothing +end + +@testset "warm aliased refactorize+solve loop is allocation-free" begin + n = 51 + A = rand(rng, n, n) + n * I + B = rand(rng, n, n) # matrix RHS + + cache = init( + LinearProblem(copy(A), copy(B); u0 = zeros(n, n)), LUFactorization(); + alias = LinearAliasSpecifier(alias_A = true, alias_b = true) + ) + solve!(cache) + + # Refresh the aliased owned buffer from A (it holds LU factors after each + # solve) and mark it fresh, so every iteration refactorizes in place + # before solving. Consumes the returned solution's `u` into `out` and + # returns `nothing` so `@allocated` does not count a boxed return value. + function warm_loop!(out, cache, Awork, A, N) + for _ in 1:N + copyto!(Awork, A) + cache.A = Awork + sol = solve!(cache) + out[1] += sol.u[1, 1] + end + return nothing + end + + Awork = cache.A + out = zeros(1) + warm_loop!(out, cache, Awork, A, 2) + GC.gc() + alloc = @allocated warm_loop!(out, cache, Awork, A, 100) + if VERSION >= v"1.11" + # The lightweight (cache-free) wrapper is fully elided and the LAPACK + # `getrf!(A, ipiv)` pivot-reuse path is available: genuinely 0 bytes. + @test alloc == 0 + else + # Julia 1.10 lacks `LAPACK.getrf!(A, ipiv)`, so each refactorization + # allocates a fresh `ipiv` inside `getrf!` (n * sizeof(Int) plus array + # header), and 1.10's escape analysis does not always elide the small + # solution wrapper. This floor is a pre-existing 1.10 limitation, not + # a property of the solution wrapper: it is identical before and after + # the 5.0 lightweight-solution change. + @test alloc <= 100 * (n * sizeof(Int) + 96 + 64) + end + + # correctness is unchanged across warm refactorizations + for _ in 1:3 + copyto!(Awork, A) + cache.A = Awork + sol = solve!(cache) + @test sol.retcode == ReturnCode.Success + @test sol.u ≈ A \ B + @test sol.cache === nothing + end +end diff --git a/test/LinearSolveHYPRE/hypretests.jl b/test/LinearSolveHYPRE/hypretests.jl index e028c5b9f..f4dbe16a7 100644 --- a/test/LinearSolveHYPRE/hypretests.jl +++ b/test/LinearSolveHYPRE/hypretests.jl @@ -133,7 +133,6 @@ function test_interface(alg; kw...) @test cache.isfresh == cache.cacheval.isfresh_A == cache.cacheval.isfresh_b == cache.cacheval.isfresh_u == true y = solve!(cache) - cache = y.cache @test cache.isfresh == cache.cacheval.isfresh_A == cache.cacheval.isfresh_b == cache.cacheval.isfresh_u == false @test A * to_array(y.u) ≈ b atol = atol rtol = rtol @@ -143,7 +142,6 @@ function test_interface(alg; kw...) @test cache.isfresh == cache.cacheval.isfresh_A == true @test cache.cacheval.isfresh_b == cache.cacheval.isfresh_u == false y = solve!(cache; cache_kwargs...) - cache = y.cache @test cache.isfresh == cache.cacheval.isfresh_A == cache.cacheval.isfresh_b == cache.cacheval.isfresh_u == false @test A * to_array(y.u) ≈ b atol = atol rtol = rtol @@ -157,7 +155,6 @@ function test_interface(alg; kw...) @test cache.cacheval.isfresh_b @test cache.cacheval.isfresh_A == cache.cacheval.isfresh_u == false y = solve!(cache; cache_kwargs...) - cache = y.cache @test cache.isfresh == cache.cacheval.isfresh_A == cache.cacheval.isfresh_b == cache.cacheval.isfresh_u == false @test A * to_array(y.u) ≈ to_array(b2) atol = atol rtol = rtol @@ -167,13 +164,14 @@ end function test_retcode_failure() prob = failure_prob() - sol = solve( + cache = SciMLBase.init( prob, HYPREAlgorithm(HYPRE.PCG); abstol = 1.0e-12, reltol = 1.0e-12, maxiters = 1 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.MaxIters @test sol.iters == 1 - return @test sol.resid > sol.cache.reltol + return @test sol.resid > cache.reltol end const comm = MPI.COMM_WORLD diff --git a/test/LinearSolveHYPRE/hypretests_mpi.jl b/test/LinearSolveHYPRE/hypretests_mpi.jl index c03e8603c..20a0883e0 100644 --- a/test/LinearSolveHYPRE/hypretests_mpi.jl +++ b/test/LinearSolveHYPRE/hypretests_mpi.jl @@ -132,10 +132,11 @@ A_fail = spdiagm( -1 => -ones(n_fail - 1), 0 => 2.0 .* ones(n_fail), 1 => -ones(n_fail - 1) ) b_fail = ones(n_fail) -sol = solve( +cache_fail = init( LinearProblem(A_fail, b_fail), HYPREAlgorithm(HYPRE.PCG; comm = comm); abstol = 1.0e-12, reltol = 1.0e-12, maxiters = 1 ) +sol = solve!(cache_fail) @test sol.retcode == SciMLBase.ReturnCode.MaxIters @test sol.iters == 1 -@test sol.resid > sol.cache.reltol +@test sol.resid > cache_fail.reltol diff --git a/test/LinearSolveMUMPS/mumps.jl b/test/LinearSolveMUMPS/mumps.jl index 9e7ad1d3a..edfbd54c7 100644 --- a/test/LinearSolveMUMPS/mumps.jl +++ b/test/LinearSolveMUMPS/mumps.jl @@ -31,10 +31,11 @@ end ) alg = MUMPSFactorization() - sol = LinearSolve.solve(LinearProblem(A, b1), alg) + lincache = LinearSolve.init(LinearProblem(A, b1), alg) + sol = LinearSolve.solve!(lincache) @test sol.retcode == ReturnCode.Success @test residual_ok(A, sol.u, b1) - MUMPSExt.cleanup_mumps_cache!(sol) + MUMPSExt.cleanup_mumps_cache!(lincache) cache = LinearSolve.init(LinearProblem(A, b1), alg) sol1 = LinearSolve.solve!(cache) @@ -63,13 +64,14 @@ end x = ComplexF64[1.0 - 1im, 2.0 + 0.5im] b = transpose(A) * x - sol = LinearSolve.solve( + cache = LinearSolve.init( LinearProblem(A, b), MUMPSFactorization(; transposed = true) ) + sol = LinearSolve.solve!(cache) @test sol.retcode == ReturnCode.Success @test sol.u ≈ x atol = 1.0e-8 rtol = 1.0e-8 - MUMPSExt.cleanup_mumps_cache!(sol) + MUMPSExt.cleanup_mumps_cache!(cache) end @testset "MUMPSFactorization rejects unsupported scalar types" begin diff --git a/test/LinearSolvePETSc/petsctests.jl b/test/LinearSolvePETSc/petsctests.jl index ede6b17ab..7bec104a1 100644 --- a/test/LinearSolvePETSc/petsctests.jl +++ b/test/LinearSolvePETSc/petsctests.jl @@ -26,39 +26,44 @@ const nprocs = MPI.Comm_size(MPI.COMM_WORLD) prob = LinearProblem(A, b) @testset "GMRES" begin - sol = solve(prob, PETScAlgorithm(:gmres); abstol = 1.0e-10, reltol = 1.0e-10) + cache = SciMLBase.init(prob, PETScAlgorithm(:gmres); abstol = 1.0e-10, reltol = 1.0e-10) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "CG" begin - sol = solve(prob, PETScAlgorithm(:cg); abstol = 1.0e-10, reltol = 1.0e-10) + cache = SciMLBase.init(prob, PETScAlgorithm(:cg); abstol = 1.0e-10, reltol = 1.0e-10) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "BiCGSTAB" begin - sol = solve(prob, PETScAlgorithm(:bcgs); abstol = 1.0e-10, reltol = 1.0e-10) + cache = SciMLBase.init(prob, PETScAlgorithm(:bcgs); abstol = 1.0e-10, reltol = 1.0e-10) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "GMRES + Jacobi" begin - sol = solve( + cache = SciMLBase.init( prob, PETScAlgorithm(:gmres; pc_type = :jacobi); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "GMRES + ILU" begin - sol = solve( + cache = SciMLBase.init( prob, PETScAlgorithm(:gmres; pc_type = :ilu); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -66,9 +71,10 @@ end n = 50 A = rand(n, n) + 10I; A = A'A b = rand(n) - sol = solve(LinearProblem(A, b), PETScAlgorithm(:gmres); abstol = 1.0e-10) + cache = SciMLBase.init(LinearProblem(A, b), PETScAlgorithm(:gmres); abstol = 1.0e-10) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "Serial: Cache Interface" begin @@ -98,10 +104,11 @@ end b = rand(n); b .-= sum(b) / n alg = PETScAlgorithm(:cg; pc_type = :jacobi, nullspace = :constant) - sol = solve(LinearProblem(D, b), alg; abstol = 1.0e-10) + cache = SciMLBase.init(LinearProblem(D, b), alg; abstol = 1.0e-10) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success @test norm(D * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "Serial: Nullspace Custom" begin @@ -118,9 +125,10 @@ end @testset "Serial: Transposed Solve" begin n = 50 A = sprand(n, n, 0.2) + 5I; b = rand(n) - sol = solve(LinearProblem(A, b), PETScAlgorithm(:gmres; transposed = true)) + cache = SciMLBase.init(LinearProblem(A, b), PETScAlgorithm(:gmres; transposed = true)) + sol = solve!(cache) @test norm(A' * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "Serial: Warm Start" begin @@ -128,12 +136,13 @@ end A = sprand(n, n, 0.02) + 10I; A = A'A; b = rand(n) prob = LinearProblem(A, b) - sol_cold = solve( + cache_cold = SciMLBase.init( prob, PETScAlgorithm(:cg; initial_guess_nonzero = false); reltol = 1.0e-12 ) + sol_cold = solve!(cache_cold) iters_cold = sol_cold.iters - PETScExt.cleanup_petsc_cache!(sol_cold) + PETScExt.cleanup_petsc_cache!(cache_cold) cache_warm = SciMLBase.init( prob, PETScAlgorithm(:cg; initial_guess_nonzero = true); reltol = 1.0e-12 @@ -150,39 +159,43 @@ end @testset "Float64 sparse" begin A = sprand(Float64, n, n, 0.1) + 10I; A = A'A; b = rand(Float64, n) - sol = solve(LinearProblem(A, b), PETScAlgorithm(:cg); abstol = 1.0e-10) + cache = SciMLBase.init(LinearProblem(A, b), PETScAlgorithm(:cg); abstol = 1.0e-10) + sol = solve!(cache) @test eltype(sol.u) == Float64 @test norm(A * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "Float32 sparse" begin A32 = sparse(Float32.(Matrix(sprand(Float64, n, n, 0.1) + 10I))) A32 = A32'A32; b32 = rand(Float32, n) - sol32 = solve(LinearProblem(A32, b32), PETScAlgorithm(:cg); abstol = 1.0f-5) + cache32 = SciMLBase.init(LinearProblem(A32, b32), PETScAlgorithm(:cg); abstol = 1.0f-5) + sol32 = solve!(cache32) @test eltype(sol32.u) == Float32 @test norm(A32 * sol32.u - b32) / norm(b32) < 1.0f-3 - PETScExt.cleanup_petsc_cache!(sol32) + PETScExt.cleanup_petsc_cache!(cache32) end @testset "ComplexF64 sparse" begin B = sprand(ComplexF64, n, n, 0.1) Ac = B' * B + 10I bc = rand(ComplexF64, n) - sol = solve(LinearProblem(Ac, bc), PETScAlgorithm(:gmres); abstol = 1.0e-10) + cache = SciMLBase.init(LinearProblem(Ac, bc), PETScAlgorithm(:gmres); abstol = 1.0e-10) + sol = solve!(cache) @test eltype(sol.u) == ComplexF64 @test norm(Ac * sol.u - bc) / norm(bc) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "ComplexF32 sparse" begin B32 = sparse(ComplexF32.(Matrix(sprand(ComplexF64, n, n, 0.1)))) Ac32 = B32' * B32 + 10I bc32 = rand(ComplexF32, n) - sol32 = solve(LinearProblem(Ac32, bc32), PETScAlgorithm(:gmres); abstol = 1.0f-5) + cache32 = SciMLBase.init(LinearProblem(Ac32, bc32), PETScAlgorithm(:gmres); abstol = 1.0f-5) + sol32 = solve!(cache32) @test eltype(sol32.u) == ComplexF32 @test norm(Ac32 * sol32.u - bc32) / norm(bc32) < 1.0f-3 - PETScExt.cleanup_petsc_cache!(sol32) + PETScExt.cleanup_petsc_cache!(cache32) end @testset "Unsupported type errors" begin @@ -202,23 +215,26 @@ end @testset "High-precision tolerance" begin alg = PETScAlgorithm(:gmres; ksp_options = (ksp_rtol = 1.0e-14, ksp_atol = 1.0e-14)) - sol = solve(prob, alg) + cache = SciMLBase.init(prob, alg) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-13 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "Monitoring flags (no crash)" begin alg = PETScAlgorithm(:cg; ksp_options = (ksp_monitor = "", ksp_view = "")) - sol = solve(prob, alg) + cache = SciMLBase.init(prob, alg) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "KSP type override via options" begin alg = PETScAlgorithm(:gmres; ksp_options = (ksp_type = "cg",)) - sol = solve(prob, alg) + cache = SciMLBase.init(prob, alg) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -228,18 +244,19 @@ end A = A'A b = rand(n) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:cg); abstol = 1.0e-16, reltol = 1.0e-16, maxiters = 1 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Failure @test sol.iters == 1 - @test norm(A * sol.u - b) / norm(b) > sol.cache.reltol - PETScExt.cleanup_petsc_cache!(sol) + @test norm(A * sol.u - b) / norm(b) > cache.reltol + PETScExt.cleanup_petsc_cache!(cache) end @testset "Serial: Cleanup" begin @@ -248,11 +265,12 @@ end prob = LinearProblem(A, b) alg = PETScAlgorithm(:gmres; pc_type = :jacobi) - @testset "via solution" begin - sol = solve(prob, alg) - pcache = sol.cache.cacheval + @testset "via cache after solve" begin + cache = SciMLBase.init(prob, alg) + solve!(cache) + pcache = cache.cacheval @test pcache.ksp !== nothing - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) @test pcache.ksp === nothing end @@ -263,10 +281,11 @@ end end @testset "idempotent" begin - sol = solve(prob, alg) - PETScExt.cleanup_petsc_cache!(sol) - PETScExt.cleanup_petsc_cache!(sol) - @test sol.cache.cacheval.ksp === nothing + cache = SciMLBase.init(prob, alg) + solve!(cache) + PETScExt.cleanup_petsc_cache!(cache) + PETScExt.cleanup_petsc_cache!(cache) + @test cache.cacheval.ksp === nothing end @testset "empty cache (before solve)" begin @@ -431,27 +450,29 @@ end b = rand(n) ptr_nzval = pointer(A.nzval) ptr_colptr = pointer(A.colptr) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:gmres; pc_type = :ilu); abstol = 1.0e-10 ) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 @test pointer(A.nzval) === ptr_nzval @test pointer(A.colptr) === ptr_colptr - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "Dense operator: Julia array not copied" begin A = make_spd_dense(n) b = rand(n) ptr_before = pointer(A) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:gmres; pc_type = :jacobi); abstol = 1.0e-10 ) + sol = solve!(cache) @test norm(A * sol.u - b) / norm(b) < 1.0e-6 @test pointer(A) === ptr_before - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "Sparse Case 3: nzval pointer stable across value-only updates" begin @@ -515,13 +536,14 @@ end A = make_spd_dense(n) A_snapshot = copy(A) b = rand(n) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:preonly; pc_type = :lu); abstol = 1.0e-10 ) + sol = solve!(cache) @test norm(A_snapshot * sol.u - b) / norm(b) < 1.0e-6 # A may have been overwritten by LU — that's expected, no assertion on A's values - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -537,28 +559,31 @@ end b = rand(n) @testset "GMRES (CSR)" begin - sol = solve( + cache = SciMLBase.init( LinearProblem(A_csr, b), PETScAlgorithm(:gmres); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A_csc * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "CG (CSR)" begin - sol = solve( + cache = SciMLBase.init( LinearProblem(A_csr, b), PETScAlgorithm(:cg); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A_csc * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "GMRES + ILU (CSR)" begin - sol = solve( + cache = SciMLBase.init( LinearProblem(A_csr, b), PETScAlgorithm(:gmres; pc_type = :ilu); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A_csc * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -696,28 +721,31 @@ end b = rand(n) @testset "GMRES (CSR{0})" begin - sol = solve( + cache = SciMLBase.init( LinearProblem(A_csr0, b), PETScAlgorithm(:gmres); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A_csc * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "CG (CSR{0})" begin - sol = solve( + cache = SciMLBase.init( LinearProblem(A_csr0, b), PETScAlgorithm(:cg); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A_csc * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end @testset "GMRES + ILU (CSR{0})" begin - sol = solve( + cache = SciMLBase.init( LinearProblem(A_csr0, b), PETScAlgorithm(:gmres; pc_type = :ilu); abstol = 1.0e-10, reltol = 1.0e-10 ) + sol = solve!(cache) @test norm(A_csc * sol.u - b) / norm(b) < 1.0e-6 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end diff --git a/test/LinearSolvePETSc/petsctests_mpi.jl b/test/LinearSolvePETSc/petsctests_mpi.jl index 193685053..b0f8bf598 100644 --- a/test/LinearSolvePETSc/petsctests_mpi.jl +++ b/test/LinearSolvePETSc/petsctests_mpi.jl @@ -94,20 +94,21 @@ end end @testset "basic 2-rank GMRES solve replicates full solution" begin - sol = solve( + lincache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:gmres; comm = MPI.COMM_WORLD); abstol = 1.0e-10, reltol = 1.0e-10 ) - pcache = sol.cache.cacheval + sol = solve!(lincache) + pcache = lincache.cacheval @test sol.retcode == SciMLBase.ReturnCode.Success @test norm(A * sol.u - b) / norm(b) < 1.0e-10 @test sol.u ≈ x_ref atol = 1.0e-10 @test pcache.rstart >= 0 @test pcache.rend >= pcache.rstart - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(lincache) end @testset "cache reuse with new rhs preserves full-solution contract" begin @@ -133,18 +134,19 @@ end end @testset "maxiters failure sets retcode" begin - sol = solve( + lincache = SciMLBase.init( LinearProblem(A, b), PETScAlgorithm(:gmres; comm = MPI.COMM_WORLD); abstol = 1.0e-16, reltol = 1.0e-16, maxiters = 1 ) + sol = solve!(lincache) @test sol.retcode == SciMLBase.ReturnCode.Failure @test sol.iters == 1 - @test norm(A * sol.u - b) / norm(b) > sol.cache.reltol - PETScExt.cleanup_petsc_cache!(sol) + @test norm(A * sol.u - b) / norm(b) > lincache.reltol + PETScExt.cleanup_petsc_cache!(lincache) end end @@ -209,10 +211,11 @@ end with_mpi() do distribute rp = mpi_row_partition(distribute, n) A, b, u = build_splitmat_diag(rp) - sol = solve(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -221,10 +224,11 @@ end with_mpi() do distribute rp = mpi_row_partition(distribute, n) A, b, u = build_splitmat_diag(rp) - sol = solve(LinearProblem(A, b; u0 = u), PETScAlgorithm(:cg); abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), PETScAlgorithm(:cg); abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -233,14 +237,15 @@ end with_mpi() do distribute rp = mpi_row_partition(distribute, n) A, b, u = build_splitmat_diag(rp) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres; pc_type = :jacobi); abstol = 1.0e-12 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -251,10 +256,11 @@ end A, b, u = build_splitmat_diag(rp) P, _, _ = build_splitmat_diag(rp) alg = PETScAlgorithm(:gmres; pc_type = :jacobi, prec_matrix = P) - sol = solve(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -278,9 +284,10 @@ end u = PVector(map(rng -> zeros(length(rng)), rp), rp) alg = PETScAlgorithm(:gmres; pc_type = :jacobi) - sol = solve(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-10, reltol = 1.0e-10) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-10, reltol = 1.0e-10) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -302,16 +309,17 @@ end b = PVector(map(rng -> ones(length(rng)), rp), rp) u = PVector(map(rng -> zeros(length(rng)), rp), rp) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-16, reltol = 1.0e-16, maxiters = 1 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Failure @test sol.iters == 1 - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -333,15 +341,16 @@ end b = PVector(map(rng -> ones(length(rng)), rp), rp) u = PVector(map(rng -> zeros(length(rng)), rp), rp) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres; ksp_options = (ksp_rtol = 0.9,)); abstol = 0.0, reltol = 1.0e-12, maxiters = 100 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.APosterioriSafetyFailure - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -416,10 +425,11 @@ end with_mpi() do distribute rp = mpi_row_partition(distribute, n) A, b, u = build_csr_diag(rp) - sol = solve(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -428,14 +438,15 @@ end with_mpi() do distribute rp = mpi_row_partition(distribute, n) A, b, u = build_csr_diag(rp) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres; pc_type = :jacobi); abstol = 1.0e-12 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -446,10 +457,11 @@ end A, b, u = build_csr_diag(rp) P, _, _ = build_csr_diag(rp) alg = PETScAlgorithm(:gmres; pc_type = :jacobi, prec_matrix = P) - sol = solve(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -528,10 +540,11 @@ end with_debug() do distribute rp = debug_row_partition(distribute, n) A, b, u = build_splitmat_diag(rp) - sol = solve(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -540,10 +553,11 @@ end with_debug() do distribute rp = debug_row_partition(distribute, n) A, b, u = build_splitmat_diag(rp) - sol = solve(LinearProblem(A, b; u0 = u), PETScAlgorithm(:cg); abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), PETScAlgorithm(:cg); abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -552,14 +566,15 @@ end with_debug() do distribute rp = debug_row_partition(distribute, n) A, b, u = build_splitmat_diag(rp) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres; pc_type = :jacobi); abstol = 1.0e-12 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -570,10 +585,11 @@ end A, b, u = build_splitmat_diag(rp) P, _, _ = build_splitmat_diag(rp) alg = PETScAlgorithm(:gmres; pc_type = :jacobi, prec_matrix = P) - sol = solve(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -641,10 +657,11 @@ end with_debug() do distribute rp = debug_row_partition(distribute, n) A, b, u = build_csr_diag(rp) - sol = solve(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -653,14 +670,15 @@ end with_debug() do distribute rp = debug_row_partition(distribute, n) A, b, u = build_csr_diag(rp) - sol = solve( + cache = SciMLBase.init( LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres; pc_type = :jacobi); abstol = 1.0e-12 ) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -671,10 +689,11 @@ end A, b, u = build_csr_diag(rp) P, _, _ = build_csr_diag(rp) alg = PETScAlgorithm(:gmres; pc_type = :jacobi, prec_matrix = P) - sol = solve(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), alg; abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success assert_owned_approx(sol.u, 1.0) - PETScExt.cleanup_petsc_cache!(sol) + PETScExt.cleanup_petsc_cache!(cache) end end @@ -744,10 +763,11 @@ end with_mpi() do distribute rp = mpi_row_partition(distribute, n) A, b, u = build_splitmat_diag(rp) - sol = solve(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + cache = SciMLBase.init(LinearProblem(A, b; u0 = u), PETScAlgorithm(:gmres); abstol = 1.0e-12) + sol = solve!(cache) @test sol.retcode == SciMLBase.ReturnCode.Success # cleanup should not throw - PETScExt.cleanup_petsc_cache!(sol) - @test sol.cache.cacheval.ksp === nothing + PETScExt.cleanup_petsc_cache!(cache) + @test cache.cacheval.ksp === nothing end end diff --git a/test/LinearSolveSuperLUDIST/superludist.jl b/test/LinearSolveSuperLUDIST/superludist.jl index c08a0b231..529ab964e 100644 --- a/test/LinearSolveSuperLUDIST/superludist.jl +++ b/test/LinearSolveSuperLUDIST/superludist.jl @@ -30,10 +30,11 @@ end ) alg = SuperLUDISTFactorization(; comm = MPI.COMM_SELF) - sol = solve(LinearProblem(A, b1), alg) + lincache = init(LinearProblem(A, b1), alg) + sol = solve!(lincache) @test sol.retcode == ReturnCode.Success @test residual_ok(A, sol.u, b1) - SuperLUDISTExt.cleanup_superludist_cache!(sol) + SuperLUDISTExt.cleanup_superludist_cache!(lincache) cache = init(LinearProblem(A, b1), alg) sol1 = solve!(cache) diff --git a/test/qa/jet.jl b/test/qa/jet.jl index 660c908d4..8aadbf829 100644 --- a/test/qa/jet.jl +++ b/test/qa/jet.jl @@ -131,7 +131,9 @@ end # The dispatches occur in sparse_check_Ti and SparseMatrixCSC constructor # These are stdlib issues, not LinearSolve issues JET.@test_opt solve(prob_sparse, UMFPACKFactorization()) broken = true - JET.@test_opt solve(prob_sparse, KLUFactorization()) broken = true + # Passes since the 5.0 lightweight solution: with the cache no longer + # captured in the returned LinearSolution, the KLU solve is dispatch-clean. + JET.@test_opt solve(prob_sparse, KLUFactorization()) JET.@test_opt solve(prob_sparse_spd, CHOLMODFactorization()) broken = true # SparspakFactorization requires Sparspak to be loaded diff --git a/test/runtests.jl b/test/runtests.jl index 54faef520..d169d9205 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -66,6 +66,7 @@ else @time @safetestset "Batched RHS" include("Core/batch.jl") @time @safetestset "GESV Factorization" include("Core/gesv.jl") @time @safetestset "LU Refactorization Reuse" include("Core/lu_refactorization.jl") + @time @safetestset "Lightweight Solution (no cache)" include("Core/lightweight_solution.jl") @time @safetestset "Return codes" include("Core/retcodes.jl") @time @safetestset "Re-solve" include("Core/resolve.jl") @time @safetestset "Zero Initialization Tests" include("Core/zeroinittests.jl") From af74ae5759afb45e96208b6824f81eb314a14c22 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 11 Jul 2026 07:15:13 -0400 Subject: [PATCH 4/6] Update docs to 5.0 lightweight-solution semantics The caching interface tutorial no longer teaches retrieving the cache via sol.cache, the preconditioner example keeps the cache from init, and the PETSc/MUMPS memory-management examples clean up through the held LinearCache. Co-Authored-By: Claude Fable 5 Co-Authored-By: Chris Rackauckas --- docs/src/basics/Preconditioners.md | 4 ++-- docs/src/solvers/solvers.md | 10 ++++++---- docs/src/tutorials/caching_interface.md | 7 +++++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/src/basics/Preconditioners.md b/docs/src/basics/Preconditioners.md index b5c2328bb..b6ab0b232 100644 --- a/docs/src/basics/Preconditioners.md +++ b/docs/src/basics/Preconditioners.md @@ -89,11 +89,11 @@ A = n * LA.I - rand(n, n) b = rand(n) prob = LS.LinearProblem(A, b) -sol = LS.solve(prob, LS.KrylovJL_GMRES(precs = WeightedDiagonalPreconBuilder(w = 0.9))) +cache = LS.init(prob, LS.KrylovJL_GMRES(precs = WeightedDiagonalPreconBuilder(w = 0.9))) +sol = LS.solve!(cache) sol.u B = A .+ 0.1 -cache = sol.cache LS.reinit!(cache, A = B, reuse_precs = true) sol = LS.solve!(cache, LS.KrylovJL_GMRES(precs = WeightedDiagonalPreconBuilder(w = 0.9))) sol.u diff --git a/docs/src/solvers/solvers.md b/docs/src/solvers/solvers.md index 978acb15d..13c6bca45 100644 --- a/docs/src/solvers/solvers.md +++ b/docs/src/solvers/solvers.md @@ -275,10 +275,10 @@ ParUFactorization !!! warning Call `cleanup_mumps_cache!` explicitly through the extension before - `MPI.Finalize()`: + `MPI.Finalize()`, using the `LinearCache` from an `init`/`solve!` cycle: ```julia MUMPSExt = Base.get_extension(LinearSolve, :LinearSolveMUMPSExt) - MUMPSExt.cleanup_mumps_cache!(sol) + MUMPSExt.cleanup_mumps_cache!(cache) ``` ```@docs @@ -768,8 +768,10 @@ PETSc objects live in C-managed memory outside Julia's GC. Call ```julia PETScExt = Base.get_extension(LinearSolve, :LinearSolvePETScExt) -PETScExt.cleanup_petsc_cache!(sol) # after solve(...) -PETScExt.cleanup_petsc_cache!(cache) # after init/solve! cycle + +cache = SciMLBase.init(prob, PETScAlgorithm(:gmres)) +sol = solve!(cache) +PETScExt.cleanup_petsc_cache!(cache) # after the init/solve! cycle ``` A GC finalizer is registered as a safety net, but explicit cleanup is strongly preferred. diff --git a/docs/src/tutorials/caching_interface.md b/docs/src/tutorials/caching_interface.md index 355f6c16c..d89bf3e61 100644 --- a/docs/src/tutorials/caching_interface.md +++ b/docs/src/tutorials/caching_interface.md @@ -52,8 +52,11 @@ sol3.u ``` The factorization occurs on the first solve, and it stores the factorization in -the cache. You can retrieve this cache via `sol.cache`, which is the same object -as the `init`, but updated to know not to re-solve the factorization. +the cache: the `linsolve` object returned by `init` is updated in-place to know +not to re-solve the factorization. Keep the object from `init` around to reuse +it; the solution returned by `solve!` is a lightweight value type that only +carries the answer (`sol.u`, `sol.resid`, `sol.retcode`, ...), so its `cache` +field is `nothing` (as of LinearSolve.jl 5.0). The advantage of course with import LinearSolve.jl in this form is that it is efficient while being agnostic to the linear solver. One can easily swap in From 0bdb927eae527eeb528a8aa461a0bbaab4e8a5f7 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 11 Jul 2026 07:32:38 -0400 Subject: [PATCH 5/6] Gate lu_refactorization wrapper-elision ceilings on Julia 1.12 The zero-allocation assertions introduced in #1082 assume the returned LinearSolution wrapper is elided at testset scope, which holds on 1.12+ but not on 1.11 (which CI never runs: lts/1/pre = 1.10/1.12/pre). The failure reproduces identically on unmodified main under 1.11 (48 bytes there; 32 with the 5.0 lightweight solution). Allow the boxed wrapper (<= 48 bytes) on 1.11 only; a leaked pivot vector would add >= n * sizeof(Int) on top, so the reuse assertions remain meaningful. Co-Authored-By: Claude Fable 5 Co-Authored-By: Chris Rackauckas --- test/Core/lu_refactorization.jl | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/test/Core/lu_refactorization.jl b/test/Core/lu_refactorization.jl index c74761c42..736b3a3d7 100644 --- a/test/Core/lu_refactorization.jl +++ b/test/Core/lu_refactorization.jl @@ -7,6 +7,16 @@ using LinearSolve, LinearAlgebra, StableRNGs, Test # non-BLAS eltypes) is allocation-free on every supported version. const BLAS_IPIV_REUSE = VERSION >= v"1.11" +# On Julia 1.11 exactly, `@allocated refactor_solve!(...)` at testset scope +# still counts one boxed `LinearSolution` return (<= 48 bytes): 1.11 has the +# `getrf!(A, ipiv)` reuse API but its escape analysis does not elide the +# returned solution wrapper here; 1.12+ elides it fully. A leaked pivot vector +# would add >= n * sizeof(Int) bytes on top, so the reuse assertion stays +# meaningful under this ceiling. (CI runs lts/1/pre = 1.10/1.12+/pre, so this +# only shows up on local 1.11 runs; the ceilings were introduced in #1082 +# assuming the 1.12 elision behavior.) +const WRAPPER_CEILING = VERSION >= v"1.12" ? 0 : 48 + rng = StableRNG(42) function refactor_solve!(cache, Awork, A) @@ -28,12 +38,18 @@ end # 1.10's compiler does not always elide the small `LU`/solution wrapper # constructions that 1.11+ removes, hence the small nonzero ceiling. @testset "$(name)" for (name, alg, maxalloc) in ( - ("LUFactorization RowMaximum", LUFactorization(), BLAS_IPIV_REUSE ? 0 : nothing), + ( + "LUFactorization RowMaximum", LUFactorization(), + BLAS_IPIV_REUSE ? WRAPPER_CEILING : nothing, + ), ( "LUFactorization NoPivot", LUFactorization(pivot = NoPivot()), - VERSION >= v"1.11" ? 0 : 64, + VERSION >= v"1.11" ? WRAPPER_CEILING : 64, + ), + ( + "default algorithm", nothing, + BLAS_IPIV_REUSE ? WRAPPER_CEILING : nothing, ), - ("default algorithm", nothing, BLAS_IPIV_REUSE ? 0 : nothing), ) cache = init( LinearProblem(copy(A1), copy(b)), alg; @@ -163,7 +179,7 @@ end refactor_solve!(cache, Awork, A2) alloc = @allocated refactor_solve!(cache, Awork, A2) if BLAS_IPIV_REUSE - @test alloc == 0 + @test alloc <= WRAPPER_CEILING end @test cache.u ≈ A2 \ b2 end From 793d25f3236477740efbeacba94f779e89798c9e Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sat, 11 Jul 2026 16:29:15 -0400 Subject: [PATCH 6/6] Restore relative [sources] paths leaked from scratch dev environments The AD and qa test Project.tomls had absolute scratch paths spliced in by a Pkg.develop during this PR's work; the GPU one carried an even older leak already on main. All three restored to {path = "../.."}. Co-Authored-By: Claude Fable 5 Co-Authored-By: Chris Rackauckas --- test/AD/Project.toml | 2 +- test/GPU/Project.toml | 2 +- test/qa/Project.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/AD/Project.toml b/test/AD/Project.toml index dbab7ae4d..ab41a9eba 100644 --- a/test/AD/Project.toml +++ b/test/AD/Project.toml @@ -17,7 +17,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] -LinearSolve = {path = "/home/crackauc/sandbox/tmp_20260611_010705_2268/p16"} +LinearSolve = {path = "../.."} [compat] AllocCheck = "0.2" diff --git a/test/GPU/Project.toml b/test/GPU/Project.toml index d12ab941e..bd7329acd 100644 --- a/test/GPU/Project.toml +++ b/test/GPU/Project.toml @@ -13,7 +13,7 @@ cuSOLVER = "887afef0-6a32-4de5-add4-7827692ba8fc" cuSPARSE = "b26da814-b3bc-49ef-b0ee-c816305aa060" [sources] -LinearSolve = {path = "/home/crackauc/sandbox/tmp_20260531_035946_10202/dg/fr_LinearSolve.jl"} +LinearSolve = {path = "../.."} [compat] BlockDiagonals = "0.2" diff --git a/test/qa/Project.toml b/test/qa/Project.toml index f38367f30..42897be44 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -7,7 +7,7 @@ SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [sources] -LinearSolve = {path = "/home/crackauc/sandbox/tmp_20260611_010705_2268/p16"} +LinearSolve = {path = "../.."} [compat] Aqua = "0.8"