Skip to content

Commit 16972b5

Browse files
authored
Rework named-type parameters around a single Named scalar (#173)
## Summary Reworks the type parameters of the named array and scalar types so the name leads and every parameter is inferable from the fields, and collapses the named-integer type hierarchy into a single `Named` scalar. The named types now read `Named{Name, Denamed}` (the scalar), `AbstractNamedArray{Name, DenamedT, N} <: AbstractArray{Named{Name, DenamedT}, N}`, and `AbstractNamedUnitRange{Name, DenamedT<:Integer}`, with `Name` leading to match `AbstractITensor{DimName}` and the element type derived as `Named{Name, DenamedT}` rather than carried as a separate parameter. The concrete `NamedArray`, `NamedUnitRange`, and `Index` infer all of their parameters from their `value` and `name` fields. A single `Named` wrapper replaces `AbstractNamedInteger`, the `NamedInteger` struct, `IndexVal`, and `NoncontiguousIndex`: a named integer is `Named{Name, <:Integer}`, an index value is `Named{IndexName, <:Integer}`, and a non-contiguous index is a `NamedArray` with an `IndexName` name. `NamedInteger` stays available as a parametric alias, `const NamedInteger{Name, Denamed<:Integer} = Named{Name, Denamed}`. Because the wrapper is universal, a `NamedArray` now works for any element type, where a non-integer `NamedArray` errored before. Equality and hashing across named array types are type-agnostic, following Base's array convention (`[1, 2, 3] == 1:3` and they hash equally): one generic `hash(::AbstractNamedArray)` uses a single shared tag rather than the concrete type, so `a == b` implies `hash(a) == hash(b)`. Breaking: the type-parameter order and shape change, and the `AbstractNamedInteger`, `NamedInteger` struct, `IndexVal`, and `NoncontiguousIndex` names are removed.
1 parent 7bc5ad8 commit 16972b5

13 files changed

Lines changed: 255 additions & 269 deletions

ext/ITensorBaseBlockArraysExt/ITensorBaseBlockArraysExt.jl

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
module ITensorBaseBlockArraysExt
22
using ArrayLayouts: ArrayLayouts
33
using BlockArrays: Block, BlockRange
4-
using ITensorBase: AbstractITensor, AbstractNamedInteger, AbstractNamedUnitRange,
5-
getindex_named, view_nameddims
6-
7-
# The first parameter of `AbstractNamedUnitRange` is its element type, an
8-
# `AbstractNamedInteger`, which is not an `Integer`. These methods disambiguate
9-
# named-range block indexing from `BlockArrays`' generic `AbstractArray`
10-
# block-indexing methods.
11-
function Base.getindex(r::AbstractNamedUnitRange{<:AbstractNamedInteger}, I::Block{1})
4+
using ITensorBase: AbstractITensor, AbstractNamedUnitRange, getindex_named, view_nameddims
5+
6+
# These methods disambiguate named-range block indexing from `BlockArrays`' generic
7+
# `AbstractArray` block-indexing methods.
8+
function Base.getindex(r::AbstractNamedUnitRange, I::Block{1})
129
# TODO: Use `Derive.@interface NamedArrayInterface() r[I]` instead.
1310
return getindex_named(r, I)
1411
end
1512

16-
function Base.getindex(r::AbstractNamedUnitRange{<:AbstractNamedInteger}, I::BlockRange{1})
13+
function Base.getindex(r::AbstractNamedUnitRange, I::BlockRange{1})
1714
# TODO: Use `Derive.@interface NamedArrayInterface() r[I]` instead.
1815
return getindex_named(r, I)
1916
end

src/ITensorBase.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ using Compat: @compat
99
# Named-array machinery (relocated from NamedDimsArrays.jl).
1010
include("isnamed.jl")
1111
include("randname.jl")
12-
include("abstractnamedinteger.jl")
13-
include("namedinteger.jl")
12+
include("name.jl")
13+
include("named.jl")
1414
include("abstractnamedarray.jl")
1515
include("namedarray.jl")
1616
include("abstractnamedunitrange.jl")

src/abstractitensor.jl

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,11 @@ Base.IndexStyle(s1::IndexStyle, s2::NamedIndexCartesian) = NamedIndexCartesian()
373373
Base.IndexStyle(s1::NamedIndexCartesian, s2::IndexStyle) = NamedIndexCartesian()
374374

375375
# Like CartesianIndex but with named dimensions.
376-
struct NamedDimsCartesianIndex{N, Index <: Tuple{Vararg{AbstractNamedInteger, N}}} <:
376+
struct NamedDimsCartesianIndex{N, Index <: Tuple{Vararg{NamedInteger, N}}} <:
377377
Base.AbstractCartesianIndex{N}
378378
I::Index
379379
end
380-
NamedDimsCartesianIndex(I::AbstractNamedInteger...) = NamedDimsCartesianIndex(I)
380+
NamedDimsCartesianIndex(I::NamedInteger...) = NamedDimsCartesianIndex(I)
381381
Base.Tuple(I::NamedDimsCartesianIndex) = I.I
382382
function Base.show(io::IO, I::NamedDimsCartesianIndex)
383383
print(io, "NamedDimsCartesianIndex")
@@ -390,7 +390,7 @@ struct NamedDimsCartesianIndices{
390390
N,
391391
DimName,
392392
Indices <: Tuple{Vararg{AbstractNamedUnitRange, N}},
393-
Index <: Tuple{Vararg{AbstractNamedInteger, N}},
393+
Index <: Tuple{Vararg{NamedInteger, N}},
394394
} <: AbstractITensor{DimName}
395395
indices::Indices
396396
function NamedDimsCartesianIndices(indices::Tuple{Vararg{AbstractNamedUnitRange}})
@@ -511,7 +511,7 @@ function Base.checkbounds(::Type{Bool}, a::AbstractITensor, I::Int...)
511511
end
512512

513513
function Base.to_indices(
514-
a::AbstractITensor, I::Tuple{AbstractNamedInteger, Vararg{AbstractNamedInteger}}
514+
a::AbstractITensor, I::Tuple{NamedInteger, Vararg{NamedInteger}}
515515
)
516516
perm = getperm(name.(I), dimnames(a))
517517
# TODO: Throw a `NameMismatch` error.
@@ -545,7 +545,7 @@ function Base.getindex(a::AbstractITensor, I1::Int, Irest::Int...)
545545
return getindex(denamed(a), I1, Irest...)
546546
end
547547
function Base.getindex(
548-
a::AbstractITensor, I1::AbstractNamedInteger, Irest::AbstractNamedInteger...
548+
a::AbstractITensor, I1::NamedInteger, Irest::NamedInteger...
549549
)
550550
return getindex(a, to_indices(a, (I1, Irest...))...)
551551
end
@@ -567,8 +567,8 @@ function Base.setindex!(a::AbstractITensor, value, I::CartesianIndex)
567567
end
568568

569569
function Base.setindex!(
570-
a::AbstractITensor, value, I1::AbstractNamedInteger,
571-
Irest::AbstractNamedInteger...
570+
a::AbstractITensor, value, I1::NamedInteger,
571+
Irest::NamedInteger...
572572
)
573573
setindex!(a, value, to_indices(a, (I1, Irest...))...)
574574
return a
@@ -599,7 +599,7 @@ end
599599

600600
# Like `const ViewIndex = Union{Real,AbstractArray}`.
601601
const NamedViewIndex =
602-
Union{AbstractNamedInteger, AbstractNamedUnitRange, AbstractNamedArray}
602+
Union{NamedInteger, AbstractNamedUnitRange, AbstractNamedArray}
603603

604604
using ArrayLayouts: ArrayLayouts, MemoryLayout
605605

@@ -625,10 +625,10 @@ end
625625
function Base.getindex(a::AbstractArray, I1::NamedViewIndex, Irest::NamedViewIndex...)
626626
return copy(view(a, I1, Irest...))
627627
end
628-
# Disambiguate from `Base.getindex(A::Array, I::AbstractUnitRange{<:Integer})`.
629-
# The element type of a named unit range is an `AbstractNamedInteger`, which is not
630-
# an `Integer`.
631-
function Base.getindex(a::Array, I1::AbstractNamedUnitRange{<:AbstractNamedInteger})
628+
# A named unit range is an `AbstractArray`, so for a concrete `Array` the Base
629+
# `getindex(::Array, ::AbstractVector)` method would otherwise win over the generic
630+
# named `getindex` above. This restores the named behavior for `Array`.
631+
function Base.getindex(a::Array, I1::AbstractNamedUnitRange)
632632
return copy(view(a, I1))
633633
end
634634
function Base.view(a::AbstractArray, I1::NamedViewIndex, Irest::NamedViewIndex...)
@@ -803,12 +803,12 @@ for (f, f′) in [(:rand, :_rand), (:randn, :_randn)]
803803
function Base.$f(
804804
rng::AbstractRNG,
805805
elt::Type{<:Number},
806-
dims::Tuple{AbstractNamedInteger, Vararg{AbstractNamedInteger}}
806+
dims::Tuple{NamedInteger, Vararg{NamedInteger}}
807807
)
808808
return $f(rng, elt, Base.oneto.(dims))
809809
end
810810
end
811-
for dimtype in [:AbstractNamedInteger, :AbstractNamedUnitRange]
811+
for dimtype in [:NamedInteger, :AbstractNamedUnitRange]
812812
@eval begin
813813
function Base.$f(
814814
rng::AbstractRNG, elt::Type{<:Number}, dim1::$dimtype,
@@ -827,7 +827,7 @@ for (f, f′) in [(:rand, :_rand), (:randn, :_randn)]
827827
end
828828
end
829829
end
830-
for f in [:zeros, :ones], dimtype in [:AbstractNamedInteger, :AbstractNamedUnitRange]
830+
for f in [:zeros, :ones], dimtype in [:NamedInteger, :AbstractNamedUnitRange]
831831
@eval begin
832832
function Base.$f(
833833
elt::Type{<:Number}, ax::Tuple{$dimtype, Vararg{$dimtype}}
@@ -842,7 +842,7 @@ for f in [:zeros, :ones], dimtype in [:AbstractNamedInteger, :AbstractNamedUnitR
842842
Base.$f(dim1::$dimtype, dims::Vararg{$dimtype}) = $f((dim1, dims...))
843843
end
844844
end
845-
for dimtype in [:AbstractNamedInteger, :AbstractNamedUnitRange]
845+
for dimtype in [:NamedInteger, :AbstractNamedUnitRange]
846846
@eval begin
847847
function Base.fill(value, ax::Tuple{$dimtype, Vararg{$dimtype}})
848848
a = fill(value, denamed.(ax))

src/abstractnamedarray.jl

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
using TypeParameterAccessors: unspecify_type_parameters
1+
# `Name` leads (matching `AbstractITensor{DimName}`); `DenamedT` is the unwrapped
2+
# element type and `N` the rank. The element type is always `Named{Name, DenamedT}`,
3+
# so it is hardcoded in the `AbstractArray` supertype rather than carried as a
4+
# parameter. The wrapped-container type lives only on the concrete subtypes.
5+
abstract type AbstractNamedArray{Name, DenamedT, N} <:
6+
AbstractArray{Named{Name, DenamedT}, N} end
27

3-
abstract type AbstractNamedArray{T, N, Value <: AbstractArray, Name} <: AbstractArray{T, N} end
4-
5-
const AbstractNamedVector{T, Value <: AbstractVector, Name} =
6-
AbstractNamedArray{T, 1, Value, Name}
7-
const AbstractNamedMatrix{T, Value <: AbstractVector, Name} =
8-
AbstractNamedArray{T, 2, Value, Name}
8+
const AbstractNamedVector{Name, DenamedT} = AbstractNamedArray{Name, DenamedT, 1}
9+
const AbstractNamedMatrix{Name, DenamedT} = AbstractNamedArray{Name, DenamedT, 2}
910

1011
# Minimal interface.
1112
denamed(a::AbstractNamedArray) = throw(MethodError(denamed, Tuple{typeof(a)}))
1213
name(a::AbstractNamedArray) = throw(MethodError(name, Tuple{typeof(a)}))
1314

14-
# This can be customized to output different named integer types,
15+
# This can be customized to output different named array types,
1516
# such as `namedarray(a::AbstractArray, name::IndexName) = Index(a, name)`.
1617
namedarray(a::AbstractArray, name) = NamedArray(a, name)
1718

@@ -22,31 +23,32 @@ named(a::AbstractArray, name) = namedarray(a, name)
2223
# TODO: Use `Accessors.@set`?
2324
setname(a::AbstractNamedArray, name) = namedarray(denamed(a), name)
2425

25-
# TODO: Use `TypeParameterAccessors`.
26-
denamedtype(::Type{<:AbstractNamedArray{<:Any, <:Any, Value}}) where {Value} = Value
27-
nametype(::Type{<:AbstractNamedArray{<:Any, <:Any, <:Any, Name}}) where {Name} = Name
26+
# `Name` leads, so `nametype` reads it from the abstract type. The wrapped
27+
# container type lives only on the concrete subtypes, so `denamedtype` is defined
28+
# per concrete type rather than here.
29+
nametype(::Type{<:AbstractNamedArray{Name}}) where {Name} = Name
2830

2931
# Traits.
3032
isnamed(::Type{<:AbstractNamedArray}) = true
3133

32-
# TODO: Should they also have the same base type?
34+
# Equality and hashing are type-agnostic across named array types, following Base's
35+
# array convention (`[1, 2, 3] == 1:3`, and they hash equally): two named arrays are
36+
# equal when their names and denamed values are equal, regardless of concrete type.
37+
# Hashing uses a single shared tag (not the concrete type) so that
38+
# `a == b => hash(a) == hash(b)` holds; there are no external subtypes that need to
39+
# override this.
3340
function Base.:(==)(a1::AbstractNamedArray, a2::AbstractNamedArray)
3441
return name(a1) == name(a2) && denamed(a1) == denamed(a2)
3542
end
36-
function Base.hash(a::AbstractNamedArray, h::UInt)
37-
h = hash(Symbol(unspecify_type_parameters(typeof(a))), h)
38-
# TODO: Double check how this is handling blocking/sector information.
39-
h = hash(denamed(a), h)
40-
return hash(name(a), h)
41-
end
43+
Base.hash(a::AbstractNamedArray, h::UInt) = hash_named(:NamedArray, a, h)
4244

4345
getindex_named(a::AbstractArray, I...) = named(getindex(denamed(a), I...), name(a))
4446

4547
# Array funcionality.
4648
Base.size(a::AbstractNamedArray) = map(s -> named(s, name(a)), size(denamed(a)))
4749
Base.axes(a::AbstractNamedArray) = map(s -> named(s, name(a)), axes(denamed(a)))
4850
Base.eachindex(a::AbstractNamedArray) = eachindex(denamed(a))
49-
function Base.getindex(a::AbstractNamedArray{<:Any, N}, I::Vararg{Int, N}) where {N}
51+
function Base.getindex(a::AbstractNamedArray{<:Any, <:Any, N}, I::Vararg{Int, N}) where {N}
5052
return getindex_named(a, I...)
5153
end
5254
function Base.getindex(a::AbstractNamedArray, I::Int)

src/abstractnamedinteger.jl

Lines changed: 0 additions & 168 deletions
This file was deleted.

0 commit comments

Comments
 (0)