From 24ca355792486c70ecfea0c63336afa4b0f55912 Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Fri, 17 Oct 2025 23:03:37 +0200 Subject: [PATCH 1/7] docs update part 1 --- docs/make.jl | 8 +- docs/src/{man => appendix}/categories.md | 0 .../img/diagram-Brelation.svg | 0 .../{man => appendix}/img/diagram-Fmove.svg | 0 .../{man => appendix}/img/diagram-Fmove1.svg | 0 .../img/diagram-Zisomorphism.svg | 0 .../{man => appendix}/img/diagram-ZtoF.svg | 0 .../img/diagram-braiding.svg | 0 .../img/diagram-braiding2.svg | 0 .../img/diagram-braidingR.svg | 0 .../img/diagram-braidingdual.svg | 0 .../{man => appendix}/img/diagram-dagger.svg | 0 .../{man => appendix}/img/diagram-fusion.svg | 0 .../img/diagram-isomorphism.svg | 0 .../img/diagram-leftdual.svg | 0 .../img/diagram-pivotalfromtwist.svg | 0 .../img/diagram-rightdual.svg | 0 .../img/diagram-simpletwist.svg | 0 .../img/diagram-splittingfusionrelation.svg | 0 .../img/diagram-tensorproduct.svg | 0 .../{man => appendix}/img/diagram-trace.svg | 0 .../img/diagram-transpose.svg | 0 .../img/diagram-twistfrobeniusschur.svg | 0 .../{man => appendix}/img/diagram-twists.svg | 0 .../img/diagram_morphism.svg | 0 docs/src/{man => appendix}/img/diagrams.pdf | Bin docs/src/{man => appendix}/img/diagrams.svg | 0 docs/src/index.md | 33 ++- docs/src/lib/fusiontrees.md | 55 ++++ docs/src/lib/sectors.md | 144 ++++++----- docs/src/lib/spaces.md | 9 +- docs/src/man/intro.md | 9 +- docs/src/man/spaces.md | 234 +++++++++++------- 33 files changed, 312 insertions(+), 180 deletions(-) rename docs/src/{man => appendix}/categories.md (100%) rename docs/src/{man => appendix}/img/diagram-Brelation.svg (100%) rename docs/src/{man => appendix}/img/diagram-Fmove.svg (100%) rename docs/src/{man => appendix}/img/diagram-Fmove1.svg (100%) rename docs/src/{man => appendix}/img/diagram-Zisomorphism.svg (100%) rename docs/src/{man => appendix}/img/diagram-ZtoF.svg (100%) rename docs/src/{man => appendix}/img/diagram-braiding.svg (100%) rename docs/src/{man => appendix}/img/diagram-braiding2.svg (100%) rename docs/src/{man => appendix}/img/diagram-braidingR.svg (100%) rename docs/src/{man => appendix}/img/diagram-braidingdual.svg (100%) rename docs/src/{man => appendix}/img/diagram-dagger.svg (100%) rename docs/src/{man => appendix}/img/diagram-fusion.svg (100%) rename docs/src/{man => appendix}/img/diagram-isomorphism.svg (100%) rename docs/src/{man => appendix}/img/diagram-leftdual.svg (100%) rename docs/src/{man => appendix}/img/diagram-pivotalfromtwist.svg (100%) rename docs/src/{man => appendix}/img/diagram-rightdual.svg (100%) rename docs/src/{man => appendix}/img/diagram-simpletwist.svg (100%) rename docs/src/{man => appendix}/img/diagram-splittingfusionrelation.svg (100%) rename docs/src/{man => appendix}/img/diagram-tensorproduct.svg (100%) rename docs/src/{man => appendix}/img/diagram-trace.svg (100%) rename docs/src/{man => appendix}/img/diagram-transpose.svg (100%) rename docs/src/{man => appendix}/img/diagram-twistfrobeniusschur.svg (100%) rename docs/src/{man => appendix}/img/diagram-twists.svg (100%) rename docs/src/{man => appendix}/img/diagram_morphism.svg (100%) rename docs/src/{man => appendix}/img/diagrams.pdf (100%) rename docs/src/{man => appendix}/img/diagrams.svg (100%) create mode 100644 docs/src/lib/fusiontrees.md diff --git a/docs/make.jl b/docs/make.jl index 5a7f2e8db..057981577 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -13,11 +13,15 @@ links = InterLinks( pages = [ "Home" => "index.md", "Manual" => [ - "man/intro.md", "man/tutorial.md", "man/categories.md", + "man/intro.md", "man/tutorial.md", "man/spaces.md", "man/sectors.md", "man/tensors.md", ], - "Library" => ["lib/sectors.md", "lib/spaces.md", "lib/tensors.md"], + "Library" => [ + "lib/sectors.md", "lib/fusiontrees.md", + "lib/spaces.md", "lib/tensors.md", + ], "Index" => ["index/index.md"], + "Appendix" => ["appendix/categories.md"], ] makedocs(; diff --git a/docs/src/man/categories.md b/docs/src/appendix/categories.md similarity index 100% rename from docs/src/man/categories.md rename to docs/src/appendix/categories.md diff --git a/docs/src/man/img/diagram-Brelation.svg b/docs/src/appendix/img/diagram-Brelation.svg similarity index 100% rename from docs/src/man/img/diagram-Brelation.svg rename to docs/src/appendix/img/diagram-Brelation.svg diff --git a/docs/src/man/img/diagram-Fmove.svg b/docs/src/appendix/img/diagram-Fmove.svg similarity index 100% rename from docs/src/man/img/diagram-Fmove.svg rename to docs/src/appendix/img/diagram-Fmove.svg diff --git a/docs/src/man/img/diagram-Fmove1.svg b/docs/src/appendix/img/diagram-Fmove1.svg similarity index 100% rename from docs/src/man/img/diagram-Fmove1.svg rename to docs/src/appendix/img/diagram-Fmove1.svg diff --git a/docs/src/man/img/diagram-Zisomorphism.svg b/docs/src/appendix/img/diagram-Zisomorphism.svg similarity index 100% rename from docs/src/man/img/diagram-Zisomorphism.svg rename to docs/src/appendix/img/diagram-Zisomorphism.svg diff --git a/docs/src/man/img/diagram-ZtoF.svg b/docs/src/appendix/img/diagram-ZtoF.svg similarity index 100% rename from docs/src/man/img/diagram-ZtoF.svg rename to docs/src/appendix/img/diagram-ZtoF.svg diff --git a/docs/src/man/img/diagram-braiding.svg b/docs/src/appendix/img/diagram-braiding.svg similarity index 100% rename from docs/src/man/img/diagram-braiding.svg rename to docs/src/appendix/img/diagram-braiding.svg diff --git a/docs/src/man/img/diagram-braiding2.svg b/docs/src/appendix/img/diagram-braiding2.svg similarity index 100% rename from docs/src/man/img/diagram-braiding2.svg rename to docs/src/appendix/img/diagram-braiding2.svg diff --git a/docs/src/man/img/diagram-braidingR.svg b/docs/src/appendix/img/diagram-braidingR.svg similarity index 100% rename from docs/src/man/img/diagram-braidingR.svg rename to docs/src/appendix/img/diagram-braidingR.svg diff --git a/docs/src/man/img/diagram-braidingdual.svg b/docs/src/appendix/img/diagram-braidingdual.svg similarity index 100% rename from docs/src/man/img/diagram-braidingdual.svg rename to docs/src/appendix/img/diagram-braidingdual.svg diff --git a/docs/src/man/img/diagram-dagger.svg b/docs/src/appendix/img/diagram-dagger.svg similarity index 100% rename from docs/src/man/img/diagram-dagger.svg rename to docs/src/appendix/img/diagram-dagger.svg diff --git a/docs/src/man/img/diagram-fusion.svg b/docs/src/appendix/img/diagram-fusion.svg similarity index 100% rename from docs/src/man/img/diagram-fusion.svg rename to docs/src/appendix/img/diagram-fusion.svg diff --git a/docs/src/man/img/diagram-isomorphism.svg b/docs/src/appendix/img/diagram-isomorphism.svg similarity index 100% rename from docs/src/man/img/diagram-isomorphism.svg rename to docs/src/appendix/img/diagram-isomorphism.svg diff --git a/docs/src/man/img/diagram-leftdual.svg b/docs/src/appendix/img/diagram-leftdual.svg similarity index 100% rename from docs/src/man/img/diagram-leftdual.svg rename to docs/src/appendix/img/diagram-leftdual.svg diff --git a/docs/src/man/img/diagram-pivotalfromtwist.svg b/docs/src/appendix/img/diagram-pivotalfromtwist.svg similarity index 100% rename from docs/src/man/img/diagram-pivotalfromtwist.svg rename to docs/src/appendix/img/diagram-pivotalfromtwist.svg diff --git a/docs/src/man/img/diagram-rightdual.svg b/docs/src/appendix/img/diagram-rightdual.svg similarity index 100% rename from docs/src/man/img/diagram-rightdual.svg rename to docs/src/appendix/img/diagram-rightdual.svg diff --git a/docs/src/man/img/diagram-simpletwist.svg b/docs/src/appendix/img/diagram-simpletwist.svg similarity index 100% rename from docs/src/man/img/diagram-simpletwist.svg rename to docs/src/appendix/img/diagram-simpletwist.svg diff --git a/docs/src/man/img/diagram-splittingfusionrelation.svg b/docs/src/appendix/img/diagram-splittingfusionrelation.svg similarity index 100% rename from docs/src/man/img/diagram-splittingfusionrelation.svg rename to docs/src/appendix/img/diagram-splittingfusionrelation.svg diff --git a/docs/src/man/img/diagram-tensorproduct.svg b/docs/src/appendix/img/diagram-tensorproduct.svg similarity index 100% rename from docs/src/man/img/diagram-tensorproduct.svg rename to docs/src/appendix/img/diagram-tensorproduct.svg diff --git a/docs/src/man/img/diagram-trace.svg b/docs/src/appendix/img/diagram-trace.svg similarity index 100% rename from docs/src/man/img/diagram-trace.svg rename to docs/src/appendix/img/diagram-trace.svg diff --git a/docs/src/man/img/diagram-transpose.svg b/docs/src/appendix/img/diagram-transpose.svg similarity index 100% rename from docs/src/man/img/diagram-transpose.svg rename to docs/src/appendix/img/diagram-transpose.svg diff --git a/docs/src/man/img/diagram-twistfrobeniusschur.svg b/docs/src/appendix/img/diagram-twistfrobeniusschur.svg similarity index 100% rename from docs/src/man/img/diagram-twistfrobeniusschur.svg rename to docs/src/appendix/img/diagram-twistfrobeniusschur.svg diff --git a/docs/src/man/img/diagram-twists.svg b/docs/src/appendix/img/diagram-twists.svg similarity index 100% rename from docs/src/man/img/diagram-twists.svg rename to docs/src/appendix/img/diagram-twists.svg diff --git a/docs/src/man/img/diagram_morphism.svg b/docs/src/appendix/img/diagram_morphism.svg similarity index 100% rename from docs/src/man/img/diagram_morphism.svg rename to docs/src/appendix/img/diagram_morphism.svg diff --git a/docs/src/man/img/diagrams.pdf b/docs/src/appendix/img/diagrams.pdf similarity index 100% rename from docs/src/man/img/diagrams.pdf rename to docs/src/appendix/img/diagrams.pdf diff --git a/docs/src/man/img/diagrams.svg b/docs/src/appendix/img/diagrams.svg similarity index 100% rename from docs/src/man/img/diagrams.svg rename to docs/src/appendix/img/diagrams.svg diff --git a/docs/src/index.md b/docs/src/index.md index bf88806fe..be59bb44e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -17,29 +17,38 @@ factorizations). Finally, tensor contractions can be performed using the `@tenso from [TensorOperations.jl](https://github.com/QuantumKitHub/TensorOperations.jl). Currently, most effort is oriented towards tensors as they appear in the context of quantum -many body physics and in particular the field of tensor networks. Such tensors often have -large dimensions and take on a specific structure when symmetries are present. To deal with -generic symmetries, we employ notations and concepts from category theory all the way down -to the definition of a tensor. +many-body physics and in particular the field of tensor networks. Such tensors often have +large dimensions and take on a specific structure when symmetries are present. By employing +concepts from category theory, we can represent and manipulate tensors with a large +variety of symmetries, including abelian and non-abelian symmetries, fermionic statistics, +as well as generalized (a.k.a. non-invertible or anyonic) symmetries. At the same time, TensorKit.jl focusses on computational efficiency and performance. The -underlying storage of a tensor's data can be any `DenseArray`. Currently, certain operations -are already multithreaded, either by distributing the different blocks in case of a -structured tensor (i.e. with symmetries) or by using multithreading provided by the package -[Strided.jl](https://github.com/Jutho/Strided.jl). In the future, we also plan to -investigate using `CuArray`s as underlying storage for the tensors data, so as to leverage -GPUs for the different operations defined on tensors. +underlying storage of a tensor's data can be any `DenseArray`. When the data is stored +in main memory (corresponding to `Array`), multiple CPUs can be leveraged as many +operations come with multithreaded implementations, either by distributing the different +blocks in case of a structured tensor (i.e. with symmetries) or by using multithreading +provided by the package [Strided.jl](https://github.com/Jutho/Strided.jl). Support for +storing and manipulating tensors on NVidia and AMD GPUs is currently being developed, +whereas support for distributed arrays is planned for the future. ## Contents of the manual ```@contents -Pages = ["man/intro.md", "man/categories.md", "man/spaces.md", "man/sectors.md", "man/tensors.md"] +Pages = ["man/intro.md", "man/spaces.md", "man/sectors.md", "man/tensors.md"] Depth = 3 ``` ## Library outline ```@contents -Pages = ["lib/sectors.md","lib/spaces.md","lib/tensors.md"] +Pages = ["lib/sectors.md","lib/fusiontrees.md","lib/spaces.md","lib/tensors.md"] Depth = 2 ``` + +## Appendix + +```@contents +Pages = ["appendix/categories.md"] +Depth = 3 +``` diff --git a/docs/src/lib/fusiontrees.md b/docs/src/lib/fusiontrees.md new file mode 100644 index 000000000..b3cdb041e --- /dev/null +++ b/docs/src/lib/fusiontrees.md @@ -0,0 +1,55 @@ +# Fusion trees + +```@meta +CurrentModule = TensorKit +``` + +# Type hierarchy + +```@docs +FusionTree +``` + +## Methods for defining and generating fusion trees + +```@docs +fusiontrees(uncoupled::NTuple{N,I}, coupled::I, + isdual::NTuple{N,Bool}) where {N,I<:Sector} +``` + +## Methods for manipulating fusion trees + +For manipulating single fusion trees, the following internal methods are defined: +```@docs +insertat +split +merge +elementary_trace +planar_trace(f::FusionTree{I,N}, q1::IndexTuple{N₃}, q2::IndexTuple{N₃}) where {I<:Sector,N,N₃} +artin_braid +braid(f::FusionTree{I,N}, levels::NTuple{N,Int}, p::NTuple{N,Int}) where {I<:Sector,N} +permute(f::FusionTree{I,N}, p::NTuple{N,Int}) where {I<:Sector,N} +``` + +These can be composed to implement elementary manipulations of fusion-splitting tree pairs, +according to the following methods + +```julia +# TODO: add documentation for the following methods +TensorKit.bendright +TensorKit.bendleft +TensorKit.foldright +TensorKit.foldleft +TensorKit.cycleclockwise +TensorKit.cycleanticlockwise +``` + +Finally, these are used to define large manipulations of fusion-splitting tree pairs, which +are then used in the index manipulation of `AbstractTensorMap` objects. The following methods +defined on fusion splitting tree pairs have an associated definition for tensors. +```@docs +repartition(::FusionTree{I,N₁}, ::FusionTree{I,N₂}, ::Int) where {I<:Sector,N₁,N₂} +transpose(::FusionTree{I}, ::FusionTree{I}, ::IndexTuple{N₁}, ::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} +braid(::FusionTree{I}, ::FusionTree{I}, ::IndexTuple, ::IndexTuple, ::IndexTuple{N₁}, ::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} +permute(::FusionTree{I}, ::FusionTree{I}, ::IndexTuple{N₁}, ::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} +``` diff --git a/docs/src/lib/sectors.md b/docs/src/lib/sectors.md index 931bd9170..f6a4dc86b 100644 --- a/docs/src/lib/sectors.md +++ b/docs/src/lib/sectors.md @@ -1,38 +1,98 @@ -# Symmetry sectors and fusion trees +# Symmetry sectors ```@meta CurrentModule = TensorKit ``` ## Type hierarchy + +The fundamental abstract supertype for symmetry sectors is `Sector`: + ```@docs Sector -SectorValues -FusionStyle -BraidingStyle -AbstractIrrep +``` + +Various concrete subtypes of `Sector` are provided within the TensorKitSectors library: + +```@docs Trivial +AbstractIrrep ZNIrrep +DNIrrep U1Irrep SU2Irrep CU1Irrep -ProductSector +AbstractGroupElement +ZNElement FermionParity FermionNumber FermionSpin FibonacciAnyon IsingAnyon +PlanarTrivial +IsingBimodule +TimeReversed +ProductSector +``` + +Several more concrete sector types can be found in other packages such as +[SUNRepresentations.jl](https://github.com/QuantumKitHub/SUNRepresentations.jl), +[CategoryData.jl](https://github.com/lkdvos/CategoryData.jl), +[QWignerSymbols.jl](https://github.com/lkdvos/QWignerSymbols.jl), ...: + +Some of these types are parameterized by a type parameter that represents a group. +We therefore also provide a number of types to represent groups: + +```@docs +TensorKitSectors.Group +TensorKitSectors.AbelianGroup +TensorKitSectors.Cyclic +TensorKitSectors.U₁ +TensorKitSectors.CU₁ +TensorKitSectors.SU +TensorKitSectors.Dihedral +TensorKitSectors.ProductGroup +``` + +The following types are used to characterise different properties of the different types +of sectors: + +```@docs +FusionStyle +BraidingStyle +UnitStyle +``` + +Finally, the following auxiliary types are defined to facilitate the implementation +of some of the methods on sectors: + +```@docs +TensorKitSectors.SectorValues +TensorKitSectors.SectorProductIterator ``` ## Useful constants + +The following constants are defined to facilitate obtaining the type associated +with the group elements or the irreducible representations of a given group: + ```@docs Irrep +GroupElement ``` -## Methods for defining and characterizing `Sector` subtypes +## Methods for characterizing and manipulating `Sector` objects + +The following methods can be used to obtain properties such as topological data +of sector objects, or to manipulate them or create related sectors: + ```@docs -unit(::Sector) -dual +unit +isunit +leftunit +rightunit +allunits +dual(::Sector) Nsymbol ⊗ Fsymbol @@ -40,81 +100,37 @@ Rsymbol Bsymbol dim(::Sector) frobenius_schur_phase +frobenius_schur_indicator twist(::Sector) Base.isreal(::Type{<:Sector}) TensorKitSectors.sectorscalartype deligneproduct(::Sector, ::Sector) ``` -Compile all revelant methods for a sector: +We have also the following methods that are specific to certain types of sectors +and serve as accessors to their fields: ```@docs -TensorKitSectors.precompile_sector +charge +modulus ``` - -## Types and methods for groups - -Types and constants: - -```julia -# TODO: add documentation for the following types -Group -TensorKitSectors.AbelianGroup -U₁ -ℤ{N} where N -SU{N} where N -const SU₂ = SU{2} -ProductGroup -``` - -Specific methods: +Furthermore, we also have one specific method acting on groups, represented as types ```@docs × ``` +Because we sometimes want to customize the string representation of our sector types, +we also have the following method: -## Methods for defining and generating fusion trees ```@docs -FusionTree -fusiontrees(uncoupled::NTuple{N,I}, coupled::I, - isdual::NTuple{N,Bool}) where {N,I<:Sector} +TensorKitSectors.type_repr ``` -## Methods for manipulating fusion trees +Finally, we provide functionality to compile all revelant methods for a sector: -For manipulating single fusion trees, the following internal methods are defined: ```@docs -insertat -split -merge -elementary_trace -planar_trace(f::FusionTree{I,N}, q1::IndexTuple{N₃}, q2::IndexTuple{N₃}) where {I<:Sector,N,N₃} -artin_braid -braid(f::FusionTree{I,N}, levels::NTuple{N,Int}, p::NTuple{N,Int}) where {I<:Sector,N} -permute(f::FusionTree{I,N}, p::NTuple{N,Int}) where {I<:Sector,N} -``` - -These can be composed to implement elementary manipulations of fusion-splitting tree pairs, -according to the following methods - -```julia -# TODO: add documentation for the following methods -TensorKit.bendright -TensorKit.bendleft -TensorKit.foldright -TensorKit.foldleft -TensorKit.cycleclockwise -TensorKit.cycleanticlockwise +TensorKitSectors.precompile_sector ``` -Finally, these are used to define large manipulations of fusion-splitting tree pairs, which -are then used in the index manipulation of `AbstractTensorMap` objects. The following methods -defined on fusion splitting tree pairs have an associated definition for tensors. -```@docs -repartition(::FusionTree{I,N₁}, ::FusionTree{I,N₂}, ::Int) where {I<:Sector,N₁,N₂} -transpose(::FusionTree{I}, ::FusionTree{I}, ::IndexTuple{N₁}, ::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} -braid(::FusionTree{I}, ::FusionTree{I}, ::IndexTuple, ::IndexTuple, ::IndexTuple{N₁}, ::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} -permute(::FusionTree{I}, ::FusionTree{I}, ::IndexTuple{N₁}, ::IndexTuple{N₂}) where {I<:Sector,N₁,N₂} -``` diff --git a/docs/src/lib/spaces.md b/docs/src/lib/spaces.md index 10d2bf4f6..e11b689b3 100644 --- a/docs/src/lib/spaces.md +++ b/docs/src/lib/spaces.md @@ -85,14 +85,15 @@ space The following methods act specifically on `ElementarySpace` spaces: ```@docs -isdual dual(::VectorSpace) conj +isconj +isdual flip -⊕ -⊖ zerospace unitspace +⊕ +⊖ supremum infimum ``` @@ -100,8 +101,8 @@ infimum while the following also work on both `ElementarySpace` and `ProductSpace` ```@docs -one(::VectorSpace) fuse +one(::VectorSpace) ⊗(::VectorSpace, ::VectorSpace) ⊠(::VectorSpace, ::VectorSpace) ismonomorphic diff --git a/docs/src/man/intro.md b/docs/src/man/intro.md index 32a93d0ba..00232fb3c 100644 --- a/docs/src/man/intro.md +++ b/docs/src/man/intro.md @@ -93,9 +93,10 @@ to the dual space of ``W``. This simple example introduces two new concepts. ``V ⊗ W^*`` is not an equivalence but an isomorphism, which needs to be defined. Similarly, there is an isomorphism between between ``V ⊗ W`` and ``W ⊗ V`` that can be non-trivial (e.g. in the case of fermions / super vector spaces). The correct formalism - here is provided by theory of monoidal categories, which is introduced on the next - page. Nonetheless, we try to hide these canonical isomorphisms from the user wherever - possible, and one does not need to know category theory to be able to use this package. + here is provided by theory of monoidal categories, the details of which are explained + in the appendix. Nonetheless, we try to hide these canonical isomorphisms from the user + wherever possible, and one does not need to know category theory to be able to use this + package. This brings us to our final (yet formal) definition @@ -103,7 +104,7 @@ This brings us to our final (yet formal) definition (or some subcategory thereof). In practice, this will be ``\mathbf{FinVect}``, the category of finite dimensional vector spaces. More generally even, our concept of a tensor makes sense, in principle, for any linear (a.k.a. ``\mathbf{Vect}``-enriched) - monoidal category. We refer to the next page on + monoidal category. For more details, we refer the curious reader to the appendix on "[Monoidal categories and their properties](@ref s_categories)". ## [Symmetries and block sparsity](@id ss_symmetries) diff --git a/docs/src/man/spaces.md b/docs/src/man/spaces.md index ff2d5990f..0e4951a5a 100644 --- a/docs/src/man/spaces.md +++ b/docs/src/man/spaces.md @@ -10,13 +10,12 @@ domain and codomain of the map. The starting point is an abstract type `VectorSp ```julia abstract type VectorSpace end ``` -which is actually a too restricted name. All instances of subtypes of `VectorSpace` will -represent objects in ``𝕜``-linear monoidal categories, but this can go beyond normal -vector spaces (i.e. objects in the category ``\mathbf{Vect}``) and even beyond objects of -``\mathbf{SVect}``. However, in order not to make the remaining discussion to abstract -or complicated, we will simply refer to subtypes of `VectorSpace` instead of specific -categories, and to spaces (i.e. `VectorSpace` instances) instead of objects from these -categories. In particular, we define two abstract subtypes + +Technically speaking, this name does not capture the full generality that TensorKit.jl supports, +as instances of subtypes of `VectorSpace` can encode general objects in linear monoidal categories, +which are not necessarily vector spaces. However, in order not to make the remaining discussion +to abstract or complicated, we will simply use the nomenclature of vector spaces. In particular, +we define two abstract subtypes ```julia abstract type ElementarySpace <: VectorSpace end const IndexSpace = ElementarySpace @@ -29,9 +28,8 @@ associated with the individual indices of a tensor, as hinted to by its alias `I On the other hand, subtypes of `CompositeSpace{S}` where `S<:ElementarySpace` are composed of a number of elementary spaces of type `S`. So far, there is a single concrete type `ProductSpace{S,N}` that represents the tensor product of `N` vector spaces of a homogeneous -type `S`. Its properties are discussed in the section on -[Composite spaces](@ref ss_compositespaces), together with possible extensions for the -future. +type `S`. Its properties are discussed in the section on [Composite spaces](@ref ss_compositespaces), +together with possible extensions for the future. Throughout TensorKit.jl, the function `spacetype` returns the type of `ElementarySpace` associated with e.g. a composite space or a tensor. It works both on instances and in the @@ -39,9 +37,9 @@ type domain. Its use will be illustrated below. ## [Fields](@id ss_fields) -Vector spaces (linear categories) are defined over a field of scalars ``𝔽``. We define a -type hierarchy to specify the scalar field, but so far only support real and complex -numbers, via +Vector spaces (and linear categories more generally) are defined over a field of scalars +``𝔽``. We define a type hierarchy to specify the scalar field, but so far only support +real and complex numbers, via ```julia abstract type Field end @@ -76,13 +74,15 @@ field As mentioned at the beginning of this section, vector spaces that are associated with the individual indices of a tensor should be implemented as subtypes of `ElementarySpace`. As the domain and codomain of a tensor map will be the tensor product of such objects which all -have the same type, it is important that related vector spaces, e.g. the dual space, are -objects of the same concrete type (i.e. with the same type parameters in case of a +have the same type, it is important that associated vector spaces, such as the dual space, +are objects of the same concrete type (i.e. with the same type parameters in case of a parametric type). In particular, every `ElementarySpace` should implement the following methods * `dim(::ElementarySpace)` returns the dimension of the space +* `field(::ElementarySpace)` returns the field `𝔽` over which the vector space is defined + * `dual(::S) where {S<:ElementarySpace} -> ::S` returns the [dual space](http://en.wikipedia.org/wiki/Dual_space) `dual(V)`, using an instance of the same concrete type (i.e. not via type parameters); this should satisfy @@ -94,17 +94,10 @@ methods this should satisfy `conj(conj(V)) == V` and we automatically have `conj(V::ElementarySpace{ℝ}) = V`. -For convenience, the dual of a space `V` can also be obtained as `V'`. - -There is concrete type `GeneralSpace` which is completely characterized by its field `𝔽`, -its dimension and whether its the dual and/or complex conjugate of $𝔽^d$. -```julia -struct GeneralSpace{𝔽} <: ElementarySpace - d::Int - dual::Bool - conj::Bool -end -``` +For convenience, the dual of a space `V` can also be obtained as `V'`. Furthermore, it is +sometimes necessary to test whether a space is a dual or conjugate space, for which the +methods [`isdual(::ElementarySpace)`](@ref) and [`isconj(::ElementarySpace)`](@ref) should +be implemented. We furthermore define the trait types ```julia @@ -116,15 +109,31 @@ struct EuclideanInnerProduct <: HasInnerProduct end to denote for a vector space `V` whether it has an inner product and thus a canonical mapping from `dual(V)` to `V` (for real fields `𝔽 ⊆ ℝ`) or from `dual(V)` to `conj(V)` (for complex fields). This mapping is provided by the metric, but no further support for -working with metrics is currently implemented. +working with vector spaces with general metrics is currently implemented. + +A number of concrete elementary spaces are implemented in TensorKit.jl. There is concrete +type `GeneralSpace` which is completely characterized by its field `𝔽`, its dimension and +whether its the dual and/or complex conjugate of $𝔽^d$. +```julia +struct GeneralSpace{𝔽} <: ElementarySpace + d::Int + dual::Bool + conj::Bool +end +``` + +However, as the `InnerProductStyle` of `GeneralSpace` is currently set to `NoInnerProduct()`, +this type of vector space is currently quite limited, though it supports constructing +tensors and contracting them. However, most tensor factorizations will depend on the presence +of an Euclidean inner product. -Spaces with the `EuclideanInnerProduct` style have the natural isomorphisms `dual(V) == V` -(for `𝔽 == ℝ`) or `dual(V) == conj(V)` (for ` 𝔽 == ℂ`). In the language of the previous -section on [categories](@ref s_categories), this trait represents -[dagger or unitary categories](@ref ss_adjoints), and these vector spaces support an -`adjoint` operation. +Spaces with the `EuclideanInnerProduct()` style, i.e. with a standard Euclidean metric, +have the natural isomorphisms `dual(V) == V` (for `𝔽 == ℝ`) or `dual(V) == conj(V)` +(for `𝔽 == ℂ`). In the language of the appendix on [categories](@ref s_categories), +this trait represents [dagger or unitary categories](@ref ss_adjoints), and these vector +spaces support an `adjoint` operation. -In particular, the two concrete types +In particular, two concrete types are provided: ```julia struct CartesianSpace <: ElementarySpace d::Int @@ -134,9 +143,9 @@ struct ComplexSpace <: ElementarySpace dual::Bool end ``` -represent the Euclidean spaces $ℝ^d$ or $ℂ^d$ without further inner structure. They can be -created using the syntax `CartesianSpace(d) == ℝ^d` and `ComplexSpace(d) == ℂ^d`, or -`ComplexSpace(d, true) == ComplexSpace(d; dual = true) == (ℂ^d)'` for the dual +They represent the Euclidean spaces $ℝ^d$ or $ℂ^d$ without further inner structure. +They can be created using the syntax `CartesianSpace(d) == ℝ^d` and `ComplexSpace(d) == ℂ^d`, +or `ComplexSpace(d, true) == ComplexSpace(d; dual = true) == (ℂ^d)'` for the dual space of the latter. Note that the brackets are required because of the precedence rules, since `d' == d` for `d::Integer`. @@ -163,17 +172,22 @@ InnerProductStyle(ℂ^5) contraction between two tensor indices will check that one is living in the space and the other in the dual space. This is in contrast with several other software packages, especially in the context of tensor networks, where arrows are only introduced when - discussing symmetries. We believe that our more purist approach can be useful to detect + discussing symmetries. We believe that our more puristic approach can be useful to detect errors (e.g. unintended contractions). Only with `ℝ^n` will their be no distinction between a space and its dual. When creating tensors with indices in `ℝ^n` that have complex data, a one-time warning will be printed, but most operations should continue to work nonetheless. -One more important instance of `ElementarySpace` is the `GradedSpace`, which is used to -represent a graded complex vector space with Euclidean inner product, where the grading is -provided by the irreducible representations of a group, or more generally, the simple -objects of a fusion category. We refer to the subsection on [graded spaces](@ref ss_rep) on -the [next page](@ref s_sectorsrepfusion) for further information about `GradedSpace`. +One more important concrete implementation of `ElementarySpace` with a `EuclideanInnerProduct()` +is the `GradedSpace`, which is used to represent a graded complex vector space, where the grading +is provided by the irreducible representations of a group, or more generally, the simple +objects of a unitary fusion category. We refer to the subsection on [graded spaces](@ref ss_rep) +on the [next page](@ref s_sectorsrepfusion) for further information about `GradedSpace`. + +## Operations with elementary spaces + +Instances of `ElementarySpace` support a number of useful operations. + ## [Composite spaces](@id ss_compositespaces) @@ -198,20 +212,35 @@ V1 ⊗ V2 ⊗ V1' == V1 * V2 * V1' == ProductSpace(V1, V2, V1') == ProductSpace( V1^3 dim(V1 ⊗ V2) dims(V1 ⊗ V2) -dual(V1 ⊗ V2) +dual(V1 ⊗ V2 ⊗ V1') spacetype(V1 ⊗ V2) spacetype(ProductSpace{ComplexSpace,3}) ``` -Here, the new function `dims` maps `dim` to the individual spaces in a `ProductSpace` and -returns the result as a tuple. Note that the rationale for the last result was explained in -the subsection on [duality](@ref ss_dual) in the introduction to +Here, the newly introduced function `dims` maps `dim` to the individual spaces in a +`ProductSpace` and returns the result as a tuple. The rationale for the dual space of +a `ProductSpace` being the tensor product of the dual spaces in reverse order is +explained in the subsection on [duality](@ref ss_dual) in the appendix on [category theory](@ref s_categories). -Following Julia's Base library, the function `one` applied to a `ProductSpace{S,N}` returns -the multiplicative identity, which is `ProductSpace{S,0}(())`. The same result is obtained -when acting on an instance `V` of `S::ElementarySpace` directly, however note that `V ⊗ -one(V)` will yield a `ProductSpace{S,1}(V)` and not `V` itself. The same result can be -obtained with `⊗(V)`. Similar to Julia Base, `one` also works in the type domain. +Following Julia's Base library, the function `one` applied to an instance of `ProductSpace{S,N}` +or of `S<:ElementarySpace` itself returns the multiplicative identity for these objects. +Similar to Julia Base, `one` also works in the type domain. The multiplicative identity for +vector spaces corresponds to the (monoidal) unit, which is represented as `ProductSpace{S,0}(())` +and simply printed as `one(S)` for the specific type `S`. Note, however, that for `V::S`, +`V ⊗ one(V)` will yield `ProductSpace{S,1}(V)` and not `V` itself. However, even though +`V ⊗ one(V)` is not strictly equal to `V`, the object `ProductSpace(V)`, which can also be +created as `⊗(V)`, does mathematically encapsulate the same vector space as `V`. + +```@repl tensorkit +one(V1) +typeof(one(V1)) +V1 * one(V1) == ProductSpace(V1) == ⊗(V1) +V1 * one(V1) == V1 +P = V1 * V2; +one(P) +one(typeof(P)) +P * one(P) == P == one(P) ⊗ P +``` In the future, other `CompositeSpace` types could be added. For example, the wave function of an `N`-particle quantum system in first quantization would require the introduction of a @@ -221,50 +250,8 @@ which correspond to the symmetric (permutation invariant) or antisymmetric subsp domains, like general relativity, might also benefit from tensors living in a subspace with certain symmetries under specific index permutations. -## [Space of morphisms](@id ss_homspaces) -Given that we define tensor maps as morphisms in a ``𝕜``-linear monoidal category, i.e. -linear maps, we also define a type to denote the corresponding space. Indeed, in a -``𝕜``-linear category ``C``, the set of morphisms ``\mathrm{Hom}(W,V)`` for ``V,W ∈ C`` is -always an actual vector space, irrespective of whether or not ``C`` is a subcategory of -``\mathbf{(S)Vect}``. -We introduce the type -```julia -struct HomSpace{S<:ElementarySpace, P1<:CompositeSpace{S}, P2<:CompositeSpace{S}} - codomain::P1 - domain::P2 -end -``` -and can create it as either `domain → codomain` or `codomain ← domain` (where the arrows -are obtained as `\to+TAB` or `\leftarrow+TAB`, and as `\rightarrow+TAB` respectively). The -reason for first listing the codomain and than the domain will become clear in the -[section on tensor maps](@ref s_tensors). - -Note that `HomSpace` is not a subtype of `VectorSpace`, i.e. we restrict the latter to -denote certain categories and their objects, and keep `HomSpace` distinct. However, -`HomSpace` has a number of properties defined, which we illustrate via examples -```@repl tensorkit -W = ℂ^2 ⊗ ℂ^3 → ℂ^3 ⊗ dual(ℂ^4) -field(W) -dual(W) -adjoint(W) -spacetype(W) -spacetype(typeof(W)) -W[1] -W[2] -W[3] -W[4] -dim(W) -``` -Note that indexing `W` yields first the spaces in the codomain, followed by the dual of the -spaces in the domain. This particular convention is useful in combination with the -instances of type [`TensorMap`](@ref), which represent morphisms living in such a -`HomSpace`. Also note that `dim(W)` here seems to be the product of the dimensions of the -individual spaces, but that this is no longer true once symmetries are involved. At any -time will `dim(::HomSpace)` represent the number of linearly independent morphisms in this -space. - -## Partial order among vector spaces +## Operations on and relations between vector spaces Vector spaces of the same `spacetype` can be given a partial order, based on whether there exist injective morphisms (a.k.a *monomorphisms*) or surjective morphisms (a.k.a. @@ -345,3 +332,62 @@ supremum(ℂ^5, (ℂ^3)') The names `infimum` and `supremum` are especially suited in the case of [`GradedSpace`](@ref), as the infimum of two spaces might be different from either of those two spaces, and similar for the supremum. + +## [Space of morphisms](@id ss_homspaces) +As mentioned in the introduction, we will define tensor maps as linear maps from +a `ProductSpace` domain to a `ProductSpace` codomain. All tensor maps with a fixed +domain and codomain form a vector space, which we represent with the `HomSpace` type. +```julia +struct HomSpace{S<:ElementarySpace, P1<:CompositeSpace{S}, P2<:CompositeSpace{S}} + codomain::P1 + domain::P2 +end +``` +Aside from the standard constructor, a `HomSpace` instance can be created as either +`domain → codomain` or `codomain ← domain` (where the arrows are obtained as +`\to+TAB` or `\leftarrow+TAB`, and as `\rightarrow+TAB` respectively). The +reason for first listing the codomain and than the domain will become clear in the +[section on tensor maps](@ref s_tensors). + +Note that `HomSpace` is not a subtype of `VectorSpace`, i.e. we restrict the latter to +encode all spaces and generalizations thereof (i.e. objects in linear monoidal categories) +that are associated with the indices and the domain and codomain of a tensor map. Even when +these genearalizations are no longer strictly vector spaces and have unconventional properties +(such as non-integer dimensions), the space of tensor maps (homomorphisms) between a given +domain and codomain is always a vector space in the strict mathematical sense. Furthermore, +on these `HomSpace` instances, we define a number of useful methods that are a predecessor +to the corresponidng methods that we will define to manipulate the actual tensors, as we +illustrate in the following example: +```@repl tensorkit +W = ℂ^2 ⊗ ℂ^3 → ℂ^3 ⊗ dual(ℂ^4) +field(W) +dual(W) +adjoint(W) +spacetype(W) +spacetype(typeof(W)) +W[1] +W[2] +W[3] +W[4] +dim(W) +domain(W) +codomain(W) +numin(W) +numout(W) +numind(W) +numind(W) == numin(W) + numout(W) +permute(W, ((2, 3), (1, 4))) +flip(W, 3) +insertleftunit(W, 3) +insertrightunit(W, 2) +removeunit(insertrightunit(W, 2), 3) +TensorKit.compose(W, adjoint(W)) +``` +Note that indexing `W` yields first the spaces in the codomain, followed by the dual of the +spaces in the domain. This particular convention is useful in combination with the +instances of type [`TensorMap`](@ref), which represent morphisms living in such a +`HomSpace`. Also note that `dim(W)` is here given by the product of the dimensions of the +individual spaces, but that this is no longer true once symmetries are involved. At any +time will `dim(::HomSpace)` represent the number of linearly independent morphisms in this +space, or thus, the number of independent components that a corresponding `TensorMap` +object will have. From bb4a08c3b625ca838a18440bbc2ca0bf86722a89 Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Fri, 17 Oct 2025 23:03:54 +0200 Subject: [PATCH 2/7] change empty ProductSpace printing --- src/spaces/productspace.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index 08e7011b0..62c708634 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -68,7 +68,7 @@ dual(P::ProductSpace) = ProductSpace(map(dual, reverse(P.spaces))) function Base.show(io::IO, P::ProductSpace{S}) where {S <: ElementarySpace} spaces = P.spaces if length(spaces) == 0 - print(io, "ProductSpace{", S, ", 0}") + return print(io, "one(", type_repr(S), ")") end if length(spaces) == 1 print(io, "ProductSpace") From c9ba5bc8f913e9449477e748dbe86aee5482e3c0 Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Fri, 17 Oct 2025 23:52:15 +0200 Subject: [PATCH 3/7] some spaces restructuring --- src/spaces/cartesianspace.jl | 31 ++++++++------ src/spaces/complexspace.jl | 25 ++++++----- src/spaces/generalspace.jl | 23 +++++----- src/spaces/gradedspace.jl | 83 +++++++++++++++++------------------- src/spaces/homspace.jl | 8 ++-- src/spaces/productspace.jl | 6 +-- src/spaces/vectorspaces.jl | 41 ++++++++++++------ 7 files changed, 119 insertions(+), 98 deletions(-) diff --git a/src/spaces/cartesianspace.jl b/src/spaces/cartesianspace.jl index 3ee4b6ca8..29eea1b0d 100644 --- a/src/spaces/cartesianspace.jl +++ b/src/spaces/cartesianspace.jl @@ -1,8 +1,12 @@ """ struct CartesianSpace <: ElementarySpace + CartesianSpace(d::Integer = 0; dual = false) + ℝ^d -A real Euclidean space `ℝ^d`, which is therefore self-dual. `CartesianSpace` has no -additonal structure and is completely characterised by its dimension `d`. This is the +A real Euclidean space ``ℝ^d``. `CartesianSpace` has no additonal structure and +is completely characterised by its dimension `d`. A `dual` keyword argument is +accepted for compatibility with other space constructors, but is ignored +since the dual of a Cartesian space is isomorphic to itself. This is the vector space that is implicitly assumed in most of matrix algebra. """ struct CartesianSpace <: ElementarySpace @@ -29,23 +33,23 @@ function CartesianSpace(dims::AbstractDict; kwargs...) end CartesianSpace(g::Base.Generator; kwargs...) = CartesianSpace(g...; kwargs...) -field(::Type{CartesianSpace}) = ℝ -InnerProductStyle(::Type{CartesianSpace}) = EuclideanInnerProduct() - -Base.conj(V::CartesianSpace) = V -isdual(V::CartesianSpace) = false - # convenience constructor Base.getindex(::RealNumbers) = CartesianSpace Base.:^(::RealNumbers, d::Int) = CartesianSpace(d) # Corresponding methods: #------------------------ +field(::Type{CartesianSpace}) = ℝ +InnerProductStyle(::Type{CartesianSpace}) = EuclideanInnerProduct() + dim(V::CartesianSpace, ::Trivial = Trivial()) = V.d Base.axes(V::CartesianSpace, ::Trivial = Trivial()) = Base.OneTo(dim(V)) -hassector(V::CartesianSpace, ::Trivial) = dim(V) != 0 -sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) -sectortype(::Type{CartesianSpace}) = Trivial + +dual(V::CartesianSpace) = V +Base.conj(V::CartesianSpace) = dual(V) +isdual(V::CartesianSpace) = false +isconj(V::CartesianSpace) = false +flip(V::CartesianSpace) = V unitspace(::Type{CartesianSpace}) = CartesianSpace(1) zerospace(::Type{CartesianSpace}) = CartesianSpace(0) @@ -56,9 +60,12 @@ function ⊖(V::CartesianSpace, W::CartesianSpace) end fuse(V₁::CartesianSpace, V₂::CartesianSpace) = CartesianSpace(V₁.d * V₂.d) -flip(V::CartesianSpace) = V infimum(V₁::CartesianSpace, V₂::CartesianSpace) = CartesianSpace(min(V₁.d, V₂.d)) supremum(V₁::CartesianSpace, V₂::CartesianSpace) = CartesianSpace(max(V₁.d, V₂.d)) +hassector(V::CartesianSpace, ::Trivial) = dim(V) != 0 +sectors(V::CartesianSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) +sectortype(::Type{CartesianSpace}) = Trivial + Base.show(io::IO, V::CartesianSpace) = print(io, "ℝ^$(V.d)") diff --git a/src/spaces/complexspace.jl b/src/spaces/complexspace.jl index 87e9e1ddc..01e59f173 100644 --- a/src/spaces/complexspace.jl +++ b/src/spaces/complexspace.jl @@ -1,7 +1,9 @@ """ struct ComplexSpace <: ElementarySpace + ComplexSpace(d::Integer = 0; dual = false) + ℂ^d -A standard complex vector space ℂ^d with Euclidean inner product and no additional +A standard complex vector space ``ℂ^d`` with Euclidean inner product and no additional structure. It is completely characterised by its dimension and whether its the normal space or its dual (which is canonically isomorphic to the conjugate space). """ @@ -30,23 +32,23 @@ function ComplexSpace(dims::AbstractDict; kwargs...) end ComplexSpace(g::Base.Generator; kwargs...) = ComplexSpace(g...; kwargs...) -field(::Type{ComplexSpace}) = ℂ -InnerProductStyle(::Type{ComplexSpace}) = EuclideanInnerProduct() - # convenience constructor Base.getindex(::ComplexNumbers) = ComplexSpace Base.:^(::ComplexNumbers, d::Int) = ComplexSpace(d) # Corresponding methods: #------------------------ +field(::Type{ComplexSpace}) = ℂ +InnerProductStyle(::Type{ComplexSpace}) = EuclideanInnerProduct() + dim(V::ComplexSpace, s::Trivial = Trivial()) = V.d -isdual(V::ComplexSpace) = V.dual Base.axes(V::ComplexSpace, ::Trivial = Trivial()) = Base.OneTo(dim(V)) -hassector(V::ComplexSpace, ::Trivial) = dim(V) != 0 -sectors(V::ComplexSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) -sectortype(::Type{ComplexSpace}) = Trivial -Base.conj(V::ComplexSpace) = ComplexSpace(dim(V), !isdual(V)) +dual(V::ComplexSpace) = ComplexSpace(dim(V), !isdual(V)) +Base.conj(V::ComplexSpace) = dual(V) +isdual(V::ComplexSpace) = V.dual +isconj(V::ComplexSpace) = true +flip(V::ComplexSpace) = dual(V) unitspace(::Type{ComplexSpace}) = ComplexSpace(1) zerospace(::Type{ComplexSpace}) = ComplexSpace(0) @@ -62,7 +64,6 @@ function ⊖(V::ComplexSpace, W::ComplexSpace) end fuse(V₁::ComplexSpace, V₂::ComplexSpace) = ComplexSpace(V₁.d * V₂.d) -flip(V::ComplexSpace) = dual(V) function infimum(V₁::ComplexSpace, V₂::ComplexSpace) return isdual(V₁) == isdual(V₂) ? @@ -75,4 +76,8 @@ function supremum(V₁::ComplexSpace, V₂::ComplexSpace) throw(SpaceMismatch("Supremum of space and dual space does not exist")) end +hassector(V::ComplexSpace, ::Trivial) = dim(V) != 0 +sectors(V::ComplexSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) +sectortype(::Type{ComplexSpace}) = Trivial + Base.show(io::IO, V::ComplexSpace) = print(io, isdual(V) ? "(ℂ^$(V.d))'" : "ℂ^$(V.d)") diff --git a/src/spaces/generalspace.jl b/src/spaces/generalspace.jl index 62b16d654..73cd2b645 100644 --- a/src/spaces/generalspace.jl +++ b/src/spaces/generalspace.jl @@ -1,5 +1,6 @@ """ struct GeneralSpace{𝔽} <: ElementarySpace + GeneralSpace{𝔽}(d::Integer = 0; dual::Bool = false, conj::Bool = false) A finite-dimensional space over an arbitrary field `𝔽` without additional structure. It is thus characterized by its dimension, and whether or not it is the dual and/or @@ -23,23 +24,25 @@ function GeneralSpace{𝔽}(d::Int = 0; dual::Bool = false, conj::Bool = false) return GeneralSpace{𝔽}(d, dual, conj) end -dim(V::GeneralSpace, s::Trivial = Trivial()) = V.d -isdual(V::GeneralSpace) = V.dual -isconj(V::GeneralSpace) = V.conj +# Corresponding methods: +#------------------------ +field(::Type{GeneralSpace{𝔽}}) where {𝔽} = 𝔽 +InnerProductStyle(::Type{<:GeneralSpace}) = NoInnerProduct() +dim(V::GeneralSpace, s::Trivial = Trivial()) = V.d Base.axes(V::GeneralSpace, ::Trivial = Trivial()) = Base.OneTo(dim(V)) -hassector(V::GeneralSpace, ::Trivial) = dim(V) != 0 -sectors(V::GeneralSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) -sectortype(::Type{<:GeneralSpace}) = Trivial -field(::Type{GeneralSpace{𝔽}}) where {𝔽} = 𝔽 -InnerProductStyle(::Type{<:GeneralSpace}) = NoInnerProduct() +dual(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), !isdual(V), isconj(V)) +Base.conj(V::GeneralSpace{𝔽}) where {𝔽} = 𝔽 == ℝ ? V : GeneralSpace{𝔽}(dim(V), isdual(V), !isconj(V)) +isdual(V::GeneralSpace) = V.dual +isconj(V::GeneralSpace) = 𝔽 == ℝ ? false : V.conj unitspace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(1, false, false) zerospace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(0, false, false) -dual(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), !isdual(V), isconj(V)) -Base.conj(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), isdual(V), !isconj(V)) +hassector(V::GeneralSpace, ::Trivial) = dim(V) != 0 +sectors(V::GeneralSpace) = OneOrNoneIterator(dim(V) != 0, Trivial()) +sectortype(::Type{<:GeneralSpace}) = Trivial function Base.show(io::IO, V::GeneralSpace{𝔽}) where {𝔽} isconj(V) && print(io, "conj(") diff --git a/src/spaces/gradedspace.jl b/src/spaces/gradedspace.jl index 687c79acd..4bc349a80 100644 --- a/src/spaces/gradedspace.jl +++ b/src/spaces/gradedspace.jl @@ -1,19 +1,18 @@ """ struct GradedSpace{I<:Sector, D} <: ElementarySpace - dims::D - dual::Bool - end + GradedSpace{I,D}(dims; dual::Bool = false) where {I<:Sector, D} -A complex Euclidean space with a direct sum structure corresponding to labels in a set `I`, -the objects of which have the structure of a monoid with respect to a monoidal product `⊗`. -In practice, we restrict the label set to be a set of superselection sectors of type -`I<:Sector`, e.g. the set of distinct irreps of a finite or compact group, or the -isomorphism classes of simple objects of a unitary and pivotal (pre-)fusion category. +A complex Euclidean space with a grading, i.e. a direct sum structure corresponding +to labels in a set `I`, the objects of which have the structure of a monoid with +respect to a monoidal product `⊗`. In practice, we restrict the label set to be a set +of superselection sectors of type `I<:Sector`, e.g. the set of distinct irreps of a +finite or compact group, or the isomorphism classes of simple objects of a unitary +and pivotal (pre-, multi-) fusion category. Here `dims` represents the degeneracy or multiplicity of every sector. -The data structure `D` of `dims` will depend on the result `Base.IteratorSize(values(I))`; -if the result is of type `HasLength` or `HasShape`, `dims` will be stored in a +The data structure `D` of `dims` will depend on the result `Base.IteratorSize(values(I))`. +If the result is of type `HasLength` or `HasShape`, `dims` will be stored in a `NTuple{N,Int}` with `N = length(values(I))`. This requires that a sector `s::I` can be transformed into an index via `s == getindex(values(I), i)` and `i == findindex(values(I), s)`. If `Base.IteratorElsize(values(I))` results `IsInfinite()` @@ -85,12 +84,11 @@ end GradedSpace(g::Base.Generator; dual::Bool = false) = GradedSpace(g...; dual = dual) GradedSpace(g::AbstractDict; dual::Bool = false) = GradedSpace(g...; dual = dual) -Base.hash(V::GradedSpace, h::UInt) = hash(V.dual, hash(V.dims, h)) - # Corresponding methods: -# properties +#------------------------ field(::Type{<:GradedSpace}) = ℂ InnerProductStyle(::Type{<:GradedSpace}) = EuclideanInnerProduct() + function dim(V::GradedSpace) return reduce( +, dim(V, c) * dim(c) for c in sectors(V); @@ -103,27 +101,6 @@ end function dim(V::GradedSpace{I, <:Tuple}, c::I) where {I <: Sector} return V.dims[findindex(values(I), isdual(V) ? dual(c) : c)] end - -function sectors(V::GradedSpace{I, <:AbstractDict}) where {I <: Sector} - return SectorSet{I}(s -> isdual(V) ? dual(s) : s, keys(V.dims)) -end -function sectors(V::GradedSpace{I, NTuple{N, Int}}) where {I <: Sector, N} - return SectorSet{I}(Iterators.filter(n -> V.dims[n] != 0, 1:N)) do n - return isdual(V) ? dual(values(I)[n]) : values(I)[n] - end -end - -hassector(V::GradedSpace{I}, s::I) where {I <: Sector} = dim(V, s) != 0 - -Base.conj(V::GradedSpace) = typeof(V)(V.dims, !V.dual) -isdual(V::GradedSpace) = V.dual - -# equality / comparison -function Base.:(==)(V₁::GradedSpace, V₂::GradedSpace) - return sectortype(V₁) == sectortype(V₂) && (V₁.dims == V₂.dims) && V₁.dual == V₂.dual -end - -# axes Base.axes(V::GradedSpace) = Base.OneTo(dim(V)) function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} offset = 0 @@ -134,9 +111,20 @@ function Base.axes(V::GradedSpace{I}, c::I) where {I <: Sector} return (offset + 1):(offset + dim(c) * dim(V, c)) end +dual(V::GradedSpace) = typeof(V)(V.dims, !V.dual) +Base.conj(V::GradedSpace) = dual(V) +isdual(V::GradedSpace) = V.dual +isconj(V::GradedSpace) = isdual(V) +function flip(V::GradedSpace{I}) where {I <: Sector} + return if isdual(V) + typeof(V)(c => dim(V, c) for c in sectors(V)) + else + typeof(V)(dual(c) => dim(V, c) for c in sectors(V))' + end +end + unitspace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 1) zerospace(S::Type{<:GradedSpace{I}}) where {I <: Sector} = S(unit(I) => 0) - # TODO: the following methods can probably be implemented more efficiently for # `FiniteGradedSpace`, but we don't expect them to be used often in hot loops, so # these generic definitions (which are still quite efficient) are good for now. @@ -151,7 +139,6 @@ function ⊕(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} end return typeof(V₁)(dims; dual = dual1) end - function ⊖(V::GradedSpace{I}, W::GradedSpace{I}) where {I <: Sector} dual = isdual(V) V ≿ W && dual == isdual(W) || @@ -159,14 +146,6 @@ function ⊖(V::GradedSpace{I}, W::GradedSpace{I}) where {I <: Sector} return typeof(V)(c => dim(V, c) - dim(W, c) for c in sectors(V); dual) end -function flip(V::GradedSpace{I}) where {I <: Sector} - return if isdual(V) - typeof(V)(c => dim(V, c) for c in sectors(V)) - else - typeof(V)(dual(c) => dim(V, c) for c in sectors(V))' - end -end - function fuse(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} dims = SectorDict{I, Int}() for a in sectors(V₁), b in sectors(V₂) @@ -186,7 +165,6 @@ function infimum(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} for c in intersect(sectors(V₁), sectors(V₂)); dual = Visdual ) end - function supremum(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector} Visdual = isdual(V₁) Visdual == isdual(V₂) || @@ -197,6 +175,21 @@ function supremum(V₁::GradedSpace{I}, V₂::GradedSpace{I}) where {I <: Sector ) end +hassector(V::GradedSpace{I}, s::I) where {I <: Sector} = dim(V, s) != 0 +function sectors(V::GradedSpace{I, <:AbstractDict}) where {I <: Sector} + return SectorSet{I}(s -> isdual(V) ? dual(s) : s, keys(V.dims)) +end +function sectors(V::GradedSpace{I, NTuple{N, Int}}) where {I <: Sector, N} + return SectorSet{I}(Iterators.filter(n -> V.dims[n] != 0, 1:N)) do n + return isdual(V) ? dual(values(I)[n]) : values(I)[n] + end +end + +Base.hash(V::GradedSpace, h::UInt) = hash(V.dual, hash(V.dims, h)) +function Base.:(==)(V₁::GradedSpace, V₂::GradedSpace) + return sectortype(V₁) == sectortype(V₂) && (V₁.dims == V₂.dims) && V₁.dual == V₂.dual +end + function Base.show(io::IO, V::GradedSpace{I}) where {I <: Sector} print(io, type_repr(typeof(V)), "(") separator = "" diff --git a/src/spaces/homspace.jl b/src/spaces/homspace.jl index f020535c5..10a3cfae3 100644 --- a/src/spaces/homspace.jl +++ b/src/spaces/homspace.jl @@ -1,12 +1,10 @@ """ struct HomSpace{S<:ElementarySpace, P1<:CompositeSpace{S}, P2<:CompositeSpace{S}} - codomain::P1 - domain::P2 - end + HomSpace(codomain::CompositeSpace{S}, domain::CompositeSpace{S}) where {S<:ElementarySpace} Represents the linear space of morphisms with codomain of type `P1` and domain of type `P2`. -Note that HomSpace is not a subtype of VectorSpace, i.e. we restrict the latter to denote -certain categories and their objects, and keep HomSpace distinct. +Note that `HomSpace`` is not a subtype of [`VectorSpace`](@ref), i.e. we restrict the latter +to denote categories and their objects, and keep `HomSpace` distinct. """ struct HomSpace{S <: ElementarySpace, P1 <: CompositeSpace{S}, P2 <: CompositeSpace{S}} codomain::P1 diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index 62c708634..49d3696c6 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -1,9 +1,9 @@ """ struct ProductSpace{S<:ElementarySpace, N} <: CompositeSpace{S} + ProductSpace(spaces::NTuple{N, S}) where {S<:ElementarySpace, N} -A `ProductSpace` is a tensor product space of `N` vector spaces of type -`S<:ElementarySpace`. Only tensor products between [`ElementarySpace`](@ref) objects of the -same type are allowed. +A `ProductSpace` is a tensor product space of `N` vector spaces of type `S<:ElementarySpace`. +Only tensor products between [`ElementarySpace`](@ref) objects of the same type are allowed. """ struct ProductSpace{S <: ElementarySpace, N} <: CompositeSpace{S} spaces::NTuple{N, S} diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index 607666730..6bace9a46 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -73,26 +73,41 @@ Return the dual space of `V`; also obtained via `V'`. This should satisfy `dual(dual(V)) == V`. It is assumed that `typeof(V) == typeof(V')`. """ dual(::VectorSpace) +@doc """ + conj(V::VectorSpace) -> VectorSpace + +Return the conjugate space of `V`. This should satisfy `conj(conj(V)) == V`. +For vector spaces over the real numbers, it must hold that `conj(V) == V`. +For vector spaces with a Euclidean inner product, it must hold that `conj(V) == dual(V)`. +""" conj(::VectorSpace) + + # convenience definitions: Base.adjoint(V::VectorSpace) = dual(V) -""" +@doc """ isdual(V::ElementarySpace) -> Bool -Return whether an ElementarySpace `V` is normal or rather a dual space. Always returns -`false` for spaces where `V == dual(V)`. -""" -function isdual end +Return whether an ElementarySpace `V` is normal or rather a dual space. +Always returns `false` for spaces where `V == dual(V)`. +""" isdual(::ElementarySpace) + +@doc """ + isconj(V::ElementarySpace) -> Bool + +Return whether an ElementarySpace `V` is normal or rather the conjugated space. +Always returns `false` for spaces where `V == conj(V)`, i.e. vector spaces over `ℝ`. +""" isconj(::ElementarySpace) # Hierarchy of elementary vector spaces #--------------------------------------- """ - abstract type ElementarySpace <: VectorSpace end + abstract type ElementarySpace <: VectorSpace Elementary finite-dimensional vector space over a field that can be used as the index space corresponding to the indices of a tensor. ElementarySpace is a supertype for all vector spaces (objects) that can be associated with the individual indices of a tensor, -as hinted to by its alias IndexSpace. +as hinted to by its alias `IndexSpace``. Every elementary vector space should respond to the methods [`conj`](@ref) and [`dual`](@ref), returning the complex conjugate space and the dual space respectively. The @@ -103,15 +118,13 @@ of a homogeneous tensor product of these spaces. abstract type ElementarySpace <: VectorSpace end const IndexSpace = ElementarySpace -# field(::Type{<:ElementarySpace{𝕜}}) where {𝕜} = 𝕜 - @doc """ dim(V::ElementarySpace, s::Sector) -> Int Return the degeneracy dimension corresponding to the sector `s` of the vector space `V`. """ dim(::ElementarySpace, ::Sector) -@doc """ +""" reduceddim(V::ElementarySpace) -> Int Return the sum of all degeneracy dimensions of the vector space `V`. @@ -122,8 +135,10 @@ reduceddim(V::ElementarySpace) = sum(Base.Fix1(dim, V), sectors(V); init = 0) unitspace(V::S) where {S<:ElementarySpace} -> S Return the corresponding vector space of type `S` that represents the trivial -one-dimensional space, i.e. the space that is isomorphic to the corresponding field. Note -that this is different from `one(V::S)`, which returns the empty product space +one-dimensional space, i.e. the space that is isomorphic to the corresponding field. + +!!! note +`unitspace(V)`is different from `one(V)`. The latter returns the empty product space `ProductSpace{S,0}(())`. `Base.oneunit` falls back to `unitspace`. """ unitspace(V::ElementarySpace) = unitspace(typeof(V)) @@ -134,7 +149,7 @@ Base.oneunit(::Type{V}) where {V <: ElementarySpace} = unitspace(V) zerospace(V::S) where {S<:ElementarySpace} -> S Return the corresponding vector space of type `S` that represents the zero-dimensional or empty space. -This is, with a slight abuse of notation, the zero element of the direct sum of vector spaces. +This is the zero element of the direct sum of vector spaces. `Base.zero` falls back to `zerospace`. """ zerospace(V::ElementarySpace) = zerospace(typeof(V)) From f414486e2034a7068054cec80371b3de4ad97f22 Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Sat, 18 Oct 2025 00:34:40 +0200 Subject: [PATCH 4/7] some more vector space changes --- src/spaces/productspace.jl | 4 ++-- src/spaces/vectorspaces.jl | 34 +++++++++++++++------------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/spaces/productspace.jl b/src/spaces/productspace.jl index 49d3696c6..654b29849 100644 --- a/src/spaces/productspace.jl +++ b/src/spaces/productspace.jl @@ -62,8 +62,8 @@ Base.axes(P::ProductSpace, n::Int) = axes(P.spaces[n]) dual(P::ProductSpace{<:ElementarySpace, 0}) = P dual(P::ProductSpace) = ProductSpace(map(dual, reverse(P.spaces))) - -# Base.conj(P::ProductSpace) = ProductSpace(map(conj, P.spaces)) +Base.conj(P::ProductSpace{<:ElementarySpace, 0}) = P +Base.conj(P::ProductSpace) = ProductSpace(map(conj, P.spaces)) function Base.show(io::IO, P::ProductSpace{S}) where {S <: ElementarySpace} spaces = P.spaces diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index 6bace9a46..d4901d08e 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -81,24 +81,9 @@ For vector spaces over the real numbers, it must hold that `conj(V) == V`. For vector spaces with a Euclidean inner product, it must hold that `conj(V) == dual(V)`. """ conj(::VectorSpace) - # convenience definitions: Base.adjoint(V::VectorSpace) = dual(V) -@doc """ - isdual(V::ElementarySpace) -> Bool - -Return whether an ElementarySpace `V` is normal or rather a dual space. -Always returns `false` for spaces where `V == dual(V)`. -""" isdual(::ElementarySpace) - -@doc """ - isconj(V::ElementarySpace) -> Bool - -Return whether an ElementarySpace `V` is normal or rather the conjugated space. -Always returns `false` for spaces where `V == conj(V)`, i.e. vector spaces over `ℝ`. -""" isconj(::ElementarySpace) - # Hierarchy of elementary vector spaces #--------------------------------------- """ @@ -131,6 +116,20 @@ Return the sum of all degeneracy dimensions of the vector space `V`. """ reduceddim(V::ElementarySpace) = sum(Base.Fix1(dim, V), sectors(V); init = 0) +@doc """ + isdual(V::ElementarySpace) -> Bool + +Return whether an ElementarySpace `V` is normal or rather a dual space. +Always returns `false` for spaces where `V == dual(V)`. +""" isdual(::ElementarySpace) + +@doc """ + isconj(V::ElementarySpace) -> Bool + +Return whether an ElementarySpace `V` is normal or rather the conjugated space. +Always returns `false` for spaces where `V == conj(V)`, i.e. vector spaces over `ℝ`. +""" isconj(::ElementarySpace) + """ unitspace(V::S) where {S<:ElementarySpace} -> S @@ -164,7 +163,7 @@ Return the corresponding vector space of type `S` that represents the direct sum spaces `V₁`, `V₂`, ... Note that all the individual spaces should have the same value for [`isdual`](@ref), as otherwise the direct sum is not defined. """ -function ⊕ end +⊕(V₁::S, V₂::S) where {S <: ElementarySpace} ⊕(V₁::ElementarySpace, V₂::ElementarySpace) = ⊕(promote(V₁, V₂)...) ⊕(V::Vararg{ElementarySpace}) = foldl(⊕, V) const oplus = ⊕ @@ -267,9 +266,6 @@ InnerProductStyle(::Type{<:VectorSpace}) = NoInnerProduct() throw(ArgumentError("$fname requires Euclidean inner product")) end -dual(V::VectorSpace) = dual(InnerProductStyle(V), V) -dual(::EuclideanInnerProduct, V::VectorSpace) = conj(V) - """ sectortype(a) -> Type{<:Sector} sectortype(::Type) -> Type{<:Sector} From 2795af61a367bdf3ab7f7ea60a6f4b0d73d0c2b8 Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Sat, 18 Oct 2025 00:36:08 +0200 Subject: [PATCH 5/7] require TensorKitSectors 0.3.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index aacd69f69..55d66f61b 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ PackageExtensionCompat = "1" Random = "1" ScopedValues = "1.3.0" Strided = "2" -TensorKitSectors = "0.3" +TensorKitSectors = "0.3.1" TensorOperations = "5.1" Test = "1" TestExtras = "0.2,0.3" From 6f67b7cb86d7532f9d979b60820af4d436af973c Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Sat, 18 Oct 2025 23:22:24 +0200 Subject: [PATCH 6/7] fix bug in generalspace --- src/spaces/generalspace.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spaces/generalspace.jl b/src/spaces/generalspace.jl index 73cd2b645..1ddfbc2d2 100644 --- a/src/spaces/generalspace.jl +++ b/src/spaces/generalspace.jl @@ -35,7 +35,7 @@ Base.axes(V::GeneralSpace, ::Trivial = Trivial()) = Base.OneTo(dim(V)) dual(V::GeneralSpace{𝔽}) where {𝔽} = GeneralSpace{𝔽}(dim(V), !isdual(V), isconj(V)) Base.conj(V::GeneralSpace{𝔽}) where {𝔽} = 𝔽 == ℝ ? V : GeneralSpace{𝔽}(dim(V), isdual(V), !isconj(V)) isdual(V::GeneralSpace) = V.dual -isconj(V::GeneralSpace) = 𝔽 == ℝ ? false : V.conj +isconj(V::GeneralSpace{𝔽}) where {𝔽} = 𝔽 == ℝ ? false : V.conj unitspace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(1, false, false) zerospace(::Type{GeneralSpace{𝔽}}) where {𝔽} = GeneralSpace{𝔽}(0, false, false) From 7874b816449870585fbf5d47f7b73a8a3414a827 Mon Sep 17 00:00:00 2001 From: Jutho Haegeman Date: Wed, 22 Oct 2025 00:37:50 +0200 Subject: [PATCH 7/7] some updates on spaces docs --- docs/src/lib/spaces.md | 4 +- docs/src/man/spaces.md | 216 +++++++++++++++++++------------------ src/spaces/vectorspaces.jl | 5 +- 3 files changed, 117 insertions(+), 108 deletions(-) diff --git a/docs/src/lib/spaces.md b/docs/src/lib/spaces.md index e11b689b3..63ecb7f01 100644 --- a/docs/src/lib/spaces.md +++ b/docs/src/lib/spaces.md @@ -1,4 +1,4 @@ -# Vector spaces +# [Vector spaces](@id s_libvectorspaces) ```@meta CurrentModule = TensorKit @@ -21,7 +21,7 @@ ProductSpace HomSpace ``` -together with the following specific types for encoding the inner product structure of +together with the following specific type for encoding the inner product structure of a space: ```@docs diff --git a/docs/src/man/spaces.md b/docs/src/man/spaces.md index 0e4951a5a..56ed1fe27 100644 --- a/docs/src/man/spaces.md +++ b/docs/src/man/spaces.md @@ -6,7 +6,7 @@ using TensorKit From the [Introduction](@ref s_intro), it should be clear that an important aspect in the definition of a tensor (map) is specifying the vector spaces and their structure in the -domain and codomain of the map. The starting point is an abstract type `VectorSpace` +domain and codomain of the map. The starting point is an abstract type [`VectorSpace`](@ref) ```julia abstract type VectorSpace end ``` @@ -22,16 +22,16 @@ const IndexSpace = ElementarySpace abstract type CompositeSpace{S<:ElementarySpace} <: VectorSpace end ``` -Here, `ElementarySpace` is a super type for all vector spaces (objects) that can be +Here, [`ElementarySpace`](@ref) is a super type for all vector spaces (objects) that can be associated with the individual indices of a tensor, as hinted to by its alias `IndexSpace`. -On the other hand, subtypes of `CompositeSpace{S}` where `S<:ElementarySpace` are composed +On the other hand, subtypes of [`CompositeSpace{S}`](@ref) where `S<:ElementarySpace` are composed of a number of elementary spaces of type `S`. So far, there is a single concrete type -`ProductSpace{S,N}` that represents the tensor product of `N` vector spaces of a homogeneous +[`ProductSpace{S,N}`](@ref) that represents the tensor product of `N` vector spaces of a homogeneous type `S`. Its properties are discussed in the section on [Composite spaces](@ref ss_compositespaces), together with possible extensions for the future. -Throughout TensorKit.jl, the function `spacetype` returns the type of `ElementarySpace` +Throughout TensorKit.jl, the function [`spacetype`](@ref) returns the type of `ElementarySpace` associated with e.g. a composite space or a tensor. It works both on instances and in the type domain. Its use will be illustrated below. @@ -63,7 +63,7 @@ ComplexF64 ⊆ ℂ ``` and furthermore —probably more usefully— `ℝ^n` and `ℂ^n` create specific elementary vector spaces as described in the next section. The underlying field of a vector space or tensor -`a` can be obtained with `field(a)`: +`a` can be obtained with [`field(a)`](@ref): ```@docs; canonical=false field @@ -79,32 +79,21 @@ are objects of the same concrete type (i.e. with the same type parameters in cas parametric type). In particular, every `ElementarySpace` should implement the following methods -* `dim(::ElementarySpace)` returns the dimension of the space - -* `field(::ElementarySpace)` returns the field `𝔽` over which the vector space is defined - -* `dual(::S) where {S<:ElementarySpace} -> ::S` returns the - [dual space](http://en.wikipedia.org/wiki/Dual_space) `dual(V)`, using an instance of - the same concrete type (i.e. not via type parameters); this should satisfy - `dual(dual(V)) == V` - -* `conj(::S) where {S<:ElementarySpace} -> ::S` returns the - [complex conjugate space](http://en.wikipedia.org/wiki/Complex_conjugate_vector_space) - `conj(V)`, using an instance of the same concrete type (i.e. not via type parameters); - this should satisfy `conj(conj(V)) == V` and we automatically have - `conj(V::ElementarySpace{ℝ}) = V`. +```@docs; canonical=false +dim(::ElementarySpace) +field(::ElementarySpace) +dual(::S) where {S<:ElementarySpace} +conj(::S) where {S<:ElementarySpace} +``` For convenience, the dual of a space `V` can also be obtained as `V'`. Furthermore, it is sometimes necessary to test whether a space is a dual or conjugate space, for which the methods [`isdual(::ElementarySpace)`](@ref) and [`isconj(::ElementarySpace)`](@ref) should be implemented. -We furthermore define the trait types -```julia -abstract type InnerProductStyle end -struct NoInnerProduct <: InnerProductStyle end -abstract type HasInnerProduct <: InnerProductStyle end -struct EuclideanInnerProduct <: HasInnerProduct end +We furthermore define a trait type +```@docs; canonical=false +InnerProductStyle ``` to denote for a vector space `V` whether it has an inner product and thus a canonical mapping from `dual(V)` to `V` (for real fields `𝔽 ⊆ ℝ`) or from `dual(V)` to `conj(V)` @@ -179,15 +168,77 @@ InnerProductStyle(ℂ^5) to work nonetheless. One more important concrete implementation of `ElementarySpace` with a `EuclideanInnerProduct()` -is the `GradedSpace`, which is used to represent a graded complex vector space, where the grading -is provided by the irreducible representations of a group, or more generally, the simple -objects of a unitary fusion category. We refer to the subsection on [graded spaces](@ref ss_rep) +is the [`GradedSpace`](@ref) type, which is used to represent a graded complex vector space, +where the grading is provided by the irreducible representations of a group, or more generally, +the simple objects of a unitary fusion category. We refer to the subsection on [graded spaces](@ref ss_rep) on the [next page](@ref s_sectorsrepfusion) for further information about `GradedSpace`. ## Operations with elementary spaces -Instances of `ElementarySpace` support a number of useful operations. +Instances of `ElementarySpace` support a number of useful operations. Firstly, we define the direct +sum of two vector spaces `V1` and `V2` of the same `spacetype` (and with the same value of `isdual`) +as [`V1 ⊕ V2`](@ref), where `⊕` is obtained by typing `\oplus`+TAB. [`zerospace(V)`](@ref) corresponds +to the identity or zero element with respect to this direct sum operation, i.e. it corresponds to +a zero-dimensional space. Furthermore, [`unitspace(V)`] (@ref) applied to an elementary space returns a +one-dimensional space, that is isomorphic to the scalar field underlying the space itself. Finally, +we have also introduced the non-standard convention `V1 ⊖ V2` (obtained by typing `\ominus`+TAB.) +in order to obtain a space that is isomorphic to the quotient space of `V1` by `V2`, or thus, +a particular choice of complement of `V2` in `V1` such that `V1 == V2 ⊕ (V1 ⊖ V2)` is satisfied. + +Some examples illustrate this better. +```@repl tensorkit +ℝ^5 ⊕ ℝ^3 +ℂ^5 ⊕ ℂ^3 +ℂ^5 ⊕ (ℂ^3)' +zerospace(ℂ^3) +unitspace(ℝ^3) +ℂ^5 ⊕ unitspace(ComplexSpace) +unitspace((ℂ^3)') +(ℂ^5) ⊕ unitspace((ℂ^5)) +(ℂ^5)' ⊕ unitspace((ℂ^5)') +(ℝ^5) ⊖ (ℝ^3) +(ℂ^5) ⊖ (ℂ^3) +(ℂ^5)' ⊖ (ℂ^3)' +ℂ^5 == ((ℂ^5) ⊖ (ℂ^3)) ⊕ (ℂ^3) == (ℂ^3) ⊕ ((ℂ^5) ⊖ (ℂ^3)) +``` + +Note, finally, that we have defined `oplus` and `ominus` as ASCII alternatives for `⊕` and `⊖` respectively. + +A second type of operation with elementary spaces is the function [`flip(V::ElementarySpace)`](@ref), +which returns a space that is isomorphic to `V` but has `isdual(flip(V)) == isdual(V')`, i.e., if `V` +is a normal space, then `flip(V)` is a dual space. `flip(V)` is different from `dual(V)` in the case +of [`GradedSpace`](@ref). It is useful to flip a tensor index from a ket to a bra (or vice versa), +by contracting that index with a unitary map from `V1` to `flip(V1)`. + +While we provide some trivial examples here, we refer to the section on [graded spaces](@ref ss_rep) +for examples where `flip` acts non-trivially and produces results that are different than `dual`. +```@repl tensorkit +flip(ℂ^4) +flip(ℂ^4) ≅ ℂ^4 +flip(ℂ^4) == ℂ^4 +``` + +Finally, we provide two methods [`infimum(V1, V2)`](@ref) and [`supremum(V1, V2)`](@ref) for +elementary spaces `V1` and `V2` with the same `spacetype` and value of `isdual`. The former +returns the "largest" elementary space `V::ElementarySpace` with the same value of `isdual` +such that we can construct surjective morphisms from both `V1` and `V2` to `V`. Similarly, +the latter returns the "smallest" elementary space `W::ElementarySpace` with the same value +of `isdual` such that we can construct injective morphisms from both `V1` and `V2` to `W`. +For `CartesianSpace` and `ComplexSpace`, this simply amounts to the space with minimal or maximal +dimension, but it is again more interesting in the case of [`GradedSpace`](@ref), as discussed +on the [next page](@ref ss_). It is that case where `infimum(V1, V2)` might be different from either +`V1` or `V2`, and similar for `supremum(V1, V2)`, which justifies the choice of these names +over simply `min` and `max`. Also note that these methods are a direct consequence of the +partial order that we can define between vector spaces of the same `spacetype` more generally, +as discussed below in the subsection ["More operations with vector spaces"](@ref ss_spaceops). +Some examples: +```@repl tensorkit +infimum(ℝ^5, ℝ^3) +supremum(ℂ^5, ℂ^3) +supremum(ℂ^5, (ℂ^3)') +supremum((ℂ^5)', (ℂ^3)') +``` ## [Composite spaces](@id ss_compositespaces) @@ -226,10 +277,11 @@ Following Julia's Base library, the function `one` applied to an instance of `Pr or of `S<:ElementarySpace` itself returns the multiplicative identity for these objects. Similar to Julia Base, `one` also works in the type domain. The multiplicative identity for vector spaces corresponds to the (monoidal) unit, which is represented as `ProductSpace{S,0}(())` -and simply printed as `one(S)` for the specific type `S`. Note, however, that for `V::S`, -`V ⊗ one(V)` will yield `ProductSpace{S,1}(V)` and not `V` itself. However, even though -`V ⊗ one(V)` is not strictly equal to `V`, the object `ProductSpace(V)`, which can also be -created as `⊗(V)`, does mathematically encapsulate the same vector space as `V`. +and simply printed as `one(S)` for the specific type `S`. Note, however, that `one(S)` is +strictly speaking only the multiplicative identity when multiplied with `ProductSpace{S,N}` +instances. For elementary spaces `V::S`, `V ⊗ one(V)` will yield `ProductSpace{S,1}(V)` and not +`V` itself. However, even though `V ⊗ one(V)` is not strictly equal to `V`, the object `ProductSpace(V)`, +which can also be created as `⊗(V)`, does mathematically encapsulate the same vector space as `V`. ```@repl tensorkit one(V1) @@ -247,11 +299,10 @@ of an `N`-particle quantum system in first quantization would require the introd `SymmetricSpace{S,N}` or a `AntiSymmetricSpace{S,N}` for bosons or fermions respectively, which correspond to the symmetric (permutation invariant) or antisymmetric subspace of `V^N`, where `V::S` represents the Hilbert space of the single particle system. Other -domains, like general relativity, might also benefit from tensors living in a subspace with -certain symmetries under specific index permutations. +scientific fields, like general relativity, might also benefit from tensors living in +subspace with certain symmetries under specific index permutations. - -## Operations on and relations between vector spaces +## [More operations with vector spaces](@id ss_spaceops) Vector spaces of the same `spacetype` can be given a partial order, based on whether there exist injective morphisms (a.k.a *monomorphisms*) or surjective morphisms (a.k.a. @@ -283,60 +334,12 @@ corresponding spaces, but in general none of those will be canonical. There are also a number of convenience functions to create isomorphic spaces. The function `fuse(V1, V2, ...)` or `fuse(V1 ⊗ V2 ⊗ ...)` returns an elementary space that is isomorphic -to `V1 ⊗ V2 ⊗ ...`. The function `flip(V::ElementarySpace)` returns a space that is -isomorphic to `V` but has `isdual(flip(V)) == isdual(V')`, i.e., if `V` is a normal space, -then `flip(V)` is a dual space. `flip(V)` is different from `dual(V)` in the case of -[`GradedSpace`](@ref). It is useful to flip a tensor index from a ket to a bra (or -vice versa), by contracting that index with a unitary map from `V1` to `flip(V1)`. We refer -to the reference on [vector space methods](@ref s_spacemethods) for further information. -Some examples: -```@repl tensorkit -ℝ^3 ≾ ℝ^5 -ℂ^3 ≾ (ℂ^5)' -(ℂ^5) ≅ (ℂ^5)' -fuse(ℝ^5, ℝ^3) -fuse(ℂ^3, (ℂ^5)' ⊗ ℂ^2) -fuse(ℂ^3, (ℂ^5)') ⊗ ℂ^2 ≅ fuse(ℂ^3, (ℂ^5)', ℂ^2) ≅ ℂ^3 ⊗ (ℂ^5)' ⊗ ℂ^2 -flip(ℂ^4) -flip(ℂ^4) ≅ ℂ^4 -flip(ℂ^4) == ℂ^4 -``` - -We also define the direct sum `V1` and `V2` as `V1 ⊕ V2`, where `⊕` is obtained by typing -`\oplus`+TAB. This is possible only if `isdual(V1) == isdual(V2)`. `unitspace` applied to an elementary space -(in the value or type domain) returns the one-dimensional space, which is isomorphic to the -scalar field of the space itself. Some examples illustrate this better. -```@repl tensorkit -ℝ^5 ⊕ ℝ^3 -ℂ^5 ⊕ ℂ^3 -ℂ^5 ⊕ (ℂ^3)' -unitspace(ℝ^3) -ℂ^5 ⊕ unitspace(ComplexSpace) -unitspace((ℂ^3)') -(ℂ^5) ⊕ unitspace((ℂ^5)) -(ℂ^5)' ⊕ unitspace((ℂ^5)') -``` - -Finally, while spaces have a partial order, there is no unique infimum or supremum of a two -or more spaces. However, if `V1` and `V2` are two `ElementarySpace` instances with -`isdual(V1) == isdual(V2)`, then we can define a unique infimum `V::ElementarySpace` with -the same value of `isdual` that satisfies `V ≾ V1` and `V ≾ V2`, as well as a unique -supremum `W::ElementarySpace` with the same value of `isdual` that satisfies `W ≿ V1` -and `W ≿ V2`. For `CartesianSpace` and `ComplexSpace`, this simply amounts to the -space with minimal or maximal dimension, i.e. -```@repl tensorkit -infimum(ℝ^5, ℝ^3) -supremum(ℂ^5, ℂ^3) -supremum(ℂ^5, (ℂ^3)') -``` -The names `infimum` and `supremum` are especially suited in the case of -[`GradedSpace`](@ref), as the infimum of two spaces might be different from either -of those two spaces, and similar for the supremum. +to `V1 ⊗ V2 ⊗ ...`. ## [Space of morphisms](@id ss_homspaces) -As mentioned in the introduction, we will define tensor maps as linear maps from -a `ProductSpace` domain to a `ProductSpace` codomain. All tensor maps with a fixed -domain and codomain form a vector space, which we represent with the `HomSpace` type. +As mentioned in the introduction, we define tensor maps as linear maps from +a `ProductSpace` domain to a `ProductSpace` codomain. The set of all tensor maps with a fixed +domain and codomain constitutes a vector space, which we represent with the `HomSpace` type. ```julia struct HomSpace{S<:ElementarySpace, P1<:CompositeSpace{S}, P2<:CompositeSpace{S}} codomain::P1 @@ -352,12 +355,16 @@ reason for first listing the codomain and than the domain will become clear in t Note that `HomSpace` is not a subtype of `VectorSpace`, i.e. we restrict the latter to encode all spaces and generalizations thereof (i.e. objects in linear monoidal categories) that are associated with the indices and the domain and codomain of a tensor map. Even when -these genearalizations are no longer strictly vector spaces and have unconventional properties +these generalizations are no longer strictly vector spaces and have unconventional properties (such as non-integer dimensions), the space of tensor maps (homomorphisms) between a given -domain and codomain is always a vector space in the strict mathematical sense. Furthermore, -on these `HomSpace` instances, we define a number of useful methods that are a predecessor -to the corresponidng methods that we will define to manipulate the actual tensors, as we -illustrate in the following example: +domain and codomain, represented by a `HomSpace` instance, is always a vector space in the +strict mathematical sense (with in particular an integer dimension). Because `HomSpace` and +the different subtypes of `VectorSpace` represent very different mathematical concepts that do +not directly interact, we have chosen to keep them separate in the type hierarchy. + +Furthermore, on these `HomSpace` instances, we define a number of useful methods that are a +precursor to the corresponding methods that we will define to manipulate the actual tensors, +as illustrated in the following example: ```@repl tensorkit W = ℂ^2 ⊗ ℂ^3 → ℂ^3 ⊗ dual(ℂ^4) field(W) @@ -383,11 +390,14 @@ insertrightunit(W, 2) removeunit(insertrightunit(W, 2), 3) TensorKit.compose(W, adjoint(W)) ``` -Note that indexing `W` yields first the spaces in the codomain, followed by the dual of the -spaces in the domain. This particular convention is useful in combination with the -instances of type [`TensorMap`](@ref), which represent morphisms living in such a -`HomSpace`. Also note that `dim(W)` is here given by the product of the dimensions of the -individual spaces, but that this is no longer true once symmetries are involved. At any -time will `dim(::HomSpace)` represent the number of linearly independent morphisms in this -space, or thus, the number of independent components that a corresponding `TensorMap` -object will have. +Note that indexing `W` follows an order that first targets the spaces in the codomain, +followed by the dual of the spaces in the domain. This particular convention is useful +in combination with the instances of type [`TensorMap`](@ref), which represent the actual +morphisms living in such a `HomSpace`. Also note that `dim(W)` is here given by the product +of the dimensions of the individual spaces, but that this is no longer true once symmetries +are involved. At any time will `dim(::HomSpace)` represent the number of linearly independent +morphisms in this space, or thus, the number of independent components that a corresponding +`TensorMap` object will have. + +A complete list of methods defined on `HomSpace` instances together with the corresponding +documentation is provided in the [library section on Vector spaces](@ref s_libvectorspaces). diff --git a/src/spaces/vectorspaces.jl b/src/spaces/vectorspaces.jl index d4901d08e..2c86d5245 100644 --- a/src/spaces/vectorspaces.jl +++ b/src/spaces/vectorspaces.jl @@ -36,8 +36,7 @@ Base.issubset(::ComplexNumbers, ::ComplexNumbers) = true abstract type VectorSpace end Abstract type at the top of the type hierarchy for denoting vector spaces, or, more -accurately, 𝕜-linear categories. All instances of subtypes of VectorSpace will -represent objects in 𝕜-linear monoidal categories. +generally, objects in linear monoidal categories. """ abstract type VectorSpace end @@ -46,7 +45,7 @@ abstract type VectorSpace end field(::Type{T}) -> Type{𝔽<:Field} Return the type of field over which object `a` (e.g. a vector space or a tensor) is defined. -Also works in type domain. +This also works in type domain. """ field(x) = field(typeof(x)) field(::Type{T}) where {T} = field(spacetype(T))