diff --git a/Project.toml b/Project.toml index 3f5a5eb..42828b3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ITensorBase" uuid = "4795dd04-0d67-49bb-8f44-b89c448a1dc7" -version = "0.11.4" +version = "0.11.5" authors = ["ITensor developers and contributors"] [workspace] @@ -11,7 +11,6 @@ AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MatrixAlgebraKit = "6c742aac-3347-4629-af66-fc926824e5e4" @@ -45,7 +44,6 @@ Accessors = "0.1.39" Adapt = "4.1.1" ArrayLayouts = "1.11" Combinatorics = "1" -Compat = "4.16" ConstructionBase = "1.6" LinearAlgebra = "1.10" MatrixAlgebraKit = "0.2, 0.3, 0.4, 0.5, 0.6" diff --git a/src/ITensorBase.jl b/src/ITensorBase.jl index f0325e2..97d5168 100644 --- a/src/ITensorBase.jl +++ b/src/ITensorBase.jl @@ -5,18 +5,13 @@ export AbstractNamedTensor, NamedTensor, AbstractITensor, ITensor, Index, dimnames, dimnametype, domainnames, hascommoninds, id, inds, mapinds, named, nameddims, noncommoninds, noprime, operator, prime, replaceinds, sim, similar_operator, state, trycommonind, trynoncommonind, uniqueind, uniqueinds, unioninds, uniquename -using Compat: @compat -@compat public @names -@compat public IndexName, name, nametype, replacedimnames, setname, unnamed, unnamedtype -@compat public decoration, - emptytags, - gettag, - gettags, - hastag, - plev, - settags, - tags, - unsettags +if VERSION >= v"1.11.0-DEV.469" + eval( + Meta.parse( + "public @names, IndexName, name, nametype, replacedimnames, setname, space, unnamed, unnamedtype, decoration, emptytags, gettag, gettags, hastag, plev, settags, tags, unsettags" + ) + ) +end # Named-array machinery (relocated from NamedDimsArrays.jl). include("isnamed.jl") diff --git a/src/namedtensor.jl b/src/namedtensor.jl index 100f59c..dbba7ea 100644 --- a/src/namedtensor.jl +++ b/src/namedtensor.jl @@ -25,24 +25,10 @@ struct NamedTensor{DimName} <: AbstractNamedTensor{DimName} # `axes`/algebra interface) can be the parent directly. See the TensorKit extension. unnamed::Any dimnames::Vector{DimName} - function NamedTensor{DimName}(unnamed, dimnames) where {DimName} - # Catch the common ITensors.jl-style mistake of passing indices (`NamedUnitRange`s - # such as `Index`) as the names. Checked before `collect(DimName, ...)`, which would - # otherwise fail with an opaque `convert` error when `DimName` is the dimension-name - # type (e.g. `ITensor`'s `IndexName`). - ( - dimnames isa NamedUnitRange || - any(dimname -> dimname isa NamedUnitRange, dimnames) - ) && throw( - ArgumentError( - "The `NamedTensor`/`ITensor` constructor takes dimension names, not indices \ - (`NamedUnitRange`s such as `Index`), got $(dimnames). To build a tensor from \ - an array and indices, either index the array to inherit the space from the \ - indices, as in `array[i, j]`, or attach only the names and take the space \ - from the array, as in `ITensor(array, name.((i, j)))`." - ) - ) - dimnames = collect(DimName, dimnames) + # The sole inner constructor: enforces the representation invariants (one name per dimension, + # names distinct) on already-collected names. The outer constructors below normalize the + # inputs (strip index names, fix the eltype) and funnel through here. + global function _NamedTensor(unnamed, dimnames::Vector{DimName}) where {DimName} TensorAlgebra.ndims(unnamed) == length(dimnames) || throw(ArgumentError("Number of named dims must match ndims.")) allunique(dimnames) || @@ -51,7 +37,30 @@ struct NamedTensor{DimName} <: AbstractNamedTensor{DimName} end end -NamedTensor(unnamed, dims) = NamedTensor{eltype(dims)}(unnamed, dims) +# `dimnames` can hold plain names or indices (`NamedUnitRange`s such as `Index`): `name` maps an +# index to its name and is the identity on a plain name, so an index's space is ignored (the array +# carries the axes). A single bare index is rejected, since it is ambiguous as `dimnames` (a +# `NamedUnitRange` is itself an iterable of its range values). The two methods repeat this +# normalization rather than one delegating to the other, so each strips names exactly once. +function NamedTensor{DimName}(unnamed, dimnames) where {DimName} + dimnames isa NamedUnitRange && throw( + ArgumentError( + "Got a single index (`NamedUnitRange` such as `Index`) as the dimension names. \ + Pass a tuple or vector, e.g. `ITensor(array, (i, j))`." + ) + ) + return _NamedTensor(unnamed, collect(DimName, name.(dimnames))) +end +# The dimension-name type is inferred from the names, so indices infer `IndexName`, not their type. +function NamedTensor(unnamed, dimnames) + dimnames isa NamedUnitRange && throw( + ArgumentError( + "Got a single index (`NamedUnitRange` such as `Index`) as the dimension names. \ + Pass a tuple or vector, e.g. `ITensor(array, (i, j))`." + ) + ) + return _NamedTensor(unnamed, collect(name.(dimnames))) +end NamedTensor(a::AbstractNamedTensor, inds) = throw(ArgumentError("Already named.")) NamedTensor(a::AbstractNamedTensor) = NamedTensor(unnamed(a), dimnames(a)) diff --git a/src/namedunitrange.jl b/src/namedunitrange.jl index 9f5e12e..044d3a4 100644 --- a/src/namedunitrange.jl +++ b/src/namedunitrange.jl @@ -30,6 +30,14 @@ unnamed(i::NamedUnitRange) = i.value name(i::NamedUnitRange) = i.name unnamedtype(::Type{<:NamedUnitRange{<:Any, <:Any, Unnamed}}) where {Unnamed} = Unnamed +""" + space(i::NamedUnitRange) + +The space of a named range `i`: its underlying (unnamed) range or axis object, with the name +dropped. Equal to [`unnamed`](@ref) for a `NamedUnitRange`. +""" +space(i::NamedUnitRange) = unnamed(i) + # Construct from a space, minting a fresh name of the requested flavor. The space is # anything `to_range` accepts (an `Integer`, an existing range, or a sector-pair vector # when GradedArrays is loaded), so `Index(2)`, `Index(1:3)`, and diff --git a/test/test_basics.jl b/test/test_basics.jl index 6312590..fecad95 100644 --- a/test/test_basics.jl +++ b/test/test_basics.jl @@ -124,14 +124,18 @@ using UUIDs: UUID @test_throws ArgumentError NamedTensor(randn(elt, 4), (:i, :j)) @test_throws MethodError NamedTensor(randn(elt, 2, 2), :i, :j) - # The constructor takes names only, not indices, so passing indices (the - # ITensors.jl idiom) errors, whether as a tuple, a vector, or a single index. + # Passing indices as a tuple or vector builds the tensor, using only their names and + # taking the space from the array. A single bare index still errors (it is ambiguous). i, j = Index.((2, 3)) - @test_throws ArgumentError NamedTensor(randn(elt, 2, 2), Index.((2, 2))) - @test_throws ArgumentError ITensor(randn(elt, 2, 3), (i, j)) - @test_throws ArgumentError ITensor(randn(elt, 2, 3), [i, j]) + @test NamedTensor(randn(elt, 2, 3), (i, j)) isa ITensor + @test ITensor(randn(elt, 2, 3), (i, j)) isa ITensor + @test ITensor(randn(elt, 2, 3), [i, j]) isa ITensor + t = ITensor(randn(elt, 2, 3), (i, j)) + @test issetequal(name.(inds(t)), name.((i, j))) @test_throws ArgumentError ITensor(randn(elt, 2), i) - # The two supported constructions: index the array (inherit the space from the + # The space is taken from the array, not from the index (a mismatched index dim is ignored). + @test size(unnamed(ITensor(randn(elt, 2, 3), (i, Index(9))))) == (2, 3) + # The other supported constructions: index the array (inherit the space from the # indices), or attach only the names (take the space from the array). @test randn(elt, 2, 3)[i, j] isa ITensor @test ITensor(randn(elt, 2, 3), name.((i, j))) isa ITensor diff --git a/test/test_exports.jl b/test/test_exports.jl index 8a37727..397908d 100644 --- a/test/test_exports.jl +++ b/test/test_exports.jl @@ -12,7 +12,7 @@ using Test: @test, @testset :uniqueind, :uniqueinds, :unioninds, :uniquename, ] publics = [ - :IndexName, :name, :nametype, :replacedimnames, :setname, :unnamed, + :IndexName, :name, :nametype, :replacedimnames, :setname, :space, :unnamed, :unnamedtype, :decoration, :emptytags, :gettag, :gettags, :hastag, :plev, :settags, :tags, :unsettags,