diff --git a/docs/make.jl b/docs/make.jl
index ac7cb3e3..35ba3604 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -29,6 +29,7 @@ makedocs(;
"Optional Methods" => "interface/optional.md",
"Traits and Styles" => "interface/traits.md",
"Implementation Guidelines" => "interface/guidelines.md",
+ "Testing a Sector Implementation" => "interface/testsuite.md",
],
"Sector Types" => [
"Overview" => "sectors.md",
diff --git a/docs/src/interface/optional.md b/docs/src/interface/optional.md
index 9d947dd9..8b244d5c 100644
--- a/docs/src/interface/optional.md
+++ b/docs/src/interface/optional.md
@@ -55,8 +55,7 @@ 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.
+ 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
diff --git a/docs/src/interface/overview.md b/docs/src/interface/overview.md
index b9d9f6fa..4736daf5 100644
--- a/docs/src/interface/overview.md
+++ b/docs/src/interface/overview.md
@@ -30,3 +30,4 @@ The interface documentation is divided into several pages:
- **[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
+- **[Testing a Sector Implementation](testsuite.md)**: A reusable test suite that checks whether a new sector type satisfies the required categorical properties and is functional with TensorKit.jl
diff --git a/docs/src/interface/testsuite.md b/docs/src/interface/testsuite.md
new file mode 100644
index 00000000..4bc0808e
--- /dev/null
+++ b/docs/src/interface/testsuite.md
@@ -0,0 +1,99 @@
+```@meta
+CollapsedDocStrings = true
+```
+
+# Testing a Sector Implementation
+
+Implementing a new `Sector` subtype means providing a family of mutually consistent methods: fusion rules, F- and R-symbols, dimensions, dualities, an ordering, ...
+These have to satisfy several non-trivial categorical identities (the pentagon and hexagon equations, unitarity of the F- and R-move, ...), but must also be compatible with TensorKit.jl as a data structure.
+TensorKitSectors.jl ships a reusable test suite, `SectorTestSuite`, that checks exactly these properties for any `Sector` subtype.
+It is defined within the package's own tests, but can be accessed by downstream packages or users testing their own sector implementations locally.
+
+Here, we explain how to use the test suite, what it checks, and how to add new tests.
+Currently, the suite is designed to run all tests for a single sector type at a time, as every single test within the test suite necessarily must pass for compatibility with TensorKit.jl.
+
+## Running the test suite
+
+Since the test suite is not part of the installed package, it is loaded as follows:
+
+```julia
+import TensorKitSectors
+testsuite_path = joinpath(
+ dirname(dirname(pathof(TensorKitSectors))), # TensorKitSectors root
+ "test", "testsuite.jl"
+)
+include(testsuite_path)
+
+SectorTestSuite.test_sector(MySectorType)
+```
+
+`test_sector` runs one `@testsuite` per property (see [What gets tested](@ref) below) and reports which ones fail for `MySectorType`.
+The suite relies on [`TestExtras.jl`](https://github.com/Jutho/TestExtras.jl) for `@testinferred`, which checks both the return value and the type stability of an expression, so downstream packages should add `TestExtras` as a test dependency.
+
+
+## What gets tested
+
+Broadly, the sector tests fall into three categories:
+
+```@raw html
+
+
+
+ | Category | Checks |
+
+
+
+ | Interface & type stability |
+
+ 1) Basic properties: the required methods exist, are type-stable, and return the documented types
+ 2) Show and parse: Sectors are printed in a parseable manner
+ 3) Value iterator: Sectors are ordered within values(I) in a consistent and expected manner (through findindex)
+ |
+
+
+
+ | Category-theoretic consistency |
+ The algebraic identities a unitary (braided) fusion category must satisfy: pentagon and hexagon equations, unitarity of the F- and R-move, triangle equation, ribbon condition, self-duality of the braiding, symmetric braiding condition, Artin braid equality |
+
+
+
+ | Cross-consistency of derived quantities |
+ Whenever a quantity can be computed both directly and through a generic fallback derived from other data (e.g. dim versus the internal dim_from_Fsymbol, or Bsymbol versus Bsymbol_from_fusiontensor), the two must agree |
+
+
+
+```
+
+Tests that only apply to a subset of sectors are skipped automatically based on traits: braiding-related testsets pass untested unless `BraidingStyle(I) isa HasBraiding`, the fusion-tensor comparisons are skipped when `fusiontensor` has no method for `I`, and so on.
+A test therefore never fails simply because a sector chooses not to implement an optional method.
+
+## Test utilities
+
+Most tests need a handful of representative sectors rather than the full (possibly infinite) set of values of `I`.
+`SectorTestSuite` exports a few helpers to construct those:
+
+- `smallset(I, size=5, maxdim=10)`: a small, shuffled sample of sectors of type `I` with dimension below `maxdim`, biased to include a non-abelian sector when `FusionStyle(I) isa MultipleFusion` so tests don't silently degenerate to the abelian case.
+- `randsector(I)`: draws a single, non-unit sector at random from `smallset(I)`.
+- `random_fusion(I, N)`: draws `N` random sectors such that every consecutive pair can be fused, i.e. builds a valid fusion series `a ⊗ b ⊗ ...`.
+
+Besides this, other utility functions are:
+- `can_fuse(a, b)`: whether `a ⊗ b` is non-empty.
+ Allows to test only non-trivial topological data, e.g. the F-symbols of fusion vertices that actually exist.
+- `hasfusiontensor(I)`: whether [`fusiontensor`](@ref) has an implementation for `I`.
+
+Additionally, the following tests are provided as standalone functions that can be used outside of the test suite:
+- `F_unitarity_test(a, b, c; kwargs...)` / `R_unitarity_test(a, b; kwargs...)`: check unitarity of the F- and R-move, forwarding `kwargs` to `isapprox`.
+
+These are all exported by `SectorTestSuite` and are also useful when writing additional, sector-specific tests beyond what the shared suite covers.
+
+## Adding a new test to the test suite
+
+Users who wish to contribute new checks to this package can register them with the `@testsuite` macro, which takes a name and a function of the sector type `I`:
+
+```julia
+@testsuite "My new property" I -> begin
+ # test body
+end
+```
+
+These should be added under `test/sectors.jl`, while additional utility functions should be added to `test/testsuite.jl` and exported from `SectorTestSuite`.
\ No newline at end of file
diff --git a/docs/src/sectors.md b/docs/src/sectors.md
index cf19dcd0..f3f365bc 100644
--- a/docs/src/sectors.md
+++ b/docs/src/sectors.md
@@ -33,7 +33,8 @@ It is intended as a reference implementation illustrating how to build such cate
## 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:
+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/composite/product.md b/docs/src/sectors/composite/product.md
index a3415387..4d7e7262 100644
--- a/docs/src/sectors/composite/product.md
+++ b/docs/src/sectors/composite/product.md
@@ -7,7 +7,8 @@ end
# 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.
+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
diff --git a/docs/src/sectors/groupelement/znelement.md b/docs/src/sectors/groupelement/znelement.md
index 9d696912..6a183650 100644
--- a/docs/src/sectors/groupelement/znelement.md
+++ b/docs/src/sectors/groupelement/znelement.md
@@ -70,7 +70,8 @@ Braiding is only defined in two cases:
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)``.
+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()`.
diff --git a/docs/src/sectors/nonabelian/cu1.md b/docs/src/sectors/nonabelian/cu1.md
index a4d03944..910a3c2c 100644
--- a/docs/src/sectors/nonabelian/cu1.md
+++ b/docs/src/sectors/nonabelian/cu1.md
@@ -52,7 +52,8 @@ For a two-dimensional irrep `(j, 2)` with `j > 0`, the basis is ordered as a pai
\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.
+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:
@@ -94,7 +95,8 @@ For two positive charges, the sum channel is diagonal:
\ket{-j_a,-j_b}↦\ket{-(j_a+j_b)}.
```
-The difference channel pairs opposite weights. If `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
@@ -108,7 +110,8 @@ If `j_b > j_a`, the output basis is ordered by the positive charge `j_b - j_a`,
\ket{+j_a,-j_b}↦\ket{-(j_b-j_a)}.
```
-All omitted entries are zero. These conventions determine the real [`Fsymbol`](@ref) values.
+All omitted entries are zero.
+These conventions determine the real [`Fsymbol`](@ref) values.
## Iteration