diff --git a/ext/TensorKitGPUArraysExt.jl b/ext/TensorKitGPUArraysExt.jl index 948f9fa75..66e867e8b 100644 --- a/ext/TensorKitGPUArraysExt.jl +++ b/ext/TensorKitGPUArraysExt.jl @@ -195,7 +195,7 @@ struct DeviceGenericTreeTransformer{VO <: AbstractVector{Int}, DA <: DeviceUniqu end # strides of a dense array of shape `sz` -_dense_strides(size::Dims) = (1, Base.front(cumprod(size))...) +_dense_strides(size::Dims) = Base.front((1, cumprod(size)...)) # `permute(Vsrc, p) == Vdst` is enforced when the transformer is built, so the permuted # source shape always matches `sz_dst` and the two views share Cartesian inds. diff --git a/src/auxiliary/caches.jl b/src/auxiliary/caches.jl index bdc07ca08..d6753ead9 100644 --- a/src/auxiliary/caches.jl +++ b/src/auxiliary/caches.jl @@ -87,18 +87,16 @@ function _cached(mod::Module, ex) end fcall = ex.args[1] if Meta.isexpr(fcall, :where) - hasparams = true params = fcall.args[2:end] fcall = fcall.args[1] else - hasparams = false + params = nothing end if Meta.isexpr(fcall, :(::)) - typed = true typeex = fcall.args[2] fcall = fcall.args[1] else - typed = false + typeex = nothing end Meta.isexpr(fcall, :call) || _cached_error("expected a function signature", fcall) fname = fcall.args[1] @@ -117,14 +115,14 @@ function _cached(mod::Module, ex) # actual implenetation, with underscore name _fname = Symbol(:_, basename) _fcall = Expr(:call, _fname, fargs...) - if hasparams + if !isnothing(params) _fcall = Expr(:where, _fcall, params...) end _fex = Expr(:function, _fcall, _fbody) # implementation that chooses the cache style newfcall = fcall - if hasparams + if !isnothing(params) newfcall = Expr(:where, newfcall, params...) end cachestylevar = gensym(:cachestyle) @@ -138,11 +136,11 @@ function _cached(mod::Module, ex) # nocache implementation fnocachecall = Expr(:call, fname, fargs..., :(::$NoCache)) - if hasparams + if !isnothing(params) fnocachecall = Expr(:where, fnocachecall, params...) end fnocachebody = _timeit_expr(misslabel, Expr(:call, _fname, fargnames...)) - if typed + if !isnothing(typeex) T = gensym(:T) fnocachebody = Expr(:block, Expr(:(=), T, typeex), Expr(:(::), fnocachebody, T)) end @@ -151,7 +149,7 @@ function _cached(mod::Module, ex) # tasklocal cache implementation Dvar = gensym(:D) flocalcachecall = Expr(:call, fname, fargs..., :(::$TaskLocalCache{$Dvar})) - if hasparams + if !isnothing(params) flocalcachecall = Expr(:where, flocalcachecall, params..., Dvar) else flocalcachecall = Expr(:where, flocalcachecall, Dvar) @@ -172,7 +170,7 @@ function _cached(mod::Module, ex) end missex = _timeit_expr(misslabel, Expr(:call, _fname, fargnames...)) getvalex = _timeit_expr(lookuplabel, :(get!(() -> $missex, $cachevar, $key))) - if typed + if !isnothing(typeex) T = gensym(:T) flocalcachebody = Expr( :block, @@ -193,11 +191,11 @@ function _cached(mod::Module, ex) # # global cache implementation fglobalcachecall = Expr(:call, fname, fargs..., :(::$GlobalLRUCache)) - if hasparams + if !isnothing(params) fglobalcachecall = Expr(:where, fglobalcachecall, params...) end getglobalcachex = Expr(:(=), cachevar, globalcachename) - if typed + if !isnothing(typeex) T = gensym(:T) fglobalcachebody = Expr( :block, diff --git a/src/auxiliary/iterators.jl b/src/auxiliary/iterators.jl index 211c0dd93..6a4be06b7 100644 --- a/src/auxiliary/iterators.jl +++ b/src/auxiliary/iterators.jl @@ -4,7 +4,7 @@ struct OneOrNoneIterator{T} end function Base.iterate(it::OneOrNoneIterator, state = true) - if state && it.cond + if isone(state) && it.cond return (it.first, false) else return nothing diff --git a/src/factorizations/matrixalgebrakit.jl b/src/factorizations/matrixalgebrakit.jl index 5974a60de..17b8cb660 100644 --- a/src/factorizations/matrixalgebrakit.jl +++ b/src/factorizations/matrixalgebrakit.jl @@ -103,7 +103,6 @@ MAK.zero!(t::AbstractTensorMap) = zerovector!(t) for f in [ :lq_full, :lq_compact, :lq_null, :qr_full, :qr_compact, :qr_null, - :schur_full, :schur_vals, :eig_full, :eig_vals, :eig_trunc, :eig_trunc_no_error, :eigh_full, :eigh_vals, :eigh_trunc, :eigh_trunc_no_error, :svd_full, :svd_compact, :svd_trunc, :svd_trunc_no_error, :svd_vals, diff --git a/src/factorizations/pullbacks.jl b/src/factorizations/pullbacks.jl index fe2b186b6..a568a6316 100644 --- a/src/factorizations/pullbacks.jl +++ b/src/factorizations/pullbacks.jl @@ -1,5 +1,5 @@ nothing_or_block(x, c) = isnothing(x) ? x : block(x, c) -nothing_or_block(x::Diagonal, c) = block(MAK.diagonal(parent(x)), c) +nothing_or_block(x::Diagonal{T, <:SectorVector{T}}, c) where {T} = block(MAK.diagonal(parent(x)), c) for pullback! in ( :qr_pullback!, :lq_pullback!, :left_polar_pullback!, :right_polar_pullback!, diff --git a/src/fusiontrees/braiding_manipulations.jl b/src/fusiontrees/braiding_manipulations.jl index ce08f510c..368e326a7 100644 --- a/src/fusiontrees/braiding_manipulations.jl +++ b/src/fusiontrees/braiding_manipulations.jl @@ -332,7 +332,7 @@ end if N₂ == 0 return dst => U else - dst, U_tmp = repartition(dst, N₁) + dst, U_tmp = repartition(dst::FusionTreeBlock{I}, N₁) U = U_tmp * U return dst => U end diff --git a/src/fusiontrees/duality_manipulations.jl b/src/fusiontrees/duality_manipulations.jl index c595afdc2..e399f87ab 100644 --- a/src/fusiontrees/duality_manipulations.jl +++ b/src/fusiontrees/duality_manipulations.jl @@ -657,7 +657,7 @@ function planar_trace(f::FusionTree, (q₁, q₂)::Index2Tuple) # We thus handle the total trace recursively, by first looking for and # tracing away neighbouring pairs. k = 1 - local i, j + i = j = 0 while k <= length(q₁) if mod1(q₁[k] + 1, length(f)) == q₂[k] i = q₁[k] diff --git a/src/fusiontrees/fusiontrees.jl b/src/fusiontrees/fusiontrees.jl index 7b97223ec..f20445f36 100644 --- a/src/fusiontrees/fusiontrees.jl +++ b/src/fusiontrees/fusiontrees.jl @@ -338,8 +338,7 @@ function fusiontensor((f₁, f₂)::FusionTreePair) d1 = TupleTools.front(sz1) d2 = TupleTools.front(sz2) return reshape( - reshape(F₁, TupleTools.prod(d1), sz1[end]) * - reshape(F₂, TupleTools.prod(d2), sz2[end])', (d1..., d2...) + reshape(F₁, :, sz1[end]) * reshape(F₂, :, sz2[end])', (d1..., d2...) ) end fusiontensor(src::FusionTreeBlock) = sum(fusiontensor, fusiontrees(src)) diff --git a/src/planar/macros.jl b/src/planar/macros.jl index a14695812..895917efc 100644 --- a/src/planar/macros.jl +++ b/src/planar/macros.jl @@ -56,7 +56,7 @@ function planarparser(planarexpr, kwargs...) if name == :order isexpr(val, :tuple) || throw(ArgumentError("Invalid use of `order`, should be `order=(...,)`")) - indexorder = map(normalizeindex, val.args) + indexorder = map(TO.normalizeindex, val.args) parser.contractiontreebuilder = network -> TO.indexordertree( network, indexorder ) diff --git a/src/planar/preprocessors.jl b/src/planar/preprocessors.jl index 9e8363205..77c614ae1 100644 --- a/src/planar/preprocessors.jl +++ b/src/planar/preprocessors.jl @@ -180,22 +180,18 @@ function _construct_braidingtensors!(ex, preargs, indexmap, non_braiding) # ex i i1a, i2a, = rightind end - foundV1, foundV2 = false, false + V1 = V2 = nothing if haskey(indexmap, i1a) V1 = indexmap[i1a] - foundV1 = true elseif haskey(indexmap, i1b) V1 = Expr(:call, :dual, indexmap[i1b]) - foundV1 = true end if haskey(indexmap, i2a) V2 = indexmap[i2a] - foundV2 = true elseif haskey(indexmap, i2b) V2 = Expr(:call, :dual, indexmap[i2b]) - foundV2 = true end - if foundV1 && foundV2 + if !isnothing(V1) && !isnothing(V2) s = gensym(:τ) scalarex = Expr( :call, :promote_type, @@ -609,7 +605,7 @@ function _extract_contraction_pairs(rhs, lhs, pre, temporaries) newarg = _extract_contraction_pairs(rhs.args[3], lhs, pre, temporaries) return Expr(:call, :\, rhs.args[2], newarg) else - throw(ArgumentError("unknown tensor expression $ex")) + throw(ArgumentError("unknown tensor expression $rhs")) end end diff --git a/src/pullbacks/tensoroperations.jl b/src/pullbacks/tensoroperations.jl index 64507861f..4e784af5b 100644 --- a/src/pullbacks/tensoroperations.jl +++ b/src/pullbacks/tensoroperations.jl @@ -47,8 +47,11 @@ function blas_contract_pullback_ΔB!( return nothing end +# Only the primal `A` is annotated: it fixes the element and space types of the workspace `E` +# below, which `twist!` needs. The tangents stay untyped because their representation differs +# per AD backend (Mooncake passes a tangent object, Enzyme the `.dval` shadow). function trace_permute_pullback_ΔA!( - ΔA, ΔC, A, p, q, α, backend + ΔA, ΔC, A::AbstractTensorMap, p, q, α, backend ) ip = invperm((linearize(p)..., q[1]..., q[2]...)) pdA = TO.repartition(ip, numout(A)) diff --git a/src/tensors/abstracttensor.jl b/src/tensors/abstracttensor.jl index ff2f44f10..d8ca7354f 100644 --- a/src/tensors/abstracttensor.jl +++ b/src/tensors/abstracttensor.jl @@ -238,7 +238,7 @@ See also [`numin`](@ref) and [`numind`](@ref). """ numout numout(x) = numout(typeof(x)) -numout(T::Type) = throw(MethodError(numout, T)) # avoid infinite recursion +numout(T::Type) = throw(MethodError(numout, (T,))) # avoid infinite recursion numout(::Type{<:AbstractTensorMap{T, S, N₁}}) where {T, S, N₁} = N₁ @doc """ @@ -252,7 +252,7 @@ See also [`numout`](@ref) and [`numind`](@ref). """ numin numin(x) = numin(typeof(x)) -numin(T::Type) = throw(MethodError(numin, T)) # avoid infinite recursion +numin(T::Type) = throw(MethodError(numin, (T,))) # avoid infinite recursion numin(::Type{<:AbstractTensorMap{T, S, N₁, N₂}}) where {T, S, N₁, N₂} = N₂ """ @@ -314,7 +314,6 @@ InnerProductStyle(t::AbstractTensorMap) = InnerProductStyle(typeof(t)) numout(t::AbstractTensorMap) = numout(typeof(t)) numin(t::AbstractTensorMap) = numin(typeof(t)) -numind(t::AbstractTensorMap) = numind(typeof(t)) # tensor characteristics: data structure and properties #------------------------------------------------------ diff --git a/src/tensors/adjoint.jl b/src/tensors/adjoint.jl index 820e87375..6aa5810e4 100644 --- a/src/tensors/adjoint.jl +++ b/src/tensors/adjoint.jl @@ -13,6 +13,7 @@ end Base.parent(t::AdjointTensorMap) = t.parent parenttype(t::AdjointTensorMap) = parenttype(typeof(t)) parenttype(::Type{AdjointTensorMap{T, S, N₁, N₂, TT}}) where {T, S, N₁, N₂, TT} = TT +parenttype(::Type{Union{}}) = throw(MethodError(parenttype, (Union{},))) # Constructor: construct from taking adjoint of a tensor Base.adjoint(t::AdjointTensorMap) = parent(t) diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index 61a4a6a12..f6623c197 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -219,7 +219,7 @@ See also [`permute`](@ref) for creating a new tensor. α::Number = One(), β::Number = Zero(), backend::AbstractBackend = TO.DefaultBackend(), allocator = TO.DefaultAllocator() ) - @boundscheck spacecheck_transform(permute, tdst, tsrc, p) + @boundscheck spacecheck_transform(permute, space(tdst), space(tsrc), p) @timeit_debug GLOBAL_TIMER "permute!/braid!" begin tdst′, tsrc′, p′, _, conjsrc′, α′, β′ = unwrap_adjoints(tdst, tsrc, p, nothing, false, α, β) @inbounds _braid!(tdst′, tsrc′, p′, conjsrc′, allind(tsrc′), α′, β′, backend, allocator) @@ -309,7 +309,7 @@ See also [`braid`](@ref) for creating a new tensor. α::Number = One(), β::Number = Zero(), backend::AbstractBackend = TO.DefaultBackend(), allocator = TO.DefaultAllocator() ) - @boundscheck spacecheck_transform(braid, tdst, tsrc, p, levels) + @boundscheck spacecheck_transform(braid, space(tdst), space(tsrc), p, levels) @timeit_debug GLOBAL_TIMER "permute!/braid!" begin tdst′, tsrc′, p′, levels′, conjsrc′, α′, β′ = unwrap_adjoints(tdst, tsrc, p, levels, false, α, β) @inbounds _braid!(tdst′, tsrc′, p′, conjsrc′, levels′, α′, β′, backend, allocator) @@ -382,7 +382,7 @@ end α::Number = One(), β::Number = Zero(), backend::AbstractBackend = TO.DefaultBackend(), allocator = TO.DefaultAllocator() ) - @boundscheck spacecheck_transform(transpose, tdst, tsrc, p) + @boundscheck spacecheck_transform(transpose, space(tdst), space(tsrc), p) @timeit_debug GLOBAL_TIMER "transpose!" begin tdst′, tsrc′, p′, _, conjsrc′, α′, β′ = unwrap_adjoints(tdst, tsrc, p, nothing, false, α, β) @inbounds _transpose!(tdst′, tsrc′, p′, conjsrc′, α′, β′, backend, allocator) @@ -505,8 +505,6 @@ for (operation, manipulation) in ( end end -spacecheck_transform(f, tdst::AbstractTensorMap, tsrc::AbstractTensorMap, args...) = - spacecheck_transform(f, space(tdst), space(tsrc), args...) @noinline function spacecheck_transform(f, Vdst::TensorMapSpace, Vsrc::TensorMapSpace, p::Index2Tuple) check_spacetype(Vdst, Vsrc) f(Vsrc, p) == Vdst || @@ -604,9 +602,10 @@ end @propagate_inbounds function _braid!( tdst, tsrc, p::Index2Tuple, conjsrc::Bool, levels::IndexTuple, α, β, backend, allocator ) - @boundscheck spacecheck_transform(permute, tdst, tsrc, p, conjsrc) + Vdst, Vsrc = space(tdst), space(tsrc) + @boundscheck spacecheck_transform(permute, Vdst, Vsrc, p, conjsrc) all(has_array_view, (tdst, tsrc)) && return _dense_transform!(tdst, tsrc, p, conjsrc, α, β, backend, allocator) - transformer = treebraider(tdst, tsrc, p, conjsrc, levels) + transformer = treebraider(storagetype(tdst), Vdst, Vsrc, p, conjsrc, levels) return add_transform!(tdst, tsrc, p, conjsrc, transformer, α, β, backend, allocator) end @@ -614,9 +613,10 @@ end @propagate_inbounds function _transpose!( tdst, tsrc, p::Index2Tuple, conjsrc::Bool, α, β, backend, allocator ) - @boundscheck spacecheck_transform(permute, tdst, tsrc, p, conjsrc) + Vdst, Vsrc = space(tdst), space(tsrc) + @boundscheck spacecheck_transform(permute, Vdst, Vsrc, p, conjsrc) all(has_array_view, (tdst, tsrc)) && return _dense_transform!(tdst, tsrc, p, conjsrc, α, β, backend, allocator) - transformer = treetransposer(tdst, tsrc, p, conjsrc) + transformer = treetransposer(storagetype(tdst), Vdst, Vsrc, p, conjsrc) return add_transform!(tdst, tsrc, p, conjsrc, transformer, α, β, backend, allocator) end @@ -737,7 +737,7 @@ function _recouple!( buffer::CPUStorage{Complex{R}}, buffer_dst::StridedView, buffer_src::StridedView, U::Matrix{R} ) where {R <: LinearAlgebra.BlasReal} rbuffer = reinterpret(R, buffer) - LinearAlgebra.BLAS.gemm!('N', 'T', one(R), _realview(rbuffer, buffer_src), U, zero(R), _realview(rbuffer, buffer_dst)) + LinearAlgebra.BLAS.gemm!('N', 'T', true, _realview(rbuffer, buffer_src), U, false, _realview(rbuffer, buffer_dst)) return nothing end # real view of the complex column-major view `b` into the reinterpreted buffer `rbuffer` diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index a0893749d..16d595ba7 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -586,7 +586,7 @@ tdst[sub_axes...] .= tsrc[sub_axes...] absorb(tdst::AbstractTensorMap, tsrc::AbstractTensorMap) = absorb!(copy(tdst), tsrc) function absorb!(tdst::AbstractTensorMap, tsrc::AbstractTensorMap) numin(tdst) == numin(tsrc) && numout(tdst) == numout(tsrc) || - throw(DimensionError("Incompatible number of indices for source and destination")) + throw(IndexError("Incompatible number of indices for source and destination")) S = check_spacetype(tdst, tsrc) dom = mapreduce(infimum, ⊗, domain(tdst), domain(tsrc); init = one(S)) cod = mapreduce(infimum, ⊗, codomain(tdst), codomain(tsrc); init = one(S)) diff --git a/src/tensors/tensoroperations.jl b/src/tensors/tensoroperations.jl index 14e803c47..e11a1edf0 100644 --- a/src/tensors/tensoroperations.jl +++ b/src/tensors/tensoroperations.jl @@ -236,7 +236,10 @@ function trace_permute!( return tdst end -function _trace_permute!(::UniqueFusion, tdst, tsrc, (p₁, p₂), (q₁, q₂), α, β, backend) +function _trace_permute!( + ::UniqueFusion, tdst::AbstractTensorMap, tsrc::AbstractTensorMap, + (p₁, p₂), (q₁, q₂), α, β, backend + ) scale!(tdst, β) r₁, r₂ = (p₁..., q₁...), (p₂..., q₂...) N₁, N₂ = length(p₁), length(p₂) @@ -261,7 +264,10 @@ function _trace_permute!(::UniqueFusion, tdst, tsrc, (p₁, p₂), (q₁, q₂), return tdst end -function _trace_permute!(::FusionStyle, tdst, tsrc, (p₁, p₂), (q₁, q₂), α, β, backend) +function _trace_permute!( + ::FusionStyle, tdst::AbstractTensorMap, tsrc::AbstractTensorMap, + (p₁, p₂), (q₁, q₂), α, β, backend + ) scale!(tdst, β) r₁, r₂ = (p₁..., q₁...), (p₂..., q₂...) N₁, N₂ = length(p₁), length(p₂) diff --git a/src/tensors/treetransformers.jl b/src/tensors/treetransformers.jl index 990e50d2d..b274c6f8c 100644 --- a/src/tensors/treetransformers.jl +++ b/src/tensors/treetransformers.jl @@ -68,7 +68,7 @@ struct GenericTreeTransformer{T, N} <: TreeTransformer structure_src::Vector{StridedStructure{N}} end -function UniqueTreeTransformer(::Type{A}, transform, p, Vdst, Vsrc, conjsrc::Bool) where {A} +function UniqueTreeTransformer(::Type{A}, transform, p, Vdst::HomSpace, Vsrc::HomSpace, conjsrc::Bool) where {A} t₀ = Base.time() spacecheck_transform(permute, Vdst, Vsrc, p, conjsrc) @@ -93,7 +93,7 @@ function UniqueTreeTransformer(::Type{A}, transform, p, Vdst, Vsrc, conjsrc::Boo return transformer end -function GenericTreeTransformer(::Type{A}, transform, p, Vdst, Vsrc, conjsrc::Bool) where {A} +function GenericTreeTransformer(::Type{A}, transform, p, Vdst::HomSpace, Vsrc::HomSpace, conjsrc::Bool) where {A} t₀ = Base.time() spacecheck_transform(permute, Vdst, Vsrc, p, conjsrc) # the fusion blocks that are transformed are those of the adjoint space for a conjugated source @@ -183,11 +183,6 @@ function TreeTransformer( end # braid is special because it has levels -function treebraider( - tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple, conjsrc::Bool, levels::IndexTuple - ) - return treebraider(storagetype(tdst), space(tdst), space(tsrc), p, conjsrc, levels) -end @cached function treebraider( A::Type{TA}, Vdst::TensorMapSpace, Vsrc::TensorMapSpace, p::Index2Tuple, conjsrc::Bool, levels::IndexTuple )::treetransformertype(A, Vdst, Vsrc) where {TA} @@ -199,9 +194,6 @@ end return TreeTransformer(A, fusiontreebraider, p, Vdst, Vsrc, conjsrc) end -function treetransposer(tdst::AbstractTensorMap, tsrc::AbstractTensorMap, p::Index2Tuple, conjsrc::Bool) - return treetransposer(storagetype(tdst), space(tdst), space(tsrc), p, conjsrc) -end @cached function treetransposer( A::Type{TA}, Vdst::TensorMapSpace, Vsrc::TensorMapSpace, p::Index2Tuple, conjsrc::Bool )::treetransformertype(A, Vdst, Vsrc) where {TA} diff --git a/test/Project.toml b/test/Project.toml index ce51b70a3..a63d0b3e9 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -39,7 +39,7 @@ AllocCheck = "0.2" ChainRulesTestUtils = "1" Combinatorics = "1" cuTENSOR = "6" -JET = "0.9, 0.10, 0.11" +JET = "0.9, 0.12" ParallelTestRunner = "2" Test = "1" TestExtras = "0.2,0.3" diff --git a/test/README.md b/test/README.md index d978612f6..c5d7f49f2 100644 --- a/test/README.md +++ b/test/README.md @@ -1,8 +1,8 @@ # TensorKit.jl test suite -Tests use [ParallelTestRunner.jl](https://github.com/vchuravy/ParallelTestRunner.jl) for parallel -execution. Each test file runs in its own worker process. Shared helpers are loaded automatically -via `init_code` — test files do not need to include `setup.jl` themselves. +Tests use [ParallelTestRunner.jl](https://github.com/vchuravy/ParallelTestRunner.jl) for parallel execution. +Each test file runs in its own worker process. +Shared helpers are loaded automatically via `init_code` — test files do not need to include `setup.jl` themselves. ## Running tests @@ -37,17 +37,21 @@ julia --project=test test/runtests.jl --jobs=4 |-------|----------| | `symmetries` | Spaces and fusion trees | | `tensors` | Core tensor operations, factorizations, planar tensors, diagonal tensors | -| `other` | Aqua code-quality checks, bug-fix regressions | +| `other` | Aqua code-quality checks, JET static analysis, bug-fix regressions | | `chainrules` | ChainRulesCore AD tests | | `mooncake` | Mooncake AD tests | | `cuda` | CUDA GPU tests (only run when a functional GPU is present) | +JET only functions on the Julia versions it supports, which it reports through `JET.JET_AVAILABLE`. +`runtests.jl` drops the JET-dependent files from the testsuite when that is `false`: +`other/jet` (whole-package analysis, which additionally requires the JET 0.12 generation) +and `mooncake/tangent` (reaches JET through Mooncake's extension). + ## Fast mode (`--fast`) Skips `chainrules` and `mooncake` groups entirely, and reduces coverage in the remaining tests: -- **Sector types**: tests only `Z2Irrep`, `SU2Irrep`, `FermionParity ⊠ U1Irrep ⊠ SU2Irrep`, - and `FibonacciAnyon` (instead of the full `sectorlist`) +- **Sector types**: tests only `Z2Irrep`, `SU2Irrep`, `FermionParity ⊠ U1Irrep ⊠ SU2Irrep`, and `FibonacciAnyon` (instead of the full `sectorlist`) - **Space lists**: tests only `(Vtr, Vℤ₂, VSU₂)` (trivial, abelian, non-abelian) - **Scalar types**: tests only `Float64` and `ComplexF64` (instead of all integer/float variants) diff --git a/test/mooncake/tangent.jl b/test/mooncake/tangent.jl index 7b7c9ab4d..3c0c14fd2 100644 --- a/test/mooncake/tangent.jl +++ b/test/mooncake/tangent.jl @@ -10,8 +10,12 @@ rng = Random.default_rng() spacelist = ad_spacelist(fast_tests) eltypes = (Float64, ComplexF64) +# `test_data` reaches JET through Mooncake's extension, so it needs a functional JET; this is also +# gated in runtests.jl. JET < 0.12 does not define `JET_AVAILABLE`. +jet_available = !isdefined(JET, :JET_AVAILABLE) || JET.JET_AVAILABLE + # only run on Linux since allocation tests are broken on other versions -Sys.islinux() && @timedtestset "Mooncake - Tangent type: $(TensorKit.type_repr(sectortype(eltype(V)))) ($T)" for V in spacelist, T in eltypes +jet_available && Sys.islinux() && @timedtestset "Mooncake - Tangent type: $(TensorKit.type_repr(sectortype(eltype(V)))) ($T)" for V in spacelist, T in eltypes A = randn(T, V[1] ⊗ V[2] ⊗ V[3] ← (V[4] ⊗ V[5])') Mooncake.TestUtils.test_data(rng, A) diff --git a/test/other/jet.jl b/test/other/jet.jl new file mode 100644 index 000000000..d611625e5 --- /dev/null +++ b/test/other/jet.jl @@ -0,0 +1,20 @@ +using TensorKit +using Test +using JET + +# Reports that are not TensorKit's to fix. Each entry needs an upstream issue. +const IGNORED = ( + # `local variable kwargs may be undefined` inside `GenericTreeTransformer`, coming entirely + # from Base's `@debug` expansion (`local msg, kwargs` bound in a short-circuit guard) rather + # than from any TensorKit code: https://github.com/aviatesk/JET.jl/issues/860 + # `AnyFrameMethod` rather than `LastFrameMethod`: the `@debug` sits inside a `taskforeach` + # closure, so the innermost frame is a gensym'd closure name rather than the constructor. + JET.AnyFrameMethod(:GenericTreeTransformer), +) + +# also gated in runtests.jl; JET < 0.12 does not define `JET_AVAILABLE` +if isdefined(JET, :JET_AVAILABLE) && JET.JET_AVAILABLE + JET.test_package(TensorKit; target_modules = (TensorKit,), ignored_modules = IGNORED) +else + @info "Full JET functionality is unavailable on Julia $VERSION; skipping JET analysis" +end diff --git a/test/runtests.jl b/test/runtests.jl index c5645cd66..ae8446f2a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,6 +13,17 @@ CUDA.functional() || filter!(!startswith("cuda") ∘ first, testsuite) using AMDGPU AMDGPU.functional() || filter!(!startswith("amd") ∘ first, testsuite) +# JET tests: JET ≥ 0.12 (Julia 1.12+) signals through `JET_AVAILABLE` whether it is functional, +# and loads empty stubs when it is not. Older JET versions, still needed on Julia < 1.12, do not +# define it, hence the `isdefined` check instead of `using JET: JET_AVAILABLE`. +using JET: JET +const jet_new_generation = isdefined(JET, :JET_AVAILABLE) +const jet_available = !jet_new_generation || JET.JET_AVAILABLE +# whole-package analysis is pinned to the JET 0.12 generation, so its reports need not be curated for several JET/Julia combinations +(jet_new_generation && jet_available) || delete!(testsuite, "other/jet") +# Mooncake's tangent tests reach JET through Mooncake's extension: any functional JET works, the empty stubs do not +jet_available || delete!(testsuite, "mooncake/tangent") + # On Buildkite (GPU CI runner): only run CUDA and AMDGPU tests if get(ENV, "BUILDKITE", "false") == "true" f(str) = startswith(first(str), "cuda") || startswith(first(str), "amd")