diff --git a/.github/workflows/PR_comment.yml b/.github/workflows/PR_comment.yml new file mode 100644 index 00000000..1c193280 --- /dev/null +++ b/.github/workflows/PR_comment.yml @@ -0,0 +1,17 @@ +name: Docs preview comment +on: + pull_request: + types: [labeled] + +permissions: + pull-requests: write +jobs: + pr_comment: + runs-on: ubuntu-latest + steps: + - name: Create PR comment + if: github.event_name == 'pull_request' && github.repository == github.event.pull_request.head.repo.full_name && github.event.label.name == 'documentation' + uses: thollander/actions-comment-pull-request@v3 + with: + message: 'After the build completes, the updated documentation will be available [here](https://quantumkithub.github.io/TensorKitSectors.jl/previews/PR${{ github.event.number }}/)' + comment-tag: 'preview-doc' diff --git a/docs/Project.toml b/docs/Project.toml index dcc80af7..707c82f4 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,7 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" TensorKitSectors = "13a9c161-d5da-41f0-bcbd-e1a08ae0647f" +TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2" [compat] Documenter = "1" diff --git a/docs/make.jl b/docs/make.jl index 13f2ec05..af258e46 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -19,6 +19,41 @@ makedocs(; ), pages = [ "Home" => "index.md", + "Sector Interface" => [ + "Overview" => "interface/overview.md", + "Required Methods" => "interface/required.md", + "Optional Methods" => "interface/optional.md", + "Traits and Styles" => "interface/traits.md", + "Implementation Guidelines" => "interface/guidelines.md", + ], + "Sector Types" => [ + "Overview" => "sectors.md", + "Abelian Groups" => [ + "Trivial" => "sectors/abelian/trivial.md", + "ℤₙ (Cyclic)" => "sectors/abelian/zn.md", + "U₁" => "sectors/abelian/u1.md", + ], + "Non-Abelian Groups" => [ + "SU₂" => "sectors/nonabelian/su2.md", + "CU₁" => "sectors/nonabelian/cu1.md", + "Dₙ (Dihedral)" => "sectors/nonabelian/dn.md", + "A₄ (Alternating)" => "sectors/nonabelian/a4.md", + ], + "Anyonic Sectors" => [ + "PlanarTrivial" => "sectors/anyons/planartrivial.md", + "Fibonacci" => "sectors/anyons/fibonacci.md", + "Ising" => "sectors/anyons/ising.md", + ], + "Fermionic Sectors" => [ + "Fermion Parity" => "sectors/fermions/parity.md", + "Fermion Number" => "sectors/fermions/number.md", + "Fermion Spin" => "sectors/fermions/spin.md", + ], + "Composite Sectors" => [ + "Product" => "sectors/composite/product.md", + "Time-Reversed" => "sectors/composite/timereversed.md", + ], + ], "Library" => "lib.md", ], checkdocs = :exports, diff --git a/docs/src/index.md b/docs/src/index.md index fb7fdea5..60e387d1 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -2,11 +2,9 @@ A Julia package for working with objects in fusion categories. -This package provides functionality for defining objects in fusion categories, along with -their topological data. This includes the fusion rules, the associators, and the braiding. -In particular, this is the data that is needed to define (symmetric) tensors, which are -defined over vector spaces graded by these objects. For the full functionality, we refer to -[TensorKit.jl](https://github.com/QuantumKitHub/TensorKit.jl) and [its -documentation](https://quantumkithub.github.io/TensorKit.jl/stable/). +This package provides functionality for defining objects in fusion categories, along with their topological data. +This includes the fusion rules, the associators, and the braiding. +In particular, this is the data that is needed to define (symmetric) tensors, which are defined over vector spaces graded by these objects. +For the full functionality, we refer to [TensorKit.jl](https://github.com/QuantumKitHub/TensorKit.jl) and [its documentation](https://quantumkithub.github.io/TensorKit.jl/stable/). Install via the package manager. diff --git a/docs/src/interface/guidelines.md b/docs/src/interface/guidelines.md new file mode 100644 index 00000000..518518fb --- /dev/null +++ b/docs/src/interface/guidelines.md @@ -0,0 +1,63 @@ +```@meta +CollapsedDocStrings = true +``` + +# Implementation Guidelines + +This section provides practical advice for implementing new sector types efficiently and correctly. + +## Helper Type: SectorProductIterator + +Instead of materializing all fusion outputs in a tuple or array, one can use `SectorProductIterator` for type-stable, lazy iteration. + +```@docs; canonical = false +TensorKitSectors.SectorProductIterator +``` + +This is enabled by default, and new sectors should provide implementations for the following functions: + +```julia +Base.iterate(ab::SectorProductIterator{I}, [state]) = ... +# optional optimizations: +Base.IteratorSize(::Type{SectorProductIterator{I}}) = HasLength() +Base.length(ab::SectorProductIterator{I}) = ... +``` + +This has the benefit of helping with type stability, and has a pretty-printing overload: + +```@example +using TensorKitSectors # hide +SU2Irrep(1) ⊗ SU2Irrep(1) +``` + +## Shape of Topological Data + +The size of [`Fsymbol`](@ref) and [`Rsymbol`](@ref) data depends on the fusion multiplicities ([`Nsymbol`](@ref)). +For multiplicity‑free sectors scalars are sufficient; for generic fusion we need arrays. +Therefore, we distinguish the behavior through the [`FusionStyle`](@ref). + + +### [`MultiplicityFreeFusion`](@ref) + +- `Nsymbol(a, b, c)`: Returns a `Bool`. +- `Fsymbol(a, b, c, d, e, f)`: Returns a scalar of type `sectorscalartype(I)`. +- `Rsymbol(a, b, c)`: Returns a scalar of type `sectorscalartype(I)`. + +Additionally, if the [`Fsymbol`](@ref) and [`Rsymbol`](@ref) do not correspond to valid fusion channels, the result is ``0``. +Therefore, computing the number of valid fusion channels for both diagrams as the product of the relevant [`Nsymbol`](@ref)s, we have: + +```math +\left(N^{ab}_e N^{ec}_d = 0 \lor N^{af}_d N^{bc}_f = 0\right) \implies (F_{abc}^d)^e_f = 0 +``` + +```math +N^{ab}_c = 0 \implies R^{ab}_c = 0 +``` + +### [`GenericFusion`](@ref) + +- `Nsymbol(a, b, c)`: Returns a positive `Integer`. +- `Fsymbol(a, b, c, d, e, f)`: Returns a ``N^{ab}_e \times N^{ec}_d \times N^{af}_d \times N^{bc}_f`` array of `sectorscalartype(I)` elements. +- `Rsymbol(a, b, c)`: Returns a ``N^{ab}_c \times N^{ba}_c`` array of `sectorscalartype(I)` elements. + +Here invalid fusion channels will necessarily lead to empty arrays. diff --git a/docs/src/interface/optional.md b/docs/src/interface/optional.md new file mode 100644 index 00000000..124b9ae3 --- /dev/null +++ b/docs/src/interface/optional.md @@ -0,0 +1,96 @@ +```@meta +CollapsedDocStrings = true +``` + +# Optional Methods + +The following methods have default implementations but can be overridden for performance or to provide additional functionality. + +## Quantum Dimensions + +The quantum dimension of a sector is a fundamental invariant that determines the behavior of fusions and braiding. +The default implementation extracts the dimension from [`Fsymbol`](@ref) via the quantum dimension formula. + +```math +d_a = \left| \frac{1}{(F_{a \bar{a} a}^a)^{1_a}_{_a1} } \right| +``` + +For many common sectors, however, the dimension is known directly from representation theory. +In these cases, it can be beneficial to overload [`dim`](@ref) to bypass computing F-symbols, either for performance reasons or to enforce tighter output types. +For example, dimensions of irreducible representations of groups are always integers. + +```@docs; canonical = false +dim +sqrtdim +invsqrtdim +``` + +## Frobenius-Schur Indicators + +The Frobenius-Schur indicator and phase characterize the self-duality properties of sectors. + +```math +\kappa_a = \text{sign}\left( (F_{a \bar{a} a}^a)^{1_a}_{_a1} \right) +``` + +The indicator distinguishes real, complex, and quaternionic representations. +The phase is the category-theoretic version that appears in line bending operations. + +```@docs; canonical = false +frobenius_schur_indicator +frobenius_schur_phase +``` + +## Scalar Type + +Various utility functions exist for determining what number type is used in various parts of the topological data. + +```@docs; canonical = false +fusionscalartype +braidingscalartype +dimscalartype +sectorscalartype +``` + +!!! note + While there is a fallback definition that tries to determine the result from computing the functions on the unit sector, + it is often a good idea to define this method explicitly to avoid depending on compiler heuristics to constant-fold these calls. + +## Topological Data Symbols + +The [`Asymbol`](@ref), [`Bsymbol`](@ref) and [`twist`](@ref) are derived from F- and R-symbols but are often occurring combinations. +The A-symbol and B-symbol relate different ways of bending strands, while the twist is the topological spin (quantum dimension phase) of a sector. + +The A-symbol ``A^{ab}_c`` relates splitting and fusion vertices: +```math +A^{ab}_c = \sqrt{\frac{d_a d_b}{d_c}} \overline{\kappa_a} (F_{\bar{a} a b}^b)^1_c +``` + +The B-symbol ``B^{ab}_c`` relates splitting and fusion vertices: +```math +B^{ab}_c = \sqrt{\frac{d_a d_b}{d_c}} (F_{a b \bar{b}}^a)^c_1 +``` + +The twist ``\theta_a`` of a sector is the topological spin phase, computed as the trace of the R-matrix for braiding a sector with itself: +```math +\theta_a = \frac{1}{d_a} \sum_{b \in a \otimes a} d_b \text{tr}(R^{aa}_b) +``` + +```@docs; canonical = false +Asymbol +Bsymbol +twist +``` + +## Fusion Basis + +The fusion tensor provides explicit matrix elements for the tensor product of representations. +It is a rank-4 array whose components are the Clebsch-Gordan coefficients for fusing sector `a` and `b` into `c`. +The fusion tensor is not uniquely determined by topological data alone, instead the topological data can be extracted from it when it is available. +However, there is not always a concrete representation of these tensors in terms of simple `Array` objects, and the exact representation is not important for TensorKit.jl. +For this reason, it is optional: TensorKit can work with the topological data alone. +Note however that they are still required whenever we want to convert symmetric tensors to and from dense arrays. + +```@docs; canonical = false +fusiontensor +``` diff --git a/docs/src/interface/overview.md b/docs/src/interface/overview.md new file mode 100644 index 00000000..6c06cb3f --- /dev/null +++ b/docs/src/interface/overview.md @@ -0,0 +1,32 @@ +# Sector Interface + +## Introduction + +A `Sector` is a label for the different *symmetry charges* or *quantum numbers* that organize graded vector spaces. +In particular, these are the labels use to decompose vector spaces as: + +```math +V = \bigoplus_{a \in \text{Sectors}} \mathbb{C}^{n_a} \otimes V_a +``` + +where ``n_a`` is the multiplicity of sector ``a`` and ``V_a`` is the associated vector space. + +Sectors encode the structural rules TensorKit needs: +- **Objects**: the labels themselves (irreps, anyons, …) +- **Fusion rules**: which labels appear in ``a \otimes b \rightarrow \bigoplus_c N^{ab}_c c`` +- **Associativity**: how different parenthesizations are related +- **Braiding**: how labels behave under exchange (if supported) + +More rigorously, a `Sector` represents the isomorphism classes of simple objects in unitary and pivotal fusion categories. +This package defines an interface for accessing the topological data that is associated to these categories. +This section explains the required and optional methods needed to create a new sector type. +Once this interface is fulfilled, TensorKit.jl will create symmetric tensors that correspond to the implemented sector. + +## Organization + +The interface documentation is divided into several pages: + +- **[Required Methods](required.md)**: The minimum interface needed for a valid sector type +- **[Optional Methods](optional.md)**: Additional methods with default implementations that can be specialized +- **[Traits and Styles](traits.md)**: Compile-time properties that control behavior and optimizations +- **[Implementation Guidelines](guidelines.md)**: Practical advice and helper types for implementing sectors diff --git a/docs/src/interface/required.md b/docs/src/interface/required.md new file mode 100644 index 00000000..7a427c75 --- /dev/null +++ b/docs/src/interface/required.md @@ -0,0 +1,151 @@ +```@meta +CollapsedDocStrings = true +``` + +# Required Methods + +The following methods **must** be implemented for any new sector type `I <: Sector`. +These methods are grouped by functionality to help understand their purpose. + +## Defining the Set of Sectors + +A sector type `I <: Sector` represents the set of all labels that can be used to grade a vector space. +This corresponds to all irreducible representations of a group, or all simple objects in a fusion category. +The first requirement is making this set **enumerable** through the iterator interface. + +The set of all sector values is obtained via `values(I)`, which returns a `SectorValues{I}()` singleton type by default. +This `SectorValues{I}()` must be iterable, enabling enumeration of all sectors of type `I`. +To do so, one needs to implement the [Iteration interface](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-iteration). +In particular, we require the following methods to be defined: + +- `Base.iterate(::SectorValues{I}, state...)` — Iterate over all possible values of sector type `I`. +- `Base.IteratorSize(::Type{SectorValues{I}})` — Specify whether the number of sector values is known, finite, or infinite. + +Here the `IteratorSize` is either `HasLength()`, `SizeUnknown()` or `IsInfinite()`. +If the length is known (`HasLength()`), three further methods are required so that sectors can be indexed by position: + +- `Base.length(::SectorValues{I})` — Return the number of sectors. +- `Base.getindex(::SectorValues{I}, i::Int)` — Access the `i`-th sector value. +- `findindex(::SectorValues{I}, c::I)` — Find the index of sector `c`. + +!!! note + The choice of `IteratorSize` determines how associative containers are constructed in e.g. a `GradedSpace`. + In the case of `HasLength()`, an implicit mapping between the values and the position is used to enable storage through `Tuple`s or `Vector`s. + In the other cases one has to resort to `AbstractDict`-like containers. + If the set of simple objects is sufficiently large, it might be beneficial to register its length as `SizeUnknown()` to avoid putting too much pressure on the compiler. + + +## Fusion Structure + +The fusion structure describes how sectors combine when taking tensor products. +In code, `a ⊗ b` returns the allowed output labels, and `Nsymbol(a, b, c)` tells you whether (or how many times) `c` appears. + +The formal decomposition is: +```math +a ⊗ b = \bigoplus_c N^{ab}_c \, c +``` +where ``N^{ab}_c`` is the fusion multiplicity. + +The fusion structure is defined through three related methods: + +```@docs; canonical = false +⊗ +Nsymbol +FusionStyle +``` + + +## Identity and Duality + +Every sector type has a unit (identity) label and a notion of dual (conjugate). +`unit` returns the identity label, and `dual` returns the label that fuses with `a` to give the unit. + +The unit ``\mathbb{1}`` acts as the identity under fusion: +```math +\mathbb{1} ⊗ a ≅ a ≅ a ⊗ \mathbb{1} +``` +The dual of a sector ``a`` is the unique sector ``\bar{a}`` such that: +```math +N^{a\bar{a}}_{\mathbb{1}} = 1 \quad \text{and} \quad N^{\bar{a}a}_{\mathbb{1}} = 1 +``` + +```@docs; canonical = false +unit +dual +``` + +**Multifusion categories** can have multiple units and may distinguish between left and right units. +For such cases, additional methods are available: + +```@docs; canonical = false +allunits +leftunit +rightunit +``` + +For regular fusion categories the unit object is unique, such that `unit`, `leftunit` and `rightunit` all coincide. + + +## Associativity + +The associativity of the fusion tensor product tells us how to relate the basis states ``|(a ⊗ b → e) ⊗ c → d\rangle`` to the states ``|a ⊗ (b ⊗ c → f) → d\rangle``. +This is encoded in the F-symbols, which give the coefficients to transform the different ways of fusing three sectors to one. + +```@docs; canonical = false +Fsymbol +``` + +Formally, the F-symbol ``F^{abc}_d`` with intermediate sectors ``e`` and ``f`` is a linear transformation between the two different parenthesizations: + +```math +(F_{abc}^d)^e_f : (a ⊗ b → e) ⊗ c → d \quad \longrightarrow \quad a ⊗ (b ⊗ c → f) → d +``` + +For sectors with `UniqueFusion` or `SimpleFusion`, the F-symbol is a scalar `<:Number`. +For `GenericFusion`, it is a rank-4 tensor with indices corresponding to the multiplicity labels of each fusion vertex. + +The F-symbols must satisfy the **pentagon equation** for every choice of sectors: + +```math +(F_{fcd}^e)^g_h (F_{abh}^e)^f_i = (F_{abc}^g)^f_j (F_{ajd}^e)^g_i (F_{bcd}^i)^j_h +``` + +This ensures that all ways of reassociating four tensor factors ``(((a ⊗ b) ⊗ c) ⊗ d)`` to ``(a ⊗ (b ⊗ (c ⊗ d)))`` give the same result, regardless of the sequence of reassociations. + + +## Braiding + +Sectors can have a braiding structure that describes the effect of exchanging two tensor factors. +The braiding is encoded in the R-symbol ``R^{ab}_c``, which is a linear transformation between the fusion channels ``a ⊗ b → c`` and ``b ⊗ a → c``. +For sectors with `UniqueFusion` or `SimpleFusion`, the R-symbol is a complex phase. +For `GenericFusion`, it is a square matrix relating the multiplicity spaces of the two fusion orders. + +The R-symbols must satisfy the **hexagon equations** together with the F-symbols: + +```math +R^{cd}_e (\overline{F}_{dab}^e)^g_c \overline{R}^{da}_g = (F_{abd}^e)^c_f R^{bd}_f (\overline{F}_{adb}^e)^g_f +``` + +and the analogous equation with ``a`` and ``b`` swapped. +These ensure that the braiding is compatible with the associativity encoded by F-symbols. + +The `BraidingStyle` trait categorizes behavior into four classes: +- `NoBraiding()` for planar categories where braiding is undefined +- `Bosonic()` for symmetric braiding with trivial twist (all R-symbols square to identity, all twists equal +1) +- `Fermionic()` for symmetric braiding with fermion parity (twists can be ±1) +- `Anyonic()` for general braiding with arbitrary phases or non-symmetric exchange + +```@docs; canonical = false +Rsymbol +BraidingStyle +``` + + +## Utility Methods + +Sectors must support a deterministic ordering and hashing so they can be used as dictionary keys, sorted collections, and canonical fusion outputs. +One should keep the order consistent with `values(I)`, i.e. the enumeration of objects should happen in a sorted fashion. +To achieve this, we must have + +- `Base.isless(::Sector, ::Sector)` — Define an order on the sectors. +- `Base.hash(::Sector, h::UInt)` — Associate a hash value with a sector. diff --git a/docs/src/interface/traits.md b/docs/src/interface/traits.md new file mode 100644 index 00000000..a863a240 --- /dev/null +++ b/docs/src/interface/traits.md @@ -0,0 +1,95 @@ +```@meta +CollapsedDocStrings = true + +DocTestSetup = quote + using TensorKitSectors +end +``` + +# Traits and Styles + +Traits define compile-time properties that can be assumed about a sector type. +They control behavior and enable optimizations. + +## FusionStyle + +The `FusionStyle` trait indicates how many outputs to expect when fusing two sectors. + +```@docs; canonical = false +FusionStyle +UniqueFusion +SimpleFusion +GenericFusion +``` + +This enables various optimizations for different cases. +Firstly, since the shape (size of the arrays) of the topological data is determined by combinations of the [`Nsymbol`](@ref), for `UniqueFusion` and `SimpleFusion` we can use scalar quantities instead of arrays. +Secondly, in the `UniqueFusion` case, there is only a single channel for ``a \otimes b \otimes c \otimes \ldots``, avoiding the need to iterate through all options. + +It is additionally possible to combine fusion styles through the `&` operator, which returns the style with the least assumptions. +For example: + +```jldoctest +julia> UniqueFusion() & SimpleFusion() +SimpleFusion() + +julia> GenericFusion() & UniqueFusion() +GenericFusion() +``` + +Finally, some predefined combinations that appear often have dedicated names: + +```@docs; canonical = false +MultipleFusion +MultiplicityFreeFusion +``` + +## BraidingStyle + +The `BraidingStyle` describes whether and how exchange of sectors is defined. +It determines how TensorKit interprets [`Rsymbol`](@ref) and [`twist`](@ref). + +```@docs; canonical = false +BraidingStyle +NoBraiding +Bosonic +Fermionic +Anyonic +``` + +Additionally, this dictates whether or not permutations are sufficient to specify generic exchanges, or if a full braid group representation is needed. + +It is also possible to combine braiding styles through the `&` operator, which returns the style with the least assumptions. +For example: + +```jldoctest +julia> Bosonic() & Fermionic() +Fermionic() + +julia> Fermionic() & Anyonic() +Anyonic() + +julia> Bosonic() & NoBraiding() +NoBraiding() +``` + +Finally, some predefined combinations that appear often have dedicated names: + +```@docs; canonical = false +HasBraiding +SymmetricBraiding +``` + +## UnitStyle + +The `UnitStyle` tells whether there is a single identity label or multiple units. +By default, it is derived from `length(allunits(I))`. + +```@docs; canonical = false +UnitStyle +SimpleUnit +GenericUnit +``` + +Whenever the style is `SimpleUnit`, a unique value of [`unit`](@ref) can be defined and there is no distinction between [`leftunit`](@ref) and [`rightunit`](@ref). +For `GenericUnit`, this is no longer the case and special care has to be taken to use the *correct* unit for various fusion diagrams. diff --git a/docs/src/sectors.md b/docs/src/sectors.md new file mode 100644 index 00000000..d13bfa17 --- /dev/null +++ b/docs/src/sectors.md @@ -0,0 +1,22 @@ +# Sector Types + +This page provides an overview of the concrete sector types implemented in TensorKitSectors.jl. + +## Summary Table + +| Sector Type | Group/Category | Fusion Style | Braiding | Infinite? | Common Use Cases | +|--------------------------|--------------------------|--------------|-----------|-----------|---------------------------------------| +| [`Trivial`](@ref) | Trivial group | Unique | Bosonic | No | No symmetry | +| [`ZNIrrep`](@ref) | ℤₙ (cyclic) | Unique | Bosonic | No | Clock models, discrete symmetries | +| [`U1Irrep`](@ref) | U(1) | Unique | Bosonic | Yes | Particle number, charge conservation | +| [`SU2Irrep`](@ref) | SU(2) | Simple | Bosonic | Yes | Spin systems, angular momentum | +| [`CU1Irrep`](@ref) | U(1) ⋊ ℤ₂ | Simple | Bosonic | Yes | Particle-hole symmetry, O(2) | +| [`DNIrrep`](@ref) | Dₙ (dihedral) | Simple | Bosonic | No | Molecular/crystal symmetries | +| [`A4Irrep`](@ref) | A₄ (alternating) | Generic | Bosonic | No | Tetrahedral symmetry | +| [`FibonacciAnyon`](@ref) | Fibonacci category | Simple | Anyonic | No | Topological quantum computing | +| [`IsingAnyon`](@ref) | Ising category | Simple | Anyonic | No | Majorana fermions, ν=5/2 QHE | +| [`FermionParity`](@ref) | fℤ₂ | Unique | Fermionic | No | Fermion parity conservation | +| [`FermionNumber`](@ref) | fU₁ | Unique | Fermionic | Yes | Fermion number conservation | +| [`FermionSpin`](@ref) | fSU₂ | Simple | Fermionic | Yes | Fermions with spin symmetry | +| [`ProductSector`](@ref) | Product categories | Varies | Varies | Varies | Multiple simultaneous symmetries | +| [`TimeReversed`](@ref) | Inverted braiding | Varies | Varies | Varies | ?? | diff --git a/docs/src/sectors/abelian/trivial.md b/docs/src/sectors/abelian/trivial.md new file mode 100644 index 00000000..8a77e58e --- /dev/null +++ b/docs/src/sectors/abelian/trivial.md @@ -0,0 +1,44 @@ +# Trivial Sector: `Trivial` + +`Trivial` is the trivial sector for ordinary vector spaces. +It is the trivial representation of the trivial group, equivalently `Rep[ℤ₁]`. + +## Sector type + +```@docs; canonical = false +Trivial +``` + +There is only one label: + +```julia +using TensorKitSectors + +Trivial() +``` + +## Fusion Rules + +Fusion is uniquely trivial: + +```math +1 \otimes 1 = 1. +``` + +Therefore `FusionStyle(Trivial) = UniqueFusion()` and `Nsymbol(Trivial(), Trivial(), Trivial()) == true`. + +## Topological Data + +The quantum dimension is `1`. +`fusiontensor` is a `1 × 1 × 1 × 1` array containing `1`. +The associator and braiding are both trivial: + +```math +F = 1,\qquad R = 1. +``` + +The braiding style is `Bosonic()`. + +## References + +- [Trivial group](https://en.wikipedia.org/wiki/Trivial_group) diff --git a/docs/src/sectors/abelian/u1.md b/docs/src/sectors/abelian/u1.md new file mode 100644 index 00000000..0125359a --- /dev/null +++ b/docs/src/sectors/abelian/u1.md @@ -0,0 +1,49 @@ +# ``U(1)`` Representations: `U1Irrep` + +`U1Irrep` represents irreducible representations of the compact abelian group ``U(1)`` as a `Sector`. +The implementation stores charges as half-integers, which is useful for spin systems where z-component of an ``SU(2)`` spin may be half-integral. + +## Sector type + +```@docs; canonical = false +U1Irrep +``` + +The unit is `U1Irrep(0)`, and duality negates the charge: + +```math +q^* = -q. +``` + +## Fusion Rules + +Fusion adds charges: + +```math +q_1 \otimes q_2 = q_1 + q_2. +``` + +## Topological Data + +The category has `FusionStyle(U1Irrep) = UniqueFusion()`. +All irreps have quantum dimension `1`, and the [`Nsymbol`](@ref) is `true` exactly when the output charge is the sum of the input charges. + +The [`Fsymbol`](@ref), [`Rsymbol`](@ref), and [`fusiontensor`](@ref) are all trivial on allowed fusion channels. +Since this is a representation category, the braiding style is `Bosonic()` and all twists are `1`. + +## Iteration and basis conventions + +`values(U1Irrep)` is infinite. +The iterator starts at zero and alternates positive and negative charges: + +```julia +using TensorKitSectors + +values(U1Irrep)[6] # output: Irrep[U₁](3/2) +values(U1Irrep)[7] # output: Irrep[U₁](-3/2) +``` + +## References + +- [Unitary group](https://en.wikipedia.org/wiki/Unitary_group) +- [Circle group](https://en.wikipedia.org/wiki/Circle_group) diff --git a/docs/src/sectors/abelian/zn.md b/docs/src/sectors/abelian/zn.md new file mode 100644 index 00000000..dd5805ca --- /dev/null +++ b/docs/src/sectors/abelian/zn.md @@ -0,0 +1,47 @@ +# Cyclic Group Representations: `ZNIrrep` + +`ZNIrrep{N}` and `LargeZNIrrep{N}` represent irreducible representations of the cyclic group ``\mathbb{Z}_N``. +Use `ZNIrrep{N}` when possible; it selects the compact storage type automatically. + +## Sector types + +```@docs; canonical = false +ZNIrrep +LargeZNIrrep +``` + +`Z2Irrep`, `Z3Irrep`, and `Z4Irrep` are aliases for `ZNIrrep{2}`, `ZNIrrep{3}`, and `ZNIrrep{4}`. +For small `N`, labels are stored as `UInt8`; larger `N` uses `LargeZNIrrep{N}`. + +## Fusion Rules + +Labels are charges modulo `N`. +The unit is charge `0`, duality negates the charge modulo `N`, and fusion adds charges: + +```math +a \otimes b = (a + b) \bmod N,\qquad a^* = -a \bmod N. +``` + +The category has `FusionStyle(ZNIrrep{N}) = UniqueFusion()`. + +## Topological Data + +All quantum dimensions are `1`, as well as all twist. +F-symbols, R-symbols and fusion tensors are trivial in allowed channels. +The braiding style is `Bosonic()`. + +## Iteration and basis conventions + +`values(ZNIrrep{N})` iterates charges in increasing order from `0` to `N - 1`. + +```julia +using TensorKitSectors + +values(ZNIrrep{5})[4] # output is Irrep[ℤ{5}](3) +values(ZNIrrep{34})[34] # output is Irrep[ℤ{34}](33) +``` + +## References + +- [Cyclic group](https://en.wikipedia.org/wiki/Cyclic_group) +- [Representation theory of finite groups](https://en.wikipedia.org/wiki/Representation_theory_of_finite_groups) diff --git a/docs/src/sectors/anyons/fibonacci.md b/docs/src/sectors/anyons/fibonacci.md new file mode 100644 index 00000000..81bbcece --- /dev/null +++ b/docs/src/sectors/anyons/fibonacci.md @@ -0,0 +1,60 @@ +# Fibonacci Anyons: `FibonacciAnyon` + +`FibonacciAnyon` represents the Fibonacci modular fusion category. +It has a trivial anyon `:I` and a non-trivial anyon `:τ` (`:tau` is accepted as an ASCII constructor alias). + +## Sector type + +```@docs; canonical = false +FibonacciAnyon +``` + +Both sectors are self-dual, and the unit is `FibonacciAnyon(:I)`. + +## Fusion Rules + +The only non-trivial fusion rule is + +```math +\tau \otimes \tau = I \oplus \tau. +``` + +Thus `FusionStyle(FibonacciAnyon) = SimpleFusion()`. +The quantum dimensions are + +```math +d_I = 1,\qquad d_\tau = \varphi = \frac{1 + \sqrt{5}}{2}. +``` + +## Topological Data + +The non-trivial associator appears when all external anyons are ``\tau``: + +```math +F^{\tau\tau\tau}_{\tau} = +\begin{pmatrix} +\varphi^{-1} & \varphi^{-1/2}\\ +\varphi^{-1/2} & -\varphi^{-1} +\end{pmatrix}, +``` + +in the intermediate basis ``(I,\tau)``. +All other allowed `Fsymbol` values are `1`. + +The braiding style is `Anyonic()`. +For ``\tau \otimes \tau``, + +```math +R^{\tau\tau}_I = e^{4\pi i/5},\qquad +R^{\tau\tau}_\tau = e^{-3\pi i/5}. +``` + +There is no fusion tensor as the fusion category does not originate from a group or its representations. + +## Iteration and basis conventions +`values(FibonacciAnyon)` iterates the labels in the order `:I`, `:τ`. + +## References + +- [Fibonacci anyon](https://en.wikipedia.org/wiki/Anyon#Fibonacci_anyons) +- [Modular tensor category](https://en.wikipedia.org/wiki/Modular_tensor_category) diff --git a/docs/src/sectors/anyons/ising.md b/docs/src/sectors/anyons/ising.md new file mode 100644 index 00000000..fc7d23b1 --- /dev/null +++ b/docs/src/sectors/anyons/ising.md @@ -0,0 +1,64 @@ +# Ising Anyons: `IsingAnyon` + +`IsingAnyon` represents the Ising modular fusion category. +It has labels `:I`, `:σ`, and `:ψ`; the ASCII aliases `:sigma` and `:psi` are accepted by the constructor. + +## Sector type + +```@docs; canonical = false +IsingAnyon +``` + +All three sectors are self-dual, and the unit is `IsingAnyon(:I)`. + +## Fusion Rules + +The non-trivial fusion rules are + +```math +\psi \otimes \psi = I,\qquad +\sigma \otimes \psi = \psi \otimes \sigma = \sigma,\qquad +\sigma \otimes \sigma = I \oplus \psi. +``` + +Thus `FusionStyle(IsingAnyon) = SimpleFusion()`. +The quantum dimensions are + +```math +d_I = 1,\qquad d_\psi = 1,\qquad d_\sigma = \sqrt{2}. +``` + +## Topological Data + +The non-trivial associator for four ``\sigma`` anyons is + +```math +F^{\sigma\sigma\sigma}_{\sigma} = +\frac{1}{\sqrt{2}} +\begin{pmatrix} +1 & 1\\ +1 & -1 +\end{pmatrix}, +``` + +in the intermediate basis ``(I,\psi)``. +Additional signs involving the fermion ``\psi`` are encoded by [`Fsymbol`](@ref). + +The braiding style is `Anyonic()`. +Important braiding phases are + +```math +R^{\sigma\sigma}_I = e^{-\pi i/8},\qquad +R^{\sigma\sigma}_\psi = e^{3\pi i/8},\qquad +R^{\psi\psi}_I = -1. +``` + +There is no fusion tensor as the fusion category does not originate from a group or its representations. + +## Iteration and basis conventions +`values(IsingAnyon)` iterates the labels in the order `:I`, `:σ`, `:ψ`. + +## References + +- [Ising anyon](https://en.wikipedia.org/wiki/Anyon#Ising_anyons) +- [Modular tensor category](https://en.wikipedia.org/wiki/Modular_tensor_category) diff --git a/docs/src/sectors/anyons/planartrivial.md b/docs/src/sectors/anyons/planartrivial.md new file mode 100644 index 00000000..a824780d --- /dev/null +++ b/docs/src/sectors/anyons/planartrivial.md @@ -0,0 +1,31 @@ +# Planar Trivial Anyon: `PlanarTrivial` + +`PlanarTrivial` is the one-object fusion category without braiding. +It is mostly useful as the smallest planar, non-braided sector in tests and examples. + +## Sector type + +```@docs; canonical = false +PlanarTrivial +``` + +There is only one label, `PlanarTrivial()`. +It is its own unit and dual. + +## Fusion Rules + +Fusion is uniquely trivial: + +```math +1 \otimes 1 = 1. +``` + +`FusionStyle(PlanarTrivial) = UniqueFusion()` and `Nsymbol` is `1` for the only allowed channel. + +## Topological Data + +The [`Fsymbol`](@ref) is `1`. +The quantum dimension is `1`. + +Unlike [`Trivial`](@ref), this sector has `BraidingStyle(PlanarTrivial) = NoBraiding()`. +Consequently, braiding data such as [`Rsymbol`](@ref) and twists are not part of this sector. diff --git a/docs/src/sectors/composite/product.md b/docs/src/sectors/composite/product.md new file mode 100644 index 00000000..05f0efae --- /dev/null +++ b/docs/src/sectors/composite/product.md @@ -0,0 +1,105 @@ +# Product Sectors: `ProductSector` + +`ProductSector` represents the Deligne tensor product of sector categories. +It is the standard way to combine independent symmetry or anyon labels, such as charge and parity, or two different anyon theories. For the bosonic group or representation categories, this Deligne product corresponds to the ordinary product of groups or direct product of representations. + +## Sector type + +```@docs; canonical = false +ProductSector +⊠ +``` + +A product sector stores its component sectors in a tuple. +The recommended constructor is the Deligne product operator `⊠` or `boxtimes`: + +```julia +using TensorKitSectors + +a = Z2Irrep(1) ⊠ FibonacciAnyon(:τ) # (Irrep[ℤ₂](1) ⊠ FibonacciAnyon(:τ)) +``` + +The same operator works on sector types: + +```julia +using TensorKitSectors + +I = Z2Irrep ⊠ FibonacciAnyon +a = I(1, :τ) +``` + +Products are flattened, and `Trivial` factors are removed: + +```math +(a \boxtimes b) \boxtimes c = a \boxtimes b \boxtimes c,\qquad +a \boxtimes \mathbf{1} = a. +``` + +For representation categories, type-level products are displayed as irreps of the product group. +For example, `Z2Irrep ⊠ Z3Irrep` behaves as `Irrep[ℤ₂ × ℤ₃]`. +Product sectors are used to define several convenience aliases, such as [`FermionNumber`](@ref) and [`FermionSpin`](@ref). + +## Fusion Rules + +Fusion is componentwise. +For product sectors + +```math +a = (a_1,\ldots,a_n),\qquad b = (b_1,\ldots,b_n), +``` + +the fusion outputs are + +```math +a \otimes b = +\bigoplus_{c_i \in a_i \otimes b_i} N^{ab}_c +(c_1,\ldots,c_n). +``` + +The fusion multiplicities multiply: + +```math +N^{ab}_c = \prod_i N^{a_i b_i}_{c_i}. +``` + +The fusion style is the combined style of the components. +In particular, a product of unique-fusion sectors remains unique, while adding a simple or generic component promotes the product accordingly. + +Duals, units, and quantum dimensions are also componentwise: + +```math +(a_1,\ldots,a_n)^* = (a_1^*,\ldots,a_n^*),\qquad +d_{(a_1,\ldots,a_n)} = \prod_i d_{a_i}. +``` + +If a component category has multiple units, `allunits` returns the product of the component unit sets. + +## Topological Data + +The topological data is the tensor product of the component data. +For multiplicity-free channels this reduces to ordinary multiplication: + +```math +F_{\boxtimes_i a_i,\boxtimes_i b_i,\boxtimes_i c_i}^{\boxtimes_i d_i} += \prod_i F_{a_i b_i c_i}^{d_i}, +\qquad +R_{\boxtimes_i a_i,\boxtimes_i b_i}^{\boxtimes_i c_i} += \prod_i R_{a_i b_i}^{c_i}. +``` + +When multiplicity spaces are present, the implementation forms the corresponding Kronecker products so that the product multiplicity basis is ordered componentwise. +The same convention is used for `Fsymbol`, `Rsymbol`, `Asymbol`, `Bsymbol`, and `fusiontensor`. + +Braiding style and scalar types are combined from the component categories. + +For products containing [`FermionParity`](@ref), `fermionparity` is the XOR of the component parities. + +## Iteration + +`values(I1 ⊠ I2)` iterates over the Cartesian product of `values(I1)` and `values(I2)`. +Indexing and `findindex` use the same componentwise ordering. +For infinite component sectors, the product iterator can also be infinite. + +## References + +- [Deligne tensor product](https://ncatlab.org/nlab/show/Deligne+tensor+product) \ No newline at end of file diff --git a/docs/src/sectors/composite/timereversed.md b/docs/src/sectors/composite/timereversed.md new file mode 100644 index 00000000..769c0dcd --- /dev/null +++ b/docs/src/sectors/composite/timereversed.md @@ -0,0 +1,70 @@ +# Time-Reversed Sectors: `TimeReversed` + +`TimeReversed{I}` represents the time-reversed, or conjugate-braided, version of a sector type `I`. +It keeps the same objects, fusion rules, dimensions, and associators, but reverses the braiding. + +## Sector type + +```@docs; canonical = false +TimeReversed +timereversed +``` + +Construct a wrapped sector with `TimeReversed(a)` or `TimeReversed{I}(a)`. +The helper `timereversed(a)` avoids unnecessary wrappers for symmetric braiding categories and unwraps an already time-reversed sector: + +```julia +using TensorKitSectors + +a = IsingAnyon(:σ) +timereversed(a) # TimeReversed{IsingAnyon}(:σ) +timereversed(timereversed(a)) # IsingAnyon(:σ) +``` + +`TimeReversed` is not defined for sectors with `NoBraiding()`. + +## Fusion Rules + +Fusion is inherited from the original sector type. +The fusion multiplicities, fusion style, units, duals, and quantum dimensions are unchanged: + +```math +N_{\overline{c}}^{\overline{a}\,\overline{b}} = N_c^{ab},\qquad +d_{\overline{a}} = d_a,\qquad +\overline{a}^{\,*} = \overline{a^*}. +``` + +`values(TimeReversed{I})` follows the same order as `values(I)`, with every element wrapped. + +## Topological Data + +The [`Fsymbol`](@ref), [`Asymbol`](@ref), and [`Bsymbol`](@ref) are inherited directly from the original sector. +The [`Rsymbol`](@ref) is adjointed: + +```math +R_{\overline{c}}^{\overline{a}\,\overline{b}} += \left(R_c^{ab}\right)^\dagger. +``` + +For scalar braiding phases this is complex conjugation, so anyonic spins change sign: + +```math +\theta_{\overline{a}} = \overline{\theta_a}. +``` + +For example, in the conventions of this package, + +```math +\theta_\sigma = e^{\pi i/8} +\quad\Longrightarrow\quad +\theta_{\overline{\sigma}} = e^{-\pi i/8} +``` + +for the Ising `σ` anyon. + +The braiding style and scalar types are reported as those of the original sector type. +For bosonic or fermionic symmetric categories, time reversal is physically trivial; `timereversed(a)` therefore returns `a` directly. + +## References + +- [Time reversal symmetry](https://en.wikipedia.org/wiki/T-symmetry) \ No newline at end of file diff --git a/docs/src/sectors/fermions/number.md b/docs/src/sectors/fermions/number.md new file mode 100644 index 00000000..38a9c9a2 --- /dev/null +++ b/docs/src/sectors/fermions/number.md @@ -0,0 +1,42 @@ +# Fermion Number: `FermionNumber` + +`FermionNumber` is a convenience sector for integer ``U(1)`` charge together with the matching fermion parity. +It is implemented as `U1Irrep ⊠ FermionParity`, with odd charge assigned odd parity. + +## Sector type + +```@docs; canonical = false +FermionNumber +``` + +Construct `FermionNumber(n)` from an integer charge `n`. +The underlying product label is + +```math +n \mapsto (U(1)\text{ charge } n,\; n \bmod 2). +``` + +The unit is `FermionNumber(0)`, and duality negates the ``U(1)`` charge while preserving the parity constraint. + +## Fusion Rules + +Fusion adds charges: + +```math +n_1 \otimes n_2 = n_1 + n_2. +``` + +The fermion parity component is then fixed automatically by the resulting charge. + +## Topological data + +All sectors have quantum dimension `1`. + +The ``U(1)`` part has bosonic representation-category data, while the parity part supplies the fermionic exchange sign. +Thus exchanging two odd-charge sectors contributes a minus sign. + +Because this is a product sector, its fusion and topological data is inherited componentwise from [`U1Irrep`](@ref) and [`FermionParity`](@ref). + +## References + +- [Fermion number](https://en.wikipedia.org/wiki/Fermion_number) \ No newline at end of file diff --git a/docs/src/sectors/fermions/parity.md b/docs/src/sectors/fermions/parity.md new file mode 100644 index 00000000..049cc0d2 --- /dev/null +++ b/docs/src/sectors/fermions/parity.md @@ -0,0 +1,48 @@ +# Fermion Parity: `FermionParity` + +`FermionParity` is the two-sector fermionic category ``SVect`` for even and odd parity. +Odd sectors pick up a minus sign when exchanged with another odd sector. + +## Sector type + +```@docs; canonical = false +FermionParity +``` + +The labels are `FermionParity(false)` for even parity and `FermionParity(true)` for odd parity. +They are displayed as `FermionParity(0)` and `FermionParity(1)`. +The unit is even parity, and every sector is self-dual. + +## Fusion Rules + +Fusion is addition modulo two: + +```math +p \otimes q = p \oplus q. +``` + +The category has `FusionStyle(FermionParity) = UniqueFusion()`. + +## Topological data + +All quantum dimensions are `1`, and [`Fsymbol`](@ref) is trivial on allowed channels. + +The braiding style is `Fermionic()`. +The [`Rsymbol`](@ref) is `-1` only when both input sectors are odd: + +```math +R^{11}_0 = -1. +``` + +The twists are + +```math +\theta_0 = 1,\qquad \theta_1 = -1. +``` + +`fusiontensor` is defined for array construction, but it emits a warning because plain arrays with `FermionParity` labels do not preserve all fermionic signs. + +## References + +- [Fermion parity](https://en.wikipedia.org/wiki/Fermion_parity) +- [Super vector space](https://en.wikipedia.org/wiki/Super_vector_space) \ No newline at end of file diff --git a/docs/src/sectors/fermions/spin.md b/docs/src/sectors/fermions/spin.md new file mode 100644 index 00000000..2352e29f --- /dev/null +++ b/docs/src/sectors/fermions/spin.md @@ -0,0 +1,45 @@ +# Fermion Spin: `FermionSpin` + +`FermionSpin` is a convenience sector for ``SU(2)`` spin together with the matching fermion parity. +It is implemented as `SU2Irrep ⊠ FermionParity`, with half-integer spin assigned odd parity. + +## Sector type + +```@docs; canonical = false +FermionSpin +``` + +Construct `FermionSpin(j)` from an integer or half-integer spin. +The parity is odd exactly when `2j` is odd: + +```math +j \mapsto (SU(2)\text{ spin }j,\; 2j \bmod 2). +``` + +The unit is `FermionSpin(0)`, and all sectors are self-dual because the ``SU(2)`` irreps and parity sectors are self-dual. + +## Fusion Rules + +The ``SU(2)`` component fuses by angular-momentum addition: + +```math +j_1 \otimes j_2 = +\bigoplus_{j = |j_1-j_2|}^{j_1+j_2} j. +``` + +The parity component is fixed by the output spin. +Quantum dimensions are inherited from `SU2Irrep`: + +```math +d_j = 2j + 1. +``` + +The parity component supplies fermionic signs, so exchanging two half-integer-spin sectors has the odd-parity sign in addition to the `SU2Irrep` representation-category data. + +Because this is a product sector, most fusion and topological data is inherited componentwise from [`SU2Irrep`](@ref) and [`FermionParity`](@ref). + +## References + +- [Spin (physics)](https://en.wikipedia.org/wiki/Spin_(physics)) +- [SU(2)](https://en.wikipedia.org/wiki/SU(2)) +- [Fermion parity](https://en.wikipedia.org/wiki/Fermion_parity) diff --git a/docs/src/sectors/nonabelian/a4.md b/docs/src/sectors/nonabelian/a4.md new file mode 100644 index 00000000..9340a6c9 --- /dev/null +++ b/docs/src/sectors/nonabelian/a4.md @@ -0,0 +1,140 @@ +# Representation of the Alternating Group ``A_4``: `A4Irrep` + +`A4Irrep` represents irreducible representations (irreps) of the alternating group ``A_4`` as a `Sector`. +This page documents how the type behaves in code (construction, iteration, fusion, and access to topological data). + +## Sector type + +The irreps of ``A_4`` are labeled by integers `0:3`. +The first three labels are the one-dimensional representations ``1``, ``1'``, and ``1''``; label `3` is the three-dimensional irrep ``3``. + +```@docs; canonical = false +A4Irrep +``` + +The trivial sector is `A4Irrep(0)`. +Duals are + +```math +0^* = 0,\qquad 1^* = 2,\qquad 2^* = 1,\qquad 3^* = 3. +``` + +## Fusion Rules + +The one-dimensional irreps fuse as the group ``\mathbb{Z}_3``: + +```math +a \otimes b = (a + b) \bmod 3,\qquad a,b \in \{0,1,2\}. +``` + +Fusing a one-dimensional irrep with the triplet gives the triplet again: + +```math +a \otimes 3 = 3 \otimes a = 3,\qquad a \in \{0,1,2\}. +``` + +The triplet product decomposes as + +```math +3 \otimes 3 = 0 \oplus 1 \oplus 2 \oplus 2 * 3. +``` + +Because the triplet appears with multiplicity two in ``3 \otimes 3``, `FusionStyle(A4Irrep) = GenericFusion()`. +The [`Nsymbol`](@ref) returns the corresponding integer multiplicity: + +```math +N_c^{ab} = +\begin{cases} +1 + \delta_{c,3}, & a = b = 3,\\ +\delta_{c,3}, & a = 3 \text{ or } b = 3,\\ +\delta_{c,(a+b)\bmod 3}, & a,b \in \{0,1,2\}. +\end{cases} +``` + +The quantum dimensions are the ordinary representation space dimensions: + +```math +d_0 = d_1 = d_2 = 1,\qquad d_3 = 3. +``` + +## Topological Data + +`A4Irrep` describes the fusion category ``\mathsf{Rep(A_4)}``. +The braiding is bosonic, and all topological twists are `1`. + +The [`Fsymbol`](@ref) is computed from the chosen [`fusiontensor`](@ref) basis, see below. +The [`Rsymbol`](@ref) is the identity on all simple fusion channels, except for the two ``3`` channels in +``3 \otimes 3 \to 3``: + +```math +R^{33}_3 = +\begin{pmatrix} +-1 & 0\\ +0 & 1 +\end{pmatrix}. +``` + +The first channel is the antisymmetric triplet and the second channel is the symmetric triplet. + +## Fusion Tensor and Basis Conventions + +`fusiontensor(a, b, c)` returns a rank-4 array of size ``d_a \times d_b \times d_c \times N_c^{ab}``. +The one-dimensional fusion tensors are scalar phases/permutations in the selected basis. +For ``3 \otimes 3``, the singlet channels are normalized with a factor ``1/\sqrt{3}``, and the two triplet channels use the real antisymmetric and symmetric Clebsch-Gordan tensors from the tetrahedral basis convention used in the implementation. + +In this basis the triplet representation can be generated by + +```math +T_3 = +\begin{pmatrix} +1 & 0 & 0\\ +0 & \omega & 0\\ +0 & 0 & \omega^2 +\end{pmatrix}, +\qquad +S_3 = \frac{1}{3} +\begin{pmatrix} +-1 & 2 & 2\\ +2 & -1 & 2\\ +2 & 2 & -1 +\end{pmatrix}, +\qquad +\omega = e^{2\pi i/3}. +``` + +The one-dimensional irreps have ``S = 1`` and ``T = \omega^n`` for label `n`. +For every allowed fusion channel, the fusion tensor intertwines the product representation with the output representation. + +```jldoctest a4_intertwiner; output = false +using TensorKitSectors +using LinearAlgebra: kron +using Test: @test + +omega = cis(2pi / 3) +T3 = [1 0 0; 0 omega 0; 0 0 omega^2] +S3 = 1 / 3 * [-1 2 2; 2 -1 2; 2 2 -1] + +T(a::A4Irrep) = a.n == 3 ? T3 : hcat(omega^a.n) +S(a::A4Irrep) = a.n == 3 ? S3 : hcat(1) + +a = A4Irrep(3) +b = A4Irrep(3) +c = A4Irrep(3) +C = fusiontensor(a, b, c) + +for mu in 1:Nsymbol(a, b, c) + Cmat = reshape(view(C, :, :, :, mu), dim(a) * dim(b), dim(c)) + @test Cmat' * kron(T(a), T(b)) * Cmat ≈ T(c) + @test Cmat' * kron(S(a), S(b)) * Cmat ≈ S(c) +end + +# output +``` + +## References + +For background on the group and the representation basis, see: + +- [Alternating group](https://en.wikipedia.org/wiki/Alternating_group) +- [Tetrahedral symmetry](https://en.wikipedia.org/wiki/Tetrahedral_symmetry) +- G. Altarelli and F. Feruglio, "Discrete Flavor Symmetries and Models of Neutrino Mixing", Rev. Mod. Phys. 82, 2701 (2010). diff --git a/docs/src/sectors/nonabelian/cu1.md b/docs/src/sectors/nonabelian/cu1.md new file mode 100644 index 00000000..1e69222e --- /dev/null +++ b/docs/src/sectors/nonabelian/cu1.md @@ -0,0 +1,134 @@ +# ``CU(1)`` Representations: `CU1Irrep` + +`CU1Irrep` represents irreducible representations of ``U(1) ⋊ C``, where `C` acts by charge conjugation. +This group is also known as ``O(2)``. + +## Sector type + +```@docs; canonical = false +CU1Irrep +``` + +Labels are pairs `(j, s)`, where `j` is a `U1Irrep` charge and `s` is an integer in `0:2`. +For `j == 0`, there are two one-dimensional irreps: `CU1Irrep(0, 0)` and `CU1Irrep(0, 1)`. +For `j > 0`, only `s == 2` is valid and the representation is two-dimensional. +The unit is `CU1Irrep(0, 0)`, and every irrep is self-dual. + +## Fusion Rules + +The two one-dimensional irreps fuse by XOR of the `s` label. +Fusing either one-dimensional irrep with a two-dimensional irrep returns the same two-dimensional label. +For two positive charges, the outputs are governed by sum and absolute difference: + +```math +j_1 \otimes j_2 = +|j_1-j_2| \oplus (j_1+j_2), +``` + +with the special case `j1 == j2`, where the zero-charge difference splits into the two one-dimensional irreps. +The category has `FusionStyle(CU1Irrep) = SimpleFusion()`. + +Quantum dimensions are + +```math +d_{(0,0)} = d_{(0,1)} = 1,\qquad d_{(j,2)} = 2\quad (j>0). +``` + +## Topological Data + +`CU1Irrep` is a bosonic representation category, so all twists are `1`. +The [`Fsymbol`](@ref) values are real and encode the chosen Clebsch-Gordan basis. +The [`Rsymbol`](@ref) is real and equals the N-symbol, with an additional negative sign on channels with `c.s == 1` and positive left overbraiding input charge. + +## Fusion Tensor and Basis Conventions + +`fusiontensor(a, b, c)` returns a rank-4 array of size ``d_a \times d_b \times d_c \times N_c^{ab}``. +Since `CU1Irrep` has simple fusion, the final multiplicity axis has length `0` or `1`. +For allowed channels, the tensor entries are real Clebsch-Gordan coefficients in the convention explained below. + +For a two-dimensional irrep `(j, 2)` with `j > 0`, the basis is ordered as a pair of charge-conjugate ``U(1)`` weights, which we can denote by + +```math +\ket{+j},\ \ket{-j}. +``` + +The zero-charge irreps `(0, 0)` and `(0, 1)` are one-dimensional. The label `(0, 0)` is even under charge conjugation, while `(0, 1)` is odd. + +When two equal positive-charge irreps fuse to a zero-charge irrep, the fusion tensors pick the symmetric and antisymmetric combinations: + +```math +\ket{(0,0)} = +\frac{1}{\sqrt{2}}\left(\ket{+j}\otimes\ket{-j} + \ket{-j}\otimes\ket{+j}\right), +``` + +```math +\ket{(0,1)} = +\frac{1}{\sqrt{2}}\left(\ket{+j}\otimes\ket{-j} - \ket{-j}\otimes\ket{+j}\right). +``` + +In array form, these are the entries + +```math +C_{1,2,1} = \frac{1}{\sqrt{2}},\qquad +C_{2,1,1} = \pm\frac{1}{\sqrt{2}}, +``` + +with the plus sign for `(0, 0)` and the minus sign for `(0, 1)`. + +Fusing a zero-charge irrep with a two-dimensional irrep leaves the charge label unchanged. +The odd zero-charge irrep contributes a sign on the second basis vector: + +```math +(0,s) \otimes (j,2) \to (j,2):\qquad +\ket{+j}\mapsto\ket{+j},\quad +\ket{-j}\mapsto (-1)^s\ket{-j}. +``` + +The same convention is used for `(j,2) ⊗ (0,s)`, with the sign attached to the second basis vector of the two-dimensional input. + +For two positive charges, the sum channel is diagonal: + +```math +(j_a,2)\otimes(j_b,2)\to(j_a+j_b,2):\qquad +\ket{+j_a,+j_b}\mapsto\ket{+(j_a+j_b)},\quad +\ket{-j_a,-j_b}\mapsto\ket{-(j_a+j_b)}. +``` + +The difference channel pairs opposite weights. If `j_a > j_b`, + +```math +\ket{+j_a,-j_b}\mapsto\ket{+(j_a-j_b)},\qquad +\ket{-j_a,+j_b}\mapsto\ket{-(j_a-j_b)}. +``` + +If `j_b > j_a`, the output basis is ordered by the positive charge `j_b - j_a`, so the two nonzero entries are swapped accordingly: + +```math +\ket{-j_a,+j_b}\mapsto\ket{+(j_b-j_a)},\qquad +\ket{+j_a,-j_b}\mapsto\ket{-(j_b-j_a)}. +``` + +All omitted entries are zero. These conventions determine the real [`Fsymbol`](@ref) values. + +## Iteration + +`values(CU1Irrep)` is infinite. +It starts with the two zero-charge irreps and then lists positive half-integer charges: + +```julia +using TensorKitSectors + +values(CU1Irrep)[1] +Irrep[CU₁](0, 0) + +values(CU1Irrep)[2] +Irrep[CU₁](0, 1) + +values(CU1Irrep)[5] +Irrep[CU₁](3/2, 2) +``` + +## References + +- [Orthogonal group in two dimensions](https://en.wikipedia.org/wiki/Orthogonal_group_in_two_dimensions) +- [Semidirect product](https://en.wikipedia.org/wiki/Semidirect_product) \ No newline at end of file diff --git a/docs/src/sectors/nonabelian/dn.md b/docs/src/sectors/nonabelian/dn.md new file mode 100644 index 00000000..c11b6f58 --- /dev/null +++ b/docs/src/sectors/nonabelian/dn.md @@ -0,0 +1,72 @@ +# Dihedral Group Representations: `DNIrrep` + +`DNIrrep{N}` represents irreducible representations of the dihedral group ``D_N = \mathbb{Z}_N ⋊ C``. +The aliases `D3Irrep` and `D4Irrep` are provided for `DNIrrep{3}` and `DNIrrep{4}`. + +## Sector type + +```@docs; canonical = false +DNIrrep +``` + +Labels are constructed as `DNIrrep{N}(j, isodd=false)`. +The integer `j` satisfies `0 <= j <= N ÷ 2`. +The `isodd` flag is valid only for one-dimensional irreps: always at `j == 0`, and also at `j == N/2` when `N` is even. + +For odd `N`, the labels are + +```math +(0,\text{false}),\ (0,\text{true}),\ 1,2,\ldots,(\frac{N-1}{2},\text{false}). +``` + +For even `N`, the labels are + +```math +(0,\text{false}),\ (0,\text{true}),\ 1,2,\ldots,\frac{N}{2}-1,\ (N/2,\text{false}),\ (N/2,\text{true}). +``` + +The unit is `DNIrrep{N}(0, false)`, and all irreps are self-dual. + +## Fusion Rules + +The one-dimensional irreps fuse by XOR of the parity label. +The fusion product of a one-dimensional irrep with a two-dimensional irrep preserves the two-dimensional label, up to the special one-dimensional labels at `j == N/2` for even `N`. + +For ordinary two-dimensional labels, fusion follows the dihedral character rule: + +```math +\rho_i \otimes \rho_j = \rho_{|i-j|} \oplus \rho_{i+j}, +``` + +where labels are folded back into the range `0:N÷2`. +When a folded output lands on a one-dimensional point (`0`, or `N/2` for even `N`), it splits into the corresponding even and odd one-dimensional irreps. + +For `N < 3`, `FusionStyle(DNIrrep{N}) = UniqueFusion()`. +Otherwise, `FusionStyle(DNIrrep{N}) = SimpleFusion()`. + +Quantum dimensions are + +```math +d_{(0,\pm)} = 1,\qquad d_{\rho_j} = 2,\qquad +d_{(N/2,\pm)} = 1\quad (N \text{ even}). +``` + +## Topological Data + +`DNIrrep{N}` is a bosonic representation category, so all twists are `1`. +The [`Fsymbol`](@ref) is computed from the selected [`fusiontensor`](@ref) basis. +The [`Rsymbol`](@ref) is the N-symbol with a possible sign on odd one-dimensional output channels from two non-trivial two-dimensional inputs. + +## Fusion tensor and basis conventions + +`fusiontensor(a, b, c)` returns a rank-4 array of size ``d_a \times d_b \times d_c \times N_c^{ab}``. +The implementation uses real Clebsch-Gordan coefficients. +#TODO: source for the convention used here + +## Iteration + +The `DNIrrep{N}` labels are iterable, with the same ordering as described above. + +## References + +- [Dihedral group](https://en.wikipedia.org/wiki/Dihedral_group) \ No newline at end of file diff --git a/docs/src/sectors/nonabelian/su2.md b/docs/src/sectors/nonabelian/su2.md new file mode 100644 index 00000000..1eea2341 --- /dev/null +++ b/docs/src/sectors/nonabelian/su2.md @@ -0,0 +1,130 @@ +# ``SU(2)`` Representations: `SU2Irrep` + +`SU2Irrep` represents irreducible representations (irreps) of the compact group ``SU(2)`` as a `Sector`. +This page documents how the type behaves in code (construction, iteration, fusion, and access to topological data). + +## Sector type + +The irreps of ``SU(2)`` are labeled by non-negative integers or half-integers (e.g. ``0``, ``\frac{1}{2}``, ``1``, ``\frac{3}{2}``, …). + +```@docs; canonical = false +SU2Irrep +``` + +Fusing two irreps together leads to a direct sum of irreps: + +```math +a \otimes b = \bigoplus_{c_j = |j_a - j_b|}^{j_a + j_b} c +``` + +Since each output appears only once, we have `FusionStyle(SU2Irrep) = SimpleFusion()`. +The [`Nsymbol`](@ref) returns a `Bool` that checks the triangle inequality: + +```math +N_c^{ab} = |j_a - j_b| \leq j_c \leq j_a + j_b \land j_a + j_b + j_c \in \mathbb{N} +``` + +Each irrep has dimension ``d = 2j + 1``. + +The [`Fsymbol`](@ref) is computed from [Wigner ``6j``](https://en.wikipedia.org/wiki/6-j_symbol) (Racah-``W``) symbols as + +```math +\left(F_{abc}^d\right)_e^f = (-1)^{j_a + j_b + j_c + j_e} \sqrt{d_e d_f} \begin{Bmatrix} +j_a & j_b & j_d \\ +j_c & j_e & j_f +\end{Bmatrix} +``` + +The `BraidingStyle` is `Bosonic`, and [`Rsymbol`](@ref) is ``\pm 1`` for allowed fusion channels based on the parity of ``j_a + j_b - j_c``. + +## Fusion Tensor and Basis Conventions + +`fusiontensor(a, b, c)` returns Clebsch–Gordan coefficients as a rank‑4 array of size ``d_a \times d_b \times d_c \times 1``. +We can label the basis by using ``\ket{j, m}``, where the magnetic quantum number takes on values ``m \in \{j, j-1, \ldots, -j\}`` (in that order). + +Each irrep acts on the standard basis $\lvert j, m \rangle$ where $m = j, j-1, \dots, -j$. +In that basis, the generators of ``\mathfrak{su}(2)`` are represented by the usual angular momentum operators: + +```math +J_z \lvert j, m \rangle = m \lvert j, m \rangle, \quad +J_\pm \lvert j, m \rangle = \sqrt{(j \mp m)(j \pm m + 1)}\, \lvert j, m \pm 1 \rangle. +``` + +```jldoctest generators; output = false +using TensorKitSectors + +function generators(a::SU2Irrep) + Jp = zeros(dim(a), dim(a)) + Jm = zeros(dim(a), dim(a)) + Jz = zeros(dim(a), dim(a)) + + for row in axes(Jp, 1), col in axes(Jp, 2) + m = a.j - col + 1 + if row == col + Jz[row, col] = m + elseif row + 1 == col + Jp[row, col] = sqrt((a.j - m) * (a.j + m + 1)) + elseif row == col + 1 + Jm[row, col] = sqrt((a.j + m) * (a.j - m + 1)) + end + end + + return Jp, Jm, Jz +end + +a = SU2Irrep(1) +Jp, Jm, Jz = generators(a) + +# output +([0.0 1.4142135623730951 0.0; 0.0 0.0 1.4142135623730951; 0.0 0.0 0.0], [0.0 0.0 0.0; 1.4142135623730951 0.0 0.0; 0.0 1.4142135623730951 0.0], [1.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 -1.0]) +``` + +The tensor product representation for ``a \otimes b`` is given in terms of the representations ``a`` and ``b`` as: + +```math +\mathbf{J}^{(a \otimes b)} = \mathbf{J}^{(a)} \otimes \mathbf{1}^{(b)} + \mathbf{1}^{(a)} \otimes \mathbf{J}^{(b)} +``` + +The [`fusiontensor`](@ref) supplies the change‑of‑basis coefficients ``C^{J M}_{j_a m_a, j_b m_b}`` that map the uncoupled product basis to the coupled basis: + +```math +\lvert J, M \rangle = \sum_{m_a, m_b} C^{J M}_{j_a m_a, j_b m_b} +\lvert j_a, m_a \rangle \otimes \lvert j_b, m_b \rangle. +``` + +In particular, this block-diagonalizes the generators, and we must have that for every ``\mathbf{J}``, the following holds: + +```math +\left(\mathbf{J}^{(a)} \otimes \mathbf{1}^{(b)} + \mathbf{1}^{(a)} \otimes \mathbf{J}^{(b)}\right) \cdot C^c_{ab} = + C^c_{ab} \cdot \mathbf{J}^{(c)} +``` + +```jldoctest generators; output = false +using TensorOperations: @tensor +using Test: @test + +a = SU2Irrep(1) +b = SU2Irrep(1) + +for c in a ⊗ b + CGC = dropdims(fusiontensor(a, b, c); dims = 4) # drop trivial multiplicity dimension + for (ga, gb, gc) in zip(generators(a), generators(b), generators(c)) + @tensor lhs[a b; c] := ga[a; a'] * CGC[a' b; c] + gb[b; b'] * CGC[a b'; c] + @tensor rhs[a b; c] := CGC[a b; c'] * gc[c'; c] + @test isapprox(lhs, rhs) + end +end + +# output + +``` + +## References + +For a quick refresher on the group structure and representation theory (without turning this page into a math course), the following references are useful: + +- [Special unitary group](https://en.wikipedia.org/wiki/Special_unitary_group) +- [SU(2)](https://en.wikipedia.org/wiki/SU(2)) +- [Representation theory of SU(2)](https://en.wikipedia.org/wiki/Representation_theory_of_SU(2)) +- [Clebsch–Gordan coefficients](https://en.wikipedia.org/wiki/Clebsch%E2%80%93Gordan_coefficients) +- [Wigner 6j symbol](https://en.wikipedia.org/wiki/6-j_symbol) diff --git a/src/irreps/su2irrep.jl b/src/irreps/su2irrep.jl index 27312ed3..dc92ba26 100644 --- a/src/irreps/su2irrep.jl +++ b/src/irreps/su2irrep.jl @@ -3,9 +3,8 @@ SU2Irrep(j::Real) Irrep[SU₂](j::Real) -Represents irreps of the group ``SU₂``. The irrep is labelled by a half integer `j` which -can be entered as an abitrary `Real`, but is stored as a `HalfInt` from the HalfIntegers.jl -package. +Represents irreps of the group ``SU₂``. +The irrep is labeled by a half integer `j` which can be entered as an arbitrary `Real`, but is stored as a `HalfInt` from the HalfIntegers.jl package. ## Fields - `j::HalfInt`: the label of the irrep, which can be any non-negative half integer. diff --git a/src/sectors.jl b/src/sectors.jl index 2faaebea..c2c62d6b 100644 --- a/src/sectors.jl +++ b/src/sectors.jl @@ -220,9 +220,12 @@ Base.isreal(I::Type{<:Sector}) = sectorscalartype(I) <: Real otimes(a::I, b::I...) where {I <: Sector} Return an iterable of elements of `c::I` that appear in the fusion product `a ⊗ b`. +Each sector `c` should appear at most once in this iteration, even if the multiplicity ``N_c^{ab} > 1``. +The actual multiplicities are accessed separately through [`Nsymbol`](@ref). -Note that every element `c` should appear at most once, fusion degeneracies (if -`FusionStyle(I) == GenericFusion()`) should be accessed via `Nsymbol(a, b, c)`. +The return type is typically [`SectorProductIterator{I}`](@ref) which provides a type-stable iterable that supports pretty-printing, but could also be any custom iterable. + +See also [`FusionStyle`](@ref) for the trait associated to the fusion behavior of a given sector type. """ function ⊗ end const otimes = ⊗ @@ -297,8 +300,11 @@ end """ Nsymbol(a::I, b::I, c::I) where {I <: Sector} -> Integer -Return an `Integer` representing the number of times `c` appears in the fusion product -`a ⊗ b`. Could be a `Bool` if `FusionStyle(I) == UniqueFusion()` or `SimpleFusion()`. +The fusion multiplicity ``N_c^{ab}``, indicating how many times sector `c` appears in the fusion product `a ⊗ b`. + +The return type depends on the [`FusionStyle`](@ref), where [`UniqueFusion`](@ref) and [`SimpleFusion`](@ref) return `Bool` values, while [`GenericFusion`](@ref) returns `Int`. + +See also [`⊗`](@ref) to obtain the set of sectors `c` that appear in `a ⊗ b`. """ function Nsymbol end @@ -309,29 +315,63 @@ function Nsymbol end FusionStyle(I::Type{<:Sector}) Trait to describe the fusion behavior of sectors of type `I`, which can be either -* `UniqueFusion()`: single fusion output when fusing two sectors; -* `SimpleFusion()`: multiple outputs, but every output occurs at most one, - also known as multiplicity-free (e.g. irreps of ``SU(2)``); -* `GenericFusion()`: multiple outputs that can occur more than once (e.g. irreps - of ``SU(3)``). +* [`UniqueFusion`](@ref): each fusion `a ⊗ b` has exactly one output `c`. +* [`SimpleFusion`](@ref): fusing `a ⊗ b` can lead to multiple values `c`, but each appears at most once. +* [`GenericFusion`](@ref): fusing `a ⊗ b` can lead to multiple values `c` that could appear multiple times. -There is an abstract supertype `MultipleFusion` of which both `SimpleFusion` and -`GenericFusion` are subtypes. Furthermore, there is a type alias `MultiplicityFreeFusion` -for those fusion types which do not require muliplicity labels, i.e. -`MultiplicityFreeFusion = Union{UniqueFusion,SimpleFusion}`. +There is an abstract supertype [`MultipleFusion`](@ref) of which both `SimpleFusion` and `GenericFusion` are subtypes. +Furthermore, there is a type alias [`MultiplicityFreeFusion`](@ref) for those fusion types which do not require muliplicity labels. """ abstract type FusionStyle end FusionStyle(a::Sector) = FusionStyle(typeof(a)) -struct UniqueFusion <: FusionStyle end # unique fusion output when fusing two sectors +""" + struct UniqueFusion <: FusionStyle + +Fusion style where every product `a ⊗ b` has exactly one output `c`. +As a result, ``N_c^{ab} ≤ 1`` and no multiplicity labels are needed. + +See also [`FusionStyle`](@ref). +""" +struct UniqueFusion <: FusionStyle end + +""" + abstract type MultipleFusion <: FusionStyle + +Fusion styles that allow more than one fusion output for `a ⊗ b`. + +See also [`SimpleFusion`](@ref), [`GenericFusion`](@ref) and [`FusionStyle`](@ref). +""" abstract type MultipleFusion <: FusionStyle end -struct SimpleFusion <: MultipleFusion end # multiple fusion but multiplicity free -struct GenericFusion <: MultipleFusion end # multiple fusion with multiplicities -const MultiplicityFreeFusion = Union{UniqueFusion, SimpleFusion} -@doc (@doc FusionStyle) UniqueFusion -@doc (@doc FusionStyle) SimpleFusion -@doc (@doc FusionStyle) GenericFusion +""" + struct SimpleFusion <: MultipleFusion + +Fusion style where multiple outputs `c` can appear in `a ⊗ b`, but each appears at most once. +As a result, ``N_c^{ab} ≤ 1`` and no multiplicity labels are needed. + +See also [`FusionStyle`](@ref). +""" +struct SimpleFusion <: MultipleFusion end + +""" + struct GenericFusion <: MultipleFusion + +Fusion style with potentially multiple outputs `c` and nontrivial multiplicities. +Here ``N_c^{ab}`` can exceed 1, and multiplicity labels are required. + +See also [`FusionStyle`](@ref). +""" +struct GenericFusion <: MultipleFusion end + +""" + const MultiplicityFreeFusion = Union{UniqueFusion, SimpleFusion} + +Convenience alias for fusion styles that can assume `Nsymbol(a, b, c)::Bool`, and therefore never require multiplicity labels. + +See also [`UniqueFusion`](@ref), [`SimpleFusion`](@ref) and [`FusionStyle`](@ref). +""" +const MultiplicityFreeFusion = Union{UniqueFusion, SimpleFusion} # combine fusion properties of tensor products of sectors Base.:&(f::F, ::F) where {F <: FusionStyle} = f @@ -349,17 +389,31 @@ Base.:&(::GenericFusion, ::SimpleFusion) = GenericFusion() Trait to describe the semisimplicity of the unit sector of type `I`. This can be either -* `SimpleUnit()`: the unit is simple (e.g. fusion categories); -* `GenericUnit()`: the unit is semisimple. +* [`SimpleUnit`](@ref): the unit is simple (e.g. fusion categories). +* [`GenericUnit`](@ref): the unit is semisimple (e.g. multifusion categories). """ abstract type UnitStyle end UnitStyle(a::Sector) = UnitStyle(typeof(a)) +""" + struct SimpleUnit <: UnitStyle + +Unit style for fusion categories with a unique unit (identity) object. +The unit satisfies ``\\mathbb{1} ⊗ a ≅ a ≅ a ⊗ \\mathbb{1}`` for all sectors. + +See also [`UnitStyle`](@ref). +""" struct SimpleUnit <: UnitStyle end -struct GenericUnit <: UnitStyle end -@doc (@doc UnitStyle) SimpleUnit -@doc (@doc UnitStyle) GenericUnit +""" + struct GenericUnit <: UnitStyle + +Unit style for multifusion categories with multiple unit objects (semisimple unit). +Requires implementation of `allunits(::Type{I})`, `leftunit(a)`, and `rightunit(a)`. + +See also [`UnitStyle`](@ref). +""" +struct GenericUnit <: UnitStyle end UnitStyle(::Type{I}) where {I <: Sector} = length(allunits(I)) == 1 ? SimpleUnit() : GenericUnit() @@ -367,7 +421,7 @@ UnitStyle(::Type{I}) where {I <: Sector} = length(allunits(I)) == 1 ? SimpleUnit throw(DomainError(I, "Sector has multiple units, use `allunits` instead of `unit`")) end -# combine fusion properties of tensor products of multifusion sectors +# combine unitstyle properties of tensor products of multifusion sectors Base.:&(f::F, ::F) where {F <: UnitStyle} = f Base.:&(f₁::UnitStyle, f₂::UnitStyle) = f₂ & f₁ @@ -437,7 +491,27 @@ function dim_from_Fsymbol(a::Sector) abs(1 / Fsymbol(a, dual(a), a, a, leftunit(a), rightunit(a))[1]) end end + +""" + sqrtdim(a::Sector) + +Return the square root of the (quantum) dimension of sector `a`. + +This is a performance specialization that avoids computing `sqrt(1)` for sectors with +`UniqueFusion`, preserving the number type (returning `1::Int` instead of `1.0::Float64`). +For other sectors, it is equivalent to `sqrt(dim(a))`. +""" sqrtdim(a::Sector) = (FusionStyle(a) isa UniqueFusion) ? 1 : sqrt(dim(a)) + +""" + invsqrtdim(a::Sector) + +Return the inverse square root of the (quantum) dimension of sector `a`. + +This is a performance specialization that avoids computing `inv(sqrt(1))` for sectors with +`UniqueFusion`, preserving the number type (returning `1::Int` instead of `1.0::Float64`). +For other sectors, it is equivalent to `inv(sqrt(dim(a)))`. +""" invsqrtdim(a::Sector) = (FusionStyle(a) isa UniqueFusion) ? 1 : inv(sqrt(dim(a))) """ @@ -479,7 +553,7 @@ context of line bending. """ function frobenius_schur_indicator(a::Sector) ν = frobenius_schur_phase(a) - return a == conj(a) ? ν : zero(ν) + return a == dual(a) ? ν : zero(ν) end """ @@ -569,35 +643,81 @@ end # trait to describe type to denote how the elementary spaces in a tensor product space # interact under permutations or actions of the braid group """ - abstract type BradingStyle + abstract type BraidingStyle BraidingStyle(::Sector) -> ::BraidingStyle BraidingStyle(I::Type{<:Sector}) -> ::BraidingStyle -Return the type of braiding and twist behavior of sectors of type `I`, which can be either -* `NoBraiding()`: no braiding structure -* `Bosonic()`: symmetric braiding with trivial twist (i.e. identity) -* `Fermionic()`: symmetric braiding with non-trivial twist (squares to identity) -* `Anyonic()`: general ``R^{ab}_c`` phase or matrix (depending on `SimpleFusion` or - `GenericFusion` fusion) and arbitrary twists +Trait to describe the braiding behavior of sectors of type `I`, which can be either +* [`NoBraiding`](@ref): no braiding structure defined. +* [`Bosonic`](@ref): symmetric braiding structure with a trivial twist. +* [`Fermionic`](@ref): symmetric braiding structure with a non-trivial twist that squares to identity. +* [`Anyonic`](@ref): general braiding structure and arbitrary twists. -Note that `Bosonic` and `Fermionic` are subtypes of `SymmetricBraiding`, which means that -braids are in fact equivalent to crossings (i.e. braiding twice is an identity: -`isone(Rsymbol(b,a,c)*Rsymbol(a,b,c)) == true`) and permutations are uniquely defined. +There is an abstract supertype [`HasBraiding`](@ref) that includes all styles that define [`Rsymbol`](@ref) (everything but `NoBraiding`). +Furthermore, the abstract supertype [`SymmetricBraiding`](@ref) denotes the cases where braidings are equivalent to crossings, i.e. braiding twice is an identity operation. +This includes the `Bosonic` and `Fermionic` styles, for which we can uniquely define permutations. """ abstract type BraidingStyle end BraidingStyle(a::Sector) = BraidingStyle(typeof(a)) +""" + abstract type HasBraiding <: BraidingStyle + +Supertype for all braiding styles where an [`Rsymbol`](@ref) is defined. +This includes all current `BraidingStyle`s except `NoBraiding`. +""" abstract type HasBraiding <: BraidingStyle end + +""" + struct NoBraiding <: BraidingStyle + +Braiding style for categories without a braiding structure. +Except for braiding with the unit sector, only planar diagrams are meaningful; [`Rsymbol`](@ref) is undefined. + +See also [`BraidingStyle`](@ref). +""" struct NoBraiding <: BraidingStyle end -abstract type SymmetricBraiding <: HasBraiding end # symmetric braiding => actions of permutation group are well defined -struct Bosonic <: SymmetricBraiding end # all twists are one -struct Fermionic <: SymmetricBraiding end # twists one and minus one -struct Anyonic <: HasBraiding end -@doc (@doc BraidingStyle) NoBraiding -@doc (@doc BraidingStyle) Bosonic -@doc (@doc BraidingStyle) Fermionic -@doc (@doc BraidingStyle) Anyonic +""" + abstract type SymmetricBraiding <: HasBraiding + +Supertype for braiding styles with symmetric braiding, where braiding twice is the identity operation. +Subtypes include [`Bosonic`](@ref) (trivial twist) and [`Fermionic`](@ref) (nontrivial twist ±1). +Supports permutation group statistics. + +See also [`BraidingStyle`](@ref). +""" +abstract type SymmetricBraiding <: HasBraiding end + +""" + struct Bosonic <: SymmetricBraiding + +Braiding style with symmetric braiding and trivial twist. +This is characterized by ``R^{ab}_c R^{ba}_c = 1`` and ``\\theta_a = 1`` for all sectors. + +See also [`BraidingStyle`](@ref). +""" +struct Bosonic <: SymmetricBraiding end + +""" + struct Fermionic <: SymmetricBraiding + +Braiding style with symmetric braiding and nontrivial (symmetric) twist. +This is characterized by ``R^{ab}_c R^{ba}_c = 1`` and ``\\theta_a = \\pm 1`` for all sectors. + +See also [`BraidingStyle`](@ref). +""" +struct Fermionic <: SymmetricBraiding end + +""" + struct Anyonic <: HasBraiding + +Braiding style with general (non-symmetric) braiding and arbitrary twists. +Characterized by nontrivial braid group representations where ``R^{ab}_c R^{ba}_c ≠ 1`` in general. + +See also [`BraidingStyle`](@ref). +""" +struct Anyonic <: HasBraiding end Base.:&(b::B, ::B) where {B <: BraidingStyle} = b Base.:&(B1::BraidingStyle, B2::BraidingStyle) = B2 & B1