From 0ee6b1b7dfecdd8eac2660a6d958fce91e767a21 Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Fri, 13 Dec 2024 16:11:32 +0100 Subject: [PATCH 1/2] add flipping functionality --- src/spaces/homspace.jl | 16 ++++++++++++++++ src/tensors/indexmanipulations.jl | 16 ++++++++++++++++ test/tensors.jl | 17 +++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index 608b5404..c6569597 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -147,6 +147,22 @@ function select(W::HomSpace{S}, (p₁, p₂)::Index2Tuple{N₁,N₂}) where {S,N return cod ← dom end +""" + flip(W::HomSpace, I) + +Return a new `HomSpace` object by applying `flip` to each of the spaces in the domain and +codomain of `W` for which the linear index `i` satisfies `i ∈ I`. +""" +function flip(W::HomSpace{S}, I) where {S} + cod′ = let cod = codomain(W) + ProductSpace{S}(ntuple(i -> i ∈ I ? flip(cod[i]) : cod[i], numout(W))) + end + dom′ = let dom = domain(W) + ProductSpace{S}(ntuple(i -> (i + numout(W)) ∈ I ? flip(dom[i]) : dom[i], numin(W))) + end + return cod′ ← dom′ +end + """ compose(W::HomSpace, V::HomSpace) diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index ee7f90fc..5f8fc1ae 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -1,5 +1,21 @@ # Index manipulations #--------------------- +""" + flip(t::AbstractTensorMap, I) -> t′::AbstractTensorMap + +Return a new tensor that is isomorphic to `t` but where the arrows on the indices `i` that satisfy +`i ∈ I` are flipped, i.e. `space(t′, i) = flip(space(t, i))`. +""" +function flip(t::AbstractTensorMap, I) + P = flip(space(t), I) + t2 = similar(t, P) + for (c, b) in blocks(t) + copy!(t2[c], b) + end + return t +end +flip(t::TensorMap, I) = TensorMap(t.data, flip(space(t), I)) + """ permute!(tdst::AbstractTensorMap, tsrc::AbstractTensorMap, (p₁, p₂)::Index2Tuple) -> tdst diff --git a/test/tensors.jl b/test/tensors.jl index 545ac6cc..c89a5dba 100644 --- a/test/tensors.jl +++ b/test/tensors.jl @@ -326,6 +326,23 @@ for V in spacelist @test HrA12array ≈ convert(Array, HrA12) end end + if BraidingStyle(I) isa Bosonic # TODO: add fermionic tests by including parity tensors + @timedtestset "Index flipping: test via contraction" begin + t1 = rand(ComplexF64, V1 ⊗ V2 ⊗ V3 ← V4) + t2 = rand(ComplexF64, V2' ⊗ V5 ← V4' ⊗ V1) + @tensor ta[a, b] := t1[x, y, a, z] * t2[y, b, z, x] + @tensor tb[a, b] := flip(t1, 1)[x, y, a, z] * flip(t2, 4)[y, b, z, x] + @test ta ≈ tb + @tensor tb[a, b] := flip(t1, (2, 4))[x, y, a, z] * + flip(t2, (1, 3))[y, b, z, x] + @test ta ≈ tb + @tensor tb[a, b] := flip(t1, (1, 2, 4))[x, y, a, z] * + flip(t2, (1, 3, 4))[y, b, z, x] + @tensor tb[a, b] := flip(t1, (1, 3))[x, y, a, z] * + flip(t2, (2, 4))[y, b, z, x] + @test flip(ta, (1, 2)) ≈ tb + end + end @timedtestset "Multiplication of isometries: test properties" begin W2 = V4 ⊗ V5 W1 = W2 ⊗ (oneunit(V1) ⊕ oneunit(V1)) From 1056afd92c0766709a3b8623e18ee9e88dc80675 Mon Sep 17 00:00:00 2001 From: Jutho Date: Mon, 16 Dec 2024 22:17:53 +0100 Subject: [PATCH 2/2] fix implementation and support fermions --- src/fusiontrees/manipulations.jl | 20 ++++++++++++++ src/tensors/indexmanipulations.jl | 15 +++++++---- test/tensors.jl | 43 +++++++++++++++++++------------ 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/fusiontrees/manipulations.jl b/src/fusiontrees/manipulations.jl index e2269f18..092d4134 100644 --- a/src/fusiontrees/manipulations.jl +++ b/src/fusiontrees/manipulations.jl @@ -242,6 +242,26 @@ end # -> B-move (bendleft, bendright) is simple in standard basis # -> A-move (foldleft, foldright) is complicated, needs to be reexpressed in standard form +# flip a duality flag of a fusion tree +function flip(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}, i::Int) where {I<:Sector,N₁,N₂} + @assert 0 < i ≤ N₁ + N₂ + if i ≤ N₁ + a = f₁.uncoupled[i] + fs = frobeniusschur(a) * twist(a) + factor = f₁.isdual[i] ? fs : one(fs) + isdual′ = TupleTools.setindex(f₁.isdual, !f₁.isdual[i], i) + f₁′ = FusionTree{I}(f₁.uncoupled, f₁.coupled, isdual′, f₁.innerlines, f₁.vertices) + return SingletonDict((f₁′, f₂) => factor) + else + i -= N₁ + a = f₂.uncoupled[i] + factor = f₂.isdual[i] ? frobeniusschur(a) : twist(a) + isdual′ = TupleTools.setindex(f₂.isdual, !f₂.isdual[i], i) + f₂′ = FusionTree{I}(f₂.uncoupled, f₂.coupled, isdual′, f₂.innerlines, f₂.vertices) + return SingletonDict((f₁, f₂′) => factor) + end +end + # change to N₁ - 1, N₂ + 1 function bendright(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}) where {I<:Sector,N₁,N₂} # map final splitting vertex (a, b)<-c to fusion vertex a<-(c, dual(b)) diff --git a/src/tensors/indexmanipulations.jl b/src/tensors/indexmanipulations.jl index 5f8fc1ae..a8bdb30a 100644 --- a/src/tensors/indexmanipulations.jl +++ b/src/tensors/indexmanipulations.jl @@ -8,13 +8,18 @@ Return a new tensor that is isomorphic to `t` but where the arrows on the indice """ function flip(t::AbstractTensorMap, I) P = flip(space(t), I) - t2 = similar(t, P) - for (c, b) in blocks(t) - copy!(t2[c], b) + t′ = similar(t, P) + for (f₁, f₂) in fusiontrees(t) + f₁′, f₂′ = f₁, f₂ + factor = one(scalartype(t)) + for i in I + (f₁′, f₂′), s = only(flip(f₁′, f₂′, i)) + factor *= s + end + scale!(t′[f₁′, f₂′], t[f₁, f₂], factor) end - return t + return t′ end -flip(t::TensorMap, I) = TensorMap(t.data, flip(space(t), I)) """ permute!(tdst::AbstractTensorMap, tsrc::AbstractTensorMap, (p₁, p₂)::Index2Tuple) diff --git a/test/tensors.jl b/test/tensors.jl index c89a5dba..4c3328e6 100644 --- a/test/tensors.jl +++ b/test/tensors.jl @@ -326,22 +326,33 @@ for V in spacelist @test HrA12array ≈ convert(Array, HrA12) end end - if BraidingStyle(I) isa Bosonic # TODO: add fermionic tests by including parity tensors - @timedtestset "Index flipping: test via contraction" begin - t1 = rand(ComplexF64, V1 ⊗ V2 ⊗ V3 ← V4) - t2 = rand(ComplexF64, V2' ⊗ V5 ← V4' ⊗ V1) - @tensor ta[a, b] := t1[x, y, a, z] * t2[y, b, z, x] - @tensor tb[a, b] := flip(t1, 1)[x, y, a, z] * flip(t2, 4)[y, b, z, x] - @test ta ≈ tb - @tensor tb[a, b] := flip(t1, (2, 4))[x, y, a, z] * - flip(t2, (1, 3))[y, b, z, x] - @test ta ≈ tb - @tensor tb[a, b] := flip(t1, (1, 2, 4))[x, y, a, z] * - flip(t2, (1, 3, 4))[y, b, z, x] - @tensor tb[a, b] := flip(t1, (1, 3))[x, y, a, z] * - flip(t2, (2, 4))[y, b, z, x] - @test flip(ta, (1, 2)) ≈ tb - end + @timedtestset "Index flipping: test via explicit flip" begin + t = rand(ComplexF64, V1 ⊗ V1' ← V1' ⊗ V1) + F1 = unitary(flip(V1), V1) + + @tensor tf[a, b; c, d] := F1[a, a'] * t[a', b; c, d] + @test flip(t, 1) ≈ tf + @tensor tf[a, b; c, d] := conj(F1[b, b']) * t[a, b'; c, d] + @test twist!(flip(t, 2), 2) ≈ tf + @tensor tf[a, b; c, d] := F1[c, c'] * t[a, b; c', d] + @test flip(t, 3) ≈ tf + @tensor tf[a, b; c, d] := conj(F1[d, d']) * t[a, b; c, d'] + @test twist!(flip(t, 4), 4) ≈ tf + end + @timedtestset "Index flipping: test via contraction" begin + t1 = rand(ComplexF64, V1 ⊗ V2 ⊗ V3 ← V4) + t2 = rand(ComplexF64, V2' ⊗ V5 ← V4' ⊗ V1) + @tensor ta[a, b] := t1[x, y, a, z] * t2[y, b, z, x] + @tensor tb[a, b] := flip(t1, 1)[x, y, a, z] * flip(t2, 4)[y, b, z, x] + @test ta ≈ tb + @tensor tb[a, b] := flip(t1, (2, 4))[x, y, a, z] * + flip(t2, (1, 3))[y, b, z, x] + @test ta ≈ tb + @tensor tb[a, b] := flip(t1, (1, 2, 4))[x, y, a, z] * + flip(t2, (1, 3, 4))[y, b, z, x] + @tensor tb[a, b] := flip(t1, (1, 3))[x, y, a, z] * + flip(t2, (2, 4))[y, b, z, x] + @test flip(ta, (1, 2)) ≈ tb end @timedtestset "Multiplication of isometries: test properties" begin W2 = V4 ⊗ V5