Skip to content

Commit 5508f7c

Browse files
authored
BlockTensorKit.oplus renamed to BlockTensorKit.boxplus (#34)
* replace name conflict with boxplus * add docstring and export * fix import qualification
1 parent 3959823 commit 5508f7c

7 files changed

Lines changed: 80 additions & 79 deletions

File tree

docs/src/sumspaces.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,21 @@ These spaces are a natural extension of the `TensorKit` vector spaces, and you c
66
### `SumSpace`
77

88
In `BlockTensorKit`, we provide a type `SumSpace` that allows you to define such direct sums.
9-
They can be defined either directly via the constructor, or by using the `` operator.
9+
They can be defined either directly via the constructor, or by using the `` (`\boxplus<TAB>`) operator.
1010
In order for the direct sum to be wll-defined, all components must have the same value of `isdual`.
1111

1212
Essentially, that is all there is to it, and you can now use these `SumSpace` objects much in the same way as you would use an `IndexSpace` object in `TensorKit`.
1313
In particular, it adheres to the interface of `ElementarySpace`, which means that you can query the properties as you would expect.
1414

1515
!!! note
1616

17-
The operator `⊕` is used in both TensorKit and BlockTensorKit, and therefore it must be explicitly imported to avoid name clashes.
18-
Both functions achieve almost the same thing, as `BlockTensorKit.` can be thought of as a _lazy_ version of `TensorKit.⊕`.
17+
The notion of a direct sum of vector spaces is used in both TensorKit (`⊕` or `oplus`) and BlockTensorKit (`⊞` or `boxplus`).
18+
Both functions achieve almost the same thing, and `BlockTensorKit.` can be thought of as a _lazy_ version of `TensorKit.⊕`.
1919

2020
```@repl sumspaces
2121
using TensorKit, BlockTensorKit
22-
using BlockTensorKit: ⊕
23-
V = ℂ^1 ⊕ ℂ^2 ⊕ ℂ^3
24-
ℂ^2 ⊕ (ℂ^2)' ⊕ ℂ^2 # error
22+
V = ℂ^1 ⊞ ℂ^2 ⊞ ℂ^3
23+
ℂ^2 ⊞ (ℂ^2)' ⊞ ℂ^2 # error
2524
dim(V)
2625
isdual(V)
2726
isdual(V')
@@ -43,7 +42,7 @@ Because these objects are naturally `ElementarySpace` objects, they can be used
4342
Additionally, when mixing spaces and their sumspaces, all components are promoted to `SumSpace` instances.
4443

4544
```@repl sumspaces
46-
V1 = ℂ^1 ℂ^2 ℂ^3
45+
V1 = ℂ^1 ℂ^2 ℂ^3
4746
V2 = ℂ^2
4847
V1 ⊗ V2 ⊗ V1' == V1 * V2 * V1' == ProductSpace(V1,V2,V1') == ProductSpace(V1,V2) ⊗ V1'
4948
V1^3

src/BlockTensorKit.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module BlockTensorKit
22

3-
export SumSpace, ProductSumSpace
3+
export SumSpace, ProductSumSpace, , boxplus
44
export eachspace, SumSpaceIndices, sumspacetype
55

66
export AbstractBlockTensorMap, BlockTensorMap, SparseBlockTensorMap

src/linalg/factorizations.jl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ function MAK.check_input(::typeof(qr_full!), t::AbstractBlockTensorMap, QR, ::Ab
7272
@check_scalar R t
7373

7474
# space checks
75-
V_Q = TK.oplus(fuse(codomain(t)))
75+
V_Q = (fuse(codomain(t)))
7676
@check_space(Q, codomain(t) V_Q)
7777
@check_space(R, V_Q domain(t))
7878

@@ -81,7 +81,7 @@ end
8181
MAK.check_input(::typeof(qr_full!), t::AbstractBlockTensorMap, QR, ::DiagonalAlgorithm) = error()
8282

8383
function MAK.initialize_output(::typeof(qr_full!), t::AbstractBlockTensorMap, ::AbstractAlgorithm)
84-
V_Q = TK.oplus(fuse(codomain(t)))
84+
V_Q = (fuse(codomain(t)))
8585
Q = dense_similar(t, codomain(t) V_Q)
8686
R = dense_similar(t, V_Q domain(t))
8787
return Q, R
@@ -111,7 +111,7 @@ function MAK.check_input(::typeof(lq_full!), t::AbstractBlockTensorMap, LQ, ::Ab
111111
@check_scalar Q t
112112

113113
# space checks
114-
V_Q = TK.oplus(fuse(domain(t)))
114+
V_Q = (fuse(domain(t)))
115115
@check_space(L, codomain(t) V_Q)
116116
@check_space(Q, V_Q domain(t))
117117

@@ -120,7 +120,7 @@ end
120120
MAK.check_input(::typeof(lq_full!), t::AbstractBlockTensorMap, LQ, ::DiagonalAlgorithm) = error()
121121

122122
function MAK.initialize_output(::typeof(lq_full!), t::AbstractBlockTensorMap, ::AbstractAlgorithm)
123-
V_Q = TK.oplus(fuse(domain(t)))
123+
V_Q = (fuse(domain(t)))
124124
L = dense_similar(t, codomain(t) V_Q)
125125
Q = dense_similar(t, V_Q domain(t))
126126
return L, Q
@@ -151,7 +151,7 @@ function MAK.check_input(::typeof(MAK.left_orth_polar!), t::AbstractBlockTensorM
151151
@check_scalar P t
152152

153153
# space checks
154-
VW = TK.oplus(fuse(domain(t)))
154+
VW = (fuse(domain(t)))
155155
@check_space(W, codomain(t) VW)
156156
@check_space(P, VW domain(t))
157157

@@ -176,7 +176,7 @@ function MAK.check_input(::typeof(MAK.right_orth_polar!), t::AbstractBlockTensor
176176
@check_scalar Wᴴ t
177177

178178
# space checks
179-
VW = TK.oplus(fuse(codomain(t)))
179+
VW = (fuse(codomain(t)))
180180
@check_space(P, codomain(t) VW)
181181
@check_space(Wᴴ, VW domain(t))
182182

@@ -214,7 +214,7 @@ function MAK.check_input(::typeof(eigh_full!), t::AbstractBlockTensorMap, DV, ::
214214
@check_scalar V t
215215

216216
# space checks
217-
V_D = TK.oplus(fuse(domain(t)))
217+
V_D = (fuse(domain(t)))
218218
@check_space(D, V_D V_D)
219219
@check_space(V, codomain(t) V_D)
220220

@@ -225,14 +225,14 @@ MAK.check_input(::typeof(eigh_full!), t::AbstractBlockTensorMap, DV, ::DiagonalA
225225
function MAK.check_input(::typeof(eigh_vals!), t::AbstractBlockTensorMap, D, ::AbstractAlgorithm)
226226
@check_scalar D t real
227227
@assert D isa DiagonalTensorMap
228-
V_D = TK.oplus(fuse(domain(t)))
228+
V_D = (fuse(domain(t)))
229229
@check_space(D, V_D V_D)
230230
return nothing
231231
end
232232
MAK.check_input(::typeof(eigh_vals!), t::AbstractBlockTensorMap, D, ::DiagonalAlgorithm) = error()
233233

234234
function MAK.initialize_output(::typeof(eigh_full!), t::AbstractBlockTensorMap, ::AbstractAlgorithm)
235-
V_D = TK.oplus(fuse(domain(t)))
235+
V_D = (fuse(domain(t)))
236236
T = real(scalartype(t))
237237
D = DiagonalTensorMap{T}(undef, V_D)
238238
V = dense_similar(t, codomain(t) V_D)
@@ -255,7 +255,7 @@ function MAK.check_input(::typeof(eig_full!), t::AbstractBlockTensorMap, DV, ::A
255255
@check_scalar V t complex
256256

257257
# space checks
258-
V_D = TK.oplus(fuse(domain(t)))
258+
V_D = (fuse(domain(t)))
259259
@check_space(D, V_D V_D)
260260
@check_space(V, codomain(t) V_D)
261261

@@ -264,7 +264,7 @@ end
264264
MAK.check_input(::typeof(eig_full!), t::AbstractBlockTensorMap, DV, ::DiagonalAlgorithm) = error()
265265

266266
function MAK.initialize_output(::typeof(eig_full!), t::AbstractBlockTensorMap, ::AbstractAlgorithm)
267-
V_D = TK.oplus(fuse(domain(t)))
267+
V_D = (fuse(domain(t)))
268268
Tc = complex(scalartype(t))
269269
D = DiagonalTensorMap{Tc}(undef, V_D)
270270
V = dense_similar(t, Tc, codomain(t) V_D)
@@ -285,17 +285,17 @@ function MAK.check_input(::typeof(svd_full!), t::AbstractBlockTensorMap, USVᴴ,
285285
@check_scalar Vᴴ t
286286

287287
# space checks
288-
V_cod = TK.oplus(fuse(codomain(t)))
289-
V_dom = TK.oplus(fuse(domain(t)))
288+
V_cod = (fuse(codomain(t)))
289+
V_dom = (fuse(domain(t)))
290290
@check_space(U, codomain(t) V_cod)
291291
@check_space(S, V_cod V_dom)
292292
@check_space(Vᴴ, V_dom domain(t))
293293

294294
return nothing
295295
end
296296
function MAK.initialize_output(::typeof(svd_full!), t::AbstractBlockTensorMap, ::AbstractAlgorithm)
297-
V_cod = TK.oplus(fuse(codomain(t)))
298-
V_dom = TK.oplus(fuse(domain(t)))
297+
V_cod = (fuse(codomain(t)))
298+
V_dom = (fuse(domain(t)))
299299
U = dense_similar(t, codomain(t) V_cod)
300300
S = similar(t, real(scalartype(t)), V_cod V_dom)
301301
Vᴴ = dense_similar(t, V_dom domain(t))

src/vectorspaces/sumspace.jl

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,35 @@ const ProductSumSpace{S, N} = ProductSpace{SumSpace{S}, N}
2525
const TensorSumSpace{S} = TensorSpace{SumSpace{S}}
2626
const TensorMapSumSpace{S, N₁, N₂} = TensorMapSpace{SumSpace{S}, N₁, N₂}
2727

28+
# unicode name
29+
"""
30+
V1 ⊞ V2...
31+
boxplus(V1::ElementarySpace, V2::ElementarySpace...)
32+
33+
Create a lazy representation of the direct sum of the supplied vector spaces, which retains the order.
34+
See also [`SumSpace`](@ref).
35+
"""
36+
function end
37+
const boxplus =
38+
39+
(V₁::VectorSpace, V₂::VectorSpace) = (promote(V₁, V₂)...)
40+
(V::Vararg{VectorSpace}) = reduce(, V)
41+
42+
(V::ElementarySpace) = V isa SumSpace ? V : SumSpace(V)
43+
function (V₁::S V₂::S) where {S <: ElementarySpace}
44+
return if isdual(V₁) == isdual(V₂)
45+
SumSpace(V₁, V₂)
46+
else
47+
throw(SpaceMismatch("Direct sum of a vector space and its dual does not exist"))
48+
end
49+
end
50+
function (V₁::SumSpace{S} V₂::SumSpace{S}) where {S}
51+
V = SumSpace(vcat(V₁.spaces, V₂.spaces))
52+
allequal(isdual, V.spaces) ||
53+
throw(SpaceMismatch("Direct sum of a vector space and its dual does not exist"))
54+
return V
55+
end
56+
2857
# AbstractArray behavior
2958
# ----------------------
3059
Base.size(S::SumSpace) = size(S.spaces)
@@ -128,36 +157,12 @@ function Base.:(==)(V::TensorMapSumSpace{S}, W::TensorMapSumSpace{S}) where {S <
128157
end
129158

130159

131-
TensorKit.infimum(V::S, W::S) where {S <: SumSpace} = infimum(TensorKit.oplus(V), TensorKit.oplus(W))
132-
TensorKit.supremum(V::S, W::S) where {S <: SumSpace} = supremum(TensorKit.oplus(V), TensorKit.oplus(W))
133-
TensorKit.ominus(V::S, W::S) where {S <: SumSpace} = ominus(TensorKit.oplus(V), TensorKit.oplus(W))
134-
# this conflicts with the definition in TensorKit, so users always need to specify
135-
# ⊕(Vs::IndexSpace...) = SumSpace(Vs...)
136-
137-
function end
138-
(V₁::VectorSpace, V₂::VectorSpace) = (promote(V₁, V₂)...)
139-
(V::Vararg{VectorSpace}) = foldl(, V)
140-
const oplus =
141-
142-
(V::ElementarySpace) = V isa SumSpace ? V : SumSpace(V)
143-
function (V₁::S, V₂::S) where {S <: ElementarySpace}
144-
return if isdual(V₁) == isdual(V₂)
145-
SumSpace(V₁, V₂)
146-
else
147-
throw(SpaceMismatch("Direct sum of a vector space and its dual does not exist"))
148-
end
149-
end
150-
function (V₁::SumSpace{S}, V₂::SumSpace{S}) where {S}
151-
V = SumSpace(vcat(V₁.spaces, V₂.spaces))
152-
allequal(isdual, V.spaces) ||
153-
throw(SpaceMismatch("Direct sum of a vector space and its dual does not exist"))
154-
return V
155-
end
160+
TensorKit.infimum(V::S, W::S) where {S <: SumSpace} = infimum((V), (W))
161+
TensorKit.supremum(V::S, W::S) where {S <: SumSpace} = supremum((V), (W))
162+
TensorKit.ominus(V::S, W::S) where {S <: SumSpace} = ominus((V), (W))
156163

157-
#! format: off
158-
TensorKit.:(V::SumSpace{S}) where {S} = reduce(TK.oplus, V.spaces; init = isdual(V) ? zero(S)' : zero(S))
159-
TensorKit.:(V1::SumSpace{S}, V2::SumSpace{S}...) where {S} = TensorKit.oplus((V1, V2...))
160-
#! format: on
164+
TensorKit.oplus(V::SumSpace{S}) where {S} = reduce(, V.spaces; init = isdual(V) ? zero(S)' : zero(S))
165+
TensorKit.oplus(V1::SumSpace{S}, V2::SumSpace{S}...) where {S} = mapreduce(, , (V1, V2...))
161166

162167
function TensorKit.fuse(V1::S, V2::S) where {S <: SumSpace}
163168
return SumSpace(vec([fuse(v1, v2) for (v1, v2) in Base.product(V1.spaces, V2.spaces)]))
@@ -186,7 +191,7 @@ function Base.promote_rule(
186191
return TensorMapSumSpace{S}
187192
end
188193

189-
Base.convert(::Type{I}, S::SumSpace{I}) where {I <: ElementarySpace} = TensorKit.oplus(S)
194+
Base.convert(::Type{I}, S::SumSpace{I}) where {I <: ElementarySpace} = (S)
190195
Base.convert(::Type{SumSpace{S}}, V::S) where {S <: ElementarySpace} = SumSpace(V)
191196
function Base.convert(::Type{<:ProductSumSpace{S, N}}, V::ProductSpace{S, N}) where {S, N}
192197
return ProductSumSpace{S, N}(SumSpace.(V.spaces)...)
@@ -195,7 +200,7 @@ function Base.convert(::Type{<:ProductSumSpace{S}}, V::ProductSpace{S, N}) where
195200
return ProductSumSpace{S, N}(SumSpace.(V.spaces)...)
196201
end
197202
function Base.convert(::Type{<:ProductSpace{S, N}}, V::ProductSumSpace{S, N}) where {S, N}
198-
return ProductSpace{S, N}(TensorKit.oplus.(V.spaces)...)
203+
return ProductSpace{S, N}(map(, V.spaces)...)
199204
end
200205
function Base.convert(
201206
::Type{<:TensorMapSumSpace{S}}, V::TensorMapSpace{S, N₁, N₂}
@@ -216,7 +221,7 @@ end
216221
const SUMSPACE_SHOW_LIMIT = Ref(5)
217222
function Base.show(io::IO, V::SumSpace)
218223
if length(V) == 1
219-
print(io, "(")
224+
print(io, "(")
220225
show(io, V[1])
221226
print(io, ")")
222227
return nothing
@@ -227,11 +232,11 @@ function Base.show(io::IO, V::SumSpace)
227232
ax = axes(V.spaces, 1)
228233
f, l = first(ax), last(ax)
229234
h = SUMSPACE_SHOW_LIMIT[] ÷ 2
230-
Base.show_delim_array(io, V.spaces, "(", " ", "", false, f, f + h)
231-
print(io, " ")
232-
Base.show_delim_array(io, V.spaces, "", " ", ")", false, l - h, l)
235+
Base.show_delim_array(io, V.spaces, "(", " ", "", false, f, f + h)
236+
print(io, " ")
237+
Base.show_delim_array(io, V.spaces, "", " ", ")", false, l - h, l)
233238
else
234-
Base.show_delim_array(io, V.spaces, "(", " ", ")", false)
239+
Base.show_delim_array(io, V.spaces, "(", " ", ")", false)
235240
end
236241
return nothing
237242
end

src/vectorspaces/sumspaceindices.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ function Base._cat(
126126
allA = (A, As...)
127127
Vs = ntuple(N₁ + N₂) do i
128128
return if i <= length(catdims) && catdims[i]
129-
((allA[j].sumspaces[i] for j in 1:length(allA))...)
129+
((allA[j].sumspaces[i] for j in 1:length(allA))...)
130130
else
131131
A.sumspaces[i]
132132
end

test/abstracttensor/sparseblocktensor.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ end
123123
@test i1 * i2 == @constinferred(id(storagetype(t), V1 V2))
124124
@test i2 * i1 == @constinferred(id(storagetype(t), V2 V1))
125125

126-
w = @constinferred(isometry(storagetype(t), V1 (oneunit(V1) oneunit(V1)), V1))
126+
w = @constinferred(isometry(storagetype(t), V1 (oneunit(V1) oneunit(V1)), V1))
127127
@test dim(w) == 2 * dim(V1 V1)
128128
@test w' * w == id(storagetype(t), V1)
129129
@test w * w' == (w * w')^2

test/vectorspaces/sumspace.jl

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ using TensorKit, BlockTensorKit
66
using Test, TestExtras
77

88
using TensorKit: hassector
9-
using BlockTensorKit:
109

1110
ds = [2, 3, 2]
1211
d = sum(ds)
@@ -42,25 +41,24 @@ using TensorKit, BlockTensorKit
4241
@test @constinferred(axes(V)) == Base.OneTo(d)
4342
W = @constinferred SumSpace(ℝ^1)
4443
@test @constinferred(oneunit(V)) == W == oneunit(typeof(V))
45-
@test @constinferred((V, V)) == SumSpace(vcat(V.spaces, V.spaces))
46-
@test @constinferred((V, oneunit(V))) == SumSpace(vcat(V.spaces, ℝ^1))
47-
@test @constinferred((V, V, V, V)) == SumSpace(repeat(V.spaces, 4))
44+
@test @constinferred((V, V)) == SumSpace(vcat(V.spaces, V.spaces))
45+
@test @constinferred((V, oneunit(V))) == SumSpace(vcat(V.spaces, ℝ^1))
46+
@test @constinferred((V, V, V, V)) == SumSpace(repeat(V.spaces, 4))
4847
@test @constinferred(fuse(V, V)) SumSpace(ℝ^(d^2))
4948
@test @constinferred(fuse(V, V', V, V')) SumSpace(ℝ^(d^4))
5049
@test @constinferred(flip(V)) V'
5150
@test flip(V) V
5251
@test flip(V) V
5352
@test flip(V) V
54-
@test V (V, V)
55-
@test !(V (V, V))
53+
@test V (V, V)
54+
@test !(V (V, V))
5655
end
5756

5857
@testset "ComplexSpace" begin
5958
using TensorKit, BlockTensorKit
6059
using Test, TestExtras
6160

6261
using TensorKit: hassector
63-
using BlockTensorKit:
6462

6563
ds = [2, 3, 2]
6664
d = sum(ds)
@@ -90,25 +88,24 @@ end
9088
@test @constinferred(axes(V)) == Base.OneTo(d)
9189
W = @constinferred SumSpace(ℂ^1)
9290
@test @constinferred(oneunit(V)) == W == oneunit(typeof(V))
93-
@test @constinferred((V, V)) == SumSpace(vcat(V.spaces, V.spaces))
94-
@test @constinferred((V, oneunit(V))) == SumSpace(vcat(V.spaces, ℂ^1))
95-
@test @constinferred((V, V, V, V)) == SumSpace(repeat(V.spaces, 4))
91+
@test @constinferred((V, V)) == SumSpace(vcat(V.spaces, V.spaces))
92+
@test @constinferred((V, oneunit(V))) == SumSpace(vcat(V.spaces, ℂ^1))
93+
@test @constinferred((V, V, V, V)) == SumSpace(repeat(V.spaces, 4))
9694
@test @constinferred(fuse(V, V)) SumSpace(ℂ^(d^2))
9795
@test @constinferred(fuse(V, V', V, V')) SumSpace(ℂ^(d^4))
9896
@test @constinferred(flip(V)) V'
9997
@test flip(V) V
10098
@test flip(V) V
10199
@test flip(V) V
102-
@test V (V, V)
103-
@test !(V (V, V))
100+
@test V (V, V)
101+
@test !(V (V, V))
104102
end
105103

106104
@testset"GradedSpace" begin
107105
using TensorKit, BlockTensorKit
108106
using Test, TestExtras
109107

110108
using TensorKit: hassector
111-
using BlockTensorKit:
112109

113110
V1 = U1Space(0 => 1, 1 => 1)
114111
V2 = U1Space(0 => 1, 1 => 2)
@@ -143,16 +140,16 @@ end
143140
@test @constinferred(axes(V)) == Base.OneTo(d)
144141
W = @constinferred SumSpace(U1Space(0 => 1))
145142
@test @constinferred(oneunit(V)) == W == @constinferred(oneunit(typeof(V)))
146-
@test @constinferred((V, V)) == SumSpace(vcat(V.spaces, V.spaces))
147-
@test @constinferred((V, oneunit(V))) == SumSpace(vcat(V.spaces, oneunit(V1)))
148-
@test @constinferred((V, V, V, V)) == SumSpace(repeat(V.spaces, 4))
143+
@test @constinferred((V, V)) == SumSpace(vcat(V.spaces, V.spaces))
144+
@test @constinferred((V, oneunit(V))) == SumSpace(vcat(V.spaces, oneunit(V1)))
145+
@test @constinferred((V, V, V, V)) == SumSpace(repeat(V.spaces, 4))
149146
@test @constinferred(fuse(V, V)) SumSpace(U1Space(0 => 9, 1 => 24, 2 => 16))
150147
@test @constinferred(fuse(V, V', V, V'))
151148
SumSpace(U1Space(0 => 913, 1 => 600, -1 => 600, 2 => 144, -2 => 144))
152149
@test @constinferred(flip(V)) SumSpace(flip.(V.spaces)...)
153150
@test flip(V) V
154151
@test flip(V) V
155152
@test flip(V) V
156-
@test V (V, V)
157-
@test !(V (V, V))
153+
@test V (V, V)
154+
@test !(V (V, V))
158155
end

0 commit comments

Comments
 (0)