diff --git a/Project.toml b/Project.toml index a5c275c..dc611a3 100644 --- a/Project.toml +++ b/Project.toml @@ -33,7 +33,7 @@ PrecompileTools = "1" Printf = "1.10" Random = "1" SafeTestsets = "0.1, 1" -SciMLTesting = "1" +SciMLTesting = "1.6" SparseArrays = "1.10" StaticArrays = "1.9.8" Test = "1" diff --git a/src/ExponentialUtilities.jl b/src/ExponentialUtilities.jl index 9e89db3..dcfa006 100644 --- a/src/ExponentialUtilities.jl +++ b/src/ExponentialUtilities.jl @@ -1,6 +1,6 @@ module ExponentialUtilities using LinearAlgebra, SparseArrays, Printf -using ArrayInterface: ismutable, allowed_getindex, allowed_setindex! +using ArrayInterface: ismutable, allowed_setindex! using PrecompileTools import GenericSchur import GPUArraysCore diff --git a/src/exp_noalloc.jl b/src/exp_noalloc.jl index d93d630..80047db 100644 --- a/src/exp_noalloc.jl +++ b/src/exp_noalloc.jl @@ -18,9 +18,19 @@ function alloc_mem(A, ::ExpMethodHigham2005) end # Import the generated code -for i in 1:13 - include("exp_generated/exp_$i.jl") -end +include("exp_generated/exp_1.jl") +include("exp_generated/exp_2.jl") +include("exp_generated/exp_3.jl") +include("exp_generated/exp_4.jl") +include("exp_generated/exp_5.jl") +include("exp_generated/exp_6.jl") +include("exp_generated/exp_7.jl") +include("exp_generated/exp_8.jl") +include("exp_generated/exp_9.jl") +include("exp_generated/exp_10.jl") +include("exp_generated/exp_11.jl") +include("exp_generated/exp_12.jl") +include("exp_generated/exp_13.jl") function getmem(cache, k) # Called from generated code return cache[k - 1] @@ -89,13 +99,22 @@ function exponential!(A, method::ExpMethodHigham2005, _cache = alloc_mem(A, meth n = LinearAlgebra.checksquare(A) nA = opnorm(A, 1) - # Maybe to balancing + # Maybe to balancing. `ilo`/`ihi`/`scale` are seeded with no-op defaults so they are + # always defined before the symmetric undo block below; the two `do_balancing` + # branches are not provably correlated to the compiler, so without these seeds the + # undo block reads possibly-undefined locals (flagged by JET typo-mode). + ilo = 1 + ihi = n + scale = _scale + prow = nothing # row/col permutations from the GenericSchur (non-BLAS) balancing path + pcol = nothing if method.do_balancing if A isa StridedMatrix{<:LinearAlgebra.BLAS.BlasFloat} ilo, ihi, scale = gebal_noalloc!('B', A, _scale) # modifies A and _scale else A, bal = GenericSchur.balance!(A) ilo, ihi, scale = bal.ilo, bal.ihi, bal.D + prow, pcol = bal.prow, bal.pcol end end @@ -134,12 +153,12 @@ function exponential!(A, method::ExpMethodHigham2005, _cache = alloc_mem(A, meth else if ilo > 1 # apply lower permutations in reverse order for j in (ilo - 1):-1:1 - LinearAlgebra.rcswap!(j, bal.prow[j], X) + LinearAlgebra.rcswap!(j, prow[j], X) end end if ihi < n # apply upper permutations in forward order for j in (ihi + 1):n - LinearAlgebra.rcswap!(j, bal.pcol[j - ihi], X) + LinearAlgebra.rcswap!(j, pcol[j - ihi], X) end end end diff --git a/test/qa/Project.toml b/test/qa/Project.toml index 1eb4dc2..2b51c79 100644 --- a/test/qa/Project.toml +++ b/test/qa/Project.toml @@ -6,13 +6,13 @@ SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[sources] -ExponentialUtilities = {path = "../.."} - [compat] Aqua = "0.8" JET = "0.9, 0.10, 0.11" SafeTestsets = "0.1, 1" -SciMLTesting = "1" +SciMLTesting = "1.6" Test = "1" julia = "1.10" + +[sources] +ExponentialUtilities = {path = "../.."} diff --git a/test/qa/qa.jl b/test/qa/qa.jl index e096d72..9679ba6 100644 --- a/test/qa/qa.jl +++ b/test/qa/qa.jl @@ -1,73 +1,34 @@ -using ExponentialUtilities, Aqua, JET, Test - -@testset "Aqua" begin - Aqua.find_persistent_tasks_deps(ExponentialUtilities) - Aqua.test_ambiguities(ExponentialUtilities, recursive = false) - Aqua.test_deps_compat( - ExponentialUtilities, - ignore = [:libblastrampoline_jll] - ) - Aqua.test_piracies(ExponentialUtilities) - Aqua.test_project_extras(ExponentialUtilities) - Aqua.test_stale_deps(ExponentialUtilities) - Aqua.test_unbound_args(ExponentialUtilities) - Aqua.test_undefined_exports(ExponentialUtilities) -end - -# Analyze only ExponentialUtilities' own code. Without this, JET on Julia 1.12 traces -# into LinearAlgebra/Base internals (e.g. `norm(::Vector)` -> `norm_recursive_check`, -# and the broadcast `unalias`/`copyto_unaliased!` path over `Adjoint{T, Union{}}`) and -# reports abstract-interpretation artifacts there that are not under this package's -# control. Scoping to `ExponentialUtilities` keeps full coverage of this package's code -# (it still flags real `may be undefined` findings here) without asserting that all of -# the stdlib is JET-clean. -const JET_TARGET = (ExponentialUtilities,) - -@testset "JET static analysis" begin - @testset "expv" begin - rep = JET.report_call( - expv, (Float64, Matrix{Float64}, Vector{Float64}); target_modules = JET_TARGET - ) - @test length(JET.get_reports(rep)) == 0 - end - - @testset "arnoldi" begin - rep = JET.report_call( - arnoldi, (Matrix{Float64}, Vector{Float64}); target_modules = JET_TARGET - ) - @test length(JET.get_reports(rep)) == 0 - end - - @testset "phi" begin - rep = JET.report_call(phi, (Matrix{Float64}, Int); target_modules = JET_TARGET) - @test length(JET.get_reports(rep)) == 0 - end - - @testset "exponential!" begin - rep = JET.report_call( - ExponentialUtilities.exponential!, (Matrix{Float64},); target_modules = JET_TARGET - ) - @test length(JET.get_reports(rep)) == 0 - end - - @testset "phiv" begin - rep = JET.report_call( - phiv, (Float64, Matrix{Float64}, Vector{Float64}, Int); target_modules = JET_TARGET - ) - @test length(JET.get_reports(rep)) == 0 - end - - @testset "kiops" begin - rep = JET.report_call( - kiops, (Float64, Matrix{Float64}, Vector{Float64}); target_modules = JET_TARGET - ) - @test length(JET.get_reports(rep)) == 0 - end - - @testset "expv_timestep" begin - rep = JET.report_call( - expv_timestep, (Float64, Matrix{Float64}, Vector{Float64}); target_modules = JET_TARGET - ) - @test length(JET.get_reports(rep)) == 0 - end -end +using SciMLTesting, ExponentialUtilities, JET, Test + +run_qa( + ExponentialUtilities; + explicit_imports = true, + aqua_kwargs = (; deps_compat = (; ignore = [:libblastrampoline_jll])), + ei_kwargs = (; + # Names owned elsewhere but reached through LinearAlgebra.BLAS. + all_qualified_accesses_via_owners = (; + ignore = (:BlasFloat, :chkstride1, :libblastrampoline), + ), + # Non-public names of Base / LinearAlgebra(.BLAS/.LAPACK) accessed qualified. + all_qualified_accesses_are_public = (; + ignore = ( + Symbol("@aliasscope"), Symbol("@assume_effects"), + Symbol("@blasfunc"), Symbol("@propagate_inbounds"), + :BlasFloat, :Cartesian, :Const, :Experimental, + :checksquare, :chkfinite, :chklapackerror, :chkstride1, + :gebal!, :gesv!, :libblastrampoline, :rcswap!, :stegr!, + ), + ), + # Non-public names explicitly imported from LinearAlgebra(.BLAS/.LAPACK, + # incl. the Stegr submodule) / ArrayInterface / Base. + all_explicit_imports_are_public = (; + ignore = ( + :BlasInt, :checksquare, :allowed_setindex!, :ismutable, :typename, + Symbol("@blasfunc"), :stegr!, + ), + ), + ), + # Heavy `using LinearAlgebra, SparseArrays, Printf, PrecompileTools` brings ~31 + # names implicitly; making them explicit is a large refactor tracked separately. + ei_broken = (:no_implicit_imports,) +) # SciML/ExponentialUtilities.jl#231