Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ PackageExtensionCompat = "1"
Random = "1"
ScopedValues = "1.3.0"
Strided = "2"
TensorKitSectors = "0.1.4, 0.2"
TensorKitSectors = "0.3"
TensorOperations = "5.1"
Test = "1"
TestExtras = "0.2,0.3"
Expand Down
6 changes: 3 additions & 3 deletions docs/src/lib/sectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ Irrep

## Methods for defining and characterizing `Sector` subtypes
```@docs
Base.one(::Sector)
dual(::Sector)
unit(::Sector)
dual
Nsymbol
Fsymbol
Rsymbol
Bsymbol
dim(::Sector)
frobeniusschur
frobenius_schur_phase
twist(::Sector)
Base.isreal(::Type{<:Sector})
TensorKitSectors.sectorscalartype
Expand Down
6 changes: 3 additions & 3 deletions docs/src/lib/spaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ The following methods act specifically on `ElementarySpace` spaces:

```@docs
isdual
dual
dual(::VectorSpace)
conj
flip
zero(::ElementarySpace)
oneunit
zerospace
unitspace
supremum
infimum
```
Expand Down
2 changes: 1 addition & 1 deletion docs/src/man/sectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ For practical reasons, we also require some additional methods to be defined:

Further information, such as the quantum dimensions ``d_a`` and Frobenius-Schur indicator
``χ_a`` (only if ``a == \overline{a}``) are encoded in the F-symbol. They are obtained as
[`dim(a)`](@ref) and [`frobeniusschur(a)`](@ref). These functions have default definitions
[`dim(a)`](@ref) and [`frobenius_schur_phase(a)`](@ref). These functions have default definitions
which extract the requested data from `Fsymbol(a, conj(a), a, a, one(a), one(a))`, but they
can be overloaded in case the value can be computed more efficiently.

Expand Down
17 changes: 8 additions & 9 deletions docs/src/man/spaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,19 +316,18 @@ flip(ℂ^4) == ℂ^4
```

We also define the direct sum `V1` and `V2` as `V1 ⊕ V2`, where `⊕` is obtained by typing
`\oplus`+TAB. This is possible only if `isdual(V1) == isdual(V2)`. With a little pun on
Julia Base, `oneunit` applied to an elementary space (in the value or type domain) returns
the one-dimensional space, which is isomorphic to the scalar field of the space itself. Some
examples illustrate this better
`\oplus`+TAB. This is possible only if `isdual(V1) == isdual(V2)`. `unitspace` applied to an elementary space
(in the value or type domain) returns the one-dimensional space, which is isomorphic to the
scalar field of the space itself. Some examples illustrate this better.
```@repl tensorkit
ℝ^5 ⊕ ℝ^3
ℂ^5 ⊕ ℂ^3
ℂ^5 ⊕ (ℂ^3)'
oneunit(ℝ^3)
ℂ^5 ⊕ oneunit(ComplexSpace)
oneunit((ℂ^3)')
(ℂ^5) ⊕ oneunit((ℂ^5))
(ℂ^5)' ⊕ oneunit((ℂ^5)')
unitspace(ℝ^3)
ℂ^5 ⊕ unitspace(ComplexSpace)
unitspace((ℂ^3)')
(ℂ^5) ⊕ unitspace((ℂ^5))
(ℂ^5)' ⊕ unitspace((ℂ^5)')
```

Finally, while spaces have a partial order, there is no unique infimum or supremum of a two
Expand Down
9 changes: 6 additions & 3 deletions src/TensorKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ module TensorKit
#---------
# Types:
export Sector, AbstractIrrep, Irrep
export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion,
SimpleFusion, GenericFusion
export FusionStyle, UniqueFusion, MultipleFusion, MultiplicityFreeFusion, SimpleFusion, GenericFusion
export UnitStyle, SimpleUnit, GenericUnit
export BraidingStyle, SymmetricBraiding, Bosonic, Fermionic, Anyonic, NoBraiding
export Trivial, Z2Irrep, Z3Irrep, Z4Irrep, ZNIrrep, U1Irrep, SU2Irrep, CU1Irrep
export ProductSector
export FermionParity, FermionNumber, FermionSpin
export FibonacciAnyon, IsingAnyon
export unit, rightunit, leftunit, allunits, isunit

export VectorSpace, Field, ElementarySpace # abstract vector spaces
export unitspace, zerospace
export InnerProductStyle, NoInnerProduct, HasInnerProduct, EuclideanInnerProduct
export ComplexSpace, CartesianSpace, GeneralSpace, GradedSpace # concrete spaces
export ZNSpace, Z2Space, Z3Space, Z4Space, U1Space, CU1Space, SU2Space
Expand All @@ -39,7 +41,8 @@ export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic

# methods for sectors and properties thereof
export sectortype, sectors, hassector, Nsymbol, Fsymbol, Rsymbol, Bsymbol,
frobeniusschur, twist, otimes, sectorscalartype, deligneproduct
frobeniusschur, frobenius_schur_phase, frobenius_schur_indicator,
twist, otimes, sectorscalartype, deligneproduct
export fusiontrees, braid, permute, transpose
export ZNSpace, SU2Irrep, U1Irrep, CU1Irrep
# other fusion tree manipulations, should not be exported:
Expand Down
16 changes: 8 additions & 8 deletions src/fusiontrees/fusiontrees.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct FusionTree{I <: Sector, N, M, L}
) where
{I <: Sector, N, M, L}
# if N == 0
# @assert coupled == one(coupled)
# @assert coupled == unit(coupled)
# elseif N == 1
# @assert coupled == uncoupled[1]
# elseif N == 2
Expand Down Expand Up @@ -89,7 +89,7 @@ function FusionTree(
end

function FusionTree{I}(
uncoupled::NTuple{N}, coupled = one(I), isdual = ntuple(n -> false, N)
uncoupled::NTuple{N}, coupled = unit(I), isdual = ntuple(Returns(false), N)
) where {I <: Sector, N}
FusionStyle(I) isa UniqueFusion ||
error("fusion tree requires inner lines if `FusionStyle(I) <: MultipleFusion`")
Expand All @@ -103,7 +103,7 @@ function FusionTree(
) where {N, I <: Sector}
return FusionTree{I}(uncoupled, coupled, isdual)
end
FusionTree(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector} = FusionTree(uncoupled, one(I))
FusionTree(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector} = FusionTree(uncoupled, unit(I))

# Properties
sectortype(::Type{<:FusionTree{I}}) where {I <: Sector} = I
Expand Down Expand Up @@ -159,17 +159,17 @@ end

# converting to actual array
function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 0}) where {I}
X = convert(A, fusiontensor(one(I), one(I), one(I)))[1, 1, :]
X = convert(A, fusiontensor(unit(I), unit(I), unit(I)))[1, 1, :]
return X
end
function Base.convert(A::Type{<:AbstractArray}, f::FusionTree{I, 1}) where {I}
c = f.coupled
if f.isdual[1]
sqrtdc = sqrtdim(c)
Zcbartranspose = sqrtdc * convert(A, fusiontensor(conj(c), c, one(c)))[:, :, 1, 1]
Zcbartranspose = sqrtdc * convert(A, fusiontensor(dual(c), c, unit(c)))[:, :, 1, 1]
X = conj!(Zcbartranspose) # we want Zcbar^†
else
X = convert(A, fusiontensor(c, one(c), c))[:, 1, :, 1, 1]
X = convert(A, fusiontensor(c, unit(c), c))[:, 1, :, 1, 1]
end
return X
end
Expand Down Expand Up @@ -257,13 +257,13 @@ include("iterator.jl")
# _abelianinner: generate the inner indices for given outer indices in the abelian case
_abelianinner(outer::Tuple{}) = ()
function _abelianinner(outer::Tuple{I}) where {I <: Sector}
return isone(outer[1]) ? () : throw(SectorMismatch())
return isunit(outer[1]) ? () : throw(SectorMismatch())
end
function _abelianinner(outer::Tuple{I, I}) where {I <: Sector}
return outer[1] == dual(outer[2]) ? () : throw(SectorMismatch())
end
function _abelianinner(outer::Tuple{I, I, I}) where {I <: Sector}
return isone(first(⊗(outer...))) ? () : throw(SectorMismatch())
return isunit(first(⊗(outer...))) ? () : throw(SectorMismatch())
end
function _abelianinner(outer::Tuple{I, I, I, I, Vararg{I}}) where {I <: Sector}
c = first(outer[1] ⊗ outer[2])
Expand Down
8 changes: 4 additions & 4 deletions src/fusiontrees/iterator.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
fusiontrees(uncoupled::NTuple{N,I}[,
coupled::I=one(I)[, isdual::NTuple{N,Bool}=ntuple(n -> false, length(uncoupled))]])
coupled::I=unit(I)[, isdual::NTuple{N,Bool}=ntuple(n -> false, length(uncoupled))]])
where {N,I<:Sector} -> FusionTreeIterator{I,N,I}

Return an iterator over all fusion trees with a given coupled sector label `coupled` and
Expand All @@ -17,7 +17,7 @@ function fusiontrees(uncoupled::Tuple{Vararg{I}}, coupled::I) where {I <: Sector
return fusiontrees(uncoupled, coupled, isdual)
end
function fusiontrees(uncoupled::Tuple{I, Vararg{I}}) where {I <: Sector}
coupled = one(I)
coupled = unit(I)
isdual = ntuple(n -> false, length(uncoupled))
return fusiontrees(uncoupled, coupled, isdual)
end
Expand All @@ -38,7 +38,7 @@ Base.IteratorEltype(::FusionTreeIterator) = Base.HasEltype()
Base.eltype(::Type{<:FusionTreeIterator{I, N}}) where {I <: Sector, N} = fusiontreetype(I, N)

Base.length(iter::FusionTreeIterator) = _fusiondim(iter.uncouplediterators, iter.coupled)
_fusiondim(::Tuple{}, c::I) where {I <: Sector} = Int(isone(c))
_fusiondim(::Tuple{}, c::I) where {I <: Sector} = Int(isunit(c))
_fusiondim(iters::NTuple{1}, c::I) where {I <: Sector} = Int(c ∈ iters[1])
function _fusiondim(iters::NTuple{2}, c::I) where {I <: Sector}
d = 0
Expand All @@ -60,7 +60,7 @@ end

# * Iterator methods:
# Start with special cases:
function Base.iterate(it::FusionTreeIterator{I, 0}, state = !isone(it.coupled)) where {I <: Sector}
function Base.iterate(it::FusionTreeIterator{I, 0}, state = !isunit(it.coupled)) where {I <: Sector}
state && return nothing
tree = FusionTree{I}((), it.coupled, (), (), ())
return tree, true
Expand Down
44 changes: 22 additions & 22 deletions src/fusiontrees/manipulations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ operation is the inverse of `insertat` in the sense that if
f₂ = FusionTree{I}(f.uncoupled, f.coupled, isdual2, f.innerlines, f.vertices)
return f₁, f₂
elseif M === 0
u = leftone(f.uncoupled[1])
u = leftunit(f.uncoupled[1])
f₁ = FusionTree{I}((), u, (), ())
uncoupled2 = (u, f.uncoupled...)
coupled2 = f.coupled
Expand Down Expand Up @@ -246,7 +246,7 @@ function flip(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, i::Int; inv:
@assert 0 < i ≤ N₁ + N₂
if i ≤ N₁
a = f₁.uncoupled[i]
χₐ = frobeniusschur(a)
χₐ = frobenius_schur_phase(a)
θₐ = twist(a)
if !inv
factor = f₁.isdual[i] ? χₐ * θₐ : one(θₐ)
Expand All @@ -259,7 +259,7 @@ function flip(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}, i::Int; inv:
else
i -= N₁
a = f₂.uncoupled[i]
χₐ = frobeniusschur(a)
χₐ = frobenius_schur_phase(a)
θₐ = twist(a)
if !inv
factor = f₂.isdual[i] ? χₐ * one(θₐ) : θₐ
Expand All @@ -286,7 +286,7 @@ function bendright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {
# map final splitting vertex (a, b)<-c to fusion vertex a<-(c, dual(b))
@assert N₁ > 0
c = f₁.coupled
a = N₁ == 1 ? leftone(f₁.uncoupled[1]) :
a = N₁ == 1 ? leftunit(f₁.uncoupled[1]) :
(N₁ == 2 ? f₁.uncoupled[1] : f₁.innerlines[end])
b = f₁.uncoupled[N₁]

Expand All @@ -302,7 +302,7 @@ function bendright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {

coeff₀ = sqrtdim(c) * invsqrtdim(a)
if f₁.isdual[N₁]
coeff₀ *= conj(frobeniusschur(dual(b)))
coeff₀ *= conj(frobenius_schur_phase(dual(b)))
end
if FusionStyle(I) isa MultiplicityFreeFusion
coeff = coeff₀ * Bsymbol(a, b, c)
Expand Down Expand Up @@ -343,7 +343,7 @@ function foldright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {
isduala = f₁.isdual[1]
factor = sqrtdim(a)
if !isduala
factor *= conj(frobeniusschur(a))
factor *= conj(frobenius_schur_phase(a))
end
c1 = dual(a)
c2 = f₁.coupled
Expand All @@ -358,7 +358,7 @@ function foldright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {
hasmultiplicities = FusionStyle(a) isa GenericFusion
local newtrees
if N₁ == 1
cset = (leftone(c1),) # or rightone(a)
cset = (leftunit(c1),) # or rightunit(a)
elseif N₁ == 2
cset = (f₁.uncoupled[2],)
else
Expand All @@ -369,7 +369,7 @@ function foldright(f₁::FusionTree{I, N₁}, f₂::FusionTree{I, N₂}) where {
for μ in 1:Nsymbol(c1, c2, c)
fc = FusionTree((c1, c2), c, (!isduala, false), (), (μ,))
for (fl′, coeff1) in insertat(fc, 2, f₁)
N₁ > 1 && !isone(fl′.innerlines[1]) && continue
N₁ > 1 && !isunit(fl′.innerlines[1]) && continue
coupled = fl′.coupled
uncoupled = Base.tail(Base.tail(fl′.uncoupled))
isdual = Base.tail(Base.tail(fl′.isdual))
Expand Down Expand Up @@ -722,7 +722,7 @@ corresponding coefficients.
function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N}
(N > 1 && 1 <= i <= N) ||
throw(ArgumentError("Cannot trace outputs i=$i and i+1 out of only $N outputs"))
i < N || isone(f.coupled) ||
i < N || isunit(f.coupled) ||
throw(ArgumentError("Cannot trace outputs i=$N and 1 of fusion tree that couples to non-trivial sector"))

T = sectorscalartype(I)
Expand All @@ -735,7 +735,7 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N}
# if trace is zero, return empty dict
(b == dual(b′) && f.isdual[i] != f.isdual[j]) || return newtrees
if i < N
inner_extended = (leftone(f.uncoupled[1]), f.uncoupled[1], f.innerlines..., f.coupled)
inner_extended = (leftunit(f.uncoupled[1]), f.uncoupled[1], f.innerlines..., f.coupled)
a = inner_extended[i]
d = inner_extended[i + 2]
a == d || return newtrees
Expand All @@ -759,25 +759,25 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N}
if i > 1
c = f.innerlines[i - 1]
if FusionStyle(I) isa MultiplicityFreeFusion
coeff *= Fsymbol(a, b, dual(b), a, c, rightone(a))
coeff *= Fsymbol(a, b, dual(b), a, c, rightunit(a))
else
μ = f.vertices[i - 1]
ν = f.vertices[i]
coeff *= Fsymbol(a, b, dual(b), a, c, rightone(a))[μ, ν, 1, 1]
coeff *= Fsymbol(a, b, dual(b), a, c, rightunit(a))[μ, ν, 1, 1]
end
end
if f.isdual[i]
coeff *= frobeniusschur(b)
coeff *= frobenius_schur_phase(b)
end
push!(newtrees, f′ => coeff)
return newtrees
else # i == N
unit = leftone(b)
unit = leftunit(b)
if N == 2
f′ = FusionTree{I}((), unit, (), (), ())
coeff = sqrtdim(b)
if !(f.isdual[N])
coeff *= conj(frobeniusschur(b))
coeff *= conj(frobenius_schur_phase(b))
end
push!(newtrees, f′ => coeff)
return newtrees
Expand All @@ -789,16 +789,16 @@ function elementary_trace(f::FusionTree{I, N}, i) where {I <: Sector, N}
vertices_ = TupleTools.front(f.vertices)
f_ = FusionTree(uncoupled_, coupled_, isdual_, inner_, vertices_)
fs = FusionTree((b,), b, (!f.isdual[1],), (), ())
for (f_′, coeff) in merge(fs, f_, unit, 1) # coloring gets reversed here, should be the other unit
for (f_′, coeff) in merge(fs, f_, unit, 1)
f_′.innerlines[1] == unit || continue
uncoupled′ = Base.tail(Base.tail(f_′.uncoupled))
isdual′ = Base.tail(Base.tail(f_′.isdual))
inner′ = N <= 4 ? () : Base.tail(Base.tail(f_′.innerlines))
vertices′ = N <= 3 ? () : Base.tail(Base.tail(f_′.vertices))
f′ = FusionTree(uncoupled′, unit, isdual′, inner′, vertices′) # and this one?
f′ = FusionTree(uncoupled′, unit, isdual′, inner′, vertices′)
coeff *= sqrtdim(b)
if !(f.isdual[N])
coeff *= conj(frobeniusschur(b))
coeff *= conj(frobenius_schur_phase(b))
end
newtrees[f′] = get(newtrees, f′, zero(coeff)) + coeff
end
Expand Down Expand Up @@ -838,14 +838,14 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I <: Sect
vertices = f.vertices
oneT = one(sectorscalartype(I))

if isone(uncoupled[i]) || isone(uncoupled[i + 1])
if isunit(a) || isunit(b)
# braiding with trivial sector: simple and always possible
inner′ = inner
vertices′ = vertices
if i > 1 # we also need to alter innerlines and vertices
inner′ = TupleTools.setindex(
inner,
inner_extended[isone(a) ? (i + 1) : (i - 1)],
inner_extended[isunit(a) ? (i + 1) : (i - 1)],
i - 1
)
vertices′ = TupleTools.setindex(vertices′, vertices[i], i - 1)
Expand Down Expand Up @@ -903,7 +903,7 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I <: Sect
return fusiontreedict(I)(f′ => coeff)
elseif FusionStyle(I) isa SimpleFusion
local newtrees
for c′ in intersect(a ⊗ d, e ⊗ conj(b))
for c′ in intersect(a ⊗ d, e ⊗ dual(b))
coeff = oftype(
oneT,
if inv
Expand All @@ -924,7 +924,7 @@ function artin_braid(f::FusionTree{I, N}, i; inv::Bool = false) where {I <: Sect
return newtrees
else # GenericFusion
local newtrees
for c′ in intersect(a ⊗ d, e ⊗ conj(b))
for c′ in intersect(a ⊗ d, e ⊗ dual(b))
Rmat1 = inv ? Rsymbol(d, c, e)' : Rsymbol(c, d, e)
Rmat2 = inv ? Rsymbol(d, a, c′)' : Rsymbol(a, d, c′)
Fmat = Fsymbol(d, a, b, e, c′, c)
Expand Down
5 changes: 2 additions & 3 deletions src/spaces/cartesianspace.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ hassector(V::CartesianSpace, ::Trivial) = dim(V) != 0
sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial())
sectortype(::Type{CartesianSpace}) = Trivial

Base.oneunit(::Type{CartesianSpace}) = CartesianSpace(1)
Base.zero(::Type{CartesianSpace}) = CartesianSpace(0)

unitspace(::Type{CartesianSpace}) = CartesianSpace(1)
zerospace(::Type{CartesianSpace}) = CartesianSpace(0)
⊕(V₁::CartesianSpace, V₂::CartesianSpace) = CartesianSpace(V₁.d + V₂.d)
function ⊖(V::CartesianSpace, W::CartesianSpace)
V ≿ W || throw(ArgumentError("$(W) is not a subspace of $(V)"))
Expand Down
Loading
Loading