From 3cceadd2207e61936f1adba04f29638629f98e8e Mon Sep 17 00:00:00 2001 From: Lukas Devos Date: Mon, 5 Jan 2026 17:03:23 +0100 Subject: [PATCH 01/12] change blocktype of TensorMap to `StridedView` --- src/spaces/homspace.jl | 122 +++++++++++++++++++++++++++++++++++++++++ src/tensors/tensor.jl | 33 ++++++----- 2 files changed, 142 insertions(+), 13 deletions(-) diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index 8460f7578..103074c7c 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -353,3 +353,125 @@ function removeunit(P::HomSpace, ::Val{i}) where {i} return codomain(P) ← removeunit(domain(P), Val(i - numout(P))) end end + +# Block and fusion tree ranges: structure information for building tensors +#-------------------------------------------------------------------------- + +# sizes, strides, offset +const StridedStructure{N} = Tuple{NTuple{N, Int}, NTuple{N, Int}, Int} + +struct FusionBlockStructure{I, N, F₁, F₂} + totaldim::Int + blockstructure::SectorDict{I, StridedStructure{2}} + fusiontreelist::Vector{Tuple{F₁, F₂}} + fusiontreestructure::Vector{StridedStructure{N}} + fusiontreeindices::FusionTreeDict{Tuple{F₁, F₂}, Int} +end + +function fusionblockstructuretype(W::HomSpace) + N₁ = length(codomain(W)) + N₂ = length(domain(W)) + N = N₁ + N₂ + I = sectortype(W) + F₁ = fusiontreetype(I, N₁) + F₂ = fusiontreetype(I, N₂) + return FusionBlockStructure{I, N, F₁, F₂} +end + +@cached function fusionblockstructure(W::HomSpace)::fusionblockstructuretype(W) + codom = codomain(W) + dom = domain(W) + N₁ = length(codom) + N₂ = length(dom) + I = sectortype(W) + F₁ = fusiontreetype(I, N₁) + F₂ = fusiontreetype(I, N₂) + + # output structure + blockstructure = SectorDict{I, StridedStructure{2}}() # size, strides, offset + fusiontreelist = Vector{Tuple{F₁, F₂}}() + fusiontreestructure = Vector{StridedStructure{N₁ + N₂}}() # size, strides, offset + + # temporary data structures + splittingtrees = Vector{F₁}() + splittingstructure = Vector{Tuple{Int, Int}}() + + # main computational routine + blockoffset = 0 + for c in blocksectors(W) + empty!(splittingtrees) + empty!(splittingstructure) + + offset₁ = 0 + for f₁ in fusiontrees(codom, c) + push!(splittingtrees, f₁) + d₁ = dim(codom, f₁.uncoupled) + push!(splittingstructure, (offset₁, d₁)) + offset₁ += d₁ + end + blockdim₁ = offset₁ + strides = (1, blockdim₁) + + offset₂ = 0 + for f₂ in fusiontrees(dom, c) + s₂ = f₂.uncoupled + d₂ = dim(dom, s₂) + for (f₁, (offset₁, d₁)) in zip(splittingtrees, splittingstructure) + push!(fusiontreelist, (f₁, f₂)) + totaloffset = blockoffset + offset₂ * blockdim₁ + offset₁ + subsz = (dims(codom, f₁.uncoupled)..., dims(dom, f₂.uncoupled)...) + @assert !any(isequal(0), subsz) + substr = _subblock_strides(subsz, (d₁, d₂), strides) + push!(fusiontreestructure, (subsz, substr, totaloffset)) + end + offset₂ += d₂ + end + blockdim₂ = offset₂ + blocksize = (blockdim₁, blockdim₂) + blocklength = blockdim₁ * blockdim₂ + blockrange = (blockoffset + 1):(blockoffset + blocklength) + blockstructure[c] = (blocksize, strides, blockoffset) + blockoffset = last(blockrange) + end + + fusiontreeindices = sizehint!( + FusionTreeDict{Tuple{F₁, F₂}, Int}(), length(fusiontreelist) + ) + for (i, f₁₂) in enumerate(fusiontreelist) + fusiontreeindices[f₁₂] = i + end + totaldim = blockoffset + structure = FusionBlockStructure( + totaldim, blockstructure, fusiontreelist, fusiontreestructure, fusiontreeindices + ) + return structure +end + +function _subblock_strides(subsz, sz, str) + sz_simplify = Strided.StridedViews._simplifydims(sz, str) + strides = Strided.StridedViews._computereshapestrides(subsz, sz_simplify...) + isnothing(strides) && + throw(ArgumentError("unexpected error in computing subblock strides")) + return strides +end + +function CacheStyle(::typeof(fusionblockstructure), W::HomSpace) + return GlobalLRUCache() +end + +# Diagonal ranges +#---------------- +# TODO: is this something we want to cache? +function diagonalblockstructure(W::HomSpace) + ((numin(W) == numout(W) == 1) && domain(W) == codomain(W)) || + throw(SpaceMismatch("Diagonal only support on V←V with a single space V")) + structure = SectorDict{sectortype(W), UnitRange{Int}}() # range + offset = 0 + dom = domain(W)[1] + for c in blocksectors(W) + d = dim(dom, c) + structure[c] = offset .+ (1:d) + offset += d + end + return structure +end diff --git a/src/tensors/tensor.jl b/src/tensors/tensor.jl index ed5682367..47bdfbd46 100644 --- a/src/tensors/tensor.jl +++ b/src/tensors/tensor.jl @@ -467,31 +467,38 @@ block(t::TensorMap, c::Sector) = blocks(t)[c] blocks(t::TensorMap) = BlockIterator(t, blockstructure(space(t))) -function blocktype(::Type{TensorMap{T, S, N₁, N₂, A}}) where {T, S, N₁, N₂, A <: Vector{T}} - return Base.ReshapedArray{T, 2, SubArray{T, 1, A, Tuple{UnitRange{Int}}, true}, Tuple{}} +function blocktype(::Type{TT}) where {TT <: TensorMap} + A = storagetype(TT) + T = eltype(A) + @static if isdefined(Core, :Memory) # StridedViews normalizes parent types! + if A <: Vector{T} + A = GenericMemory{T} + end + end + return StridedView{T, 2, A, typeof(identity)} end function Base.iterate(iter::BlockIterator{<:TensorMap}, state...) next = iterate(pairs(iter.structure), state...) isnothing(next) && return next - (c, (sz, r)), newstate = next - return c => reshape(view(iter.t.data, r), sz), newstate + (c, (sz, str, offset)), newstate = next + return c => StridedView(iter.t.data, sz, str, offset), newstate end function Base.getindex(iter::BlockIterator{<:TensorMap}, c::Sector) sectortype(iter.t) === typeof(c) || throw(SectorMismatch()) - found, token = gettoken(iter.structure, c) - if found - (d₁, d₂), r = gettokenvalue(iter.structure, token) - return reshape(view(iter.t.data, r), (d₁, d₂)) - else - # if c is not a key, at least one of the two dimensions will be zero: + (d₁, d₂), (s₁, s₂), offset = get(iter.structure, c) do + # is c is not a key, at least one of the two dimensions will be zero: # it then does not matter where exactly we construct a view in `t.data`, # as it will have length zero anyway - d₁ = blockdim(codomain(iter.t), c) - d₂ = blockdim(domain(iter.t), c) - return reshape(view(iter.t.data, 1:(d₁ * d₂)), (d₁, d₂)) + d₁′ = blockdim(codomain(iter.t), c) + d₂′ = blockdim(domain(iter.t), c) + s₁ = 1 + s₂ = 0 + offset = 0 + return (d₁′, d₂′), (s₁, s₂), offset end + return StridedView(iter.t.data, (d₁, d₂), (s₁, s₂), offset) end # Getting and setting the data at the subblock level From 9871134debd493db10d14cec45c8d1ff8dcef8e0 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 6 Aug 2026 15:12:33 +0200 Subject: [PATCH 02/12] Use Strided branch with BigFloat fix --- Project.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Project.toml b/Project.toml index 5edabec65..ebb002786 100644 --- a/Project.toml +++ b/Project.toml @@ -66,3 +66,6 @@ TensorOperations = "5.5.2, 5.6" TupleTools = "1.5" VectorInterface = "0.6" julia = "1.10" + +[sources] +Strided = {url = "https://github.com/QuantumKitHub/Strided.jl", rev = "ksh/big"} From 0bba2f3cb38bc70f8484e1b557b0de4ffe1af77b Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 6 Aug 2026 15:12:47 +0200 Subject: [PATCH 03/12] Don't exactly compare two floats --- test/tensors/diagonal.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tensors/diagonal.jl b/test/tensors/diagonal.jl index 8a4c46161..c2dfcd369 100644 --- a/test/tensors/diagonal.jl +++ b/test/tensors/diagonal.jl @@ -151,8 +151,8 @@ diagspacelist = ( @timedtestset "Trace, Multiplication and inverse" begin t1 = DiagonalTensorMap(rand(Float64, reduceddim(V)), V) t2 = DiagonalTensorMap(rand(ComplexF64, reduceddim(V)), V) - @test tr(TensorMap(t1)) == @constinferred tr(t1) - @test tr(TensorMap(t2)) == @constinferred tr(t2) + @test tr(TensorMap(t1)) ≈ @constinferred tr(t1) + @test tr(TensorMap(t2)) ≈ @constinferred tr(t2) @test TensorMap(@constinferred t1 * t2) ≈ TensorMap(t1) * TensorMap(t2) @test TensorMap(@constinferred t1 \ t2) ≈ TensorMap(t1) \ TensorMap(t2) @test TensorMap(@constinferred t1 / t2) ≈ TensorMap(t1) / TensorMap(t2) From 5ace7eaab9e05cf356f6798df3549d23ae7530cb Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 6 Aug 2026 15:13:05 +0200 Subject: [PATCH 04/12] Fix block iteration for StridedView --- src/tensors/tensor.jl | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/tensors/tensor.jl b/src/tensors/tensor.jl index 47bdfbd46..fef2c97e3 100644 --- a/src/tensors/tensor.jl +++ b/src/tensors/tensor.jl @@ -472,7 +472,7 @@ function blocktype(::Type{TT}) where {TT <: TensorMap} T = eltype(A) @static if isdefined(Core, :Memory) # StridedViews normalizes parent types! if A <: Vector{T} - A = GenericMemory{T} + A = Memory{T} end end return StridedView{T, 2, A, typeof(identity)} @@ -481,24 +481,25 @@ end function Base.iterate(iter::BlockIterator{<:TensorMap}, state...) next = iterate(pairs(iter.structure), state...) isnothing(next) && return next - (c, (sz, str, offset)), newstate = next - return c => StridedView(iter.t.data, sz, str, offset), newstate + (c, ((d₁, d₂), r)), newstate = next + return c => StridedView(iter.t.data, (d₁, d₂), (1, d₁), first(r) - 1), newstate end function Base.getindex(iter::BlockIterator{<:TensorMap}, c::Sector) sectortype(iter.t) === typeof(c) || throw(SectorMismatch()) - (d₁, d₂), (s₁, s₂), offset = get(iter.structure, c) do - # is c is not a key, at least one of the two dimensions will be zero: + found, token = gettoken(iter.structure, c) + if found + (d₁, d₂), r = gettokenvalue(iter.structure, token) + offset = first(r) - 1 + else + # if c is not a key, at least one of the two dimensions will be zero: # it then does not matter where exactly we construct a view in `t.data`, # as it will have length zero anyway - d₁′ = blockdim(codomain(iter.t), c) - d₂′ = blockdim(domain(iter.t), c) - s₁ = 1 - s₂ = 0 + d₁ = blockdim(codomain(iter.t), c) + d₂ = blockdim(domain(iter.t), c) offset = 0 - return (d₁′, d₂′), (s₁, s₂), offset end - return StridedView(iter.t.data, (d₁, d₂), (s₁, s₂), offset) + return StridedView(iter.t.data, (d₁, d₂), (1, d₁), offset) end # Getting and setting the data at the subblock level From f47cc1184971d49e9ec37d80c64f6d2ead33c0c3 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 6 Aug 2026 15:13:32 +0200 Subject: [PATCH 05/12] Don't use parent(block(t)) w StridedView --- src/tensors/vectorinterface.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tensors/vectorinterface.jl b/src/tensors/vectorinterface.jl index 460000787..a439337b1 100644 --- a/src/tensors/vectorinterface.jl +++ b/src/tensors/vectorinterface.jl @@ -129,9 +129,9 @@ function VectorInterface.inner(tx::TensorMap, ty::TensorMap) else T = VectorInterface.promote_inner(tx, ty) s = zero(T) - for c in blocksectors(tx) - bx = parent(block(tx, c)) # matrix structure (reshape) does not matter - by = parent(block(ty, c)) # but does lead to slower path in inner + for (c, (_, r)) in pairs(blockstructure(space(tx))) + bx = view(tx.data, r) # matrix structure (reshape) does not matter + by = view(ty.data, r) # but does lead to slower path in inner s += convert(T, dim(c)) * inner(bx, by) end end From d301afe9a58545366d134f886b960a6dd487856e Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 6 Aug 2026 15:13:42 +0200 Subject: [PATCH 06/12] Fix ambiguities --- src/spaces/homspace.jl | 14 -------------- src/spaces/structure.jl | 8 -------- 2 files changed, 22 deletions(-) diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index 103074c7c..89f6ab584 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -187,20 +187,6 @@ function fusionblocks(W::HomSpace) return fblocks end -function diagonalblockstructure(W::HomSpace) - ((numin(W) == numout(W) == 1) && domain(W) == codomain(W)) || - throw(SpaceMismatch("Diagonal only support on V←V with a single space V")) - structure = SectorDict{sectortype(W), UnitRange{Int}}() # range - offset = 0 - dom = domain(W)[1] - for c in blocksectors(W) - d = dim(dom, c) - structure[c] = offset .+ (1:d) - offset += d - end - return structure -end - # Operations on HomSpaces # ----------------------- """ diff --git a/src/spaces/structure.jl b/src/spaces/structure.jl index 9a4ddf5c0..78b6fbbb4 100644 --- a/src/spaces/structure.jl +++ b/src/spaces/structure.jl @@ -187,12 +187,4 @@ See also [`sectorstructure`](@ref), [`blockstructure`](@ref), [`subblockstructur return DegeneracyStructure(blockoffset, blockvalues, structurevalues) end -function _subblock_strides(subsz, sz, str) - sz_simplify = Strided.StridedViews._simplifydims(sz, str) - strides = Strided.StridedViews._computereshapestrides(subsz, sz_simplify...) - isnothing(strides) && - throw(ArgumentError("unexpected error in computing subblock strides")) - return strides -end - CacheStyle(::typeof(degeneracystructure), ::HomSpace) = GlobalLRUCache() From 7ef72c8024d1448e70658455eb0b3f38e7db9e61 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 6 Aug 2026 18:02:18 +0200 Subject: [PATCH 07/12] Use MAK extension --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index ebb002786..291c1524f 100644 --- a/Project.toml +++ b/Project.toml @@ -69,3 +69,4 @@ julia = "1.10" [sources] Strided = {url = "https://github.com/QuantumKitHub/Strided.jl", rev = "ksh/big"} +MatrixAlgebraKit = {url = "https://github.com/QuantumKitHub/MatrixAlgebraKit.jl", rev = "ksh/stridedviews"} From 0a1dddf1d48f66ca04e37c4824dedb7fcdf957e6 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Thu, 6 Aug 2026 19:21:52 +0200 Subject: [PATCH 08/12] Mark schur and svd dependent tests broken for now --- test/tensors/linalg.jl | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/test/tensors/linalg.jl b/test/tensors/linalg.jl index 738e85939..8168df6d1 100644 --- a/test/tensors/linalg.jl +++ b/test/tensors/linalg.jl @@ -102,12 +102,12 @@ for V in spacelist @test t1 * (t1 \ t) ≈ t @test (t / t2) * t2 ≈ t @test t1 \ one(t1) ≈ inv(t1) - @test one(t1) / t1 ≈ pinv(t1) + @test_broken one(t1) / t1 ≈ pinv(t1) # pinv doesn't yet work for StridedView @test_throws SpaceMismatch inv(t) @test_throws SpaceMismatch t2 \ t @test_throws SpaceMismatch t / t1 - tp = pinv(t) * t - @test tp ≈ tp * tp + #tp = pinv(t) * t + #@test tp ≈ tp * tp end end if BraidingStyle(I) isa Bosonic && hasfusiontensor(I) @@ -129,7 +129,7 @@ for V in spacelist @test reshape(convert(Array, t2' * t'), d2, d1) ≈ At2' * At' @test reshape(convert(Array, inv(t1)), d1, d1) ≈ inv(At1) - @test reshape(convert(Array, pinv(t)), d2, d1) ≈ pinv(At) + @test_broken reshape(convert(Array, pinv(t)), d2, d1) ≈ pinv(At) # pinv doesn't yet work for StridedView if T == Float32 || T == ComplexF32 continue @@ -165,22 +165,23 @@ for V in spacelist @test reshape(convert(Array, expt), (s, s)) ≈ exp(reshape(convert(Array, t), (s, s))) - @test (@constinferred sqrt(t))^2 ≈ t - @test reshape(convert(Array, sqrt(t^2)), (s, s)) ≈ + # TODO waiting on schur! support + @test_broken (@constinferred sqrt(t))^2 ≈ t + @test_broken reshape(convert(Array, sqrt(t^2)), (s, s)) ≈ sqrt(reshape(convert(Array, t^2), (s, s))) - @test exp(@constinferred log(expt)) ≈ expt - @test reshape(convert(Array, log(expt)), (s, s)) ≈ + @test_broken exp(@constinferred log(expt)) ≈ expt + @test_broken reshape(convert(Array, log(expt)), (s, s)) ≈ log(reshape(convert(Array, expt), (s, s))) - @test (@constinferred cos(t))^2 + (@constinferred sin(t))^2 ≈ id(W) - @test (@constinferred tan(t)) ≈ sin(t) / cos(t) - @test (@constinferred cot(t)) ≈ cos(t) / sin(t) - @test (@constinferred cosh(t))^2 - (@constinferred sinh(t))^2 ≈ id(W) - @test (@constinferred tanh(t)) ≈ sinh(t) / cosh(t) - @test (@constinferred coth(t)) ≈ cosh(t) / sinh(t) + @test_broken (@constinferred cos(t))^2 + (@constinferred sin(t))^2 ≈ id(W) + @test_broken (@constinferred tan(t)) ≈ sin(t) / cos(t) + @test_broken (@constinferred cot(t)) ≈ cos(t) / sin(t) + @test_broken (@constinferred cosh(t))^2 - (@constinferred sinh(t))^2 ≈ id(W) + @test_broken (@constinferred tanh(t)) ≈ sinh(t) / cosh(t) + @test_broken (@constinferred coth(t)) ≈ cosh(t) / sinh(t) - t1 = sin(t) + #=t1 = sin(t) @test sin(@constinferred asin(t1)) ≈ t1 t2 = cos(t) @test cos(@constinferred acos(t2)) ≈ t2 @@ -203,11 +204,12 @@ for V in spacelist sqrt, log, asin, acos, acosh, atanh, acoth, ) @test_throws SpaceMismatch f(t) - end + end=# end end end - @timedtestset "Sylvester equation" begin + # TODO waiting on schur! support + #=@timedtestset "Sylvester equation" begin for T in (Float32, ComplexF64) tA = rand(T, V1 ⊗ V2, V1 ⊗ V2) tB = rand(T, (V3 ⊗ V4 ⊗ V5)', (V3 ⊗ V4 ⊗ V5)') @@ -224,7 +226,7 @@ for V in spacelist @test matrix(t) ≈ sylvester(matrix(tA), matrix(tB), matrix(tC)) end end - end + end=# end TensorKit.empty_globalcaches!() end From 5b545db4dd67d92f12116894dd80bcb3922ce7d1 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 7 Aug 2026 11:22:57 +0200 Subject: [PATCH 09/12] Restore broken tests --- test/tensors/linalg.jl | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/test/tensors/linalg.jl b/test/tensors/linalg.jl index 8168df6d1..738e85939 100644 --- a/test/tensors/linalg.jl +++ b/test/tensors/linalg.jl @@ -102,12 +102,12 @@ for V in spacelist @test t1 * (t1 \ t) ≈ t @test (t / t2) * t2 ≈ t @test t1 \ one(t1) ≈ inv(t1) - @test_broken one(t1) / t1 ≈ pinv(t1) # pinv doesn't yet work for StridedView + @test one(t1) / t1 ≈ pinv(t1) @test_throws SpaceMismatch inv(t) @test_throws SpaceMismatch t2 \ t @test_throws SpaceMismatch t / t1 - #tp = pinv(t) * t - #@test tp ≈ tp * tp + tp = pinv(t) * t + @test tp ≈ tp * tp end end if BraidingStyle(I) isa Bosonic && hasfusiontensor(I) @@ -129,7 +129,7 @@ for V in spacelist @test reshape(convert(Array, t2' * t'), d2, d1) ≈ At2' * At' @test reshape(convert(Array, inv(t1)), d1, d1) ≈ inv(At1) - @test_broken reshape(convert(Array, pinv(t)), d2, d1) ≈ pinv(At) # pinv doesn't yet work for StridedView + @test reshape(convert(Array, pinv(t)), d2, d1) ≈ pinv(At) if T == Float32 || T == ComplexF32 continue @@ -165,23 +165,22 @@ for V in spacelist @test reshape(convert(Array, expt), (s, s)) ≈ exp(reshape(convert(Array, t), (s, s))) - # TODO waiting on schur! support - @test_broken (@constinferred sqrt(t))^2 ≈ t - @test_broken reshape(convert(Array, sqrt(t^2)), (s, s)) ≈ + @test (@constinferred sqrt(t))^2 ≈ t + @test reshape(convert(Array, sqrt(t^2)), (s, s)) ≈ sqrt(reshape(convert(Array, t^2), (s, s))) - @test_broken exp(@constinferred log(expt)) ≈ expt - @test_broken reshape(convert(Array, log(expt)), (s, s)) ≈ + @test exp(@constinferred log(expt)) ≈ expt + @test reshape(convert(Array, log(expt)), (s, s)) ≈ log(reshape(convert(Array, expt), (s, s))) - @test_broken (@constinferred cos(t))^2 + (@constinferred sin(t))^2 ≈ id(W) - @test_broken (@constinferred tan(t)) ≈ sin(t) / cos(t) - @test_broken (@constinferred cot(t)) ≈ cos(t) / sin(t) - @test_broken (@constinferred cosh(t))^2 - (@constinferred sinh(t))^2 ≈ id(W) - @test_broken (@constinferred tanh(t)) ≈ sinh(t) / cosh(t) - @test_broken (@constinferred coth(t)) ≈ cosh(t) / sinh(t) + @test (@constinferred cos(t))^2 + (@constinferred sin(t))^2 ≈ id(W) + @test (@constinferred tan(t)) ≈ sin(t) / cos(t) + @test (@constinferred cot(t)) ≈ cos(t) / sin(t) + @test (@constinferred cosh(t))^2 - (@constinferred sinh(t))^2 ≈ id(W) + @test (@constinferred tanh(t)) ≈ sinh(t) / cosh(t) + @test (@constinferred coth(t)) ≈ cosh(t) / sinh(t) - #=t1 = sin(t) + t1 = sin(t) @test sin(@constinferred asin(t1)) ≈ t1 t2 = cos(t) @test cos(@constinferred acos(t2)) ≈ t2 @@ -204,12 +203,11 @@ for V in spacelist sqrt, log, asin, acos, acosh, atanh, acoth, ) @test_throws SpaceMismatch f(t) - end=# + end end end end - # TODO waiting on schur! support - #=@timedtestset "Sylvester equation" begin + @timedtestset "Sylvester equation" begin for T in (Float32, ComplexF64) tA = rand(T, V1 ⊗ V2, V1 ⊗ V2) tB = rand(T, (V3 ⊗ V4 ⊗ V5)', (V3 ⊗ V4 ⊗ V5)') @@ -226,7 +224,7 @@ for V in spacelist @test matrix(t) ≈ sylvester(matrix(tA), matrix(tB), matrix(tC)) end end - end=# + end end TensorKit.empty_globalcaches!() end From bdc3e98c8218544dfa21082f9ad9950f340da523 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 7 Aug 2026 13:38:20 +0200 Subject: [PATCH 10/12] Turn off tr forward test for now --- test/enzyme-linalg/tr.jl | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/test/enzyme-linalg/tr.jl b/test/enzyme-linalg/tr.jl index f42e02ee1..7501c0601 100644 --- a/test/enzyme-linalg/tr.jl +++ b/test/enzyme-linalg/tr.jl @@ -22,18 +22,12 @@ TDs = is_ci ? (Duplicated,) : (Const, Duplicated) @testset "tr reverse: RT $RT, TD $TD" for RT in rRTs, TD in TDs EnzymeTestUtils.test_reverse(tr, RT, (D1, TD); atol, rtol) EnzymeTestUtils.test_reverse(tr, RT, (D2, TD); atol, rtol) - # see https://github.com/QuantumKitHub/TensorKit.jl/issues/457 - @static if VERSION ≥ v"1.11.0-rc" - EnzymeTestUtils.test_reverse(tr, RT, (D3, TD); atol, rtol) - end + EnzymeTestUtils.test_reverse(tr, RT, (D3, TD); atol, rtol) end - @testset "tr forward: RT $RT, TD $TD" for RT in fRTs, TD in TDs + #=@testset "tr forward: RT $RT, TD $TD" for RT in fRTs, TD in TDs EnzymeTestUtils.test_forward(tr, RT, (D1, TD); atol, rtol) EnzymeTestUtils.test_forward(tr, RT, (D2, TD); atol, rtol) - # see https://github.com/QuantumKitHub/TensorKit.jl/issues/457 - @static if VERSION ≥ v"1.11.0-rc" - EnzymeTestUtils.test_forward(tr, RT, (D3, TD); atol, rtol) - end - end + EnzymeTestUtils.test_forward(tr, RT, (D3, TD); atol, rtol) + end=# end end From 46c0bf7b56c984450e61d37f4e2725a5b93dbff5 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 7 Aug 2026 15:41:19 +0200 Subject: [PATCH 11/12] Fix tr fwd and re-enable tests --- ext/TensorKitEnzymeExt/linalg.jl | 4 ++-- test/enzyme-linalg/tr.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/TensorKitEnzymeExt/linalg.jl b/ext/TensorKitEnzymeExt/linalg.jl index 35e3eaaf8..736b46767 100644 --- a/ext/TensorKitEnzymeExt/linalg.jl +++ b/ext/TensorKitEnzymeExt/linalg.jl @@ -129,15 +129,15 @@ function EnzymeRules.reverse( end function EnzymeRules.forward( config::EnzymeRules.FwdConfigWidth{1}, - ::Type{RT}, func::Const{typeof(tr)}, + ::Type{RT}, A::Annotation{<:AbstractTensorMap}, ) where {RT} y = EnzymeRules.needs_primal(config) ? tr(A.val) : nothing Δy = if EnzymeRules.needs_shadow(config) && !isa(A, Const) tr(A.dval) elseif EnzymeRules.needs_shadow(config) - zero(eltype(A.dval)) + zero(eltype(A.val)) else nothing end diff --git a/test/enzyme-linalg/tr.jl b/test/enzyme-linalg/tr.jl index 7501c0601..1ba0c2df7 100644 --- a/test/enzyme-linalg/tr.jl +++ b/test/enzyme-linalg/tr.jl @@ -24,10 +24,10 @@ TDs = is_ci ? (Duplicated,) : (Const, Duplicated) EnzymeTestUtils.test_reverse(tr, RT, (D2, TD); atol, rtol) EnzymeTestUtils.test_reverse(tr, RT, (D3, TD); atol, rtol) end - #=@testset "tr forward: RT $RT, TD $TD" for RT in fRTs, TD in TDs + @testset "tr forward: RT $RT, TD $TD" for RT in fRTs, TD in TDs EnzymeTestUtils.test_forward(tr, RT, (D1, TD); atol, rtol) EnzymeTestUtils.test_forward(tr, RT, (D2, TD); atol, rtol) EnzymeTestUtils.test_forward(tr, RT, (D3, TD); atol, rtol) - end=# + end end end From f10a247dd7e831b86285ee10542f46c728025369 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 7 Aug 2026 16:17:01 +0200 Subject: [PATCH 12/12] Try StridedView/FD fix DO NOT MERGE --- ext/TensorKitFiniteDifferencesExt.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/TensorKitFiniteDifferencesExt.jl b/ext/TensorKitFiniteDifferencesExt.jl index 8a19ae62a..5d692d48a 100644 --- a/ext/TensorKitFiniteDifferencesExt.jl +++ b/ext/TensorKitFiniteDifferencesExt.jl @@ -4,6 +4,13 @@ using TensorKit using TensorKit: sqrtdim, invsqrtdim, SectorVector using VectorInterface: scale! using FiniteDifferences +using Strided: StridedView + +function FiniteDifferences.to_vec(x::StridedView) + x_vec, from_vec = FiniteDifferences.to_vec(Array(x)) + StridedView_from_vec(x_vec) = StridedView(from_vec(x_vec)) + return x_vec, StridedView_from_vec +end function FiniteDifferences.to_vec(t::AbstractTensorMap) # convert to vector of vectors to make use of existing functionality