Skip to content

Commit 5b0ed3a

Browse files
mtfishmanclaude
andauthored
Add Data{N} type, undef constructors, and data/block indexing (#150)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5f2e234 commit 5b0ed3a

15 files changed

Lines changed: 402 additions & 119 deletions

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "GradedArrays"
22
uuid = "bc96ca6e-b7c8-4bb6-888e-c93f838762c2"
3-
version = "0.8.1"
3+
version = "0.8.2"
44
authors = ["ITensor developers <support@itensor.org> and contributors"]
55

66
[workspace]

src/GradedArrays.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,22 @@ export AbstractSectorArray,
1111
export AbstractGradedArray, AbstractGradedMatrix
1212
export AbelianGradedArray, AbelianGradedVector, AbelianGradedMatrix
1313
export FusedGradedMatrix
14-
export gradedrange
1514

1615
export dual, flip, gradedrange, isdual,
1716
data, dataaxes, dataaxes1, datalength, datalengths,
17+
eachdataaxis, eachsectoraxis,
1818
sector, sectoraxes, sectoraxes1, sectorlength, sectorlengths,
19-
sectors, sectortype
19+
sectors, sectortype,
20+
Data
2021

2122
# imports
2223
# -------
2324
import FunctionImplementations as FI
2425
using BlockArrays: BlockArrays, AbstractBlockArray, AbstractBlockVector, Block,
25-
BlockIndexRange, BlockVector, blocklength, blocklengths, blocks, eachblockaxes1
26-
using BlockSparseArrays: BlockSparseArrays, eachblockaxis, eachblockstoredindex, mortar_axis
26+
BlockIndexRange, BlockVector, BlockedOneTo, blockedrange, blocklength, blocklengths,
27+
blocks, eachblockaxes1
28+
using BlockSparseArrays:
29+
BlockSparseArrays, blockdiagindices, eachblockaxis, eachblockstoredindex, mortar_axis
2730
using KroneckerArrays: KroneckerArrays, kroneckerfactors, ×,
2831
using LinearAlgebra: LinearAlgebra, Adjoint, mul!
2932
using TensorAlgebra: TensorAlgebra, BlockedTuple, FusionStyle, matricize, matricize_axes,
@@ -33,6 +36,7 @@ using TensorKitSectors: TensorKitSectors as TKS
3336
using TypeParameterAccessors: type_parameters, unspecify_type_parameters
3437

3538
include("sectorrange.jl")
39+
include("data.jl")
3640
include("sectoroneto.jl")
3741
include("gradedoneto.jl")
3842
include("abstractsectordelta.jl")

src/abeliangradedarray.jl

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ end
2727
function AbelianGradedArray{T, N, D, S}(
2828
::UndefInitializer, axs::NTuple{N, GradedOneTo{S}}
2929
) where {T, N, D <: AbstractArray{T, N}, S <: SectorRange}
30+
block_axes = map(eachdataaxis, axs)
31+
function allocate_block(bk)
32+
bk_inds = Int.(Tuple(bk))
33+
return similar(D, ntuple(d -> block_axes[d][bk_inds[d]], Val(N)))
34+
end
3035
bks = allowedblocks(axs)
3136
blockdata = Dict{NTuple{N, Int}, D}(
32-
Int.(Tuple(bk)) =>
33-
D(undef, ntuple(d -> blocklengths(axs[d])[Int(Tuple(bk)[d])], Val(N)))
34-
for bk in bks
37+
Int.(Tuple(bk)) => allocate_block(bk) for bk in bks
3538
)
3639
return AbelianGradedArray{T, N, D, S}(blockdata, axs)
3740
end
@@ -66,16 +69,12 @@ BlockSparseArrays.blocktype(a::AbelianGradedArray) = BlockSparseArrays.blocktype
6669
# view (primitive): returns AbelianSectorArray sharing data with blockdata
6770
# ---------------------------------------------------------------------------
6871

69-
# view: returns a AbelianSectorArray sharing data (errors for unstored blocks)
70-
function Base.view(a::AbelianGradedArray{T, N}, I::Vararg{Block{1}, N}) where {T, N}
71-
bk = ntuple(d -> Int(I[d]), Val(N))
72+
function Base.view(a::AbelianGradedArray{T, N}, I::Block{N}) where {T, N}
73+
bk = Int.(Tuple(I))
7274
haskey(a.blockdata, bk) || error("Block $bk is not stored.")
7375
sects = ntuple(d -> sectors(axes(a, d))[bk[d]], Val(N))
7476
return AbelianSectorArray(sects, a.blockdata[bk])
7577
end
76-
function Base.view(a::AbelianGradedArray{T, N}, I::Block{N}) where {T, N}
77-
return view(a, Tuple(I)...)
78-
end
7978

8079
# ---------------------------------------------------------------------------
8180
# blocks — lazy view delegating to view (following BlockArrays convention)
@@ -108,32 +107,6 @@ function Base.setindex!(
108107
return b
109108
end
110109

111-
# ---------------------------------------------------------------------------
112-
# getindex / setindex! on AbelianGradedArray with Block — convenience wrappers
113-
# ---------------------------------------------------------------------------
114-
115-
# getindex: returns a copy (errors for unstored blocks)
116-
function Base.getindex(a::AbelianGradedArray{T, N}, I::Vararg{Block{1}, N}) where {T, N}
117-
return copy(view(a, I...))
118-
end
119-
function Base.getindex(a::AbelianGradedArray{T, N}, I::Block{N}) where {T, N}
120-
return a[Tuple(I)...]
121-
end
122-
123-
# setindex!: Block{N} unpacks to Vararg{Block{1}, N} (following BlockArrays convention)
124-
function Base.setindex!(a::AbelianGradedArray{<:Any, N}, value, I::Block{N}) where {N}
125-
return setindex!(a, value, Tuple(I)...)
126-
end
127-
128-
# Primitive: view existing block, then broadcast in.
129-
# Handles both AbelianSectorArray and raw data values.
130-
function Base.setindex!(
131-
a::AbelianGradedArray{<:Any, N}, value::AbstractArray{<:Any, N}, I::Vararg{Block{1}, N}
132-
) where {N}
133-
view(a, I...) .= value
134-
return a
135-
end
136-
137110
# ---------------------------------------------------------------------------
138111
# Splitting getindex: each I[d][k] = Block(b)[r] means dest block k comes
139112
# from source block b at subrange r. Inverse of the merging getindex.

src/abeliansectorarray.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ function AbelianSectorArray(
3636
) where {N}
3737
return AbelianSectorArray(delta.sectors, data)
3838
end
39+
function AbelianSectorArray{T, N, A, S}(
40+
delta::AbelianSectorDelta{<:Any, N, S},
41+
data::A
42+
) where {T, N, A <: AbstractArray{T, N}, S <: SectorRange}
43+
return AbelianSectorArray{T, N, A, S}(delta.sectors, data)
44+
end
3945

4046
const AbelianSectorVector{T, A <: AbstractVector{T}, S <: SectorRange} =
4147
AbelianSectorArray{T, 1, A, S}
@@ -55,23 +61,19 @@ datatype(::Type{AbelianSectorArray{T, N, A, S}}) where {T, N, A, S} = A
5561
# AbstractArray interface
5662
# -----------------------
5763
function Base.axes(sa::AbelianSectorArray)
58-
sa_axes = sectoraxes(sa)
59-
da_axes = dataaxes(sa)
60-
return ntuple(d -> SectorOneTo(sa_axes[d], length(da_axes[d])), Val(ndims(sa)))
64+
return map(SectorOneTo, sectoraxes(sa), dataaxes(sa))
6165
end
6266

6367
Base.copy(A::AbelianSectorArray) = AbelianSectorArray(sector(A), copy(data(A)))
6468

6569
# similar for AbelianSectorArray with SectorOneTo axes.
6670
# Delegates to similar on the data array for the data dimensions.
6771
function Base.similar(
68-
a::AbelianSectorArray,
72+
::AbelianSectorArray,
6973
::Type{T},
7074
axes::Tuple{SectorOneTo, Vararg{SectorOneTo}}
7175
) where {T}
72-
sects = sector.(axes)
73-
data_ax = data.(axes)
74-
return AbelianSectorArray(sects, similar(data(a), T, data_ax))
76+
return AbelianSectorArray{T}(undef, axes)
7577
end
7678

7779
function Base.fill!(A::AbelianSectorArray, v)
@@ -88,7 +90,7 @@ function Base.convert(
8890
x::AbelianSectorArray{T₂, N, B, S}
8991
)::AbelianSectorArray{T₁, N, A, S} where {T₁, T₂, N, A, B, S}
9092
A === B && return x
91-
return AbelianSectorArray(sector(x), convert(A, data(x)))
93+
return AbelianSectorArray{T₁, N, A, S}(sector(x), convert(A, data(x)))
9294
end
9395

9496
# ======================== permutedims ========================

src/abstractgradedarray.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,54 @@ function Base.setindex!(::AbstractGradedArray, _, ::Vararg{Int})
2626
"Scalar indexing is not supported for AbstractGradedArray. Use block indexing."
2727
)
2828
end
29+
30+
# ---------------------------------------------------------------------------
31+
# Block indexing interface
32+
#
33+
# Concrete subtypes must implement:
34+
# view(a::ConcreteType, ::Block{N}) → sector-wrapped view (e.g. SectorMatrix)
35+
#
36+
# Everything else is derived here.
37+
# ---------------------------------------------------------------------------
38+
39+
function Base.view(a::AbstractGradedArray{T, N}, I::Vararg{Block{1}, N}) where {T, N}
40+
return view(a, Block(Int.(I)))
41+
end
42+
43+
function Base.getindex(a::AbstractGradedArray{T, N}, I::Block{N}) where {T, N}
44+
return copy(view(a, I))
45+
end
46+
function Base.getindex(a::AbstractGradedArray{T, N}, I::Vararg{Block{1}, N}) where {T, N}
47+
return a[Block(Int.(I))]
48+
end
49+
50+
function Base.setindex!(a::AbstractGradedArray{<:Any, N}, value, I::Block{N}) where {N}
51+
return setindex!(a, value, Tuple(I)...)
52+
end
53+
function Base.setindex!(
54+
a::AbstractGradedArray{<:Any, N}, value, I::Vararg{Block{1}, N}
55+
) where {N}
56+
view(a, I...) .= value
57+
return a
58+
end
59+
60+
# ---------------------------------------------------------------------------
61+
# Data indexing — raw block data without sector wrappers
62+
#
63+
# Built on top of Block view: view(a, Data(I)) = data(view(a, Block(I)))
64+
# ---------------------------------------------------------------------------
65+
66+
function Base.view(a::AbstractGradedArray{T, N}, I::Data{N}) where {T, N}
67+
return data(view(a, Block(I)))
68+
end
69+
70+
function Base.getindex(a::AbstractGradedArray{T, N}, I::Data{N}) where {T, N}
71+
return copy(view(a, I))
72+
end
73+
74+
function Base.setindex!(
75+
a::AbstractGradedArray{<:Any, N}, value::AbstractArray{<:Any, N}, I::Data{N}
76+
) where {N}
77+
view(a, I) .= value
78+
return a
79+
end

src/data.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
Data{N}
3+
4+
Block-data indexing type analogous to `BlockArrays.Block{N}`. Indexing a graded
5+
array with `Data(i, j, ...)` accesses the raw data array for that block, without
6+
sector metadata wrappers.
7+
"""
8+
struct Data{N}
9+
n::NTuple{N, Int}
10+
end
11+
Data(n::Vararg{Int, N}) where {N} = Data{N}(n)
12+
BlockArrays.Block(I::Data) = Block(I.n)
13+
Data(I::Block) = Data(Int.(Tuple(I)))
14+
Base.Tuple(I::Data) = I.n

0 commit comments

Comments
 (0)