Skip to content

Commit

Permalink
Remove field type-parameter for vectorspaces (#83)
Browse files Browse the repository at this point in the history
* Setup ChainRules package extension

* Port over some methods from TensorKitAD

* Updates for leftorth and rightorth rrules

* Formatting

* Fix missing using PackageExtensionCompat

* Add some missing `rrule`s.

* little bit of cleanup

* repartition

* Remove overloaded `rrule`s in favor of TensorOperations update

* Update VectorInterface 0.4

* Various AD bugfixes

* some AD tests amd updates

* Update AD rules, clean up tests

* Include qdim in vectors

* Formatter

* Setup ChainRules package extension

* Port over some methods from TensorKitAD

* Updates for leftorth and rightorth rrules

* Formatting

* Fix missing using PackageExtensionCompat

* Add some missing `rrule`s.

* little bit of cleanup

* repartition

* Remove overloaded `rrule`s in favor of TensorOperations update

* Remove eltype deprecation

* Add constructor BraidingTensor

`BraidingTensor(HomSpace)` is now allowed

* remove space type parameter

* Generalize VectorSpace methods with promotion system

* Remove field parameter from docstring

* export TensorMapSpace, TrivialTensorMap

This allows these to function as a type alias, thus improving printing

* Fix `convert(ProductSpace, V)`

* remove `thunk` in trace

* Formatter

* Add rrule for efficient copy constructor

* Fix non-existent argument name

* Add ProjectTo functionality

* Add tensoroperations tests

* Changes for TensorOperations 4.0.6

tensorscalar now has a `rrule`

* remove obsolete test

* Refactor deligne field test in a function

* Remove unsupported 1.6 string syntax

* Avoid allocation in deligne product field checking

* Avoid allocation in InnerProduct checks

* @test_throws julia 1.6 compat

* Remove `flip(ProductSpace)`

* Formatter

---------

Co-authored-by: leburgel <[email protected]>
  • Loading branch information
lkdvos and leburgel authored Oct 5, 2023
1 parent 2cfbb64 commit ec40cea
Show file tree
Hide file tree
Showing 16 changed files with 117 additions and 100 deletions.
3 changes: 2 additions & 1 deletion src/TensorKit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export ZNSpace, Z2Space, Z3Space, Z4Space, U1Space, CU1Space, SU2Space
export Vect, Rep # space constructors
export CompositeSpace, ProductSpace # composite spaces
export FusionTree
export IndexSpace, TensorSpace, AbstractTensorMap, AbstractTensor, TensorMap, Tensor # tensors and tensor properties
export IndexSpace, TensorSpace, TensorMapSpace
export AbstractTensorMap, AbstractTensor, TensorMap, Tensor, TrivialTensorMap # tensors and tensor properties
export TruncationScheme
export SpaceMismatch, SectorMismatch, IndexError # error types

Expand Down
4 changes: 1 addition & 3 deletions src/auxiliary/deprecate.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import Base: eltype, transpose
@deprecate eltype(T::Type{<:AbstractTensorMap}) scalartype(T)
@deprecate eltype(t::AbstractTensorMap) scalartype(t)
import Base: transpose

#! format: off
@deprecate permute(t::AbstractTensorMap, p1::IndexTuple, p2::IndexTuple; copy::Bool=false) permute(t, (p1, p2); copy=copy)
Expand Down
2 changes: 1 addition & 1 deletion src/fusiontrees/manipulations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function bendright(f₁::FusionTree{I,N₁}, f₂::FusionTree{I,N₂}) where {I<
coeff₀ = sqrtdim(c) * isqrtdim(a)
if f₁.isdual[N₁]
coeff₀ *= conj(frobeniusschur(dual(b)))
end
end
if FusionStyle(I) isa MultiplicityFreeFusion
coeff = coeff₀ * Bsymbol(a, b, c)
vertices2 = N₂ > 0 ? (f₂.vertices..., nothing) : ()
Expand Down
6 changes: 4 additions & 2 deletions src/spaces/cartesianspace.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""
struct CartesianSpace <: ElementarySpace{ℝ}
struct CartesianSpace <: ElementarySpace
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
vector space that is implicitly assumed in most of matrix algebra.
"""
struct CartesianSpace <: ElementarySpace{ℝ}
struct CartesianSpace <: ElementarySpace
d::Int
end
CartesianSpace(d::Integer=0; dual=false) = CartesianSpace(Int(d))
Expand All @@ -28,8 +28,10 @@ function CartesianSpace(dims::AbstractDict; kwargs...)
end
end

field(::Type{CartesianSpace}) =
InnerProductStyle(::Type{CartesianSpace}) = EuclideanProduct()

Base.conj(V::CartesianSpace) = V
isdual(V::CartesianSpace) = false

# convenience constructor
Expand Down
5 changes: 3 additions & 2 deletions src/spaces/complexspace.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""
struct ComplexSpace <: ElementarySpace{ℂ}
struct ComplexSpace <: ElementarySpace
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).
"""
struct ComplexSpace <: ElementarySpace{ℂ}
struct ComplexSpace <: ElementarySpace
d::Int
dual::Bool
end
Expand All @@ -29,6 +29,7 @@ function ComplexSpace(dims::AbstractDict; kwargs...)
end
end

field(::Type{ComplexSpace}) =
InnerProductStyle(::Type{ComplexSpace}) = EuclideanProduct()

# convenience constructor
Expand Down
34 changes: 24 additions & 10 deletions src/spaces/deligne.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,52 +12,66 @@ The Deligne tensor product also works in the type domain and for sectors and ten
group representations, we have `Rep[G₁] ⊠ Rep[G₂] == Rep[G₁ × G₂]`, i.e. these are the
natural representation spaces of the direct product of two groups.
"""
(V₁::VectorSpace, V₂::VectorSpace) = (V₁ one(V₂)) (one(V₁) V₂)
function (V₁::VectorSpace, V₂::VectorSpace)
field(V₁) == field(V₂) || throw_incompatible_fields(V₁, V₂)
return (V₁ one(V₂)) (one(V₁) V₂)
end

# define deligne products with empty tensor product: just add a trivial sector of the type of the empty space to each of the sectors in the non-empty space
function (V::GradedSpace, P₀::ProductSpace{<:ElementarySpace{ℂ},0})
# define deligne products with empty tensor product: just add a trivial sector of the type
# of the empty space to each of the sectors in the non-empty space
function (V::GradedSpace, P₀::ProductSpace{<:ElementarySpace,0})
field(V) == field(P₀) || throw_incompatible_fields(V, P₀)
I₁ = sectortype(V)
I₂ = sectortype(P₀)
return Vect[I₁ I₂](ifelse(isdual(V), dual(c), c) one(I₂) => dim(V, c)
for c in sectors(V); dual=isdual(V))
end

function (P₀::ProductSpace{<:ElementarySpace{ℂ},0}, V::GradedSpace)
function (P₀::ProductSpace{<:ElementarySpace,0}, V::GradedSpace)
field(P₀) == field(V) || throw_incompatible_fields(P₀, V)
I₁ = sectortype(P₀)
I₂ = sectortype(V)
return Vect[I₁ I₂](one(I₁) ifelse(isdual(V), dual(c), c) => dim(V, c)
for c in sectors(V); dual=isdual(V))
end

function (V::ComplexSpace, P₀::ProductSpace{<:ElementarySpace{ℂ},0})
function (V::ComplexSpace, P₀::ProductSpace{<:ElementarySpace,0})
field(V) == field(P₀) || throw_incompatible_fields(V, P₀)
I₂ = sectortype(P₀)
return Vect[I₂](one(I₂) => dim(V); dual=isdual(V))
end

function (P₀::ProductSpace{<:ElementarySpace{ℂ},0}, V::ComplexSpace)
function (P₀::ProductSpace{<:ElementarySpace,0}, V::ComplexSpace)
field(P₀) == field(V) || throw_incompatible_fields(P₀, V)
I₁ = sectortype(P₀)
return Vect[I₁](one(I₁) => dim(V); dual=isdual(V))
end

function (P::ProductSpace{<:ElementarySpace{ℂ},0},
P₀::ProductSpace{<:ElementarySpace{ℂ},0})
function (P::ProductSpace{<:ElementarySpace,0}, P₀::ProductSpace{<:ElementarySpace,0})
field(P) == field(P₀) || throw_incompatible_fields(P, P₀)
I₁ = sectortype(P)
I₂ = sectortype(P₀)
return one(Vect[I₁ I₂])
end

function (P::ProductSpace{<:ElementarySpace{ℂ}}, P₀::ProductSpace{<:ElementarySpace{ℂ},0})
function (P::ProductSpace{<:ElementarySpace}, P₀::ProductSpace{<:ElementarySpace,0})
field(P) == field(P₀) || throw_incompatible_fields(P, P₀)
I₁ = sectortype(P)
I₂ = sectortype(P₀)
S = Vect[I₁ I₂]
N = length(P)
return ProductSpace{S,N}(map(V -> V P₀, tuple(P...)))
end

function (P₀::ProductSpace{<:ElementarySpace{ℂ},0}, P::ProductSpace{<:ElementarySpace{ℂ}})
function (P₀::ProductSpace{<:ElementarySpace,0}, P::ProductSpace{<:ElementarySpace})
field(P₀) == field(P) || throw_incompatible_fields(P₀, P)
I₁ = sectortype(P₀)
I₂ = sectortype(P)
S = Vect[I₁ I₂]
N = length(P)
return ProductSpace{S,N}(map(V -> P₀ V, tuple(P...)))
end

@noinline function throw_incompatible_fields(P₁, P₂)
throw(ArgumentError("Deligne products require spaces over the same field: $(field(P₁))$(field(P₂))"))
end
5 changes: 3 additions & 2 deletions src/spaces/generalspace.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""
struct GeneralSpace{𝕜} <: ElementarySpace{𝕜}
struct GeneralSpace{𝕜} <: ElementarySpace
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
conjugate space. For a real field `𝕜`, the space and its conjugate are the same.
"""
struct GeneralSpace{𝕜} <: ElementarySpace{𝕜}
struct GeneralSpace{𝕜} <: ElementarySpace
d::Int
dual::Bool
conj::Bool
Expand All @@ -29,6 +29,7 @@ isconj(V::GeneralSpace) = V.conj

Base.axes(V::GeneralSpace) = Base.OneTo(dim(V))

field(::Type{GeneralSpace{𝕜}}) where {𝕜} = 𝕜
InnerProductStyle(::Type{<:GeneralSpace}) = NoInnerProduct()

dual(V::GeneralSpace{𝕜}) where {𝕜} = GeneralSpace{𝕜}(dim(V), !isdual(V), isconj(V))
Expand Down
5 changes: 3 additions & 2 deletions src/spaces/gradedspace.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
struct GradedSpace{I<:Sector, D} <: ElementarySpace{ℂ}
struct GradedSpace{I<:Sector, D} <: ElementarySpace
dims::D
dual::Bool
end
Expand All @@ -24,7 +24,7 @@ and should typically be of no concern.
The concrete type `GradedSpace{I,D}` with correct `D` can be obtained as `Vect[I]`, or if
`I == Irrep[G]` for some `G<:Group`, as `Rep[G]`.
"""
struct GradedSpace{I<:Sector,D} <: ElementarySpace{ℂ}
struct GradedSpace{I<:Sector,D} <: ElementarySpace
dims::D
dual::Bool
end
Expand Down Expand Up @@ -84,6 +84,7 @@ Base.hash(V::GradedSpace, h::UInt) = hash(V.dual, hash(V.dims, h))

# Corresponding methods:
# properties
field(::Type{<:GradedSpace}) =
InnerProductStyle(::Type{<:GradedSpace}) = EuclideanProduct()
function dim(V::GradedSpace)
return reduce(+, dim(V, c) * dim(c) for c in sectors(V);
Expand Down
9 changes: 5 additions & 4 deletions src/spaces/homspace.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ function Base.getindex(W::TensorMapSpace{<:IndexSpace,N₁,N₂}, i) where {N₁
return i <= N₁ ? codomain(W)[i] : dual(domain(W)[i - N₁])
end

function (dom::TensorSpace{S}, codom::TensorSpace{S}) where {S<:ElementarySpace}
return HomSpace(ProductSpace(codom), ProductSpace(dom))
function (codom::ProductSpace{S}, dom::ProductSpace{S}) where {S<:ElementarySpace}
return HomSpace(codom, dom)
end

function (codom::TensorSpace{S}, dom::TensorSpace{S}) where {S<:ElementarySpace}
function (codom::S, dom::S) where {S<:ElementarySpace}
return HomSpace(ProductSpace(codom), ProductSpace(dom))
end
(codom::VectorSpace, dom::VectorSpace) = (promote(codom, dom)...)
(dom::VectorSpace, codom::VectorSpace) = (codom, dom)

function Base.show(io::IO, W::HomSpace)
if length(W.codomain) == 1
Expand Down
35 changes: 19 additions & 16 deletions src/spaces/productspace.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,21 +177,11 @@ Base.hash(P::ProductSpace, h::UInt) = hash(P.spaces, h)

# Default construction from product of spaces
#---------------------------------------------
(V₁::S, V₂::S) where {S<:ElementarySpace} = ProductSpace((V₁, V₂))
function (P1::ProductSpace{S}, V₂::S) where {S<:ElementarySpace}
return ProductSpace(tuple(P1.spaces..., V₂))
end
function (V₁::S, P2::ProductSpace{S}) where {S<:ElementarySpace}
return ProductSpace(tuple(V₁, P2.spaces...))
end
(V::Vararg{S}) where {S<:ElementarySpace} = ProductSpace(V)
(P::ProductSpace) = P
function (P1::ProductSpace{S}, P2::ProductSpace{S}) where {S<:ElementarySpace}
return ProductSpace(tuple(P1.spaces..., P2.spaces...))
return ProductSpace{S}(tuple(P1.spaces..., P2.spaces...))
end
(P::ProductSpace{S,0}, ::ProductSpace{S,0}) where {S<:ElementarySpace} = P
(P::ProductSpace{S}, ::ProductSpace{S,0}) where {S<:ElementarySpace} = P
(::ProductSpace{S,0}, P::ProductSpace{S}) where {S<:ElementarySpace} = P
(V::ElementarySpace) = ProductSpace((V,))
(P::ProductSpace) = P

# unit element with respect to the monoidal structure of taking tensor products
"""
Expand All @@ -205,14 +195,12 @@ Base.one(V::VectorSpace) = one(typeof(V))
Base.one(::Type{<:ProductSpace{S}}) where {S<:ElementarySpace} = ProductSpace{S,0}(())
Base.one(::Type{S}) where {S<:ElementarySpace} = ProductSpace{S,0}(())

Base.convert(::Type{<:ProductSpace}, V::ElementarySpace) = ProductSpace((V,))
Base.:^(V::ElementarySpace, N::Int) = ProductSpace{typeof(V),N}(ntuple(n -> V, N))
Base.:^(V::ProductSpace, N::Int) = (ntuple(n -> V, N)...)
function Base.literal_pow(::typeof(^), V::ElementarySpace, p::Val{N}) where {N}
return ProductSpace{typeof(V),N}(ntuple(n -> V, p))
end
Base.convert(::Type{S}, P::ProductSpace{S,0}) where {S<:ElementarySpace} = oneunit(S)
Base.convert(::Type{S}, P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...)

fuse(P::ProductSpace{S,0}) where {S<:ElementarySpace} = oneunit(S)
fuse(P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...)

Expand Down Expand Up @@ -255,3 +243,18 @@ Base.eltype(P::ProductSpace) = eltype(typeof(P))

Base.IteratorEltype(::Type{<:ProductSpace}) = Base.HasEltype()
Base.IteratorSize(::Type{<:ProductSpace}) = Base.HasLength()

Base.reverse(P::ProductSpace) = ProductSpace(reverse(P.spaces))

# Promotion and conversion
# ------------------------
function Base.promote_rule(::Type{S}, ::Type{<:ProductSpace{S}}) where {S<:ElementarySpace}
return ProductSpace{S}
end

# ProductSpace to ElementarySpace
Base.convert(::Type{S}, P::ProductSpace{S,0}) where {S<:ElementarySpace} = oneunit(S)
Base.convert(::Type{S}, P::ProductSpace{S}) where {S<:ElementarySpace} = fuse(P.spaces...)

# ElementarySpace to ProductSpace
Base.convert(::Type{<:ProductSpace}, V::S) where {S<:ElementarySpace} = (V)
28 changes: 19 additions & 9 deletions src/spaces/vectorspaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ function isdual end
# Hierarchy of elementary vector spaces
#---------------------------------------
"""
abstract type ElementarySpace{𝕜} <: VectorSpace end
abstract type ElementarySpace <: VectorSpace end
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 super type for all
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.
Expand All @@ -100,10 +100,11 @@ complex conjugate of the dual space is obtained as `dual(conj(V)) === conj(dual(
different spaces should be of the same type, so that a tensor can be defined as an element
of a homogeneous tensor product of these spaces.
"""
abstract type ElementarySpace{𝕜} <: VectorSpace end
abstract type ElementarySpace <: VectorSpace end
const IndexSpace = ElementarySpace

field(::Type{<:ElementarySpace{𝕜}}) where {𝕜} = 𝕜
field(V::ElementarySpace) = field(typeof(V))
# field(::Type{<:ElementarySpace{𝕜}}) where {𝕜} = 𝕜

"""
oneunit(V::S) where {S<:ElementarySpace} -> S
Expand All @@ -123,7 +124,8 @@ spaces `V₁`, `V₂`, ... Note that all the individual spaces should have the s
[`isdual`](@ref), as otherwise the direct sum is not defined.
"""
function end
(V₁, V₂, V₃, V₄...) = ((V₁, V₂), V₃, V₄...)
(V₁::VectorSpace, V₂::VectorSpace) = (promote(V₁, V₂)...)
(V::Vararg{VectorSpace}) = foldl(, V)

"""
⊗(V₁::S, V₂::S, V₃::S...) where {S<:ElementarySpace} -> S
Expand All @@ -136,7 +138,8 @@ The tensor product structure is preserved, see [`fuse`](@ref) for returning a si
elementary space of type `S` that is isomorphic to this tensor product.
"""
function end
(V₁, V₂, V₃, V₄...) = ((V₁, V₂), V₃, V₄...)
(V₁::VectorSpace, V₂::VectorSpace) = (promote(V₁, V₂)...)
(V::Vararg{VectorSpace}) = foldl(, V)

# convenience definitions:
Base.:*(V₁::VectorSpace, V₂::VectorSpace) = (V₁, V₂)
Expand Down Expand Up @@ -171,7 +174,10 @@ Return the conjugate space of `V`. This should satisfy `conj(conj(V)) == V`.
For `field(V)==ℝ`, `conj(V) == V`. It is assumed that `typeof(V) == typeof(conj(V))`.
"""
Base.conj(V::ElementarySpace{ℝ}) = V
function Base.conj(V::ElementarySpace)
@assert field(V) =="default conj only defined for Vector spaces over ℝ"
return V
end

# trait to describe the inner product type of vector spaces
abstract type InnerProductStyle end
Expand All @@ -192,6 +198,10 @@ Return the type of inner product for vector spaces, which can be either
InnerProductStyle(V::VectorSpace) = InnerProductStyle(typeof(V))
InnerProductStyle(::Type{<:VectorSpace}) = NoInnerProduct()

@noinline function throw_invalid_innerproduct(fname)
throw(ArgumentError("$fname requires Euclidean inner product"))
end

dual(V::VectorSpace) = dual(InnerProductStyle(V), V)
dual(::EuclideanProduct, V::VectorSpace) = conj(V)

Expand Down Expand Up @@ -229,7 +239,7 @@ end
abstract type CompositeSpace{S<:ElementarySpace} <: VectorSpace end
Abstract type for composite spaces that are defined in terms of a number of elementary
vector spaces of a homogeneous type `S<:ElementarySpace{𝕜}`.
vector spaces of a homogeneous type `S<:ElementarySpace`.
"""
abstract type CompositeSpace{S<:ElementarySpace} <: VectorSpace end

Expand Down
6 changes: 5 additions & 1 deletion src/tensors/braidingtensor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ function BraidingTensor(V1::S, V2::S, adjoint::Bool=false) where {S<:IndexSpace}
return BraidingTensor{S,Matrix{ComplexF64}}(V1, V2, adjoint)
end
end

function BraidingTensor(V::HomSpace{S}, adjoint::Bool=false) where {S<:IndexSpace}
domain(V) == reverse(codomain(V)) ||
throw(SpaceMismatch("Cannot define a braiding on $V"))
return BraidingTensor(V[1], V[2], adjoint)
end
function Base.adjoint(b::BraidingTensor{S,A}) where {S<:IndexSpace,A<:DenseMatrix}
return BraidingTensor{S,A}(b.V1, b.V2, !b.adjoint)
end
Expand Down
Loading

0 comments on commit ec40cea

Please sign in to comment.