Skip to content

Commit ac926e3

Browse files
authored
Move code from GradedUnitRanges.jl, LabelledNumbers.jl, SymmetrySectors.jl (#1)
1 parent 5bb5e60 commit ac926e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+4156
-14
lines changed

Project.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,21 @@ uuid = "bc96ca6e-b7c8-4bb6-888e-c93f838762c2"
33
authors = ["ITensor developers <[email protected]> and contributors"]
44
version = "0.1.0"
55

6+
[deps]
7+
BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"
8+
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
9+
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
10+
HalfIntegers = "f0d1745a-41c9-11e9-1dd9-e5d34d218721"
11+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
12+
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
13+
TensorProducts = "decf83d6-1968-43f4-96dc-fdb3fe15fc6d"
14+
615
[compat]
16+
BlockArrays = "1.5.0"
17+
Compat = "4.16.0"
18+
FillArrays = "1.13.0"
19+
HalfIntegers = "1.6.0"
20+
Random = "1.10.0"
21+
SplitApplyCombine = "1.2.3"
22+
TensorProducts = "0.1.3"
723
julia = "1.10"

docs/src/reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Reference
22

33
```@autodocs
4-
Modules = [GradedArrays]
4+
Modules = [GradedArrays, GradedArrays.LabelledNumbers, GradedArrays.GradedUnitRanges, GradedArrays.SymmetrySectors]
55
```

src/GradedArrays.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
module GradedArrays
22

3-
# Write your package code here.
3+
include("lib/LabelledNumbers/LabelledNumbers.jl")
4+
using .LabelledNumbers: LabelledNumbers
5+
include("lib/GradedUnitRanges/GradedUnitRanges.jl")
6+
using .GradedUnitRanges: GradedUnitRanges
7+
include("lib/SymmetrySectors/SymmetrySectors.jl")
8+
using .SymmetrySectors: SymmetrySectors
49

510
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module GradedUnitRanges
2+
3+
export gradedrange
4+
5+
include("blockedunitrange.jl")
6+
include("gradedunitrange.jl")
7+
include("dual.jl")
8+
include("labelledunitrangedual.jl")
9+
include("gradedunitrangedual.jl")
10+
include("fusion.jl")
11+
12+
end
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
using BlockArrays:
2+
BlockArrays,
3+
AbstractBlockedUnitRange,
4+
AbstractBlockVector,
5+
Block,
6+
BlockIndex,
7+
BlockIndexRange,
8+
BlockRange,
9+
BlockSlice,
10+
BlockVector,
11+
block,
12+
blockindex,
13+
findblock,
14+
findblockindex,
15+
mortar
16+
17+
# Custom `BlockedUnitRange` constructor that takes a unit range
18+
# and a set of block lengths, similar to `BlockArray(::AbstractArray, blocklengths...)`.
19+
function blockedunitrange(a::AbstractUnitRange, blocklengths)
20+
blocklengths_shifted = copy(blocklengths)
21+
blocklengths_shifted[1] += (first(a) - 1)
22+
blocklasts = cumsum(blocklengths_shifted)
23+
return BlockArrays._BlockedUnitRange(first(a), blocklasts)
24+
end
25+
26+
# TODO: Move this to a `BlockArraysExtensions` library.
27+
# TODO: Rename this. `BlockArrays.findblock(a, k)` finds the
28+
# block of the value `k`, while this finds the block of the index `k`.
29+
# This could make use of the `BlockIndices` object, i.e. `block(BlockIndices(a)[index])`.
30+
function blockedunitrange_findblock(a::AbstractBlockedUnitRange, index::Integer)
31+
@boundscheck index in 1:length(a) || throw(BoundsError(a, index))
32+
return @inbounds findblock(a, index + first(a) - 1)
33+
end
34+
35+
# TODO: Move this to a `BlockArraysExtensions` library.
36+
# TODO: Rename this. `BlockArrays.findblockindex(a, k)` finds the
37+
# block index of the value `k`, while this finds the block index of the index `k`.
38+
# This could make use of the `BlockIndices` object, i.e. `BlockIndices(a)[index]`.
39+
function blockedunitrange_findblockindex(a::AbstractBlockedUnitRange, index::Integer)
40+
@boundscheck index in 1:length(a) || throw(BoundsError())
41+
return @inbounds findblockindex(a, index + first(a) - 1)
42+
end
43+
44+
function blockedunitrange_getindices(a::AbstractUnitRange, indices)
45+
return a[indices]
46+
end
47+
48+
# TODO: Move this to a `BlockArraysExtensions` library.
49+
# Like `a[indices]` but preserves block structure.
50+
# TODO: Consider calling this something else, for example
51+
# `blocked_getindex`. See the discussion here:
52+
# https://github.com/JuliaArrays/BlockArrays.jl/issues/347
53+
function blockedunitrange_getindices(
54+
a::AbstractBlockedUnitRange, indices::AbstractUnitRange{<:Integer}
55+
)
56+
first_blockindex = blockedunitrange_findblockindex(a, first(indices))
57+
last_blockindex = blockedunitrange_findblockindex(a, last(indices))
58+
first_block = block(first_blockindex)
59+
last_block = block(last_blockindex)
60+
blocklengths = if first_block == last_block
61+
[length(indices)]
62+
else
63+
map(first_block:last_block) do block
64+
if block == first_block
65+
return length(a[first_block]) - blockindex(first_blockindex) + 1
66+
end
67+
if block == last_block
68+
return blockindex(last_blockindex)
69+
end
70+
return length(a[block])
71+
end
72+
end
73+
return blockedunitrange(indices .+ (first(a) - 1), blocklengths)
74+
end
75+
76+
# TODO: Make sure this handles block labels (AbstractGradedUnitRange) correctly.
77+
# TODO: Make a special case for `BlockedVector{<:Block{1},<:BlockRange{1}}`?
78+
# For example:
79+
# ```julia
80+
# blocklengths = map(bs -> sum(b -> length(a[b]), bs), blocks(indices))
81+
# return blockedrange(blocklengths)
82+
# ```
83+
function blockedunitrange_getindices(
84+
a::AbstractBlockedUnitRange, indices::AbstractBlockVector{<:Block{1}}
85+
)
86+
blks = map(bs -> mortar(map(b -> a[b], bs)), blocks(indices))
87+
# We pass `length.(blks)` to `mortar` in order
88+
# to pass block labels to the axes of the output,
89+
# if they exist. This makes it so that
90+
# `only(axes(a[indices])) isa `GradedUnitRange`
91+
# if `a isa `GradedUnitRange`, for example.
92+
# Note there is a more specialized definition:
93+
# ```julia
94+
# function blockedunitrange_getindices(
95+
# a::AbstractGradedUnitRange, indices::AbstractBlockVector{<:Block{1}}
96+
# )
97+
# ```
98+
# that does a better job of preserving labels, since `length`
99+
# may drop labels for certain block types.
100+
return mortar(blks, length.(blks))
101+
end
102+
103+
# TODO: Move this to a `BlockArraysExtensions` library.
104+
function blockedunitrange_getindices(a::AbstractBlockedUnitRange, indices::BlockIndexRange)
105+
return a[block(indices)][only(indices.indices)]
106+
end
107+
108+
# TODO: Move this to a `BlockArraysExtensions` library.
109+
function blockedunitrange_getindices(a::AbstractBlockedUnitRange, indices::BlockSlice)
110+
# TODO: Is this a good definition? It ignores `indices.indices`.
111+
return a[indices.block]
112+
end
113+
114+
# TODO: Move this to a `BlockArraysExtensions` library.
115+
function blockedunitrange_getindices(
116+
a::AbstractBlockedUnitRange, indices::Vector{<:Integer}
117+
)
118+
return map(index -> a[index], indices)
119+
end
120+
121+
# TODO: Move this to a `BlockArraysExtensions` library.
122+
# TODO: Make a special definition for `BlockedVector{<:Block{1}}` in order
123+
# to merge blocks.
124+
function blockedunitrange_getindices(
125+
a::AbstractBlockedUnitRange, indices::AbstractVector{<:Union{Block{1},BlockIndexRange{1}}}
126+
)
127+
# Without converting `indices` to `Vector`,
128+
# mapping `indices` outputs a `BlockVector`
129+
# which is harder to reason about.
130+
blocks = map(index -> a[index], Vector(indices))
131+
# We pass `length.(blocks)` to `mortar` in order
132+
# to pass block labels to the axes of the output,
133+
# if they exist. This makes it so that
134+
# `only(axes(a[indices])) isa `GradedUnitRange`
135+
# if `a isa `GradedUnitRange`, for example.
136+
return mortar(blocks, length.(blocks))
137+
end
138+
139+
# TODO: Move this to a `BlockArraysExtensions` library.
140+
function blockedunitrange_getindices(a::AbstractBlockedUnitRange, indices::Block{1})
141+
return a[indices]
142+
end
143+
144+
function blockedunitrange_getindices(
145+
a::AbstractBlockedUnitRange,
146+
indices::BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}},
147+
)
148+
return mortar(map(b -> a[b], blocks(indices)))
149+
end
150+
151+
# TODO: Move this to a `BlockArraysExtensions` library.
152+
function blockedunitrange_getindices(a::AbstractBlockedUnitRange, indices)
153+
return error("Not implemented.")
154+
end
155+
156+
# The blocks of the corresponding slice.
157+
_blocks(a::AbstractUnitRange, indices) = error("Not implemented")
158+
function _blocks(a::AbstractUnitRange, indices::AbstractUnitRange)
159+
return findblock(a, first(indices)):findblock(a, last(indices))
160+
end
161+
function _blocks(a::AbstractUnitRange, indices::BlockRange)
162+
return indices
163+
end
164+
165+
# Slice `a` by `I`, returning a:
166+
# `BlockVector{<:BlockIndex{1},<:Vector{<:BlockIndexRange{1}}}`
167+
# with the `BlockIndex{1}` corresponding to each value of `I`.
168+
function to_blockindices(a::AbstractBlockedUnitRange{<:Integer}, I::UnitRange{<:Integer})
169+
return mortar(
170+
map(blocks(blockedunitrange_getindices(a, I))) do r
171+
bi_first = findblockindex(a, first(r))
172+
bi_last = findblockindex(a, last(r))
173+
@assert block(bi_first) == block(bi_last)
174+
return block(bi_first)[blockindex(bi_first):blockindex(bi_last)]
175+
end,
176+
)
177+
end
178+
179+
# This handles non-blocked slices.
180+
# For example:
181+
# a = BlockSparseArray{Float64}([2, 2, 2, 2])
182+
# I = BlockedVector(Block.(1:4), [2, 2])
183+
# @views a[I][Block(1)]
184+
to_blockindices(a::Base.OneTo{<:Integer}, I::UnitRange{<:Integer}) = I

src/lib/GradedUnitRanges/dual.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using ..LabelledNumbers: LabelledInteger, label, labelled, unlabel
2+
3+
"""
4+
dual(x)
5+
6+
Take the dual of the symmetry sector, graded unit range, etc.
7+
By default, it just returns `x`, i.e. it assumes the object
8+
is self-dual.
9+
"""
10+
dual(x) = x
11+
12+
nondual(r::AbstractUnitRange) = r
13+
isdual(::AbstractUnitRange) = false
14+
15+
dual_type(x) = dual_type(typeof(x))
16+
dual_type(T::Type) = T
17+
nondual_type(x) = nondual_type(typeof(x))
18+
nondual_type(T::Type) = T
19+
20+
dual(i::LabelledInteger) = labelled(unlabel(i), dual(label(i)))
21+
flip(a::AbstractUnitRange) = dual(map_blocklabels(dual, a))
22+
23+
"""
24+
dag(r::AbstractUnitRange)
25+
26+
Same as `dual(r)`.
27+
"""
28+
dag(r::AbstractUnitRange) = dual(r)
29+
30+
"""
31+
dag(a::AbstractArray)
32+
33+
Complex conjugates `a` and takes the dual of the axes.
34+
"""
35+
function dag(a::AbstractArray)
36+
a′ = similar(a, dual.(axes(a)))
37+
a′ .= conj.(a)
38+
return a′
39+
end

src/lib/GradedUnitRanges/fusion.jl

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using BlockArrays: AbstractBlockedUnitRange, blocklengths
2+
using ..LabelledNumbers: LabelledInteger, label, labelled
3+
using SplitApplyCombine: groupcount
4+
using TensorProducts: TensorProducts, OneToOne, tensor_product
5+
6+
flip_dual(r::AbstractUnitRange) = r
7+
flip_dual(r::GradedUnitRangeDual) = flip(r)
8+
9+
function fuse_labels(x, y)
10+
return error(
11+
"`fuse_labels` not implemented for object of type `$(typeof(x))` and `$(typeof(y))`."
12+
)
13+
end
14+
15+
function fuse_blocklengths(x::LabelledInteger, y::LabelledInteger)
16+
# return blocked unit range to keep non-abelian interface
17+
return blockedrange([labelled(x * y, fuse_labels(label(x), label(y)))])
18+
end
19+
20+
unmerged_tensor_product() = OneToOne()
21+
unmerged_tensor_product(a) = a
22+
unmerged_tensor_product(a, ::OneToOne) = a
23+
unmerged_tensor_product(::OneToOne, a) = a
24+
unmerged_tensor_product(::OneToOne, ::OneToOne) = OneToOne()
25+
unmerged_tensor_product(a1, a2) = tensor_product(a1, a2)
26+
function unmerged_tensor_product(a1, a2, as...)
27+
return unmerged_tensor_product(unmerged_tensor_product(a1, a2), as...)
28+
end
29+
30+
function unmerged_tensor_product(a1::AbstractGradedUnitRange, a2::AbstractGradedUnitRange)
31+
nested = map(Iterators.flatten((Iterators.product(blocks(a1), blocks(a2)),))) do it
32+
return mapreduce(length, fuse_blocklengths, it)
33+
end
34+
new_blocklengths = mapreduce(blocklengths, vcat, nested)
35+
return blockedrange(new_blocklengths)
36+
end
37+
38+
# convention: sort GradedUnitRangeDual according to nondual blocks
39+
function blocksortperm(a::AbstractUnitRange)
40+
return Block.(sortperm(blocklabels(nondual(a))))
41+
end
42+
43+
# Get the permutation for sorting, then group by common elements.
44+
# groupsortperm([2, 1, 2, 3]) == [[2], [1, 3], [4]]
45+
function groupsortperm(v; kwargs...)
46+
perm = sortperm(v; kwargs...)
47+
v_sorted = @view v[perm]
48+
group_lengths = collect(groupcount(identity, v_sorted))
49+
return BlockVector(perm, group_lengths)
50+
end
51+
52+
# Used by `TensorAlgebra.splitdims` in `BlockSparseArraysGradedUnitRangesExt`.
53+
# Get the permutation for sorting, then group by common elements.
54+
# groupsortperm([2, 1, 2, 3]) == [[2], [1, 3], [4]]
55+
function blockmergesortperm(a::AbstractUnitRange)
56+
return Block.(groupsortperm(blocklabels(nondual(a))))
57+
end
58+
59+
# Used by `TensorAlgebra.splitdims` in `BlockSparseArraysGradedUnitRangesExt`.
60+
invblockperm(a::Vector{<:Block{1}}) = Block.(invperm(Int.(a)))
61+
62+
function blockmergesort(g::AbstractGradedUnitRange)
63+
glabels = blocklabels(g)
64+
gblocklengths = blocklengths(g)
65+
new_blocklengths = map(sort(unique(glabels))) do la
66+
return labelled(sum(gblocklengths[findall(==(la), glabels)]; init=0), la)
67+
end
68+
return gradedrange(new_blocklengths)
69+
end
70+
71+
blockmergesort(g::GradedUnitRangeDual) = flip(blockmergesort(flip(g)))
72+
blockmergesort(g::AbstractUnitRange) = g
73+
74+
# tensor_product produces a sorted, non-dual GradedUnitRange
75+
TensorProducts.tensor_product(g::AbstractGradedUnitRange) = blockmergesort(flip_dual(g))
76+
77+
function TensorProducts.tensor_product(
78+
g1::AbstractGradedUnitRange, g2::AbstractGradedUnitRange
79+
)
80+
return blockmergesort(unmerged_tensor_product(g1, g2))
81+
end

0 commit comments

Comments
 (0)