Skip to content

Commit 7bc5ad8

Browse files
mtfishmanclaude
andauthored
Drop the AbstractNamedInteger <: Integer subtyping (#172)
## Summary Makes `AbstractNamedInteger` a standalone abstract type instead of `<: Integer`. A named integer is a tagged scalar, not a true integer: operations like `i1 * i2` fuse names rather than multiply cleanly, mixed named and unnamed arithmetic has no well-defined meaning, and inherited `Integer` fallbacks risk silently dropping the name. The type keeps the explicit integer-like surface it actually needs (equality, hashing, the arithmetic it supports, ordering, `show`). This also removes the named-to-plain-number conversion path (the `Number` constructor and `convert`) that let named and unnamed integers interoperate. That interoperation is what the standalone design deliberately drops, and a `convert` that silently discards the name is exactly the name loss being avoided. The one internal consumer, resolving `begin` and `end` to a plain index in `to_index`, now reads the underlying value with `denamed`, which is also how callers should get the plain integer from a named integer. The element-type constraints that disambiguate named-range indexing (the `BlockArrays` `getindex` methods and the plain-`Array` indexing method) change from `{<:Integer}` to `{<:AbstractNamedInteger}`, since a named unit range's element type is a named integer, which is not an `Integer`. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent fd324eb commit 7bc5ad8

7 files changed

Lines changed: 25 additions & 20 deletions

File tree

ext/ITensorBaseBlockArraysExt/ITensorBaseBlockArraysExt.jl

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
module ITensorBaseBlockArraysExt
22
using ArrayLayouts: ArrayLayouts
33
using BlockArrays: Block, BlockRange
4-
using ITensorBase: AbstractITensor, AbstractNamedUnitRange, getindex_named, view_nameddims
5-
6-
function Base.getindex(r::AbstractNamedUnitRange{<:Integer}, I::Block{1})
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})
712
# TODO: Use `Derive.@interface NamedArrayInterface() r[I]` instead.
813
return getindex_named(r, I)
914
end
1015

11-
function Base.getindex(r::AbstractNamedUnitRange{<:Integer}, I::BlockRange{1})
16+
function Base.getindex(r::AbstractNamedUnitRange{<:AbstractNamedInteger}, I::BlockRange{1})
1217
# TODO: Use `Derive.@interface NamedArrayInterface() r[I]` instead.
1318
return getindex_named(r, I)
1419
end

src/abstractitensor.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,9 @@ function Base.getindex(a::AbstractArray, I1::NamedViewIndex, Irest::NamedViewInd
626626
return copy(view(a, I1, Irest...))
627627
end
628628
# Disambiguate from `Base.getindex(A::Array, I::AbstractUnitRange{<:Integer})`.
629-
function Base.getindex(a::Array, I1::AbstractNamedUnitRange{<: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})
630632
return copy(view(a, I1))
631633
end
632634
function Base.view(a::AbstractArray, I1::NamedViewIndex, Irest::NamedViewIndex...)

src/abstractnamedinteger.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using TypeParameterAccessors: unspecify_type_parameters
22

3-
abstract type AbstractNamedInteger{Value, Name} <: Integer end
3+
# A named integer is a tagged scalar, not a true `Integer`: mixed named/unnamed
4+
# arithmetic and operations like `i1 * i2` are not cleanly definable under the
5+
# `Integer` contract, and inherited `Integer` fallbacks risk silently dropping the
6+
# name. So it is standalone and supplies the integer-like surface it needs directly.
7+
abstract type AbstractNamedInteger{Value, Name} end
48

59
# Minimal interface.
610
denamed(i::AbstractNamedInteger) = throw(MethodError(dename, Tuple{typeof(i)}))
@@ -143,8 +147,6 @@ function Base.string(i::AbstractNamedInteger; kwargs...)
143147
return "named($(string(denamed(i); kwargs...)), $(repr(name(i))))"
144148
end
145149

146-
(type::Type{<:Number})(i::AbstractNamedInteger) = type(denamed(i))
147-
148150
struct NameMismatch <: Exception
149151
message::String
150152
end

src/abstractnamedunitrange.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ struct FirstIndex{Arr, Dim}
8686
array::Arr
8787
dim::Dim
8888
end
89-
Base.to_index(i::FirstIndex) = Int(first(axes(i.array, i.dim)))
89+
Base.to_index(i::FirstIndex) = denamed(first(axes(i.array, i.dim)))
9090

9191
struct LastIndex{Arr, Dim}
9292
array::Arr
9393
dim::Dim
9494
end
95-
Base.to_index(i::LastIndex) = Int(last(axes(i.array, i.dim)))
95+
Base.to_index(i::LastIndex) = denamed(last(axes(i.array, i.dim)))
9696

9797
function Base.getindex(r::AbstractNamedUnitRange, I::FirstIndex)
9898
return first(r)

test/test_basics.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64})
5757
Index(2; tags = ("X" => "Y",)),
5858
Index(2; tags = "X" => "Y"),
5959
)
60-
@test Int(length(i)) == 2
60+
@test denamed(length(i)) == 2
6161
@test hastag(i, "X")
6262
@test gettag(i, "X") == "Y"
6363
@test plev(i) == 0
@@ -137,17 +137,17 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64})
137137
@test j inds(y)
138138
@test eltype(x) === elt
139139
@test eltype(y) === elt
140-
@test Int.(Tuple(size(x))) == (2, 2)
141-
@test Int.(Tuple(size(y))) == (2, 2)
140+
@test denamed.(Tuple(size(x))) == (2, 2)
141+
@test denamed.(Tuple(size(y))) == (2, 2)
142142

143143
i = Index(2)
144144
j = Index(2)
145145
a = randn(elt, i) * randn(elt, j)
146146
for kwargs in ((; rtol = 1.0e-2), (; cutoff = 1.0e-2))
147147
x, y = factorize(a, (i,); kwargs...)
148148
@test a x * y
149-
@test Int.(Tuple(size(x))) == (2, 1)
150-
@test Int.(Tuple(size(y))) == (1, 2)
149+
@test denamed.(Tuple(size(x))) == (2, 1)
150+
@test denamed.(Tuple(size(y))) == (1, 2)
151151
end
152152
end
153153
end

test/test_namedinteger.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,4 @@ using Test: @test, @testset
77
@test i isa AbstractNamedInteger
88
@test denamed(i) 3
99
@test name(i) :i
10-
for type in (Int32, Int64, Float32, Float64)
11-
@test type(i) type(3)
12-
@test convert(type, i) type(3)
13-
end
1410
end

test/test_tensoralgebra.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ using Test: @test, @test_broken, @testset
104104
a = randn(elt, i, j, k, l)
105105
u, s, v = svd(a, (i, k), (j, l); trunc = (; maxrank = 2))
106106
@test u * s * v a
107-
@test Int.(Tuple(size(s))) == (2, 2)
107+
@test denamed.(Tuple(size(s))) == (2, 2)
108108
end
109109
@testset "left_null/right_null" begin
110110
dims = (2, 2, 2, 2)

0 commit comments

Comments
 (0)