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/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 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/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/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/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/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 diff --git a/test/AD/Project.toml b/test/AD/Project.toml index 80ccdf35a..ab41a9eba 100644 --- a/test/AD/Project.toml +++ b/test/AD/Project.toml @@ -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/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/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 diff --git a/test/GPU/Project.toml b/test/GPU/Project.toml index 27f05c058..bd7329acd 100644 --- a/test/GPU/Project.toml +++ b/test/GPU/Project.toml @@ -13,13 +13,13 @@ 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" 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/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/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/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/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/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/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/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/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..42897be44 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -12,7 +12,7 @@ LinearSolve = {path = "../.."} [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" 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")