Repository navigation
Keep the intermediate blocks of fsbraid in production order
#580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b84ffa7
18dd7e8
263e63d
c0fcb5c
7acc443
be7ed71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,8 +16,13 @@ tree with non-zero coefficient, namely `f` with coefficient `1`. This keyword ha | |
| if `BraidingStyle(sectortype(f)) isa SymmetricBraiding`. | ||
| """ | ||
| function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I, N} | ||
| 1 <= i < N || throw(ArgumentError(lazy"Cannot swap outputs i=$i and i+1 out of only $N outputs")) | ||
| @assert FusionStyle(I) === UniqueFusion() | ||
| return _artin_braid_single(f, i, inv) | ||
| end | ||
| # also for a tree that is the only one with its uncoupled sectors, whose braid is again a single | ||
| # tree (`only` throws otherwise) | ||
| function _artin_braid_single(f::FusionTree{I, N}, i, inv::Bool) where {I, N} | ||
| 1 <= i < N || throw(ArgumentError(lazy"Cannot swap outputs i=$i and i+1 out of only $N outputs")) | ||
|
|
||
| uncoupled = f.uncoupled | ||
| a, b = uncoupled[i], uncoupled[i + 1] | ||
|
|
@@ -53,7 +58,7 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I, N} | |
|
|
||
| if i == 1 | ||
| c = N > 2 ? inner[1] : coupled′ | ||
| R = oftype(oneT, (inv ? conj(Rsymbol(b, a, c)) : Rsymbol(a, b, c))) | ||
| R = oftype(oneT, (inv ? conj(only(Rsymbol(b, a, c))) : only(Rsymbol(a, b, c)))) | ||
| f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner, vertices) | ||
| return f′ => R | ||
| end | ||
|
|
@@ -64,21 +69,31 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I, N} | |
| a = inner_extended[i - 1] | ||
| c = inner_extended[i] | ||
| e = inner_extended[i + 1] | ||
| c′ = only(a ⊗ d) | ||
| c′ = FusionStyle(I) isa UniqueFusion ? only(a ⊗ d) : only(_artin_channels(a, b, d, e)) | ||
| coeff = oftype( | ||
| oneT, | ||
| if inv | ||
| conj(Rsymbol(d, c, e) * Fsymbol(d, a, b, e, c′, c)) * Rsymbol(d, a, c′) | ||
| conj(only(Rsymbol(d, c, e)) * only(Fsymbol(d, a, b, e, c′, c))) * only(Rsymbol(d, a, c′)) | ||
| else | ||
| Rsymbol(c, d, e) * conj(Fsymbol(d, a, b, e, c′, c) * Rsymbol(a, d, c′)) | ||
| only(Rsymbol(c, d, e)) * conj(only(Fsymbol(d, a, b, e, c′, c)) * only(Rsymbol(a, d, c′))) | ||
| end | ||
| ) | ||
| inner′ = TupleTools.setindex(inner, c′, i - 1) | ||
| f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner′) | ||
| return f′ => coeff | ||
| end | ||
|
|
||
| function artin_braid(src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false) where {I, N} | ||
| # the inner lines `c′` of `a ⊗ d → c′ → c′ ⊗ b → e`, i.e. `intersect(a ⊗ d, e ⊗ conj(b))`, | ||
| # without constructing the second fusion product for sectors with a simple unit | ||
| function _artin_channels(a::I, b::I, d::I, e::I) where {I <: Sector} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I'm not mistaken this is currently only used in the implementation that immediately calls
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edit, I missed you also have it below. I'm still curious whether it might make sense to use the fact that the products are sorted for a fast intersect iterable |
||
| UnitStyle(I) isa SimpleUnit || return intersect(a ⊗ d, e ⊗ conj(b)) | ||
| return Iterators.filter(c′ -> !iszero(Nsymbol(c′, b, e)), a ⊗ d) | ||
| end | ||
|
|
||
| function artin_braid( | ||
| src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false, canonical::Val = Val(true), | ||
| buffers = (nothing, nothing) # a tree vector and a matrix that may be overwritten | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the matrix, since it is fixed size, it might make sense to just have |
||
| ) where {I, N} | ||
| 1 <= i < N || throw(ArgumentError("Cannot swap outputs i=$i and i+1 out of only $N outputs")) | ||
| isempty(src) && return src => zeros(sectorscalartype(I), 0, 0) | ||
|
|
||
|
|
@@ -91,12 +106,12 @@ function artin_braid(src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false) where | |
| isdual = src.isdual[1] | ||
| isdual′ = TupleTools.setindex(isdual, isdual[i], i + 1) | ||
| isdual′ = TupleTools.setindex(isdual′, isdual[i + 1], i) | ||
| dst = FusionTreeBlock{I}((uncoupled′, ()), (isdual′, ()); sizehint = length(src)) | ||
| rows = TreeRows(I, (uncoupled′, ()), (isdual′, ()), length(src), canonical, buffers[1]) | ||
|
|
||
| oneT = one(sectorscalartype(I)) | ||
|
|
||
| indexmap = treeindex_map(dst) | ||
| U = zeros(sectorscalartype(I), length(dst), length(src)) | ||
| U = buffers[2] isa Matrix{typeof(oneT)} ? fill!(buffers[2], zero(oneT)) : | ||
| zeros(sectorscalartype(I), max(length(rows.trees), length(src)), length(src)) # see `bendright` | ||
|
|
||
| if isunit(a) || isunit(b) # braiding with trivial sector: simple and always possible | ||
| for (col, (f, f₂)) in enumerate(fusiontrees(src)) | ||
|
|
@@ -113,10 +128,10 @@ function artin_braid(src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false) where | |
| vertices′ = TupleTools.setindex(vertices′, vertices[i - 1], i) | ||
| end | ||
| f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner′, vertices′) | ||
| row = indexmap[treeindex_data((f′, f₂))] | ||
| row = row!(rows, (f′, f₂)) | ||
| @inbounds U[row, col] = oneT | ||
| end | ||
| return dst => U | ||
| return FusionTreeBlock(rows.trees) => U | ||
| end | ||
|
|
||
| BraidingStyle(I) isa NoBraiding && | ||
|
|
@@ -132,7 +147,7 @@ function artin_braid(src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false) where | |
| if FusionStyle(I) isa MultiplicityFreeFusion | ||
| R = oftype(oneT, (inv ? conj(Rsymbol(b, a, c)) : Rsymbol(a, b, c))) | ||
| f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner, vertices) | ||
| row = indexmap[treeindex_data((f′, f₂))] | ||
| row = row!(rows, (f′, f₂)) | ||
| @inbounds U[row, col] = R | ||
| else # GenericFusion | ||
| μ = vertices[1] | ||
|
|
@@ -142,7 +157,7 @@ function artin_braid(src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false) where | |
| iszero(R) && continue | ||
| vertices′ = TupleTools.setindex(vertices, ν, 1) | ||
| f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner, vertices′) | ||
| row = indexmap[treeindex_data((f′, f₂))] | ||
| row = row!(rows, (f′, f₂)) | ||
| @inbounds U[row, col] = R | ||
| end | ||
| end | ||
|
|
@@ -155,29 +170,30 @@ function artin_braid(src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false) where | |
| c = inner_extended[i] | ||
| e = inner_extended[i + 1] | ||
| if FusionStyle(I) isa MultiplicityFreeFusion | ||
| for c′ in intersect(a ⊗ d, e ⊗ conj(b)) | ||
| R = inv ? Rsymbol(d, c, e) : Rsymbol(c, d, e) | ||
| for c′ in _artin_channels(a, b, d, e) | ||
| coeff = if inv | ||
| conj(Rsymbol(d, c, e) * Fsymbol(d, a, b, e, c′, c)) * Rsymbol(d, a, c′) | ||
| conj(R * Fsymbol(d, a, b, e, c′, c)) * Rsymbol(d, a, c′) | ||
| else | ||
| Rsymbol(c, d, e) * conj(Fsymbol(d, a, b, e, c′, c) * Rsymbol(a, d, c′)) | ||
| R * conj(Fsymbol(d, a, b, e, c′, c) * Rsymbol(a, d, c′)) | ||
| end | ||
| iszero(coeff) && continue | ||
| inner′ = TupleTools.setindex(inner, c′, i - 1) | ||
| f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner′) | ||
| row = indexmap[treeindex_data((f′, f₂))] | ||
| row = row!(rows, (f′, f₂)) | ||
| @inbounds U[row, col] = coeff | ||
| end | ||
| else # GenericFusion | ||
| for c′ in intersect(a ⊗ d, e ⊗ conj(b)) | ||
| Rmat1 = inv ? Rsymbol(d, c, e)' : Rsymbol(c, d, e) | ||
| Rmat1 = inv ? Rsymbol(d, c, e)' : Rsymbol(c, d, e) | ||
| μ = vertices[i - 1] | ||
| ν = vertices[i] | ||
| for c′ in _artin_channels(a, b, d, e) | ||
| Rmat2 = inv ? Rsymbol(d, a, c′)' : Rsymbol(a, d, c′) | ||
| Fmat = Fsymbol(d, a, b, e, c′, c) | ||
| μ = vertices[i - 1] | ||
| ν = vertices[i] | ||
| for σ in 1:Nsymbol(a, d, c′) | ||
| for λ in 1:Nsymbol(c′, b, e) | ||
| for σ in axes(Rmat2, 1) | ||
| for λ in axes(Fmat, 2) | ||
| coeff = zero(oneT) | ||
| for ρ in 1:Nsymbol(d, c, e), κ in 1:Nsymbol(d, a, c′) | ||
| for ρ in axes(Rmat1, 2), κ in axes(Rmat2, 2) | ||
| coeff += Rmat1[ν, ρ] * conj(Fmat[κ, λ, μ, ρ]) * | ||
| conj(Rmat2[σ, κ]) | ||
| end | ||
|
|
@@ -186,15 +202,15 @@ function artin_braid(src::FusionTreeBlock{I, N, 0}, i; inv::Bool = false) where | |
| vertices′ = TupleTools.setindex(vertices′, λ, i) | ||
| inner′ = TupleTools.setindex(inner, c′, i - 1) | ||
| f′ = FusionTree{I}(uncoupled′, coupled′, isdual′, inner′, vertices′) | ||
| row = indexmap[treeindex_data((f′, f₂))] | ||
| row = row!(rows, (f′, f₂)) | ||
| @inbounds U[row, col] = coeff | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| return dst => U | ||
| return FusionTreeBlock(rows.trees) => U | ||
| end | ||
|
|
||
| # braid fusion tree | ||
|
|
@@ -312,29 +328,53 @@ end | |
| return (f₁′, f₂′) => coeff1 * coeff2 * coeff3 | ||
| else | ||
| src, (p1, p2), (l1, l2) = key | ||
| src = src::FusionTreeBlock{I} | ||
|
|
||
| p = linearizepermutation(p1, p2, numout(src), numin(src)) | ||
| levels = (l1..., reverse(l2)...) | ||
|
|
||
| dst, U = repartition(src, numind(src)) | ||
| if length(src) == 1 # then so is every intermediate block: the braid is a product of scalars | ||
| (f, f₀), coeff = _bend_single(_bendleft_single, only(fusiontrees(src)), Val(numin(src))) | ||
| for s in permutation2swaps(p) | ||
| _check_levels(BraidingStyle(I), levels, s) | ||
| inv = levels[s] > levels[s + 1] | ||
| f, c = _artin_braid_single(f, s, inv) | ||
| coeff *= c | ||
| l = levels[s] | ||
| levels = TupleTools.setindex(levels, levels[s + 1], s) | ||
| levels = TupleTools.setindex(levels, l, s + 1) | ||
| end | ||
| (f₁′, f₂′), c = _bend_single(_bendright_single, (f, f₀), Val(N₂)) | ||
| return FusionTreeBlock([(f₁′, f₂′)]) => fill(coeff * c, 1, 1) | ||
| end | ||
|
|
||
| # the intermediate blocks keep their trees in the order in which they are produced, | ||
| # and only the result is brought into canonical order | ||
| dst, U = repartition_noncanonical(src, Val(numind(src))) | ||
|
|
||
| # the swaps keep the size of the block, so that the tree vector and the matrices of the | ||
| # previous step are recycled (except for the trees of `src`, which are part of the key) | ||
| buffers = (nothing, nothing) | ||
| U_spare = nothing | ||
| braid_style = BraidingStyle(I) | ||
| for s in permutation2swaps(p) | ||
| _check_levels(braid_style, levels, s) | ||
| inv = levels[s] > levels[s + 1] | ||
| dst, U_tmp = artin_braid(dst, s; inv) | ||
| U = U_tmp * U | ||
| dst′, U_tmp = artin_braid(dst, s; inv, canonical = Val(false), buffers) | ||
| buffers = (dst.trees === src.trees ? nothing : dst.trees, U_tmp) | ||
| dst = dst′ | ||
| U_spare, U = U, U_spare isa typeof(U_tmp) ? mul!(U_spare, U_tmp, U) : U_tmp * U | ||
| l = levels[s] | ||
| levels = TupleTools.setindex(levels, levels[s + 1], s) | ||
| levels = TupleTools.setindex(levels, l, s + 1) | ||
| end | ||
|
|
||
| if N₂ == 0 | ||
| return dst => U | ||
| return canonicalize(dst, U) | ||
| else | ||
| dst, U_tmp = repartition(dst::FusionTreeBlock{I}, N₁) | ||
| dst, U_tmp = repartition_noncanonical(dst::FusionTreeBlock{I}, Val(N₁)) | ||
| U = U_tmp * U | ||
| return dst => U | ||
| return canonicalize(dst, U) | ||
| end | ||
| end | ||
| end | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put the style check inside the helper function and just use dispatch?