diff --git a/Project.toml b/Project.toml index e4aafbc38..a60aa7a09 100644 --- a/Project.toml +++ b/Project.toml @@ -77,6 +77,7 @@ ChainRulesCore = "1.22" ConcreteStructs = "0.2.3" DocStringExtensions = "0.9.3" EnumX = "1.0.4" +ExplicitImports = "1" EnzymeCore = "0.8.1" FastAlmostBandedMatrices = "0.1" FastLapackInterface = "2" @@ -122,6 +123,7 @@ julia = "1.10" [extras] AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7" BandedMatrices = "aae01518-5342-5314-be14-df237901396f" BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" FastAlmostBandedMatrices = "9d29842c-ecb8-4973-b1e9-a27b1157504e" @@ -150,4 +152,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [targets] -test = ["Aqua", "Test", "IterativeSolvers", "InteractiveUtils", "KrylovKit", "KrylovPreconditioners", "Pkg", "Random", "SafeTestsets", "MultiFloats", "ForwardDiff", "HYPRE", "MPI", "BlockDiagonals", "FiniteDiff", "BandedMatrices", "FastAlmostBandedMatrices", "StaticArrays", "AllocCheck", "StableRNGs", "Zygote", "RecursiveFactorization", "Sparspak", "FastLapackInterface", "SparseArrays"] +test = ["Aqua", "Test", "IterativeSolvers", "InteractiveUtils", "KrylovKit", "KrylovPreconditioners", "Pkg", "Random", "SafeTestsets", "MultiFloats", "ForwardDiff", "HYPRE", "MPI", "BlockDiagonals", "FiniteDiff", "BandedMatrices", "FastAlmostBandedMatrices", "StaticArrays", "AllocCheck", "StableRNGs", "Zygote", "RecursiveFactorization", "Sparspak", "FastLapackInterface", "SparseArrays", "ExplicitImports"] diff --git a/ext/LinearSolveCUDAExt.jl b/ext/LinearSolveCUDAExt.jl index 291c8bcec..48840c097 100644 --- a/ext/LinearSolveCUDAExt.jl +++ b/ext/LinearSolveCUDAExt.jl @@ -1,7 +1,9 @@ module LinearSolveCUDAExt using CUDA -using LinearSolve +using LinearSolve: LinearSolve, is_cusparse, defaultalg, cudss_loaded, DefaultLinearSolver, + DefaultAlgorithmChoice, ALREADY_WARNED_CUDSS, LinearCache, needs_concrete_A, + error_no_cudss_lu, CUDSS_LOADED, init_cacheval using LinearSolve.LinearAlgebra, LinearSolve.SciMLBase, LinearSolve.ArrayInterface using SciMLBase: AbstractSciMLOperator diff --git a/ext/LinearSolveCUDSSExt.jl b/ext/LinearSolveCUDSSExt.jl index 6bf4da020..506ada99a 100644 --- a/ext/LinearSolveCUDSSExt.jl +++ b/ext/LinearSolveCUDSSExt.jl @@ -1,6 +1,6 @@ module LinearSolveCUDSSExt -using LinearSolve +using LinearSolve: LinearSolve, cudss_loaded using CUDSS LinearSolve.cudss_loaded(A::CUDSS.CUDA.CUSPARSE.CuSparseMatrixCSR) = true diff --git a/ext/LinearSolveEnzymeExt.jl b/ext/LinearSolveEnzymeExt.jl index 1d2956830..5f7bb2add 100644 --- a/ext/LinearSolveEnzymeExt.jl +++ b/ext/LinearSolveEnzymeExt.jl @@ -1,6 +1,8 @@ module LinearSolveEnzymeExt -using LinearSolve +using LinearSolve: LinearSolve, SciMLLinearSolveAlgorithm, init, solve!, LinearProblem, + LinearCache, AbstractKrylovSubspaceMethod, DefaultLinearSolver, + defaultalg_adjoint_eval, solve using LinearSolve.LinearAlgebra using EnzymeCore using EnzymeCore: EnzymeRules diff --git a/ext/LinearSolvePardisoExt.jl b/ext/LinearSolvePardisoExt.jl index 7f27bf875..5b459d8cc 100644 --- a/ext/LinearSolvePardisoExt.jl +++ b/ext/LinearSolvePardisoExt.jl @@ -3,10 +3,9 @@ module LinearSolvePardisoExt using Pardiso, LinearSolve using SparseArrays using SparseArrays: nonzeros, rowvals, getcolptr -using LinearSolve: PardisoJL +using LinearSolve: PardisoJL, @unpack using LinearSolve.SciMLBase -using LinearSolve.UnPack LinearSolve.needs_concrete_A(alg::PardisoJL) = true diff --git a/ext/LinearSolveRecursiveFactorizationExt.jl b/ext/LinearSolveRecursiveFactorizationExt.jl index 3e8455eb2..a7892e6df 100644 --- a/ext/LinearSolveRecursiveFactorizationExt.jl +++ b/ext/LinearSolveRecursiveFactorizationExt.jl @@ -1,7 +1,8 @@ module LinearSolveRecursiveFactorizationExt -using LinearSolve +using LinearSolve: LinearSolve, userecursivefactorization, LinearCache, @get_cacheval, RFLUFactorization using LinearSolve.LinearAlgebra, LinearSolve.ArrayInterface, RecursiveFactorization +using SciMLBase: SciMLBase, ReturnCode LinearSolve.userecursivefactorization(A::Union{Nothing, AbstractMatrix}) = true diff --git a/ext/LinearSolveSparseArraysExt.jl b/ext/LinearSolveSparseArraysExt.jl index edb44f735..ae36933ae 100644 --- a/ext/LinearSolveSparseArraysExt.jl +++ b/ext/LinearSolveSparseArraysExt.jl @@ -1,9 +1,17 @@ module LinearSolveSparseArraysExt -using LinearSolve, LinearAlgebra -using SparseArrays -using SparseArrays: AbstractSparseMatrixCSC, nonzeros, rowvals, getcolptr -using LinearSolve: BLASELTYPES, pattern_changed, ArrayInterface +using LinearSolve: LinearSolve, BLASELTYPES, pattern_changed, ArrayInterface, + @get_cacheval, CHOLMODFactorization, GenericFactorization, GenericLUFactorization, + KLUFactorization, LUFactorization, NormalCholeskyFactorization, OperatorAssumptions, + QRFactorization, RFLUFactorization, UMFPACKFactorization, solve +using ArrayInterface: ArrayInterface +using LinearAlgebra: LinearAlgebra, I, Hermitian, Symmetric, cholesky, ldiv!, lu, lu!, QR +using SparseArrays: SparseArrays, AbstractSparseArray, AbstractSparseMatrixCSC, SparseMatrixCSC, + nonzeros, rowvals, getcolptr, sparse, sprand +using SparseArrays.UMFPACK: UMFPACK_OK +using Base: /, \, convert +using SciMLBase: SciMLBase, LinearProblem, ReturnCode +import StaticArraysCore: SVector # Can't `using KLU` because cannot have a dependency in there without # requiring the user does `using KLU` @@ -187,7 +195,7 @@ function SciMLBase.solve!( end F = LinearSolve.@get_cacheval(cache, :UMFPACKFactorization) - if F.status == SparseArrays.UMFPACK.UMFPACK_OK + if F.status == UMFPACK_OK y = ldiv!(cache.u, F, cache.b) SciMLBase.build_linear_solution(alg, y, nothing, cache; retcode = ReturnCode.Success) else @@ -298,36 +306,36 @@ function LinearSolve.init_cacheval(alg::NormalCholeskyFactorization, Symmetric{T, <:AbstractSparseArray{T}}}, b, u, Pl, Pr, maxiters::Int, abstol, reltol, verbose::Bool, assumptions::OperatorAssumptions) where {T <: BLASELTYPES} - LinearSolve.ArrayInterface.cholesky_instance(convert(AbstractMatrix, A)) + ArrayInterface.cholesky_instance(convert(AbstractMatrix, A)) end # Specialize QR for the non-square case # Missing ldiv! definitions: https://github.com/JuliaSparse/SparseArrays.jl/issues/242 function LinearSolve._ldiv!(x::Vector, - A::Union{SparseArrays.QR, LinearAlgebra.QRCompactWY, + A::Union{QR, LinearAlgebra.QRCompactWY, SparseArrays.SPQR.QRSparse, SparseArrays.CHOLMOD.Factor}, b::Vector) x .= A \ b end function LinearSolve._ldiv!(x::AbstractVector, - A::Union{SparseArrays.QR, LinearAlgebra.QRCompactWY, + A::Union{QR, LinearAlgebra.QRCompactWY, SparseArrays.SPQR.QRSparse, SparseArrays.CHOLMOD.Factor}, b::AbstractVector) x .= A \ b end # Ambiguity removal -function LinearSolve._ldiv!(::LinearSolve.SVector, +function LinearSolve._ldiv!(::SVector, A::Union{SparseArrays.CHOLMOD.Factor, LinearAlgebra.QR, LinearAlgebra.QRCompactWY, SparseArrays.SPQR.QRSparse}, b::AbstractVector) (A \ b) end -function LinearSolve._ldiv!(::LinearSolve.SVector, +function LinearSolve._ldiv!(::SVector, A::Union{SparseArrays.CHOLMOD.Factor, LinearAlgebra.QR, LinearAlgebra.QRCompactWY, SparseArrays.SPQR.QRSparse}, - b::LinearSolve.SVector) + b::SVector) (A \ b) end @@ -363,7 +371,7 @@ end function LinearSolve.init_cacheval(alg::QRFactorization, A::SparseMatrixCSC{Float64, <:Integer}, b, u, Pl, Pr, maxiters::Int, abstol, reltol, verbose::Bool, assumptions::OperatorAssumptions) - LinearSolve.ArrayInterface.qr_instance(convert(AbstractMatrix, A), alg.pivot) + ArrayInterface.qr_instance(convert(AbstractMatrix, A), alg.pivot) end function LinearSolve.init_cacheval( diff --git a/src/KLU/klu.jl b/src/KLU/klu.jl index 63c68f179..c31fdd782 100644 --- a/src/KLU/klu.jl +++ b/src/KLU/klu.jl @@ -1,15 +1,20 @@ module KLU -using SparseArrays -using SparseArrays: SparseMatrixCSC +using SparseArrays: SparseArrays, SparseMatrixCSC import SparseArrays: nnz export klu, klu! const libklu = :libklu +const libsuitesparseconfig = :libsuitesparseconfig +using Base: Ptr, Cvoid, Cint, Cdouble, Cchar, Csize_t include("wrappers.jl") -import Base: (\), size, getproperty, setproperty!, propertynames, show +import Base: (\), size, getproperty, setproperty!, propertynames, show, + copy, eachindex, view, sortperm, unsafe_load, zeros, convert, eltype, + length, parent, stride, finalizer, Complex, complex, imag, real, map!, + summary, println, oneunit, sizeof, isdefined, setfield!, getfield, + OutOfMemoryError, ArgumentError, OverflowError, ErrorException, DimensionMismatch # Convert from 1-based to 0-based indices function decrement!(A::AbstractArray{T}) where {T <: Integer} @@ -29,7 +34,8 @@ function increment!(A::AbstractArray{T}) where {T <: Integer} end increment(A::AbstractArray{<:Integer}) = increment!(copy(A)) -using LinearAlgebra +using LinearAlgebra: LinearAlgebra, ldiv!, Adjoint, Transpose, Factorization +import LinearAlgebra: issuccess const AdjointFact = isdefined(LinearAlgebra, :AdjointFactorization) ? LinearAlgebra.AdjointFactorization : Adjoint diff --git a/src/LinearSolve.jl b/src/LinearSolve.jl index 270b64932..42bc13a7b 100644 --- a/src/LinearSolve.jl +++ b/src/LinearSolve.jl @@ -5,27 +5,33 @@ if isdefined(Base, :Experimental) && end import PrecompileTools -using ArrayInterface -using Base: cache_dependencies, Bool -using LinearAlgebra +using ArrayInterface: ArrayInterface +using Base: Bool, convert, copyto!, adjoint, transpose, /, \, require_one_based_indexing +using LinearAlgebra: LinearAlgebra, BlasInt, LU, Adjoint, BLAS, Bidiagonal, BunchKaufman, + ColumnNorm, Diagonal, Factorization, Hermitian, I, LAPACK, NoPivot, + RowMaximum, RowNonZero, SymTridiagonal, Symmetric, Transpose, + Tridiagonal, UniformScaling, axpby!, axpy!, bunchkaufman, bunchkaufman!, + cholesky, cholesky!, diagind, dot, inv, ldiv!, ldlt!, lu, lu!, mul!, norm, + qr, qr!, svd, svd! using LazyArrays: @~, BroadcastArray -using SciMLBase: AbstractLinearAlgorithm, LinearAliasSpecifier -using SciMLOperators -using SciMLOperators: AbstractSciMLOperator, IdentityOperator -using Setfield -using UnPack -using DocStringExtensions -using EnumX -using Markdown -using ChainRulesCore +using SciMLBase: SciMLBase, LinearAliasSpecifier, AbstractSciMLOperator, + init, solve!, reinit!, solve, ReturnCode, LinearProblem +using SciMLOperators: SciMLOperators, AbstractSciMLOperator, IdentityOperator, MatrixOperator, + has_ldiv!, issquare +using Setfield: @set, @set! +using UnPack: @unpack +using DocStringExtensions: DocStringExtensions +using EnumX: EnumX, @enumx +using Markdown: Markdown, @doc_str +using ChainRulesCore: ChainRulesCore, NoTangent +using Reexport: Reexport, @reexport +using Libdl: Libdl, dlsym_e import InteractiveUtils import RecursiveArrayTools -import StaticArraysCore: StaticArray, SVector, MVector, SMatrix, MMatrix +import StaticArraysCore: StaticArray, SVector, SMatrix -using LinearAlgebra: BlasInt, LU -using LinearAlgebra.LAPACK: require_one_based_indexing, - chkfinite, chkstride1, +using LinearAlgebra.LAPACK: chkfinite, chkstride1, @blasfunc, chkargsok import GPUArraysCore @@ -34,8 +40,6 @@ import ConcreteStructs: @concrete # wrap import Krylov -using SciMLBase -import Preferences const CRC = ChainRulesCore @@ -51,9 +55,7 @@ else const usemkl = false end -using Reexport @reexport using SciMLBase -using SciMLBase: _unwrap_val abstract type SciMLLinearSolveAlgorithm <: SciMLBase.AbstractLinearAlgorithm end abstract type AbstractFactorization <: SciMLLinearSolveAlgorithm end @@ -95,6 +97,11 @@ issparsematrix(A) = false make_SparseMatrixCSC(A) = nothing makeempty_SparseMatrixCSC(A) = nothing +# Stub functions for SparseArrays - overridden in extension +getcolptr(A) = error("SparseArrays extension not loaded") +rowvals(A) = error("SparseArrays extension not loaded") +nonzeros(A) = error("SparseArrays extension not loaded") + EnumX.@enumx DefaultAlgorithmChoice begin LUFactorization QRFactorization diff --git a/src/appleaccelerate.jl b/src/appleaccelerate.jl index 72a5b34a5..a867cc53f 100644 --- a/src/appleaccelerate.jl +++ b/src/appleaccelerate.jl @@ -252,7 +252,7 @@ function SciMLBase.solve!(cache::LinearCache, alg::AppleAccelerateLUFactorizatio end A, info = @get_cacheval(cache, :AppleAccelerateLUFactorization) - LinearAlgebra.require_one_based_indexing(cache.u, cache.b) + require_one_based_indexing(cache.u, cache.b) m, n = size(A, 1), size(A, 2) if m > n Bc = copy(cache.b) diff --git a/src/common.jl b/src/common.jl index da1aea2e1..90d00757f 100644 --- a/src/common.jl +++ b/src/common.jl @@ -207,7 +207,8 @@ function SciMLBase.init(prob::LinearProblem, alg::SciMLLinearSolveAlgorithm, elseif b isa Array copy(b) elseif issparsematrixcsc(b) - SparseMatrixCSC(size(b)..., getcolptr(b), rowvals(b), nonzeros(b)) + # Extension must be loaded if issparsematrixcsc returns true + make_SparseMatrixCSC(b) else deepcopy(b) end diff --git a/src/default.jl b/src/default.jl index 5051d000e..07a12b09c 100644 --- a/src/default.jl +++ b/src/default.jl @@ -125,7 +125,7 @@ function defaultalg(A::GPUArraysCore.AnyGPUArray, b::GPUArraysCore.AnyGPUArray, end end -function defaultalg(A::SciMLBase.AbstractSciMLOperator, b, +function defaultalg(A::SciMLOperators.AbstractSciMLOperator, b, assump::OperatorAssumptions{Bool}) if has_ldiv!(A) return DefaultLinearSolver(DefaultAlgorithmChoice.DirectLdiv!) @@ -142,7 +142,7 @@ function defaultalg(A::SciMLBase.AbstractSciMLOperator, b, end # Fix ambiguity -function defaultalg(A::SciMLBase.AbstractSciMLOperator, b::GPUArraysCore.AnyGPUArray, +function defaultalg(A::SciMLOperators.AbstractSciMLOperator, b::GPUArraysCore.AnyGPUArray, assump::OperatorAssumptions{Bool}) if has_ldiv!(A) return DefaultLinearSolver(DefaultAlgorithmChoice.DirectLdiv!) diff --git a/test/qa.jl b/test/qa.jl index 9b4045063..f69b08f96 100644 --- a/test/qa.jl +++ b/test/qa.jl @@ -1,4 +1,6 @@ using LinearSolve, Aqua +using ExplicitImports + @testset "Aqua" begin Aqua.find_persistent_tasks_deps(LinearSolve) Aqua.test_ambiguities(LinearSolve, recursive = false, broken = true) @@ -10,3 +12,21 @@ using LinearSolve, Aqua Aqua.test_unbound_args(LinearSolve) Aqua.test_undefined_exports(LinearSolve) end + +@testset "Explicit Imports" begin + # Get extension modules that might be unanalyzable + klu_mod = try + Base.get_extension(LinearSolve, :LinearSolveSparseArraysExt).KLU + catch + nothing + end + unanalyzable_mods = (LinearSolve.OperatorCondition, LinearSolve.DefaultAlgorithmChoice) + if klu_mod !== nothing + unanalyzable_mods = (unanalyzable_mods..., klu_mod) + end + + @test check_no_implicit_imports(LinearSolve; skip = (Base, Core), + allow_unanalyzable = unanalyzable_mods) === nothing + @test check_no_stale_explicit_imports(LinearSolve; allow_unanalyzable = unanalyzable_mods) === nothing + @test check_all_qualified_accesses_via_owners(LinearSolve) === nothing +end