diff --git a/Project.toml b/Project.toml index ab7d6ed0..aacd69f6 100644 --- a/Project.toml +++ b/Project.toml @@ -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" diff --git a/docs/src/lib/sectors.md b/docs/src/lib/sectors.md index 4029b68c..931bd917 100644 --- a/docs/src/lib/sectors.md +++ b/docs/src/lib/sectors.md @@ -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 diff --git a/docs/src/lib/spaces.md b/docs/src/lib/spaces.md index 205301d8..10d2bf4f 100644 --- a/docs/src/lib/spaces.md +++ b/docs/src/lib/spaces.md @@ -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 ``` diff --git a/docs/src/man/sectors.md b/docs/src/man/sectors.md index db7913f1..73c1630a 100644 --- a/docs/src/man/sectors.md +++ b/docs/src/man/sectors.md @@ -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. diff --git a/docs/src/man/spaces.md b/docs/src/man/spaces.md index 503730bf..ff2d5990 100644 --- a/docs/src/man/spaces.md +++ b/docs/src/man/spaces.md @@ -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 diff --git a/src/TensorKit.jl b/src/TensorKit.jl index e80de65f..6b59dac9 100644 --- a/src/TensorKit.jl +++ b/src/TensorKit.jl @@ -7,16 +7,17 @@ module TensorKit # Exports #--------- -# Types: +# Reexport common sector 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 common vector space, fusion tree and tensor types export VectorSpace, Field, ElementarySpace # abstract vector spaces export InnerProductStyle, NoInnerProduct, HasInnerProduct, EuclideanInnerProduct export ComplexSpace, CartesianSpace, GeneralSpace, GradedSpace # concrete spaces @@ -30,18 +31,19 @@ export DiagonalTensorMap, BraidingTensor export TruncationScheme export SpaceMismatch, SectorMismatch, IndexError # error types -# general vector space methods -export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual, oplus, ominus, - insertleftunit, insertrightunit, removeunit - -# partial order for vector spaces +# Export general vector space methods +export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual +export unitspace, zerospace, oplus, ominus +export insertleftunit, insertrightunit, removeunit 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 +# Reexport methods for sectors and properties thereof +export sectortype, sectors, hassector +export unit, rightunit, leftunit, allunits, isunit, otimes +export Nsymbol, Fsymbol, Rsymbol, Bsymbol, frobenius_schur_phase, frobenius_schur_indicator, twist, sectorscalartype, deligneproduct + +# Export methods for fusion trees export fusiontrees, braid, permute, transpose -export ZNSpace, SU2Irrep, U1Irrep, CU1Irrep # other fusion tree manipulations, should not be exported: # export insertat, split, merge, repartition, artin_braid, # bendleft, bendright, foldleft, foldright, cycleclockwise, cycleanticlockwise @@ -52,9 +54,9 @@ export ℤ₂, ℤ₃, ℤ₄, U₁, SU, SU₂, CU₁ export fℤ₂, fU₁, fSU₂ export ℤ₂Space, ℤ₃Space, ℤ₄Space, U₁Space, CU₁Space, SU₂Space -# tensor maps +# Export tensor map methods export domain, codomain, numind, numout, numin, domainind, codomainind, allind -export spacetype, sectortype, storagetype, scalartype, tensormaptype +export spacetype, storagetype, scalartype, tensormaptype export blocksectors, blockdim, block, blocks # random methods for constructor diff --git a/src/fusiontrees/fusiontrees.jl b/src/fusiontrees/fusiontrees.jl index 419a01c9..1665cdb2 100644 --- a/src/fusiontrees/fusiontrees.jl +++ b/src/fusiontrees/fusiontrees.jl @@ -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 @@ -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`") @@ -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 @@ -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 @@ -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]) diff --git a/src/fusiontrees/iterator.jl b/src/fusiontrees/iterator.jl index 4284b78e..50f0781f 100644 --- a/src/fusiontrees/iterator.jl +++ b/src/fusiontrees/iterator.jl @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/src/fusiontrees/manipulations.jl b/src/fusiontrees/manipulations.jl index 6455e9d1..1564b1b6 100644 --- a/src/fusiontrees/manipulations.jl +++ b/src/fusiontrees/manipulations.jl @@ -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 @@ -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(θₐ) @@ -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(θₐ) : θₐ @@ -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₁] @@ -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) @@ -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 @@ -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 @@ -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)) @@ -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) @@ -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 @@ -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 @@ -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 @@ -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) @@ -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 @@ -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) diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index 778fa498..3ee4b6ca 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -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)")) diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index 00e4f2a3..87e9e1dd 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -48,9 +48,8 @@ sectortype(::Type{ComplexSpace}) = Trivial Base.conj(V::ComplexSpace) = ComplexSpace(dim(V), !isdual(V)) -Base.oneunit(::Type{ComplexSpace}) = ComplexSpace(1) -Base.zero(::Type{ComplexSpace}) = ComplexSpace(0) - +unitspace(::Type{ComplexSpace}) = ComplexSpace(1) +zerospace(::Type{ComplexSpace}) = ComplexSpace(0) function ⊕(V₁::ComplexSpace, V₂::ComplexSpace) return isdual(V₁) == isdual(V₂) ? ComplexSpace(dim(V₁) + dim(V₂), isdual(V₁)) : diff --git a/src/spaces/deligne.jl b/src/spaces/deligne.jl index 926d59e4..5a5a2d71 100644 --- a/src/spaces/deligne.jl +++ b/src/spaces/deligne.jl @@ -24,7 +24,7 @@ function ⊠(V::GradedSpace, P₀::ProductSpace{<:ElementarySpace, 0}) I₁ = sectortype(V) I₂ = sectortype(P₀) return Vect[I₁ ⊠ I₂]( - ifelse(isdual(V), dual(c), c) ⊠ one(I₂) => dim(V, c) + ifelse(isdual(V), dual(c), c) ⊠ unit(I₂) => dim(V, c) for c in sectors(V); dual = isdual(V) ) end @@ -34,7 +34,7 @@ function ⊠(P₀::ProductSpace{<:ElementarySpace, 0}, V::GradedSpace) I₁ = sectortype(P₀) I₂ = sectortype(V) return Vect[I₁ ⊠ I₂]( - one(I₁) ⊠ ifelse(isdual(V), dual(c), c) => dim(V, c) + unit(I₁) ⊠ ifelse(isdual(V), dual(c), c) => dim(V, c) for c in sectors(V); dual = isdual(V) ) end @@ -42,13 +42,13 @@ end function ⊠(V::ComplexSpace, P₀::ProductSpace{<:ElementarySpace, 0}) field(V) == field(P₀) || throw_incompatible_fields(V, P₀) I₂ = sectortype(P₀) - return Vect[I₂](one(I₂) => dim(V); dual = isdual(V)) + return Vect[I₂](unit(I₂) => dim(V); dual = isdual(V)) end function ⊠(P₀::ProductSpace{<:ElementarySpace, 0}, V::ComplexSpace) field(P₀) == field(V) || throw_incompatible_fields(P₀, V) I₁ = sectortype(P₀) - return Vect[I₁](one(I₁) => dim(V); dual = isdual(V)) + return Vect[I₁](unit(I₁) => dim(V); dual = isdual(V)) end function ⊠(P::ProductSpace{<:ElementarySpace, 0}, P₀::ProductSpace{<:ElementarySpace, 0}) diff --git a/src/spaces/generalspace.jl b/src/spaces/generalspace.jl index fa9db1ba..62b16d65 100644 --- a/src/spaces/generalspace.jl +++ b/src/spaces/generalspace.jl @@ -35,8 +35,8 @@ sectortype(::Type{<:GeneralSpace}) = Trivial field(::Type{GeneralSpace{𝔽}}) where {𝔽} = 𝔽 InnerProductStyle(::Type{<:GeneralSpace}) = NoInnerProduct() -Base.oneunit(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(1, false, false) -Base.zero(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(0, false, false) +unitspace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(1, false, false) +zerospace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(0, false, false) dual(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), !isdual(V), isconj(V)) Base.conj(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), isdual(V), !isconj(V)) diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index d55a71d6..687c79ac 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -94,7 +94,7 @@ InnerProductStyle(::Type{<:GradedSpace}) = EuclideanInnerProduct() function dim(V::GradedSpace) return reduce( +, dim(V, c) * dim(c) for c in sectors(V); - init = zero(dim(one(sectortype(V)))) + init = zero(dim(unit(sectortype(V)))) ) end function dim(V::GradedSpace{I, <:AbstractDict}, c::I) where {I <: Sector} @@ -134,8 +134,8 @@ function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} return (offset + 1):(offset + dim(c) * dim(V, c)) end -Base.oneunit(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(one(I) => 1) -Base.zero(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(one(I) => 0) +unitspace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 1) +zerospace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 0) # TODO: the following methods can probably be implemented more efficiently for # `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index 3958030b..f020535c 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -22,7 +22,7 @@ end function HomSpace(codomain::S, domain::S) where {S <: ElementarySpace} return HomSpace(⊗(codomain), ⊗(domain)) end -HomSpace(codomain::VectorSpace) = HomSpace(codomain, zero(codomain)) +HomSpace(codomain::VectorSpace) = HomSpace(codomain, zerospace(codomain)) codomain(W::HomSpace) = W.codomain domain(W::HomSpace) = W.domain diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index 0911a5f5..08e7011b 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -147,7 +147,7 @@ function blocksectors(P::ProductSpace{S, N}) where {S, N} end bs = Vector{I}() if N == 0 - push!(bs, one(I)) + push!(bs, unit(I)) elseif N == 1 for s in sectors(P) push!(bs, first(s)) @@ -240,7 +240,7 @@ function Base.literal_pow(::typeof(^), V::ElementarySpace, p::Val{N}) where {N} return ProductSpace{typeof(V), N}(ntuple(n -> V, p)) end -fuse(P::ProductSpace{S, 0}) where {S <: ElementarySpace} = oneunit(S) +fuse(P::ProductSpace{S, 0}) where {S <: ElementarySpace} = unitspace(S) fuse(P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) """ @@ -253,9 +253,10 @@ More specifically, adds a left monoidal unit or its dual. See also [`insertrightunit`](@ref insertrightunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ function insertleftunit( - P::ProductSpace, ::Val{i} = Val(length(P) + 1); conj::Bool = false, dual::Bool = false + P::ProductSpace, ::Val{i} = Val(length(P) + 1); + conj::Bool = false, dual::Bool = false ) where {i} - u = oneunit(spacetype(P)) + u = unitspace(spacetype(P)) if dual u = TensorKit.dual(u) end @@ -275,9 +276,10 @@ More specifically, adds a right monoidal unit or its dual. See also [`insertleftunit`](@ref insertleftunit(::ProductSpace, ::Val{i}) where {i}), [`removeunit`](@ref removeunit(::ProductSpace, ::Val{i}) where {i}). """ function insertrightunit( - P::ProductSpace, ::Val{i} = Val(length(P)); conj::Bool = false, dual::Bool = false + P::ProductSpace, ::Val{i} = Val(length(P)); + conj::Bool = false, dual::Bool = false ) where {i} - u = oneunit(spacetype(P)) + u = unitspace(spacetype(P)) if dual u = TensorKit.dual(u) end @@ -299,7 +301,7 @@ and [`insertrightunit`](@ref insertrightunit(::ProductSpace, ::Val{i}) where {i} """ function removeunit(P::ProductSpace, ::Val{i}) where {i} 1 ≤ i ≤ length(P) || _boundserror(P, i) - isisomorphic(P[i], oneunit(P[i])) || _nontrivialspaceerror(P, i) + isisomorphic(P[i], unitspace(P[i])) || _nontrivialspaceerror(P, i) return ProductSpace{spacetype(P)}(TupleTools.deleteat(P.spaces, i)) end @@ -326,7 +328,7 @@ function Base.promote_rule(::Type{S}, ::Type{<:ProductSpace{S}}) where {S <: Ele end # ProductSpace to ElementarySpace -Base.convert(::Type{S}, P::ProductSpace{S, 0}) where {S <: ElementarySpace} = oneunit(S) +Base.convert(::Type{S}, P::ProductSpace{S, 0}) where {S <: ElementarySpace} = unitspace(S) Base.convert(::Type{S}, P::ProductSpace{S}) where {S <: ElementarySpace} = fuse(P.spaces...) # ElementarySpace to ProductSpace diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index b15216aa..60766673 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -66,13 +66,12 @@ function space end Return the total dimension of the vector space `V` as an Int. """ dim(::VectorSpace) -""" +@doc """ dual(V::VectorSpace) -> VectorSpace Return the dual space of `V`; also obtained via `V'`. This should satisfy `dual(dual(V)) == V`. It is assumed that `typeof(V) == typeof(V')`. -""" -function dual end +""" dual(::VectorSpace) # convenience definitions: Base.adjoint(V::VectorSpace) = dual(V) @@ -80,7 +79,7 @@ Base.adjoint(V::VectorSpace) = dual(V) """ isdual(V::ElementarySpace) -> Bool -Return wether an ElementarySpace `V` is normal or rather a dual space. Always returns +Return whether an ElementarySpace `V` is normal or rather a dual space. Always returns `false` for spaces where `V == dual(V)`. """ function isdual end @@ -120,22 +119,27 @@ Return the sum of all degeneracy dimensions of the vector space `V`. reduceddim(V::ElementarySpace) = sum(Base.Fix1(dim, V), sectors(V); init = 0) """ - oneunit(V::S) where {S<:ElementarySpace} -> S + unitspace(V::S) where {S<:ElementarySpace} -> S Return the corresponding vector space of type `S` that represents the trivial one-dimensional space, i.e. the space that is isomorphic to the corresponding field. Note that this is different from `one(V::S)`, which returns the empty product space -`ProductSpace{S,0}(())`. +`ProductSpace{S,0}(())`. `Base.oneunit` falls back to `unitspace`. """ -Base.oneunit(V::ElementarySpace) = oneunit(typeof(V)) +unitspace(V::ElementarySpace) = unitspace(typeof(V)) +Base.oneunit(V::ElementarySpace) = unitspace(V) +Base.oneunit(::Type{V}) where {V <: ElementarySpace} = unitspace(V) """ - zero(V::S) where {S<:ElementarySpace} -> S + zerospace(V::S) where {S<:ElementarySpace} -> S Return the corresponding vector space of type `S` that represents the zero-dimensional or empty space. -This is, with a slight abuse of notation, the zero element of the direct sum of vector spaces. +This is, with a slight abuse of notation, the zero element of the direct sum of vector spaces. +`Base.zero` falls back to `zerospace`. """ -Base.zero(V::ElementarySpace) = zero(typeof(V)) +zerospace(V::ElementarySpace) = zerospace(typeof(V)) +Base.zero(V::ElementarySpace) = zerospace(V) +Base.zero(::Type{V}) where {V <: ElementarySpace} = zerospace(V) """ ⊕(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} -> S diff --git a/src/tensors/braidingtensor.jl b/src/tensors/braidingtensor.jl index 44b1673c..9c92c67a 100644 --- a/src/tensors/braidingtensor.jl +++ b/src/tensors/braidingtensor.jl @@ -366,7 +366,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # f₀ = FusionTree{I}((), u, (), (), ()) # braidingtensor_levels = A.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindA[2]] > braidingtensor_levels[cindA[3]] @@ -385,10 +385,10 @@ end # f₁′′.innerlines[1] == u || continue # coeff = coeff′ * coeff′′ * sqrtdim(a) * sqrtdim(b) # if f₁′′.isdual[1] -# coeff *= frobeniusschur(a) +# coeff *= frobenius_schur_phase(a) # end # if f₁′′.isdual[3] -# coeff *= frobeniusschur(b) +# coeff *= frobenius_schur_phase(b) # end # f12 = (f₀, f₂′) # if @isdefined newtrees @@ -434,7 +434,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # f₀ = FusionTree{I}((), u, (), (), ()) # braidingtensor_levels = B.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindB[2]] > braidingtensor_levels[cindB[3]] @@ -453,10 +453,10 @@ end # f₂′′.innerlines[1] == u || continue # coeff = coeff′ * conj(coeff′′ * sqrtdim(a) * sqrtdim(b)) # if f₂′′.isdual[1] -# coeff *= conj(frobeniusschur(a)) +# coeff *= conj(frobenius_schur_phase(a)) # end # if f₂′′.isdual[3] -# coeff *= conj(frobeniusschur(b)) +# coeff *= conj(frobenius_schur_phase(b)) # end # f12 = (f₁′, f₀) # if @isdefined newtrees @@ -501,7 +501,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # braidingtensor_levels = A.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindA[2]] > braidingtensor_levels[cindA[3]] # for (f₁, f₂) in fusiontrees(B) @@ -517,7 +517,7 @@ end # f₁′′.innerlines[1] == u || continue # coeff = coeff′ * coeff′′ * sqrtdim(a) # if f₁′′.isdual[1] -# coeff *= frobeniusschur(a) +# coeff *= frobenius_schur_phase(a) # end # f₁′′′ = FusionTree{I}((b,), b, (f₁′′.isdual[3],), (), ()) # f12 = (f₁′′′, f₂′) @@ -563,7 +563,7 @@ end # rmul!(C, β) # end # I = sectortype(B) -# u = one(I) +# u = unit(I) # braidingtensor_levels = B.adjoint ? (1, 2, 2, 1) : (2, 1, 1, 2) # inv_braid = braidingtensor_levels[cindB[2]] > braidingtensor_levels[cindB[3]] # for (f₁, f₂) in fusiontrees(A) @@ -579,7 +579,7 @@ end # f₂′′.innerlines[1] == u || continue # coeff = coeff′ * conj(coeff′′ * sqrtdim(a)) # if f₂′′.isdual[1] -# coeff *= conj(frobeniusschur(a)) +# coeff *= conj(frobenius_schur_phase(a)) # end # f₂′′′ = FusionTree{I}((b,), b, (f₂′′.isdual[3],), (), ()) # f12 = (f₁′, f₂′′′) diff --git a/src/tensors/linalg.jl b/src/tensors/linalg.jl index 43758951..d09609ed 100644 --- a/src/tensors/linalg.jl +++ b/src/tensors/linalg.jl @@ -564,38 +564,44 @@ Compute the tensor product between two `AbstractTensorMap` instances, which resu new `TensorMap` instance whose codomain is `codomain(t1) ⊗ codomain(t2)` and whose domain is `domain(t1) ⊗ domain(t2)`. """ -function ⊗(t1::AbstractTensorMap, t2::AbstractTensorMap) - (S = spacetype(t1)) === spacetype(t2) || - throw(SpaceMismatch("spacetype(t1) ≠ spacetype(t2)")) - cod1, cod2 = codomain(t1), codomain(t2) - dom1, dom2 = domain(t1), domain(t2) - cod = cod1 ⊗ cod2 - dom = dom1 ⊗ dom2 - T = promote_type(scalartype(t1), scalartype(t2)) - t = zerovector!(similar(t1, T, cod ← dom)) - for (f1l, f1r) in fusiontrees(t1) - for (f2l, f2r) in fusiontrees(t2) +function ⊗(A::AbstractTensorMap, B::AbstractTensorMap) + (S = spacetype(A)) === spacetype(B) || throw(SpaceMismatch("incompatible space types")) + + # allocate destination with correct scalartype + pA = ((codomainind(A)..., domainind(A)...), ()) + pB = ((), (codomainind(B)..., domainind(B)...)) + NA = numind(A) + pAB = ( + (codomainind(A)..., (codomainind(B) .+ NA)...), + (domainind(A)..., (domainind(B) .+ NA)...), + ) + TC = TO.promote_contract(scalartype(A), scalartype(B)) + C = TO.tensoralloc_contract(TC, A, pA, false, B, pB, false, pAB, Val(false)) + zerovector!(C) + + # implement tensor product + for (f1l, f1r) in fusiontrees(A) + @inbounds a = A[f1l, f1r] + for (f2l, f2r) in fusiontrees(B) + @inbounds b = B[f2l, f2r] c1 = f1l.coupled # = f1r.coupled c2 = f2l.coupled # = f2r.coupled - for c in c1 ⊗ c2 - for μ in 1:Nsymbol(c1, c2, c) - for (fl, coeff1) in merge(f1l, f2l, c, μ) - for (fr, coeff2) in merge(f1r, f2r, c, μ) - d1 = dim(cod1, f1l.uncoupled) - d2 = dim(cod2, f2l.uncoupled) - d3 = dim(dom1, f1r.uncoupled) - d4 = dim(dom2, f2r.uncoupled) - m1 = sreshape(t1[f1l, f1r], (d1, 1, d3, 1)) - m2 = sreshape(t2[f2l, f2r], (1, d2, 1, d4)) - m = sreshape(t[fl, fr], (d1, d2, d3, d4)) - m .+= coeff1 .* conj(coeff2) .* m1 .* m2 - end + for c in c1 ⊗ c2, μ in 1:Nsymbol(c1, c2, c) + for (fl, coeff1) in merge(f1l, f2l, c, μ) + for (fr, coeff2) in merge(f1r, f2r, c, μ) + TO.tensorcontract!( + C[fl, fr], + A[f1l, f1r], pA, false, + B[f2l, f2r], pB, false, + pAB, + coeff1 * conj(coeff2), One() + ) end end end end end - return t + return C end # deligne product of tensors @@ -608,13 +614,13 @@ function ⊠(t1::AbstractTensorMap, t2::AbstractTensorMap) dom1 = domain(t1) ⊠ one(S2) t1′ = similar(t1, codom1 ← dom1) for (c, b) in blocks(t1) - copy!(block(t1′, c ⊠ one(I2)), b) + copy!(block(t1′, c ⊠ unit(I2)), b) end codom2 = one(S1) ⊠ codomain(t2) dom2 = one(S1) ⊠ domain(t2) t2′ = similar(t2, codom2 ← dom2) for (c, b) in blocks(t2) - copy!(block(t2′, one(I1) ⊠ c), b) + copy!(block(t2′, unit(I1) ⊠ c), b) end return t1′ ⊗ t2′ end diff --git a/src/tensors/tensor.jl b/src/tensors/tensor.jl index accada77..3f28f168 100644 --- a/src/tensors/tensor.jl +++ b/src/tensors/tensor.jl @@ -548,9 +548,9 @@ since it assumes a uniquely defined coupled charge. throw(ArgumentError("Number of sectors does not match.")) s₁ = TupleTools.getindices(sectors, codomainind(t)) s₂ = map(dual, TupleTools.getindices(sectors, domainind(t))) - c1 = length(s₁) == 0 ? one(I) : (length(s₁) == 1 ? s₁[1] : first(⊗(s₁...))) + c1 = length(s₁) == 0 ? unit(I) : (length(s₁) == 1 ? s₁[1] : first(⊗(s₁...))) @boundscheck begin - c2 = length(s₂) == 0 ? one(I) : (length(s₂) == 1 ? s₂[1] : first(⊗(s₂...))) + c2 = length(s₂) == 0 ? unit(I) : (length(s₂) == 1 ? s₂[1] : first(⊗(s₂...))) c2 == c1 || throw(SectorMismatch("Not a valid sector for this tensor")) hassector(codomain(t), s₁) && hassector(domain(t), s₂) end diff --git a/test/ad.jl b/test/ad.jl index 39bc9b16..553bca74 100644 --- a/test/ad.jl +++ b/test/ad.jl @@ -33,7 +33,7 @@ function ChainRulesTestUtils.test_approx( end # make sure that norms are computed correctly: -function FiniteDifferences.to_vec(t::TensorKit.SectorDict) +function FiniteDifferences.to_vec(t::TK.SectorDict) T = scalartype(valtype(t)) vec = mapreduce(vcat, t; init = T[]) do (c, b) return reshape(b, :) .* sqrt(dim(c)) @@ -43,7 +43,7 @@ function FiniteDifferences.to_vec(t::TensorKit.SectorDict) function from_vec(x_real) x = T <: Real ? x_real : reinterpret(T, x_real) ctr = 0 - return TensorKit.SectorDict( + return TK.SectorDict( c => ( n = length(b); b′ = reshape(view(x, ctr .+ (1:n)), size(b)) ./ sqrt(dim(c)); @@ -197,7 +197,7 @@ spacelist = ( for V in spacelist I = sectortype(eltype(V)) - Istr = TensorKit.type_repr(I) + Istr = TK.type_repr(I) eltypes = isreal(sectortype(eltype(V))) ? (Float64, ComplexF64) : (ComplexF64,) symmetricbraiding = BraidingStyle(sectortype(eltype(V))) isa SymmetricBraiding println("---------------------------------------") diff --git a/test/diagonal.jl b/test/diagonal.jl index 74214949..5a358236 100644 --- a/test/diagonal.jl +++ b/test/diagonal.jl @@ -32,7 +32,7 @@ diagspacelist = ( b2 = @constinferred block(t, first(blocksectors(t))) @test b1 == b2 @test eltype(bs) === Pair{typeof(c), typeof(b1)} - @test typeof(b1) === TensorKit.blocktype(t) + @test typeof(b1) === TK.blocktype(t) # basic linear algebra @test isa(@constinferred(norm(t)), real(T)) @test norm(t)^2 ≈ dot(t, t) diff --git a/test/fusiontrees.jl b/test/fusiontrees.jl index 0e0920eb..75bb76e9 100644 --- a/test/fusiontrees.jl +++ b/test/fusiontrees.jl @@ -2,9 +2,9 @@ println("------------------------------------") println("Fusion Trees") println("------------------------------------") ti = time() -@timedtestset "Fusion trees for $(TensorKit.type_repr(I))" verbose = true for I in +@timedtestset "Fusion trees for $(TK.type_repr(I))" verbose = true for I in sectorlist - Istr = TensorKit.type_repr(I) + Istr = TK.type_repr(I) N = 5 out = ntuple(n -> randsector(I), N) isdual = ntuple(n -> rand(Bool), N) @@ -26,7 +26,7 @@ ti = time() @test eval(Meta.parse(sprint(show, f))) == f end @testset "Fusion tree $Istr: constructor properties" begin - u = one(I) + u = unit(I) @constinferred FusionTree((), u, (), (), ()) @constinferred FusionTree((u,), u, (false,), (), ()) @constinferred FusionTree((u, u), u, (false, false), (), (1,)) @@ -130,7 +130,7 @@ ti = time() outgoing = (s, dual(s), s, dual(s), s, dual(s)) for bool in (true, false) isdual = (bool, !bool, bool, !bool, bool, !bool) - for f in fusiontrees(outgoing, one(s), isdual) + for f in fusiontrees(outgoing, unit(s), isdual) af = convert(Array, f) T = eltype(af) @@ -216,7 +216,7 @@ ti = time() end end end - @testset "Fusion tree $Istr: elementy artin braid" begin + @testset "Fusion tree $Istr: elementary artin braid" begin N = length(out) isdual = ntuple(n -> rand(Bool), N) for in in ⊗(out...) @@ -273,7 +273,7 @@ ti = time() end end @testset "Fusion tree $Istr: braiding and permuting" begin - f = rand(collect(fusiontrees(out, in, isdual))) + f = rand(collect(it)) p = tuple(randperm(N)...) ip = invperm(p) @@ -389,7 +389,7 @@ ti = time() f1 = rand(collect(fusiontrees(out, incoming, ntuple(n -> rand(Bool), N)))) f2 = rand(collect(fusiontrees(out[randperm(N)], incoming, ntuple(n -> rand(Bool), N)))) - @testset "Double fusion tree $Istr: repartioning" begin + @testset "Double fusion tree $Istr: repartitioning" begin for n in 0:(2 * N) d = @constinferred TK.repartition(f1, f2, $n) @test dim(incoming) ≈ @@ -446,12 +446,12 @@ ti = time() ip = invperm(p) ip1, ip2 = ip[1:N], ip[(N + 1):(2N)] - d = @constinferred TensorKit.permute(f1, f2, p1, p2) + d = @constinferred TK.permute(f1, f2, p1, p2) @test dim(incoming) ≈ sum(abs2(coef) * dim(f1.coupled) for ((f1, f2), coef) in d) d2 = Dict{typeof((f1, f2)), valtype(d)}() for ((f1′, f2′), coeff) in d - d′ = TensorKit.permute(f1′, f2′, ip1, ip2) + d′ = TK.permute(f1′, f2′, ip1, ip2) for ((f1′′, f2′′), coeff2) in d′ d2[(f1′′, f2′′)] = get(d2, (f1′′, f2′′), zero(coeff)) + coeff2 * coeff @@ -570,7 +570,7 @@ ti = time() @testset "Double fusion tree $Istr: planar trace" begin d1 = transpose(f1, f1, (N + 1, 1:N..., ((2N):-1:(N + 3))...), (N + 2,)) f1front, = TK.split(f1, N - 1) - T = typeof(Fsymbol(one(I), one(I), one(I), one(I), one(I), one(I))[1, 1, 1, 1]) + T = TensorKitSectors._Fscalartype(I) d2 = Dict{typeof((f1front, f1front)), T}() for ((f1′, f2′), coeff′) in d1 for ((f1′′, f2′′), coeff′′) in @@ -590,7 +590,7 @@ ti = time() end end end - TensorKit.empty_globalcaches!() + TK.empty_globalcaches!() end tf = time() printstyled( diff --git a/test/runtests.jl b/test/runtests.jl index 8fa1600a..3ae62e9b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,7 +3,8 @@ using TestExtras using Random using TensorKit using Combinatorics -using TensorKit: ProductSector, fusiontensor, pentagon_equation, hexagon_equation +using TensorKit: ProductSector, fusiontensor +using TensorKitSectors: TensorKitSectors using TensorOperations using Base.Iterators: take, product # using SUNRepresentations: SUNIrrep @@ -33,14 +34,14 @@ end function randsector(::Type{I}) where {I <: Sector} s = collect(smallset(I)) a = rand(s) - while a == one(a) # don't use trivial label + while isunit(a) # don't use trivial label a = rand(s) end return a end function hasfusiontensor(I::Type{<:Sector}) try - fusiontensor(one(I), one(I), one(I)) + fusiontensor(unit(I), unit(I), unit(I)) return true catch e if e isa MethodError diff --git a/test/spaces.jl b/test/spaces.jl index 1ca8eb70..3d1730f2 100644 --- a/test/spaces.jl +++ b/test/spaces.jl @@ -63,18 +63,18 @@ println("------------------------------------") @test @constinferred(sectortype(V)) == Trivial @test ((@constinferred sectors(V))...,) == (Trivial(),) @test length(sectors(V)) == 1 - @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(TK.hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test dim(@constinferred(zero(V))) == 0 - @test (sectors(zero(V))...,) == () - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test dim(@constinferred(zerospace(V))) == 0 + @test (sectors(zerospace(V))...,) == () + @test @constinferred(TK.axes(V)) == Base.OneTo(d) @test ℝ^d == ℝ[](d) == CartesianSpace(d) == typeof(V)(d) W = @constinferred ℝ^1 - @test @constinferred(oneunit(V)) == W == oneunit(typeof(V)) - @test @constinferred(zero(V)) == ℝ^0 == zero(typeof(V)) - @test @constinferred(⊕(V, zero(V))) == V + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) + @test @constinferred(zerospace(V)) == ℝ^0 == zerospace(typeof(V)) + @test @constinferred(⊕(V, zerospace(V))) == V @test @constinferred(⊕(V, V)) == ℝ^(2d) - @test @constinferred(⊕(V, oneunit(V))) == ℝ^(d + 1) + @test @constinferred(⊕(V, unitspace(V))) == ℝ^(d + 1) @test @constinferred(⊕(V, V, V, V)) == ℝ^(4d) @test @constinferred(fuse(V, V)) == ℝ^(d^2) @test @constinferred(fuse(V, V', V, V')) == ℝ^(d^4) @@ -109,16 +109,16 @@ println("------------------------------------") @test @constinferred(sectortype(V)) == Trivial @test ((@constinferred sectors(V))...,) == (Trivial(),) @test length(sectors(V)) == 1 - @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(TK.hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test dim(@constinferred(zero(V))) == 0 - @test (sectors(zero(V))...,) == () - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test dim(@constinferred(zerospace(V))) == 0 + @test (sectors(zerospace(V))...,) == () + @test @constinferred(TK.axes(V)) == Base.OneTo(d) @test ℂ^d == Vect[Trivial](d) == Vect[](Trivial() => d) == ℂ[](d) == typeof(V)(d) W = @constinferred ℂ^1 - @test @constinferred(oneunit(V)) == W == oneunit(typeof(V)) - @test @constinferred(zero(V)) == ℂ^0 == zero(typeof(V)) - @test @constinferred(⊕(V, zero(V))) == V + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) + @test @constinferred(zerospace(V)) == ℂ^0 == zerospace(typeof(V)) + @test @constinferred(⊕(V, zerospace(V))) == V @test @constinferred(⊕(V, V)) == ℂ^(2d) @test_throws SpaceMismatch (⊕(V, V')) # promote_except = ErrorException("promotion of types $(typeof(ℝ^d)) and " * @@ -126,7 +126,7 @@ println("------------------------------------") # @test_throws promote_except (⊕(ℝ^d, ℂ^d)) @test_throws ErrorException (⊗(ℝ^d, ℂ^d)) @test @constinferred(⊕(V, V)) == ℂ^(2d) - @test @constinferred(⊕(V, oneunit(V))) == ℂ^(d + 1) + @test @constinferred(⊕(V, unitspace(V))) == ℂ^(d + 1) @test @constinferred(⊕(V, V, V, V)) == ℂ^(4d) @test @constinferred(fuse(V, V)) == ℂ^(d^2) @test @constinferred(fuse(V, V', V, V')) == ℂ^(d^4) @@ -151,10 +151,10 @@ println("------------------------------------") @test isdual(V') @test !isdual(conj(V)) @test isdual(conj(V')) - @test !TensorKit.isconj(V) - @test !TensorKit.isconj(V') - @test TensorKit.isconj(conj(V)) - @test TensorKit.isconj(conj(V')) + @test !TK.isconj(V) + @test !TK.isconj(V') + @test TK.isconj(conj(V)) + @test TK.isconj(conj(V')) @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @test !isa(InnerProductStyle(V), HasInnerProduct) @@ -163,20 +163,20 @@ println("------------------------------------") @test @constinferred(dual(V)) != @constinferred(conj(V)) != V @test @constinferred(field(V)) == ℂ @test @constinferred(sectortype(V)) == Trivial - @test @constinferred(TensorKit.hassector(V, Trivial())) + @test @constinferred(TK.hassector(V, Trivial())) @test @constinferred(dim(V)) == d == @constinferred(dim(V, Trivial())) - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(d) + @test @constinferred(TK.axes(V)) == Base.OneTo(d) end - @timedtestset "ElementarySpace: $(TensorKit.type_repr(Vect[I]))" for I in sectorlist + @timedtestset "ElementarySpace: $(TK.type_repr(Vect[I]))" for I in sectorlist if Base.IteratorSize(values(I)) === Base.IsInfinite() - set = unique(vcat(one(I), [randsector(I) for k in 1:10])) + set = unique(vcat(unit(I), [randsector(I) for k in 1:10])) gen = (c => 2 for c in set) else gen = (values(I)[k] => (k + 1) for k in 1:length(values(I))) end V = GradedSpace(gen) - @test eval(Meta.parse(TensorKit.type_repr(typeof(V)))) == typeof(V) + @test eval(Meta.parse(TK.type_repr(typeof(V)))) == typeof(V) @test eval(Meta.parse(sprint(show, V))) == V @test eval(Meta.parse(sprint(show, V'))) == V' @test V' == GradedSpace(gen; dual = true) @@ -202,14 +202,14 @@ println("------------------------------------") @test eval(Meta.parse(sprint(show, V))) == V @test eval(Meta.parse(sprint(show, typeof(V)))) == typeof(V) # space with no sectors - @test dim(@constinferred(zero(V))) == 0 + @test dim(@constinferred(zerospace(V))) == 0 # space with a single sector - W = @constinferred GradedSpace(one(I) => 1) - @test W == GradedSpace(one(I) => 1, randsector(I) => 0) - @test @constinferred(oneunit(V)) == W == oneunit(typeof(V)) - @test @constinferred(zero(V)) == GradedSpace(one(I) => 0) + W = @constinferred GradedSpace(unit(I) => 1) + @test W == GradedSpace(unit(I) => 1, randsector(I) => 0) + @test @constinferred(unitspace(V)) == W == unitspace(typeof(V)) + @test @constinferred(zerospace(V)) == GradedSpace(unit(I) => 0) # randsector never returns trivial sector, so this cannot error - @test_throws ArgumentError GradedSpace(one(I) => 1, randsector(I) => 0, one(I) => 3) + @test_throws ArgumentError GradedSpace(unit(I) => 1, randsector(I) => 0, unit(I) => 3) @test eval(Meta.parse(sprint(show, W))) == W @test isa(V, VectorSpace) @test isa(V, ElementarySpace) @@ -221,17 +221,17 @@ println("------------------------------------") @test @constinferred(field(V)) == ℂ @test @constinferred(sectortype(V)) == I slist = @constinferred sectors(V) - @test @constinferred(TensorKit.hassector(V, first(slist))) + @test @constinferred(TK.hassector(V, first(slist))) @test @constinferred(dim(V)) == sum(dim(s) * dim(V, s) for s in slist) @test @constinferred(reduceddim(V)) == sum(dim(V, s) for s in slist) @constinferred dim(V, first(slist)) if hasfusiontensor(I) - @test @constinferred(TensorKit.axes(V)) == Base.OneTo(dim(V)) + @test @constinferred(TK.axes(V)) == Base.OneTo(dim(V)) end - @test @constinferred(⊕(V, zero(V))) == V + @test @constinferred(⊕(V, zerospace(V))) == V @test @constinferred(⊕(V, V)) == Vect[I](c => 2dim(V, c) for c in sectors(V)) @test @constinferred(⊕(V, V, V, V)) == Vect[I](c => 4dim(V, c) for c in sectors(V)) - @test @constinferred(⊕(V, oneunit(V))) == Vect[I](c => isone(c) + dim(V, c) for c in sectors(V)) + @test @constinferred(⊕(V, oneunit(V))) == Vect[I](c => isunit(c) + dim(V, c) for c in sectors(V)) @test @constinferred(fuse(V, oneunit(V))) == V d = Dict{I, Int}() for a in sectors(V), b in sectors(V) @@ -248,7 +248,7 @@ println("------------------------------------") @test V == @constinferred infimum(V, ⊕(V, V)) @test V ≺ ⊕(V, V) @test !(V ≻ ⊕(V, V)) - @test infimum(V, GradedSpace(one(I) => 3)) == GradedSpace(one(I) => 2) + @test infimum(V, GradedSpace(unit(I) => 3)) == GradedSpace(unit(I) => 2) @test_throws SpaceMismatch (⊕(V, V')) end @@ -273,10 +273,10 @@ println("------------------------------------") @test @constinferred(⊗(V1 ⊗ V2, V3 ⊗ V4)) == P @test @constinferred(⊗(V1, V2, V3 ⊗ V4)) == P @test @constinferred(⊗(V1, V2 ⊗ V3, V4)) == P - @test V1 * V2 * oneunit(V1) * V3 * V4 == + @test V1 * V2 * unitspace(V1) * V3 * V4 == @constinferred(insertleftunit(P, 3)) == @constinferred(insertrightunit(P, 2)) - @test @constinferred(removeunit(V1 * V2 * oneunit(V1)' * V3 * V4, 3)) == P + @test @constinferred(removeunit(V1 * V2 * unitspace(V1)' * V3 * V4, 3)) == P @test fuse(V1, V2', V3) ≅ V1 ⊗ V2' ⊗ V3 @test fuse(V1, V2', V3) ≾ V1 ⊗ V2' ⊗ V3 @test fuse(V1, V2', V3) ≿ V1 ⊗ V2' ⊗ V3 @@ -334,7 +334,7 @@ println("------------------------------------") @test @constinferred(*(V1, V2, V3)) == P @test @constinferred(⊗(V1, V2, V3)) == P @test @constinferred(adjoint(P)) == dual(P) == V3' ⊗ V2' ⊗ V1' - @test V1 * V2 * oneunit(V1)' * V3 == + @test V1 * V2 * unitspace(V1)' * V3 == @constinferred(insertleftunit(P, 3; conj = true)) == @constinferred(insertrightunit(P, 2; conj = true)) @test P == @constinferred(removeunit(insertleftunit(P, 3), 3)) @@ -397,7 +397,7 @@ println("------------------------------------") @timedtestset "HomSpace" begin for (V1, V2, V3, V4, V5) in (Vtr, Vℤ₃, VSU₂) - W = TensorKit.HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) + W = TK.HomSpace(V1 ⊗ V2, V3 ⊗ V4 ⊗ V5) @test W == (V3 ⊗ V4 ⊗ V5 → V1 ⊗ V2) @test W == (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) @test W' == (V1 ⊗ V2 → V3 ⊗ V4 ⊗ V5) @@ -414,25 +414,25 @@ println("------------------------------------") @test W == deepcopy(W) @test W == @constinferred permute(W, ((1, 2), (3, 4, 5))) @test permute(W, ((2, 4, 5), (3, 1))) == (V2 ⊗ V4' ⊗ V5' ← V3 ⊗ V1') - @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred TensorKit.compose(W, W') - @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ oneunit(V5)) == + @test (V1 ⊗ V2 ← V1 ⊗ V2) == @constinferred TK.compose(W, W') + @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)) == @constinferred(insertleftunit(W)) == @constinferred(insertrightunit(W)) @test @constinferred(removeunit(insertleftunit(W), $(numind(W) + 1))) == W - @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ oneunit(V5)') == + @test (V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5 ⊗ unitspace(V5)') == @constinferred(insertleftunit(W; conj = true)) == @constinferred(insertrightunit(W; conj = true)) - @test (oneunit(V1) ⊗ V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) == + @test (unitspace(V1) ⊗ V1 ⊗ V2 ← V3 ⊗ V4 ⊗ V5) == @constinferred(insertleftunit(W, 1)) == @constinferred(insertrightunit(W, 0)) - @test (V1 ⊗ V2 ⊗ oneunit(V1) ← V3 ⊗ V4 ⊗ V5) == + @test (V1 ⊗ V2 ⊗ unitspace(V1) ← V3 ⊗ V4 ⊗ V5) == @constinferred(insertrightunit(W, 2)) - @test (V1 ⊗ V2 ← oneunit(V1) ⊗ V3 ⊗ V4 ⊗ V5) == + @test (V1 ⊗ V2 ← unitspace(V1) ⊗ V3 ⊗ V4 ⊗ V5) == @constinferred(insertleftunit(W, 3)) @test @constinferred(removeunit(insertleftunit(W, 3), 3)) == W - @test @constinferred(insertrightunit(one(V1) ← V1, 0)) == (oneunit(V1) ← V1) + @test @constinferred(insertrightunit(one(V1) ← V1, 0)) == (unitspace(V1) ← V1) @test_throws BoundsError insertleftunit(one(V1) ← V1, 0) end end - TensorKit.empty_globalcaches!() + TK.empty_globalcaches!() end diff --git a/test/tensors.jl b/test/tensors.jl index 73462306..c0c1d90b 100644 --- a/test/tensors.jl +++ b/test/tensors.jl @@ -17,7 +17,7 @@ end for V in spacelist I = sectortype(first(V)) - Istr = TensorKit.type_repr(I) + Istr = TK.type_repr(I) println("---------------------------------------") println("Tensors with symmetry: $Istr") println("---------------------------------------") @@ -42,7 +42,7 @@ for V in spacelist b2 = @constinferred block(t, first(blocksectors(t))) @test b1 == b2 @test eltype(bs) === Pair{typeof(c), typeof(b1)} - @test typeof(b1) === TensorKit.blocktype(t) + @test typeof(b1) === TK.blocktype(t) @test typeof(c) === sectortype(t) end end @@ -80,7 +80,7 @@ for V in spacelist end end for T in (Int, Float32, ComplexF64) - t = randn(T, V1 ⊗ V2 ← zero(V1)) + t = randn(T, V1 ⊗ V2 ← zerospace(V1)) a = convert(Array, t) @test norm(a) == 0 end @@ -104,7 +104,7 @@ for V in spacelist b2 = @constinferred block(t', first(blocksectors(t'))) @test b1 == b2 @test eltype(bs) === Pair{typeof(c), typeof(b1)} - @test typeof(b1) === TensorKit.blocktype(t') + @test typeof(b1) === TK.blocktype(t') @test typeof(c) === sectortype(t) # linear algebra @test isa(@constinferred(norm(t)), real(T)) @@ -550,7 +550,7 @@ for V in spacelist @test t3 ≈ t4 end end - TensorKit.empty_globalcaches!() + TK.empty_globalcaches!() end @timedtestset "Deligne tensor product: test via conversion" begin