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..6765daa3 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -19,6 +19,46 @@ 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", + "Heisenberg" => "sectors/nonabelian/heisenberg.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", + ], + "Group Elements" => [ + "ℤₙ (Cyclic)" => "sectors/groupelement/znelement.md", + ], + "Composite Sectors" => [ + "Product" => "sectors/composite/product.md", + "Named" => "sectors/composite/named.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..460fbd9e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -2,11 +2,20 @@ 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/). - -Install via the package manager. +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/). + +## Installation + +Install via the package manager, by entering the Pkg REPL mode with `]` and running: + +```julia-repl +pkg> add TensorKitSectors +``` + +## Where to go next + +- **[Sector Interface](@ref)** — the methods a sector type must (and may) implement, and the traits that control its behavior. Start here to implement a new sector. +- **[Sector Types](@ref)** — the concrete sectors shipped with this package (groups, anyons, fermions, and composites), with a summary table and a page per type. diff --git a/docs/src/interface/guidelines.md b/docs/src/interface/guidelines.md new file mode 100644 index 00000000..6bc9f198 --- /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 ∨ N^{af}_d N^{bc}_f = 0\right) ⟹ (F_{abc}^d)^e_f = 0 +``` + +```math +N^{ab}_c = 0 ⟹ 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 × N^{ec}_d × N^{af}_d × N^{bc}_f`` array of `sectorscalartype(I)` elements. +- `Rsymbol(a, b, c)`: Returns a ``N^{ab}_c × 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..9d947dd9 --- /dev/null +++ b/docs/src/interface/optional.md @@ -0,0 +1,98 @@ +```@meta +CollapsedDocStrings = true +``` + +# Optional Methods + +Most of the following methods have default implementations derived from the required methods, but can be overridden for performance or to provide additional functionality. +The exception is [`fusiontensor`](@ref), which has no default: it is genuinely optional and, when needed, must be supplied explicitly (see the Fusion Basis section below). + +## 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. +The Frobenius-Schur phase ``κ_a`` is obtained from the F-symbol as + +```math +κ_a = \text{sign}\left( (F_{a \bar{a} a}^a)^{1_a}_{_a1} \right) +``` + +and is the category-theoretic quantity that appears in line-bending operations. +The Frobenius-Schur indicator ``ν_a`` coincides with ``κ_a`` for self-dual sectors (``a = \bar{a}``) and is ``0`` otherwise; it distinguishes real, complex, and quaternionic representations. + +```@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{κ_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 ``θ_a`` of a sector is the topological spin phase, computed as the trace of the R-matrix for braiding a sector with itself: +```math +θ_a = \frac{1}{d_a} \sum_{b ∈ a ⊗ 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..b9d9f6fa --- /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 used to decompose vector spaces as: + +```math +V = \bigoplus_{a ∈ \text{Sectors}} ℂ^{n_a} ⊗ 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 ⊗ b → \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..895535e0 --- /dev/null +++ b/docs/src/interface/required.md @@ -0,0 +1,152 @@ +```@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. +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()`), sectors can be indexed by position. +This requires `Base.length`, while `Base.getindex` and `findindex` have generic fallbacks that are typically overridden for performance: + +- `Base.length(::SectorValues{I})` — Return the number of sectors. **(required for `HasLength()`)** +- `Base.getindex(::SectorValues{I}, i::Int)` — Access the `i`-th sector value. A fallback linearly iterates the values. +- `findindex(::SectorValues{I}, c::I)` — Find the index of sector `c`. A fallback linearly searches the values. + +!!! 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 ``𝟙`` acts as the identity under fusion: +```math +𝟙 ⊗ a ≅ a ≅ a ⊗ 𝟙 +``` +The dual of a sector ``a`` is the unique sector ``\bar{a}`` such that: +```math +N^{a\bar{a}}_{𝟙} = 1 \quad \text{and} \quad N^{\bar{a}a}_{𝟙} = 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 ⟶ \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..dcdcc407 --- /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 ⊗ b ⊗ c ⊗ …``, 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..cf19dcd0 --- /dev/null +++ b/docs/src/sectors.md @@ -0,0 +1,39 @@ +# 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) | Rep[ℤₙ] | Unique | Bosonic | No | Clock models, discrete symmetries | +| [`ZNElement`](@ref) | Vec[ℤₙ] | Unique | Varies | No | Group-graded categories, discrete torsion | +| [`U1Irrep`](@ref) | Rep[U₁] | Unique | Bosonic | Yes | Particle number, charge conservation | +| [`SU2Irrep`](@ref) | Rep[SU₂] | Simple | Bosonic | Yes | Spin systems, angular momentum | +| [`CU1Irrep`](@ref) | Rep[U₁ ⋊ ℤ₂] | Simple | Bosonic | Yes | Particle-hole symmetry, O(2) | +| [`DNIrrep`](@ref) | Rep[Dₙ] | Simple | Bosonic | No | Molecular/crystal symmetries | +| [`A4Irrep`](@ref) | Rep[A₄] | Generic | Bosonic | No | Tetrahedral symmetry | +| [`HeisenbergIrrep`](@ref)| Rep[H_N] | Generic | Bosonic | No | Weyl-Heisenberg symmetry, projective representations | +| [`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 | +| [`NamedSector`](@ref) | Product categories | Varies | Varies | Varies | Product sectors with name-accessible components | +| [`TimeReversed`](@ref) | Inverted braiding | Varies | Varies | Varies | Time-reversal or other orientation-reversing symmetries | + +## Multifusion categories + +Beyond fusion categories (with a unique simple unit), TensorKitSectors.jl also supports *multifusion* categories, whose unit is semisimple. +These are signalled by `UnitStyle(I) === GenericUnit()` and require the additional `allunits`, `leftunit`, and `rightunit` methods. +The exported [`IsingBimodule`](@ref) is a minimal worked example: the Ising category reinterpreted as a bimodule category over two copies of `Irrep[ℤ₂]`. +It is intended as a reference implementation illustrating how to build such categories, rather than as a general-purpose sector, so it is not listed in the summary table above. + +## Other packages + +TensorKitSectors.jl provides the architecture for implementing new sector types, but does not implement all possible sectors. Other packages which implement additional sector types and their topological data include: +- [`SUNRepresentations.jl`](https://github.com/QuantumKitHub/SUNRepresentations.jl): Implements irreducible representations of SU(N) for arbitrary N +- [`CategoryData.jl`](https://github.com/QuantumKitHub/CategoryData.jl): Provides a variety of fusion categories up to rank 7, based on the [`AnyonWiki`](https://anyonwiki.github.io/) +- [`QWignerSymbols.jl`](https://github.com/QuantumKitHub/QWignerSymbols.jl): Provides the irreps of q-deformed SU(2) diff --git a/docs/src/sectors/abelian/trivial.md b/docs/src/sectors/abelian/trivial.md new file mode 100644 index 00000000..bff8dd7e --- /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 ⊗ 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..88ebf3e2 --- /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 ⊗ 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..a9a28506 --- /dev/null +++ b/docs/src/sectors/abelian/zn.md @@ -0,0 +1,51 @@ +# Cyclic Group Representations: `ZNIrrep` + +`ZNIrrep{N}` and `LargeZNIrrep{N}` represent irreducible representations of the cyclic group ``ℤ_N``. +Use `ZNIrrep{N}` when possible; it selects the compact storage type automatically. + +!!! note + `ZNIrrep{N}` grades a space by the *irreps* of ``ℤ_N`` (the `Rep[ℤₙ]` category). + To instead grade by the *group elements* of ``ℤ_N`` (the `Vec[ℤₙ]` category, which also supports discrete torsion), see [`ZNElement`](@ref). + +## 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 ⊗ 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 twists. +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..6d1b3a46 --- /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 +τ ⊗ τ = I ⊕ τ. +``` + +Thus `FusionStyle(FibonacciAnyon) = SimpleFusion()`. +The quantum dimensions are + +```math +d_I = 1,\qquad d_τ = φ = \frac{1 + \sqrt{5}}{2}. +``` + +## Topological Data + +The non-trivial associator appears when all external anyons are ``τ``: + +```math +F^{τττ}_{τ} = +\begin{pmatrix} +φ^{-1} & φ^{-1/2}\\ +φ^{-1/2} & -φ^{-1} +\end{pmatrix}, +``` + +in the intermediate basis ``(I,τ)``. +All other allowed `Fsymbol` values are `1`. + +The braiding style is `Anyonic()`. +For ``τ ⊗ τ``, + +```math +R^{ττ}_I = e^{4π i/5},\qquad +R^{ττ}_τ = e^{-3π 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..b9ab54d5 --- /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 +ψ ⊗ ψ = I,\qquad +σ ⊗ ψ = ψ ⊗ σ = σ,\qquad +σ ⊗ σ = I ⊕ ψ. +``` + +Thus `FusionStyle(IsingAnyon) = SimpleFusion()`. +The quantum dimensions are + +```math +d_I = 1,\qquad d_ψ = 1,\qquad d_σ = \sqrt{2}. +``` + +## Topological Data + +The non-trivial associator for four ``σ`` anyons is + +```math +F^{σσσ}_{σ} = +\frac{1}{\sqrt{2}} +\begin{pmatrix} +1 & 1\\ +1 & -1 +\end{pmatrix}, +``` + +in the intermediate basis ``(I,ψ)``. +Additional signs involving the fermion ``ψ`` are encoded by [`Fsymbol`](@ref). + +The braiding style is `Anyonic()`. +Important braiding phases are + +```math +R^{σσ}_I = e^{-π i/8},\qquad +R^{σσ}_ψ = e^{3π i/8},\qquad +R^{ψψ}_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..d2072aed --- /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 non-braided sector to be used in tests and examples, e.g. of planar contractions (hence the name). + +## 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 ⊗ 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/named.md b/docs/src/sectors/composite/named.md new file mode 100644 index 00000000..8f399e14 --- /dev/null +++ b/docs/src/sectors/composite/named.md @@ -0,0 +1,45 @@ +# Named Sectors: `NamedSector` + +`NamedSector` represents the same Deligne tensor product as [`ProductSector`](@ref), but stores its component sectors in a *named* tuple so that they can be accessed by name rather than by position. +This is convenient when a product mixes several symmetries (e.g. a charge and a spin) and positional indexing would be error-prone. + +## Sector type + +```@docs; canonical = false +NamedSector +@NamedSector +``` + +A named sector is constructed with keyword arguments to `⊠`/`NamedSector`, or from a type built with the [`@NamedSector`](@ref) macro: + +```julia +using TensorKitSectors + +s = ⊠(; charge = U1Irrep(1), spin = SU2Irrep(1//2)) # (charge=1, spin=1/2) +s.charge # 1 +s.spin # 1/2 + +I = @NamedSector{charge::U1Irrep, spin::SU2Irrep} +I(U1Irrep(1), SU2Irrep(1//2)) # same as above +``` + + +## Fusion Rules + +`NamedSector` behaves identically to the underlying `ProductSector`: fusion, duality, and units act componentwise, and every piece of topological data is forwarded to the corresponding `ProductSector` of the same components. +Concretely, `NamedSector` converts to `ProductSector` (via `ProductSector(s)`) and delegates [`⊗`](@ref), [`Nsymbol`](@ref), [`dual`](@ref), [`unit`](@ref), and [`allunits`](@ref). +See the [`ProductSector`](@ref) page for the componentwise fusion rules. + +## Topological Data + +[`Fsymbol`](@ref), [`Rsymbol`](@ref), [`Asymbol`](@ref), [`Bsymbol`](@ref), [`fusiontensor`](@ref), and [`dim`](@ref) are all computed by forwarding to the underlying `ProductSector`. +Likewise, [`FusionStyle`](@ref), [`BraidingStyle`](@ref), `UnitStyle`, and the scalar-type traits are inherited from the product of the components. + +## Iteration + +`values(I)` for a `NamedSector` type `I` iterates the same Cartesian product as the corresponding `ProductSector`, wrapping each result back into the named type. +Indexing and `findindex` use the same ordering. + +## References + +- [Deligne tensor product](https://ncatlab.org/nlab/show/Deligne+tensor+product) diff --git a/docs/src/sectors/composite/product.md b/docs/src/sectors/composite/product.md new file mode 100644 index 00000000..42545bc8 --- /dev/null +++ b/docs/src/sectors/composite/product.md @@ -0,0 +1,108 @@ +# 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 +⊠ +deligneproduct +``` + +A product sector stores its component sectors in a tuple. +The recommended constructor is the Deligne product operator `⊠` (typed as `\boxtimes`). +The more verbose alias `deligneproduct` is also available: + +```julia +using TensorKitSectors + +a = Z2Irrep(1) ⊠ FibonacciAnyon(:τ) # (Irrep[ℤ₂](1) ⊠ FibonacciAnyon(:τ)) +deligneproduct(Z2Irrep(1), FibonacciAnyon(:τ)) # same as above +``` + +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,…,a_n),\qquad b = (b_1,…,b_n), +``` + +the fusion outputs are + +```math +a ⊗ b = +\bigoplus_{c_i ∈ a_i ⊗ b_i} N^{ab}_c +(c_1,…,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,…,a_n)^* = (a_1^*,…,a_n^*),\qquad +d_{(a_1,…,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..e670bf38 --- /dev/null +++ b/docs/src/sectors/composite/timereversed.md @@ -0,0 +1,69 @@ +# 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 replaced by its adjoint: + +```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 +θ_{\overline{a}} = \overline{θ_a}. +``` + +For example, in the conventions of this package, + +```math +θ_σ = e^{π i/8} +\quad⟹\quad +θ_{\overline{σ}} = e^{-π 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) diff --git a/docs/src/sectors/fermions/number.md b/docs/src/sectors/fermions/number.md new file mode 100644 index 00000000..10651b84 --- /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 ↦ (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 ⊗ 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..53929e89 --- /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 ⊗ q = p ⊕ 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 +θ_0 = 1,\qquad θ_1 = -1. +``` + +`fusiontensor` is defined for array construction, but it emits a warning because plain arrays with `FermionParity` labels cannot 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) diff --git a/docs/src/sectors/fermions/spin.md b/docs/src/sectors/fermions/spin.md new file mode 100644 index 00000000..6ddcc84b --- /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 ↦ (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 ⊗ 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/groupelement/znelement.md b/docs/src/sectors/groupelement/znelement.md new file mode 100644 index 00000000..9d696912 --- /dev/null +++ b/docs/src/sectors/groupelement/znelement.md @@ -0,0 +1,104 @@ +# Cyclic Group Elements: `ZNElement{N,p}` + +`ZNElement{N,p}` represents the elements of the cyclic group ``ℤ_N`` directly as a `Sector`, as opposed to [`ZNIrrep`](@ref) which represents its (one-dimensional) irreps. +Fusion is given by the group multiplication itself, and the associator can carry a nontrivial phase determined by a level-`p` 3-cocycle on ``ℤ_N``. +For special values of `p` this also yields a consistent braiding, turning `ZNElement{N,p}` into a simple model of abelian anyons. +As a fusion category, this is identified with ``\text{Vec}_{ℤ_N}^{ω_p}``, the category of ``ℤ_N``-graded vector spaces with associator the 3-cocycle ``ω_p``. + +## Group elements as sectors + +```@docs; canonical = false +AbstractGroupElement +``` + +`ZNElement{N,p}` is currently the only concrete subtype of `AbstractGroupElement{Group}`. + +## Sector type + +```@docs; canonical = false +ZNElement +``` + +Elements are labeled by a single integer `n`, stored as `Int8` and only meaningful modulo `N` (which must satisfy `N < 64`). +The concrete type is obtained through `GroupElement[ℤ{N}, p]`, or through the aliases `Z2Element{p}`, `Z3Element{p}`, and `Z4Element{p}` for `N = 2, 3, 4`. +If `p` is omitted, it defaults to `0`, i.e. the trivial cocycle. + +The unit is `ZNElement{N,p}(0)`, and duals are given by group inversion: + +```math +a^* = a^{-1} = -a \bmod N. +``` + +## Fusion Rules + +Fusion is literally the group operation, so there is a single fusion channel with multiplicity one: + +```math +a ⊗ b = (a + b) \bmod N, +``` + +and `FusionStyle(ZNElement{N,p}) = UniqueFusion()` for every `N` and `p`. +All quantum dimensions are `1`. + +## Associativity: the 3-cocycle and F-symbol + +The [`Fsymbol`](@ref) is not assumed trivial: it is given by the 3-cocycle + +```math +ω_p(a,b,c) = \exp\left(\frac{2π i\, p\, a\,\big(b + c - [b+c]_N\big)}{N^2}\right), \qquad [x]_N := x \bmod N, +``` + +which is a representative of a class in ``H^3(ℤ_N, U(1)) ≅ ℤ_N``, indexed by the level `p ∈ 0:N-1`. +Concretely, + +```math +F^{abc}_{e,d,f} = ω_p(a,b,c) \quad \text{when } e = a+b,\ d = e+c,\ f = b + c \pmod N, +``` + +and zero otherwise (since fusion is unique, all the other symbols such as [`Asymbol`](@ref), [`Bsymbol`](@ref) and `frobenius_schur_phase` are likewise built directly out of `ω_p`). +For `p = 0` the cocycle is trivial and `ZNElement{N,0}` reduces to the ordinary ``ℤ_N``-graded vector spaces. +The [`pentagon_equation`](@ref) holds for every `N` and every `p ∈ 0:N-1`, i.e. every `ZNElement{N,p}` is a consistent fusion category. + +## Topological Data (Braiding) + +Braiding is only defined in two cases: + +- `p = 0`: `BraidingStyle(ZNElement{N,0}) = Bosonic()`, and the [`Rsymbol`](@ref) is trivial (`Rsymbol(a,b,c) = Nsymbol(a,b,c)`). +- `2p ≡ N`: `BraidingStyle(ZNElement{N,p}) = Anyonic()`, and + + `````math + R^{ab}_{a+b} = \exp\left(\frac{2\pi i\, p\, a\, b}{N^2}\right), + ````` + + giving abelian anyonic braiding statistics for the ``ℤ_N`` charges. For example, `N = 4, p = 2` (used in the example below) yields topological spins ``θ_a = \exp(iπ a^2 / 4)``. + +For any other value of `p`, `BraidingStyle(ZNElement{N,p}) = NoBraiding()`. + +## Iteration + +`values(ZNElement{N,p})` iterates the labels `0, 1, …, N-1` in increasing order, exactly as for [`ZNIrrep`](@ref). + +## Examples + +```jldoctest znelement_consistency +using TensorKitSectors + +a, b = Z3Element(1), Z3Element(2) +only(a ⊗ b) == unit(typeof(a)) + +# output +true +``` + +```jldoctest znelement_consistency +I = Z4Element{2} # 2p == N, so this admits a braiding +BraidingStyle(I) + +# output +Anyonic() +``` + +## References + +- [Cyclic group](https://en.wikipedia.org/wiki/Cyclic_group) +- [Group cohomology](https://en.wikipedia.org/wiki/Group_cohomology) \ No newline at end of file diff --git a/docs/src/sectors/nonabelian/a4.md b/docs/src/sectors/nonabelian/a4.md new file mode 100644 index 00000000..0d5f98d8 --- /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 ``ℤ_3``: + +```math +a ⊗ b = (a + b) \bmod 3,\qquad a,b ∈ \{0,1,2\}. +``` + +Fusing a one-dimensional irrep with the triplet gives the triplet again: + +```math +a ⊗ 3 = 3 ⊗ a = 3,\qquad a ∈ \{0,1,2\}. +``` + +The triplet product decomposes as + +```math +3 ⊗ 3 = 0 ⊕ 1 ⊕ 2 ⊕ 2 * 3. +``` + +Because the triplet appears with multiplicity two in ``3 ⊗ 3``, `FusionStyle(A4Irrep) = GenericFusion()`. +The [`Nsymbol`](@ref) returns the corresponding integer multiplicity: + +```math +N_c^{ab} = +\begin{cases} +1 + δ_{c,3}, & a = b = 3,\\ +δ_{c,3}, & a = 3 \text{ or } b = 3,\\ +δ_{c,(a+b)\bmod 3}, & a,b ∈ \{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 ⊗ 3 → 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 × d_b × d_c × N_c^{ab}``. +The one-dimensional fusion tensors are scalar phases/permutations in the selected basis. +For ``3 ⊗ 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 & ω & 0\\ +0 & 0 & ω^2 +\end{pmatrix}, +\qquad +S_3 = \frac{1}{3} +\begin{pmatrix} +-1 & 2 & 2\\ +2 & -1 & 2\\ +2 & 2 & -1 +\end{pmatrix}, +\qquad +ω = e^{2π i/3}. +``` + +The one-dimensional irreps have ``S = 1`` and ``T = ω^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..a4d03944 --- /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 ⊗ j_2 = +|j_1-j_2| ⊕ (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 × d_b × d_c × 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}⊗\ket{-j} + \ket{-j}⊗\ket{+j}\right), +``` + +```math +\ket{(0,1)} = +\frac{1}{\sqrt{2}}\left(\ket{+j}⊗\ket{-j} - \ket{-j}⊗\ket{+j}\right). +``` + +In array form, these are the entries + +```math +C_{1,2,1} = \frac{1}{\sqrt{2}},\qquad +C_{2,1,1} = ±\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) ⊗ (j,2) → (j,2):\qquad +\ket{+j}↦\ket{+j},\quad +\ket{-j}↦ (-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)⊗(j_b,2)→(j_a+j_b,2):\qquad +\ket{+j_a,+j_b}↦\ket{+(j_a+j_b)},\quad +\ket{-j_a,-j_b}↦\ket{-(j_a+j_b)}. +``` + +The difference channel pairs opposite weights. If `j_a > j_b`, + +```math +\ket{+j_a,-j_b}↦\ket{+(j_a-j_b)},\qquad +\ket{-j_a,+j_b}↦\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}↦\ket{+(j_b-j_a)},\qquad +\ket{+j_a,-j_b}↦\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: + +```jldoctest +julia> using TensorKitSectors + +julia> values(CU1Irrep)[1] +Irrep[CU₁](0, 0) + +julia> values(CU1Irrep)[2] +Irrep[CU₁](0, 1) + +julia> 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..9a9e59b9 --- /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 = ℤ_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,…,(\frac{N-1}{2},\text{false}). +``` + +For even `N`, the labels are + +```math +(0,\text{false}),\ (0,\text{true}),\ 1,2,…,\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 +ρ_i ⊗ ρ_j = ρ_{|i-j|} ⊕ ρ_{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,±)} = 1,\qquad d_{ρ_j} = 2,\qquad +d_{(N/2,±)} = 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) equals the N-symbol, up to a sign of ``-1`` on the ``(0,-)`` odd one-dimensional output channel produced by fusing two two-dimensional irreps. + +## Fusion Tensor and Basis Conventions + +`fusiontensor(a, b, c)` returns a rank-4 array of size ``d_a × d_b × d_c × 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/heisenberg.md b/docs/src/sectors/nonabelian/heisenberg.md new file mode 100644 index 00000000..e0bc60df --- /dev/null +++ b/docs/src/sectors/nonabelian/heisenberg.md @@ -0,0 +1,128 @@ +# Representation of the finite Heisenberg Group ``H_N``: `HeisenbergIrrep{N}` + +`HeisenbergIrrep{N}` represents irreducible representations (irreps) of the finite Heisenberg group ``H_N`` as a `Sector`. +``H_N`` is the group of ``3× 3`` upper triangular matrices which is identified with the group ``ℤ_N ⋊ ℤ_N^2``; it is only defined here for prime `N`. +This page documents how the type behaves in code (construction, iteration, fusion, and access to topological data). + +## Sector type + +```@docs; canonical = false +HeisenbergIrrep +``` + +Every irrep is labeled by a single integer `n::Int8`, but the meaning of `n` depends on its sign: + +- If `0 <= n < N^2`, the irrep is the one-dimensional character ``χ_{a,b}`` with `a, b = divrem(n, N)`. + There are ``N^2`` such characters, one for each pair `(a, b) ∈ 0:N-1`. +- If `-N < n < 0`, the irrep is the ``N``-dimensional Schrödinger representation ``π_k`` with `k = -n`, `k ∈ 1:N-1`. + +In total there are ``N^2 + N - 1`` irreps (the trivial ``k = 0`` Schrödinger representation is excluded, since it is reducible and decomposes into the ``N^2`` characters). +The alias `Heis3Irrep = HeisenbergIrrep{3}` is provided for the smallest (and most commonly used) case. + +The trivial sector is `HeisenbergIrrep{N}(0)`, i.e. ``χ_{0,0}``. +Duals are + +```math +χ_{a,b}^* = χ_{-a \bmod N,\,-b \bmod N}, \qquad π_k^* = π_{N-k}. +``` + +## Fusion Rules + +Characters fuse among themselves as the abelian group ``ℤ_N × ℤ_N``: + +```math +χ_{a,b} ⊗ χ_{a',b'} = χ_{(a+a')\bmod N,\,(b+b')\bmod N}. +``` + +Fusing a character with a Schrödinger irrep leaves the latter unchanged, regardless of the character's labels: + +```math +χ_{a,b} ⊗ π_k = π_k ⊗ χ_{a,b} = π_k. +``` + +This reflects that ``π_k`` is the unique irrep with a given nontrivial central character, so twisting it by any character of the full group produces an isomorphic representation. + +Two Schrödinger irreps fuse according to whether their labels cancel modulo `N`: + +```math +π_k ⊗ π_{k'} = +\begin{cases} +N ⋅ π_{(k+k') \bmod N}, & k + k' ≢ 0 \pmod N, \\[4pt] +\displaystyle\bigoplus_{a,b=0}^{N-1} χ_{a,b}, & k + k' ≡ 0 \pmod N. +\end{cases} +``` + +In the first case the ``N``-fold multiplicity accounts for the dimension count ``N × N = N^2`` (``N`` copies of the ``N``-dimensional ``π_{(k+k')\bmod N}``); in the second, the ``N^2``-dimensional product decomposes into all ``N^2`` one-dimensional characters, each with multiplicity one. + +Because of this multiplicity-``N`` channel, `FusionStyle(HeisenbergIrrep) = GenericFusion()`. +The [`Nsymbol`](@ref) returns the corresponding integer multiplicities directly from the case distinction above. + +Quantum dimensions are the ordinary representation space dimensions: + +```math +d_{χ_{a,b}} = 1, \qquad d_{π_k} = N. +``` + +## Topological Data + +`BraidingStyle(HeisenbergIrrep) = Bosonic()`: braiding is symmetric, and `braidingscalartype(HeisenbergIrrep) = ComplexF64` since the Clebsch-Gordan coefficients are generally complex. + +The [`Fsymbol`](@ref) and [`Rsymbol`](@ref) are both computed generically from the chosen [`fusiontensor`](@ref) basis (via `Fsymbol_from_fusiontensor` and `Rsymbol_from_fusiontensor`), see below. +On the single-channel sectors (``χ ⊗ χ → χ`` and ``χ ⊗ π → π``) the R-symbol evaluates to the trivial phase `1`. +On the multiplicity-``N`` channel ``π_k ⊗ π_{k'} → π_{(k+k')\bmod N}`` and on the channels ``π_k ⊗ π_{-k} → χ_{a,b}``, the R-symbol carries nontrivial phases built out of `N`-th roots of unity. + +## Fusion Tensor and Basis Conventions + +`fusiontensor(a, b, c)` returns a rank-4 array of size ``d_a × d_b × d_c × N_c^{ab}``. +The one-dimensional (character) fusion tensors are trivial scalars. +For the Schrödinger irreps, the fusion tensor is built out of the ``N``-th root of unity ``ω = e^{2π i/N}``, following the reference below: + +- ``π_k ⊗ π_{k'} → π_{(k+k')\bmod N}`` (``k+k' ≢ 0``): a permutation tensor (with a phase for the case where one of the factors is a character, see below). +- ``π_k ⊗ π_{-k} → χ_{a,b}``: normalized with a factor ``1/\sqrt{N}``, with phase ``ω^{-a i}`` along the surviving diagonal. +- ``χ_{a,b} ⊗ π_k → π_k`` (and the symmetric ``π_k ⊗ χ_{a,b} → π_k``): a permutation tensor dressed with the phase ``ω^{a m}``. + +The representation can be checked against the generators `X` and `Z` of ``H_N`` acting as ``X|i\rangle = |i+1\rangle`` and ``Z|i\rangle = ω^{ki}|i\rangle`` on ``π_k``, and as ``X ↦ ω^a``, ``Z ↦ ω^b`` on ``χ_{a,b}``. +For every allowed fusion channel, the fusion tensor acts as an intertwiner between the tensor product representation and the fused representation: + +```jldoctest heisenberg_intertwiner; output = false +using TensorKitSectors +using LinearAlgebra: kron, Diagonal +using Test: @test + +N = 3 +ω = cispi(2 / N) + +function X(s::HeisenbergIrrep{N}) where {N} + s.n < 0 || return hcat(ω^div(s.n, N)) + M = zeros(ComplexF64, N, N) + for i in 0:(N - 1) + M[mod(i + 1, N) + 1, i + 1] = 1 + end + return M +end +Z(s::HeisenbergIrrep{N}) where {N} = s.n < 0 ? Diagonal([ω^(-s.n * i) for i in 0:(N - 1)]) : hcat(ω^mod(s.n, N)) + +for a in values(HeisenbergIrrep{N}), b in values(HeisenbergIrrep{N}) + for c in a ⊗ b + C = fusiontensor(a, b, c) + for μ in 1:Nsymbol(a, b, c) + Cmat = reshape(view(C, :, :, :, μ), dim(a) * dim(b), dim(c)) + @test Cmat' * kron(X(b), X(a)) * Cmat ≈ X(c) + @test Cmat' * kron(Z(b), Z(a)) * Cmat ≈ Z(c) + end + end +end + +# output +``` + +## Iteration + +Iterating over `values(HeisenbergIrrep{N})` first yields the ``N^2`` characters ``χ_{0,0}, χ_{0,1}, …`` (in order of increasing `n`), followed by the Schrödinger irreps ``π_1, …, π_{N-1}``. + +## References + +For background on the group and the representation basis, see: + +- [Heisenberg group](https://en.wikipedia.org/wiki/Heisenberg_group#Heisenberg_group_modulo_an_odd_prime_p) +- [Bacon, D., How a Clebsch-Gordan Transform Helps to Solve the Heisenberg Hidden Subgroup Problem](https://arxiv.org/abs/quant-ph/0612107) diff --git a/docs/src/sectors/nonabelian/su2.md b/docs/src/sectors/nonabelian/su2.md new file mode 100644 index 00000000..7c63b81f --- /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 ⊗ 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| ≤ j_c ≤ j_a + j_b ∧ j_a + j_b + j_c ∈ ℕ +``` + +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_d} \sqrt{d_e d_f} \begin{Bmatrix} +j_a & j_b & j_e \\ +j_c & j_d & j_f +\end{Bmatrix} +``` + +The `BraidingStyle` is `Bosonic`, and [`Rsymbol`](@ref) is ``± 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 × d_b × d_c × 1``. +We can label the basis by using ``\ket{j, m}``, where the magnetic quantum number takes on values ``m ∈ \{j, j-1, …, -j\}`` (in that order). + +Each irrep acts on the standard basis $\lvert j, m \rangle$ where $m = j, j-1, …, -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_± \lvert j, m \rangle = \sqrt{(j ∓ m)(j ± m + 1)}\, \lvert j, m ± 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 ⊗ b`` is given in terms of the representations ``a`` and ``b`` as: + +```math +\mathbf{J}^{(a ⊗ b)} = \mathbf{J}^{(a)} ⊗ \mathbf{1}^{(b)} + \mathbf{1}^{(a)} ⊗ \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 ⊗ \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)} ⊗ \mathbf{1}^{(b)} + \mathbf{1}^{(a)} ⊗ \mathbf{J}^{(b)}\right) ⋅ C^c_{ab} = + C^c_{ab} ⋅ \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 (Ja, Jb, Jc) in zip(generators(a), generators(b), generators(c)) + @tensor lhs[a b; c] := Ja[a; a'] * CGC[a' b; c] + Jb[b; b'] * CGC[a b'; c] + @tensor rhs[a b; c] := CGC[a b; c'] * Jc[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/groupelements.jl b/src/groupelements.jl index 2657b01d..3309f016 100644 --- a/src/groupelements.jl +++ b/src/groupelements.jl @@ -16,19 +16,21 @@ overridden by a concrete implementation of `AbstractGroupElement`. For the fusion structure, a specific `SomeGroupElement <: AbstractGroupElement{SomeGroup}` should only implement the following methods + ```julia -Base.:*(c1::GroupElement, c2::GroupElement) -> GroupElement -Base.one(::Type{GroupElement}) -> GroupElement -Base.inv(c::GroupElement) -> GroupElement +Base.:*(c1::SomeGroupElement, c2::SomeGroupElement) -> SomeGroupElement +Base.one(::Type{SomeGroupElement}) -> SomeGroupElement +Base.inv(c::SomeGroupElement) -> SomeGroupElement # and optionally -TensorKitSectors.cocycle(c1::GroupElement, c2::GroupElement, c3::GroupElement) -> Number +TensorKitSectors.cocycle(c1::SomeGroupElement, c2::SomeGroupElement, c3::SomeGroupElement) -> Number ``` + The methods `conj`, `dual`, `⊗`, `Nsymbol`, `Fsymbol`, `dim`, `Asymbol`, `Bsymbol` and `frobenius_schur_phase` will then be automatically defined. If no `cocycle` method is defined, the cocycle will be assumed to be trivial, i.e. equal to `1`. - """ abstract type AbstractGroupElement{G <: Group} <: Sector end + FusionStyle(::Type{<:AbstractGroupElement}) = UniqueFusion() BraidingStyle(::Type{<:AbstractGroupElement}) = NoBraiding() @@ -100,7 +102,7 @@ to the constructor, but only the value `mod(n, N)` is relevant. The second type is then being given by ```julia -cocycle(a, b, c) = cispi(2 * p * a.n * (b.n + c.n - mod(b.n + c.n, N)) / N)) +cocycle(a, b, c) = cispi(2 * p * a.n * (b.n + c.n - mod(b.n + c.n, N)) / N^2)) ``` If `p` is not specified, it defaults to `0`, i.e. the trivial cocycle. 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/named.jl b/src/named.jl index 2114d36d..8d0eb130 100644 --- a/src/named.jl +++ b/src/named.jl @@ -23,7 +23,7 @@ julia> s.spin ``` !!! warning - The sectors are stored internally in the `s.sectors` field, so the named components should not contain the `sectors` as a name. + The sectors are stored internally in the `s.sectors` field, so the named components should not contain `sectors` as a name. """ struct NamedSector{NT <: NamedSectorTuple} <: Sector sectors::NT diff --git a/src/sectors.jl b/src/sectors.jl index 6c58dda8..e658ba69 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