Skip to content

Commit 435a2a3

Browse files
committed
more fixes and first tests
1 parent 1c723ea commit 435a2a3

File tree

10 files changed

+159
-25
lines changed

10 files changed

+159
-25
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ TensorOperations = "5.1"
4040
Test = "1"
4141
TestExtras = "0.2,0.3"
4242
TupleTools = "1.1"
43-
VectorInterface = "0.4"
43+
VectorInterface = "0.4,0.5"
4444
julia = "1.10"
4545

4646
[extras]

src/TensorKit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export TruncationScheme
3131
export SpaceMismatch, SectorMismatch, IndexError # error types
3232

3333
# general vector space methods
34-
export space, field, dual, dim, dims, fuse, flip, isdual, insertunit, oplus
34+
export space, field, dual, dim, reduceddim, dims, fuse, flip, isdual, insertunit, oplus
3535

3636
# partial order for vector spaces
3737
export infimum, supremum, isisomorphic, ismonomorphic, isepimorphic

src/spaces/gradedspace.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ function GradedSpace{I,NTuple{N,Int}}(dims; dual::Bool=false) where {I,N}
3434
d = ntuple(n -> 0, N)
3535
isset = ntuple(n -> false, N)
3636
for (c, dc) in dims
37-
i = findindex(values(I), convert(I, c))
37+
k = convert(I, c)
38+
i = findindex(values(I), k)
39+
k = dc < 0 && throw(ArgumentError("Sector $k has negative dimension $dc"))
3840
isset[i] && throw(ArgumentError("Sector $c appears multiple times"))
3941
isset = TupleTools.setindex(isset, true, i)
4042
d = TupleTools.setindex(d, dc, i)
@@ -50,6 +52,7 @@ function GradedSpace{I,SectorDict{I,Int}}(dims; dual::Bool=false) where {I<:Sect
5052
for (c, dc) in dims
5153
k = convert(I, c)
5254
haskey(d, k) && throw(ArgumentError("Sector $k appears multiple times"))
55+
dc < 0 && throw(ArgumentError("Sector $k has negative dimension $dc"))
5356
!iszero(dc) && push!(d, k => dc)
5457
end
5558
return GradedSpace{I,SectorDict{I,Int}}(d, dual)

src/spaces/vectorspaces.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ field(V::ElementarySpace) = field(typeof(V))
111111
Return the degeneracy dimension corresponding to the sector `s` of the vector space `V`.
112112
""" dim(::ElementarySpace, ::Sector)
113113

114+
@doc """
115+
reduceddim(V::ElementarySpace) -> Int
116+
117+
Return the sum of all degeneracy dimensions of the vector space `V`.
118+
"""
119+
reduceddim(V::ElementarySpace) = sum(c -> dim(V, c), sectors(V); init=0)
120+
114121
"""
115122
oneunit(V::S) where {S<:ElementarySpace} -> S
116123

src/tensors/diagtensor.jl

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ struct DiagonalTensorMap{T,S<:IndexSpace,A<:DenseVector{T}} <: AbstractTensorMap
88
function DiagonalTensorMap{T,S,A}(::UndefInitializer,
99
dom::S) where {T,S<:IndexSpace,A<:DenseVector{T}}
1010
data = A(undef, reduceddim(dom))
11+
if !isbitstype(T)
12+
zerovector!(data)
13+
end
1114
return DiagonalTensorMap{T,S,A}(data, dom)
1215
end
1316
# constructors from data
@@ -17,7 +20,6 @@ struct DiagonalTensorMap{T,S<:IndexSpace,A<:DenseVector{T}} <: AbstractTensorMap
1720
return new{T,S,A}(data, dom)
1821
end
1922
end
20-
reduceddim(V::IndexSpace) = sum(c -> dim(V, c), sectors(V); init=0)
2123

2224
# Basic methods for characterising a tensor:
2325
#--------------------------------------------
@@ -43,6 +45,7 @@ Construct a `DiagonalTensorMap` with uninitialized data.
4345
function DiagonalTensorMap{T}(::UndefInitializer, V::S) where {T,S<:IndexSpace}
4446
return DiagonalTensorMap{T,S,Vector{T}}(undef, V)
4547
end
48+
DiagonalTensorMap(::UndefInitializer, V::IndexSpace) = DiagonalTensorMap{Float64}(undef, V)
4649

4750
function DiagonalTensorMap{T}(data::A, V::S) where {T,S<:IndexSpace,A<:DenseVector{T}}
4851
length(data) == reduceddim(V) ||
@@ -75,6 +78,14 @@ end
7578
TensorMap(d::DiagonalTensorMap) = copy!(similar(d), d)
7679
Base.convert(::Type{TensorMap}, d::DiagonalTensorMap) = TensorMap(d)
7780

81+
function Base.convert(::Type{DiagonalTensorMap{T,S,A}},
82+
d::DiagonalTensorMap{T,S,A}) where {T,S,A}
83+
return d
84+
end
85+
function Base.convert(D::Type{<:DiagonalTensorMap}, d::DiagonalTensorMap)
86+
return DiagonalTensorMap(convert(storagetype(D), d.data), d.domain)
87+
end
88+
7889
# Complex, real and imaginary parts
7990
#-----------------------------------
8091
for f in (:real, :imag, :complex)
@@ -87,22 +98,21 @@ end
8798

8899
# Getting and setting the data at the block level
89100
#-------------------------------------------------
90-
blocksectors(d::DiagonalTensorMap) = blocksectors(d.domain)
91-
92101
function block(d::DiagonalTensorMap, s::Sector)
93102
sectortype(d) == typeof(s) || throw(SectorMismatch())
94103
offset = 0
95104
dom = domain(d)[1]
96-
for c in sectors(dom)
105+
for c in blocksectors(d)
97106
if c < s
98107
offset += dim(dom, c)
99108
elseif c == s
100109
r = offset .+ (1:dim(dom, c))
101110
return Diagonal(view(d.data, r))
102111
else # s not in sectors(t)
103-
return Diagonal(view(d.data, 1:0))
112+
break
104113
end
105114
end
115+
return Diagonal(view(d.data, 1:0))
106116
end
107117

108118
# TODO: is relying on generic AbstractTensorMap blocks sufficient?
@@ -133,29 +143,33 @@ end
133143
# Index manipulations
134144
# -------------------
135145
function has_shared_permute(d::DiagonalTensorMap, (p₁, p₂)::Index2Tuple)
136-
if p₁ === codomainind(d) && p₂ === domainind(d)
146+
if p₁ === (1,) && p₂ === (2,)
137147
return true
138-
elseif BraidingStyle(sectortype(d)) isa Bosonic
139-
# TODO: is this always correct? transpose has no effect for bosonic sectors?
140-
return p₁ === domainind(d) && p₂ === codomainind(d)
148+
elseif p₁ === (2,) && p₂ === (1,) # transpose
149+
return sectortype(d) === Trivial
141150
else
142151
return false
143152
end
144153
end
145154

146-
function permute(d::DiagonalTensorMap, (p₁, p₂)::Index2Tuple{N₁,N₂};
147-
copy::Bool=false) where {N₁,N₂}
148-
if !copy
149-
if p₁ === codomainind(d) && p₂ === domainind(d)
150-
return d
151-
elseif has_shared_permute(d, (p₁, p₂)) # tranpose for bosonic sectors
152-
return DiagonalTensorMap(d.data, dual(d.domain))
155+
function permute(d::DiagonalTensorMap, (p₁, p₂)::Index2Tuple{1,1};
156+
copy::Bool=false)
157+
if p₁ === (1,) && p₂ === (2,)
158+
return copy ? Base.copy(d) : d
159+
elseif p₁ === (2,) && p₂ === (1,) # transpose
160+
if has_shared_permute(d, (p₁, p₂)) # tranpose for bosonic sectors
161+
return DiagonalTensorMap(copy ? Base.copy(d.data) : d.data, dual(d.domain))
153162
end
154-
end
155-
# general case
156-
space′ = permute(space(d), (p₁, p₂))
157-
@inbounds begin
158-
return permute!(similar(d, space′), d, (p₁, p₂))
163+
d′ = typeof(d)(undef, dual(d.domain))
164+
for (c, b) in blocks(d)
165+
f = only(fusiontrees(codomain(d), c))
166+
((f′, _), coeff) = only(permute(f, f, p₁, p₂))
167+
c′ = f′.coupled
168+
scale!(block(d′, c′), b, coeff)
169+
end
170+
return d′
171+
else
172+
throw(ArgumentError("invalid permutation $((p₁, p₂)) for tensor in space $(space(d))"))
159173
end
160174
end
161175

src/tensors/indexmanipulations.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ function permute(t::AbstractTensorMap, (p₁, p₂)::Index2Tuple{N₁,N₂};
4040
end
4141
end
4242
function permute(t::TensorMap, (p₁, p₂)::Index2Tuple{N₁,N₂}; copy::Bool=false) where {N₁,N₂}
43-
@show (p₁, p₂)
4443
space′ = permute(space(t), (p₁, p₂))
4544
# share data if possible
4645
if !copy

src/tensors/tensor.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ struct TensorMap{T,S<:IndexSpace,N₁,N₂,A<:DenseVector{T}} <: AbstractTensorM
1818
A<:DenseVector{T}}
1919
d = fusionblockstructure(space).totaldim
2020
data = A(undef, d)
21+
if !isbitstype(T)
22+
zerovector!(data)
23+
end
2124
return TensorMap{T,S,N₁,N₂,A}(data, space)
2225
end
2326

test/diagtensors.jl

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
diagspacelist = ((ℂ^4)', ℂ[Z2Irrep](0 => 2, 1 => 3),
2+
ℂ[FermionNumber](0 => 2, 1 => 2, -1 => 1)',
3+
ℂ[SU2Irrep](0 => 2, 1 => 1), ℂ[FibonacciAnyon](:I => 2, :τ => 2))
4+
5+
@testset "DiagonalTensor with domain $V" for V in diagspacelist
6+
@timedtestset "Basic properties and algebra" begin
7+
for T in (Float32, Float64, ComplexF32, ComplexF64, BigFloat)
8+
t = @constinferred DiagonalTensorMap{T}(undef, V)
9+
t = @constinferred DiagonalTensorMap(rand(T, reduceddim(V)), V)
10+
@test @constinferred(hash(t)) == hash(deepcopy(t))
11+
@test scalartype(t) == T
12+
@test codomain(t) == ProductSpace(V)
13+
@test domain(t) == ProductSpace(V)
14+
@test space(t) == (V V)
15+
@test space(t') == (V ← V)
16+
@test dim(t) == dim(space(t))
17+
@test isa(@constinferred(norm(t)), real(T))
18+
@test norm(t)^2 ≈ dot(t, t)
19+
α = rand(T)
20+
@test norm(α * t) ≈ abs(α) * norm(t)
21+
@test norm(t + t, 2) ≈ 2 * norm(t, 2)
22+
@test norm(t + t, 1) ≈ 2 * norm(t, 1)
23+
@test norm(t + t, Inf) ≈ 2 * norm(t, Inf)
24+
p = 3 * rand(Float64)
25+
@test norm(t + t, p) ≈ 2 * norm(t, p)
26+
@test norm(t) ≈ norm(t')
27+
28+
@test t == @constinferred(TensorMap(t))
29+
@test norm(t + TensorMap(t)) 2 * norm(t)
30+
31+
@test norm(zerovector!(t)) == 0
32+
@test norm(one!(t)) sqrt(dim(V))
33+
@test one!(t) == id(V)
34+
@test norm(one!(t) - id(V)) == 0
35+
36+
t1 = DiagonalTensorMap(rand(T, reduceddim(V)), V)
37+
t2 = DiagonalTensorMap(rand(T, reduceddim(V)), V)
38+
t3 = DiagonalTensorMap(rand(T, reduceddim(V)), V)
39+
α = rand(T)
40+
β = rand(T)
41+
@test @constinferred(dot(t1, t2)) conj(dot(t2, t1))
42+
@test dot(t2, t1) conj(dot(t2', t1'))
43+
@test dot(t3, α * t1 + β * t2) α * dot(t3, t1) + β * dot(t3, t2)
44+
end
45+
end
46+
I = sectortype(V)
47+
@timedtestset "Basic linear algebra: test via conversion" begin
48+
for T in (Float32, ComplexF64)
49+
t1 = DiagonalTensorMap(rand(T, reduceddim(V)), V)
50+
t2 = DiagonalTensorMap(rand(T, reduceddim(V)), V)
51+
@test norm(t1, 2) norm(convert(TensorMap, t1), 2)
52+
@test dot(t2, t1) dot(convert(TensorMap, t2), convert(TensorMap, t1))
53+
α = rand(T)
54+
@test convert(TensorMap, α * t1) α * convert(TensorMap, t1)
55+
@test convert(TensorMap, t1') ≈ convert(TensorMap, t1)'
56+
@test convert(TensorMap, t1 + t2)
57+
convert(TensorMap, t1) + convert(TensorMap, t2)
58+
end
59+
end
60+
@timedtestset "Real and imaginary parts" begin
61+
for T in (Float64, ComplexF64, ComplexF32)
62+
t = DiagonalTensorMap(rand(T, reduceddim(V)), V)
63+
64+
tr = @constinferred real(t)
65+
@test scalartype(tr) <: Real
66+
@test real(convert(TensorMap, t)) == convert(TensorMap, tr)
67+
68+
ti = @constinferred imag(t)
69+
@test scalartype(ti) <: Real
70+
@test imag(convert(TensorMap, t)) == convert(TensorMap, ti)
71+
72+
tc = @inferred complex(t)
73+
@test scalartype(tc) <: Complex
74+
@test complex(convert(TensorMap, t)) == convert(TensorMap, tc)
75+
76+
tc2 = @inferred complex(tr, ti)
77+
@test tc2 tc
78+
end
79+
end
80+
@timedtestset "Tensor conversion" begin
81+
t = @constinferred DiagonalTensorMap(undef, V)
82+
rand!(t.data)
83+
tc = complex(t)
84+
@test convert(typeof(tc), t) == tc
85+
@test typeof(convert(typeof(tc), t)) == typeof(tc)
86+
end
87+
I = sectortype(V)
88+
if BraidingStyle(I) isa SymmetricBraiding
89+
@timedtestset "Permutations" begin
90+
t = DiagonalTensorMap(randn(ComplexF64, reduceddim(V)), V)
91+
t1 = @constinferred permute(t, $(((2,), (1,))))
92+
if BraidingStyle(sectortype(V)) isa Bosonic
93+
@test t1 == transpose(t)
94+
end
95+
@test convert(TensorMap, t1) == permute(convert(TensorMap, t), (((2,), (1,))))
96+
t2 = @constinferred permute(t, $(((1, 2), ())))
97+
@test convert(TensorMap, t2) == permute(convert(TensorMap, t), (((1, 2), ())))
98+
t3 = @constinferred permute(t, $(((2, 1), ())))
99+
@test convert(TensorMap, t3) == permute(convert(TensorMap, t), (((2, 1), ())))
100+
t4 = @constinferred permute(t, $(((), (1, 2))))
101+
@test convert(TensorMap, t4) == permute(convert(TensorMap, t), (((), (1, 2))))
102+
t5 = @constinferred permute(t, $(((), (2, 1))))
103+
@test convert(TensorMap, t5) == permute(convert(TensorMap, t), (((), (2, 1))))
104+
end
105+
end
106+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Ti = time()
5757
include("fusiontrees.jl")
5858
include("spaces.jl")
5959
include("tensors.jl")
60+
include("diagtensors.jl")
6061
include("planar.jl")
6162
include("ad.jl")
6263
include("bugfixes.jl")

test/spaces.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ println("------------------------------------")
221221
slist = @constinferred sectors(V)
222222
@test @constinferred(TensorKit.hassector(V, first(slist)))
223223
@test @constinferred(dim(V)) == sum(dim(s) * dim(V, s) for s in slist)
224+
@test @constinferred(reduceddim(V)) == sum(dim(V, s) for s in slist)
224225
@constinferred dim(V, first(slist))
225226
if hasfusiontensor(I)
226227
@test @constinferred(TensorKit.axes(V)) == Base.OneTo(dim(V))

0 commit comments

Comments
 (0)